#include #include #include #include #include #include #include #include #include #include #include template 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 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(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(pipe_ends[0], command_buffer); close(pipe_ends[0]); int wait_status{ 0 }; wait(&wait_status); make_path(filename, "./expectations/", file_entry_name, ".txt"); std::unique_ptr 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(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; }