go-hep.org/x/hep@v0.38.1/hbook/ntup/ntuple_test.go (about) 1 // Copyright ©2016 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 ntup 6 7 import ( 8 "database/sql" 9 "reflect" 10 "testing" 11 12 "go-hep.org/x/hep/csvutil/csvdriver" 13 "go-hep.org/x/hep/hbook" 14 ) 15 16 var ( 17 nt *Ntuple 18 ) 19 20 func TestScanH1D(t *testing.T) { 21 h := hbook.NewH1D(10, 0, 10) 22 h, err := nt.ScanH1D("x", h) 23 if err != nil { 24 t.Errorf("error running query: %v\n", err) 25 } 26 want := struct { 27 entries int64 28 len int 29 mean float64 30 rms float64 31 }{ 32 entries: 10, 33 len: 10, 34 mean: 4.5, 35 rms: 5.338539126015656, 36 } 37 38 if h.Entries() != want.entries { 39 t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries) 40 } 41 if h.Len() != want.len { 42 t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len) 43 } 44 45 for i := range h.Len() { 46 v := h.Value(i) 47 if v != 1 { 48 t.Errorf("error bin(%d)=%v. want=1\n", i, v) 49 } 50 } 51 52 if mean := h.XMean(); mean != want.mean { 53 t.Errorf("error: mean=%v. want=%v\n", mean, want.mean) 54 } 55 if rms := h.XRMS(); rms != want.rms { 56 t.Errorf("error: rms=%v. want=%v\n", rms, want.rms) 57 } 58 } 59 60 func TestScanH1DWithoutH1(t *testing.T) { 61 want := hbook.NewH1D(100, 0, nextULP(9)) 62 for i := range 10 { 63 want.Fill(float64(i), 1) 64 } 65 66 h, err := nt.ScanH1D("x", nil) 67 if err != nil { 68 t.Errorf("error running query: %v\n", err) 69 } 70 if h.Entries() != want.Entries() { 71 t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.Entries()) 72 } 73 if h.Len() != want.Len() { 74 t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.Len()) 75 } 76 77 for i := range h.Len() { 78 v := h.Value(i) 79 if v != want.Value(i) { 80 t.Errorf("error bin(%d)=%v. want=%v\n", i, v, want.Value(i)) 81 } 82 } 83 84 if mean := h.XMean(); mean != want.XMean() { 85 t.Errorf("error: mean=%v. want=%v\n", mean, want.XMean()) 86 } 87 if rms := h.XRMS(); rms != want.XRMS() { 88 t.Errorf("error: rms=%v. want=%v\n", rms, want.XRMS()) 89 } 90 } 91 92 func TestScanH1DWhere(t *testing.T) { 93 for _, where := range []string{ 94 "x where (id > 4 && id < 10)", 95 "x WHERE (id > 4 && id < 10)", 96 "x where (id > 4 && id < 10) order by id();", 97 "x WHERE (id > 4 && id < 10) ORDER by id();", 98 "x WHERE (id > 4 && id < 10) order by id();", 99 "x where (id > 4 && id < 10) ORDER by id();", 100 } { 101 t.Run("", func(t *testing.T) { 102 h := hbook.NewH1D(10, 0, 10) 103 h, err := nt.ScanH1D(where, h) 104 if err != nil { 105 t.Errorf("error running query: %v\n", err) 106 } 107 108 want := struct { 109 entries int64 110 len int 111 mean float64 112 rms float64 113 }{ 114 entries: 5, 115 len: 10, 116 mean: 7, 117 rms: 7.14142842854285, 118 } 119 120 if h.Entries() != want.entries { 121 t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries) 122 } 123 if h.Len() != want.len { 124 t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len) 125 } 126 127 for i := range h.Len() { 128 v := h.Value(i) 129 want := float64(0) 130 if i > 4 { 131 want = 1 132 } 133 if v != want { 134 t.Errorf("error bin(%d)=%v. want=%v\n", i, v, want) 135 } 136 } 137 138 if mean := h.XMean(); mean != want.mean { 139 t.Errorf("error: mean=%v. want=%v\n", mean, want.mean) 140 } 141 if rms := h.XRMS(); rms != want.rms { 142 t.Errorf("error: rms=%v. want=%v\n", rms, want.rms) 143 } 144 }) 145 } 146 } 147 148 func TestScanH1DInt(t *testing.T) { 149 h := hbook.NewH1D(10, 0, 10) 150 h, err := nt.ScanH1D("id", h) 151 if err != nil { 152 t.Errorf("error running query: %v\n", err) 153 } 154 want := struct { 155 entries int64 156 len int 157 mean float64 158 rms float64 159 }{ 160 entries: 10, 161 len: 10, 162 mean: 4.5, 163 rms: 5.338539126015656, 164 } 165 166 if h.Entries() != want.entries { 167 t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries) 168 } 169 if h.Len() != want.len { 170 t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len) 171 } 172 173 for i := range h.Len() { 174 v := h.Value(i) 175 if v != 1 { 176 t.Errorf("error bin(%d)=%v. want=1\n", i, v) 177 } 178 } 179 180 if mean := h.XMean(); mean != want.mean { 181 t.Errorf("error: mean=%v. want=%v\n", mean, want.mean) 182 } 183 if rms := h.XRMS(); rms != want.rms { 184 t.Errorf("error: rms=%v. want=%v\n", rms, want.rms) 185 } 186 } 187 188 func TestScanH2D(t *testing.T) { 189 want := hbook.NewH2D(10, 0, 10, 10, 0, 10) 190 for i := range 10 { 191 v := float64(i) 192 want.Fill(v, v, 1) 193 } 194 195 h := hbook.NewH2D(10, 0, 10, 10, 0, 10) 196 h, err := nt.ScanH2D("id, x", h) 197 if err != nil { 198 t.Errorf("error running query: %v\n", err) 199 } 200 201 if h.Entries() != want.Entries() { 202 t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.Entries()) 203 } 204 205 type gridXYZer interface { 206 Dims() (c, r int) 207 Z(c, r int) float64 208 X(c int) float64 209 Y(r int) float64 210 } 211 212 cmpGrid := func(a, b gridXYZer) { 213 ac, ar := a.Dims() 214 bc, br := b.Dims() 215 if ac != bc { 216 t.Fatalf("got=%d want=%d", ac, bc) 217 } 218 if ar != br { 219 t.Fatalf("got=%d want=%d", ar, br) 220 } 221 for i := range ar { 222 ay := a.Y(i) 223 by := b.Y(i) 224 if ay != by { 225 t.Fatalf("got=%v. want=%v", ay, by) 226 } 227 for j := range ac { 228 if i == 0 { 229 ax := a.X(j) 230 bx := b.X(j) 231 if ax != bx { 232 t.Fatalf("got=%v. want=%v", ax, bx) 233 } 234 } 235 az := a.Z(j, i) 236 bz := b.Z(j, i) 237 if az != bz { 238 t.Fatalf("got=%v. want=%v", az, bz) 239 } 240 } 241 } 242 } 243 244 cmpGrid(h.GridXYZ(), want.GridXYZ()) 245 246 if mean := h.XMean(); mean != want.XMean() { 247 t.Errorf("error: mean=%v. want=%v\n", mean, want.XMean()) 248 } 249 if rms := h.XRMS(); rms != want.XRMS() { 250 t.Errorf("error: rms=%v. want=%v\n", rms, want.XRMS()) 251 } 252 } 253 254 func TestScanH2DWithoutH2D(t *testing.T) { 255 want := hbook.NewH2D(100, 0, nextULP(9), 100, 0, nextULP(9)) 256 for i := range 10 { 257 v := float64(i) 258 want.Fill(v, v, 1) 259 } 260 261 h, err := nt.ScanH2D("id, x", nil) 262 if err != nil { 263 t.Errorf("error running query: %v\n", err) 264 } 265 266 if h.Entries() != want.Entries() { 267 t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.Entries()) 268 } 269 270 type gridXYZer interface { 271 Dims() (c, r int) 272 Z(c, r int) float64 273 X(c int) float64 274 Y(r int) float64 275 } 276 277 cmpGrid := func(a, b gridXYZer) { 278 ac, ar := a.Dims() 279 bc, br := b.Dims() 280 if ac != bc { 281 t.Fatalf("got=%d want=%d", ac, bc) 282 } 283 if ar != br { 284 t.Fatalf("got=%d want=%d", ar, br) 285 } 286 for i := range ar { 287 ay := a.Y(i) 288 by := b.Y(i) 289 if ay != by { 290 t.Fatalf("got=%v. want=%v", ay, by) 291 } 292 for j := range ac { 293 if i == 0 { 294 ax := a.X(j) 295 bx := b.X(j) 296 if ax != bx { 297 t.Fatalf("got=%v. want=%v", ax, bx) 298 } 299 } 300 az := a.Z(j, i) 301 bz := b.Z(j, i) 302 if az != bz { 303 t.Fatalf("got=%v. want=%v", az, bz) 304 } 305 } 306 } 307 } 308 309 cmpGrid(h.GridXYZ(), want.GridXYZ()) 310 311 if mean := h.XMean(); mean != want.XMean() { 312 t.Errorf("error: mean=%v. want=%v\n", mean, want.XMean()) 313 } 314 if rms := h.XRMS(); rms != want.XRMS() { 315 t.Errorf("error: rms=%v. want=%v\n", rms, want.XRMS()) 316 } 317 } 318 319 func TestScan(t *testing.T) { 320 h := hbook.NewH1D(10, 0, 10) 321 err := nt.Scan("id, x", func(id int64, x float64) error { 322 h.Fill(x, 1) 323 return nil 324 }) 325 if err != nil { 326 t.Errorf("error running query: %v\n", err) 327 } 328 want := struct { 329 entries int64 330 len int 331 mean float64 332 rms float64 333 }{ 334 entries: 10, 335 len: 10, 336 mean: 4.5, 337 rms: 5.338539126015656, 338 } 339 340 if h.Entries() != want.entries { 341 t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries) 342 } 343 if h.Len() != want.len { 344 t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len) 345 } 346 347 for i := range h.Len() { 348 v := h.Value(i) 349 if v != 1 { 350 t.Errorf("error bin(%d)=%v. want=1\n", i, v) 351 } 352 } 353 354 if mean := h.XMean(); mean != want.mean { 355 t.Errorf("error: mean=%v. want=%v\n", mean, want.mean) 356 } 357 if rms := h.XRMS(); rms != want.rms { 358 t.Errorf("error: rms=%v. want=%v\n", rms, want.rms) 359 } 360 } 361 362 func TestScanH1DFromCSVWithCommas(t *testing.T) { 363 db, err := sql.Open("csv", "testdata/simple-comma.csv") 364 if err != nil { 365 t.Fatalf("error opening CSV db: %v\n", err) 366 } 367 defer db.Close() 368 369 nt, err := Open(db, "csv") 370 if err != nil { 371 t.Fatalf("error opening ntuple: %v\n", err) 372 } 373 374 h := hbook.NewH1D(10, 0, 10) 375 h, err = nt.ScanH1D("var2", h) 376 if err != nil { 377 t.Errorf("error running query: %v\n", err) 378 } 379 want := struct { 380 entries int64 381 len int 382 mean float64 383 rms float64 384 }{ 385 entries: 10, 386 len: 10, 387 mean: 4.5, 388 rms: 5.338539126015656, 389 } 390 391 if h.Entries() != want.entries { 392 t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries) 393 } 394 if h.Len() != want.len { 395 t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len) 396 } 397 398 for i := range h.Len() { 399 v := h.Value(i) 400 if v != 1 { 401 t.Errorf("error bin(%d)=%v. want=1\n", i, v) 402 } 403 } 404 405 if mean := h.XMean(); mean != want.mean { 406 t.Errorf("error: mean=%v. want=%v\n", mean, want.mean) 407 } 408 if rms := h.XRMS(); rms != want.rms { 409 t.Errorf("error: rms=%v. want=%v\n", rms, want.rms) 410 } 411 } 412 413 func TestScanH1DFromCSV(t *testing.T) { 414 db, err := csvdriver.Conn{ 415 File: "testdata/simple.csv", 416 Comma: ';', 417 Comment: '#', 418 }.Open() 419 if err != nil { 420 t.Fatalf("error opening CSV db: %v\n", err) 421 } 422 defer db.Close() 423 424 nt, err := Open(db, "csv") 425 if err != nil { 426 t.Fatalf("error opening ntuple: %v\n", err) 427 } 428 429 h := hbook.NewH1D(10, 0, 10) 430 h, err = nt.ScanH1D("var2", h) 431 if err != nil { 432 t.Errorf("error running query: %v\n", err) 433 } 434 want := struct { 435 entries int64 436 len int 437 mean float64 438 rms float64 439 }{ 440 entries: 10, 441 len: 10, 442 mean: 4.5, 443 rms: 5.338539126015656, 444 } 445 446 if h.Entries() != want.entries { 447 t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries) 448 } 449 if h.Len() != want.len { 450 t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len) 451 } 452 453 for i := range h.Len() { 454 v := h.Value(i) 455 if v != 1 { 456 t.Errorf("error bin(%d)=%v. want=1\n", i, v) 457 } 458 } 459 460 if mean := h.XMean(); mean != want.mean { 461 t.Errorf("error: mean=%v. want=%v\n", mean, want.mean) 462 } 463 if rms := h.XRMS(); rms != want.rms { 464 t.Errorf("error: rms=%v. want=%v\n", rms, want.rms) 465 } 466 } 467 468 func TestScanInvalid(t *testing.T) { 469 for _, tc := range []struct { 470 name string 471 fct any 472 }{ 473 { 474 name: "nil func", 475 fct: nil, 476 }, 477 { 478 name: "not a func", 479 fct: 0, 480 }, 481 { 482 name: "0-arity", 483 fct: func() {}, 484 }, 485 { 486 name: "invalid func", 487 fct: func() int { return 0 }, 488 }, 489 { 490 name: "2-arity", 491 fct: func() (int, error) { return 1, nil }, 492 }, 493 } { 494 t.Run(tc.name, func(t *testing.T) { 495 err := nt.Scan("id, x", tc.fct) 496 if err == nil { 497 t.Fatalf("expected an error") 498 } 499 }) 500 } 501 } 502 503 func TestCreate(t *testing.T) { 504 db, err := sql.Open("ql", "memory://ntuple.db") 505 if err != nil { 506 t.Fatalf("error creating db: %v\n", err) 507 } 508 defer db.Close() 509 510 const ntname = "ntup" 511 nt, err := Create(db, ntname, int64(0), float64(0)) 512 if err != nil { 513 t.Fatalf("error creating ntuple: %v\n", err) 514 } 515 516 if nt.Name() != ntname { 517 t.Errorf("invalid ntuple name. got=%q want=%q\n", nt.Name(), ntname) 518 } 519 520 descr := []struct { 521 n string 522 t reflect.Type 523 }{ 524 { 525 n: "var1", 526 t: reflect.TypeOf(int64(0)), 527 }, 528 { 529 n: "var2", 530 t: reflect.TypeOf(float64(0)), 531 }, 532 } 533 if len(nt.Cols()) != len(descr) { 534 t.Fatalf("invalid cols. got=%d. want=%d\n", len(nt.Cols()), len(descr)) 535 } 536 537 for i := range descr { 538 col := nt.Cols()[i] 539 exp := descr[i] 540 if col.Name() != exp.n { 541 t.Errorf("col[%d]: invalid name. got=%q. want=%q\n", 542 i, col.Name(), exp.n, 543 ) 544 } 545 if col.Type() != exp.t { 546 t.Errorf("col[%d]: invalid type. got=%v. want=%v\n", 547 i, col.Type(), exp.t, 548 ) 549 } 550 } 551 } 552 553 func TestCreateFromStruct(t *testing.T) { 554 db, err := sql.Open("ql", "memory://ntuple-struct.db") 555 if err != nil { 556 t.Fatalf("error creating db: %v\n", err) 557 } 558 defer db.Close() 559 560 type dataType struct { 561 I int64 562 F float64 563 FF float64 `rio:"ff" hbook:"-"` 564 S string `rio:"STR" hbook:"str"` 565 } 566 567 const ntname = "ntup" 568 nt, err := Create(db, ntname, dataType{}) 569 if err != nil { 570 t.Fatalf("error creating ntuple: %v\n", err) 571 } 572 573 if nt.Name() != ntname { 574 t.Errorf("invalid ntuple name. got=%q want=%q\n", nt.Name(), ntname) 575 } 576 577 descr := []struct { 578 n string 579 t reflect.Type 580 }{ 581 { 582 n: "I", 583 t: reflect.TypeOf(int64(0)), 584 }, 585 { 586 n: "F", 587 t: reflect.TypeOf(float64(0)), 588 }, 589 { 590 n: "ff", 591 t: reflect.TypeOf(float64(0)), 592 }, 593 { 594 n: "str", 595 t: reflect.TypeOf(""), 596 }, 597 } 598 if len(nt.Cols()) != len(descr) { 599 t.Fatalf("invalid cols. got=%d. want=%d\n", len(nt.Cols()), len(descr)) 600 } 601 602 for i := range descr { 603 col := nt.Cols()[i] 604 exp := descr[i] 605 if col.Name() != exp.n { 606 t.Errorf("col[%d]: invalid name. got=%q. want=%q\n", 607 i, col.Name(), exp.n, 608 ) 609 } 610 if col.Type() != exp.t { 611 t.Errorf("col[%d]: invalid type. got=%v. want=%v\n", 612 i, col.Type(), exp.t, 613 ) 614 } 615 } 616 } 617 618 func TestCreateInvalid(t *testing.T) { 619 for _, tc := range []struct { 620 name string 621 err error 622 cols []any 623 }{ 624 { 625 name: "missing-col-def.db", 626 err: ErrMissingColDef, 627 }, 628 { 629 name: "one-value.db", 630 cols: []any{int64(0)}, 631 }, 632 { 633 name: "err-chan.db", 634 cols: []any{make(chan int)}, 635 err: errChanType, 636 }, 637 { 638 name: "err-struct-chan.db", 639 cols: []any{func() any { 640 type Person struct { 641 Field chan int 642 } 643 return Person{Field: make(chan int)} 644 }(), 645 }, 646 err: errChanType, 647 }, 648 // { 649 // name: "err-iface.db", 650 // cols: []any{(io.Writer)(os.Stdout)}, 651 // err: errIfaceType, 652 // }, 653 // { 654 // name: "err-eface.db", 655 // cols: []any{any(nil)}, 656 // err: errIfaceType, 657 // }, 658 { 659 name: "err-map.db", 660 cols: []any{make(map[string]int)}, 661 err: errMapType, 662 }, 663 { 664 name: "err-struct-map.db", 665 cols: []any{func() any { 666 type Person struct { 667 Field map[string]int 668 } 669 return Person{Field: make(map[string]int)} 670 }(), 671 }, 672 err: errMapType, 673 }, 674 { 675 name: "err-slice.db", 676 cols: []any{make([]int, 2)}, 677 err: errSliceType, 678 }, 679 { 680 name: "err-struct-slice.db", 681 cols: []any{func() any { 682 type Person struct { 683 Field []int 684 } 685 return Person{Field: make([]int, 2)} 686 }(), 687 }, 688 err: errSliceType, 689 }, 690 { 691 name: "err-struct.db", 692 cols: []any{func() any { 693 type Name struct { 694 Name string 695 } 696 type Person struct { 697 Name Name 698 } 699 var p Person 700 p.Name.Name = "bob" 701 return p 702 }(), 703 }, 704 err: errStructType, 705 }, 706 { 707 name: "err-estruct.db", 708 cols: []any{func() any { 709 type Name struct{} 710 type Anon struct { 711 Name Name 712 } 713 var v Anon 714 return v 715 }(), 716 }, 717 err: errStructType, 718 }, 719 } { 720 t.Run(tc.name, func(t *testing.T) { 721 db, err := sql.Open("ql", "memory://"+tc.name) 722 if err != nil { 723 t.Fatalf("error creating db: %v\n", err) 724 } 725 defer db.Close() 726 727 const ntname = "ntup" 728 nt, err := Create(db, ntname, tc.cols...) 729 if tc.err != nil && err == nil { 730 t.Fatalf("expected an error") 731 } 732 if err != tc.err { 733 t.Fatalf("got=%v. want=%v", err, tc.err) 734 } 735 if nt != nil { 736 defer func() { 737 err = nt.DB().Close() 738 if err != nil { 739 t.Fatal(err) 740 } 741 }() 742 } 743 }) 744 } 745 } 746 747 func init() { 748 var err error 749 db, err := sql.Open("ql", "memory://mem.db") 750 if err != nil { 751 panic(err) 752 } 753 754 tx, err := db.Begin() 755 if err != nil { 756 panic(err) 757 } 758 _, err = tx.Exec("create table data (id int, x float64);") 759 if err != nil { 760 panic(err) 761 } 762 763 for i := range 10 { 764 x := float64(i) 765 _, err = tx.Exec("insert into data values($1, $2);", i, x) 766 if err != nil { 767 panic(err) 768 } 769 } 770 771 err = tx.Commit() 772 if err != nil { 773 panic(err) 774 } 775 776 nt, err = Open(db, "data") 777 if err != nil { 778 panic(err) 779 } 780 }