go-hep.org/x/hep@v0.38.1/groot/rtree/rvar_test.go (about) 1 // Copyright ©2020 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package rtree 6 7 import ( 8 "reflect" 9 "testing" 10 11 "go-hep.org/x/hep/groot/riofs" 12 "go-hep.org/x/hep/groot/root" 13 ) 14 15 func TestNewReadVars(t *testing.T) { 16 f, err := riofs.Open("../testdata/leaves.root") 17 if err != nil { 18 t.Fatal(err) 19 } 20 defer f.Close() 21 22 o, err := f.Get("tree") 23 if err != nil { 24 t.Fatal(err) 25 } 26 27 tree := o.(Tree) 28 29 vars := NewReadVars(tree) 30 want := []ReadVar{ 31 {Name: "B", Leaf: "B", Value: new(bool)}, 32 {Name: "Str", Leaf: "Str", Value: new(string)}, 33 {Name: "I8", Leaf: "I8", Value: new(int8)}, 34 {Name: "I16", Leaf: "I16", Value: new(int16)}, 35 {Name: "I32", Leaf: "I32", Value: new(int32)}, 36 {Name: "I64", Leaf: "I64", Value: new(int64)}, 37 {Name: "G64", Leaf: "G64", Value: new(int64)}, 38 {Name: "U8", Leaf: "U8", Value: new(uint8)}, 39 {Name: "U16", Leaf: "U16", Value: new(uint16)}, 40 {Name: "U32", Leaf: "U32", Value: new(uint32)}, 41 {Name: "U64", Leaf: "U64", Value: new(uint64)}, 42 {Name: "UGG", Leaf: "UGG", Value: new(uint64)}, 43 {Name: "F32", Leaf: "F32", Value: new(float32)}, 44 {Name: "F64", Leaf: "F64", Value: new(float64)}, 45 {Name: "D16", Leaf: "D16", Value: new(root.Float16)}, 46 {Name: "D32", Leaf: "D32", Value: new(root.Double32)}, 47 // arrays 48 {Name: "ArrBs", Leaf: "ArrBs", Value: new([10]bool)}, 49 {Name: "ArrI8", Leaf: "ArrI8", Value: new([10]int8)}, 50 {Name: "ArrI16", Leaf: "ArrI16", Value: new([10]int16)}, 51 {Name: "ArrI32", Leaf: "ArrI32", Value: new([10]int32)}, 52 {Name: "ArrI64", Leaf: "ArrI64", Value: new([10]int64)}, 53 {Name: "ArrG64", Leaf: "ArrG64", Value: new([10]int64)}, 54 {Name: "ArrU8", Leaf: "ArrU8", Value: new([10]uint8)}, 55 {Name: "ArrU16", Leaf: "ArrU16", Value: new([10]uint16)}, 56 {Name: "ArrU32", Leaf: "ArrU32", Value: new([10]uint32)}, 57 {Name: "ArrU64", Leaf: "ArrU64", Value: new([10]uint64)}, 58 {Name: "ArrUGG", Leaf: "ArrUGG", Value: new([10]uint64)}, 59 {Name: "ArrF32", Leaf: "ArrF32", Value: new([10]float32)}, 60 {Name: "ArrF64", Leaf: "ArrF64", Value: new([10]float64)}, 61 {Name: "ArrD16", Leaf: "ArrD16", Value: new([10]root.Float16)}, 62 {Name: "ArrD32", Leaf: "ArrD32", Value: new([10]root.Double32)}, 63 // slices 64 {Name: "N", Leaf: "N", Value: new(int32)}, 65 {Name: "SliBs", Leaf: "SliBs", Value: new([]bool)}, 66 {Name: "SliI8", Leaf: "SliI8", Value: new([]int8)}, 67 {Name: "SliI16", Leaf: "SliI16", Value: new([]int16)}, 68 {Name: "SliI32", Leaf: "SliI32", Value: new([]int32)}, 69 {Name: "SliI64", Leaf: "SliI64", Value: new([]int64)}, 70 {Name: "SliG64", Leaf: "SliG64", Value: new([]int64)}, 71 {Name: "SliU8", Leaf: "SliU8", Value: new([]uint8)}, 72 {Name: "SliU16", Leaf: "SliU16", Value: new([]uint16)}, 73 {Name: "SliU32", Leaf: "SliU32", Value: new([]uint32)}, 74 {Name: "SliU64", Leaf: "SliU64", Value: new([]uint64)}, 75 {Name: "SliUGG", Leaf: "SliUGG", Value: new([]uint64)}, 76 {Name: "SliF32", Leaf: "SliF32", Value: new([]float32)}, 77 {Name: "SliF64", Leaf: "SliF64", Value: new([]float64)}, 78 {Name: "SliD16", Leaf: "SliD16", Value: new([]root.Float16)}, 79 {Name: "SliD32", Leaf: "SliD32", Value: new([]root.Double32)}, 80 } 81 82 n := min(len(vars), len(want)) 83 84 for i := range n { 85 got := vars[i] 86 if got.Name != want[i].Name { 87 t.Fatalf("invalid read-var name[%d]: got=%q, want=%q", i, got.Name, want[i].Name) 88 } 89 if got.Leaf != want[i].Leaf { 90 t.Fatalf("invalid read-var (name=%q) leaf-name[%d]: got=%q, want=%q", got.Name, i, got.Leaf, want[i].Leaf) 91 } 92 if got, want := reflect.TypeOf(got.Value), reflect.TypeOf(want[i].Value); got != want { 93 t.Fatalf("invalid read-var (name=%q) type[%d]: got=%v, want=%v", vars[i].Name, i, got, want) 94 } 95 } 96 97 if len(want) != len(vars) { 98 t.Fatalf("invalid lengths. got=%d, want=%d", len(vars), len(want)) 99 } 100 } 101 102 func TestReadVarsFromStruct(t *testing.T) { 103 for _, tc := range []struct { 104 name string 105 ptr any 106 want []ReadVar 107 panics string 108 }{ 109 { 110 name: "not-ptr", 111 ptr: struct { 112 I32 int32 113 }{}, 114 panics: "rtree: expect a pointer value, got struct { I32 int32 }", 115 }, 116 { 117 name: "not-ptr-to-struct", 118 ptr: new(int32), 119 panics: "rtree: expect a pointer to struct value, got *int32", 120 }, 121 { 122 name: "struct-with-int", 123 ptr: &struct { 124 I32 int 125 F32 float32 126 Str string 127 }{}, 128 panics: "rtree: invalid field type for \"I32\": int", 129 }, 130 { 131 name: "struct-with-map", // FIXME(sbinet) 132 ptr: &struct { 133 Map map[int32]string 134 }{}, 135 panics: "rtree: invalid field type for \"Map\": map[int32]string (not yet supported)", 136 }, 137 { 138 name: "invalid-struct-tag", 139 ptr: &struct { 140 N int32 `groot:"N[42]"` 141 }{}, 142 panics: "rtree: invalid field type for \"N\", or invalid struct-tag \"N[42]\": int32", 143 }, 144 { 145 name: "simple", 146 ptr: &struct { 147 I32 int32 148 F32 float32 149 Str string 150 }{}, 151 want: []ReadVar{{Name: "I32"}, {Name: "F32"}, {Name: "Str"}}, 152 }, 153 { 154 name: "simple-with-unexported", 155 ptr: &struct { 156 I32 int32 157 F32 float32 158 val float32 159 Str string 160 }{}, 161 want: []ReadVar{{Name: "I32"}, {Name: "F32"}, {Name: "Str"}}, 162 }, 163 { 164 name: "slices", 165 ptr: &struct { 166 N int32 167 NN int64 168 SliF32 []float32 `groot:"F32s[N]"` 169 SliF64 []float64 `groot:"F64s[NN]"` 170 }{}, 171 want: []ReadVar{ 172 {Name: "N"}, 173 {Name: "NN"}, 174 {Name: "F32s", count: "N"}, 175 {Name: "F64s", count: "NN"}, 176 }, 177 }, 178 { 179 name: "slices-no-count", 180 ptr: &struct { 181 F1 int32 182 F2 []float32 `groot:"F2[F1]"` 183 X3 []float64 `groot:"F3"` 184 F4 []float64 185 }{}, 186 want: []ReadVar{ 187 {Name: "F1"}, 188 {Name: "F2", count: "F1"}, 189 {Name: "F3"}, 190 {Name: "F4"}, 191 }, 192 }, 193 { 194 name: "arrays", 195 ptr: &struct { 196 N int32 `groot:"n"` 197 Arr01 [10]float64 198 Arr02 [10][10]float64 199 Arr03 [10][10][10]float64 200 Arr11 [10]float64 `groot:"arr11[10]"` 201 Arr12 [10][10]float64 `groot:"arr12[10][10]"` 202 Arr13 [10][10][10]float64 `groot:"arr13[10][10][10]"` 203 Arr14 [10][10][10]float64 `groot:"arr14"` 204 }{}, 205 want: []ReadVar{ 206 {Name: "n"}, 207 {Name: "Arr01"}, 208 {Name: "Arr02"}, 209 {Name: "Arr03"}, 210 {Name: "arr11"}, 211 {Name: "arr12"}, 212 {Name: "arr13"}, 213 {Name: "arr14"}, 214 }, 215 }, 216 { 217 name: "struct-with-struct", 218 ptr: &struct { 219 F1 int64 220 F2 struct { 221 FF1 int64 222 FF2 float64 223 FF3 struct { 224 FFF1 float64 225 } 226 } 227 }{}, 228 want: []ReadVar{ 229 {Name: "F1"}, 230 {Name: "F2"}, 231 }, 232 }, 233 { 234 name: "struct-with-struct+slice", 235 ptr: &struct { 236 F1 int64 237 F2 struct { 238 FF1 int64 239 FF2 float64 240 FF3 []float64 241 FF4 []struct { 242 FFF1 float64 243 FFF2 []float64 244 } 245 } 246 }{}, 247 want: []ReadVar{ 248 {Name: "F1"}, 249 {Name: "F2"}, 250 }, 251 }, 252 { 253 name: "invalid-slice-tag", 254 ptr: &struct { 255 N int32 256 Sli []int32 `groot:"vs[N][N]"` 257 }{}, 258 panics: "rtree: invalid number of slice-dimensions for field \"Sli\": \"vs[N][N]\"", 259 }, 260 { 261 name: "invalid-array-tag", 262 ptr: &struct { 263 N int32 264 Arr [24]int32 `groot:"vs[1][2][3][4]"` 265 }{}, 266 panics: "rtree: invalid number of array-dimension for field \"Arr\": \"vs[1][2][3][4]\"", 267 }, 268 { 269 name: "invalid-array-tag-dimensions", 270 ptr: &struct { 271 N int32 272 Arr [7]int32 `groot:"vs[1][2][x]"` 273 }{}, 274 panics: "rtree: could not infer dimensions from \"vs[1][2][x]\": strconv.Atoi: parsing \"x\": invalid syntax", 275 }, 276 { 277 name: "invalid-array-tag-capacity", 278 ptr: &struct { 279 N int32 280 Arr [7]int32 `groot:"vs[1][2][3]"` 281 }{}, 282 panics: "rtree: field type dimension inconsistency: groot-tag=\"vs[1][2][3]\" vs go-type=[7]int32: 6 vs 7", 283 }, 284 } { 285 t.Run(tc.name, func(t *testing.T) { 286 if tc.panics != "" { 287 defer func() { 288 err := recover() 289 if err == nil { 290 t.Fatalf("expected a panic") 291 } 292 if got, want := err.(error).Error(), tc.panics; got != want { 293 t.Fatalf("invalid panic message:\ngot= %q\nwant=%q", got, want) 294 } 295 }() 296 } 297 got := ReadVarsFromStruct(tc.ptr) 298 if got, want := len(got), len(tc.want); got != want { 299 t.Fatalf("invalid number of rvars: got=%d, want=%d", got, want) 300 } 301 for i := range got { 302 if got, want := got[i].Name, tc.want[i].Name; got != want { 303 t.Fatalf("invalid name for rvar[%d]: got=%q, want=%q", i, got, want) 304 } 305 if got, want := got[i].count, tc.want[i].count; got != want { 306 t.Fatalf("invalid count for rvar[%d]: got=%q, want=%q", i, got, want) 307 } 308 } 309 }) 310 } 311 } 312 313 func TestNameOf(t *testing.T) { 314 for _, tc := range []struct { 315 ptr any 316 want string 317 }{ 318 { 319 ptr: &struct { 320 F int64 `groot:"field"` 321 }{}, 322 want: "field", 323 }, 324 { 325 ptr: &struct { 326 F [1]int64 `groot:"field[1]"` 327 }{}, 328 want: "field[1]", 329 }, 330 { 331 ptr: &struct { 332 F [1]int64 `groot:"field"` 333 }{}, 334 want: "field[1]", 335 }, 336 { 337 ptr: &struct { 338 F [1][2][3]int64 `groot:"field"` 339 }{}, 340 want: "field[1][2][3]", 341 }, 342 { 343 ptr: &struct { 344 F [1][2][3]int64 `groot:"field[6]"` 345 }{}, 346 want: "field[6]", 347 }, 348 { 349 ptr: &struct { 350 F []int64 `groot:"field"` 351 }{}, 352 want: "field", 353 }, 354 { 355 ptr: &struct { 356 N int32 `groot:"N"` 357 F []int64 `groot:"field[N]"` 358 }{}, 359 want: "field[N]", 360 }, 361 } { 362 t.Run("", func(t *testing.T) { 363 rt := reflect.TypeOf(tc.ptr).Elem() 364 field, ok := rt.FieldByName("F") 365 if !ok { 366 t.Fatalf("could not retrieve field named \"F\" for %T", tc.ptr) 367 } 368 got := nameOf(field) 369 if got, want := got, tc.want; got != want { 370 t.Fatalf("invalid name: got=%q, want=%q", got, want) 371 } 372 }) 373 } 374 }