vladislav
Просто есть такая штука как языковой барьер
Berkus
а как именно, поясни плиз
ну так тебе ж надо проверить что асинхронная операция что-то сделает, запускаешь ее в одном треде, вейт фьючура с таймаутом в основном, и вперёд
Roman
короче суть в том, что мой вопрос относится не к unit-testing'у а functional testing'у в котором доказывается верная работоспособность асинхронных сущностей..
придам пример (грубый, но пойдёт)
testCase() {
readFile("/this/file.txt", [](std::string result) {
// will never execute
ASSERT(result, expected)
});
}
testCase не дождётся результата и завершится, ASSERT никогда не проверится
Roman
для того чтоб тест правильно выполнился нужно изменить его немного:
testCase(test) {
readFile("/this/file.txt", [](std::string result) {
ASSERT(result, expected)
test.finish()
});
}
Roman
в данном случае тест либо дождётся вызова асинхронного callback'а либо timeout'ит
Berkus
testCase() {
readFile("/this/file.txt", [](std::string result) {
ASSERT(result, expected)
future.set(true);
});
future.waitWithTimeout();
}
Roman
библиотека которую я хочу протестить не столь проста
streams.create([](handle) {
handle.close("data")
})
.attach([](data, handle) {
ASSERT(data, "data")
handle.fail("fuck")
})
.failure([](error, handle) {
ASSERT(error, "fuck")
})
в данном случае мы создаём асинхронный объект, который при закрытии запускает при'attach'енный второй объект
Roman
короче говоря: нужно доказать, что control flow шёл правильным путём и что данные были верно переданы
Berkus
и ты пишешь для этого тестинг фреймворк?
Roman
я пытаюсь протестировать код)) но не могу найти правильный подход
Roman
т.е. создать просто несколько future'ов и ими блокировать завершение тестовой функции?
Roman
а с макросами я просто эксперементирую, может и так можно решить проблему
Roman
кстати.. почему finish() это костыль, интересен аргумент(ы)
Berkus
я бы разложил фьючеры по пути следования и в конце подождал чтобы они все стали с верными значениями
Berkus
кстати.. почему finish() это костыль, интересен аргумент(ы)
я уже не помню как оно работало в моче или в чае, но когда начали его портировать на промисы и асинхронщину, любой забытый done() превращал отладку в ад - тест мог не закончиться или закончиться с непонятной совершенно ошибкой глубоко в кишках тест фреймворка. в ++ типизация построже конечно, но лучше не делать _специальный_флаг_завершения_теста_ пушо его всяко кто-нибудь забудет вызвать в замороченном случае и будет плеваться. с твоим вложенным путем исполнения где надо finish() а где не надо? да пофиг на самом деле, он ведь асинхронный, а ожидать выполнения мы должны в основном треде если толково это делать
Roman
в моём эксперементальном решении это невозможно, ставится определённый timeout, по истечению которого кейс убивается, т.е. если кто-то где-то забыл done() вызвать то этот кейс в результатах будет помечен как fail (timeout)
Berkus
а таймаут у тебя глобальный для всех кейсов?
Berkus
что если одни асинхронные операции завершаются за 1 секунду а другие за 100?
Roman
есть default timeout, который определяется всем кейсам, да
Berkus
ну то есть ты не можешь сделать в одном тест кейсе три фьючера и ждать один с таймаутом 1, второй с таймаутом 10 и третий с таймаутом 100?
Berkus
кейсам да
Berkus
а путям внутри одного кейса?
Roman
кейс он неделимый
Berkus
ну ок, твои кейсы тебе и решать
Roman
ты ему определяешь таймаут, если кейс не завершается, например по причине забытого done(), тогда - failure (timeout)
Roman
кейсы не должны быть слишком большими, забыть done() не так легко, ибо просто будет постоянно таймаутить, а значит что-то не так
Constantine
Constantine
или тестирование такое, что наличие лишнего барьера памяти все поломает?
Roman
с future'ами проблема идентичная... если неправильно выставить timeout'ы или забывать завершать future... но там будет больше boiler plate кода чем с done()
Constantine
Berkus
у future уже есть флаг, done() вводит глобальный стейт в тесткейсы, который в целом не нужен
Berkus
на уровне тест-фреймворка это ненужная сущность
Constantine
как вообще в плюсах можно что-то забыть
Constantine
если есть деструктор
Roman
ну так мне придётся прописывать более 3-4 future'ов и каждый закрывать, каждому таймаут ставить
а done() нужно лишь раз вызвать, меньше кода
Berkus
Roman
да вот, пробую, просто хочется опять-же от boilerplate'а избавиться...
$Test(someTestCase) {
readFile("/some/file.txt", [] (std::string result) {
$AssertEqual(std::string, result, "bla")
$Finish
});
}
Roman
после этого:
int main () {
// default timeout 1000 ms
testing::Test tst("My Test", {someTestCase}, 1000)
tst.run();
tst.printResults();
}
Roman
но хотелось бы сократить, в $Test уже каким-то образом зарегистрировать функцию
Berkus
ну вот посмотри как BOOST_AUTO_TEST_CASE ее регистрирует например
Constantine
а я хочу, чтобы все тесты были в json :)
Roman
Berkus
Vladislav
Vladislav
Для начала
Berkus
Berkus
без них прекрасно работает
Vladislav
Строго говоря
Roman
без них прекрасно работает
вот я не могу понять как они это сделали...
ибо регистрировать надо после декларации, но в макросе нет доступ к "после-декларации" потому-что function block не внутри макроса
Berkus
вот и загляни к ним в кишки
Berkus
всё ж есть
Berkus
я не буду за тебя гуглить сорцы буста, извини
Roman
я уже в них копаюсь))
Roman
правда найти не могу BOOST_AUTO_TEST_CASE
Roman
ща
Constantine
Он не яп
Шарите. Поэтому тесты в JSON, интерпретатор - прекомпилить
Vladislav
Roman
да куда они его спрятали то о_О
Constantine
Чет сложна
Нет, если уже есть парсер (на самом деле xml но пофик), который в С++ транслирует
Vladislav
Constantine
Я хочу написать
//GENERATOR_USE(""_image3_dpi_aware)
Roman
документацию BOOST_AUTO_TEST_CASE нахожу без проблем, в сорсах - ни следа о_О
Constantine
И хочу написать
<function-factory-visitor base="Interface"/>
Vladislav
Constantine
Х3 что там в джаве, но меня парит писать 30 виртуальных функций для каждого имени для различных классов сообщений руками
Vladislav