go-hep.org/x/hep@v0.38.1/groot/riofs/rw_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  	"io"
     9  	"os"
    10  	"path/filepath"
    11  	"reflect"
    12  	"testing"
    13  
    14  	"go-hep.org/x/hep/groot/internal/rtests"
    15  	"go-hep.org/x/hep/groot/rbase"
    16  	"go-hep.org/x/hep/groot/rbytes"
    17  	"go-hep.org/x/hep/groot/rtypes"
    18  )
    19  
    20  func TestWRBuffer(t *testing.T) {
    21  	for _, tc := range []struct {
    22  		name string
    23  		want rtests.ROOTer
    24  	}{
    25  		{
    26  			name: "TFree",
    27  			want: &freeSegment{
    28  				first: 21,
    29  				last:  24,
    30  			},
    31  		},
    32  		{
    33  			name: "TFree",
    34  			want: &freeSegment{
    35  				first: 21,
    36  				last:  kStartBigFile + 24,
    37  			},
    38  		},
    39  		{
    40  			name: "TKey",
    41  			want: &Key{
    42  				nbytes:   1024,
    43  				rvers:    4, // small file
    44  				objlen:   10,
    45  				datetime: datime2time(1576331001),
    46  				keylen:   12,
    47  				cycle:    2,
    48  				seekkey:  1024,
    49  				seekpdir: 2048,
    50  				class:    "MyClass",
    51  				name:     "my-key",
    52  				title:    "my key title",
    53  			},
    54  		},
    55  		{
    56  			name: "TKey",
    57  			want: &Key{
    58  				nbytes:   1024,
    59  				rvers:    1004, // big file
    60  				objlen:   10,
    61  				datetime: datime2time(1576331001),
    62  				keylen:   12,
    63  				cycle:    2,
    64  				seekkey:  1024,
    65  				seekpdir: 2048,
    66  				class:    "MyClass",
    67  				name:     "my-key",
    68  				title:    "my key title",
    69  			},
    70  		},
    71  		{
    72  			name: "TDirectory",
    73  			want: &tdirectory{
    74  				rvers: 4, // small file
    75  				named: *rbase.NewNamed("my-name", "my-title"),
    76  				uuid: rbase.UUID{
    77  					0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
    78  					10, 11, 12, 13, 14, 15,
    79  				},
    80  			},
    81  		},
    82  		{
    83  			name: "TDirectory",
    84  			want: &tdirectory{
    85  				rvers: 1004, // big file
    86  				named: *rbase.NewNamed("my-name", "my-title"),
    87  				uuid: rbase.UUID{
    88  					0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
    89  					10, 11, 12, 13, 14, 15,
    90  				},
    91  			},
    92  		},
    93  		{
    94  			name: "TDirectoryFile",
    95  			want: &tdirectoryFile{
    96  				dir: tdirectory{
    97  					rvers: 4, // small file
    98  					named: *rbase.NewNamed("", ""),
    99  					uuid: rbase.UUID{
   100  						0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
   101  						10, 11, 12, 13, 14, 15,
   102  					},
   103  				},
   104  				ctime:      datime2time(1576331001),
   105  				mtime:      datime2time(1576331010),
   106  				nbyteskeys: 1,
   107  				nbytesname: 2,
   108  				seekdir:    3,
   109  				seekparent: 4,
   110  				seekkeys:   5,
   111  			},
   112  		},
   113  		{
   114  			name: "TDirectoryFile",
   115  			want: &tdirectoryFile{
   116  				dir: tdirectory{
   117  					rvers: 1004, // big file
   118  					named: *rbase.NewNamed("", ""),
   119  					uuid: rbase.UUID{
   120  						0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
   121  						10, 11, 12, 13, 14, 15,
   122  					},
   123  				},
   124  				ctime:      datime2time(1576331001),
   125  				mtime:      datime2time(1576331010),
   126  				nbyteskeys: 1,
   127  				nbytesname: 2,
   128  				seekdir:    3,
   129  				seekparent: 4,
   130  				seekkeys:   5,
   131  			},
   132  		},
   133  	} {
   134  		t.Run(tc.name, func(t *testing.T) {
   135  			{
   136  				wbuf := rbytes.NewWBuffer(nil, nil, 0, nil)
   137  				wbuf.SetErr(io.EOF)
   138  				_, err := tc.want.MarshalROOT(wbuf)
   139  				if err == nil {
   140  					t.Fatalf("expected an error")
   141  				}
   142  				if err != io.EOF {
   143  					t.Fatalf("got=%v, want=%v", err, io.EOF)
   144  				}
   145  			}
   146  			wbuf := rbytes.NewWBuffer(nil, nil, 0, nil)
   147  			_, err := tc.want.MarshalROOT(wbuf)
   148  			if err != nil {
   149  				t.Fatalf("could not marshal ROOT: %v", err)
   150  			}
   151  
   152  			rbuf := rbytes.NewRBuffer(wbuf.Bytes(), nil, 0, nil)
   153  			class := tc.want.Class()
   154  			obj := rtypes.Factory.Get(class)().Interface().(rbytes.Unmarshaler)
   155  			{
   156  				rbuf.SetErr(io.EOF)
   157  				err = obj.UnmarshalROOT(rbuf)
   158  				if err == nil {
   159  					t.Fatalf("expected an error")
   160  				}
   161  				if err != io.EOF {
   162  					t.Fatalf("got=%v, want=%v", err, io.EOF)
   163  				}
   164  				rbuf.SetErr(nil)
   165  			}
   166  			err = obj.UnmarshalROOT(rbuf)
   167  			if err != nil {
   168  				t.Fatalf("could not unmarshal ROOT: %v", err)
   169  			}
   170  
   171  			if !reflect.DeepEqual(obj, tc.want) {
   172  				t.Fatalf("error\ngot= %+v\nwant=%+v\n", obj, tc.want)
   173  			}
   174  		})
   175  	}
   176  }
   177  
   178  func TestWriteBigFile(t *testing.T) {
   179  	tmp, err := os.MkdirTemp("", "groot-riofs-")
   180  	if err != nil {
   181  		t.Fatalf("could not create tmp dir: %+v", err)
   182  	}
   183  	defer os.RemoveAll(tmp)
   184  
   185  	fname := filepath.Join(tmp, "big-file.root")
   186  
   187  	kvals := []struct {
   188  		k string
   189  		v string
   190  	}{
   191  		{k: "key1", v: "obj1"},
   192  		{k: "key2", v: "obj2"},
   193  	}
   194  
   195  	func() {
   196  		f, err := Create(fname)
   197  		if err != nil {
   198  			t.Fatalf("could not create output file: %+v", err)
   199  		}
   200  		defer f.Close()
   201  
   202  		kv := kvals[0]
   203  		err = f.Put(kv.k, rbase.NewObjString(kv.v))
   204  		if err != nil {
   205  			t.Fatalf("could not write %s: %+v", kv.k, err)
   206  		}
   207  
   208  		_, err = f.WriteAt([]byte{1}, kStartBigFile+1)
   209  		if err != nil {
   210  			t.Fatalf("could not write past big-file-mark: %+v", err)
   211  		}
   212  		f.end = kStartBigFile + 1
   213  
   214  		kv = kvals[1]
   215  		err = f.Put(kv.k, rbase.NewObjString(kv.v))
   216  		if err != nil {
   217  			t.Fatalf("could not write %s: %+v", kv.k, err)
   218  		}
   219  
   220  		err = f.Close()
   221  		if err != nil {
   222  			t.Fatalf("could not close ROOT file: %+v", err)
   223  		}
   224  
   225  		if f.units != 8 {
   226  			t.Fatalf("not a big file")
   227  		}
   228  	}()
   229  
   230  	f, err := Open(fname)
   231  	if err != nil {
   232  		t.Fatalf("could not open ROOT file: %+v", err)
   233  	}
   234  	defer f.Close()
   235  
   236  	for _, kv := range kvals {
   237  		obj, err := f.Get(kv.k)
   238  		if err != nil {
   239  			t.Fatalf("could not get %s: %+v", kv.k, err)
   240  		}
   241  		if got, want := obj.(*rbase.ObjString).String(), kv.v; got != want {
   242  			t.Fatalf("invalid %s value: got=%q, want=%q", kv.k, got, want)
   243  		}
   244  	}
   245  
   246  	if f.units != 8 {
   247  		t.Fatalf("not a big file")
   248  	}
   249  
   250  	if !rtests.HasROOT {
   251  		t.Logf("skip test with ROOT/C++")
   252  		return
   253  	}
   254  
   255  	const rootls = `#include <iostream>
   256  #include "TFile.h"
   257  #include "TNamed.h"
   258  #include "TObjString.h"
   259  
   260  #include <string>
   261  
   262  void rootls(const char *fname, const char *kname, const char *v) {
   263  	auto f = TFile::Open(fname);
   264  	auto o = f->Get<TObjString>(kname);
   265  	if (o == NULL) {
   266  		std:cerr << "could not retrieve [" << kname << "]" << std::endl;
   267  		o->ClassName();
   268  	}
   269  	std::cout << "retrieved: [" << kname << "]: [" << o->GetString() << "]" << std::endl;
   270  
   271  	auto got = std::string(o->GetString());
   272  	auto want = std::string(v);
   273  	if (got != want) {
   274  		std::cerr << "invalid key value for [" << kname << "]: got=[" << got << "], want=[" << want << "]\n";
   275  		exit(1);
   276  	}
   277  }
   278  `
   279  	for _, kv := range kvals {
   280  		out, err := rtests.RunCxxROOT("rootls", []byte(rootls), fname, kv.k, kv.v)
   281  		if err != nil {
   282  			t.Fatalf("ROOT/C++ could not process file %q:\n%s", fname, string(out))
   283  		}
   284  	}
   285  }