Skip to content

Added a test generator #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added TestGenerator/TestGenerator
Binary file not shown.
Binary file added TestGenerator/TestGenerator.exe
Binary file not shown.
75 changes: 75 additions & 0 deletions TestGenerator/caseHandler.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "constants.h"
#include "error.h"
#include "caseHandler.h"

int caseHandler_GetNextCase(char* programNumber) {
int numbers[3];
if(sscanf(programNumber, "%d-%d-%d", &numbers[0], &numbers[1], &numbers[2]) != 3)
error("Invalid program number: %s\n", programNumber);

// Get the name of the directory
char dirName[MAX_PROGRAM_PATH_SIZE];
snprintf(dirName, MAX_PROGRAM_PATH_SIZE, "%s/tests/%02d-%02d-%02d/",
BASE_PATH, numbers[0], numbers[1], numbers[2]);

// Find what the first unwritten test number is
int number = 1;
char testName[MAX_PROGRAM_PATH_SIZE+10];
while(1) {
snprintf(testName, MAX_PROGRAM_PATH_SIZE+10, "%s1-%02d.in", dirName, number);
FILE* file = fopen(testName, "r");
if(file) {
fclose(file);
number++;
continue;
}
break;
}

return number;
}

void caseHandler_PopulateNextCase(char* programNumber, int caseNumber, char** programIO) {
int numbers[3];
if(sscanf(programNumber, "%d-%d-%d", &numbers[0], &numbers[1], &numbers[2]) != 3)
error("Invalid program number: %s\n", programNumber);

// Open up the input and output files
char inFileName[MAX_PROGRAM_PATH_SIZE], outFileName[MAX_PROGRAM_PATH_SIZE];
snprintf(inFileName, MAX_PROGRAM_PATH_SIZE, "%s/tests/%02d-%02d-%02d/1-%02d.in",
BASE_PATH, numbers[0], numbers[1], numbers[2], caseNumber);
snprintf(outFileName, MAX_PROGRAM_PATH_SIZE, "%s/tests/%02d-%02d-%02d/1-%02d.out",
BASE_PATH, numbers[0], numbers[1], numbers[2], caseNumber);
FILE* inFile = fopen(inFileName, "w");
if(!inFile) error("Failed to write to .in file\n");
FILE* outFile = fopen(outFileName, "w");
if(!outFile) error("Failed to write to .out file\n");

// Turn all "\r\n" occurrences in the output into just '\n'
char* newOutput = calloc(strlen(programIO[1])+1, 1);
if(!newOutput) error("Failed to allocate new output buffer\n");
int i = 0, index = 0;
while(programIO[1][i]) {
if(programIO[1][i] == '\r' && programIO[1][i+1] == '\n') {
newOutput[index++] = '\n';
i += 2;
} else newOutput[index++] = programIO[1][i++];
}
free(programIO[1]);
programIO[1] = newOutput;

// Strip the '\n' at the end of the input and output
programIO[0][strlen(programIO[0])-1] = 0;
programIO[1][strlen(programIO[1])-1] = 0;

// Write the input and output from programIO to the files
fwrite(programIO[0], 1, strlen(programIO[0]), inFile);
fwrite(programIO[1], 1, strlen(programIO[1]), outFile);

// Close the files
fclose(inFile);
fclose(outFile);
}
20 changes: 20 additions & 0 deletions TestGenerator/caseHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef CASEHANDLER_H
#define CASEHANDLER_H

/**
* \brief Gets the next case yet to be populated. For example, if 1-01.in and 1-02.in
* existed, this function would return 3 because 1-03.in is the next case to fill
* \param programNumber The program number formatted as "<Unit>-<Chapter>-<Program>"
* \return The next case yet to be filled
*/
int caseHandler_GetNextCase(char* programNumber);

/**
* \brief Populates a case's .in and .out files
* \param programNumber The program number formatted as "<Unit>-<Chapter>-<Program>"
* \param caseNumber The case number to populate
* \param programIO The IO gathered from the generator program
*/
void caseHandler_PopulateNextCase(char* programNumber, int caseNumber, char** programIO);

#endif
38 changes: 38 additions & 0 deletions TestGenerator/constants.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include <windows.h>
#define PATH_SEPARATOR '\\'
#else
#include <unistd.h>
#include <limits.h>
#define PATH_SEPARATOR '/'
#endif
#include "error.h"
#include "constants.h"

char BASE_PATH[MAX_PROGRAM_PATH_SIZE];

void constants_GetBasePath(void) {
char exePath[MAX_PROGRAM_PATH_SIZE];

#ifdef WIN32
DWORD length = GetModuleFileNameA(NULL, exePath, MAX_PROGRAM_PATH_SIZE);
if(!length || length == MAX_PROGRAM_PATH_SIZE)
error("Failed to get the executable's path\n");
#else
int length = readlink("/proc/self/exe", exePath, MAX_PROGRAM_PATH_SIZE-1);
if(length == -1)
error("Readlink failed\n");
exePath[length] = 0;
#endif

// Remove the executable's name and go up one directory
char* lastSep = strrchr(exePath, PATH_SEPARATOR);
if(lastSep) *lastSep = 0;
lastSep = strrchr(exePath, PATH_SEPARATOR);
if(lastSep) *lastSep = 0;

strncpy(BASE_PATH, exePath, MAX_PROGRAM_PATH_SIZE-1);
}
21 changes: 21 additions & 0 deletions TestGenerator/constants.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef CONSTANTS_H
#define CONSTANTS_H

#define MAX_PROGRAM_PATH_SIZE 2048

#define INPUT_BUFFER_SIZE 4096

#define PROGRAMIO_INITIAL_SIZE 4096
#define PROGRAMIO_STEP_SIZE 4096

// In milliseconds
#define OUTPUT_DELAY 100

// Fill this in yourself. Only necessary on Windows
#define C_CODE_PATH ""

// Populated at the beginning of runtime
extern char BASE_PATH[];
void constants_GetBasePath(void);

#endif
21 changes: 21 additions & 0 deletions TestGenerator/error.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "constants.h"
#include "error.h"

_Noreturn void __error(int lineNumber, const char* fileName, const char* format, ...) {
if(!format) exit(0);

#ifdef WIN32
fprintf(stderr, "Error at %s:%d: ", fileName+strlen(C_CODE_PATH), lineNumber);
#else
fprintf(stderr, "Error at %s:%d: ", fileName, lineNumber);
#endif
va_list argv;
va_start(argv, format);
vfprintf(stderr, format, argv);
va_end(argv);

exit(1);
}
14 changes: 14 additions & 0 deletions TestGenerator/error.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef ERROR_H
#define ERROR_H

#include <stdarg.h>

_Noreturn void __error(int lineNumber, const char* fileName, const char* format, ...);

/**
* \brief Prints out an error message and exits the program. error(NULL) to exit normally.
* Takes in arguments just like printf and prints them to stderr before exiting
*/
#define error(___format, ...) __error(__LINE__, __FILE__, ___format, ##__VA_ARGS__)

#endif
31 changes: 31 additions & 0 deletions TestGenerator/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include <stdio.h>
#include <stdlib.h>
#include "programHandler.h"
#include "caseHandler.h"
#include "constants.h"

#include <string.h>
int main(int argc, char** argv) {
if(argc != 2) {
printf("Usage: %s <Program Number>\n", argv[0]);
return 1;
}

// Get the base path
constants_GetBasePath();

// Run the program and get the input/output
char* name = programHandler_GetProgramName(argv[1]);
char** programIO = programHandler_RunPythonProgram(name);
free(name);

// Write the test case
int nextCase = caseHandler_GetNextCase(argv[1]);
caseHandler_PopulateNextCase(argv[1], nextCase, programIO);

free(programIO[0]);
free(programIO[1]);
free(programIO);

printf("Wrote test %d!\n", nextCase);
}
Loading