go-hep.org/x/hep@v0.38.1/cmd/npy2root/main_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 main
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"os"
    11  	"path/filepath"
    12  	"reflect"
    13  	"testing"
    14  
    15  	"codeberg.org/sbinet/npyio/npy"
    16  	"go-hep.org/x/hep/groot"
    17  	"go-hep.org/x/hep/groot/rtree"
    18  )
    19  
    20  func TestConvert(t *testing.T) {
    21  	for _, tc := range []struct {
    22  		name string
    23  		want any
    24  	}{
    25  		// 4 scalars
    26  		{
    27  			name: "bool_4x1",
    28  			want: [4]bool{true, true, false, true},
    29  		},
    30  		{
    31  			name: "uint8_4x1",
    32  			want: [4]uint8{0, 1, 2, 3},
    33  		},
    34  		{
    35  			name: "uint16_4x1",
    36  			want: [4]uint16{0, 1, 2, 3},
    37  		},
    38  		{
    39  			name: "uint32_4x1",
    40  			want: [4]uint32{0, 1, 2, 3},
    41  		},
    42  		{
    43  			name: "uint64_4x1",
    44  			want: [4]uint64{0, 1, 2, 3},
    45  		},
    46  		{
    47  			name: "int8_4x1",
    48  			want: [4]int8{0, 1, 2, 3},
    49  		},
    50  		{
    51  			name: "int16_4x1",
    52  			want: [4]int16{0, 1, 2, 3},
    53  		},
    54  		{
    55  			name: "int32_4x1",
    56  			want: [4]int32{0, 1, 2, 3},
    57  		},
    58  		{
    59  			name: "int64_4x1",
    60  			want: [4]int64{0, 1, 2, 3},
    61  		},
    62  		{
    63  			name: "float32_4x1",
    64  			want: [4]float32{0, 1, 2, 3},
    65  		},
    66  		{
    67  			name: "float64_4x1",
    68  			want: [4]float64{0, 1, 2, 3},
    69  		},
    70  		{
    71  			name: "nans_4x1",
    72  			want: [4]float64{math.Inf(-1), math.Inf(+1), math.NaN(), 0},
    73  		},
    74  		// 4 1d-arrays
    75  		{
    76  			name: "bool_4x2",
    77  			want: [4][2]bool{{true, false}, {true, false}, {false, true}, {true, false}},
    78  		},
    79  		{
    80  			name: "uint8_4x2",
    81  			want: [4][2]uint8{{0, 0}, {1, 1}, {2, 2}, {3, 3}},
    82  		},
    83  		{
    84  			name: "uint16_4x2",
    85  			want: [4][2]uint16{{0, 0}, {1, 1}, {2, 2}, {3, 3}},
    86  		},
    87  		{
    88  			name: "uint32_4x2",
    89  			want: [4][2]uint32{{0, 0}, {1, 1}, {2, 2}, {3, 3}},
    90  		},
    91  		{
    92  			name: "uint64_4x2",
    93  			want: [4][2]uint64{{0, 0}, {1, 1}, {2, 2}, {3, 3}},
    94  		},
    95  		{
    96  			name: "int8_4x2",
    97  			want: [4][2]int8{{0, 0}, {1, 1}, {2, 2}, {3, 3}},
    98  		},
    99  		{
   100  			name: "int16_4x2",
   101  			want: [4][2]int16{{0, 0}, {1, 1}, {2, 2}, {3, 3}},
   102  		},
   103  		{
   104  			name: "int32_4x2",
   105  			want: [4][2]int32{{0, 0}, {1, 1}, {2, 2}, {3, 3}},
   106  		},
   107  		{
   108  			name: "int64_4x2",
   109  			want: [4][2]int64{{0, 0}, {1, 1}, {2, 2}, {3, 3}},
   110  		},
   111  		{
   112  			name: "float32_4x2",
   113  			want: [4][2]float32{{0, 0}, {1, 1}, {2, 2}, {3, 3}},
   114  		},
   115  		{
   116  			name: "float64_4x2",
   117  			want: [4][2]float64{{0, 0}, {1, 1}, {2, 2}, {3, 3}},
   118  		},
   119  		// 4 2d-arrays
   120  		{
   121  			name: "bool_4x3x2",
   122  			want: [4][3][2]bool{
   123  				{{true, false}, {true, false}, {true, false}},
   124  				{{false, true}, {false, true}, {false, true}},
   125  				{{true, false}, {true, false}, {true, false}},
   126  				{{false, true}, {false, true}, {false, true}},
   127  			},
   128  		},
   129  		{
   130  			name: "uint8_4x3x2",
   131  			want: [4][3][2]uint8{
   132  				{{10, 11}, {12, 13}, {14, 15}},
   133  				{{16, 17}, {18, 19}, {20, 21}},
   134  				{{22, 23}, {24, 25}, {26, 27}},
   135  				{{28, 29}, {30, 31}, {32, 33}},
   136  			},
   137  		},
   138  		{
   139  			name: "uint16_4x3x2",
   140  			want: [4][3][2]uint16{
   141  				{{10, 11}, {12, 13}, {14, 15}},
   142  				{{16, 17}, {18, 19}, {20, 21}},
   143  				{{22, 23}, {24, 25}, {26, 27}},
   144  				{{28, 29}, {30, 31}, {32, 33}},
   145  			},
   146  		},
   147  		{
   148  			name: "uint32_4x3x2",
   149  			want: [4][3][2]uint32{
   150  				{{10, 11}, {12, 13}, {14, 15}},
   151  				{{16, 17}, {18, 19}, {20, 21}},
   152  				{{22, 23}, {24, 25}, {26, 27}},
   153  				{{28, 29}, {30, 31}, {32, 33}},
   154  			},
   155  		},
   156  		{
   157  			name: "uint64_4x3x2",
   158  			want: [4][3][2]uint64{
   159  				{{10, 11}, {12, 13}, {14, 15}},
   160  				{{16, 17}, {18, 19}, {20, 21}},
   161  				{{22, 23}, {24, 25}, {26, 27}},
   162  				{{28, 29}, {30, 31}, {32, 33}},
   163  			},
   164  		},
   165  		{
   166  			name: "int8_4x3x2",
   167  			want: [4][3][2]int8{
   168  				{{10, 11}, {12, 13}, {14, 15}},
   169  				{{16, 17}, {18, 19}, {20, 21}},
   170  				{{22, 23}, {24, 25}, {26, 27}},
   171  				{{28, 29}, {30, 31}, {32, 33}},
   172  			},
   173  		},
   174  		{
   175  			name: "int16_4x3x2",
   176  			want: [4][3][2]int16{
   177  				{{10, 11}, {12, 13}, {14, 15}},
   178  				{{16, 17}, {18, 19}, {20, 21}},
   179  				{{22, 23}, {24, 25}, {26, 27}},
   180  				{{28, 29}, {30, 31}, {32, 33}},
   181  			},
   182  		},
   183  		{
   184  			name: "int32_4x3x2",
   185  			want: [4][3][2]int32{
   186  				{{10, 11}, {12, 13}, {14, 15}},
   187  				{{16, 17}, {18, 19}, {20, 21}},
   188  				{{22, 23}, {24, 25}, {26, 27}},
   189  				{{28, 29}, {30, 31}, {32, 33}},
   190  			},
   191  		},
   192  		{
   193  			name: "int64_4x3x2",
   194  			want: [4][3][2]int64{
   195  				{{10, 11}, {12, 13}, {14, 15}},
   196  				{{16, 17}, {18, 19}, {20, 21}},
   197  				{{22, 23}, {24, 25}, {26, 27}},
   198  				{{28, 29}, {30, 31}, {32, 33}},
   199  			},
   200  		},
   201  		{
   202  			name: "float32_4x3x2",
   203  			want: [4][3][2]float32{
   204  				{{10, 11}, {12, 13}, {14, 15}},
   205  				{{16, 17}, {18, 19}, {20, 21}},
   206  				{{22, 23}, {24, 25}, {26, 27}},
   207  				{{28, 29}, {30, 31}, {32, 33}},
   208  			},
   209  		},
   210  		{
   211  			name: "float64_4x3x2",
   212  			want: [4][3][2]float64{
   213  				{{10, 11}, {12, 13}, {14, 15}},
   214  				{{16, 17}, {18, 19}, {20, 21}},
   215  				{{22, 23}, {24, 25}, {26, 27}},
   216  				{{28, 29}, {30, 31}, {32, 33}},
   217  			},
   218  		},
   219  		// 3d-array
   220  		{
   221  			name: "float64_4x3x2x1",
   222  			want: [4][3][2][1]float64{
   223  				{{{10}, {11}}, {{12}, {13}}, {{14}, {15}}},
   224  				{{{16}, {17}}, {{18}, {19}}, {{20}, {21}}},
   225  				{{{22}, {23}}, {{24}, {25}}, {{26}, {27}}},
   226  				{{{28}, {29}}, {{30}, {31}}, {{32}, {33}}},
   227  			},
   228  		},
   229  		// 4d-array
   230  		{
   231  			name: "float64_4x3x2x1x2",
   232  			want: [4][3][2][1][2]float64{
   233  				{{{{10, 1}}, {{11, 2}}}, {{{12, 3}}, {{13, 4}}}, {{{14, 5}}, {{15, 6}}}},
   234  				{{{{16, 1}}, {{17, 2}}}, {{{18, 3}}, {{19, 4}}}, {{{20, 5}}, {{21, 6}}}},
   235  				{{{{22, 1}}, {{23, 2}}}, {{{24, 3}}, {{25, 4}}}, {{{26, 5}}, {{27, 6}}}},
   236  				{{{{28, 1}}, {{29, 2}}}, {{{30, 3}}, {{31, 4}}}, {{{32, 5}}, {{33, 6}}}},
   237  			},
   238  		},
   239  	} {
   240  		t.Run(tc.name, func(t *testing.T) {
   241  			tmp, err := os.MkdirTemp("", "npy2root-")
   242  			if err != nil {
   243  				t.Fatalf("%+v", err)
   244  			}
   245  			defer os.RemoveAll(tmp)
   246  
   247  			fname := filepath.Join(tmp, "data.npy")
   248  			src, err := os.Create(fname)
   249  			if err != nil {
   250  				t.Fatalf("could not create NumPy data file: %+v", err)
   251  			}
   252  			defer src.Close()
   253  
   254  			err = npy.Write(src, tc.want)
   255  			if err != nil {
   256  				t.Fatalf("could not save NumPy data file: %+v", err)
   257  			}
   258  
   259  			err = src.Close()
   260  			if err != nil {
   261  				t.Fatalf("could not close NumPy data file: %+v", err)
   262  			}
   263  
   264  			oname := filepath.Join(tmp, "out.root")
   265  			err = process(oname, "tree", fname)
   266  			if err != nil {
   267  				t.Fatalf("could not create ROOT data file: %+v", err)
   268  			}
   269  
   270  			f, err := groot.Open(oname)
   271  			if err != nil {
   272  				t.Fatalf("could not open ROOT file: %+v", err)
   273  			}
   274  			defer f.Close()
   275  
   276  			obj, err := f.Get("tree")
   277  			if err != nil {
   278  				t.Fatalf("could not get ROOT tree: %+v", err)
   279  			}
   280  
   281  			tree := obj.(rtree.Tree)
   282  			rvars := rtree.NewReadVars(tree)
   283  			r, err := rtree.NewReader(tree, rvars)
   284  			if err != nil {
   285  				t.Fatalf("could not create tree reader: %+v", err)
   286  			}
   287  			defer r.Close()
   288  
   289  			want := reflect.ValueOf(tc.want)
   290  			n := 0
   291  			err = r.Read(func(ctx rtree.RCtx) error {
   292  				i := int(ctx.Entry)
   293  				want := want.Index(i).Interface()
   294  				got := reflect.ValueOf(rvars[0].Value).Elem().Interface()
   295  				ok := false
   296  				switch want := want.(type) {
   297  				case float64:
   298  					got := got.(float64)
   299  					switch {
   300  					case math.IsNaN(want):
   301  						ok = math.IsNaN(got)
   302  					case math.IsInf(want, +1):
   303  						ok = math.IsInf(got, +1)
   304  					case math.IsInf(want, -1):
   305  						ok = math.IsInf(got, -1)
   306  					default:
   307  						ok = got == want
   308  					}
   309  				default:
   310  					ok = reflect.DeepEqual(got, want)
   311  				}
   312  
   313  				if !ok {
   314  					return fmt.Errorf("invalid value for entry %d:\ngot= %v\nwant=%v", ctx.Entry, got, want)
   315  				}
   316  				n++
   317  				return nil
   318  			})
   319  			if err != nil {
   320  				t.Fatalf("could not read tree: %+v", err)
   321  			}
   322  
   323  			if got, want := n, want.Len(); got != want {
   324  				t.Fatalf("invalid number of events: got=%d, want=%d", got, want)
   325  			}
   326  		})
   327  	}
   328  }