| // 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. |
| |
| 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'; |
| |
| // AUTOGENERATED START |
| // |
| // Update these constants by running: |
| // |
| // dart pkg/vm_service/test/update_line_numbers.dart pkg/vm_service/test/get_source_report_test.dart |
| // |
| const LINE_A = 38; |
| const LINE_B = 61; |
| const OFFSET_START = 0872; |
| const OFFSET_COMPARE = 0943; |
| const OFFSET_PRINT_NEGATIVE = 0984; |
| const OFFSET_PRINT_POSITIVE = 1050; |
| const OFFSET_DEBUGGER = 1101; |
| const OFFSET_END = 1146; |
| // AUTOGENERATED END |
| |
| int globalVar = 100; |
| |
| class MyClass { |
| /* OFFSET_START */ static void myFunction(int value) { |
| if (value /* OFFSET_COMPARE */ < 0) { |
| /* OFFSET_PRINT_NEGATIVE */ print('negative'); |
| } else { |
| /* OFFSET_PRINT_POSITIVE */ print('positive'); |
| } |
| /* OFFSET_DEBUGGER */ debugger(); // LINE_A |
| /* OFFSET_END */ |
| } |
| |
| static void otherFunction(int value) { |
| if (value < 0) { |
| print('otherFunction <'); |
| } else { |
| print('otherFunction >='); |
| } |
| } |
| } |
| |
| void testFunction() { |
| MyClass.otherFunction(-100); |
| MyClass.myFunction(10000); |
| } |
| |
| class MyConstClass { |
| const MyConstClass(); |
| static const MyConstClass instance = MyConstClass(); |
| |
| void foo() { |
| debugger(); // LINE_B |
| } |
| } |
| |
| void testFunction2() { |
| MyConstClass.instance.foo(); |
| } |
| |
| bool allRangesCompiled(SourceReport coverage) { |
| for (final range in coverage.ranges!) { |
| if (!range.compiled!) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| final tests = <IsolateTest>[ |
| hasStoppedAtBreakpoint, |
| stoppedAtLine(LINE_A), |
| (VmService service, IsolateRef isolateRef) async { |
| final isolateId = isolateRef.id!; |
| final stack = await service.getStack(isolateId); |
| final func = stack.frames!.first.function!; |
| final scriptId = func.location!.script!.id!; |
| |
| final expectedRange = SourceReportRange( |
| scriptIndex: 0, |
| startPos: OFFSET_START, |
| endPos: OFFSET_END, |
| compiled: true, |
| coverage: SourceReportCoverage( |
| hits: const [ |
| OFFSET_START, |
| OFFSET_COMPARE, |
| OFFSET_PRINT_POSITIVE, |
| OFFSET_DEBUGGER, |
| ], |
| misses: [OFFSET_PRINT_NEGATIVE], |
| ), |
| ); |
| |
| // Full script |
| var coverage = await service.getSourceReport( |
| isolateId, |
| [SourceReportKind.kCoverage], |
| scriptId: scriptId, |
| ); |
| |
| var ranges = coverage.ranges!; |
| expect(ranges.length, greaterThanOrEqualTo(10)); |
| // TODO(bkonyi): implement operator== properly. |
| expect(ranges[0].toJson(), expectedRange.toJson()); |
| |
| var scripts = coverage.scripts!; |
| expect(coverage.scripts!.length, 1); |
| expect(scripts[0].uri!, endsWith('get_source_report_test.dart')); |
| expect(allRangesCompiled(coverage), false); |
| |
| // Force compilation. |
| coverage = await service.getSourceReport( |
| isolateId, |
| [SourceReportKind.kCoverage], |
| scriptId: scriptId, |
| forceCompile: true, |
| ); |
| ranges = coverage.ranges!; |
| expect(ranges.length, greaterThanOrEqualTo(10)); |
| expect(allRangesCompiled(coverage), isTrue); |
| |
| // One function |
| coverage = await service.getSourceReport( |
| isolateId, |
| [SourceReportKind.kCoverage], |
| scriptId: scriptId, |
| tokenPos: func.location!.tokenPos!, |
| endTokenPos: func.location!.endTokenPos!, |
| ); |
| ranges = coverage.ranges!; |
| scripts = coverage.scripts!; |
| expect(ranges.length, 1); |
| // TODO(bkonyi): implement operator== properly. |
| expect(ranges[0].toJson(), expectedRange.toJson()); |
| expect(scripts.length, 1); |
| expect(scripts[0].uri!, endsWith('get_source_report_test.dart')); |
| |
| // Full isolate |
| coverage = await service.getSourceReport( |
| isolateId, |
| [SourceReportKind.kCoverage], |
| ); |
| ranges = coverage.ranges!; |
| scripts = coverage.scripts!; |
| expect(ranges.length, greaterThan(1)); |
| expect(scripts.length, greaterThan(1)); |
| |
| // Full isolate |
| coverage = await service.getSourceReport( |
| isolateId, |
| [SourceReportKind.kCoverage], |
| forceCompile: true, |
| ); |
| ranges = coverage.ranges!; |
| scripts = coverage.scripts!; |
| expect(ranges.length, greaterThan(1)); |
| expect(scripts.length, greaterThan(1)); |
| |
| // Multiple reports (make sure enum list parameter parsing works). |
| coverage = await service.getSourceReport( |
| isolateId, |
| [ |
| SourceReportKind.kCoverage, |
| SourceReportKind.kPossibleBreakpoints, |
| '_CallSites', |
| ], |
| scriptId: scriptId, |
| tokenPos: func.location!.tokenPos!, |
| endTokenPos: func.location!.endTokenPos!, |
| ); |
| ranges = coverage.ranges!; |
| expect(ranges.length, 1); |
| final range = ranges[0]; |
| expect(coverage.json!['ranges'][0].containsKey('callSites'), true); |
| expect(range.coverage, isNotNull); |
| expect(range.possibleBreakpoints, isNotNull); |
| |
| // missing scriptId with tokenPos. |
| bool caughtException = false; |
| try { |
| await service.getSourceReport( |
| isolateId, |
| [SourceReportKind.kCoverage], |
| tokenPos: func.location!.tokenPos!, |
| ); |
| fail('Unreachable'); |
| } on RPCError catch (e) { |
| caughtException = true; |
| expect(e.code, RPCErrorKind.kInvalidParams.code); |
| expect( |
| e.details, |
| "getSourceReport: the 'tokenPos' parameter requires the " |
| "'scriptId' parameter"); |
| } |
| expect(caughtException, true); |
| |
| // missing scriptId with endTokenPos. |
| caughtException = false; |
| try { |
| await service.getSourceReport( |
| isolateId, |
| [SourceReportKind.kCoverage], |
| endTokenPos: func.location!.endTokenPos!, |
| ); |
| fail('Unreachable'); |
| } on RPCError catch (e) { |
| caughtException = true; |
| expect(e.code, RPCErrorKind.kInvalidParams.code); |
| expect( |
| e.details, |
| "getSourceReport: the 'endTokenPos' parameter requires the " |
| "'scriptId' parameter"); |
| } |
| expect(caughtException, true); |
| }, |
| ]; |
| |
| void main([args = const <String>[]]) => runIsolateTests( |
| args, |
| tests, |
| 'get_source_report_test.dart', |
| testeeConcurrent: testFunction, |
| ); |