blob: 0c2ef265ab3aeb08fb7aad29e48b14e7bb625e1b [file] [log] [blame]
// 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:
"Error: The getter 'notInScope' isn't defined for the"
" type 'C'.",
);
});
test('local not in inner scope', () async {
await driver.checkInFrame(
breakpointId: 'innerScopeBP',
expression: 'innerNotInScope',
expectedError:
"Error: The getter 'innerNotInScope' isn't defined for the"
" type 'C'.",
);
});
});
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: "Error: The getter 'typo' isn't defined",
);
});
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: "The getter 'typo' isn't defined for the type 'C'",
);
});
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: "The getter 'typo' isn't defined for the type 'C'",
);
});
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: "The getter 'typo' isn't defined for the type 'D'",
);
});
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',
);
});
});
});
}