1 /*
<lambda>null2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package platform.test.motion.truth
18 
19 import androidx.test.ext.junit.runners.AndroidJUnit4
20 import com.google.common.truth.Correspondence
21 import com.google.common.truth.ExpectFailure
22 import com.google.common.truth.TruthFailureSubject
23 import org.junit.Test
24 import org.junit.runner.RunWith
25 import platform.test.motion.golden.Feature
26 import platform.test.motion.golden.SupplementalFrameId
27 import platform.test.motion.golden.TimeSeries
28 import platform.test.motion.golden.TimestampFrameId
29 import platform.test.motion.golden.asDataPoint
30 import platform.test.motion.truth.TimeSeriesSubject.Companion.assertThat
31 
32 @RunWith(AndroidJUnit4::class)
33 class TimeSeriesSubjectTest {
34 
35     @Test
36     fun isEqualTo_nonTimeSeriesObject_usesDefaultImplementation() {
37         with(assertThrows { assertThat(TimeSeries(listOf(), emptyList())).isEqualTo("foo") }) {
38             factValue("expected").isEqualTo("foo")
39             factValue("but was").isEqualTo("TimeSeries(frameIds=[], features={})")
40         }
41     }
42 
43     @Test
44     fun isEqualTo_matchingTimeSeries() {
45         val timeSeries = createTimeSeries(2)
46 
47         assertThat(timeSeries).isEqualTo(timeSeries.copy())
48     }
49 
50     @Test
51     fun isEqualTo_actualHasDifferentFrameTimes() {
52         val expected = createTimeSeries(2)
53         val actual =
54             expected.copy(frameIds = listOf(expected.frameIds[0], SupplementalFrameId("x")))
55 
56         with(assertThrows { assertThat(actual).isEqualTo(expected) }) {
57             factKeys().contains("TimeSeries.frames does not match")
58             factValue("|  expected").isEqualTo("[0ms, 1ms]")
59             factValue("|  but got").isEqualTo("[0ms, x]")
60             factValue("|  unexpected (1)").isEqualTo("[x]")
61             factValue("|  missing (1)").isEqualTo("[1ms]")
62 
63             factKeys().comparingElementsUsing(startsWith).doesNotContain("TimeSeries.features")
64         }
65     }
66 
67     @Test
68     fun isEqualTo_missingFrame() {
69         val expected = createTimeSeries(3)
70         val actual = createTimeSeries(2)
71 
72         with(assertThrows { assertThat(actual).isEqualTo(expected) }) {
73             factKeys().contains("TimeSeries.frames does not match")
74 
75             factValue("|  expected").isEqualTo("[0ms, 1ms, 2ms]")
76             factValue("|  but got").isEqualTo("[0ms, 1ms]")
77             factValue("|  missing (1)").isEqualTo("[2ms]")
78 
79             factKeys().comparingElementsUsing(startsWith).doesNotContain("TimeSeries.features")
80         }
81     }
82 
83     @Test
84     fun isEqualTo_additionalFrame() {
85         val expected = createTimeSeries(1)
86         val actual = createTimeSeries(2)
87 
88         with(assertThrows { assertThat(actual).isEqualTo(expected) }) {
89             factKeys().contains("TimeSeries.frames does not match")
90 
91             factValue("|  expected").isEqualTo("[0ms]")
92             factValue("|  but got").isEqualTo("[0ms, 1ms]")
93             factValue("|  unexpected (1)").isEqualTo("[1ms]")
94 
95             factKeys().comparingElementsUsing(startsWith).doesNotContain("TimeSeries.features")
96         }
97     }
98 
99     @Test
100     fun isEqualTo_missingFeature() {
101         val expected = createTimeSeries(2)
102         val actual =
103             expected.copy(
104                 features =
105                     buildMap {
106                         putAll(expected.features)
107                         remove("bar")
108                     }
109             )
110 
111         with(assertThrows { assertThat(actual).isEqualTo(expected) }) {
112             factKeys().contains("TimeSeries.features does not match")
113 
114             factValue("|  missing (1)").isEqualTo("[bar]")
115         }
116     }
117 
118     @Test
119     fun isEqualTo_additionalFeature() {
120         val expected = createTimeSeries(2)
121         val actual =
122             expected.copy(
123                 features =
124                     buildMap {
125                         putAll(expected.features)
126                         put("baz", createIntFeature("baz", 2))
127                     }
128             )
129 
130         with(assertThrows { assertThat(actual).isEqualTo(expected) }) {
131             factKeys().contains("TimeSeries.features does not match")
132 
133             factValue("|  unexpected (1)").isEqualTo("[baz]")
134         }
135     }
136 
137     @Test
138     fun isEqualTo_actualHasDifferentDataPoint() {
139         val expected =
140             TimeSeries(
141                 createFrames(2),
142                 listOf(Feature("foo", listOf(1.asDataPoint(), 2.asDataPoint())))
143             )
144         val actual =
145             TimeSeries(
146                 createFrames(2),
147                 listOf(Feature("foo", listOf(1.asDataPoint(), 3.asDataPoint())))
148             )
149 
150         with(assertThrows { assertThat(actual).isEqualTo(expected) }) {
151             factKeys()
152                 .containsExactly(
153                     "TimeSeries.features[foo].dataPoints do not match",
154                     "|  @1ms",
155                     "|    expected",
156                     "|    but was"
157                 )
158                 .inOrder()
159 
160             factValue("|    expected").isEqualTo("2 (int)")
161             factValue("|    but was").isEqualTo("3 (int)")
162         }
163     }
164 
165     private fun createTimeSeries(frameCount: Int) =
166         TimeSeries(
167             createFrames(frameCount),
168             listOf(createIntFeature("foo", frameCount), createStringFeature("bar", frameCount))
169         )
170 
171     private fun createFrames(frameCount: Int) = List(frameCount) { TimestampFrameId(it.toLong()) }
172     private fun createIntFeature(name: String, dataPoints: Int) =
173         Feature(name, List(dataPoints) { it.asDataPoint() })
174     private fun createStringFeature(name: String, dataPoints: Int) =
175         Feature(name, List(dataPoints) { it.toString().asDataPoint() })
176 
177     private inline fun assertThrows(body: () -> Unit): TruthFailureSubject {
178         try {
179             body()
180         } catch (e: Throwable) {
181             if (e is AssertionError) {
182                 return ExpectFailure.assertThat(e)
183             }
184             throw e
185         }
186         throw AssertionError("Body completed successfully. Expected AssertionError")
187     }
188     companion object {
189         val startsWith: Correspondence<String, String> =
190             Correspondence.from(
191                 { actual, expected -> checkNotNull(actual).startsWith(checkNotNull(expected)) },
192                 "starts with"
193             )
194     }
195 }
196