go-hep.org/x/hep@v0.38.1/groot/riofs/file_test.go (about)

     1  // Copyright ©2017 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 riofs_test
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"os"
    11  	"path/filepath"
    12  	"reflect"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"go-hep.org/x/hep/groot"
    18  	"go-hep.org/x/hep/groot/internal/rtests"
    19  	"go-hep.org/x/hep/groot/rbase"
    20  	"go-hep.org/x/hep/groot/rcont"
    21  	"go-hep.org/x/hep/groot/riofs"
    22  	"go-hep.org/x/hep/groot/root"
    23  	"go-hep.org/x/hep/groot/rtree"
    24  )
    25  
    26  func TestFileSegmentMap(t *testing.T) {
    27  	f, err := groot.Open("../testdata/dirs-6.14.00.root")
    28  	if err != nil {
    29  		t.Fatalf("could not open ROOT file: %+v", err)
    30  	}
    31  	defer f.Close()
    32  
    33  	out := new(bytes.Buffer)
    34  	err = f.SegmentMap(out)
    35  	if err != nil {
    36  		t.Fatalf("could not run segment map: %+v", err)
    37  	}
    38  
    39  	got := out.String()
    40  	want := `20180703/110855  At:100    N=130       TFile         
    41  20180703/110855  At:230    N=107       TDirectory    
    42  20180703/110855  At:337    N=107       TDirectory    
    43  20180703/110855  At:444    N=107       TDirectory    
    44  20180703/110855  At:551    N=109       TDirectory    
    45  20180703/110855  At:660    N=345       TH1F           CX =  2.82
    46  20180703/110855  At:1005   N=90        TDirectory    
    47  20180703/110855  At:1095   N=100       TDirectory    
    48  20180703/110855  At:1195   N=51        TDirectory    
    49  20180703/110855  At:1246   N=51        TDirectory    
    50  20180703/110855  At:1297   N=196       KeysList      
    51  20180703/110855  At:1493   N=3845      StreamerInfo   CX =  2.44
    52  20180703/110855  At:5338   N=61        FreeSegments  
    53  20180703/110855  At:5399   N=1         END           
    54  `
    55  
    56  	if got != want {
    57  		t.Fatalf("invalid segment map:\ngot:\n%v\nwant:\n%v\n", got, want)
    58  	}
    59  }
    60  
    61  func TestFileRecords(t *testing.T) {
    62  	f, err := groot.Open("../testdata/dirs-6.14.00.root")
    63  	if err != nil {
    64  		t.Fatalf("could not open ROOT file: %+v", err)
    65  	}
    66  	defer f.Close()
    67  
    68  	out := new(bytes.Buffer)
    69  	err = f.Records(out)
    70  	if err != nil {
    71  		t.Fatalf("could not run segment map: %+v", err)
    72  	}
    73  
    74  	got := out.String()
    75  	want := `=== file "../testdata/dirs-6.14.00.root" ===
    76  begin: 100
    77  end:   5399
    78  seek-free: 5338 nbytes-free=61 nfree=1
    79  seek-info: 1493 nbytes-info=3845
    80  === dir "dirs-6.14.00.root" @100 ===
    81  parent:      <nil>
    82  nbytes-keys: 196
    83  nbytes-name: 70
    84  seek-dir:    100
    85  seek-parent: 0
    86  seek-keys:   1297
    87  class:       "TFile"
    88  keys:        3
    89   key[0]: "dir1"
    90    === key "dir1" ===
    91    nbytes:    107
    92    keylen:    47
    93    objlen:    60
    94    cycle:     1
    95    seek-key:  230
    96    seek-pdir: 100
    97    class:     "TDirectoryFile"
    98    parent:    @100
    99      === dir "dir1" @230 ===
   100      parent:      @100
   101      nbytes-keys: 100
   102      nbytes-name: 47
   103      seek-dir:    230
   104      seek-parent: 100
   105      seek-keys:   1095
   106      class:       "TDirectoryFile"
   107      keys:        1
   108       key[0]: "dir11"
   109        === key "dir11" ===
   110        nbytes:    109
   111        keylen:    49
   112        objlen:    60
   113        cycle:     1
   114        seek-key:  551
   115        seek-pdir: 230
   116        class:     "TDirectoryFile"
   117        parent:    @230
   118          === dir "dir11" @551 ===
   119          parent:      @230
   120          nbytes-keys: 90
   121          nbytes-name: 49
   122          seek-dir:    551
   123          seek-parent: 100
   124          seek-keys:   1005
   125          class:       "TDirectoryFile"
   126          keys:        1
   127           key[0]: "h1"
   128            === key "h1" ===
   129            nbytes:    345
   130            keylen:    37
   131            objlen:    936
   132            cycle:     1
   133            seek-key:  660
   134            seek-pdir: 551
   135            class:     "TH1F"
   136            parent:    @551
   137   key[1]: "dir2"
   138    === key "dir2" ===
   139    nbytes:    107
   140    keylen:    47
   141    objlen:    60
   142    cycle:     1
   143    seek-key:  337
   144    seek-pdir: 100
   145    class:     "TDirectoryFile"
   146    parent:    @100
   147      === dir "dir2" @337 ===
   148      parent:      @100
   149      nbytes-keys: 51
   150      nbytes-name: 47
   151      seek-dir:    337
   152      seek-parent: 100
   153      seek-keys:   1195
   154      class:       "TDirectoryFile"
   155      keys:        0
   156   key[2]: "dir3"
   157    === key "dir3" ===
   158    nbytes:    107
   159    keylen:    47
   160    objlen:    60
   161    cycle:     1
   162    seek-key:  444
   163    seek-pdir: 100
   164    class:     "TDirectoryFile"
   165    parent:    @100
   166      === dir "dir3" @444 ===
   167      parent:      @100
   168      nbytes-keys: 51
   169      nbytes-name: 47
   170      seek-dir:    444
   171      seek-parent: 100
   172      seek-keys:   1246
   173      class:       "TDirectoryFile"
   174      keys:        0
   175  `
   176  
   177  	if got != want {
   178  		t.Fatalf("invalid segment map:\ngot:\n%v\nwant:\n%v\n", got, want)
   179  	}
   180  }
   181  
   182  func TestFileDirectory(t *testing.T) {
   183  	for _, fname := range []string{
   184  		"../testdata/small-flat-tree.root",
   185  		rtests.XrdRemote("testdata/small-flat-tree.root"),
   186  	} {
   187  		t.Run(fname, func(t *testing.T) {
   188  			f, err := groot.Open(fname)
   189  			if err != nil {
   190  				t.Fatal(err.Error())
   191  			}
   192  			defer f.Close()
   193  
   194  			for _, table := range []struct {
   195  				test  string
   196  				value string
   197  				want  string
   198  			}{
   199  				{"Name", f.Name(), "test-small.root"}, // name when created
   200  				{"Title", f.Title(), "small event file"},
   201  				{"Class", f.Class(), "TFile"},
   202  			} {
   203  				if table.value != table.want {
   204  					t.Fatalf("%v: got=%q, want=%q", table.test, table.value, table.want)
   205  				}
   206  			}
   207  
   208  			for _, table := range []struct {
   209  				name string
   210  				want bool
   211  			}{
   212  				{"tree", true},
   213  				{"tree;0", false},
   214  				{"tree;1", true},
   215  				{"tree;9999", true},
   216  				{"tree_nope", false},
   217  				{"tree_nope;0", false},
   218  				{"tree_nope;1", false},
   219  				{"tree_nope;9999", false},
   220  			} {
   221  				_, err := f.Get(table.name)
   222  				if (err == nil) != table.want {
   223  					t.Fatalf("%s: got key (err=%v). want=%v", table.name, err, table.want)
   224  				}
   225  			}
   226  
   227  			for _, table := range []struct {
   228  				name string
   229  				want string
   230  			}{
   231  				{"tree", "TTree"},
   232  				{"tree;1", "TTree"},
   233  			} {
   234  				k, err := f.Get(table.name)
   235  				if err != nil {
   236  					t.Fatalf("%s: expected key to exist! (got %v)", table.name, err)
   237  				}
   238  
   239  				if k.Class() != table.want {
   240  					t.Fatalf("%s: got key with class=%s (want=%s)", table.name, k.Class(), table.want)
   241  				}
   242  			}
   243  
   244  			for _, table := range []struct {
   245  				name string
   246  				want string
   247  			}{
   248  				{"tree", "tree"},
   249  				{"tree;1", "tree"},
   250  			} {
   251  				o, err := f.Get(table.name)
   252  				if err != nil {
   253  					t.Fatalf("%s: expected key to exist! (got %v)", table.name, err)
   254  				}
   255  
   256  				k := o.(root.Named)
   257  				if k.Name() != table.want {
   258  					t.Fatalf("%s: got key with name=%s (want=%v)", table.name, k.Name(), table.want)
   259  				}
   260  			}
   261  
   262  			for _, table := range []struct {
   263  				name string
   264  				want string
   265  			}{
   266  				{"tree", "my tree title"},
   267  				{"tree;1", "my tree title"},
   268  			} {
   269  				o, err := f.Get(table.name)
   270  				if err != nil {
   271  					t.Fatalf("%s: expected key to exist! (got %v)", table.name, err)
   272  				}
   273  
   274  				k := o.(root.Named)
   275  				if k.Title() != table.want {
   276  					t.Fatalf("%s: got key with title=%s (want=%v)", table.name, k.Title(), table.want)
   277  				}
   278  			}
   279  		})
   280  	}
   281  }
   282  
   283  func TestFileOpenStreamerInfo(t *testing.T) {
   284  	for _, fname := range []string{
   285  		"../testdata/small-flat-tree.root",
   286  		"../testdata/simple.root",
   287  		rtests.XrdRemote("testdata/small-flat-tree.root"),
   288  		rtests.XrdRemote("testdata/simple.root"),
   289  	} {
   290  		f, err := groot.Open(fname)
   291  		if err != nil {
   292  			t.Errorf("error opening %q: %v\n", fname, err)
   293  			continue
   294  		}
   295  		defer f.Close()
   296  
   297  		_ = f.StreamerInfos()
   298  	}
   299  }
   300  
   301  func TestOpenEmptyFile(t *testing.T) {
   302  	f, err := groot.Open("../testdata/uproot/issue70.root")
   303  	if err != nil {
   304  		t.Fatal(err)
   305  	}
   306  	defer f.Close()
   307  	si := f.StreamerInfos()
   308  	if si != nil {
   309  		t.Fatalf("expected no StreamerInfos in empty file")
   310  	}
   311  }
   312  
   313  func TestCreate(t *testing.T) {
   314  	dir, err := os.MkdirTemp("", "riofs-")
   315  	if err != nil {
   316  		t.Fatal(err)
   317  	}
   318  	defer os.RemoveAll(dir)
   319  
   320  	for i, tc := range []struct {
   321  		name string
   322  		skip bool
   323  		want []rtests.ROOTer
   324  	}{
   325  		{name: "", want: nil},
   326  		{
   327  			name: "TObjString",
   328  			want: []rtests.ROOTer{rbase.NewObjString("hello")},
   329  		},
   330  		{
   331  			name: "TObjString",
   332  			want: []rtests.ROOTer{rbase.NewObjString("hello"), rbase.NewObjString("world")},
   333  		},
   334  		{
   335  			name: "TObjString",
   336  			want: func() []rtests.ROOTer {
   337  				var out []rtests.ROOTer
   338  				for _, i := range []int{0, 1, 253, 254, 255, 256, 512, 1024} {
   339  					str := strings.Repeat("=", i)
   340  					out = append(out, rbase.NewObjString(str))
   341  				}
   342  				return out
   343  			}(),
   344  		},
   345  		{
   346  			name: "TObject",
   347  			want: []rtests.ROOTer{rbase.NewObject()},
   348  		},
   349  		{
   350  			name: "TNamed",
   351  			want: []rtests.ROOTer{
   352  				rbase.NewNamed("n0", "t0"),
   353  				rbase.NewNamed("n1", "t1"),
   354  				rbase.NewNamed("n2", "t2"),
   355  			},
   356  		},
   357  		{
   358  			name: "TList",
   359  			want: []rtests.ROOTer{rcont.NewList("list-name", []root.Object{
   360  				rbase.NewNamed("n0", "t0"),
   361  				rbase.NewNamed("n1", "t1"),
   362  				rbase.NewNamed("n2", "t2"),
   363  			})},
   364  		},
   365  		{
   366  			name: "TArrayF",
   367  			want: []rtests.ROOTer{
   368  				&rcont.ArrayF{Data: []float32{1, 2, 3, 4, 5, 6}},
   369  			},
   370  		},
   371  		{
   372  			name: "TArrayD",
   373  			want: []rtests.ROOTer{
   374  				&rcont.ArrayD{Data: []float64{1, 2, 3, 4, 5, 6}},
   375  			},
   376  		},
   377  		{
   378  			name: "TArrays",
   379  			want: []rtests.ROOTer{
   380  				&rcont.ArrayF{Data: []float32{1, 2, 3, 4, 5, 6}},
   381  				&rcont.ArrayD{Data: []float64{1, 2, 3, 4, 5, 6}},
   382  			},
   383  		},
   384  	} {
   385  		fname := filepath.Join(dir, fmt.Sprintf("out-%d.root", i))
   386  		t.Run(tc.name, func(t *testing.T) {
   387  			if tc.skip {
   388  				t.Skip()
   389  			}
   390  
   391  			w, err := groot.Create(fname)
   392  			if err != nil {
   393  				t.Fatal(err)
   394  			}
   395  
   396  			for i := range tc.want {
   397  				var (
   398  					kname = fmt.Sprintf("key-%s-%02d", tc.name, i)
   399  					want  = tc.want[i]
   400  				)
   401  
   402  				err = w.Put(kname, want)
   403  				if err != nil {
   404  					t.Fatal(err)
   405  				}
   406  			}
   407  
   408  			if got, want := len(w.Keys()), len(tc.want); got != want {
   409  				t.Fatalf("invalid number of keys. got=%d, want=%d", got, want)
   410  			}
   411  
   412  			err = w.Close()
   413  			if err != nil {
   414  				t.Fatalf("error closing file: %+v", err)
   415  			}
   416  
   417  			r, err := groot.Open(fname)
   418  			if err != nil {
   419  				t.Fatal(err)
   420  			}
   421  			defer r.Close()
   422  
   423  			if got, want := len(r.Keys()), len(tc.want); got != want {
   424  				t.Fatalf("invalid number of keys. got=%d, want=%d", got, want)
   425  			}
   426  
   427  			for i := range tc.want {
   428  				var (
   429  					kname = fmt.Sprintf("key-%s-%02d", tc.name, i)
   430  					want  = tc.want[i]
   431  				)
   432  
   433  				rgot, err := r.Get(kname)
   434  				if err != nil {
   435  					t.Fatal(err)
   436  				}
   437  
   438  				if got := rgot.(rtests.ROOTer); !reflect.DeepEqual(got, want) {
   439  					t.Fatalf("error reading back value[%d].\ngot = %#v\nwant = %#v", i, got, want)
   440  				}
   441  			}
   442  
   443  			err = r.Close()
   444  			if err != nil {
   445  				t.Fatalf("error closing file: %+v", err)
   446  			}
   447  
   448  			if !rtests.HasROOT {
   449  				t.Logf("skip test with ROOT/C++")
   450  				return
   451  			}
   452  
   453  			const rootls = `#include <iostream>
   454  #include "TFile.h"
   455  #include "TNamed.h"
   456  
   457  void rootls(const char *fname, const char *kname) {
   458  	auto f = TFile::Open(fname);
   459  	auto o = f->Get(kname);
   460  	if (o == NULL) {
   461  		std:cerr << "could not retrieve [" << kname << "]" << std::endl;
   462  		o->ClassName();
   463  	}
   464  	std::cout << "retrieved: [" << kname << "]" << std::endl;
   465  }
   466  `
   467  			for i := range tc.want {
   468  				kname := fmt.Sprintf("key-%s-%02d", tc.name, i)
   469  
   470  				out, err := rtests.RunCxxROOT("rootls", []byte(rootls), fname, kname)
   471  				if err != nil {
   472  					t.Fatalf("ROOT/C++ could not open file %q:\n%s", fname, string(out))
   473  				}
   474  			}
   475  		})
   476  	}
   477  }
   478  
   479  func TestOpenBigFile(t *testing.T) {
   480  	ch := make(chan error)
   481  	go func() {
   482  		fname := rtests.XrdRemote("testdata/SMHiggsToZZTo4L.root")
   483  		f, err := riofs.Open(fname)
   484  		if err != nil {
   485  			ch <- err
   486  			return
   487  		}
   488  		defer f.Close()
   489  
   490  		o, err := f.Get("Events")
   491  		if err != nil {
   492  			ch <- err
   493  			return
   494  		}
   495  
   496  		tree := o.(rtree.Tree)
   497  		if got, want := tree.Entries(), int64(299973); got != want {
   498  			ch <- fmt.Errorf("invalid entries: got=%d, want=%d", got, want)
   499  			return
   500  		}
   501  		ch <- nil
   502  	}()
   503  
   504  	timeout := time.NewTimer(30 * time.Second)
   505  	defer timeout.Stop()
   506  	select {
   507  	case err := <-ch:
   508  		if err != nil {
   509  			t.Fatalf("error: %+v", err)
   510  		}
   511  	case <-timeout.C:
   512  		t.Fatalf("timeout")
   513  	}
   514  }
   515  
   516  func TestReadOnlyFile(t *testing.T) {
   517  	f, err := groot.Open("../testdata/dirs-6.14.00.root")
   518  	if err != nil {
   519  		t.Fatal(err)
   520  	}
   521  	defer f.Close()
   522  
   523  	err = f.Put("o1", rbase.NewObjString("v1"))
   524  	if err == nil {
   525  		t.Fatalf("expected an error. got nil")
   526  	}
   527  
   528  	o, err := f.Get("dir1")
   529  	if err != nil {
   530  		t.Fatal(err)
   531  	}
   532  
   533  	dir1 := o.(riofs.Directory)
   534  	err = dir1.Put("o2", rbase.NewObjString("v2"))
   535  	if err == nil {
   536  		t.Fatalf("expected an error. got nil")
   537  	}
   538  
   539  	o, err = dir1.Get("dir11")
   540  	if err != nil {
   541  		t.Fatal(err)
   542  	}
   543  
   544  	dir11 := o.(riofs.Directory)
   545  	err = dir11.Put("o3", rbase.NewObjString("v3"))
   546  	if err == nil {
   547  		t.Fatalf("expected an error. got nil")
   548  	}
   549  }
   550  
   551  func TestTopLevelString(t *testing.T) {
   552  	f, err := groot.Open("../testdata/string-example.root")
   553  	if err != nil {
   554  		t.Fatal(err)
   555  	}
   556  	defer f.Close()
   557  
   558  	o, err := riofs.Get[*rbase.String](f, "FileSummaryRecord")
   559  	if err != nil {
   560  		t.Fatal(err)
   561  	}
   562  
   563  	got := o.String()
   564  	want := `{"LumiCounter.eventsByRun":{"counts":{},"empty":true,"type":"LumiEventCounter"},"guid":"5FE9437E-D958-11EE-AB88-3CECEF1070AC"}`
   565  	if got != want {
   566  		t.Fatalf("got=%q, want=%q", got, want)
   567  	}
   568  }