common.dart 5.36 KB
Newer Older
1 2 3 4
// 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.

5 6
import 'dart:async';

7
import 'package:args/command_runner.dart';
8
import 'package:flutter_tools/src/base/common.dart';
9
import 'package:flutter_tools/src/base/file_system.dart';
10
import 'package:flutter_tools/src/base/platform.dart';
11
import 'package:flutter_tools/src/base/process.dart';
12
import 'package:flutter_tools/src/commands/create.dart';
13 14
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
15 16
import 'package:test_api/test_api.dart' as test_package show TypeMatcher;
import 'package:test_api/test_api.dart' hide TypeMatcher, isInstanceOf;
17

18
export 'package:test_core/test_core.dart' hide TypeMatcher, isInstanceOf; // Defines a 'package:test' shim.
19 20 21

/// 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
22
Matcher isInstanceOf<T>() => test_package.TypeMatcher<T>();
23

24 25 26 27 28 29 30 31 32 33 34
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');
  }
}

35 36 37 38 39 40
/// 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() {
41
  if (platform.environment.containsKey('FLUTTER_ROOT')) {
42
    return platform.environment['FLUTTER_ROOT'];
43
  }
44

45
  Error invalidScript() => StateError('Invalid script: ${platform.script}');
46 47 48 49 50 51 52

  Uri scriptUri;
  switch (platform.script.scheme) {
    case 'file':
      scriptUri = platform.script;
      break;
    case 'data':
53
      final RegExp flutterTools = RegExp(r'(file://[^"]*[/\\]flutter_tools[/\\][^"]+\.dart)', multiLine: true);
54
      final Match match = flutterTools.firstMatch(Uri.decodeFull(platform.script.path));
55
      if (match == null) {
56
        throw invalidScript();
57
      }
58 59 60 61 62 63 64 65
      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');
66
  if (toolsIndex == -1) {
67
    throw invalidScript();
68
  }
69 70 71 72
  final String toolsPath = fs.path.joinAll(parts.sublist(0, toolsIndex + 1));
  return fs.path.normalize(fs.path.join(toolsPath, '..', '..'));
}

73
CommandRunner<void> createTestCommandRunner([ FlutterCommand command ]) {
74
  final FlutterCommandRunner runner = FlutterCommandRunner();
75
  if (command != null) {
76
    runner.addCommand(command);
77
  }
78
  return runner;
79
}
80 81

/// Updates [path] to have a modification time [seconds] from now.
82 83 84 85 86
void updateFileModificationTime(
  String path,
  DateTime baseTime,
  int seconds,
) {
87
  final DateTime modificationTime = baseTime.add(Duration(seconds: seconds));
88
  fs.file(path).setLastModifiedSync(modificationTime);
89
}
90

91
/// Matcher for functions that throw [ToolExit].
92
Matcher throwsToolExit({ int exitCode, Pattern message }) {
93
  Matcher matcher = isToolExit;
94
  if (exitCode != null) {
95
    matcher = allOf(matcher, (ToolExit e) => e.exitCode == exitCode);
96 97
  }
  if (message != null) {
98
    matcher = allOf(matcher, (ToolExit e) => e.message.contains(message));
99
  }
100
  return throwsA(matcher);
101 102 103
}

/// Matcher for [ToolExit]s.
104
final Matcher isToolExit = isInstanceOf<ToolExit>();
105 106

/// Matcher for functions that throw [ProcessExit].
107
Matcher throwsProcessExit([ dynamic exitCode ]) {
108 109 110 111 112 113
  return exitCode == null
      ? throwsA(isProcessExit)
      : throwsA(allOf(isProcessExit, (ProcessExit e) => e.exitCode == exitCode));
}

/// Matcher for [ProcessExit]s.
114
final Matcher isProcessExit = isInstanceOf<ProcessExit>();
115

116 117
/// Creates a flutter project in the [temp] directory using the
/// [arguments] list if specified, or `--no-pub` if not.
118
/// Returns the path to the flutter project.
119
Future<String> createProject(Directory temp, { List<String> arguments }) async {
120
  arguments ??= <String>['--no-pub'];
121
  final String projectPath = fs.path.join(temp.path, 'flutter_project');
122
  final CreateCommand command = CreateCommand();
123
  final CommandRunner<void> runner = createTestCommandRunner(command);
124
  await runner.run(<String>['create', ...arguments, projectPath]);
125 126
  // Created `.packages` since it's not created when the flag `--no-pub` is passed.
  fs.file(fs.path.join(projectPath, '.packages')).createSync();
127
  return projectPath;
128 129 130
}

/// Test case timeout for tests involving remote calls to `pub get` or similar.
131
const Timeout allowForRemotePubInvocation = Timeout.factor(10.0);
132 133 134

/// Test case timeout for tests involving creating a Flutter project with
/// `--no-pub`. Use [allowForRemotePubInvocation] when creation involves `pub`.
135
const Timeout allowForCreateFlutterProject = Timeout.factor(3.0);
136 137 138 139 140 141 142 143 144 145 146

Future<void> expectToolExitLater(Future<dynamic> future, Matcher messageMatcher) async {
  try {
    await future;
    fail('ToolExit expected, but nothing thrown');
  } on ToolExit catch(e) {
    expect(e.message, messageMatcher);
  } catch(e, trace) {
    fail('ToolExit expected, got $e\n$trace');
  }
}