summaryrefslogtreecommitdiff
path: root/tools/timeline_editor/test_format.html
blob: 12b788f5dea3e248bd73cadf79aff4e78cd32c9a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Timeline Format Test</title>
    <style>
        body { font-family: monospace; padding: 20px; background: #1e1e1e; color: #d4d4d4; }
        .test { margin: 20px 0; padding: 10px; border: 1px solid #444; }
        .pass { border-color: #4ec9b0; }
        .fail { border-color: #f48771; }
        pre { background: #2d2d30; padding: 10px; overflow-x: auto; }
    </style>
</head>
<body>
    <h1>Timeline Format V2 Test</h1>
    <div id="results"></div>

    <script type="module">
        import { TimelineFormat } from './timeline-format.js';

        const timelineFormat = new TimelineFormat();
        const results = document.getElementById('results');

        function addTest(name, passed, details) {
            const div = document.createElement('div');
            div.className = `test ${passed ? 'pass' : 'fail'}`;
            div.innerHTML = `<h3>${passed ? '✓' : '✗'} ${name}</h3>${details}`;
            results.appendChild(div);
        }

        // Test 1: Parse existing timeline (no NODE declarations)
        const testTimeline1 = `# WORKSPACE: test
# BPM 120

SEQUENCE 0.0 0 "MainLoop"
EFFECT + Hybrid3D source -> temp1 0.00 4.00
EFFECT + GaussianBlur temp1 -> sink 0.00 4.00`;

        const parsed1 = timelineFormat.parse(testTimeline1, 120);
        const test1Pass = parsed1.sequences.length === 1 &&
                         parsed1.sequences[0].effects.length === 2 &&
                         parsed1.sequences[0].effects[0].inputs[0] === 'source' &&
                         parsed1.sequences[0].effects[0].outputs[0] === 'temp1';
        addTest('Parse timeline without NODE declarations', test1Pass,
            `<pre>Sequences: ${parsed1.sequences.length}
Effects: ${parsed1.sequences[0].effects.length}
First effect inputs: ${parsed1.sequences[0].effects[0].inputs.join(', ')}
First effect outputs: ${parsed1.sequences[0].effects[0].outputs.join(', ')}</pre>`);

        // Test 2: Parse timeline with NODE declarations
        const testTimeline2 = `# BPM 120
SEQUENCE 0.0 0 "v2_test"
  NODE temp1 u8x4_norm
  NODE temp2 f32x4
  EFFECT + Hybrid3D source -> temp1 0.0 4.0
  EFFECT + GaussianBlur temp1 -> temp2 0.0 4.0`;

        const parsed2 = timelineFormat.parse(testTimeline2, 120);
        const test2Pass = parsed2.sequences[0].nodes.length === 2 &&
                         parsed2.sequences[0].nodes[0].name === 'temp1' &&
                         parsed2.sequences[0].nodes[0].type === 'u8x4_norm';
        addTest('Parse timeline with NODE declarations', test2Pass,
            `<pre>Nodes: ${parsed2.sequences[0].nodes.length}
Node 1: ${parsed2.sequences[0].nodes[0].name} (${parsed2.sequences[0].nodes[0].type})
Node 2: ${parsed2.sequences[0].nodes[1].name} (${parsed2.sequences[0].nodes[1].type})</pre>`);

        // Test 3: Serialize without nodes
        const serialized1 = timelineFormat.serialize(parsed1.sequences, 120);
        const test3Pass = serialized1.includes('EFFECT + Hybrid3D source -> temp1') &&
                         !serialized1.includes('NODE');
        addTest('Serialize timeline without NODE declarations', test3Pass,
            `<pre>${serialized1}</pre>`);

        // Test 4: Serialize with nodes
        const serialized2 = timelineFormat.serialize(parsed2.sequences, 120);
        const test4Pass = serialized2.includes('NODE temp1 u8x4_norm') &&
                         serialized2.includes('NODE temp2 f32x4');
        addTest('Serialize timeline with NODE declarations', test4Pass,
            `<pre>${serialized2}</pre>`);

        // Test 5: Validation (no nodes - should pass)
        const errors1 = timelineFormat.validateSequence(parsed1.sequences[0]);
        const test5Pass = errors1.length === 0;
        addTest('Validation without NODE declarations', test5Pass,
            `<pre>Errors: ${errors1.length}</pre>`);

        // Test 6: Validation (with nodes - should pass)
        const errors2 = timelineFormat.validateSequence(parsed2.sequences[0]);
        const test6Pass = errors2.length === 0;
        addTest('Validation with NODE declarations', test6Pass,
            `<pre>Errors: ${errors2.length}</pre>`);

        // Test 7: Validation failure (undeclared node)
        const invalidSeq = {
            nodes: [{ name: 'temp1', type: 'u8x4_norm' }],
            effects: [{
                className: 'Test',
                inputs: ['source'],
                outputs: ['temp2'], // temp2 not declared!
                startTime: 0,
                endTime: 4
            }]
        };
        const errors3 = timelineFormat.validateSequence(invalidSeq);
        const test7Pass = errors3.length > 0 && errors3[0].includes('temp2');
        addTest('Validation detects undeclared nodes', test7Pass,
            `<pre>Errors: ${errors3.join('\n')}</pre>`);

        // Test 8: Round-trip (parse → serialize → parse)
        const reparsed = timelineFormat.parse(serialized2, 120);
        const test8Pass = JSON.stringify(parsed2.sequences[0].nodes) ===
                         JSON.stringify(reparsed.sequences[0].nodes);
        addTest('Round-trip consistency', test8Pass,
            `<pre>Original nodes: ${JSON.stringify(parsed2.sequences[0].nodes, null, 2)}
Reparsed nodes: ${JSON.stringify(reparsed.sequences[0].nodes, null, 2)}</pre>`);

        // Summary
        const allTests = document.querySelectorAll('.test');
        const passed = document.querySelectorAll('.test.pass').length;
        const total = allTests.length;
        const summary = document.createElement('div');
        summary.style.cssText = 'margin-top: 30px; padding: 20px; background: #2d2d30; font-size: 18px;';
        summary.innerHTML = `<strong>Results: ${passed}/${total} tests passed</strong>`;
        results.appendChild(summary);
    </script>
</body>
</html>