|
2 | 2 | // Use of this source code is governed by a BSD-style license that can be
|
3 | 3 | // found in the LICENSE file.
|
4 | 4 |
|
| 5 | +import 'dart:convert' show jsonEncode; |
| 6 | +import 'dart:io' show Directory, File; |
| 7 | + |
| 8 | +import 'package:coverage/src/hitmap.dart'; |
5 | 9 | import 'package:flutter_tools/src/test/coverage_collector.dart';
|
| 10 | +import 'package:flutter_tools/src/test/test_device.dart' show TestDevice; |
| 11 | +import 'package:stream_channel/stream_channel.dart' show StreamChannel; |
6 | 12 | import 'package:vm_service/vm_service.dart';
|
7 | 13 |
|
8 | 14 | import '../src/common.dart';
|
@@ -138,94 +144,7 @@ void main() {
|
138 | 144 | });
|
139 | 145 |
|
140 | 146 | testWithoutContext('Coverage collector with null libraryNames accepts all libraries', () async {
|
141 |
| - final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( |
142 |
| - requests: <VmServiceExpectation>[ |
143 |
| - FakeVmServiceRequest( |
144 |
| - method: 'getVersion', |
145 |
| - jsonResponse: Version(major: 3, minor: 51).toJson(), |
146 |
| - ), |
147 |
| - FakeVmServiceRequest( |
148 |
| - method: 'getVM', |
149 |
| - jsonResponse: (VM.parse(<String, Object>{})! |
150 |
| - ..isolates = <IsolateRef>[ |
151 |
| - IsolateRef.parse(<String, Object>{ |
152 |
| - 'id': '1', |
153 |
| - })!, |
154 |
| - ] |
155 |
| - ).toJson(), |
156 |
| - ), |
157 |
| - FakeVmServiceRequest( |
158 |
| - method: 'getScripts', |
159 |
| - args: <String, Object>{ |
160 |
| - 'isolateId': '1', |
161 |
| - }, |
162 |
| - jsonResponse: ScriptList(scripts: <ScriptRef>[ |
163 |
| - ScriptRef(uri: 'package:foo/foo.dart', id: '1'), |
164 |
| - ScriptRef(uri: 'package:bar/bar.dart', id: '2'), |
165 |
| - ]).toJson(), |
166 |
| - ), |
167 |
| - FakeVmServiceRequest( |
168 |
| - method: 'getSourceReport', |
169 |
| - args: <String, Object>{ |
170 |
| - 'isolateId': '1', |
171 |
| - 'reports': <Object>['Coverage'], |
172 |
| - 'scriptId': '1', |
173 |
| - 'forceCompile': true, |
174 |
| - 'reportLines': true, |
175 |
| - }, |
176 |
| - jsonResponse: SourceReport( |
177 |
| - ranges: <SourceReportRange>[ |
178 |
| - SourceReportRange( |
179 |
| - scriptIndex: 0, |
180 |
| - startPos: 0, |
181 |
| - endPos: 0, |
182 |
| - compiled: true, |
183 |
| - coverage: SourceReportCoverage( |
184 |
| - hits: <int>[1, 3], |
185 |
| - misses: <int>[2], |
186 |
| - ), |
187 |
| - ), |
188 |
| - ], |
189 |
| - scripts: <ScriptRef>[ |
190 |
| - ScriptRef( |
191 |
| - uri: 'package:foo/foo.dart', |
192 |
| - id: '1', |
193 |
| - ), |
194 |
| - ], |
195 |
| - ).toJson(), |
196 |
| - ), |
197 |
| - FakeVmServiceRequest( |
198 |
| - method: 'getSourceReport', |
199 |
| - args: <String, Object>{ |
200 |
| - 'isolateId': '1', |
201 |
| - 'reports': <Object>['Coverage'], |
202 |
| - 'scriptId': '2', |
203 |
| - 'forceCompile': true, |
204 |
| - 'reportLines': true, |
205 |
| - }, |
206 |
| - jsonResponse: SourceReport( |
207 |
| - ranges: <SourceReportRange>[ |
208 |
| - SourceReportRange( |
209 |
| - scriptIndex: 0, |
210 |
| - startPos: 0, |
211 |
| - endPos: 0, |
212 |
| - compiled: true, |
213 |
| - coverage: SourceReportCoverage( |
214 |
| - hits: <int>[47, 21], |
215 |
| - misses: <int>[32, 86], |
216 |
| - ), |
217 |
| - ), |
218 |
| - ], |
219 |
| - scripts: <ScriptRef>[ |
220 |
| - ScriptRef( |
221 |
| - uri: 'package:bar/bar.dart', |
222 |
| - id: '2', |
223 |
| - ), |
224 |
| - ], |
225 |
| - ).toJson(), |
226 |
| - ), |
227 |
| - ], |
228 |
| - ); |
| 147 | + final FakeVmServiceHost fakeVmServiceHost = createFakeVmServiceHostWithFooAndBar(); |
229 | 148 |
|
230 | 149 | final Map<String, Object?> result = await collect(
|
231 | 150 | null,
|
@@ -417,4 +336,183 @@ void main() {
|
417 | 336 | });
|
418 | 337 | expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
419 | 338 | });
|
| 339 | + |
| 340 | + testWithoutContext('Coverage collector caches read files', () async { |
| 341 | + Directory? tempDir; |
| 342 | + try { |
| 343 | + tempDir = Directory.systemTemp.createTempSync('flutter_coverage_collector_test.'); |
| 344 | + final File file = File('${tempDir.path}/packages.json'); |
| 345 | + file.writeAsStringSync(jsonEncode(<String, dynamic>{ |
| 346 | + 'configVersion': 2, |
| 347 | + 'packages': <Map<String, String>>[ |
| 348 | + <String, String>{ |
| 349 | + 'name': 'foo', |
| 350 | + 'rootUri': 'foo', |
| 351 | + }, |
| 352 | + <String, String>{ |
| 353 | + 'name': 'bar', |
| 354 | + 'rootUri': 'bar', |
| 355 | + }, |
| 356 | + ], |
| 357 | + })); |
| 358 | + final Directory fooDir = Directory('${tempDir.path}/foo/'); |
| 359 | + fooDir.createSync(); |
| 360 | + final File fooFile = File('${fooDir.path}/foo.dart'); |
| 361 | + fooFile.writeAsStringSync('hit\nnohit but ignored // coverage:ignore-line\nhit\n'); |
| 362 | + |
| 363 | + final String packagesPath = file.path; |
| 364 | + final CoverageCollector collector = CoverageCollector( |
| 365 | + libraryNames: <String>{'foo', 'bar'}, |
| 366 | + verbose: false, |
| 367 | + packagesPath: packagesPath, |
| 368 | + resolver: await CoverageCollector.getResolver(packagesPath) |
| 369 | + ); |
| 370 | + await collector.collectCoverage(TestTestDevice(), connector: (Uri? uri) async { |
| 371 | + return createFakeVmServiceHostWithFooAndBar().vmService; |
| 372 | + }); |
| 373 | + |
| 374 | + Future<void> getHitMapAndVerify() async { |
| 375 | + final Map<String, HitMap> gottenHitmap = <String, HitMap>{}; |
| 376 | + await collector.finalizeCoverage(formatter: (Map<String, HitMap> hitmap) { |
| 377 | + gottenHitmap.addAll(hitmap); |
| 378 | + return ''; |
| 379 | + }); |
| 380 | + expect(gottenHitmap.keys.toList()..sort(), <String>['package:bar/bar.dart', 'package:foo/foo.dart']); |
| 381 | + expect(gottenHitmap['package:foo/foo.dart']!.lineHits, <int, int>{1: 1, /* 2: 0, is ignored in file */ 3: 1}); |
| 382 | + expect(gottenHitmap['package:bar/bar.dart']!.lineHits, <int, int>{21: 1, 32: 0, 47: 1, 86: 0}); |
| 383 | + } |
| 384 | + |
| 385 | + Future<void> verifyHitmapEmpty() async { |
| 386 | + final Map<String, HitMap> gottenHitmap = <String, HitMap>{}; |
| 387 | + await collector.finalizeCoverage(formatter: (Map<String, HitMap> hitmap) { |
| 388 | + gottenHitmap.addAll(hitmap); |
| 389 | + return ''; |
| 390 | + }); |
| 391 | + expect(gottenHitmap.isEmpty, isTrue); |
| 392 | + } |
| 393 | + |
| 394 | + // Get hit map the first time. |
| 395 | + await getHitMapAndVerify(); |
| 396 | + |
| 397 | + // Getting the hitmap clears it so we now doesn't get any data. |
| 398 | + await verifyHitmapEmpty(); |
| 399 | + |
| 400 | + // Collecting again gets us the same data even though the foo file has been deleted. |
| 401 | + // This means that the fact that line 2 was ignored has been cached. |
| 402 | + fooFile.deleteSync(); |
| 403 | + await collector.collectCoverage(TestTestDevice(), connector: (Uri? uri) async { |
| 404 | + return createFakeVmServiceHostWithFooAndBar().vmService; |
| 405 | + }); |
| 406 | + await getHitMapAndVerify(); |
| 407 | + } finally { |
| 408 | + tempDir?.deleteSync(recursive: true); |
| 409 | + } |
| 410 | + }); |
| 411 | +} |
| 412 | + |
| 413 | +FakeVmServiceHost createFakeVmServiceHostWithFooAndBar() { |
| 414 | + return FakeVmServiceHost( |
| 415 | + requests: <VmServiceExpectation>[ |
| 416 | + FakeVmServiceRequest( |
| 417 | + method: 'getVersion', |
| 418 | + jsonResponse: Version(major: 3, minor: 51).toJson(), |
| 419 | + ), |
| 420 | + FakeVmServiceRequest( |
| 421 | + method: 'getVM', |
| 422 | + jsonResponse: (VM.parse(<String, Object>{})! |
| 423 | + ..isolates = <IsolateRef>[ |
| 424 | + IsolateRef.parse(<String, Object>{ |
| 425 | + 'id': '1', |
| 426 | + })!, |
| 427 | + ] |
| 428 | + ).toJson(), |
| 429 | + ), |
| 430 | + FakeVmServiceRequest( |
| 431 | + method: 'getScripts', |
| 432 | + args: <String, Object>{ |
| 433 | + 'isolateId': '1', |
| 434 | + }, |
| 435 | + jsonResponse: ScriptList(scripts: <ScriptRef>[ |
| 436 | + ScriptRef(uri: 'package:foo/foo.dart', id: '1'), |
| 437 | + ScriptRef(uri: 'package:bar/bar.dart', id: '2'), |
| 438 | + ]).toJson(), |
| 439 | + ), |
| 440 | + FakeVmServiceRequest( |
| 441 | + method: 'getSourceReport', |
| 442 | + args: <String, Object>{ |
| 443 | + 'isolateId': '1', |
| 444 | + 'reports': <Object>['Coverage'], |
| 445 | + 'scriptId': '1', |
| 446 | + 'forceCompile': true, |
| 447 | + 'reportLines': true, |
| 448 | + }, |
| 449 | + jsonResponse: SourceReport( |
| 450 | + ranges: <SourceReportRange>[ |
| 451 | + SourceReportRange( |
| 452 | + scriptIndex: 0, |
| 453 | + startPos: 0, |
| 454 | + endPos: 0, |
| 455 | + compiled: true, |
| 456 | + coverage: SourceReportCoverage( |
| 457 | + hits: <int>[1, 3], |
| 458 | + misses: <int>[2], |
| 459 | + ), |
| 460 | + ), |
| 461 | + ], |
| 462 | + scripts: <ScriptRef>[ |
| 463 | + ScriptRef( |
| 464 | + uri: 'package:foo/foo.dart', |
| 465 | + id: '1', |
| 466 | + ), |
| 467 | + ], |
| 468 | + ).toJson(), |
| 469 | + ), |
| 470 | + FakeVmServiceRequest( |
| 471 | + method: 'getSourceReport', |
| 472 | + args: <String, Object>{ |
| 473 | + 'isolateId': '1', |
| 474 | + 'reports': <Object>['Coverage'], |
| 475 | + 'scriptId': '2', |
| 476 | + 'forceCompile': true, |
| 477 | + 'reportLines': true, |
| 478 | + }, |
| 479 | + jsonResponse: SourceReport( |
| 480 | + ranges: <SourceReportRange>[ |
| 481 | + SourceReportRange( |
| 482 | + scriptIndex: 0, |
| 483 | + startPos: 0, |
| 484 | + endPos: 0, |
| 485 | + compiled: true, |
| 486 | + coverage: SourceReportCoverage( |
| 487 | + hits: <int>[47, 21], |
| 488 | + misses: <int>[32, 86], |
| 489 | + ), |
| 490 | + ), |
| 491 | + ], |
| 492 | + scripts: <ScriptRef>[ |
| 493 | + ScriptRef( |
| 494 | + uri: 'package:bar/bar.dart', |
| 495 | + id: '2', |
| 496 | + ), |
| 497 | + ], |
| 498 | + ).toJson(), |
| 499 | + ), |
| 500 | + ], |
| 501 | + ); |
| 502 | +} |
| 503 | + |
| 504 | +class TestTestDevice extends TestDevice { |
| 505 | + @override |
| 506 | + Future<void> get finished => Future<void>.delayed(const Duration(seconds: 1)); |
| 507 | + |
| 508 | + @override |
| 509 | + Future<void> kill() => Future<void>.value(); |
| 510 | + |
| 511 | + @override |
| 512 | + Future<Uri?> get observatoryUri => Future<Uri?>.value(); |
| 513 | + |
| 514 | + @override |
| 515 | + Future<StreamChannel<String>> start(String entrypointPath) { |
| 516 | + throw UnimplementedError(); |
| 517 | + } |
420 | 518 | }
|
0 commit comments