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 }