github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/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 := testCons(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.Cons(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 // testCons creates a vector containing 0...n-1 with Cons, and ensures that the 69 // length of the old and new vectors are expected after each Cons. It returns 70 // the created vector. 71 func testCons(t *testing.T, n int) Vector { 72 v := Empty 73 for i := 0; i < n; i++ { 74 oldv := v 75 v = v.Cons(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.Cons(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.Cons("233"), 1, 2, 3, "233") { 187 t.Errorf("v[0:4].Cons 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.Cons(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.Cons(elem) 251 v2 = v2.Cons(elem) 252 if !eqVector(v1, v2) { 253 t.Errorf("Not equal after Cons'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.Cons(element) 305 } 306 return v 307 } 308 309 func BenchmarkConsNativeN1(b *testing.B) { benchmarkNativeAppend(b, N1) } 310 func BenchmarkConsNativeN2(b *testing.B) { benchmarkNativeAppend(b, N2) } 311 func BenchmarkConsNativeN3(b *testing.B) { benchmarkNativeAppend(b, N3) } 312 func BenchmarkConsNativeN4(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 } 321 } 322 323 func BenchmarkConsPersistentN1(b *testing.B) { benchmarkCons(b, N1) } 324 func BenchmarkConsPersistentN2(b *testing.B) { benchmarkCons(b, N2) } 325 func BenchmarkConsPersistentN3(b *testing.B) { benchmarkCons(b, N3) } 326 func BenchmarkConsPersistentN4(b *testing.B) { benchmarkCons(b, N4) } 327 328 func benchmarkCons(b *testing.B, n int) { 329 for r := 0; r < b.N; r++ { 330 v := Empty 331 for i := 0; i < n; i++ { 332 v = v.Cons(i) 333 } 334 } 335 } 336 337 var ( 338 sliceN4 = make([]interface{}, N4) 339 vectorN4 = Empty 340 ) 341 342 func init() { 343 for i := 0; i < N4; i++ { 344 vectorN4 = vectorN4.Cons(i) 345 } 346 } 347 348 var x interface{} 349 350 func BenchmarkIndexSeqNativeN4(b *testing.B) { benchmarkIndexSeqNative(b, N4) } 351 352 func benchmarkIndexSeqNative(b *testing.B, n int) { 353 for r := 0; r < b.N; r++ { 354 for i := 0; i < n; i++ { 355 x = sliceN4[i] 356 } 357 } 358 } 359 360 func BenchmarkIndexSeqPersistentN4(b *testing.B) { benchmarkIndexSeqPersistent(b, N4) } 361 362 func benchmarkIndexSeqPersistent(b *testing.B, n int) { 363 for r := 0; r < b.N; r++ { 364 for i := 0; i < n; i++ { 365 x, _ = vectorN4.Index(i) 366 } 367 } 368 } 369 370 var randIndicies []int 371 372 func init() { 373 randIndicies = make([]int, N4) 374 for i := 0; i < N4; i++ { 375 randIndicies[i] = rand.Intn(N4) 376 } 377 } 378 379 func BenchmarkIndexRandNative(b *testing.B) { 380 for r := 0; r < b.N; r++ { 381 for _, i := range randIndicies { 382 x = sliceN4[i] 383 } 384 } 385 } 386 387 func BenchmarkIndexRandPersistent(b *testing.B) { 388 for r := 0; r < b.N; r++ { 389 for _, i := range randIndicies { 390 x, _ = vectorN4.Index(i) 391 } 392 } 393 } 394 395 func nativeEqual(s1, s2 []int) bool { 396 if len(s1) != len(s2) { 397 return false 398 } 399 for i, v1 := range s1 { 400 if v1 != s2[i] { 401 return false 402 } 403 } 404 return true 405 } 406 407 func BenchmarkEqualNative(b *testing.B) { 408 b.StopTimer() 409 var s1, s2 []int 410 for i := 0; i < N4; i++ { 411 s1 = append(s1, i) 412 s2 = append(s2, i) 413 } 414 b.StartTimer() 415 416 for r := 0; r < b.N; r++ { 417 eq := nativeEqual(s1, s2) 418 if !eq { 419 panic("not equal") 420 } 421 } 422 } 423 424 func BenchmarkEqualPersistent(b *testing.B) { 425 b.StopTimer() 426 v1, v2 := Empty, Empty 427 for i := 0; i < N4; i++ { 428 v1 = v1.Cons(i) 429 v2 = v2.Cons(i) 430 } 431 b.StartTimer() 432 433 for r := 0; r < b.N; r++ { 434 eq := eqVector(v1, v2) 435 if !eq { 436 panic("not equal") 437 } 438 } 439 }