go-hep.org/x/hep@v0.38.1/groot/rcont/clonesarray_test.go (about) 1 // Copyright ©2019 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 rcont_test 6 7 import ( 8 "fmt" 9 "io" 10 "os" 11 "path/filepath" 12 "reflect" 13 "testing" 14 15 "go-hep.org/x/hep/groot" 16 "go-hep.org/x/hep/groot/internal/rtests" 17 "go-hep.org/x/hep/groot/rbase" 18 "go-hep.org/x/hep/groot/rbytes" 19 "go-hep.org/x/hep/groot/rcont" 20 "go-hep.org/x/hep/groot/root" 21 "go-hep.org/x/hep/groot/rtypes" 22 ) 23 24 func TestTClonesArray(t *testing.T) { 25 for _, fname := range []string{ 26 "../testdata/tclonesarray-no-streamerbypass.root", 27 // "../testdata/tclonesarray-with-streamerbypass.root", // FIXME(sbinet): needs member-wise streaming. 28 } { 29 t.Run(fname, func(t *testing.T) { 30 f, err := groot.Open(fname) 31 if err != nil { 32 t.Fatal(err) 33 } 34 defer f.Close() 35 36 o, err := f.Get("clones") 37 if err != nil { 38 t.Fatal(err) 39 } 40 41 tca := o.(*rcont.ClonesArray) 42 if got, want := tca.Len(), 3; got != want { 43 t.Fatalf("invalid length: got=%d, want=%d", got, want) 44 } 45 if got, want := tca.Last(), 2; got != want { 46 t.Fatalf("invalid last: got=%d, want=%d", got, want) 47 } 48 49 for i, want := range []root.Object{ 50 rbase.NewObjString("Elem-0"), 51 rbase.NewObjString("elem-1"), 52 rbase.NewObjString("Elem-20"), 53 } { 54 got := tca.At(i) 55 if !reflect.DeepEqual(got, want) { 56 t.Errorf("invalid obj[%d]: got=%+v, want=%+v", i, got, want) 57 } 58 } 59 }) 60 } 61 } 62 63 func TestTClonesArrayRW(t *testing.T) { 64 dir, err := os.MkdirTemp("", "groot-") 65 if err != nil { 66 t.Fatal(err) 67 } 68 defer os.RemoveAll(dir) 69 70 for i, tc := range []struct { 71 name string 72 want *rcont.ClonesArray 73 cmp func(a, b *rcont.ClonesArray) bool 74 }{ 75 { 76 name: "TClonesArray", 77 want: func() *rcont.ClonesArray { 78 o := rcont.NewClonesArray() 79 o.SetElems([]root.Object{ 80 rbase.NewObjString("Elem-0"), 81 rbase.NewObjString("elem-1"), 82 rbase.NewObjString("Elem-20"), 83 }) 84 return o 85 }(), 86 cmp: func(got, want *rcont.ClonesArray) bool { 87 if g, w := got.Len(), want.Len(); g != w { 88 return false 89 } 90 if g, w := got.Last(), want.Last(); g != w { 91 return false 92 } 93 for i := range got.Len() { 94 if g, w := got.At(i), want.At(i); !reflect.DeepEqual(g, w) { 95 return false 96 } 97 } 98 return true 99 }, 100 }, 101 } { 102 fname := filepath.Join(dir, fmt.Sprintf("tclonesarray-%d.root", i)) 103 t.Run(tc.name, func(t *testing.T) { 104 const kname = "my-key" 105 106 w, err := groot.Create(fname) 107 if err != nil { 108 t.Fatal(err) 109 } 110 defer w.Close() 111 112 err = w.Put(kname, tc.want) 113 if err != nil { 114 t.Fatal(err) 115 } 116 117 if got, want := len(w.Keys()), 1; got != want { 118 t.Fatalf("invalid number of keys. got=%d, want=%d", got, want) 119 } 120 121 { 122 wbuf := rbytes.NewWBuffer(nil, nil, 0, w) 123 wbuf.SetErr(io.EOF) 124 _, err := tc.want.MarshalROOT(wbuf) 125 if err == nil { 126 t.Fatalf("expected an error") 127 } 128 if err != io.EOF { 129 t.Fatalf("got=%v, want=%v", err, io.EOF) 130 } 131 132 rbuf := rbytes.NewRBuffer(wbuf.Bytes(), nil, 0, w) 133 class := tc.want.Class() 134 obj := rtypes.Factory.Get(class)().Interface().(rbytes.Unmarshaler) 135 rbuf.SetErr(io.EOF) 136 err = obj.UnmarshalROOT(rbuf) 137 if err == nil { 138 t.Fatalf("expected an error") 139 } 140 if err != io.EOF { 141 t.Fatalf("got=%v, want=%v", err, io.EOF) 142 } 143 rbuf.SetErr(nil) 144 } 145 146 err = w.Close() 147 if err != nil { 148 t.Fatalf("error closing file: %v", err) 149 } 150 151 r, err := groot.Open(fname) 152 if err != nil { 153 t.Fatal(err) 154 } 155 defer r.Close() 156 157 si := r.StreamerInfos() 158 if len(si) == 0 { 159 t.Fatalf("empty list of streamers") 160 } 161 162 if got, want := len(r.Keys()), 1; got != want { 163 t.Fatalf("invalid number of keys. got=%d, want=%d", got, want) 164 } 165 166 rgot, err := r.Get(kname) 167 if err != nil { 168 t.Fatal(err) 169 } 170 171 if got, want := rgot.(*rcont.ClonesArray), tc.want; !tc.cmp(got, want) { 172 t.Fatalf("error reading back objstring.\ngot = %#v\nwant = %#v", got, want) 173 } 174 175 err = r.Close() 176 if err != nil { 177 t.Fatalf("error closing file: %v", err) 178 } 179 180 if !rtests.HasROOT { 181 t.Logf("skip test with ROOT/C++") 182 return 183 } 184 185 const rootls = `#include <iostream> 186 #include "TFile.h" 187 #include "TClonesArray.h" 188 189 void rootls(const char *fname, const char *kname) { 190 auto f = TFile::Open(fname); 191 auto o = f->Get<TClonesArray>(kname); 192 if (o == NULL) { 193 std:cerr << "could not retrieve [" << kname << "]" << std::endl; 194 o->ClassName(); 195 } 196 std::cout << "retrieved TClonesArray: [" << kname << "]" << std::endl; 197 } 198 ` 199 out, err := rtests.RunCxxROOT("rootls", []byte(rootls), fname, kname) 200 if err != nil { 201 t.Fatalf("ROOT/C++ could not open file %q:\n%s", fname, string(out)) 202 } 203 }) 204 } 205 }