diff --git a/dev/devicelab/bin/tasks/gradle_plugin_light_apk_test.dart b/dev/devicelab/bin/tasks/gradle_plugin_light_apk_test.dart index d791ae80417..252681a0a44 100644 --- a/dev/devicelab/bin/tasks/gradle_plugin_light_apk_test.dart +++ b/dev/devicelab/bin/tasks/gradle_plugin_light_apk_test.dart @@ -252,10 +252,14 @@ Future main() async { section('gradlew on build script with error'); await project.introduceError(); ProcessResult result = await inDirectory(project.rootPath, () { - return executeFlutter('build', options: [ - 'apk', - '--release', - ]); + return executeFlutter( + 'build', + options: [ + 'apk', + '--release', + ], + canFail: true, + ); }); if (result.exitCode == 0) { @@ -278,10 +282,14 @@ Future main() async { section('flutter build apk on build script with error'); await project.introduceError(); result = await inDirectory(project.rootPath, () { - return executeFlutter('build', options: [ - 'apk', - '--release', - ]); + return executeFlutter( + 'build', + options: [ + 'apk', + '--release', + ], + canFail: true, + ); }); if (result.exitCode == 0) { throw failure( @@ -304,10 +312,14 @@ Future main() async { section('gradlew assembleDebug forwards stderr'); await project.introducePubspecError(); final ProcessResult result = await inDirectory(project.rootPath, () { - return executeFlutter('build', options: [ - 'apk', - '--release', - ]); + return executeFlutter( + 'build', + options: [ + 'apk', + '--release', + ], + canFail: true, + ); }); if (result.exitCode == 0) { throw failure( diff --git a/dev/devicelab/lib/framework/utils.dart b/dev/devicelab/lib/framework/utils.dart index dae7465b51c..493d1f3d442 100644 --- a/dev/devicelab/lib/framework/utils.dart +++ b/dev/devicelab/lib/framework/utils.dart @@ -471,10 +471,15 @@ Future flutter(String command, { bool canFail = false, // as in, whether failures are ok. False means that they are fatal. Map? environment, String? workingDirectory, -}) { +}) async { final List args = _flutterCommandArgs(command, options); - return exec(path.join(flutterDirectory.path, 'bin', 'flutter'), args, + final int exitCode = await exec(path.join(flutterDirectory.path, 'bin', 'flutter'), args, canFail: canFail, environment: environment, workingDirectory: workingDirectory); + + if (exitCode != 0 && !canFail) { + await _flutterScreenshot(workingDirectory: workingDirectory); + } + return exitCode; } /// Starts a Flutter subprocess. @@ -506,13 +511,20 @@ Future startFlutter(String command, { String? workingDirectory, }) async { final List args = _flutterCommandArgs(command, options); - return startProcess( + final Process process = await startProcess( path.join(flutterDirectory.path, 'bin', 'flutter'), args, environment: environment, isBot: isBot, workingDirectory: workingDirectory, ); + + unawaited(process.exitCode.then((int exitCode) async { + if (exitCode != 0) { + await _flutterScreenshot(workingDirectory: workingDirectory); + } + })); + return process; } /// Runs a `flutter` command and returns the standard output as a string. @@ -530,12 +542,53 @@ Future evalFlutter(String command, { Future executeFlutter(String command, { List options = const [], + bool canFail = false, // as in, whether failures are ok. False means that they are fatal. }) async { final List args = _flutterCommandArgs(command, options); - return _processManager.run( + final ProcessResult processResult = await _processManager.run( [path.join(flutterDirectory.path, 'bin', 'flutter'), ...args], workingDirectory: cwd, ); + + if (processResult.exitCode != 0 && !canFail) { + await _flutterScreenshot(); + } + return processResult; +} + +Future _flutterScreenshot({ String? workingDirectory }) async { + try { + final Directory? dumpDirectory = hostAgent.dumpDirectory; + if (dumpDirectory == null) { + return; + } + // On command failure try uploading screenshot of failing command. + final String screenshotPath = path.join( + dumpDirectory.path, + 'device-screenshot-${DateTime.now().toLocal().toIso8601String()}.png', + ); + + final String deviceId = (await devices.workingDevice).deviceId; + print('Taking screenshot of working device $deviceId at $screenshotPath'); + final List args = _flutterCommandArgs( + 'screenshot', + [ + '--out', + screenshotPath, + '-d', deviceId, + ], + ); + final ProcessResult screenshot = await _processManager.run( + [path.join(flutterDirectory.path, 'bin', 'flutter'), ...args], + workingDirectory: workingDirectory ?? cwd, + ); + + if (screenshot.exitCode != 0) { + print('Failed to take screenshot. Continuing.'); + } + } catch (exception) { + print('Failed to take screenshot. Continuing.\n$exception'); + } } String get dartBin =>