go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/data/convert_to_test.go (about) 1 // Copyright 2023 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package data 16 17 import ( 18 "bytes" 19 "testing" 20 21 "golang.org/x/exp/slices" 22 ) 23 24 // okToType checks that a type conversion succeeds. 25 func okToType[T comparable](t *testing.T, input any, expect T) { 26 t.Helper() 27 if val, ok := LosslessConvertTo[T](input); !ok || val != expect { 28 t.Errorf("%[1]T(%[1]v) - ok=%[2]t, value=%[3]T(%[3]v) | expect=%[4]v", input, ok, val, expect) 29 } 30 } 31 32 // failToType checks that a type conversion fails. 33 func failToType[T comparable](t *testing.T, input any) { 34 t.Helper() 35 if val, ok := LosslessConvertTo[T](input); ok { 36 t.Errorf("%[1]T(%[1]v) - ok=%[2]t, value=%[3]T(%[3]v)", input, ok, val) 37 } 38 } 39 40 // TestConvertToInt tests conversion to signed integral types. 41 func TestConvertToInt(t *testing.T) { 42 t.Parallel() 43 44 // The integer 100 can be represented as an int8. 45 // 46 // However, we want to allow conversions to succeed only when **any** value of the given type would be possible to convert. 47 // Only an int8 can cast losslessly to an int8. 48 failToType[int8](t, int(100)) 49 okToType[int8](t, int8(100), 100) 50 failToType[int8](t, int16(100)) 51 failToType[int8](t, int32(100)) 52 failToType[int8](t, int64(100)) 53 54 // An int8 or an int16 can cast losslessly to an int16 55 failToType[int16](t, int(100)) 56 okToType[int16](t, int8(100), 100) 57 okToType[int16](t, int16(100), 100) 58 failToType[int16](t, int32(100)) 59 failToType[int16](t, int64(100)) 60 61 // An int8, int16, or int32 can cast losslessly to an int32. 62 failToType[int32](t, int(100)) 63 okToType[int32](t, int8(100), 100) 64 okToType[int32](t, int16(100), 100) 65 okToType[int32](t, int32(100), 100) 66 failToType[int32](t, int64(100)) 67 68 // An int8, int16, int32, or int64 can cast losslessly to an int64. 69 okToType[int64](t, int(100), 100) 70 okToType[int64](t, int8(100), 100) 71 okToType[int64](t, int16(100), 100) 72 okToType[int64](t, int32(100), 100) 73 okToType[int64](t, int64(100), 100) 74 75 // Can convert uint of a smaller bit size 76 okToType[int64](t, uint16(100), 100) 77 78 // all other coversions fail 79 failToType[int](t, "no") 80 failToType[int](t, nil) 81 failToType[int](t, &struct{}{}) 82 } 83 84 // TestConvertToInt tests conversion to unsigned integral types. 85 func TestConvertToUint(t *testing.T) { 86 // only uint8 casts losslessly to uint8 87 failToType[uint8](t, uint(100)) 88 okToType[uint8](t, uint8(100), 100) 89 failToType[uint8](t, uint16(100)) 90 failToType[uint8](t, uint32(100)) 91 failToType[uint8](t, uint64(100)) 92 93 // only uint8 and uint16 casts losslessly to uint16 94 failToType[uint16](t, uint(100)) 95 okToType[uint16](t, uint8(100), 100) 96 okToType[uint16](t, uint16(100), 100) 97 failToType[uint16](t, uint32(100)) 98 failToType[uint16](t, uint64(100)) 99 100 // only uint8, uint16, and uint32 casts losslessly to uint32 101 failToType[uint32](t, uint(100)) 102 okToType[uint32](t, uint8(100), 100) 103 okToType[uint32](t, uint16(100), 100) 104 okToType[uint32](t, uint32(100), 100) 105 failToType[uint32](t, uint64(100)) 106 107 // All unsigned integral types cast losslessly to uint32 108 okToType[uint64](t, uint(100), 100) 109 okToType[uint64](t, uint8(100), 100) 110 okToType[uint64](t, uint16(100), 100) 111 okToType[uint64](t, uint32(100), 100) 112 okToType[uint64](t, uint64(100), 100) 113 114 // Can not convert int of any size to uint 115 failToType[uint64](t, int8(100)) 116 117 // all other coversions fail 118 failToType[uint](t, "no") 119 failToType[uint](t, nil) 120 failToType[uint](t, &struct{}{}) 121 } 122 123 // TestConvertToFloat tests conversions between float32 and float64. 124 func TestConvertToFloat(t *testing.T) { 125 okToType[float32](t, float32(100.0), 100) 126 okToType[float64](t, float32(100.0), 100) 127 okToType[float64](t, float64(100.0), 100) 128 failToType[float32](t, float64(100.0)) 129 130 // An int8 can be represented losslessly as a float. 131 // However, we are going to be conservative and prevent this cast. 132 // 133 // I hope this decision doesn't come back to bite us. 134 failToType[float32](t, int8(100)) 135 136 // All other coversions fail. 137 failToType[float32](t, "no") 138 failToType[float32](t, nil) 139 failToType[float32](t, &struct{}{}) 140 } 141 142 // TestConvertToComplex tests converting numbers to complex numbers. 143 func TestConvertToComplex(t *testing.T) { 144 t.Parallel() 145 146 // We allow conversions between complex64 and complex128 except for complex128->complex64. 147 okToType[complex64](t, complex64(100.0), 100) 148 okToType[complex128](t, complex64(100.0), 100) 149 okToType[complex128](t, complex128(100.0), 100) 150 failToType[complex64](t, complex128(100.0)) 151 152 // All other coversions fail. 153 failToType[complex64](t, "no") 154 failToType[complex64](t, int8(1)) 155 failToType[complex64](t, float32(1)) 156 failToType[complex64](t, nil) 157 failToType[complex64](t, &struct{}{}) 158 } 159 160 // TestConvertStrings tests conversion to strings. 161 func TestConvertStrings(t *testing.T) { 162 t.Parallel() 163 okToType[string](t, "hello", "hello") 164 okToType[string](t, []byte("hello"), "hello") 165 okToType[string](t, []rune("hello"), "hello") 166 167 fail := func(message string, ok bool, val any, input any) { 168 t.Errorf("%[1]T(%[1]v) - ok=%[2]t, value=%[3]T(%[3]v) | expect=%[4]v", message, ok, val, input) 169 } 170 171 if val, ok := LosslessConvertTo[[]byte]("hello"); !ok || !bytes.Equal(val, []byte("hello")) { 172 fail("hello", ok, val, []byte("hello")) 173 } 174 175 if val, ok := LosslessConvertTo[[]rune]("hello"); !ok || !slices.Equal(val, []rune("hello")) { 176 fail("hello", ok, val, []rune("hello")) 177 } 178 179 // reflect.Value.ConvertTo would allow this, but we do not. 180 failToType[string](t, 100) 181 } 182 183 // TestNilConversion tests conversion of nil values. 184 func TestNilConversion(t *testing.T) { 185 t.Parallel() 186 okToType[any](t, nil, nil) 187 okToType[*struct{}](t, nil, nil) 188 189 if val, ok := LosslessConvertTo[func()](nil); !ok || val != nil { 190 t.Errorf("%[1]T(%[1]v) - ok=%[2]t, value=%[3]T(%[3]v) | expect=%[4]v", "hello", ok, any(val), []byte("hello")) 191 } 192 193 } 194 195 // TestInterfaceConversion tests converting a value to an interface that it satisfies. 196 func TestInterfaceConversion(t *testing.T) { 197 t.Parallel() 198 okToType[any](t, 100, 100) 199 } 200 201 // TestDifferentConcreteTypes tests casting between different conrete types with teh same underlying representation. 202 func TestDifferentConcreteTypes(t *testing.T) { 203 t.Parallel() 204 type myString string 205 okToType[string](t, myString("hello"), "hello") 206 okToType[myString](t, "hello", "hello") 207 208 type myStruct struct{ CoolField int } 209 okToType[myStruct](t, struct{ CoolField int }{100}, myStruct{100}) 210 okToType[myStruct](t, myStruct{100}, myStruct{100}) 211 okToType[struct{ CoolField int }](t, myStruct{100}, struct{ CoolField int }{100}) 212 213 if val, ok := LosslessConvertTo[*myStruct](&struct{ CoolField int }{100}); !ok || val == nil || val.CoolField != 100 { 214 t.Error("failed to losslessly convert &struct{...} to *myStruct") 215 } 216 }