src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/persistent/vector/vector_test.go (about)

     1  package vector
     2  
     3  import (
     4  	"errors"
     5  	"math/rand"
     6  	"strconv"
     7  	"testing"
     8  )
     9  
    10  // Nx is the minimum number of elements for the internal tree of the vector to
    11  // be x levels deep.
    12  const (
    13  	N1 = tailMaxLen + 1                              // 33
    14  	N2 = nodeSize + tailMaxLen + 1                   // 65
    15  	N3 = nodeSize*nodeSize + tailMaxLen + 1          // 1057
    16  	N4 = nodeSize*nodeSize*nodeSize + tailMaxLen + 1 // 32801
    17  )
    18  
    19  func TestVector(t *testing.T) {
    20  	run := func(n int) {
    21  		t.Run(strconv.Itoa(n), func(t *testing.T) {
    22  			v := testConj(t, n)
    23  			testIndex(t, v, 0, n)
    24  			testAssoc(t, v, "233")
    25  			testIterator(t, v.Iterator(), 0, n)
    26  			testPop(t, v)
    27  		})
    28  	}
    29  
    30  	for i := 0; i <= N3; i++ {
    31  		run(i)
    32  	}
    33  	run(N4)
    34  }
    35  
    36  // Regression test against #4.
    37  func TestIterator_VectorWithNil(t *testing.T) {
    38  	run := func(n int) {
    39  		t.Run(strconv.Itoa(n), func(t *testing.T) {
    40  			v := Empty
    41  			for i := 0; i < n; i++ {
    42  				v = v.Conj(nil)
    43  			}
    44  
    45  			iterated := 0
    46  			for it := v.Iterator(); it.HasElem(); it.Next() {
    47  				iterated++
    48  				if it.Elem() != nil {
    49  					t.Errorf("element not nil")
    50  				}
    51  			}
    52  			if iterated != n {
    53  				t.Errorf("did not iterate %d items", n)
    54  			}
    55  		})
    56  	}
    57  	for i := 0; i <= N3; i++ {
    58  		run(i)
    59  	}
    60  	run(N4)
    61  }
    62  
    63  // testConj creates a vector containing 0...n-1 with Conj, and ensures that the
    64  // length of the old and new vectors are expected after each Conj. It returns
    65  // the created vector.
    66  func testConj(t *testing.T, n int) Vector {
    67  	v := Empty
    68  	for i := 0; i < n; i++ {
    69  		oldv := v
    70  		v = v.Conj(i)
    71  
    72  		if count := oldv.Len(); count != i {
    73  			t.Errorf("oldv.Count() == %v, want %v", count, i)
    74  		}
    75  		if count := v.Len(); count != i+1 {
    76  			t.Errorf("v.Count() == %v, want %v", count, i+1)
    77  		}
    78  	}
    79  	return v
    80  }
    81  
    82  // testIndex tests Index, assuming that the vector contains begin...int-1.
    83  func testIndex(t *testing.T, v Vector, begin, end int) {
    84  	n := v.Len()
    85  	for i := 0; i < n; i++ {
    86  		elem, _ := v.Index(i)
    87  		if elem != i {
    88  			t.Errorf("v.Index(%v) == %v, want %v", i, elem, i)
    89  		}
    90  	}
    91  	for _, i := range []int{-2, -1, n, n + 1, n * 2} {
    92  		if elem, _ := v.Index(i); elem != nil {
    93  			t.Errorf("v.Index(%d) == %v, want nil", i, elem)
    94  		}
    95  	}
    96  }
    97  
    98  // testIterator tests the iterator, assuming that the result is begin...end-1.
    99  func testIterator(t *testing.T, it Iterator, begin, end int) {
   100  	i := begin
   101  	for ; it.HasElem(); it.Next() {
   102  		elem := it.Elem()
   103  		if elem != i {
   104  			t.Errorf("iterator produce %v, want %v", elem, i)
   105  		}
   106  		i++
   107  	}
   108  	if i != end {
   109  		t.Errorf("iterator produces up to %v, want %v", i, end)
   110  	}
   111  }
   112  
   113  // testAssoc tests Assoc by replacing each element.
   114  func testAssoc(t *testing.T, v Vector, subst any) {
   115  	n := v.Len()
   116  	for i := 0; i <= n; i++ {
   117  		oldv := v
   118  		v = v.Assoc(i, subst)
   119  
   120  		if i < n {
   121  			elem, _ := oldv.Index(i)
   122  			if elem != i {
   123  				t.Errorf("oldv.Index(%v) == %v, want %v", i, elem, i)
   124  			}
   125  		}
   126  
   127  		elem, _ := v.Index(i)
   128  		if elem != subst {
   129  			t.Errorf("v.Index(%v) == %v, want %v", i, elem, subst)
   130  		}
   131  	}
   132  
   133  	n++
   134  	for _, i := range []int{-1, n + 1, n + 2, n * 2} {
   135  		newv := v.Assoc(i, subst)
   136  		if newv != nil {
   137  			t.Errorf("v.Assoc(%d) = %v, want nil", i, newv)
   138  		}
   139  	}
   140  }
   141  
   142  // testPop tests Pop by removing each element.
   143  func testPop(t *testing.T, v Vector) {
   144  	n := v.Len()
   145  	for i := 0; i < n; i++ {
   146  		oldv := v
   147  		v = v.Pop()
   148  
   149  		if count := oldv.Len(); count != n-i {
   150  			t.Errorf("oldv.Count() == %v, want %v", count, n-i)
   151  		}
   152  		if count := v.Len(); count != n-i-1 {
   153  			t.Errorf("oldv.Count() == %v, want %v", count, n-i-1)
   154  		}
   155  	}
   156  	newv := v.Pop()
   157  	if newv != nil {
   158  		t.Errorf("v.Pop() = %v, want nil", newv)
   159  	}
   160  }
   161  
   162  func TestSubVector(t *testing.T) {
   163  	v := Empty
   164  	for i := 0; i < 10; i++ {
   165  		v = v.Conj(i)
   166  	}
   167  
   168  	sv := v.SubVector(0, 4)
   169  	testIndex(t, sv, 0, 4)
   170  	testAssoc(t, sv, "233")
   171  	testIterator(t, sv.Iterator(), 0, 4)
   172  	testPop(t, sv)
   173  
   174  	sv = v.SubVector(1, 4)
   175  	if !checkVector(sv, 1, 2, 3) {
   176  		t.Errorf("v[0:4] is not expected")
   177  	}
   178  	if !checkVector(sv.Assoc(1, "233"), 1, "233", 3) {
   179  		t.Errorf("v[0:4].Assoc is not expected")
   180  	}
   181  	if !checkVector(sv.Conj("233"), 1, 2, 3, "233") {
   182  		t.Errorf("v[0:4].Conj is not expected")
   183  	}
   184  	if !checkVector(sv.Pop(), 1, 2) {
   185  		t.Errorf("v[0:4].Pop is not expected")
   186  	}
   187  	if !checkVector(sv.SubVector(1, 2), 2) {
   188  		t.Errorf("v[0:4][1:2] is not expected")
   189  	}
   190  	testIterator(t, sv.Iterator(), 1, 4)
   191  
   192  	if !checkVector(v.SubVector(1, 1)) {
   193  		t.Errorf("v[1:1] is not expected")
   194  	}
   195  	// Begin is allowed to be equal to n if end is also n
   196  	if !checkVector(v.SubVector(10, 10)) {
   197  		t.Errorf("v[10:10] is not expected")
   198  	}
   199  
   200  	bad := v.SubVector(-1, 0)
   201  	if bad != nil {
   202  		t.Errorf("v.SubVector(-1, 0) = %v, want nil", bad)
   203  	}
   204  	bad = v.SubVector(5, 100)
   205  	if bad != nil {
   206  		t.Errorf("v.SubVector(5, 100) = %v, want nil", bad)
   207  	}
   208  	bad = v.SubVector(-1, 100)
   209  	if bad != nil {
   210  		t.Errorf("v.SubVector(-1, 100) = %v, want nil", bad)
   211  	}
   212  	bad = v.SubVector(4, 2)
   213  	if bad != nil {
   214  		t.Errorf("v.SubVector(4, 2) = %v, want nil", bad)
   215  	}
   216  }
   217  
   218  // Regression test for https://b.elv.sh/1287: crash when tree has a height >= 1
   219  // and start of subvector is in the tail.
   220  func TestSubVector_BeginFromTail(t *testing.T) {
   221  	v := Empty
   222  	for i := 0; i < 65; i++ {
   223  		v = v.Conj(i)
   224  	}
   225  	sv := v.SubVector(64, 65)
   226  	testIterator(t, sv.Iterator(), 64, 65)
   227  }
   228  
   229  func checkVector(v Vector, values ...any) bool {
   230  	if v.Len() != len(values) {
   231  		return false
   232  	}
   233  	for i, a := range values {
   234  		if x, _ := v.Index(i); x != a {
   235  			return false
   236  		}
   237  	}
   238  	return true
   239  }
   240  
   241  func TestVectorEqual(t *testing.T) {
   242  	v1, v2 := Empty, Empty
   243  	for i := 0; i < N3; i++ {
   244  		elem := rand.Int63()
   245  		v1 = v1.Conj(elem)
   246  		v2 = v2.Conj(elem)
   247  		if !eqVector(v1, v2) {
   248  			t.Errorf("Not equal after Conj'ing %d elements", i+1)
   249  		}
   250  	}
   251  }
   252  
   253  func eqVector(v1, v2 Vector) bool {
   254  	if v1.Len() != v2.Len() {
   255  		return false
   256  	}
   257  	for i := 0; i < v1.Len(); i++ {
   258  		a1, _ := v1.Index(i)
   259  		a2, _ := v2.Index(i)
   260  		if a1 != a2 {
   261  			return false
   262  		}
   263  	}
   264  	return true
   265  }
   266  
   267  var marshalJSONTests = []struct {
   268  	in      Vector
   269  	wantOut string
   270  	wantErr error
   271  }{
   272  	{makeVector("1", 2, nil), `["1",2,null]`, nil},
   273  	{makeVector("1", makeVector(2)), `["1",[2]]`, nil},
   274  	{makeVector(0, 1, 2, 3, 4, 5).SubVector(1, 5), `[1,2,3,4]`, nil},
   275  	{makeVector(0, func() {}), "", errors.New("element 1: json: unsupported type: func()")},
   276  }
   277  
   278  func TestMarshalJSON(t *testing.T) {
   279  	for i, test := range marshalJSONTests {
   280  		out, err := test.in.MarshalJSON()
   281  		if string(out) != test.wantOut {
   282  			t.Errorf("v%d.MarshalJSON -> out %q, want %q", i, out, test.wantOut)
   283  		}
   284  		if err == nil || test.wantErr == nil {
   285  			if err != test.wantErr {
   286  				t.Errorf("v%d.MarshalJSON -> err %v, want %v", i, err, test.wantErr)
   287  			}
   288  		} else {
   289  			if err.Error() != test.wantErr.Error() {
   290  				t.Errorf("v%d.MarshalJSON -> err %v, want %v", i, err, test.wantErr)
   291  			}
   292  		}
   293  	}
   294  }
   295  
   296  func makeVector(elements ...any) Vector {
   297  	v := Empty
   298  	for _, element := range elements {
   299  		v = v.Conj(element)
   300  	}
   301  	return v
   302  }
   303  
   304  func BenchmarkConjNativeN1(b *testing.B) { benchmarkNativeAppend(b, N1) }
   305  func BenchmarkConjNativeN2(b *testing.B) { benchmarkNativeAppend(b, N2) }
   306  func BenchmarkConjNativeN3(b *testing.B) { benchmarkNativeAppend(b, N3) }
   307  func BenchmarkConjNativeN4(b *testing.B) { benchmarkNativeAppend(b, N4) }
   308  
   309  func benchmarkNativeAppend(b *testing.B, n int) {
   310  	for r := 0; r < b.N; r++ {
   311  		var s []any
   312  		for i := 0; i < n; i++ {
   313  			s = append(s, i)
   314  		}
   315  		_ = s
   316  	}
   317  }
   318  
   319  func BenchmarkConjPersistentN1(b *testing.B) { benchmarkConj(b, N1) }
   320  func BenchmarkConjPersistentN2(b *testing.B) { benchmarkConj(b, N2) }
   321  func BenchmarkConjPersistentN3(b *testing.B) { benchmarkConj(b, N3) }
   322  func BenchmarkConjPersistentN4(b *testing.B) { benchmarkConj(b, N4) }
   323  
   324  func benchmarkConj(b *testing.B, n int) {
   325  	for r := 0; r < b.N; r++ {
   326  		v := Empty
   327  		for i := 0; i < n; i++ {
   328  			v = v.Conj(i)
   329  		}
   330  	}
   331  }
   332  
   333  var (
   334  	sliceN4  = make([]any, N4)
   335  	vectorN4 = Empty
   336  )
   337  
   338  func init() {
   339  	for i := 0; i < N4; i++ {
   340  		vectorN4 = vectorN4.Conj(i)
   341  	}
   342  }
   343  
   344  var x any
   345  
   346  func BenchmarkIndexSeqNativeN4(b *testing.B) { benchmarkIndexSeqNative(b, N4) }
   347  
   348  func benchmarkIndexSeqNative(b *testing.B, n int) {
   349  	for r := 0; r < b.N; r++ {
   350  		for i := 0; i < n; i++ {
   351  			x = sliceN4[i]
   352  		}
   353  	}
   354  }
   355  
   356  func BenchmarkIndexSeqPersistentN4(b *testing.B) { benchmarkIndexSeqPersistent(b, N4) }
   357  
   358  func benchmarkIndexSeqPersistent(b *testing.B, n int) {
   359  	for r := 0; r < b.N; r++ {
   360  		for i := 0; i < n; i++ {
   361  			x, _ = vectorN4.Index(i)
   362  		}
   363  	}
   364  }
   365  
   366  var randIndices []int
   367  
   368  func init() {
   369  	randIndices = make([]int, N4)
   370  	for i := 0; i < N4; i++ {
   371  		randIndices[i] = rand.Intn(N4)
   372  	}
   373  }
   374  
   375  func BenchmarkIndexRandNative(b *testing.B) {
   376  	for r := 0; r < b.N; r++ {
   377  		for _, i := range randIndices {
   378  			x = sliceN4[i]
   379  		}
   380  	}
   381  }
   382  
   383  func BenchmarkIndexRandPersistent(b *testing.B) {
   384  	for r := 0; r < b.N; r++ {
   385  		for _, i := range randIndices {
   386  			x, _ = vectorN4.Index(i)
   387  		}
   388  	}
   389  }
   390  
   391  func nativeEqual(s1, s2 []int) bool {
   392  	if len(s1) != len(s2) {
   393  		return false
   394  	}
   395  	for i, v1 := range s1 {
   396  		if v1 != s2[i] {
   397  			return false
   398  		}
   399  	}
   400  	return true
   401  }
   402  
   403  func BenchmarkEqualNative(b *testing.B) {
   404  	b.StopTimer()
   405  	var s1, s2 []int
   406  	for i := 0; i < N4; i++ {
   407  		s1 = append(s1, i)
   408  		s2 = append(s2, i)
   409  	}
   410  	b.StartTimer()
   411  
   412  	for r := 0; r < b.N; r++ {
   413  		eq := nativeEqual(s1, s2)
   414  		if !eq {
   415  			panic("not equal")
   416  		}
   417  	}
   418  }
   419  
   420  func BenchmarkEqualPersistent(b *testing.B) {
   421  	b.StopTimer()
   422  	v1, v2 := Empty, Empty
   423  	for i := 0; i < N4; i++ {
   424  		v1 = v1.Conj(i)
   425  		v2 = v2.Conj(i)
   426  	}
   427  	b.StartTimer()
   428  
   429  	for r := 0; r < b.N; r++ {
   430  		eq := eqVector(v1, v2)
   431  		if !eq {
   432  			panic("not equal")
   433  		}
   434  	}
   435  }