| OLD | NEW | 
|     1 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file |     1 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file | 
|     2 // for details. All rights reserved. Use of this source code is governed by a |     2 // for details. All rights reserved. Use of this source code is governed by a | 
|     3 // BSD-style license that can be found in the LICENSE file. |     3 // BSD-style license that can be found in the LICENSE file. | 
|     4  |     4  | 
|     5 /** |     5 /** | 
|     6  * Classes and methods for executing tests. |     6  * Classes and methods for executing tests. | 
|     7  * |     7  * | 
|     8  * This module includes: |     8  * This module includes: | 
|     9  * - Managing parallel execution of tests, including timeout checks. |     9  * - Managing parallel execution of tests, including timeout checks. | 
|    10  * - Evaluating the output of each test as pass/fail/crash/timeout. |    10  * - Evaluating the output of each test as pass/fail/crash/timeout. | 
|    11  */ |    11  */ | 
|    12 import 'dart:async'; |    12 import 'dart:async'; | 
|    13 import 'dart:collection'; |    13 import 'dart:collection'; | 
|    14 import 'dart:convert'; |    14 import 'dart:convert'; | 
|    15 // We need to use the 'io' prefix here, otherwise io.exitCode will shadow |    15 // We need to use the 'io' prefix here, otherwise io.exitCode will shadow | 
|    16 // CommandOutput.exitCode in subclasses of CommandOutput. |    16 // CommandOutput.exitCode in subclasses of CommandOutput. | 
|    17 import 'dart:io' as io; |    17 import 'dart:io' as io; | 
|    18 import 'dart:math' as math; |    18 import 'dart:math' as math; | 
|    19  |    19  | 
|    20 import 'android.dart'; |    20 import 'android.dart'; | 
|    21 import 'browser_controller.dart'; |    21 import 'browser_controller.dart'; | 
 |    22 import 'command.dart'; | 
 |    23 import 'command_output.dart'; | 
|    22 import 'configuration.dart'; |    24 import 'configuration.dart'; | 
|    23 import 'dependency_graph.dart' as dgraph; |    25 import 'dependency_graph.dart' as dgraph; | 
|    24 import 'expectation.dart'; |    26 import 'expectation.dart'; | 
|    25 import 'path.dart'; |  | 
|    26 import 'runtime_configuration.dart'; |    27 import 'runtime_configuration.dart'; | 
|    27 import 'test_progress.dart'; |    28 import 'test_progress.dart'; | 
|    28 import 'test_suite.dart'; |    29 import 'test_suite.dart'; | 
|    29 import 'utils.dart'; |    30 import 'utils.dart'; | 
|    30  |    31  | 
|    31 const int CRASHING_BROWSER_EXITCODE = -10; |    32 const int CRASHING_BROWSER_EXITCODE = -10; | 
|    32 const int SLOW_TIMEOUT_MULTIPLIER = 4; |    33 const int SLOW_TIMEOUT_MULTIPLIER = 4; | 
|    33 const int NON_UTF_FAKE_EXITCODE = 0xFFFD; |    34 const int NON_UTF_FAKE_EXITCODE = 0xFFFD; | 
|    34  |    35  | 
|    35 const MESSAGE_CANNOT_OPEN_DISPLAY = 'Gtk-WARNING **: cannot open display'; |    36 const MESSAGE_CANNOT_OPEN_DISPLAY = 'Gtk-WARNING **: cannot open display'; | 
|    36 const MESSAGE_FAILED_TO_RUN_COMMAND = 'Failed to run command. return code=1'; |    37 const MESSAGE_FAILED_TO_RUN_COMMAND = 'Failed to run command. return code=1'; | 
|    37  |    38  | 
|    38 typedef void TestCaseEvent(TestCase testCase); |    39 typedef void TestCaseEvent(TestCase testCase); | 
|    39 typedef void ExitCodeEvent(int exitCode); |    40 typedef void ExitCodeEvent(int exitCode); | 
|    40 typedef void EnqueueMoreWork(ProcessQueue queue); |    41 typedef void EnqueueMoreWork(ProcessQueue queue); | 
|    41 typedef void Action(); |    42 typedef void Action(); | 
|    42 typedef Future<AdbCommandResult> StepFunction(); |    43 typedef Future<AdbCommandResult> StepFunction(); | 
|    43  |    44  | 
|    44 // Some IO tests use these variables and get confused if the host environment |    45 // Some IO tests use these variables and get confused if the host environment | 
|    45 // variables are inherited so they are excluded. |    46 // variables are inherited so they are excluded. | 
|    46 const EXCLUDED_ENVIRONMENT_VARIABLES = const [ |    47 const EXCLUDED_ENVIRONMENT_VARIABLES = const [ | 
|    47   'http_proxy', |    48   'http_proxy', | 
|    48   'https_proxy', |    49   'https_proxy', | 
|    49   'no_proxy', |    50   'no_proxy', | 
|    50   'HTTP_PROXY', |    51   'HTTP_PROXY', | 
|    51   'HTTPS_PROXY', |    52   'HTTPS_PROXY', | 
|    52   'NO_PROXY' |    53   'NO_PROXY' | 
|    53 ]; |    54 ]; | 
|    54  |    55  | 
|    55 /** A command executed as a step in a test case. */ |  | 
|    56 class Command { |  | 
|    57   /** A descriptive name for this command. */ |  | 
|    58   String displayName; |  | 
|    59  |  | 
|    60   /** Number of times this command *can* be retried */ |  | 
|    61   int get maxNumRetries => 2; |  | 
|    62  |  | 
|    63   /** Reproduction command */ |  | 
|    64   String get reproductionCommand => null; |  | 
|    65  |  | 
|    66   // We compute the Command.hashCode lazily and cache it here, since it might |  | 
|    67   // be expensive to compute (and hashCode is called often). |  | 
|    68   int _cachedHashCode; |  | 
|    69  |  | 
|    70   Command._(this.displayName); |  | 
|    71  |  | 
|    72   int get hashCode { |  | 
|    73     if (_cachedHashCode == null) { |  | 
|    74       var builder = new HashCodeBuilder(); |  | 
|    75       _buildHashCode(builder); |  | 
|    76       _cachedHashCode = builder.value; |  | 
|    77     } |  | 
|    78     return _cachedHashCode; |  | 
|    79   } |  | 
|    80  |  | 
|    81   operator ==(Object other) => |  | 
|    82       identical(this, other) || |  | 
|    83       (runtimeType == other.runtimeType && _equal(other as Command)); |  | 
|    84  |  | 
|    85   void _buildHashCode(HashCodeBuilder builder) { |  | 
|    86     builder.addJson(displayName); |  | 
|    87   } |  | 
|    88  |  | 
|    89   bool _equal(covariant Command other) => |  | 
|    90       hashCode == other.hashCode && displayName == other.displayName; |  | 
|    91  |  | 
|    92   String toString() => reproductionCommand; |  | 
|    93  |  | 
|    94   Future<bool> get outputIsUpToDate => new Future.value(false); |  | 
|    95 } |  | 
|    96  |  | 
|    97 class ProcessCommand extends Command { |  | 
|    98   /** Path to the executable of this command. */ |  | 
|    99   String executable; |  | 
|   100  |  | 
|   101   /** Command line arguments to the executable. */ |  | 
|   102   List<String> arguments; |  | 
|   103  |  | 
|   104   /** Environment for the command */ |  | 
|   105   Map<String, String> environmentOverrides; |  | 
|   106  |  | 
|   107   /** Working directory for the command */ |  | 
|   108   final String workingDirectory; |  | 
|   109  |  | 
|   110   ProcessCommand._(String displayName, this.executable, this.arguments, |  | 
|   111       [this.environmentOverrides = null, this.workingDirectory = null]) |  | 
|   112       : super._(displayName) { |  | 
|   113     if (io.Platform.operatingSystem == 'windows') { |  | 
|   114       // Windows can't handle the first command if it is a .bat file or the like |  | 
|   115       // with the slashes going the other direction. |  | 
|   116       // NOTE: Issue 1306 |  | 
|   117       executable = executable.replaceAll('/', '\\'); |  | 
|   118     } |  | 
|   119   } |  | 
|   120  |  | 
|   121   void _buildHashCode(HashCodeBuilder builder) { |  | 
|   122     super._buildHashCode(builder); |  | 
|   123     builder.addJson(executable); |  | 
|   124     builder.addJson(workingDirectory); |  | 
|   125     builder.addJson(arguments); |  | 
|   126     builder.addJson(environmentOverrides); |  | 
|   127   } |  | 
|   128  |  | 
|   129   bool _equal(ProcessCommand other) => |  | 
|   130       super._equal(other) && |  | 
|   131       executable == other.executable && |  | 
|   132       deepJsonCompare(arguments, other.arguments) && |  | 
|   133       workingDirectory == other.workingDirectory && |  | 
|   134       deepJsonCompare(environmentOverrides, other.environmentOverrides); |  | 
|   135  |  | 
|   136   String get reproductionCommand { |  | 
|   137     var env = new StringBuffer(); |  | 
|   138     environmentOverrides?.forEach((key, value) => |  | 
|   139         (io.Platform.operatingSystem == 'windows') |  | 
|   140             ? env.write('set $key=${escapeCommandLineArgument(value)} & ') |  | 
|   141             : env.write('$key=${escapeCommandLineArgument(value)} ')); |  | 
|   142     var command = ([executable]..addAll(batchArguments)..addAll(arguments)) |  | 
|   143         .map(escapeCommandLineArgument) |  | 
|   144         .join(' '); |  | 
|   145     if (workingDirectory != null) { |  | 
|   146       command = "$command (working directory: $workingDirectory)"; |  | 
|   147     } |  | 
|   148     return "$env$command"; |  | 
|   149   } |  | 
|   150  |  | 
|   151   Future<bool> get outputIsUpToDate => new Future.value(false); |  | 
|   152  |  | 
|   153   /// Arguments that are passed to the process when starting batch mode. |  | 
|   154   /// |  | 
|   155   /// In non-batch mode, they should be passed before [arguments]. |  | 
|   156   List<String> get batchArguments => const []; |  | 
|   157 } |  | 
|   158  |  | 
|   159 class CompilationCommand extends ProcessCommand { |  | 
|   160   final String _outputFile; |  | 
|   161   final bool _neverSkipCompilation; |  | 
|   162   final List<Uri> _bootstrapDependencies; |  | 
|   163  |  | 
|   164   CompilationCommand._( |  | 
|   165       String displayName, |  | 
|   166       this._outputFile, |  | 
|   167       this._neverSkipCompilation, |  | 
|   168       this._bootstrapDependencies, |  | 
|   169       String executable, |  | 
|   170       List<String> arguments, |  | 
|   171       Map<String, String> environmentOverrides) |  | 
|   172       : super._(displayName, executable, arguments, environmentOverrides); |  | 
|   173  |  | 
|   174   Future<bool> get outputIsUpToDate { |  | 
|   175     if (_neverSkipCompilation) return new Future.value(false); |  | 
|   176  |  | 
|   177     Future<List<Uri>> readDepsFile(String path) { |  | 
|   178       var file = new io.File(new Path(path).toNativePath()); |  | 
|   179       if (!file.existsSync()) { |  | 
|   180         return new Future.value(null); |  | 
|   181       } |  | 
|   182       return file.readAsLines().then((List<String> lines) { |  | 
|   183         var dependencies = new List<Uri>(); |  | 
|   184         for (var line in lines) { |  | 
|   185           line = line.trim(); |  | 
|   186           if (line.length > 0) { |  | 
|   187             dependencies.add(Uri.parse(line)); |  | 
|   188           } |  | 
|   189         } |  | 
|   190         return dependencies; |  | 
|   191       }); |  | 
|   192     } |  | 
|   193  |  | 
|   194     return readDepsFile("$_outputFile.deps").then((dependencies) { |  | 
|   195       if (dependencies != null) { |  | 
|   196         dependencies.addAll(_bootstrapDependencies); |  | 
|   197         var jsOutputLastModified = TestUtils.lastModifiedCache |  | 
|   198             .getLastModified(new Uri(scheme: 'file', path: _outputFile)); |  | 
|   199         if (jsOutputLastModified != null) { |  | 
|   200           for (var dependency in dependencies) { |  | 
|   201             var dependencyLastModified = |  | 
|   202                 TestUtils.lastModifiedCache.getLastModified(dependency); |  | 
|   203             if (dependencyLastModified == null || |  | 
|   204                 dependencyLastModified.isAfter(jsOutputLastModified)) { |  | 
|   205               return false; |  | 
|   206             } |  | 
|   207           } |  | 
|   208           return true; |  | 
|   209         } |  | 
|   210       } |  | 
|   211       return false; |  | 
|   212     }); |  | 
|   213   } |  | 
|   214  |  | 
|   215   void _buildHashCode(HashCodeBuilder builder) { |  | 
|   216     super._buildHashCode(builder); |  | 
|   217     builder.addJson(_outputFile); |  | 
|   218     builder.addJson(_neverSkipCompilation); |  | 
|   219     builder.addJson(_bootstrapDependencies); |  | 
|   220   } |  | 
|   221  |  | 
|   222   bool _equal(CompilationCommand other) => |  | 
|   223       super._equal(other) && |  | 
|   224       _outputFile == other._outputFile && |  | 
|   225       _neverSkipCompilation == other._neverSkipCompilation && |  | 
|   226       deepJsonCompare(_bootstrapDependencies, other._bootstrapDependencies); |  | 
|   227 } |  | 
|   228  |  | 
|   229 class KernelCompilationCommand extends CompilationCommand { |  | 
|   230   KernelCompilationCommand._( |  | 
|   231       String displayName, |  | 
|   232       String outputFile, |  | 
|   233       bool neverSkipCompilation, |  | 
|   234       List<Uri> bootstrapDependencies, |  | 
|   235       String executable, |  | 
|   236       List<String> arguments, |  | 
|   237       Map<String, String> environmentOverrides) |  | 
|   238       : super._(displayName, outputFile, neverSkipCompilation, |  | 
|   239             bootstrapDependencies, executable, arguments, environmentOverrides); |  | 
|   240  |  | 
|   241   int get maxNumRetries => 1; |  | 
|   242 } |  | 
|   243  |  | 
|   244 /// This is just a Pair(String, Map) class with hashCode and operator == |  | 
|   245 class AddFlagsKey { |  | 
|   246   final String flags; |  | 
|   247   final Map env; |  | 
|   248   AddFlagsKey(this.flags, this.env); |  | 
|   249   // Just use object identity for environment map |  | 
|   250   bool operator ==(Object other) => |  | 
|   251       other is AddFlagsKey && flags == other.flags && env == other.env; |  | 
|   252   int get hashCode => flags.hashCode ^ env.hashCode; |  | 
|   253 } |  | 
|   254  |  | 
|   255 class ContentShellCommand extends ProcessCommand { |  | 
|   256   ContentShellCommand._( |  | 
|   257       String executable, |  | 
|   258       String htmlFile, |  | 
|   259       List<String> options, |  | 
|   260       List<String> dartFlags, |  | 
|   261       Map<String, String> environmentOverrides) |  | 
|   262       : super._("content_shell", executable, _getArguments(options, htmlFile), |  | 
|   263             _getEnvironment(environmentOverrides, dartFlags)); |  | 
|   264  |  | 
|   265   // Cache the modified environments in a map from the old environment and |  | 
|   266   // the string of Dart flags to the new environment.  Avoid creating new |  | 
|   267   // environment object for each command object. |  | 
|   268   static Map<AddFlagsKey, Map<String, String>> environments = {}; |  | 
|   269  |  | 
|   270   static Map<String, String> _getEnvironment( |  | 
|   271       Map<String, String> env, List<String> dartFlags) { |  | 
|   272     var needDartFlags = dartFlags != null && dartFlags.isNotEmpty; |  | 
|   273     if (needDartFlags) { |  | 
|   274       if (env == null) { |  | 
|   275         env = const <String, String>{}; |  | 
|   276       } |  | 
|   277       var flags = dartFlags.join(' '); |  | 
|   278       return environments.putIfAbsent( |  | 
|   279           new AddFlagsKey(flags, env), |  | 
|   280           () => new Map<String, String>.from(env) |  | 
|   281             ..addAll({'DART_FLAGS': flags, 'DART_FORWARDING_PRINT': '1'})); |  | 
|   282     } |  | 
|   283     return env; |  | 
|   284   } |  | 
|   285  |  | 
|   286   static List<String> _getArguments(List<String> options, String htmlFile) { |  | 
|   287     var arguments = options.toList(); |  | 
|   288     arguments.add(htmlFile); |  | 
|   289     return arguments; |  | 
|   290   } |  | 
|   291  |  | 
|   292   int get maxNumRetries => 3; |  | 
|   293 } |  | 
|   294  |  | 
|   295 class BrowserTestCommand extends Command { |  | 
|   296   Runtime get browser => configuration.runtime; |  | 
|   297   final String url; |  | 
|   298   final Configuration configuration; |  | 
|   299   final bool retry; |  | 
|   300  |  | 
|   301   BrowserTestCommand._(this.url, this.configuration, this.retry) |  | 
|   302       : super._(configuration.runtime.name); |  | 
|   303  |  | 
|   304   void _buildHashCode(HashCodeBuilder builder) { |  | 
|   305     super._buildHashCode(builder); |  | 
|   306     builder.addJson(browser.name); |  | 
|   307     builder.addJson(url); |  | 
|   308     builder.add(configuration); |  | 
|   309     builder.add(retry); |  | 
|   310   } |  | 
|   311  |  | 
|   312   bool _equal(BrowserTestCommand other) => |  | 
|   313       super._equal(other) && |  | 
|   314       browser == other.browser && |  | 
|   315       url == other.url && |  | 
|   316       identical(configuration, other.configuration) && |  | 
|   317       retry == other.retry; |  | 
|   318  |  | 
|   319   String get reproductionCommand { |  | 
|   320     var parts = [ |  | 
|   321       io.Platform.resolvedExecutable, |  | 
|   322       'tools/testing/dart/launch_browser.dart', |  | 
|   323       browser.name, |  | 
|   324       url |  | 
|   325     ]; |  | 
|   326     return parts.map(escapeCommandLineArgument).join(' '); |  | 
|   327   } |  | 
|   328  |  | 
|   329   int get maxNumRetries => 4; |  | 
|   330 } |  | 
|   331  |  | 
|   332 class BrowserHtmlTestCommand extends BrowserTestCommand { |  | 
|   333   List<String> expectedMessages; |  | 
|   334   BrowserHtmlTestCommand._(String url, Configuration configuration, |  | 
|   335       this.expectedMessages, bool retry) |  | 
|   336       : super._(url, configuration, retry); |  | 
|   337  |  | 
|   338   void _buildHashCode(HashCodeBuilder builder) { |  | 
|   339     super._buildHashCode(builder); |  | 
|   340     builder.addJson(expectedMessages); |  | 
|   341   } |  | 
|   342  |  | 
|   343   bool _equal(BrowserHtmlTestCommand other) => |  | 
|   344       super._equal(other) && |  | 
|   345       identical(expectedMessages, other.expectedMessages); |  | 
|   346 } |  | 
|   347  |  | 
|   348 class AnalysisCommand extends ProcessCommand { |  | 
|   349   final String flavor; |  | 
|   350  |  | 
|   351   AnalysisCommand._(this.flavor, String displayName, String executable, |  | 
|   352       List<String> arguments, Map<String, String> environmentOverrides) |  | 
|   353       : super._(displayName, executable, arguments, environmentOverrides); |  | 
|   354  |  | 
|   355   void _buildHashCode(HashCodeBuilder builder) { |  | 
|   356     super._buildHashCode(builder); |  | 
|   357     builder.addJson(flavor); |  | 
|   358   } |  | 
|   359  |  | 
|   360   bool _equal(AnalysisCommand other) => |  | 
|   361       super._equal(other) && flavor == other.flavor; |  | 
|   362 } |  | 
|   363  |  | 
|   364 class VmCommand extends ProcessCommand { |  | 
|   365   VmCommand._(String executable, List<String> arguments, |  | 
|   366       Map<String, String> environmentOverrides) |  | 
|   367       : super._("vm", executable, arguments, environmentOverrides); |  | 
|   368 } |  | 
|   369  |  | 
|   370 class VmBatchCommand extends ProcessCommand implements VmCommand { |  | 
|   371   final String dartFile; |  | 
|   372   final bool checked; |  | 
|   373  |  | 
|   374   VmBatchCommand._(String executable, String dartFile, List<String> arguments, |  | 
|   375       Map<String, String> environmentOverrides, |  | 
|   376       {this.checked: true}) |  | 
|   377       : this.dartFile = dartFile, |  | 
|   378         super._('vm-batch', executable, arguments, environmentOverrides); |  | 
|   379  |  | 
|   380   @override |  | 
|   381   List<String> get batchArguments => |  | 
|   382       checked ? ['--checked', dartFile] : [dartFile]; |  | 
|   383  |  | 
|   384   @override |  | 
|   385   bool _equal(VmBatchCommand other) { |  | 
|   386     return super._equal(other) && |  | 
|   387         dartFile == other.dartFile && |  | 
|   388         checked == other.checked; |  | 
|   389   } |  | 
|   390  |  | 
|   391   @override |  | 
|   392   void _buildHashCode(HashCodeBuilder builder) { |  | 
|   393     super._buildHashCode(builder); |  | 
|   394     builder.addJson(dartFile); |  | 
|   395     builder.addJson(checked); |  | 
|   396   } |  | 
|   397 } |  | 
|   398  |  | 
|   399 class AdbPrecompilationCommand extends Command { |  | 
|   400   final String precompiledRunnerFilename; |  | 
|   401   final String processTestFilename; |  | 
|   402   final String precompiledTestDirectory; |  | 
|   403   final List<String> arguments; |  | 
|   404   final bool useBlobs; |  | 
|   405  |  | 
|   406   AdbPrecompilationCommand._( |  | 
|   407       this.precompiledRunnerFilename, |  | 
|   408       this.processTestFilename, |  | 
|   409       this.precompiledTestDirectory, |  | 
|   410       this.arguments, |  | 
|   411       this.useBlobs) |  | 
|   412       : super._("adb_precompilation"); |  | 
|   413  |  | 
|   414   void _buildHashCode(HashCodeBuilder builder) { |  | 
|   415     super._buildHashCode(builder); |  | 
|   416     builder.add(precompiledRunnerFilename); |  | 
|   417     builder.add(precompiledTestDirectory); |  | 
|   418     builder.add(arguments); |  | 
|   419     builder.add(useBlobs); |  | 
|   420   } |  | 
|   421  |  | 
|   422   bool _equal(AdbPrecompilationCommand other) => |  | 
|   423       super._equal(other) && |  | 
|   424       precompiledRunnerFilename == other.precompiledRunnerFilename && |  | 
|   425       useBlobs == other.useBlobs && |  | 
|   426       arguments == other.arguments && |  | 
|   427       precompiledTestDirectory == other.precompiledTestDirectory; |  | 
|   428  |  | 
|   429   String toString() => 'Steps to push precompiled runner and precompiled code ' |  | 
|   430       'to an attached device. Uses (and requires) adb.'; |  | 
|   431 } |  | 
|   432  |  | 
|   433 class JSCommandlineCommand extends ProcessCommand { |  | 
|   434   JSCommandlineCommand._( |  | 
|   435       String displayName, String executable, List<String> arguments, |  | 
|   436       [Map<String, String> environmentOverrides = null]) |  | 
|   437       : super._(displayName, executable, arguments, environmentOverrides); |  | 
|   438 } |  | 
|   439  |  | 
|   440 class PubCommand extends ProcessCommand { |  | 
|   441   final String command; |  | 
|   442  |  | 
|   443   PubCommand._(String pubCommand, String pubExecutable, |  | 
|   444       String pubspecYamlDirectory, String pubCacheDirectory, List<String> args) |  | 
|   445       : command = pubCommand, |  | 
|   446         super._( |  | 
|   447             'pub_$pubCommand', |  | 
|   448             new io.File(pubExecutable).absolute.path, |  | 
|   449             [pubCommand]..addAll(args), |  | 
|   450             {'PUB_CACHE': pubCacheDirectory}, |  | 
|   451             pubspecYamlDirectory); |  | 
|   452  |  | 
|   453   void _buildHashCode(HashCodeBuilder builder) { |  | 
|   454     super._buildHashCode(builder); |  | 
|   455     builder.addJson(command); |  | 
|   456   } |  | 
|   457  |  | 
|   458   bool _equal(PubCommand other) => |  | 
|   459       super._equal(other) && command == other.command; |  | 
|   460 } |  | 
|   461  |  | 
|   462 /* [ScriptCommand]s are executed by dart code. */ |  | 
|   463 abstract class ScriptCommand extends Command { |  | 
|   464   ScriptCommand._(String displayName) : super._(displayName); |  | 
|   465  |  | 
|   466   Future<ScriptCommandOutputImpl> run(); |  | 
|   467 } |  | 
|   468  |  | 
|   469 class CleanDirectoryCopyCommand extends ScriptCommand { |  | 
|   470   final String _sourceDirectory; |  | 
|   471   final String _destinationDirectory; |  | 
|   472  |  | 
|   473   CleanDirectoryCopyCommand._(this._sourceDirectory, this._destinationDirectory) |  | 
|   474       : super._('dir_copy'); |  | 
|   475  |  | 
|   476   String get reproductionCommand => |  | 
|   477       "Copying '$_sourceDirectory' to '$_destinationDirectory'."; |  | 
|   478  |  | 
|   479   Future<ScriptCommandOutputImpl> run() { |  | 
|   480     var watch = new Stopwatch()..start(); |  | 
|   481  |  | 
|   482     var destination = new io.Directory(_destinationDirectory); |  | 
|   483  |  | 
|   484     return destination.exists().then((bool exists) { |  | 
|   485       Future cleanDirectoryFuture; |  | 
|   486       if (exists) { |  | 
|   487         cleanDirectoryFuture = TestUtils.deleteDirectory(_destinationDirectory); |  | 
|   488       } else { |  | 
|   489         cleanDirectoryFuture = new Future.value(null); |  | 
|   490       } |  | 
|   491       return cleanDirectoryFuture.then((_) { |  | 
|   492         return TestUtils.copyDirectory(_sourceDirectory, _destinationDirectory); |  | 
|   493       }); |  | 
|   494     }).then((_) { |  | 
|   495       return new ScriptCommandOutputImpl( |  | 
|   496           this, Expectation.pass, "", watch.elapsed); |  | 
|   497     }).catchError((error) { |  | 
|   498       return new ScriptCommandOutputImpl( |  | 
|   499           this, Expectation.fail, "An error occured: $error.", watch.elapsed); |  | 
|   500     }); |  | 
|   501   } |  | 
|   502  |  | 
|   503   void _buildHashCode(HashCodeBuilder builder) { |  | 
|   504     super._buildHashCode(builder); |  | 
|   505     builder.addJson(_sourceDirectory); |  | 
|   506     builder.addJson(_destinationDirectory); |  | 
|   507   } |  | 
|   508  |  | 
|   509   bool _equal(CleanDirectoryCopyCommand other) => |  | 
|   510       super._equal(other) && |  | 
|   511       _sourceDirectory == other._sourceDirectory && |  | 
|   512       _destinationDirectory == other._destinationDirectory; |  | 
|   513 } |  | 
|   514  |  | 
|   515 /* |  | 
|   516  * [MakeSymlinkCommand] makes a symbolic link to another directory. |  | 
|   517  */ |  | 
|   518 class MakeSymlinkCommand extends ScriptCommand { |  | 
|   519   String _link; |  | 
|   520   String _target; |  | 
|   521  |  | 
|   522   MakeSymlinkCommand._(this._link, this._target) : super._('make_symlink'); |  | 
|   523  |  | 
|   524   String get reproductionCommand => |  | 
|   525       "Make symbolic link '$_link' (target: $_target)'."; |  | 
|   526  |  | 
|   527   Future<ScriptCommandOutputImpl> run() { |  | 
|   528     var watch = new Stopwatch()..start(); |  | 
|   529     var targetFile = new io.Directory(_target); |  | 
|   530     return targetFile.exists().then((bool targetExists) { |  | 
|   531       if (!targetExists) { |  | 
|   532         throw new Exception("Target '$_target' does not exist"); |  | 
|   533       } |  | 
|   534       var link = new io.Link(_link); |  | 
|   535  |  | 
|   536       return link.exists().then((bool exists) { |  | 
|   537         if (exists) return link.delete(); |  | 
|   538       }).then((_) => link.create(_target)); |  | 
|   539     }).then((_) { |  | 
|   540       return new ScriptCommandOutputImpl( |  | 
|   541           this, Expectation.pass, "", watch.elapsed); |  | 
|   542     }).catchError((error) { |  | 
|   543       return new ScriptCommandOutputImpl( |  | 
|   544           this, Expectation.fail, "An error occured: $error.", watch.elapsed); |  | 
|   545     }); |  | 
|   546   } |  | 
|   547  |  | 
|   548   void _buildHashCode(HashCodeBuilder builder) { |  | 
|   549     super._buildHashCode(builder); |  | 
|   550     builder.addJson(_link); |  | 
|   551     builder.addJson(_target); |  | 
|   552   } |  | 
|   553  |  | 
|   554   bool _equal(MakeSymlinkCommand other) => |  | 
|   555       super._equal(other) && _link == other._link && _target == other._target; |  | 
|   556 } |  | 
|   557  |  | 
|   558 class CommandBuilder { |  | 
|   559   static final CommandBuilder instance = new CommandBuilder._(); |  | 
|   560  |  | 
|   561   bool _cleared = false; |  | 
|   562   final _cachedCommands = new Map<Command, Command>(); |  | 
|   563  |  | 
|   564   CommandBuilder._(); |  | 
|   565  |  | 
|   566   void clearCommandCache() { |  | 
|   567     _cachedCommands.clear(); |  | 
|   568     _cleared = true; |  | 
|   569   } |  | 
|   570  |  | 
|   571   ContentShellCommand getContentShellCommand( |  | 
|   572       String executable, |  | 
|   573       String htmlFile, |  | 
|   574       List<String> options, |  | 
|   575       List<String> dartFlags, |  | 
|   576       Map<String, String> environment) { |  | 
|   577     ContentShellCommand command = new ContentShellCommand._( |  | 
|   578         executable, htmlFile, options, dartFlags, environment); |  | 
|   579     return _getUniqueCommand(command); |  | 
|   580   } |  | 
|   581  |  | 
|   582   BrowserTestCommand getBrowserTestCommand( |  | 
|   583       String url, Configuration configuration, bool retry) { |  | 
|   584     var command = new BrowserTestCommand._(url, configuration, retry); |  | 
|   585     return _getUniqueCommand(command); |  | 
|   586   } |  | 
|   587  |  | 
|   588   BrowserHtmlTestCommand getBrowserHtmlTestCommand(String url, |  | 
|   589       Configuration configuration, List<String> expectedMessages, bool retry) { |  | 
|   590     var command = new BrowserHtmlTestCommand._( |  | 
|   591         url, configuration, expectedMessages, retry); |  | 
|   592     return _getUniqueCommand(command); |  | 
|   593   } |  | 
|   594  |  | 
|   595   CompilationCommand getCompilationCommand( |  | 
|   596       String displayName, |  | 
|   597       String outputFile, |  | 
|   598       bool neverSkipCompilation, |  | 
|   599       List<Uri> bootstrapDependencies, |  | 
|   600       String executable, |  | 
|   601       List<String> arguments, |  | 
|   602       Map<String, String> environment) { |  | 
|   603     var command = new CompilationCommand._( |  | 
|   604         displayName, |  | 
|   605         outputFile, |  | 
|   606         neverSkipCompilation, |  | 
|   607         bootstrapDependencies, |  | 
|   608         executable, |  | 
|   609         arguments, |  | 
|   610         environment); |  | 
|   611     return _getUniqueCommand(command); |  | 
|   612   } |  | 
|   613  |  | 
|   614   CompilationCommand getKernelCompilationCommand( |  | 
|   615       String displayName, |  | 
|   616       String outputFile, |  | 
|   617       bool neverSkipCompilation, |  | 
|   618       List<Uri> bootstrapDependencies, |  | 
|   619       String executable, |  | 
|   620       List<String> arguments, |  | 
|   621       Map<String, String> environment) { |  | 
|   622     var command = new KernelCompilationCommand._( |  | 
|   623         displayName, |  | 
|   624         outputFile, |  | 
|   625         neverSkipCompilation, |  | 
|   626         bootstrapDependencies, |  | 
|   627         executable, |  | 
|   628         arguments, |  | 
|   629         environment); |  | 
|   630     return _getUniqueCommand(command); |  | 
|   631   } |  | 
|   632  |  | 
|   633   AnalysisCommand getAnalysisCommand(String displayName, String executable, |  | 
|   634       List<String> arguments, Map<String, String> environmentOverrides, |  | 
|   635       {String flavor: 'dart2analyzer'}) { |  | 
|   636     var command = new AnalysisCommand._( |  | 
|   637         flavor, displayName, executable, arguments, environmentOverrides); |  | 
|   638     return _getUniqueCommand(command); |  | 
|   639   } |  | 
|   640  |  | 
|   641   VmCommand getVmCommand(String executable, List<String> arguments, |  | 
|   642       Map<String, String> environmentOverrides) { |  | 
|   643     var command = new VmCommand._(executable, arguments, environmentOverrides); |  | 
|   644     return _getUniqueCommand(command); |  | 
|   645   } |  | 
|   646  |  | 
|   647   VmBatchCommand getVmBatchCommand(String executable, String tester, |  | 
|   648       List<String> arguments, Map<String, String> environmentOverrides, |  | 
|   649       {bool checked: true}) { |  | 
|   650     var command = new VmBatchCommand._( |  | 
|   651         executable, tester, arguments, environmentOverrides, |  | 
|   652         checked: checked); |  | 
|   653     return _getUniqueCommand(command); |  | 
|   654   } |  | 
|   655  |  | 
|   656   AdbPrecompilationCommand getAdbPrecompiledCommand( |  | 
|   657       String precompiledRunner, |  | 
|   658       String processTest, |  | 
|   659       String testDirectory, |  | 
|   660       List<String> arguments, |  | 
|   661       bool useBlobs) { |  | 
|   662     var command = new AdbPrecompilationCommand._( |  | 
|   663         precompiledRunner, processTest, testDirectory, arguments, useBlobs); |  | 
|   664     return _getUniqueCommand(command); |  | 
|   665   } |  | 
|   666  |  | 
|   667   Command getJSCommandlineCommand( |  | 
|   668       String displayName, String executable, List<String> arguments, |  | 
|   669       [Map<String, String> environment]) { |  | 
|   670     var command = new JSCommandlineCommand._( |  | 
|   671         displayName, executable, arguments, environment); |  | 
|   672     return _getUniqueCommand(command); |  | 
|   673   } |  | 
|   674  |  | 
|   675   Command getProcessCommand( |  | 
|   676       String displayName, String executable, List<String> arguments, |  | 
|   677       [Map<String, String> environment, String workingDirectory]) { |  | 
|   678     var command = new ProcessCommand._( |  | 
|   679         displayName, executable, arguments, environment, workingDirectory); |  | 
|   680     return _getUniqueCommand(command); |  | 
|   681   } |  | 
|   682  |  | 
|   683   Command getCopyCommand(String sourceDirectory, String destinationDirectory) { |  | 
|   684     var command = |  | 
|   685         new CleanDirectoryCopyCommand._(sourceDirectory, destinationDirectory); |  | 
|   686     return _getUniqueCommand(command); |  | 
|   687   } |  | 
|   688  |  | 
|   689   Command getPubCommand(String pubCommand, String pubExecutable, |  | 
|   690       String pubspecYamlDirectory, String pubCacheDirectory, |  | 
|   691       {List<String> arguments: const <String>[]}) { |  | 
|   692     var command = new PubCommand._(pubCommand, pubExecutable, |  | 
|   693         pubspecYamlDirectory, pubCacheDirectory, arguments); |  | 
|   694     return _getUniqueCommand(command); |  | 
|   695   } |  | 
|   696  |  | 
|   697   Command getMakeSymlinkCommand(String link, String target) { |  | 
|   698     return _getUniqueCommand(new MakeSymlinkCommand._(link, target)); |  | 
|   699   } |  | 
|   700  |  | 
|   701   T _getUniqueCommand<T extends Command>(T command) { |  | 
|   702     // All Command classes implement hashCode and operator==. |  | 
|   703     // We check if this command has already been built. |  | 
|   704     //  If so, we return the cached one. Otherwise we |  | 
|   705     // store the one given as [command] argument. |  | 
|   706     if (_cleared) { |  | 
|   707       throw new Exception( |  | 
|   708           "CommandBuilder.get[type]Command called after cache cleared"); |  | 
|   709     } |  | 
|   710     var cachedCommand = _cachedCommands[command]; |  | 
|   711     if (cachedCommand != null) { |  | 
|   712       return cachedCommand as T; |  | 
|   713     } |  | 
|   714     _cachedCommands[command] = command; |  | 
|   715     return command; |  | 
|   716   } |  | 
|   717 } |  | 
|   718  |  | 
|   719 /** |    56 /** | 
|   720  * TestCase contains all the information needed to run a test and evaluate |    57  * TestCase contains all the information needed to run a test and evaluate | 
|   721  * its output.  Running a test involves starting a separate process, with |    58  * its output.  Running a test involves starting a separate process, with | 
|   722  * the executable and arguments given by the TestCase, and recording its |    59  * the executable and arguments given by the TestCase, and recording its | 
|   723  * stdout and stderr output streams, and its exit code.  TestCase only |    60  * stdout and stderr output streams, and its exit code.  TestCase only | 
|   724  * contains static information about the test; actually running the test is |    61  * contains static information about the test; actually running the test is | 
|   725  * performed by [ProcessQueue] using a [RunningProcess] object. |    62  * performed by [ProcessQueue] using a [RunningProcess] object. | 
|   726  * |    63  * | 
|   727  * The output information is stored in a [CommandOutput] instance contained |    64  * The output information is stored in a [CommandOutput] instance contained | 
|   728  * in TestCase.commandOutputs. The last CommandOutput instance is responsible |    65  * in TestCase.commandOutputs. The last CommandOutput instance is responsible | 
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|   882       bool isNegative, |   219       bool isNegative, | 
|   883       this._testingUrl) |   220       this._testingUrl) | 
|   884       : super(displayName, commands, configuration, expectedOutcomes, |   221       : super(displayName, commands, configuration, expectedOutcomes, | 
|   885             isNegative: isNegative, info: info); |   222             isNegative: isNegative, info: info); | 
|   886  |   223  | 
|   887   String _testingUrl; |   224   String _testingUrl; | 
|   888  |   225  | 
|   889   String get testingUrl => _testingUrl; |   226   String get testingUrl => _testingUrl; | 
|   890 } |   227 } | 
|   891  |   228  | 
|   892 class UnittestSuiteMessagesMixin { |  | 
|   893   bool _isAsyncTest(String testOutput) { |  | 
|   894     return testOutput.contains("unittest-suite-wait-for-done"); |  | 
|   895   } |  | 
|   896  |  | 
|   897   bool _isAsyncTestSuccessful(String testOutput) { |  | 
|   898     return testOutput.contains("unittest-suite-success"); |  | 
|   899   } |  | 
|   900  |  | 
|   901   Expectation _negateOutcomeIfIncompleteAsyncTest( |  | 
|   902       Expectation outcome, String testOutput) { |  | 
|   903     // If this is an asynchronous test and the asynchronous operation didn't |  | 
|   904     // complete successfully, it's outcome is Expectation.FAIL. |  | 
|   905     // TODO: maybe we should introduce a AsyncIncomplete marker or so |  | 
|   906     if (outcome == Expectation.pass) { |  | 
|   907       if (_isAsyncTest(testOutput) && !_isAsyncTestSuccessful(testOutput)) { |  | 
|   908         return Expectation.fail; |  | 
|   909       } |  | 
|   910     } |  | 
|   911     return outcome; |  | 
|   912   } |  | 
|   913 } |  | 
|   914  |  | 
|   915 /** |  | 
|   916  * CommandOutput records the output of a completed command: the process's exit |  | 
|   917  * code, the standard output and standard error, whether the process timed out, |  | 
|   918  * and the time the process took to run.  It does not contain a pointer to the |  | 
|   919  * [TestCase] this is the output of, so some functions require the test case |  | 
|   920  * to be passed as an argument. |  | 
|   921  */ |  | 
|   922 abstract class CommandOutput { |  | 
|   923   Command get command; |  | 
|   924  |  | 
|   925   Expectation result(TestCase testCase); |  | 
|   926  |  | 
|   927   bool get hasCrashed; |  | 
|   928  |  | 
|   929   bool get hasTimedOut; |  | 
|   930  |  | 
|   931   bool didFail(TestCase testCase); |  | 
|   932  |  | 
|   933   bool hasFailed(TestCase testCase); |  | 
|   934  |  | 
|   935   bool get canRunDependendCommands; |  | 
|   936  |  | 
|   937   bool get successful; // otherwise we might to retry running |  | 
|   938  |  | 
|   939   Duration get time; |  | 
|   940  |  | 
|   941   int get exitCode; |  | 
|   942  |  | 
|   943   int get pid; |  | 
|   944  |  | 
|   945   List<int> get stdout; |  | 
|   946  |  | 
|   947   List<int> get stderr; |  | 
|   948  |  | 
|   949   List<String> get diagnostics; |  | 
|   950  |  | 
|   951   bool get compilationSkipped; |  | 
|   952 } |  | 
|   953  |  | 
|   954 class CommandOutputImpl extends UniqueObject implements CommandOutput { |  | 
|   955   Command command; |  | 
|   956   int exitCode; |  | 
|   957  |  | 
|   958   bool timedOut; |  | 
|   959   List<int> stdout; |  | 
|   960   List<int> stderr; |  | 
|   961   Duration time; |  | 
|   962   List<String> diagnostics; |  | 
|   963   bool compilationSkipped; |  | 
|   964   int pid; |  | 
|   965  |  | 
|   966   /** |  | 
|   967    * A flag to indicate we have already printed a warning about ignoring the VM |  | 
|   968    * crash, to limit the amount of output produced per test. |  | 
|   969    */ |  | 
|   970   bool alreadyPrintedWarning = false; |  | 
|   971  |  | 
|   972   CommandOutputImpl( |  | 
|   973       Command this.command, |  | 
|   974       int this.exitCode, |  | 
|   975       bool this.timedOut, |  | 
|   976       List<int> this.stdout, |  | 
|   977       List<int> this.stderr, |  | 
|   978       Duration this.time, |  | 
|   979       bool this.compilationSkipped, |  | 
|   980       int this.pid) { |  | 
|   981     diagnostics = []; |  | 
|   982   } |  | 
|   983  |  | 
|   984   Expectation result(TestCase testCase) { |  | 
|   985     if (hasCrashed) return Expectation.crash; |  | 
|   986     if (hasTimedOut) return Expectation.timeout; |  | 
|   987     if (hasFailed(testCase)) return Expectation.fail; |  | 
|   988     if (hasNonUtf8) return Expectation.nonUtf8Error; |  | 
|   989     return Expectation.pass; |  | 
|   990   } |  | 
|   991  |  | 
|   992   bool get hasCrashed { |  | 
|   993     // dart2js exits with code 253 in case of unhandled exceptions. |  | 
|   994     // The dart binary exits with code 253 in case of an API error such |  | 
|   995     // as an invalid snapshot file. |  | 
|   996     // In either case an exit code of 253 is considered a crash. |  | 
|   997     if (exitCode == 253) return true; |  | 
|   998     if (io.Platform.operatingSystem == 'windows') { |  | 
|   999       // The VM uses std::abort to terminate on asserts. |  | 
|  1000       // std::abort terminates with exit code 3 on Windows. |  | 
|  1001       if (exitCode == 3 || exitCode == CRASHING_BROWSER_EXITCODE) { |  | 
|  1002         return !timedOut; |  | 
|  1003       } |  | 
|  1004       // If a program receives an uncaught system exception, the program |  | 
|  1005       // terminates with the exception code as exit code. |  | 
|  1006       // The 0x3FFFFF00 mask here tries to determine if an exception indicates |  | 
|  1007       // a crash of the program. |  | 
|  1008       // System exception codes can be found in 'winnt.h', for example |  | 
|  1009       // "#define STATUS_ACCESS_VIOLATION  ((DWORD) 0xC0000005)" |  | 
|  1010       return (!timedOut && (exitCode < 0) && ((0x3FFFFF00 & exitCode) == 0)); |  | 
|  1011     } |  | 
|  1012     return !timedOut && ((exitCode < 0)); |  | 
|  1013   } |  | 
|  1014  |  | 
|  1015   bool get hasTimedOut => timedOut; |  | 
|  1016  |  | 
|  1017   bool didFail(TestCase testCase) { |  | 
|  1018     return (exitCode != 0 && !hasCrashed); |  | 
|  1019   } |  | 
|  1020  |  | 
|  1021   bool get canRunDependendCommands { |  | 
|  1022     // FIXME(kustermann): We may need to change this |  | 
|  1023     return !hasTimedOut && exitCode == 0; |  | 
|  1024   } |  | 
|  1025  |  | 
|  1026   bool get successful { |  | 
|  1027     // FIXME(kustermann): We may need to change this |  | 
|  1028     return !hasTimedOut && exitCode == 0; |  | 
|  1029   } |  | 
|  1030  |  | 
|  1031   // Reverse result of a negative test. |  | 
|  1032   bool hasFailed(TestCase testCase) { |  | 
|  1033     return testCase.isNegative ? !didFail(testCase) : didFail(testCase); |  | 
|  1034   } |  | 
|  1035  |  | 
|  1036   bool get hasNonUtf8 => exitCode == NON_UTF_FAKE_EXITCODE; |  | 
|  1037  |  | 
|  1038   Expectation _negateOutcomeIfNegativeTest( |  | 
|  1039       Expectation outcome, bool isNegative) { |  | 
|  1040     if (!isNegative) return outcome; |  | 
|  1041     if (outcome == Expectation.ignore) return outcome; |  | 
|  1042     if (outcome.canBeOutcomeOf(Expectation.fail)) { |  | 
|  1043       return Expectation.pass; |  | 
|  1044     } |  | 
|  1045     return Expectation.fail; |  | 
|  1046   } |  | 
|  1047 } |  | 
|  1048  |  | 
|  1049 class ContentShellCommandOutputImpl extends CommandOutputImpl { |  | 
|  1050   // Although tests are reported as passing, content shell sometimes exits with |  | 
|  1051   // a nonzero exitcode which makes our dartium builders extremely falky. |  | 
|  1052   // See: http://dartbug.com/15139. |  | 
|  1053   // TODO(rnystrom): Is this still needed? The underlying bug is closed. |  | 
|  1054   static int WHITELISTED_CONTENTSHELL_EXITCODE = -1073740022; |  | 
|  1055   static bool isWindows = io.Platform.operatingSystem == 'windows'; |  | 
|  1056   static bool _failedBecauseOfFlakyInfrastructure( |  | 
|  1057       Command command, bool timedOut, List<int> stderrBytes) { |  | 
|  1058     // If the browser test failed, it may have been because content shell |  | 
|  1059     // and the virtual framebuffer X server didn't hook up, or it crashed with |  | 
|  1060     // a core dump. Sometimes content shell crashes after it has set the stdout |  | 
|  1061     // to PASS, so we have to do this check first. |  | 
|  1062     // Content shell also fails with a broken pipe message: Issue 26739 |  | 
|  1063     var zygoteCrash = |  | 
|  1064         new RegExp(r"ERROR:zygote_linux\.cc\(\d+\)] write: Broken pipe"); |  | 
|  1065     var stderr = decodeUtf8(stderrBytes); |  | 
|  1066     // TODO(7564): See http://dartbug.com/7564 |  | 
|  1067     // This may not be happening anymore.  Test by removing this suppression. |  | 
|  1068     if (stderr.contains(MESSAGE_CANNOT_OPEN_DISPLAY) || |  | 
|  1069         stderr.contains(MESSAGE_FAILED_TO_RUN_COMMAND)) { |  | 
|  1070       DebugLogger.warning( |  | 
|  1071           "Warning: Failure because of missing XDisplay. Test ignored"); |  | 
|  1072       return true; |  | 
|  1073     } |  | 
|  1074     // TODO(26739): See http://dartbug.com/26739 |  | 
|  1075     if (zygoteCrash.hasMatch(stderr)) { |  | 
|  1076       DebugLogger.warning("Warning: Failure because of content_shell " |  | 
|  1077           "zygote crash. Test ignored"); |  | 
|  1078       return true; |  | 
|  1079     } |  | 
|  1080     return false; |  | 
|  1081   } |  | 
|  1082  |  | 
|  1083   bool _infraFailure; |  | 
|  1084  |  | 
|  1085   ContentShellCommandOutputImpl( |  | 
|  1086       Command command, |  | 
|  1087       int exitCode, |  | 
|  1088       bool timedOut, |  | 
|  1089       List<int> stdout, |  | 
|  1090       List<int> stderr, |  | 
|  1091       Duration time, |  | 
|  1092       bool compilationSkipped) |  | 
|  1093       : _infraFailure = |  | 
|  1094             _failedBecauseOfFlakyInfrastructure(command, timedOut, stderr), |  | 
|  1095         super(command, exitCode, timedOut, stdout, stderr, time, |  | 
|  1096             compilationSkipped, 0); |  | 
|  1097  |  | 
|  1098   Expectation result(TestCase testCase) { |  | 
|  1099     if (_infraFailure) { |  | 
|  1100       return Expectation.ignore; |  | 
|  1101     } |  | 
|  1102  |  | 
|  1103     // Handle crashes and timeouts first |  | 
|  1104     if (hasCrashed) return Expectation.crash; |  | 
|  1105     if (hasTimedOut) return Expectation.timeout; |  | 
|  1106     if (hasNonUtf8) return Expectation.nonUtf8Error; |  | 
|  1107  |  | 
|  1108     var outcome = _getOutcome(); |  | 
|  1109  |  | 
|  1110     if (testCase.hasRuntimeError) { |  | 
|  1111       if (!outcome.canBeOutcomeOf(Expectation.runtimeError)) { |  | 
|  1112         return Expectation.missingRuntimeError; |  | 
|  1113       } |  | 
|  1114     } |  | 
|  1115     if (testCase.isNegative) { |  | 
|  1116       if (outcome.canBeOutcomeOf(Expectation.fail)) return Expectation.pass; |  | 
|  1117       return Expectation.fail; |  | 
|  1118     } |  | 
|  1119     return outcome; |  | 
|  1120   } |  | 
|  1121  |  | 
|  1122   bool get successful => canRunDependendCommands; |  | 
|  1123  |  | 
|  1124   bool get canRunDependendCommands { |  | 
|  1125     // We cannot rely on the exit code of content_shell as a method to |  | 
|  1126     // determine if we were successful or not. |  | 
|  1127     return super.canRunDependendCommands && !didFail(null); |  | 
|  1128   } |  | 
|  1129  |  | 
|  1130   bool get hasCrashed { |  | 
|  1131     return super.hasCrashed || _rendererCrashed; |  | 
|  1132   } |  | 
|  1133  |  | 
|  1134   Expectation _getOutcome() { |  | 
|  1135     if (_browserTestFailure) { |  | 
|  1136       return Expectation.runtimeError; |  | 
|  1137     } |  | 
|  1138     return Expectation.pass; |  | 
|  1139   } |  | 
|  1140  |  | 
|  1141   bool get _rendererCrashed => |  | 
|  1142       decodeUtf8(super.stdout).contains("#CRASHED - rendere"); |  | 
|  1143  |  | 
|  1144   bool get _browserTestFailure { |  | 
|  1145     // Browser tests fail unless stdout contains |  | 
|  1146     // 'Content-Type: text/plain' followed by 'PASS'. |  | 
|  1147     bool hasContentType = false; |  | 
|  1148     var stdoutLines = decodeUtf8(super.stdout).split("\n"); |  | 
|  1149     var containsFail = false; |  | 
|  1150     var containsPass = false; |  | 
|  1151     for (String line in stdoutLines) { |  | 
|  1152       switch (line) { |  | 
|  1153         case 'Content-Type: text/plain': |  | 
|  1154           hasContentType = true; |  | 
|  1155           break; |  | 
|  1156         case 'FAIL': |  | 
|  1157           if (hasContentType) { |  | 
|  1158             containsFail = true; |  | 
|  1159           } |  | 
|  1160           break; |  | 
|  1161         case 'PASS': |  | 
|  1162           if (hasContentType) { |  | 
|  1163             containsPass = true; |  | 
|  1164           } |  | 
|  1165           break; |  | 
|  1166       } |  | 
|  1167     } |  | 
|  1168     if (hasContentType) { |  | 
|  1169       if (containsFail && containsPass) { |  | 
|  1170         DebugLogger.warning("Test had 'FAIL' and 'PASS' in stdout. ($command)"); |  | 
|  1171       } |  | 
|  1172       if (!containsFail && !containsPass) { |  | 
|  1173         DebugLogger.warning("Test had neither 'FAIL' nor 'PASS' in stdout. " |  | 
|  1174             "($command)"); |  | 
|  1175         return true; |  | 
|  1176       } |  | 
|  1177       if (containsFail) { |  | 
|  1178         return true; |  | 
|  1179       } |  | 
|  1180       assert(containsPass); |  | 
|  1181       if (exitCode != 0) { |  | 
|  1182         var message = "All tests passed, but exitCode != 0. " |  | 
|  1183             "Actual exitcode: $exitCode. " |  | 
|  1184             "($command)"; |  | 
|  1185         DebugLogger.warning(message); |  | 
|  1186         diagnostics.add(message); |  | 
|  1187       } |  | 
|  1188       return (!hasCrashed && |  | 
|  1189           exitCode != 0 && |  | 
|  1190           (!isWindows || exitCode != WHITELISTED_CONTENTSHELL_EXITCODE)); |  | 
|  1191     } |  | 
|  1192     DebugLogger.warning("Couldn't find 'Content-Type: text/plain' in output. " |  | 
|  1193         "($command)."); |  | 
|  1194     return true; |  | 
|  1195   } |  | 
|  1196 } |  | 
|  1197  |  | 
|  1198 // TODO(29869): Remove this class after verifying it isn't used. |  | 
|  1199 class HTMLBrowserCommandOutputImpl extends ContentShellCommandOutputImpl { |  | 
|  1200   HTMLBrowserCommandOutputImpl( |  | 
|  1201       Command command, |  | 
|  1202       int exitCode, |  | 
|  1203       bool timedOut, |  | 
|  1204       List<int> stdout, |  | 
|  1205       List<int> stderr, |  | 
|  1206       Duration time, |  | 
|  1207       bool compilationSkipped) |  | 
|  1208       : super(command, exitCode, timedOut, stdout, stderr, time, |  | 
|  1209             compilationSkipped); |  | 
|  1210  |  | 
|  1211   bool didFail(TestCase testCase) { |  | 
|  1212     return _getOutcome() != Expectation.pass; |  | 
|  1213   } |  | 
|  1214  |  | 
|  1215   bool get _browserTestFailure { |  | 
|  1216     // We should not need to convert back and forward. |  | 
|  1217     var output = decodeUtf8(super.stdout); |  | 
|  1218     if (output.contains("FAIL")) return true; |  | 
|  1219     return !output.contains("PASS"); |  | 
|  1220   } |  | 
|  1221 } |  | 
|  1222  |  | 
|  1223 class BrowserTestJsonResult { |  | 
|  1224   static const ALLOWED_TYPES = const [ |  | 
|  1225     'sync_exception', |  | 
|  1226     'window_onerror', |  | 
|  1227     'script_onerror', |  | 
|  1228     'window_compilationerror', |  | 
|  1229     'print', |  | 
|  1230     'message_received', |  | 
|  1231     'dom', |  | 
|  1232     'debug' |  | 
|  1233   ]; |  | 
|  1234  |  | 
|  1235   final Expectation outcome; |  | 
|  1236   final String htmlDom; |  | 
|  1237   final List<dynamic> events; |  | 
|  1238  |  | 
|  1239   BrowserTestJsonResult(this.outcome, this.htmlDom, this.events); |  | 
|  1240  |  | 
|  1241   static BrowserTestJsonResult parseFromString(String content) { |  | 
|  1242     void validate(String assertion, bool value) { |  | 
|  1243       if (!value) { |  | 
|  1244         throw "InvalidFormat sent from browser driving page: $assertion:\n\n" |  | 
|  1245             "$content"; |  | 
|  1246       } |  | 
|  1247     } |  | 
|  1248  |  | 
|  1249     try { |  | 
|  1250       var events = JSON.decode(content); |  | 
|  1251       if (events != null) { |  | 
|  1252         validate("Message must be a List", events is List); |  | 
|  1253  |  | 
|  1254         var messagesByType = <String, List<String>>{}; |  | 
|  1255         ALLOWED_TYPES.forEach((type) => messagesByType[type] = <String>[]); |  | 
|  1256  |  | 
|  1257         for (var entry in events) { |  | 
|  1258           validate("An entry must be a Map", entry is Map); |  | 
|  1259  |  | 
|  1260           var type = entry['type']; |  | 
|  1261           var value = entry['value'] as String; |  | 
|  1262           var timestamp = entry['timestamp']; |  | 
|  1263  |  | 
|  1264           validate("'type' of an entry must be a String", type is String); |  | 
|  1265           validate("'type' has to be in $ALLOWED_TYPES.", |  | 
|  1266               ALLOWED_TYPES.contains(type)); |  | 
|  1267           validate( |  | 
|  1268               "'timestamp' of an entry must be a number", timestamp is num); |  | 
|  1269  |  | 
|  1270           messagesByType[type].add(value); |  | 
|  1271         } |  | 
|  1272         validate("The message must have exactly one 'dom' entry.", |  | 
|  1273             messagesByType['dom'].length == 1); |  | 
|  1274  |  | 
|  1275         var dom = messagesByType['dom'][0]; |  | 
|  1276         if (dom.endsWith('\n')) { |  | 
|  1277           dom = '$dom\n'; |  | 
|  1278         } |  | 
|  1279  |  | 
|  1280         return new BrowserTestJsonResult( |  | 
|  1281             _getOutcome(messagesByType), dom, events as List<dynamic>); |  | 
|  1282       } |  | 
|  1283     } catch (error) { |  | 
|  1284       // If something goes wrong, we know the content was not in the correct |  | 
|  1285       // JSON format. So we can't parse it. |  | 
|  1286       // The caller is responsible for falling back to the old way of |  | 
|  1287       // determining if a test failed. |  | 
|  1288     } |  | 
|  1289  |  | 
|  1290     return null; |  | 
|  1291   } |  | 
|  1292  |  | 
|  1293   static Expectation _getOutcome(Map<String, List<String>> messagesByType) { |  | 
|  1294     occured(String type) => messagesByType[type].length > 0; |  | 
|  1295     searchForMsg(List<String> types, String message) { |  | 
|  1296       return types.any((type) => messagesByType[type].contains(message)); |  | 
|  1297     } |  | 
|  1298  |  | 
|  1299     // FIXME(kustermann,ricow): I think this functionality doesn't work in |  | 
|  1300     // test_controller.js: So far I haven't seen anything being reported on |  | 
|  1301     // "window.compilationerror" |  | 
|  1302     if (occured('window_compilationerror')) { |  | 
|  1303       return Expectation.compileTimeError; |  | 
|  1304     } |  | 
|  1305  |  | 
|  1306     if (occured('sync_exception') || |  | 
|  1307         occured('window_onerror') || |  | 
|  1308         occured('script_onerror')) { |  | 
|  1309       return Expectation.runtimeError; |  | 
|  1310     } |  | 
|  1311  |  | 
|  1312     if (messagesByType['dom'][0].contains('FAIL')) { |  | 
|  1313       return Expectation.runtimeError; |  | 
|  1314     } |  | 
|  1315  |  | 
|  1316     // We search for these messages in 'print' and 'message_received' because |  | 
|  1317     // the unittest implementation posts these messages using |  | 
|  1318     // "window.postMessage()" instead of the normal "print()" them. |  | 
|  1319  |  | 
|  1320     var isAsyncTest = searchForMsg( |  | 
|  1321         ['print', 'message_received'], 'unittest-suite-wait-for-done'); |  | 
|  1322     var isAsyncSuccess = |  | 
|  1323         searchForMsg(['print', 'message_received'], 'unittest-suite-success') || |  | 
|  1324             searchForMsg(['print', 'message_received'], 'unittest-suite-done'); |  | 
|  1325  |  | 
|  1326     if (isAsyncTest) { |  | 
|  1327       if (isAsyncSuccess) { |  | 
|  1328         return Expectation.pass; |  | 
|  1329       } |  | 
|  1330       return Expectation.runtimeError; |  | 
|  1331     } |  | 
|  1332  |  | 
|  1333     var mainStarted = |  | 
|  1334         searchForMsg(['print', 'message_received'], 'dart-calling-main'); |  | 
|  1335     var mainDone = |  | 
|  1336         searchForMsg(['print', 'message_received'], 'dart-main-done'); |  | 
|  1337  |  | 
|  1338     if (mainStarted && mainDone) { |  | 
|  1339       return Expectation.pass; |  | 
|  1340     } |  | 
|  1341     return Expectation.fail; |  | 
|  1342   } |  | 
|  1343 } |  | 
|  1344  |  | 
|  1345 class BrowserControllerTestOutcome extends CommandOutputImpl |  | 
|  1346     with UnittestSuiteMessagesMixin { |  | 
|  1347   BrowserTestOutput _result; |  | 
|  1348   Expectation _rawOutcome; |  | 
|  1349  |  | 
|  1350   factory BrowserControllerTestOutcome( |  | 
|  1351       Command command, BrowserTestOutput result) { |  | 
|  1352     String indent(String string, int numSpaces) { |  | 
|  1353       var spaces = new List.filled(numSpaces, ' ').join(''); |  | 
|  1354       return string |  | 
|  1355           .replaceAll('\r\n', '\n') |  | 
|  1356           .split('\n') |  | 
|  1357           .map((line) => "$spaces$line") |  | 
|  1358           .join('\n'); |  | 
|  1359     } |  | 
|  1360  |  | 
|  1361     String stdout = ""; |  | 
|  1362     String stderr = ""; |  | 
|  1363     Expectation outcome; |  | 
|  1364  |  | 
|  1365     var parsedResult = |  | 
|  1366         BrowserTestJsonResult.parseFromString(result.lastKnownMessage); |  | 
|  1367     if (parsedResult != null) { |  | 
|  1368       outcome = parsedResult.outcome; |  | 
|  1369     } else { |  | 
|  1370       // Old way of determining whether a test failed or passed. |  | 
|  1371       if (result.lastKnownMessage.contains("FAIL")) { |  | 
|  1372         outcome = Expectation.runtimeError; |  | 
|  1373       } else if (result.lastKnownMessage.contains("PASS")) { |  | 
|  1374         outcome = Expectation.pass; |  | 
|  1375       } else { |  | 
|  1376         outcome = Expectation.runtimeError; |  | 
|  1377       } |  | 
|  1378     } |  | 
|  1379  |  | 
|  1380     if (result.didTimeout) { |  | 
|  1381       if (result.delayUntilTestStarted != null) { |  | 
|  1382         stderr = "This test timed out. The delay until the test actually " |  | 
|  1383             "started was: ${result.delayUntilTestStarted}."; |  | 
|  1384       } else { |  | 
|  1385         stderr = "This test has not notified test.py that it started running."; |  | 
|  1386       } |  | 
|  1387     } |  | 
|  1388  |  | 
|  1389     if (parsedResult != null) { |  | 
|  1390       stdout = "events:\n${indent(prettifyJson(parsedResult.events), 2)}\n\n"; |  | 
|  1391     } else { |  | 
|  1392       stdout = "message:\n${indent(result.lastKnownMessage, 2)}\n\n"; |  | 
|  1393     } |  | 
|  1394  |  | 
|  1395     stderr = '$stderr\n\n' |  | 
|  1396         'BrowserOutput while running the test (* EXPERIMENTAL *):\n' |  | 
|  1397         'BrowserOutput.stdout:\n' |  | 
|  1398         '${indent(result.browserOutput.stdout.toString(), 2)}\n' |  | 
|  1399         'BrowserOutput.stderr:\n' |  | 
|  1400         '${indent(result.browserOutput.stderr.toString(), 2)}\n' |  | 
|  1401         '\n'; |  | 
|  1402     return new BrowserControllerTestOutcome._internal( |  | 
|  1403         command, result, outcome, encodeUtf8(stdout), encodeUtf8(stderr)); |  | 
|  1404   } |  | 
|  1405  |  | 
|  1406   BrowserControllerTestOutcome._internal( |  | 
|  1407       Command command, |  | 
|  1408       BrowserTestOutput result, |  | 
|  1409       this._rawOutcome, |  | 
|  1410       List<int> stdout, |  | 
|  1411       List<int> stderr) |  | 
|  1412       : super(command, 0, result.didTimeout, stdout, stderr, result.duration, |  | 
|  1413             false, 0) { |  | 
|  1414     _result = result; |  | 
|  1415   } |  | 
|  1416  |  | 
|  1417   Expectation result(TestCase testCase) { |  | 
|  1418     // Handle timeouts first |  | 
|  1419     if (_result.didTimeout) { |  | 
|  1420       if (testCase.configuration.runtime == Runtime.ie11) { |  | 
|  1421         // TODO(28955): See http://dartbug.com/28955 |  | 
|  1422         DebugLogger.warning("Timeout of ie11 on test ${testCase.displayName}"); |  | 
|  1423         return Expectation.ignore; |  | 
|  1424       } |  | 
|  1425       return Expectation.timeout; |  | 
|  1426     } |  | 
|  1427  |  | 
|  1428     if (hasNonUtf8) return Expectation.nonUtf8Error; |  | 
|  1429  |  | 
|  1430     // Multitests are handled specially |  | 
|  1431     if (testCase.hasRuntimeError) { |  | 
|  1432       if (_rawOutcome == Expectation.runtimeError) return Expectation.pass; |  | 
|  1433       return Expectation.missingRuntimeError; |  | 
|  1434     } |  | 
|  1435  |  | 
|  1436     return _negateOutcomeIfNegativeTest(_rawOutcome, testCase.isNegative); |  | 
|  1437   } |  | 
|  1438 } |  | 
|  1439  |  | 
|  1440 class AnalysisCommandOutputImpl extends CommandOutputImpl { |  | 
|  1441   // An error line has 8 fields that look like: |  | 
|  1442   // ERROR|COMPILER|MISSING_SOURCE|file:/tmp/t.dart|15|1|24|Missing source. |  | 
|  1443   final int ERROR_LEVEL = 0; |  | 
|  1444   final int ERROR_TYPE = 1; |  | 
|  1445   final int FILENAME = 3; |  | 
|  1446   final int FORMATTED_ERROR = 7; |  | 
|  1447  |  | 
|  1448   AnalysisCommandOutputImpl( |  | 
|  1449       Command command, |  | 
|  1450       int exitCode, |  | 
|  1451       bool timedOut, |  | 
|  1452       List<int> stdout, |  | 
|  1453       List<int> stderr, |  | 
|  1454       Duration time, |  | 
|  1455       bool compilationSkipped) |  | 
|  1456       : super(command, exitCode, timedOut, stdout, stderr, time, |  | 
|  1457             compilationSkipped, 0); |  | 
|  1458  |  | 
|  1459   Expectation result(TestCase testCase) { |  | 
|  1460     // TODO(kustermann): If we run the analyzer not in batch mode, make sure |  | 
|  1461     // that command.exitCodes matches 2 (errors), 1 (warnings), 0 (no warnings, |  | 
|  1462     // no errors) |  | 
|  1463  |  | 
|  1464     // Handle crashes and timeouts first |  | 
|  1465     if (hasCrashed) return Expectation.crash; |  | 
|  1466     if (hasTimedOut) return Expectation.timeout; |  | 
|  1467     if (hasNonUtf8) return Expectation.nonUtf8Error; |  | 
|  1468  |  | 
|  1469     // Get the errors/warnings from the analyzer |  | 
|  1470     List<String> errors = []; |  | 
|  1471     List<String> warnings = []; |  | 
|  1472     parseAnalyzerOutput(errors, warnings); |  | 
|  1473  |  | 
|  1474     // Handle errors / missing errors |  | 
|  1475     if (testCase.expectCompileError) { |  | 
|  1476       if (errors.length > 0) { |  | 
|  1477         return Expectation.pass; |  | 
|  1478       } |  | 
|  1479       return Expectation.missingCompileTimeError; |  | 
|  1480     } |  | 
|  1481     if (errors.length > 0) { |  | 
|  1482       return Expectation.compileTimeError; |  | 
|  1483     } |  | 
|  1484  |  | 
|  1485     // Handle static warnings / missing static warnings |  | 
|  1486     if (testCase.hasStaticWarning) { |  | 
|  1487       if (warnings.length > 0) { |  | 
|  1488         return Expectation.pass; |  | 
|  1489       } |  | 
|  1490       return Expectation.missingStaticWarning; |  | 
|  1491     } |  | 
|  1492     if (warnings.length > 0) { |  | 
|  1493       return Expectation.staticWarning; |  | 
|  1494     } |  | 
|  1495  |  | 
|  1496     assert(errors.length == 0 && warnings.length == 0); |  | 
|  1497     assert(!testCase.hasCompileError && !testCase.hasStaticWarning); |  | 
|  1498     return Expectation.pass; |  | 
|  1499   } |  | 
|  1500  |  | 
|  1501   void parseAnalyzerOutput(List<String> outErrors, List<String> outWarnings) { |  | 
|  1502     // Parse a line delimited by the | character using \ as an escape character |  | 
|  1503     // like:  FOO|BAR|FOO\|BAR|FOO\\BAZ as 4 fields: FOO BAR FOO|BAR FOO\BAZ |  | 
|  1504     List<String> splitMachineError(String line) { |  | 
|  1505       StringBuffer field = new StringBuffer(); |  | 
|  1506       List<String> result = []; |  | 
|  1507       bool escaped = false; |  | 
|  1508       for (var i = 0; i < line.length; i++) { |  | 
|  1509         var c = line[i]; |  | 
|  1510         if (!escaped && c == '\\') { |  | 
|  1511           escaped = true; |  | 
|  1512           continue; |  | 
|  1513         } |  | 
|  1514         escaped = false; |  | 
|  1515         if (c == '|') { |  | 
|  1516           result.add(field.toString()); |  | 
|  1517           field = new StringBuffer(); |  | 
|  1518           continue; |  | 
|  1519         } |  | 
|  1520         field.write(c); |  | 
|  1521       } |  | 
|  1522       result.add(field.toString()); |  | 
|  1523       return result; |  | 
|  1524     } |  | 
|  1525  |  | 
|  1526     for (String line in decodeUtf8(super.stderr).split("\n")) { |  | 
|  1527       if (line.length == 0) continue; |  | 
|  1528       List<String> fields = splitMachineError(line); |  | 
|  1529       // We only consider errors/warnings for files of interest. |  | 
|  1530       if (fields.length > FORMATTED_ERROR) { |  | 
|  1531         if (fields[ERROR_LEVEL] == 'ERROR') { |  | 
|  1532           outErrors.add(fields[FORMATTED_ERROR]); |  | 
|  1533         } else if (fields[ERROR_LEVEL] == 'WARNING') { |  | 
|  1534           outWarnings.add(fields[FORMATTED_ERROR]); |  | 
|  1535         } |  | 
|  1536         // OK to Skip error output that doesn't match the machine format |  | 
|  1537       } |  | 
|  1538     } |  | 
|  1539   } |  | 
|  1540 } |  | 
|  1541  |  | 
|  1542 class VmCommandOutputImpl extends CommandOutputImpl |  | 
|  1543     with UnittestSuiteMessagesMixin { |  | 
|  1544   static const DART_VM_EXITCODE_DFE_ERROR = 252; |  | 
|  1545   static const DART_VM_EXITCODE_COMPILE_TIME_ERROR = 254; |  | 
|  1546   static const DART_VM_EXITCODE_UNCAUGHT_EXCEPTION = 255; |  | 
|  1547  |  | 
|  1548   VmCommandOutputImpl(Command command, int exitCode, bool timedOut, |  | 
|  1549       List<int> stdout, List<int> stderr, Duration time, int pid) |  | 
|  1550       : super(command, exitCode, timedOut, stdout, stderr, time, false, pid); |  | 
|  1551  |  | 
|  1552   Expectation result(TestCase testCase) { |  | 
|  1553     // Handle crashes and timeouts first |  | 
|  1554     if (exitCode == DART_VM_EXITCODE_DFE_ERROR) return Expectation.dartkCrash; |  | 
|  1555     if (hasCrashed) return Expectation.crash; |  | 
|  1556     if (hasTimedOut) return Expectation.timeout; |  | 
|  1557     if (hasNonUtf8) return Expectation.nonUtf8Error; |  | 
|  1558  |  | 
|  1559     // Multitests are handled specially |  | 
|  1560     if (testCase.expectCompileError) { |  | 
|  1561       if (exitCode == DART_VM_EXITCODE_COMPILE_TIME_ERROR) { |  | 
|  1562         return Expectation.pass; |  | 
|  1563       } |  | 
|  1564       return Expectation.missingCompileTimeError; |  | 
|  1565     } |  | 
|  1566     if (testCase.hasRuntimeError) { |  | 
|  1567       // TODO(kustermann): Do we consider a "runtimeError" only an uncaught |  | 
|  1568       // exception or does any nonzero exit code fullfil this requirement? |  | 
|  1569       if (exitCode != 0) { |  | 
|  1570         return Expectation.pass; |  | 
|  1571       } |  | 
|  1572       return Expectation.missingRuntimeError; |  | 
|  1573     } |  | 
|  1574  |  | 
|  1575     // The actual outcome depends on the exitCode |  | 
|  1576     Expectation outcome; |  | 
|  1577     if (exitCode == DART_VM_EXITCODE_COMPILE_TIME_ERROR) { |  | 
|  1578       outcome = Expectation.compileTimeError; |  | 
|  1579     } else if (exitCode == DART_VM_EXITCODE_UNCAUGHT_EXCEPTION) { |  | 
|  1580       outcome = Expectation.runtimeError; |  | 
|  1581     } else if (exitCode != 0) { |  | 
|  1582       // This is a general fail, in case we get an unknown nonzero exitcode. |  | 
|  1583       outcome = Expectation.fail; |  | 
|  1584     } else { |  | 
|  1585       outcome = Expectation.pass; |  | 
|  1586     } |  | 
|  1587     outcome = _negateOutcomeIfIncompleteAsyncTest(outcome, decodeUtf8(stdout)); |  | 
|  1588     return _negateOutcomeIfNegativeTest(outcome, testCase.isNegative); |  | 
|  1589   } |  | 
|  1590 } |  | 
|  1591  |  | 
|  1592 class CompilationCommandOutputImpl extends CommandOutputImpl { |  | 
|  1593   static const DART2JS_EXITCODE_CRASH = 253; |  | 
|  1594  |  | 
|  1595   CompilationCommandOutputImpl( |  | 
|  1596       Command command, |  | 
|  1597       int exitCode, |  | 
|  1598       bool timedOut, |  | 
|  1599       List<int> stdout, |  | 
|  1600       List<int> stderr, |  | 
|  1601       Duration time, |  | 
|  1602       bool compilationSkipped) |  | 
|  1603       : super(command, exitCode, timedOut, stdout, stderr, time, |  | 
|  1604             compilationSkipped, 0); |  | 
|  1605  |  | 
|  1606   Expectation result(TestCase testCase) { |  | 
|  1607     // Handle general crash/timeout detection. |  | 
|  1608     if (hasCrashed) return Expectation.crash; |  | 
|  1609     if (hasTimedOut) { |  | 
|  1610       bool isWindows = io.Platform.operatingSystem == 'windows'; |  | 
|  1611       bool isBrowserTestCase = |  | 
|  1612           testCase.commands.any((command) => command is BrowserTestCommand); |  | 
|  1613       // TODO(26060) Dart2js batch mode hangs on Windows under heavy load. |  | 
|  1614       return (isWindows && isBrowserTestCase) |  | 
|  1615           ? Expectation.ignore |  | 
|  1616           : Expectation.timeout; |  | 
|  1617     } |  | 
|  1618     if (hasNonUtf8) return Expectation.nonUtf8Error; |  | 
|  1619  |  | 
|  1620     // Handle dart2js specific crash detection |  | 
|  1621     if (exitCode == DART2JS_EXITCODE_CRASH || |  | 
|  1622         exitCode == VmCommandOutputImpl.DART_VM_EXITCODE_COMPILE_TIME_ERROR || |  | 
|  1623         exitCode == VmCommandOutputImpl.DART_VM_EXITCODE_UNCAUGHT_EXCEPTION) { |  | 
|  1624       return Expectation.crash; |  | 
|  1625     } |  | 
|  1626  |  | 
|  1627     // Multitests are handled specially |  | 
|  1628     if (testCase.expectCompileError) { |  | 
|  1629       // Nonzero exit code of the compiler means compilation failed |  | 
|  1630       // TODO(kustermann): Do we have a special exit code in that case??? |  | 
|  1631       if (exitCode != 0) { |  | 
|  1632         return Expectation.pass; |  | 
|  1633       } |  | 
|  1634       return Expectation.missingCompileTimeError; |  | 
|  1635     } |  | 
|  1636  |  | 
|  1637     // TODO(kustermann): This is a hack, remove it |  | 
|  1638     if (testCase.hasRuntimeError && testCase.commands.length > 1) { |  | 
|  1639       // We expected to run the test, but we got an compile time error. |  | 
|  1640       // If the compilation succeeded, we wouldn't be in here! |  | 
|  1641       assert(exitCode != 0); |  | 
|  1642       return Expectation.compileTimeError; |  | 
|  1643     } |  | 
|  1644  |  | 
|  1645     Expectation outcome = |  | 
|  1646         exitCode == 0 ? Expectation.pass : Expectation.compileTimeError; |  | 
|  1647     return _negateOutcomeIfNegativeTest(outcome, testCase.isNegative); |  | 
|  1648   } |  | 
|  1649 } |  | 
|  1650  |  | 
|  1651 class KernelCompilationCommandOutputImpl extends CompilationCommandOutputImpl { |  | 
|  1652   KernelCompilationCommandOutputImpl( |  | 
|  1653       Command command, |  | 
|  1654       int exitCode, |  | 
|  1655       bool timedOut, |  | 
|  1656       List<int> stdout, |  | 
|  1657       List<int> stderr, |  | 
|  1658       Duration time, |  | 
|  1659       bool compilationSkipped) |  | 
|  1660       : super(command, exitCode, timedOut, stdout, stderr, time, |  | 
|  1661             compilationSkipped); |  | 
|  1662  |  | 
|  1663   bool get canRunDependendCommands { |  | 
|  1664     // See [BatchRunnerProcess]: 0 means success, 1 means compile-time error. |  | 
|  1665     // TODO(asgerf): When the frontend supports it, continue running even if |  | 
|  1666     //   there were compile-time errors. See kernel_sdk issue #18. |  | 
|  1667     return !hasCrashed && !timedOut && exitCode == 0; |  | 
|  1668   } |  | 
|  1669  |  | 
|  1670   Expectation result(TestCase testCase) { |  | 
|  1671     Expectation result = super.result(testCase); |  | 
|  1672     if (result.canBeOutcomeOf(Expectation.crash)) { |  | 
|  1673       return Expectation.dartkCrash; |  | 
|  1674     } else if (result.canBeOutcomeOf(Expectation.timeout)) { |  | 
|  1675       return Expectation.dartkTimeout; |  | 
|  1676     } else if (result.canBeOutcomeOf(Expectation.compileTimeError)) { |  | 
|  1677       return Expectation.dartkCompileTimeError; |  | 
|  1678     } |  | 
|  1679     return result; |  | 
|  1680   } |  | 
|  1681  |  | 
|  1682   // If the compiler was able to produce a Kernel IR file we want to run the |  | 
|  1683   // result on the Dart VM.  We therefore mark the [KernelCompilationCommand] as |  | 
|  1684   // successful. |  | 
|  1685   // => This ensures we test that the DartVM produces correct CompileTime errors |  | 
|  1686   //    as it is supposed to for our test suites. |  | 
|  1687   bool get successful => canRunDependendCommands; |  | 
|  1688 } |  | 
|  1689  |  | 
|  1690 class JsCommandlineOutputImpl extends CommandOutputImpl |  | 
|  1691     with UnittestSuiteMessagesMixin { |  | 
|  1692   JsCommandlineOutputImpl(Command command, int exitCode, bool timedOut, |  | 
|  1693       List<int> stdout, List<int> stderr, Duration time) |  | 
|  1694       : super(command, exitCode, timedOut, stdout, stderr, time, false, 0); |  | 
|  1695  |  | 
|  1696   Expectation result(TestCase testCase) { |  | 
|  1697     // Handle crashes and timeouts first |  | 
|  1698     if (hasCrashed) return Expectation.crash; |  | 
|  1699     if (hasTimedOut) return Expectation.timeout; |  | 
|  1700     if (hasNonUtf8) return Expectation.nonUtf8Error; |  | 
|  1701  |  | 
|  1702     if (testCase.hasRuntimeError) { |  | 
|  1703       if (exitCode != 0) return Expectation.pass; |  | 
|  1704       return Expectation.missingRuntimeError; |  | 
|  1705     } |  | 
|  1706  |  | 
|  1707     var outcome = exitCode == 0 ? Expectation.pass : Expectation.runtimeError; |  | 
|  1708     outcome = _negateOutcomeIfIncompleteAsyncTest(outcome, decodeUtf8(stdout)); |  | 
|  1709     return _negateOutcomeIfNegativeTest(outcome, testCase.isNegative); |  | 
|  1710   } |  | 
|  1711 } |  | 
|  1712  |  | 
|  1713 class PubCommandOutputImpl extends CommandOutputImpl { |  | 
|  1714   PubCommandOutputImpl(PubCommand command, int exitCode, bool timedOut, |  | 
|  1715       List<int> stdout, List<int> stderr, Duration time) |  | 
|  1716       : super(command, exitCode, timedOut, stdout, stderr, time, false, 0); |  | 
|  1717  |  | 
|  1718   Expectation result(TestCase testCase) { |  | 
|  1719     // Handle crashes and timeouts first |  | 
|  1720     if (hasCrashed) return Expectation.crash; |  | 
|  1721     if (hasTimedOut) return Expectation.timeout; |  | 
|  1722     if (hasNonUtf8) return Expectation.nonUtf8Error; |  | 
|  1723  |  | 
|  1724     if (exitCode == 0) { |  | 
|  1725       return Expectation.pass; |  | 
|  1726     } else if ((command as PubCommand).command == 'get') { |  | 
|  1727       return Expectation.pubGetError; |  | 
|  1728     } else { |  | 
|  1729       return Expectation.fail; |  | 
|  1730     } |  | 
|  1731   } |  | 
|  1732 } |  | 
|  1733  |  | 
|  1734 class ScriptCommandOutputImpl extends CommandOutputImpl { |  | 
|  1735   final Expectation _result; |  | 
|  1736  |  | 
|  1737   ScriptCommandOutputImpl(ScriptCommand command, this._result, |  | 
|  1738       String scriptExecutionInformation, Duration time) |  | 
|  1739       : super(command, 0, false, [], [], time, false, 0) { |  | 
|  1740     var lines = scriptExecutionInformation.split("\n"); |  | 
|  1741     diagnostics.addAll(lines); |  | 
|  1742   } |  | 
|  1743  |  | 
|  1744   Expectation result(TestCase testCase) => _result; |  | 
|  1745  |  | 
|  1746   bool get canRunDependendCommands => _result == Expectation.pass; |  | 
|  1747  |  | 
|  1748   bool get successful => _result == Expectation.pass; |  | 
|  1749 } |  | 
|  1750  |  | 
|  1751 CommandOutput createCommandOutput(Command command, int exitCode, bool timedOut, |  | 
|  1752     List<int> stdout, List<int> stderr, Duration time, bool compilationSkipped, |  | 
|  1753     [int pid = 0]) { |  | 
|  1754   if (command is ContentShellCommand) { |  | 
|  1755     return new ContentShellCommandOutputImpl( |  | 
|  1756         command, exitCode, timedOut, stdout, stderr, time, compilationSkipped); |  | 
|  1757   } else if (command is BrowserTestCommand) { |  | 
|  1758     return new HTMLBrowserCommandOutputImpl( |  | 
|  1759         command, exitCode, timedOut, stdout, stderr, time, compilationSkipped); |  | 
|  1760   } else if (command is AnalysisCommand) { |  | 
|  1761     return new AnalysisCommandOutputImpl( |  | 
|  1762         command, exitCode, timedOut, stdout, stderr, time, compilationSkipped); |  | 
|  1763   } else if (command is VmCommand) { |  | 
|  1764     return new VmCommandOutputImpl( |  | 
|  1765         command, exitCode, timedOut, stdout, stderr, time, pid); |  | 
|  1766   } else if (command is KernelCompilationCommand) { |  | 
|  1767     return new KernelCompilationCommandOutputImpl( |  | 
|  1768         command, exitCode, timedOut, stdout, stderr, time, compilationSkipped); |  | 
|  1769   } else if (command is AdbPrecompilationCommand) { |  | 
|  1770     return new VmCommandOutputImpl( |  | 
|  1771         command, exitCode, timedOut, stdout, stderr, time, pid); |  | 
|  1772   } else if (command is CompilationCommand) { |  | 
|  1773     if (command.displayName == 'precompiler' || |  | 
|  1774         command.displayName == 'app_jit') { |  | 
|  1775       return new VmCommandOutputImpl( |  | 
|  1776           command, exitCode, timedOut, stdout, stderr, time, pid); |  | 
|  1777     } |  | 
|  1778     return new CompilationCommandOutputImpl( |  | 
|  1779         command, exitCode, timedOut, stdout, stderr, time, compilationSkipped); |  | 
|  1780   } else if (command is JSCommandlineCommand) { |  | 
|  1781     return new JsCommandlineOutputImpl( |  | 
|  1782         command, exitCode, timedOut, stdout, stderr, time); |  | 
|  1783   } else if (command is PubCommand) { |  | 
|  1784     return new PubCommandOutputImpl( |  | 
|  1785         command, exitCode, timedOut, stdout, stderr, time); |  | 
|  1786   } |  | 
|  1787  |  | 
|  1788   return new CommandOutputImpl(command, exitCode, timedOut, stdout, stderr, |  | 
|  1789       time, compilationSkipped, pid); |  | 
|  1790 } |  | 
|  1791  |  | 
|  1792 /** |   229 /** | 
|  1793  * An OutputLog records the output from a test, but truncates it if |   230  * An OutputLog records the output from a test, but truncates it if | 
|  1794  * it is longer than MAX_HEAD characters, and just keeps the head and |   231  * it is longer than MAX_HEAD characters, and just keeps the head and | 
|  1795  * the last TAIL_LENGTH characters of the output. |   232  * the last TAIL_LENGTH characters of the output. | 
|  1796  */ |   233  */ | 
|  1797 class OutputLog { |   234 class OutputLog { | 
|  1798   static const int MAX_HEAD = 100 * 1024; |   235   static const int MAX_HEAD = 100 * 1024; | 
|  1799   static const int TAIL_LENGTH = 10 * 1024; |   236   static const int TAIL_LENGTH = 10 * 1024; | 
|  1800   List<int> head = <int>[]; |   237   List<int> head = <int>[]; | 
|  1801   List<int> tail; |   238   List<int> tail; | 
| (...skipping 932 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  2734       // For now, we always run dartk in batch mode. |  1171       // For now, we always run dartk in batch mode. | 
|  2735       var name = command.displayName; |  1172       var name = command.displayName; | 
|  2736       assert(name == 'dartk'); |  1173       assert(name == 'dartk'); | 
|  2737       return _getBatchRunner(name) |  1174       return _getBatchRunner(name) | 
|  2738           .runCommand(name, command, timeout, command.arguments); |  1175           .runCommand(name, command, timeout, command.arguments); | 
|  2739     } else if (command is CompilationCommand && |  1176     } else if (command is CompilationCommand && | 
|  2740         globalConfiguration.batchDart2JS) { |  1177         globalConfiguration.batchDart2JS) { | 
|  2741       return _getBatchRunner("dart2js") |  1178       return _getBatchRunner("dart2js") | 
|  2742           .runCommand("dart2js", command, timeout, command.arguments); |  1179           .runCommand("dart2js", command, timeout, command.arguments); | 
|  2743     } else if (command is AnalysisCommand && globalConfiguration.batch) { |  1180     } else if (command is AnalysisCommand && globalConfiguration.batch) { | 
|  2744       return _getBatchRunner(command.flavor) |  1181       return _getBatchRunner(command.displayName) | 
|  2745           .runCommand(command.flavor, command, timeout, command.arguments); |  1182           .runCommand(command.displayName, command, timeout, command.arguments); | 
|  2746     } else if (command is ScriptCommand) { |  1183     } else if (command is ScriptCommand) { | 
|  2747       return command.run(); |  1184       return command.run(); | 
|  2748     } else if (command is AdbPrecompilationCommand) { |  1185     } else if (command is AdbPrecompilationCommand) { | 
|  2749       assert(adbDevicePool != null); |  1186       assert(adbDevicePool != null); | 
|  2750       return adbDevicePool.acquireDevice().then((AdbDevice device) { |  1187       return adbDevicePool.acquireDevice().then((AdbDevice device) { | 
|  2751         return _runAdbPrecompilationCommand(device, command, timeout) |  1188         return _runAdbPrecompilationCommand(device, command, timeout) | 
|  2752             .whenComplete(() { |  1189             .whenComplete(() { | 
|  2753           adbDevicePool.releaseDevice(device); |  1190           adbDevicePool.releaseDevice(device); | 
|  2754         }); |  1191         }); | 
|  2755       }); |  1192       }); | 
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  2856       if (!runner._currentlyRunning) return runner; |  1293       if (!runner._currentlyRunning) return runner; | 
|  2857     } |  1294     } | 
|  2858     throw new Exception('Unable to find inactive batch runner.'); |  1295     throw new Exception('Unable to find inactive batch runner.'); | 
|  2859   } |  1296   } | 
|  2860  |  1297  | 
|  2861   Future<CommandOutput> _startBrowserControllerTest( |  1298   Future<CommandOutput> _startBrowserControllerTest( | 
|  2862       BrowserTestCommand browserCommand, int timeout) { |  1299       BrowserTestCommand browserCommand, int timeout) { | 
|  2863     var completer = new Completer<CommandOutput>(); |  1300     var completer = new Completer<CommandOutput>(); | 
|  2864  |  1301  | 
|  2865     var callback = (BrowserTestOutput output) { |  1302     var callback = (BrowserTestOutput output) { | 
|  2866       completer |  1303       completer.complete(new BrowserCommandOutputImpl(browserCommand, output)); | 
|  2867           .complete(new BrowserControllerTestOutcome(browserCommand, output)); |  | 
|  2868     }; |  1304     }; | 
|  2869  |  1305  | 
|  2870     BrowserTest browserTest; |  1306     BrowserTest browserTest; | 
|  2871     if (browserCommand is BrowserHtmlTestCommand) { |  1307     if (browserCommand is BrowserHtmlTestCommand) { | 
|  2872       browserTest = new HtmlTest(browserCommand.url, callback, timeout, |  1308       browserTest = new HtmlTest(browserCommand.url, callback, timeout, | 
|  2873           browserCommand.expectedMessages); |  1309           browserCommand.expectedMessages); | 
|  2874     } else { |  1310     } else { | 
|  2875       browserTest = new BrowserTest(browserCommand.url, callback, timeout); |  1311       browserTest = new BrowserTest(browserCommand.url, callback, timeout); | 
|  2876     } |  1312     } | 
|  2877     _getBrowserTestRunner(browserCommand.configuration).then((testRunner) { |  1313     _getBrowserTestRunner(browserCommand.configuration).then((testRunner) { | 
| (...skipping 326 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  3204     if (_globalConfiguration.listTests) { |  1640     if (_globalConfiguration.listTests) { | 
|  3205       setupForListing(testCaseEnqueuer); |  1641       setupForListing(testCaseEnqueuer); | 
|  3206     } else { |  1642     } else { | 
|  3207       setupForRunning(testCaseEnqueuer); |  1643       setupForRunning(testCaseEnqueuer); | 
|  3208     } |  1644     } | 
|  3209  |  1645  | 
|  3210     // Start enqueing all TestCases |  1646     // Start enqueing all TestCases | 
|  3211     testCaseEnqueuer.enqueueTestSuites(testSuites); |  1647     testCaseEnqueuer.enqueueTestSuites(testSuites); | 
|  3212   } |  1648   } | 
|  3213  |  1649  | 
|  3214   void freeEnqueueingStructures() { |  | 
|  3215     CommandBuilder.instance.clearCommandCache(); |  | 
|  3216   } |  | 
|  3217  |  | 
|  3218   void eventFinishedTestCase(TestCase testCase) { |  1650   void eventFinishedTestCase(TestCase testCase) { | 
|  3219     for (var listener in _eventListener) { |  1651     for (var listener in _eventListener) { | 
|  3220       listener.done(testCase); |  1652       listener.done(testCase); | 
|  3221     } |  1653     } | 
|  3222   } |  1654   } | 
|  3223  |  1655  | 
|  3224   void eventTestAdded(TestCase testCase) { |  1656   void eventTestAdded(TestCase testCase) { | 
|  3225     for (var listener in _eventListener) { |  1657     for (var listener in _eventListener) { | 
|  3226       listener.testAdded(); |  1658       listener.testAdded(); | 
|  3227     } |  1659     } | 
|  3228   } |  1660   } | 
|  3229  |  1661  | 
|  3230   void eventAllTestsKnown() { |  1662   void eventAllTestsKnown() { | 
|  3231     freeEnqueueingStructures(); |  | 
|  3232     for (var listener in _eventListener) { |  1663     for (var listener in _eventListener) { | 
|  3233       listener.allTestsKnown(); |  1664       listener.allTestsKnown(); | 
|  3234     } |  1665     } | 
|  3235   } |  1666   } | 
|  3236  |  1667  | 
|  3237   void eventAllTestsDone() { |  1668   void eventAllTestsDone() { | 
|  3238     for (var listener in _eventListener) { |  1669     for (var listener in _eventListener) { | 
|  3239       listener.allDone(); |  1670       listener.allDone(); | 
|  3240     } |  1671     } | 
|  3241     _allDone(); |  1672     _allDone(); | 
|  3242   } |  1673   } | 
|  3243 } |  1674 } | 
| OLD | NEW |