go-hep.org/x/hep@v0.38.1/groot/rtree/chain_test.go (about)

     1  // Copyright ©2018 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_test
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  	"testing"
    11  
    12  	"go-hep.org/x/hep/groot/internal/rtests"
    13  	"go-hep.org/x/hep/groot/riofs"
    14  	_ "go-hep.org/x/hep/groot/riofs/plugin/xrootd"
    15  	"go-hep.org/x/hep/groot/rtree"
    16  )
    17  
    18  func TestChain(t *testing.T) {
    19  	for _, tc := range []struct {
    20  		fnames  []string
    21  		entries int64
    22  		name    string
    23  		title   string
    24  		brs     []string
    25  		brOK    string
    26  		brNOT   string
    27  		lvs     []string
    28  		lvOK    string
    29  		lvNOT   string
    30  	}{
    31  		{
    32  			fnames:  nil,
    33  			entries: 0,
    34  			name:    "",
    35  			title:   "",
    36  		},
    37  		{
    38  			fnames:  []string{"../testdata/chain.1.root"},
    39  			entries: 10,
    40  			name:    "tree",
    41  			title:   "my tree title",
    42  			brs:     []string{"evt"},
    43  			brOK:    "evt",
    44  			brNOT:   "foo",
    45  			lvs:     []string{"evt"},
    46  			lvOK:    "evt",
    47  			lvNOT:   "foo",
    48  		},
    49  		{
    50  			fnames:  []string{rtests.XrdRemote("testdata/chain.1.root")},
    51  			entries: 10,
    52  			name:    "tree",
    53  			title:   "my tree title",
    54  			brs:     []string{"evt"},
    55  			brOK:    "evt",
    56  			brNOT:   "foo",
    57  			lvs:     []string{"evt"},
    58  			lvOK:    "evt",
    59  			lvNOT:   "foo",
    60  		},
    61  		{
    62  			// twice the same tree
    63  			fnames:  []string{"../testdata/chain.1.root", "../testdata/chain.1.root"},
    64  			entries: 20,
    65  			name:    "tree",
    66  			title:   "my tree title",
    67  			brs:     []string{"evt"},
    68  			brOK:    "evt",
    69  			brNOT:   "foo",
    70  			lvs:     []string{"evt"},
    71  			lvOK:    "evt",
    72  			lvNOT:   "foo",
    73  		},
    74  		{
    75  			// twice the same tree
    76  			fnames: []string{
    77  				rtests.XrdRemote("testdata/chain.1.root"),
    78  				rtests.XrdRemote("testdata/chain.1.root"),
    79  			},
    80  			entries: 20,
    81  			name:    "tree",
    82  			title:   "my tree title",
    83  			brs:     []string{"evt"},
    84  			brOK:    "evt",
    85  			brNOT:   "foo",
    86  			lvs:     []string{"evt"},
    87  			lvOK:    "evt",
    88  			lvNOT:   "foo",
    89  		},
    90  		{
    91  			// two different trees (with the same schema)
    92  			fnames:  []string{"../testdata/chain.1.root", "../testdata/chain.2.root"},
    93  			entries: 20,
    94  			name:    "tree",
    95  			title:   "my tree title",
    96  			brs:     []string{"evt"},
    97  			brOK:    "evt",
    98  			brNOT:   "foo",
    99  			lvs:     []string{"evt"},
   100  			lvOK:    "evt",
   101  			lvNOT:   "foo",
   102  		},
   103  		{
   104  			// two different trees (with the same schema)
   105  			fnames: []string{
   106  				rtests.XrdRemote("testdata/chain.1.root"),
   107  				rtests.XrdRemote("testdata/chain.2.root"),
   108  			},
   109  			entries: 20,
   110  			name:    "tree",
   111  			title:   "my tree title",
   112  			brs:     []string{"evt"},
   113  			brOK:    "evt",
   114  			brNOT:   "foo",
   115  			lvs:     []string{"evt"},
   116  			lvOK:    "evt",
   117  			lvNOT:   "foo",
   118  		},
   119  		{
   120  			// two different (flat) trees (with the same schema)
   121  			fnames: []string{
   122  				"../testdata/chain.flat.1.root",
   123  				"../testdata/chain.flat.2.root",
   124  			},
   125  			entries: 10,
   126  			name:    "tree",
   127  			title:   "my tree title",
   128  			brs: []string{
   129  				"B",
   130  				"Str",
   131  				"I8", "I16", "I32", "I64",
   132  				"U8", "U16", "U32", "U64",
   133  				"F32", "F64",
   134  				"ArrBs",
   135  				"ArrI8", "ArrI16", "ArrI32", "ArrI64",
   136  				"ArrU8", "ArrU16", "ArrU32", "ArrU64",
   137  				"ArrF32", "ArrF64",
   138  				"N",
   139  				"SliBs",
   140  				"SliI8", "SliI16", "SliI32", "SliI64",
   141  				"SliU8", "SliU16", "SliU32", "SliU64",
   142  				"SliF32", "SliF64",
   143  			},
   144  			brOK:  "N",
   145  			brNOT: "foo",
   146  			lvs: []string{
   147  				"B",
   148  				"Str",
   149  				"I8", "I16", "I32", "I64",
   150  				"U8", "U16", "U32", "U64",
   151  				"F32", "F64",
   152  				"ArrBs",
   153  				"ArrI8", "ArrI16", "ArrI32", "ArrI64",
   154  				"ArrU8", "ArrU16", "ArrU32", "ArrU64",
   155  				"ArrF32", "ArrF64",
   156  				"N",
   157  				"SliBs",
   158  				"SliI8", "SliI16", "SliI32", "SliI64",
   159  				"SliU8", "SliU16", "SliU32", "SliU64",
   160  				"SliF32", "SliF64",
   161  			},
   162  			lvOK:  "N",
   163  			lvNOT: "foo",
   164  		},
   165  		// TODO(sbinet): add a test with 2 trees with different schemas)
   166  	} {
   167  		t.Run("", func(t *testing.T) {
   168  			files := make([]*riofs.File, len(tc.fnames))
   169  			trees := make([]rtree.Tree, len(tc.fnames))
   170  			for i, fname := range tc.fnames {
   171  				f, err := riofs.Open(fname)
   172  				if err != nil {
   173  					t.Fatalf("could not open ROOT file %q: %v", fname, err)
   174  				}
   175  				defer f.Close()
   176  				files[i] = f
   177  
   178  				obj, err := f.Get(tc.name)
   179  				if err != nil {
   180  					t.Fatal(err)
   181  				}
   182  
   183  				trees[i] = obj.(rtree.Tree)
   184  			}
   185  
   186  			chain := rtree.Chain(trees...)
   187  
   188  			if got, want := chain.Class(), "TChain"; got != want {
   189  				t.Fatalf("class name differ\ngot = %q, want= %q", got, want)
   190  			}
   191  			if got, want := chain.Name(), tc.name; got != want {
   192  				t.Fatalf("names differ\ngot = %q, want= %q", got, want)
   193  			}
   194  			if got, want := chain.Title(), tc.title; got != want {
   195  				t.Fatalf("titles differ\ngot = %q, want= %q", got, want)
   196  			}
   197  			if got, want := chain.Entries(), tc.entries; got != want {
   198  				t.Fatalf("titles differ\ngot = %v, want= %v", got, want)
   199  			}
   200  			{
   201  				brs := chain.Branches()
   202  				n := min(len(brs), len(tc.brs))
   203  
   204  				for i := range n {
   205  					if got, want := brs[i].Name(), tc.brs[i]; got != want {
   206  						t.Fatalf("invalid branch name[%d]: got=%q, want=%q", i, got, want)
   207  					}
   208  				}
   209  
   210  				if got, want := len(brs), len(tc.brs); got != want {
   211  					t.Fatalf("invalid number of branches: got=%d, want=%d", got, want)
   212  				}
   213  
   214  				if tc.brOK != "" {
   215  					br := chain.Branch(tc.brOK)
   216  					if br == nil {
   217  						t.Fatalf("could not retrieve branch %q", tc.brOK)
   218  					}
   219  					if got, want := br.Name(), tc.brOK; got != want {
   220  						t.Fatalf("invalid name for branch-ok: got=%q, want=%q", got, want)
   221  					}
   222  				}
   223  
   224  				br := chain.Branch(tc.brNOT)
   225  				if br != nil {
   226  					t.Fatalf("unexpected branch for branch-not (%s): got=%#v", tc.brNOT, br)
   227  				}
   228  			}
   229  			{
   230  				lvs := chain.Leaves()
   231  				n := min(len(lvs), len(tc.lvs))
   232  
   233  				for i := range n {
   234  					if got, want := lvs[i].Name(), tc.lvs[i]; got != want {
   235  						t.Fatalf("invalid leaf name[%d]: got=%q, want=%q", i, got, want)
   236  					}
   237  				}
   238  
   239  				if got, want := len(lvs), len(tc.lvs); got != want {
   240  					t.Fatalf("invalid number of leaves: got=%d, want=%d", got, want)
   241  				}
   242  
   243  				if tc.lvOK != "" {
   244  					lv := chain.Leaf(tc.lvOK)
   245  					if lv == nil {
   246  						t.Fatalf("could not retrieve leaf %q", tc.lvOK)
   247  					}
   248  					if got, want := lv.Name(), tc.lvOK; got != want {
   249  						t.Fatalf("invalid name for leaf-ok: got=%q, want=%q", got, want)
   250  					}
   251  					br := lv.Branch()
   252  					if br == nil || br.Name() != tc.lvOK {
   253  						t.Fatalf("invalid leaf-branch: ptr-ok=%v", br != nil)
   254  					}
   255  				}
   256  				lv := chain.Leaf(tc.lvNOT)
   257  				if lv != nil {
   258  					t.Fatalf("unexpected leaf for leaf-not (%s): got=%#v", tc.lvNOT, lv)
   259  				}
   260  			}
   261  		})
   262  	}
   263  }
   264  
   265  func TestChainOf(t *testing.T) {
   266  	for _, tc := range []struct {
   267  		fnames  []string
   268  		entries int64
   269  		name    string
   270  		title   string
   271  	}{
   272  		{
   273  			fnames:  nil,
   274  			entries: 0,
   275  			name:    "",
   276  			title:   "",
   277  		},
   278  		{
   279  			fnames:  []string{"../testdata/chain.1.root"},
   280  			entries: 10,
   281  			name:    "tree",
   282  			title:   "my tree title",
   283  		},
   284  		{
   285  			fnames:  []string{rtests.XrdRemote("testdata/chain.1.root")},
   286  			entries: 10,
   287  			name:    "tree",
   288  			title:   "my tree title",
   289  		},
   290  		{
   291  			// twice the same tree
   292  			fnames:  []string{"../testdata/chain.1.root", "../testdata/chain.1.root"},
   293  			entries: 20,
   294  			name:    "tree",
   295  			title:   "my tree title",
   296  		},
   297  		{
   298  			// twice the same tree
   299  			fnames: []string{
   300  				rtests.XrdRemote("testdata/chain.1.root"),
   301  				rtests.XrdRemote("testdata/chain.1.root"),
   302  			},
   303  			entries: 20,
   304  			name:    "tree",
   305  			title:   "my tree title",
   306  		},
   307  		{
   308  			// two different trees (with the same schema)
   309  			fnames:  []string{"../testdata/chain.1.root", "../testdata/chain.2.root"},
   310  			entries: 20,
   311  			name:    "tree",
   312  			title:   "my tree title",
   313  		},
   314  		{
   315  			// two different trees (with the same schema)
   316  			fnames: []string{
   317  				rtests.XrdRemote("testdata/chain.1.root"),
   318  				rtests.XrdRemote("testdata/chain.2.root"),
   319  			},
   320  			entries: 20,
   321  			name:    "tree",
   322  			title:   "my tree title",
   323  		},
   324  		// TODO(sbinet): add a test with 2 trees with different schemas)
   325  	} {
   326  		t.Run("", func(t *testing.T) {
   327  			chain, closer, err := rtree.ChainOf(tc.name, tc.fnames...)
   328  			if err != nil {
   329  				t.Fatalf("could not create chain: %v", err)
   330  			}
   331  			defer func() {
   332  				_ = closer()
   333  			}()
   334  
   335  			if got, want := chain.Name(), tc.name; got != want {
   336  				t.Fatalf("names differ\ngot = %q, want= %q", got, want)
   337  			}
   338  			if got, want := chain.Title(), tc.title; got != want {
   339  				t.Fatalf("titles differ\ngot = %q, want= %q", got, want)
   340  			}
   341  			if got, want := chain.Entries(), tc.entries; got != want {
   342  				t.Fatalf("titles differ\ngot = %v, want= %v", got, want)
   343  			}
   344  		})
   345  	}
   346  }
   347  
   348  func TestChainReaderStruct(t *testing.T) {
   349  	files := []string{
   350  		"../testdata/chain.1.root",
   351  		"../testdata/chain.2.root",
   352  	}
   353  	var total struct {
   354  		got, want int64
   355  	}
   356  	trees := make([]rtree.Tree, len(files))
   357  	for i, fname := range files {
   358  		f, err := riofs.Open(fname)
   359  		if err != nil {
   360  			t.Fatalf("could not open ROOT file %q: %v", fname, err)
   361  		}
   362  		defer f.Close()
   363  
   364  		obj, err := f.Get("tree")
   365  		if err != nil {
   366  			t.Fatal(err)
   367  		}
   368  
   369  		trees[i] = obj.(rtree.Tree)
   370  		total.want += trees[i].Entries()
   371  	}
   372  
   373  	chain := rtree.Chain(trees...)
   374  
   375  	type Data struct {
   376  		Event struct {
   377  			Beg       string      `groot:"Beg"`
   378  			F64       float64     `groot:"F64"`
   379  			ArrF64    [10]float64 `groot:"ArrayF64"`
   380  			N         int32       `groot:"N"`
   381  			SliF64    []float64   `groot:"SliceF64"`
   382  			StdStr    string      `groot:"StdStr"`
   383  			StlVecF64 []float64   `groot:"StlVecF64"`
   384  			StlVecStr []string    `groot:"StlVecStr"`
   385  			End       string      `groot:"End"`
   386  		} `groot:"evt"`
   387  	}
   388  
   389  	want := func(i int64) (data Data) {
   390  		evt := &data.Event
   391  		evt.Beg = fmt.Sprintf("beg-%03d", i)
   392  		evt.F64 = float64(i)
   393  		for j := range evt.ArrF64 {
   394  			evt.ArrF64[j] = float64(i)
   395  		}
   396  		evt.N = int32(i) % 10
   397  		evt.StdStr = fmt.Sprintf("std-%03d", i)
   398  		switch i {
   399  		case 0:
   400  			evt.SliF64 = nil
   401  			evt.StlVecF64 = nil
   402  			evt.StlVecStr = nil
   403  		default:
   404  			evt.SliF64 = make([]float64, evt.N)
   405  			evt.StlVecF64 = make([]float64, int(evt.N))
   406  			evt.StlVecStr = make([]string, int(evt.N))
   407  		}
   408  		for ii := range int(evt.N) {
   409  			evt.SliF64[ii] = float64(i)
   410  			evt.StlVecF64[ii] = float64(i)
   411  			evt.StlVecStr[ii] = fmt.Sprintf("vec-%03d", i)
   412  		}
   413  		evt.End = fmt.Sprintf("end-%03d", i)
   414  
   415  		return data
   416  	}
   417  
   418  	var data Data
   419  	r, err := rtree.NewReader(chain, rtree.ReadVarsFromStruct(&data))
   420  	if err != nil {
   421  		t.Fatal(err)
   422  	}
   423  	defer r.Close()
   424  
   425  	err = r.Read(func(ctx rtree.RCtx) error {
   426  		i := ctx.Entry
   427  		if !reflect.DeepEqual(data, want(i)) {
   428  			return fmt.Errorf("entry[%d]:\ngot= %#v\nwant=%#v\n", i, data, want(i))
   429  		}
   430  		total.got++
   431  		return nil
   432  	})
   433  	if err != nil {
   434  		t.Fatal(err)
   435  	}
   436  
   437  	if total.got != total.want {
   438  		t.Fatalf("entries differ: got=%d want=%d", total.got, total.want)
   439  	}
   440  }