go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/starlark/typed/testdata/dict.star (about) 1 # Copyright 2018 The LUCI Authors. 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 # Helpers. 16 def key_conv(x): 17 if type(x) == 'int' and x < 0: 18 fail('nope') 19 return str(x) 20 def val_conv(x): 21 if type(x) == 'int' and x < 0: 22 fail('nope') 23 return str(x) 24 def typed(d): 25 return typed_dict(key_conv, val_conv, d) 26 27 28 # Typed dict implements same methods as a regular dict. If this test fails, it 29 # means go.starlark.net added a new dict method that should be added to typed 30 # dicts as well. 31 def test_same_dir_as_dict(): 32 assert.eq(dir(typed({})), dir({})) 33 test_same_dir_as_dict() 34 35 36 # Value interface. 37 def test_basic_value_interface(): 38 assert.eq(str(typed({1: 2})), 'dict<K,V>({"1": "2"})') 39 assert.eq(type(typed({})), 'dict<K,V>') # see starlark_test.go for K, V 40 assert.eq(bool(typed({})), False) 41 assert.eq(bool(typed({1: 2})), True) 42 test_basic_value_interface() 43 44 45 # Basic dict interface. 46 def test_dict(): 47 a = typed({1: 2, 3: 4}) 48 assert.eq(dict(a), {'1': '2', '3': '4'}) 49 50 # Iteration works. 51 keys = [] 52 for k in a: 53 keys.append(k) 54 assert.eq(keys, ['1', '3']) 55 56 assert.eq(len(a), 2) 57 assert.eq(bool(a), True) 58 assert.eq(bool(typed({})), False) 59 60 assert.eq(a['1'], '2') 61 assert.eq('1' in a, True) 62 assert.eq(1 in a, False) # no type coercion 63 64 a['1'] = 6 65 assert.eq(a['1'], '6') 66 67 def bad_key_type(): 68 a[-1] = '6' 69 assert.fails(bad_key_type, 'bad key: fail: nope') 70 71 def bad_val_type(): 72 a[1] = -1 73 assert.fails(bad_val_type, 'bad value: fail: nope') 74 test_dict() 75 76 77 # '==' and '!=' operators. 78 def test_equality_operators(): 79 assert.true(typed({}) == typed({})) 80 assert.true(typed({0:1, 2:3}) == typed({0:1, 2:3})) 81 assert.true(typed({0:1, 2:3}) != typed({0:1})) 82 assert.true(typed({0:1, 2:3}) != typed({0:1, 9:3})) 83 assert.true(typed({0:1, 2:3}) != typed({0:1, 2:9})) 84 # Different key type. 85 assert.true(typed({0:1}) != typed_dict(lambda x: str(x), val_conv, {0:1})) 86 # Different value type. 87 assert.true(typed({0:1}) != typed_dict(key_conv, lambda x: str(x), {0:1})) 88 test_equality_operators() 89 90 91 # 'setdefault' method. 92 def test_setdefault(): 93 a = typed({1: 2, 3: 4}) 94 assert.eq(a.setdefault(1, 666), '2') 95 assert.eq(a.setdefault(5, 666), '666') 96 assert.eq(a['5'], '666') 97 assert.eq(a.setdefault(6), 'None') 98 def bad_key_type(): 99 a.setdefault(-1, '6') 100 assert.fails(bad_key_type, 'bad key: fail: nope') 101 def bad_val_type(): 102 a.setdefault(1, -1) 103 assert.fails(bad_val_type, 'bad value: fail: nope') 104 test_setdefault() 105 106 107 # 'update' method. 108 def test_update(): 109 # Fastest path. 110 a = typed({1: 2}) 111 a.update(typed({3: 4, 5: 6})) 112 assert.eq(dict(a), {'1': '2', '3': '4', '5': '6'}) 113 114 # Fast path. 115 a = typed({1: 2}) 116 a.update({3: 4, 5: 6}) 117 assert.eq(dict(a), {'1': '2', '3': '4', '5': '6'}) 118 119 # Slow path. 120 a = typed({1: 2}) 121 a.update([(3, 4), (5, 6)]) 122 assert.eq(dict(a), {'1': '2', '3': '4', '5': '6'}) 123 124 # Doesn't accept kwargs. 125 assert.fails(lambda: a.update(a=1), 'update: got unexpected keyword argument') 126 127 # Needs exactly one positional arg. 128 assert.fails(lambda: a.update(), 'update: got 0 arguments, want exactly 1') 129 assert.fails(lambda: a.update({}, {}), 'update: got 2 arguments, want exactly 1') 130 131 # Need an iterable that returns pairs. 132 assert.fails(lambda: a.update(1), 'update: got int, want iterable') 133 assert.fails(lambda: a.update([1]), r'update: element #0 is not iterable \(int\)') 134 assert.fails(lambda: a.update([(1,1,1)]), 'update: element #0 has length 3, want 2') 135 136 # Bad conversion in fast path. 137 assert.fails(lambda: a.update({-1: 0}), 'update: element #0: bad key: fail: nope') 138 assert.fails(lambda: a.update({0: -1}), 'update: element #0: bad value: fail: nope') 139 140 # Bad conversion in slow path. 141 assert.fails(lambda: a.update([(-1, 0)]), 'update: element #0: bad key: fail: nope') 142 assert.fails(lambda: a.update([(0, -1)]), 'update: element #0: bad value: fail: nope') 143 test_update() 144 145 146 # Other builtins we didn't touch. 147 def test_builtins(): 148 a = typed({1: 2, 3: 4}) 149 150 assert.eq(a.get('1'), '2') 151 assert.eq(a.get(1), None) # no type coercion 152 153 assert.eq(a.items(), [('1', '2'), ('3', '4')]) 154 assert.eq(a.keys(), ['1', '3']) 155 assert.eq(a.values(), ['2', '4']) 156 157 assert.eq(a.pop('1'), '2') 158 assert.eq(a.pop(3, None), None) # no type coercion 159 assert.eq(a.popitem(), ('3', '4')) 160 161 a = typed({1: 2, 3: 4}) 162 a.clear() 163 assert.eq(len(a), 0) 164 test_builtins()