From 446566f65695ff169aa9be6d7365c34eae1d82f6 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Tue, 14 Mar 2023 09:11:50 -0700 Subject: [PATCH 1/6] Temp --- dwds/lib/src/debugging/debugger.dart | 11 ++++++++++- dwds/lib/src/debugging/location.dart | 18 ++++++++++++++++++ fixtures/_webdevSoundSmoke/web/main.dart | 13 +++++++++++++ webdev/lib/src/pubspec.dart | 1 + 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/dwds/lib/src/debugging/debugger.dart b/dwds/lib/src/debugging/debugger.dart index c8e313d9a..732a4e5cc 100644 --- a/dwds/lib/src/debugging/debugger.dart +++ b/dwds/lib/src/debugging/debugger.dart @@ -83,6 +83,7 @@ class Debugger extends Domain { FrameComputer? stackComputer; bool _isStepping = false; + DartLocation? _previousSteppingLocation; void updateInspector(AppInspectorInterface appInspector) { inspector = appInspector; @@ -143,6 +144,7 @@ class Debugger extends Domain { } } else { _isStepping = false; + _previousSteppingLocation = null; result = await _remoteDebugger.resume(); } handleErrorIfPresent(result); @@ -354,7 +356,14 @@ class Debugger extends Domain { final url = urlForScriptId(scriptId); if (url == null) return null; - return _locations.locationForJs(url, line, column); + final loc = await _locations.locationForJs(url, line, column); + if (loc == null || loc.dartLocation == _previousSteppingLocation) { + return null; + } + logger.severe('Previous location: $_previousSteppingLocation'); + logger.severe('Location: $loc'); + _previousSteppingLocation = loc.dartLocation; + return loc; } /// Returns script ID for the paused event. diff --git a/dwds/lib/src/debugging/location.dart b/dwds/lib/src/debugging/location.dart index e0e0fc633..eaf524384 100644 --- a/dwds/lib/src/debugging/location.dart +++ b/dwds/lib/src/debugging/location.dart @@ -74,6 +74,24 @@ class DartLocation { return result == 0 ? column.compareTo(otherColumn) : result; } + @override + int get hashCode => Object.hashAll([uri, line, column]); + + @override + bool operator ==(Object? other) { + if (other is! DartLocation) { + return false; + } + print('Compare locations: $this, $other'); + final result = uri.serverPath == other.uri.serverPath && + line == other.line && + column == other.column; + print('Locations are the same: $result'); + return uri.serverPath == other.uri.serverPath && + line == other.line && + column == other.column; + } + @override String toString() => '[${uri.serverPath}:$line:$column]'; diff --git a/fixtures/_webdevSoundSmoke/web/main.dart b/fixtures/_webdevSoundSmoke/web/main.dart index 907bfdbba..a8a519203 100644 --- a/fixtures/_webdevSoundSmoke/web/main.dart +++ b/fixtures/_webdevSoundSmoke/web/main.dart @@ -19,5 +19,18 @@ void main() { var count = 0; Timer.periodic(const Duration(seconds: 1), (_) { print('Counter is: ${++count}'); // Breakpoint: printCounter + + final lor = testLogicalOr([0, 1]); + print(lor); }); } + +String testLogicalOr(Object obj) { + switch (obj) { + case [var a, int n] || [int n, var a] when n == 1 && a is String: + case [double n, var a] || [var a, double n] when (n - 3.14).abs() < 0.001: + return a.toString(); + default: + return 'default'; + } +} diff --git a/webdev/lib/src/pubspec.dart b/webdev/lib/src/pubspec.dart index 88f356c8c..70198af75 100644 --- a/webdev/lib/src/pubspec.dart +++ b/webdev/lib/src/pubspec.dart @@ -138,6 +138,7 @@ Future> _validateBuildDaemonVersion( // Only warn of build_daemon issues if they have a dependency on the package. if (buildDaemonIssues.any((issue) => !issue._missingDependency)) { + print('Build daemon issues: $buildDaemonIssues'); var info = await _latestPackageInfo(); var issuePreamble = 'This version of webdev does not support the `build_daemon` ' From 4b356241343183c1ca7961bf202d03bd1e038bb6 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 20 Mar 2023 16:23:28 -0700 Subject: [PATCH 2/6] Add tests and cleanup --- dwds/lib/src/debugging/debugger.dart | 2 - dwds/lib/src/debugging/location.dart | 5 - .../instances/instance_inspection_common.dart | 16 +- .../instances/patterns_inspection_test.dart | 150 ++++++++++++++++++ .../instances/record_inspection_test.dart | 24 +-- fixtures/_experimentSound/web/main.dart | 50 +++--- fixtures/_webdevSoundSmoke/web/main.dart | 13 -- webdev/lib/src/pubspec.dart | 1 - 8 files changed, 209 insertions(+), 52 deletions(-) create mode 100644 dwds/test/instances/patterns_inspection_test.dart diff --git a/dwds/lib/src/debugging/debugger.dart b/dwds/lib/src/debugging/debugger.dart index 732a4e5cc..88914abab 100644 --- a/dwds/lib/src/debugging/debugger.dart +++ b/dwds/lib/src/debugging/debugger.dart @@ -360,8 +360,6 @@ class Debugger extends Domain { if (loc == null || loc.dartLocation == _previousSteppingLocation) { return null; } - logger.severe('Previous location: $_previousSteppingLocation'); - logger.severe('Location: $loc'); _previousSteppingLocation = loc.dartLocation; return loc; } diff --git a/dwds/lib/src/debugging/location.dart b/dwds/lib/src/debugging/location.dart index eaf524384..29588848f 100644 --- a/dwds/lib/src/debugging/location.dart +++ b/dwds/lib/src/debugging/location.dart @@ -82,11 +82,6 @@ class DartLocation { if (other is! DartLocation) { return false; } - print('Compare locations: $this, $other'); - final result = uri.serverPath == other.uri.serverPath && - line == other.line && - column == other.column; - print('Locations are the same: $result'); return uri.serverPath == other.uri.serverPath && line == other.line && column == other.column; diff --git a/dwds/test/instances/instance_inspection_common.dart b/dwds/test/instances/instance_inspection_common.dart index 22a8eb7be..f4be789c7 100644 --- a/dwds/test/instances/instance_inspection_common.dart +++ b/dwds/test/instances/instance_inspection_common.dart @@ -119,6 +119,20 @@ class TestInspector { expect(result, isA()); return result as Instance; } + + Future> getFrameVariables( + String isolateId, Frame frame) async { + final refs = { + for (var variable in frame.vars!) + variable.name!: variable.value as InstanceRef + }; + final instances = {}; + for (final p in refs.entries) { + instances[p.key] = + await service.getObject(isolateId, p.value.id!) as Instance; + } + return instances; + } } Map _associationsToMap( @@ -179,7 +193,7 @@ Object? _getValue(InstanceRef instanceRef) { return instanceRef.valueAsString == 'true'; case InstanceKind.kDouble: case InstanceKind.kInt: - return int.parse(instanceRef.valueAsString!); + return double.parse(instanceRef.valueAsString!); case InstanceKind.kString: return instanceRef.valueAsString; default: diff --git a/dwds/test/instances/patterns_inspection_test.dart b/dwds/test/instances/patterns_inspection_test.dart new file mode 100644 index 000000000..b0803a3df --- /dev/null +++ b/dwds/test/instances/patterns_inspection_test.dart @@ -0,0 +1,150 @@ +// Copyright (c) 2023, 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. + +@TestOn('vm') +@Timeout(Duration(minutes: 2)) + +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 'instance_inspection_common.dart'; + +void main() async { + // Enable verbose logging for debugging. + final debug = false; + + final provider = TestSdkConfigurationProvider(verbose: debug); + tearDownAll(provider.dispose); + + for (var compilationMode in CompilationMode.values) { + await _runTests( + provider: provider, + compilationMode: compilationMode, + debug: debug, + ); + } +} + +Future _runTests({ + required TestSdkConfigurationProvider provider, + required CompilationMode compilationMode, + required bool debug, +}) async { + final context = + TestContext(TestProject.testExperimentWithSoundNullSafety, provider); + final testInspector = TestInspector(context); + + late VmServiceInterface service; + late Stream stream; + late String isolateId; + late ScriptRef mainScript; + + onBreakPoint(breakPointId, body) => testInspector.onBreakPoint( + stream, isolateId, mainScript, breakPointId, body); + + getInstanceRef(frame, expression) => + testInspector.getInstanceRef(isolateId, frame, expression); + + getFields(instanceRef, {offset, count}) => testInspector + .getFields(isolateId, instanceRef, offset: offset, count: count); + + getFrameVariables(Frame frame) => + testInspector.getFrameVariables(isolateId, frame); + + group('$compilationMode |', () { + setUpAll(() async { + setCurrentLogWriter(debug: debug); + await context.setUp( + compilationMode: compilationMode, + enableExpressionEvaluation: true, + verboseCompiler: debug, + experiments: ['records', 'patterns'], + ); + service = context.debugConnection.vmService; + + final vm = await service.getVM(); + isolateId = vm.isolates!.first.id!; + final scripts = await service.getScripts(isolateId); + + await service.streamListen('Debug'); + stream = service.onEvent('Debug'); + + mainScript = scripts.scripts! + .firstWhere((each) => each.uri!.contains('main.dart')); + }); + + tearDownAll(() async { + await context.tearDown(); + }); + + setUp(() => setCurrentLogWriter(debug: debug)); + tearDown(() => service.resume(isolateId)); + + test('pattern match case 1', () async { + await onBreakPoint('testPatternCase1', (event) async { + final frame = event.topFrame!; + + expect(await getFrameVariables(frame), { + 'obj': matchListInstance(type: 'List'), + 'a': matchPrimitiveInstance(kind: InstanceKind.kString, value: 'a'), + 'n': matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 1), + }); + }); + }); + + test('pattern match case 2', () async { + await onBreakPoint('testPatternCase2', (event) async { + final frame = event.topFrame!; + + expect(await getFrameVariables(frame), { + 'obj': matchListInstance(type: 'List'), + 'a': matchPrimitiveInstance(kind: InstanceKind.kString, value: 'b'), + 'n': matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 3.14), + }); + }); + }); + + test('pattern match default case', () async { + await onBreakPoint('testPatternDefault', (event) async { + final frame = event.topFrame!; + final frameIndex = frame.index!; + final instanceRef = await getInstanceRef(frameIndex, 'obj'); + expect(await getFields(instanceRef), [0, 1]); + + expect(await getFrameVariables(frame), { + 'obj': matchListInstance(type: 'List'), + }); + }); + }); + + test('stepping through pattern match', () async { + await onBreakPoint('callTestPattern1', (Event event) async { + var previousLocation = event.topFrame!.location; + for (var step in [ + // Make sure we step into the callee. + for (var i = 0; i < 4; i++) 'Into', + // Make a few steps inside the callee. + for (var i = 0; i < 4; i++) 'Over', + ]) { + await service.resume(isolateId, step: step); + + event = await stream + .firstWhere((e) => e.kind == EventKind.kPauseInterrupted); + + if (step == 'Over') { + expect(event.topFrame!.code!.name, 'testPattern'); + } + + final location = event.topFrame!.location; + expect(location, isNot(equals(previousLocation))); + previousLocation = location; + } + }); + }); + }); +} diff --git a/dwds/test/instances/record_inspection_test.dart b/dwds/test/instances/record_inspection_test.dart index 7cf0c8216..745a60f97 100644 --- a/dwds/test/instances/record_inspection_test.dart +++ b/dwds/test/instances/record_inspection_test.dart @@ -88,7 +88,7 @@ Future _runTests({ tearDown(() => service.resume(isolateId)); test('simple records', () async { - await onBreakPoint('printSimpleLocal', (event) async { + await onBreakPoint('printSimpleLocalRecord', (event) async { final frame = event.topFrame!.index!; final instanceRef = await getInstanceRef(frame, 'record'); @@ -111,7 +111,7 @@ Future _runTests({ }); test('simple records, field access', () async { - await onBreakPoint('printSimpleLocal', (event) async { + await onBreakPoint('printSimpleLocalRecord', (event) async { final frame = event.topFrame!.index!; expect(await getInstance(frame, r'record.$1'), matchPrimitiveInstance(kind: InstanceKind.kBool, value: true)); @@ -122,7 +122,7 @@ Future _runTests({ }); test('simple records with named fields', () async { - await onBreakPoint('printSimpleNamedLocal', (event) async { + await onBreakPoint('printSimpleNamedLocalRecord', (event) async { final frame = event.topFrame!.index!; final instanceRef = await getInstanceRef(frame, 'record'); @@ -149,7 +149,7 @@ Future _runTests({ }); test('simple records with named fields, field access', () async { - await onBreakPoint('printSimpleNamedLocal', (event) async { + await onBreakPoint('printSimpleNamedLocalRecord', (event) async { final frame = event.topFrame!.index!; expect(await getInstance(frame, r'record.$1'), matchPrimitiveInstance(kind: InstanceKind.kBool, value: true)); @@ -160,7 +160,7 @@ Future _runTests({ }); test('complex records fields', () async { - await onBreakPoint('printComplexLocal', (event) async { + await onBreakPoint('printComplexLocalRecord', (event) async { final frame = event.topFrame!.index!; final instanceRef = await getInstanceRef(frame, 'record'); @@ -208,7 +208,7 @@ Future _runTests({ }); test('complex records, field access', () async { - await onBreakPoint('printComplexLocal', (event) async { + await onBreakPoint('printComplexLocalRecord', (event) async { final frame = event.topFrame!.index!; expect(await getInstance(frame, r'record.$1'), matchPrimitiveInstance(kind: InstanceKind.kBool, value: true)); @@ -223,7 +223,7 @@ Future _runTests({ }); test('complex records with named fields', () async { - await onBreakPoint('printComplexNamedLocal', (event) async { + await onBreakPoint('printComplexNamedLocalRecord', (event) async { final frame = event.topFrame!.index!; final instanceRef = await getInstanceRef(frame, 'record'); @@ -272,7 +272,7 @@ Future _runTests({ }); test('complex records with named fields, field access', () async { - await onBreakPoint('printComplexNamedLocal', (event) async { + await onBreakPoint('printComplexNamedLocalRecord', (event) async { final frame = event.topFrame!.index!; expect(await getInstance(frame, r'record.$1'), matchPrimitiveInstance(kind: InstanceKind.kBool, value: true)); @@ -287,7 +287,7 @@ Future _runTests({ }); test('nested records', () async { - await onBreakPoint('printNestedLocal', (event) async { + await onBreakPoint('printNestedLocalRecord', (event) async { final frame = event.topFrame!.index!; final instanceRef = await getInstanceRef(frame, 'record'); @@ -324,7 +324,7 @@ Future _runTests({ }); test('nested records, field access', () async { - await onBreakPoint('printNestedLocal', (event) async { + await onBreakPoint('printNestedLocalRecord', (event) async { final frame = event.topFrame!.index!; final instanceRef = await getInstanceRef(frame, r'record.$2'); @@ -338,7 +338,7 @@ Future _runTests({ }); test('nested records with named fields,', () async { - await onBreakPoint('printNestedNamedLocal', (event) async { + await onBreakPoint('printNestedNamedLocalRecord', (event) async { final frame = event.topFrame!.index!; final instanceRef = await getInstanceRef(frame, 'record'); @@ -382,7 +382,7 @@ Future _runTests({ }); test('nested records with named fields, field access', () async { - await onBreakPoint('printNestedNamedLocal', (event) async { + await onBreakPoint('printNestedNamedLocalRecord', (event) async { final frame = event.topFrame!.index!; final instanceRef = await getInstanceRef(frame, r'record.inner'); diff --git a/fixtures/_experimentSound/web/main.dart b/fixtures/_experimentSound/web/main.dart index f3200a176..1c34887e8 100644 --- a/fixtures/_experimentSound/web/main.dart +++ b/fixtures/_experimentSound/web/main.dart @@ -9,44 +9,58 @@ import 'dart:html'; void main() { // for evaluation Timer.periodic(const Duration(seconds: 1), (_) { - printSimpleLocal(); - printComplexLocal(); - printNestedLocal(); - printSimpleNamedLocal(); - printComplexNamedLocal(); - printNestedNamedLocal(); + printSimpleLocalRecord(); + printComplexLocalRecord(); + printNestedLocalRecord(); + printSimpleNamedLocalRecord(); + printComplexNamedLocalRecord(); + printNestedNamedLocalRecord(); + print('Patterns'); // Breakpoint: callTestPattern1 + testPattern(['a', 1]); + testPattern([3.14, 'b']); + testPattern([0, 1]); }); document.body!.appendText('Program is running!'); } -void printSimpleLocal() { +void printSimpleLocalRecord() { final record = (true, 3); - print(record); // Breakpoint: printSimpleLocal + print(record); // Breakpoint: printSimpleLocalRecord } -void printSimpleNamedLocal() { +void printSimpleNamedLocalRecord() { final record = (true, cat: 'Vasya'); - print(record); // Breakpoint: printSimpleNamedLocal + print(record); // Breakpoint: printSimpleNamedLocalRecord } -void printComplexLocal() { +void printComplexLocalRecord() { final record = (true, 3, {'a': 1, 'b': 5}); - print(record); // Breakpoint: printComplexLocal + print(record); // Breakpoint: printComplexLocalRecord } -void printComplexNamedLocal() { +void printComplexNamedLocalRecord() { final record = (true, 3, array: {'a': 1, 'b': 5}); - print(record); // Breakpoint: printComplexNamedLocal + print(record); // Breakpoint: printComplexNamedLocalRecord } -void printNestedLocal() { +void printNestedLocalRecord() { final record = (true, (false, 5)); - print(record); // Breakpoint: printNestedLocal + print(record); // Breakpoint: printNestedLocalRecord } -void printNestedNamedLocal() { +void printNestedNamedLocalRecord() { final record = (true, inner: (false, 5)); - print(record); // Breakpoint: printNestedNamedLocal + print(record); // Breakpoint: printNestedNamedLocalRecord } +String testPattern(Object obj) { + switch (obj) { + case [var a, int n] || [int n, var a] when n == 1 && a is String: + return a.toString(); // Breakpoint: testPatternCase1 + case [double n, var a] || [var a, double n] when (n - 3.14).abs() < 0.001: + return a.toString(); // Breakpoint: testPatternCase2 + default: + return 'default'; // Breakpoint: testPatternDefault + } +} diff --git a/fixtures/_webdevSoundSmoke/web/main.dart b/fixtures/_webdevSoundSmoke/web/main.dart index a8a519203..907bfdbba 100644 --- a/fixtures/_webdevSoundSmoke/web/main.dart +++ b/fixtures/_webdevSoundSmoke/web/main.dart @@ -19,18 +19,5 @@ void main() { var count = 0; Timer.periodic(const Duration(seconds: 1), (_) { print('Counter is: ${++count}'); // Breakpoint: printCounter - - final lor = testLogicalOr([0, 1]); - print(lor); }); } - -String testLogicalOr(Object obj) { - switch (obj) { - case [var a, int n] || [int n, var a] when n == 1 && a is String: - case [double n, var a] || [var a, double n] when (n - 3.14).abs() < 0.001: - return a.toString(); - default: - return 'default'; - } -} diff --git a/webdev/lib/src/pubspec.dart b/webdev/lib/src/pubspec.dart index 5e420f2b8..ed51aa003 100644 --- a/webdev/lib/src/pubspec.dart +++ b/webdev/lib/src/pubspec.dart @@ -138,7 +138,6 @@ Future> _validateBuildDaemonVersion( // Only warn of build_daemon issues if they have a dependency on the package. if (buildDaemonIssues.any((issue) => !issue._missingDependency)) { - print('Build daemon issues: $buildDaemonIssues'); var info = await _latestPackageInfo(); var issuePreamble = 'This version of webdev does not support the `build_daemon` ' From 37ff8fee1fdb8d7a0bd71d169a1ecc3662aafc06 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 20 Mar 2023 16:36:07 -0700 Subject: [PATCH 3/6] Update changelog and disable new tests for all versions before current main --- dwds/CHANGELOG.md | 1 + dwds/test/instances/patterns_inspection_test.dart | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md index 848870a8e..a8f3d2310 100644 --- a/dwds/CHANGELOG.md +++ b/dwds/CHANGELOG.md @@ -1,6 +1,7 @@ ## 18.0.2-dev - Support new DDC temp names for patterns. - [#2042](https://github.com/dart-lang/webdev/pull/2042) +- Make debugger find next dart location when stepping. ## 18.0.1 diff --git a/dwds/test/instances/patterns_inspection_test.dart b/dwds/test/instances/patterns_inspection_test.dart index b0803a3df..0bd016a0e 100644 --- a/dwds/test/instances/patterns_inspection_test.dart +++ b/dwds/test/instances/patterns_inspection_test.dart @@ -5,6 +5,9 @@ @TestOn('vm') @Timeout(Duration(minutes: 2)) +import 'dart:io'; + +import 'package:pub_semver/pub_semver.dart' as semver; import 'package:test/test.dart'; import 'package:test_common/logging.dart'; import 'package:test_common/test_sdk_configuration.dart'; @@ -146,5 +149,7 @@ Future _runTests({ } }); }); - }); + }, // TODO(annagrin): Remove when dart 3.0 is stable. + skip: semver.Version.parse(Platform.version.split(' ')[0]) <= + semver.Version.parse('3.0.0-322.0.dev')); } From ed788c081e67753770cc066d1be1d09411932b8b Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 20 Mar 2023 16:40:40 -0700 Subject: [PATCH 4/6] Update changelog with pull request reference --- dwds/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md index a8f3d2310..d6aac6c3f 100644 --- a/dwds/CHANGELOG.md +++ b/dwds/CHANGELOG.md @@ -1,7 +1,7 @@ ## 18.0.2-dev - Support new DDC temp names for patterns. - [#2042](https://github.com/dart-lang/webdev/pull/2042) -- Make debugger find next dart location when stepping. +- Make debugger find next dart location when stepping. -[#2043](https://github.com/dart-lang/webdev/pull/2043) ## 18.0.1 From 08a8fd611a799f769372d647b65a850fd0c3f838 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Tue, 21 Mar 2023 08:23:18 -0700 Subject: [PATCH 5/6] Fix test failures --- dwds/test/instances/record_inspection_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwds/test/instances/record_inspection_test.dart b/dwds/test/instances/record_inspection_test.dart index 745a60f97..91e558f4a 100644 --- a/dwds/test/instances/record_inspection_test.dart +++ b/dwds/test/instances/record_inspection_test.dart @@ -65,7 +65,7 @@ Future _runTests({ compilationMode: compilationMode, enableExpressionEvaluation: true, verboseCompiler: debug, - experiments: ['records'], + experiments: ['records', 'patterns'], ); service = context.debugConnection.vmService; From 73621f98531c0eb58bb0241b0ca764d1705e6fe9 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Tue, 21 Mar 2023 08:30:01 -0700 Subject: [PATCH 6/6] Update patterns tests to skip correct sdk versions --- dwds/test/instances/patterns_inspection_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dwds/test/instances/patterns_inspection_test.dart b/dwds/test/instances/patterns_inspection_test.dart index 0bd016a0e..03bf7dc57 100644 --- a/dwds/test/instances/patterns_inspection_test.dart +++ b/dwds/test/instances/patterns_inspection_test.dart @@ -150,6 +150,6 @@ Future _runTests({ }); }); }, // TODO(annagrin): Remove when dart 3.0 is stable. - skip: semver.Version.parse(Platform.version.split(' ')[0]) <= - semver.Version.parse('3.0.0-322.0.dev')); + skip: semver.Version.parse(Platform.version.split(' ')[0]) < + semver.Version.parse('3.0.0-351.0.dev')); }