Added automated testing... and there are a lot of issues
This commit is contained in:
20
run_test.sh
Executable file
20
run_test.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
OUT=/tmp/tultemplategen_test_bundle.js
|
||||
|
||||
cat \
|
||||
tests/init.js \
|
||||
lib/jszip.min.js \
|
||||
src/data/autogen.js \
|
||||
src/data/typst_header.js \
|
||||
src/data/l10n.js \
|
||||
src/l10n.js \
|
||||
src/data/steps.js \
|
||||
src/output/header.js \
|
||||
src/output/zip.js \
|
||||
tests/headless.js \
|
||||
tests/main.js \
|
||||
> $OUT
|
||||
|
||||
node $OUT
|
||||
|
||||
131
tests/headless.js
Normal file
131
tests/headless.js
Normal file
@@ -0,0 +1,131 @@
|
||||
// Minimal implementations of layout classes for analyzing possible inputs.
|
||||
|
||||
class LayoutButtonList {
|
||||
constructor(inputskey, options, optional_cb) {
|
||||
this.options = options;
|
||||
this.optional_cb = optional_cb;
|
||||
this.inputskey = inputskey;
|
||||
}
|
||||
|
||||
narrow() { return this; }
|
||||
horizontal() { return this; }
|
||||
|
||||
*iter(inputs) {
|
||||
inputs[this.inputskey] = "";
|
||||
yield `${this.inputskey}: Empty string`;
|
||||
|
||||
for(const val in this.options) {
|
||||
if(val == "dis" || val == "hab" || val == "teze" || val == "autoref" || val == "sp") continue; // complete bodge until upstream supports all thesis types
|
||||
if(this.inputskey == "toolchain" && val == "online") continue; // bodge for skipping tests for archives intended for the online editor
|
||||
|
||||
inputs[this.inputskey] = val;
|
||||
yield `${this.inputskey}: Value from options: ${val}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutTextInput {
|
||||
constructor(inputskey) {
|
||||
this.inputskey = inputskey;
|
||||
this.togglekey = null;
|
||||
}
|
||||
|
||||
hint() { return this; }
|
||||
|
||||
toggle(togglekey) {
|
||||
this.togglekey = togglekey;
|
||||
return this;
|
||||
}
|
||||
|
||||
*iter(inputs) {
|
||||
if(this.togglekey) {
|
||||
inputs[this.togglekey] = false;
|
||||
|
||||
inputs[this.inputskey] = "";
|
||||
yield `${this.inputskey}, ${this.togglekey}: Empty string (disabled)`;
|
||||
|
||||
inputs[this.inputskey] = "Skibidi";
|
||||
yield `${this.inputskey}, ${this.togglekey}: Populated string (disabled)`;
|
||||
|
||||
inputs[this.togglekey] = true;
|
||||
}
|
||||
|
||||
inputs[this.inputskey] = "";
|
||||
yield `${this.inputskey}, ${this.togglekey}: Empty string`;
|
||||
|
||||
inputs[this.inputskey] = "Skibidi";
|
||||
yield `${this.inputskey}, ${this.togglekey}: Populated string`;
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutBinaryInput {
|
||||
constructor(inputskey) {
|
||||
this.inputskey = inputskey;
|
||||
}
|
||||
|
||||
label() { return this; }
|
||||
|
||||
*iter(inputs) {
|
||||
inputs[this.inputskey] = false;
|
||||
yield `${this.inputskey}: Unchecked`;
|
||||
|
||||
inputs[this.inputskey] = true;
|
||||
yield `${this.inputskey}: Checked`;
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutFileUpload {
|
||||
constructor(namekey, contentskey) {
|
||||
this.namekey = namekey;
|
||||
this.contentskey = contentskey;
|
||||
}
|
||||
|
||||
magic() { return this; }
|
||||
|
||||
*iter(inputs) {
|
||||
inputs[this.namekey] = null;
|
||||
inputs[this.contentskey] = null;
|
||||
yield `${this.namekey}, ${this.contentskey}: No file selected`;
|
||||
|
||||
inputs[this.namekey] = "test-pages.pdf"
|
||||
inputs[this.contentskey] = new Uint8Array(test_pages_pdf);
|
||||
yield `${this.namekey}, ${this.contentskey}: Selected example PDF file`;
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutDropdownList {
|
||||
constructor(inputskey, options) {
|
||||
this.inputskey = inputskey;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
*iter(inputs) {
|
||||
for(const val in this.options) {
|
||||
inputs[this.inputskey] = val;
|
||||
yield `${this.inputskey}: Value from options: ${val}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutSpacer {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
class LayoutHint {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
class LayoutLabel {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
// Minimal implementations of functions just so that the generator can work.
|
||||
|
||||
function show_experimental_faculty_warning() {}
|
||||
|
||||
function fetch_cached_blob(name) {
|
||||
if(name != "template.zip") throw "files other than the main assets bundle not supported";
|
||||
|
||||
return template_zip;
|
||||
}
|
||||
|
||||
10
tests/init.js
Normal file
10
tests/init.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import fs from "fs/promises";
|
||||
import { exec } from "child_process";
|
||||
|
||||
let config = {
|
||||
lang: "cs"
|
||||
};
|
||||
|
||||
let template_zip = await fs.readFile("template.zip");
|
||||
let test_pages_pdf = await fs.readFile("tests/test-pages.pdf");
|
||||
|
||||
193
tests/main.js
Normal file
193
tests/main.js
Normal file
@@ -0,0 +1,193 @@
|
||||
import { unzip } from "zlib";
|
||||
|
||||
let step_keys = Object.keys(steps);
|
||||
|
||||
async function finalize_step(step) {
|
||||
for(const key in step.inputs) {
|
||||
step.outputs[key] = step.inputs[key];
|
||||
}
|
||||
|
||||
if(step.next) {
|
||||
await step.next(step.inputs, step.outputs);
|
||||
}
|
||||
}
|
||||
|
||||
function allow_next_step(step) {
|
||||
if(!step.only_continue_if) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return step.only_continue_if(step.inputs);
|
||||
}
|
||||
|
||||
async function exec_process(cmd) {
|
||||
return new Promise(r => {
|
||||
exec(cmd, (error, stdout, stderr) => {
|
||||
if(error) {
|
||||
// console.log(`Error running ${error}: ${stderr}`);
|
||||
r(stderr);
|
||||
}
|
||||
|
||||
r(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function iterate_layout(inputs, layout, layout_idx, full_cb) {
|
||||
if(layout_idx >= layout.length) {
|
||||
await full_cb();
|
||||
return;
|
||||
}
|
||||
|
||||
let input = layout[layout_idx];
|
||||
|
||||
if(input.iter) {
|
||||
for(const val of input.iter(inputs)) {
|
||||
// console.log(`${layout_idx}/${layout.length}: ${val}`);
|
||||
await iterate_layout(inputs, layout, layout_idx + 1, full_cb);
|
||||
}
|
||||
} else {
|
||||
await iterate_layout(inputs, layout, layout_idx + 1, full_cb);
|
||||
}
|
||||
}
|
||||
|
||||
async function iterate_steps(steps_idx, dead_end_cb, finish_cb) {
|
||||
if(steps_idx >= step_keys.length) {
|
||||
await finish_cb();
|
||||
return;
|
||||
}
|
||||
|
||||
let step = steps[step_keys[steps_idx]];
|
||||
step.outputs = {};
|
||||
|
||||
let old_inputs = {};
|
||||
|
||||
for(const input in step.inputs) {
|
||||
old_inputs[input] = step.inputs[input];
|
||||
}
|
||||
|
||||
if(step.condition && !step.condition()) {
|
||||
await iterate_steps(steps_idx + 1, dead_end_cb, finish_cb);
|
||||
return;
|
||||
}
|
||||
|
||||
await iterate_layout(step.inputs, step.layout(), 0, async () => {
|
||||
if(!allow_next_step(step)) {
|
||||
await dead_end_cb(steps_idx);
|
||||
return;
|
||||
};
|
||||
|
||||
await finalize_step(step);
|
||||
|
||||
await iterate_steps(steps_idx + 1, dead_end_cb, finish_cb);
|
||||
});
|
||||
|
||||
step.inputs = old_inputs;
|
||||
step.outputs = {};
|
||||
}
|
||||
|
||||
// for(const step in steps) {
|
||||
// console.log(step);
|
||||
// if(steps[step].condition) steps[step].condition();
|
||||
// for(const input of steps[step].layout()) {
|
||||
// console.log(input);
|
||||
// if(input.iter) for(const val of input.iter(steps[step].inputs)) {
|
||||
// console.log(" - " + val);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// console.log(steps[step].inputs);
|
||||
// }
|
||||
|
||||
let dead_ends = 0;
|
||||
let viable_paths = 0;
|
||||
|
||||
await iterate_steps(0, async (step_idx) => {
|
||||
// console.log("dead end at step idx " + step_idx + " (" + step_keys[step_idx] + ")");
|
||||
//
|
||||
// let out = {};
|
||||
//
|
||||
// for(const key in steps) {
|
||||
// out[key] = steps[key].outputs;
|
||||
// }
|
||||
//
|
||||
// console.log(out);
|
||||
|
||||
dead_ends += 1;
|
||||
}, async () => {
|
||||
viable_paths += 1;
|
||||
});
|
||||
|
||||
console.log(`Found ${viable_paths} viable paths and ${dead_ends} dead ends.`);
|
||||
|
||||
let test_ctr = 0;
|
||||
let failed_test_ctr = 0;
|
||||
|
||||
|
||||
|
||||
const base_path = `/tmp/tultemplategen_test_${new Date().toISOString()}`;
|
||||
await fs.mkdir(base_path)
|
||||
|
||||
async function store_test_results(idx, stderr) {
|
||||
let inputs = {};
|
||||
let outputs = {};
|
||||
|
||||
for(const key in steps) {
|
||||
inputs[key] = steps[key].inputs;
|
||||
outputs[key] = steps[key].outputs;
|
||||
}
|
||||
|
||||
let outdir = `${base_path}/tultemplategen_failed_test_${idx}`;
|
||||
await fs.mkdir(outdir);
|
||||
await fs.rename(`${base_path}/tultemplategen_test_bundle.zip`, `${outdir}/thesis.zip`);
|
||||
await fs.writeFile(`${outdir}/inputs.json`, JSON.stringify(inputs));
|
||||
await fs.writeFile(`${outdir}/outputs.json`, JSON.stringify(outputs));
|
||||
await fs.writeFile(`${outdir}/stderr`, stderr);
|
||||
|
||||
failed_test_ctr++;
|
||||
|
||||
if(failed_test_ctr >= 10) {
|
||||
console.log("10 test failed. Aborting.");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
await iterate_steps(0, () => {}, async () => {
|
||||
test_ctr++;
|
||||
console.log(`Running test ${test_ctr}/${viable_paths}...`);
|
||||
|
||||
// console.log(" - generating archive...");
|
||||
|
||||
let zip = await generate_zip();
|
||||
|
||||
// console.log(" - saving archive...");
|
||||
|
||||
await fs.writeFile(`${base_path}/tultemplategen_test_bundle.zip`, zip);
|
||||
|
||||
// console.log(" - clearing old directory...");
|
||||
|
||||
await fs.rm(`${base_path}/tul-thesis`, { recursive: true, force: true }, () => r());
|
||||
|
||||
// console.log(" - extracting archive...");
|
||||
|
||||
let unzip_output = await exec_process(`cd ${base_path} && unzip tultemplategen_test_bundle.zip`);
|
||||
|
||||
if(unzip_output !== null) {
|
||||
console.log(" - failed during extraction");
|
||||
store_test_results(test_ctr, unzip_output);
|
||||
return;
|
||||
}
|
||||
|
||||
// console.log(" - compiling...")
|
||||
|
||||
let make_output = await exec_process(`cd ${base_path}/tul-thesis && make thesis.pdf`);
|
||||
|
||||
if(make_output !== null) {
|
||||
console.log(" - failed during building");
|
||||
store_test_results(test_ctr, make_output);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
console.log("Finished.");
|
||||
|
||||
BIN
tests/test-pages.pdf
Normal file
BIN
tests/test-pages.pdf
Normal file
Binary file not shown.
Reference in New Issue
Block a user