github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/runtime/dict_test.go (about) 1 // Copyright 2016 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package grumpy 16 17 import ( 18 "reflect" 19 "regexp" 20 "runtime" 21 "sync" 22 "testing" 23 "time" 24 ) 25 26 // hashFoo is the hash of the string 'foo'. We use this to validate some corner 27 // cases around hash collision below. 28 // NOTE: Inline func helps support 32bit systems. 29 var hashFoo = NewInt(func(i int64) int { return int(i) }(-4177197833195190597)).ToObject() 30 31 func TestNewStringDict(t *testing.T) { 32 cases := []struct { 33 m map[string]*Object 34 want *Dict 35 }{ 36 {nil, NewDict()}, 37 {map[string]*Object{"baz": NewFloat(3.14).ToObject()}, newTestDict("baz", 3.14)}, 38 {map[string]*Object{"foo": NewInt(2).ToObject(), "bar": NewInt(4).ToObject()}, newTestDict("bar", 4, "foo", 2)}, 39 } 40 for _, cas := range cases { 41 fun := newBuiltinFunction("newStringDict", func(*Frame, Args, KWArgs) (*Object, *BaseException) { 42 return newStringDict(cas.m).ToObject(), nil 43 }).ToObject() 44 if err := runInvokeTestCase(fun, &invokeTestCase{want: cas.want.ToObject()}); err != "" { 45 t.Error(err) 46 } 47 } 48 } 49 50 func TestDictClear(t *testing.T) { 51 clear := mustNotRaise(GetAttr(NewRootFrame(), DictType.ToObject(), NewStr("clear"), nil)) 52 fun := newBuiltinFunction("TestDictClear", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { 53 if _, raised := clear.Call(f, args, nil); raised != nil { 54 return nil, raised 55 } 56 return args[0], nil 57 }).ToObject() 58 cases := []invokeTestCase{ 59 {args: wrapArgs(NewDict()), want: NewDict().ToObject()}, 60 {args: wrapArgs(newStringDict(map[string]*Object{"foo": NewInt(1).ToObject()})), want: NewDict().ToObject()}, 61 {args: wrapArgs(newTestDict(2, None, "baz", 3.14)), want: NewDict().ToObject()}, 62 {args: wrapArgs(NewDict(), NewList()), wantExc: mustCreateException(TypeErrorType, "'clear' of 'dict' requires 1 arguments")}, 63 {args: wrapArgs(NewDict(), None), wantExc: mustCreateException(TypeErrorType, "'clear' of 'dict' requires 1 arguments")}, 64 {args: wrapArgs(None), wantExc: mustCreateException(TypeErrorType, "unbound method clear() must be called with dict instance as first argument (got NoneType instance instead)")}, 65 } 66 for _, cas := range cases { 67 if err := runInvokeTestCase(fun, &cas); err != "" { 68 t.Error(err) 69 } 70 } 71 } 72 73 func TestDictContains(t *testing.T) { 74 cases := []invokeTestCase{ 75 {args: wrapArgs(NewDict(), "foo"), want: False.ToObject()}, 76 {args: wrapArgs(newTestDict("foo", 1, "bar", 2), "foo"), want: True.ToObject()}, 77 {args: wrapArgs(newTestDict(3, "foo", "bar", 42), 42), want: False.ToObject()}, 78 } 79 for _, cas := range cases { 80 if err := runInvokeMethodTestCase(DictType, "__contains__", &cas); err != "" { 81 t.Error(err) 82 } 83 } 84 } 85 86 func TestDictDelItem(t *testing.T) { 87 fun := newBuiltinFunction("TestDictDelItem", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { 88 if raised := checkMethodArgs(f, "TestDictDelItem", args, DictType, ObjectType); raised != nil { 89 return nil, raised 90 } 91 if raised := DelItem(f, args[0], args[1]); raised != nil { 92 return nil, raised 93 } 94 return args[0], nil 95 }).ToObject() 96 testDict := newTestDict("a", 1, "b", 2, "c", 3) 97 cases := []invokeTestCase{ 98 {args: wrapArgs(newTestDict("foo", 1), "foo"), want: NewDict().ToObject()}, 99 {args: wrapArgs(NewDict(), 10), wantExc: mustCreateException(KeyErrorType, "10")}, 100 {args: wrapArgs(testDict, "a"), want: newTestDict("b", 2, "c", 3).ToObject()}, 101 {args: wrapArgs(testDict, "c"), want: newTestDict("b", 2).ToObject()}, 102 {args: wrapArgs(testDict, "a"), wantExc: mustCreateException(KeyErrorType, "a")}, 103 {args: wrapArgs(NewDict(), NewList()), wantExc: mustCreateException(TypeErrorType, "unhashable type: 'list'")}, 104 } 105 for _, cas := range cases { 106 if err := runInvokeTestCase(fun, &cas); err != "" { 107 t.Error(err) 108 } 109 } 110 } 111 112 func TestDictDelItemString(t *testing.T) { 113 fun := newBuiltinFunction("TestDictDelItemString", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { 114 if raised := checkMethodArgs(f, "TestDictDelItemString", args, DictType, StrType); raised != nil { 115 return nil, raised 116 } 117 deleted, raised := toDictUnsafe(args[0]).DelItemString(f, toStrUnsafe(args[1]).Value()) 118 if raised != nil { 119 return nil, raised 120 } 121 return newTestTuple(deleted, args[0]).ToObject(), nil 122 }).ToObject() 123 cases := []invokeTestCase{ 124 {args: wrapArgs(newTestDict("foo", 1), "foo"), want: newTestTuple(true, NewDict()).ToObject()}, 125 {args: wrapArgs(NewDict(), "qux"), want: newTestTuple(false, NewDict()).ToObject()}, 126 } 127 for _, cas := range cases { 128 if err := runInvokeTestCase(fun, &cas); err != "" { 129 t.Error(err) 130 } 131 } 132 } 133 134 func TestDictEqNE(t *testing.T) { 135 fun := wrapFuncForTest(func(f *Frame, v, w *Object) (*Object, *BaseException) { 136 eq, raised := Eq(f, v, w) 137 if raised != nil { 138 return nil, raised 139 } 140 ne, raised := NE(f, v, w) 141 if raised != nil { 142 return nil, raised 143 } 144 valid := GetBool(eq == True.ToObject() && ne == False.ToObject() || eq == False.ToObject() && ne == True.ToObject()).ToObject() 145 if raised := Assert(f, valid, NewStr("invalid values for __eq__ or __ne__").ToObject()); raised != nil { 146 return nil, raised 147 } 148 return eq, nil 149 }) 150 f := NewRootFrame() 151 large1, large2 := NewDict(), NewDict() 152 largeSize := 100 153 for i := 0; i < largeSize; i++ { 154 s, raised := ToStr(f, NewInt(i).ToObject()) 155 if raised != nil { 156 t.Fatal(raised) 157 } 158 large1.SetItem(f, NewInt(i).ToObject(), s.ToObject()) 159 s, raised = ToStr(f, NewInt(largeSize-i-1).ToObject()) 160 if raised != nil { 161 t.Fatal(raised) 162 } 163 large2.SetItem(f, NewInt(largeSize-i-1).ToObject(), s.ToObject()) 164 } 165 o := newObject(ObjectType) 166 cases := []invokeTestCase{ 167 {args: wrapArgs(NewDict(), NewDict()), want: True.ToObject()}, 168 {args: wrapArgs(NewDict(), newTestDict("foo", true)), want: False.ToObject()}, 169 {args: wrapArgs(newTestDict("foo", "foo"), newTestDict("foo", "foo")), want: True.ToObject()}, 170 {args: wrapArgs(newTestDict("foo", true), newTestDict("bar", true)), want: False.ToObject()}, 171 {args: wrapArgs(newTestDict("foo", true), newTestDict("foo", newObject(ObjectType))), want: False.ToObject()}, 172 {args: wrapArgs(newTestDict("foo", true, "bar", false), newTestDict("bar", true)), want: False.ToObject()}, 173 {args: wrapArgs(newTestDict("foo", o, "bar", o), newTestDict("foo", o, "bar", o)), want: True.ToObject()}, 174 {args: wrapArgs(newTestDict(2, None, "foo", o), newTestDict("foo", o, 2, None)), want: True.ToObject()}, 175 {args: wrapArgs(large1, large2), want: True.ToObject()}, 176 {args: wrapArgs(NewDict(), 123), want: False.ToObject()}, 177 } 178 for _, cas := range cases { 179 if err := runInvokeTestCase(fun, &cas); err != "" { 180 t.Error(err) 181 } 182 } 183 } 184 185 func TestDictGet(t *testing.T) { 186 cases := []invokeTestCase{ 187 {args: wrapArgs(NewDict(), "foo"), want: None}, 188 {args: wrapArgs(newTestDict("foo", 1, "bar", 2), "foo"), want: NewInt(1).ToObject()}, 189 {args: wrapArgs(newTestDict(3, "foo", "bar", 42), 42, "nope"), want: NewStr("nope").ToObject()}, 190 } 191 for _, cas := range cases { 192 if err := runInvokeMethodTestCase(DictType, "get", &cas); err != "" { 193 t.Error(err) 194 } 195 } 196 } 197 198 func TestDictGetItem(t *testing.T) { 199 getItem := newBuiltinFunction("TestDictGetItem", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { 200 if raised := checkFunctionArgs(f, "TestDictGetItem", args, DictType, ObjectType); raised != nil { 201 return nil, raised 202 } 203 result, raised := toDictUnsafe(args[0]).GetItem(f, args[1]) 204 if raised == nil && result == nil { 205 result = None 206 } 207 return result, raised 208 }).ToObject() 209 f := NewRootFrame() 210 h, raised := Hash(f, NewStr("foo").ToObject()) 211 if raised != nil { 212 t.Fatal(raised) 213 } 214 if b, raised := IsTrue(f, mustNotRaise(NE(f, h.ToObject(), hashFoo))); raised != nil { 215 t.Fatal(raised) 216 } else if b { 217 t.Fatalf("hash('foo') = %v, want %v", h, hashFoo) 218 } 219 deletedItemDict := newTestDict(hashFoo, true, "foo", true) 220 deletedItemDict.DelItem(f, hashFoo) 221 cases := []invokeTestCase{ 222 {args: wrapArgs(NewDict(), "foo"), want: None}, 223 {args: wrapArgs(newStringDict(map[string]*Object{"foo": True.ToObject()}), "foo"), want: True.ToObject()}, 224 {args: wrapArgs(newTestDict(2, "bar", "baz", 3.14), 2), want: NewStr("bar").ToObject()}, 225 {args: wrapArgs(newTestDict(2, "bar", "baz", 3.14), 3), want: None}, 226 {args: wrapArgs(deletedItemDict, hashFoo), want: None}, 227 {args: wrapArgs(NewDict(), NewList()), wantExc: mustCreateException(TypeErrorType, "unhashable type: 'list'")}, 228 } 229 for _, cas := range cases { 230 if err := runInvokeTestCase(getItem, &cas); err != "" { 231 t.Error(err) 232 } 233 } 234 } 235 236 // BenchmarkDictGetItem is to keep an eye on the speed of contended dict access 237 // in a fast read loop. 238 func BenchmarkDictGetItem(b *testing.B) { 239 d := newTestDict( 240 "foo", 1, 241 "bar", 2, 242 None, 3, 243 4, 5) 244 k := NewInt(4).ToObject() 245 246 b.ResetTimer() 247 b.RunParallel(func(pb *testing.PB) { 248 f := NewRootFrame() 249 var ret *Object 250 var raised *BaseException 251 for pb.Next() { 252 ret, raised = d.GetItem(f, k) 253 } 254 runtime.KeepAlive(ret) 255 runtime.KeepAlive(raised) 256 }) 257 } 258 259 func BenchmarkDictIterItems(b *testing.B) { 260 bench := func(d *Dict) func(*testing.B) { 261 return func(b *testing.B) { 262 f := NewRootFrame() 263 args := f.MakeArgs(1) 264 args[0] = d.ToObject() 265 b.ResetTimer() 266 267 var ret *Object 268 var raised *BaseException 269 for i := 0; i < b.N; i++ { 270 iter, _ := dictIterItems(f, args, nil) 271 for { 272 ret, raised = Next(f, iter) 273 if raised != nil { 274 if !raised.isInstance(StopIterationType) { 275 b.Fatalf("iteration failed with: %v", raised) 276 } 277 f.RestoreExc(nil, nil) 278 break 279 } 280 } 281 } 282 runtime.KeepAlive(ret) 283 runtime.KeepAlive(raised) 284 } 285 } 286 287 b.Run("0-elements", bench(newTestDict())) 288 b.Run("1-elements", bench(newTestDict(1, 2))) 289 b.Run("2-elements", bench(newTestDict(1, 2, 3, 4))) 290 b.Run("3-elements", bench(newTestDict(1, 2, 3, 4, 5, 6))) 291 b.Run("4-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8))) 292 b.Run("5-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))) 293 b.Run("6-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))) 294 b.Run("7-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14))) 295 b.Run("8-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16))) 296 } 297 298 func BenchmarkDictIterKeys(b *testing.B) { 299 bench := func(d *Dict) func(*testing.B) { 300 return func(b *testing.B) { 301 f := NewRootFrame() 302 args := f.MakeArgs(1) 303 args[0] = d.ToObject() 304 b.ResetTimer() 305 306 var ret *Object 307 var raised *BaseException 308 for i := 0; i < b.N; i++ { 309 iter, _ := dictIterKeys(f, args, nil) 310 for { 311 ret, raised = Next(f, iter) 312 if raised != nil { 313 if !raised.isInstance(StopIterationType) { 314 b.Fatalf("iteration failed with: %v", raised) 315 } 316 f.RestoreExc(nil, nil) 317 break 318 } 319 } 320 } 321 runtime.KeepAlive(ret) 322 runtime.KeepAlive(raised) 323 } 324 } 325 326 b.Run("0-elements", bench(newTestDict())) 327 b.Run("1-elements", bench(newTestDict(1, 2))) 328 b.Run("2-elements", bench(newTestDict(1, 2, 3, 4))) 329 b.Run("3-elements", bench(newTestDict(1, 2, 3, 4, 5, 6))) 330 b.Run("4-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8))) 331 b.Run("5-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))) 332 b.Run("6-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))) 333 b.Run("7-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14))) 334 b.Run("8-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16))) 335 } 336 337 func BenchmarkDictIterValues(b *testing.B) { 338 bench := func(d *Dict) func(*testing.B) { 339 return func(b *testing.B) { 340 f := NewRootFrame() 341 args := f.MakeArgs(1) 342 args[0] = d.ToObject() 343 b.ResetTimer() 344 345 var ret *Object 346 var raised *BaseException 347 for i := 0; i < b.N; i++ { 348 iter, _ := dictIterValues(f, args, nil) 349 for { 350 ret, raised = Next(f, iter) 351 if raised != nil { 352 if !raised.isInstance(StopIterationType) { 353 b.Fatalf("iteration failed with: %v", raised) 354 } 355 f.RestoreExc(nil, nil) 356 break 357 } 358 } 359 } 360 runtime.KeepAlive(ret) 361 runtime.KeepAlive(raised) 362 } 363 } 364 365 b.Run("0-elements", bench(newTestDict())) 366 b.Run("1-elements", bench(newTestDict(1, 2))) 367 b.Run("2-elements", bench(newTestDict(1, 2, 3, 4))) 368 b.Run("3-elements", bench(newTestDict(1, 2, 3, 4, 5, 6))) 369 b.Run("4-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8))) 370 b.Run("5-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))) 371 b.Run("6-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))) 372 b.Run("7-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14))) 373 b.Run("8-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16))) 374 } 375 376 func TestDictGetItemString(t *testing.T) { 377 getItemString := newBuiltinFunction("TestDictGetItemString", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { 378 if raised := checkFunctionArgs(f, "TestDictGetItem", args, DictType, StrType); raised != nil { 379 return nil, raised 380 } 381 result, raised := toDictUnsafe(args[0]).GetItemString(f, toStrUnsafe(args[1]).Value()) 382 if raised == nil && result == nil { 383 result = None 384 } 385 return result, raised 386 }).ToObject() 387 cases := []invokeTestCase{ 388 {args: wrapArgs(NewDict(), "foo"), want: None}, 389 {args: wrapArgs(newTestDict("foo", true), "foo"), want: True.ToObject()}, 390 {args: wrapArgs(newTestDict(2, "bar", "baz", 3.14), "baz"), want: NewFloat(3.14).ToObject()}, 391 {args: wrapArgs(newTestDict(2, "bar", "baz", 3.14), "qux"), want: None}, 392 } 393 for _, cas := range cases { 394 if err := runInvokeTestCase(getItemString, &cas); err != "" { 395 t.Error(err) 396 } 397 } 398 } 399 400 func TestDictHasKey(t *testing.T) { 401 cases := []invokeTestCase{ 402 {args: wrapArgs(NewDict(), "foo"), want: False.ToObject()}, 403 {args: wrapArgs(newTestDict("foo", 1, "bar", 2), "foo"), want: True.ToObject()}, 404 {args: wrapArgs(newTestDict(3, "foo", "bar", 42), 42), want: False.ToObject()}, 405 } 406 for _, cas := range cases { 407 if err := runInvokeMethodTestCase(DictType, "has_key", &cas); err != "" { 408 t.Error(err) 409 } 410 } 411 } 412 413 func TestDictItemIteratorIter(t *testing.T) { 414 iter := &newDictItemIterator(NewDict()).Object 415 cas := &invokeTestCase{args: wrapArgs(iter), want: iter} 416 if err := runInvokeMethodTestCase(dictItemIteratorType, "__iter__", cas); err != "" { 417 t.Error(err) 418 } 419 } 420 421 func TestDictItemIterModified(t *testing.T) { 422 f := NewRootFrame() 423 iterItems := mustNotRaise(GetAttr(f, DictType.ToObject(), NewStr("iteritems"), nil)) 424 d := NewDict() 425 iter := mustNotRaise(iterItems.Call(f, wrapArgs(d), nil)) 426 if raised := d.SetItemString(f, "foo", None); raised != nil { 427 t.Fatal(raised) 428 } 429 cas := invokeTestCase{ 430 args: wrapArgs(iter), 431 wantExc: mustCreateException(RuntimeErrorType, "dictionary changed during iteration"), 432 } 433 if err := runInvokeMethodTestCase(dictItemIteratorType, "next", &cas); err != "" { 434 t.Error(err) 435 } 436 } 437 438 func TestDictIter(t *testing.T) { 439 iter := newBuiltinFunction("TestDictIter", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { 440 if raised := checkFunctionArgs(f, "TestDictIter", args, DictType); raised != nil { 441 return nil, raised 442 } 443 iter, raised := Iter(f, args[0]) 444 if raised != nil { 445 return nil, raised 446 } 447 return TupleType.Call(f, []*Object{iter}, nil) 448 }).ToObject() 449 f := NewRootFrame() 450 deletedItemDict := newTestDict(hashFoo, None, "foo", None) 451 deletedItemDict.DelItem(f, hashFoo) 452 cases := []invokeTestCase{ 453 {args: wrapArgs(NewDict()), want: NewTuple().ToObject()}, 454 {args: wrapArgs(newStringDict(map[string]*Object{"foo": NewInt(1).ToObject(), "bar": NewInt(2).ToObject()})), want: newTestTuple("foo", "bar").ToObject()}, 455 {args: wrapArgs(newTestDict(123, True, "foo", False)), want: newTestTuple(123, "foo").ToObject()}, 456 {args: wrapArgs(deletedItemDict), want: newTestTuple("foo").ToObject()}, 457 } 458 for _, cas := range cases { 459 if err := runInvokeTestCase(iter, &cas); err != "" { 460 t.Error(err) 461 } 462 } 463 } 464 465 func TestDictIterKeys(t *testing.T) { 466 iterkeys := mustNotRaise(GetAttr(NewRootFrame(), DictType.ToObject(), NewStr("iterkeys"), nil)) 467 fun := wrapFuncForTest(func(f *Frame, args ...*Object) (*Object, *BaseException) { 468 iter, raised := iterkeys.Call(f, args, nil) 469 if raised != nil { 470 return nil, raised 471 } 472 return TupleType.Call(f, Args{iter}, nil) 473 }) 474 cases := []invokeTestCase{ 475 {args: wrapArgs(NewDict()), want: NewTuple().ToObject()}, 476 {args: wrapArgs(newTestDict("foo", 1, "bar", 2)), want: newTestTuple("foo", "bar").ToObject()}, 477 {args: wrapArgs(NewDict(), "bad"), wantExc: mustCreateException(TypeErrorType, "'iterkeys' of 'dict' requires 1 arguments")}, 478 } 479 for _, cas := range cases { 480 if err := runInvokeTestCase(fun, &cas); err != "" { 481 t.Error(err) 482 } 483 } 484 } 485 486 func TestDictIterValues(t *testing.T) { 487 itervalues := mustNotRaise(GetAttr(NewRootFrame(), DictType.ToObject(), NewStr("itervalues"), nil)) 488 fun := wrapFuncForTest(func(f *Frame, args ...*Object) (*Object, *BaseException) { 489 iter, raised := itervalues.Call(f, args, nil) 490 if raised != nil { 491 return nil, raised 492 } 493 return TupleType.Call(f, Args{iter}, nil) 494 }) 495 cases := []invokeTestCase{ 496 {args: wrapArgs(NewDict()), want: NewTuple().ToObject()}, 497 {args: wrapArgs(newTestDict("foo", 1, "bar", 2)), want: newTestTuple(1, 2).ToObject()}, 498 {args: wrapArgs(NewDict(), "bad"), wantExc: mustCreateException(TypeErrorType, "'itervalues' of 'dict' requires 1 arguments")}, 499 } 500 for _, cas := range cases { 501 if err := runInvokeTestCase(fun, &cas); err != "" { 502 t.Error(err) 503 } 504 } 505 } 506 507 // Tests dict.items and dict.iteritems. 508 func TestDictItems(t *testing.T) { 509 f := NewRootFrame() 510 iterItems := mustNotRaise(GetAttr(f, DictType.ToObject(), NewStr("iteritems"), nil)) 511 items := newBuiltinFunction("TestDictIterItems", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { 512 if raised := checkFunctionArgs(f, "TestDictIterItems", args, DictType); raised != nil { 513 return nil, raised 514 } 515 iter, raised := iterItems.Call(f, []*Object{args[0]}, nil) 516 if raised != nil { 517 return nil, raised 518 } 519 return ListType.Call(f, []*Object{iter}, nil) 520 }).ToObject() 521 deletedItemDict := newTestDict(hashFoo, None, "foo", None) 522 deletedItemDict.DelItem(f, hashFoo) 523 cases := []invokeTestCase{ 524 {args: wrapArgs(NewDict()), want: NewList().ToObject()}, 525 {args: wrapArgs(newStringDict(map[string]*Object{"foo": NewInt(1).ToObject(), "bar": NewInt(2).ToObject()})), want: newTestList(newTestTuple("foo", 1), newTestTuple("bar", 2)).ToObject()}, 526 {args: wrapArgs(newTestDict(123, True, "foo", False)), want: newTestList(newTestTuple(123, true), newTestTuple("foo", false)).ToObject()}, 527 {args: wrapArgs(deletedItemDict), want: newTestList(newTestTuple("foo", None)).ToObject()}, 528 } 529 for _, cas := range cases { 530 if err := runInvokeTestCase(items, &cas); err != "" { 531 t.Error(err) 532 } 533 if err := runInvokeMethodTestCase(DictType, "items", &cas); err != "" { 534 t.Error(err) 535 } 536 } 537 } 538 539 func TestDictKeyIteratorIter(t *testing.T) { 540 iter := &newDictKeyIterator(NewDict()).Object 541 cas := &invokeTestCase{args: wrapArgs(iter), want: iter} 542 if err := runInvokeMethodTestCase(dictKeyIteratorType, "__iter__", cas); err != "" { 543 t.Error(err) 544 } 545 } 546 547 func TestDictKeyIterModified(t *testing.T) { 548 f := NewRootFrame() 549 d := NewDict() 550 iter := mustNotRaise(Iter(f, d.ToObject())) 551 if raised := d.SetItemString(f, "foo", None); raised != nil { 552 t.Fatal(raised) 553 } 554 cas := invokeTestCase{ 555 args: wrapArgs(iter), 556 wantExc: mustCreateException(RuntimeErrorType, "dictionary changed during iteration"), 557 } 558 if err := runInvokeMethodTestCase(dictKeyIteratorType, "next", &cas); err != "" { 559 t.Error(err) 560 } 561 } 562 563 func TestDictKeys(t *testing.T) { 564 cases := []invokeTestCase{ 565 {args: wrapArgs(NewDict()), want: NewList().ToObject()}, 566 {args: wrapArgs(newTestDict("foo", None, 42, None)), want: newTestList(42, "foo").ToObject()}, 567 } 568 for _, cas := range cases { 569 if err := runInvokeMethodTestCase(DictType, "keys", &cas); err != "" { 570 t.Error(err) 571 } 572 } 573 } 574 575 func TestDictPop(t *testing.T) { 576 cases := []invokeTestCase{ 577 {args: wrapArgs(newTestDict("foo", 42), "foo"), want: NewInt(42).ToObject()}, 578 {args: wrapArgs(NewDict(), "foo", 42), want: NewInt(42).ToObject()}, 579 {args: wrapArgs(NewDict(), "foo"), wantExc: mustCreateException(KeyErrorType, "foo")}, 580 } 581 for _, cas := range cases { 582 if err := runInvokeMethodTestCase(DictType, "pop", &cas); err != "" { 583 t.Error(err) 584 } 585 } 586 } 587 588 func TestDictPopItem(t *testing.T) { 589 popItem := mustNotRaise(GetAttr(NewRootFrame(), DictType.ToObject(), NewStr("popitem"), nil)) 590 fun := wrapFuncForTest(func(f *Frame, d *Dict) (*Object, *BaseException) { 591 result := NewDict() 592 item, raised := popItem.Call(f, wrapArgs(d), nil) 593 for ; raised == nil; item, raised = popItem.Call(f, wrapArgs(d), nil) { 594 t := toTupleUnsafe(item) 595 result.SetItem(f, t.GetItem(0), t.GetItem(1)) 596 } 597 if raised != nil { 598 if !raised.isInstance(KeyErrorType) { 599 return nil, raised 600 } 601 f.RestoreExc(nil, nil) 602 } 603 if raised = Assert(f, GetBool(d.Len() == 0).ToObject(), nil); raised != nil { 604 return nil, raised 605 } 606 return result.ToObject(), nil 607 }) 608 cases := []invokeTestCase{ 609 {args: wrapArgs(newTestDict("foo", 42)), want: newTestDict("foo", 42).ToObject()}, 610 {args: wrapArgs(newTestDict("foo", 42, 123, "bar")), want: newTestDict("foo", 42, 123, "bar").ToObject()}, 611 } 612 for _, cas := range cases { 613 if err := runInvokeTestCase(fun, &cas); err != "" { 614 t.Error(err) 615 } 616 } 617 } 618 619 func TestDictNewInit(t *testing.T) { 620 cases := []invokeTestCase{ 621 {args: wrapArgs(), want: NewDict().ToObject()}, 622 {args: wrapArgs(newTestDict("foo", 42)), want: newTestDict("foo", 42).ToObject()}, 623 {args: wrapArgs(), kwargs: wrapKWArgs("foo", 42), want: newTestDict("foo", 42).ToObject()}, 624 {args: wrapArgs(newTestDict("foo", 42)), kwargs: wrapKWArgs("foo", "bar"), want: newTestDict("foo", "bar").ToObject()}, 625 {args: wrapArgs(newTestList(newTestTuple("baz", 42))), kwargs: wrapKWArgs("foo", "bar"), want: newTestDict("baz", 42, "foo", "bar").ToObject()}, 626 {args: wrapArgs(True), wantExc: mustCreateException(TypeErrorType, "'bool' object is not iterable")}, 627 {args: wrapArgs(NewList(), "foo"), wantExc: mustCreateException(TypeErrorType, "'__init__' requires 1 arguments")}, 628 } 629 for _, cas := range cases { 630 if err := runInvokeTestCase(DictType.ToObject(), &cas); err != "" { 631 t.Error(err) 632 } 633 } 634 } 635 636 func TestDictNewRaises(t *testing.T) { 637 cases := []invokeTestCase{ 638 {args: wrapArgs(), wantExc: mustCreateException(TypeErrorType, "'__new__' requires 1 arguments")}, 639 {args: wrapArgs(123), wantExc: mustCreateException(TypeErrorType, `'__new__' requires a 'type' object but received a "int"`)}, 640 {args: wrapArgs(NoneType), wantExc: mustCreateException(TypeErrorType, "dict.__new__(NoneType): NoneType is not a subtype of dict")}, 641 } 642 for _, cas := range cases { 643 if err := runInvokeMethodTestCase(DictType, "__new__", &cas); err != "" { 644 t.Error(err) 645 } 646 } 647 } 648 649 func TestDictSetDefault(t *testing.T) { 650 setDefaultMethod := mustNotRaise(GetAttr(NewRootFrame(), DictType.ToObject(), NewStr("setdefault"), nil)) 651 setDefault := newBuiltinFunction("TestDictSetDefault", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 652 i, raised := setDefaultMethod.Call(f, args, kwargs) 653 if raised != nil { 654 return nil, raised 655 } 656 return NewTuple(i, args[0]).ToObject(), nil 657 }).ToObject() 658 cases := []invokeTestCase{ 659 {args: wrapArgs(NewDict(), "foo"), want: newTestTuple(None, newTestDict("foo", None)).ToObject()}, 660 {args: wrapArgs(NewDict(), "foo", 42), want: newTestTuple(42, newTestDict("foo", 42)).ToObject()}, 661 {args: wrapArgs(newTestDict("foo", 42), "foo"), want: newTestTuple(42, newTestDict("foo", 42)).ToObject()}, 662 {args: wrapArgs(newTestDict("foo", 42), "foo", 43), want: newTestTuple(42, newTestDict("foo", 42)).ToObject()}, 663 {args: wrapArgs(NewDict()), wantExc: mustCreateException(TypeErrorType, "setdefault expected at least 1 arguments, got 0")}, 664 {args: wrapArgs(NewDict(), "foo", "bar", "baz"), wantExc: mustCreateException(TypeErrorType, "setdefault expected at most 2 arguments, got 3")}, 665 } 666 for _, cas := range cases { 667 if err := runInvokeTestCase(setDefault, &cas); err != "" { 668 t.Error(err) 669 } 670 } 671 } 672 673 func TestDictSetItem(t *testing.T) { 674 setItem := newBuiltinFunction("TestDictSetItem", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { 675 if raised := checkFunctionArgs(f, "TestDictSetItem", args, DictType, ObjectType, ObjectType); raised != nil { 676 return nil, raised 677 } 678 d := toDictUnsafe(args[0]) 679 if raised := d.SetItem(f, args[1], args[2]); raised != nil { 680 return nil, raised 681 } 682 return d.ToObject(), nil 683 }).ToObject() 684 f := NewRootFrame() 685 o := newObject(ObjectType) 686 deletedItemDict := newStringDict(map[string]*Object{"foo": None}) 687 if _, raised := deletedItemDict.DelItemString(f, "foo"); raised != nil { 688 t.Fatal(raised) 689 } 690 modifiedDict := newTestDict(0, None) 691 modifiedType := newTestClass("Foo", []*Type{IntType}, newStringDict(map[string]*Object{ 692 "__eq__": newBuiltinFunction("__eq__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 693 for i := 1000; i < 1100; i++ { 694 if raised := modifiedDict.SetItem(f, NewInt(i).ToObject(), None); raised != nil { 695 return nil, raised 696 } 697 } 698 return False.ToObject(), nil 699 }).ToObject(), 700 })) 701 cases := []invokeTestCase{ 702 {args: wrapArgs(NewDict(), "foo", o), want: newStringDict(map[string]*Object{"foo": o}).ToObject()}, 703 {args: wrapArgs(newStringDict(map[string]*Object{"foo": NewInt(1).ToObject()}), "foo", 2), want: newStringDict(map[string]*Object{"foo": NewInt(2).ToObject()}).ToObject()}, 704 {args: wrapArgs(newTestDict(2, None, "baz", 3.14), 2, o), want: newTestDict(2, o, "baz", 3.14).ToObject()}, 705 {args: wrapArgs(deletedItemDict, "foo", o), want: newStringDict(map[string]*Object{"foo": o}).ToObject()}, 706 {args: wrapArgs(NewDict(), NewList(), None), wantExc: mustCreateException(TypeErrorType, "unhashable type: 'list'")}, 707 {args: wrapArgs(modifiedDict, newObject(modifiedType), None), wantExc: mustCreateException(RuntimeErrorType, "dictionary changed during write")}, 708 } 709 for _, cas := range cases { 710 if err := runInvokeTestCase(setItem, &cas); err != "" { 711 t.Error(err) 712 } 713 } 714 } 715 716 func TestDictSetItemString(t *testing.T) { 717 setItemString := newBuiltinFunction("TestDictSetItemString", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { 718 if raised := checkFunctionArgs(f, "TestDictSetItemString", args, DictType, StrType, ObjectType); raised != nil { 719 return nil, raised 720 } 721 d := toDictUnsafe(args[0]) 722 if raised := d.SetItemString(f, toStrUnsafe(args[1]).Value(), args[2]); raised != nil { 723 return nil, raised 724 } 725 return d.ToObject(), nil 726 }).ToObject() 727 o := newObject(ObjectType) 728 cases := []invokeTestCase{ 729 {args: wrapArgs(NewDict(), "foo", o), want: newStringDict(map[string]*Object{"foo": o}).ToObject()}, 730 {args: wrapArgs(newStringDict(map[string]*Object{"foo": NewInt(1).ToObject()}), "foo", 2), want: newStringDict(map[string]*Object{"foo": NewInt(2).ToObject()}).ToObject()}, 731 {args: wrapArgs(newTestDict(2, None, "baz", 3.14), "baz", o), want: newTestDict(2, None, "baz", o).ToObject()}, 732 {args: wrapArgs(newTestDict(hashFoo, o, "foo", None), "foo", 3.14), want: newTestDict(hashFoo, o, "foo", 3.14).ToObject()}, 733 } 734 for _, cas := range cases { 735 if err := runInvokeTestCase(setItemString, &cas); err != "" { 736 t.Error(err) 737 } 738 } 739 } 740 741 func TestDictStrRepr(t *testing.T) { 742 recursiveDict := NewDict() 743 if raised := recursiveDict.SetItemString(NewRootFrame(), "key", recursiveDict.ToObject()); raised != nil { 744 t.Fatal(raised) 745 } 746 cases := []struct { 747 o *Object 748 wantPatterns []string 749 }{ 750 {NewDict().ToObject(), []string{"^{}$"}}, 751 {newStringDict(map[string]*Object{"foo": NewStr("foo value").ToObject()}).ToObject(), []string{`^\{'foo': 'foo value'\}$`}}, 752 {newStringDict(map[string]*Object{"foo": NewStr("foo value").ToObject(), "bar": NewStr("bar value").ToObject()}).ToObject(), []string{`^{.*, .*}$`, `'foo': 'foo value'`, `'bar': 'bar value'`}}, 753 {recursiveDict.ToObject(), []string{`^{'key': {\.\.\.}}$`}}, 754 } 755 for _, cas := range cases { 756 fun := wrapFuncForTest(func(f *Frame) *BaseException { 757 for _, pattern := range cas.wantPatterns { 758 re := regexp.MustCompile(pattern) 759 s, raised := ToStr(f, cas.o) 760 if raised != nil { 761 return raised 762 } 763 if !re.MatchString(s.Value()) { 764 t.Errorf("str(%v) = %v, want %q", cas.o, s, re) 765 } 766 s, raised = Repr(f, cas.o) 767 if raised != nil { 768 return raised 769 } 770 if !re.MatchString(s.Value()) { 771 t.Errorf("repr(%v) = %v, want %q", cas.o, s, re) 772 } 773 } 774 return nil 775 }) 776 if err := runInvokeTestCase(fun, &invokeTestCase{want: None}); err != "" { 777 t.Error(err) 778 } 779 } 780 } 781 782 func TestDictUpdate(t *testing.T) { 783 updateMethod := mustNotRaise(GetAttr(NewRootFrame(), DictType.ToObject(), NewStr("update"), nil)) 784 update := newBuiltinFunction("TestDictUpdate", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 785 if raised := checkFunctionVarArgs(f, "TestDictUpdate", args, DictType); raised != nil { 786 return nil, raised 787 } 788 if _, raised := updateMethod.Call(f, args, kwargs); raised != nil { 789 return nil, raised 790 } 791 return args[0], nil 792 }).ToObject() 793 cases := []invokeTestCase{ 794 {args: wrapArgs(newTestDict(42, "foo")), want: newTestDict(42, "foo").ToObject()}, 795 {args: wrapArgs(NewDict(), NewDict()), want: NewDict().ToObject()}, 796 {args: wrapArgs(NewDict(), newTestDict("foo", 42, "bar", 43)), want: newTestDict("foo", 42, "bar", 43).ToObject()}, 797 {args: wrapArgs(newTestDict(123, None), newTestDict(124, True)), want: newTestDict(123, None, 124, True).ToObject()}, 798 {args: wrapArgs(newTestDict("foo", 3.14), newTestDict("foo", "bar")), want: newTestDict("foo", "bar").ToObject()}, 799 {args: wrapArgs(NewDict(), NewTuple()), want: NewDict().ToObject()}, 800 {args: wrapArgs(NewDict(), newTestList(newTestTuple("foo", 42), newTestTuple("bar", 43))), want: newTestDict("foo", 42, "bar", 43).ToObject()}, 801 {args: wrapArgs(newTestDict(123, None), newTestTuple(newTestTuple(124, True))), want: newTestDict(123, None, 124, True).ToObject()}, 802 {args: wrapArgs(newTestDict("foo", 3.14), newTestList(newTestList("foo", "bar"))), want: newTestDict("foo", "bar").ToObject()}, 803 {args: wrapArgs(NewDict(), None), wantExc: mustCreateException(TypeErrorType, "'NoneType' object is not iterable")}, 804 {args: wrapArgs(NewDict(), newTestTuple(newTestList(None, 42, "foo"))), wantExc: mustCreateException(ValueErrorType, "dictionary update sequence element has length 3; 2 is required")}, 805 {args: wrapArgs(NewDict()), want: NewDict().ToObject()}, 806 {args: wrapArgs(NewDict()), kwargs: wrapKWArgs("foo", "bar"), want: newTestDict("foo", "bar").ToObject()}, 807 {args: wrapArgs(newTestDict("foo", 1, "bar", 3.14), newTestDict("foo", 2)), kwargs: wrapKWArgs("foo", 3), want: newTestDict("foo", 3, "bar", 3.14).ToObject()}, 808 } 809 for _, cas := range cases { 810 if err := runInvokeTestCase(update, &cas); err != "" { 811 t.Error(err) 812 } 813 } 814 } 815 816 func TestDictValues(t *testing.T) { 817 cases := []invokeTestCase{ 818 {args: wrapArgs(NewDict()), want: NewList().ToObject()}, 819 {args: wrapArgs(newTestDict("foo", 1, "bar", 2)), want: newTestList(1, 2).ToObject()}, 820 {args: wrapArgs(NewDict(), "bad"), wantExc: mustCreateException(TypeErrorType, "'values' of 'dict' requires 1 arguments")}, 821 } 822 for _, cas := range cases { 823 if err := runInvokeMethodTestCase(DictType, "values", &cas); err != "" { 824 t.Error(err) 825 } 826 } 827 } 828 829 func TestParallelDictUpdates(t *testing.T) { 830 keys := []*Object{ 831 NewStr("abc").ToObject(), 832 NewStr("def").ToObject(), 833 NewStr("ghi").ToObject(), 834 NewStr("jkl").ToObject(), 835 NewStr("mno").ToObject(), 836 NewStr("pqr").ToObject(), 837 NewStr("stu").ToObject(), 838 NewStr("vwx").ToObject(), 839 NewStr("yz0").ToObject(), 840 NewStr("123").ToObject(), 841 NewStr("456").ToObject(), 842 NewStr("789").ToObject(), 843 NewStr("ABC").ToObject(), 844 NewStr("DEF").ToObject(), 845 NewStr("GHI").ToObject(), 846 NewStr("JKL").ToObject(), 847 NewStr("MNO").ToObject(), 848 NewStr("PQR").ToObject(), 849 NewStr("STU").ToObject(), 850 NewStr("VWX").ToObject(), 851 NewStr("YZ)").ToObject(), 852 NewStr("!@#").ToObject(), 853 NewStr("$%^").ToObject(), 854 NewStr("&*(").ToObject(), 855 } 856 857 var started, finished sync.WaitGroup 858 stop := make(chan struct{}) 859 runner := func(f func(*Frame, *Object, int)) { 860 for i := 0; i < 8; i++ { 861 started.Add(1) 862 finished.Add(1) 863 go func() { 864 defer finished.Done() 865 frame := NewRootFrame() 866 i := 0 867 for _, k := range keys { 868 f(frame, k, i) 869 frame.RestoreExc(nil, nil) 870 i++ 871 } 872 started.Done() 873 for { 874 if _, ok := <-stop; !ok { 875 break 876 } 877 for _, k := range keys { 878 f(frame, k, i) 879 frame.RestoreExc(nil, nil) 880 i++ 881 } 882 } 883 }() 884 } 885 } 886 887 d := NewDict().ToObject() 888 runner(func(f *Frame, k *Object, _ int) { 889 GetItem(f, d, k) 890 }) 891 892 runner(func(f *Frame, k *Object, i int) { 893 mustNotRaise(nil, SetItem(f, d, k, NewInt(i).ToObject())) 894 }) 895 896 runner(func(f *Frame, k *Object, _ int) { 897 DelItem(f, d, k) 898 }) 899 900 started.Wait() 901 time.AfterFunc(time.Second, func() { close(stop) }) 902 finished.Wait() 903 } 904 905 func newTestDict(elems ...interface{}) *Dict { 906 if len(elems)%2 != 0 { 907 panic("invalid test dict spec") 908 } 909 numItems := len(elems) / 2 910 d := NewDict() 911 f := NewRootFrame() 912 for i := 0; i < numItems; i++ { 913 k := mustNotRaise(WrapNative(f, reflect.ValueOf(elems[i*2]))) 914 v := mustNotRaise(WrapNative(f, reflect.ValueOf(elems[i*2+1]))) 915 d.SetItem(f, k, v) 916 } 917 return d 918 }