Bash скрипт для тестов

Тесты для задачек Advent of Code будут описаны на Bash. Парадигма ввода-вывода в Си отдаёт приоритет прямой работе с STDIN, STDOUT и STDERR. Bash в таком случае – хороший компаньон для работы. Он позволит сделать универсальный скрипт и для компиляции программы, и для линтинга, и для запуска тестов.

Посмотрим на код тестового скрипта test_i.sh для задачки первого дня.

#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

BIN=/tmp/part_i
SRC=./part_i.c

RESET='\033[0;0m'
RED='\033[0;31m'
GREEN='\033[0;32m'
BOLD='\033[1;37m'

check () {
        echo -ne "${BOLD}Test${RESET} $1 => $2 "
        OUTPUT=$(echo "$1" | $BIN 2>&1) && [ "$OUTPUT" = "$2" ] || {
                echo -e "${RED}FAILED${RESET}, output: $OUTPUT"
                false
        }
        echo -e "${GREEN}PASSED${RESET}"
}

clang -g -Og -std=c99 \
        -Weverything -Werror \
        -fsanitize=undefined,address \
        -fno-sanitize-recover=all \
        -o "$BIN" "$SRC"

gcc -g -Og -std=c99 \
        -Wall -Wextra -Wpedantic -Werror \
        -fsanitize=undefined,address \
        -fno-sanitize-recover=all \
        -o "$BIN" "$SRC"

clang-tidy $SRC 2> /dev/null

check '(())' 0
check '()()' 0
check '(((' 3
check '(()(()(' 3
check '))(((((' 3
check '())' -1
check '))(' -1
check ')))' -3
check ')())())' -3
echo 'All tests are completed and passed!'

ANSWER="$($BIN < ./input 2>&1)" && echo "Answer for input: $ANSWER" || {
        echo -e "${RED}Program has exited with non-zero code!${RESET}"
        echo "Output: $ANSWER"
}

В начале скрипта присутствует set -euo pipeline и IFS=$'\n\t'. Это ключевые части unofficial strict mode. Погружаться в подробности мы не будем, отметим только, что использование unofficial strict mode для Bash – дело личного вкуса.

Основная часть тестового скрипта – наивная функция check. Она принимает два аргумента. Первый $1 – строка, которая с помощью echo отправляется на вход приложения $BIN. Второй $2 – ожидаемый вывод программы, который проверяется в квадратных скобках с помощью =.

Если приложение завершилось с ошибкой или вывод не имеет точного соответствия, функция check выведет ошибку и вернёт false. Если всё ок – скажет PASSED.

Структура функции check может выглядеть сложно, но если её декомпозировать, то большая часть кода – вывод с помощью echo и разукрашивание текста с помощью управляющих последовательностей ANSI: RED, GREEN, BOLD и RESET.

До начала всех проверок скрипт прогонит исходник $SRC на 2-ух компиляторах. Clang с флагом -Weverything послужит хорошим референсом для отлавливания сомнительного кода. Дальнейшее тестирование будет проводиться на версии из GCC.

Дополнительная проверка от clang-tidy должна помочь с углубленным выявлением проблемного кода и отдельного класса стилистических ошибок, связанных с Си.

После всех тестов в программу $BIN будет передан файл input, который содержит ответ на задачу. В случае, если исполнение программы не завершилось успешно, то будет выведена ошибка, как и в упомянутой выше функции check.

Отдельная ремарка должна быть посвящена конструкции 2&>1. Это перенаправление вывода STDERR в STDOUT. Нужно перенаправление для того, чтобы Вы могли получить ошибку в $OUTPUT и $ANSWER. В ином случае там может быть пустая строка.

Этот скрипт, незначительно адаптированный, используется во всех задачках Advent of Code. Решения задачек и тестовые скрипты для них можно найти в репозитории на GitHub.

Журнал изменений

14 марта 2025 г.: реструктурировал заметку, оставил только информацию о Bash скрипте.

2 января 2026 г.: перенёс исходный код на GitHub.

Если Вы хотите обсудить содержание заметки, задать вопросы или предложить изменения, то со мной можно связаться в Telegram