| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library tests.dart2js.lookup_map_test; | 5 library tests.dart2js.lookup_map_test; |
| 6 | 6 |
| 7 import 'package:test/test.dart'; | 7 import 'package:test/test.dart'; |
| 8 import 'compiler_helper.dart'; | 8 import 'compiler_helper.dart'; |
| 9 | 9 |
| 10 main() { | 10 main() { |
| 11 Map<String, String> testDeclarations = { |
| 12 'types': r''' |
| 13 import 'package:lookup_map/lookup_map.dart'; |
| 14 class A {} |
| 15 class B {} |
| 16 class C {} |
| 17 class D {} |
| 18 class E {}''', |
| 19 |
| 20 'const keys': r''' |
| 21 import 'package:lookup_map/lookup_map.dart'; |
| 22 class Key { final name; const Key(this.name); } |
| 23 const A = const Key("A"); |
| 24 const B = const Key("B"); |
| 25 const C = const Key("C"); |
| 26 const D = const Key("D"); |
| 27 const E = const Key("E");''', |
| 28 |
| 29 'mixed keys': r''' |
| 30 import 'package:lookup_map/lookup_map.dart'; |
| 31 class Key { final name; const Key(this.name); } |
| 32 const A = const Key("A"); |
| 33 class B {} |
| 34 const C = const Key("C"); |
| 35 class D {} |
| 36 const E = const Key("E");''', |
| 37 }; |
| 38 |
| 39 testDeclarations.forEach((name, declarations) { |
| 40 group(name, () => _commonTests(declarations)); |
| 41 }); |
| 42 group('generic', _genericTests); |
| 43 group('metadata', _metadataTests); |
| 44 group('unsupported', _unsupportedKeysTests); |
| 45 } |
| 46 |
| 47 /// Common tests for both declarations that use Types or other const expressions |
| 48 /// as keys. The argument [declaration] should contain a declaration for |
| 49 /// constant keys named `A`, `B`, `C`, `D`, and `E`. |
| 50 _commonTests(String declarations) { |
| 11 test('live entries are kept', () async { | 51 test('live entries are kept', () async { |
| 12 String generated = await compileAll(r""" | 52 String generated = await compileAll(""" |
| 13 import 'package:lookup_map/lookup_map.dart'; | 53 $declarations |
| 14 class A{} | |
| 15 const map = const LookupMap(const [ | 54 const map = const LookupMap(const [ |
| 16 A, "the-text-for-A", | 55 A, "the-text-for-A", |
| 17 ]); | 56 ]); |
| 18 main() => print(map[A]); | 57 main() => print(map[A]); |
| 19 """); | 58 """); |
| 20 expect(generated, contains("the-text-for-A")); | 59 expect(generated, contains("the-text-for-A")); |
| 21 }); | 60 }); |
| 22 | 61 |
| 23 test('live entries are kept - single-pair', () async { | 62 test('live entries are kept - single-pair', () async { |
| 24 String generated = await compileAll(r""" | 63 String generated = await compileAll(""" |
| 25 import 'package:lookup_map/lookup_map.dart'; | 64 $declarations |
| 26 class A{} | |
| 27 const map = const LookupMap.pair(A, "the-text-for-A"); | 65 const map = const LookupMap.pair(A, "the-text-for-A"); |
| 28 main() => print(map[A]); | 66 main() => print(map[A]); |
| 29 """); | 67 """); |
| 30 expect(generated, contains("the-text-for-A")); | 68 expect(generated, contains("the-text-for-A")); |
| 31 }); | 69 }); |
| 32 | 70 |
| 33 test('unused entries are removed', () async { | 71 test('unused entries are removed', () async { |
| 34 String generated = await compileAll(r""" | 72 String generated = await compileAll(""" |
| 35 import 'package:lookup_map/lookup_map.dart'; | 73 $declarations |
| 36 class A{} | |
| 37 class B{} | |
| 38 const map = const LookupMap(const [ | 74 const map = const LookupMap(const [ |
| 39 A, "the-text-for-A", | 75 A, "the-text-for-A", |
| 40 B, "the-text-for-B", | 76 B, "the-text-for-B", |
| 41 ]); | 77 ]); |
| 42 main() => print(map[A]); | 78 main() => print(map[A]); |
| 43 """); | 79 """); |
| 44 expect(generated, isNot(contains("the-text-for-B"))); | 80 expect(generated, isNot(contains("the-text-for-B"))); |
| 45 }); | 81 }); |
| 46 | 82 |
| 47 test('unused entries are removed - nested maps', () async { | 83 test('unused entries are removed - nested maps', () async { |
| 48 String generated = await compileAll(r""" | 84 String generated = await compileAll(""" |
| 49 import 'package:lookup_map/lookup_map.dart'; | 85 $declarations |
| 50 class A{} | |
| 51 class B{} | |
| 52 const map = const LookupMap(const [], const [ | 86 const map = const LookupMap(const [], const [ |
| 53 const LookupMap(const [ | 87 const LookupMap(const [ |
| 54 A, "the-text-for-A", | 88 A, "the-text-for-A", |
| 55 B, "the-text-for-B", | 89 B, "the-text-for-B", |
| 56 ]), | 90 ]), |
| 57 ]); | 91 ]); |
| 58 main() => print(map[A]); | 92 main() => print(map[A]); |
| 59 """); | 93 """); |
| 60 expect(generated, isNot(contains("the-text-for-B"))); | 94 expect(generated, isNot(contains("the-text-for-B"))); |
| 61 }); | 95 }); |
| 62 | 96 |
| 63 test('unused entries are removed - single-pair', () async { | 97 test('unused entries are removed - single-pair', () async { |
| 64 String generated = await compileAll(r""" | 98 String generated = await compileAll(""" |
| 65 import 'package:lookup_map/lookup_map.dart'; | 99 $declarations |
| 66 class A{} | |
| 67 class B{} | |
| 68 const map = const LookupMap.pair(A, "the-text-for-A"); | 100 const map = const LookupMap.pair(A, "the-text-for-A"); |
| 69 main() => print(map[A]); | 101 main() => print(map[A]); |
| 70 """); | 102 """); |
| 71 expect(generated, isNot(contains("the-text-for-B"))); | 103 expect(generated, isNot(contains("the-text-for-B"))); |
| 72 }); | 104 }); |
| 73 | 105 |
| 74 test('unused entries are removed - nested single-pair', () async { | 106 test('unused entries are removed - nested single-pair', () async { |
| 75 String generated = await compileAll(r""" | 107 String generated = await compileAll(""" |
| 76 import 'package:lookup_map/lookup_map.dart'; | 108 import 'package:lookup_map/lookup_map.dart'; |
| 77 class A{} | 109 $declarations |
| 78 class B{} | |
| 79 const map = const LookupMap(const [], const [ | 110 const map = const LookupMap(const [], const [ |
| 80 const LookupMap.pair(A, "the-text-for-A"), | 111 const LookupMap.pair(A, "the-text-for-A"), |
| 81 const LookupMap.pair(B, "the-text-for-B"), | 112 const LookupMap.pair(B, "the-text-for-B"), |
| 82 ]); | 113 ]); |
| 83 main() => print(map[A]); | 114 main() => print(map[A]); |
| 84 """); | 115 """); |
| 85 expect(generated, isNot(contains("the-text-for-B"))); | 116 expect(generated, isNot(contains("the-text-for-B"))); |
| 86 }); | 117 }); |
| 87 | 118 |
| 88 test('works if entries are declared separate from map', () async { | 119 test('works if entries are declared separate from map', () async { |
| 89 String generated = await compileAll(r""" | 120 String generated = await compileAll(""" |
| 90 import 'package:lookup_map/lookup_map.dart'; | 121 $declarations |
| 91 class A{} | |
| 92 class B{} | |
| 93 const entries = const [ | 122 const entries = const [ |
| 94 A, "the-text-for-A", | 123 A, "the-text-for-A", |
| 95 B, "the-text-for-B", | 124 B, "the-text-for-B", |
| 96 ]; | 125 ]; |
| 97 const map = const LookupMap(entries); | 126 const map = const LookupMap(entries); |
| 98 main() => print(map[A]); | 127 main() => print(map[A]); |
| 99 """); | 128 """); |
| 100 expect(generated, isNot(contains("the-text-for-B"))); | 129 expect(generated, isNot(contains("the-text-for-B"))); |
| 101 }); | 130 }); |
| 102 | 131 |
| 103 test('escaping entries disable tree-shaking', () async { | 132 test('escaping entries disable tree-shaking', () async { |
| 104 String generated = await compileAll(r""" | 133 String generated = await compileAll(""" |
| 105 import 'package:lookup_map/lookup_map.dart'; | 134 $declarations |
| 106 class A{} | |
| 107 class B{} | |
| 108 const entries = const [ | 135 const entries = const [ |
| 109 A, "the-text-for-A", | 136 A, "the-text-for-A", |
| 110 B, "the-text-for-B", | 137 B, "the-text-for-B", |
| 111 ]; | 138 ]; |
| 112 const map = const LookupMap(entries); | 139 const map = const LookupMap(entries); |
| 113 main() { | 140 main() { |
| 114 entries.forEach(print); | 141 entries.forEach(print); |
| 115 print(map[A]); | 142 print(map[A]); |
| 116 } | 143 } |
| 117 """); | 144 """); |
| 118 expect(generated, contains("the-text-for-B")); | 145 expect(generated, contains("the-text-for-B")); |
| 119 }); | 146 }); |
| 120 | 147 |
| 121 test('uses include recursively reachable data', () async { | 148 test('uses include recursively reachable data', () async { |
| 122 String generated = await compileAll(r""" | 149 String generated = await compileAll(""" |
| 123 import 'package:lookup_map/lookup_map.dart'; | 150 $declarations |
| 124 class A{} | |
| 125 class B{} | |
| 126 class C{} | |
| 127 class D{} | |
| 128 class E{} | |
| 129 const map = const LookupMap(const [ | 151 const map = const LookupMap(const [ |
| 130 A, const ["the-text-for-A", B], | 152 A, const ["the-text-for-A", B], |
| 131 B, const ["the-text-for-B", C], | 153 B, const ["the-text-for-B", C], |
| 132 C, const ["the-text-for-C"], | 154 C, const ["the-text-for-C"], |
| 133 D, const ["the-text-for-D", E], | 155 D, const ["the-text-for-D", E], |
| 134 E, const ["the-text-for-E"], | 156 E, const ["the-text-for-E"], |
| 135 ]); | 157 ]); |
| 136 main() => print(map[map[A][1]]); | 158 main() => print(map[map[A][1]]); |
| 137 """); | 159 """); |
| 138 expect(generated, contains("the-text-for-A")); | 160 expect(generated, contains("the-text-for-A")); |
| 139 expect(generated, contains("the-text-for-B")); | 161 expect(generated, contains("the-text-for-B")); |
| 140 expect(generated, contains("the-text-for-C")); | 162 expect(generated, contains("the-text-for-C")); |
| 141 expect(generated, isNot(contains("the-text-for-D"))); | 163 expect(generated, isNot(contains("the-text-for-D"))); |
| 142 expect(generated, isNot(contains("the-text-for-E"))); | 164 expect(generated, isNot(contains("the-text-for-E"))); |
| 143 }); | 165 }); |
| 144 | 166 |
| 145 test('uses are found through newly discovered code', () async { | 167 test('uses are found through newly discovered code', () async { |
| 146 String generated = await compileAll(r""" | 168 String generated = await compileAll(""" |
| 147 import 'package:lookup_map/lookup_map.dart'; | 169 $declarations |
| 148 class A{ A(B x);} | 170 f1() => map[B][1](); |
| 149 class B{} | 171 f2() => E; |
| 150 class C{} | |
| 151 class D{} | |
| 152 class E{} | |
| 153 createA() => new A(map[B][1]()); | |
| 154 createB() => new B(); | |
| 155 const map = const LookupMap(const [ | 172 const map = const LookupMap(const [ |
| 156 A, const ["the-text-for-A", createA], | 173 A, const ["the-text-for-A", f1], |
| 157 B, const ["the-text-for-B", createB], | 174 B, const ["the-text-for-B", f2], |
| 158 C, const ["the-text-for-C"], | 175 C, const ["the-text-for-C"], |
| 176 D, const ["the-text-for-D"], |
| 177 E, const ["the-text-for-E"], |
| 159 ]); | 178 ]); |
| 160 main() => print(map[A][1]()); | 179 main() => print(map[A][1]()); |
| 161 """); | 180 """); |
| 162 expect(generated, contains("the-text-for-A")); | 181 expect(generated, contains("the-text-for-A")); |
| 163 expect(generated, contains("the-text-for-B")); | 182 expect(generated, contains("the-text-for-B")); |
| 164 expect(generated, isNot(contains("the-text-for-C"))); | 183 expect(generated, isNot(contains("the-text-for-C"))); |
| 184 expect(generated, isNot(contains("the-text-for-C"))); |
| 185 expect(generated, contains("the-text-for-E")); |
| 165 }); | 186 }); |
| 166 | 187 |
| 188 test('support subclassing LookupMap', () async { |
| 189 String generated = await compileAll(""" |
| 190 $declarations |
| 191 class S extends LookupMap { |
| 192 const S(list) : super(list); |
| 193 } |
| 194 const map = const S(const [ |
| 195 A, "the-text-for-A", |
| 196 B, "the-text-for-B", |
| 197 ]); |
| 198 |
| 199 main() => print(map[A]); |
| 200 """); |
| 201 expect(generated, contains("the-text-for-A")); |
| 202 expect(generated, isNot(contains("the-text-for-B"))); |
| 203 }); |
| 204 |
| 205 test('constants keys are processed recursively', () async { |
| 206 String generated = await compileAll(""" |
| 207 $declarations |
| 208 |
| 209 const nested = const [ B ]; |
| 210 const map = const LookupMap(const [ |
| 211 A, "the-text-for-A", |
| 212 B, "the-text-for-B", |
| 213 ]); |
| 214 main() => print(map[nested]); |
| 215 """); |
| 216 expect(generated, isNot(contains("the-text-for-A"))); |
| 217 expect(generated, contains("the-text-for-B")); |
| 218 }); |
| 219 } |
| 220 |
| 221 /// Tests specific to type keys, we ensure that generic type arguments are |
| 222 /// considered. |
| 223 _genericTests() { |
| 167 test('generic type allocations are considered used', () async { | 224 test('generic type allocations are considered used', () async { |
| 168 String generated = await compileAll(r""" | 225 String generated = await compileAll(r""" |
| 169 import 'package:lookup_map/lookup_map.dart'; | 226 import 'package:lookup_map/lookup_map.dart'; |
| 170 class A{} | 227 class A{} |
| 171 class M<T>{ get type => T; } | 228 class M<T>{ get type => T; } |
| 172 const map = const LookupMap(const [ | 229 const map = const LookupMap(const [ |
| 173 A, "the-text-for-A", | 230 A, "the-text-for-A", |
| 174 ]); | 231 ]); |
| 175 main() => print(map[new M<A>().type]); | 232 main() => print(map[new M<A>().type]); |
| 176 """); | 233 """); |
| 177 expect(generated, contains("the-text-for-A")); | 234 expect(generated, contains("the-text-for-A")); |
| 178 }); | 235 }); |
| 179 | 236 |
| 180 test('generics in type signatures are ignored', () async { | 237 test('generics in type signatures are ignored', () async { |
| 181 String generated = await compileAll(r""" | 238 String generated = await compileAll(r""" |
| 182 import 'package:lookup_map/lookup_map.dart'; | 239 import 'package:lookup_map/lookup_map.dart'; |
| 183 class A{} | 240 class A{} |
| 184 class B{} | 241 class B{} |
| 185 class M<T>{ get type => T; } | 242 class M<T>{ get type => T; } |
| 186 _factory(M<B> t) => t; | 243 _factory(M<B> t) => t; |
| 187 const map = const LookupMap(const [ | 244 const map = const LookupMap(const [ |
| 188 A, const ["the-text-for-A", _factory], | 245 A, const ["the-text-for-A", _factory], |
| 189 B, "the-text-for-B", | 246 B, "the-text-for-B", |
| 190 ]); | 247 ]); |
| 191 main() => print(map[A]); | 248 main() => print(map[A]); |
| 192 """); | 249 """); |
| 193 expect(generated, isNot(contains("the-text-for-B"))); | 250 expect(generated, isNot(contains("the-text-for-B"))); |
| 194 }); | 251 }); |
| 195 | 252 |
| 253 // regression test for a failure when looking up `dynamic` in a generic. |
| 254 test('do not choke with dynamic type arguments', () async { |
| 255 await compileAll(r""" |
| 256 import 'package:lookup_map/lookup_map.dart'; |
| 257 class A{} |
| 258 class M<T>{ get type => T; } |
| 259 const map = const LookupMap(const [ |
| 260 A, "the-text-for-A", |
| 261 ]); |
| 262 main() => print(map[new M<dynamic>().type]); |
| 263 """); |
| 264 }); |
| 265 } |
| 266 |
| 267 /// Sanity checks about metadata: it is ignored for codegen even though it is |
| 268 /// visited during resolution. |
| 269 _metadataTests() { |
| 196 test('metadata is ignored', () async { | 270 test('metadata is ignored', () async { |
| 197 String generated = await compileAll(r""" | 271 String generated = await compileAll(r""" |
| 198 import 'package:lookup_map/lookup_map.dart'; | 272 import 'package:lookup_map/lookup_map.dart'; |
| 199 class A{ const A(); } | 273 class A{ const A(); } |
| 200 | 274 |
| 201 @A() | 275 @A() |
| 202 class M {} | 276 class M {} |
| 203 const map = const LookupMap(const [ | 277 const map = const LookupMap(const [ |
| 204 A, "the-text-for-A", | 278 A, "the-text-for-A", |
| 205 ]); | 279 ]); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 218 class B{ final Type foo; const B({this.foo}); } | 292 class B{ final Type foo; const B({this.foo}); } |
| 219 | 293 |
| 220 class M {} | 294 class M {} |
| 221 const map = const LookupMap(const [ | 295 const map = const LookupMap(const [ |
| 222 A, const ["the-text-for-A", annot] | 296 A, const ["the-text-for-A", annot] |
| 223 ]); | 297 ]); |
| 224 main() => print(map[M]); | 298 main() => print(map[M]); |
| 225 """); | 299 """); |
| 226 expect(generated, isNot(contains("the-text-for-A"))); | 300 expect(generated, isNot(contains("the-text-for-A"))); |
| 227 }); | 301 }); |
| 302 } |
| 228 | 303 |
| 229 // regression test for a failure when looking up `dynamic` in a generic. | 304 _unsupportedKeysTests() { |
| 230 test('do not choke on dynamic types', () async { | 305 test('primitive and string keys are always kept', () async { |
| 231 await compileAll(r""" | 306 String generated = await compileAll(""" |
| 232 import 'package:lookup_map/lookup_map.dart'; | 307 import 'package:lookup_map/lookup_map.dart'; |
| 233 class A{} | 308 const A = "A"; |
| 234 class M<T>{ get type => T; } | 309 const B = "B"; |
| 235 const map = const LookupMap(const [ | 310 const map = const LookupMap(const [ |
| 236 A, "the-text-for-A", | 311 A, "the-text-for-A", |
| 312 B, "the-text-for-B", |
| 313 3, "the-text-for-3", |
| 314 1.1, "the-text-for-1.1", |
| 315 false, "the-text-for-false", |
| 237 ]); | 316 ]); |
| 238 main() => print(map[new M<dynamic>().type]); | 317 main() => print(map[A]); |
| 239 """); | 318 """); |
| 319 expect(generated, contains("the-text-for-A")); |
| 320 expect(generated, contains("the-text-for-B")); |
| 321 expect(generated, contains("the-text-for-3")); |
| 322 expect(generated, contains("the-text-for-1.1")); |
| 323 expect(generated, contains("the-text-for-false")); |
| 240 }); | 324 }); |
| 241 | 325 |
| 242 | 326 test('non-type const keys implementing equals are not removed', () async { |
| 243 test('support subclassing LookupMap', () async { | 327 String generated = await compileAll(""" |
| 244 String generated = await compileAll(r""" | |
| 245 import 'package:lookup_map/lookup_map.dart'; | 328 import 'package:lookup_map/lookup_map.dart'; |
| 246 class A{} | 329 class Key { |
| 247 class B{} | 330 final name; |
| 248 class S extends LookupMap { | 331 const Key(this.name); |
| 249 const S(list) : super(list); | 332 int get hashCode => name.hashCode * 13; |
| 333 operator ==(other) => other is Key && name == other.name; |
| 250 } | 334 } |
| 251 const map = const S(const [ | 335 const A = const Key("A"); |
| 336 const B = const Key("B"); |
| 337 const map = const LookupMap(const [ |
| 252 A, "the-text-for-A", | 338 A, "the-text-for-A", |
| 253 B, "the-text-for-B", | 339 B, "the-text-for-B", |
| 254 ]); | 340 ]); |
| 255 | |
| 256 main() => print(map[A]); | 341 main() => print(map[A]); |
| 257 """); | 342 """); |
| 258 expect(generated, contains("the-text-for-A")); | 343 expect(generated, contains("the-text-for-B")); |
| 259 expect(generated, isNot(contains("the-text-for-B"))); | |
| 260 }); | 344 }); |
| 261 } | 345 } |
| OLD | NEW |