File size: 3,864 Bytes
e033515
 
 
 
 
 
 
 
 
 
e8b2f06
 
 
 
 
e033515
 
 
 
 
 
e8b2f06
 
e033515
 
 
 
 
e8b2f06
 
 
 
e033515
e8b2f06
e033515
e8b2f06
 
e033515
 
 
 
 
 
 
 
 
e8b2f06
 
e033515
 
 
 
 
e8b2f06
e033515
 
e8b2f06
 
e033515
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e8b2f06
 
 
 
 
 
 
 
 
 
e033515
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
"""Unit tests for engine.py's pure continuation planning — the bounded-lead +
generation-buffer math. No model, no GPU: `plan_continuation` is a pure
function, so these run anywhere torch+numpy import.

Contract (post deployed-bug fix): plan_continuation(source, total) returns
(lead, new_seconds, buffer_seconds) where
  - lead   = min(source, MAX_LEAD_SECONDS)   — tail run-up fed to SA3
  - new    = extend to `total`, floored at MIN_NEW, bounded so lead+new <= cap
  - buffer = lead + new                       — the masked region is [lead, buffer]
"""
import pytest

import engine


def test_short_source_keeps_whole_clip_as_lead():
    # source shorter than the lead cap: keep the whole clip, extend to total
    lead, new, buf = engine.plan_continuation(30, 60)
    assert lead == 30                       # whole 30s clip is the lead-in
    assert new == 30                        # generate up to the 60s finished length
    assert buf == 60                        # buffer = lead + new


def test_buffer_capped_at_120():
    lead, new, buf = engine.plan_continuation(30, 200)
    assert lead == 30
    assert buf == engine.MAX_TOTAL_SECONDS == 120
    assert new == 90                        # lead(30) + new(90) == 120 cap


def test_min_new_floor_enforced():
    # asking for barely-longer-than-source still generates at least MIN_NEW
    lead, new, buf = engine.plan_continuation(30, 31)
    assert new == engine.MIN_NEW_SECONDS == 5
    assert lead == 30 and buf == 35


def test_long_source_clamps_lead_and_still_extends():
    # the deployed bug: a 100s source kept the whole clip and masked ~5s, which
    # the sampler rendered as near-silence. now the lead is clamped and the
    # request still extends toward the asked-for total.
    lead, new, buf = engine.plan_continuation(100, 120)
    assert lead == engine.MAX_LEAD_SECONDS == 30   # not 100s of kept context
    assert new == 20                                # 120 - 100 of fresh audio
    assert buf == 50                                # bounded, healthy buffer
    assert buf - lead == new                        # masked span == new audio


def test_request_below_source_still_makes_real_audio():
    # total < source is contradictory (can't shorten); we still generate a
    # MIN_NEW tail rather than nothing, and the lead stays bounded.
    lead, new, buf = engine.plan_continuation(100, 60)
    assert lead == 30
    assert new == engine.MIN_NEW_SECONDS == 5
    assert buf == 35
    assert buf > lead                               # mask never inverts


def test_mask_brackets_the_new_region_across_inputs():
    for src, req in [(15, 40), (29.5, 60), (50, 90), (10, 120), (100, 110)]:
        lead, new, buf = engine.plan_continuation(src, req)
        assert lead == min(src, engine.MAX_LEAD_SECONDS)
        assert abs((buf - lead) - new) < 1e-6       # masked span == new audio
        assert buf > lead                           # never inverts
        assert new >= engine.MIN_NEW_SECONDS - 1e-6
        assert buf <= engine.MAX_TOTAL_SECONDS


def test_normal_request_reaches_requested_total():
    # for a legitimate request (total > source) the finished length is exactly
    # source + new == total: it extends to what the user asked for.
    for src, total in [(15, 40), (29.5, 60), (20, 90)]:
        _, new, _ = engine.plan_continuation(src, total)
        assert abs((src + new) - total) < 1e-6


def test_overlong_source_raises():
    # a clip longer than MAX_SOURCE_SECONDS is a finished track, not a clip
    for src in (116, 120, 125, 200):
        with pytest.raises(ValueError):
            engine.plan_continuation(src, 60)


def test_constants_match_sa3_contract():
    assert engine.SR == 44100
    assert engine.STEPS == 8
    assert engine.SAMPLER == "pingpong"
    assert engine.MAX_TOTAL_SECONDS == 120
    assert engine.MAX_LEAD_SECONDS == 30