Skip to content
Merged
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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@
},
"scripts": {
"vscode:prepublish": "npm run build",
"build": "npm run compile && cp src/webview.html out/ && cp -r assets out/",
"build": "npm run compile && cp src/webview.html out/ && cp src/webview.css out/ && cp src/webview.js out/ && cp -r assets out/",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile && npm run lint",
"lint": "eslint src",
"test:unit": "jest",
"test:integration": "vscode-test",
"test": "npm run test:unit && npm run test:integration"
"test": "npm run test:unit"
},
"devDependencies": {
"@types/diff": "^7.0.2",
Expand Down
26 changes: 26 additions & 0 deletions src/MyPanelProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export class MyPanelProvider

// Implementation of ITestReporter
public reportProgress(message: any): void {
if(message.command === 'error'){
this.reportError(message.message);
}
this._view?.webview.postMessage(message);
}

Expand Down Expand Up @@ -98,6 +101,15 @@ export class MyPanelProvider
case "run":
this._uiService.runStressTest(message.numTests);
return;
case "get-runs":
this._uiService.getRunsForActiveSolution();
return;
case "get-test-cases":
this._uiService.getTestCasesForRun(message.runId);
return;
case "rerun-tests":
this._uiService.reRunTests(message.testCases);
return;
}
});

Expand Down Expand Up @@ -140,6 +152,12 @@ export class MyPanelProvider
"diff2html.min.js"
)
);
const webviewCssUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, "out", "webview.css")
);
const webviewJsUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, "out", "webview.js")
);
const htmlPath = vscode.Uri.joinPath(
this._extensionUri,
"out",
Expand All @@ -152,6 +170,14 @@ export class MyPanelProvider
"%DIFF2HTML_CSS%",
diff2htmlCssUri.toString()
);
htmlContent = htmlContent.replace(
"%WEBVIEW_CSS%",
webviewCssUri.toString()
);
htmlContent = htmlContent.replace(
"%WEBVIEW_JS%",
webviewJsUri.toString()
);
htmlContent = htmlContent.replace(
"%DIFF2HTML_JS%",
diff2htmlJsUri.toString()
Expand Down
16 changes: 10 additions & 6 deletions src/core/CompileAndRun/TestRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,31 @@ export class TestRunner implements ITestRunner {
return { status: 'Error', message: `Generator error: ${genError}` };
}

const { stdout: userOutput, stderr: solError, duration, memory, status: solStatus } = await this._executor.runWithLimits(solutionExec, testCase);
return this.runWithInput(tempDir, solutionExec, checkerExec, testCase);
}

public async runWithInput(tempDir: string, solutionExec: string, checkerExec: string, input: string): Promise<ITestRunResult> {
const { stdout: userOutput, stderr: solError, duration, memory, status: solStatus } = await this._executor.runWithLimits(solutionExec, input);

if (solStatus !== 'OK') {
return { status: solStatus, input: testCase, duration, memory };
return { status: solStatus, input: input, duration, memory };
}

if (solError) {
return { status: 'RUNTIME_ERROR', message: `Solution runtime error: ${solError}`, input: testCase, duration, memory };
return { status: 'RUNTIME_ERROR', message: `Solution runtime error: ${solError}`, input: input, duration, memory };
}

const inputFile = path.join(tempDir, 'input.txt');
const outputFile = path.join(tempDir, 'output.txt');
this._fileManager.writeFile(inputFile, testCase);
this._fileManager.writeFile(inputFile, input);
this._fileManager.writeFile(outputFile, userOutput);

const checkerResult = await this._executor.runRaw(checkerExec, [inputFile, outputFile]);

if (checkerResult.code === 0) { // OK
return { status: 'OK', duration, memory, input: testCase, output: userOutput };
return { status: 'OK', duration, memory, input: input, output: userOutput };
} else if (checkerResult.code === 1) { // WA
return { status: 'WA', input: testCase, output: userOutput, duration, memory, reason: checkerResult.stderr };
return { status: 'WA', input: input, output: userOutput, duration, memory, reason: checkerResult.stderr };
} else { // Checker error
return { status: 'Error', message: `Checker error: ${checkerResult.stderr}` };
}
Expand Down
216 changes: 197 additions & 19 deletions src/core/Interfaces/classes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as vscode from 'vscode';
import { IExecutablePaths, IJsonTestResult, IRawExecutionResult, ITestPaths, ITestRunResult } from './datastructures';
import { IExecutablePaths, IJsonTestResult, IRawExecutionResult, ITestPaths, ITestRunResult, ITempDirPath, ISolutionPath, IResultDirPath, IRunDirPath, ISolutionName, IRunId, IMainJson, IMainJsonPath, ITestCaseJsonPath } from './datastructures';

/**
* @deprecated This interface is obsolete and will be removed. Use IOrchestrationService instead.
Expand Down Expand Up @@ -76,6 +76,16 @@ export interface IFileManager {
* @returns True if the path exists, false otherwise.
*/
exists(path: string): boolean;
/**
* Deletes a file.
* @param path The path of the file to delete.
*/
deleteFile(path: string): void;
/**
* Deletes a directory recursively.
* @param path The path of the directory to delete.
*/
deleteDirectory(path: string): void;
/**
* Deletes a list of files or directories.
* @param files The paths to clean up.
Expand Down Expand Up @@ -150,6 +160,16 @@ export interface ICompilationManager {
* @returns A promise that resolves with the paths to the executables, or null if compilation fails.
*/
compile(tempDir: string, solutionPath: string, generatorValidatorPath: string, checkerPath: string): Promise<IExecutablePaths | null>;

/**
* Compiles the solution and checker files for re-running a test with existing input.
* This is used when the generator is not needed.
* @param tempDir The temporary directory for compilation.
* @param solutionPath The file path to the C++ solution.
* @param checkerPath The file path to the checker.
* @returns A promise that resolves with the paths to the executables, or null if compilation fails.
*/
compileForReRun(tempDir: string, solutionPath: string, checkerPath: string): Promise<IExecutablePaths | null>;
}

/**
Expand All @@ -164,59 +184,217 @@ export interface ITestRunner {
* @returns A promise that resolves with the test run result.
*/
run(tempDir: string, solutionExec: string, generatorExec: string, checkerExec: string): Promise<ITestRunResult>;

/**
* Runs a single test case with a given input.
* @param tempDir The temporary directory for the test run.
* @param solutionExec The path to the solution executable.
* @param checkerExec The path to the checker executable.
* @param input The input for the test case.
* @returns A promise that resolves with the test run result.
*/
runWithInput(tempDir: string, solutionExec: string, checkerExec: string, input: string): Promise<ITestRunResult>;
}

/**
* Defines the contract for managing the .cpst folder structure.
*/
export interface ICPSTFolderManager {
/**
* Sets up the necessary directories for a test run.
* @param solutionPath The path to the solution file.
* @returns An object containing the paths to the created directories.
* Extracts the solution name from its path.
* @param solutionPath The full path to the solution file.
* @returns The base name of the solution file.
*/
getSolutionName(solutionPath: ISolutionPath): ISolutionName;

/**
* Extracts the run ID from its path.
* @param runFolderPath The full path to the run folder.
* @returns The base name of the run folder (timestamp).
*/
getRunId(runFolderPath: IRunDirPath): IRunId;

/**
* Gets the path to the temporary directory.
* @returns The absolute path to the temporary directory.
*/
getTempDirPath(): ITempDirPath;

/**
* Gets the path to the results directory.
* @returns The absolute path to the results directory.
*/
getResultDirPath(): IResultDirPath;

/**
* Gets the path to the main JSON file that tracks all solutions and runs.
* @returns The absolute path to main.json.
*/
getMainJsonPath(): IMainJsonPath;

/**
* Gets the path to a specific run's result directory.
* @param solutionName The name of the solution.
* @param runId The ID of the run.
* @returns The absolute path to the run's result directory.
*/
getRunResultDirPath(runId: IRunId): IRunDirPath;

/**
* Gets the path for a specific test case's JSON result file.
* @param solutionName The name of the solution.
* @param runId The ID of the run.
* @param testCaseNo The test case number.
* @returns The absolute path to the test case's result file.
*/
getTestCaseResultPath(runId: IRunId, testCaseNo: number): ITestCaseJsonPath;

/**
* Extracts the test case number from its result file path.
* @param testCasePath The path to the test case's result file.
* @returns The test case number.
*/
getTestCaseNo(testCasePath: ITestCaseJsonPath): number;

/**
* Reads and parses the JSON data for a specific test case result.
* @param testCasePath The path to the test case's result file.
* @returns The parsed test case result object.
*/
getTestCaseResultData(testCasePath: ITestCaseJsonPath): IJsonTestResult;

/**
* Constructs all the necessary paths for a given test run.
* @param solutionPath The path of the solution file.
* @param runId The ID of the run.
* @returns An object containing all relevant paths for the test run.
*/
getTestPaths(solutionPath: ISolutionPath, runId: IRunId): ITestPaths;

/**
* Generates a unique nonce string based on the current timestamp.
* @returns A unique string identifier.
*/
generateNonce(): string;

/**
* Creates the temporary directory if it doesn't exist.
* @returns The path to the temporary directory.
*/
createTempDir(): void;

/**
* Creates the results directory if it doesn't exist.
* @returns The path to the results directory.
*/
createResultDir(): void;

/**
* Creates a directory for a specific test run.
* @param solutionPath The path of the solution file.
* @param runId The ID of the run.
* @returns The path to the created run directory.
*/
createRunFolder(runId: IRunId): void;

/**
* Adds a solution to the main JSON file.
* @param solutionName The name of the solution to add.
*/
addSolutionToMainJson(solutionName: ISolutionName): void;

/**
* Adds a run to a solution in the main JSON file.
* @param solutionName The name of the solution.
* @param runId The ID of the run to add.
*/
addRunToMainJson(solutionName: ISolutionName, runId: IRunId): void;

/**
* Adds a new solution, creating its folder and updating the main JSON file.
* @param solutionPath The path of the solution file.
*/
addSolution(solutionPath : ISolutionPath): void;

/**
* Adds a new run for a solution, creating its folder and updating the main JSON file.
* @param solutionName The name of the solution.
* @param runId The ID of the run.
*/
addRun(solutionName: ISolutionName, runId: IRunId): void;

/**
* Reads and parses the main JSON file.
* @returns The parsed main JSON object.
*/
setup(solutionPath: string): ITestPaths;
readMainJson(): IMainJson;

/**
* Initializes the main JSON file for a new test run.
* @param solutionName The name of the solution file.
* @param runFolderName The name of the folder for the current run.
* @param mainJsonPath The path to the main JSON file.
* Initializes a new test run by adding it to the tracking system.
* @param solutionName The name of the solution.
* @param runId The ID of the run.
*/
initializeTestRun(solutionName: string, runFolderName: string, mainJsonPath: string): void;
initializeTestRun(solutionName: ISolutionName, runId: IRunId): void;

/**
* Saves a test result to a JSON file.
* @param runFolderPath The path to the folder for the current run.
* @param result The result to save.
*/
saveResult(runFolderPath: string, result: IJsonTestResult): void;
saveResult(runFolderPath: IRunDirPath, result: IJsonTestResult): void;

/**
* Gets a list of all solutions that have been tested.
* @returns An array of solution names.
*/
getSolutions(): string[];
getAllSolutions(): ISolutionName[];

/**
* Gets a list of all test runs for a given solution.
* @param solutionName The name of the solution.
* @returns An array of run folder names (timestamps).
*/
getRuns(solutionName: string): string[];
getAllRuns(solutionName: ISolutionName): IRunId[];

/**
* Retrieves all test results for a specific test run.
* @param solutionName The name of the solution.
* @param runId The ID of the test run (timestamp).
* @returns An array of test results.
*/
getTestResults(solutionName: string, runId: string): IJsonTestResult[];
getAllTestResults(runId: IRunId): IJsonTestResult[];

/**
* Deletes a solution and all its associated runs.
* @param solutionName The name of the solution to delete.
*/
deleteSolution(solutionName: ISolutionName): void;

/**
* Deletes a specific test run.
* @param runId The ID of the run to delete.
*/
deleteRun(runId: IRunId): void;

/**
* Deletes a specific test case result.
* @param runId The ID of the run containing the test case.
* @param testCaseNo The number of the test case to delete.
*/
deleteTestResult(runId: IRunId, testCaseNo: number): void;

/**
* Updates an existing test result JSON file.
* @param runId The ID of the run containing the test case.
* @param newJsonResult The new result object to save.
*/
updateTestResult(runId: IRunId, newJsonResult: IJsonTestResult): void;

/**
* Cleans up temporary files and directories.
* @param paths An array of paths to delete.
*/
cleanup(paths: string[]): void;
/**
* Gets the path to the temporary directory.
* @returns The absolute path to the temporary directory.
*/
getTempDir(): string;
}

/**
Expand Down
Loading