Files

183 lines
5.0 KiB
Ucode

#!/usr/bin/ucode
'use strict';
import { readfile, writefile, stat } from 'fs';
const base_dir = ARGV?.[0] || 'tests/generator-golden';
const generator = ARGV?.[1] || '/etc/homeproxy/scripts/generate_client.uc';
const module_dir = ARGV?.[2];
function hasFlag(flag) {
const argv = (type(ARGV) === 'array') ? ARGV : [];
for (let item in argv)
if (item === flag)
return true;
return false;
}
const update_expected = hasFlag('--update');
function shellquote(s) {
return `'${replace(s, "'", "'\\''")}'`;
}
function fail(message) {
warn(message + '\n');
exit(1);
}
function path(...parts) {
return join('/', parts);
}
function exists(file) {
return stat(file)?.type === 'file';
}
function readJson(file, fallback) {
if (!exists(file))
return fallback;
return json(readfile(file));
}
function normalize(value, test_root) {
let value_type = type(value);
if (value_type === 'string')
return replace(value, test_root, '__TEST_ROOT__');
if (value_type === 'array') {
let result = [];
for (let item in value)
push(result, normalize(item, test_root));
return result;
}
if (value_type === 'object') {
let result = {};
for (let key in value) {
if (key === 'time')
continue;
result[key] = normalize(value[key], test_root);
}
return result;
}
return value;
}
function canonical(value) {
return sprintf('%.J', value);
}
function compareJson(case_name, label, actual, expected) {
if (canonical(actual) === canonical(expected))
return;
warn(sprintf('%s: %s golden mismatch\n', case_name, label));
warn('expected:\n' + sprintf('%.J\n', expected));
warn('actual:\n' + sprintf('%.J\n', actual));
exit(1);
}
function generatorCommand() {
if (module_dir && module_dir !== '--update')
return sprintf('ucode -S -L %s %s', shellquote(module_dir), shellquote(generator));
return shellquote(generator);
}
function prepareRoot(case_name, fixture_dir) {
const test_root = '/tmp/homeproxy-golden-' + replace(case_name, /[^A-Za-z0-9_-]/g, '_');
system('rm -rf ' + shellquote(test_root));
system('mkdir -p ' +
shellquote(path(test_root, 'etc/config')) + ' ' +
shellquote(path(test_root, 'etc/homeproxy/resources')) + ' ' +
shellquote(path(test_root, 'var/run/homeproxy')));
writefile(path(test_root, 'etc/config/homeproxy'), readfile(path(fixture_dir, 'homeproxy')));
const direct_list = path(fixture_dir, 'resources/direct_list.txt'),
proxy_list = path(fixture_dir, 'resources/proxy_list.txt');
writefile(path(test_root, 'etc/homeproxy/resources/direct_list.txt'), exists(direct_list) ? readfile(direct_list) : '');
writefile(path(test_root, 'etc/homeproxy/resources/proxy_list.txt'), exists(proxy_list) ? readfile(proxy_list) : '');
return test_root;
}
function runCase(item) {
const case_name = item.name,
fixture_dir = path(base_dir, 'fixtures', case_name),
meta = readJson(path(fixture_dir, 'case.json'), null);
if (!meta)
fail(sprintf('%s: missing fixture case.json', case_name));
const test_root = prepareRoot(case_name, fixture_dir),
stdout_path = path(test_root, 'var/run/homeproxy/stdout.log'),
stderr_path = path(test_root, 'var/run/homeproxy/stderr.log'),
command = sprintf('%s --test-root %s >%s 2>%s',
generatorCommand(),
shellquote(test_root),
shellquote(stdout_path),
shellquote(stderr_path)),
status = system(command),
success = (status === 0);
if (!!meta.success !== success) {
warn(sprintf('%s: expected success=%J, got status=%d\n', case_name, meta.success, status));
warn(readfile(stderr_path) || '');
system('rm -rf ' + shellquote(test_root));
exit(1);
}
const actual_config_path = path(test_root, 'var/run/homeproxy/sing-box-c.json'),
expected_config_path = path(fixture_dir, 'expected/sing-box-c.json');
if (meta.success) {
if (!exists(actual_config_path))
fail(sprintf('%s: generator succeeded but sing-box-c.json is missing', case_name));
const actual_config = normalize(json(readfile(actual_config_path)), test_root);
if (update_expected)
writefile(expected_config_path, sprintf('%.J\n', actual_config));
else
compareJson(case_name, 'sing-box-c.json',
actual_config,
readJson(expected_config_path, null));
}
const actual_diagnostics = exists(path(test_root, 'var/run/homeproxy/config-diagnostics.json'))
? json(readfile(path(test_root, 'var/run/homeproxy/config-diagnostics.json')))
: { items: [] };
const normalized_diagnostics = normalize(actual_diagnostics, test_root),
expected_diagnostics_path = path(fixture_dir, 'expected/config-diagnostics.json');
if (update_expected)
writefile(expected_diagnostics_path, sprintf('%.J\n', normalized_diagnostics));
else
compareJson(case_name, 'config-diagnostics.json',
normalized_diagnostics,
readJson(expected_diagnostics_path, { items: [] }));
system('rm -rf ' + shellquote(test_root));
printf('%s ok\n', case_name);
}
const cases = readJson(path(base_dir, 'cases.json'), []);
let count = 0;
for (let item in cases) {
if (!item.generator_fixture)
continue;
runCase(item);
count++;
}
printf('generator golden fixtures: %d ok\n', count);