go-hep.org/x/hep@v0.38.1/groot/rcmd/merge_test.go (about) 1 // Copyright ©2019 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 rcmd_test 6 7 import ( 8 "bytes" 9 "fmt" 10 "os" 11 "path/filepath" 12 "testing" 13 14 "go-hep.org/x/hep/groot" 15 "go-hep.org/x/hep/groot/rcmd" 16 "go-hep.org/x/hep/groot/rhist" 17 "go-hep.org/x/hep/groot/riofs" 18 "go-hep.org/x/hep/groot/rtree" 19 "go-hep.org/x/hep/hbook" 20 "go-hep.org/x/hep/hbook/rootcnv" 21 ) 22 23 func TestMerge(t *testing.T) { 24 tmp, err := os.MkdirTemp("", "groot-root-merge-") 25 if err != nil { 26 t.Fatalf("%+v", err) 27 } 28 defer os.RemoveAll(tmp) 29 30 type funcT func(t *testing.T, fname string) error 31 for _, tc := range []struct { 32 name string 33 inputs []funcT 34 output funcT 35 panics string 36 }{ 37 { 38 name: "flat-tree-1", 39 inputs: []funcT{makeFlatTree(1)}, 40 output: makeFlatTree(1), 41 }, 42 { 43 name: "flat-tree-2", 44 inputs: []funcT{makeFlatTree(1), makeFlatTree(1)}, 45 output: makeFlatTree(2), 46 }, 47 { 48 name: "h1f-1", 49 inputs: []funcT{makeH1F(1)}, 50 output: makeH1F(1), 51 }, 52 { 53 name: "h1f-2", 54 inputs: []funcT{makeH1F(1), makeH1F(1)}, 55 output: makeH1F(2), 56 }, 57 { 58 name: "h1d-1", 59 inputs: []funcT{makeH1D(1)}, 60 output: makeH1D(1), 61 }, 62 { 63 name: "h1d-2", 64 inputs: []funcT{makeH1D(1), makeH1D(1)}, 65 output: makeH1D(2), 66 }, 67 { 68 name: "h1i-1", 69 inputs: []funcT{makeH1I(1)}, 70 output: makeH1I(1), 71 }, 72 { 73 name: "h1i-2", 74 inputs: []funcT{makeH1I(1), makeH1I(1)}, 75 output: makeH1I(2), 76 }, 77 { 78 name: "h2d-1", 79 inputs: []funcT{makeH2D(1)}, 80 output: makeH2D(1), 81 }, 82 { 83 name: "h2d-2", 84 inputs: []funcT{makeH2D(1), makeH2D(1)}, 85 output: makeH2D(2), 86 panics: "not implemented", // FIXME(sbinet) 87 }, 88 { 89 name: "graph-1", 90 inputs: []funcT{makeGraph(0, 1)}, 91 output: makeGraph(0, 1), 92 }, 93 { 94 name: "graph-2", 95 inputs: []funcT{makeGraph(0, 1), makeGraph(1, 2)}, 96 output: makeGraph(0, 2), 97 }, 98 { 99 name: "graph-err-1", 100 inputs: []funcT{makeGraphErr(0, 1)}, 101 output: makeGraphErr(0, 1), 102 }, 103 { 104 name: "graph-err-2", 105 inputs: []funcT{makeGraphErr(0, 1), makeGraphErr(1, 2)}, 106 output: makeGraphErr(0, 2), 107 }, 108 { 109 name: "graph-asymmerr-1", 110 inputs: []funcT{makeGraphAsymmErr(0, 1)}, 111 output: makeGraphAsymmErr(0, 1), 112 }, 113 { 114 name: "graph-asymmerr-2", 115 inputs: []funcT{makeGraphAsymmErr(0, 1), makeGraphAsymmErr(1, 2)}, 116 output: makeGraphAsymmErr(0, 2), 117 }, 118 } { 119 t.Run(tc.name, func(t *testing.T) { 120 var ( 121 fnames []string 122 oname = filepath.Join(tmp, tc.name+".out.root") 123 verbose = true 124 deep = true 125 ) 126 for i, fct := range tc.inputs { 127 fname := filepath.Join(tmp, fmt.Sprintf("%s-%02d.root", tc.name, i)) 128 err := fct(t, fname) 129 if err != nil { 130 t.Fatalf("%+v", err) 131 } 132 fnames = append(fnames, fname) 133 } 134 refname := filepath.Join(tmp, tc.name+".want.root") 135 err := tc.output(t, refname) 136 if err != nil { 137 t.Fatalf("%+v", err) 138 } 139 140 if tc.panics != "" { 141 defer func() { 142 err := recover() 143 if err == nil { 144 t.Fatalf("expected a panic") 145 } 146 if got, want := err.(string), tc.panics; got != want { 147 t.Fatalf("invalid panic message. got=%q, want=%q", got, want) 148 } 149 }() 150 } 151 152 err = rcmd.Merge(oname, fnames, verbose) 153 if err != nil { 154 t.Fatalf("could not run root-merge: %+v", err) 155 } 156 157 got := new(bytes.Buffer) 158 err = rcmd.Dump(got, oname, deep, nil) 159 if err != nil { 160 t.Fatalf("could not run root-dump: %+v", err) 161 } 162 163 want := new(bytes.Buffer) 164 err = rcmd.Dump(want, refname, deep, nil) 165 if err != nil { 166 t.Fatalf("could not run root-dump: %+v", err) 167 } 168 169 if got, want := got.String(), want.String(); got != want { 170 t.Fatalf("invalid root-merge output:\ngot:\n%swant:\n%s", got, want) 171 } 172 }) 173 } 174 } 175 176 func makeFlatTree(n int) func(t *testing.T, fname string) error { 177 return func(t *testing.T, fname string) error { 178 type Data struct { 179 I32 int32 180 F64 float64 181 Str string 182 ArrF64 [5]float64 183 N int32 184 SliF64 []float64 `groot:"SliF64[N]"` 185 } 186 const ( 187 nevts = 5 188 ) 189 190 f, err := groot.Create(fname) 191 if err != nil { 192 t.Fatalf("%+v", err) 193 } 194 defer f.Close() 195 196 dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 197 if err != nil { 198 t.Fatalf("could not create directory: %+v", err) 199 } 200 201 var evt Data 202 tree, err := rtree.NewWriter(dir, "mytree", rtree.WriteVarsFromStruct(&evt)) 203 if err != nil { 204 t.Fatalf("could not create tree writer: %+v", err) 205 } 206 207 for range n { 208 for i := range nevts { 209 evt.I32 = int32(i) 210 evt.F64 = float64(i) 211 evt.Str = fmt.Sprintf("evt-%0d", i) 212 evt.ArrF64 = [5]float64{float64(i), float64(i + 1), float64(i + 2), float64(i + 3), float64(i + 4)} 213 evt.N = int32(i) 214 evt.SliF64 = []float64{float64(i), float64(i + 1), float64(i + 2), float64(i + 3), float64(i + 4)}[:i] 215 _, err = tree.Write() 216 if err != nil { 217 t.Fatalf("could not write event %d: %+v", i, err) 218 } 219 } 220 } 221 222 err = tree.Close() 223 if err != nil { 224 t.Fatalf("could not write tree: %+v", err) 225 } 226 227 err = f.Close() 228 if err != nil { 229 t.Fatalf("could not close file: %+v", err) 230 } 231 232 return nil 233 } 234 } 235 236 func makeH1F(n int) func(t *testing.T, fname string) error { 237 return func(t *testing.T, fname string) error { 238 f, err := groot.Create(fname) 239 if err != nil { 240 t.Fatalf("%+v", err) 241 } 242 defer f.Close() 243 244 _, err = riofs.Dir(f).Mkdir("dir-1/dir-11") 245 if err != nil { 246 t.Fatalf("could not create directory: %+v", err) 247 } 248 249 dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11") 250 if err != nil { 251 t.Fatalf("could not create directory: %+v", err) 252 } 253 254 h := hbook.NewH1D(10, 0, 10) 255 h.Annotation()["title"] = "h1f" 256 for range n { 257 h.Fill(5, 1) 258 h.Fill(6, 2) 259 } 260 261 err = dir21.Put("h1f", rhist.NewH1FFrom(h)) 262 if err != nil { 263 t.Fatalf("could not save H1F: %+v", err) 264 } 265 266 err = f.Close() 267 if err != nil { 268 t.Fatalf("could not close file: %+v", err) 269 } 270 271 return nil 272 } 273 } 274 275 func makeH1D(n int) func(t *testing.T, fname string) error { 276 return func(t *testing.T, fname string) error { 277 f, err := groot.Create(fname) 278 if err != nil { 279 t.Fatalf("%+v", err) 280 } 281 defer f.Close() 282 283 _, err = riofs.Dir(f).Mkdir("dir-1/dir-11") 284 if err != nil { 285 t.Fatalf("could not create directory: %+v", err) 286 } 287 288 dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11") 289 if err != nil { 290 t.Fatalf("could not create directory: %+v", err) 291 } 292 293 h := hbook.NewH1D(10, 0, 10) 294 h.Annotation()["title"] = "h1d" 295 for range n { 296 h.Fill(5, 1) 297 h.Fill(6, 2) 298 } 299 300 err = dir21.Put("h1d", rootcnv.FromH1D(h)) 301 if err != nil { 302 t.Fatalf("could not save H1D: %+v", err) 303 } 304 305 err = f.Close() 306 if err != nil { 307 t.Fatalf("could not close file: %+v", err) 308 } 309 310 return nil 311 } 312 } 313 314 func makeH1I(n int) func(t *testing.T, fname string) error { 315 return func(t *testing.T, fname string) error { 316 f, err := groot.Create(fname) 317 if err != nil { 318 t.Fatalf("%+v", err) 319 } 320 defer f.Close() 321 322 _, err = riofs.Dir(f).Mkdir("dir-1/dir-11") 323 if err != nil { 324 t.Fatalf("could not create directory: %+v", err) 325 } 326 327 dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11") 328 if err != nil { 329 t.Fatalf("could not create directory: %+v", err) 330 } 331 332 h := hbook.NewH1D(10, 0, 10) 333 h.Annotation()["title"] = "h1i" 334 for range n { 335 h.Fill(5, 1) 336 h.Fill(6, 2) 337 } 338 339 err = dir21.Put("h1i", rhist.NewH1IFrom(h)) 340 if err != nil { 341 t.Fatalf("could not save H1I: %+v", err) 342 } 343 344 err = f.Close() 345 if err != nil { 346 t.Fatalf("could not close file: %+v", err) 347 } 348 349 return nil 350 } 351 } 352 353 func makeH2D(n int) func(t *testing.T, fname string) error { 354 return func(t *testing.T, fname string) error { 355 f, err := groot.Create(fname) 356 if err != nil { 357 t.Fatalf("%+v", err) 358 } 359 defer f.Close() 360 361 _, err = riofs.Dir(f).Mkdir("dir-1/dir-11") 362 if err != nil { 363 t.Fatalf("could not create directory: %+v", err) 364 } 365 366 dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11") 367 if err != nil { 368 t.Fatalf("could not create directory: %+v", err) 369 } 370 371 h := hbook.NewH2D(10, 0, 10, 10, 0, 10) 372 h.Annotation()["title"] = "h2d" 373 for range n { 374 h.Fill(5, 5, 1) 375 h.Fill(6, 6, 2) 376 } 377 378 err = dir21.Put("h2d", rootcnv.FromH2D(h)) 379 if err != nil { 380 t.Fatalf("could not save H2D: %+v", err) 381 } 382 383 err = f.Close() 384 if err != nil { 385 t.Fatalf("could not close file: %+v", err) 386 } 387 388 return nil 389 } 390 } 391 392 func makeGraph(beg, end int) func(t *testing.T, fname string) error { 393 return func(t *testing.T, fname string) error { 394 f, err := groot.Create(fname) 395 if err != nil { 396 t.Fatalf("%+v", err) 397 } 398 defer f.Close() 399 400 _, err = riofs.Dir(f).Mkdir("dir-1/dir-11") 401 if err != nil { 402 t.Fatalf("could not create directory: %+v", err) 403 } 404 405 dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11") 406 if err != nil { 407 t.Fatalf("could not create directory: %+v", err) 408 } 409 410 var ( 411 xs []float64 412 ys []float64 413 ) 414 for i := beg; i < end; i++ { 415 for j := range 10 { 416 xs = append(xs, float64(10*(1+i)+j)) 417 ys = append(ys, float64(10*(1+i)+j)) 418 } 419 } 420 421 gr := hbook.NewS2DFrom(xs, ys) 422 gr.Annotation()["title"] = "graph" 423 err = dir21.Put("graph", rhist.NewGraphFrom(gr)) 424 if err != nil { 425 t.Fatalf("could not save S2D: %+v", err) 426 } 427 428 err = f.Close() 429 if err != nil { 430 t.Fatalf("could not close file: %+v", err) 431 } 432 433 return nil 434 } 435 } 436 437 func makeGraphErr(beg, end int) func(t *testing.T, fname string) error { 438 return func(t *testing.T, fname string) error { 439 f, err := groot.Create(fname) 440 if err != nil { 441 t.Fatalf("%+v", err) 442 } 443 defer f.Close() 444 445 _, err = riofs.Dir(f).Mkdir("dir-1/dir-11") 446 if err != nil { 447 t.Fatalf("could not create directory: %+v", err) 448 } 449 450 dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11") 451 if err != nil { 452 t.Fatalf("could not create directory: %+v", err) 453 } 454 455 var ( 456 pts []hbook.Point2D 457 ) 458 for i := beg; i < end; i++ { 459 for j := range 10 { 460 var ( 461 x = float64(10*(1+i) + j) 462 y = float64(10*(1+i) + j) 463 xerr = 2.5 464 yerr = 3.5 465 ) 466 pts = append(pts, hbook.Point2D{ 467 X: x, 468 Y: y, 469 ErrX: hbook.Range{Min: xerr, Max: xerr}, 470 ErrY: hbook.Range{Min: yerr, Max: yerr}, 471 }) 472 } 473 } 474 475 gr := hbook.NewS2D(pts...) 476 gr.Annotation()["title"] = "graph" 477 err = dir21.Put("graph", rhist.NewGraphErrorsFrom(gr)) 478 if err != nil { 479 t.Fatalf("could not save S2D: %+v", err) 480 } 481 482 err = f.Close() 483 if err != nil { 484 t.Fatalf("could not close file: %+v", err) 485 } 486 487 return nil 488 } 489 } 490 491 func makeGraphAsymmErr(beg, end int) func(t *testing.T, fname string) error { 492 return func(t *testing.T, fname string) error { 493 f, err := groot.Create(fname) 494 if err != nil { 495 t.Fatalf("%+v", err) 496 } 497 defer f.Close() 498 499 _, err = riofs.Dir(f).Mkdir("dir-1/dir-11") 500 if err != nil { 501 t.Fatalf("could not create directory: %+v", err) 502 } 503 504 dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11") 505 if err != nil { 506 t.Fatalf("could not create directory: %+v", err) 507 } 508 509 var ( 510 pts []hbook.Point2D 511 ) 512 for i := beg; i < end; i++ { 513 for j := range 10 { 514 var ( 515 x = float64(10*(1+i) + j) 516 y = float64(10*(1+i) + j) 517 ) 518 pts = append(pts, hbook.Point2D{ 519 X: x, 520 Y: y, 521 ErrX: hbook.Range{Min: 1.5, Max: 2.5}, 522 ErrY: hbook.Range{Min: 1.5, Max: 2.5}, 523 }) 524 } 525 } 526 527 gr := hbook.NewS2D(pts...) 528 gr.Annotation()["title"] = "graph" 529 err = dir21.Put("graph", rhist.NewGraphAsymmErrorsFrom(gr)) 530 if err != nil { 531 t.Fatalf("could not save S2D: %+v", err) 532 } 533 534 err = f.Close() 535 if err != nil { 536 t.Fatalf("could not close file: %+v", err) 537 } 538 539 return nil 540 } 541 }