Prevent tests from importing other tests. (#27800)
This pattern is problematic when Flutter is ported to build systems that require each executable end-point to be self-contained (e.g. Bazel).
This commit is contained in:
parent
ead5e8e4d7
commit
58939b70ef
10
.cirrus.yml
10
.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:
|
||||
|
@ -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<void> main(List<String> 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<void> _verifyNoBadImportsInFlutter(String workingDirectory) async {
|
||||
// Verify that the imports are well-ordered.
|
||||
final Map<String, Set<String>> dependencyMap = <String, Set<String>>{};
|
||||
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<T>(List<T> a, List<T> 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<String> _findDependencies(String srcPath, List<String> errors, { bool checkForMeta = false }) {
|
||||
Set<String> _findFlutterDependencies(String srcPath, List<String> errors, { bool checkForMeta = false }) {
|
||||
return Directory(srcPath).listSync(recursive: true).where((FileSystemEntity entity) {
|
||||
return entity is File && path.extension(entity.path) == '.dart';
|
||||
}).map<Set<String>>((FileSystemEntity entity) {
|
||||
@ -462,6 +472,37 @@ Future<void> _verifyNoBadImportsInFlutterTools(String workingDirectory) async {
|
||||
}
|
||||
}
|
||||
|
||||
final RegExp _testImportPattern = RegExp(r'''import (['"])([^'"]+_test\.dart)\1''');
|
||||
const Set<String> _exemptTestImports = <String>{
|
||||
'package:flutter_test/flutter_test.dart',
|
||||
'hit_test.dart',
|
||||
'package:test_api/src/backend/live_test.dart',
|
||||
};
|
||||
|
||||
Future<void> _verifyNoTestImports(String workingDirectory) async {
|
||||
final List<String> errors = <String>[];
|
||||
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<void> _verifyGeneratedPluginRegistrants(String flutterRoot) async {
|
||||
final Directory flutterRootDir = Directory(flutterRoot);
|
||||
|
||||
|
@ -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<ui.Codec> 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();
|
||||
|
@ -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();
|
||||
|
27
packages/flutter/test/painting/painting_utils.dart
Normal file
27
packages/flutter/test/painting/painting_utils.dart
Normal file
@ -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<ui.Codec> 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.
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
25
packages/flutter/test/widgets/editable_text_utils.dart
Normal file
25
packages/flutter/test/widgets/editable_text_utils.dart
Normal file
@ -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;
|
||||
}
|
@ -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';
|
||||
|
@ -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<int> 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<FlutterCommandResult> 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<String> get usagePath => noUsagePath ? null : super.usagePath;
|
||||
|
||||
@override
|
||||
String get name => 'dummy';
|
||||
|
||||
@override
|
||||
Future<FlutterCommandResult> runCommand() async {
|
||||
return commandFunction == null ? null : await commandFunction();
|
||||
}
|
||||
}
|
||||
|
||||
class MockCache extends Mock implements Cache {}
|
||||
|
||||
class MockUsage extends Mock implements Usage {}
|
||||
|
45
packages/flutter_tools/test/runner/utils.dart
Normal file
45
packages/flutter_tools/test/runner/utils.dart
Normal file
@ -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<FlutterCommandResult> 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<String> get usagePath => noUsagePath ? null : super.usagePath;
|
||||
|
||||
@override
|
||||
String get name => 'dummy';
|
||||
|
||||
@override
|
||||
Future<FlutterCommandResult> runCommand() async {
|
||||
return commandFunction == null ? null : await commandFunction();
|
||||
}
|
||||
}
|
||||
|
||||
class MockitoCache extends Mock implements Cache {}
|
||||
|
||||
class MockitoUsage extends Mock implements Usage {}
|
Loading…
x
Reference in New Issue
Block a user