Тестирование кода
Всем доброго дня! Хочу автоматизировать проверку программ на языке С++. Допустим есть 20 файлов .cpp (полученных от учеников:) ) выполняющих одно и то же, но написанных совершенно в разном стиле и т.п. Можно ли написать программу, которая будет тестировать на заданных тестах, некоторый исходник? Представим, что есть код:
#include <iostream>
#include <cmath>
int main()
{
int a, b;
cin >> a >> b;
if(b == 0)
{
cout << "ERROR";
return 1;
}
cout << log(a) << endl;
cout << a / b;
return 0;
}
Для него есть текстовый файл с тестами:
1 2
0 0 //<- результат
2 5
0.30102 0.4
0 4
ERROR //т.е. программа должна вывести сообщение об ошибке
3 0
ERROR
Запустив проверку программы на выходе будем иметь что-то вроде этого:
Test #1
OK
Test #2
OK
Test #3
Fail
Test #4
OK
Собственно проблема в том, что нет идей как это сделать. Буду благодарен за любую помощь!
Ответы (2 шт):
Нет, просто так это сделать нельзя. Анализировать код может ИИ или человек. Можно попытаться создать подобный ИИ. Во первых: нужно анализировать каждую строку кода. Во вторых: нужно описать абсолютно ВСЁ, что есть в с++(работа с переменными, массивами, классами, функциями, пространствами имён и тп) Ну и в третьих: описать правила работы этого всего.
Это будет очень долго, ведь функций в с++ очень много, и вам надо будет их все описать, и описать как они должны и не должны работать. Да и к тому же, есть уже готовые анализаторы и генераторы кода. Тот же chatgpt. Так что не советовал бы этим заниматься, так как это уже давно созданно, и нет смысла тратить столько времени, на изобретение велосипеда. Исправлено: компилятор тоже может замечать ошибки, но это вряд ли поможет в создании своего собственного анализатора
Ну... Без костылей не обойтесь(
#include <cerrno>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std::string_literals;
const std::string TESTS_FILENAME = "tests.txt"s;
struct Test {
std::string input;
std::string expecting;
};
int main(int argc, char** argv)
{
if (argc < 2)
{
std::cerr << "usage: " << argv[0] << " PROGRAM\n";
return 1;
}
std::ifstream tests_file;
tests_file.open(TESTS_FILENAME, std::ios::in);
if (tests_file.fail())
{
std::cerr << "error when opening tests: " << std::strerror(errno) << '\n';
return 2;
}
std::vector<Test> tests;
std::string line;
for (;;)
{
if (!std::getline(tests_file, line))
break;
if (line.empty())
continue;
Test test;
test.input = line;
if (!std::getline(tests_file, line))
break;
if (line.empty())
continue;
test.expecting = line;
tests.push_back(test);
}
size_t i = 0;
for (const auto& test : tests)
{
if (i++ > 0)
{
std::cout << '\n';
}
std::cout << "Test #" << i << '\n';
char proc_buffer[128];
sprintf(proc_buffer, "bash -c '%s <<< \"%s\"'\n", argv[1], test.input.c_str());
FILE* process = popen(proc_buffer, "r");
if (process == nullptr)
{
std::cerr << "error when opening process: " << std::strerror(errno) << '\n';
tests_file.close();
return 3;
}
std::string output;
int c;
while ((c = getc(process)) != EOF)
{
if (c == '\n')
{
c = ' ';
}
output.append(1, (unsigned char)c);
}
if (output != test.expecting)
{
std::cout << "Fail\n";
}
else
{
std::cout << "OK\n";
}
}
return 0;
}