import fs from "node:fs" import path from "node:path" import { performance } from "node:perf_hooks" import type { Solution } from "./Solution.js" import { docker } from "./Docker.js" import { SolutionTestsResult } from "./SolutionTestsResult.js" import { TemporaryFolder } from "./TemporaryFolder.js" export interface InputOutput { input: string output: string } export interface TestRunOptions { testNumber: number path: string solution: Solution } export interface TestOptions { testNumber: number path: string isSuccess: boolean input: string output: string stdout: string } export class Test implements TestOptions { public testNumber: number public path: string public isSuccess: boolean public input: string public output: string public stdout: string constructor(options: TestOptions) { this.testNumber = options.testNumber this.path = options.path this.isSuccess = options.isSuccess this.input = options.input this.output = options.output this.stdout = options.stdout } static async runAll(solution: Solution): Promise { const testsPath = path.join(solution.challenge.path, "test") const testsFolders = await fs.promises.readdir(testsPath) const testsNumbers = testsFolders.map((test) => { return Number(test) }) const testsPromises: Array> = [] const start = performance.now() for (const testNumber of testsNumbers) { const testPath = path.join(testsPath, testNumber.toString()) testsPromises.push(Test.run({ testNumber, path: testPath, solution })) } const tests = await Promise.all(testsPromises) const end = performance.now() const elapsedTimeMilliseconds = end - start return new SolutionTestsResult({ solution, tests, elapsedTimeMilliseconds }) } static async getInputOutput(testPath: string): Promise { const inputPath = path.join(testPath, "input.txt") const outputPath = path.join(testPath, "output.txt") const input = await fs.promises.readFile(inputPath, { encoding: "utf-8" }) const output = await fs.promises.readFile(outputPath, { encoding: "utf-8", }) return { input, output } } static async runManyWithSolutions(solutions: Solution[]): Promise { const solutionTestsResultsPromises: Array> = [] let isSolutionSuccess = true for (const solution of solutions) { const solutionTestsResultPromise = solution.test() solutionTestsResultsPromises.push(solutionTestsResultPromise) solutionTestsResultPromise .then((solutionTestsResult) => { solutionTestsResult.print() if (!solutionTestsResult.isSuccess) { isSolutionSuccess = false } }) .catch(() => { isSolutionSuccess = false }) } await Promise.all(solutionTestsResultsPromises) await TemporaryFolder.cleanAll() if (isSolutionSuccess) { console.log(SolutionTestsResult.SUCCESS_MESSAGE) return 0 } return 1 } static async run(options: TestRunOptions): Promise { const { input, output } = await Test.getInputOutput(options.path) try { const { stdout } = await docker.run( input, options.solution.temporaryFolder.id, ) const test = new Test({ path: options.path, testNumber: options.testNumber, input, output, stdout, isSuccess: stdout === output, }) return test } catch (error: any) { throw new Error( `solution: ${options.solution.path}\n${error.message as string}\n`, ) } } }