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 }