diff --git a/.cirrus.yml b/.cirrus.yml index eead250883..83af9d1f6f 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -46,12 +46,12 @@ task: test_script: ./dev/bots/deploy_gallery.sh - name: analyze test_script: - - dart ./dev/bots/analyze.dart + - dart --enable-asserts ./dev/bots/analyze.dart - name: tests-linux env: SHARD: tests test_script: - - dart ./dev/bots/test.dart + - dart --enable-asserts ./dev/bots/test.dart container: cpu: 4 memory: 12G @@ -59,7 +59,7 @@ task: env: SHARD: tool_tests test_script: - - dart ./dev/bots/test.dart + - dart --enable-asserts ./dev/bots/test.dart container: cpu: 4 memory: 12G @@ -77,7 +77,7 @@ task: - echo "$CIRRUS_COMMIT_MESSAGE" > /tmp/cirrus_commit_message.txt - export CIRRUS_CHANGE_MESSAGE="" - export CIRRUS_COMMIT_MESSAGE="" - - dart ./dev/bots/test.dart + - dart --enable-asserts ./dev/bots/test.dart - export CIRRUS_CHANGE_MESSAGE=`cat /tmp/cirrus_change_message.txt` - export CIRRUS_COMMIT_MESSAGE=`cat /tmp/cirrus_commit_message.txt` container: @@ -192,7 +192,7 @@ task: - bin/flutter update-packages test_all_script: | ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 - bin/cache/dart-sdk/bin/dart -c dev/bots/test.dart + bin/cache/dart-sdk/bin/dart --enable-asserts dev/bots/test.dart matrix: - name: tests-macos env: diff --git a/dev/bots/analyze.dart b/dev/bots/analyze.dart index 1d3f4758e2..448ae55c27 100644 --- a/dev/bots/analyze.dart +++ b/dev/bots/analyze.dart @@ -24,6 +24,13 @@ final String pubCache = path.join(flutterRoot, '.pub-cache'); /// For example: /// bin/cache/dart-sdk/bin/dart dev/bots/analyze.dart --dart-sdk=/tmp/dart-sdk Future main(List args) async { + bool assertsEnabled = false; + assert(() { assertsEnabled = true; return true; }()); + if (!assertsEnabled) { + print('The analyze.dart script must be run with --enable-asserts.'); + exit(1); + } + await _verifyNoTestImports(flutterRoot); await _verifyNoTestPackageImports(flutterRoot); await _verifyGeneratedPluginRegistrants(flutterRoot); await _verifyNoBadImportsInFlutter(flutterRoot); @@ -339,8 +346,11 @@ Future _verifyNoBadImportsInFlutter(String workingDirectory) async { // Verify that the imports are well-ordered. final Map> dependencyMap = >{}; for (String directory in directories) { - dependencyMap[directory] = _findDependencies(path.join(srcPath, directory), errors, checkForMeta: directory != 'foundation'); + dependencyMap[directory] = _findFlutterDependencies(path.join(srcPath, directory), errors, checkForMeta: directory != 'foundation'); } + assert(dependencyMap['material'].contains('widgets') && + dependencyMap['widgets'].contains('rendering') && + dependencyMap['rendering'].contains('painting')); // to make sure we're convinced _findFlutterDependencies is finding some for (String package in dependencyMap.keys) { if (dependencyMap[package].contains(package)) { errors.add( @@ -384,9 +394,9 @@ bool _matches(List a, List b) { } final RegExp _importPattern = RegExp(r'''^\s*import (['"])package:flutter/([^.]+)\.dart\1'''); -final RegExp _importMetaPattern = RegExp(r'''^\s*import (['"])package:meta/meta.dart\1'''); +final RegExp _importMetaPattern = RegExp(r'''^\s*import (['"])package:meta/meta\.dart\1'''); -Set _findDependencies(String srcPath, List errors, { bool checkForMeta = false }) { +Set _findFlutterDependencies(String srcPath, List errors, { bool checkForMeta = false }) { return Directory(srcPath).listSync(recursive: true).where((FileSystemEntity entity) { return entity is File && path.extension(entity.path) == '.dart'; }).map>((FileSystemEntity entity) { @@ -462,6 +472,37 @@ Future _verifyNoBadImportsInFlutterTools(String workingDirectory) async { } } +final RegExp _testImportPattern = RegExp(r'''import (['"])([^'"]+_test\.dart)\1'''); +const Set _exemptTestImports = { + 'package:flutter_test/flutter_test.dart', + 'hit_test.dart', + 'package:test_api/src/backend/live_test.dart', +}; + +Future _verifyNoTestImports(String workingDirectory) async { + final List errors = []; + assert("// foo\nimport 'binding_test.dart' as binding;\n'".contains(_testImportPattern)); + for (FileSystemEntity entity in Directory(path.join(workingDirectory, 'packages')) + .listSync(recursive: true) + .where((FileSystemEntity entity) => entity is File && path.extension(entity.path) == '.dart')) { + final File file = entity; + for (String line in file.readAsLinesSync()) { + final Match match = _testImportPattern.firstMatch(line); + if (match != null && !_exemptTestImports.contains(match.group(2))) + errors.add(file.path); + } + } + // Fail if any errors + if (errors.isNotEmpty) { + print('$redLine'); + final String s = errors.length == 1 ? '' : 's'; + print('${bold}The following file$s import a test directly. Test utilities should be in their own file.$reset\n'); + print(errors.join('\n')); + print('$redLine\n'); + exit(1); + } +} + Future _verifyGeneratedPluginRegistrants(String flutterRoot) async { final Directory flutterRootDir = Directory(flutterRoot); diff --git a/packages/flutter/test/painting/binding_test.dart b/packages/flutter/test/painting/binding_test.dart index ab24ca065b..20f0cccfae 100644 --- a/packages/flutter/test/painting/binding_test.dart +++ b/packages/flutter/test/painting/binding_test.dart @@ -1,30 +1,15 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + import 'dart:typed_data' show Uint8List; -import 'dart:ui' as ui show instantiateImageCodec, Codec; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter/painting.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import '../painting/image_data.dart'; - -class PaintingBindingSpy extends BindingBase with ServicesBinding, PaintingBinding { - int counter = 0; - int get instantiateImageCodecCalledCount => counter; - - @override - Future instantiateImageCodec(Uint8List list) { - counter++; - return ui.instantiateImageCodec(list, decodedCacheRatioCap: decodedCacheRatioCap); // ignore: deprecated_member_use_from_same_package - } - - @override - void initLicenses() { - // Do not include any licenses, because we're a test, and the LICENSE file - // doesn't get generated for tests. - } -} +import 'image_data.dart'; +import 'painting_utils.dart'; void main() { final PaintingBindingSpy binding = PaintingBindingSpy(); diff --git a/packages/flutter/test/painting/image_decoder_test.dart b/packages/flutter/test/painting/image_decoder_test.dart index a80726ce14..0d3c4a7e44 100644 --- a/packages/flutter/test/painting/image_decoder_test.dart +++ b/packages/flutter/test/painting/image_decoder_test.dart @@ -8,8 +8,8 @@ import 'dart:ui' as ui; import 'package:flutter/painting.dart'; import '../flutter_test_alternative.dart'; -import 'binding_test.dart'; import 'image_data.dart'; +import 'painting_utils.dart'; void main() { final PaintingBindingSpy binding = PaintingBindingSpy(); diff --git a/packages/flutter/test/painting/painting_utils.dart b/packages/flutter/test/painting/painting_utils.dart new file mode 100644 index 0000000000..861c0a27ec --- /dev/null +++ b/packages/flutter/test/painting/painting_utils.dart @@ -0,0 +1,27 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; +import 'dart:ui' as ui; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/painting.dart'; +import 'package:flutter/services.dart'; + +class PaintingBindingSpy extends BindingBase with ServicesBinding, PaintingBinding { + int counter = 0; + int get instantiateImageCodecCalledCount => counter; + + @override + Future instantiateImageCodec(Uint8List list) { + counter++; + return ui.instantiateImageCodec(list, decodedCacheRatioCap: decodedCacheRatioCap); // ignore: deprecated_member_use_from_same_package + } + + @override + void initLicenses() { + // Do not include any licenses, because we're a test, and the LICENSE file + // doesn't get generated for tests. + } +} diff --git a/packages/flutter/test/widgets/editable_text_cursor_test.dart b/packages/flutter/test/widgets/editable_text_cursor_test.dart index 9f5c4a8e0c..d2f7839d5d 100644 --- a/packages/flutter/test/widgets/editable_text_cursor_test.dart +++ b/packages/flutter/test/widgets/editable_text_cursor_test.dart @@ -11,7 +11,13 @@ import 'package:flutter/widgets.dart'; import 'package:flutter/services.dart'; import '../rendering/mock_canvas.dart'; -import 'editable_text_test.dart'; +import 'editable_text_utils.dart'; + +final TextEditingController controller = TextEditingController(); +final FocusNode focusNode = FocusNode(); +final FocusScopeNode focusScopeNode = FocusScopeNode(); +const TextStyle textStyle = TextStyle(); +const Color cursorColor = Color.fromARGB(0xFF, 0xFF, 0x00, 0x00); void main() { testWidgets('cursor has expected width and radius', (WidgetTester tester) async { diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart index 4a5b4a94d6..73841fb117 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -12,25 +12,9 @@ import 'package:flutter/services.dart'; import 'package:mockito/mockito.dart'; import 'package:flutter/foundation.dart'; +import 'editable_text_utils.dart'; import 'semantics_tester.dart'; -RenderEditable findRenderEditable(WidgetTester tester) { - final RenderObject root = tester.renderObject(find.byType(EditableText)); - expect(root, isNotNull); - - RenderEditable renderEditable; - void recursiveFinder(RenderObject child) { - if (child is RenderEditable) { - renderEditable = child; - return; - } - child.visitChildren(recursiveFinder); - } - root.visitChildren(recursiveFinder); - expect(renderEditable, isNotNull); - return renderEditable; -} - final TextEditingController controller = TextEditingController(); final FocusNode focusNode = FocusNode(); final FocusScopeNode focusScopeNode = FocusScopeNode(); diff --git a/packages/flutter/test/widgets/editable_text_utils.dart b/packages/flutter/test/widgets/editable_text_utils.dart new file mode 100644 index 0000000000..8601f74e04 --- /dev/null +++ b/packages/flutter/test/widgets/editable_text_utils.dart @@ -0,0 +1,25 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/rendering.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +RenderEditable findRenderEditable(WidgetTester tester) { + final RenderObject root = tester.renderObject(find.byType(EditableText)); + expect(root, isNotNull); + + RenderEditable renderEditable; + void recursiveFinder(RenderObject child) { + if (child is RenderEditable) { + renderEditable = child; + return; + } + child.visitChildren(recursiveFinder); + } + root.visitChildren(recursiveFinder); + expect(renderEditable, isNotNull); + return renderEditable; +} diff --git a/packages/flutter_tools/test/runner/flutter_command_runner_test.dart b/packages/flutter_tools/test/runner/flutter_command_runner_test.dart index f1a366f86e..563d480fd2 100644 --- a/packages/flutter_tools/test/runner/flutter_command_runner_test.dart +++ b/packages/flutter_tools/test/runner/flutter_command_runner_test.dart @@ -17,7 +17,7 @@ import 'package:process/process.dart'; import '../src/common.dart'; import '../src/context.dart'; -import 'flutter_command_test.dart'; +import 'utils.dart'; const String _kFlutterRoot = '/flutter/flutter'; const String _kEngineRoot = '/flutter/engine'; diff --git a/packages/flutter_tools/test/runner/flutter_command_test.dart b/packages/flutter_tools/test/runner/flutter_command_test.dart index 4b6499d7d9..3b144478e4 100644 --- a/packages/flutter_tools/test/runner/flutter_command_test.dart +++ b/packages/flutter_tools/test/runner/flutter_command_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/base/time.dart'; import 'package:flutter_tools/src/usage.dart'; @@ -13,19 +11,19 @@ import 'package:mockito/mockito.dart'; import '../src/common.dart'; import '../src/context.dart'; +import 'utils.dart'; + void main() { - group('Flutter Command', () { - - MockCache cache; + MockitoCache cache; + MockitoUsage usage; MockClock clock; - MockUsage usage; List mockTimes; setUp(() { - cache = MockCache(); + cache = MockitoCache(); + usage = MockitoUsage(); clock = MockClock(); - usage = MockUsage(); when(usage.isFirstRun).thenReturn(false); when(clock.now()).thenAnswer( (Invocation _) => DateTime.fromMillisecondsSinceEpoch(mockTimes.removeAt(0)) @@ -157,40 +155,4 @@ void main() { }); }); - } - -typedef CommandFunction = Future Function(); - -class DummyFlutterCommand extends FlutterCommand { - - DummyFlutterCommand({ - this.shouldUpdateCache = false, - this.noUsagePath = false, - this.commandFunction, - }); - - final bool noUsagePath; - final CommandFunction commandFunction; - - @override - final bool shouldUpdateCache; - - @override - String get description => 'does nothing'; - - @override - Future get usagePath => noUsagePath ? null : super.usagePath; - - @override - String get name => 'dummy'; - - @override - Future runCommand() async { - return commandFunction == null ? null : await commandFunction(); - } -} - -class MockCache extends Mock implements Cache {} - -class MockUsage extends Mock implements Usage {} diff --git a/packages/flutter_tools/test/runner/utils.dart b/packages/flutter_tools/test/runner/utils.dart new file mode 100644 index 0000000000..b7b1ec1292 --- /dev/null +++ b/packages/flutter_tools/test/runner/utils.dart @@ -0,0 +1,45 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter_tools/src/cache.dart'; +import 'package:flutter_tools/src/usage.dart'; +import 'package:flutter_tools/src/runner/flutter_command.dart'; +import 'package:mockito/mockito.dart'; + +typedef CommandFunction = Future Function(); + +class DummyFlutterCommand extends FlutterCommand { + + DummyFlutterCommand({ + this.shouldUpdateCache = false, + this.noUsagePath = false, + this.commandFunction, + }); + + final bool noUsagePath; + final CommandFunction commandFunction; + + @override + final bool shouldUpdateCache; + + @override + String get description => 'does nothing'; + + @override + Future get usagePath => noUsagePath ? null : super.usagePath; + + @override + String get name => 'dummy'; + + @override + Future runCommand() async { + return commandFunction == null ? null : await commandFunction(); + } +} + +class MockitoCache extends Mock implements Cache {} + +class MockitoUsage extends Mock implements Usage {}