diff --git a/analysis_options.yaml b/analysis_options.yaml index e5203782a1..55c346a3c6 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -71,8 +71,8 @@ linter: - avoid_init_to_null # - avoid_js_rounded_ints # only useful when targeting JS runtime - avoid_null_checks_in_equality_operators - # - avoid_positional_boolean_parameters # not yet tested - # - avoid_print # not yet tested + # - avoid_positional_boolean_parameters # would have been nice to enable this but by now there's too many places that break it + - avoid_print # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356) # - avoid_redundant_argument_values # not yet tested - avoid_relative_lib_imports diff --git a/dev/analysis_options.yaml b/dev/analysis_options.yaml new file mode 100644 index 0000000000..68626cc5f4 --- /dev/null +++ b/dev/analysis_options.yaml @@ -0,0 +1,5 @@ +include: ../analysis_options.yaml + +linter: + rules: + avoid_print: false # We use prints as debugging tools here all the time. diff --git a/dev/automated_tests/flutter_test/flutter_gold_expectation.txt b/dev/automated_tests/flutter_test/flutter_gold_expectation.txt new file mode 100644 index 0000000000..b3e2cce386 --- /dev/null +++ b/dev/automated_tests/flutter_test/flutter_gold_expectation.txt @@ -0,0 +1,7 @@ +[0-9]+:[0-9]+ [+]0: Local passes non-existent baseline for new test, null expectation * + *No expectations provided by Skia Gold for test: library.flutter.new_golden_test.1. This may be a new test. If this is an unexpected result, check https://flutter-gold.skia.org. + *Validate image output found at flutter/test/library/ +[0-9]+:[0-9]+ [+]1: Local passes non-existent baseline for new test, empty expectation * + *No expectations provided by Skia Gold for test: library.flutter.new_golden_test.2. This may be a new test. If this is an unexpected result, check https://flutter-gold.skia.org. + *Validate image output found at flutter/test/library/ +[0-9]+:[0-9]+ [+]2: All tests passed! * diff --git a/dev/automated_tests/flutter_test/flutter_gold_test.dart b/dev/automated_tests/flutter_test/flutter_gold_test.dart new file mode 100644 index 0000000000..36831b6623 --- /dev/null +++ b/dev/automated_tests/flutter_test/flutter_gold_test.dart @@ -0,0 +1,77 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// See also packages/flutter_goldens/test/flutter_goldens_test.dart + +import 'dart:typed_data'; + +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_goldens/flutter_goldens.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:platform/platform.dart'; + +// 1x1 colored pixel +const List _kFailPngBytes = [137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, + 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, + 0, 0, 0, 13, 73, 68, 65, 84, 120, 1, 99, 249, 207, 240, 255, 63, 0, 7, 18, 3, + 2, 164, 147, 160, 197, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]; + +void main() { + final MemoryFileSystem fs = MemoryFileSystem(); + final Directory basedir = fs.directory('flutter/test/library/') + ..createSync(recursive: true); + final FakeSkiaGoldClient fakeSkiaClient = FakeSkiaGoldClient() + ..expectationForTestValues['flutter.new_golden_test.1'] = ''; + final FlutterLocalFileComparator comparator = FlutterLocalFileComparator( + basedir.uri, + fakeSkiaClient, + fs: fs, + platform: FakePlatform( + environment: {'FLUTTER_ROOT': '/flutter'}, + operatingSystem: 'macos' + ), + ); + + test('Local passes non-existent baseline for new test, null expectation', () async { + expect( + await comparator.compare( + Uint8List.fromList(_kFailPngBytes), + Uri.parse('flutter.new_golden_test.1'), + ), + isTrue, + ); + }); + + test('Local passes non-existent baseline for new test, empty expectation', () async { + expect( + await comparator.compare( + Uint8List.fromList(_kFailPngBytes), + Uri.parse('flutter.new_golden_test.2'), + ), + isTrue, + ); + }); +} + +// See also packages/flutter_goldens/test/flutter_goldens_test.dart +class FakeSkiaGoldClient extends Fake implements SkiaGoldClient { + Map expectationForTestValues = {}; + Object? getExpectationForTestThrowable; + @override + Future getExpectationForTest(String testName) async { + if (getExpectationForTestThrowable != null) { + throw getExpectationForTestThrowable!; + } + return expectationForTestValues[testName] ?? ''; + } + + Map> imageBytesValues = >{}; + @override + Future> getImageBytes(String imageHash) async => imageBytesValues[imageHash]!; + + Map cleanTestNameValues = {}; + @override + String cleanTestName(String fileName) => cleanTestNameValues[fileName] ?? ''; +} diff --git a/dev/automated_tests/flutter_test/working_directory_test.dart b/dev/automated_tests/flutter_test/working_directory_test.dart index 8b5ac9bef5..370228c410 100644 --- a/dev/automated_tests/flutter_test/working_directory_test.dart +++ b/dev/automated_tests/flutter_test/working_directory_test.dart @@ -8,5 +8,5 @@ import 'package:test/test.dart' hide TypeMatcher, isInstanceOf; void main() { test('working directory is the root of this package', () { expect(Directory.current.path, endsWith('automated_tests')); - }); + }); } diff --git a/dev/automated_tests/pubspec.yaml b/dev/automated_tests/pubspec.yaml index eed7e95cb2..f523877959 100644 --- a/dev/automated_tests/pubspec.yaml +++ b/dev/automated_tests/pubspec.yaml @@ -8,6 +8,8 @@ dependencies: sdk: flutter flutter_test: sdk: flutter + flutter_goldens: + sdk: flutter integration_test: sdk: flutter test: 1.17.12 diff --git a/dev/conductor/core/analysis_options.yaml b/dev/conductor/core/analysis_options.yaml index 59d57729f5..2d24879e53 100644 --- a/dev/conductor/core/analysis_options.yaml +++ b/dev/conductor/core/analysis_options.yaml @@ -1,12 +1,10 @@ -# Use the analysis options settings from the top level of the repo (not -# the ones from above, which include the `public_member_api_docs` rule). - -include: ../../../analysis_options.yaml +include: ../../analysis_options.yaml analyzer: exclude: - # Ignore protoc generated files + # Ignore protoc generated files - "lib/src/proto/*" + linter: rules: avoid_catches_without_on_clauses: true diff --git a/dev/conductor/ui/analysis_options.yaml b/dev/conductor/ui/analysis_options.yaml index 405ebb6adb..60e3733706 100644 --- a/dev/conductor/ui/analysis_options.yaml +++ b/dev/conductor/ui/analysis_options.yaml @@ -1,7 +1,4 @@ -# Use the analysis options settings from the top level of the repo (not -# the ones from above, which include the `public_member_api_docs` rule). - -include: ../../../analysis_options.yaml +include: ../../analysis_options.yaml linter: rules: diff --git a/dev/devicelab/analysis_options.yaml b/dev/devicelab/analysis_options.yaml index acd91c6c38..2e46286d62 100644 --- a/dev/devicelab/analysis_options.yaml +++ b/dev/devicelab/analysis_options.yaml @@ -1,4 +1,4 @@ -include: ../../analysis_options.yaml +include: ../analysis_options.yaml linter: rules: diff --git a/dev/integration_tests/flutter_gallery/analysis_options.yaml b/dev/integration_tests/flutter_gallery/analysis_options.yaml index 8fe5f684cf..c4a47172d2 100644 --- a/dev/integration_tests/flutter_gallery/analysis_options.yaml +++ b/dev/integration_tests/flutter_gallery/analysis_options.yaml @@ -1,7 +1,4 @@ -# Take our settings from the repo's main analysis_options.yaml file, but add -# an exclude for the build directory. - -include: ../../../analysis_options.yaml +include: ../../analysis_options.yaml analyzer: exclude: diff --git a/examples/api/lib/material/autocomplete/autocomplete.0.dart b/examples/api/lib/material/autocomplete/autocomplete.0.dart index 7f89258a3d..0fdcd98cf5 100644 --- a/examples/api/lib/material/autocomplete/autocomplete.0.dart +++ b/examples/api/lib/material/autocomplete/autocomplete.0.dart @@ -47,7 +47,7 @@ class AutocompleteBasicExample extends StatelessWidget { }); }, onSelected: (String selection) { - print('You just selected $selection'); + debugPrint('You just selected $selection'); }, ); } diff --git a/examples/api/lib/material/autocomplete/autocomplete.1.dart b/examples/api/lib/material/autocomplete/autocomplete.1.dart index 497e692d40..d4da62e74d 100644 --- a/examples/api/lib/material/autocomplete/autocomplete.1.dart +++ b/examples/api/lib/material/autocomplete/autocomplete.1.dart @@ -79,7 +79,7 @@ class AutocompleteBasicUserExample extends StatelessWidget { }); }, onSelected: (User selection) { - print('You just selected ${_displayStringForOption(selection)}'); + debugPrint('You just selected ${_displayStringForOption(selection)}'); }, ); } diff --git a/examples/api/lib/material/card/card.1.dart b/examples/api/lib/material/card/card.1.dart index 5a3038cd88..3eb4457afd 100644 --- a/examples/api/lib/material/card/card.1.dart +++ b/examples/api/lib/material/card/card.1.dart @@ -35,7 +35,7 @@ class MyStatelessWidget extends StatelessWidget { child: InkWell( splashColor: Colors.blue.withAlpha(30), onTap: () { - print('Card tapped.'); + debugPrint('Card tapped.'); }, child: const SizedBox( width: 300, diff --git a/examples/api/lib/material/checkbox_list_tile/checkbox_list_tile.1.dart b/examples/api/lib/material/checkbox_list_tile/checkbox_list_tile.1.dart index 78319e8bf9..470d21fe39 100644 --- a/examples/api/lib/material/checkbox_list_tile/checkbox_list_tile.1.dart +++ b/examples/api/lib/material/checkbox_list_tile/checkbox_list_tile.1.dart @@ -59,7 +59,7 @@ class LinkedLabelCheckbox extends StatelessWidget { ), recognizer: TapGestureRecognizer() ..onTap = () { - print('Label has been tapped.'); + debugPrint('Label has been tapped.'); }, ), ), diff --git a/examples/api/lib/material/floating_action_button_location/standard_fab_location.0.dart b/examples/api/lib/material/floating_action_button_location/standard_fab_location.0.dart index db5fc0faf4..a7dd2548a1 100644 --- a/examples/api/lib/material/floating_action_button_location/standard_fab_location.0.dart +++ b/examples/api/lib/material/floating_action_button_location/standard_fab_location.0.dart @@ -45,7 +45,7 @@ class MyStatelessWidget extends StatelessWidget { ), floatingActionButton: FloatingActionButton( onPressed: () { - print('FAB pressed.'); + debugPrint('FAB pressed.'); }, tooltip: 'Increment', child: const Icon(Icons.add), diff --git a/examples/api/lib/material/outlined_button/outlined_button.0.dart b/examples/api/lib/material/outlined_button/outlined_button.0.dart index c55d0edbdf..887c5769da 100644 --- a/examples/api/lib/material/outlined_button/outlined_button.0.dart +++ b/examples/api/lib/material/outlined_button/outlined_button.0.dart @@ -34,7 +34,7 @@ class MyStatelessWidget extends StatelessWidget { Widget build(BuildContext context) { return OutlinedButton( onPressed: () { - print('Received click'); + debugPrint('Received click'); }, child: const Text('Click Me'), ); diff --git a/examples/api/lib/material/radio_list_tile/radio_list_tile.1.dart b/examples/api/lib/material/radio_list_tile/radio_list_tile.1.dart index 98cc2f4131..d4fb40b668 100644 --- a/examples/api/lib/material/radio_list_tile/radio_list_tile.1.dart +++ b/examples/api/lib/material/radio_list_tile/radio_list_tile.1.dart @@ -64,7 +64,7 @@ class LinkedLabelRadio extends StatelessWidget { ), recognizer: TapGestureRecognizer() ..onTap = () { - print('Label has been tapped.'); + debugPrint('Label has been tapped.'); }, ), ), diff --git a/examples/api/lib/material/switch_list_tile/switch_list_tile.1.dart b/examples/api/lib/material/switch_list_tile/switch_list_tile.1.dart index c810ac8a4d..ea24f2ae56 100644 --- a/examples/api/lib/material/switch_list_tile/switch_list_tile.1.dart +++ b/examples/api/lib/material/switch_list_tile/switch_list_tile.1.dart @@ -59,7 +59,7 @@ class LinkedLabelSwitch extends StatelessWidget { ), recognizer: TapGestureRecognizer() ..onTap = () { - print('Label has been tapped.'); + debugPrint('Label has been tapped.'); }, ), ), diff --git a/examples/api/lib/material/text_form_field/text_form_field.1.dart b/examples/api/lib/material/text_form_field/text_form_field.1.dart index aa2c1d864c..890179c0fb 100644 --- a/examples/api/lib/material/text_form_field/text_form_field.1.dart +++ b/examples/api/lib/material/text_form_field/text_form_field.1.dart @@ -54,7 +54,7 @@ class _MyStatefulWidgetState extends State { constraints: BoxConstraints.tight(const Size(200, 50)), child: TextFormField( onSaved: (String? value) { - print('Value for field $index saved as "$value"'); + debugPrint('Value for field $index saved as "$value"'); }, ), ), diff --git a/examples/api/lib/widgets/actions/action_listener.0.dart b/examples/api/lib/widgets/actions/action_listener.0.dart index 42fb6fe296..c481a9cec4 100644 --- a/examples/api/lib/widgets/actions/action_listener.0.dart +++ b/examples/api/lib/widgets/actions/action_listener.0.dart @@ -92,13 +92,13 @@ class MyAction extends Action { @override void addActionListener(ActionListenerCallback listener) { super.addActionListener(listener); - print('Action Listener was added'); + debugPrint('Action Listener was added'); } @override void removeActionListener(ActionListenerCallback listener) { super.removeActionListener(listener); - print('Action Listener was removed'); + debugPrint('Action Listener was removed'); } @override diff --git a/examples/api/lib/widgets/actions/actions.0.dart b/examples/api/lib/widgets/actions/actions.0.dart index c037053ab1..3fb02a1102 100644 --- a/examples/api/lib/widgets/actions/actions.0.dart +++ b/examples/api/lib/widgets/actions/actions.0.dart @@ -34,7 +34,7 @@ class Model { int save() { if (isDirty.value) { - print('Saved Data: ${data.value}'); + debugPrint('Saved Data: ${data.value}'); isDirty.value = false; } return data.value; diff --git a/examples/api/lib/widgets/focus_manager/focus_node.0.dart b/examples/api/lib/widgets/focus_manager/focus_node.0.dart index 7c32781a73..cb63bfbe3a 100644 --- a/examples/api/lib/widgets/focus_manager/focus_node.0.dart +++ b/examples/api/lib/widgets/focus_manager/focus_node.0.dart @@ -57,21 +57,21 @@ class _ColorfulButtonState extends State { KeyEventResult _handleKeyPress(FocusNode node, RawKeyEvent event) { if (event is RawKeyDownEvent) { - print('Focus node ${node.debugLabel} got key event: ${event.logicalKey}'); + debugPrint('Focus node ${node.debugLabel} got key event: ${event.logicalKey}'); if (event.logicalKey == LogicalKeyboardKey.keyR) { - print('Changing color to red.'); + debugPrint('Changing color to red.'); setState(() { _color = Colors.red; }); return KeyEventResult.handled; } else if (event.logicalKey == LogicalKeyboardKey.keyG) { - print('Changing color to green.'); + debugPrint('Changing color to green.'); setState(() { _color = Colors.green; }); return KeyEventResult.handled; } else if (event.logicalKey == LogicalKeyboardKey.keyB) { - print('Changing color to blue.'); + debugPrint('Changing color to blue.'); setState(() { _color = Colors.blue; }); diff --git a/examples/api/lib/widgets/focus_scope/focus.0.dart b/examples/api/lib/widgets/focus_scope/focus.0.dart index adcef1ec88..54525b708c 100644 --- a/examples/api/lib/widgets/focus_scope/focus.0.dart +++ b/examples/api/lib/widgets/focus_scope/focus.0.dart @@ -38,21 +38,21 @@ class _MyStatefulWidgetState extends State { KeyEventResult _handleKeyPress(FocusNode node, RawKeyEvent event) { if (event is RawKeyDownEvent) { - print('Focus node ${node.debugLabel} got key event: ${event.logicalKey}'); + debugPrint('Focus node ${node.debugLabel} got key event: ${event.logicalKey}'); if (event.logicalKey == LogicalKeyboardKey.keyR) { - print('Changing color to red.'); + debugPrint('Changing color to red.'); setState(() { _color = Colors.red; }); return KeyEventResult.handled; } else if (event.logicalKey == LogicalKeyboardKey.keyG) { - print('Changing color to green.'); + debugPrint('Changing color to green.'); setState(() { _color = Colors.green; }); return KeyEventResult.handled; } else if (event.logicalKey == LogicalKeyboardKey.keyB) { - print('Changing color to blue.'); + debugPrint('Changing color to blue.'); setState(() { _color = Colors.blue; }); diff --git a/examples/api/lib/widgets/focus_scope/focus_scope.0.dart b/examples/api/lib/widgets/focus_scope/focus_scope.0.dart index e905a84e32..5a5806e799 100644 --- a/examples/api/lib/widgets/focus_scope/focus_scope.0.dart +++ b/examples/api/lib/widgets/focus_scope/focus_scope.0.dart @@ -115,7 +115,7 @@ class _MyStatefulWidgetState extends State { // This button would be not visible, but still focusable from // the foreground pane without the FocusScope. ElevatedButton( - onPressed: () => print('You pressed the other button!'), + onPressed: () => debugPrint('You pressed the other button!'), child: const Text('ANOTHER BUTTON TO FOCUS'), ), DefaultTextStyle( diff --git a/examples/api/lib/widgets/focus_traversal/focus_traversal_group.0.dart b/examples/api/lib/widgets/focus_traversal/focus_traversal_group.0.dart index 84e404f636..7182b67ed5 100644 --- a/examples/api/lib/widgets/focus_traversal/focus_traversal_group.0.dart +++ b/examples/api/lib/widgets/focus_traversal/focus_traversal_group.0.dart @@ -68,7 +68,7 @@ class _OrderedButtonState extends State> { void _handleOnPressed() { focusNode.requestFocus(); - print('Button ${widget.name} pressed.'); + debugPrint('Button ${widget.name} pressed.'); debugDumpFocusTree(); } diff --git a/examples/api/lib/widgets/focus_traversal/ordered_traversal_policy.0.dart b/examples/api/lib/widgets/focus_traversal/ordered_traversal_policy.0.dart index e7f1173455..aea86f97e4 100644 --- a/examples/api/lib/widgets/focus_traversal/ordered_traversal_policy.0.dart +++ b/examples/api/lib/widgets/focus_traversal/ordered_traversal_policy.0.dart @@ -40,7 +40,7 @@ class DemoButton extends StatelessWidget { final double order; void _handleOnPressed() { - print('Button $name pressed.'); + debugPrint('Button $name pressed.'); debugDumpFocusTree(); } diff --git a/examples/api/lib/widgets/interactive_viewer/interactive_viewer.builder.0.dart b/examples/api/lib/widgets/interactive_viewer/interactive_viewer.builder.0.dart index 3efa90e348..a85d51670c 100644 --- a/examples/api/lib/widgets/interactive_viewer/interactive_viewer.builder.0.dart +++ b/examples/api/lib/widgets/interactive_viewer/interactive_viewer.builder.0.dart @@ -122,10 +122,10 @@ class _IVBuilderExampleState extends State<_IVBuilderExample> { cellWidth: _cellWidth, builder: (BuildContext context, int row, int column) { if (!_isCellVisible(row, column, viewport)) { - print('removing cell ($row, $column)'); + debugPrint('removing cell ($row, $column)'); return Container(height: _cellHeight); } - print('building cell ($row, $column)'); + debugPrint('building cell ($row, $column)'); return Container( height: _cellHeight, color: row % 2 + column % 2 == 1 diff --git a/examples/api/lib/widgets/notification_listener/notification.0.dart b/examples/api/lib/widgets/notification_listener/notification.0.dart index c8103ebcd4..75c9083dc3 100644 --- a/examples/api/lib/widgets/notification_listener/notification.0.dart +++ b/examples/api/lib/widgets/notification_listener/notification.0.dart @@ -45,9 +45,9 @@ class MyStatelessWidget extends StatelessWidget { body: NotificationListener( onNotification: (ScrollNotification scrollNotification) { if (scrollNotification is ScrollStartNotification) { - print('Scrolling has started'); + debugPrint('Scrolling has started'); } else if (scrollNotification is ScrollEndNotification) { - print('Scrolling has ended'); + debugPrint('Scrolling has ended'); } // Return true to cancel the notification bubbling. return true; diff --git a/examples/image_list/lib/main.dart b/examples/image_list/lib/main.dart index 6dc43e31e5..192891b144 100644 --- a/examples/image_list/lib/main.dart +++ b/examples/image_list/lib/main.dart @@ -98,7 +98,7 @@ Future main() async { final HttpServer httpServer = await HttpServer.bindSecure('localhost', 0, serverContext); final int port = httpServer.port; - print('Listening on port $port.'); + debugPrint('Listening on port $port.'); // Initializes bindings before using any platform channels. WidgetsFlutterBinding.ensureInitialized(); @@ -193,7 +193,7 @@ class _MyHomePageState extends State with TickerProviderStateMixin { ).toList(); final DateTime started = DateTime.now(); Future.wait(futures).then((_) { - print( + debugPrint( '===image_list=== all loaded in ${DateTime.now().difference(started).inMilliseconds}ms.', ); }); diff --git a/examples/layers/services/isolate.dart b/examples/layers/services/isolate.dart index fbd32bddfd..9aa3234c97 100644 --- a/examples/layers/services/isolate.dart +++ b/examples/layers/services/isolate.dart @@ -47,8 +47,8 @@ class Calculator { final int n = result.length; onResultListener('Decoded $n results'); } catch (e, stack) { - print('Invalid JSON file: $e'); - print(stack); + debugPrint('Invalid JSON file: $e'); + debugPrint('$stack'); } } diff --git a/packages/flutter/lib/src/foundation/print.dart b/packages/flutter/lib/src/foundation/print.dart index 5aef1d3076..b12f4902cc 100644 --- a/packages/flutter/lib/src/foundation/print.dart +++ b/packages/flutter/lib/src/foundation/print.dart @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This file implements debugPrint in terms of print, so avoiding +// calling "print" is sort of a non-starter here... +// ignore_for_file: avoid_print + import 'dart:async'; import 'dart:collection'; diff --git a/packages/flutter/test/services/text_input_utils.dart b/packages/flutter/test/services/text_input_utils.dart index fd58e40548..e1e3ddf9b5 100644 --- a/packages/flutter/test/services/text_input_utils.dart +++ b/packages/flutter/test/services/text_input_utils.dart @@ -42,6 +42,7 @@ class FakeTextChannel implements MethodChannel { void validateOutgoingMethodCalls(List calls) { expect(outgoingCalls.length, calls.length); + final StringBuffer output = StringBuffer(); bool hasError = false; for (int i = 0; i < calls.length; i++) { final ByteData outgoingData = codec.encodeMethodCall(outgoingCalls[i]); @@ -50,7 +51,7 @@ class FakeTextChannel implements MethodChannel { final String expectedString = utf8.decode(expectedData.buffer.asUint8List()); if (outgoingString != expectedString) { - print( + output.writeln( 'Index $i did not match:\n' ' actual: $outgoingString\n' ' expected: $expectedString', @@ -59,7 +60,7 @@ class FakeTextChannel implements MethodChannel { } } if (hasError) { - fail('Calls did not match.'); + fail('Calls did not match:\n$output'); } } } diff --git a/packages/flutter/test/widgets/multichild_test.dart b/packages/flutter/test/widgets/multichild_test.dart index f3c44b0724..8b4a581055 100644 --- a/packages/flutter/test/widgets/multichild_test.dart +++ b/packages/flutter/test/widgets/multichild_test.dart @@ -26,7 +26,7 @@ void checkTree(WidgetTester tester, List expectedDecorations) { } expect(child, isNull); } catch (e) { - print(renderObject.toStringDeep()); + debugPrint(renderObject.toStringDeep()); rethrow; } } diff --git a/packages/flutter/test/widgets/parent_data_test.dart b/packages/flutter/test/widgets/parent_data_test.dart index e6fce60303..56eb8d9e7f 100644 --- a/packages/flutter/test/widgets/parent_data_test.dart +++ b/packages/flutter/test/widgets/parent_data_test.dart @@ -41,7 +41,7 @@ void checkTree(WidgetTester tester, List expectedParentData) { } expect(child, isNull); } catch (e) { - print(renderObject.toStringDeep()); + debugPrint(renderObject.toStringDeep()); rethrow; } } diff --git a/packages/flutter/test_private/analysis_options.yaml b/packages/flutter/test_private/analysis_options.yaml new file mode 100644 index 0000000000..a3c97d35d0 --- /dev/null +++ b/packages/flutter/test_private/analysis_options.yaml @@ -0,0 +1,5 @@ +include: ../analysis_options.yaml + +linter: + rules: + avoid_print: false # This is a CLI tool, so printing to the console is fine. diff --git a/packages/flutter_driver/lib/src/driver/vmservice_driver.dart b/packages/flutter_driver/lib/src/driver/vmservice_driver.dart index a5b39bccef..cbe460441f 100644 --- a/packages/flutter_driver/lib/src/driver/vmservice_driver.dart +++ b/packages/flutter_driver/lib/src/driver/vmservice_driver.dart @@ -50,7 +50,7 @@ class VMServiceFlutterDriver extends FlutterDriver { // TODO(awdavies): Use something other than print. On fuchsia // `stderr`/`stdout` appear to have issues working correctly. driverLog = (String source, String message) { - print('$source: $message'); + print('$source: $message'); // ignore: avoid_print }; fuchsiaModuleTarget ??= Platform.environment['FUCHSIA_MODULE_TARGET']; if (fuchsiaModuleTarget == null) { diff --git a/packages/flutter_driver/lib/src/driver/web_driver.dart b/packages/flutter_driver/lib/src/driver/web_driver.dart index b9a67d610f..e62ec2071b 100644 --- a/packages/flutter_driver/lib/src/driver/web_driver.dart +++ b/packages/flutter_driver/lib/src/driver/web_driver.dart @@ -222,11 +222,7 @@ class WebFlutterDriver extends FlutterDriver { class FlutterWebConnection { /// Creates a FlutterWebConnection with WebDriver /// and whether the WebDriver supports timeline action. - FlutterWebConnection(this._driver, this.supportsTimelineAction) { - _driver.logs.get(async_io.LogType.browser).listen((async_io.LogEntry entry) { - print('[${entry.level}]: ${entry.message}'); - }); - } + FlutterWebConnection(this._driver, this.supportsTimelineAction); final async_io.WebDriver _driver; diff --git a/packages/flutter_driver/test/common.dart b/packages/flutter_driver/test/common.dart index 0fb8ed3db7..85b6fb9714 100644 --- a/packages/flutter_driver/test/common.dart +++ b/packages/flutter_driver/test/common.dart @@ -20,7 +20,7 @@ void tryToDelete(Directory directory) { try { directory.deleteSync(recursive: true); } on FileSystemException catch (error) { - print('Failed to delete ${directory.path}: $error'); + driverLog('test', 'Failed to delete ${directory.path}: $error'); } } diff --git a/packages/flutter_goldens/lib/flutter_goldens.dart b/packages/flutter_goldens/lib/flutter_goldens.dart index 97375513a3..e2bd660331 100644 --- a/packages/flutter_goldens/lib/flutter_goldens.dart +++ b/packages/flutter_goldens/lib/flutter_goldens.dart @@ -385,9 +385,11 @@ class FlutterSkippingFileComparator extends FlutterGoldenFileComparator { @override Future compare(Uint8List imageBytes, Uri golden) async { - print( - 'Skipping "$golden" test : $reason' - ); + // Ideally we would use markTestSkipped here but in some situations, + // comparators are called outside of tests. + // See also: https://github.com/flutter/flutter/issues/91285 + // ignore: avoid_print + print('Skipping "$golden" test: $reason'); return true; } @@ -502,8 +504,13 @@ class FlutterLocalFileComparator extends FlutterGoldenFileComparator with LocalC testExpectation = await skiaClient.getExpectationForTest(testName); if (testExpectation == null || testExpectation.isEmpty) { - // There is no baseline for this test - print('No expectations provided by Skia Gold for test: $golden. ' + // There is no baseline for this test. + // Ideally we would use markTestSkipped here but in some situations, + // comparators are called outside of tests. + // See also: https://github.com/flutter/flutter/issues/91285 + // ignore: avoid_print + print( + 'No expectations provided by Skia Gold for test: $golden. ' 'This may be a new test. If this is an unexpected result, check ' 'https://flutter-gold.skia.org.\n' 'Validate image output found at $basedir' diff --git a/packages/flutter_goldens/test/flutter_goldens_test.dart b/packages/flutter_goldens/test/flutter_goldens_test.dart index b6a74423d3..e47bdb30bf 100644 --- a/packages/flutter_goldens/test/flutter_goldens_test.dart +++ b/packages/flutter_goldens/test/flutter_goldens_test.dart @@ -8,18 +8,19 @@ // Fails with "flutter test --test-randomize-ordering-seed=123" @Tags(['no-shuffle']) +// See also dev/automated_tests/flutter_test/flutter_gold_test.dart + import 'dart:async'; import 'dart:convert'; -import 'dart:core'; import 'dart:io' hide Directory; import 'dart:typed_data'; import 'dart:ui' show hashValues, hashList; import 'package:file/file.dart'; import 'package:file/memory.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_goldens/flutter_goldens.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:meta/meta.dart'; import 'package:platform/platform.dart'; import 'package:process/process.dart'; @@ -41,17 +42,6 @@ const List _kFailPngBytes = 120, 1, 99, 249, 207, 240, 255, 63, 0, 7, 18, 3, 2, 164, 147, 160, 197, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]; -Future testWithOutput(String name, Future Function() body, String expectedOutput) async { - test(name, () async { - final StringBuffer output = StringBuffer(); - void _recordPrint(Zone self, ZoneDelegate parent, Zone zone, String line) { - output.write(line); - } - await runZoned>(body, zoneSpecification: ZoneSpecification(print: _recordPrint)); - expect(output.toString(), expectedOutput); - }); -} - void main() { late MemoryFileSystem fs; late FakePlatform platform; @@ -569,7 +559,6 @@ void main() { const String hash = '55109a4bed52acc780530f7a9aeff6c0'; fakeSkiaClient.expectationForTestValues['flutter.golden_test.1'] = hash; - fakeSkiaClient.expectationForTestValues['flutter.new_golden_test.1'] = ''; fakeSkiaClient.imageBytesValues[hash] =_kTestPngBytes; fakeSkiaClient.cleanTestNameValues['library.flutter.golden_test.1.png'] = 'flutter.golden_test.1'; }); @@ -584,32 +573,6 @@ void main() { ); }); - testWithOutput('passes non-existent baseline for new test, null expectation', () async { - expect( - await comparator.compare( - Uint8List.fromList(_kFailPngBytes), - Uri.parse('flutter.new_golden_test.1'), - ), - isTrue, - ); - }, 'No expectations provided by Skia Gold for test: library.flutter.new_golden_test.1. ' - 'This may be a new test. If this is an unexpected result, check https://flutter-gold.skia.org.\n' - 'Validate image output found at flutter/test/library/' - ); - - testWithOutput('passes non-existent baseline for new test, empty expectation', () async { - expect( - await comparator.compare( - Uint8List.fromList(_kFailPngBytes), - Uri.parse('flutter.new_golden_test.2'), - ), - isTrue, - ); - }, 'No expectations provided by Skia Gold for test: library.flutter.new_golden_test.2. ' - 'This may be a new test. If this is an unexpected result, check https://flutter-gold.skia.org.\n' - 'Validate image output found at flutter/test/library/' - ); - test('compare properly awaits validation & output before failing.', () async { final Completer completer = Completer(); final Future result = comparator.compare( @@ -713,14 +676,14 @@ class FakeProcessManager extends Fake implements ProcessManager { workingDirectories.add(workingDirectory); final ProcessResult? result = processResults[RunInvocation(command.cast(), workingDirectory)]; if (result == null && fallbackProcessResult == null) { - // Throwing here might gobble up the exception message if a test fails. - print('ProcessManager.run was called with $command ($workingDirectory) unexpectedly - $processResults.'); + printOnFailure('ProcessManager.run was called with $command ($workingDirectory) unexpectedly - $processResults.'); fail('See above.'); } return result ?? fallbackProcessResult!; } } +// See also dev/automated_tests/flutter_test/flutter_gold_test.dart class FakeSkiaGoldClient extends Fake implements SkiaGoldClient { Map expectationForTestValues = {}; Object? getExpectationForTestThrowable; diff --git a/packages/flutter_goldens_client/lib/skia_client.dart b/packages/flutter_goldens_client/lib/skia_client.dart index db03764e6a..22fdc175e8 100644 --- a/packages/flutter_goldens_client/lib/skia_client.dart +++ b/packages/flutter_goldens_client/lib/skia_client.dart @@ -183,9 +183,10 @@ class SkiaGoldClient { if (result.exitCode != 0) { // We do not want to throw for non-zero exit codes here, as an intentional // change or new golden file test expect non-zero exit codes. Logging here - // is meant to inform when an unexpected result occurs. - print('goldctl imgtest add stdout: ${result.stdout}'); - print('goldctl imgtest add stderr: ${result.stderr}'); + // is meant to help debugging in CI when an unexpected result occurs. + // See also: https://github.com/flutter/flutter/issues/91285 + print('goldctl imgtest add stdout: ${result.stdout}'); // ignore: avoid_print + print('goldctl imgtest add stderr: ${result.stderr}'); // ignore: avoid_print } return true; @@ -301,7 +302,10 @@ class SkiaGoldClient { throw const FormatException('Skia gold expectations do not match expected format.'); expectation = jsonResponse['digest'] as String?; } on FormatException catch (error) { - print( + // Ideally we'd use something like package:test's printOnError, but best reliabilty + // in getting logs on CI for now we're just using print. + // See also: https://github.com/flutter/flutter/issues/91285 + print( // ignore: avoid_print 'Formatting error detected requesting expectations from Flutter Gold.\n' 'error: $error\n' 'url: $requestForExpectations\n' diff --git a/packages/flutter_test/lib/src/goldens.dart b/packages/flutter_test/lib/src/goldens.dart index f88306f43c..a969fd5eed 100644 --- a/packages/flutter_test/lib/src/goldens.dart +++ b/packages/flutter_test/lib/src/goldens.dart @@ -6,6 +6,8 @@ import 'dart:typed_data'; import 'dart:ui'; import 'package:path/path.dart' as path; +import 'package:test_api/test_api.dart'; // ignore: deprecated_member_use + import '_goldens_io.dart' if (dart.library.html) '_goldens_web.dart' as _goldens; /// Compares image pixels against a golden image file. @@ -268,6 +270,10 @@ class TrivialComparator implements GoldenFileComparator { @override Future compare(Uint8List imageBytes, Uri golden) { + // Ideally we would use markTestSkipped here but in some situations, + // comparators are called outside of tests. + // See also: https://github.com/flutter/flutter/issues/91285 + // ignore: avoid_print print('Golden file comparison requested for "$golden"; skipping...'); return Future.value(true); } @@ -288,6 +294,10 @@ class _TrivialWebGoldenComparator implements WebGoldenComparator { @override Future compare(double width, double height, Uri golden) { + // Ideally we would use markTestSkipped here but in some situations, + // comparators are called outside of tests. + // See also: https://github.com/flutter/flutter/issues/91285 + // ignore: avoid_print print('Golden comparison requested for "$golden"; skipping...'); return Future.value(true); } diff --git a/packages/flutter_test/lib/src/test_compat.dart b/packages/flutter_test/lib/src/test_compat.dart index a73998a3c5..60428a3c59 100644 --- a/packages/flutter_test/lib/src/test_compat.dart +++ b/packages/flutter_test/lib/src/test_compat.dart @@ -96,7 +96,7 @@ Future _runLiveTest(Suite suiteConfig, LiveTest liveTest, _Reporter report Future _runSkippedTest(Suite suiteConfig, Test test, List parents, _Reporter reporter) async { final LocalTest skipped = LocalTest(test.name, test.metadata, () { }, trace: test.trace); if (skipped.metadata.skipReason != null) { - print('Skip: ${skipped.metadata.skipReason}'); + reporter.log('Skip: ${skipped.metadata.skipReason}'); } final LiveTest liveTest = skipped.load(suiteConfig); reporter._onTestStarted(liveTest); @@ -334,7 +334,7 @@ class _Reporter { if (message.type == MessageType.skip) { text = ' $_yellow$text$_noColor'; } - print(text); + log(text); })); } @@ -351,8 +351,8 @@ class _Reporter { return; } _progressLine(_description(liveTest), suffix: ' $_bold$_red[E]$_noColor'); - print(_indent(error.toString())); - print(_indent('$stackTrace')); + log(_indent(error.toString())); + log(_indent('$stackTrace')); } /// A callback called when the engine is finished running tests. @@ -421,7 +421,7 @@ class _Reporter { buffer.write(message); buffer.write(_noColor); - print(buffer.toString()); + log(buffer.toString()); } /// Returns a representation of [duration] as `MM:SS`. @@ -442,6 +442,13 @@ class _Reporter { } return name; } + + /// Print the message to the console. + void log(String message) { + // We centralize all the prints in this file through this one method so that + // in principle we can reroute the output easily should we need to. + print(message); // ignore: avoid_print + } } String _indent(String string, { int? size, String? first }) { diff --git a/packages/flutter_test/lib/src/widget_tester.dart b/packages/flutter_test/lib/src/widget_tester.dart index 29638fa533..eb9e64417a 100644 --- a/packages/flutter_test/lib/src/widget_tester.dart +++ b/packages/flutter_test/lib/src/widget_tester.dart @@ -416,7 +416,7 @@ Future benchmarkWidgets( assert(() { if (mayRunWithAsserts) return true; - print(kDebugWarning); + debugPrint(kDebugWarning); return true; }()); final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized() as TestWidgetsFlutterBinding; diff --git a/packages/flutter_tools/analysis_options.yaml b/packages/flutter_tools/analysis_options.yaml index 7b7e75c439..d777166af5 100644 --- a/packages/flutter_tools/analysis_options.yaml +++ b/packages/flutter_tools/analysis_options.yaml @@ -1,12 +1,11 @@ -# Use the analysis options settings from the top level of the repo (not -# the ones from above, which include the `public_member_api_docs` rule). - -include: ../../analysis_options.yaml +include: ../analysis_options.yaml linter: rules: avoid_catches_without_on_clauses: true curly_braces_in_flow_control_structures: true - library_private_types_in_public_api: false # Tool does not have any public API + library_private_types_in_public_api: false # Tool does not have any public API. + no_runtimeType_toString: false # We use runtimeType for debugging in the tool. prefer_relative_imports: true + public_member_api_docs: false # Tool does not have any public API. unawaited_futures: true diff --git a/packages/flutter_tools/bin/analysis_options.yaml b/packages/flutter_tools/bin/analysis_options.yaml new file mode 100644 index 0000000000..19391e33ff --- /dev/null +++ b/packages/flutter_tools/bin/analysis_options.yaml @@ -0,0 +1,5 @@ +include: ../analysis_options.yaml + +linter: + rules: + avoid_print: false # These are CLI tools which print as a matter of course. diff --git a/packages/flutter_tools/lib/src/base/io.dart b/packages/flutter_tools/lib/src/base/io.dart index 1f3cebb27f..05a60e0dd3 100644 --- a/packages/flutter_tools/lib/src/base/io.dart +++ b/packages/flutter_tools/lib/src/base/io.dart @@ -25,6 +25,12 @@ /// about any additional exports that you add to this file, as doing so will /// increase the API surface that we have to test in Flutter tools, and the APIs /// in `dart:io` can sometimes be hard to use in tests. + +// We allow `print()` in this file as a fallback for writing to the terminal via +// regular stdout/stderr/stdio paths. Everything else in the flutter_tools +// library should route terminal I/O through the [Stdio] class defined below. +// ignore_for_file: avoid_print + import 'dart:async'; import 'dart:io' as io show diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart index f91c9923ea..e6a39ca39c 100644 --- a/packages/flutter_tools/lib/src/cache.dart +++ b/packages/flutter_tools/lib/src/cache.dart @@ -247,6 +247,7 @@ class Cache { } } on Exception catch (error) { // There is currently no logger attached since this is computed at startup. + // ignore: avoid_print print(userMessages.runnerNoRoot('$error')); } return normalize('.'); diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart index f356bec230..3e99d24829 100644 --- a/packages/flutter_tools/lib/src/commands/daemon.dart +++ b/packages/flutter_tools/lib/src/commands/daemon.dart @@ -307,6 +307,7 @@ class DaemonDomain extends Domain { if (message.level == 'status') { // We use `print()` here instead of `stdout.writeln()` in order to // capture the print output for testing. + // ignore: avoid_print print(message.message); } else if (message.level == 'error') { globals.stdio.stderrWrite('${message.message}\n'); diff --git a/packages/flutter_tools/lib/src/macos/macos_device.dart b/packages/flutter_tools/lib/src/macos/macos_device.dart index dc746a522d..51b274d655 100644 --- a/packages/flutter_tools/lib/src/macos/macos_device.dart +++ b/packages/flutter_tools/lib/src/macos/macos_device.dart @@ -96,7 +96,7 @@ class MacOSDevice extends DesktopDevice { 'open', package.applicationBundle(buildMode), ]).then((ProcessResult result) { if (result.exitCode != 0) { - print('Failed to foreground app; open returned ${result.exitCode}'); + _logger.printError('Failed to foreground app; open returned ${result.exitCode}'); } }); } diff --git a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart index 8d23794123..f741e5607d 100755 --- a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart @@ -2723,10 +2723,9 @@ Future _ensureFlutterToolsSnapshot() async { '../../bin/cache/dart-sdk/bin/dart', snapshotArgs, ); - if (snapshotResult.exitCode != 0) { - print(snapshotResult.stdout); - print(snapshotResult.stderr); - } + printOnFailure('Results of generating snapshot:'); + printOnFailure(snapshotResult.stdout.toString()); + printOnFailure(snapshotResult.stderr.toString()); expect(snapshotResult.exitCode, 0); } @@ -2768,10 +2767,9 @@ Future _analyzeProject(String workingDir, { List expectedFailures workingDirectory: workingDir, ); if (expectedFailures.isEmpty) { - if (exec.exitCode != 0) { - print(exec.stdout); - print(exec.stderr); - } + printOnFailure('Results of running analyzer:'); + printOnFailure(exec.stdout.toString()); + printOnFailure(exec.stderr.toString()); expect(exec.exitCode, 0); return; } @@ -2825,10 +2823,9 @@ Future _runFlutterTest(Directory workingDir, { String target }) async { args, workingDirectory: workingDir.path, ); - if (exec.exitCode != 0) { - print(exec.stdout); - print(exec.stderr); - } + printOnFailure('Output of running flutter test:'); + printOnFailure(exec.stdout.toString()); + printOnFailure(exec.stderr.toString()); expect(exec.exitCode, 0); } diff --git a/packages/flutter_tools/test/general.shard/runner/runner_test.dart b/packages/flutter_tools/test/general.shard/runner/runner_test.dart index 2e07984b83..f460fe6a33 100644 --- a/packages/flutter_tools/test/general.shard/runner/runner_test.dart +++ b/packages/flutter_tools/test/general.shard/runner/runner_test.dart @@ -179,7 +179,6 @@ void main() { expect(logContents, contains('String: an exception % --')); expect(logContents, contains('CrashingFlutterCommand.runCommand')); expect(logContents, contains('[✓] Flutter')); - print(globals.crashReporter.runtimeType); final CrashDetails sentDetails = (globals.crashReporter as WaitingCrashReporter)._details; expect(sentDetails.command, 'flutter crash'); diff --git a/packages/flutter_tools/test/integration.shard/analyze_once_test.dart b/packages/flutter_tools/test/integration.shard/analyze_once_test.dart index 0a0e85cfdc..95bd0f4d59 100644 --- a/packages/flutter_tools/test/integration.shard/analyze_once_test.dart +++ b/packages/flutter_tools/test/integration.shard/analyze_once_test.dart @@ -28,8 +28,9 @@ void main() { '--no-color', ...arguments, ], workingDirectory: projectPath); - print(result.stdout); - print(result.stderr); + printOnFailure('Output of flutter ${arguments.join(" ")}'); + printOnFailure(result.stdout.toString()); + printOnFailure(result.stderr.toString()); expect(result.exitCode, exitCode, reason: 'Expected to exit with non-zero exit code.'); assertContains(result.stdout.toString(), statusTextContains); assertContains(result.stdout.toString(), errorTextContains); diff --git a/packages/flutter_tools/test/integration.shard/analyze_size_test.dart b/packages/flutter_tools/test/integration.shard/analyze_size_test.dart index 42620d1e13..1fe91a542a 100644 --- a/packages/flutter_tools/test/integration.shard/analyze_size_test.dart +++ b/packages/flutter_tools/test/integration.shard/analyze_size_test.dart @@ -31,8 +31,9 @@ void main() { '--target-platform=android-arm64' ], workingDirectory: workingDirectory); - print(result.stdout); - print(result.stderr); + printOnFailure('Output of flutter build apk:'); + printOnFailure(result.stdout.toString()); + printOnFailure(result.stderr.toString()); expect(result.stdout.toString(), contains('app-release.apk (total compressed)')); final String line = result.stdout.toString() @@ -67,8 +68,9 @@ void main() { '--no-codesign', ], workingDirectory: workingDirectory); - print(result.stdout); - print(result.stderr); + printOnFailure('Output of flutter build ios:'); + printOnFailure(result.stdout.toString()); + printOnFailure(result.stderr.toString()); expect(result.stdout.toString(), contains('Dart AOT symbols accounted decompressed size')); final String line = result.stdout.toString() @@ -102,8 +104,9 @@ void main() { '--enable-macos-desktop', ], workingDirectory: workingDirectory); - print(configResult.stdout); - print(configResult.stderr); + printOnFailure('Output of flutter config:'); + printOnFailure(configResult.stdout.toString()); + printOnFailure(configResult.stderr.toString()); final ProcessResult result = await processManager.run([ flutterBin, @@ -113,8 +116,9 @@ void main() { '--code-size-directory=${codeSizeDir.path}', ], workingDirectory: workingDirectory); - print(result.stdout); - print(result.stderr); + printOnFailure('Output of flutter build macos:'); + printOnFailure(result.stdout.toString()); + printOnFailure(result.stderr.toString()); expect(result.stdout.toString(), contains('Dart AOT symbols accounted decompressed size')); final String line = result.stdout.toString() @@ -147,8 +151,9 @@ void main() { '--debug', ], workingDirectory: fileSystem.path.join(getFlutterRoot(), 'examples', 'hello_world')); - print(result.stdout); - print(result.stderr); + printOnFailure('Output of flutter build apk:'); + printOnFailure(result.stdout.toString()); + printOnFailure(result.stderr.toString()); expect(result.stderr.toString(), contains('"--analyze-size" can only be used on release builds')); expect(result.exitCode, 1); diff --git a/packages/flutter_tools/test/integration.shard/background_isolate_test.dart b/packages/flutter_tools/test/integration.shard/background_isolate_test.dart index 8285d418fe..bc1cb9a67f 100644 --- a/packages/flutter_tools/test/integration.shard/background_isolate_test.dart +++ b/packages/flutter_tools/test/integration.shard/background_isolate_test.dart @@ -34,7 +34,7 @@ void main() { final Completer sawBackgroundMessage = Completer.sync(); final Completer sawNewBackgroundMessage = Completer.sync(); final StreamSubscription subscription = flutter.stdout.listen((String line) { - print('[LOG]:"$line"'); + printOnFailure('[LOG]:"$line"'); if (line.contains('Main thread') && !sawForegroundMessage.isCompleted) { sawForegroundMessage.complete(); } @@ -68,7 +68,7 @@ void main() { final Completer sawBackgroundMessage = Completer.sync(); final Completer sawNewBackgroundMessage = Completer.sync(); final StreamSubscription subscription = flutter.stdout.listen((String line) { - print('[LOG]:"$line"'); + printOnFailure('[LOG]:"$line"'); if (line.contains('Isolate thread') && !sawBackgroundMessage.isCompleted) { sawBackgroundMessage.complete(); } diff --git a/packages/flutter_tools/test/integration.shard/build_ios_config_only_test.dart b/packages/flutter_tools/test/integration.shard/build_ios_config_only_test.dart index 61957cf538..f5adc08a02 100644 --- a/packages/flutter_tools/test/integration.shard/build_ios_config_only_test.dart +++ b/packages/flutter_tools/test/integration.shard/build_ios_config_only_test.dart @@ -33,8 +33,9 @@ void main() { '--no-codesign', ], workingDirectory: workingDirectory); - print(result.stdout); - print(result.stderr); + printOnFailure('Output of flutter build ios:'); + printOnFailure(result.stdout.toString()); + printOnFailure(result.stderr.toString()); expect(result.exitCode, 0); diff --git a/packages/flutter_tools/test/integration.shard/cache_test.dart b/packages/flutter_tools/test/integration.shard/cache_test.dart index c45ece1b4b..8f155d6c59 100644 --- a/packages/flutter_tools/test/integration.shard/cache_test.dart +++ b/packages/flutter_tools/test/integration.shard/cache_test.dart @@ -64,11 +64,7 @@ Future main(List args) async { await cache.lock(); process.kill(io.ProcessSignal.sigkill); } finally { - try { - tempDir.deleteSync(recursive: true); - } on FileSystemException { - // Ignore filesystem exceptions when trying to delete tempdir. - } + tryToDelete(tempDir); Cache.flutterRoot = oldRoot; } expect(logger.statusText, isEmpty); diff --git a/packages/flutter_tools/test/integration.shard/deprecated_gradle_settings_test.dart b/packages/flutter_tools/test/integration.shard/deprecated_gradle_settings_test.dart index c2c2ae4a68..142812120d 100644 --- a/packages/flutter_tools/test/integration.shard/deprecated_gradle_settings_test.dart +++ b/packages/flutter_tools/test/integration.shard/deprecated_gradle_settings_test.dart @@ -26,8 +26,10 @@ void main() { '--target-platform', 'android-arm', '--verbose', ], workingDirectory: woringDirectory); - print(result.stdout); - print(result.stderr); + + printOnFailure('Output of flutter build apk:'); + printOnFailure(result.stdout.toString()); + printOnFailure(result.stderr.toString()); expect(result.exitCode, 0); diff --git a/packages/flutter_tools/test/integration.shard/downgrade_upgrade_integration_test.dart b/packages/flutter_tools/test/integration.shard/downgrade_upgrade_integration_test.dart index ea7d48eeb1..0e0f91b39f 100644 --- a/packages/flutter_tools/test/integration.shard/downgrade_upgrade_integration_test.dart +++ b/packages/flutter_tools/test/integration.shard/downgrade_upgrade_integration_test.dart @@ -38,11 +38,7 @@ void main() { }); tearDown(() { - try { - parentDirectory.deleteSync(recursive: true); - } on FileSystemException { - print('Failed to delete test directory'); - } + tryToDelete(parentDirectory); }); testWithoutContext('Can upgrade and downgrade a Flutter checkout', () async { @@ -56,7 +52,7 @@ void main() { 'git', 'config', '--system', 'core.longpaths', 'true', ]); - print('Step 1 - clone the $_kBranch of flutter into the test directory'); + printOnFailure('Step 1 - clone the $_kBranch of flutter into the test directory'); exitCode = await processUtils.stream([ 'git', 'clone', @@ -64,7 +60,7 @@ void main() { ], workingDirectory: parentDirectory.path, trace: true); expect(exitCode, 0); - print('Step 2 - switch to the $_kBranch'); + printOnFailure('Step 2 - switch to the $_kBranch'); exitCode = await processUtils.stream([ 'git', 'checkout', @@ -75,7 +71,7 @@ void main() { ], workingDirectory: testDirectory.path, trace: true); expect(exitCode, 0); - print('Step 3 - revert back to $_kInitialVersion'); + printOnFailure('Step 3 - revert back to $_kInitialVersion'); exitCode = await processUtils.stream([ 'git', 'reset', @@ -84,7 +80,7 @@ void main() { ], workingDirectory: testDirectory.path, trace: true); expect(exitCode, 0); - print('Step 4 - upgrade to the newest $_kBranch'); + printOnFailure('Step 4 - upgrade to the newest $_kBranch'); // This should update the persistent tool state with the sha for HEAD exitCode = await processUtils.stream([ flutterBin, @@ -94,7 +90,7 @@ void main() { ], workingDirectory: testDirectory.path, trace: true); expect(exitCode, 0); - print('Step 5 - verify that the version is different'); + printOnFailure('Step 5 - verify that the version is different'); final RunResult versionResult = await processUtils.run([ 'git', 'describe', @@ -104,10 +100,9 @@ void main() { '--tags', ], workingDirectory: testDirectory.path); expect(versionResult.stdout, isNot(contains(_kInitialVersion))); - print('current version is ${versionResult.stdout.trim()}\ninitial was $_kInitialVersion'); + printOnFailure('current version is ${versionResult.stdout.trim()}\ninitial was $_kInitialVersion'); - print('Step 6 - downgrade back to the initial version'); - // Step 6. Downgrade back to initial version. + printOnFailure('Step 6 - downgrade back to the initial version'); exitCode = await processUtils.stream([ flutterBin, 'downgrade', @@ -116,8 +111,7 @@ void main() { ], workingDirectory: testDirectory.path, trace: true); expect(exitCode, 0); - print('Step 7 - verify downgraded version matches original version'); - // Step 7. Verify downgraded version matches original version. + printOnFailure('Step 7 - verify downgraded version matches original version'); final RunResult oldVersionResult = await processUtils.run([ 'git', 'describe', @@ -127,6 +121,6 @@ void main() { '--tags', ], workingDirectory: testDirectory.path); expect(oldVersionResult.stdout, contains(_kInitialVersion)); - print('current version is ${oldVersionResult.stdout.trim()}\ninitial was $_kInitialVersion'); + printOnFailure('current version is ${oldVersionResult.stdout.trim()}\ninitial was $_kInitialVersion'); }); } diff --git a/packages/flutter_tools/test/integration.shard/exit_code_test.dart b/packages/flutter_tools/test/integration.shard/exit_code_test.dart index 891326bbec..e6b3e6e413 100644 --- a/packages/flutter_tools/test/integration.shard/exit_code_test.dart +++ b/packages/flutter_tools/test/integration.shard/exit_code_test.dart @@ -36,8 +36,9 @@ void main() { fileSystem.path.join(tempDir.path, 'main.dart'), ]); - print(result.stdout); - print(result.stderr); + printOnFailure('Output of dart main.dart:'); + printOnFailure(result.stdout.toString()); + printOnFailure(result.stderr.toString()); expect(result.exitCode, 0); }); @@ -55,8 +56,9 @@ void main() { fileSystem.path.join(tempDir.path, 'main.dart'), ]); - print(result.stdout); - print(result.stderr); + printOnFailure('Output of dart main.dart:'); + printOnFailure(result.stdout.toString()); + printOnFailure(result.stderr.toString()); expect(result.exitCode, 1); }); } diff --git a/packages/flutter_tools/test/integration.shard/flutter_build_null_unsafe_test.dart b/packages/flutter_tools/test/integration.shard/flutter_build_null_unsafe_test.dart index 483d8eba96..70cc4c1c8a 100644 --- a/packages/flutter_tools/test/integration.shard/flutter_build_null_unsafe_test.dart +++ b/packages/flutter_tools/test/integration.shard/flutter_build_null_unsafe_test.dart @@ -67,7 +67,6 @@ String unsafeString = null; for (final String targetPlatform in targetPlatforms) { testWithoutContext('flutter build $targetPlatform --no-sound-null-safety', () { - print(tempDir); final ProcessResult result = processManager.runSync([ flutterBin, ...getLocalEngineArguments(), diff --git a/packages/flutter_tools/test/integration.shard/generated_plugin_registrant_test.dart b/packages/flutter_tools/test/integration.shard/generated_plugin_registrant_test.dart index df491ea312..7332f77c0c 100644 --- a/packages/flutter_tools/test/integration.shard/generated_plugin_registrant_test.dart +++ b/packages/flutter_tools/test/integration.shard/generated_plugin_registrant_test.dart @@ -138,10 +138,9 @@ Future _ensureFlutterToolsSnapshot() async { '../../bin/cache/dart-sdk/bin/dart', snapshotArgs, ); - if (snapshotResult.exitCode != 0) { - print(snapshotResult.stdout); - print(snapshotResult.stderr); - } + printOnFailure('Output of dart ${snapshotArgs.join(" ")}:'); + printOnFailure(snapshotResult.stdout.toString()); + printOnFailure(snapshotResult.stderr.toString()); expect(snapshotResult.exitCode, 0); } @@ -237,9 +236,8 @@ Future _analyzeProject(Directory workingDir) async { args, workingDirectory: workingDir.path, ); - if (exec.exitCode != 0) { - print(exec.stdout); - print(exec.stderr); - } + printOnFailure('Output of flutter analyze:'); + printOnFailure(exec.stdout.toString()); + printOnFailure(exec.stderr.toString()); expect(exec.exitCode, 0); } diff --git a/packages/flutter_tools/test/integration.shard/hot_reload_test.dart b/packages/flutter_tools/test/integration.shard/hot_reload_test.dart index 86eb9e0a58..389297e354 100644 --- a/packages/flutter_tools/test/integration.shard/hot_reload_test.dart +++ b/packages/flutter_tools/test/integration.shard/hot_reload_test.dart @@ -108,8 +108,14 @@ void main() { await flutter.resume(); // we start paused so we can set up our TICK 1 listener before the app starts unawaited(sawTick1.future.timeout( const Duration(seconds: 5), - onTimeout: () { print('The test app is taking longer than expected to print its synchronization line...'); }, + onTimeout: () { + // This print is useful for people debugging this test. Normally we would avoid printing in + // a test but this is an exception because it's useful ambient information. + // ignore: avoid_print + print('The test app is taking longer than expected to print its synchronization line...'); + }, )); + printOnFailure('waiting for synchronization line...'); await sawTick1.future; // after this, app is in steady state await flutter.addBreakpoint( project.scheduledBreakpointUri, @@ -126,19 +132,19 @@ void main() { ); bool reloaded = false; final Future reloadFuture = flutter.hotReload().then((void value) { reloaded = true; }); - print('waiting for pause...'); + printOnFailure('waiting for pause...'); isolate = await flutter.waitForPause(); expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint)); - print('waiting for debugger message...'); + printOnFailure('waiting for debugger message...'); await sawDebuggerPausedMessage.future; expect(reloaded, isFalse); - print('waiting for resume...'); + printOnFailure('waiting for resume...'); await flutter.resume(); - print('waiting for reload future...'); + printOnFailure('waiting for reload future...'); await reloadFuture; expect(reloaded, isTrue); reloaded = false; - print('subscription cancel...'); + printOnFailure('subscription cancel...'); await subscription.cancel(); }); @@ -148,7 +154,7 @@ void main() { final Completer sawDebuggerPausedMessage2 = Completer(); final StreamSubscription subscription = flutter.stdout.listen( (String line) { - print('[LOG]:"$line"'); + printOnFailure('[LOG]:"$line"'); if (line.contains('(((TICK 1)))')) { expect(sawTick1.isCompleted, isFalse); sawTick1.complete(); diff --git a/packages/flutter_tools/test/integration.shard/hot_reload_with_asset_test.dart b/packages/flutter_tools/test/integration.shard/hot_reload_with_asset_test.dart index 35308e8f68..550c6f25e9 100644 --- a/packages/flutter_tools/test/integration.shard/hot_reload_with_asset_test.dart +++ b/packages/flutter_tools/test/integration.shard/hot_reload_with_asset_test.dart @@ -47,7 +47,7 @@ void main() { onSecondLoad.complete(); } }); - flutter.stdout.listen(print); + flutter.stdout.listen(printOnFailure); await flutter.run(); await onFirstLoad.future; @@ -74,7 +74,7 @@ void main() { onSecondLoad.complete(); } }); - flutter.stdout.listen(print); + flutter.stdout.listen(printOnFailure); await flutter.run(); await onFirstLoad.future; diff --git a/packages/flutter_tools/test/integration.shard/ios_content_validation_test.dart b/packages/flutter_tools/test/integration.shard/ios_content_validation_test.dart index 0481fb6740..1d3315683d 100644 --- a/packages/flutter_tools/test/integration.shard/ios_content_validation_test.dart +++ b/packages/flutter_tools/test/integration.shard/ios_content_validation_test.dart @@ -29,7 +29,7 @@ void main() { 'flutter', ); - final ProcessResult createResult = processManager.runSync([ + processManager.runSync([ flutterBin, ...getLocalEngineArguments(), 'create', @@ -39,7 +39,6 @@ void main() { 'objc', 'hello', ], workingDirectory: tempDir.path); - print(createResult.stdout); projectRoot = tempDir.childDirectory('hello').path; }); @@ -59,7 +58,7 @@ void main() { File outputAppFrameworkBinary; setUpAll(() { - final ProcessResult buildResult = processManager.runSync([ + processManager.runSync([ flutterBin, ...getLocalEngineArguments(), 'build', @@ -70,7 +69,6 @@ void main() { '--obfuscate', '--split-debug-info=foo debug info/', ], workingDirectory: projectRoot); - print(buildResult.stdout); buildPath = fileSystem.directory(fileSystem.path.join( projectRoot, @@ -125,7 +123,6 @@ void main() { infoPlistPath, ], ); - print(bonjourServices.stdout); final bool bonjourServicesFound = (bonjourServices.stdout as String).contains('_dartobservatory._tcp'); expect(bonjourServicesFound, buildMode == BuildMode.debug); @@ -140,7 +137,6 @@ void main() { infoPlistPath, ], ); - print(localNetworkUsage.stdout); final bool localNetworkUsageFound = localNetworkUsage.exitCode == 0; expect(localNetworkUsageFound, buildMode == BuildMode.debug); }); @@ -155,7 +151,6 @@ void main() { 'arm64', ], ); - print(symbols.stdout); final bool aotSymbolsFound = (symbols.stdout as String).contains('_kDartVmSnapshot'); expect(aotSymbolsFound, buildMode != BuildMode.debug); }); @@ -197,7 +192,9 @@ void main() { // Skip bitcode stripping since we just checked that above. }, ); - print(xcodeBackendResult.stdout); + printOnFailure('Output of xcode_backend.sh:'); + printOnFailure(xcodeBackendResult.stdout.toString()); + printOnFailure(xcodeBackendResult.stderr.toString()); expect(xcodeBackendResult.exitCode, 0); expect(outputFlutterFrameworkBinary.existsSync(), isTrue); @@ -211,7 +208,6 @@ void main() { 'hello', outputAppFrameworkBinary.path, ]); - print(grepResult.stdout); expect(grepResult.stdout, isNot(contains('matches'))); }); }); @@ -233,7 +229,6 @@ void main() { 'FLUTTER_XCODE_ONLY_ACTIVE_ARCH': 'NO', }, ); - print(buildSimulator.stdout); // This test case would fail if arm64 or x86_64 simulators could not build. expect(buildSimulator.exitCode, 0); @@ -251,7 +246,6 @@ void main() { final ProcessResult archs = processManager.runSync( ['file', simulatorAppFrameworkBinary.path], ); - print(archs.stdout); expect(archs.stdout, contains('Mach-O 64-bit dynamically linked shared library x86_64')); expect(archs.stdout, contains('Mach-O 64-bit dynamically linked shared library arm64')); }); diff --git a/packages/flutter_tools/test/integration.shard/macos_content_validation_test.dart b/packages/flutter_tools/test/integration.shard/macos_content_validation_test.dart index f047814562..11a4c7132f 100644 --- a/packages/flutter_tools/test/integration.shard/macos_content_validation_test.dart +++ b/packages/flutter_tools/test/integration.shard/macos_content_validation_test.dart @@ -53,8 +53,9 @@ void main() { ]; final ProcessResult result = processManager.runSync(buildCommand, workingDirectory: workingDirectory); - print(result.stdout); - print(result.stderr); + printOnFailure('Output of flutter build macos:'); + printOnFailure(result.stdout.toString()); + printOnFailure(result.stderr.toString()); expect(result.exitCode, 0); expect(result.stdout, contains('Running pod install')); @@ -134,8 +135,9 @@ void main() { // Build again without cleaning. final ProcessResult secondBuild = processManager.runSync(buildCommand, workingDirectory: workingDirectory); - print(secondBuild.stdout); - print(secondBuild.stderr); + printOnFailure('Output of second build:'); + printOnFailure(secondBuild.stdout.toString()); + printOnFailure(secondBuild.stderr.toString()); expect(secondBuild.exitCode, 0); expect(secondBuild.stdout, isNot(contains('Running pod install'))); diff --git a/packages/flutter_tools/test/integration.shard/observatory_port_test.dart b/packages/flutter_tools/test/integration.shard/observatory_port_test.dart index 9968488967..0912deb59d 100644 --- a/packages/flutter_tools/test/integration.shard/observatory_port_test.dart +++ b/packages/flutter_tools/test/integration.shard/observatory_port_test.dart @@ -27,7 +27,7 @@ Future waitForObservatoryMessage(Process process, int port) async { process.stdout .transform(utf8.decoder) .listen((String line) { - print(line); + printOnFailure(line); if (line.contains('An Observatory debugger and profiler on Flutter test device is available at')) { if (line.contains('http://127.0.0.1:$port')) { completer.complete(); @@ -38,7 +38,7 @@ Future waitForObservatoryMessage(Process process, int port) async { }); process.stderr .transform(utf8.decoder) - .listen(print); + .listen(printOnFailure); return completer.future; } diff --git a/packages/flutter_tools/test/integration.shard/overall_experience_test.dart b/packages/flutter_tools/test/integration.shard/overall_experience_test.dart index c2c015437c..37cfe86a7c 100644 --- a/packages/flutter_tools/test/integration.shard/overall_experience_test.dart +++ b/packages/flutter_tools/test/integration.shard/overall_experience_test.dart @@ -43,6 +43,13 @@ const ProcessManager processManager = LocalProcessManager(); final String flutterRoot = getFlutterRoot(); final String flutterBin = fileSystem.path.join(flutterRoot, 'bin', 'flutter'); +void debugPrint(String message) { + // This is called to intentionally print debugging output when a test is + // either taking too long or has failed. + // ignore: avoid_print + print(message); +} + typedef LineHandler = String/*?*/ Function(String line); abstract class Transition { @@ -136,7 +143,7 @@ class LogLine { String toString() => '$stamp $channel: $message'; void printClearly() { - print('$stamp $channel: ${clarify(message)}'); + debugPrint('$stamp $channel: ${clarify(message)}'); } static String clarify(String line) { @@ -197,9 +204,9 @@ Future runFlutter( int nextTransition = 0; void describeStatus() { if (transitions.isNotEmpty) { - print('Expected state transitions:'); + debugPrint('Expected state transitions:'); for (int index = 0; index < transitions.length; index += 1) { - print( + debugPrint( '${index.toString().padLeft(5)} ' '${index < nextTransition ? 'ALREADY MATCHED ' : index == nextTransition ? 'NOW WAITING FOR>' : @@ -207,9 +214,9 @@ Future runFlutter( } } if (logs.isEmpty) { - print('So far nothing has been logged${ debug ? "" : "; use debug:true to print all output" }.'); + debugPrint('So far nothing has been logged${ debug ? "" : "; use debug:true to print all output" }.'); } else { - print('Log${ debug ? "" : " (only contains logged lines; use debug:true to print all output)" }:'); + debugPrint('Log${ debug ? "" : " (only contains logged lines; use debug:true to print all output)" }:'); for (final LogLine log in logs) { log.printClearly(); } @@ -221,12 +228,12 @@ Future runFlutter( if (!streamingLogs) { streamingLogs = true; if (!debug) { - print('Test is taking a long time (${clock.elapsed.inSeconds} seconds so far).'); + debugPrint('Test is taking a long time (${clock.elapsed.inSeconds} seconds so far).'); } describeStatus(); - print('(streaming all logs from this point on...)'); + debugPrint('(streaming all logs from this point on...)'); } else { - print('(taking a long time...)'); + debugPrint('(taking a long time...)'); } } String stamp() => '[${(clock.elapsed.inMilliseconds / 1000.0).toStringAsFixed(1).padLeft(5, " ")}s]'; @@ -240,7 +247,7 @@ Future runFlutter( } if (nextTransition < transitions.length && transitions[nextTransition].matches(line)) { if (streamingLogs) { - print('(matched ${transitions[nextTransition]})'); + debugPrint('(matched ${transitions[nextTransition]})'); } if (transitions[nextTransition].logging != null) { if (!logging && transitions[nextTransition].logging/*!*/) { @@ -249,9 +256,9 @@ Future runFlutter( logging = transitions[nextTransition].logging/*!*/; if (streamingLogs) { if (logging) { - print('(enabled logging)'); + debugPrint('(enabled logging)'); } else { - print('(disabled logging)'); + debugPrint('(disabled logging)'); } } } @@ -286,8 +293,8 @@ Future runFlutter( process.stdout.transform(utf8.decoder).transform(const LineSplitter()).listen(processStdout); process.stderr.transform(utf8.decoder).transform(const LineSplitter()).listen(processStderr); unawaited(process.exitCode.timeout(expectedMaxDuration, onTimeout: () { // This is a failure timeout, must not be short. - print('${stamp()} (process is not quitting, trying to send a "q" just in case that helps)'); - print('(a functional test should never reach this point)'); + debugPrint('${stamp()} (process is not quitting, trying to send a "q" just in case that helps)'); + debugPrint('(a functional test should never reach this point)'); final LogLine inLog = LogLine('stdin', stamp(), 'q'); logs.add(inLog); if (streamingLogs) { @@ -298,24 +305,24 @@ Future runFlutter( }).catchError((Object error) { /* ignore errors here, they will be reported on the next line */ })); final int exitCode = await process.exitCode; if (streamingLogs) { - print('${stamp()} (process terminated with exit code $exitCode)'); + debugPrint('${stamp()} (process terminated with exit code $exitCode)'); } timeout?.cancel(); if (nextTransition < transitions.length) { - print('The subprocess terminated before all the expected transitions had been matched.'); + debugPrint('The subprocess terminated before all the expected transitions had been matched.'); if (logs.any((LogLine line) => line.couldBeCrash)) { - print('The subprocess may in fact have crashed. Check the stderr logs below.'); + debugPrint('The subprocess may in fact have crashed. Check the stderr logs below.'); } - print('The transition that we were hoping to see next but that we never saw was:'); - print('${nextTransition.toString().padLeft(5)} NOW WAITING FOR> ${transitions[nextTransition]}'); + debugPrint('The transition that we were hoping to see next but that we never saw was:'); + debugPrint('${nextTransition.toString().padLeft(5)} NOW WAITING FOR> ${transitions[nextTransition]}'); if (!streamingLogs) { describeStatus(); - print('(process terminated with exit code $exitCode)'); + debugPrint('(process terminated with exit code $exitCode)'); } throw TestFailure('Missed some expected transitions.'); } if (streamingLogs) { - print('${stamp()} (completed execution successfully!)'); + debugPrint('${stamp()} (completed execution successfully!)'); } return ProcessTestResult(exitCode, logs); } diff --git a/packages/flutter_tools/test/integration.shard/test_driver.dart b/packages/flutter_tools/test/integration.shard/test_driver.dart index 00d00b744b..c9a0e98eb6 100644 --- a/packages/flutter_tools/test/integration.shard/test_driver.dart +++ b/packages/flutter_tools/test/integration.shard/test_driver.dart @@ -79,6 +79,10 @@ abstract class FlutterTestDriver { lastTime = time; } if (_printDebugOutputToStdOut) { + // This is the one place in this file that can call print. It is gated by + // _printDebugOutputToStdOut which should not be set to true in CI; it is + // intended only for use in local debugging. + // ignore: avoid_print print('$time$_logPrefix$line'); } } diff --git a/packages/flutter_tools/test/integration.shard/test_test.dart b/packages/flutter_tools/test/integration.shard/test_test.dart index bb2aa3751f..67dcef1af4 100644 --- a/packages/flutter_tools/test/integration.shard/test_test.dart +++ b/packages/flutter_tools/test/integration.shard/test_test.dart @@ -215,6 +215,10 @@ void main() { } expect(result.exitCode, 0); }); + + testWithoutContext('flutter gold skips tests where the expectations are missing', () async { + return _testFile('flutter_gold', automatedTestsDirectory, flutterTestDirectory, exitCode: isZero); + }); } Future _testFile( diff --git a/packages/flutter_tools/test/integration.shard/timeline_test.dart b/packages/flutter_tools/test/integration.shard/timeline_test.dart index d06952b55d..9939b242d0 100644 --- a/packages/flutter_tools/test/integration.shard/timeline_test.dart +++ b/packages/flutter_tools/test/integration.shard/timeline_test.dart @@ -40,6 +40,8 @@ void main() { // Regression test for https://github.com/flutter/flutter/issues/79498 testWithoutContext('Can connect to the timeline without getting ANR from the application', () async { final Timer timer = Timer(const Duration(minutes: 5), () { + // This message is intended to show up in CI logs. + // ignore: avoid_print print( 'Warning: test isolate is still active after 5 minutes. This is likely an ' 'app-not-responding error and not a flake. See https://github.com/flutter/flutter/issues/79498 ' diff --git a/packages/flutter_tools/test/integration.shard/variable_expansion_windows.dart b/packages/flutter_tools/test/integration.shard/variable_expansion_windows.dart index 179d742903..0e6923f963 100644 --- a/packages/flutter_tools/test/integration.shard/variable_expansion_windows.dart +++ b/packages/flutter_tools/test/integration.shard/variable_expansion_windows.dart @@ -4,5 +4,6 @@ // Not a test file, invoked by `variable_expansion_windows_test.dart`. void main(List args) { - print('args: $args'); + // This print is used for communicating with the test host. + print('args: $args'); // ignore: avoid_print } diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart index 79c9192f39..feb0ad4954 100644 --- a/packages/flutter_tools/test/src/common.dart +++ b/packages/flutter_tools/test/src/common.dart @@ -27,6 +27,9 @@ void tryToDelete(Directory directory) { directory.deleteSync(recursive: true); } } on FileSystemException catch (error) { + // We print this so that it's visible in the logs, to get an idea of how + // common this problem is, and if any patterns are ever noticed by anyone. + // ignore: avoid_print print('Failed to delete ${directory.path}: $error'); } } diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart index 38abdae9d2..68ece0dd63 100644 --- a/packages/flutter_tools/test/src/context.dart +++ b/packages/flutter_tools/test/src/context.dart @@ -127,7 +127,7 @@ void testUsingContext( }, body: () { final String flutterRoot = getFlutterRoot(); - return runZoned>(() { + return runZonedGuarded>(() { try { return context.run( // Apply the overrides to the test context in the zone since their @@ -148,9 +148,10 @@ void testUsingContext( _printBufferedErrors(context); rethrow; } - }, onError: (Object error, StackTrace stackTrace) { // ignore: deprecated_member_use - print(error); - print(stackTrace); + }, (Object error, StackTrace stackTrace) { + // When things fail, it's ok to print to the console! + print(error); // ignore: avoid_print + print(stackTrace); // ignore: avoid_print _printBufferedErrors(context); throw error; }); @@ -176,7 +177,9 @@ void _printBufferedErrors(AppContext testContext) { if (testContext.get() is BufferLogger) { final BufferLogger bufferLogger = testContext.get() as BufferLogger; if (bufferLogger.errorText.isNotEmpty) { - print(bufferLogger.errorText); + // This is where the logger outputting errors is implemented, so it has + // to use `print`. + print(bufferLogger.errorText); // ignore: avoid_print } bufferLogger.clear(); } diff --git a/packages/flutter_tools/test/web.shard/hot_reload_web_test.dart b/packages/flutter_tools/test/web.shard/hot_reload_web_test.dart index 6a69626b9f..57732e3515 100644 --- a/packages/flutter_tools/test/web.shard/hot_reload_web_test.dart +++ b/packages/flutter_tools/test/web.shard/hot_reload_web_test.dart @@ -38,7 +38,6 @@ void main() { testWithoutContext('newly added code executes during hot restart', () async { final Completer completer = Completer(); final StreamSubscription subscription = flutter.stdout.listen((String line) { - print(line); if (line.contains('(((((RELOAD WORKED)))))')) { completer.complete(); } @@ -56,7 +55,6 @@ void main() { testWithoutContext('newly added code executes during hot restart - canvaskit', () async { final Completer completer = Completer(); final StreamSubscription subscription = flutter.stdout.listen((String line) { - print(line); if (line.contains('(((((RELOAD WORKED)))))')) { completer.complete(); } diff --git a/packages/flutter_tools/tool/analysis_options.yaml b/packages/flutter_tools/tool/analysis_options.yaml new file mode 100644 index 0000000000..19391e33ff --- /dev/null +++ b/packages/flutter_tools/tool/analysis_options.yaml @@ -0,0 +1,5 @@ +include: ../analysis_options.yaml + +linter: + rules: + avoid_print: false # These are CLI tools which print as a matter of course. diff --git a/packages/fuchsia_remote_debug_protocol/lib/src/common/logging.dart b/packages/fuchsia_remote_debug_protocol/lib/src/common/logging.dart index 4dd4eaee82..b2250a745d 100644 --- a/packages/fuchsia_remote_debug_protocol/lib/src/common/logging.dart +++ b/packages/fuchsia_remote_debug_protocol/lib/src/common/logging.dart @@ -51,6 +51,7 @@ typedef LoggingFunction = void Function(LogMessage log); /// /// Exits with status code 1 if the `log` is [LoggingLevel.severe]. void defaultLoggingFunction(LogMessage log) { + // ignore: avoid_print print('[${log.levelName}]::${log.tag}--${log.time}: ${log.message}'); if (log.level == LoggingLevel.severe) { exit(1); diff --git a/packages/integration_test/lib/integration_test.dart b/packages/integration_test/lib/integration_test.dart index 83489150fc..ea69c02843 100644 --- a/packages/integration_test/lib/integration_test.dart +++ b/packages/integration_test/lib/integration_test.dart @@ -64,7 +64,7 @@ class IntegrationTestWidgetsFlutterBinding extends LiveTestWidgetsFlutterBinding }, ); } on MissingPluginException { - print(r''' + debugPrint(r''' Warning: integration_test plugin was not detected. If you're running the tests with `flutter drive`, please make sure your tests @@ -391,7 +391,7 @@ https://flutter.dev/docs/testing/integration-tests#testing-on-firebase-test-lab count++; await Future.delayed(const Duration(seconds: 2)); if (count > 20) { - print('delayForFrameTimings is taking longer than expected...'); + debugPrint('delayForFrameTimings is taking longer than expected...'); } } } diff --git a/packages/integration_test/lib/integration_test_driver.dart b/packages/integration_test/lib/integration_test_driver.dart index 149eec4314..b3e654c267 100644 --- a/packages/integration_test/lib/integration_test_driver.dart +++ b/packages/integration_test/lib/integration_test_driver.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This is a CLI library; we use prints as part of the interface. +// ignore_for_file: avoid_print + import 'dart:async'; import 'dart:convert'; import 'dart:io'; diff --git a/packages/integration_test/lib/integration_test_driver_extended.dart b/packages/integration_test/lib/integration_test_driver_extended.dart index 9131858cce..3ed6473479 100644 --- a/packages/integration_test/lib/integration_test_driver_extended.dart +++ b/packages/integration_test/lib/integration_test_driver_extended.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This is a CLI library; we use prints as part of the interface. +// ignore_for_file: avoid_print + import 'dart:async'; import 'dart:io'; diff --git a/packages/integration_test/test/binding_test.dart b/packages/integration_test/test/binding_test.dart index c6ceb544d1..a77ab58dea 100644 --- a/packages/integration_test/test/binding_test.dart +++ b/packages/integration_test/test/binding_test.dart @@ -104,7 +104,6 @@ Future main() async { testWidgets('Test traceAction', (WidgetTester tester) async { await integrationBinding.enableTimeline(vmService: fakeVM); await integrationBinding.traceAction(() async {}); - print(integrationBinding.reportData); expect(integrationBinding.reportData, isNotNull); expect(integrationBinding.reportData!.containsKey('timeline'), true); expect( diff --git a/packages/integration_test/test/data/fail_test_script.dart b/packages/integration_test/test/data/fail_test_script.dart index c2c5f6302e..9dda0cb6c5 100644 --- a/packages/integration_test/test/data/fail_test_script.dart +++ b/packages/integration_test/test/data/fail_test_script.dart @@ -10,6 +10,8 @@ import 'package:integration_test/integration_test.dart'; Future main() async { final IntegrationTestWidgetsFlutterBinding binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized() as IntegrationTestWidgetsFlutterBinding; binding.allTestsPassed.future.then((_) { + // We use this print to communicate with ../binding_fail_test.dart + // ignore: avoid_print print('IntegrationTestWidgetsFlutterBinding test results: ${jsonEncode(binding.results)}'); }); diff --git a/packages/integration_test/test/data/fail_then_pass_test_script.dart b/packages/integration_test/test/data/fail_then_pass_test_script.dart index df97bc474f..c27f20659f 100644 --- a/packages/integration_test/test/data/fail_then_pass_test_script.dart +++ b/packages/integration_test/test/data/fail_then_pass_test_script.dart @@ -10,6 +10,8 @@ import 'package:integration_test/integration_test.dart'; Future main() async { final IntegrationTestWidgetsFlutterBinding binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized() as IntegrationTestWidgetsFlutterBinding; binding.allTestsPassed.future.then((_) { + // We use this print to communicate with ../binding_fail_test.dart + // ignore: avoid_print print('IntegrationTestWidgetsFlutterBinding test results: ${jsonEncode(binding.results)}'); }); diff --git a/packages/integration_test/test/data/pass_test_script.dart b/packages/integration_test/test/data/pass_test_script.dart index 3ab5040cc7..e40b865f56 100644 --- a/packages/integration_test/test/data/pass_test_script.dart +++ b/packages/integration_test/test/data/pass_test_script.dart @@ -10,6 +10,8 @@ import 'package:integration_test/integration_test.dart'; Future main() async { final IntegrationTestWidgetsFlutterBinding binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized() as IntegrationTestWidgetsFlutterBinding; binding.allTestsPassed.future.then((_) { + // We use this print to communicate with ../binding_fail_test.dart + // ignore: avoid_print print('IntegrationTestWidgetsFlutterBinding test results: ${jsonEncode(binding.results)}'); }); diff --git a/packages/integration_test/test/data/pass_then_fail_test_script.dart b/packages/integration_test/test/data/pass_then_fail_test_script.dart index c87bc00f57..92400baadb 100644 --- a/packages/integration_test/test/data/pass_then_fail_test_script.dart +++ b/packages/integration_test/test/data/pass_then_fail_test_script.dart @@ -10,6 +10,8 @@ import 'package:integration_test/integration_test.dart'; Future main() async { final IntegrationTestWidgetsFlutterBinding binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized() as IntegrationTestWidgetsFlutterBinding; binding.allTestsPassed.future.then((_) { + // We use this print to communicate with ../binding_fail_test.dart + // ignore: avoid_print print('IntegrationTestWidgetsFlutterBinding test results: ${jsonEncode(binding.results)}'); });