diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json index 981de4b8d4b..4281850595d 100644 --- a/.dart_tool/package_config.json +++ b/.dart_tool/package_config.json @@ -709,6 +709,11 @@ "packageUri": "lib/", "languageVersion": "2.12" }, + { + "name": "test_package", + "rootUri": "../pkg/vm_service/test/test_package", + "languageVersion": "2.12" + }, { "name": "test_process", "rootUri": "../third_party/pkg/test_process", diff --git a/.packages b/.packages index 568fa668f11..f5ea5bc5faa 100644 --- a/.packages +++ b/.packages @@ -105,6 +105,7 @@ test:third_party/pkg/test/pkgs/test/lib test_api:third_party/pkg/test/pkgs/test_api/lib test_core:third_party/pkg/test/pkgs/test_core/lib test_descriptor:third_party/pkg/test_descriptor/lib +test_package:pkg/vm_service/test/test_package test_process:third_party/pkg/test_process/lib test_reflective_loader:third_party/pkg/test_reflective_loader/lib test_runner:pkg/test_runner/lib diff --git a/pkg/vm_service/pubspec.yaml b/pkg/vm_service/pubspec.yaml index 81e92a4e6c6..29845923571 100644 --- a/pkg/vm_service/pubspec.yaml +++ b/pkg/vm_service/pubspec.yaml @@ -21,3 +21,5 @@ dev_dependencies: process: ^4.0.0 pub_semver: ^2.0.0-nullsafety.0 test: ^1.16.0-nullsafety.13 + test_package: + path: 'test/test_package' diff --git a/pkg/vm_service/test/add_breakpoint_rpc_kernel_test.dart b/pkg/vm_service/test/add_breakpoint_rpc_kernel_test.dart new file mode 100644 index 00000000000..bead75c44d8 --- /dev/null +++ b/pkg/vm_service/test/add_breakpoint_rpc_kernel_test.dart @@ -0,0 +1,178 @@ +// Copyright (c) 2017, 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 'dart:async'; + +import 'package:test/test.dart'; +import 'package:vm_service/vm_service.dart'; + +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +const int LINE_A = 24; +const int LINE_B = 26; + +int value = 0; + +int incValue(int amount) { + value += amount; + return amount; +} + +Future testMain() async { + incValue(incValue(1)); // line A. + + incValue(incValue(1)); // line B. +} + +var tests = [ + hasPausedAtStart, + + // Test future breakpoints. + (VmService service, IsolateRef isolateRef) async { + final isolateId = isolateRef.id!; + final isolate = await service.getIsolate(isolateId); + final rootLib = + await service.getObject(isolateId, isolate.rootLib!.id!) as Library; + final rootLibId = rootLib.id!; + final scriptId = rootLib.scripts![0].id!; + final script = await service.getObject(isolateId, scriptId) as Script; + + // Future breakpoint. + var futureBpt1 = await service.addBreakpoint(isolateId, scriptId, LINE_A); + expect(futureBpt1.breakpointNumber, 1); + expect(futureBpt1.resolved, isFalse); + expect(await futureBpt1.location!.line!, LINE_A); + expect(await futureBpt1.location!.column, null); + + // Future breakpoint with specific column. + var futureBpt2 = + await service.addBreakpoint(isolateId, scriptId, LINE_A, column: 3); + expect(futureBpt2.breakpointNumber, 2); + expect(futureBpt2.resolved, isFalse); + expect(await futureBpt2.location!.line!, LINE_A); + expect(await futureBpt2.location!.column!, 3); + + int resolvedCount = await resumeAndCountResolvedBreakpointsUntilPause( + service, + isolate, + ); + + // After resolution the breakpoints have assigned line & column. + expect(resolvedCount, 2); + + // Refresh objects + futureBpt1 = + await service.getObject(isolateId, futureBpt1.id!) as Breakpoint; + futureBpt2 = + await service.getObject(isolateId, futureBpt2.id!) as Breakpoint; + + expect(futureBpt1.resolved, isTrue); + expect(script.getLineNumberFromTokenPos(futureBpt1.location!.tokenPos!), + LINE_A); + expect( + script.getColumnNumberFromTokenPos(futureBpt1.location!.tokenPos!), 12); + expect(futureBpt2.resolved, isTrue); + expect(script.getLineNumberFromTokenPos(futureBpt2.location!.tokenPos!), + LINE_A); + expect( + script.getColumnNumberFromTokenPos(futureBpt2.location!.tokenPos!), 3); + + // The first breakpoint hits before value is modified. + InstanceRef result = + await service.evaluate(isolateId, rootLibId, 'value') as InstanceRef; + expect(result.valueAsString, '0'); + + await service.resume(isolateId); + await hasStoppedAtBreakpoint(service, isolate); + + // The second breakpoint hits after value has been modified once. + result = + await service.evaluate(isolateId, rootLibId, 'value') as InstanceRef; + expect(result.valueAsString, '1'); + + // Remove the breakpoints. + expect((await service.removeBreakpoint(isolateId, futureBpt1.id!)).type, + 'Success'); + expect((await service.removeBreakpoint(isolateId, futureBpt2.id!)).type, + 'Success'); + }, + + // Test resolution of column breakpoints. + (VmService service, IsolateRef isolateRef) async { + final isolateId = isolateRef.id!; + final isolate = await service.getIsolate(isolateId); + final rootLibId = isolate.rootLib!.id!; + final rootLib = await service.getObject(isolateId, rootLibId) as Library; + + final scriptId = rootLib.scripts![0].id!; + final script = await service.getObject(isolateId, scriptId) as Script; + + // Try all columns, including some columns that are too big. + for (int col = 1; col <= 50; col++) { + final bpt = + await service.addBreakpoint(isolateId, scriptId, LINE_A, column: col); + expect(bpt.resolved, isTrue); + int resolvedLine = + script.getLineNumberFromTokenPos(bpt.location!.tokenPos!)!; + int resolvedCol = + script.getColumnNumberFromTokenPos(bpt.location!.tokenPos!)!; + print('$LINE_A:${col} -> ${resolvedLine}:${resolvedCol}'); + if (col <= 12) { + expect(resolvedLine, LINE_A); + expect(resolvedCol, 3); + } else if (col <= 36) { + expect(resolvedLine, LINE_A); + expect(resolvedCol, 12); + } else { + expect(resolvedLine, LINE_B); + expect(resolvedCol, 12); + } + expect( + (await service.removeBreakpoint(isolateId, bpt.id!)).type, 'Success'); + } + + // Make sure that a zero column is an error. + var caughtException = false; + try { + await service.addBreakpoint(isolateId, scriptId, 20, column: 0); + expect(false, isTrue, reason: 'Unreachable'); + } on RPCError catch (e) { + caughtException = true; + expect(e.code, RPCError.kInvalidParams); + expect(e.details, "addBreakpoint: invalid 'column' parameter: 0"); + } + expect(caughtException, isTrue); + }, +]; + +Future resumeAndCountResolvedBreakpointsUntilPause( + VmService service, Isolate isolate) async { + final completer = Completer(); + late StreamSubscription subscription; + int resolvedCount = 0; + + subscription = service.onDebugEvent.listen((event) { + if (event.kind == EventKind.kBreakpointResolved) { + resolvedCount++; + } else if (event.kind == EventKind.kPauseBreakpoint) { + subscription.cancel(); + service.streamCancel(EventStreams.kDebug); + completer.complete(); + } + }); + await service.streamListen(EventStreams.kDebug); + + await service.resume(isolate.id!); + await completer.future; + return resolvedCount; +} + +main(args) => runIsolateTests( + args, + tests, + 'add_breakpoint_rpc_kernel_test.dart', + testeeConcurrent: testMain, + pause_on_start: true, + ); diff --git a/pkg/vm_service/test/allocations_test.dart b/pkg/vm_service/test/allocations_test.dart new file mode 100644 index 00000000000..9e38e57fc44 --- /dev/null +++ b/pkg/vm_service/test/allocations_test.dart @@ -0,0 +1,43 @@ +// Copyright (c) 2014, 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 'package:vm_service/vm_service.dart'; + +import 'common/test_helper.dart'; + +class Foo {} + +// Prevent TFA from removing this static field to ensure the objects are kept +// alive, so the allocation stats will report them via the service api. +@pragma('vm:entry-point') +List? foos; + +void script() { + foos = [ + Foo(), + Foo(), + Foo(), + ]; +} + +var tests = [ + (VmService service, IsolateRef isolateRef) async { + var profile = await service.callMethod('_getAllocationProfile', + isolateId: isolateRef.id!) as AllocationProfile; + print(profile.runtimeType); + var classHeapStats = profile.members!.singleWhere((stats) { + return stats.classRef!.name == 'Foo'; + }); + expect(classHeapStats.instancesCurrent, 3); + expect(classHeapStats.instancesAccumulated, 3); + }, +]; + +main(args) => runIsolateTests( + args, + tests, + 'allocations_test.dart', + testeeBefore: script, + ); diff --git a/pkg/vm_service/test/async_next_regression_18877_test.dart b/pkg/vm_service/test/async_next_regression_18877_test.dart new file mode 100644 index 00000000000..6912a672888 --- /dev/null +++ b/pkg/vm_service/test/async_next_regression_18877_test.dart @@ -0,0 +1,59 @@ +// Copyright (c) 2018, 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. +// VMOptions=--verbose_debug + +import 'dart:developer'; + +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +const int LINE_A = 24; +const int LINE_B = 25; +const int LINE_C = 26; + +foo() async {} + +doAsync(stop) async { + // Flutter issue 18877: + // If a closure is defined in the context of an async method, stepping over + // an await causes the implicit breakpoint to be set for that closure instead + // of the async_op, resulting in the debugger falling through. + final baz = () => print('doAsync($stop) done!'); + if (stop) debugger(); + await foo(); // Line A. + await foo(); // Line B. + await foo(); // Line C. + baz(); + return null; +} + +testMain() { + // With two runs of doAsync floating around, async step should only cause + // us to stop in the run we started in. + doAsync(false); + doAsync(true); +} + +var tests = [ + hasStoppedAtBreakpoint, + stoppedAtLine(LINE_A), + stepOver, // foo() + stoppedAtLine(LINE_A), + asyncNext, + hasStoppedAtBreakpoint, + stoppedAtLine(LINE_B), + stepOver, // foo() + stoppedAtLine(LINE_B), + asyncNext, + hasStoppedAtBreakpoint, + stoppedAtLine(LINE_C), + resumeIsolate, +]; + +main(args) => runIsolateTests( + args, + tests, + 'async_next_regression_18877_test.dart', + testeeConcurrent: testMain, + ); diff --git a/pkg/vm_service/test/awaiter_async_stack_contents_2_test.dart b/pkg/vm_service/test/awaiter_async_stack_contents_2_test.dart new file mode 100644 index 00000000000..88d17d0a849 --- /dev/null +++ b/pkg/vm_service/test/awaiter_async_stack_contents_2_test.dart @@ -0,0 +1,64 @@ +// Copyright (c) 2018, 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. +// +// VMOptions=--async-debugger --verbose-debug --lazy-async-stacks + +import 'dart:developer'; +import 'package:test/test.dart'; +import 'package:vm_service/vm_service.dart'; + +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +const LINE_A = 28; +const LINE_B = 34; +const LINE_C = 38; + +notCalled() async { + await null; + await null; + await null; + await null; +} + +foobar() async { + await null; + debugger(); + print('foobar'); // LINE_A. +} + +helper() async { + await null; + print('helper'); + await foobar(); // LINE_B. +} + +testMain() async { + helper(); // LINE_C. +} + +var tests = [ + hasStoppedAtBreakpoint, + stoppedAtLine(LINE_A), + (VmService service, IsolateRef isolate) async { + final isolateId = isolate.id!; + // Verify awaiter stack trace is the current frame + the awaiter. + Stack stack = await service.getStack(isolateId); + expect(stack.awaiterFrames, isNotNull); + List awaiterFrames = stack.awaiterFrames!; + expect(awaiterFrames.length, greaterThanOrEqualTo(2)); + // Awaiter frame. + expect(awaiterFrames[0].function!.owner.name, 'foobar'); + // Awaiter frame. + expect(awaiterFrames[1].function!.owner.name, 'helper'); + }, +]; + +main(args) => runIsolateTestsSynchronous( + args, + tests, + 'awaiter_async_stack_contents_2_test.dart', + testeeConcurrent: testMain, + extraArgs: extraDebuggingArgs, + ); diff --git a/pkg/vm_service/test/awaiter_async_stack_contents_test.dart b/pkg/vm_service/test/awaiter_async_stack_contents_test.dart new file mode 100644 index 00000000000..0fea350090e --- /dev/null +++ b/pkg/vm_service/test/awaiter_async_stack_contents_test.dart @@ -0,0 +1,72 @@ +// Copyright (c) 2017, 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. +// +// VMOptions=--async-debugger --verbose-debug --lazy-async-stacks + +import 'dart:developer'; +import 'package:test/test.dart'; +import 'package:vm_service/vm_service.dart'; + +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +const LINE_C = 22; +const LINE_A = 28; +const LINE_B = 34; +const LINE_D = 29; + +foobar() async { + await null; + debugger(); + print('foobar'); // LINE_C. +} + +helper() async { + await null; + debugger(); + print('helper'); // LINE_A. + await foobar(); // LINE_D +} + +testMain() { + debugger(); + helper(); // LINE_B. +} + +var tests = [ + hasStoppedAtBreakpoint, + stoppedAtLine(LINE_B), + (VmService service, IsolateRef isolateRef) async { + Stack stack = await service.getStack(isolateRef.id!); + // No awaiter frames because we are in a completely synchronous stack. + expect(stack.awaiterFrames, isNull); + }, + resumeIsolate, + hasStoppedAtBreakpoint, + stoppedAtLine(LINE_A), + resumeIsolate, + hasStoppedAtBreakpoint, + stoppedAtLine(LINE_C), + (VmService service, IsolateRef isolateRef) async { + // Verify awaiter stack trace is the current frame + the awaiter. + Stack stack = await service.getStack(isolateRef.id!); + expect(stack.awaiterFrames, isNotNull); + List awaiterFrames = stack.awaiterFrames!; + + expect(awaiterFrames.length, greaterThanOrEqualTo(2)); + // Awaiter frame. + expect(await awaiterFrames[0].function!.owner.name, 'foobar'); + // Awaiter frame. + expect(await awaiterFrames[1].function!.owner.name, 'helper'); + // "helper" is not await'ed. + }, +]; + +main(args) => runIsolateTestsSynchronous( + args, + tests, + 'awaiter_async_stack_contents_test.dart', + testeeConcurrent: testMain, + extraArgs: extraDebuggingArgs, + ); diff --git a/pkg/vm_service/test/breakpoint_async_break_test.dart b/pkg/vm_service/test/breakpoint_async_break_test.dart new file mode 100644 index 00000000000..d0c244317f1 --- /dev/null +++ b/pkg/vm_service/test/breakpoint_async_break_test.dart @@ -0,0 +1,79 @@ +// Copyright (c) 2019, 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 'dart:async'; +import 'package:test/test.dart'; +import 'package:vm_service/vm_service.dart'; + +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +const int LINE = 18; + +// Issue: https://github.com/dart-lang/sdk/issues/36622 +Future testMain() async { + for (int i = 0; i < 2; i++) { + if (i > 0) { + break; // breakpoint here + } + await Future.delayed(Duration(seconds: 1)); + } +} + +var tests = [ + hasPausedAtStart, + // Test future breakpoints. + (VmService service, IsolateRef isolateRef) async { + final isolateId = isolateRef.id!; + final isolate = await service.getIsolate(isolateId); + final rootLib = + await service.getObject(isolateId, isolate.rootLib!.id!) as Library; + final scriptId = rootLib.scripts![0].id!; + final script = await service.getObject(isolateId, scriptId) as Script; + + // Future breakpoint. + var futureBpt = await service.addBreakpoint(isolateId, scriptId, LINE); + expect(futureBpt.breakpointNumber, 1); + expect(futureBpt.resolved, isFalse); + expect(await futureBpt.location!.line, LINE); + expect(await futureBpt.location!.column, null); + + final completer = Completer(); + int resolvedCount = 0; + late StreamSubscription subscription; + subscription = service.onDebugEvent.listen((event) { + if (event.kind == EventKind.kBreakpointResolved) { + resolvedCount++; + } else if (event.kind == EventKind.kPauseBreakpoint) { + subscription.cancel(); + service.streamCancel(EventStreams.kDebug); + completer.complete(); + } + }); + + await service.streamListen(EventStreams.kDebug); + await service.resume(isolateId); + await hasStoppedAtBreakpoint(service, isolate); + + // After resolution the breakpoints have assigned line & column. + expect(resolvedCount, 1); + futureBpt = await service.getObject(isolateId, futureBpt.id!) as Breakpoint; + expect(futureBpt.resolved, isTrue); + expect( + script.getLineNumberFromTokenPos(futureBpt.location!.tokenPos), LINE); + expect(script.getColumnNumberFromTokenPos(futureBpt.location!.tokenPos), 7); + + // Remove the breakpoints. + expect((await service.removeBreakpoint(isolateId, futureBpt.id!)).type, + 'Success'); + }, +]; + +main(args) => runIsolateTests( + args, + tests, + 'breakpoint_async_break_test.dart', + testeeConcurrent: testMain, + pause_on_start: true, + ); diff --git a/pkg/vm_service/test/breakpoint_in_package_parts_class_file_uri_test.dart b/pkg/vm_service/test/breakpoint_in_package_parts_class_file_uri_test.dart new file mode 100644 index 00000000000..70cd0fd0226 --- /dev/null +++ b/pkg/vm_service/test/breakpoint_in_package_parts_class_file_uri_test.dart @@ -0,0 +1,48 @@ +// Copyright (c) 2019, 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 'dart:io' show Platform; + +import 'package:path/path.dart' as path; +import 'package:test_package/has_part.dart' as has_part; + +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +// Chop off the file name. +String baseDirectory = path.dirname(Platform.script.path) + '/'; +Uri baseUri = Platform.script.replace(path: baseDirectory); +Uri breakpointFile = baseUri.resolve('test_package/the_part.dart'); +const String shortFile = "the_part.dart"; + +const int LINE = 87; + +code() { + has_part.main(); +} + +List stops = []; + +List expected = [ + "$shortFile:${LINE + 0}:5", // on 'print' + "$shortFile:${LINE + 1}:3" // on class ending '}' +]; + +var tests = [ + hasPausedAtStart, + setBreakpointAtUriAndLine(breakpointFile.toString(), LINE), + runStepThroughProgramRecordingStops(stops), + checkRecordedStops(stops, expected) +]; + +main([args = const []]) { + runIsolateTestsSynchronous( + args, + tests, + 'breakpoint_in_package_parts_class_file_uri_test.dart', + testeeConcurrent: code, + pause_on_start: true, + pause_on_exit: true, + ); +} diff --git a/pkg/vm_service/test/breakpoint_in_package_parts_class_test.dart b/pkg/vm_service/test/breakpoint_in_package_parts_class_test.dart new file mode 100644 index 00000000000..2dcf30fee96 --- /dev/null +++ b/pkg/vm_service/test/breakpoint_in_package_parts_class_test.dart @@ -0,0 +1,42 @@ +// Copyright (c) 2019, 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. + +library breakpoint_in_parts_class; + +import 'package:test_package/has_part.dart' as has_part; +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +const int LINE = 87; +const String breakpointFile = "package:test_package/the_part.dart"; +const String shortFile = "the_part.dart"; + +code() { + has_part.main(); +} + +List stops = []; + +List expected = [ + "$shortFile:${LINE + 0}:5", // on 'print' + "$shortFile:${LINE + 1}:3" // on class ending '}' +]; + +var tests = [ + hasPausedAtStart, + setBreakpointAtUriAndLine(breakpointFile, LINE), + runStepThroughProgramRecordingStops(stops), + checkRecordedStops(stops, expected) +]; + +main(args) { + runIsolateTestsSynchronous( + args, + tests, + 'breakpoint_in_package_parts_class_test.dart', + testeeConcurrent: code, + pause_on_start: true, + pause_on_exit: true, + ); +} diff --git a/pkg/vm_service/test/breakpoint_in_parts_class_part.dart b/pkg/vm_service/test/breakpoint_in_parts_class_part.dart new file mode 100644 index 00000000000..4855332a0b8 --- /dev/null +++ b/pkg/vm_service/test/breakpoint_in_parts_class_part.dart @@ -0,0 +1,91 @@ +// Copyright (c) 2017, 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. + +part of breakpoint_in_parts_class; + +void foo() { + print("lalala"); +} + +class Foo1 { + final foo; + + Foo1(this.foo) { + print("hello from foo!"); + } +} + +class Foo2 { + final foo; + + Foo2(this.foo) { + print("hello from foo!"); + } +} + +class Foo3 { + final foo; + + Foo3(this.foo) { + print("hello from foo!"); + } +} + +class Foo4 { + final foo; + + Foo4(this.foo) { + print("hello from foo!"); + } +} + +class Foo5 { + final foo; + + Foo5(this.foo) { + print("hello from foo!"); + } +} + +class Foo6 { + final foo; + + Foo6(this.foo) { + print("hello from foo!"); + } +} + +class Foo7 { + final foo; + + Foo7(this.foo) { + print("hello from foo!"); + } +} + +class Foo8 { + final foo; + + Foo8(this.foo) { + print("hello from foo!"); + } +} + +class Foo9 { + final foo; + + Foo9(this.foo) { + print("hello from foo!"); + } +} + +class Foo10 { + final foo; + + Foo10(this.foo) { + print("hello from foo!"); + } +} + +var foo2 = foo() as dynamic; diff --git a/pkg/vm_service/test/breakpoint_in_parts_class_test.dart b/pkg/vm_service/test/breakpoint_in_parts_class_test.dart new file mode 100644 index 00000000000..89f4e7f572c --- /dev/null +++ b/pkg/vm_service/test/breakpoint_in_parts_class_test.dart @@ -0,0 +1,43 @@ +// Copyright (c) 2017, 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. + +library breakpoint_in_parts_class; + +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +part 'breakpoint_in_parts_class_part.dart'; + +const int LINE = 87; +const String file = "breakpoint_in_parts_class_part.dart"; + +code() { + final foo = Foo10("Foo!"); + print(foo); +} + +List stops = []; + +List expected = [ + "$file:${LINE + 0}:5", // on 'print' + "$file:${LINE + 1}:3" // on class ending '}' +]; + +var tests = [ + hasPausedAtStart, + setBreakpointAtUriAndLine(file, LINE), + runStepThroughProgramRecordingStops(stops), + checkRecordedStops(stops, expected) +]; + +main(args) { + runIsolateTestsSynchronous( + args, + tests, + 'breakpoint_in_parts_class_test.dart', + testeeConcurrent: code, + pause_on_start: true, + pause_on_exit: true, + ); +} diff --git a/pkg/vm_service/test/breakpoint_non_debuggable_library_test.dart b/pkg/vm_service/test/breakpoint_non_debuggable_library_test.dart new file mode 100644 index 00000000000..4ecd5c41710 --- /dev/null +++ b/pkg/vm_service/test/breakpoint_non_debuggable_library_test.dart @@ -0,0 +1,83 @@ +// Copyright (c) 2019, 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 'package:test_package/has_part.dart' as test_pkg; +import 'package:vm_service/vm_service.dart'; + +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +const String file = 'package:test_package/has_part.dart'; +// print() within fooz() +const int LINE_A = 15; +// print() within barz() +const int LINE_B = 11; + +testMain() { + test_pkg.fooz(); +} + +var tests = [ + hasPausedAtStart, + (VmService service, IsolateRef isolateRef) async { + // Mark 'package:observatory_test_package/has_part.dart' as not debuggable. + final isolateId = isolateRef.id!; + final isolate = await service.getIsolate(isolateId); + + LibraryRef has_part_ref = isolate.libraries!.firstWhere( + (LibraryRef library) => library.uri == file, + ); + + Library has_part = + await service.getObject(isolateId, has_part_ref.id!) as Library; + expect(has_part.debuggable, true); + // SetBreakpoint before setting library to non-debuggable. + // Breakpoints are allowed to be set (before marking library as + // non-debuggable) but are not hit when running (after marking library + // as non-debuggable). + ScriptRef script = has_part.scripts!.firstWhere( + (ScriptRef script) => script.uri == file, + ); + Breakpoint bpt = await service.addBreakpoint(isolateId, script.id!, LINE_A); + print("Breakpoint is $bpt"); + expect(bpt, isNotNull); + + // Set breakpoint and check later that this breakpoint won't be added if + // the library is non-debuggable. + bpt = await service.addBreakpoint(isolateId, script.id!, LINE_B); + print("Breakpoint is $bpt"); + expect(bpt, isNotNull); + + // Remove breakpoint. + final res = await service.removeBreakpoint(isolateId, bpt.id!); + expect(res.type, 'Success'); + + await service.setLibraryDebuggable(isolateId, has_part.id!, false); + has_part = await service.getObject(isolateId, has_part.id!) as Library; + expect(has_part.debuggable, false); + print('$has_part is debuggable: ${has_part.debuggable}'); + + // Breakpoints are not allowed to set on non-debuggable libraries. + try { + await service.addBreakpoint(isolateId, script.id!, LINE_B); + } on RPCError catch (e) { + // Cannot add breakpoint error code + expect(e.code, 102); + expect(e.details, contains("Cannot add breakpoint at line '11'")); + print("Set Breakpoint to non-debuggable library is not allowed"); + } + }, + resumeIsolate, + hasStoppedAtExit, +]; + +main(args) => runIsolateTests( + args, + tests, + 'breakpoint_non_debuggable_library_test.dart', + testeeConcurrent: testMain, + pause_on_start: true, + pause_on_exit: true, + ); diff --git a/pkg/vm_service/test/breakpoint_on_if_null_1_test.dart b/pkg/vm_service/test/breakpoint_on_if_null_1_test.dart new file mode 100644 index 00000000000..c46cabeecf7 --- /dev/null +++ b/pkg/vm_service/test/breakpoint_on_if_null_1_test.dart @@ -0,0 +1,56 @@ +// Copyright (c) 2019, 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. + +library breakpoint_in_parts_class; + +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +const int LINE = 18; +const String file = "breakpoint_on_if_null_1_test.dart"; + +code() { + foo(42); +} + +foo(dynamic args) { + if (args == null) { + print("was null"); + } + if (args != null) { + print("was not null"); + } + if (args == 42) { + print("was 42!"); + } +} + +List stops = []; + +List expected = [ + "$file:${LINE + 0}:12", // on '==' + "$file:${LINE + 3}:12", // on '!=' + "$file:${LINE + 4}:5", // on 'print' + "$file:${LINE + 6}:12", // on '==' + "$file:${LINE + 7}:5", // on 'print' + "$file:${LINE + 9}:1", // on ending '}' +]; + +var tests = [ + hasPausedAtStart, + setBreakpointAtUriAndLine(file, LINE), + runStepThroughProgramRecordingStops(stops), + checkRecordedStops(stops, expected) +]; + +main(args) { + runIsolateTestsSynchronous( + args, + tests, + 'breakpoint_on_if_null_1_test.dart', + testeeConcurrent: code, + pause_on_start: true, + pause_on_exit: true, + ); +} diff --git a/pkg/vm_service/test/breakpoint_on_if_null_2_test.dart b/pkg/vm_service/test/breakpoint_on_if_null_2_test.dart new file mode 100644 index 00000000000..69bee59739b --- /dev/null +++ b/pkg/vm_service/test/breakpoint_on_if_null_2_test.dart @@ -0,0 +1,59 @@ +// Copyright (c) 2019, 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. + +library breakpoint_in_parts_class; + +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +const int LINE = 21; +const String file = "breakpoint_on_if_null_2_test.dart"; + +dynamic compareWithMe = 43; + +code() { + compareWithMe = null; + foo(42); +} + +foo(dynamic args) { + if (args == compareWithMe) { + print("was null"); + } + if (args != compareWithMe) { + print("was not null"); + } + if (args == 42) { + print("was 42!"); + } +} + +List stops = []; + +List expected = [ + "$file:${LINE + 0}:12", // on '==' + "$file:${LINE + 3}:12", // on '!=' + "$file:${LINE + 4}:5", // on 'print' + "$file:${LINE + 6}:12", // on '==' + "$file:${LINE + 7}:5", // on 'print' + "$file:${LINE + 9}:1", // on ending '}' +]; + +var tests = [ + hasPausedAtStart, + setBreakpointAtUriAndLine(file, LINE), + runStepThroughProgramRecordingStops(stops), + checkRecordedStops(stops, expected) +]; + +main(args) { + runIsolateTestsSynchronous( + args, + tests, + 'breakpoint_on_if_null_2_test.dart', + testeeConcurrent: code, + pause_on_start: true, + pause_on_exit: true, + ); +} diff --git a/pkg/vm_service/test/breakpoint_on_if_null_3_test.dart b/pkg/vm_service/test/breakpoint_on_if_null_3_test.dart new file mode 100644 index 00000000000..92114100772 --- /dev/null +++ b/pkg/vm_service/test/breakpoint_on_if_null_3_test.dart @@ -0,0 +1,57 @@ +// Copyright (c) 2019, 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. + +library breakpoint_in_parts_class; + +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +const int LINE = 17; +const String file = "breakpoint_on_if_null_3_test.dart"; + +code() { + foo(42); +} + +foo(dynamic args) { + if (args == null) { + print("was null"); + } + if (args != null) { + print("was not null"); + } + if (args == 42) { + print("was 42!"); + } +} + +List stops = []; + +List expected = [ + "$file:${LINE + 0}:13", // on 'args' + "$file:${LINE + 1}:12", // on '==' + "$file:${LINE + 4}:12", // on '!=' + "$file:${LINE + 5}:5", // on 'print' + "$file:${LINE + 7}:12", // on '==' + "$file:${LINE + 8}:5", // on 'print' + "$file:${LINE + 10}:1", // on ending '}' +]; + +var tests = [ + hasPausedAtStart, + setBreakpointAtUriAndLine(file, LINE), + runStepThroughProgramRecordingStops(stops), + checkRecordedStops(stops, expected) +]; + +main(args) { + runIsolateTestsSynchronous( + args, + tests, + 'breakpoint_on_if_null_3_test.dart', + testeeConcurrent: code, + pause_on_start: true, + pause_on_exit: true, + ); +} diff --git a/pkg/vm_service/test/breakpoint_on_if_null_4_test.dart b/pkg/vm_service/test/breakpoint_on_if_null_4_test.dart new file mode 100644 index 00000000000..08b77604b2b --- /dev/null +++ b/pkg/vm_service/test/breakpoint_on_if_null_4_test.dart @@ -0,0 +1,60 @@ +// Copyright (c) 2019, 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. + +library breakpoint_in_parts_class; + +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +const int LINE = 20; +const String file = "breakpoint_on_if_null_4_test.dart"; + +dynamic compareWithMe = 43; + +code() { + compareWithMe = null; + foo(42); +} + +foo(dynamic args) { + if (args == compareWithMe) { + print("was null"); + } + if (args != compareWithMe) { + print("was not null"); + } + if (args == 42) { + print("was 42!"); + } +} + +List stops = []; + +List expected = [ + "$file:${LINE + 0}:13", // on 'args' + "$file:${LINE + 1}:12", // on '==' + "$file:${LINE + 4}:12", // on '!=' + "$file:${LINE + 5}:5", // on 'print' + "$file:${LINE + 7}:12", // on '==' + "$file:${LINE + 8}:5", // on 'print' + "$file:${LINE + 10}:1", // on ending '}' +]; + +var tests = [ + hasPausedAtStart, + setBreakpointAtUriAndLine(file, LINE), + runStepThroughProgramRecordingStops(stops), + checkRecordedStops(stops, expected) +]; + +main(args) { + runIsolateTestsSynchronous( + args, + tests, + 'breakpoint_on_if_null_4_test.dart', + testeeConcurrent: code, + pause_on_start: true, + pause_on_exit: true, + ); +} diff --git a/pkg/vm_service/test/breakpoint_partfile_test.dart b/pkg/vm_service/test/breakpoint_partfile_test.dart new file mode 100644 index 00000000000..a3c5fd15359 --- /dev/null +++ b/pkg/vm_service/test/breakpoint_partfile_test.dart @@ -0,0 +1,42 @@ +// Copyright (c) 2019, 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. + +library breakpoint_in_parts_class; + +import 'package:test_package/has_part.dart' as has_part; +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +const int LINE = 8; +const String breakpointFile = "package:test_package/the_part_2.dart"; +const String shortFile = "the_part_2.dart"; + +code() { + has_part.bar(); +} + +List stops = []; + +List expected = [ + "$shortFile:${LINE + 0}:3", // on 'print' + "$shortFile:${LINE + 1}:1" // on class ending '}' +]; + +var tests = [ + hasPausedAtStart, + setBreakpointAtUriAndLine(breakpointFile, LINE), + runStepThroughProgramRecordingStops(stops), + checkRecordedStops(stops, expected) +]; + +main(args) { + runIsolateTestsSynchronous( + args, + tests, + 'breakpoint_partfile_test.dart', + testeeConcurrent: code, + pause_on_start: true, + pause_on_exit: true, + ); +} diff --git a/pkg/vm_service/test/breakpoint_two_args_checked_test.dart b/pkg/vm_service/test/breakpoint_two_args_checked_test.dart new file mode 100644 index 00000000000..40d0dc65ebf --- /dev/null +++ b/pkg/vm_service/test/breakpoint_two_args_checked_test.dart @@ -0,0 +1,87 @@ +// Copyright (c) 2016, 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. +// VMOptions=--verbose_debug + +// TODO(bkonyi): consider deleting now that DBC is no more. +// This test was mostly interesting for DBC, which needed to patch two bytecodes +// to create a breakpoint for fast Smi ops. + +import 'dart:developer'; + +import 'package:test/test.dart'; +import 'package:vm_service/vm_service.dart'; + +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +const int LINE_A = 29; +const int LINE_B = 30; +const int LINE_C = 31; + +class NotGeneric {} + +testeeMain() { + final x = List.filled(1, null); + final y = 7; + debugger(); + print("Statement"); + x[0] = 3; // Line A. + x is NotGeneric; // Line B. + y & 4; // Line C. +} + +final tests = [ + hasStoppedAtBreakpoint, + +// Add breakpoints. + (VmService service, IsolateRef isolateRef) async { + final isolateId = isolateRef.id!; + final isolate = await service.getIsolate(isolateId); + Library rootLib = + await service.getObject(isolateId, isolate.rootLib!.id!) as Library; + + final script = + await service.getObject(isolateId, rootLib.scripts![0].id!) as Script; + final scriptId = script.id!; + + final bpt1 = await service.addBreakpoint(isolateId, scriptId, LINE_A); + print(bpt1); + expect(bpt1.resolved, isTrue); + expect(script.getLineNumberFromTokenPos(bpt1.location!.tokenPos), + equals(LINE_A)); + + final bpt2 = await service.addBreakpoint(isolateId, scriptId, LINE_B); + print(bpt2); + expect(bpt2.resolved, isTrue); + expect(script.getLineNumberFromTokenPos(bpt2.location!.tokenPos), + equals(LINE_B)); + + final bpt3 = await service.addBreakpoint(isolateId, scriptId, LINE_C); + print(bpt3); + expect(bpt3.resolved, isTrue); + expect(script.getLineNumberFromTokenPos(bpt3.location!.tokenPos), + equals(LINE_C)); + }, + + resumeIsolate, + + hasStoppedAtBreakpoint, + stoppedAtLine(LINE_A), + resumeIsolate, + + hasStoppedAtBreakpoint, + stoppedAtLine(LINE_B), + resumeIsolate, + + hasStoppedAtBreakpoint, + stoppedAtLine(LINE_C), + resumeIsolate, +]; + +main(args) => runIsolateTests( + args, + tests, + 'breakpoint_two_args_checked_test.dart', + testeeConcurrent: testeeMain, + ); diff --git a/pkg/vm_service/test/breakpoints_with_mixin_lib1.dart b/pkg/vm_service/test/breakpoints_with_mixin_lib1.dart new file mode 100644 index 00000000000..3c43715440f --- /dev/null +++ b/pkg/vm_service/test/breakpoints_with_mixin_lib1.dart @@ -0,0 +1,7 @@ +// Copyright (c) 2020, 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 "breakpoints_with_mixin_lib3.dart"; + +class Test1 extends Object with Foo {} diff --git a/pkg/vm_service/test/breakpoints_with_mixin_lib2.dart b/pkg/vm_service/test/breakpoints_with_mixin_lib2.dart new file mode 100644 index 00000000000..c0541763e3c --- /dev/null +++ b/pkg/vm_service/test/breakpoints_with_mixin_lib2.dart @@ -0,0 +1,7 @@ +// Copyright (c) 2020, 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 "breakpoints_with_mixin_lib3.dart"; + +class Test2 extends Object with Foo {} diff --git a/pkg/vm_service/test/breakpoints_with_mixin_lib3.dart b/pkg/vm_service/test/breakpoints_with_mixin_lib3.dart new file mode 100644 index 00000000000..a7329de0d80 --- /dev/null +++ b/pkg/vm_service/test/breakpoints_with_mixin_lib3.dart @@ -0,0 +1,15 @@ +// Copyright (c) 2020, 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. + +class Foo { + foo() { + print("I should be breakable!"); + } +} + +class Bar { + bar() { + print("I should be breakable too!"); + } +} diff --git a/pkg/vm_service/test/breakpoints_with_mixin_test.dart b/pkg/vm_service/test/breakpoints_with_mixin_test.dart new file mode 100644 index 00000000000..f382628495b --- /dev/null +++ b/pkg/vm_service/test/breakpoints_with_mixin_test.dart @@ -0,0 +1,62 @@ +// Copyright (c) 2020, 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 "breakpoints_with_mixin_lib1.dart"; +import "breakpoints_with_mixin_lib2.dart"; +import "breakpoints_with_mixin_lib3.dart"; +import 'common/service_test_common.dart'; +import 'common/test_helper.dart'; + +const String testFilename = "breakpoints_with_mixin_test.dart"; +const int testCodeLineStart = 17; +const String lib3Filename = "breakpoints_with_mixin_lib3.dart"; +const int lib3Bp1 = 7; +const int lib3Bp2 = 13; + +void code() { + Test1 test1 = Test1(); + test1.foo(); + Test2 test2 = Test2(); + test2.foo(); + Foo foo = Foo(); + foo.foo(); + Bar bar = Bar(); + bar.bar(); + test1.foo(); + test2.foo(); + foo.foo(); + bar.bar(); +} + +List stops = []; + +List expected = [ + "$lib3Filename:$lib3Bp1:5 ($testFilename:${testCodeLineStart + 2}:9)", + "$lib3Filename:$lib3Bp1:5 ($testFilename:${testCodeLineStart + 4}:9)", + "$lib3Filename:$lib3Bp1:5 ($testFilename:${testCodeLineStart + 6}:7)", + "$lib3Filename:$lib3Bp2:5 ($testFilename:${testCodeLineStart + 8}:7)", + "$lib3Filename:$lib3Bp1:5 ($testFilename:${testCodeLineStart + 9}:9)", + "$lib3Filename:$lib3Bp1:5 ($testFilename:${testCodeLineStart + 10}:9)", + "$lib3Filename:$lib3Bp1:5 ($testFilename:${testCodeLineStart + 11}:7)", + "$lib3Filename:$lib3Bp2:5 ($testFilename:${testCodeLineStart + 12}:7)", +]; + +var tests = [ + hasPausedAtStart, + setBreakpointAtUriAndLine(lib3Filename, lib3Bp1), + setBreakpointAtUriAndLine(lib3Filename, lib3Bp2), + resumeProgramRecordingStops(stops, true), + checkRecordedStops(stops, expected) +]; + +main(args) { + runIsolateTestsSynchronous( + args, + tests, + 'breakpoints_with_mixin_test.dart', + testeeConcurrent: code, + pause_on_start: true, + pause_on_exit: true, + ); +} diff --git a/pkg/vm_service/test/common/service_test_common.dart b/pkg/vm_service/test/common/service_test_common.dart index 4f616ab3d38..6e84d16d72d 100644 --- a/pkg/vm_service/test/common/service_test_common.dart +++ b/pkg/vm_service/test/common/service_test_common.dart @@ -6,6 +6,7 @@ library service_test_common; import 'dart:async'; +import 'package:path/path.dart'; import 'package:test/test.dart'; import 'package:vm_service/vm_service.dart'; @@ -131,6 +132,16 @@ IsolateTest setBreakpointAtLine(int line) { }; } +IsolateTest setBreakpointAtUriAndLine(String uri, int line) { + return (VmService service, IsolateRef isolateRef) async { + print("Setting breakpoint for line $line in $uri"); + Breakpoint bpt = + await service.addBreakpointWithScriptUri(isolateRef.id!, uri, line); + print("Breakpoint is $bpt"); + expect(bpt, isNotNull); + }; +} + IsolateTest stoppedAtLine(int line) { return (VmService service, IsolateRef isolateRef) async { print("Checking we are at line $line"); @@ -221,3 +232,154 @@ Future stepOut(VmService service, IsolateRef isolateRef) async { await hasStoppedAtBreakpoint(service, isolateRef); await _unsubscribeDebugStream(service); } + +IsolateTest resumeProgramRecordingStops( + List recordStops, bool includeCaller) { + return (VmService service, IsolateRef isolateRef) async { + final completer = Completer(); + + late StreamSubscription subscription; + subscription = service.onDebugEvent.listen((event) async { + if (event.kind == EventKind.kPauseBreakpoint) { + final stack = await service.getStack(isolateRef.id!); + final frames = stack.frames!; + expect(frames.length, greaterThanOrEqualTo(2)); + + String brokeAt = + await _locationToString(service, isolateRef, frames[0]); + if (includeCaller) { + brokeAt = + '$brokeAt (${await _locationToString(service, isolateRef, frames[1])})'; + } + recordStops.add(brokeAt); + await service.resume(isolateRef.id!); + } else if (event.kind == EventKind.kPauseExit) { + await subscription.cancel(); + await service.streamCancel(EventStreams.kDebug); + completer.complete(); + } + }); + + await service.streamListen(EventStreams.kDebug); + await service.resume(isolateRef.id!); + return completer.future; + }; +} + +Future _locationToString( + VmService service, + IsolateRef isolateRef, + Frame frame, +) async { + final location = frame.location!; + Script script = + await service.getObject(isolateRef.id!, 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'; +} + +IsolateTest runStepThroughProgramRecordingStops(List recordStops) { + return (VmService service, IsolateRef isolateRef) async { + final completer = Completer(); + + late StreamSubscription subscription; + subscription = service.onDebugEvent.listen((event) async { + if (event.kind == EventKind.kPauseBreakpoint) { + final isolate = await service.getIsolate(isolateRef.id!); + final frame = isolate.pauseEvent!.topFrame!; + recordStops.add(await _locationToString(service, isolateRef, frame)); + if (event.atAsyncSuspension ?? false) { + await service.resume(isolateRef.id!, + step: StepOption.kOverAsyncSuspension); + } else { + await service.resume(isolateRef.id!, step: StepOption.kOver); + } + } else if (event.kind == EventKind.kPauseExit) { + await subscription.cancel(); + await service.streamCancel(EventStreams.kDebug); + completer.complete(); + } + }); + + await service.streamListen(EventStreams.kDebug); + await service.resume(isolateRef.id!); + return completer.future; + }; +} + +IsolateTest checkRecordedStops( + List recordStops, List expectedStops, + {bool removeDuplicates = false, + bool debugPrint = false, + String? debugPrintFile, + int? debugPrintLine}) { + return (VmService service, IsolateRef isolate) async { + if (debugPrint) { + for (int i = 0; i < recordStops.length; i++) { + String line = recordStops[i]; + String output = line; + int firstColon = line.indexOf(":"); + int lastColon = line.lastIndexOf(":"); + if (debugPrintFile != null && + debugPrintLine != null && + firstColon > 0 && + lastColon > 0) { + int lineNumber = int.parse(line.substring(firstColon + 1, lastColon)); + int relativeLineNumber = lineNumber - debugPrintLine; + var columnNumber = line.substring(lastColon + 1); + var file = line.substring(0, firstColon); + if (file == debugPrintFile) { + output = '\$file:\${LINE+$relativeLineNumber}:$columnNumber'; + } + } + String comma = i == recordStops.length - 1 ? "" : ","; + print('"$output"$comma'); + } + } + if (removeDuplicates) { + recordStops = removeAdjacentDuplicates(recordStops); + expectedStops = removeAdjacentDuplicates(expectedStops); + } + + // Single stepping may record extra stops. + // Allow the extra ones as long as the expected ones are recorded. + int i = 0; + int j = 0; + while (i < recordStops.length && j < expectedStops.length) { + if (recordStops[i] != expectedStops[j]) { + // Check if recordStops[i] is an extra stop. + int k = i + 1; + while (k < recordStops.length && recordStops[k] != expectedStops[j]) { + k++; + } + if (k < recordStops.length) { + // Allow and ignore extra recorded stops from i to k-1. + i = k; + } else { + // This will report an error. + expect(recordStops[i], expectedStops[j]); + } + } + i++; + j++; + } + + expect(recordStops.length >= expectedStops.length, true, + reason: "Expects at least ${expectedStops.length} breaks, " + "got ${recordStops.length}."); + }; +} + +List removeAdjacentDuplicates(List fromList) { + List result = []; + String? latestLine; + for (String s in fromList) { + if (s == latestLine) continue; + latestLine = s; + result.add(s); + } + return result; +} diff --git a/pkg/vm_service/test/common/test_helper.dart b/pkg/vm_service/test/common/test_helper.dart index 45ce97b063e..585722f1015 100644 --- a/pkg/vm_service/test/common/test_helper.dart +++ b/pkg/vm_service/test/common/test_helper.dart @@ -277,6 +277,7 @@ class _ServiceTesterRunner { vm = await vmServiceConnectUri(serviceWebsocketAddress); print('Done loading VM'); isolate = await getFirstIsolate(vm); + print('Got first isolate'); }); }); @@ -326,19 +327,24 @@ class _ServiceTesterRunner { Completer? completer = Completer(); late StreamSubscription subscription; subscription = service.onIsolateEvent.listen((Event event) async { + print('Isolate event: $event'); if (completer == null) { await subscription.cancel(); return; } if (event.kind == EventKind.kIsolateRunnable) { + print(event.isolate!.name); vm = await service.getVM(); - assert(vmIsolates.isNotEmpty); + //assert(vmIsolates.isNotEmpty); await subscription.cancel(); - completer!.complete(vmIsolates.first); + await service.streamCancel(EventStreams.kIsolate); + completer!.complete(event.isolate!); completer = null; } }); + await service.streamListen(EventStreams.kIsolate); + // The isolate may have started before we subscribed. vm = await service.getVM(); if (vmIsolates.isNotEmpty) { diff --git a/pkg/vm_service/test/test_package/has_part.dart b/pkg/vm_service/test/test_package/has_part.dart new file mode 100644 index 00000000000..fb68ee773cd --- /dev/null +++ b/pkg/vm_service/test/test_package/has_part.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2019, 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. + +library has_part; + +part 'the_part.dart'; +part 'the_part_2.dart'; + +barz() { + print('in bar!'); +} + +fooz() { + print('in foo!'); + bar(); +} + +main() { + Foo10 foo = Foo10("Foo!"); + print(foo); +} diff --git a/pkg/vm_service/test/test_package/pubspec.yaml b/pkg/vm_service/test/test_package/pubspec.yaml new file mode 100644 index 00000000000..b5bc9d7ac78 --- /dev/null +++ b/pkg/vm_service/test/test_package/pubspec.yaml @@ -0,0 +1,4 @@ +name: test_package +publish_to: none +environment: + sdk: '>=2.12.0-0 <3.0.0' diff --git a/pkg/vm_service/test/test_package/the_part.dart b/pkg/vm_service/test/test_package/the_part.dart new file mode 100644 index 00000000000..d8545f87100 --- /dev/null +++ b/pkg/vm_service/test/test_package/the_part.dart @@ -0,0 +1,91 @@ +// Copyright (c) 2019, 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. + +part of has_part; + +void foo() { + print("lalala"); +} + +class Foo1 { + final foo; + + Foo1(this.foo) { + print("hello from foo!"); + } +} + +class Foo2 { + final foo; + + Foo2(this.foo) { + print("hello from foo!"); + } +} + +class Foo3 { + final foo; + + Foo3(this.foo) { + print("hello from foo!"); + } +} + +class Foo4 { + final foo; + + Foo4(this.foo) { + print("hello from foo!"); + } +} + +class Foo5 { + final foo; + + Foo5(this.foo) { + print("hello from foo!"); + } +} + +class Foo6 { + final foo; + + Foo6(this.foo) { + print("hello from foo!"); + } +} + +class Foo7 { + final foo; + + Foo7(this.foo) { + print("hello from foo!"); + } +} + +class Foo8 { + final foo; + + Foo8(this.foo) { + print("hello from foo!"); + } +} + +class Foo9 { + final foo; + + Foo9(this.foo) { + print("hello from foo!"); + } +} + +class Foo10 { + final foo; + + Foo10(this.foo) { + print("hello from foo!"); + } +} + +var foo2 = foo() as dynamic; diff --git a/pkg/vm_service/test/test_package/the_part_2.dart b/pkg/vm_service/test/test_package/the_part_2.dart new file mode 100644 index 00000000000..c38df9cdb8a --- /dev/null +++ b/pkg/vm_service/test/test_package/the_part_2.dart @@ -0,0 +1,9 @@ +// Copyright (c) 2019, 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. + +part of has_part; + +void bar() { + print('Should break here'); +} diff --git a/tools/generate_package_config.dart b/tools/generate_package_config.dart index 066eb2c8635..d6c7b5613da 100644 --- a/tools/generate_package_config.dart +++ b/tools/generate_package_config.dart @@ -56,6 +56,7 @@ void main(List args) { packageDirectory('runtime/observatory_2'), packageDirectory( 'runtime/observatory_2/tests/service_2/observatory_test_package_2'), + packageDirectory('pkg/vm_service/test/test_package'), packageDirectory('sdk/lib/_internal/sdk_library_metadata'), packageDirectory('third_party/devtools/devtools_server'), packageDirectory('third_party/devtools/devtools_shared'),