| // Copyright 2016 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:args/command_runner.dart'; | |
| import 'package:flutter_tools/src/desktop.dart'; | |
| import 'package:flutter_tools/src/web/workflow.dart'; | |
| import 'package:test_api/test_api.dart' hide TypeMatcher, isInstanceOf; | |
| import 'package:test_api/test_api.dart' as test_package show TypeMatcher; | |
| import 'package:flutter_tools/src/base/common.dart'; | |
| import 'package:flutter_tools/src/base/file_system.dart'; | |
| import 'package:flutter_tools/src/base/platform.dart'; | |
| import 'package:flutter_tools/src/base/process.dart'; | |
| import 'package:flutter_tools/src/commands/create.dart'; | |
| import 'package:flutter_tools/src/runner/flutter_command.dart'; | |
| import 'package:flutter_tools/src/runner/flutter_command_runner.dart'; | |
| export 'package:test_core/test_core.dart' hide TypeMatcher, isInstanceOf; // Defines a 'package:test' shim. | |
| /// Disable both web and desktop to make testing easier. For example, prevent | |
| /// them from showing up in the devices list if the host happens to be setup | |
| /// properly. | |
| set debugDisableWebAndDesktop(bool value) { | |
| if (value) { | |
| debugDisableDesktop = true; | |
| debugDisableWeb = true; | |
| } else { | |
| debugDisableDesktop = false; | |
| debugDisableWeb = false; | |
| } | |
| } | |
| /// A matcher that compares the type of the actual value to the type argument T. | |
| // TODO(ianh): Remove this once https://github.com/dart-lang/matcher/issues/98 is fixed | |
| Matcher isInstanceOf<T>() => test_package.TypeMatcher<T>(); | |
| void tryToDelete(Directory directory) { | |
| // This should not be necessary, but it turns out that | |
| // on Windows it's common for deletions to fail due to | |
| // bogus (we think) "access denied" errors. | |
| try { | |
| directory.deleteSync(recursive: true); | |
| } on FileSystemException catch (error) { | |
| print('Failed to delete ${directory.path}: $error'); | |
| } | |
| } | |
| /// Gets the path to the root of the Flutter repository. | |
| /// | |
| /// This will first look for a `FLUTTER_ROOT` environment variable. If the | |
| /// environment variable is set, it will be returned. Otherwise, this will | |
| /// deduce the path from `platform.script`. | |
| String getFlutterRoot() { | |
| if (platform.environment.containsKey('FLUTTER_ROOT')) | |
| return platform.environment['FLUTTER_ROOT']; | |
| Error invalidScript() => StateError('Invalid script: ${platform.script}'); | |
| Uri scriptUri; | |
| switch (platform.script.scheme) { | |
| case 'file': | |
| scriptUri = platform.script; | |
| break; | |
| case 'data': | |
| final RegExp flutterTools = RegExp(r'(file://[^"]*[/\\]flutter_tools[/\\][^"]+\.dart)', multiLine: true); | |
| final Match match = flutterTools.firstMatch(Uri.decodeFull(platform.script.path)); | |
| if (match == null) | |
| throw invalidScript(); | |
| scriptUri = Uri.parse(match.group(1)); | |
| break; | |
| default: | |
| throw invalidScript(); | |
| } | |
| final List<String> parts = fs.path.split(fs.path.fromUri(scriptUri)); | |
| final int toolsIndex = parts.indexOf('flutter_tools'); | |
| if (toolsIndex == -1) | |
| throw invalidScript(); | |
| final String toolsPath = fs.path.joinAll(parts.sublist(0, toolsIndex + 1)); | |
| return fs.path.normalize(fs.path.join(toolsPath, '..', '..')); | |
| } | |
| CommandRunner<void> createTestCommandRunner([ FlutterCommand command ]) { | |
| final FlutterCommandRunner runner = FlutterCommandRunner(); | |
| if (command != null) | |
| runner.addCommand(command); | |
| return runner; | |
| } | |
| /// Updates [path] to have a modification time [seconds] from now. | |
| void updateFileModificationTime( | |
| String path, | |
| DateTime baseTime, | |
| int seconds, | |
| ) { | |
| final DateTime modificationTime = baseTime.add(Duration(seconds: seconds)); | |
| fs.file(path).setLastModifiedSync(modificationTime); | |
| } | |
| /// Matcher for functions that throw [ToolExit]. | |
| Matcher throwsToolExit({ int exitCode, Pattern message }) { | |
| Matcher matcher = isToolExit; | |
| if (exitCode != null) | |
| matcher = allOf(matcher, (ToolExit e) => e.exitCode == exitCode); | |
| if (message != null) | |
| matcher = allOf(matcher, (ToolExit e) => e.message.contains(message)); | |
| return throwsA(matcher); | |
| } | |
| /// Matcher for [ToolExit]s. | |
| final Matcher isToolExit = isInstanceOf<ToolExit>(); | |
| /// Matcher for functions that throw [ProcessExit]. | |
| Matcher throwsProcessExit([ dynamic exitCode ]) { | |
| return exitCode == null | |
| ? throwsA(isProcessExit) | |
| : throwsA(allOf(isProcessExit, (ProcessExit e) => e.exitCode == exitCode)); | |
| } | |
| /// Matcher for [ProcessExit]s. | |
| final Matcher isProcessExit = isInstanceOf<ProcessExit>(); | |
| /// Creates a flutter project in the [temp] directory using the | |
| /// [arguments] list if specified, or `--no-pub` if not. | |
| /// Returns the path to the flutter project. | |
| Future<String> createProject(Directory temp, { List<String> arguments }) async { | |
| arguments ??= <String>['--no-pub']; | |
| final String projectPath = fs.path.join(temp.path, 'flutter_project'); | |
| final CreateCommand command = CreateCommand(); | |
| final CommandRunner<void> runner = createTestCommandRunner(command); | |
| await runner.run(<String>['create', ...arguments, projectPath]); | |
| return projectPath; | |
| } | |
| /// Test case timeout for tests involving remote calls to `pub get` or similar. | |
| const Timeout allowForRemotePubInvocation = Timeout.factor(10.0); | |
| /// Test case timeout for tests involving creating a Flutter project with | |
| /// `--no-pub`. Use [allowForRemotePubInvocation] when creation involves `pub`. | |
| const Timeout allowForCreateFlutterProject = Timeout.factor(3.0); |