go-hep.org/x/hep@v0.38.1/xrootd/file_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 xrootd // import "go-hep.org/x/hep/xrootd" 6 7 import ( 8 "context" 9 "crypto/rand" 10 "fmt" 11 "log" 12 "path" 13 "reflect" 14 "testing" 15 16 "go-hep.org/x/hep/xrootd/xrdfs" 17 ) 18 19 func testFile_Close(t *testing.T, addr string) { 20 t.Parallel() 21 22 client, err := NewClient(context.Background(), addr, "gopher") 23 if err != nil { 24 t.Fatalf("could not create client: %v", err) 25 } 26 defer client.Close() 27 28 fs := client.FS() 29 30 file, err := fs.Open(context.Background(), "/tmp/dir1/file1.txt", xrdfs.OpenModeOtherRead, xrdfs.OpenOptionsNone) 31 if err != nil { 32 t.Fatalf("invalid open call: %v", err) 33 } 34 35 err = file.Close(context.Background()) 36 if err != nil { 37 t.Fatalf("invalid close call: %v", err) 38 } 39 } 40 41 func TestFile_Close(t *testing.T) { 42 for _, addr := range testClientAddrs { 43 t.Run(addr, func(t *testing.T) { 44 testFile_Close(t, addr) 45 }) 46 } 47 } 48 49 func testFile_CloseVerify(t *testing.T, addr string) { 50 t.Parallel() 51 52 fileName := "close-verify.txt" 53 client, err := NewClient(context.Background(), addr, "gopher") 54 if err != nil { 55 t.Fatalf("could not create client: %v", err) 56 } 57 defer client.Close() 58 59 fs := client.FS() 60 61 dir, err := tempdir(client, "/tmp/", "xrd-test-close-verify") 62 if err != nil { 63 t.Fatal(err) 64 } 65 defer func() { 66 _ = fs.RemoveAll(context.Background(), dir) 67 }() 68 filePath := path.Join(dir, fileName) 69 70 file, err := fs.Open(context.Background(), filePath, xrdfs.OpenModeOwnerWrite, xrdfs.OpenOptionsNew) 71 if err != nil { 72 t.Fatalf("invalid open call: %v", err) 73 } 74 75 // TODO: Remove these 2 lines when XRootD server will follow protocol specification and fail such requests. 76 // See https://github.com/xrootd/xrootd/issues/727. 77 defer file.Close(context.Background()) 78 t.Skip("Skipping test because the XRootD C++ server doesn't fail request when the wrong size is passed.") 79 80 err = file.CloseVerify(context.Background(), 14) 81 if err == nil { 82 t.Fatal("close call should fail when the wrong size is passed") 83 } 84 } 85 86 func TestFile_CloseVerify(t *testing.T) { 87 for _, addr := range testClientAddrs { 88 t.Run(addr, func(t *testing.T) { 89 testFile_CloseVerify(t, addr) 90 }) 91 } 92 } 93 94 func testFile_ReadAt(t *testing.T, addr string) { 95 t.Parallel() 96 97 client, err := NewClient(context.Background(), addr, "gopher") 98 if err != nil { 99 t.Fatalf("could not create client: %v", err) 100 } 101 defer client.Close() 102 103 fs := client.FS() 104 105 file, err := fs.Open(context.Background(), "/tmp/file1.txt", xrdfs.OpenModeOtherRead, xrdfs.OpenOptionsNone) 106 if err != nil { 107 t.Fatalf("invalid open call: %v", err) 108 } 109 defer file.Close(context.Background()) 110 111 want := []byte("Hello XRootD.\n") 112 got := make([]uint8, 20) 113 n, err := file.ReadAt(got, 0) 114 if err != nil { 115 t.Fatalf("invalid read call: %v", err) 116 } 117 118 if n != len(want) { 119 t.Fatalf("read count does not match:\ngot = %v\nwant = %v", n, len(want)) 120 } 121 122 if !reflect.DeepEqual(got[:n], want) { 123 t.Fatalf("read data does not match:\ngot = %v\nwant = %v", got[:n], want) 124 } 125 } 126 127 func TestFile_ReadAt(t *testing.T) { 128 for _, addr := range testClientAddrs { 129 t.Run(addr, func(t *testing.T) { 130 testFile_ReadAt(t, addr) 131 }) 132 } 133 } 134 135 func testFile_WriteAt(t *testing.T, addr string) { 136 t.Parallel() 137 138 fileName := "test_rw.txt" 139 want := make([]byte, 8*1024) 140 rand.Read(want) 141 142 client, err := NewClient(context.Background(), addr, "gopher") 143 if err != nil { 144 t.Fatalf("could not create client: %v", err) 145 } 146 defer client.Close() 147 fs := client.FS() 148 149 dir, err := tempdir(client, "/tmp/", "xrd-test-file-write-at-") 150 if err != nil { 151 t.Fatal(err) 152 } 153 defer func() { 154 _ = fs.RemoveAll(context.Background(), dir) 155 }() 156 filePath := path.Join(dir, fileName) 157 158 file, err := fs.Open(context.Background(), filePath, xrdfs.OpenModeOwnerWrite, xrdfs.OpenOptionsNew) 159 if err != nil { 160 t.Fatalf("invalid open call: %v", err) 161 } 162 defer file.Close(context.Background()) 163 164 _, err = file.WriteAt(want, 0) 165 if err != nil { 166 t.Fatalf("invalid write call: %v", err) 167 } 168 169 err = file.Sync(context.Background()) 170 if err != nil { 171 t.Fatalf("invalid sync call: %v", err) 172 } 173 174 file.Close(context.Background()) 175 file, err = fs.Open(context.Background(), filePath, xrdfs.OpenModeOwnerRead, xrdfs.OpenOptionsOpenRead) 176 if err != nil { 177 t.Fatalf("could not open %q: %v", filePath, err) 178 } 179 defer file.Close(context.Background()) 180 181 got := make([]uint8, len(want)+10) 182 n, err := file.ReadAt(got, 0) 183 if err != nil { 184 t.Fatalf("invalid read call: %v", err) 185 } 186 187 if n != len(want) { 188 t.Fatalf("read count does not match:\ngot = %v\nwant = %v", n, len(want)) 189 } 190 191 if !reflect.DeepEqual(got[:n], want) { 192 t.Fatalf("read data does not match:\ngot = %v\nwant = %v", got[:n], want) 193 } 194 195 } 196 197 func TestFile_WriteAt(t *testing.T) { 198 for _, addr := range testClientAddrs { 199 t.Run(addr, func(t *testing.T) { 200 testFile_WriteAt(t, addr) 201 }) 202 } 203 } 204 205 func testFile_Truncate(t *testing.T, addr string) { 206 t.Parallel() 207 208 fileName := "test_truncate.txt" 209 write := []uint8{1, 2, 3, 4, 5, 6, 7, 8} 210 want := write[:4] 211 212 client, err := NewClient(context.Background(), addr, "gopher") 213 if err != nil { 214 t.Fatalf("could not create client: %v", err) 215 } 216 defer client.Close() 217 218 fs := client.FS() 219 220 dir, err := tempdir(client, "/tmp/", "xrd-test-truncate") 221 if err != nil { 222 t.Fatal(err) 223 } 224 defer func() { 225 _ = fs.RemoveAll(context.Background(), dir) 226 }() 227 filePath := path.Join(dir, fileName) 228 229 file, err := fs.Open(context.Background(), filePath, xrdfs.OpenModeOwnerWrite, xrdfs.OpenOptionsNew) 230 if err != nil { 231 t.Fatalf("invalid open call: %v", err) 232 } 233 defer file.Close(context.Background()) 234 235 _, err = file.WriteAt(write, 0) 236 if err != nil { 237 t.Fatalf("invalid write call: %v", err) 238 } 239 240 err = file.Truncate(context.Background(), int64(len(want))) 241 if err != nil { 242 t.Fatalf("invalid truncate call: %v", err) 243 } 244 245 err = file.Sync(context.Background()) 246 if err != nil { 247 t.Fatalf("invalid sync call: %v", err) 248 } 249 250 err = file.Close(context.Background()) 251 if err != nil { 252 t.Fatalf("invalid close call: %v", err) 253 } 254 file, err = fs.Open(context.Background(), filePath, xrdfs.OpenModeOwnerRead, xrdfs.OpenOptionsOpenRead) 255 if err != nil { 256 t.Fatalf("invalid open call: %v", err) 257 } 258 defer file.Close(context.Background()) 259 260 got := make([]uint8, len(want)+10) 261 n, err := file.ReadAt(got, 0) 262 if err != nil { 263 t.Fatalf("invalid read call: %v", err) 264 } 265 266 if n != len(want) { 267 t.Fatalf("read count does not match:\ngot = %v\nwant = %v", n, len(want)) 268 } 269 270 if !reflect.DeepEqual(got[:n], want) { 271 t.Fatalf("read data does not match:\ngot = %v\nwant = %v", got[:n], want) 272 } 273 274 } 275 276 func TestFile_Truncate(t *testing.T) { 277 for _, addr := range testClientAddrs { 278 t.Run(addr, func(t *testing.T) { 279 testFile_Truncate(t, addr) 280 }) 281 } 282 } 283 284 func testFile_Stat(t *testing.T, addr string) { 285 t.Parallel() 286 287 want := fstest["/tmp/dir1/file1.txt"] 288 289 client, err := NewClient(context.Background(), addr, "gopher") 290 if err != nil { 291 t.Fatalf("could not create client: %v", err) 292 } 293 defer client.Close() 294 295 fs := client.FS() 296 file, err := fs.Open(context.Background(), "/tmp/dir1/file1.txt", xrdfs.OpenModeOwnerRead, xrdfs.OpenOptionsNone) 297 if err != nil { 298 t.Fatalf("invalid open call: %v", err) 299 } 300 defer file.Close(context.Background()) 301 302 got, err := file.Stat(context.Background()) 303 if err != nil { 304 t.Fatalf("invalid stat call: %v", err) 305 } 306 307 if !reflect.DeepEqual(&got, want) { 308 t.Fatalf("stat info does not match:\ngot = %v\nwant = %v", &got, want) 309 } 310 if !reflect.DeepEqual(file.Info(), want) { 311 t.Fatalf("stat info does not match:\nfile.Info = %v\nwant = %v", file.Info(), want) 312 } 313 } 314 315 func TestFile_Stat(t *testing.T) { 316 for _, addr := range testClientAddrs { 317 t.Run(addr, func(t *testing.T) { 318 testFile_Stat(t, addr) 319 }) 320 } 321 } 322 323 func testFile_StatVirtualFS(t *testing.T, addr string) { 324 t.Parallel() 325 326 client, err := NewClient(context.Background(), addr, "gopher") 327 if err != nil { 328 t.Fatalf("could not create client: %v", err) 329 } 330 defer client.Close() 331 332 fs := client.FS() 333 file, err := fs.Open(context.Background(), "/tmp/dir1/file1.txt", xrdfs.OpenModeOwnerRead, xrdfs.OpenOptionsNone) 334 if err != nil { 335 t.Fatalf("invalid open call: %v", err) 336 } 337 defer file.Close(context.Background()) 338 339 // FIXME: Investigate whether this request is allowed by the protocol: https://github.com/xrootd/xrootd/issues/728 340 t.Skip("Skipping this test because XRootD server probably doesn't support such requests.") 341 342 want := xrdfs.VirtualFSStat{ 343 NumberRW: 1, 344 FreeRW: 444, 345 UtilizationRW: 6, 346 } 347 348 got, err := file.StatVirtualFS(context.Background()) 349 if err != nil { 350 t.Fatalf("invalid stat call: %v", err) 351 } 352 353 if !reflect.DeepEqual(got, want) { 354 t.Errorf("File.FetchVirtualStatInfo()\ngot = %v\nwant = %v", got, want) 355 } 356 } 357 358 func TestFile_StatVirtualFS(t *testing.T) { 359 for _, addr := range testClientAddrs { 360 t.Run(addr, func(t *testing.T) { 361 testFile_StatVirtualFS(t, addr) 362 }) 363 } 364 } 365 366 func testFile_VerifyWriteAt(t *testing.T, addr string) { 367 t.Parallel() 368 369 // TODO: Enable this test once XRootD server starts to support kXR_verifyw request: https://github.com/xrootd/xrootd/issues/738. 370 t.Skipf("Skipping this test because XRootD server doesn't support such request.") 371 372 fileName := "test_verify_write.txt" 373 want := make([]byte, 8*1024) 374 rand.Read(want) 375 376 client, err := NewClient(context.Background(), addr, "gopher") 377 if err != nil { 378 t.Fatalf("could not create client: %v", err) 379 } 380 defer client.Close() 381 fs := client.FS() 382 383 dir, err := tempdir(client, "/tmp/", "xrd-test-verify-write") 384 if err != nil { 385 t.Fatal(err) 386 } 387 defer func() { 388 _ = fs.RemoveAll(context.Background(), dir) 389 }() 390 filePath := path.Join(dir, fileName) 391 392 file, err := fs.Open(context.Background(), filePath, xrdfs.OpenModeOwnerWrite, xrdfs.OpenOptionsNew) 393 if err != nil { 394 t.Fatalf("invalid open call: %v", err) 395 } 396 defer file.Close(context.Background()) 397 398 err = file.VerifyWriteAt(context.Background(), want, 0) 399 if err != nil { 400 t.Fatalf("invalid verifyw call: %v", err) 401 } 402 403 err = file.Sync(context.Background()) 404 if err != nil { 405 t.Fatalf("invalid sync call: %v", err) 406 } 407 408 err = file.Close(context.Background()) 409 if err != nil { 410 t.Fatalf("invalid close call: %v", err) 411 } 412 file, err = fs.Open(context.Background(), filePath, xrdfs.OpenModeOwnerRead, xrdfs.OpenOptionsOpenRead) 413 if err != nil { 414 t.Fatalf("invalid open call: %v", err) 415 } 416 defer file.Close(context.Background()) 417 418 got := make([]uint8, len(want)+10) 419 n, err := file.ReadAt(got, 0) 420 if err != nil { 421 t.Fatalf("invalid read call: %v", err) 422 } 423 424 if n != len(want) { 425 t.Fatalf("read count does not match:\ngot = %v\nwant = %v", n, len(want)) 426 } 427 428 if !reflect.DeepEqual(got[:n], want) { 429 t.Fatalf("read data does not match:\ngot = %v\nwant = %v", got[:n], want) 430 } 431 432 } 433 434 func TestFile_VerifyWriteAt(t *testing.T) { 435 for _, addr := range testClientAddrs { 436 t.Run(addr, func(t *testing.T) { 437 testFile_VerifyWriteAt(t, addr) 438 }) 439 } 440 } 441 442 func ExampleClient_write() { 443 ctx := context.Background() 444 const username = "gopher" 445 client, err := NewClient(ctx, "ccxrootdgotest.in2p3.fr:9001", username) 446 if err != nil { 447 log.Fatal(err) 448 } 449 defer client.Close() 450 451 file, err := client.FS().Open(ctx, "/tmp/test.txt", xrdfs.OpenModeOwnerRead|xrdfs.OpenModeOwnerWrite, xrdfs.OpenOptionsOpenUpdate|xrdfs.OpenOptionsNew) 452 if err != nil { 453 log.Fatal(err) 454 } 455 defer file.Close(ctx) 456 457 if _, err := file.WriteAt([]byte("test"), 0); err != nil { 458 log.Fatal(err) 459 } 460 461 if err := file.Sync(ctx); err != nil { 462 log.Fatal(err) 463 } 464 465 if err := file.Close(ctx); err != nil { 466 log.Fatal(err) 467 } 468 469 if err := client.Close(); err != nil { 470 log.Fatal(err) 471 } 472 } 473 474 func ExampleClient_read() { 475 ctx := context.Background() 476 const username = "gopher" 477 client, err := NewClient(ctx, "ccxrootdgotest.in2p3.fr:9001", username) 478 if err != nil { 479 log.Fatal(err) 480 } 481 defer client.Close() 482 483 file, err := client.FS().Open(ctx, "/tmp/test.txt", xrdfs.OpenModeOwnerRead, xrdfs.OpenOptionsOpenRead) 484 if err != nil { 485 log.Fatal(err) 486 } 487 defer file.Close(ctx) 488 489 data := make([]byte, 10) 490 n, err := file.ReadAt(data, 0) 491 if err != nil { 492 log.Fatal(err) 493 } 494 495 data = data[:n] 496 fmt.Printf("%s\n", data) 497 498 if err := file.Close(ctx); err != nil { 499 log.Fatal(err) 500 } 501 502 if err := client.Close(); err != nil { 503 log.Fatal(err) 504 } 505 506 // Output: 507 // test 508 }