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