• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:async';
6
7import 'package:args/command_runner.dart';
8import 'package:flutter_tools/src/base/common.dart';
9import 'package:flutter_tools/src/base/file_system.dart';
10import 'package:flutter_tools/src/base/platform.dart';
11import 'package:flutter_tools/src/base/process.dart';
12import 'package:flutter_tools/src/commands/create.dart';
13import 'package:flutter_tools/src/runner/flutter_command.dart';
14import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
15import 'package:test_api/test_api.dart' as test_package show TypeMatcher;
16import 'package:test_api/test_api.dart' hide TypeMatcher, isInstanceOf;
17
18export 'package:test_core/test_core.dart' hide TypeMatcher, isInstanceOf; // Defines a 'package:test' shim.
19
20/// A matcher that compares the type of the actual value to the type argument T.
21// TODO(ianh): Remove this once https://github.com/dart-lang/matcher/issues/98 is fixed
22Matcher isInstanceOf<T>() => test_package.TypeMatcher<T>();
23
24void tryToDelete(Directory directory) {
25  // This should not be necessary, but it turns out that
26  // on Windows it's common for deletions to fail due to
27  // bogus (we think) "access denied" errors.
28  try {
29    directory.deleteSync(recursive: true);
30  } on FileSystemException catch (error) {
31    print('Failed to delete ${directory.path}: $error');
32  }
33}
34
35/// Gets the path to the root of the Flutter repository.
36///
37/// This will first look for a `FLUTTER_ROOT` environment variable. If the
38/// environment variable is set, it will be returned. Otherwise, this will
39/// deduce the path from `platform.script`.
40String getFlutterRoot() {
41  if (platform.environment.containsKey('FLUTTER_ROOT'))
42    return platform.environment['FLUTTER_ROOT'];
43
44  Error invalidScript() => StateError('Invalid script: ${platform.script}');
45
46  Uri scriptUri;
47  switch (platform.script.scheme) {
48    case 'file':
49      scriptUri = platform.script;
50      break;
51    case 'data':
52      final RegExp flutterTools = RegExp(r'(file://[^"]*[/\\]flutter_tools[/\\][^"]+\.dart)', multiLine: true);
53      final Match match = flutterTools.firstMatch(Uri.decodeFull(platform.script.path));
54      if (match == null)
55        throw invalidScript();
56      scriptUri = Uri.parse(match.group(1));
57      break;
58    default:
59      throw invalidScript();
60  }
61
62  final List<String> parts = fs.path.split(fs.path.fromUri(scriptUri));
63  final int toolsIndex = parts.indexOf('flutter_tools');
64  if (toolsIndex == -1)
65    throw invalidScript();
66  final String toolsPath = fs.path.joinAll(parts.sublist(0, toolsIndex + 1));
67  return fs.path.normalize(fs.path.join(toolsPath, '..', '..'));
68}
69
70CommandRunner<void> createTestCommandRunner([ FlutterCommand command ]) {
71  final FlutterCommandRunner runner = FlutterCommandRunner();
72  if (command != null)
73    runner.addCommand(command);
74  return runner;
75}
76
77/// Updates [path] to have a modification time [seconds] from now.
78void updateFileModificationTime(
79  String path,
80  DateTime baseTime,
81  int seconds,
82) {
83  final DateTime modificationTime = baseTime.add(Duration(seconds: seconds));
84  fs.file(path).setLastModifiedSync(modificationTime);
85}
86
87/// Matcher for functions that throw [ToolExit].
88Matcher throwsToolExit({ int exitCode, Pattern message }) {
89  Matcher matcher = isToolExit;
90  if (exitCode != null)
91    matcher = allOf(matcher, (ToolExit e) => e.exitCode == exitCode);
92  if (message != null)
93    matcher = allOf(matcher, (ToolExit e) => e.message.contains(message));
94  return throwsA(matcher);
95}
96
97/// Matcher for [ToolExit]s.
98final Matcher isToolExit = isInstanceOf<ToolExit>();
99
100/// Matcher for functions that throw [ProcessExit].
101Matcher throwsProcessExit([ dynamic exitCode ]) {
102  return exitCode == null
103      ? throwsA(isProcessExit)
104      : throwsA(allOf(isProcessExit, (ProcessExit e) => e.exitCode == exitCode));
105}
106
107/// Matcher for [ProcessExit]s.
108final Matcher isProcessExit = isInstanceOf<ProcessExit>();
109
110/// Creates a flutter project in the [temp] directory using the
111/// [arguments] list if specified, or `--no-pub` if not.
112/// Returns the path to the flutter project.
113Future<String> createProject(Directory temp, { List<String> arguments }) async {
114  arguments ??= <String>['--no-pub'];
115  final String projectPath = fs.path.join(temp.path, 'flutter_project');
116  final CreateCommand command = CreateCommand();
117  final CommandRunner<void> runner = createTestCommandRunner(command);
118  await runner.run(<String>['create', ...arguments, projectPath]);
119  return projectPath;
120}
121
122/// Test case timeout for tests involving remote calls to `pub get` or similar.
123const Timeout allowForRemotePubInvocation = Timeout.factor(10.0);
124
125/// Test case timeout for tests involving creating a Flutter project with
126/// `--no-pub`. Use [allowForRemotePubInvocation] when creation involves `pub`.
127const Timeout allowForCreateFlutterProject = Timeout.factor(3.0);
128
129Future<void> expectToolExitLater(Future<dynamic> future, Matcher messageMatcher) async {
130  try {
131    await future;
132    fail('ToolExit expected, but nothing thrown');
133  } on ToolExit catch(e) {
134    expect(e.message, messageMatcher);
135  } catch(e, trace) {
136    fail('ToolExit expected, got $e\n$trace');
137  }
138}
139