Skip to content

Add tests covering the ability to debug dot shorthands #2636

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dwds/test/instances/common/class_inspection_common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ void runTests({
compilationMode: compilationMode,
enableExpressionEvaluation: true,
verboseCompiler: debug,
experiments: ['dot-shorthands'],
canaryFeatures: canaryFeatures,
moduleFormat: provider.ddcModuleFormat,
),
Expand Down
129 changes: 129 additions & 0 deletions dwds/test/instances/common/dot_shorthands_common.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright (c) 2025, 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:dwds/src/services/expression_compiler.dart' show ModuleFormat;
import 'package:path/path.dart' show basename;
import 'package:test/test.dart';
import 'package:test_common/logging.dart';
import 'package:test_common/test_sdk_configuration.dart';
import 'package:vm_service/vm_service.dart';

import '../../fixtures/context.dart';
import '../../fixtures/project.dart';
import '../../fixtures/utilities.dart';
import 'test_inspector.dart';

void runTests({
required TestSdkConfigurationProvider provider,
required CompilationMode compilationMode,
required bool canaryFeatures,
required bool debug,
}) {
final context = TestContext(TestProject.testExperiment, provider);
final testInspector = TestInspector(context);

late VmService service;
late Stream<Event> stream;
late String isolateId;
late ScriptRef mainScript;

Future<void> onBreakpoint(
String breakPointId,
Future<void> Function(Event) body,
) => testInspector.onBreakPoint(
stream,
isolateId,
mainScript,
breakPointId,
body,
);

Future<InstanceRef> getInstanceRef(frame, expression) =>
testInspector.getInstanceRef(isolateId, frame, expression);

group('$compilationMode | dot shorthands:', () {
setUp(() async {
setCurrentLogWriter(debug: debug);
await context.setUp(
testSettings: TestSettings(
compilationMode: compilationMode,
enableExpressionEvaluation: true,
verboseCompiler: debug,
experiments: ['dot-shorthands'],
canaryFeatures: canaryFeatures,
moduleFormat: provider.ddcModuleFormat,
),
);
service = context.debugConnection.vmService;

final vm = await service.getVM();
isolateId = vm.isolates!.first.id!;
final scripts = await service.getScripts(isolateId);

await service.streamListen(EventStreams.kDebug);
stream = service.onDebugEvent;

mainScript = scripts.scripts!.firstWhere(
(each) => each.uri!.contains('main.dart'),
);
});

tearDown(() async {
await context.tearDown();
});

test('expression evaluation and single-stepping', () async {
await onBreakpoint('testDotShorthands', (event) async {
final frame = event.topFrame!.index!;

var instanceRef = await getInstanceRef(frame, '(c = .two).value');
expect(instanceRef.valueAsString, '2');

instanceRef = await getInstanceRef(frame, '(c = .three).value');
expect(instanceRef.valueAsString, '3');

instanceRef = await getInstanceRef(frame, '(c = .four()).value');
expect(instanceRef.valueAsString, '4');

final scriptBasename = basename(mainScript.uri!);

const lineA = 116;
const lineB = 118;
const lineC = 119;
const lineD = 120;
const lineE = 127;
const lineF = 129;
const lineG = 131;
const lineH = 132;

final expected = [
'$scriptBasename:$lineE:3', // on 'c'
// TODO(2638): Investigate why this conditional exclusion is needed.
if (provider.ddcModuleFormat == ModuleFormat.ddc)
'$scriptBasename:$lineB:20', // on '2'
'$scriptBasename:$lineF:3', // on 'c'
'$scriptBasename:$lineC:25', // on 'C'
'$scriptBasename:$lineA:10', // on 'v' of 'value'
'$scriptBasename:$lineA:16', // on ';'
'$scriptBasename:$lineC:27', // on '3'
'$scriptBasename:$lineG:3', // on 'c'
'$scriptBasename:$lineD:22', // on 'C'
'$scriptBasename:$lineA:10', // on 'v' of 'value'
'$scriptBasename:$lineA:16', // on ';'
'$scriptBasename:$lineD:24', // on '4'
'$scriptBasename:$lineH:3', // on 'p' of 'print'
];

final stops = <String>[];
await testInspector.runStepIntoThroughProgramRecordingStops(
isolateId,
stops,
provider.ddcModuleFormat == ModuleFormat.ddc ? 13 : 12,
);

expect(stops, expected);
});
});
});
}
2 changes: 1 addition & 1 deletion dwds/test/instances/common/patterns_inspection_common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ void runTests({
compilationMode: compilationMode,
enableExpressionEvaluation: true,
verboseCompiler: debug,
experiments: ['records', 'patterns'],
experiments: ['dot-shorthands'],
canaryFeatures: canaryFeatures,
),
);
Expand Down
2 changes: 1 addition & 1 deletion dwds/test/instances/common/record_inspection_common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ void runTests({
compilationMode: compilationMode,
enableExpressionEvaluation: true,
verboseCompiler: debug,
experiments: ['records', 'patterns'],
experiments: ['dot-shorthands'],
canaryFeatures: canaryFeatures,
moduleFormat: provider.ddcModuleFormat,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ void runTests({
compilationMode: compilationMode,
enableExpressionEvaluation: true,
verboseCompiler: debug,
experiments: ['records', 'patterns'],
experiments: ['dot-shorthands'],
canaryFeatures: canaryFeatures,
moduleFormat: provider.ddcModuleFormat,
),
Expand Down
53 changes: 53 additions & 0 deletions dwds/test/instances/common/test_inspector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// 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 'dart:async' show Completer, StreamSubscription;

import 'package:path/path.dart' show basename;
import 'package:test/test.dart';
import 'package:vm_service/vm_service.dart';

Expand Down Expand Up @@ -214,6 +217,56 @@ class TestInspector {
),
);
}

Future<String> _locationToString(
VmService service,
String isolateId,
SourceLocation location,
) async {
final script =
await service.getObject(isolateId, location.script!.id!) as Script;
final scriptName = basename(script.uri!);
final tokenPos = location.tokenPos!;
final line = script.getLineNumberFromTokenPos(tokenPos);
final column = script.getColumnNumberFromTokenPos(tokenPos);
return '$scriptName:$line:$column';
}

Future<void> runStepIntoThroughProgramRecordingStops(
String isolateId,

/// A list to which the pause location is added after each single-step.
List<String> recordedStops,

/// A limit on the number of stops to record.
///
/// The program will not be resumed after the length of [recordedStops]
/// becomes [numStops].
int numStops,
) async {
final completer = Completer<void>();

late StreamSubscription subscription;
subscription = service.onDebugEvent.listen((event) async {
if (event.kind == EventKind.kPauseInterrupted) {
final isolate = await service.getIsolate(isolateId);
final frame = isolate.pauseEvent!.topFrame!;
recordedStops.add(
await _locationToString(service, isolateId, frame.location!),
);
if (recordedStops.length == numStops) {
await subscription.cancel();
completer.complete();
}
await service.resume(isolateId, step: StepOption.kInto);
} else if (event.kind == EventKind.kPauseExit) {
await subscription.cancel();
completer.complete();
}
});
await service.resume(isolateId, step: StepOption.kInto);
await completer.future;
}
}

Map<String, InstanceRef> _associationsToMap(
Expand Down
2 changes: 1 addition & 1 deletion dwds/test/instances/common/type_inspection_common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ void runTests({
compilationMode: compilationMode,
enableExpressionEvaluation: true,
verboseCompiler: debug,
experiments: ['records', 'patterns'],
experiments: ['dot-shorthands'],
canaryFeatures: canaryFeatures,
),
);
Expand Down
39 changes: 39 additions & 0 deletions dwds/test/instances/dot_shorthands_amd_canary_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) 2025, 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.

@Tags(['daily'])
@TestOn('vm')
@Timeout(Duration(minutes: 2))
library;

import 'package:dwds/expression_compiler.dart';
import 'package:test/test.dart';
import 'package:test_common/test_sdk_configuration.dart';

import '../fixtures/context.dart';
import 'common/dot_shorthands_common.dart';

void main() {
// Enable verbose logging for debugging.
const debug = false;
const canaryFeatures = true;

group('canary: $canaryFeatures |', () {
final provider = TestSdkConfigurationProvider(
verbose: debug,
canaryFeatures: canaryFeatures,
ddcModuleFormat: ModuleFormat.amd,
);
tearDownAll(provider.dispose);

for (final compilationMode in CompilationMode.values) {
runTests(
provider: provider,
compilationMode: compilationMode,
canaryFeatures: canaryFeatures,
debug: debug,
);
}
});
}
39 changes: 39 additions & 0 deletions dwds/test/instances/dot_shorthands_amd_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) 2025, 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.

@Tags(['daily'])
@TestOn('vm')
@Timeout(Duration(minutes: 2))
library;

import 'package:dwds/src/services/expression_compiler.dart';
import 'package:test/test.dart';
import 'package:test_common/test_sdk_configuration.dart';

import '../fixtures/context.dart';
import 'common/dot_shorthands_common.dart';

void main() {
// Enable verbose logging for debugging.
const debug = false;
const canaryFeatures = false;

group('canary: $canaryFeatures |', () {
final provider = TestSdkConfigurationProvider(
verbose: debug,
canaryFeatures: canaryFeatures,
ddcModuleFormat: ModuleFormat.amd,
);
tearDownAll(provider.dispose);

for (final compilationMode in CompilationMode.values) {
runTests(
provider: provider,
compilationMode: compilationMode,
canaryFeatures: canaryFeatures,
debug: debug,
);
}
});
}
37 changes: 37 additions & 0 deletions dwds/test/instances/dot_shorthands_ddc_library_bundle_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) 2025, 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.

@Tags(['daily'])
@TestOn('vm')
@Timeout(Duration(minutes: 2))
library;

import 'package:dwds/expression_compiler.dart';
import 'package:test/test.dart';
import 'package:test_common/test_sdk_configuration.dart';

import '../fixtures/context.dart';
import 'common/dot_shorthands_common.dart';

void main() {
// Enable verbose logging for debugging.
const debug = false;
const canaryFeatures = true;
const compilationMode = CompilationMode.frontendServer;

group('canary: $canaryFeatures |', () {
final provider = TestSdkConfigurationProvider(
verbose: debug,
canaryFeatures: canaryFeatures,
ddcModuleFormat: ModuleFormat.ddc,
);
tearDownAll(provider.dispose);
runTests(
provider: provider,
compilationMode: compilationMode,
canaryFeatures: canaryFeatures,
debug: debug,
);
});
}
Loading
Loading