#include #include #include #include #include #include #include #define FILENAME_BUFFER_SIZE 256 size_t read_command(int descriptor, char *command_buffer) { ssize_t bytes_read = 0; size_t read_so_far = 0; while ((bytes_read = read(descriptor, command_buffer + read_so_far, FILENAME_BUFFER_SIZE - read_so_far - 1)) > 0) { read_so_far += bytes_read; if (read_so_far >= FILENAME_BUFFER_SIZE - 1) { break; } } command_buffer[read_so_far] = 0; return read_so_far; } enum status { status_success, status_failure, status_warning, status_fatal }; unsigned int make_path(char *destination, const char *directory, const char *filename, const char *extension) { unsigned int i = 0; for (; i < FILENAME_BUFFER_SIZE; i++) { if (directory[i] == 0) { break; } destination[i] = directory[i]; } for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++) { if (filename[j] == 0) { break; } destination[i] = filename[j]; } if (extension == NULL) { goto done; } for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++) { if (extension[j] == 0) { break; } destination[i] = extension[j]; } done: destination[i] = 0; return i; } enum status run_test(const char *file_entry_name) { printf("Running %s. ", file_entry_name); char filename[FILENAME_BUFFER_SIZE]; char command_buffer[FILENAME_BUFFER_SIZE]; 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, NULL); int 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]); _exit(1); } else { close(pipe_ends[1]); // Close the write end. read_command(pipe_ends[0], command_buffer); close(pipe_ends[0]); int wait_status = 0; make_path(filename, "./expectations/", file_entry_name, ".txt"); FILE *expectation_descriptor = fopen(filename, "r"); if (expectation_descriptor == NULL) { return status_warning; } size_t read_from_file = fread(file_buffer, 1, sizeof(file_buffer) - 1, expectation_descriptor); fclose(expectation_descriptor); file_buffer[read_from_file] = 0; for (unsigned int i = 0; ; ++i) { if (command_buffer[i] == 0 && file_buffer[i] == 0) { fwrite("\n", 1, 1, stdout); return status_success; } else if (command_buffer[i] != file_buffer[i]) { printf("Failed. Got:\n%s", command_buffer); return status_failure; } } } } struct summary { size_t total; size_t failure; size_t success; }; void walk() { DIR *directory_stream = opendir("./tests"); struct dirent *file_entry; struct summary test_summary = { .total = 0, .failure = 0, .success = 0 }; while ((file_entry = readdir(directory_stream)) != NULL) { 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() { int 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; }