oficina / meet Public

Clone
git clone https://amadan.net/oficina/meet

Plain git — no account needed to clone.

Download

Download this file

1import XCTest
2
3@_spi(Fixtures) @testable import CallsKit
4
5/// The warm rim (0.4.3, "T5, warm soft breath"): the four layers'
6/// opacity and blur for a level, exactly the mock-up's box-shadow recipe;
7/// its colours; when it is on; and Reduce Motion.
8final class WarmRimTests: XCTestCase {
9 private func level(db: Float) -> Float { pow(10, db / 20) }
10
11 func testTheFourLayersFollowTheLevel() {
12 for lvl in stride(from: 0.0, through: 1, by: 0.05) {
13 let rim = WarmRim(on: true, level: lvl)
14 XCTAssertEqual(rim.rimOpacity, 0.10 + lvl * 0.45, accuracy: 1e-12, "rim at \(lvl)")
15 XCTAssertEqual(rim.topOpacity, 0, "T5 has no top highlight")
16 XCTAssertEqual(rim.innerOpacity, 0.06 + lvl * 0.30, accuracy: 1e-12, "inner at \(lvl)")
17 XCTAssertEqual(rim.innerBlur, 3 + lvl * 6, accuracy: 1e-12)
18 XCTAssertEqual(rim.outerOpacity, lvl * 0.25, accuracy: 1e-12, "outer at \(lvl)")
19 XCTAssertEqual(rim.outerBlur, 1 + lvl * 7, accuracy: 1e-12)
20 }
21 // The ends, spelled out.
22 let quiet = WarmRim(on: true, level: 0)
23 XCTAssertEqual(quiet.rimOpacity, 0.10, accuracy: 1e-12)
24 XCTAssertEqual(quiet.innerOpacity, 0.06, accuracy: 1e-12)
25 XCTAssertEqual(quiet.innerBlur, 3)
26 XCTAssertEqual(quiet.outerOpacity, 0, "no breath until the voice rises")
27 XCTAssertEqual(quiet.outerBlur, 1)
28 let loud = WarmRim(on: true, level: 1)
29 XCTAssertEqual(loud.rimOpacity, 0.55, accuracy: 1e-12)
30 XCTAssertEqual(loud.innerOpacity, 0.36, accuracy: 1e-12)
31 XCTAssertEqual(loud.innerBlur, 9)
32 XCTAssertEqual(loud.outerOpacity, 0.25, accuracy: 1e-12)
33 XCTAssertEqual(loud.outerBlur, 8)
34 XCTAssertEqual(WarmRim.rimWidth, 1)
35 }
36
37 func testOffIsNothingAtAnyLevel() {
38 for lvl in [0, 0.3, 1.0] {
39 let rim = WarmRim(on: false, level: lvl)
40 XCTAssertEqual(rim.rimOpacity, 0)
41 XCTAssertEqual(rim.innerOpacity, 0)
42 XCTAssertEqual(rim.outerOpacity, 0)
43 }
44 XCTAssertEqual(WarmRim.off, WarmRim(on: false, level: 0))
45 }
46
47 func testALevelOutOfRangeIsHeldToIt() {
48 XCTAssertEqual(WarmRim(on: true, level: 2), WarmRim(on: true, level: 1))
49 XCTAssertEqual(WarmRim(on: true, level: -1), WarmRim(on: true, level: 0))
50 XCTAssertEqual(WarmRim(on: true, level: .nan), WarmRim(on: true, level: 0))
51 }
52
53 func testTheColoursAreWarmWhite() {
54 // hsl(38°, 80 %, 82 / 72 / 66 %), as Chromium computes them.
55 func check(_ l: Double, _ r: Int, _ g: Int, _ b: Int) {
56 let c = WarmRim.rgb(hue: WarmRim.hue, saturation: WarmRim.saturation, lightness: l)
57 XCTAssertEqual(Int((c.red * 255).rounded()), r, "red at \(l)")
58 XCTAssertEqual(Int((c.green * 255).rounded()), g, "green at \(l)")
59 XCTAssertEqual(Int((c.blue * 255).rounded()), b, "blue at \(l)")
60 }
61 check(WarmRim.rimLightness, 246, 219, 172)
62 check(WarmRim.innerLightness, 241, 199, 126)
63 check(WarmRim.outerLightness, 238, 187, 99)
64 XCTAssertEqual(WarmRim.hue, 38)
65 XCTAssertEqual(WarmRim.saturation, 0.80)
66 }
67
68 func testSpeechTurnsItOnAndItFadesOut() {
69 var glow = SpeakingGlow()
70 XCTAssertEqual(glow.rim(at: 0), .off, "silence: nothing")
71 let loud = level(db: -12)
72 glow.update(level: loud, speaking: true, now: 0)
73 XCTAssertFalse(glow.on(at: 0), "not before the onset")
74 glow.update(level: loud, speaking: true, now: 0.2)
75 XCTAssertTrue(glow.on(at: 0.2))
76 // Lit, the level rises over the attack from nothing.
77 XCTAssertEqual(glow.rim(at: 0.2), WarmRim(on: true, level: 0))
78 XCTAssertEqual(glow.rim(at: 0.2 + SpeakingGlow.attack).level, 1 - exp(-1), accuracy: 1e-6)
79 XCTAssertEqual(glow.rim(at: 5).level, 1, accuracy: 1e-6)
80 // Silence: still on while the level falls over the release, and off
81 // once it is under `outBelow` — not at once.
82 glow.update(level: 0, speaking: false, now: 5)
83 XCTAssertFalse(glow.lit)
84 XCTAssertTrue(glow.on(at: 5 + SpeakingGlow.release), "fading")
85 let out = 5 + SpeakingGlow.release * log(1 / SpeakingGlow.outBelow)
86 XCTAssertTrue(glow.on(at: out - 0.01))
87 XCTAssertFalse(glow.on(at: out + 0.01))
88 let gone = glow.rim(at: out + 0.01)
89 XCTAssertFalse(gone.on)
90 XCTAssertEqual([gone.rimOpacity, gone.innerOpacity, gone.outerOpacity], [0, 0, 0], "nothing drawn")
91 XCTAssertLessThan(out, glow.settles, "and the view is still redrawing when it goes off")
92 }
93
94 func testReduceMotionIsASteadyRimAtPointSix() {
95 var glow = SpeakingGlow()
96 XCTAssertEqual(glow.rim(at: 0, reduceMotion: true), .off)
97 glow.update(level: level(db: -10), speaking: true, now: 0)
98 glow.update(level: level(db: -10), speaking: true, now: 0.2)
99 for (db, t) in [(-10, 0.3), (-35, 0.4), (-20, 0.5)] as [(Float, Double)] {
100 glow.update(level: level(db: db), speaking: true, now: t)
101 for dt in [0, 0.01, 0.1, 1.0] {
102 XCTAssertEqual(glow.rim(at: t + dt, reduceMotion: true), WarmRim(on: true, level: 0.6))
103 }
104 }
105 XCTAssertEqual(SpeakingGlow.steady, 0.6)
106 // Out at once, with no fade.
107 glow.update(level: 0, speaking: false, now: 1)
108 XCTAssertEqual(glow.rim(at: 1, reduceMotion: true), .off)
109 }
110
111 func testTheFixturesLevels() {
112 XCTAssertEqual(SpeakingGlow.fixture(level: level(db: FixtureSpeaking.silent.decibels)).rim(at: 0), .off)
113 let expected: [(FixtureSpeaking, Double)] = [(.low, 13.0 / 30), (.mid, 21.0 / 30), (.high, 1)]
114 for (fixture, lvl) in expected {
115 let rim = SpeakingGlow.fixture(level: level(db: fixture.decibels)).rim(at: 0)
116 XCTAssertTrue(rim.on, "\(fixture)")
117 XCTAssertEqual(rim.level, lvl, accuracy: 1e-5, "\(fixture)")
118 }
119 }
120}
121