LINUX.ORG.RU

Stringify выражения в unit тесте

 ,


0

1

Хочу написать небольшой велосипедный unit test фреймворк, требования следующие: во-первых, нужно чтобы в выхлопе теста фигурировало тестируемое выражение, во-вторых, у тестов должны быть опциональные аргументы, например описание. Пример (ASSERT, понятно, проверяет выражение на истинность):

int main() {
  ASSERT(true && true && true);
  ASSERT(true && true && true, "is always true");
  ASSERT(true && false);
  ASSERT(true && false, "is actually false");
}

выдаёт

passed: true && true && true 
passed: true && true && true (is always true)
failed: true && false
failed: true && false (is actually false)

Вопрос как это реализовать. Т.к. нужен stringify выражения, понятно что без макросов не обойтись. Но с макросами проблема в невозможности перегрузки и дефолтных зрачений (чтобы не нужен было указывать каждый раз все опциональные аргументы).

Есть variadic macros, но #define ASSERT(expr, ...) my_overloaded_function(#expr, expr, __VA_ARGS__) будет требовать хотя бы одного опционального аргумента, что не хочется. Есть ##__VA_ARGS__, которая эту проблему решает, но это GNU расширение.

Совсем ли нет других решений? GNU расширение я не хочу, писать каждый раз все опциональные аргументы не хочу, и делать пачку макросов на каждую комбинацию тоже не хочу.

★★★★★
bool is_actually_false = true && false;
ASSERT(is_actually_false);

Очень советую ознакомиться с модульным тестированием хотя бы на начальном уровне, прежде чем писать тесты и, тем более, фреймворки.

E ★★★
()

Есть ##__VA_ARGS__, которая эту проблему решает, но это GNU расширение.

Вроде, C99 и C++11, так что можно не переживать так сильно.

const86 ★★★★★
()

Если речь о строковом литерале, то довольно легко. Для произвольного типа аргумента тоже можно извратиться.

Ещё вариант с настоящей перегрузкой. Только нужно расширить на большее количество опциональных аргументов (возможно, экспоненциальным количеством перегрузок).

xaizek ★★★★★
()
Ответ на: комментарий от E

Очень советую ознакомиться с модульным тестированием хотя бы на начальном уровне, прежде чем писать тесты и, тем более, фреймворки.

Давай либо говори что-нибудь по существу, либо помалкивай, советчик ни о чём.

slovazap ★★★★★
() автор топика
Ответ на: комментарий от const86

Вроде, C99 и C++11, так что можно не переживать так сильно.

Нет.

% clang++ -Wall -Wextra -pedantic -Werror 8.cc
8.cc:3:21: error: variadic macros are a C99 feature [-Werror,-Wvariadic-macros]
#define xxx(format, ...) printf(format, ##__VA_ARGS__)
                    ^
8.cc:3:39: error: token pasting of ',' and __VA_ARGS__ is a GNU extension [-Werror,-Wgnu]
#define xxx(format, ...) printf(format, ##__VA_ARGS__)
                                      ^
2 errors generated.
% clang++ -std=c++11 -Wall -Wextra -pedantic -Werror 8.cc 
8.cc:3:39: error: token pasting of ',' and __VA_ARGS__ is a GNU extension [-Werror,-Wgnu]
#define xxx(format, ...) printf(format, ##__VA_ARGS__)
                                      ^
1 error generated.
eddy@box% g++5 -Wall -Wextra -pedantic -Werror 8.cc 
8.cc:3:21: error: anonymous variadic macros were introduced in C++11 [-Werror=variadic-macros]
 #define xxx(format, ...) printf(format, ##__VA_ARGS__)
                     ^
8.cc:6:10: error: ISO C++11 requires at least one argument for the "..." in a variadic macro [-Werror]
  xxx("AA");
          ^
8.cc:6:10: error: invoking macro xxx argument 2: empty macro arguments are undefined in ISO C++98 [-Werror]
cc1plus: all warnings being treated as errors
% g++5 -std=c++11 -Wall -Wextra -pedantic -Werror 8.cc 
8.cc:6:10: error: ISO C++11 requires at least one argument for the "..." in a variadic macro [-Werror]
  xxx("AA");
          ^
cc1plus: all warnings being treated as errors
slovazap ★★★★★
() автор топика
Ответ на: комментарий от xaizek

Если речь о строковом литерале, то довольно легко

Ещё вариант с настоящей перегрузкой

А вот вам спасибо. Пока не до конца осознал, но направление понял. Круто.

slovazap ★★★★★
() автор топика

Посмотрите как сделано в cppunit

vromanov ★★★
()
Ответ на: комментарий от xaizek

Не, к сожалению всё это не работает. Оказывается, GNU extension это уже не указание дополнительных аргументов на месте ..., поэтому #define ASSERT(expr, ...) не применим. А с #define ASSERT(...) получится превратить в строку только весь список аргументов. Парсить его потом тоже не вариант.

slovazap ★★★★★
() автор топика
Ответ на: комментарий от slovazap

А впрочем решение оказалось простым:

#define ASSERT1(expr, ...)
#define ASSERT(...) ASSERT1(__VA_ARGS__, 0)

т.е. просто обернуть в макрос добавляющий лишний аргумент

slovazap ★★★★★
() автор топика
Ответ на: комментарий от slovazap

Глупо

А-а, ну хорошо.

На всякий случай распишу подробнее.

Не используя каких-то внешних утилит, вы будете должны оставаться в рамках С++. Мне кажется для тестирования софтварей это не всегда удобно. Вот например как можно было бы сделать с препроцессором:

  y = add(2, 2);  /* !! y != 5 */
  x = do_smth(y); /* !! x >= 0; if not { WARN("x == %d", x); DONT_STOP; }; */
  long_calc(); /* !! if (EXEC_TIME > 2*1000) { STOP; }; */

Прогнал такое через препроцессор и комментарии превратились в ассерты и директивы. Теоретически можно на коленке сделать интересную утилиту, вместо написания очередного бессмысленного УГ

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.