test_test.dart 11.4 KB
Newer Older
1 2 3 4 5
// Copyright 2015 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';
6
import 'dart:io' as io;
7

8
import 'package:flutter_tools/src/base/file_system.dart';
9
import 'package:flutter_tools/src/base/io.dart';
10 11 12
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/dart/sdk.dart';

13 14
import '../../src/common.dart';
import '../../src/context.dart';
15 16 17

// This test depends on some files in ///dev/automated_tests/flutter_test/*

18
Future<void> _testExclusionLock;
19

20
void main() {
21
  group('flutter test should', () {
22

23 24
    final String automatedTestsDirectory = fs.path.join('..', '..', 'dev', 'automated_tests');
    final String flutterTestDirectory = fs.path.join(automatedTestsDirectory, 'flutter_test');
25

26 27 28
    testUsingContext('not have extraneous error messages', () async {
      Cache.flutterRoot = '../..';
      return _testFile('trivial_widget', automatedTestsDirectory, flutterTestDirectory, exitCode: isZero);
29
    }, skip: io.Platform.isLinux); // Flutter on Linux sometimes has problems with font resolution (#7224)
30

31
    testUsingContext('report nice errors for exceptions thrown within testWidgets()', () async {
32 33
      Cache.flutterRoot = '../..';
      return _testFile('exception_handling', automatedTestsDirectory, flutterTestDirectory);
34
    }, skip: io.Platform.isWindows); // TODO(chunhtai): Dart on Windows has trouble with unicode characters in output (#35425).
35

36
    testUsingContext('report a nice error when a guarded function was called without await', () async {
37
      Cache.flutterRoot = '../..';
38
      return _testFile('test_async_utils_guarded', automatedTestsDirectory, flutterTestDirectory);
39
    }, skip: io.Platform.isWindows); // TODO(chunhtai): Dart on Windows has trouble with unicode characters in output (#35425).
40

41
    testUsingContext('report a nice error when an async function was called without await', () async {
42
      Cache.flutterRoot = '../..';
43
      return _testFile('test_async_utils_unguarded', automatedTestsDirectory, flutterTestDirectory);
44
    }, skip: io.Platform.isWindows); // TODO(chunhtai): Dart on Windows has trouble with unicode characters in output (#35425).
45

46 47 48
    testUsingContext('report a nice error when a Ticker is left running', () async {
      Cache.flutterRoot = '../..';
      return _testFile('ticker', automatedTestsDirectory, flutterTestDirectory);
49
    }, skip: io.Platform.isWindows); // TODO(chunhtai): Dart on Windows has trouble with unicode characters in output (#35425).
50

51
    testUsingContext('report a nice error when a pubspec.yaml is missing a flutter_test dependency', () async {
52
      final String missingDependencyTests = fs.path.join('..', '..', 'dev', 'missing_dependency_tests');
53
      Cache.flutterRoot = '../..';
54
      return _testFile('trivial', missingDependencyTests, missingDependencyTests);
55 56 57 58 59 60 61 62 63 64
    }, skip: io.Platform.isWindows); // TODO(chunhtai): Dart on Windows has trouble with unicode characters in output (#35425).

    testUsingContext('report which user created widget caused the error', () async {
      Cache.flutterRoot = '../..';
      return _testFile('print_user_created_ancestor', automatedTestsDirectory, flutterTestDirectory,
          extraArguments: const <String>['--track-widget-creation']);
    }, skip: io.Platform.isWindows); // TODO(chunhtai): Dart on Windows has trouble with unicode characters in output (#35425).

    testUsingContext('report which user created widget caused the error - no flag', () async {
      Cache.flutterRoot = '../..';
65 66
      return _testFile('print_user_created_ancestor_no_flag', automatedTestsDirectory, flutterTestDirectory,
         extraArguments: const <String>['--no-track-widget-creation']);
67
    }, skip: io.Platform.isWindows); // TODO(chunhtai): Dart on Windows has trouble with unicode characters in output (#35425).
68

69 70 71 72 73 74
    testUsingContext('report correct created widget caused the error', () async {
      Cache.flutterRoot = '../..';
      return _testFile('print_correct_local_widget', automatedTestsDirectory, flutterTestDirectory,
        extraArguments: const <String>['--track-widget-creation']);
    }, skip: io.Platform.isWindows); // TODO(chunhtai): Dart on Windows has trouble with unicode characters in output (#35425).

75 76 77 78 79
    testUsingContext('can load assets within its own package', () async {
      Cache.flutterRoot = '../..';
      return _testFile('package_assets', automatedTestsDirectory, flutterTestDirectory, exitCode: isZero);
    }, skip: io.Platform.isWindows);

80 81 82
    testUsingContext('run a test when its name matches a regexp', () async {
      Cache.flutterRoot = '../..';
      final ProcessResult result = await _runFlutterTest('filtering', automatedTestsDirectory, flutterTestDirectory,
83
        extraArguments: const <String>['--name', 'inc.*de']);
84
      if (!result.stdout.contains('+1: All tests passed')) {
85
        fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
86
      }
87 88 89 90 91 92
      expect(result.exitCode, 0);
    });

    testUsingContext('run a test when its name contains a string', () async {
      Cache.flutterRoot = '../..';
      final ProcessResult result = await _runFlutterTest('filtering', automatedTestsDirectory, flutterTestDirectory,
93
        extraArguments: const <String>['--plain-name', 'include']);
94
      if (!result.stdout.contains('+1: All tests passed')) {
95
        fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
96
      }
97 98 99 100 101 102
      expect(result.exitCode, 0);
    });

    testUsingContext('test runs to completion', () async {
      Cache.flutterRoot = '../..';
      final ProcessResult result = await _runFlutterTest('trivial', automatedTestsDirectory, flutterTestDirectory,
103
        extraArguments: const <String>['--verbose']);
104 105 106 107
      if ((!result.stdout.contains('+1: All tests passed')) ||
          (!result.stdout.contains('test 0: starting shell process')) ||
          (!result.stdout.contains('test 0: deleting temporary directory')) ||
          (!result.stdout.contains('test 0: finished')) ||
108
          (!result.stdout.contains('test package returned with exit code 0'))) {
109
        fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
110 111
      }
      if (result.stderr.isNotEmpty) {
112
        fail('unexpected error output from test:\n\n${result.stderr}\n-- end stderr --\n\n');
113
      }
114 115 116
      expect(result.exitCode, 0);
    });

117 118 119 120 121 122 123 124
    testUsingContext('run all tests inside of a directory with no trailing slash', () async {
      Cache.flutterRoot = '../..';
      final ProcessResult result = await _runFlutterTest(null, automatedTestsDirectory, flutterTestDirectory + '/child_directory',
        extraArguments: const <String>['--verbose']);
      if ((!result.stdout.contains('+2: All tests passed')) ||
          (!result.stdout.contains('test 0: starting shell process')) ||
          (!result.stdout.contains('test 0: deleting temporary directory')) ||
          (!result.stdout.contains('test 0: finished')) ||
125
          (!result.stdout.contains('test package returned with exit code 0'))) {
126
        fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
127 128
      }
      if (result.stderr.isNotEmpty) {
129
        fail('unexpected error output from test:\n\n${result.stderr}\n-- end stderr --\n\n');
130
      }
131 132 133
      expect(result.exitCode, 0);
    });

134
  });
135 136
}

137
Future<void> _testFile(
138 139 140 141 142 143
  String testName,
  String workingDirectory,
  String testDirectory, {
  Matcher exitCode,
  List<String> extraArguments = const <String>[],
}) async {
144
  exitCode ??= isNonZero;
145
  final String fullTestExpectation = fs.path.join(testDirectory, '${testName}_expectation.txt');
146
  final File expectationFile = fs.file(fullTestExpectation);
147
  if (!expectationFile.existsSync()) {
148
    fail('missing expectation file: $expectationFile');
149
  }
150

151
  while (_testExclusionLock != null) {
152
    await _testExclusionLock;
153
  }
154

155 156 157 158 159 160
  final ProcessResult exec = await _runFlutterTest(
    testName,
    workingDirectory,
    testDirectory,
    extraArguments: extraArguments,
  );
161

162
  expect(exec.exitCode, exitCode);
163
  final List<String> output = exec.stdout.split('\n');
164
  if (output.first == 'Waiting for another flutter command to release the startup lock...') {
165
    output.removeAt(0);
166 167
  }
  if (output.first.startsWith('Running "flutter pub get" in')) {
168
    output.removeAt(0);
169
  }
170 171
  output.add('<<stderr>>');
  output.addAll(exec.stderr.split('\n'));
172
  final List<String> expectations = fs.file(fullTestExpectation).readAsLinesSync();
173 174 175
  bool allowSkip = false;
  int expectationLineNumber = 0;
  int outputLineNumber = 0;
176
  bool haveSeenStdErrMarker = false;
177
  while (expectationLineNumber < expectations.length) {
178 179 180 181 182
    expect(
      output,
      hasLength(greaterThan(outputLineNumber)),
      reason: 'Failure in $testName to compare to $fullTestExpectation',
    );
183
    final String expectationLine = expectations[expectationLineNumber];
184
    String outputLine = output[outputLineNumber];
185 186 187 188 189 190
    if (expectationLine == '<<skip until matching line>>') {
      allowSkip = true;
      expectationLineNumber += 1;
      continue;
    }
    if (allowSkip) {
191
      if (!RegExp(expectationLine).hasMatch(outputLine)) {
192 193 194 195 196
        outputLineNumber += 1;
        continue;
      }
      allowSkip = false;
    }
197 198 199 200
    if (expectationLine == '<<stderr>>') {
      expect(haveSeenStdErrMarker, isFalse);
      haveSeenStdErrMarker = true;
    }
201 202 203 204 205 206 207 208 209 210 211 212
    if (!RegExp(expectationLine).hasMatch(outputLine) && outputLineNumber + 1 < output.length) {
      // Check if the RegExp can match the next two lines in the output so
      // that it is possible to write expectations that still hold even if a
      // line is wrapped slightly differently due to for example a file name
      // being longer on one platform than another.
      final String mergedLines = '$outputLine\n${output[outputLineNumber+1]}';
      if (RegExp(expectationLine).hasMatch(mergedLines)) {
        outputLineNumber += 1;
        outputLine = mergedLines;
      }
    }

213
    expect(outputLine, matches(expectationLine), reason: 'Full output:\n- - - -----8<----- - - -\n${output.join("\n")}\n- - - -----8<----- - - -');
214 215 216 217
    expectationLineNumber += 1;
    outputLineNumber += 1;
  }
  expect(allowSkip, isFalse);
218
  if (!haveSeenStdErrMarker) {
219
    expect(exec.stderr, '');
220
  }
221
}
222

223 224 225 226
Future<ProcessResult> _runFlutterTest(
  String testName,
  String workingDirectory,
  String testDirectory, {
227
  List<String> extraArguments = const <String>[],
228 229
}) async {

230 231 232 233 234
  String testPath;
  if (testName == null) {
    // Test everything in the directory.
    testPath = testDirectory;
    final Directory directoryToTest = fs.directory(testPath);
235
    if (!directoryToTest.existsSync()) {
236
      fail('missing test directory: $directoryToTest');
237
    }
238 239
  } else {
    // Test just a specific test file.
240
    testPath = fs.path.join(testDirectory, '${testName}_test.dart');
241
    final File testFile = fs.file(testPath);
242
    if (!testFile.existsSync()) {
243
      fail('missing test file: $testFile');
244
    }
245
  }
246

247 248 249 250 251
  final List<String> args = <String>[
    ...dartVmFlags,
    fs.path.absolute(fs.path.join('bin', 'flutter_tools.dart')),
    'test',
    '--no-color',
252
    ...extraArguments,
253
    testPath,
254
  ];
255

256
  while (_testExclusionLock != null) {
257
    await _testExclusionLock;
258
  }
259

260
  final Completer<void> testExclusionCompleter = Completer<void>();
261 262 263 264 265 266 267 268 269 270 271 272
  _testExclusionLock = testExclusionCompleter.future;
  try {
    return await Process.run(
      fs.path.join(dartSdkPath, 'bin', 'dart'),
      args,
      workingDirectory: workingDirectory,
    );
  } finally {
    _testExclusionLock = null;
    testExclusionCompleter.complete();
  }
}