186 lines
4.3 KiB
C++
186 lines
4.3 KiB
C++
#include <algorithm>
|
|
#include <memory>
|
|
#include <cstdlib>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/reboot.h>
|
|
|
|
template<std::size_t size>
|
|
std::size_t read_command(int descriptor, char *command_buffer)
|
|
{
|
|
std::ptrdiff_t bytes_read{ 0 };
|
|
std::size_t read_so_far{ 0 };
|
|
|
|
while ((bytes_read = read(descriptor, command_buffer + read_so_far, size - read_so_far - 1)) > 0)
|
|
{
|
|
read_so_far += bytes_read;
|
|
if (read_so_far >= size - 1)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
command_buffer[read_so_far] = 0;
|
|
return read_so_far;
|
|
}
|
|
|
|
enum class status
|
|
{
|
|
success,
|
|
failure,
|
|
warning,
|
|
fatal
|
|
};
|
|
|
|
template<std::size_t size>
|
|
void make_path(char *destination, const char *directory, const char *filename, const char *extension)
|
|
{
|
|
memcpy(destination, directory, strlen(directory));
|
|
std::size_t remaining_space = size - strlen(directory);
|
|
|
|
if (extension != nullptr)
|
|
{
|
|
remaining_space -= strlen(extension) + 1;
|
|
}
|
|
strncpy(destination + strlen(directory), filename, remaining_space);
|
|
|
|
if (extension != nullptr)
|
|
{
|
|
strncpy(destination + strlen(destination), extension, strlen(extension));
|
|
}
|
|
}
|
|
|
|
status run_test(const char *file_entry_name)
|
|
{
|
|
printf("Running %s. ", file_entry_name);
|
|
|
|
char filename[256];
|
|
char command_buffer[256];
|
|
char file_buffer[256];
|
|
int pipe_ends[2];
|
|
|
|
if (pipe(pipe_ends) == -1)
|
|
{
|
|
perror("pipe");
|
|
return status::fatal;
|
|
}
|
|
make_path<sizeof(filename)>(filename, "./tests/", file_entry_name, nullptr);
|
|
|
|
auto child_pid = fork();
|
|
if (child_pid == -1)
|
|
{
|
|
return status::fatal;
|
|
}
|
|
else if (child_pid == 0)
|
|
{
|
|
close(STDIN_FILENO);
|
|
close(STDERR_FILENO);
|
|
close(pipe_ends[0]); // Close the read end.
|
|
|
|
if (dup2(pipe_ends[1], STDOUT_FILENO) == -1)
|
|
{
|
|
perror("dup2");
|
|
}
|
|
else
|
|
{
|
|
execl(filename, filename);
|
|
perror("execl");
|
|
}
|
|
close(STDOUT_FILENO);
|
|
close(pipe_ends[1]);
|
|
std::exit(1);
|
|
}
|
|
else
|
|
{
|
|
close(pipe_ends[1]); // Close the write end.
|
|
auto read_from_command = read_command<sizeof(command_buffer)>(pipe_ends[0], command_buffer);
|
|
close(pipe_ends[0]);
|
|
|
|
int wait_status{ 0 };
|
|
wait(&wait_status);
|
|
|
|
make_path<sizeof(filename)>(filename, "./expectations/", file_entry_name, ".txt");
|
|
|
|
std::unique_ptr<FILE, int(*)(FILE *)> expectation_descriptor(fopen(filename, "r"), &fclose);
|
|
|
|
if (expectation_descriptor == nullptr)
|
|
{
|
|
return status::warning;
|
|
}
|
|
auto read_from_file = fread(file_buffer, 1, sizeof(file_buffer) - 1, expectation_descriptor.get());
|
|
file_buffer[std::max<std::ptrdiff_t>(0, read_from_file)] = 0;
|
|
|
|
if (strcmp(command_buffer, file_buffer) != 0)
|
|
{
|
|
printf("Failed. Got:\n%s", command_buffer);
|
|
|
|
return status::failure;
|
|
}
|
|
else
|
|
{
|
|
fwrite("\n", 1, 1, stdout);
|
|
return status::success;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct summary
|
|
{
|
|
std::size_t total{ 0 };
|
|
std::size_t failure{ 0 };
|
|
std::size_t success{ 0 };
|
|
};
|
|
|
|
void walk()
|
|
{
|
|
auto directory_stream = opendir("./tests");
|
|
dirent *file_entry;
|
|
|
|
summary test_summary;
|
|
|
|
while ((file_entry = readdir(directory_stream)) != nullptr)
|
|
{
|
|
if (file_entry->d_name[0] == '.')
|
|
{
|
|
continue;
|
|
}
|
|
++test_summary.total;
|
|
switch (run_test(file_entry->d_name))
|
|
{
|
|
case status::failure:
|
|
++test_summary.failure;
|
|
break;
|
|
case status::success:
|
|
++test_summary.success;
|
|
break;
|
|
case status::warning:
|
|
break;
|
|
case status::fatal:
|
|
goto end_walk;
|
|
}
|
|
}
|
|
printf("Successful: %lu, Failed: %lu, Total: %lu.\n",
|
|
test_summary.success, test_summary.failure, test_summary.total);
|
|
end_walk:
|
|
closedir(directory_stream);
|
|
}
|
|
|
|
int main()
|
|
{
|
|
auto dev_console = open("/dev/console", O_WRONLY);
|
|
if (dev_console != -1)
|
|
{
|
|
dup2(dev_console, STDOUT_FILENO);
|
|
walk();
|
|
close(dev_console);
|
|
}
|
|
sync();
|
|
reboot(RB_POWER_OFF);
|
|
|
|
return 1;
|
|
}
|