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 )