diff --git a/package.json b/package.json index 265ba81e0..4297eb22f 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "build:ui:dev:https": "node packages/ui-default/build --gulp && node --trace-deprecation packages/ui-default/build --dev --https", "build:ui:production": "cross-env NODE_OPTIONS=--max_old_space_size=8192 node packages/ui-default/build --gulp && node packages/ui-default/build --production", "build:ui:production:webpack": "cross-env NODE_OPTIONS=--max_old_space_size=8192 node packages/ui-default/build --production", - "test": "node test/entry.js", + "test": "node -r @hydrooj/utils/lib/register packages/utils/tests/subtask.spec.ts && node test/entry.js", "benchmark": "cross-env BENCHMARK=true node test/entry.js", "lint": "eslint packages plugins modules --ext .ts,.tsx,.jsx --fix", "lint:ci": "eslint packages plugins modules --ext .ts,.tsx,.jsx --max-warnings=0", @@ -33,12 +33,14 @@ "devDependencies": { "@simplewebauthn/typescript-types": "7.4.0", "@types/autocannon": "^7.12.5", + "@types/chai": "^4", "@types/cross-spawn": "^6.0.6", "@types/node": "^20.10.0", "@types/semver": "^7.5.6", "@types/supertest": "^2.0.16", "autocannon": "^7.14.0", "cac": "^6.7.14", + "chai": "^4.3.10", "chokidar": "^3.5.3", "cross-env": "^7.0.3", "cross-spawn": "^7.0.3", diff --git a/packages/utils/lib/common.ts b/packages/utils/lib/common.ts index ead3c3936..19605b5ad 100644 --- a/packages/utils/lib/common.ts +++ b/packages/utils/lib/common.ts @@ -240,7 +240,7 @@ interface MatchRule { const SubtaskMatcher: MatchRule[] = [ { - regex: /^(([A-Za-z0-9]*?)(?:(\d*)[-_])?(\d+))\.(in|txt)$/, + regex: /^(([A-Za-z0-9._-]*?)(?:(\d*)[-_])?(\d+))\.(in|txt)$/, output: [ (a) => `${a[1]}.out`, (a) => `${a[1]}.ans`, @@ -332,6 +332,7 @@ export function readSubtasksFromFiles(files: string[], config) { memory: config.memory, type, cases: [c], + id: sid, }; } else if (!subtask[sid].cases) subtask[sid].cases = [c]; else subtask[sid].cases.push(c); diff --git a/packages/utils/tests/subtask.spec.ts b/packages/utils/tests/subtask.spec.ts new file mode 100644 index 000000000..3d7298668 --- /dev/null +++ b/packages/utils/tests/subtask.spec.ts @@ -0,0 +1,62 @@ +import { expect } from 'chai'; +import { describe, it } from 'node:test'; +import { readSubtasksFromFiles } from '../lib/common'; + +interface Info { + subtask: number; + id: number; +} + +function shouldFindTestcase(files: string[], info?: Info) { + const subtasks = readSubtasksFromFiles(files, {}); + if (files.includes('a2_1.in')) console.log(subtasks); + expect(subtasks).to.be.lengthOf(1); + if (info?.subtask) expect(subtasks[0].id).to.equal(info.subtask); + const cases = subtasks.map((i) => i.cases).flat(); + expect(cases).to.be.lengthOf(1); + if (info?.id) expect(cases[0].id).to.deep.equal(info.id); +} + +describe('single case', () => { + it('1.in/1.out', () => { + shouldFindTestcase(['1.in', '1.out']); + }); + it('1.in/2.ans', () => { + shouldFindTestcase(['1.in', '1.ans']); + }); + it('file1.in/file1.out', () => { + shouldFindTestcase(['file1.in', 'file1.out']); + }); + it('file.in1/file.ou1', () => { + shouldFindTestcase(['file.in1', 'file.ou1']); + }); + it('input1.txt/output1.txt', () => { + shouldFindTestcase(['input1.txt', 'output1.txt']); + }); + it('data.1.in/data.1.out', () => { + shouldFindTestcase(['data.1.in', 'data.1.out']); + }); +}); +describe('subtask', () => { + it('1_1.in/1_1.out', () => { + shouldFindTestcase(['1_1.in', '1_1.out'], { subtask: 1, id: 1 }); + }); + it('a1_1.in/a1_1.out', () => { + shouldFindTestcase(['a1_1.in', 'a1_1.out'], { subtask: 1, id: 1 }); + }); + it('a01_01.in/a01_01.out', () => { + shouldFindTestcase(['a01_01.in', 'a01_01.out'], { subtask: 1, id: 1 }); + }); + it('1-1.in/1-1.out', () => { + shouldFindTestcase(['1-1.in', '1-1.out'], { subtask: 1, id: 1 }); + }); + it('a1-1.in/a1-1.out', () => { + shouldFindTestcase(['a1-1.in', 'a1-1.out'], { subtask: 1, id: 1 }); + }); + it('subtask_1_1.in/subtask_1_1.out', () => { + shouldFindTestcase(['subtask_1_1.in', 'subtask_1_1.out'], { subtask: 1, id: 1 }); + }); + it('01sample-01.in/01sample-01.out', () => { + shouldFindTestcase(['01sample-01.in', '01sample-01.out'], { subtask: 1, id: 1 }); + }); +});