From 0d2e23d6792ec8ed3681237e8b9a74614477990e Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Fri, 8 Nov 2024 16:36:02 -0800 Subject: [PATCH 1/5] [ci] Take screenshot when native drive test is taking longer than 10 minutes --- .../tool/lib/src/drive_examples_command.dart | 33 +++++-- script/tool/pubspec.yaml | 1 + .../test/drive_examples_command_test.dart | 95 +++++++++++++++++++ 3 files changed, 122 insertions(+), 7 deletions(-) diff --git a/script/tool/lib/src/drive_examples_command.dart b/script/tool/lib/src/drive_examples_command.dart index 8ae12485602..7f2a6f36fdc 100644 --- a/script/tool/lib/src/drive_examples_command.dart +++ b/script/tool/lib/src/drive_examples_command.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -445,17 +446,35 @@ class DriveExamplesCommand extends PackageLoopingCommand { bool passed = true; for (final String target in individualRunTargets) { - final int exitCode = await processRunner.runAndStream( + final Timer timeoutTimer = Timer(const Duration(minutes: 10), () async { + printError('Test is taking a long time, taking screenshot...'); + final String screenshotBasename = + 'test-timeout-screenshot_${target.replaceAll(platform.pathSeparator, '_')}.png'; + await processRunner.runAndStream( flutterCommand, [ - 'test', + 'screenshot', ...deviceFlags, - if (enableExperiment.isNotEmpty) - '--enable-experiment=$enableExperiment', - if (logsDirectory != null) '--debug-logs-dir=${logsDirectory.path}', - target, + if (logsDirectory != null) + '--out=${logsDirectory.childFile(screenshotBasename).path}', ], - workingDir: example.directory); + workingDir: example.directory, + ); + }); + final int exitCode = await processRunner.runAndStream( + flutterCommand, + [ + 'test', + ...deviceFlags, + if (enableExperiment.isNotEmpty) + '--enable-experiment=$enableExperiment', + if (logsDirectory != null) '--debug-logs-dir=${logsDirectory.path}', + target, + ], + workingDir: example.directory, + ); + + timeoutTimer.cancel(); passed = passed && (exitCode == 0); } return passed; diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index ee24eb90d4f..3152c43566d 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: async: ^2.10.0 collection: ^1.17.0 colorize: ^3.0.0 + fake_async: ^1.3.1 file: ^7.0.1 git: ^2.0.0 http: ^1.0.0 diff --git a/script/tool/test/drive_examples_command_test.dart b/script/tool/test/drive_examples_command_test.dart index a1d48865bc4..9b469efe8cd 100644 --- a/script/tool/test/drive_examples_command_test.dart +++ b/script/tool/test/drive_examples_command_test.dart @@ -3,13 +3,16 @@ // found in the LICENSE file. import 'dart:convert'; +import 'dart:io' as io; import 'package:args/command_runner.dart'; +import 'package:fake_async/fake_async.dart'; import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; import 'package:flutter_plugin_tools/src/drive_examples_command.dart'; +import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; import 'package:test/test.dart'; @@ -387,6 +390,81 @@ void main() { ])); }); + test('saves a screenshot if test is taking too long', () async { + setMockFlutterDevicesOutput(); + final RepositoryPackage plugin = createFakePlugin( + 'plugin', + packagesDir, + extraFiles: [ + 'example/integration_test/bar_test.dart', + 'example/ios/ios.m', + ], + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), + }, + ); + + final FakeAsync fakeAsync = FakeAsync(); + processRunner.mockProcessesForExecutable['flutter']! + .addAll([ + FakeProcessInfo( + _MockDelayingProcess( + delayDuration: const Duration(minutes: 11), + fakeAsync: fakeAsync), + ['test']), + FakeProcessInfo(MockProcess(), ['screenshot']), + ]); + + final Directory pluginExampleDirectory = getExampleDir(plugin); + + List output = []; + fakeAsync.run((_) { + () async { + output = await runCapturingPrint( + runner, ['drive-examples', '--ios']); + }(); + }); + fakeAsync.flushTimers(); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + contains('Test is taking a long time, taking screenshot...'), + contains('No issues found!'), + ]), + ); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall(getFlutterCommand(mockPlatform), + const ['devices', '--machine'], null), + ProcessCall( + getFlutterCommand(mockPlatform), + const [ + 'test', + '-d', + _fakeIOSDevice, + '--debug-logs-dir=/path/to/logs', + 'integration_test', + ], + pluginExampleDirectory.path, + ), + ProcessCall( + getFlutterCommand(mockPlatform), + const [ + 'screenshot', + '-d', + _fakeIOSDevice, + '--out=/path/to/logs/test-timeout-screenshot_integration_test.png', + ], + pluginExampleDirectory.path, + ), + ])); + }); + test('driving when plugin does not support Linux is a no-op', () async { createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/plugin_test.dart', @@ -1697,3 +1775,20 @@ void main() { }); }); } + +class _MockDelayingProcess extends Mock implements io.Process { + /// Creates a mock process that takes [delayDuration] time to exit successfully. + _MockDelayingProcess( + {required Duration delayDuration, required FakeAsync fakeAsync}) + : _delayDuration = delayDuration, + _fakeAsync = fakeAsync; + + final Duration _delayDuration; + final FakeAsync _fakeAsync; + + @override + Future get exitCode async { + _fakeAsync.elapse(_delayDuration); + return 0; + } +} From 03ddd0fc67eef89b941a625b653f3a9312cca44b Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Mon, 11 Nov 2024 16:28:38 -0800 Subject: [PATCH 2/5] dev_dependencies --- script/tool/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 3152c43566d..d5ea5b93447 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -9,7 +9,6 @@ dependencies: async: ^2.10.0 collection: ^1.17.0 colorize: ^3.0.0 - fake_async: ^1.3.1 file: ^7.0.1 git: ^2.0.0 http: ^1.0.0 @@ -27,6 +26,7 @@ dependencies: dev_dependencies: build_runner: ^2.2.1 + fake_async: ^1.3.1 matcher: ^0.12.15 mockito: ^5.4.4 From a80cb68b0b86b0883831ba8a7947176030673960 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Mon, 11 Nov 2024 16:31:57 -0800 Subject: [PATCH 3/5] Fake instead of Mock --- script/tool/test/drive_examples_command_test.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/tool/test/drive_examples_command_test.dart b/script/tool/test/drive_examples_command_test.dart index 9b469efe8cd..cb751cae6ac 100644 --- a/script/tool/test/drive_examples_command_test.dart +++ b/script/tool/test/drive_examples_command_test.dart @@ -409,7 +409,7 @@ void main() { processRunner.mockProcessesForExecutable['flutter']! .addAll([ FakeProcessInfo( - _MockDelayingProcess( + _FakeDelayingProcess( delayDuration: const Duration(minutes: 11), fakeAsync: fakeAsync), ['test']), @@ -1776,9 +1776,9 @@ void main() { }); } -class _MockDelayingProcess extends Mock implements io.Process { +class _FakeDelayingProcess extends Fake implements io.Process { /// Creates a mock process that takes [delayDuration] time to exit successfully. - _MockDelayingProcess( + _FakeDelayingProcess( {required Duration delayDuration, required FakeAsync fakeAsync}) : _delayDuration = delayDuration, _fakeAsync = fakeAsync; From 4ca6c095fe618c7361b4855462a5c4504ad4c523 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Tue, 12 Nov 2024 20:12:24 -0800 Subject: [PATCH 4/5] printWarning and screenshot basename --- script/tool/lib/src/drive_examples_command.dart | 2 +- script/tool/test/drive_examples_command_test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script/tool/lib/src/drive_examples_command.dart b/script/tool/lib/src/drive_examples_command.dart index 7f2a6f36fdc..604944f37ee 100644 --- a/script/tool/lib/src/drive_examples_command.dart +++ b/script/tool/lib/src/drive_examples_command.dart @@ -447,9 +447,9 @@ class DriveExamplesCommand extends PackageLoopingCommand { bool passed = true; for (final String target in individualRunTargets) { final Timer timeoutTimer = Timer(const Duration(minutes: 10), () async { - printError('Test is taking a long time, taking screenshot...'); final String screenshotBasename = 'test-timeout-screenshot_${target.replaceAll(platform.pathSeparator, '_')}.png'; + printWarning('Test is taking a long time, taking screenshot $screenshotBasename...'); await processRunner.runAndStream( flutterCommand, [ diff --git a/script/tool/test/drive_examples_command_test.dart b/script/tool/test/drive_examples_command_test.dart index cb751cae6ac..9a3656d4e04 100644 --- a/script/tool/test/drive_examples_command_test.dart +++ b/script/tool/test/drive_examples_command_test.dart @@ -431,7 +431,7 @@ void main() { output, containsAllInOrder([ contains('Running for plugin'), - contains('Test is taking a long time, taking screenshot...'), + contains('Test is taking a long time, taking screenshot test-timeout-screenshot_integration_test.png...'), contains('No issues found!'), ]), ); From 2ba872583ec655ac0e2f7635ccc6d93dbec9751b Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Wed, 13 Nov 2024 10:34:28 -0800 Subject: [PATCH 5/5] Format --- script/tool/lib/src/drive_examples_command.dart | 3 ++- script/tool/test/drive_examples_command_test.dart | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/script/tool/lib/src/drive_examples_command.dart b/script/tool/lib/src/drive_examples_command.dart index 604944f37ee..127e51fb136 100644 --- a/script/tool/lib/src/drive_examples_command.dart +++ b/script/tool/lib/src/drive_examples_command.dart @@ -449,7 +449,8 @@ class DriveExamplesCommand extends PackageLoopingCommand { final Timer timeoutTimer = Timer(const Duration(minutes: 10), () async { final String screenshotBasename = 'test-timeout-screenshot_${target.replaceAll(platform.pathSeparator, '_')}.png'; - printWarning('Test is taking a long time, taking screenshot $screenshotBasename...'); + printWarning( + 'Test is taking a long time, taking screenshot $screenshotBasename...'); await processRunner.runAndStream( flutterCommand, [ diff --git a/script/tool/test/drive_examples_command_test.dart b/script/tool/test/drive_examples_command_test.dart index 9a3656d4e04..1cc158da706 100644 --- a/script/tool/test/drive_examples_command_test.dart +++ b/script/tool/test/drive_examples_command_test.dart @@ -431,7 +431,8 @@ void main() { output, containsAllInOrder([ contains('Running for plugin'), - contains('Test is taking a long time, taking screenshot test-timeout-screenshot_integration_test.png...'), + contains( + 'Test is taking a long time, taking screenshot test-timeout-screenshot_integration_test.png...'), contains('No issues found!'), ]), );