UNPKG

online-judge-supporter

Version:
192 lines (189 loc) 6.46 kB
import { Logger } from "../logger.js"; import { Compiler } from "../../compiler/compiler.js"; import { TestCase } from "../../test/testcase.js"; import { Exception } from "../../error_handler/error.js"; import { Verdict } from "../../test/verdict.js"; import { createSpinner } from "nanospinner"; import { Creator } from "../../file_creator/creator.js"; import { formatDirPath } from "../../file_creator/utils.js"; import { testcaseStartIndex } from "../../config/load_config.js"; const path = process.cwd(); const startJudging = async (testcase, compiler, { multiTest = false } = {}) => { const spinner = createSpinner(); Logger.logInfoSpinner(`Input file: ${testcase.fileName}`); spinner.start({ text: Logger.info(`Running...`) }); const result = await compiler.runTest(testcase, { multiTest: multiTest }); if (!multiTest) { Logger.logVerdict(result["verdict"], { spinner: spinner }); } else { spinner.stop(); } if (result["verdict"] !== Verdict.AC) { Logger.input(testcase.input); Logger.output(result["output"]); if (!multiTest) { Logger.answer(testcase.output); } } }; const startJudgeWithDebug = async (testcase, compiler) => { Logger.logInfoSpinner(`Input file: ${testcase.fileName}`); Logger.input(testcase.input); await compiler.runTestWithDebug(testcase.input); }; const addTestCaseManually = (fileName) => { Creator.addTestCase(formatDirPath(path), fileName, { onBeginInput: () => { Logger.logInfoSpinner("Enter Input (press Ctrl + C to exit)"); }, onBeginOutput: () => { Logger.logInfoSpinner("Enter Expected output (press Ctrl + C to exit)"); }, onFileCreate: Logger.logFileSpinner, onFolderCreate: Logger.logFolderSpinner, }); }; const viewTestCase = (testcase) => { Logger.logInfoSpinner(`Testcase ${testcase.fileName}`); Logger.input(testcase.input); testcase.output ??= ""; Logger.output(testcase.output); }; const testCommand = (program) => { program .command("test") .description("Test source file with testcases") .alias("t") .argument("<filename>", "Name of the source code (a.rs, b.cpp, c.py, ...)") .option("-t, --test <index>", "test index (starts from 1)") .option("-m, --multi_test <index>", "multiple test index (starts from 1)") .option("-i, --interactive", "enable interactive mode (input manually)") .option("-d, --debug", "build code with debug flag") .option("-a, --add", "add testcase to the current problem") .option("-v, --view", "view testcase") .action(async (fileName, options) => { // const filePath = `${path}/${fileName}`; if (options.debug && options.view) { // this will cause frustration if user want to use debug but not view, but accidentally leave it as it is, so i warn them Logger.logErrorSpinner(`Please don't use debug with view!`); return; } const filePath = `${fileName}`; let compiler; compiler = new Compiler({ filePath: filePath }); if (options.interactive) { try { compiler.buildFile({ debug: options.debug }); } catch (_) { Logger.logVerdict(Verdict.CE); return; } await compiler.runInteractive(); return; } if (options.add) { addTestCaseManually(fileName); return; } const testIndex = options.test; let testEntry = null; if (testIndex) { testEntry = parseInt(testIndex).toString(); } const multiTestIndex = options.multi_test; let multiTestEntry = null; if (multiTestIndex) { multiTestEntry = parseInt(multiTestIndex) - testcaseStartIndex; } let testcases; let testOne; try { testcases = TestCase.getTestCasesFromDirectory(filePath); } catch (e) { Logger.logErrorSpinner(e.message); return; } try { if (Object.keys(testcases).length === 0) { Logger.logErrorSpinner(Exception.noTestAvailable(fileName).message); return; } else if (multiTestEntry === null && testEntry !== null && !testcases[testEntry]) { Logger.logErrorSpinner(Exception.testFileNotFound(fileName, testIndex).message); return; } else if (multiTestEntry !== null && testEntry === null) { Logger.logErrorSpinner("Please use -m/--multi_test <index> together with -t/-test <index>"); return; } if (testEntry !== null) { testOne = testcases[testEntry]; } // Since multitestCaseAt always start with '0' and it also being used for other purposes so i need to do some workaround here if (multiTestEntry !== null && !testOne.multiTestCaseAt(multiTestEntry)) { Logger.logErrorSpinner(Exception.testFileNotFound(fileName, multiTestIndex).message); return; } else if (multiTestEntry !== null && testOne.multiTestCaseAt(multiTestEntry)) { const multiTestCase = testOne.multiTestCaseAt(multiTestEntry); testOne = { input: multiTestCase.input, output: multiTestCase.output, fileName: testOne.fileName, }; } } catch (e) { Logger.logErrorSpinner(e.message); return; } if (options.view) { Logger.logInfoSpinner("Lisiting testcases"); if (testEntry === null) { for (const test of Object.values(testcases)) { viewTestCase(test); } } else { viewTestCase(testOne); } return; } try { compiler.buildFile({ debug: options.debug }); } catch (_) { Logger.logVerdict(Verdict.CE); return; } if (testEntry === null) { for (const test of Object.values(testcases)) { if (!options.interactive && options.debug) { await startJudgeWithDebug(test, compiler); } else { await startJudging(test, compiler); } } } else { if (!options.interactive && options.debug) { await startJudgeWithDebug(testOne, compiler); } else { await startJudging(testOne, compiler, { multiTest: multiTestEntry !== null, }); } } }); }; export { testCommand }; export default { testCommand };