122 lines
4.9 KiB
JavaScript
122 lines
4.9 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import vm from 'node:vm';
|
|
import { getRtwCenters } from '../www/core/rtw_centers.js';
|
|
import { buildRtwTickPositions, selectLocalRtwPacket } from '../www/views/realtime.js';
|
|
|
|
const source = fs.readFileSync(new URL('../www/core/audio.js', import.meta.url), 'utf8');
|
|
const configSource = fs.readFileSync(new URL('../www/core/config.js', import.meta.url), 'utf8');
|
|
const correlationNormalizerSource = configSource.match(
|
|
/export function normalizeCorrelationResponseSeconds[\s\S]*?\n\}/,
|
|
)?.[0];
|
|
assert.ok(correlationNormalizerSource, 'correlation response normalization must remain testable');
|
|
const correlationContext = vm.createContext({ Number });
|
|
vm.runInContext(
|
|
correlationNormalizerSource.replace('export function', 'function'),
|
|
correlationContext,
|
|
);
|
|
assert.equal(correlationContext.normalizeCorrelationResponseSeconds(0.5), 0.5);
|
|
assert.equal(correlationContext.normalizeCorrelationResponseSeconds(1.0), 1.0);
|
|
assert.equal(correlationContext.normalizeCorrelationResponseSeconds(2.5), 2.5);
|
|
assert.equal(correlationContext.normalizeCorrelationResponseSeconds(Number.NaN), 1.0);
|
|
|
|
const functionSource = source.match(/export function buildRtaRuntimeConfig[\s\S]*?\n\}/)?.[0];
|
|
assert.ok(functionSource, 'buildRtaRuntimeConfig must remain testable');
|
|
const context = vm.createContext({
|
|
Number,
|
|
normalizeCorrelationResponseSeconds: correlationContext.normalizeCorrelationResponseSeconds,
|
|
getRtwCenters(mode) {
|
|
if (mode === '1_12') return new Array(121).fill(0);
|
|
if (mode === '1_6') return new Array(61).fill(0);
|
|
return new Array(31).fill(0);
|
|
},
|
|
});
|
|
vm.runInContext(functionSource.replace('export function', 'function'), context);
|
|
|
|
const forced = context.buildRtaRuntimeConfig({
|
|
RTA_BAR_LAYOUT: 'rtw',
|
|
RTA_ENGINE: 'fft',
|
|
RTA_BPO_MODE: '1_12',
|
|
RTA_FREQ_RANGE: 'lf',
|
|
RTA_IIR_ORDER: 2,
|
|
RTA_DETECTOR: 'peak',
|
|
RTA_PEAK_HOLD_MODE: 'fall',
|
|
RTA_PEAK_HOLD_SEC: 4,
|
|
RTA_PEAK_DECAY_DB_PER_S: 20,
|
|
RTA_PEAK_RESET_TOKEN: 9,
|
|
CORR_RESPONSE_S: 2.5,
|
|
CORR_SILENCE_THRESHOLD_RMS_DBFS: -50,
|
|
CORR_RESET_TOKEN: 7,
|
|
XY_POINTS: 256,
|
|
});
|
|
assert.equal(forced.engine, 'iir');
|
|
assert.equal(forced.bpo, '1_12');
|
|
assert.equal(forced.freqRange, 'norm');
|
|
assert.equal(forced.order, 6);
|
|
assert.equal(forced.detector, 'peak');
|
|
assert.equal(forced.rtaPeakHoldMode, 'fall');
|
|
assert.equal(forced.rtaPeakHoldSeconds, 4);
|
|
assert.equal(forced.rtaPeakDecayDbPerSecond, 20);
|
|
assert.equal(forced.rtaPeakResetToken, 9);
|
|
assert.equal(forced.tauFast, 0.125);
|
|
assert.equal(forced.rtwCenters.length, 121);
|
|
assert.equal(forced.correlationResponseS, 2.5);
|
|
assert.equal(forced.correlationSilenceThresholdRmsDbfs, -50);
|
|
assert.equal(forced.correlationResetToken, 7);
|
|
assert.equal(forced.xyPoints, 256);
|
|
|
|
const veryFastCorrelation = context.buildRtaRuntimeConfig({ CORR_RESPONSE_S: 0.5 });
|
|
assert.equal(veryFastCorrelation.correlationResponseS, 0.5);
|
|
|
|
const extension = context.buildRtaRuntimeConfig({
|
|
RTA_BAR_LAYOUT: 'iec',
|
|
RTA_ENGINE: 'fft',
|
|
RTA_BPO_MODE: '1_12',
|
|
RTA_FREQ_RANGE: 'lf',
|
|
RTA_IIR_ORDER: 8,
|
|
});
|
|
assert.equal(extension.engine, 'fft');
|
|
assert.equal(extension.bpo, '1_12');
|
|
assert.equal(extension.freqRange, 'lf');
|
|
assert.equal(extension.order, 8);
|
|
|
|
const selectionSource = configSource.match(/function applyRtaBpoSelection[\s\S]*?\n\}/)?.[0];
|
|
assert.ok(selectionSource, 'RTA BPO selection policy must remain testable');
|
|
const selectionContext = vm.createContext({
|
|
String,
|
|
CONFIG: { RTA_BPO_MODE: '1_3', RTA_BAR_LAYOUT: 'rtw' },
|
|
});
|
|
vm.runInContext(selectionSource, selectionContext);
|
|
assert.equal(selectionContext.applyRtaBpoSelection('1_6'), '1_6');
|
|
assert.equal(selectionContext.CONFIG.RTA_BPO_MODE, '1_6');
|
|
assert.equal(selectionContext.CONFIG.RTA_BAR_LAYOUT, 'rtw');
|
|
assert.equal(selectionContext.applyRtaBpoSelection('1_3'), '1_3');
|
|
assert.equal(selectionContext.CONFIG.RTA_BAR_LAYOUT, 'rtw');
|
|
|
|
const twelfthCenters = getRtwCenters('1_12');
|
|
const staleTwelfthPacket = {
|
|
engine: 'iir',
|
|
bpo: '1_12',
|
|
centers: twelfthCenters,
|
|
bands_avg: twelfthCenters.map((_, index) => index),
|
|
bands_peak: twelfthCenters.map((_, index) => index + 1000),
|
|
};
|
|
const selectedSixth = selectLocalRtwPacket(staleTwelfthPacket, '1_6');
|
|
assert.equal(selectedSixth.bpo, '1_6');
|
|
assert.equal(selectedSixth.centers.length, getRtwCenters('1_6').length);
|
|
assert.equal(selectedSixth.bands_avg.length, selectedSixth.centers.length);
|
|
assert.equal(selectedSixth.bands_peak.length, selectedSixth.centers.length);
|
|
|
|
for (const mode of ['1_3', '1_6', '1_12']) {
|
|
const centers = getRtwCenters(mode);
|
|
const ticks = [20, 31.5, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000];
|
|
const positions = buildRtwTickPositions(centers, ticks);
|
|
for (const tick of ticks) {
|
|
const index = centers.indexOf(tick);
|
|
assert.ok(index >= 0, `${mode} must contain the ${tick} Hz display band`);
|
|
assert.equal(positions[String(tick)], (index + 0.5) / centers.length);
|
|
}
|
|
}
|
|
|
|
console.log('RTA profile regression tests passed');
|