| // Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:test/test.dart'; | |
| import '../shared_test_options.dart'; | |
| import 'expression_compiler_e2e_suite.dart'; | |
| /// Common code used by many of the tests. | |
| /// | |
| /// While having a small source file with a small test case side-by-side | |
| /// is a lot more readable, we've found that switching test files adds a big | |
| /// overhead for the test harness. It requires loading a new page in chrome each | |
| /// time, which can easily add seconds to each test, especially on the try bots. | |
| /// Instead, it's cheaper to combine many test cases in a single test file and | |
| /// call `initSource` only once for a large group of tests. | |
| const sharedSource = ''' | |
| extension NumberParsing on String { | |
| int parseIntPlusOne() { | |
| var ret = int.parse(this); | |
| // Breakpoint: parseIntPlusOneBP | |
| return ret + 1; | |
| } | |
| } | |
| class C { | |
| static int staticField = 1; | |
| static int staticFieldB = 1; | |
| static int staticFieldC = 1; | |
| static int staticFieldD = 1; | |
| static int _staticField = 2; | |
| static int _staticFieldB = 2; | |
| static int _unusedStaticField = 3; | |
| int field; | |
| int _field; | |
| int _unusedField = 4; | |
| final list = <String>[]; | |
| C(this.field, this._field) { | |
| int y = 1; | |
| // Breakpoint: constructorBP | |
| var nop; | |
| } | |
| C.named(this.field): _field = 42; | |
| C.redirecting(int x) : this(x, 99); | |
| factory C.factory() => C(42, 0); | |
| int methodFieldAccess(int x) { | |
| // Breakpoint: methodBP | |
| var inScope = 1; | |
| { | |
| var innerInScope = global + staticField + field; | |
| // Breakpoint: innerScopeBP | |
| var innerNotInScope = 2; | |
| } | |
| var notInScope = 3; | |
| return x + _field + _staticField; | |
| } | |
| Future<int> asyncMethod(int x) async { | |
| return x + _field + _staticField; | |
| } | |
| } | |
| int global = 42; | |
| late int lateGlobal; | |
| late String lateGlobal2; | |
| const soundNullSafety = !(<Null>[] is List<int>); | |
| soundNullSafetyTest() { | |
| // Breakpoint: soundNullSafetyBP | |
| print('hello world'); | |
| } | |
| class B { | |
| int field; | |
| int _field; | |
| B(this.field, this._field) {} | |
| } | |
| enum E {id1, id2, id3} | |
| enum E2 {id1, id2, id3} | |
| enumTest() { | |
| var e = E.id2; | |
| // Breakpoint: enumBP | |
| print('hello world'); | |
| } | |
| dynamic couldReturnNull() => null; | |
| couldReturnNullTest() { | |
| var i = couldReturnNull() ?? 10; | |
| // Breakpoint: couldReturnNullBP | |
| print(i); | |
| } | |
| extensionsSymbolTest() { | |
| List<int> list = []; | |
| list.add(0); | |
| // Breakpoint: extensionSymbolsBP | |
| print(list); | |
| } | |
| lateLocalVariableTest() { | |
| late int lateLocal; | |
| late int lateLocal2; | |
| if (42.isEven) { | |
| lateLocal = 42; | |
| } | |
| // Breakpoint: lateLocalVariableBP | |
| print(lateLocal); | |
| } | |
| lateGlobalVariableTest() { | |
| if (42.isEven) { | |
| lateGlobal = 42; | |
| } | |
| // Breakpoint: lateGlobalVariableBP | |
| print(lateGlobal); | |
| } | |
| int foo(int x, {int y = 0}) { | |
| int z = 3; | |
| // Breakpoint: fooBP | |
| return x + y + z; | |
| } | |
| callFooTest() => foo(1, y: 2); | |
| class D { | |
| static int staticField = 1; | |
| static int _staticField = 2; | |
| int _field; | |
| int field; | |
| D(this.field, this._field); | |
| Future<int> asyncMethod(int x) async { | |
| // Breakpoint: asyncTestBP1 | |
| return x + global + _field + field + staticField + _staticField; | |
| } | |
| } | |
| Future<int> asyncTest() async { | |
| var d = D(5, 7); | |
| // Breakpoint: asyncTestBP2 | |
| return await d.asyncMethod(1); | |
| } | |
| void asyncClosureTest() { | |
| int test = 0; | |
| int unused = 0; | |
| void foo() async { | |
| // Breakpoint: asyncClosureBP0 | |
| print('\$test'); | |
| { | |
| int test = 1; | |
| // Breakpoint: asyncClosureBP1 | |
| print('\$test'); | |
| { | |
| int test = 2; | |
| // Breakpoint: asyncClosureBP2 | |
| print('\$test'); | |
| // Breakpoint: asyncClosureBP3 | |
| print('\$test'); | |
| // Breakpoint: asyncClosureBP4 | |
| print('\$test'); | |
| } | |
| } | |
| } | |
| foo(); | |
| } | |
| void closuresTest() { | |
| int x = 15; | |
| var outerClosure = (int y) { | |
| var closureCaptureInner = (int z) { | |
| // Breakpoint: closuresTestBP | |
| var temp = x + y + z; | |
| return; | |
| }; | |
| closureCaptureInner(0); | |
| }; | |
| outerClosure(3); | |
| return; | |
| } | |
| // Caution: this test function should not be reused across multiple test cases | |
| // to prevent data races. See http://github.com/dart-lang/sdk/issues/55299 for | |
| // details. | |
| void forLoopTest() { | |
| int x = 15; | |
| for(int i = 0; i < 10; i++) { | |
| // Breakpoint: forLoopTestBP | |
| var calculation = '\$i+\$x'; | |
| }; | |
| } | |
| // Caution: this test function should not be reused across multiple test cases | |
| // to prevent data races. See http://github.com/dart-lang/sdk/issues/55299 for | |
| // details. | |
| int iteratorLoopTest() { | |
| var l = <String>['1', '2', '3']; | |
| for (var e in l) { | |
| // Breakpoint: iteratorLoopTestBP | |
| var calculation = '\$e'; | |
| }; | |
| return 0; | |
| } | |
| abstract class Key { | |
| const factory Key(String value) = ValueKey; | |
| const Key.empty(); | |
| } | |
| abstract class LocalKey extends Key { | |
| const LocalKey() : super.empty(); | |
| } | |
| class ValueKey implements LocalKey { | |
| const ValueKey(this.value); | |
| final String value; | |
| } | |
| class MyClass { | |
| const MyClass(this._t); | |
| final int _t; | |
| } | |
| int bar(int p) { | |
| return p; | |
| } | |
| String baz(String t) { | |
| return t; | |
| } | |
| missingTypesTest() { | |
| var k = Key('t'); | |
| MyClass c = MyClass(0); | |
| int p = 1; | |
| const t = 1; | |
| // Breakpoint: missingTypesTestBP | |
| return '\$c, \$k, \$t'; | |
| } | |
| int conditionalHelper(int x) { | |
| if (x == 1) { | |
| int y = 3; | |
| // Breakpoint: thenBP | |
| var calculation = '\$y+\$x'; | |
| } else { | |
| int z = 4; | |
| // Breakpoint: elseBP | |
| var calculation = '\$z+\$x'; | |
| } | |
| // Breakpoint: postBP | |
| return 0; | |
| } | |
| void conditionalTest() { | |
| conditionalHelper(1); | |
| conditionalHelper(2); | |
| } | |
| class G<T1> { | |
| void generic<T2>(T1 a, T2 b) { | |
| // Breakpoint: genericBP | |
| print(a); | |
| print(b); | |
| } | |
| } | |
| class M1 { | |
| const M1(); | |
| } | |
| class M2 { | |
| const M2(); | |
| } | |
| void moduleContainersTest() { | |
| const a = M1(); | |
| var check = a is int; | |
| // Breakpoint: moduleContainersBP | |
| return; | |
| } | |
| void exceptionTest() { | |
| try { | |
| throw Exception('meow!'); | |
| } catch (e, s) { | |
| // Breakpoint: exceptionBP | |
| print('Cat says: \$e:\$s'); | |
| } | |
| } | |
| main() { | |
| int x = 15; | |
| var c = C(5, 6); | |
| // Breakpoint: globalFunctionBP | |
| c.methodFieldAccess(10); | |
| enumTest(); | |
| soundNullSafetyTest(); | |
| couldReturnNullTest(); | |
| extensionsSymbolTest(); | |
| lateLocalVariableTest(); | |
| lateGlobalVariableTest(); | |
| "1234".parseIntPlusOne(); | |
| callFooTest(); | |
| asyncTest(); | |
| asyncClosureTest(); | |
| closuresTest(); | |
| forLoopTest(); | |
| iteratorLoopTest(); | |
| missingTypesTest(); | |
| conditionalTest(); | |
| G<int>().generic<String>(0, 'hi'); | |
| moduleContainersTest(); | |
| exceptionTest(); | |
| } | |
| '''; | |
| /// Shared tests that require a language version >=2.12.0 <2.17.0. | |
| void runNullSafeSharedTests( | |
| SetupCompilerOptions setup, | |
| ExpressionEvaluationTestDriver driver, | |
| ) { | |
| group('JS interop', () { | |
| const interopSource = r''' | |
| @JS() | |
| library debug_static_interop; | |
| import 'dart:html'; | |
| import 'dart:_js_annotations' show staticInterop; | |
| import 'dart:js_util'; | |
| import 'dart:js_interop'; | |
| @JSExport() | |
| class Counter { | |
| int value = 0; | |
| @JSExport('increment') | |
| void renamedIncrement() { | |
| value++; | |
| } | |
| } | |
| @JS() | |
| @staticInterop | |
| class JSCounter {} | |
| extension on JSCounter { | |
| external int get value; | |
| external void increment(); | |
| } | |
| void staticInteropTest() { | |
| var dartCounter = Counter(); | |
| var jsCounter = | |
| createDartExport<Counter>(dartCounter) as JSCounter; | |
| dartCounter.renamedIncrement(); | |
| jsCounter.increment(); | |
| // Breakpoint: staticInteropBP | |
| print('jsCounter: ${jsCounter.value}'); // prints '2' | |
| } | |
| extension type JSCounter2(JSObject _) { | |
| external int get value; | |
| external void increment(); | |
| } | |
| void extensionTypesTest() { | |
| var dartCounter = Counter(); | |
| var jsCounter = createDartExport<Counter>(dartCounter) as JSCounter2; | |
| jsCounter.increment(); | |
| dartCounter.renamedIncrement(); | |
| // Breakpoint: extensionTypesBP | |
| print('JS: ${jsCounter.value}'); // prints '2' | |
| } | |
| main() { | |
| staticInteropTest(); | |
| extensionTypesTest(); | |
| } | |
| '''; | |
| setUpAll(() async { | |
| await driver.initSource(setup, interopSource, experiments: {}); | |
| }); | |
| tearDownAll(() async { | |
| await driver.cleanupTest(); | |
| }); | |
| group('static interop', () { | |
| test('call extension methods of existing JS object', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'staticInteropBP', | |
| expression: 'dartCounter.value', | |
| expectedResult: '2', | |
| ); | |
| await driver.checkInFrame( | |
| breakpointId: 'staticInteropBP', | |
| expression: 'jsCounter.value', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| test('call extension methods of a new JS object', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'staticInteropBP', | |
| expression: | |
| '(createDartExport<Counter>(dartCounter) as JSCounter).value', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| }); | |
| group('extension types', () { | |
| test('call extension getters on existing JS object', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'extensionTypesBP', | |
| expression: 'dartCounter.value', | |
| expectedResult: '2', | |
| ); | |
| await driver.checkInFrame( | |
| breakpointId: 'extensionTypesBP', | |
| expression: 'jsCounter.value', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| test('call extension getters on a new JS object', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'extensionTypesBP', | |
| expression: | |
| 'JSCounter2(createDartExport<Counter>(dartCounter) as JSObject)' | |
| '.value', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| }); | |
| }); | |
| group('shared sources', () { | |
| setUpAll(() async { | |
| await driver.initSource(setup, sharedSource); | |
| }); | |
| tearDownAll(() async { | |
| await driver.cleanupTest(); | |
| }); | |
| group('Exceptions', () { | |
| test('error', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'exceptionBP', | |
| expression: 'e.toString()', | |
| expectedResult: 'meow!', | |
| ); | |
| }); | |
| test('stack trace', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'exceptionBP', | |
| expression: 's.toString()', | |
| expectedResult: '', | |
| ); | |
| }); | |
| test('scope', () async { | |
| await driver.checkScope( | |
| breakpointId: 'exceptionBP', | |
| expectedScope: {'e': 'e', 's': 's'}, | |
| ); | |
| }); | |
| }); | |
| group('Correct null safety mode used', () { | |
| test('in original source compilation', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'soundNullSafetyBP', | |
| expression: 'soundNullSafety', | |
| expectedResult: 'true', | |
| ); | |
| }); | |
| test('in expression compilation', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'soundNullSafetyBP', | |
| expression: '!(<Null>[] is List<int>)', | |
| expectedResult: 'true', | |
| ); | |
| }); | |
| }); | |
| group('library level', () { | |
| test('generic instantiation', () async { | |
| await driver.check( | |
| expression: '[B(1,1).toString(), B(2,2).toString()]', | |
| expectedResult: allOf( | |
| contains('Array(2)'), | |
| contains('0: Instance of \'B\''), | |
| contains('1: Instance of \'B\''), | |
| contains('length: 2'), | |
| ), | |
| ); | |
| }); | |
| test( | |
| 'invoke an SDK method', | |
| () async { | |
| await driver.check( | |
| expression: 'Flow.begin(id: 0) is Flow', | |
| libraryUri: Uri.parse('dart:developer'), | |
| expectedResult: 'true', | |
| ); | |
| }, | |
| // The new module format requires a per-library compiler. Since we | |
| // loaded the SDK from a summary/dill, we've never actually created a | |
| // compiler for it, and therefore can't execute library-level | |
| // expression evaluation in the SDK. Currently, no real workflow can | |
| // meaningfully use this anyways. See | |
| // https://github.com/flutter/devtools/issues/7766 for the initial | |
| // motivation. | |
| skip: setup.emitLibraryBundle, | |
| ); | |
| test( | |
| 'tearoff an SDK method', | |
| () async { | |
| await driver.check( | |
| expression: 'postEvent', | |
| libraryUri: Uri.parse('dart:developer'), | |
| expectedResult: contains('function postEvent(eventKind'), | |
| ); | |
| }, | |
| // The new module format requires a per-library compiler. Since we | |
| // loaded the SDK from a summary/dill, we've never actually created a | |
| // compiler for it, and therefore can't execute library-level | |
| // expression evaluation in the SDK. Currently, no real workflow can | |
| // meaningfully use this anyways. See | |
| // https://github.com/flutter/devtools/issues/7766 for the initial | |
| // motivation. | |
| skip: setup.emitLibraryBundle, | |
| ); | |
| }); | |
| group('method level', () { | |
| test('tear off default constructor', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'C.new.runtimeType.toString()', | |
| expectedResult: '(int, int) => C', | |
| ); | |
| }); | |
| test('call default constructor tear off', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: '(C.new)(0, 0)', | |
| expectedResult: allOf( | |
| contains('test.C.new'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.list): Array(0)'), | |
| contains('Symbol(C.field): 0'), | |
| contains('Symbol(_field): 0'), | |
| ), | |
| ); | |
| }); | |
| test('tear off named constructor', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'C.named.runtimeType.toString()', | |
| expectedResult: '(int) => C', | |
| ); | |
| }); | |
| test('call named constructor tear off', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: '(C.named)(0)', | |
| expectedResult: allOf( | |
| contains('test.C.named'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.list): Array(0)'), | |
| contains('Symbol(C.field): 0'), | |
| contains('Symbol(_field): 42'), | |
| ), | |
| ); | |
| }); | |
| test('tear off redirecting constructor', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'C.redirecting.runtimeType.toString()', | |
| expectedResult: '(int) => C', | |
| ); | |
| }); | |
| test('call redirecting constructor tear off', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: '(C.redirecting)(0)', | |
| expectedResult: allOf( | |
| contains('test.C.redirecting'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.list): Array(0)'), | |
| contains('Symbol(C.field): 0'), | |
| contains('Symbol(_field): 99'), | |
| ), | |
| ); | |
| }); | |
| test('tear off factory constructor', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'C.factory.runtimeType.toString()', | |
| expectedResult: '() => C', | |
| ); | |
| }); | |
| test('call factory constructor tear off', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: '(C.factory)()', | |
| expectedResult: allOf( | |
| contains('test.C.new'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.list): Array(0)'), | |
| contains('Symbol(C.field): 42'), | |
| contains('Symbol(_field): 0'), | |
| ), | |
| ); | |
| }); | |
| test('map access', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: ''' | |
| (Map<String, String> params) { | |
| return params["a"]; | |
| }({"a":"b"}) | |
| ''', | |
| expectedResult: 'b', | |
| ); | |
| }); | |
| }); | |
| group('top-level method', () { | |
| test('tear off default constructor', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'C.new.runtimeType.toString()', | |
| expectedResult: '(int, int) => C', | |
| ); | |
| }); | |
| test('call default constructor tear off', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: '(C.new)(0, 0)', | |
| expectedResult: allOf( | |
| contains('test.C.new'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.list): Array(0)'), | |
| contains('Symbol(C.field): 0'), | |
| contains('Symbol(_field): 0'), | |
| ), | |
| ); | |
| }); | |
| test('tear off named constructor', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'C.named.runtimeType.toString()', | |
| expectedResult: '(int) => C', | |
| ); | |
| }); | |
| test('call named constructor tear off', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: '(C.named)(0)', | |
| expectedResult: allOf( | |
| contains('test.C.named'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.list): Array(0)'), | |
| contains('Symbol(C.field): 0'), | |
| contains('Symbol(_field): 42'), | |
| ), | |
| ); | |
| }); | |
| test('tear off redirecting constructor', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'C.redirecting.runtimeType.toString()', | |
| expectedResult: '(int) => C', | |
| ); | |
| }); | |
| test('call redirecting constructor tear off', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: '(C.redirecting)(0)', | |
| expectedResult: allOf( | |
| contains('test.C.redirecting'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.list): Array(0)'), | |
| contains('Symbol(C.field): 0'), | |
| contains('Symbol(_field): 99'), | |
| ), | |
| ); | |
| }); | |
| test('tear off factory constructor', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'C.factory.runtimeType.toString()', | |
| expectedResult: '() => C', | |
| ); | |
| }); | |
| test('call factory constructor tear off', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: '(C.factory)()', | |
| expectedResult: allOf( | |
| contains('test.C.new'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.list): Array(0)'), | |
| contains('Symbol(C.field): 42'), | |
| contains('Symbol(_field): 0'), | |
| ), | |
| ); | |
| }); | |
| }); | |
| group('constructors', () { | |
| test('tear off default constructor', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'C.new.runtimeType.toString()', | |
| expectedResult: '(int, int) => C', | |
| ); | |
| }); | |
| test('call default constructor tear off', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: '(C.new)(0, 0)', | |
| expectedResult: allOf( | |
| contains('test.C.new'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.list): Array(0)'), | |
| contains('Symbol(C.field): 0'), | |
| contains('Symbol(_field): 0'), | |
| ), | |
| ); | |
| }); | |
| test('tear off named constructor', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'C.named.runtimeType.toString()', | |
| expectedResult: '(int) => C', | |
| ); | |
| }); | |
| test('call named constructor tear off', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: '(C.named)(0)', | |
| expectedResult: allOf( | |
| contains('test.C.named'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.list): Array(0)'), | |
| contains('Symbol(C.field): 0'), | |
| contains('Symbol(_field): 42'), | |
| ), | |
| ); | |
| }); | |
| test('tear off redirecting constructor', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'C.redirecting.runtimeType.toString()', | |
| expectedResult: '(int) => C', | |
| ); | |
| }); | |
| test('call redirecting constructor tear off', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: '(C.redirecting)(0)', | |
| expectedResult: allOf( | |
| contains('test.C.redirecting'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.list): Array(0)'), | |
| contains('Symbol(C.field): 0'), | |
| contains('Symbol(_field): 99'), | |
| ), | |
| ); | |
| }); | |
| test('tear off factory constructor', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'C.factory.runtimeType.toString()', | |
| expectedResult: '() => C', | |
| ); | |
| }); | |
| test('call factory constructor tear off', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: '(C.factory)()', | |
| expectedResult: allOf( | |
| contains('test.C.new'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.list): Array(0)'), | |
| contains('Symbol(C.field): 42'), | |
| contains('Symbol(_field): 0'), | |
| ), | |
| ); | |
| }); | |
| }); | |
| group('enums', () { | |
| test('evaluate to the correct string', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'enumBP', | |
| expression: 'E.id2.toString()', | |
| expectedResult: 'E.id2', | |
| ); | |
| }); | |
| test('evaluate to the correct index', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'enumBP', | |
| expression: 'E.id3.index', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| test('compare properly against themselves', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'enumBP', | |
| expression: 'e == E.id2 && E.id2 == E.id2', | |
| expectedResult: 'true', | |
| ); | |
| }); | |
| test('compare properly against other enums', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'enumBP', | |
| expression: 'e != E2.id2 && E.id2 != E2.id2', | |
| expectedResult: 'true', | |
| ); | |
| }); | |
| test('scope', () async { | |
| await driver.checkScope( | |
| breakpointId: 'enumBP', | |
| expectedScope: {'e': 'e'}, | |
| ); | |
| }); | |
| }); | |
| group('late', () { | |
| group('local', () { | |
| test('can be evaluated when initialized', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'lateLocalVariableBP', | |
| expression: 'lateLocal', | |
| expectedResult: '42', | |
| ); | |
| }); | |
| test('does not throw when evaluated and not initialized', () async { | |
| // It isn't clear if this is expected to work or not, the behavior is | |
| // somewhat undefined for the debugger. At this time we expose the | |
| // backing storage variable that can be displayed or might be null if | |
| // uninitialized. | |
| // See https://github.com/dart-lang/sdk/issues/55918 | |
| await driver.checkInFrame( | |
| breakpointId: 'lateLocalVariableBP', | |
| expression: 'lateLocal2', | |
| expectedResult: 'null', | |
| ); | |
| }); | |
| test('throws when not initialized and used in method call', () async { | |
| // It isn't clear if this is expected to work or not, the behavior is | |
| // somewhat undefined for the debugger. At this time we expose the | |
| // backing storage variable that can be displayed or might be null if | |
| // uninitialized. | |
| // See https://github.com/dart-lang/sdk/issues/55918 | |
| await driver.checkInFrame( | |
| breakpointId: 'lateLocalVariableBP', | |
| expression: 'lateLocal2.isEven', | |
| expectedError: | |
| "Error: Property 'isEven' cannot be accessed on " | |
| "'int?' because it is potentially null.", | |
| ); | |
| }); | |
| }); | |
| group('global', () { | |
| test('can be evaluated when initialized', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'lateGlobalVariableBP', | |
| expression: 'lateGlobal', | |
| expectedResult: '42', | |
| ); | |
| }); | |
| test('throws when not initialized', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'lateGlobalVariableBP', | |
| expression: 'lateGlobal2', | |
| expectedError: | |
| 'Error: LateInitializationError: ' | |
| "Field 'lateGlobal2' has not been initialized.", | |
| ); | |
| }); | |
| }); | |
| }); | |
| group('regression', () { | |
| test('don\'t crash on implicit null checks', () async { | |
| // Compiling an expression that contains a method with a non-nullable | |
| // parameter was causing a compiler crash due to the lack of a source | |
| // location and the use of the wrong null literal value. This verifies | |
| // the expression compiler can safely compile this pattern. | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: '((){bool fn(bool b) {return b;} return fn(true);})()', | |
| expectedResult: 'true', | |
| ); | |
| }); | |
| test('don\'t crash on synthetic variables', () async { | |
| // The null aware code in the test source causes the compiler to | |
| // introduce a let statement that includes a synthetic variable | |
| // declaration. That variable has no name and was causing a crash in | |
| // the expression compiler | |
| // https://github.com/dart-lang/sdk/issues/49373. | |
| await driver.checkInFrame( | |
| breakpointId: 'couldReturnNullBP', | |
| expression: 'true', | |
| expectedResult: 'true', | |
| ); | |
| }); | |
| }); | |
| }); | |
| } | |
| /// Shared tests that are valid in legacy (before 2.12) and are agnostic to | |
| /// changes in modern versions of Dart. | |
| /// | |
| /// Tests that exercise language features introduced strictly before 2.12 are | |
| /// valid here. | |
| /// | |
| /// This group of tests has been sharded manually. The others are in | |
| /// [runAgnosticSharedTestsShard2]. | |
| void runAgnosticSharedTestsShard1( | |
| SetupCompilerOptions setup, | |
| ExpressionEvaluationTestDriver driver, | |
| ) { | |
| group('shared source', () { | |
| setUpAll(() async { | |
| await driver.initSource(setup, sharedSource); | |
| }); | |
| tearDownAll(() async { | |
| await driver.cleanupTest(); | |
| }); | |
| group('Correct null safety mode used', () { | |
| test('in original source compilation', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'soundNullSafetyBP', | |
| expression: 'soundNullSafety', | |
| expectedResult: 'true', | |
| ); | |
| }); | |
| test('in expression compilation', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'soundNullSafetyBP', | |
| expression: '!(<Null>[] is List<int>)', | |
| expectedResult: 'true', | |
| ); | |
| }); | |
| }); | |
| group('scope collection', () { | |
| test('local in scope', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'innerScopeBP', | |
| expression: 'inScope', | |
| expectedResult: '1', | |
| ); | |
| }); | |
| test('local in inner scope', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'innerScopeBP', | |
| expression: 'innerInScope', | |
| expectedResult: '48', | |
| ); | |
| }); | |
| test('global in scope', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'innerScopeBP', | |
| expression: 'global', | |
| expectedResult: '42', | |
| ); | |
| }); | |
| test('static field in scope', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'innerScopeBP', | |
| expression: 'staticField', | |
| expectedResult: '1', | |
| ); | |
| }); | |
| test('field in scope', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'innerScopeBP', | |
| expression: 'field', | |
| expectedResult: '5', | |
| ); | |
| }); | |
| test('parameter in scope', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'innerScopeBP', | |
| expression: 'x', | |
| expectedResult: '10', | |
| ); | |
| }); | |
| test('local not in scope', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'innerScopeBP', | |
| expression: 'notInScope', | |
| expectedError: | |
| "DartError: NoSuchMethodError: 'notInScope'\n" | |
| 'method not found\n' | |
| "Receiver: Instance of 'C'\n" | |
| 'Arguments: []\n', | |
| ); | |
| }); | |
| test('local not in inner scope', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'innerScopeBP', | |
| expression: 'innerNotInScope', | |
| expectedError: | |
| "DartError: NoSuchMethodError: 'innerNotInScope'\n" | |
| 'method not found\n' | |
| "Receiver: Instance of 'C'\n" | |
| 'Arguments: []\n', | |
| ); | |
| }); | |
| }); | |
| group('ddc-extension symbols', () { | |
| test('extension symbol used only in expression compilation', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'extensionSymbolsBP', | |
| expression: 'list.first', | |
| expectedResult: '0', | |
| ); | |
| }); | |
| test('extension symbol used in original compilation', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'extensionSymbolsBP', | |
| expression: '() { list.add(1); return list.last; }()', | |
| expectedResult: '1', | |
| ); | |
| }); | |
| }); | |
| group('Expression compiler tests in extension method:', () { | |
| test('compilation error', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'parseIntPlusOneBP', | |
| expression: 'typo', | |
| expectedError: | |
| "DartError: NoSuchMethodError: 'typo'\n" | |
| 'method not found\n' | |
| 'Receiver: "1234"\n' | |
| 'Arguments: []\n', | |
| ); | |
| }); | |
| test('local (trimmed scope)', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'parseIntPlusOneBP', | |
| expression: 'ret', | |
| expectedResult: '1234', | |
| ); | |
| }); | |
| test('this (full scope)', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'parseIntPlusOneBP', | |
| expression: 'this', | |
| expectedResult: '1234', | |
| ); | |
| }); | |
| test('scope', () async { | |
| await driver.checkScope( | |
| breakpointId: 'parseIntPlusOneBP', | |
| expectedScope: {r'$this': '\'1234\'', 'ret': '1234'}, | |
| ); | |
| }); | |
| }); | |
| group('Expression compiler tests in static function:', () { | |
| test('compilation error', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'fooBP', | |
| expression: 'typo', | |
| expectedError: "Undefined name 'typo'", | |
| ); | |
| }); | |
| test('local', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'fooBP', | |
| expression: 'x', | |
| expectedResult: '1', | |
| ); | |
| }); | |
| test('formal', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'fooBP', | |
| expression: 'y', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| test('named formal', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'fooBP', | |
| expression: 'z', | |
| expectedResult: '3', | |
| ); | |
| }); | |
| test('function', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'fooBP', | |
| expression: 'callFooTest', | |
| expectedResult: ''' | |
| function callFooTest() { | |
| return test.foo(1, {y: 2}); | |
| }''', | |
| ); | |
| }); | |
| }); | |
| group('method level', () { | |
| test('compilation error', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'typo', | |
| expectedError: | |
| "DartError: NoSuchMethodError: 'typo'\n" | |
| 'method not found\n' | |
| "Receiver: Instance of 'C'\n" | |
| 'Arguments: []\n', | |
| ); | |
| }); | |
| test('local', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'x', | |
| expectedResult: '10', | |
| ); | |
| }); | |
| test('this', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'this', | |
| expectedResult: allOf( | |
| contains('test.C.new'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.field): 5'), | |
| contains('Symbol(_field): 6'), | |
| ), | |
| ); | |
| }); | |
| test('expression using locals', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'x + 1', | |
| expectedResult: '11', | |
| ); | |
| }); | |
| test('expression using static fields', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'x + staticField', | |
| expectedResult: '11', | |
| ); | |
| }); | |
| test('expression using private static fields', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'x + _staticField', | |
| expectedResult: '12', | |
| ); | |
| }); | |
| test('expression using fields', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'x + field', | |
| expectedResult: '15', | |
| ); | |
| }); | |
| test('expression using private fields', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'x + _field', | |
| expectedResult: '16', | |
| ); | |
| }); | |
| test('expression using globals', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'x + global', | |
| expectedResult: '52', | |
| ); | |
| }); | |
| test( | |
| 'expression using fields not referred to in the original code', | |
| () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: '_unusedField + _unusedStaticField', | |
| expectedResult: '7', | |
| ); | |
| }, | |
| ); | |
| test('private field modification', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: '_field = 2', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| test('field modification', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'field = 3', | |
| expectedResult: '3', | |
| ); | |
| }); | |
| test('private static field modification', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: '_staticFieldB = 4', | |
| expectedResult: '4', | |
| ); | |
| }); | |
| test('static field modification', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'methodBP', | |
| expression: 'staticFieldB = 5', | |
| expectedResult: '5', | |
| ); | |
| }); | |
| }); | |
| group('global function', () { | |
| test('compilation error', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'typo', | |
| expectedError: "Undefined name 'typo'.", | |
| ); | |
| }); | |
| test('local with primitive type', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'x', | |
| expectedResult: '15', | |
| ); | |
| }); | |
| test('local object', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'c', | |
| expectedResult: allOf( | |
| contains('test.C.new'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.field): 5'), | |
| contains('Symbol(_field): 6'), | |
| ), | |
| ); | |
| }); | |
| test('create new object', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'C(3, 4)', | |
| expectedResult: allOf( | |
| contains('test.C.new'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.field): 3'), | |
| contains('Symbol(_field): 4'), | |
| ), | |
| ); | |
| }); | |
| test('access field of new object', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'C(3, 4)._field', | |
| expectedResult: '4', | |
| ); | |
| }); | |
| test('access static field', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'C.staticField', | |
| expectedResult: '1', | |
| ); | |
| }); | |
| test('expression using private static fields', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'C._staticField', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| test('access field', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'c.field', | |
| expectedResult: '5', | |
| ); | |
| }); | |
| test('access private field', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'c._field', | |
| expectedResult: '6', | |
| ); | |
| }); | |
| test('method call', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'c.methodFieldAccess(2)', | |
| expectedResult: '10', | |
| ); | |
| }); | |
| test('async method call', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'c.asyncMethod(2).runtimeType.toString()', | |
| expectedResult: '_Future<int>', | |
| ); | |
| }); | |
| test('extension method call', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: '"1234".parseIntPlusOne()', | |
| expectedResult: '1235', | |
| ); | |
| }); | |
| test('private field modification', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'c._field = 10', | |
| expectedResult: '10', | |
| ); | |
| }); | |
| test('field modification', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'c._field = 11', | |
| expectedResult: '11', | |
| ); | |
| }); | |
| test('private static field modification', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'C._staticField = 2', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| test('static field modification', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'C.staticFieldC = 20', | |
| expectedResult: '20', | |
| ); | |
| }); | |
| test('call global function from core library', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'globalFunctionBP', | |
| expression: 'identical(1, 1)', | |
| expectedResult: 'true', | |
| ); | |
| }); | |
| }); | |
| group('async function', () { | |
| test('local variable', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'asyncClosureBP0', | |
| expression: 'test', | |
| expectedResult: '0', | |
| ); | |
| await driver.checkInFrame( | |
| breakpointId: 'asyncClosureBP1', | |
| expression: 'test', | |
| expectedResult: '1', | |
| ); | |
| await driver.checkInFrame( | |
| breakpointId: 'asyncClosureBP2', | |
| expression: 'test', | |
| expectedResult: '2', | |
| ); | |
| await driver.checkInFrame( | |
| breakpointId: 'asyncClosureBP3', | |
| expression: 'notTest', | |
| expectedError: "Undefined name 'notTest'", | |
| ); | |
| await driver.checkInFrame( | |
| breakpointId: 'asyncClosureBP4', | |
| expression: 'unused', | |
| expectedError: 'Value not found in scope', | |
| ); | |
| }); | |
| }); | |
| }); | |
| } | |
| /// Shared tests that are valid in legacy (before 2.12) and are agnostic to | |
| /// changes in modern versions of Dart. | |
| /// | |
| /// Tests that exercise language features introduced strictly before 2.12 are | |
| /// valid here. | |
| /// | |
| /// This group of tests has been sharded manually. The others are in | |
| /// [runAgnosticSharedTestsShard1]. | |
| void runAgnosticSharedTestsShard2( | |
| SetupCompilerOptions setup, | |
| ExpressionEvaluationTestDriver driver, | |
| ) { | |
| group('shared source', () { | |
| setUpAll(() async { | |
| await driver.initSource(setup, sharedSource); | |
| }); | |
| tearDownAll(() async { | |
| await driver.cleanupTest(); | |
| }); | |
| group('constructor', () { | |
| test('compilation error', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'typo', | |
| expectedError: | |
| "DartError: NoSuchMethodError: 'typo'\n" | |
| 'method not found\n' | |
| "Receiver: Instance of 'C'\n" | |
| 'Arguments: []\n', | |
| ); | |
| }); | |
| test('local', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'y', | |
| expectedResult: '1', | |
| ); | |
| }); | |
| test('this', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'this', | |
| expectedResult: allOf( | |
| contains('test.C.new'), | |
| contains('Symbol(_unusedField): 4'), | |
| contains('Symbol(C.field): 5'), | |
| contains('Symbol(_field): 6'), | |
| ), | |
| ); | |
| }); | |
| test('expression using locals', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'y + 1', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| test('expression using static fields', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'y + staticField', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| test('expression using private static fields', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'y + _staticField', | |
| expectedResult: '3', | |
| ); | |
| }); | |
| test('expression using fields', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'y + field', | |
| expectedResult: '6', | |
| ); | |
| }); | |
| test('expression using private fields', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'y + _field', | |
| expectedResult: '7', | |
| ); | |
| }); | |
| test('expression using globals', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'y + global', | |
| expectedResult: '43', | |
| ); | |
| }); | |
| test('method call', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'methodFieldAccess(2)', | |
| expectedResult: '10', | |
| ); | |
| }); | |
| test('async method call', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'asyncMethod(2).runtimeType.toString()', | |
| expectedResult: '_Future<int>', | |
| ); | |
| }); | |
| test('extension method call', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: '"1234".parseIntPlusOne()', | |
| expectedResult: '1235', | |
| ); | |
| }); | |
| test('private field modification', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: '_field = 2', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| test('field modification', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'field = 2', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| test('private static field modification', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: '_staticField = 2', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| test('static field modification', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'constructorBP', | |
| expression: 'staticFieldD = 2', | |
| expectedResult: '2', | |
| ); | |
| }); | |
| }); | |
| group('async methods', () { | |
| test('compilation error', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'asyncTestBP1', | |
| expression: 'typo', | |
| expectedError: | |
| "DartError: NoSuchMethodError: 'typo'\n" | |
| 'method not found\n' | |
| "Receiver: Instance of 'D'\n" | |
| 'Arguments: []\n', | |
| ); | |
| }); | |
| test('local', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'asyncTestBP1', | |
| expression: 'x', | |
| expectedResult: '1', | |
| ); | |
| }); | |
| test('this', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'asyncTestBP1', | |
| expression: 'this', | |
| expectedResult: allOf( | |
| contains('test.D.new'), | |
| contains('Symbol(D.field): 5'), | |
| contains('Symbol(_field): 7'), | |
| ), | |
| ); | |
| }); | |
| test('awaited method call', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'asyncTestBP2', | |
| expression: 'd.asyncMethod(1).runtimeType.toString()', | |
| expectedResult: '_Future<int>', | |
| ); | |
| }, skip: "'await' is not yet supported in expression evaluation."); | |
| test('awaited method call', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'asyncTestBP2', | |
| expression: 'await d.asyncMethod(1)', | |
| expectedResult: '58', | |
| ); | |
| }, skip: "'await' is not yet supported in expression evaluation."); | |
| }); | |
| group('closures', () { | |
| test('compilation error', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'closuresTestBP', | |
| expression: 'typo', | |
| expectedError: "Undefined name 'typo'.", | |
| ); | |
| }); | |
| test('expression using captured variables', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'closuresTestBP', | |
| expression: r"'$y+$z'", | |
| expectedResult: '3+0', | |
| ); | |
| }); | |
| test('expression using uncaptured variables', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'closuresTestBP', | |
| expression: r"'$x+$y+$z'", | |
| expectedResult: '15+3+0', | |
| ); | |
| }); | |
| }); | |
| group('method not already loading the types needed', () { | |
| test('call function not using type', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'missingTypesTestBP', | |
| expression: 'bar(p)', | |
| expectedResult: '1', | |
| ); | |
| }); | |
| test('call function using type', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'missingTypesTestBP', | |
| expression: "baz('\$p')", | |
| expectedResult: '1', | |
| ); | |
| }); | |
| test('evaluate new const expression', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'missingTypesTestBP', | |
| expression: 'const MyClass(1)', | |
| expectedResult: 'MyClass {Symbol(MyClass._t): 1}', | |
| ); | |
| }); | |
| test( | |
| 'evaluate optimized const expression', | |
| () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'missingTypesTestBP', | |
| expression: 't', | |
| expectedResult: '1', | |
| ); | |
| }, | |
| skip: | |
| 'Cannot compile constants optimized away by the frontend. ' | |
| 'Issue: https://github.com/dart-lang/sdk/issues/41999', | |
| ); | |
| test('evaluate factory constructor call', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'missingTypesTestBP', | |
| expression: "Key('t')", | |
| expectedResult: 'test.ValueKey.new {Symbol(ValueKey.value): t}', | |
| ); | |
| }); | |
| test('evaluate const factory constructor call', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'missingTypesTestBP', | |
| expression: "const Key('t')", | |
| expectedResult: 'ValueKey {Symbol(ValueKey.value): t}', | |
| ); | |
| }); | |
| }); | |
| group('simple loops', () { | |
| // Caution: this breakpoint should not be reused across multiple test | |
| // cases to prevent data races. See | |
| // http://github.com/dart-lang/sdk/issues/55299 for details. | |
| test('expression using local & loop var', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'forLoopTestBP', | |
| expression: r'"$x + $i"', | |
| expectedResult: '15 + 0', | |
| ); | |
| }); | |
| }); | |
| group('conditional:', () { | |
| test('(then) expression using local', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'thenBP', | |
| expression: 'y', | |
| expectedResult: '3', | |
| ); | |
| }); | |
| test('(then) expression using local out of scope', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'thenBP', | |
| expression: 'z', | |
| expectedError: "Error: Undefined name 'z'", | |
| ); | |
| }); | |
| test('(else) expression using local', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'elseBP', | |
| expression: 'z', | |
| expectedResult: '4', | |
| ); | |
| }); | |
| test('(else) expression using local out of scope', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'elseBP', | |
| expression: 'y', | |
| expectedError: "Error: Undefined name 'y'", | |
| ); | |
| }); | |
| test('(post) expression using local', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'postBP', | |
| expression: 'x', | |
| expectedResult: '1', | |
| ); | |
| }); | |
| test('(post) expression using local out of scope', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'postBP', | |
| expression: 'z', | |
| expectedError: "Error: Undefined name 'z'", | |
| ); | |
| }); | |
| test('(post) expression using local out of scope', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'postBP', | |
| expression: 'y', | |
| expectedError: "Error: Undefined name 'y'", | |
| ); | |
| }); | |
| }); | |
| group('iterator loops', () { | |
| // Caution: this breakpoint should not be reused across multiple test | |
| // cases to prevent data races. See | |
| // http://github.com/dart-lang/sdk/issues/55299 for details. | |
| test('expression loop variable', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'iteratorLoopTestBP', | |
| expression: 'e', | |
| expectedResult: '1', | |
| ); | |
| }); | |
| }); | |
| group('generic method', () { | |
| test('evaluate formals', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'genericBP', | |
| expression: "'\${a} \$b'", | |
| expectedResult: '0 hi', | |
| ); | |
| }); | |
| test('evaluate class type parameters', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'genericBP', | |
| expression: "'\$T1'", | |
| expectedResult: 'int', | |
| ); | |
| }); | |
| test('evaluate method type parameters', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'genericBP', | |
| expression: "'\$T2'", | |
| expectedResult: 'String', | |
| ); | |
| }); | |
| }); | |
| group('interactions with module containers', () { | |
| test( | |
| 'evaluation that non-destructively appends to the type container', | |
| () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'moduleContainersBP', | |
| expression: 'a is String', | |
| expectedResult: 'false', | |
| ); | |
| }, | |
| ); | |
| test('evaluation that reuses the type container', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'moduleContainersBP', | |
| expression: 'a is int', | |
| expectedResult: 'false', | |
| ); | |
| }); | |
| test( | |
| 'evaluation that non-destructively appends to the constant container', | |
| () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'moduleContainersBP', | |
| expression: 'const M2() == const M2()', | |
| expectedResult: 'true', | |
| ); | |
| }, | |
| ); | |
| test('evaluation that properly canonicalizes constants', () async { | |
| await driver.checkInFrame( | |
| breakpointId: 'moduleContainersBP', | |
| expression: 'a == const M1()', | |
| expectedResult: 'true', | |
| ); | |
| }); | |
| }); | |
| }); | |
| } |