mirror of
https://github.com/theoludwig/programming-challenges.git
synced 2024-11-09 22:08:58 +01:00
209 lines
5.5 KiB
TypeScript
209 lines
5.5 KiB
TypeScript
import util from 'util'
|
|
import path from 'path'
|
|
import * as fsWithCallbacks from 'fs'
|
|
import childProcess from 'child_process'
|
|
import { performance } from 'perf_hooks'
|
|
import chalk from 'chalk'
|
|
import deleteAllFilesExceptOne from './utils/deleteAllFilesExceptOne'
|
|
import emoji from 'node-emoji'
|
|
import prettyMilliseconds from 'pretty-ms'
|
|
import { table } from 'table'
|
|
|
|
const handleStderror = async (
|
|
stderr: string,
|
|
tempPath: string
|
|
): Promise<void> => {
|
|
console.log(chalk.bgRedBright.black('Error occurred in your solution :'))
|
|
console.log(stderr)
|
|
await deleteAllFilesExceptOne(tempPath, '.gitignore')
|
|
process.exit(0)
|
|
}
|
|
;(async () => {
|
|
const fs = fsWithCallbacks.promises
|
|
const exec = util.promisify(childProcess.exec)
|
|
const args = process.argv.slice(2)
|
|
const [challengeName, solutionName] = args
|
|
|
|
if (challengeName == null || solutionName == null) {
|
|
console.log(`
|
|
Please specify the challenge and solution name:
|
|
${chalk.cyan(`npm run test [challenge-name] [solution-name]`)}
|
|
|
|
For example:
|
|
${chalk.cyan('npm run test hello-world python-hello')}
|
|
`)
|
|
process.exit(0)
|
|
}
|
|
|
|
const challengePath = path.resolve(
|
|
__dirname,
|
|
'..',
|
|
'challenges',
|
|
challengeName
|
|
)
|
|
const solutionFolderPath = path.resolve(
|
|
challengePath,
|
|
'solutions',
|
|
solutionName
|
|
)
|
|
|
|
// Challenge valid ?
|
|
try {
|
|
await fs.access(challengePath)
|
|
} catch {
|
|
console.log(`The challenge was not found: ${chalk.red(challengeName)}`)
|
|
process.exit(0)
|
|
}
|
|
|
|
// Solution valid ?
|
|
try {
|
|
await fs.access(solutionFolderPath)
|
|
} catch {
|
|
console.log(`The solution was not found: ${chalk.red(solutionName)}`)
|
|
process.exit(0)
|
|
}
|
|
|
|
// Determinate the language to execute
|
|
const solutionFilesName = await fs.readdir(solutionFolderPath)
|
|
let solutionFilePath = null
|
|
for (const solutionFileName of solutionFilesName) {
|
|
const fileName = solutionFileName
|
|
.split('.')
|
|
.slice(0, -1)
|
|
.join('.')
|
|
if (fileName === 'solution') {
|
|
solutionFilePath = solutionFileName
|
|
break
|
|
}
|
|
}
|
|
if (solutionFilePath == null) {
|
|
console.log(`The ${chalk.red('solution')} file was not found.`)
|
|
process.exit(0)
|
|
}
|
|
const languages: {
|
|
name: string
|
|
extension: string
|
|
launch: string
|
|
}[] = require('./languages-wrapper/_languages.json')
|
|
const extensionSolution = path.extname(solutionFilePath)
|
|
const languageToExecute = languages.find(
|
|
language => language.extension === extensionSolution
|
|
)
|
|
if (languageToExecute == null) {
|
|
console.log(`Sadly, this ${chalk.red('language')} is not supported yet.`)
|
|
process.exit(0)
|
|
}
|
|
|
|
// Copy 'solution' and 'execute' files in temp
|
|
const inputOutputJSON: { input: any[]; output: any }[] = require(path.join(
|
|
__dirname,
|
|
'..',
|
|
'challenges',
|
|
challengeName,
|
|
'input-output.json'
|
|
))
|
|
const tempPath = path.join(__dirname, '..', 'temp')
|
|
const executeFile = `execute${languageToExecute.extension}`
|
|
const executeLanguagePath = path.resolve(
|
|
__dirname,
|
|
'languages-wrapper',
|
|
executeFile
|
|
)
|
|
const executeLanguageTempPath = path.join(tempPath, executeFile)
|
|
const inputPath = path.join(tempPath, 'input.json')
|
|
const outputPath = path.join(tempPath, 'output.json')
|
|
await fs.copyFile(
|
|
path.resolve(solutionFolderPath, solutionFilePath),
|
|
path.join(tempPath, solutionFilePath)
|
|
)
|
|
await fs.copyFile(executeLanguagePath, executeLanguageTempPath)
|
|
|
|
// Console.log & Tests
|
|
const totalCorrect = {
|
|
total: 0,
|
|
correct: 0
|
|
}
|
|
const tableResult = [
|
|
[
|
|
chalk.cyan('Result'),
|
|
chalk.cyan('Input'),
|
|
chalk.cyan('Output'),
|
|
chalk.cyan('Expected output')
|
|
]
|
|
]
|
|
const startTest = performance.now()
|
|
|
|
// Loop I/O
|
|
for (const { input, output } of inputOutputJSON) {
|
|
// Write input.json
|
|
const inputStringify = JSON.stringify(input)
|
|
await fs.writeFile(inputPath, inputStringify)
|
|
|
|
// Execute script (create output.json)
|
|
try {
|
|
const { stderr } = await exec(
|
|
`${languageToExecute.launch} ${executeLanguageTempPath}`
|
|
)
|
|
if (stderr.length !== 0) {
|
|
await handleStderror(stderr, tempPath)
|
|
}
|
|
} catch (error) {
|
|
await handleStderror(error.stderr, tempPath)
|
|
}
|
|
|
|
// Read output.json
|
|
const data = await fs.readFile(outputPath)
|
|
const outputJSON = JSON.parse(data.toString())
|
|
|
|
// Tests
|
|
totalCorrect.total += 1
|
|
const outputJSONStringify = JSON.stringify(outputJSON)
|
|
const outputStringify = JSON.stringify(output)
|
|
const isCorrect = outputJSONStringify === outputStringify
|
|
|
|
if (isCorrect) {
|
|
tableResult.push([
|
|
emoji.get('white_check_mark'),
|
|
inputStringify,
|
|
outputJSONStringify,
|
|
outputStringify
|
|
])
|
|
totalCorrect.correct += 1
|
|
} else {
|
|
tableResult.push([
|
|
emoji.get('x'),
|
|
inputStringify,
|
|
outputJSONStringify,
|
|
outputStringify
|
|
])
|
|
}
|
|
|
|
// Delete I/O file
|
|
await fs.unlink(inputPath)
|
|
await fs.unlink(outputPath)
|
|
}
|
|
|
|
const endTest = performance.now()
|
|
|
|
console.log(
|
|
table(tableResult, {
|
|
columns: {
|
|
0: { width: 6, alignment: 'center' },
|
|
1: { width: 20, wrapWord: true },
|
|
2: { width: 30, wrapWord: true },
|
|
3: { width: 30, wrapWord: true }
|
|
}
|
|
})
|
|
)
|
|
console.log(`
|
|
Challenge : ${challengeName}
|
|
Solution : ${solutionName}
|
|
Tests : ${chalk.green(`${totalCorrect.correct} passed`)}, ${
|
|
totalCorrect.total
|
|
} total
|
|
Time : ${chalk.yellow(prettyMilliseconds(endTest - startTest))}
|
|
`)
|
|
|
|
await deleteAllFilesExceptOne(tempPath, '.gitignore')
|
|
})()
|