go-hep.org/x/hep@v0.38.1/groot/rcmd/diff_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 rcmd_test 6 7 import ( 8 "fmt" 9 "os" 10 "path/filepath" 11 "strings" 12 "testing" 13 14 "go-hep.org/x/hep/groot" 15 "go-hep.org/x/hep/groot/rbase" 16 "go-hep.org/x/hep/groot/rcmd" 17 "go-hep.org/x/hep/groot/riofs" 18 "go-hep.org/x/hep/groot/rtree" 19 ) 20 21 func TestDiff(t *testing.T) { 22 tmp, err := os.MkdirTemp("", "groot-rcmd-diff-") 23 if err != nil { 24 t.Fatalf("%+v", err) 25 } 26 defer os.RemoveAll(tmp) 27 28 for _, tc := range []struct { 29 name string 30 keys []string 31 err error 32 want string 33 fref func(name string) *riofs.File 34 fchk func(name string) *riofs.File 35 }{ 36 { 37 name: "same", 38 fref: func(name string) *riofs.File { 39 f, err := groot.Open("../testdata/small-flat-tree.root") 40 if err != nil { 41 t.Fatalf("%+v", err) 42 } 43 return f 44 }, 45 fchk: func(name string) *riofs.File { 46 f, err := groot.Open("../testdata/small-flat-tree.root") 47 if err != nil { 48 t.Fatalf("%+v", err) 49 } 50 return f 51 }, 52 }, 53 { 54 name: "empty", 55 fref: func(name string) *riofs.File { 56 f, err := groot.Create(name) 57 if err != nil { 58 t.Fatalf("%+v", err) 59 } 60 err = f.Close() 61 if err != nil { 62 t.Fatalf("%+v", err) 63 } 64 65 f, err = groot.Open(name) 66 if err != nil { 67 t.Fatalf("%+v", err) 68 } 69 70 return f 71 }, 72 fchk: func(name string) *riofs.File { 73 f, err := groot.Create(name) 74 if err != nil { 75 t.Fatalf("%+v", err) 76 } 77 err = f.Close() 78 if err != nil { 79 t.Fatalf("%+v", err) 80 } 81 82 f, err = groot.Open(name) 83 if err != nil { 84 t.Fatalf("%+v", err) 85 } 86 87 return f 88 }, 89 err: fmt.Errorf("could not compute keys to compare: empty key set"), 90 }, 91 { 92 name: "empty-with-keys", 93 keys: []string{" "}, 94 fref: func(name string) *riofs.File { 95 f, err := groot.Create(name) 96 if err != nil { 97 t.Fatalf("%+v", err) 98 } 99 err = f.Close() 100 if err != nil { 101 t.Fatalf("%+v", err) 102 } 103 104 f, err = groot.Open(name) 105 if err != nil { 106 t.Fatalf("%+v", err) 107 } 108 109 return f 110 }, 111 fchk: func(name string) *riofs.File { 112 f, err := groot.Create(name) 113 if err != nil { 114 t.Fatalf("%+v", err) 115 } 116 err = f.Close() 117 if err != nil { 118 t.Fatalf("%+v", err) 119 } 120 121 f, err = groot.Open(name) 122 if err != nil { 123 t.Fatalf("%+v", err) 124 } 125 126 return f 127 }, 128 err: fmt.Errorf("could not compute keys to compare: empty key set"), 129 }, 130 { 131 name: "only-dirs", 132 fref: func(name string) *riofs.File { 133 f, err := groot.Create(name) 134 if err != nil { 135 t.Fatalf("%+v", err) 136 } 137 138 _, err = riofs.Dir(f).Mkdir("dir-1/dir-11/dir-111") 139 if err != nil { 140 t.Fatalf("%+v", err) 141 } 142 143 err = f.Close() 144 if err != nil { 145 t.Fatalf("%+v", err) 146 } 147 148 f, err = groot.Open(name) 149 if err != nil { 150 t.Fatalf("%+v", err) 151 } 152 153 return f 154 }, 155 fchk: func(name string) *riofs.File { 156 f, err := groot.Create(name) 157 if err != nil { 158 t.Fatalf("%+v", err) 159 } 160 161 _, err = riofs.Dir(f).Mkdir("dir-1/dir-11/dir-111") 162 if err != nil { 163 t.Fatalf("%+v", err) 164 } 165 166 err = f.Close() 167 if err != nil { 168 t.Fatalf("%+v", err) 169 } 170 171 f, err = groot.Open(name) 172 if err != nil { 173 t.Fatalf("%+v", err) 174 } 175 176 return f 177 }, 178 }, 179 { 180 name: "different-key-type", 181 fref: func(name string) *riofs.File { 182 f, err := groot.Create(name) 183 if err != nil { 184 t.Fatalf("%+v", err) 185 } 186 187 dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 188 if err != nil { 189 t.Fatalf("%+v", err) 190 } 191 192 err = dir.Put("k1", rbase.NewNamed("k1-name", "k1-title")) 193 if err != nil { 194 t.Fatalf("%+v", err) 195 } 196 197 err = f.Close() 198 if err != nil { 199 t.Fatalf("%+v", err) 200 } 201 202 f, err = groot.Open(name) 203 if err != nil { 204 t.Fatalf("%+v", err) 205 } 206 return f 207 }, 208 fchk: func(name string) *riofs.File { 209 f, err := groot.Create(name) 210 if err != nil { 211 t.Fatalf("%+v", err) 212 } 213 214 dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 215 if err != nil { 216 t.Fatalf("%+v", err) 217 } 218 219 err = dir.Put("k1", rbase.NewObjString("obj-string")) 220 if err != nil { 221 t.Fatalf("%+v", err) 222 } 223 224 err = f.Close() 225 if err != nil { 226 t.Fatalf("%+v", err) 227 } 228 229 f, err = groot.Open(name) 230 if err != nil { 231 t.Fatalf("%+v", err) 232 } 233 return f 234 }, 235 err: fmt.Errorf("dir-1: values for dir-11 in directory differ: dir-1/dir-11: values for k1 in directory differ: dir-1/dir-11/k1: type of keys differ: ref=*rbase.Named chk=*rbase.ObjString"), 236 }, 237 { 238 name: "different-key-set-chk", 239 fref: func(name string) *riofs.File { 240 f, err := groot.Create(name) 241 if err != nil { 242 t.Fatalf("%+v", err) 243 } 244 245 dir11, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 246 if err != nil { 247 t.Fatalf("%+v", err) 248 } 249 250 dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11") 251 if err != nil { 252 t.Fatalf("%+v", err) 253 } 254 _ = dir21 255 256 err = dir11.Put("k1", rbase.NewObjString("obj-string")) 257 if err != nil { 258 t.Fatalf("%+v", err) 259 } 260 261 err = f.Close() 262 if err != nil { 263 t.Fatalf("%+v", err) 264 } 265 266 f, err = groot.Open(name) 267 if err != nil { 268 t.Fatalf("%+v", err) 269 } 270 return f 271 }, 272 fchk: func(name string) *riofs.File { 273 f, err := groot.Create(name) 274 if err != nil { 275 t.Fatalf("%+v", err) 276 } 277 278 dir11, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 279 if err != nil { 280 t.Fatalf("%+v", err) 281 } 282 283 dir31, err := riofs.Dir(f).Mkdir("dir-3/dir-11") 284 if err != nil { 285 t.Fatalf("%+v", err) 286 } 287 _ = dir31 288 289 err = dir11.Put("k1", rbase.NewObjString("obj-string-xxx")) 290 if err != nil { 291 t.Fatalf("%+v", err) 292 } 293 294 err = f.Close() 295 if err != nil { 296 t.Fatalf("%+v", err) 297 } 298 299 f, err = groot.Open(name) 300 if err != nil { 301 t.Fatalf("%+v", err) 302 } 303 return f 304 }, 305 err: fmt.Errorf("could not compute keys to compare: key set differ"), 306 want: "key[dir-2] -- missing from chk-file\n", 307 }, 308 { 309 name: "different-key-set-ref", 310 keys: []string{"dir-1", "dir-3"}, 311 fref: func(name string) *riofs.File { 312 f, err := groot.Create(name) 313 if err != nil { 314 t.Fatalf("%+v", err) 315 } 316 317 dir11, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 318 if err != nil { 319 t.Fatalf("%+v", err) 320 } 321 322 dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11") 323 if err != nil { 324 t.Fatalf("%+v", err) 325 } 326 _ = dir21 327 328 err = dir11.Put("k1", rbase.NewObjString("obj-string")) 329 if err != nil { 330 t.Fatalf("%+v", err) 331 } 332 333 err = f.Close() 334 if err != nil { 335 t.Fatalf("%+v", err) 336 } 337 338 f, err = groot.Open(name) 339 if err != nil { 340 t.Fatalf("%+v", err) 341 } 342 return f 343 }, 344 fchk: func(name string) *riofs.File { 345 f, err := groot.Create(name) 346 if err != nil { 347 t.Fatalf("%+v", err) 348 } 349 350 dir11, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 351 if err != nil { 352 t.Fatalf("%+v", err) 353 } 354 355 dir31, err := riofs.Dir(f).Mkdir("dir-3/dir-11") 356 if err != nil { 357 t.Fatalf("%+v", err) 358 } 359 _ = dir31 360 361 err = dir11.Put("k1", rbase.NewObjString("obj-string-xxx")) 362 if err != nil { 363 t.Fatalf("%+v", err) 364 } 365 366 err = f.Close() 367 if err != nil { 368 t.Fatalf("%+v", err) 369 } 370 371 f, err = groot.Open(name) 372 if err != nil { 373 t.Fatalf("%+v", err) 374 } 375 return f 376 }, 377 err: fmt.Errorf("could not compute keys to compare: key set differ"), 378 want: "key[dir-3] -- missing from ref-file\n", 379 }, 380 { 381 name: "different-key-value", 382 keys: []string{"dir-1"}, 383 fref: func(name string) *riofs.File { 384 f, err := groot.Create(name) 385 if err != nil { 386 t.Fatalf("%+v", err) 387 } 388 389 dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 390 if err != nil { 391 t.Fatalf("%+v", err) 392 } 393 394 err = dir.Put("k1", rbase.NewObjString("obj-string")) 395 if err != nil { 396 t.Fatalf("%+v", err) 397 } 398 399 err = f.Close() 400 if err != nil { 401 t.Fatalf("%+v", err) 402 } 403 404 f, err = groot.Open(name) 405 if err != nil { 406 t.Fatalf("%+v", err) 407 } 408 return f 409 }, 410 fchk: func(name string) *riofs.File { 411 f, err := groot.Create(name) 412 if err != nil { 413 t.Fatalf("%+v", err) 414 } 415 416 dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 417 if err != nil { 418 t.Fatalf("%+v", err) 419 } 420 421 err = dir.Put("k1", rbase.NewObjString("obj-string-xxx")) 422 if err != nil { 423 t.Fatalf("%+v", err) 424 } 425 426 err = f.Close() 427 if err != nil { 428 t.Fatalf("%+v", err) 429 } 430 431 f, err = groot.Open(name) 432 if err != nil { 433 t.Fatalf("%+v", err) 434 } 435 return f 436 }, 437 err: fmt.Errorf("dir-1: values for dir-11 in directory differ: dir-1/dir-11: values for k1 in directory differ: dir-1/dir-11/k1: keys differ"), 438 want: `key[dir-1/dir-11/k1] (*rbase.ObjString) -- (-ref +chk) 439 -obj-string 440 +obj-string-xxx 441 `, 442 }, 443 { 444 name: "different-trees-entries", 445 fref: func(name string) *riofs.File { 446 f, err := groot.Create(name) 447 if err != nil { 448 t.Fatalf("%+v", err) 449 } 450 451 dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 452 if err != nil { 453 t.Fatalf("%+v", err) 454 } 455 456 var data struct { 457 I32 int32 458 F64 float64 459 Arr [2]float64 460 } 461 w, err := rtree.NewWriter(dir, "tree", rtree.WriteVarsFromStruct(&data)) 462 if err != nil { 463 t.Fatalf("%+v", err) 464 } 465 466 for i := range 5 { 467 data.I32 = int32(i) 468 data.F64 = float64(i) 469 data.Arr = [2]float64{float64(i + 1), float64(i + 2)} 470 _, err = w.Write() 471 if err != nil { 472 t.Fatalf("could not write event #%d: %+v", i, err) 473 } 474 } 475 476 err = w.Close() 477 if err != nil { 478 t.Fatalf("%+v", err) 479 } 480 481 err = f.Close() 482 if err != nil { 483 t.Fatalf("%+v", err) 484 } 485 486 f, err = groot.Open(name) 487 if err != nil { 488 t.Fatalf("%+v", err) 489 } 490 return f 491 }, 492 fchk: func(name string) *riofs.File { 493 f, err := groot.Create(name) 494 if err != nil { 495 t.Fatalf("%+v", err) 496 } 497 498 dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 499 if err != nil { 500 t.Fatalf("%+v", err) 501 } 502 503 var data struct { 504 I32 int32 505 F64 float64 506 Arr [2]float64 507 } 508 w, err := rtree.NewWriter(dir, "tree", rtree.WriteVarsFromStruct(&data)) 509 if err != nil { 510 t.Fatalf("%+v", err) 511 } 512 513 for i := range 6 { 514 data.I32 = int32(i) 515 data.F64 = float64(i) 516 data.Arr = [2]float64{float64(i + 1), float64(i + 2)} 517 _, err = w.Write() 518 if err != nil { 519 t.Fatalf("could not write event #%d: %+v", i, err) 520 } 521 } 522 523 err = w.Close() 524 if err != nil { 525 t.Fatalf("%+v", err) 526 } 527 528 err = f.Close() 529 if err != nil { 530 t.Fatalf("%+v", err) 531 } 532 533 f, err = groot.Open(name) 534 if err != nil { 535 t.Fatalf("%+v", err) 536 } 537 return f 538 }, 539 err: fmt.Errorf("dir-1: values for dir-11 in directory differ: dir-1/dir-11: values for tree in directory differ: dir-1/dir-11/tree: number of entries differ: ref=5 chk=6"), 540 }, 541 { 542 name: "different-trees-values", 543 fref: func(name string) *riofs.File { 544 f, err := groot.Create(name) 545 if err != nil { 546 t.Fatalf("%+v", err) 547 } 548 549 dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 550 if err != nil { 551 t.Fatalf("%+v", err) 552 } 553 554 var data struct { 555 I32 int32 556 F64 float64 557 Arr [2]float64 558 } 559 w, err := rtree.NewWriter(dir, "tree", rtree.WriteVarsFromStruct(&data)) 560 if err != nil { 561 t.Fatalf("%+v", err) 562 } 563 564 for i := range 5 { 565 data.I32 = int32(i) 566 data.F64 = float64(i + 1) 567 data.Arr = [2]float64{float64(i + 1), float64(i + 2)} 568 _, err = w.Write() 569 if err != nil { 570 t.Fatalf("could not write event #%d: %+v", i, err) 571 } 572 } 573 574 err = w.Close() 575 if err != nil { 576 t.Fatalf("%+v", err) 577 } 578 579 err = f.Close() 580 if err != nil { 581 t.Fatalf("%+v", err) 582 } 583 584 f, err = groot.Open(name) 585 if err != nil { 586 t.Fatalf("%+v", err) 587 } 588 return f 589 }, 590 fchk: func(name string) *riofs.File { 591 f, err := groot.Create(name) 592 if err != nil { 593 t.Fatalf("%+v", err) 594 } 595 596 dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 597 if err != nil { 598 t.Fatalf("%+v", err) 599 } 600 601 var data struct { 602 I32 int32 603 F64 float64 604 Arr [2]float64 605 } 606 w, err := rtree.NewWriter(dir, "tree", rtree.WriteVarsFromStruct(&data)) 607 if err != nil { 608 t.Fatalf("%+v", err) 609 } 610 611 for i := range 5 { 612 data.I32 = int32(i) 613 data.F64 = float64(i) 614 data.Arr = [2]float64{float64(i), float64(i + 2)} 615 _, err = w.Write() 616 if err != nil { 617 t.Fatalf("could not write event #%d: %+v", i, err) 618 } 619 } 620 621 err = w.Close() 622 if err != nil { 623 t.Fatalf("%+v", err) 624 } 625 626 err = f.Close() 627 if err != nil { 628 t.Fatalf("%+v", err) 629 } 630 631 f, err = groot.Open(name) 632 if err != nil { 633 t.Fatalf("%+v", err) 634 } 635 return f 636 }, 637 err: fmt.Errorf("dir-1: values for dir-11 in directory differ: dir-1/dir-11: values for tree in directory differ: dir-1/dir-11/tree: trees differ"), 638 want: `key[dir-1/dir-11/tree][0000].F64 -- (-ref +chk) 639 float64( 640 - 1, 641 + 0, 642 ) 643 key[dir-1/dir-11/tree][0000].Arr -- (-ref +chk) 644 [2]float64{ 645 - 1, 646 + 0, 647 2, 648 } 649 key[dir-1/dir-11/tree][0001].F64 -- (-ref +chk) 650 float64( 651 - 2, 652 + 1, 653 ) 654 key[dir-1/dir-11/tree][0001].Arr -- (-ref +chk) 655 [2]float64{ 656 - 2, 657 + 1, 658 3, 659 } 660 key[dir-1/dir-11/tree][0002].F64 -- (-ref +chk) 661 float64( 662 - 3, 663 + 2, 664 ) 665 key[dir-1/dir-11/tree][0002].Arr -- (-ref +chk) 666 [2]float64{ 667 - 3, 668 + 2, 669 4, 670 } 671 key[dir-1/dir-11/tree][0003].F64 -- (-ref +chk) 672 float64( 673 - 4, 674 + 3, 675 ) 676 key[dir-1/dir-11/tree][0003].Arr -- (-ref +chk) 677 [2]float64{ 678 - 4, 679 + 3, 680 5, 681 } 682 key[dir-1/dir-11/tree][0004].F64 -- (-ref +chk) 683 float64( 684 - 5, 685 + 4, 686 ) 687 key[dir-1/dir-11/tree][0004].Arr -- (-ref +chk) 688 [2]float64{ 689 - 5, 690 + 4, 691 6, 692 } 693 `, 694 }, 695 { 696 name: "different-trees-types", 697 fref: func(name string) *riofs.File { 698 f, err := groot.Create(name) 699 if err != nil { 700 t.Fatalf("%+v", err) 701 } 702 703 dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 704 if err != nil { 705 t.Fatalf("%+v", err) 706 } 707 708 var data struct { 709 I32 int64 710 F64 float64 711 Arr [2]float64 712 } 713 w, err := rtree.NewWriter(dir, "tree", rtree.WriteVarsFromStruct(&data)) 714 if err != nil { 715 t.Fatalf("%+v", err) 716 } 717 718 for i := range 5 { 719 data.I32 = int64(i) 720 data.F64 = float64(i + 1) 721 data.Arr = [2]float64{float64(i + 1), float64(i + 2)} 722 _, err = w.Write() 723 if err != nil { 724 t.Fatalf("could not write event #%d: %+v", i, err) 725 } 726 } 727 728 err = w.Close() 729 if err != nil { 730 t.Fatalf("%+v", err) 731 } 732 733 err = f.Close() 734 if err != nil { 735 t.Fatalf("%+v", err) 736 } 737 738 f, err = groot.Open(name) 739 if err != nil { 740 t.Fatalf("%+v", err) 741 } 742 return f 743 }, 744 fchk: func(name string) *riofs.File { 745 f, err := groot.Create(name) 746 if err != nil { 747 t.Fatalf("%+v", err) 748 } 749 750 dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11") 751 if err != nil { 752 t.Fatalf("%+v", err) 753 } 754 755 var data struct { 756 I32 int32 757 F64 float64 758 Arr [2]float64 759 } 760 w, err := rtree.NewWriter(dir, "tree", rtree.WriteVarsFromStruct(&data)) 761 if err != nil { 762 t.Fatalf("%+v", err) 763 } 764 765 for i := range 5 { 766 data.I32 = int32(i) 767 data.F64 = float64(i + 1) 768 data.Arr = [2]float64{float64(i + 1), float64(i + 2)} 769 _, err = w.Write() 770 if err != nil { 771 t.Fatalf("could not write event #%d: %+v", i, err) 772 } 773 } 774 775 err = w.Close() 776 if err != nil { 777 t.Fatalf("%+v", err) 778 } 779 780 err = f.Close() 781 if err != nil { 782 t.Fatalf("%+v", err) 783 } 784 785 f, err = groot.Open(name) 786 if err != nil { 787 t.Fatalf("%+v", err) 788 } 789 return f 790 }, 791 err: fmt.Errorf("dir-1: values for dir-11 in directory differ: dir-1/dir-11: values for tree in directory differ: dir-1/dir-11/tree: trees differ"), 792 want: `key[dir-1/dir-11/tree][0000].I32 -- (-ref +chk) 793 any( 794 - int64(0), 795 + int32(0), 796 ) 797 key[dir-1/dir-11/tree][0001].I32 -- (-ref +chk) 798 any( 799 - int64(1), 800 + int32(1), 801 ) 802 key[dir-1/dir-11/tree][0002].I32 -- (-ref +chk) 803 any( 804 - int64(2), 805 + int32(2), 806 ) 807 key[dir-1/dir-11/tree][0003].I32 -- (-ref +chk) 808 any( 809 - int64(3), 810 + int32(3), 811 ) 812 key[dir-1/dir-11/tree][0004].I32 -- (-ref +chk) 813 any( 814 - int64(4), 815 + int32(4), 816 ) 817 `, 818 }, 819 } { 820 t.Run(tc.name, func(t *testing.T) { 821 refname := filepath.Join(tmp, tc.name+"-ref.root") 822 fref := tc.fref(refname) 823 defer fref.Close() 824 825 chkname := filepath.Join(tmp, tc.name+"-chk.root") 826 fchk := tc.fchk(chkname) 827 defer fchk.Close() 828 829 out := new(strings.Builder) 830 err := rcmd.Diff(out, fref, fchk, tc.keys) 831 switch { 832 case err != nil && tc.err != nil: 833 if got, want := err.Error(), tc.err.Error(); got != want { 834 t.Fatalf("invalid error.\ngot= %s\nwant=%s\n", got, want) 835 } 836 case err != nil && tc.err == nil: 837 t.Fatalf("unexpected error: %+v", err) 838 839 case err == nil && tc.err != nil: 840 t.Fatalf("expected an error: %+v", tc.err) 841 842 case err == nil && tc.err == nil: 843 // ok 844 return 845 } 846 847 // replace non-breaking spaces (U+00a0) with regular space (U+0020). 848 got := strings.Replace(out.String(), " ", " ", -1) 849 850 if got, want := got, tc.want; got != want { 851 t.Fatalf("invalid diff.\ngot:\n%s\nwant:\n%s", got, want) 852 } 853 }) 854 } 855 }