Навеяло недавним достижением
spamsink: восстановлением исходников игры "Посадка на Луну" с БЭСМ-6.
https://github.com/besm6/bega-re/blob/master/landing.pas
Пусть у нас есть диалоговое приложение на Си++ с текстовым интерфейсом. Приложение запрашивает построчный ввод из std::cin и выдаёт результаты на std::cout. Условно говоря, вызов выглядит следующим образом:
Нет, это не задача для собеседования. Тут и опытный человек голову сломает.
Традиционно такие штуки принято делать через Expect, но выглядит такой подход слишком громоздко. Лучше найти решение средствами самого языка Си++.
https://github.com/besm6/bega-re/blob/master/landing.pas
Пусть у нас есть диалоговое приложение на Си++ с текстовым интерфейсом. Приложение запрашивает построчный ввод из std::cin и выдаёт результаты на std::cout. Условно говоря, вызов выглядит следующим образом:
Схематично, работает оно так:application(std::cin, std::cout);
Теперь стоит задача покрыть имеющуюся реализацию тестами. Я ж теперь адепт TDD. Хочется иметь возможность делать тесты (с применением Catch) вида:void application(std::istream &input, std::ostream &output)
{
output << "something\n";
while (!input.eof()) {
std::string line;
getline(input, line);
std::string result = something(line);
output << result;
}
}
send("foo\n");
std::string reply = receive();
REQUIRE(reply == "bar\n");
Как бы вы стали решать эту задачу? Я имею в виду, как средствами Си++ привинтить одно к другому? Чтобы, не модифицируя исходники application(), вызывать и тестировать её из Catch?Нет, это не задача для собеседования. Тут и опытный человек голову сломает.
Традиционно такие штуки принято делать через Expect, но выглядит такой подход слишком громоздко. Лучше найти решение средствами самого языка Си++.

no subject
Date: 2020-08-12 23:41 (UTC)Ничо не понимаю. Казалось бы, делов-то: делаем в тесткейсах разные стримы из строк, и проверяем, шо она мне сказала. Окей, предположим, эта апликаци я вообще отдельно. Ну так стартуем ее, подавая на вход наши тесткейсы и забирая аутпут.
И чо?
no subject
Date: 2020-08-13 00:02 (UTC)no subject
Date: 2020-08-13 00:04 (UTC)no subject
Date: 2020-08-13 00:25 (UTC)no subject
Date: 2020-08-13 00:46 (UTC)В Unit-тестах надо вызывать внутренние функции something().
А если речь идёт о тестировании целого приложения, то вызвать его в отдельном процессе с редиректом STDIN, STDOUT в Pipe.
И в тестах вызывать что-то вроде:
Verify(pipeOut, pipeIn, "some input", "expected output").
no subject
Date: 2020-08-13 00:49 (UTC)no subject
Date: 2020-08-13 00:55 (UTC)no subject
Date: 2020-08-13 01:26 (UTC)no subject
Date: 2020-08-13 02:03 (UTC)Так это, из строки у вас там нельзя стрим сделать, что ли? Это как-то уж больно было бы странно. Это если мы можем вызвать функцию. А нет - так через ОС вызвать эту хрень и приклеить stdio и stdout. На джаве элементарно.
no subject
Date: 2020-08-13 04:25 (UTC)no subject
Date: 2020-08-13 04:28 (UTC)Вызывать через ОС - это можно, называется Expect. Но выходит из пушки по воробьям.
no subject
Date: 2020-08-13 04:36 (UTC)Ха, много не надо. Для каждого кейса стрим с одним кейсом, и выход соответственно. А иначе корутины присобачивать... в джаве это можно, но нудно.
no subject
Date: 2020-08-13 04:57 (UTC)Корутины решают задачу, да. Вот я и ищу, наверняка кто-то сделал уже.
no subject
Date: 2020-08-13 05:09 (UTC)no subject
Date: 2020-08-13 05:13 (UTC)no subject
Date: 2020-08-13 05:30 (UTC)no subject
Date: 2020-08-13 05:50 (UTC)no subject
Date: 2020-08-13 07:03 (UTC)внизу :
class EgLocationTests
{
public:
EgDataNodesType testDataNodes;
bool testCreateLocations();
bool testAddLocations();
bool testLoadLocationsData();
void testShowResult(bool res, const QString &theMessage)
{
if (res)
{
qDebug().noquote() << "PASS" << theMessage;
}
else
{
qDebug().noquote() << "FAILED" << theMessage;
}
}
};
наверху:
tmpRes = attrTests.testCreateAttributes();
res = res && tmpRes;
tmpRes = attrTests.testAddAttributes();
res = res && tmpRes;
tmpRes = attrTests.testLoadAttributes();
res = res && tmpRes;
if (res)
qDebug() << "\nAll tests PASSED\n";
else
qDebug() << "\nSome tests FAILED\n";
дальше можно запускать или лапгами или каким-нть скриптом и парсить на "FAIL"
бтв, а во что оборачивать чтобы сорцы на дриме нормально показывались ?
Тоже мне бином Ньютона
Date: 2020-08-13 07:27 (UTC)#include <iostream> #include <streambuf> #include <queue> class Feedback : public std::streambuf { std::queue<char> q; char c; public: Feedback() : std::streambuf() { setp(0, 0); setg(0, 0, 0); for (auto c : "1 1 ") if (c) q.push(c); } int underflow() { c = q.front(); q.pop(); setg(& c, & c, & c+1); return c; } int overflow(int c) { std::cout << char(c); q.push(c); return 0; } }; void fib(std::istream & in, std::ostream & out) { int a, b; in >> a; for (int i = 0; i < 20; ++i) { in >> b; out << a + b << ' '; a = b; } out << '\n'; } int main() { Feedback fb; std::istream in(&fb); std::ostream out(&fb); fib(in, out); }no subject
Date: 2020-08-13 07:28 (UTC)и инициализировать ей std::istream, std::ostream с которыми вызовете application()
P.S. sorry, увеидел что уже ниже написали
no subject
Date: 2020-08-13 17:16 (UTC)https://stackoverflow.com/questions/36119525/google-mock-mock-fstream-object-and-control-of-execution-in-test
Наверняка можно аналогично и для Catch смастерить.
Красиво
Date: 2020-08-13 21:21 (UTC)Но я не вижу, как этот подход решает поставленную задачу. Попробуй запустить landing.cc, посылая ввод через send() и принимая вывод через receive().
no subject
Date: 2020-08-13 21:24 (UTC)no subject
Date: 2020-08-13 21:34 (UTC)no subject
Date: 2020-08-13 21:54 (UTC)внизу :
наверху:
дальше можно запускать или лапгами или каким-нть скриптом и парсить на "FAIL"
no subject
Date: 2020-08-13 21:54 (UTC)no subject
Date: 2020-08-13 22:00 (UTC)Конечно вам нужен свой stream-interface wrapper так как ifstream.get() не виртуальный метод.
no subject
Date: 2020-08-13 22:11 (UTC)no subject
Date: 2020-08-13 22:13 (UTC)Re: Красиво
Date: 2020-08-13 22:13 (UTC)Interact world; world.send("foo\n"); application(std::istream(& world), std::ostream(& world)); std::string reply = world.receive(); REQUIRE(reply == "bar\n");Re: Красиво
Date: 2020-08-13 22:16 (UTC)no subject
Date: 2020-08-13 22:23 (UTC)Mock может запомнить список ожидаемых значений.
Не совсем REPL, но для теста достаточно.
Потом можно проверить весь output сразу или тоже сделать мок и задать список ожидаемых строк -- если вам построчно нужно.
Или вам нужно менять ввод в зависимости от вывода?
no subject
Date: 2020-08-13 22:27 (UTC)no subject
Date: 2020-08-13 22:52 (UTC)Если это тестовое приложение, тогда моки не помогут,
нужeн pipe или streambuf, как предложили выше.
Что-то типа такого:
https://stackoverflow.com/questions/28356629/connecting-two-streaming-functions-c
Re: Красиво
Date: 2020-08-13 23:24 (UTC)no subject
Date: 2020-08-14 00:38 (UTC)no subject
Date: 2020-08-14 00:41 (UTC)no subject
Date: 2020-08-14 07:06 (UTC)no subject
Date: 2020-08-14 09:40 (UTC)а, ну собсно оно так уже написано (на третий день коты посмотрели на топик, ага), просто дёргать снаружи со своими стримами и сравнивать результаты с ожидаемыми
no subject
Date: 2020-08-14 13:33 (UTC)с функцией из топика тоже будет работать, только шаблон результата поменять на фактический
и третья строка не нужна, лишний копипаст был
Re: application(std::cin, std::cout); - ut
Date: 2020-08-15 17:30 (UTC)Лучше найти решение средствами самого языка Си++.
Вот, хоть режьте меня, но я не понимаю, зачем в этом случае Си++.
Если я правильно понял, тестировать надо, что при определённом вводе есть определённый вывод.
> Традиционно такие штуки принято делать через Expect, но выглядит такой подход слишком громоздко.
А не боимся, что с Си++ может быть ещё более громоздко?
no subject
Date: 2020-08-15 18:11 (UTC)Если основной софт на Си++, то и тестовое хозяйство к нему логично вести на Си++.
Вовсе неплохо получилось, вот пример теста:Решение можно посмотреть здесь: https://vak.dreamwidth.org/664590.html
Re: основной софт на Си++
Date: 2020-08-15 20:14 (UTC)Система конфигурации сбрки - тоже на Си++?
И система сборки - на Си++?
Логично же, нет?!
;-)
Тогда и
void next_step(const std::string & sec_name, const std::string & cmd, const std::string & answer) { SECTION(sec_name) { session.send(cmd + "\n"); reply = session.receive(); REQUIRE(reply == answer + "\n"); } }неплохо бы ввести.