xref: /aosp_15_r20/platform_testing/libraries/motion/src/platform/test/motion/golden/DataPoint.kt (revision dd0948b35e70be4c0246aabd6c72554a5eb8b22a)
1 /*
2  * 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.golden
18 
19 import org.json.JSONException
20 import org.json.JSONObject
21 
22 /**
23  * Describes a type safe data point of [T] in a motion test [TimeSeries].
24  *
25  * [DataPoint]s include a [DataPointType] that specified how a value is written to / read from a
26  * JSON golden file. Additionally, some stand-in values such as [NotFoundDataPoint] and
27  * [NullDataPoint] allow to describe why [DataPoint] are absent in [TimeSeries], making the golden
28  * tests more robust.
29  */
30 sealed interface DataPoint<out T> {
31 
asJsonnull32     fun asJson(): Any
33 
34     companion object {
35         fun <T> of(value: T?, type: DataPointType<T>): DataPoint<T> {
36             return if (value != null) {
37                 ValueDataPoint(type.ensureImmutable(value), type)
38             } else {
39                 nullValue()
40             }
41         }
42 
43         fun <T> notFound(): DataPoint<T> {
44             @Suppress("UNCHECKED_CAST")
45             return NotFoundDataPoint.instance as NotFoundDataPoint<T>
46         }
47 
48         fun <T> nullValue(): DataPoint<T> {
49             @Suppress("UNCHECKED_CAST")
50             return NullDataPoint.instance as NullDataPoint<T>
51         }
52 
53         fun <T> unknownType(): DataPoint<T> {
54             @Suppress("UNCHECKED_CAST")
55             return UnknownType.instance as UnknownType<T>
56         }
57     }
58 }
59 
60 /**
61  * Wraps a non-`null` data point value.
62  *
63  * @see DataPoint.of
64  */
65 data class ValueDataPoint<T> internal constructor(val value: T & Any, val type: DataPointType<T>) :
<lambda>null66     DataPoint<T> {
67     override fun asJson() = type.toJson(this.value)
68 
69     override fun toString(): String = "$value (${type.typeName})"
70 }
71 
72 /**
73  * [DataPoint] stand-in to represent `null` data point values.
74  *
75  * @see DataPoint.of
76  * @see DataPoint.nullValue
77  */
78 class NullDataPoint<T> private constructor() : DataPoint<T> {
79 
asJsonnull80     override fun asJson() = JSONObject.NULL
81 
82     companion object {
83         internal val instance = NullDataPoint<Any>()
84 
85         fun isNullValue(jsonValue: Any): Boolean {
86             return jsonValue == JSONObject.NULL
87         }
88     }
89 
toStringnull90     override fun toString(): String = "null"
91 }
92 
93 /**
94  * [DataPoint] stand-in to represent data points that could not be sampled.
95  *
96  * This is usually the case when the subject to sample from did not exist in a specific frame in the
97  * first place.
98  *
99  * @see DataPoint.notFound
100  */
101 class NotFoundDataPoint<T> private constructor() : DataPoint<T> {
102 
103     override fun asJson() = JSONObject().apply { put("type", "not_found") }
104 
105     override fun toString(): String = "{{not_found}}"
106 
107     companion object {
108         internal val instance = NotFoundDataPoint<Any>()
109 
110         fun isNotFoundValue(jsonValue: Any): Boolean {
111             return jsonValue is JSONObject &&
112                 jsonValue.has("type") &&
113                 jsonValue.getString("type") == "not_found"
114         }
115     }
116 }
117 
118 /** [DataPoint] type indicating that a values was not readable during de-serialization. */
119 class UnknownType<T> private constructor() : DataPoint<T> {
120 
asJsonnull121     override fun asJson() = throw JSONException("Feature must not contain UnknownDataPoints")
122 
123     override fun toString(): String = "{{unknown_type}}"
124 
125     companion object {
126         internal val instance = UnknownType<Any>()
127     }
128 }
129