import CoreGraphics import Testing @testable import SstUI struct VUScaleTests { @Test func zeroVUIsMinusTwentyDbfs() { #expect(VUScale.vu(fromDbfs: -20) == 0) #expect(VUScale.vu(fromDbfs: -40) == -20) } @Test func fractionSpansTheScale() { #expect(VUScale.fraction(forVU: -20) == 0) #expect(abs(VUScale.fraction(forVU: 3) - 1) < 1e-9) #expect(abs(VUScale.fraction(forVU: 0) - 20.0 / 23.0) < 1e-9) } @Test func fractionIsClampedAtBothEnds() { #expect(VUScale.fraction(forVU: -60) == 0) #expect(VUScale.fraction(forVU: 30) == 1.03) #expect(VUScale.fraction(forDbfs: -120) == 0) } @Test func angleIsSymmetricAroundVertical() { #expect(abs(VUScale.angle(forFraction: 0) + VUScale.angle(forFraction: 1)) < 1e-9) #expect(abs(VUScale.angle(forFraction: 0.5)) < 1e-9) #expect(abs(VUScale.angle(forFraction: 1) - 45 * .pi / 180) < 1e-9) } } struct NeedleSpringTests { func simulate(_ spring: NeedleSpring, seconds: Double, target: Double = 1) -> (final: Double, peak: Double) { var spring = spring var peak = 0.0 let dt = 1.0 / 240 for _ in 0.. 1.003) #expect(result.peak < 1.03) #expect(abs(result.final - 1) < 0.001) } @Test func criticalDampingNeverOvershoots() { let result = simulate(NeedleSpring(damping: 1), seconds: 1) #expect(result.peak <= 1.000001) } @Test func largeFrameStepsStayStable() { var spring = NeedleSpring() for _ in 0..<40 { spring.step(target: 1, dt: 0.05) } #expect(abs(spring.position - 1) < 0.001) } @Test func restsAtTheStartWithoutATarget() { var spring = NeedleSpring() for _ in 0..<60 { spring.step(target: 0, dt: 1.0 / 60) } #expect(spring.position == 0) } } struct SegmentGlyphTests { @Test func everyDigitHasASegmentPattern() { for digit in "0123456789" { #expect(!SegmentGlyph.lit(for: digit).isEmpty) } #expect(SegmentGlyph.lit(for: "8").count == 7) #expect(SegmentGlyph.lit(for: "1") == [.b, .c]) #expect(SegmentGlyph.lit(for: ":").isEmpty) } @Test func polygonsStayInsideTheDigitBoxAndDiffer() { var seen: [[CGPoint]] = [] for id in SegmentID.allCases { let polygon = SegmentGlyph.polygon(id) #expect(polygon.count == 6) for point in polygon { #expect(point.x >= 0 && point.x <= Segment.digitWidth) #expect(point.y >= 0 && point.y <= Segment.digitHeight) } #expect(!seen.contains(polygon)) seen.append(polygon) } #expect(seen.count == 7) } } struct LEDBarTests { @Test func tonesFollowTheEightThreeOneSplit() { let tones = (0..<12).map { LEDBar.tone(forSegment: $0) } #expect(tones.prefix(8).allSatisfy { $0 == .green }) #expect(tones[8...10].allSatisfy { $0 == .amber }) #expect(tones[11] == .red) } @Test func litCountScalesAndClamps() { #expect(LEDBar.litCount(forFraction: 0) == 0) #expect(LEDBar.litCount(forFraction: 0.5) == 6) #expect(LEDBar.litCount(forFraction: 1) == 12) #expect(LEDBar.litCount(forFraction: 1.03) == 12) #expect(LEDBar.litCount(forFraction: -1) == 0) } } struct SpokenLevelTests { @Test func nonFiniteInputReturnsStil() { #expect(VUScale.spokenLevel(fromDbfs: -.infinity) == "stil") #expect(VUScale.spokenLevel(fromDbfs: .nan) == "stil") } @Test func referenceLevel() { #expect(VUScale.spokenLevel(fromDbfs: -20) == "0 VU") } @Test func negativeLevel() { #expect(VUScale.spokenLevel(fromDbfs: -30) == "-9 VU") } @Test func clampedToMinimum() { #expect(VUScale.spokenLevel(fromDbfs: -120) == "-20 VU") } @Test func clampedToMaximum() { #expect(VUScale.spokenLevel(fromDbfs: 10) == "3 VU") } }