go-hep.org/x/hep@v0.38.1/groot/riofs/file_test.go (about) 1 // Copyright ©2017 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 riofs_test 6 7 import ( 8 "bytes" 9 "fmt" 10 "os" 11 "path/filepath" 12 "reflect" 13 "strings" 14 "testing" 15 "time" 16 17 "go-hep.org/x/hep/groot" 18 "go-hep.org/x/hep/groot/internal/rtests" 19 "go-hep.org/x/hep/groot/rbase" 20 "go-hep.org/x/hep/groot/rcont" 21 "go-hep.org/x/hep/groot/riofs" 22 "go-hep.org/x/hep/groot/root" 23 "go-hep.org/x/hep/groot/rtree" 24 ) 25 26 func TestFileSegmentMap(t *testing.T) { 27 f, err := groot.Open("../testdata/dirs-6.14.00.root") 28 if err != nil { 29 t.Fatalf("could not open ROOT file: %+v", err) 30 } 31 defer f.Close() 32 33 out := new(bytes.Buffer) 34 err = f.SegmentMap(out) 35 if err != nil { 36 t.Fatalf("could not run segment map: %+v", err) 37 } 38 39 got := out.String() 40 want := `20180703/110855 At:100 N=130 TFile 41 20180703/110855 At:230 N=107 TDirectory 42 20180703/110855 At:337 N=107 TDirectory 43 20180703/110855 At:444 N=107 TDirectory 44 20180703/110855 At:551 N=109 TDirectory 45 20180703/110855 At:660 N=345 TH1F CX = 2.82 46 20180703/110855 At:1005 N=90 TDirectory 47 20180703/110855 At:1095 N=100 TDirectory 48 20180703/110855 At:1195 N=51 TDirectory 49 20180703/110855 At:1246 N=51 TDirectory 50 20180703/110855 At:1297 N=196 KeysList 51 20180703/110855 At:1493 N=3845 StreamerInfo CX = 2.44 52 20180703/110855 At:5338 N=61 FreeSegments 53 20180703/110855 At:5399 N=1 END 54 ` 55 56 if got != want { 57 t.Fatalf("invalid segment map:\ngot:\n%v\nwant:\n%v\n", got, want) 58 } 59 } 60 61 func TestFileRecords(t *testing.T) { 62 f, err := groot.Open("../testdata/dirs-6.14.00.root") 63 if err != nil { 64 t.Fatalf("could not open ROOT file: %+v", err) 65 } 66 defer f.Close() 67 68 out := new(bytes.Buffer) 69 err = f.Records(out) 70 if err != nil { 71 t.Fatalf("could not run segment map: %+v", err) 72 } 73 74 got := out.String() 75 want := `=== file "../testdata/dirs-6.14.00.root" === 76 begin: 100 77 end: 5399 78 seek-free: 5338 nbytes-free=61 nfree=1 79 seek-info: 1493 nbytes-info=3845 80 === dir "dirs-6.14.00.root" @100 === 81 parent: <nil> 82 nbytes-keys: 196 83 nbytes-name: 70 84 seek-dir: 100 85 seek-parent: 0 86 seek-keys: 1297 87 class: "TFile" 88 keys: 3 89 key[0]: "dir1" 90 === key "dir1" === 91 nbytes: 107 92 keylen: 47 93 objlen: 60 94 cycle: 1 95 seek-key: 230 96 seek-pdir: 100 97 class: "TDirectoryFile" 98 parent: @100 99 === dir "dir1" @230 === 100 parent: @100 101 nbytes-keys: 100 102 nbytes-name: 47 103 seek-dir: 230 104 seek-parent: 100 105 seek-keys: 1095 106 class: "TDirectoryFile" 107 keys: 1 108 key[0]: "dir11" 109 === key "dir11" === 110 nbytes: 109 111 keylen: 49 112 objlen: 60 113 cycle: 1 114 seek-key: 551 115 seek-pdir: 230 116 class: "TDirectoryFile" 117 parent: @230 118 === dir "dir11" @551 === 119 parent: @230 120 nbytes-keys: 90 121 nbytes-name: 49 122 seek-dir: 551 123 seek-parent: 100 124 seek-keys: 1005 125 class: "TDirectoryFile" 126 keys: 1 127 key[0]: "h1" 128 === key "h1" === 129 nbytes: 345 130 keylen: 37 131 objlen: 936 132 cycle: 1 133 seek-key: 660 134 seek-pdir: 551 135 class: "TH1F" 136 parent: @551 137 key[1]: "dir2" 138 === key "dir2" === 139 nbytes: 107 140 keylen: 47 141 objlen: 60 142 cycle: 1 143 seek-key: 337 144 seek-pdir: 100 145 class: "TDirectoryFile" 146 parent: @100 147 === dir "dir2" @337 === 148 parent: @100 149 nbytes-keys: 51 150 nbytes-name: 47 151 seek-dir: 337 152 seek-parent: 100 153 seek-keys: 1195 154 class: "TDirectoryFile" 155 keys: 0 156 key[2]: "dir3" 157 === key "dir3" === 158 nbytes: 107 159 keylen: 47 160 objlen: 60 161 cycle: 1 162 seek-key: 444 163 seek-pdir: 100 164 class: "TDirectoryFile" 165 parent: @100 166 === dir "dir3" @444 === 167 parent: @100 168 nbytes-keys: 51 169 nbytes-name: 47 170 seek-dir: 444 171 seek-parent: 100 172 seek-keys: 1246 173 class: "TDirectoryFile" 174 keys: 0 175 ` 176 177 if got != want { 178 t.Fatalf("invalid segment map:\ngot:\n%v\nwant:\n%v\n", got, want) 179 } 180 } 181 182 func TestFileDirectory(t *testing.T) { 183 for _, fname := range []string{ 184 "../testdata/small-flat-tree.root", 185 rtests.XrdRemote("testdata/small-flat-tree.root"), 186 } { 187 t.Run(fname, func(t *testing.T) { 188 f, err := groot.Open(fname) 189 if err != nil { 190 t.Fatal(err.Error()) 191 } 192 defer f.Close() 193 194 for _, table := range []struct { 195 test string 196 value string 197 want string 198 }{ 199 {"Name", f.Name(), "test-small.root"}, // name when created 200 {"Title", f.Title(), "small event file"}, 201 {"Class", f.Class(), "TFile"}, 202 } { 203 if table.value != table.want { 204 t.Fatalf("%v: got=%q, want=%q", table.test, table.value, table.want) 205 } 206 } 207 208 for _, table := range []struct { 209 name string 210 want bool 211 }{ 212 {"tree", true}, 213 {"tree;0", false}, 214 {"tree;1", true}, 215 {"tree;9999", true}, 216 {"tree_nope", false}, 217 {"tree_nope;0", false}, 218 {"tree_nope;1", false}, 219 {"tree_nope;9999", false}, 220 } { 221 _, err := f.Get(table.name) 222 if (err == nil) != table.want { 223 t.Fatalf("%s: got key (err=%v). want=%v", table.name, err, table.want) 224 } 225 } 226 227 for _, table := range []struct { 228 name string 229 want string 230 }{ 231 {"tree", "TTree"}, 232 {"tree;1", "TTree"}, 233 } { 234 k, err := f.Get(table.name) 235 if err != nil { 236 t.Fatalf("%s: expected key to exist! (got %v)", table.name, err) 237 } 238 239 if k.Class() != table.want { 240 t.Fatalf("%s: got key with class=%s (want=%s)", table.name, k.Class(), table.want) 241 } 242 } 243 244 for _, table := range []struct { 245 name string 246 want string 247 }{ 248 {"tree", "tree"}, 249 {"tree;1", "tree"}, 250 } { 251 o, err := f.Get(table.name) 252 if err != nil { 253 t.Fatalf("%s: expected key to exist! (got %v)", table.name, err) 254 } 255 256 k := o.(root.Named) 257 if k.Name() != table.want { 258 t.Fatalf("%s: got key with name=%s (want=%v)", table.name, k.Name(), table.want) 259 } 260 } 261 262 for _, table := range []struct { 263 name string 264 want string 265 }{ 266 {"tree", "my tree title"}, 267 {"tree;1", "my tree title"}, 268 } { 269 o, err := f.Get(table.name) 270 if err != nil { 271 t.Fatalf("%s: expected key to exist! (got %v)", table.name, err) 272 } 273 274 k := o.(root.Named) 275 if k.Title() != table.want { 276 t.Fatalf("%s: got key with title=%s (want=%v)", table.name, k.Title(), table.want) 277 } 278 } 279 }) 280 } 281 } 282 283 func TestFileOpenStreamerInfo(t *testing.T) { 284 for _, fname := range []string{ 285 "../testdata/small-flat-tree.root", 286 "../testdata/simple.root", 287 rtests.XrdRemote("testdata/small-flat-tree.root"), 288 rtests.XrdRemote("testdata/simple.root"), 289 } { 290 f, err := groot.Open(fname) 291 if err != nil { 292 t.Errorf("error opening %q: %v\n", fname, err) 293 continue 294 } 295 defer f.Close() 296 297 _ = f.StreamerInfos() 298 } 299 } 300 301 func TestOpenEmptyFile(t *testing.T) { 302 f, err := groot.Open("../testdata/uproot/issue70.root") 303 if err != nil { 304 t.Fatal(err) 305 } 306 defer f.Close() 307 si := f.StreamerInfos() 308 if si != nil { 309 t.Fatalf("expected no StreamerInfos in empty file") 310 } 311 } 312 313 func TestCreate(t *testing.T) { 314 dir, err := os.MkdirTemp("", "riofs-") 315 if err != nil { 316 t.Fatal(err) 317 } 318 defer os.RemoveAll(dir) 319 320 for i, tc := range []struct { 321 name string 322 skip bool 323 want []rtests.ROOTer 324 }{ 325 {name: "", want: nil}, 326 { 327 name: "TObjString", 328 want: []rtests.ROOTer{rbase.NewObjString("hello")}, 329 }, 330 { 331 name: "TObjString", 332 want: []rtests.ROOTer{rbase.NewObjString("hello"), rbase.NewObjString("world")}, 333 }, 334 { 335 name: "TObjString", 336 want: func() []rtests.ROOTer { 337 var out []rtests.ROOTer 338 for _, i := range []int{0, 1, 253, 254, 255, 256, 512, 1024} { 339 str := strings.Repeat("=", i) 340 out = append(out, rbase.NewObjString(str)) 341 } 342 return out 343 }(), 344 }, 345 { 346 name: "TObject", 347 want: []rtests.ROOTer{rbase.NewObject()}, 348 }, 349 { 350 name: "TNamed", 351 want: []rtests.ROOTer{ 352 rbase.NewNamed("n0", "t0"), 353 rbase.NewNamed("n1", "t1"), 354 rbase.NewNamed("n2", "t2"), 355 }, 356 }, 357 { 358 name: "TList", 359 want: []rtests.ROOTer{rcont.NewList("list-name", []root.Object{ 360 rbase.NewNamed("n0", "t0"), 361 rbase.NewNamed("n1", "t1"), 362 rbase.NewNamed("n2", "t2"), 363 })}, 364 }, 365 { 366 name: "TArrayF", 367 want: []rtests.ROOTer{ 368 &rcont.ArrayF{Data: []float32{1, 2, 3, 4, 5, 6}}, 369 }, 370 }, 371 { 372 name: "TArrayD", 373 want: []rtests.ROOTer{ 374 &rcont.ArrayD{Data: []float64{1, 2, 3, 4, 5, 6}}, 375 }, 376 }, 377 { 378 name: "TArrays", 379 want: []rtests.ROOTer{ 380 &rcont.ArrayF{Data: []float32{1, 2, 3, 4, 5, 6}}, 381 &rcont.ArrayD{Data: []float64{1, 2, 3, 4, 5, 6}}, 382 }, 383 }, 384 } { 385 fname := filepath.Join(dir, fmt.Sprintf("out-%d.root", i)) 386 t.Run(tc.name, func(t *testing.T) { 387 if tc.skip { 388 t.Skip() 389 } 390 391 w, err := groot.Create(fname) 392 if err != nil { 393 t.Fatal(err) 394 } 395 396 for i := range tc.want { 397 var ( 398 kname = fmt.Sprintf("key-%s-%02d", tc.name, i) 399 want = tc.want[i] 400 ) 401 402 err = w.Put(kname, want) 403 if err != nil { 404 t.Fatal(err) 405 } 406 } 407 408 if got, want := len(w.Keys()), len(tc.want); got != want { 409 t.Fatalf("invalid number of keys. got=%d, want=%d", got, want) 410 } 411 412 err = w.Close() 413 if err != nil { 414 t.Fatalf("error closing file: %+v", err) 415 } 416 417 r, err := groot.Open(fname) 418 if err != nil { 419 t.Fatal(err) 420 } 421 defer r.Close() 422 423 if got, want := len(r.Keys()), len(tc.want); got != want { 424 t.Fatalf("invalid number of keys. got=%d, want=%d", got, want) 425 } 426 427 for i := range tc.want { 428 var ( 429 kname = fmt.Sprintf("key-%s-%02d", tc.name, i) 430 want = tc.want[i] 431 ) 432 433 rgot, err := r.Get(kname) 434 if err != nil { 435 t.Fatal(err) 436 } 437 438 if got := rgot.(rtests.ROOTer); !reflect.DeepEqual(got, want) { 439 t.Fatalf("error reading back value[%d].\ngot = %#v\nwant = %#v", i, got, want) 440 } 441 } 442 443 err = r.Close() 444 if err != nil { 445 t.Fatalf("error closing file: %+v", err) 446 } 447 448 if !rtests.HasROOT { 449 t.Logf("skip test with ROOT/C++") 450 return 451 } 452 453 const rootls = `#include <iostream> 454 #include "TFile.h" 455 #include "TNamed.h" 456 457 void rootls(const char *fname, const char *kname) { 458 auto f = TFile::Open(fname); 459 auto o = f->Get(kname); 460 if (o == NULL) { 461 std:cerr << "could not retrieve [" << kname << "]" << std::endl; 462 o->ClassName(); 463 } 464 std::cout << "retrieved: [" << kname << "]" << std::endl; 465 } 466 ` 467 for i := range tc.want { 468 kname := fmt.Sprintf("key-%s-%02d", tc.name, i) 469 470 out, err := rtests.RunCxxROOT("rootls", []byte(rootls), fname, kname) 471 if err != nil { 472 t.Fatalf("ROOT/C++ could not open file %q:\n%s", fname, string(out)) 473 } 474 } 475 }) 476 } 477 } 478 479 func TestOpenBigFile(t *testing.T) { 480 ch := make(chan error) 481 go func() { 482 fname := rtests.XrdRemote("testdata/SMHiggsToZZTo4L.root") 483 f, err := riofs.Open(fname) 484 if err != nil { 485 ch <- err 486 return 487 } 488 defer f.Close() 489 490 o, err := f.Get("Events") 491 if err != nil { 492 ch <- err 493 return 494 } 495 496 tree := o.(rtree.Tree) 497 if got, want := tree.Entries(), int64(299973); got != want { 498 ch <- fmt.Errorf("invalid entries: got=%d, want=%d", got, want) 499 return 500 } 501 ch <- nil 502 }() 503 504 timeout := time.NewTimer(30 * time.Second) 505 defer timeout.Stop() 506 select { 507 case err := <-ch: 508 if err != nil { 509 t.Fatalf("error: %+v", err) 510 } 511 case <-timeout.C: 512 t.Fatalf("timeout") 513 } 514 } 515 516 func TestReadOnlyFile(t *testing.T) { 517 f, err := groot.Open("../testdata/dirs-6.14.00.root") 518 if err != nil { 519 t.Fatal(err) 520 } 521 defer f.Close() 522 523 err = f.Put("o1", rbase.NewObjString("v1")) 524 if err == nil { 525 t.Fatalf("expected an error. got nil") 526 } 527 528 o, err := f.Get("dir1") 529 if err != nil { 530 t.Fatal(err) 531 } 532 533 dir1 := o.(riofs.Directory) 534 err = dir1.Put("o2", rbase.NewObjString("v2")) 535 if err == nil { 536 t.Fatalf("expected an error. got nil") 537 } 538 539 o, err = dir1.Get("dir11") 540 if err != nil { 541 t.Fatal(err) 542 } 543 544 dir11 := o.(riofs.Directory) 545 err = dir11.Put("o3", rbase.NewObjString("v3")) 546 if err == nil { 547 t.Fatalf("expected an error. got nil") 548 } 549 } 550 551 func TestTopLevelString(t *testing.T) { 552 f, err := groot.Open("../testdata/string-example.root") 553 if err != nil { 554 t.Fatal(err) 555 } 556 defer f.Close() 557 558 o, err := riofs.Get[*rbase.String](f, "FileSummaryRecord") 559 if err != nil { 560 t.Fatal(err) 561 } 562 563 got := o.String() 564 want := `{"LumiCounter.eventsByRun":{"counts":{},"empty":true,"type":"LumiEventCounter"},"guid":"5FE9437E-D958-11EE-AB88-3CECEF1070AC"}` 565 if got != want { 566 t.Fatalf("got=%q, want=%q", got, want) 567 } 568 }