From 0dc5ea4a950ed2215befd1d924f16bcd5589927d Mon Sep 17 00:00:00 2001 From: Larry McKenzie Date: Wed, 30 Oct 2019 10:35:45 -0700 Subject: [PATCH] Added a null check for ranges in the sourceReport map. (#43667) --- .../lib/src/test/coverage_collector.dart | 27 +- .../coverage_collector_test.dart | 423 +++++++++++++++++- 2 files changed, 440 insertions(+), 10 deletions(-) diff --git a/packages/flutter_tools/lib/src/test/coverage_collector.dart b/packages/flutter_tools/lib/src/test/coverage_collector.dart index ed3bdf61d54..af19fd4736a 100644 --- a/packages/flutter_tools/lib/src/test/coverage_collector.dart +++ b/packages/flutter_tools/lib/src/test/coverage_collector.dart @@ -245,20 +245,37 @@ void _buildCoverageMap( final Map> hitMaps = >{}; for (String scriptId in scripts.keys) { final Map sourceReport = sourceReports[scriptId]; - for (Map range in sourceReport['ranges']) { + final Map scripts = sourceReport['scripts']; + final List> ranges = sourceReport['ranges']; + // Ranges may sometimes be null for a report. + if (ranges == null || scripts == null) { + continue; + } + for (Map range in ranges) { final Map coverage = range['coverage']; + final String scriptIndex = range['scriptIndex']; // Coverage reports may sometimes be null for a Script. - if (coverage == null) { + if (coverage == null || scriptIndex == null) { + continue; + } + final Map scriptRef = scripts[scriptIndex]; + if (scriptRef == null) { continue; } - final Map scriptRef = sourceReport['scripts'][range['scriptIndex']]; final String uri = scriptRef['uri']; - + final String id = scriptRef['id']; + if (uri == null || id == null) { + continue; + } + final Map scriptById = scripts[id]; + if (scriptById == null) { + continue; + } hitMaps[uri] ??= {}; final Map hitMap = hitMaps[uri]; final List hits = coverage['hits']; final List misses = coverage['misses']; - final List tokenPositions = scripts[scriptRef['id']]['tokenPosTable']; + final List tokenPositions = scriptById['tokenPosTable']; // The token positions can be null if the script has no coverable lines. if (tokenPositions == null) { continue; diff --git a/packages/flutter_tools/test/general.shard/coverage_collector_test.dart b/packages/flutter_tools/test/general.shard/coverage_collector_test.dart index 1d277c0b5c0..85980592051 100644 --- a/packages/flutter_tools/test/general.shard/coverage_collector_test.dart +++ b/packages/flutter_tools/test/general.shard/coverage_collector_test.dart @@ -20,15 +20,428 @@ void main() { test('Coverage collector Can handle coverage sentinenl data', () async { when(mockVMService.vm.isolates.first.invokeRpcRaw('getScripts', params: anyNamed('params'))) - .thenAnswer((Invocation invocation) async { - return {'type': 'Sentinel', 'kind': 'Collected', 'valueAsString': ''}; - }); + .thenAnswer((Invocation invocation) async { + return {'type': 'Sentinel', 'kind': 'Collected', 'valueAsString': ''}; + }); final Map result = await collect(null, (String predicate) => true, connector: (Uri uri) async { return mockVMService; }); expect(result, {'type': 'CodeCoverage', 'coverage': []}); }); + + test('Coverage collector can handle null scripts value', () async { + when(mockVMService.vm.isolates.first.invokeRpcRaw('getScripts', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': >[ + {'uri': 'some_uri', 'id': 'some_id'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getSourceReport', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'ranges': >[{}] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getObject', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return {}; + }); + final Map result = await collect(null, (String predicate) => true, connector: (Uri uri) async { + return mockVMService; + }); + + expect(result, {'type': 'CodeCoverage', 'coverage': []}); + }); + + test('Coverage collector can handle null ranges value', () async { + when(mockVMService.vm.isolates.first.invokeRpcRaw('getScripts', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': >[ + {'uri': 'some_uri', 'id': 'some_id'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getSourceReport', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return {'scripts': {}}; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getObject', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return {}; + }); + final Map result = await collect(null, (String predicate) => true, connector: (Uri uri) async { + return mockVMService; + }); + + expect(result, {'type': 'CodeCoverage', 'coverage': []}); + }); + + test('Coverage collector can handle null scriptIndex values', () async { + when(mockVMService.vm.isolates.first.invokeRpcRaw('getScripts', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': >[ + {'uri': 'some_uri', 'id': 'some_id'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getSourceReport', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': {'uri': 'some_uri', 'id': 'some_id'}, + 'ranges': >[ + {'coverage': {}} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getObject', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return {}; + }); + final Map result = await collect(null, (String predicate) => true, connector: (Uri uri) async { + return mockVMService; + }); + + expect(result, {'type': 'CodeCoverage', 'coverage': []}); + }); + + test('Coverage collector can handle null scriptRef values', () async { + when(mockVMService.vm.isolates.first.invokeRpcRaw('getScripts', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': >[ + {'uri': 'some_uri', 'id': 'some_id'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getSourceReport', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': {'uri': 'some_uri', 'id': 'some_id'}, + 'ranges': >[ + {'coverage': {}, 'scriptIndex': 'some_value'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getObject', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return {}; + }); + final Map result = await collect(null, (String predicate) => true, connector: (Uri uri) async { + return mockVMService; + }); + + expect(result, {'type': 'CodeCoverage', 'coverage': []}); + }); + + test('Coverage collector can handle null scriptRef uri values', () async { + when(mockVMService.vm.isolates.first.invokeRpcRaw('getScripts', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': >[ + {'uri': 'some_uri', 'id': 'some_id'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getSourceReport', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': {'uri': 'some_uri', 'id': 'some_id', 'index_0': {}}, + 'ranges': >[ + {'coverage': {}, 'scriptIndex': 'index_0'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getObject', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return {}; + }); + final Map result = await collect(null, (String predicate) => true, connector: (Uri uri) async { + return mockVMService; + }); + + expect(result, {'type': 'CodeCoverage', 'coverage': []}); + }); + + test('Coverage collector can handle null scriptRef id values', () async { + when(mockVMService.vm.isolates.first.invokeRpcRaw('getScripts', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': >[ + {'uri': 'some_uri', 'id': 'some_id'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getSourceReport', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': { + 'uri': 'some_uri', + 'id': 'some_id', + 'index_0': {'uri': 'some_uri'} + }, + 'ranges': >[ + {'coverage': {}, 'scriptIndex': 'index_0'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getObject', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return {}; + }); + final Map result = await collect(null, (String predicate) => true, connector: (Uri uri) async { + return mockVMService; + }); + + expect(result, {'type': 'CodeCoverage', 'coverage': []}); + }); + + test('Coverage collector can handle null scriptById values', () async { + when(mockVMService.vm.isolates.first.invokeRpcRaw('getScripts', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': >[ + {'uri': 'some_uri', 'id': 'some_id'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getSourceReport', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': { + 'uri': 'some_uri', + 'id': 'some_id', + 'index_0': {'uri': 'some_uri', 'id': '01'}, + }, + 'ranges': >[ + {'coverage': {}, 'scriptIndex': 'index_0'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getObject', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return {}; + }); + final Map result = await collect(null, (String predicate) => true, connector: (Uri uri) async { + return mockVMService; + }); + + expect(result, {'type': 'CodeCoverage', 'coverage': []}); + }); + + test('Coverage collector can handle null tokenPosTable values', () async { + when(mockVMService.vm.isolates.first.invokeRpcRaw('getScripts', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': >[ + {'uri': 'some_uri', 'id': 'some_id'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getSourceReport', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': { + 'uri': 'some_uri', + 'id': 'some_id', + 'index_0': {'uri': 'some_uri', 'id': '01'}, + '01': {}, + }, + 'ranges': >[ + {'coverage': {}, 'scriptIndex': 'index_0'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getObject', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return {}; + }); + final Map result = await collect(null, (String predicate) => true, connector: (Uri uri) async { + return mockVMService; + }); + + expect(result, { + 'type': 'CodeCoverage', + 'coverage': >[ + { + 'source': 'some_uri', + 'script': { + 'type': '@Script', + 'fixedId': true, + 'id': 'libraries/1/scripts/some_uri', + 'uri': 'some_uri', + '_kind': 'library' + }, + 'hits': >[] + } + ] + }); + }); + + test('Coverage collector can handle null hits values', () async { + when(mockVMService.vm.isolates.first.invokeRpcRaw('getScripts', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': >[ + {'uri': 'some_uri', 'id': 'some_id'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getSourceReport', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': { + 'uri': 'some_uri', + 'id': 'some_id', + 'index_0': {'uri': 'some_uri', 'id': '01'}, + '01': {'tokenPosTable': []}, + }, + 'ranges': >[ + {'coverage': {}, 'scriptIndex': 'index_0'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getObject', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return {}; + }); + final Map result = await collect(null, (String predicate) => true, connector: (Uri uri) async { + return mockVMService; + }); + + expect(result, { + 'type': 'CodeCoverage', + 'coverage': >[ + { + 'source': 'some_uri', + 'script': { + 'type': '@Script', + 'fixedId': true, + 'id': 'libraries/1/scripts/some_uri', + 'uri': 'some_uri', + '_kind': 'library' + }, + 'hits': >[] + } + ] + }); + }); + + test('Coverage collector can handle null misses values', () async { + when(mockVMService.vm.isolates.first.invokeRpcRaw('getScripts', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': >[ + {'uri': 'some_uri', 'id': 'some_id'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getSourceReport', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': { + 'uri': 'some_uri', + 'id': 'some_id', + 'index_0': {'uri': 'some_uri', 'id': '01'}, + '01': {'tokenPosTable': []}, + }, + 'ranges': >[ + { + 'coverage': {'hits': []}, + 'scriptIndex': 'index_0' + } + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getObject', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return {}; + }); + final Map result = await collect(null, (String predicate) => true, connector: (Uri uri) async { + return mockVMService; + }); + + expect(result, { + 'type': 'CodeCoverage', + 'coverage': >[ + { + 'source': 'some_uri', + 'script': { + 'type': '@Script', + 'fixedId': true, + 'id': 'libraries/1/scripts/some_uri', + 'uri': 'some_uri', + '_kind': 'library' + }, + 'hits': >[] + } + ] + }); + }); + + test('Coverage collector should process hits and misses', () async { + when(mockVMService.vm.isolates.first.invokeRpcRaw('getScripts', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': >[ + {'uri': 'some_uri', 'id': 'some_id'} + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getSourceReport', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return { + 'scripts': { + 'uri': 'some_uri', + 'id': 'some_id', + 'index_0': {'uri': 'some_uri', 'id': '01'}, + '01': { + 'tokenPosTable': [ + [1, 100, 5, 101, 8], + [2, 102, 7], + ] + }, + }, + 'ranges': >[ + { + 'coverage': { + 'hits': [100, 101], + 'misses': [102], + }, + 'scriptIndex': 'index_0' + } + ] + }; + }); + when(mockVMService.vm.isolates.first.invokeRpcRaw('getObject', params: anyNamed('params'))) + .thenAnswer((Invocation invocation) async { + return {}; + }); + final Map result = await collect(null, (String predicate) => true, connector: (Uri uri) async { + return mockVMService; + }); + + expect(result, { + 'type': 'CodeCoverage', + 'coverage': >[ + { + 'source': 'some_uri', + 'script': { + 'type': '@Script', + 'fixedId': true, + 'id': 'libraries/1/scripts/some_uri', + 'uri': 'some_uri', + '_kind': 'library' + }, + 'hits': [1, 2, 2, 0] + } + ] + }); + }); } class MockVMService extends Mock implements VMService { @@ -38,13 +451,13 @@ class MockVMService extends Mock implements VMService { class MockVM extends Mock implements VM { @override - final List isolates = [ MockIsolate() ]; + final List isolates = [MockIsolate()]; } class MockIsolate extends Mock implements Isolate {} class MockProcess extends Mock implements Process { - final Completercompleter = Completer(); + final Completer completer = Completer(); @override Future get exitCode => completer.future;