go-hep.org/x/hep@v0.38.1/groot/rtree/chain_test.go (about) 1 // Copyright ©2018 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_test 6 7 import ( 8 "fmt" 9 "reflect" 10 "testing" 11 12 "go-hep.org/x/hep/groot/internal/rtests" 13 "go-hep.org/x/hep/groot/riofs" 14 _ "go-hep.org/x/hep/groot/riofs/plugin/xrootd" 15 "go-hep.org/x/hep/groot/rtree" 16 ) 17 18 func TestChain(t *testing.T) { 19 for _, tc := range []struct { 20 fnames []string 21 entries int64 22 name string 23 title string 24 brs []string 25 brOK string 26 brNOT string 27 lvs []string 28 lvOK string 29 lvNOT string 30 }{ 31 { 32 fnames: nil, 33 entries: 0, 34 name: "", 35 title: "", 36 }, 37 { 38 fnames: []string{"../testdata/chain.1.root"}, 39 entries: 10, 40 name: "tree", 41 title: "my tree title", 42 brs: []string{"evt"}, 43 brOK: "evt", 44 brNOT: "foo", 45 lvs: []string{"evt"}, 46 lvOK: "evt", 47 lvNOT: "foo", 48 }, 49 { 50 fnames: []string{rtests.XrdRemote("testdata/chain.1.root")}, 51 entries: 10, 52 name: "tree", 53 title: "my tree title", 54 brs: []string{"evt"}, 55 brOK: "evt", 56 brNOT: "foo", 57 lvs: []string{"evt"}, 58 lvOK: "evt", 59 lvNOT: "foo", 60 }, 61 { 62 // twice the same tree 63 fnames: []string{"../testdata/chain.1.root", "../testdata/chain.1.root"}, 64 entries: 20, 65 name: "tree", 66 title: "my tree title", 67 brs: []string{"evt"}, 68 brOK: "evt", 69 brNOT: "foo", 70 lvs: []string{"evt"}, 71 lvOK: "evt", 72 lvNOT: "foo", 73 }, 74 { 75 // twice the same tree 76 fnames: []string{ 77 rtests.XrdRemote("testdata/chain.1.root"), 78 rtests.XrdRemote("testdata/chain.1.root"), 79 }, 80 entries: 20, 81 name: "tree", 82 title: "my tree title", 83 brs: []string{"evt"}, 84 brOK: "evt", 85 brNOT: "foo", 86 lvs: []string{"evt"}, 87 lvOK: "evt", 88 lvNOT: "foo", 89 }, 90 { 91 // two different trees (with the same schema) 92 fnames: []string{"../testdata/chain.1.root", "../testdata/chain.2.root"}, 93 entries: 20, 94 name: "tree", 95 title: "my tree title", 96 brs: []string{"evt"}, 97 brOK: "evt", 98 brNOT: "foo", 99 lvs: []string{"evt"}, 100 lvOK: "evt", 101 lvNOT: "foo", 102 }, 103 { 104 // two different trees (with the same schema) 105 fnames: []string{ 106 rtests.XrdRemote("testdata/chain.1.root"), 107 rtests.XrdRemote("testdata/chain.2.root"), 108 }, 109 entries: 20, 110 name: "tree", 111 title: "my tree title", 112 brs: []string{"evt"}, 113 brOK: "evt", 114 brNOT: "foo", 115 lvs: []string{"evt"}, 116 lvOK: "evt", 117 lvNOT: "foo", 118 }, 119 { 120 // two different (flat) trees (with the same schema) 121 fnames: []string{ 122 "../testdata/chain.flat.1.root", 123 "../testdata/chain.flat.2.root", 124 }, 125 entries: 10, 126 name: "tree", 127 title: "my tree title", 128 brs: []string{ 129 "B", 130 "Str", 131 "I8", "I16", "I32", "I64", 132 "U8", "U16", "U32", "U64", 133 "F32", "F64", 134 "ArrBs", 135 "ArrI8", "ArrI16", "ArrI32", "ArrI64", 136 "ArrU8", "ArrU16", "ArrU32", "ArrU64", 137 "ArrF32", "ArrF64", 138 "N", 139 "SliBs", 140 "SliI8", "SliI16", "SliI32", "SliI64", 141 "SliU8", "SliU16", "SliU32", "SliU64", 142 "SliF32", "SliF64", 143 }, 144 brOK: "N", 145 brNOT: "foo", 146 lvs: []string{ 147 "B", 148 "Str", 149 "I8", "I16", "I32", "I64", 150 "U8", "U16", "U32", "U64", 151 "F32", "F64", 152 "ArrBs", 153 "ArrI8", "ArrI16", "ArrI32", "ArrI64", 154 "ArrU8", "ArrU16", "ArrU32", "ArrU64", 155 "ArrF32", "ArrF64", 156 "N", 157 "SliBs", 158 "SliI8", "SliI16", "SliI32", "SliI64", 159 "SliU8", "SliU16", "SliU32", "SliU64", 160 "SliF32", "SliF64", 161 }, 162 lvOK: "N", 163 lvNOT: "foo", 164 }, 165 // TODO(sbinet): add a test with 2 trees with different schemas) 166 } { 167 t.Run("", func(t *testing.T) { 168 files := make([]*riofs.File, len(tc.fnames)) 169 trees := make([]rtree.Tree, len(tc.fnames)) 170 for i, fname := range tc.fnames { 171 f, err := riofs.Open(fname) 172 if err != nil { 173 t.Fatalf("could not open ROOT file %q: %v", fname, err) 174 } 175 defer f.Close() 176 files[i] = f 177 178 obj, err := f.Get(tc.name) 179 if err != nil { 180 t.Fatal(err) 181 } 182 183 trees[i] = obj.(rtree.Tree) 184 } 185 186 chain := rtree.Chain(trees...) 187 188 if got, want := chain.Class(), "TChain"; got != want { 189 t.Fatalf("class name differ\ngot = %q, want= %q", got, want) 190 } 191 if got, want := chain.Name(), tc.name; got != want { 192 t.Fatalf("names differ\ngot = %q, want= %q", got, want) 193 } 194 if got, want := chain.Title(), tc.title; got != want { 195 t.Fatalf("titles differ\ngot = %q, want= %q", got, want) 196 } 197 if got, want := chain.Entries(), tc.entries; got != want { 198 t.Fatalf("titles differ\ngot = %v, want= %v", got, want) 199 } 200 { 201 brs := chain.Branches() 202 n := min(len(brs), len(tc.brs)) 203 204 for i := range n { 205 if got, want := brs[i].Name(), tc.brs[i]; got != want { 206 t.Fatalf("invalid branch name[%d]: got=%q, want=%q", i, got, want) 207 } 208 } 209 210 if got, want := len(brs), len(tc.brs); got != want { 211 t.Fatalf("invalid number of branches: got=%d, want=%d", got, want) 212 } 213 214 if tc.brOK != "" { 215 br := chain.Branch(tc.brOK) 216 if br == nil { 217 t.Fatalf("could not retrieve branch %q", tc.brOK) 218 } 219 if got, want := br.Name(), tc.brOK; got != want { 220 t.Fatalf("invalid name for branch-ok: got=%q, want=%q", got, want) 221 } 222 } 223 224 br := chain.Branch(tc.brNOT) 225 if br != nil { 226 t.Fatalf("unexpected branch for branch-not (%s): got=%#v", tc.brNOT, br) 227 } 228 } 229 { 230 lvs := chain.Leaves() 231 n := min(len(lvs), len(tc.lvs)) 232 233 for i := range n { 234 if got, want := lvs[i].Name(), tc.lvs[i]; got != want { 235 t.Fatalf("invalid leaf name[%d]: got=%q, want=%q", i, got, want) 236 } 237 } 238 239 if got, want := len(lvs), len(tc.lvs); got != want { 240 t.Fatalf("invalid number of leaves: got=%d, want=%d", got, want) 241 } 242 243 if tc.lvOK != "" { 244 lv := chain.Leaf(tc.lvOK) 245 if lv == nil { 246 t.Fatalf("could not retrieve leaf %q", tc.lvOK) 247 } 248 if got, want := lv.Name(), tc.lvOK; got != want { 249 t.Fatalf("invalid name for leaf-ok: got=%q, want=%q", got, want) 250 } 251 br := lv.Branch() 252 if br == nil || br.Name() != tc.lvOK { 253 t.Fatalf("invalid leaf-branch: ptr-ok=%v", br != nil) 254 } 255 } 256 lv := chain.Leaf(tc.lvNOT) 257 if lv != nil { 258 t.Fatalf("unexpected leaf for leaf-not (%s): got=%#v", tc.lvNOT, lv) 259 } 260 } 261 }) 262 } 263 } 264 265 func TestChainOf(t *testing.T) { 266 for _, tc := range []struct { 267 fnames []string 268 entries int64 269 name string 270 title string 271 }{ 272 { 273 fnames: nil, 274 entries: 0, 275 name: "", 276 title: "", 277 }, 278 { 279 fnames: []string{"../testdata/chain.1.root"}, 280 entries: 10, 281 name: "tree", 282 title: "my tree title", 283 }, 284 { 285 fnames: []string{rtests.XrdRemote("testdata/chain.1.root")}, 286 entries: 10, 287 name: "tree", 288 title: "my tree title", 289 }, 290 { 291 // twice the same tree 292 fnames: []string{"../testdata/chain.1.root", "../testdata/chain.1.root"}, 293 entries: 20, 294 name: "tree", 295 title: "my tree title", 296 }, 297 { 298 // twice the same tree 299 fnames: []string{ 300 rtests.XrdRemote("testdata/chain.1.root"), 301 rtests.XrdRemote("testdata/chain.1.root"), 302 }, 303 entries: 20, 304 name: "tree", 305 title: "my tree title", 306 }, 307 { 308 // two different trees (with the same schema) 309 fnames: []string{"../testdata/chain.1.root", "../testdata/chain.2.root"}, 310 entries: 20, 311 name: "tree", 312 title: "my tree title", 313 }, 314 { 315 // two different trees (with the same schema) 316 fnames: []string{ 317 rtests.XrdRemote("testdata/chain.1.root"), 318 rtests.XrdRemote("testdata/chain.2.root"), 319 }, 320 entries: 20, 321 name: "tree", 322 title: "my tree title", 323 }, 324 // TODO(sbinet): add a test with 2 trees with different schemas) 325 } { 326 t.Run("", func(t *testing.T) { 327 chain, closer, err := rtree.ChainOf(tc.name, tc.fnames...) 328 if err != nil { 329 t.Fatalf("could not create chain: %v", err) 330 } 331 defer func() { 332 _ = closer() 333 }() 334 335 if got, want := chain.Name(), tc.name; got != want { 336 t.Fatalf("names differ\ngot = %q, want= %q", got, want) 337 } 338 if got, want := chain.Title(), tc.title; got != want { 339 t.Fatalf("titles differ\ngot = %q, want= %q", got, want) 340 } 341 if got, want := chain.Entries(), tc.entries; got != want { 342 t.Fatalf("titles differ\ngot = %v, want= %v", got, want) 343 } 344 }) 345 } 346 } 347 348 func TestChainReaderStruct(t *testing.T) { 349 files := []string{ 350 "../testdata/chain.1.root", 351 "../testdata/chain.2.root", 352 } 353 var total struct { 354 got, want int64 355 } 356 trees := make([]rtree.Tree, len(files)) 357 for i, fname := range files { 358 f, err := riofs.Open(fname) 359 if err != nil { 360 t.Fatalf("could not open ROOT file %q: %v", fname, err) 361 } 362 defer f.Close() 363 364 obj, err := f.Get("tree") 365 if err != nil { 366 t.Fatal(err) 367 } 368 369 trees[i] = obj.(rtree.Tree) 370 total.want += trees[i].Entries() 371 } 372 373 chain := rtree.Chain(trees...) 374 375 type Data struct { 376 Event struct { 377 Beg string `groot:"Beg"` 378 F64 float64 `groot:"F64"` 379 ArrF64 [10]float64 `groot:"ArrayF64"` 380 N int32 `groot:"N"` 381 SliF64 []float64 `groot:"SliceF64"` 382 StdStr string `groot:"StdStr"` 383 StlVecF64 []float64 `groot:"StlVecF64"` 384 StlVecStr []string `groot:"StlVecStr"` 385 End string `groot:"End"` 386 } `groot:"evt"` 387 } 388 389 want := func(i int64) (data Data) { 390 evt := &data.Event 391 evt.Beg = fmt.Sprintf("beg-%03d", i) 392 evt.F64 = float64(i) 393 for j := range evt.ArrF64 { 394 evt.ArrF64[j] = float64(i) 395 } 396 evt.N = int32(i) % 10 397 evt.StdStr = fmt.Sprintf("std-%03d", i) 398 switch i { 399 case 0: 400 evt.SliF64 = nil 401 evt.StlVecF64 = nil 402 evt.StlVecStr = nil 403 default: 404 evt.SliF64 = make([]float64, evt.N) 405 evt.StlVecF64 = make([]float64, int(evt.N)) 406 evt.StlVecStr = make([]string, int(evt.N)) 407 } 408 for ii := range int(evt.N) { 409 evt.SliF64[ii] = float64(i) 410 evt.StlVecF64[ii] = float64(i) 411 evt.StlVecStr[ii] = fmt.Sprintf("vec-%03d", i) 412 } 413 evt.End = fmt.Sprintf("end-%03d", i) 414 415 return data 416 } 417 418 var data Data 419 r, err := rtree.NewReader(chain, rtree.ReadVarsFromStruct(&data)) 420 if err != nil { 421 t.Fatal(err) 422 } 423 defer r.Close() 424 425 err = r.Read(func(ctx rtree.RCtx) error { 426 i := ctx.Entry 427 if !reflect.DeepEqual(data, want(i)) { 428 return fmt.Errorf("entry[%d]:\ngot= %#v\nwant=%#v\n", i, data, want(i)) 429 } 430 total.got++ 431 return nil 432 }) 433 if err != nil { 434 t.Fatal(err) 435 } 436 437 if total.got != total.want { 438 t.Fatalf("entries differ: got=%d want=%d", total.got, total.want) 439 } 440 }