152 lines
4.3 KiB
JavaScript
152 lines
4.3 KiB
JavaScript
import { glob } from 'glob'
|
|
import path from 'path'
|
|
import childProcess from 'node:child_process'
|
|
import fs from 'node:fs/promises'
|
|
import chalk from 'chalk'
|
|
|
|
// Define constants.
|
|
const tmp = path.resolve('./build/riscv')
|
|
const toolDirectory = path.resolve('./build/tools')
|
|
|
|
async function compileTest (parsedPath) {
|
|
const testObject = path.join(tmp, `tests/${parsedPath.name}.o`)
|
|
const compilerArguments = [
|
|
'-o', testObject,
|
|
path.join(parsedPath.dir, parsedPath.base)
|
|
]
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
const compilerChild = childProcess.spawn('./build/bin/elna', compilerArguments)
|
|
const buffers = []
|
|
|
|
compilerChild.stdout.on('data', data => buffers.push(data))
|
|
compilerChild.stderr.on('data', data => buffers.push(data))
|
|
|
|
compilerChild.on('exit', function (code, signal) {
|
|
resolve({
|
|
code: code === null ? signal : code,
|
|
output: Buffer.concat(buffers).toString(),
|
|
object: testObject
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
async function checkFailure (parsedPath, buildResult) {
|
|
const failureFile = path.resolve('./tests/failures', `${parsedPath.name}.txt`)
|
|
|
|
await fs.access(failureFile)
|
|
const diffArguments = [
|
|
'-u', '--color=always',
|
|
failureFile, '-'
|
|
]
|
|
|
|
try {
|
|
childProcess.execFileSync('diff', diffArguments, {
|
|
input: buildResult.output,
|
|
stdio: ['pipe', 'inherit', 'inherit']
|
|
})
|
|
|
|
return true
|
|
} catch (e) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
function buildTest (parsedPath) {
|
|
const linker = path.join(toolDirectory, 'rootfs/riscv32-unknown-linux-gnu/bin/ld')
|
|
const testBinary = path.join(tmp, 'root/tests', parsedPath.name)
|
|
const testObject = path.join(tmp, `tests/${parsedPath.name}.o`)
|
|
|
|
childProcess.execFileSync(linker, ['-o', testBinary, testObject])
|
|
}
|
|
|
|
async function cpioArchive () {
|
|
const cwd = path.join(tmp, 'root')
|
|
const cpioImage = path.join(tmp, 'root.cpio')
|
|
const rootFiles = (await glob(path.join(cwd, '**')))
|
|
.map(file => path.relative(cwd, file))
|
|
.filter(file => file !== '')
|
|
const cpioFile = await fs.open(cpioImage, 'w')
|
|
const cpioStream = cpioFile.createWriteStream()
|
|
try {
|
|
const cpioChild = childProcess.execSync('cpio -o --format=newc', {
|
|
stdio: ['pipe', cpioStream, 'ignore'],
|
|
input: rootFiles.join("\n"),
|
|
cwd,
|
|
shell: false
|
|
})
|
|
return cpioImage
|
|
} finally {
|
|
await cpioFile.close()
|
|
}
|
|
}
|
|
|
|
async function runVM(cpioImage) {
|
|
const kernels = await glob(path.join(toolDirectory, 'linux-*/arch/riscv/boot/Image'))
|
|
const vmArguments = [
|
|
'-nographic',
|
|
'-M', 'virt',
|
|
'-bios', 'default',
|
|
'-kernel', kernels[0],
|
|
'-append', 'quiet',
|
|
'-initrd', cpioImage
|
|
]
|
|
childProcess.execFileSync('qemu-system-riscv32', vmArguments, {
|
|
stdio: ['ignore', 'inherit', 'inherit']
|
|
});
|
|
}
|
|
|
|
async function runInDirectory (directoryPath) {
|
|
let failed = 0
|
|
const sources = await glob(path.join(directoryPath, '*.eln'))
|
|
|
|
for (const testEntry of sources) {
|
|
const parsedPath = path.parse(testEntry)
|
|
|
|
console.log(`Compiling ${parsedPath.base}.`)
|
|
const buildResult = await compileTest(parsedPath)
|
|
|
|
if (buildResult.code !== 0) {
|
|
try {
|
|
if (!(await checkFailure(parsedPath, buildResult))) {
|
|
++failed
|
|
}
|
|
} catch (e) {
|
|
++failed
|
|
console.log(buildResult.output)
|
|
}
|
|
continue
|
|
}
|
|
buildTest(parsedPath)
|
|
}
|
|
const rootDirectory = path.join(tmp, 'root')
|
|
await fs.cp('./tests/expectations', path.join(rootDirectory, 'expectations'), { recursive: true })
|
|
await fs.cp(path.join(toolDirectory, 'init'), path.join(rootDirectory, 'init'));
|
|
const cpioImage = await cpioArchive()
|
|
await runVM(cpioImage)
|
|
|
|
return {
|
|
total: sources.length,
|
|
failed,
|
|
|
|
passed () {
|
|
return this.total - this.failed
|
|
}
|
|
}
|
|
}
|
|
|
|
async function createDirectories () {
|
|
await fs.rm(tmp, { recursive: true, force: true })
|
|
await fs.mkdir(path.join(tmp, 'tests'), { recursive: true })
|
|
await fs.mkdir(path.join(tmp, 'root/tests'), { recursive: true })
|
|
}
|
|
|
|
console.log('Run all tests and check the results.')
|
|
|
|
await createDirectories()
|
|
const results = await runInDirectory('./tests')
|
|
|
|
const summaryMessage = `${results.total} tests run, ${results.passed()} passed, ${results.failed} failed.`
|
|
console.log(results.failed === 0 ? chalk.green(summaryMessage) : chalk.yellow(summaryMessage))
|