From 3d1d4532b5799f9cc112ffaa04247c44b41ceb6e Mon Sep 17 00:00:00 2001 From: LinChen Date: Tue, 17 Oct 2023 17:17:09 -0500 Subject: [PATCH] make integration_test_driver_extended.dart support writeResponseData--(done) (#128382) *Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.* *List which issues are fixed by this PR. You must list at least one issue.* - https://github.com/flutter/flutter/issues/128381 *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* --- dev/bots/test.dart | 37 ++++++++++++ .../extended_integration_test.dart | 2 +- .../example/test_driver/integration_test.dart | 2 +- .../lib/integration_test_driver.dart | 8 +++ .../lib/integration_test_driver_extended.dart | 60 ++++++++++++++++++- 5 files changed, 105 insertions(+), 4 deletions(-) diff --git a/dev/bots/test.dart b/dev/bots/test.dart index d624a853f59..046dae199b8 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -1181,6 +1181,8 @@ Future _runWebLongRunningTests() async { driver: path.join('test_driver', 'integration_test.dart'), buildMode: buildMode, renderer: 'canvaskit', + expectWriteResponseFile: true, + expectResponseFileContent: 'null', ), () => _runFlutterDriverWebTest( testAppDirectory: path.join('packages', 'integration_test', 'example'), @@ -1188,6 +1190,20 @@ Future _runWebLongRunningTests() async { driver: path.join('test_driver', 'extended_integration_test.dart'), buildMode: buildMode, renderer: 'canvaskit', + expectWriteResponseFile: true, + expectResponseFileContent: ''' +{ + "screenshots": [ + { + "screenshotName": "platform_name", + "bytes": [] + }, + { + "screenshotName": "platform_name_2", + "bytes": [] + } + ] +}''', ), ], @@ -1321,6 +1337,8 @@ Future _runFlutterDriverWebTest({ String? driver, bool expectFailure = false, bool silenceBrowserOutput = false, + bool expectWriteResponseFile = false, + String expectResponseFileContent = '', }) async { printProgress('${green}Running integration tests $target in $buildMode mode.$reset'); await runCommand( @@ -1328,6 +1346,11 @@ Future _runFlutterDriverWebTest({ [ 'clean' ], workingDirectory: testAppDirectory, ); + final String responseFile = + path.join(testAppDirectory, 'build', 'integration_response_data.json'); + if (File(responseFile).existsSync()) { + File(responseFile).deleteSync(); + } await runCommand( flutter, [ @@ -1356,6 +1379,20 @@ Future _runFlutterDriverWebTest({ return false; }, ); + if (expectWriteResponseFile) { + if (!File(responseFile).existsSync()) { + foundError([ + '$bold${red}Command did not write the response file but expected response file written.$reset', + ]); + } else { + final String response = File(responseFile).readAsStringSync(); + if (response != expectResponseFileContent) { + foundError([ + '$bold${red}Command write the response file with $response but expected response file with $expectResponseFileContent.$reset', + ]); + } + } + } } // Compiles a sample web app and checks that its JS doesn't contain certain diff --git a/packages/integration_test/example/test_driver/extended_integration_test.dart b/packages/integration_test/example/test_driver/extended_integration_test.dart index e2dabae450b..121b701da08 100644 --- a/packages/integration_test/example/test_driver/extended_integration_test.dart +++ b/packages/integration_test/example/test_driver/extended_integration_test.dart @@ -25,5 +25,5 @@ Future main() async { } return true; }, - ); + writeResponseOnFailure: true); } diff --git a/packages/integration_test/example/test_driver/integration_test.dart b/packages/integration_test/example/test_driver/integration_test.dart index 600bdc88288..9213ad8bf30 100644 --- a/packages/integration_test/example/test_driver/integration_test.dart +++ b/packages/integration_test/example/test_driver/integration_test.dart @@ -4,4 +4,4 @@ import 'package:integration_test/integration_test_driver.dart'; -Future main() => integrationDriver(); +Future main() => integrationDriver(writeResponseOnFailure: true); diff --git a/packages/integration_test/lib/integration_test_driver.dart b/packages/integration_test/lib/integration_test_driver.dart index 1a791e43b52..38df03dbf70 100644 --- a/packages/integration_test/lib/integration_test_driver.dart +++ b/packages/integration_test/lib/integration_test_driver.dart @@ -67,9 +67,14 @@ Future writeResponseData( /// /// `responseDataCallback` is the handler for processing [Response.data]. /// The default value is `writeResponseData`. +/// +/// `writeResponseOnFailure` determines whether the `responseDataCallback` +/// function will be called to process the [Response.data] when a test fails. +/// The default value is `false`. Future integrationDriver({ Duration timeout = const Duration(minutes: 20), ResponseDataCallback? responseDataCallback = writeResponseData, + bool writeResponseOnFailure = false, }) async { final FlutterDriver driver = await FlutterDriver.connect(); final String jsonResult = await driver.requestData(null, timeout: timeout); @@ -85,6 +90,9 @@ Future integrationDriver({ exit(0); } else { print('Failure Details:\n${response.formattedFailureDetails}'); + if (responseDataCallback != null && writeResponseOnFailure) { + await responseDataCallback(response.data); + } exit(1); } } diff --git a/packages/integration_test/lib/integration_test_driver_extended.dart b/packages/integration_test/lib/integration_test_driver_extended.dart index d4226f4ecb2..f4a7824abdd 100644 --- a/packages/integration_test/lib/integration_test_driver_extended.dart +++ b/packages/integration_test/lib/integration_test_driver_extended.dart @@ -6,12 +6,45 @@ // ignore_for_file: avoid_print import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'package:flutter_driver/flutter_driver.dart'; +import 'package:path/path.dart' as path; import 'common.dart'; +/// Flutter Driver test output directory. +/// +/// Tests should write any output files to this directory. Defaults to the path +/// set in the FLUTTER_TEST_OUTPUTS_DIR environment variable, or `build` if +/// unset. +String testOutputsDirectory = + Platform.environment['FLUTTER_TEST_OUTPUTS_DIR'] ?? 'build'; + +/// The callback type to handle [Response.data] after the test +/// succeeds. +typedef ResponseDataCallback = FutureOr Function(Map?); + +/// Writes a json-serializable data to +/// [testOutputsDirectory]/`testOutputFilename.json`. +/// +/// This is the default `responseDataCallback` in [integrationDriver]. +Future writeResponseData( + Map? data, { + String testOutputFilename = 'integration_response_data', + String? destinationDirectory, +}) async { + destinationDirectory ??= testOutputsDirectory; + await fs.directory(destinationDirectory).create(recursive: true); + final File file = fs.file(path.join( + destinationDirectory, + '$testOutputFilename.json', + )); + final String resultString = _encodeJson(data, true); + await file.writeAsString(resultString); +} + /// Adaptor to run an integration test using `flutter drive`. /// /// To an integration test `.dart` using `flutter drive`, put a file named @@ -43,8 +76,19 @@ import 'common.dart'; /// and it returns `true` if both images are equal. /// /// As a result, returning `false` from `onScreenshot` will make the test fail. -Future integrationDriver( - {FlutterDriver? driver, ScreenshotCallback? onScreenshot}) async { +/// +/// `responseDataCallback` is the handler for processing [Response.data]. +/// The default value is `writeResponseData`. +/// +/// `writeResponseOnFailure` determines whether the `responseDataCallback` +/// function will be called to process the [Response.data] when a test fails. +/// The default value is `false`. +Future integrationDriver({ + FlutterDriver? driver, + ScreenshotCallback? onScreenshot, + ResponseDataCallback? responseDataCallback = writeResponseData, + bool writeResponseOnFailure = false, +}) async { driver ??= await FlutterDriver.connect(); // Test states that it's waiting on web driver commands. // [DriverTestMessage] is converted to string since json format causes an @@ -130,9 +174,21 @@ Future integrationDriver( if (response.allTestsPassed) { print('All tests passed.'); + if (responseDataCallback != null) { + await responseDataCallback(response.data); + } exit(0); } else { print('Failure Details:\n${response.formattedFailureDetails}'); + if (responseDataCallback != null && writeResponseOnFailure) { + await responseDataCallback(response.data); + } exit(1); } } + +const JsonEncoder _prettyEncoder = JsonEncoder.withIndent(' '); + +String _encodeJson(Map? jsonObject, bool pretty) { + return pretty ? _prettyEncoder.convert(jsonObject) : json.encode(jsonObject); +}