go-hep.org/x/hep@v0.38.1/groot/rcmd/merge_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 rcmd_test
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"os"
    11  	"path/filepath"
    12  	"testing"
    13  
    14  	"go-hep.org/x/hep/groot"
    15  	"go-hep.org/x/hep/groot/rcmd"
    16  	"go-hep.org/x/hep/groot/rhist"
    17  	"go-hep.org/x/hep/groot/riofs"
    18  	"go-hep.org/x/hep/groot/rtree"
    19  	"go-hep.org/x/hep/hbook"
    20  	"go-hep.org/x/hep/hbook/rootcnv"
    21  )
    22  
    23  func TestMerge(t *testing.T) {
    24  	tmp, err := os.MkdirTemp("", "groot-root-merge-")
    25  	if err != nil {
    26  		t.Fatalf("%+v", err)
    27  	}
    28  	defer os.RemoveAll(tmp)
    29  
    30  	type funcT func(t *testing.T, fname string) error
    31  	for _, tc := range []struct {
    32  		name   string
    33  		inputs []funcT
    34  		output funcT
    35  		panics string
    36  	}{
    37  		{
    38  			name:   "flat-tree-1",
    39  			inputs: []funcT{makeFlatTree(1)},
    40  			output: makeFlatTree(1),
    41  		},
    42  		{
    43  			name:   "flat-tree-2",
    44  			inputs: []funcT{makeFlatTree(1), makeFlatTree(1)},
    45  			output: makeFlatTree(2),
    46  		},
    47  		{
    48  			name:   "h1f-1",
    49  			inputs: []funcT{makeH1F(1)},
    50  			output: makeH1F(1),
    51  		},
    52  		{
    53  			name:   "h1f-2",
    54  			inputs: []funcT{makeH1F(1), makeH1F(1)},
    55  			output: makeH1F(2),
    56  		},
    57  		{
    58  			name:   "h1d-1",
    59  			inputs: []funcT{makeH1D(1)},
    60  			output: makeH1D(1),
    61  		},
    62  		{
    63  			name:   "h1d-2",
    64  			inputs: []funcT{makeH1D(1), makeH1D(1)},
    65  			output: makeH1D(2),
    66  		},
    67  		{
    68  			name:   "h1i-1",
    69  			inputs: []funcT{makeH1I(1)},
    70  			output: makeH1I(1),
    71  		},
    72  		{
    73  			name:   "h1i-2",
    74  			inputs: []funcT{makeH1I(1), makeH1I(1)},
    75  			output: makeH1I(2),
    76  		},
    77  		{
    78  			name:   "h2d-1",
    79  			inputs: []funcT{makeH2D(1)},
    80  			output: makeH2D(1),
    81  		},
    82  		{
    83  			name:   "h2d-2",
    84  			inputs: []funcT{makeH2D(1), makeH2D(1)},
    85  			output: makeH2D(2),
    86  			panics: "not implemented", // FIXME(sbinet)
    87  		},
    88  		{
    89  			name:   "graph-1",
    90  			inputs: []funcT{makeGraph(0, 1)},
    91  			output: makeGraph(0, 1),
    92  		},
    93  		{
    94  			name:   "graph-2",
    95  			inputs: []funcT{makeGraph(0, 1), makeGraph(1, 2)},
    96  			output: makeGraph(0, 2),
    97  		},
    98  		{
    99  			name:   "graph-err-1",
   100  			inputs: []funcT{makeGraphErr(0, 1)},
   101  			output: makeGraphErr(0, 1),
   102  		},
   103  		{
   104  			name:   "graph-err-2",
   105  			inputs: []funcT{makeGraphErr(0, 1), makeGraphErr(1, 2)},
   106  			output: makeGraphErr(0, 2),
   107  		},
   108  		{
   109  			name:   "graph-asymmerr-1",
   110  			inputs: []funcT{makeGraphAsymmErr(0, 1)},
   111  			output: makeGraphAsymmErr(0, 1),
   112  		},
   113  		{
   114  			name:   "graph-asymmerr-2",
   115  			inputs: []funcT{makeGraphAsymmErr(0, 1), makeGraphAsymmErr(1, 2)},
   116  			output: makeGraphAsymmErr(0, 2),
   117  		},
   118  	} {
   119  		t.Run(tc.name, func(t *testing.T) {
   120  			var (
   121  				fnames  []string
   122  				oname   = filepath.Join(tmp, tc.name+".out.root")
   123  				verbose = true
   124  				deep    = true
   125  			)
   126  			for i, fct := range tc.inputs {
   127  				fname := filepath.Join(tmp, fmt.Sprintf("%s-%02d.root", tc.name, i))
   128  				err := fct(t, fname)
   129  				if err != nil {
   130  					t.Fatalf("%+v", err)
   131  				}
   132  				fnames = append(fnames, fname)
   133  			}
   134  			refname := filepath.Join(tmp, tc.name+".want.root")
   135  			err := tc.output(t, refname)
   136  			if err != nil {
   137  				t.Fatalf("%+v", err)
   138  			}
   139  
   140  			if tc.panics != "" {
   141  				defer func() {
   142  					err := recover()
   143  					if err == nil {
   144  						t.Fatalf("expected a panic")
   145  					}
   146  					if got, want := err.(string), tc.panics; got != want {
   147  						t.Fatalf("invalid panic message. got=%q, want=%q", got, want)
   148  					}
   149  				}()
   150  			}
   151  
   152  			err = rcmd.Merge(oname, fnames, verbose)
   153  			if err != nil {
   154  				t.Fatalf("could not run root-merge: %+v", err)
   155  			}
   156  
   157  			got := new(bytes.Buffer)
   158  			err = rcmd.Dump(got, oname, deep, nil)
   159  			if err != nil {
   160  				t.Fatalf("could not run root-dump: %+v", err)
   161  			}
   162  
   163  			want := new(bytes.Buffer)
   164  			err = rcmd.Dump(want, refname, deep, nil)
   165  			if err != nil {
   166  				t.Fatalf("could not run root-dump: %+v", err)
   167  			}
   168  
   169  			if got, want := got.String(), want.String(); got != want {
   170  				t.Fatalf("invalid root-merge output:\ngot:\n%swant:\n%s", got, want)
   171  			}
   172  		})
   173  	}
   174  }
   175  
   176  func makeFlatTree(n int) func(t *testing.T, fname string) error {
   177  	return func(t *testing.T, fname string) error {
   178  		type Data struct {
   179  			I32    int32
   180  			F64    float64
   181  			Str    string
   182  			ArrF64 [5]float64
   183  			N      int32
   184  			SliF64 []float64 `groot:"SliF64[N]"`
   185  		}
   186  		const (
   187  			nevts = 5
   188  		)
   189  
   190  		f, err := groot.Create(fname)
   191  		if err != nil {
   192  			t.Fatalf("%+v", err)
   193  		}
   194  		defer f.Close()
   195  
   196  		dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   197  		if err != nil {
   198  			t.Fatalf("could not create directory: %+v", err)
   199  		}
   200  
   201  		var evt Data
   202  		tree, err := rtree.NewWriter(dir, "mytree", rtree.WriteVarsFromStruct(&evt))
   203  		if err != nil {
   204  			t.Fatalf("could not create tree writer: %+v", err)
   205  		}
   206  
   207  		for range n {
   208  			for i := range nevts {
   209  				evt.I32 = int32(i)
   210  				evt.F64 = float64(i)
   211  				evt.Str = fmt.Sprintf("evt-%0d", i)
   212  				evt.ArrF64 = [5]float64{float64(i), float64(i + 1), float64(i + 2), float64(i + 3), float64(i + 4)}
   213  				evt.N = int32(i)
   214  				evt.SliF64 = []float64{float64(i), float64(i + 1), float64(i + 2), float64(i + 3), float64(i + 4)}[:i]
   215  				_, err = tree.Write()
   216  				if err != nil {
   217  					t.Fatalf("could not write event %d: %+v", i, err)
   218  				}
   219  			}
   220  		}
   221  
   222  		err = tree.Close()
   223  		if err != nil {
   224  			t.Fatalf("could not write tree: %+v", err)
   225  		}
   226  
   227  		err = f.Close()
   228  		if err != nil {
   229  			t.Fatalf("could not close file: %+v", err)
   230  		}
   231  
   232  		return nil
   233  	}
   234  }
   235  
   236  func makeH1F(n int) func(t *testing.T, fname string) error {
   237  	return func(t *testing.T, fname string) error {
   238  		f, err := groot.Create(fname)
   239  		if err != nil {
   240  			t.Fatalf("%+v", err)
   241  		}
   242  		defer f.Close()
   243  
   244  		_, err = riofs.Dir(f).Mkdir("dir-1/dir-11")
   245  		if err != nil {
   246  			t.Fatalf("could not create directory: %+v", err)
   247  		}
   248  
   249  		dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11")
   250  		if err != nil {
   251  			t.Fatalf("could not create directory: %+v", err)
   252  		}
   253  
   254  		h := hbook.NewH1D(10, 0, 10)
   255  		h.Annotation()["title"] = "h1f"
   256  		for range n {
   257  			h.Fill(5, 1)
   258  			h.Fill(6, 2)
   259  		}
   260  
   261  		err = dir21.Put("h1f", rhist.NewH1FFrom(h))
   262  		if err != nil {
   263  			t.Fatalf("could not save H1F: %+v", err)
   264  		}
   265  
   266  		err = f.Close()
   267  		if err != nil {
   268  			t.Fatalf("could not close file: %+v", err)
   269  		}
   270  
   271  		return nil
   272  	}
   273  }
   274  
   275  func makeH1D(n int) func(t *testing.T, fname string) error {
   276  	return func(t *testing.T, fname string) error {
   277  		f, err := groot.Create(fname)
   278  		if err != nil {
   279  			t.Fatalf("%+v", err)
   280  		}
   281  		defer f.Close()
   282  
   283  		_, err = riofs.Dir(f).Mkdir("dir-1/dir-11")
   284  		if err != nil {
   285  			t.Fatalf("could not create directory: %+v", err)
   286  		}
   287  
   288  		dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11")
   289  		if err != nil {
   290  			t.Fatalf("could not create directory: %+v", err)
   291  		}
   292  
   293  		h := hbook.NewH1D(10, 0, 10)
   294  		h.Annotation()["title"] = "h1d"
   295  		for range n {
   296  			h.Fill(5, 1)
   297  			h.Fill(6, 2)
   298  		}
   299  
   300  		err = dir21.Put("h1d", rootcnv.FromH1D(h))
   301  		if err != nil {
   302  			t.Fatalf("could not save H1D: %+v", err)
   303  		}
   304  
   305  		err = f.Close()
   306  		if err != nil {
   307  			t.Fatalf("could not close file: %+v", err)
   308  		}
   309  
   310  		return nil
   311  	}
   312  }
   313  
   314  func makeH1I(n int) func(t *testing.T, fname string) error {
   315  	return func(t *testing.T, fname string) error {
   316  		f, err := groot.Create(fname)
   317  		if err != nil {
   318  			t.Fatalf("%+v", err)
   319  		}
   320  		defer f.Close()
   321  
   322  		_, err = riofs.Dir(f).Mkdir("dir-1/dir-11")
   323  		if err != nil {
   324  			t.Fatalf("could not create directory: %+v", err)
   325  		}
   326  
   327  		dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11")
   328  		if err != nil {
   329  			t.Fatalf("could not create directory: %+v", err)
   330  		}
   331  
   332  		h := hbook.NewH1D(10, 0, 10)
   333  		h.Annotation()["title"] = "h1i"
   334  		for range n {
   335  			h.Fill(5, 1)
   336  			h.Fill(6, 2)
   337  		}
   338  
   339  		err = dir21.Put("h1i", rhist.NewH1IFrom(h))
   340  		if err != nil {
   341  			t.Fatalf("could not save H1I: %+v", err)
   342  		}
   343  
   344  		err = f.Close()
   345  		if err != nil {
   346  			t.Fatalf("could not close file: %+v", err)
   347  		}
   348  
   349  		return nil
   350  	}
   351  }
   352  
   353  func makeH2D(n int) func(t *testing.T, fname string) error {
   354  	return func(t *testing.T, fname string) error {
   355  		f, err := groot.Create(fname)
   356  		if err != nil {
   357  			t.Fatalf("%+v", err)
   358  		}
   359  		defer f.Close()
   360  
   361  		_, err = riofs.Dir(f).Mkdir("dir-1/dir-11")
   362  		if err != nil {
   363  			t.Fatalf("could not create directory: %+v", err)
   364  		}
   365  
   366  		dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11")
   367  		if err != nil {
   368  			t.Fatalf("could not create directory: %+v", err)
   369  		}
   370  
   371  		h := hbook.NewH2D(10, 0, 10, 10, 0, 10)
   372  		h.Annotation()["title"] = "h2d"
   373  		for range n {
   374  			h.Fill(5, 5, 1)
   375  			h.Fill(6, 6, 2)
   376  		}
   377  
   378  		err = dir21.Put("h2d", rootcnv.FromH2D(h))
   379  		if err != nil {
   380  			t.Fatalf("could not save H2D: %+v", err)
   381  		}
   382  
   383  		err = f.Close()
   384  		if err != nil {
   385  			t.Fatalf("could not close file: %+v", err)
   386  		}
   387  
   388  		return nil
   389  	}
   390  }
   391  
   392  func makeGraph(beg, end int) func(t *testing.T, fname string) error {
   393  	return func(t *testing.T, fname string) error {
   394  		f, err := groot.Create(fname)
   395  		if err != nil {
   396  			t.Fatalf("%+v", err)
   397  		}
   398  		defer f.Close()
   399  
   400  		_, err = riofs.Dir(f).Mkdir("dir-1/dir-11")
   401  		if err != nil {
   402  			t.Fatalf("could not create directory: %+v", err)
   403  		}
   404  
   405  		dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11")
   406  		if err != nil {
   407  			t.Fatalf("could not create directory: %+v", err)
   408  		}
   409  
   410  		var (
   411  			xs []float64
   412  			ys []float64
   413  		)
   414  		for i := beg; i < end; i++ {
   415  			for j := range 10 {
   416  				xs = append(xs, float64(10*(1+i)+j))
   417  				ys = append(ys, float64(10*(1+i)+j))
   418  			}
   419  		}
   420  
   421  		gr := hbook.NewS2DFrom(xs, ys)
   422  		gr.Annotation()["title"] = "graph"
   423  		err = dir21.Put("graph", rhist.NewGraphFrom(gr))
   424  		if err != nil {
   425  			t.Fatalf("could not save S2D: %+v", err)
   426  		}
   427  
   428  		err = f.Close()
   429  		if err != nil {
   430  			t.Fatalf("could not close file: %+v", err)
   431  		}
   432  
   433  		return nil
   434  	}
   435  }
   436  
   437  func makeGraphErr(beg, end int) func(t *testing.T, fname string) error {
   438  	return func(t *testing.T, fname string) error {
   439  		f, err := groot.Create(fname)
   440  		if err != nil {
   441  			t.Fatalf("%+v", err)
   442  		}
   443  		defer f.Close()
   444  
   445  		_, err = riofs.Dir(f).Mkdir("dir-1/dir-11")
   446  		if err != nil {
   447  			t.Fatalf("could not create directory: %+v", err)
   448  		}
   449  
   450  		dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11")
   451  		if err != nil {
   452  			t.Fatalf("could not create directory: %+v", err)
   453  		}
   454  
   455  		var (
   456  			pts []hbook.Point2D
   457  		)
   458  		for i := beg; i < end; i++ {
   459  			for j := range 10 {
   460  				var (
   461  					x    = float64(10*(1+i) + j)
   462  					y    = float64(10*(1+i) + j)
   463  					xerr = 2.5
   464  					yerr = 3.5
   465  				)
   466  				pts = append(pts, hbook.Point2D{
   467  					X:    x,
   468  					Y:    y,
   469  					ErrX: hbook.Range{Min: xerr, Max: xerr},
   470  					ErrY: hbook.Range{Min: yerr, Max: yerr},
   471  				})
   472  			}
   473  		}
   474  
   475  		gr := hbook.NewS2D(pts...)
   476  		gr.Annotation()["title"] = "graph"
   477  		err = dir21.Put("graph", rhist.NewGraphErrorsFrom(gr))
   478  		if err != nil {
   479  			t.Fatalf("could not save S2D: %+v", err)
   480  		}
   481  
   482  		err = f.Close()
   483  		if err != nil {
   484  			t.Fatalf("could not close file: %+v", err)
   485  		}
   486  
   487  		return nil
   488  	}
   489  }
   490  
   491  func makeGraphAsymmErr(beg, end int) func(t *testing.T, fname string) error {
   492  	return func(t *testing.T, fname string) error {
   493  		f, err := groot.Create(fname)
   494  		if err != nil {
   495  			t.Fatalf("%+v", err)
   496  		}
   497  		defer f.Close()
   498  
   499  		_, err = riofs.Dir(f).Mkdir("dir-1/dir-11")
   500  		if err != nil {
   501  			t.Fatalf("could not create directory: %+v", err)
   502  		}
   503  
   504  		dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11")
   505  		if err != nil {
   506  			t.Fatalf("could not create directory: %+v", err)
   507  		}
   508  
   509  		var (
   510  			pts []hbook.Point2D
   511  		)
   512  		for i := beg; i < end; i++ {
   513  			for j := range 10 {
   514  				var (
   515  					x = float64(10*(1+i) + j)
   516  					y = float64(10*(1+i) + j)
   517  				)
   518  				pts = append(pts, hbook.Point2D{
   519  					X:    x,
   520  					Y:    y,
   521  					ErrX: hbook.Range{Min: 1.5, Max: 2.5},
   522  					ErrY: hbook.Range{Min: 1.5, Max: 2.5},
   523  				})
   524  			}
   525  		}
   526  
   527  		gr := hbook.NewS2D(pts...)
   528  		gr.Annotation()["title"] = "graph"
   529  		err = dir21.Put("graph", rhist.NewGraphAsymmErrorsFrom(gr))
   530  		if err != nil {
   531  			t.Fatalf("could not save S2D: %+v", err)
   532  		}
   533  
   534  		err = f.Close()
   535  		if err != nil {
   536  			t.Fatalf("could not close file: %+v", err)
   537  		}
   538  
   539  		return nil
   540  	}
   541  }