go-hep.org/x/hep@v0.38.1/groot/riofs/walk_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 riofs
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	stdpath "path"
    11  	"reflect"
    12  	"strings"
    13  	"testing"
    14  
    15  	"go-hep.org/x/hep/groot/rbase"
    16  	"go-hep.org/x/hep/groot/rhist"
    17  	"go-hep.org/x/hep/groot/root"
    18  	"go-hep.org/x/hep/internal/diff"
    19  )
    20  
    21  func TestGet(t *testing.T) {
    22  	f, err := Open("../testdata/dirs-6.14.00.root")
    23  	if err != nil {
    24  		t.Fatal(err)
    25  	}
    26  	defer f.Close()
    27  
    28  	h1, err := Get[rhist.H1](f, "dir1/dir11/h1")
    29  	if err != nil {
    30  		t.Fatalf("could not get histo: %+v", err)
    31  	}
    32  	if h1 == nil {
    33  		t.Fatalf("invalid H1 value")
    34  	}
    35  
    36  	h1f, err := Get[*rhist.H1F](f, "dir1/dir11/h1")
    37  	if err != nil {
    38  		t.Fatalf("could not get histo: %+v", err)
    39  	}
    40  	if h1f == nil {
    41  		t.Fatalf("invalid H1F value")
    42  	}
    43  
    44  	_, err = Get[*rhist.H1D](f, "dir1/dir11/h1")
    45  	if err == nil {
    46  		t.Fatalf("expected an error")
    47  	}
    48  	if want := fmt.Errorf(`riofs: could not convert "dir1/dir11/h1" (*rhist.H1F) to *rhist.H1D`); err.Error() != want.Error() {
    49  		t.Fatalf("invalid error:\ngot= %+v\nwant=%+v", err, want)
    50  	}
    51  
    52  	_, err = Get[any](f, "dir1/dir11/h1")
    53  	if err != nil {
    54  		t.Fatalf("could not get histo: %+v", err)
    55  	}
    56  
    57  	_, err = Get[any](f, "dir1/dir11/h1_XXX")
    58  	if err == nil {
    59  		t.Fatalf("expected an error")
    60  	}
    61  	if want := fmt.Errorf(`riofs: dir11: could not find key "h1_XXX;9999"`); err.Error() != want.Error() {
    62  		t.Fatalf("invalid error:\ngot= %+v\nwant=%+v", err, want)
    63  	}
    64  
    65  }
    66  func TestDir(t *testing.T) {
    67  	f, err := Open("../testdata/dirs-6.14.00.root")
    68  	if err != nil {
    69  		t.Fatal(err)
    70  	}
    71  	defer f.Close()
    72  
    73  	rd := Dir(f)
    74  	for _, tc := range []struct {
    75  		path  string
    76  		class string
    77  	}{
    78  		{"dir1/dir11/h1", "TH1F"},
    79  		{"dir1/dir11/h1;1", "TH1F"},
    80  		{"dir1/dir11/h1;9999", "TH1F"},
    81  		{"/dir1/dir11/h1", "TH1F"},
    82  		{"/dir1/dir11/h1;1", "TH1F"},
    83  		{"/dir1/dir11/h1;9999", "TH1F"},
    84  		{"dir1/dir11", "TDirectoryFile"},
    85  		{"dir1/dir11;1", "TDirectoryFile"},
    86  		{"dir1/dir11;9999", "TDirectoryFile"},
    87  		{"dir1", "TDirectoryFile"},
    88  		{"dir2", "TDirectoryFile"},
    89  		{"dir3", "TDirectoryFile"},
    90  		{"", "TFile"},
    91  		{"/", "TFile"},
    92  		{"/dir1", "TDirectoryFile"},
    93  	} {
    94  		t.Run(tc.path, func(t *testing.T) {
    95  			o, err := rd.Get(tc.path)
    96  			if err != nil {
    97  				t.Fatal(err)
    98  			}
    99  			if got, want := o.Class(), tc.class; got != want {
   100  				t.Fatalf("got=%q, want=%q", got, want)
   101  			}
   102  		})
   103  	}
   104  
   105  	keys := make([]string, len(rd.Keys()))
   106  	for i, k := range rd.Keys() {
   107  		keys[i] = k.Name()
   108  	}
   109  
   110  	if got, want := keys, []string{"dir1", "dir2", "dir3"}; !reflect.DeepEqual(got, want) {
   111  		t.Fatalf("invalid keys:\ngot = %v\nwant=%v\n", got, want)
   112  	}
   113  
   114  	for _, tc := range []struct {
   115  		path   string
   116  		parent string
   117  	}{
   118  		{"dir1/dir11", "dir1"},
   119  		{"/dir1/dir11", "dir1"},
   120  		{"dir1", f.Name()},
   121  		{"/dir1", f.Name()},
   122  		{"", ""},
   123  		{"/", ""},
   124  	} {
   125  		t.Run("parent:"+tc.path, func(t *testing.T) {
   126  			o, err := rd.Get(tc.path)
   127  			if err != nil {
   128  				t.Fatal(err)
   129  			}
   130  			p := o.(Directory).Parent()
   131  			switch p {
   132  			case nil:
   133  				if got, want := "", tc.parent; got != want {
   134  					t.Fatalf("invalid parent: got=%q, want=%q", got, want)
   135  				}
   136  			default:
   137  				if got, want := p.(root.Named).Name(), tc.parent; got != want {
   138  					t.Fatalf("invalid parent: got=%q, want=%q", got, want)
   139  				}
   140  			}
   141  		})
   142  	}
   143  
   144  }
   145  
   146  func TestRecDirMkdir(t *testing.T) {
   147  	tmp, err := os.CreateTemp("", "groot-riofs-")
   148  	if err != nil {
   149  		t.Fatal(err)
   150  	}
   151  	tmp.Close()
   152  	os.Remove(tmp.Name())
   153  
   154  	f, err := Create(tmp.Name() + ".root")
   155  	if err != nil {
   156  		t.Fatal(err)
   157  	}
   158  	defer f.Close()
   159  	defer os.Remove(f.Name())
   160  
   161  	rd := Dir(f)
   162  
   163  	display := func() string {
   164  		o := new(strings.Builder)
   165  		err := Walk(f, func(path string, obj root.Object, err error) error {
   166  			fmt.Fprintf(o, "%s (%s)\n", path, obj.Class())
   167  			return nil
   168  		})
   169  		if err != nil {
   170  			return fmt.Errorf("could not display file content: %w", err).Error()
   171  		}
   172  		return o.String()
   173  	}
   174  
   175  	for _, tc := range []struct {
   176  		path string
   177  		err  error
   178  	}{
   179  		{path: "dir1"},
   180  		{path: "dir2/dir21/dir211"},
   181  		{path: "dir2/dir22"},
   182  		{path: "dir2/dir22/dir222"},
   183  		{path: "/dir3"},
   184  		{path: "/dir3"}, // recursive mkdir does not fail.
   185  		{path: "/dir4/dir44"},
   186  		{path: "/", err: fmt.Errorf("riofs: invalid path \"/\" to Mkdir")},
   187  		{path: "", err: fmt.Errorf("riofs: invalid path \"\" to Mkdir")},
   188  	} {
   189  		t.Run(tc.path, func(t *testing.T) {
   190  			_, err := rd.Mkdir(tc.path)
   191  			switch err {
   192  			case nil:
   193  				if tc.err != nil {
   194  					t.Fatalf("got no error, want=%v\ncontent:\n%v", tc.err, display())
   195  				}
   196  			default:
   197  				if tc.err == nil {
   198  
   199  					t.Fatalf("could not create %q: %v\ncontent:\n%v", tc.path, err, display())
   200  				}
   201  				if got, want := err.Error(), tc.err.Error(); got != want {
   202  					t.Fatalf("invalid error.\ngot= %v\nwant=%v\ncontent:\n%v", got, want, display())
   203  				}
   204  			}
   205  		})
   206  	}
   207  
   208  	// test recursive mkdir does not work on f.
   209  	_, err = f.Mkdir("xdir/xsubdir")
   210  	if err == nil {
   211  		t.Fatalf("expected an error, got=%v\ncontent:\n%v", err, display())
   212  	}
   213  	if got, want := err.Error(), fmt.Errorf("riofs: invalid directory name %q (contains a '/')", "xdir/xsubdir").Error(); got != want {
   214  		t.Fatalf("invalid error. got=%q, want=%q", got, want)
   215  	}
   216  
   217  	// test regular mkdir fails when directory already exists
   218  	_, err = f.Mkdir("dir1")
   219  	if err == nil {
   220  		t.Fatalf("expected an error, got=%v\ncontent:\n%v", err, display())
   221  	}
   222  	if got, want := err.Error(), fmt.Errorf("riofs: %q already exists", "dir1").Error(); got != want {
   223  		t.Fatalf("invalid error. got=%q, want=%q", got, want)
   224  	}
   225  }
   226  
   227  func TestRecDirPut(t *testing.T) {
   228  	tmp, err := os.CreateTemp("", "groot-riofs-")
   229  	if err != nil {
   230  		t.Fatal(err)
   231  	}
   232  	tmp.Close()
   233  	os.Remove(tmp.Name())
   234  
   235  	f, err := Create(tmp.Name() + ".root")
   236  	if err != nil {
   237  		t.Fatal(err)
   238  	}
   239  	defer f.Close()
   240  	defer os.Remove(f.Name())
   241  
   242  	rd := Dir(f)
   243  
   244  	display := func() string {
   245  		o := new(strings.Builder)
   246  		err := Walk(f, func(path string, obj root.Object, err error) error {
   247  			if err != nil {
   248  				return err
   249  			}
   250  			name := path[len(f.Name()):]
   251  			if name == "" {
   252  				fmt.Fprintf(o, "%s (%s)\n", path, obj.Class())
   253  				return nil
   254  			}
   255  			dir, err := Dir(f).Get(stdpath.Dir(name))
   256  			if err != nil {
   257  				return err
   258  			}
   259  			pdir := dir.(Directory)
   260  			cycle := -1
   261  			for _, k := range pdir.Keys() {
   262  				if k.Name() == stdpath.Base(path) {
   263  					cycle = k.Cycle()
   264  					break
   265  				}
   266  			}
   267  			fmt.Fprintf(o, "%s;%d (%s)\n", path, cycle, obj.Class())
   268  			return nil
   269  		})
   270  		if err != nil {
   271  			return fmt.Errorf("could not display file content: %w", err).Error()
   272  		}
   273  		return o.String()
   274  	}
   275  
   276  	for _, tc := range []struct {
   277  		path  string
   278  		obj   string
   279  		cycle int
   280  		err   error
   281  	}{
   282  		{path: "dir1"},
   283  		{path: "dir2/dir21/dir211"},
   284  		{path: "dir2/dir22"},
   285  		{path: "dir2/dir22/dir222"},
   286  		{path: "/dir3"},
   287  		{path: "/dir4/dir44"},
   288  
   289  		{path: "/dir5/dir55"},
   290  		{path: "/dir5", obj: "dir55", err: keyTypeError{key: "dir55", class: "TDirectory"}},
   291  
   292  		{path: "/dir5/dir55", cycle: 2}, // recreating the same object is ok
   293  	} {
   294  		t.Run(tc.path, func(t *testing.T) {
   295  			obj := tc.obj
   296  			if obj == "" {
   297  				obj = "obj"
   298  			}
   299  			err := rd.Put(stdpath.Join(tc.path, obj), rbase.NewObjString(obj))
   300  			switch err {
   301  			case nil:
   302  				if tc.err != nil {
   303  					t.Fatalf("got no error, want=%v\ncontent:\n%v", tc.err, display())
   304  				}
   305  				cycle := 1
   306  				if tc.cycle != 0 {
   307  					cycle = tc.cycle
   308  				}
   309  				name := stdpath.Join(tc.path, obj)
   310  				namecycle := fmt.Sprintf("%s;%d", name, cycle)
   311  				_, err := rd.Get(namecycle)
   312  				if err != nil {
   313  					t.Fatalf("could not access %q: %v\ncontent:\n%v", namecycle, err, display())
   314  				}
   315  			default:
   316  				if tc.err == nil {
   317  
   318  					t.Fatalf("could not create %q: %v\ncontent:\n%v", tc.path, err, display())
   319  				}
   320  				if got, want := err.Error(), tc.err.Error(); got != want {
   321  					t.Fatalf("invalid error.\ngot= %v\nwant=%v\ncontent:\n%v", got, want, display())
   322  				}
   323  			}
   324  		})
   325  	}
   326  
   327  	// test recursive put does not work on f.
   328  	err = f.Put("xdir/xsubdir/obj", rbase.NewObjString("obj"))
   329  	if err == nil {
   330  		t.Fatalf("expected an error, got=%v\ncontent:\n%v", err, display())
   331  	}
   332  	if got, want := err.Error(), fmt.Errorf("riofs: invalid path name %q (contains a '/')", "xdir/xsubdir/obj").Error(); got != want {
   333  		t.Fatalf("invalid error. got=%q, want=%q", got, want)
   334  	}
   335  
   336  	err = rd.Put("", rbase.NewObjString("obj-empty-key"))
   337  	if err != nil {
   338  		t.Fatalf("could not create key-val with empty name: %v", err)
   339  	}
   340  }
   341  
   342  func TestFileOf(t *testing.T) {
   343  	tmp, err := os.MkdirTemp("", "groot-riofs-")
   344  	if err != nil {
   345  		t.Fatalf("%+v", err)
   346  	}
   347  	defer os.RemoveAll(tmp)
   348  
   349  	f, err := Create(stdpath.Join(tmp, "file.root"))
   350  	if err != nil {
   351  		t.Fatalf("%+v", err)
   352  	}
   353  	defer f.Close()
   354  
   355  	dir111, err := Dir(f).Mkdir("dir-1/dir-11/dir-111")
   356  	if err != nil {
   357  		t.Fatalf("%+v", err)
   358  	}
   359  
   360  	for _, tc := range []struct {
   361  		name   string
   362  		dir    Directory
   363  		panics string
   364  	}{
   365  		{
   366  			name: "file",
   367  			dir:  f,
   368  		},
   369  		{
   370  			name: "file-rec",
   371  			dir:  Dir(f),
   372  		},
   373  		{
   374  			name: "file-dir",
   375  			dir:  &f.dir,
   376  		},
   377  		{
   378  			name: "dir-111",
   379  			dir:  dir111,
   380  		},
   381  		{
   382  			name:   "panics",
   383  			dir:    &unknownDirImpl{},
   384  			panics: "riofs: unknown Directory type *riofs.unknownDirImpl",
   385  		},
   386  	} {
   387  		t.Run(tc.name, func(t *testing.T) {
   388  			if tc.panics != "" {
   389  				defer func() {
   390  					err := recover()
   391  					if err == nil {
   392  						t.Fatalf("expected a panic")
   393  					}
   394  					if got, want := err.(error).Error(), tc.panics; got != want {
   395  						t.Fatalf("invalid panic message. got=%q, want=%q", got, want)
   396  					}
   397  				}()
   398  			}
   399  			got := fileOf(tc.dir)
   400  			if got != f {
   401  				t.Fatalf("could not retrieve correct file for %q", tc.name)
   402  			}
   403  		})
   404  	}
   405  }
   406  
   407  func TestWalk(t *testing.T) {
   408  	tmp, err := os.MkdirTemp("", "groot-riofs-")
   409  	if err != nil {
   410  		t.Fatal(err)
   411  	}
   412  	defer os.RemoveAll(tmp)
   413  
   414  	err = os.MkdirAll(stdpath.Join(tmp, "data"), 0755)
   415  	if err != nil {
   416  		t.Fatal(err)
   417  	}
   418  
   419  	pwd, err := os.Getwd()
   420  	if err != nil {
   421  		t.Fatalf("could not get working directory: %+v", err)
   422  	}
   423  	defer os.Chdir(pwd)
   424  
   425  	err = os.Chdir(tmp)
   426  	if err != nil {
   427  		t.Fatal(err)
   428  	}
   429  
   430  	fname := "./data/file.root"
   431  
   432  	f, err := Create(fname)
   433  	if err != nil {
   434  		t.Fatalf("could not create ROOT file: %+v", err)
   435  	}
   436  	defer f.Close()
   437  
   438  	err = os.Chdir(pwd)
   439  	if err != nil {
   440  		t.Fatal(err)
   441  	}
   442  
   443  	rd := Dir(f)
   444  
   445  	display := func() string {
   446  		o := new(strings.Builder)
   447  		err := Walk(f, func(path string, obj root.Object, err error) error {
   448  			fmt.Fprintf(o, "%s (%s)\n", path, obj.Class())
   449  			return nil
   450  		})
   451  		if err != nil {
   452  			return fmt.Errorf("could not display file content: %w", err).Error()
   453  		}
   454  		return o.String()
   455  	}
   456  
   457  	for _, name := range []string{
   458  		"dir1/dir11/dir111",
   459  		"dir1/dir12/dir121",
   460  		"dir2/dir21",
   461  		"dir2/dir22",
   462  	} {
   463  		_, err = rd.Mkdir(name)
   464  		if err != nil {
   465  			t.Fatalf("could not create dir %q: %+v", name, err)
   466  		}
   467  	}
   468  
   469  	got := display()
   470  	want := `data/file.root (TFile)
   471  data/file.root/dir1 (TDirectoryFile)
   472  data/file.root/dir1/dir11 (TDirectoryFile)
   473  data/file.root/dir1/dir11/dir111 (TDirectoryFile)
   474  data/file.root/dir1/dir12 (TDirectoryFile)
   475  data/file.root/dir1/dir12/dir121 (TDirectoryFile)
   476  data/file.root/dir2 (TDirectoryFile)
   477  data/file.root/dir2/dir21 (TDirectoryFile)
   478  data/file.root/dir2/dir22 (TDirectoryFile)
   479  `
   480  	if got != want {
   481  		t.Fatalf("invalid Walk display:\n%s", diff.Format(got, want))
   482  	}
   483  }
   484  
   485  type unknownDirImpl struct{}
   486  
   487  func (dir *unknownDirImpl) Get(namecycle string) (root.Object, error) { panic("not implemented") }
   488  func (dir *unknownDirImpl) Put(name string, v root.Object) error      { panic("not implemented") }
   489  func (dir *unknownDirImpl) Keys() []Key                               { panic("not implemented") }
   490  func (dir *unknownDirImpl) Mkdir(name string) (Directory, error)      { panic("not implemented") }
   491  func (dir *unknownDirImpl) Parent() Directory                         { return nil }
   492  
   493  var (
   494  	_ Directory = (*unknownDirImpl)(nil)
   495  )