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
2 changes: 1 addition & 1 deletion GEMINI.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ To build and run this project, you'll need to have Node.js and npm installed.

3. **Run the tests:**
```bash
npm run test
npm run test:unit
```

4. **Lint the code:**
Expand Down
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
2 changes: 1 addition & 1 deletion 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
15 changes: 14 additions & 1 deletion src/core/CompileAndRun/Executor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import { exec, ExecOptions } from 'child_process';
import { exec, execFile, ExecOptions } from 'child_process';
import { IExecutor } from '../Interfaces/classes';
import { IExecutionOptionsConfig, IExecutionResult, IRawExecutionResult } from '../Interfaces/datastructures';

Expand Down Expand Up @@ -61,6 +61,19 @@ export class Executor implements IExecutor {
});
}


public runRaw(command: string, args: string[]): Promise<IRawExecutionResult> {
const start = process.hrtime();
return new Promise((resolve) => {
execFile(command, args, (error, stdout, stderr) => {
const duration = this.calculateDuration(start);
const code = error ? (typeof (error as any).code === 'string' ? null : (error as any).code) : 0;
const signal = error ? (error as any).signal : null;
resolve({ stdout, stderr, duration, code, signal, error: error || undefined });
});
});
}

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
13 changes: 5 additions & 8 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,10 @@ 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;
}
}

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