Рефлексия конечно помогает, я даже хотел ввернуть это в статью, что мол у других хоть рефлексия есть. Спасибо за ссылку на статью, мой RSS еще не видимо не прогрузил ее. Пример с cutter — вообще бомба, правда со своими компромиссами.
Про vector: мне обязательно надо знать что под капотом std::vector, по крайней мере много чего из внутреннего устройства (есть ли там блокировки, где и когда выделяется память, как он растет когда мы туда добавляем элементы — как минимум).
Согласен, выразительность определения теста страдает. Это интересная наводка, спасибо. Не знаю можно ли ее будет решить как-то не в макросах, каких-то сходу идей нет.
Некоторые изменения в API которые думаются на ближайшее время, это возможность указания «размера» теста, которое может быть выглядеть как параметр StartCase():
В проекте на котором я работаю исключения отключены в основном коде, но включены в тестах (из-за tut). С исключениями бывают проблемы даже не столько в embedded, сколько в неожиданных платформах вроде pnacl (где они были реализованы не сразу) или Emscripten (у меня с ними проблем не было, но судя по документации они там отключены по умолчанию в -o1 потому что реализация довольно дорогая).
Про говорить про «киллер-фичу» с динамической памятью, то я смотрю на это так что динамическая память зашитая внутрь библиотеки — это как дополнительная зависимость. Если отсутствие такой дополнительной зависимости обходится недорого, то от нее можно отказаться. В этом случае библиотека может подойти как тем кому это не принципиально, так и тем кому это важно.
Спасибо за замечания, мне это очень интересно! Библиотека в таком MVP состоянии, так что такого рода замечания наводят меня на мысли где бы ее можно было бы улучшить прежде всего.
Сколько нужно кода чтобы описать testcase и написать assert.
Assert примерно одинаково в обоих случаях, а вот определения тесто в моем случае явно чуть побольше. Если привести примеры из статьи:
TEST(MyString, DefaultConstructor) {
}
versus:
template<> void tested::Case<CASE_COUNTER>(tested::IRuntime* runtime)
{
runtime->StartCase("emptiness");
}
// ... and one group per translation unit
static tested::Group<CASE_COUNTER> x("std.vector", __FILE__);
То есть увеличение есть и может быть можно подумать как его уменьшить (а обернуть в макрос всегда успеем). Но мне кажется что если смотреть с позиции гранулярности тестов то дополнительный код не такой значительный. По моему опыту когда тесты особенно интеграционные вполне выходят за 10+ строк, так что выигрыш по строкам на моих тестах будет не слишком значительным.
2. Сколько занимает запуск измененного testcase?
Я не встраивал эту библиотеку в большой проект, но если честно то я не ожидаю какого-то оверхеда у tested. Изменение test-case это пересборка *.cpp файла + линковка, вроде нет ничего что можно было бы улучшить. Запуск теста — это то где можно что-то по-улучшать если это будет тонким местом.
Момент в производительности который я тестировал — это какой размер исполняемого файла можем получить если укажем глубокий уровень рекурсии для перебора шаблонной функции (типа Group<1024*1024> — я не дождался окончания компиляции). То есть если у вас в translation unit очень много тестов (скажем 1000), то это может увеличивать время компиляции, размер бинарника и поисков по списку. Чтобы на такого рода ограничения не попадать, я пока ограничил количество тестов в группе положительным диапазоном типа signed char (0-127).
3. Возможно фильтрации testcase, запуск всех тестов название которых удовлетворяет регулярному выражению.
Тут неплохо бы фидбэк насколько это нужно. То что я реализовал сейчас, это возможность запуска определенного теста и всех тестов в определенной группе. Например у нас группа «std.container.vector» и тест «construction». Получается что у теста можно сделать штуку под названием адрес, «std.container.vector:construction». И далее запускать регулярные выражения по таком адресу «std.container.*» или «std.container.vector:*». Но только не вполне понятно какой именно кейс для запуска именно по регулярным выражениям?
Хе-хе, я думал об этом. Но тут хотя бы есть выбор, можешь использовать макросы, можешь не использовать. В gtest попытка писать тесты развернув все макросы будет существенно сложнее.
Не то чтобы я принципиально против макросов, но ведь можно поисследовать подход как без них можно было бы обойтись. В C++ например заменили "#define max(x, y) x > y? x: y" на другой вариант с inline функциями и есть вполне понятные причины почему так было сделано.
В случае с макросами в библиотеке тестирования мне не нравится что не очень понятно что происходит на уровне языка когда я пишу тест. Когда разрабатываются unit-тесты как по учебнику, это понимание может быть не требоваться, но когда это какой-нибудь тяжелый интеграционный тест мне надо выяснять как работают все эти дополнительные волшебные коробочки завернутые в макросы и как они поведут себя в моем сложном тесте. В этом смысле голый С++ ближе для понимания, потому что его все уже знают и не требуется учить некий новый DSL на макросах. Еще одна претензия к макросам что исходники библиотеки написанной на макросах довольно тяжело читать.
В итоге, мне показалось интересным покопать вопрос насколько далеко можно уменьшить boilerplate код если выставить ограничение «без макросов».
Про vector: мне обязательно надо знать что под капотом std::vector, по крайней мере много чего из внутреннего устройства (есть ли там блокировки, где и когда выделяется память, как он растет когда мы туда добавляем элементы — как минимум).
Некоторые изменения в API которые думаются на ближайшее время, это возможность указания «размера» теста, которое может быть выглядеть как параметр StartCase():
А также указание на то что тест асинхронный:
Т.е. некоторая избыточность вроде метода StartCase() она все равно будет нужна для других целей.
Про говорить про «киллер-фичу» с динамической памятью, то я смотрю на это так что динамическая память зашитая внутрь библиотеки — это как дополнительная зависимость. Если отсутствие такой дополнительной зависимости обходится недорого, то от нее можно отказаться. В этом случае библиотека может подойти как тем кому это не принципиально, так и тем кому это важно.
Assert примерно одинаково в обоих случаях, а вот определения тесто в моем случае явно чуть побольше. Если привести примеры из статьи:
versus:
То есть увеличение есть и может быть можно подумать как его уменьшить (а обернуть в макрос всегда успеем). Но мне кажется что если смотреть с позиции гранулярности тестов то дополнительный код не такой значительный. По моему опыту когда тесты особенно интеграционные вполне выходят за 10+ строк, так что выигрыш по строкам на моих тестах будет не слишком значительным.
Я не встраивал эту библиотеку в большой проект, но если честно то я не ожидаю какого-то оверхеда у tested. Изменение test-case это пересборка *.cpp файла + линковка, вроде нет ничего что можно было бы улучшить. Запуск теста — это то где можно что-то по-улучшать если это будет тонким местом.
Момент в производительности который я тестировал — это какой размер исполняемого файла можем получить если укажем глубокий уровень рекурсии для перебора шаблонной функции (типа Group<1024*1024> — я не дождался окончания компиляции). То есть если у вас в translation unit очень много тестов (скажем 1000), то это может увеличивать время компиляции, размер бинарника и поисков по списку. Чтобы на такого рода ограничения не попадать, я пока ограничил количество тестов в группе положительным диапазоном типа signed char (0-127).
Тут неплохо бы фидбэк насколько это нужно. То что я реализовал сейчас, это возможность запуска определенного теста и всех тестов в определенной группе. Например у нас группа «std.container.vector» и тест «construction». Получается что у теста можно сделать штуку под названием адрес, «std.container.vector:construction». И далее запускать регулярные выражения по таком адресу «std.container.*» или «std.container.vector:*». Но только не вполне понятно какой именно кейс для запуска именно по регулярным выражениям?
В случае с макросами в библиотеке тестирования мне не нравится что не очень понятно что происходит на уровне языка когда я пишу тест. Когда разрабатываются unit-тесты как по учебнику, это понимание может быть не требоваться, но когда это какой-нибудь тяжелый интеграционный тест мне надо выяснять как работают все эти дополнительные волшебные коробочки завернутые в макросы и как они поведут себя в моем сложном тесте. В этом смысле голый С++ ближе для понимания, потому что его все уже знают и не требуется учить некий новый DSL на макросах. Еще одна претензия к макросам что исходники библиотеки написанной на макросах довольно тяжело читать.
В итоге, мне показалось интересным покопать вопрос насколько далеко можно уменьшить boilerplate код если выставить ограничение «без макросов».