go-hep.org/x/hep@v0.38.1/groot/rcmd/diff_test.go (about)

     1  // Copyright ©2020 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 rcmd_test
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  	"testing"
    13  
    14  	"go-hep.org/x/hep/groot"
    15  	"go-hep.org/x/hep/groot/rbase"
    16  	"go-hep.org/x/hep/groot/rcmd"
    17  	"go-hep.org/x/hep/groot/riofs"
    18  	"go-hep.org/x/hep/groot/rtree"
    19  )
    20  
    21  func TestDiff(t *testing.T) {
    22  	tmp, err := os.MkdirTemp("", "groot-rcmd-diff-")
    23  	if err != nil {
    24  		t.Fatalf("%+v", err)
    25  	}
    26  	defer os.RemoveAll(tmp)
    27  
    28  	for _, tc := range []struct {
    29  		name string
    30  		keys []string
    31  		err  error
    32  		want string
    33  		fref func(name string) *riofs.File
    34  		fchk func(name string) *riofs.File
    35  	}{
    36  		{
    37  			name: "same",
    38  			fref: func(name string) *riofs.File {
    39  				f, err := groot.Open("../testdata/small-flat-tree.root")
    40  				if err != nil {
    41  					t.Fatalf("%+v", err)
    42  				}
    43  				return f
    44  			},
    45  			fchk: func(name string) *riofs.File {
    46  				f, err := groot.Open("../testdata/small-flat-tree.root")
    47  				if err != nil {
    48  					t.Fatalf("%+v", err)
    49  				}
    50  				return f
    51  			},
    52  		},
    53  		{
    54  			name: "empty",
    55  			fref: func(name string) *riofs.File {
    56  				f, err := groot.Create(name)
    57  				if err != nil {
    58  					t.Fatalf("%+v", err)
    59  				}
    60  				err = f.Close()
    61  				if err != nil {
    62  					t.Fatalf("%+v", err)
    63  				}
    64  
    65  				f, err = groot.Open(name)
    66  				if err != nil {
    67  					t.Fatalf("%+v", err)
    68  				}
    69  
    70  				return f
    71  			},
    72  			fchk: func(name string) *riofs.File {
    73  				f, err := groot.Create(name)
    74  				if err != nil {
    75  					t.Fatalf("%+v", err)
    76  				}
    77  				err = f.Close()
    78  				if err != nil {
    79  					t.Fatalf("%+v", err)
    80  				}
    81  
    82  				f, err = groot.Open(name)
    83  				if err != nil {
    84  					t.Fatalf("%+v", err)
    85  				}
    86  
    87  				return f
    88  			},
    89  			err: fmt.Errorf("could not compute keys to compare: empty key set"),
    90  		},
    91  		{
    92  			name: "empty-with-keys",
    93  			keys: []string{" "},
    94  			fref: func(name string) *riofs.File {
    95  				f, err := groot.Create(name)
    96  				if err != nil {
    97  					t.Fatalf("%+v", err)
    98  				}
    99  				err = f.Close()
   100  				if err != nil {
   101  					t.Fatalf("%+v", err)
   102  				}
   103  
   104  				f, err = groot.Open(name)
   105  				if err != nil {
   106  					t.Fatalf("%+v", err)
   107  				}
   108  
   109  				return f
   110  			},
   111  			fchk: func(name string) *riofs.File {
   112  				f, err := groot.Create(name)
   113  				if err != nil {
   114  					t.Fatalf("%+v", err)
   115  				}
   116  				err = f.Close()
   117  				if err != nil {
   118  					t.Fatalf("%+v", err)
   119  				}
   120  
   121  				f, err = groot.Open(name)
   122  				if err != nil {
   123  					t.Fatalf("%+v", err)
   124  				}
   125  
   126  				return f
   127  			},
   128  			err: fmt.Errorf("could not compute keys to compare: empty key set"),
   129  		},
   130  		{
   131  			name: "only-dirs",
   132  			fref: func(name string) *riofs.File {
   133  				f, err := groot.Create(name)
   134  				if err != nil {
   135  					t.Fatalf("%+v", err)
   136  				}
   137  
   138  				_, err = riofs.Dir(f).Mkdir("dir-1/dir-11/dir-111")
   139  				if err != nil {
   140  					t.Fatalf("%+v", err)
   141  				}
   142  
   143  				err = f.Close()
   144  				if err != nil {
   145  					t.Fatalf("%+v", err)
   146  				}
   147  
   148  				f, err = groot.Open(name)
   149  				if err != nil {
   150  					t.Fatalf("%+v", err)
   151  				}
   152  
   153  				return f
   154  			},
   155  			fchk: func(name string) *riofs.File {
   156  				f, err := groot.Create(name)
   157  				if err != nil {
   158  					t.Fatalf("%+v", err)
   159  				}
   160  
   161  				_, err = riofs.Dir(f).Mkdir("dir-1/dir-11/dir-111")
   162  				if err != nil {
   163  					t.Fatalf("%+v", err)
   164  				}
   165  
   166  				err = f.Close()
   167  				if err != nil {
   168  					t.Fatalf("%+v", err)
   169  				}
   170  
   171  				f, err = groot.Open(name)
   172  				if err != nil {
   173  					t.Fatalf("%+v", err)
   174  				}
   175  
   176  				return f
   177  			},
   178  		},
   179  		{
   180  			name: "different-key-type",
   181  			fref: func(name string) *riofs.File {
   182  				f, err := groot.Create(name)
   183  				if err != nil {
   184  					t.Fatalf("%+v", err)
   185  				}
   186  
   187  				dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   188  				if err != nil {
   189  					t.Fatalf("%+v", err)
   190  				}
   191  
   192  				err = dir.Put("k1", rbase.NewNamed("k1-name", "k1-title"))
   193  				if err != nil {
   194  					t.Fatalf("%+v", err)
   195  				}
   196  
   197  				err = f.Close()
   198  				if err != nil {
   199  					t.Fatalf("%+v", err)
   200  				}
   201  
   202  				f, err = groot.Open(name)
   203  				if err != nil {
   204  					t.Fatalf("%+v", err)
   205  				}
   206  				return f
   207  			},
   208  			fchk: func(name string) *riofs.File {
   209  				f, err := groot.Create(name)
   210  				if err != nil {
   211  					t.Fatalf("%+v", err)
   212  				}
   213  
   214  				dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   215  				if err != nil {
   216  					t.Fatalf("%+v", err)
   217  				}
   218  
   219  				err = dir.Put("k1", rbase.NewObjString("obj-string"))
   220  				if err != nil {
   221  					t.Fatalf("%+v", err)
   222  				}
   223  
   224  				err = f.Close()
   225  				if err != nil {
   226  					t.Fatalf("%+v", err)
   227  				}
   228  
   229  				f, err = groot.Open(name)
   230  				if err != nil {
   231  					t.Fatalf("%+v", err)
   232  				}
   233  				return f
   234  			},
   235  			err: fmt.Errorf("dir-1: values for dir-11 in directory differ: dir-1/dir-11: values for k1 in directory differ: dir-1/dir-11/k1: type of keys differ: ref=*rbase.Named chk=*rbase.ObjString"),
   236  		},
   237  		{
   238  			name: "different-key-set-chk",
   239  			fref: func(name string) *riofs.File {
   240  				f, err := groot.Create(name)
   241  				if err != nil {
   242  					t.Fatalf("%+v", err)
   243  				}
   244  
   245  				dir11, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   246  				if err != nil {
   247  					t.Fatalf("%+v", err)
   248  				}
   249  
   250  				dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11")
   251  				if err != nil {
   252  					t.Fatalf("%+v", err)
   253  				}
   254  				_ = dir21
   255  
   256  				err = dir11.Put("k1", rbase.NewObjString("obj-string"))
   257  				if err != nil {
   258  					t.Fatalf("%+v", err)
   259  				}
   260  
   261  				err = f.Close()
   262  				if err != nil {
   263  					t.Fatalf("%+v", err)
   264  				}
   265  
   266  				f, err = groot.Open(name)
   267  				if err != nil {
   268  					t.Fatalf("%+v", err)
   269  				}
   270  				return f
   271  			},
   272  			fchk: func(name string) *riofs.File {
   273  				f, err := groot.Create(name)
   274  				if err != nil {
   275  					t.Fatalf("%+v", err)
   276  				}
   277  
   278  				dir11, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   279  				if err != nil {
   280  					t.Fatalf("%+v", err)
   281  				}
   282  
   283  				dir31, err := riofs.Dir(f).Mkdir("dir-3/dir-11")
   284  				if err != nil {
   285  					t.Fatalf("%+v", err)
   286  				}
   287  				_ = dir31
   288  
   289  				err = dir11.Put("k1", rbase.NewObjString("obj-string-xxx"))
   290  				if err != nil {
   291  					t.Fatalf("%+v", err)
   292  				}
   293  
   294  				err = f.Close()
   295  				if err != nil {
   296  					t.Fatalf("%+v", err)
   297  				}
   298  
   299  				f, err = groot.Open(name)
   300  				if err != nil {
   301  					t.Fatalf("%+v", err)
   302  				}
   303  				return f
   304  			},
   305  			err:  fmt.Errorf("could not compute keys to compare: key set differ"),
   306  			want: "key[dir-2] -- missing from chk-file\n",
   307  		},
   308  		{
   309  			name: "different-key-set-ref",
   310  			keys: []string{"dir-1", "dir-3"},
   311  			fref: func(name string) *riofs.File {
   312  				f, err := groot.Create(name)
   313  				if err != nil {
   314  					t.Fatalf("%+v", err)
   315  				}
   316  
   317  				dir11, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   318  				if err != nil {
   319  					t.Fatalf("%+v", err)
   320  				}
   321  
   322  				dir21, err := riofs.Dir(f).Mkdir("dir-2/dir-11")
   323  				if err != nil {
   324  					t.Fatalf("%+v", err)
   325  				}
   326  				_ = dir21
   327  
   328  				err = dir11.Put("k1", rbase.NewObjString("obj-string"))
   329  				if err != nil {
   330  					t.Fatalf("%+v", err)
   331  				}
   332  
   333  				err = f.Close()
   334  				if err != nil {
   335  					t.Fatalf("%+v", err)
   336  				}
   337  
   338  				f, err = groot.Open(name)
   339  				if err != nil {
   340  					t.Fatalf("%+v", err)
   341  				}
   342  				return f
   343  			},
   344  			fchk: func(name string) *riofs.File {
   345  				f, err := groot.Create(name)
   346  				if err != nil {
   347  					t.Fatalf("%+v", err)
   348  				}
   349  
   350  				dir11, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   351  				if err != nil {
   352  					t.Fatalf("%+v", err)
   353  				}
   354  
   355  				dir31, err := riofs.Dir(f).Mkdir("dir-3/dir-11")
   356  				if err != nil {
   357  					t.Fatalf("%+v", err)
   358  				}
   359  				_ = dir31
   360  
   361  				err = dir11.Put("k1", rbase.NewObjString("obj-string-xxx"))
   362  				if err != nil {
   363  					t.Fatalf("%+v", err)
   364  				}
   365  
   366  				err = f.Close()
   367  				if err != nil {
   368  					t.Fatalf("%+v", err)
   369  				}
   370  
   371  				f, err = groot.Open(name)
   372  				if err != nil {
   373  					t.Fatalf("%+v", err)
   374  				}
   375  				return f
   376  			},
   377  			err:  fmt.Errorf("could not compute keys to compare: key set differ"),
   378  			want: "key[dir-3] -- missing from ref-file\n",
   379  		},
   380  		{
   381  			name: "different-key-value",
   382  			keys: []string{"dir-1"},
   383  			fref: func(name string) *riofs.File {
   384  				f, err := groot.Create(name)
   385  				if err != nil {
   386  					t.Fatalf("%+v", err)
   387  				}
   388  
   389  				dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   390  				if err != nil {
   391  					t.Fatalf("%+v", err)
   392  				}
   393  
   394  				err = dir.Put("k1", rbase.NewObjString("obj-string"))
   395  				if err != nil {
   396  					t.Fatalf("%+v", err)
   397  				}
   398  
   399  				err = f.Close()
   400  				if err != nil {
   401  					t.Fatalf("%+v", err)
   402  				}
   403  
   404  				f, err = groot.Open(name)
   405  				if err != nil {
   406  					t.Fatalf("%+v", err)
   407  				}
   408  				return f
   409  			},
   410  			fchk: func(name string) *riofs.File {
   411  				f, err := groot.Create(name)
   412  				if err != nil {
   413  					t.Fatalf("%+v", err)
   414  				}
   415  
   416  				dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   417  				if err != nil {
   418  					t.Fatalf("%+v", err)
   419  				}
   420  
   421  				err = dir.Put("k1", rbase.NewObjString("obj-string-xxx"))
   422  				if err != nil {
   423  					t.Fatalf("%+v", err)
   424  				}
   425  
   426  				err = f.Close()
   427  				if err != nil {
   428  					t.Fatalf("%+v", err)
   429  				}
   430  
   431  				f, err = groot.Open(name)
   432  				if err != nil {
   433  					t.Fatalf("%+v", err)
   434  				}
   435  				return f
   436  			},
   437  			err: fmt.Errorf("dir-1: values for dir-11 in directory differ: dir-1/dir-11: values for k1 in directory differ: dir-1/dir-11/k1: keys differ"),
   438  			want: `key[dir-1/dir-11/k1] (*rbase.ObjString) -- (-ref +chk)
   439  -obj-string
   440  +obj-string-xxx
   441  `,
   442  		},
   443  		{
   444  			name: "different-trees-entries",
   445  			fref: func(name string) *riofs.File {
   446  				f, err := groot.Create(name)
   447  				if err != nil {
   448  					t.Fatalf("%+v", err)
   449  				}
   450  
   451  				dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   452  				if err != nil {
   453  					t.Fatalf("%+v", err)
   454  				}
   455  
   456  				var data struct {
   457  					I32 int32
   458  					F64 float64
   459  					Arr [2]float64
   460  				}
   461  				w, err := rtree.NewWriter(dir, "tree", rtree.WriteVarsFromStruct(&data))
   462  				if err != nil {
   463  					t.Fatalf("%+v", err)
   464  				}
   465  
   466  				for i := range 5 {
   467  					data.I32 = int32(i)
   468  					data.F64 = float64(i)
   469  					data.Arr = [2]float64{float64(i + 1), float64(i + 2)}
   470  					_, err = w.Write()
   471  					if err != nil {
   472  						t.Fatalf("could not write event #%d: %+v", i, err)
   473  					}
   474  				}
   475  
   476  				err = w.Close()
   477  				if err != nil {
   478  					t.Fatalf("%+v", err)
   479  				}
   480  
   481  				err = f.Close()
   482  				if err != nil {
   483  					t.Fatalf("%+v", err)
   484  				}
   485  
   486  				f, err = groot.Open(name)
   487  				if err != nil {
   488  					t.Fatalf("%+v", err)
   489  				}
   490  				return f
   491  			},
   492  			fchk: func(name string) *riofs.File {
   493  				f, err := groot.Create(name)
   494  				if err != nil {
   495  					t.Fatalf("%+v", err)
   496  				}
   497  
   498  				dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   499  				if err != nil {
   500  					t.Fatalf("%+v", err)
   501  				}
   502  
   503  				var data struct {
   504  					I32 int32
   505  					F64 float64
   506  					Arr [2]float64
   507  				}
   508  				w, err := rtree.NewWriter(dir, "tree", rtree.WriteVarsFromStruct(&data))
   509  				if err != nil {
   510  					t.Fatalf("%+v", err)
   511  				}
   512  
   513  				for i := range 6 {
   514  					data.I32 = int32(i)
   515  					data.F64 = float64(i)
   516  					data.Arr = [2]float64{float64(i + 1), float64(i + 2)}
   517  					_, err = w.Write()
   518  					if err != nil {
   519  						t.Fatalf("could not write event #%d: %+v", i, err)
   520  					}
   521  				}
   522  
   523  				err = w.Close()
   524  				if err != nil {
   525  					t.Fatalf("%+v", err)
   526  				}
   527  
   528  				err = f.Close()
   529  				if err != nil {
   530  					t.Fatalf("%+v", err)
   531  				}
   532  
   533  				f, err = groot.Open(name)
   534  				if err != nil {
   535  					t.Fatalf("%+v", err)
   536  				}
   537  				return f
   538  			},
   539  			err: fmt.Errorf("dir-1: values for dir-11 in directory differ: dir-1/dir-11: values for tree in directory differ: dir-1/dir-11/tree: number of entries differ: ref=5 chk=6"),
   540  		},
   541  		{
   542  			name: "different-trees-values",
   543  			fref: func(name string) *riofs.File {
   544  				f, err := groot.Create(name)
   545  				if err != nil {
   546  					t.Fatalf("%+v", err)
   547  				}
   548  
   549  				dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   550  				if err != nil {
   551  					t.Fatalf("%+v", err)
   552  				}
   553  
   554  				var data struct {
   555  					I32 int32
   556  					F64 float64
   557  					Arr [2]float64
   558  				}
   559  				w, err := rtree.NewWriter(dir, "tree", rtree.WriteVarsFromStruct(&data))
   560  				if err != nil {
   561  					t.Fatalf("%+v", err)
   562  				}
   563  
   564  				for i := range 5 {
   565  					data.I32 = int32(i)
   566  					data.F64 = float64(i + 1)
   567  					data.Arr = [2]float64{float64(i + 1), float64(i + 2)}
   568  					_, err = w.Write()
   569  					if err != nil {
   570  						t.Fatalf("could not write event #%d: %+v", i, err)
   571  					}
   572  				}
   573  
   574  				err = w.Close()
   575  				if err != nil {
   576  					t.Fatalf("%+v", err)
   577  				}
   578  
   579  				err = f.Close()
   580  				if err != nil {
   581  					t.Fatalf("%+v", err)
   582  				}
   583  
   584  				f, err = groot.Open(name)
   585  				if err != nil {
   586  					t.Fatalf("%+v", err)
   587  				}
   588  				return f
   589  			},
   590  			fchk: func(name string) *riofs.File {
   591  				f, err := groot.Create(name)
   592  				if err != nil {
   593  					t.Fatalf("%+v", err)
   594  				}
   595  
   596  				dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   597  				if err != nil {
   598  					t.Fatalf("%+v", err)
   599  				}
   600  
   601  				var data struct {
   602  					I32 int32
   603  					F64 float64
   604  					Arr [2]float64
   605  				}
   606  				w, err := rtree.NewWriter(dir, "tree", rtree.WriteVarsFromStruct(&data))
   607  				if err != nil {
   608  					t.Fatalf("%+v", err)
   609  				}
   610  
   611  				for i := range 5 {
   612  					data.I32 = int32(i)
   613  					data.F64 = float64(i)
   614  					data.Arr = [2]float64{float64(i), float64(i + 2)}
   615  					_, err = w.Write()
   616  					if err != nil {
   617  						t.Fatalf("could not write event #%d: %+v", i, err)
   618  					}
   619  				}
   620  
   621  				err = w.Close()
   622  				if err != nil {
   623  					t.Fatalf("%+v", err)
   624  				}
   625  
   626  				err = f.Close()
   627  				if err != nil {
   628  					t.Fatalf("%+v", err)
   629  				}
   630  
   631  				f, err = groot.Open(name)
   632  				if err != nil {
   633  					t.Fatalf("%+v", err)
   634  				}
   635  				return f
   636  			},
   637  			err: fmt.Errorf("dir-1: values for dir-11 in directory differ: dir-1/dir-11: values for tree in directory differ: dir-1/dir-11/tree: trees differ"),
   638  			want: `key[dir-1/dir-11/tree][0000].F64 -- (-ref +chk)
   639    float64(
   640  - 	1,
   641  + 	0,
   642    )
   643  key[dir-1/dir-11/tree][0000].Arr -- (-ref +chk)
   644    [2]float64{
   645  - 	1,
   646  + 	0,
   647    	2,
   648    }
   649  key[dir-1/dir-11/tree][0001].F64 -- (-ref +chk)
   650    float64(
   651  - 	2,
   652  + 	1,
   653    )
   654  key[dir-1/dir-11/tree][0001].Arr -- (-ref +chk)
   655    [2]float64{
   656  - 	2,
   657  + 	1,
   658    	3,
   659    }
   660  key[dir-1/dir-11/tree][0002].F64 -- (-ref +chk)
   661    float64(
   662  - 	3,
   663  + 	2,
   664    )
   665  key[dir-1/dir-11/tree][0002].Arr -- (-ref +chk)
   666    [2]float64{
   667  - 	3,
   668  + 	2,
   669    	4,
   670    }
   671  key[dir-1/dir-11/tree][0003].F64 -- (-ref +chk)
   672    float64(
   673  - 	4,
   674  + 	3,
   675    )
   676  key[dir-1/dir-11/tree][0003].Arr -- (-ref +chk)
   677    [2]float64{
   678  - 	4,
   679  + 	3,
   680    	5,
   681    }
   682  key[dir-1/dir-11/tree][0004].F64 -- (-ref +chk)
   683    float64(
   684  - 	5,
   685  + 	4,
   686    )
   687  key[dir-1/dir-11/tree][0004].Arr -- (-ref +chk)
   688    [2]float64{
   689  - 	5,
   690  + 	4,
   691    	6,
   692    }
   693  `,
   694  		},
   695  		{
   696  			name: "different-trees-types",
   697  			fref: func(name string) *riofs.File {
   698  				f, err := groot.Create(name)
   699  				if err != nil {
   700  					t.Fatalf("%+v", err)
   701  				}
   702  
   703  				dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   704  				if err != nil {
   705  					t.Fatalf("%+v", err)
   706  				}
   707  
   708  				var data struct {
   709  					I32 int64
   710  					F64 float64
   711  					Arr [2]float64
   712  				}
   713  				w, err := rtree.NewWriter(dir, "tree", rtree.WriteVarsFromStruct(&data))
   714  				if err != nil {
   715  					t.Fatalf("%+v", err)
   716  				}
   717  
   718  				for i := range 5 {
   719  					data.I32 = int64(i)
   720  					data.F64 = float64(i + 1)
   721  					data.Arr = [2]float64{float64(i + 1), float64(i + 2)}
   722  					_, err = w.Write()
   723  					if err != nil {
   724  						t.Fatalf("could not write event #%d: %+v", i, err)
   725  					}
   726  				}
   727  
   728  				err = w.Close()
   729  				if err != nil {
   730  					t.Fatalf("%+v", err)
   731  				}
   732  
   733  				err = f.Close()
   734  				if err != nil {
   735  					t.Fatalf("%+v", err)
   736  				}
   737  
   738  				f, err = groot.Open(name)
   739  				if err != nil {
   740  					t.Fatalf("%+v", err)
   741  				}
   742  				return f
   743  			},
   744  			fchk: func(name string) *riofs.File {
   745  				f, err := groot.Create(name)
   746  				if err != nil {
   747  					t.Fatalf("%+v", err)
   748  				}
   749  
   750  				dir, err := riofs.Dir(f).Mkdir("dir-1/dir-11")
   751  				if err != nil {
   752  					t.Fatalf("%+v", err)
   753  				}
   754  
   755  				var data struct {
   756  					I32 int32
   757  					F64 float64
   758  					Arr [2]float64
   759  				}
   760  				w, err := rtree.NewWriter(dir, "tree", rtree.WriteVarsFromStruct(&data))
   761  				if err != nil {
   762  					t.Fatalf("%+v", err)
   763  				}
   764  
   765  				for i := range 5 {
   766  					data.I32 = int32(i)
   767  					data.F64 = float64(i + 1)
   768  					data.Arr = [2]float64{float64(i + 1), float64(i + 2)}
   769  					_, err = w.Write()
   770  					if err != nil {
   771  						t.Fatalf("could not write event #%d: %+v", i, err)
   772  					}
   773  				}
   774  
   775  				err = w.Close()
   776  				if err != nil {
   777  					t.Fatalf("%+v", err)
   778  				}
   779  
   780  				err = f.Close()
   781  				if err != nil {
   782  					t.Fatalf("%+v", err)
   783  				}
   784  
   785  				f, err = groot.Open(name)
   786  				if err != nil {
   787  					t.Fatalf("%+v", err)
   788  				}
   789  				return f
   790  			},
   791  			err: fmt.Errorf("dir-1: values for dir-11 in directory differ: dir-1/dir-11: values for tree in directory differ: dir-1/dir-11/tree: trees differ"),
   792  			want: `key[dir-1/dir-11/tree][0000].I32 -- (-ref +chk)
   793    any(
   794  - 	int64(0),
   795  + 	int32(0),
   796    )
   797  key[dir-1/dir-11/tree][0001].I32 -- (-ref +chk)
   798    any(
   799  - 	int64(1),
   800  + 	int32(1),
   801    )
   802  key[dir-1/dir-11/tree][0002].I32 -- (-ref +chk)
   803    any(
   804  - 	int64(2),
   805  + 	int32(2),
   806    )
   807  key[dir-1/dir-11/tree][0003].I32 -- (-ref +chk)
   808    any(
   809  - 	int64(3),
   810  + 	int32(3),
   811    )
   812  key[dir-1/dir-11/tree][0004].I32 -- (-ref +chk)
   813    any(
   814  - 	int64(4),
   815  + 	int32(4),
   816    )
   817  `,
   818  		},
   819  	} {
   820  		t.Run(tc.name, func(t *testing.T) {
   821  			refname := filepath.Join(tmp, tc.name+"-ref.root")
   822  			fref := tc.fref(refname)
   823  			defer fref.Close()
   824  
   825  			chkname := filepath.Join(tmp, tc.name+"-chk.root")
   826  			fchk := tc.fchk(chkname)
   827  			defer fchk.Close()
   828  
   829  			out := new(strings.Builder)
   830  			err := rcmd.Diff(out, fref, fchk, tc.keys)
   831  			switch {
   832  			case err != nil && tc.err != nil:
   833  				if got, want := err.Error(), tc.err.Error(); got != want {
   834  					t.Fatalf("invalid error.\ngot= %s\nwant=%s\n", got, want)
   835  				}
   836  			case err != nil && tc.err == nil:
   837  				t.Fatalf("unexpected error: %+v", err)
   838  
   839  			case err == nil && tc.err != nil:
   840  				t.Fatalf("expected an error: %+v", tc.err)
   841  
   842  			case err == nil && tc.err == nil:
   843  				// ok
   844  				return
   845  			}
   846  
   847  			// replace non-breaking spaces (U+00a0) with regular space (U+0020).
   848  			got := strings.Replace(out.String(), " ", " ", -1)
   849  
   850  			if got, want := got, tc.want; got != want {
   851  				t.Fatalf("invalid diff.\ngot:\n%s\nwant:\n%s", got, want)
   852  			}
   853  		})
   854  	}
   855  }