Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions script/tool/lib/src/drive_examples_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -439,17 +440,36 @@ 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 {
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,
<String>[
'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,
<String>[
'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;
Expand Down
1 change: 1 addition & 0 deletions script/tool/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies:

dev_dependencies:
build_runner: ^2.2.1
fake_async: ^1.3.1
matcher: ^0.12.15
mockito: ^5.4.4

Expand Down
96 changes: 96 additions & 0 deletions script/tool/test/drive_examples_command_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -387,6 +390,82 @@ void main() {
]));
});

test('saves a screenshot if test is taking too long', () async {
setMockFlutterDevicesOutput();
final RepositoryPackage plugin = createFakePlugin(
'plugin',
packagesDir,
extraFiles: <String>[
'example/integration_test/bar_test.dart',
'example/ios/ios.m',
],
platformSupport: <String, PlatformDetails>{
platformAndroid: const PlatformDetails(PlatformSupport.inline),
platformIOS: const PlatformDetails(PlatformSupport.inline),
},
);

final FakeAsync fakeAsync = FakeAsync();
processRunner.mockProcessesForExecutable['flutter']!
.addAll(<FakeProcessInfo>[
FakeProcessInfo(
_FakeDelayingProcess(
delayDuration: const Duration(minutes: 11),
fakeAsync: fakeAsync),
<String>['test']),
FakeProcessInfo(MockProcess(), <String>['screenshot']),
]);

final Directory pluginExampleDirectory = getExampleDir(plugin);

List<String> output = <String>[];
fakeAsync.run((_) {
() async {
output = await runCapturingPrint(
runner, <String>['drive-examples', '--ios']);
}();
});
fakeAsync.flushTimers();

expect(
output,
containsAllInOrder(<Matcher>[
contains('Running for plugin'),
contains(
'Test is taking a long time, taking screenshot test-timeout-screenshot_integration_test.png...'),
contains('No issues found!'),
]),
);

expect(
processRunner.recordedCalls,
orderedEquals(<ProcessCall>[
ProcessCall(getFlutterCommand(mockPlatform),
const <String>['devices', '--machine'], null),
ProcessCall(
getFlutterCommand(mockPlatform),
const <String>[
'test',
'-d',
_fakeIOSDevice,
'--debug-logs-dir=/path/to/logs',
'integration_test',
],
pluginExampleDirectory.path,
),
ProcessCall(
getFlutterCommand(mockPlatform),
const <String>[
'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: <String>[
'example/integration_test/plugin_test.dart',
Expand Down Expand Up @@ -1634,3 +1713,20 @@ void main() {
});
});
}

class _FakeDelayingProcess extends Fake implements io.Process {
/// Creates a mock process that takes [delayDuration] time to exit successfully.
_FakeDelayingProcess(
{required Duration delayDuration, required FakeAsync fakeAsync})
: _delayDuration = delayDuration,
_fakeAsync = fakeAsync;

final Duration _delayDuration;
final FakeAsync _fakeAsync;

@override
Future<int> get exitCode async {
_fakeAsync.elapse(_delayDuration);
return 0;
}
}