Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
7 changes: 7 additions & 0 deletions assets/checker_template.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ bool checker(const string& input, const string& user_output_str) {
/*
TODO: This function must determine if the user's answer is a correct solution to
the problem, especially for problems with multiple correct answers.

If the answer is wrong, you can write a reason to stderr.
For example:
if (user_answer_value != expected_answer_value) {
cerr << "Wrong answer: expected " << expected_answer_value << ", but got " << user_answer_value << endl;
return false;
}
*/

return false;
Expand Down
4 changes: 2 additions & 2 deletions src/MyPanelProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class MyPanelProvider
await this._uiService.generateTestFiles();
return;
case "run":
this._uiService.runStressTest();
this._uiService.runStressTest(message.numTests);
return;
}
});
Expand Down Expand Up @@ -142,7 +142,7 @@ export class MyPanelProvider
);
const htmlPath = vscode.Uri.joinPath(
this._extensionUri,
"out",
"src",
"webview.html"
Comment thread
2077DevWave marked this conversation as resolved.
Outdated
);
let htmlContent = fs.readFileSync(htmlPath.fsPath, "utf8");
Expand Down
14 changes: 14 additions & 0 deletions src/core/CompileAndRun/Executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,20 @@ export class Executor implements IExecutor {
});
}


public runRaw(command: string, args: string[]): Promise<IRawExecutionResult> {
const start = process.hrtime();
const fullCommand = `${command} ${args.join(' ')}`;
return new Promise((resolve) => {
exec(fullCommand, (error, stdout, stderr) => {
const duration = this.calculateDuration(start);
const code = error ? (error as any).code : 0;
Comment thread
2077DevWave marked this conversation as resolved.
Outdated
const signal = error ? (error as any).signal : null;
resolve({ stdout, stderr, duration, code, signal, error: error || undefined });
});
});
}
Comment thread
2077DevWave marked this conversation as resolved.

private determineStatus(result: IRawExecutionResult): string {
if (result.signal === 'SIGTERM') {
return 'TLE';
Expand Down
25 changes: 12 additions & 13 deletions src/core/CompileAndRun/TestRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import * as path from 'path';
import { IExecutor, IFileManager, ITestRunner } from '../Interfaces/classes';
import { ITestRunResult } from '../Interfaces/datastructures';

export class TestRunner implements ITestRunner{
export class TestRunner implements ITestRunner {
constructor(
private readonly _executor: IExecutor,
private readonly _fileManager: IFileManager
) {}
) { }

public async run(tempDir: string, solutionExec: string, generatorExec: string, checkerExec: string): Promise<ITestRunResult> {
const { stdout: testCase, stderr: genError } = await this._executor.runWithLimits(generatorExec, '');
Expand All @@ -15,29 +15,28 @@ export class TestRunner implements ITestRunner{
}

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

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

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

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

try {
await this._executor.runWithLimits(`${checkerExec} ${inputFile} ${outputFile}`, '');
const checkerResult = await this._executor.runRaw(checkerExec, [inputFile, outputFile]);

if (checkerResult.code === 0) { // OK
return { status: 'OK', duration, memory, input: testCase, output: userOutput };
} catch (error: any) {
if (error.code === 1) { // WA
return { status: 'WA', input: testCase, output: userOutput, duration, memory };
} else { // Checker error
return { status: 'Error', message: `Checker error: ${error.stderr}` };
}
} else if (checkerResult.code === 1) { // WA
return { status: 'WA', input: testCase, output: userOutput, duration, memory, reason: checkerResult.stderr };
} else { // Checker error
return { status: 'Error', message: `Checker error: ${checkerResult.stderr}` };
}
Comment thread
2077DevWave marked this conversation as resolved.
}
}
10 changes: 9 additions & 1 deletion 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, ITestPaths, ITestRunResult } from './datastructures';
import { IExecutablePaths, IJsonTestResult, IRawExecutionResult, ITestPaths, ITestRunResult } from './datastructures';

/**
* @deprecated This interface is obsolete and will be removed. Use IOrchestrationService instead.
Expand Down Expand Up @@ -39,6 +39,14 @@ export interface IExecutor {
* @returns A promise that resolves with the execution result.
*/
runWithLimits(command: string, input: string): Promise<{ stdout: string, stderr: string, duration: number, memory: number, status: string }>;

/**
* Runs a command without input and returns the raw execution result.
* @param command The command to execute.
* @param args The arguments to pass to the command.
* @returns A promise that resolves with the raw execution result.
*/
runRaw(command: string, args: string[]): Promise<IRawExecutionResult>;
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/core/Interfaces/datastructures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export interface ITestRunResult {
memory?: number;
/** Any message associated with the test run, e.g., an error message. */
message?: string;
/** The reason for the result, provided by the checker. */
reason?: string;
}

/**
Expand Down Expand Up @@ -134,4 +136,6 @@ export interface IJsonTestResult {
memoryUsed?: number;
/** Any message associated with the result, e.g., an error message. */
message?: string;
/** The reason for the result, provided by the checker. */
reason?: string;
}
5 changes: 3 additions & 2 deletions src/core/Interfaces/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ export interface IOrchestrationService {
run(
solutionPath: string,
generatorValidatorPath: string,
checkerPath: string
checkerPath: string,
numTests: number
): Promise<void>;
}

Expand All @@ -100,7 +101,7 @@ export interface IUIService {
/**
* Handles the command to run the stress test.
*/
runStressTest(): Promise<void>;
runStressTest(numTests: number): Promise<void>;

/**
* Notifies the UI about the currently active solution file.
Expand Down
15 changes: 8 additions & 7 deletions src/core/Services/OrchestrationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class OrchestrationService implements IOrchestrationService {
private readonly _reporter: ITestReporter
) {}

public async run(solutionPath: string, generatorValidatorPath: string, checkerPath: string): Promise<void> {
public async run(solutionPath: string, generatorValidatorPath: string, checkerPath: string, numTests: number): Promise<void> {
const paths : ITestPaths = this._resultService.initialize(solutionPath);
const executables = await this._compilationService.compile(solutionPath, generatorValidatorPath, checkerPath);

Expand All @@ -22,7 +22,6 @@ export class OrchestrationService implements IOrchestrationService {
return;
}

const numTests = 100; // TODO: Make this configurable
for (let i = 1; i <= numTests; i++) {
this._reporter.reportProgress({ command: 'testResult', status: 'Running', testCase: i });

Expand All @@ -35,7 +34,8 @@ export class OrchestrationService implements IOrchestrationService {
userOutput: result.output,
execTime: result.duration,
memoryUsed: result.memory,
message: result.message
message: result.message,
reason: result.reason
};

this._resultService.saveResult(resultToSave, paths);
Expand All @@ -48,13 +48,14 @@ export class OrchestrationService implements IOrchestrationService {
output: result.output,
time: result.duration,
memory: result.memory,
message: result.message
message: result.message,
reason: result.reason
};
this._reporter.reportProgress(progress);

if (result.status !== 'OK') {
break;
}
// if (result.status !== 'OK') {
// break;
// }
Comment thread
2077DevWave marked this conversation as resolved.
Outdated
}

this._cpstFolderManager.cleanup([this._cpstFolderManager.getTempDir()]);
Expand Down
4 changes: 2 additions & 2 deletions src/core/Services/UIService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class UIService implements IUIService {
}
}

public async runStressTest(): Promise<void> {
public async runStressTest(numTests: number): Promise<void> {
if (!this._currentSolutionFile) {
this._reporter.reportError("Cannot run test: Could not determine the solution file.");
return;
Expand All @@ -80,6 +80,6 @@ export class UIService implements IUIService {
return;
}

await this._orchestrationService.run(solutionPath, genValPath, checkerPath);
await this._orchestrationService.run(solutionPath, genValPath, checkerPath, numTests);
}
}
Loading