Skip to content

Commit f9f8e13

Browse files
authored
Add tests covering the ability to debug dot shorthands (#2636)
This PR adds tests that ensure that expression evaluation and single-stepping interact with dot shorthands as expected.
1 parent 2172ba7 commit f9f8e13

12 files changed

+329
-4
lines changed

dwds/test/instances/common/class_inspection_common.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ void runTests({
4949
compilationMode: compilationMode,
5050
enableExpressionEvaluation: true,
5151
verboseCompiler: debug,
52+
experiments: ['dot-shorthands'],
5253
canaryFeatures: canaryFeatures,
5354
moduleFormat: provider.ddcModuleFormat,
5455
),
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:dwds/src/services/expression_compiler.dart' show ModuleFormat;
6+
import 'package:path/path.dart' show basename;
7+
import 'package:test/test.dart';
8+
import 'package:test_common/logging.dart';
9+
import 'package:test_common/test_sdk_configuration.dart';
10+
import 'package:vm_service/vm_service.dart';
11+
12+
import '../../fixtures/context.dart';
13+
import '../../fixtures/project.dart';
14+
import '../../fixtures/utilities.dart';
15+
import 'test_inspector.dart';
16+
17+
void runTests({
18+
required TestSdkConfigurationProvider provider,
19+
required CompilationMode compilationMode,
20+
required bool canaryFeatures,
21+
required bool debug,
22+
}) {
23+
final context = TestContext(TestProject.testExperiment, provider);
24+
final testInspector = TestInspector(context);
25+
26+
late VmService service;
27+
late Stream<Event> stream;
28+
late String isolateId;
29+
late ScriptRef mainScript;
30+
31+
Future<void> onBreakpoint(
32+
String breakPointId,
33+
Future<void> Function(Event) body,
34+
) => testInspector.onBreakPoint(
35+
stream,
36+
isolateId,
37+
mainScript,
38+
breakPointId,
39+
body,
40+
);
41+
42+
Future<InstanceRef> getInstanceRef(frame, expression) =>
43+
testInspector.getInstanceRef(isolateId, frame, expression);
44+
45+
group('$compilationMode | dot shorthands:', () {
46+
setUp(() async {
47+
setCurrentLogWriter(debug: debug);
48+
await context.setUp(
49+
testSettings: TestSettings(
50+
compilationMode: compilationMode,
51+
enableExpressionEvaluation: true,
52+
verboseCompiler: debug,
53+
experiments: ['dot-shorthands'],
54+
canaryFeatures: canaryFeatures,
55+
moduleFormat: provider.ddcModuleFormat,
56+
),
57+
);
58+
service = context.debugConnection.vmService;
59+
60+
final vm = await service.getVM();
61+
isolateId = vm.isolates!.first.id!;
62+
final scripts = await service.getScripts(isolateId);
63+
64+
await service.streamListen(EventStreams.kDebug);
65+
stream = service.onDebugEvent;
66+
67+
mainScript = scripts.scripts!.firstWhere(
68+
(each) => each.uri!.contains('main.dart'),
69+
);
70+
});
71+
72+
tearDown(() async {
73+
await context.tearDown();
74+
});
75+
76+
test('expression evaluation and single-stepping', () async {
77+
await onBreakpoint('testDotShorthands', (event) async {
78+
final frame = event.topFrame!.index!;
79+
80+
var instanceRef = await getInstanceRef(frame, '(c = .two).value');
81+
expect(instanceRef.valueAsString, '2');
82+
83+
instanceRef = await getInstanceRef(frame, '(c = .three).value');
84+
expect(instanceRef.valueAsString, '3');
85+
86+
instanceRef = await getInstanceRef(frame, '(c = .four()).value');
87+
expect(instanceRef.valueAsString, '4');
88+
89+
final scriptBasename = basename(mainScript.uri!);
90+
91+
const lineA = 116;
92+
const lineB = 118;
93+
const lineC = 119;
94+
const lineD = 120;
95+
const lineE = 127;
96+
const lineF = 129;
97+
const lineG = 131;
98+
const lineH = 132;
99+
100+
final expected = [
101+
'$scriptBasename:$lineE:3', // on 'c'
102+
// TODO(2638): Investigate why this conditional exclusion is needed.
103+
if (provider.ddcModuleFormat == ModuleFormat.ddc)
104+
'$scriptBasename:$lineB:20', // on '2'
105+
'$scriptBasename:$lineF:3', // on 'c'
106+
'$scriptBasename:$lineC:25', // on 'C'
107+
'$scriptBasename:$lineA:10', // on 'v' of 'value'
108+
'$scriptBasename:$lineA:16', // on ';'
109+
'$scriptBasename:$lineC:27', // on '3'
110+
'$scriptBasename:$lineG:3', // on 'c'
111+
'$scriptBasename:$lineD:22', // on 'C'
112+
'$scriptBasename:$lineA:10', // on 'v' of 'value'
113+
'$scriptBasename:$lineA:16', // on ';'
114+
'$scriptBasename:$lineD:24', // on '4'
115+
'$scriptBasename:$lineH:3', // on 'p' of 'print'
116+
];
117+
118+
final stops = <String>[];
119+
await testInspector.runStepIntoThroughProgramRecordingStops(
120+
isolateId,
121+
stops,
122+
provider.ddcModuleFormat == ModuleFormat.ddc ? 13 : 12,
123+
);
124+
125+
expect(stops, expected);
126+
});
127+
});
128+
});
129+
}

dwds/test/instances/common/patterns_inspection_common.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ void runTests({
5757
compilationMode: compilationMode,
5858
enableExpressionEvaluation: true,
5959
verboseCompiler: debug,
60-
experiments: ['records', 'patterns'],
60+
experiments: ['dot-shorthands'],
6161
canaryFeatures: canaryFeatures,
6262
),
6363
);

dwds/test/instances/common/record_inspection_common.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ void runTests({
6363
compilationMode: compilationMode,
6464
enableExpressionEvaluation: true,
6565
verboseCompiler: debug,
66-
experiments: ['records', 'patterns'],
66+
experiments: ['dot-shorthands'],
6767
canaryFeatures: canaryFeatures,
6868
moduleFormat: provider.ddcModuleFormat,
6969
),

dwds/test/instances/common/record_type_inspection_common.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ void runTests({
6262
compilationMode: compilationMode,
6363
enableExpressionEvaluation: true,
6464
verboseCompiler: debug,
65-
experiments: ['records', 'patterns'],
65+
experiments: ['dot-shorthands'],
6666
canaryFeatures: canaryFeatures,
6767
moduleFormat: provider.ddcModuleFormat,
6868
),

dwds/test/instances/common/test_inspector.dart

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'dart:async' show Completer, StreamSubscription;
6+
7+
import 'package:path/path.dart' show basename;
58
import 'package:test/test.dart';
69
import 'package:vm_service/vm_service.dart';
710

@@ -214,6 +217,56 @@ class TestInspector {
214217
),
215218
);
216219
}
220+
221+
Future<String> _locationToString(
222+
VmService service,
223+
String isolateId,
224+
SourceLocation location,
225+
) async {
226+
final script =
227+
await service.getObject(isolateId, location.script!.id!) as Script;
228+
final scriptName = basename(script.uri!);
229+
final tokenPos = location.tokenPos!;
230+
final line = script.getLineNumberFromTokenPos(tokenPos);
231+
final column = script.getColumnNumberFromTokenPos(tokenPos);
232+
return '$scriptName:$line:$column';
233+
}
234+
235+
Future<void> runStepIntoThroughProgramRecordingStops(
236+
String isolateId,
237+
238+
/// A list to which the pause location is added after each single-step.
239+
List<String> recordedStops,
240+
241+
/// A limit on the number of stops to record.
242+
///
243+
/// The program will not be resumed after the length of [recordedStops]
244+
/// becomes [numStops].
245+
int numStops,
246+
) async {
247+
final completer = Completer<void>();
248+
249+
late StreamSubscription subscription;
250+
subscription = service.onDebugEvent.listen((event) async {
251+
if (event.kind == EventKind.kPauseInterrupted) {
252+
final isolate = await service.getIsolate(isolateId);
253+
final frame = isolate.pauseEvent!.topFrame!;
254+
recordedStops.add(
255+
await _locationToString(service, isolateId, frame.location!),
256+
);
257+
if (recordedStops.length == numStops) {
258+
await subscription.cancel();
259+
completer.complete();
260+
}
261+
await service.resume(isolateId, step: StepOption.kInto);
262+
} else if (event.kind == EventKind.kPauseExit) {
263+
await subscription.cancel();
264+
completer.complete();
265+
}
266+
});
267+
await service.resume(isolateId, step: StepOption.kInto);
268+
await completer.future;
269+
}
217270
}
218271

219272
Map<String, InstanceRef> _associationsToMap(

dwds/test/instances/common/type_inspection_common.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ void runTests({
8080
compilationMode: compilationMode,
8181
enableExpressionEvaluation: true,
8282
verboseCompiler: debug,
83-
experiments: ['records', 'patterns'],
83+
experiments: ['dot-shorthands'],
8484
canaryFeatures: canaryFeatures,
8585
),
8686
);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
@Tags(['daily'])
6+
@TestOn('vm')
7+
@Timeout(Duration(minutes: 2))
8+
library;
9+
10+
import 'package:dwds/expression_compiler.dart';
11+
import 'package:test/test.dart';
12+
import 'package:test_common/test_sdk_configuration.dart';
13+
14+
import '../fixtures/context.dart';
15+
import 'common/dot_shorthands_common.dart';
16+
17+
void main() {
18+
// Enable verbose logging for debugging.
19+
const debug = false;
20+
const canaryFeatures = true;
21+
22+
group('canary: $canaryFeatures |', () {
23+
final provider = TestSdkConfigurationProvider(
24+
verbose: debug,
25+
canaryFeatures: canaryFeatures,
26+
ddcModuleFormat: ModuleFormat.amd,
27+
);
28+
tearDownAll(provider.dispose);
29+
30+
for (final compilationMode in CompilationMode.values) {
31+
runTests(
32+
provider: provider,
33+
compilationMode: compilationMode,
34+
canaryFeatures: canaryFeatures,
35+
debug: debug,
36+
);
37+
}
38+
});
39+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
@Tags(['daily'])
6+
@TestOn('vm')
7+
@Timeout(Duration(minutes: 2))
8+
library;
9+
10+
import 'package:dwds/src/services/expression_compiler.dart';
11+
import 'package:test/test.dart';
12+
import 'package:test_common/test_sdk_configuration.dart';
13+
14+
import '../fixtures/context.dart';
15+
import 'common/dot_shorthands_common.dart';
16+
17+
void main() {
18+
// Enable verbose logging for debugging.
19+
const debug = false;
20+
const canaryFeatures = false;
21+
22+
group('canary: $canaryFeatures |', () {
23+
final provider = TestSdkConfigurationProvider(
24+
verbose: debug,
25+
canaryFeatures: canaryFeatures,
26+
ddcModuleFormat: ModuleFormat.amd,
27+
);
28+
tearDownAll(provider.dispose);
29+
30+
for (final compilationMode in CompilationMode.values) {
31+
runTests(
32+
provider: provider,
33+
compilationMode: compilationMode,
34+
canaryFeatures: canaryFeatures,
35+
debug: debug,
36+
);
37+
}
38+
});
39+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
@Tags(['daily'])
6+
@TestOn('vm')
7+
@Timeout(Duration(minutes: 2))
8+
library;
9+
10+
import 'package:dwds/expression_compiler.dart';
11+
import 'package:test/test.dart';
12+
import 'package:test_common/test_sdk_configuration.dart';
13+
14+
import '../fixtures/context.dart';
15+
import 'common/dot_shorthands_common.dart';
16+
17+
void main() {
18+
// Enable verbose logging for debugging.
19+
const debug = false;
20+
const canaryFeatures = true;
21+
const compilationMode = CompilationMode.frontendServer;
22+
23+
group('canary: $canaryFeatures |', () {
24+
final provider = TestSdkConfigurationProvider(
25+
verbose: debug,
26+
canaryFeatures: canaryFeatures,
27+
ddcModuleFormat: ModuleFormat.ddc,
28+
);
29+
tearDownAll(provider.dispose);
30+
runTests(
31+
provider: provider,
32+
compilationMode: compilationMode,
33+
canaryFeatures: canaryFeatures,
34+
debug: debug,
35+
);
36+
});
37+
}

0 commit comments

Comments
 (0)