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  }