go-hep.org/x/hep@v0.38.1/groot/rtree/rvar_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 rtree
     6  
     7  import (
     8  	"reflect"
     9  	"testing"
    10  
    11  	"go-hep.org/x/hep/groot/riofs"
    12  	"go-hep.org/x/hep/groot/root"
    13  )
    14  
    15  func TestNewReadVars(t *testing.T) {
    16  	f, err := riofs.Open("../testdata/leaves.root")
    17  	if err != nil {
    18  		t.Fatal(err)
    19  	}
    20  	defer f.Close()
    21  
    22  	o, err := f.Get("tree")
    23  	if err != nil {
    24  		t.Fatal(err)
    25  	}
    26  
    27  	tree := o.(Tree)
    28  
    29  	vars := NewReadVars(tree)
    30  	want := []ReadVar{
    31  		{Name: "B", Leaf: "B", Value: new(bool)},
    32  		{Name: "Str", Leaf: "Str", Value: new(string)},
    33  		{Name: "I8", Leaf: "I8", Value: new(int8)},
    34  		{Name: "I16", Leaf: "I16", Value: new(int16)},
    35  		{Name: "I32", Leaf: "I32", Value: new(int32)},
    36  		{Name: "I64", Leaf: "I64", Value: new(int64)},
    37  		{Name: "G64", Leaf: "G64", Value: new(int64)},
    38  		{Name: "U8", Leaf: "U8", Value: new(uint8)},
    39  		{Name: "U16", Leaf: "U16", Value: new(uint16)},
    40  		{Name: "U32", Leaf: "U32", Value: new(uint32)},
    41  		{Name: "U64", Leaf: "U64", Value: new(uint64)},
    42  		{Name: "UGG", Leaf: "UGG", Value: new(uint64)},
    43  		{Name: "F32", Leaf: "F32", Value: new(float32)},
    44  		{Name: "F64", Leaf: "F64", Value: new(float64)},
    45  		{Name: "D16", Leaf: "D16", Value: new(root.Float16)},
    46  		{Name: "D32", Leaf: "D32", Value: new(root.Double32)},
    47  		// arrays
    48  		{Name: "ArrBs", Leaf: "ArrBs", Value: new([10]bool)},
    49  		{Name: "ArrI8", Leaf: "ArrI8", Value: new([10]int8)},
    50  		{Name: "ArrI16", Leaf: "ArrI16", Value: new([10]int16)},
    51  		{Name: "ArrI32", Leaf: "ArrI32", Value: new([10]int32)},
    52  		{Name: "ArrI64", Leaf: "ArrI64", Value: new([10]int64)},
    53  		{Name: "ArrG64", Leaf: "ArrG64", Value: new([10]int64)},
    54  		{Name: "ArrU8", Leaf: "ArrU8", Value: new([10]uint8)},
    55  		{Name: "ArrU16", Leaf: "ArrU16", Value: new([10]uint16)},
    56  		{Name: "ArrU32", Leaf: "ArrU32", Value: new([10]uint32)},
    57  		{Name: "ArrU64", Leaf: "ArrU64", Value: new([10]uint64)},
    58  		{Name: "ArrUGG", Leaf: "ArrUGG", Value: new([10]uint64)},
    59  		{Name: "ArrF32", Leaf: "ArrF32", Value: new([10]float32)},
    60  		{Name: "ArrF64", Leaf: "ArrF64", Value: new([10]float64)},
    61  		{Name: "ArrD16", Leaf: "ArrD16", Value: new([10]root.Float16)},
    62  		{Name: "ArrD32", Leaf: "ArrD32", Value: new([10]root.Double32)},
    63  		// slices
    64  		{Name: "N", Leaf: "N", Value: new(int32)},
    65  		{Name: "SliBs", Leaf: "SliBs", Value: new([]bool)},
    66  		{Name: "SliI8", Leaf: "SliI8", Value: new([]int8)},
    67  		{Name: "SliI16", Leaf: "SliI16", Value: new([]int16)},
    68  		{Name: "SliI32", Leaf: "SliI32", Value: new([]int32)},
    69  		{Name: "SliI64", Leaf: "SliI64", Value: new([]int64)},
    70  		{Name: "SliG64", Leaf: "SliG64", Value: new([]int64)},
    71  		{Name: "SliU8", Leaf: "SliU8", Value: new([]uint8)},
    72  		{Name: "SliU16", Leaf: "SliU16", Value: new([]uint16)},
    73  		{Name: "SliU32", Leaf: "SliU32", Value: new([]uint32)},
    74  		{Name: "SliU64", Leaf: "SliU64", Value: new([]uint64)},
    75  		{Name: "SliUGG", Leaf: "SliUGG", Value: new([]uint64)},
    76  		{Name: "SliF32", Leaf: "SliF32", Value: new([]float32)},
    77  		{Name: "SliF64", Leaf: "SliF64", Value: new([]float64)},
    78  		{Name: "SliD16", Leaf: "SliD16", Value: new([]root.Float16)},
    79  		{Name: "SliD32", Leaf: "SliD32", Value: new([]root.Double32)},
    80  	}
    81  
    82  	n := min(len(vars), len(want))
    83  
    84  	for i := range n {
    85  		got := vars[i]
    86  		if got.Name != want[i].Name {
    87  			t.Fatalf("invalid read-var name[%d]: got=%q, want=%q", i, got.Name, want[i].Name)
    88  		}
    89  		if got.Leaf != want[i].Leaf {
    90  			t.Fatalf("invalid read-var (name=%q) leaf-name[%d]: got=%q, want=%q", got.Name, i, got.Leaf, want[i].Leaf)
    91  		}
    92  		if got, want := reflect.TypeOf(got.Value), reflect.TypeOf(want[i].Value); got != want {
    93  			t.Fatalf("invalid read-var (name=%q) type[%d]: got=%v, want=%v", vars[i].Name, i, got, want)
    94  		}
    95  	}
    96  
    97  	if len(want) != len(vars) {
    98  		t.Fatalf("invalid lengths. got=%d, want=%d", len(vars), len(want))
    99  	}
   100  }
   101  
   102  func TestReadVarsFromStruct(t *testing.T) {
   103  	for _, tc := range []struct {
   104  		name   string
   105  		ptr    any
   106  		want   []ReadVar
   107  		panics string
   108  	}{
   109  		{
   110  			name: "not-ptr",
   111  			ptr: struct {
   112  				I32 int32
   113  			}{},
   114  			panics: "rtree: expect a pointer value, got struct { I32 int32 }",
   115  		},
   116  		{
   117  			name:   "not-ptr-to-struct",
   118  			ptr:    new(int32),
   119  			panics: "rtree: expect a pointer to struct value, got *int32",
   120  		},
   121  		{
   122  			name: "struct-with-int",
   123  			ptr: &struct {
   124  				I32 int
   125  				F32 float32
   126  				Str string
   127  			}{},
   128  			panics: "rtree: invalid field type for \"I32\": int",
   129  		},
   130  		{
   131  			name: "struct-with-map", // FIXME(sbinet)
   132  			ptr: &struct {
   133  				Map map[int32]string
   134  			}{},
   135  			panics: "rtree: invalid field type for \"Map\": map[int32]string (not yet supported)",
   136  		},
   137  		{
   138  			name: "invalid-struct-tag",
   139  			ptr: &struct {
   140  				N int32 `groot:"N[42]"`
   141  			}{},
   142  			panics: "rtree: invalid field type for \"N\", or invalid struct-tag \"N[42]\": int32",
   143  		},
   144  		{
   145  			name: "simple",
   146  			ptr: &struct {
   147  				I32 int32
   148  				F32 float32
   149  				Str string
   150  			}{},
   151  			want: []ReadVar{{Name: "I32"}, {Name: "F32"}, {Name: "Str"}},
   152  		},
   153  		{
   154  			name: "simple-with-unexported",
   155  			ptr: &struct {
   156  				I32 int32
   157  				F32 float32
   158  				val float32
   159  				Str string
   160  			}{},
   161  			want: []ReadVar{{Name: "I32"}, {Name: "F32"}, {Name: "Str"}},
   162  		},
   163  		{
   164  			name: "slices",
   165  			ptr: &struct {
   166  				N      int32
   167  				NN     int64
   168  				SliF32 []float32 `groot:"F32s[N]"`
   169  				SliF64 []float64 `groot:"F64s[NN]"`
   170  			}{},
   171  			want: []ReadVar{
   172  				{Name: "N"},
   173  				{Name: "NN"},
   174  				{Name: "F32s", count: "N"},
   175  				{Name: "F64s", count: "NN"},
   176  			},
   177  		},
   178  		{
   179  			name: "slices-no-count",
   180  			ptr: &struct {
   181  				F1 int32
   182  				F2 []float32 `groot:"F2[F1]"`
   183  				X3 []float64 `groot:"F3"`
   184  				F4 []float64
   185  			}{},
   186  			want: []ReadVar{
   187  				{Name: "F1"},
   188  				{Name: "F2", count: "F1"},
   189  				{Name: "F3"},
   190  				{Name: "F4"},
   191  			},
   192  		},
   193  		{
   194  			name: "arrays",
   195  			ptr: &struct {
   196  				N     int32 `groot:"n"`
   197  				Arr01 [10]float64
   198  				Arr02 [10][10]float64
   199  				Arr03 [10][10][10]float64
   200  				Arr11 [10]float64         `groot:"arr11[10]"`
   201  				Arr12 [10][10]float64     `groot:"arr12[10][10]"`
   202  				Arr13 [10][10][10]float64 `groot:"arr13[10][10][10]"`
   203  				Arr14 [10][10][10]float64 `groot:"arr14"`
   204  			}{},
   205  			want: []ReadVar{
   206  				{Name: "n"},
   207  				{Name: "Arr01"},
   208  				{Name: "Arr02"},
   209  				{Name: "Arr03"},
   210  				{Name: "arr11"},
   211  				{Name: "arr12"},
   212  				{Name: "arr13"},
   213  				{Name: "arr14"},
   214  			},
   215  		},
   216  		{
   217  			name: "struct-with-struct",
   218  			ptr: &struct {
   219  				F1 int64
   220  				F2 struct {
   221  					FF1 int64
   222  					FF2 float64
   223  					FF3 struct {
   224  						FFF1 float64
   225  					}
   226  				}
   227  			}{},
   228  			want: []ReadVar{
   229  				{Name: "F1"},
   230  				{Name: "F2"},
   231  			},
   232  		},
   233  		{
   234  			name: "struct-with-struct+slice",
   235  			ptr: &struct {
   236  				F1 int64
   237  				F2 struct {
   238  					FF1 int64
   239  					FF2 float64
   240  					FF3 []float64
   241  					FF4 []struct {
   242  						FFF1 float64
   243  						FFF2 []float64
   244  					}
   245  				}
   246  			}{},
   247  			want: []ReadVar{
   248  				{Name: "F1"},
   249  				{Name: "F2"},
   250  			},
   251  		},
   252  		{
   253  			name: "invalid-slice-tag",
   254  			ptr: &struct {
   255  				N   int32
   256  				Sli []int32 `groot:"vs[N][N]"`
   257  			}{},
   258  			panics: "rtree: invalid number of slice-dimensions for field \"Sli\": \"vs[N][N]\"",
   259  		},
   260  		{
   261  			name: "invalid-array-tag",
   262  			ptr: &struct {
   263  				N   int32
   264  				Arr [24]int32 `groot:"vs[1][2][3][4]"`
   265  			}{},
   266  			panics: "rtree: invalid number of array-dimension for field \"Arr\": \"vs[1][2][3][4]\"",
   267  		},
   268  		{
   269  			name: "invalid-array-tag-dimensions",
   270  			ptr: &struct {
   271  				N   int32
   272  				Arr [7]int32 `groot:"vs[1][2][x]"`
   273  			}{},
   274  			panics: "rtree: could not infer dimensions from \"vs[1][2][x]\": strconv.Atoi: parsing \"x\": invalid syntax",
   275  		},
   276  		{
   277  			name: "invalid-array-tag-capacity",
   278  			ptr: &struct {
   279  				N   int32
   280  				Arr [7]int32 `groot:"vs[1][2][3]"`
   281  			}{},
   282  			panics: "rtree: field type dimension inconsistency: groot-tag=\"vs[1][2][3]\" vs go-type=[7]int32: 6 vs 7",
   283  		},
   284  	} {
   285  		t.Run(tc.name, func(t *testing.T) {
   286  			if tc.panics != "" {
   287  				defer func() {
   288  					err := recover()
   289  					if err == nil {
   290  						t.Fatalf("expected a panic")
   291  					}
   292  					if got, want := err.(error).Error(), tc.panics; got != want {
   293  						t.Fatalf("invalid panic message:\ngot= %q\nwant=%q", got, want)
   294  					}
   295  				}()
   296  			}
   297  			got := ReadVarsFromStruct(tc.ptr)
   298  			if got, want := len(got), len(tc.want); got != want {
   299  				t.Fatalf("invalid number of rvars: got=%d, want=%d", got, want)
   300  			}
   301  			for i := range got {
   302  				if got, want := got[i].Name, tc.want[i].Name; got != want {
   303  					t.Fatalf("invalid name for rvar[%d]: got=%q, want=%q", i, got, want)
   304  				}
   305  				if got, want := got[i].count, tc.want[i].count; got != want {
   306  					t.Fatalf("invalid count for rvar[%d]: got=%q, want=%q", i, got, want)
   307  				}
   308  			}
   309  		})
   310  	}
   311  }
   312  
   313  func TestNameOf(t *testing.T) {
   314  	for _, tc := range []struct {
   315  		ptr  any
   316  		want string
   317  	}{
   318  		{
   319  			ptr: &struct {
   320  				F int64 `groot:"field"`
   321  			}{},
   322  			want: "field",
   323  		},
   324  		{
   325  			ptr: &struct {
   326  				F [1]int64 `groot:"field[1]"`
   327  			}{},
   328  			want: "field[1]",
   329  		},
   330  		{
   331  			ptr: &struct {
   332  				F [1]int64 `groot:"field"`
   333  			}{},
   334  			want: "field[1]",
   335  		},
   336  		{
   337  			ptr: &struct {
   338  				F [1][2][3]int64 `groot:"field"`
   339  			}{},
   340  			want: "field[1][2][3]",
   341  		},
   342  		{
   343  			ptr: &struct {
   344  				F [1][2][3]int64 `groot:"field[6]"`
   345  			}{},
   346  			want: "field[6]",
   347  		},
   348  		{
   349  			ptr: &struct {
   350  				F []int64 `groot:"field"`
   351  			}{},
   352  			want: "field",
   353  		},
   354  		{
   355  			ptr: &struct {
   356  				N int32   `groot:"N"`
   357  				F []int64 `groot:"field[N]"`
   358  			}{},
   359  			want: "field[N]",
   360  		},
   361  	} {
   362  		t.Run("", func(t *testing.T) {
   363  			rt := reflect.TypeOf(tc.ptr).Elem()
   364  			field, ok := rt.FieldByName("F")
   365  			if !ok {
   366  				t.Fatalf("could not retrieve field named \"F\" for %T", tc.ptr)
   367  			}
   368  			got := nameOf(field)
   369  			if got, want := got, tc.want; got != want {
   370  				t.Fatalf("invalid name: got=%q, want=%q", got, want)
   371  			}
   372  		})
   373  	}
   374  }