인터랙티브 아트워크 30종과 시간 기반 전환 및 개발 문서 추가
This commit is contained in:
84
tests/artwork-schedule.test.cjs
Normal file
84
tests/artwork-schedule.test.cjs
Normal file
@@ -0,0 +1,84 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const vm = require('node:vm');
|
||||
|
||||
const sandbox = {};
|
||||
vm.runInNewContext(fs.readFileSync(path.join(__dirname, '../artwork-schedule.js'), 'utf8'), sandbox);
|
||||
const schedule = sandbox.NNCArtworkSchedule;
|
||||
const order = loop => Array.from(schedule.orderForLoop(loop));
|
||||
|
||||
test('137-minute slots have a fixed selection up to the exact boundary', () => {
|
||||
const duration = 137 * 60 * 1000;
|
||||
assert.equal(schedule.SLOT_DURATION, duration);
|
||||
const now = Date.parse('2026-10-07T09:00:00+09:00');
|
||||
const start = Math.floor(now / duration) * duration;
|
||||
const first = schedule.selectionAt(start);
|
||||
assert.equal(schedule.selectionAt(start + 1).variant, first.variant);
|
||||
assert.equal(schedule.selectionAt(start + duration - 1).variant, first.variant);
|
||||
const next = schedule.selectionAt(start + duration);
|
||||
assert.equal(next.slot, first.slot + 1);
|
||||
assert.notEqual(next.variant, first.variant);
|
||||
});
|
||||
|
||||
test('PRNG streams and permutations reproduce across fresh evaluations', () => {
|
||||
const fresh = {};
|
||||
vm.runInNewContext(fs.readFileSync(path.join(__dirname, '../artwork-schedule.js'), 'utf8'), fresh);
|
||||
for (const seed of [0, 1, 100, 101, 102, 2147483647]) {
|
||||
const a = schedule.createRandom(seed);
|
||||
const b = fresh.NNCArtworkSchedule.createRandom(seed);
|
||||
for (let index = 0; index < 100; index++) {
|
||||
const value = a();
|
||||
assert(value >= 0 && value < 1);
|
||||
assert.equal(value, b());
|
||||
}
|
||||
assert.deepEqual(order(seed), Array.from(fresh.NNCArtworkSchedule.orderForLoop(seed)));
|
||||
}
|
||||
});
|
||||
|
||||
test('10,000 loops each contain all 30 artworks with no repeat across boundaries', () => {
|
||||
let previous;
|
||||
for (let loop = 0; loop < 10000; loop++) {
|
||||
const current = order(loop);
|
||||
assert.deepEqual([...current].sort((a, b) => a - b), Array.from({ length: 30 }, (_, i) => i));
|
||||
if (previous) {
|
||||
assert.notEqual(previous[29], current[0]);
|
||||
assert.notDeepEqual(previous, current);
|
||||
}
|
||||
previous = current;
|
||||
}
|
||||
});
|
||||
|
||||
test('a boundary collision swaps exactly the first two items', () => {
|
||||
function raw(loop) {
|
||||
const random = schedule.createRandom(loop);
|
||||
const result = Array.from({ length: 30 }, (_, i) => i);
|
||||
for (let i = 29; i > 0; i--) {
|
||||
const j = Math.floor(random() * (i + 1));
|
||||
[result[i], result[j]] = [result[j], result[i]];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
let collisions = 0;
|
||||
for (let loop = 1; loop < 500; loop++) {
|
||||
const expected = raw(loop);
|
||||
if (raw(loop - 1)[29] === expected[0]) {
|
||||
[expected[0], expected[1]] = [expected[1], expected[0]];
|
||||
collisions++;
|
||||
}
|
||||
assert.deepEqual(order(loop), expected);
|
||||
}
|
||||
assert(collisions > 0);
|
||||
});
|
||||
|
||||
test('all slots of a loop map to its permutation, including loop transitions', () => {
|
||||
for (const loop of [0, 100, 101, 102, 2102]) {
|
||||
for (let position = 0; position < 30; position++) {
|
||||
const value = schedule.selectionAt((loop * 30 + position) * schedule.SLOT_DURATION);
|
||||
assert.equal(value.loop, loop);
|
||||
assert.equal(value.position, position);
|
||||
assert.equal(value.variant, order(loop)[position]);
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user