github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/persistent/vector/vector_test.go (about)

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