// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. @Tags(['integration']) library; import 'dart:async'; import 'dart:convert' show json; import 'dart:io'; import 'package:coverage/coverage.dart'; import 'package:coverage/src/util.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'package:test_process/test_process.dart'; import 'test_util.dart'; final _isolateLibPath = p.join('test', 'test_files', 'test_app_isolate.dart'); final _collectAppPath = p.join('bin', 'collect_coverage.dart'); final _sampleAppFileUri = p.toUri(p.absolute(testAppPath)).toString(); final _isolateLibFileUri = p.toUri(p.absolute(_isolateLibPath)).toString(); void main() { test('collect_coverage', () async { final resultString = await _getCoverageResult(); // analyze the output json final coverage = coverageDataFromJson(json.decode(resultString) as Map); expect(coverage, isNotEmpty); final sources = coverage.sources(); for (var sampleCoverageData in sources[_sampleAppFileUri]!) { expect(sampleCoverageData['hits'], isNotNull); } for (var sampleCoverageData in sources[_isolateLibFileUri]!) { expect(sampleCoverageData['hits'], isNotEmpty); } }); test('createHitmap returns a sorted hitmap', () async { final coverage = [ { 'source': 'foo', 'script': '{type: @Script, fixedId: true, ' 'id: bar.dart, uri: bar.dart, _kind: library}', 'hits': [ 45, 1, 46, 1, 49, 0, 50, 0, 15, 1, 16, 2, 17, 2, ] } ]; // ignore: deprecated_member_use_from_same_package final hitMap = await createHitmap( coverage.cast>(), ); final expectedHits = {15: 1, 16: 2, 17: 2, 45: 1, 46: 1, 49: 0, 50: 0}; expect(hitMap['foo'], expectedHits); }); test('HitMap.parseJson returns a sorted hitmap', () async { final coverage = [ { 'source': 'foo', 'script': '{type: @Script, fixedId: true, ' 'id: bar.dart, uri: bar.dart, _kind: library}', 'hits': [ 45, 1, 46, 1, 49, 0, 50, 0, 15, 1, 16, 2, 17, 2, ] } ]; final hitMap = await HitMap.parseJson( coverage.cast>(), ); final expectedHits = {15: 1, 16: 2, 17: 2, 45: 1, 46: 1, 49: 0, 50: 0}; expect(hitMap['foo']?.lineHits, expectedHits); }); test('HitMap.parseJson', () async { final resultString = await _collectCoverage(true, true); final jsonResult = json.decode(resultString) as Map; final coverage = jsonResult['coverage'] as List; final hitMap = await HitMap.parseJson( coverage.cast>(), ); expect(hitMap, contains(_sampleAppFileUri)); final isolateFile = hitMap[_isolateLibFileUri]; final expectedHits = { 11: 1, 12: 1, 13: 1, 15: 0, 19: 1, 23: 1, 24: 2, 28: 1, 29: 1, 30: 1, 32: 0, 38: 1, 39: 1, 41: 1, 42: 4, 43: 2, 44: 1, 45: 3, 46: 1, 49: 1, 50: 1, 52: 1, 55: 1, 56: 1, 57: 1, 60: 1, 61: 1, 63: 1, 64: 1, 65: 1, 67: 1, 68: 1, 69: 1, 72: 3, }; expect(isolateFile?.lineHits, expectedHits); expect(isolateFile?.funcHits, {11: 1, 19: 1, 23: 1, 28: 1, 38: 1}); expect(isolateFile?.funcNames, { 11: 'fooSync', 19: 'BarClass.BarClass', 23: 'BarClass.baz', 28: 'fooAsync', 38: 'isolateTask' }); expect( isolateFile?.branchHits, { 11: 1, 12: 1, 15: 0, 19: 1, 23: 1, 28: 1, 29: 1, 32: 0, 38: 1, 42: 1, 72: 1, }, ); }); test('parseCoverage', () async { final tempDir = await Directory.systemTemp.createTemp('coverage.test.'); try { final outputFile = File(p.join(tempDir.path, 'coverage.json')); final coverageResults = await _getCoverageResult(); await outputFile.writeAsString(coverageResults, flush: true); // ignore: deprecated_member_use_from_same_package final parsedResult = await parseCoverage([outputFile], 1); expect(parsedResult, contains(_sampleAppFileUri)); expect(parsedResult, contains(_isolateLibFileUri)); } finally { await tempDir.delete(recursive: true); } }); test('HitMap.parseFiles', () async { final tempDir = await Directory.systemTemp.createTemp('coverage.test.'); try { final outputFile = File(p.join(tempDir.path, 'coverage.json')); final coverageResults = await _getCoverageResult(); await outputFile.writeAsString(coverageResults, flush: true); final parsedResult = await HitMap.parseFiles([outputFile]); expect(parsedResult, contains(_sampleAppFileUri)); expect(parsedResult, contains(_isolateLibFileUri)); } finally { await tempDir.delete(recursive: true); } }); test('HitMap.parseFiles with packagesPath and checkIgnoredLines', () async { final tempDir = await Directory.systemTemp.createTemp('coverage.test.'); try { final outputFile = File(p.join(tempDir.path, 'coverage.json')); final coverageResults = await _getCoverageResult(); await outputFile.writeAsString(coverageResults, flush: true); final parsedResult = await HitMap.parseFiles([outputFile], packagePath: '.', checkIgnoredLines: true); // This file has ignore:coverage-file. expect(parsedResult, isNot(contains(_sampleAppFileUri))); expect(parsedResult, contains(_isolateLibFileUri)); } finally { await tempDir.delete(recursive: true); } }); test('mergeHitmaps', () { final resultMap = >{ 'foo.dart': {10: 2, 20: 0}, 'bar.dart': {10: 3, 20: 1, 30: 0}, }; final newMap = >{ 'bar.dart': {10: 2, 20: 0, 40: 3}, 'baz.dart': {10: 1, 20: 0, 30: 1}, }; // ignore: deprecated_member_use_from_same_package mergeHitmaps(newMap, resultMap); expect(resultMap, >{ 'foo.dart': {10: 2, 20: 0}, 'bar.dart': {10: 5, 20: 1, 30: 0, 40: 3}, 'baz.dart': {10: 1, 20: 0, 30: 1}, }); }); test('FileHitMaps.merge', () { final resultMap = { 'foo.dart': HitMap({10: 2, 20: 0}, {15: 0, 25: 1}, {15: 'bobble', 25: 'cobble'}), 'bar.dart': HitMap( {10: 3, 20: 1, 30: 0}, {15: 5, 25: 0}, {15: 'gobble', 25: 'wobble'}), }; final newMap = { 'bar.dart': HitMap( {10: 2, 20: 0, 40: 3}, {15: 1, 35: 4}, {15: 'gobble', 35: 'dobble'}), 'baz.dart': HitMap( {10: 1, 20: 0, 30: 1}, {15: 0, 25: 2}, {15: 'lobble', 25: 'zobble'}), }; resultMap.merge(newMap); expect(resultMap['foo.dart']?.lineHits, {10: 2, 20: 0}); expect(resultMap['foo.dart']?.funcHits, {15: 0, 25: 1}); expect(resultMap['foo.dart']?.funcNames, {15: 'bobble', 25: 'cobble'}); expect(resultMap['bar.dart']?.lineHits, {10: 5, 20: 1, 30: 0, 40: 3}); expect(resultMap['bar.dart']?.funcHits, {15: 6, 25: 0, 35: 4}); expect(resultMap['bar.dart']?.funcNames, {15: 'gobble', 25: 'wobble', 35: 'dobble'}); expect(resultMap['baz.dart']?.lineHits, {10: 1, 20: 0, 30: 1}); expect(resultMap['baz.dart']?.funcHits, {15: 0, 25: 2}); expect(resultMap['baz.dart']?.funcNames, {15: 'lobble', 25: 'zobble'}); }); } String? _coverageData; Future _getCoverageResult() async => _coverageData ??= await _collectCoverage(false, false); Future _collectCoverage( bool functionCoverage, bool branchCoverage) async { expect(FileSystemEntity.isFileSync(testAppPath), isTrue); // Run the sample app with the right flags. final sampleProcess = await runTestApp(); // Capture the VM service URI. final serviceUri = await serviceUriFromProcess(sampleProcess.stdoutStream()); // Run the collection tool. // TODO: need to get all of this functionality in the lib final toolResult = await TestProcess.start(Platform.resolvedExecutable, [ _collectAppPath, if (functionCoverage) '--function-coverage', if (branchCoverage) '--branch-coverage', '--uri', '$serviceUri', '--resume-isolates', '--wait-paused' ]); await toolResult.shouldExit(0).timeout( timeout, onTimeout: () => throw StateError('We timed out waiting for the tool to finish.'), ); await sampleProcess.shouldExit(); return toolResult.stdoutStream().join('\n'); }