Skip to content

Commit

Permalink
Companion config (#96)
Browse files Browse the repository at this point in the history
* Fix #65 Save all config from companion.

Unblock #89

* Add better layout distribution for debugging.

This is temporary before #40
  • Loading branch information
mfornet authored May 31, 2020
1 parent 27e4ccb commit 8d57da4
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 26 deletions.
38 changes: 34 additions & 4 deletions src/companion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,32 @@ import * as express from "express";
import bodyParser = require("body-parser");
import { debug } from "./utils";

export class CompanionConfig {
name: string;
group: string;
url: string;
memoryLimit: number;
timeLimit: number;

constructor(data: any) {
this.name = data.name;
this.group = data.group;
this.url = data.url;
this.memoryLimit = data.memoryLimit;
this.timeLimit = data.timeLimit;
}
}

export class TestCase {
input: string;
output: string;

constructor(data: any) {
this.input = data.input;
this.output = data.output;
}
}

export function startCompetitiveCompanionService() {
let port = 0;
let app = express();
Expand All @@ -22,12 +48,17 @@ export function startCompetitiveCompanionService() {

app.post("/", async (req: any, res: any) => {
const data = req.body;
let companionConfig = new CompanionConfig(data);

let tests: TestCase[] = data.tests.map((value: any) => {
return new TestCase(value);
});

res.sendStatus(200);
let problmeInContest = newProblemFromCompanion(data);
let problemInContest = newProblemFromCompanion(companionConfig, tests);

let contestPath = problmeInContest.contestPath;
let mainSolution = problmeInContest.problemConfig.mainSolution.unwrapOr(
let contestPath = problemInContest.contestPath;
let mainSolution = problemInContest.problemConfig.mainSolution.unwrapOr(
""
);

Expand All @@ -47,7 +78,6 @@ export function startCompetitiveCompanionService() {
process.exit(1);
}

// TODO(#21): Move to logs of the extension for debugging purposes.
debug("companion", `Started companion. Listening on port ${port}`);
});
}
41 changes: 26 additions & 15 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
} from "./utils";
import { preRun, run, runWithArgs } from "./runner";
import { copySync } from "fs-extra";
import { CompanionConfig, TestCase } from "./companion";

/**
* Path to static folder.
Expand Down Expand Up @@ -182,11 +183,17 @@ export function initAcmX(testPath?: string) {
// Copy default languages config
let languagesFolder = globalLanguagePath();
let languageStaticFolder = join(pathToStatic(), LANGUAGES);
// TODO: Check for each languages, and copy if don't exist.
// Rationale: If we add a new language by default, users that already have this
// file created, won't have the new languages by default.
if (!existsSync(languagesFolder)) {
copySync(languageStaticFolder, languagesFolder);
} else {
readdirSync(languageStaticFolder).forEach((languageName) => {
if (!existsSync(join(languagesFolder, languageName))) {
copySync(
join(languageStaticFolder, languageName),
join(languagesFolder, languageName)
);
}
});
}

// Create checker folder
Expand Down Expand Up @@ -472,10 +479,11 @@ export function getSolutionPath() {
* Create new problem with configuration from competitive companion.
*
* @param config Json file with all data received from competitive companion.
*
* TODO: Change type of config(any) to a class with explicit arguments.
*/
export function newProblemFromCompanion(config: any) {
export function newProblemFromCompanion(
config: CompanionConfig,
tests: TestCase[]
) {
let path = getSolutionPath();

let contestPath = join(path!, config.group);
Expand All @@ -485,7 +493,7 @@ export function newProblemFromCompanion(config: any) {
let inputs: string[] = [];
let outputs: string[] = [];

config.tests.forEach(function (testCase: any) {
tests.forEach(function (testCase: TestCase) {
inputs.push(testCase.input);
outputs.push(testCase.output);
});
Expand All @@ -498,6 +506,9 @@ export function newProblemFromCompanion(config: any) {
false
);

problemConfig.setCompanionConfig(config);
problemConfig.dump(problemPath);

return new ProblemInContest(problemConfig, contestPath);
}

Expand Down Expand Up @@ -621,16 +632,14 @@ export function testSolution(path: string): Option<SolutionResult> {
let results: TestCaseResult[] = [];
let fail = Option.none<SolutionResult>();

// Try to find time limit from local config first, otherwise use global time limit.
// TODO: Add to wiki about this feature, and how to change custom time limit.
let timeout = config.timeLimit().unwrapOr(getTimeout());

testcasesId.forEach((tcId) => {
// Run on each test case and break on first failing case.
if (fail.isNone()) {
let tcResult = timedRun(
path,
tcId,
getTimeout(),
mainSolution,
checker
);
let tcResult = timedRun(path, tcId, timeout, mainSolution, checker);

if (!tcResult.isOk()) {
fail = Option.some(new SolutionResult(tcResult.status, tcId));
Expand Down Expand Up @@ -866,6 +875,8 @@ export function stressSolution(

let results = [];

let timeout = config.timeLimit().unwrapOr(getTimeout());

for (let index = 0; index < times; index++) {
// Generate input test case
generateTestCase(path, generator);
Expand Down Expand Up @@ -900,7 +911,7 @@ export function stressSolution(
let result = timedRun(
path,
GENERATED_TEST_CASE,
getTimeout(),
timeout,
mainSolution,
checker
);
Expand Down
16 changes: 10 additions & 6 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,17 @@ async function debugTestCase(path: string, tcId: string) {
orientation: 0,
groups: [
{ groups: [{}], size: 0.5 },
{ groups: [{}, [{}, {}]], size: 0.5 },
{
groups: [{ groups: [{}, {}] }, {}],
size: 0.5,
},
],
});

let sol = mainSolution(path);
let inp = join(path, TESTCASES, `${tcId}.in`);
let out = join(path, TESTCASES, `${tcId}.ans`);
let cur = join(path, TESTCASES, `${tcId}.out`);
let ans = join(path, TESTCASES, `${tcId}.ans`);
let out = join(path, TESTCASES, `${tcId}.out`);

await vscode.commands.executeCommand(
"vscode.open",
Expand All @@ -147,10 +151,10 @@ async function debugTestCase(path: string, tcId: string) {
vscode.ViewColumn.Two
);
// This file might not exist!
if (existsSync(cur)) {
if (existsSync(out)) {
await vscode.commands.executeCommand(
"vscode.open",
vscode.Uri.file(cur),
vscode.Uri.file(ans),
vscode.ViewColumn.Three
);
await vscode.commands.executeCommand(
Expand All @@ -161,7 +165,7 @@ async function debugTestCase(path: string, tcId: string) {
} else {
await vscode.commands.executeCommand(
"vscode.open",
vscode.Uri.file(out),
vscode.Uri.file(ans),
vscode.ViewColumn.Four
);
}
Expand Down
23 changes: 22 additions & 1 deletion src/primitives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { SpawnSyncReturns } from "child_process";
import { join } from "path";
import { writeToFileSync } from "./utils";
import { readFileSync, existsSync } from "fs";
import { CompanionConfig } from "./companion";

export const ATTIC = "attic";
export const TESTCASES = "testcases";
export const LANGUAGES = "languages";
// TODO: Allow checker to compile without time limit (this should be added after cancel is available).
// TODO(#68): Allow checker to compile without time limit (this should be added after cancel is available).
export const FRIEND_TIMEOUT = 50_000;
export const MAIN_SOLUTION_BINARY = "sol";
export const CHECKER_BINARY = "checker";
Expand Down Expand Up @@ -230,6 +231,14 @@ export class Option<T> {
}
}

map<R>(predicate: (arg: T) => R): Option<R> {
if (this.isSome()) {
return Option.some(predicate(this.unwrap()));
} else {
return Option.none();
}
}

mapOr<R>(value: R, predicate: (arg: T) => R): R {
if (this.isSome()) {
return predicate(this.unwrap());
Expand Down Expand Up @@ -263,6 +272,7 @@ export class ConfigFile {
bruteSolution: Option<string>;
generator: Option<string>;
checker: Option<string>;
companionConfig: Option<CompanionConfig>;

constructor(
mainSolution?: string,
Expand All @@ -274,6 +284,11 @@ export class ConfigFile {
this.bruteSolution = new Option(bruteSolution);
this.generator = new Option(generator);
this.checker = new Option(checker);
this.companionConfig = Option.none();
}

setCompanionConfig(companionConfig: CompanionConfig) {
this.companionConfig = Option.some(companionConfig);
}

/**
Expand Down Expand Up @@ -349,6 +364,8 @@ export class ConfigFile {
parsed.checker?.value
);

config.setCompanionConfig(parsed.companionConfig?.value);

if (config.verify()) {
config.dump(path);
}
Expand All @@ -362,6 +379,10 @@ export class ConfigFile {
static empty(): ConfigFile {
return new ConfigFile();
}

timeLimit(): Option<number> {
return this.companionConfig.map((config) => config.timeLimit);
}
}

export class ProblemInContest {
Expand Down

0 comments on commit 8d57da4

Please sign in to comment.