Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(542)

Unified Diff: tools/testing/dart/test_runner.dart

Issue 11586012: Call DumpRenderTree directly from test.dart instead of using the drt-trampoline.py indirection (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/testing/dart/test_runner.dart
diff --git a/tools/testing/dart/test_runner.dart b/tools/testing/dart/test_runner.dart
index 335766a1bc63f1f59a1bd9726241f09026991a86..910170cf70b8295716b286ed4496baa360bb1109 100644
--- a/tools/testing/dart/test_runner.dart
+++ b/tools/testing/dart/test_runner.dart
@@ -32,11 +32,14 @@ class Command {
/** Command line arguments to the executable. */
List<String> arguments;
+
+ /** Environment for the command */
+ Map<String,String> environment;
/** The actual command line that will be executed. */
String commandLine;
- Command(this.executable, this.arguments) {
+ Command(this.executable, this.arguments, [this.environment = null]) {
if (Platform.operatingSystem == 'windows') {
// Windows can't handle the first command if it is a .bat file or the like
// with the slashes going the other direction.
@@ -49,6 +52,8 @@ class Command {
String toString() => commandLine;
Future<bool> get outputIsUpToDate => new Future.immediate(false);
+ Path get expectedOutputFile => null;
+ bool get isPixelTest => false;
ricow1 2012/12/17 13:45:57 file a bug for us to make this nicer by using poly
kustermann 2012/12/17 14:00:47 Done. Issue 7453
}
class Dart2JsCommand extends Command {
@@ -105,6 +110,55 @@ class Dart2JsCommand extends Command {
}
}
+class DumpRenderTreeCommand extends Command {
+ /**
+ * If [expectedOutputPath] is set, the output of DumpRenderTree is compared
+ * with the content of [expectedOutputPath].
+ * This is used for example for pixel tests, where [expectedOutputPath] points
+ * to a *png file.
+ */
+ Path expectedOutputPath;
+
+ DumpRenderTreeCommand(String executable,
+ String htmlFile,
+ List<String> options,
+ List<String> dartFlags,
+ Uri packageRootUri,
+ Path this.expectedOutputPath)
+ : super(executable,
+ _getArguments(options, htmlFile),
+ _getEnvironment(dartFlags, packageRootUri));
+
+ static Map _getEnvironment(List<String> dartFlags, Uri packageRootUri) {
+ var needDartFlags = dartFlags != null && dartFlags.length > 0;
+ var needDartPackageRoot = packageRootUri != null;
+
+ var env = null;
+ if (needDartFlags || needDartPackageRoot) {
+ var env = new Map.from(Platform.environment);
+ if (needDartFlags) {
+ env['DART_FLAGS'] = Strings.join(dartFlags, " ");
+ }
+ if (needDartPackageRoot) {
+ env['DART_PACKAGE_ROOT'] = packageRootUri.toString();
+ }
+ }
+
+ return env;
+ }
+
+ static List<String> _getArguments(List<String> options, String htmlFile) {
+ var arguments = new List.from(options);
+ arguments.add(htmlFile);
+ return arguments;
+ }
+
+ Path get expectedOutputFile => expectedOutputPath;
+ bool get isPixelTest => (expectedOutputFile != null &&
+ expectedOutputFile.filename.endsWith(".png"));
+}
+
+
/**
* TestCase contains all the information needed to run a test and evaluate
* its output. Running a test involves starting a separate process, with
@@ -317,8 +371,8 @@ abstract class CommandOutput {
int exitCode,
bool incomplete,
bool timedOut,
- List<String> stdout,
- List<String> stderr,
+ List<int> stdout,
+ List<int> stderr,
Duration time,
bool compilationSkipped) {
return new CommandOutputImpl.fromCase(testCase,
@@ -332,6 +386,8 @@ abstract class CommandOutput {
compilationSkipped);
}
+ Command get command;
+
bool get incomplete;
String get result;
@@ -350,9 +406,9 @@ abstract class CommandOutput {
int get exitCode;
- List<String> get stdout;
+ List<int> get stdout;
- List<String> get stderr;
+ List<int> get stderr;
List<String> get diagnostics;
@@ -360,6 +416,7 @@ abstract class CommandOutput {
}
class CommandOutputImpl implements CommandOutput {
+ Command command;
TestCase testCase;
int exitCode;
@@ -368,8 +425,8 @@ class CommandOutputImpl implements CommandOutput {
bool timedOut;
bool failed = false;
- List<String> stdout;
- List<String> stderr;
+ List<int> stdout;
+ List<int> stderr;
Duration time;
List<String> diagnostics;
bool compilationSkipped;
@@ -389,12 +446,12 @@ class CommandOutputImpl implements CommandOutput {
// Don't call this constructor, call CommandOutput.fromCase() to
// get a new TestOutput instance.
CommandOutputImpl(TestCase this.testCase,
- Command command,
+ Command this.command,
int this.exitCode,
bool this.incomplete,
bool this.timedOut,
- List<String> this.stdout,
- List<String> this.stderr,
+ List<int> this.stdout,
+ List<int> this.stderr,
Duration this.time,
bool this.compilationSkipped) {
testCase.commandOutputs[command] = this;
@@ -405,8 +462,8 @@ class CommandOutputImpl implements CommandOutput {
int exitCode,
bool incomplete,
bool timedOut,
- List<String> stdout,
- List<String> stderr,
+ List<int> stdout,
+ List<int> stderr,
Duration time,
bool compilationSkipped) {
if (testCase is BrowserTestCase) {
@@ -504,9 +561,10 @@ class BrowserCommandOutputImpl extends CommandOutputImpl {
// and the virtual framebuffer X server didn't hook up, or DRT crashed with
// a core dump. Sometimes DRT crashes after it has set the stdout to PASS,
// so we have to do this check first.
- for (String line in super.stderr) {
+ var stderrLines = new String.fromCharCodes(super.stderr).split("\n");
+ for (String line in stderrLines) {
if (line.contains('Gtk-WARNING **: cannot open display: :99') ||
- line.contains('Failed to run command. return code=1')) {
+ line.contains('Failed to run command. return code=1')) {
// If we get the X server error, or DRT crashes with a core dump, retry
// the test.
if ((testCase as BrowserTestCase).numRetries > 0) {
@@ -516,23 +574,114 @@ class BrowserCommandOutputImpl extends CommandOutputImpl {
}
}
- // Browser tests fail unless stdout contains
- // 'Content-Type: text/plain' followed by 'PASS'.
- bool has_content_type = false;
- for (String line in super.stdout) {
- switch (line) {
- case 'Content-Type: text/plain':
- has_content_type = true;
- break;
-
- case 'PASS':
- if (has_content_type) {
- return (exitCode != 0 && !hasCrashed);
+ if (command.expectedOutputFile != null) {
+ // We are either doing a pixel test or a layout test with DumpRenderTree
+
+ int findBytes(List<int> data, List<int> pattern, [int startPos=0]) {
+ // TODO(kustermann): Use one of the fast string-matching algorithms!
+ for (int i=startPos; i < (data.length-pattern.length); i++) {
+ bool found = true;
+ for (int j=0; j<pattern.length; j++) {
+ if (data[i+j] != pattern[j]) {
+ found = false;
+ }
}
- break;
+ if (found) {
+ return i;
+ }
+ }
+ return -1;
}
+
+ bool areByteArraysEqual(List<int> buffer1,
+ int buffer1Offset,
+ List<int> buffer2,
+ int buffer2Offset,
+ int count) {
+ if ((buffer1Offset + count) > buffer1.length ||
+ (buffer2Offset + count) > buffer2.length) {
+ return false;
+ }
+
+ for (var i=0; i<count; i++) {
+ if (buffer1[buffer1Offset + i] != buffer2[buffer2Offset + i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ var stdout = testCase.commandOutputs[command].stdout;
+ var file = new File.fromPath(command.expectedOutputFile);
+ if (file.existsSync()) {
+ var bytesContentLength = "Content-Length:".charCodes;
+ var bytesNewLine = "\n".charCodes;
+ var bytesEOF = "#EOF\n".charCodes;
+
+ var expectedContent = file.readAsBytesSync();
+
+ /*
+ * The output of DumpRenderTree is different for pixel tests than for
+ * layout tests.
+ *
+ * On a pixel test, the DRT output has the following format
+ * ......
+ * ......
+ * Content-Length: ...\n
+ * <*png data>
+ * #EOF\n
+ * So we need to get the byte-range of the png data first before
+ * comparing it with the content of the expected output file.
+ *
+ * On a layout tests, the DRT output is directly compared with the
+ * content of the expected output directly.
+ */
+ if (command.expectedOutputFile.filename.endsWith(".png")) {
+ var startOfContentLength = findBytes(stdout, bytesContentLength);
+ if (startOfContentLength >= 0) {
+ var newLineAfterContentLength = findBytes(stdout,
+ bytesNewLine,
+ startOfContentLength);
+ if (newLineAfterContentLength > 0) {
+ var startPosition = newLineAfterContentLength +
+ bytesNewLine.length;
+ var endPosition = stdout.length - bytesEOF.length;
+
+ return !areByteArraysEqual(expectedContent,
+ 0,
+ stdout,
+ startPosition,
+ endPosition - startPosition);
+ }
+ }
+ return true;
+ } else {
+ return !areByteArraysEqual(expectedContent, 0,
+ stdout, 0,
+ stdout.length);
+ }
+ }
+ return true;
+ } else {
+ // Browser tests fail unless stdout contains
+ // 'Content-Type: text/plain' followed by 'PASS'.
+ bool has_content_type = false;
+ var stdoutLines = new String.fromCharCodes(super.stdout).split("\n");
+ for (String line in stdoutLines) {
+ switch (line) {
+ case 'Content-Type: text/plain':
+ has_content_type = true;
+ break;
+
+ case 'PASS':
+ if (has_content_type) {
+ return (exitCode != 0 && !hasCrashed);
+ }
+ break;
+ }
+ }
+ return true;
}
- return true;
}
}
@@ -582,7 +731,8 @@ class AnalysisCommandOutputImpl extends CommandOutputImpl {
List<String> staticWarnings = [];
// Read the returned list of errors and stuff them away.
- for (String line in super.stderr) {
+ var stderrLines = new String.fromCharCodes(super.stderr).split("\n");
+ for (String line in stderrLines) {
if (line.length == 0) continue;
List<String> fields = splitMachineError(line);
if (fields[ERROR_LEVEL] == 'ERROR') {
@@ -731,8 +881,9 @@ class RunningProcess {
bool timedOut = false;
Date startTime;
Timer timeoutTimer;
- List<String> stdout;
- List<String> stderr;
+ List<int> stdout;
+ List<int> stderr;
+ List<String> notifications;
bool compilationSkipped;
bool allowRetries;
@@ -746,6 +897,8 @@ class RunningProcess {
* Called when all commands are executed.
*/
void testComplete(CommandOutput lastCommandOutput) {
+ var command = lastCommandOutput.command;
+
if (timeoutTimer != null) {
timeoutTimer.cancel();
}
@@ -753,8 +906,14 @@ class RunningProcess {
&& testCase.configuration['verbose'] != null
&& testCase.configuration['verbose']) {
print(testCase.displayName);
- for (var line in lastCommandOutput.stderr) print(line);
- for (var line in lastCommandOutput.stdout) print(line);
+ print('');
+ if (notifications.length > 0) {
+ print("Notifications:");
+ for (var line in notifications) {
+ print(notifications);
+ }
+ print('');
+ }
}
if (allowRetries && testCase.usesWebDriver
&& lastCommandOutput.unexpectedOutput
@@ -796,14 +955,14 @@ class RunningProcess {
testComplete(createCommandOutput(command, exitCode, false));
} else if (exitCode != 0) {
// One of the steps failed.
- stderr.add('test.dart: Compilation failed$suffix, exit code $exitCode\n');
+ notifications.add('test.dart: Compilation failed$suffix, '
+ 'exit code $exitCode\n');
testComplete(createCommandOutput(command, exitCode, true));
} else {
createCommandOutput(command, exitCode, true);
// One compilation step successfully completed, move on to the
// next step.
- stderr.add('test.dart: Compilation finished $suffix\n');
- stdout.add('test.dart: Compilation finished $suffix\n');
+ notifications.add('test.dart: Compilation finished $suffix\n\n');
if (currentStep == totalSteps - 1 && testCase.usesWebDriver &&
!testCase.configuration['noBatch']) {
// Note: processQueue will always be non-null for runtime == ie9, ie10,
@@ -826,6 +985,7 @@ class RunningProcess {
CommandOutput createCommandOutput(Command command,
int exitCode,
bool incomplete) {
+ // FIXME(kustermann): should we also include this.notifications ??
var commandOutput = new CommandOutput.fromCase(
testCase,
command,
@@ -841,22 +1001,25 @@ class RunningProcess {
}
void resetLocalOutputInformation() {
- stdout = new List<String>();
- stderr = new List<String>();
+ stdout = new List<int>();
+ stderr = new List<int>();
+ notifications = new List<String>();
compilationSkipped = false;
}
- VoidFunction makeReadHandler(StringInputStream source,
- List<String> destination) {
- void handler () {
- if (source.closed) return; // TODO(whesse): Remove when bug is fixed.
- var line = source.readLine();
- while (null != line) {
- destination.add(line);
- line = source.readLine();
+ void drainStream(InputStream source, List<int> destination) {
+ void onDataHandler () {
+ if (source.closed) {
+ return; // TODO(whesse): Remove when bug is fixed.
+ }
+ var data = source.read();
+ while (data != null) {
+ destination.addAll(data);
+ data = source.read();
}
}
- return handler;
+ source.onData = onDataHandler;
+ source.onClosed = onDataHandler;
}
void start() {
@@ -874,14 +1037,20 @@ class RunningProcess {
command.outputIsUpToDate.then((bool isUpToDate) {
if (isUpToDate) {
- stdout.add("Skipped compilation because the old output is "
- "still up to date!");
+ notifications.add("Skipped compilation because the old output is "
+ "still up to date!");
compilationSkipped = true;
commandComplete(command, 0);
} else {
ProcessOptions options = new ProcessOptions();
- options.environment =
- new Map<String, String>.from(Platform.environment);
+ if (command.environment != null) {
+ options.environment =
+ new Map<String, String>.from(command.environment);
+ } else {
+ options.environment =
+ new Map<String, String>.from(Platform.environment);
+ }
+
options.environment['DART_CONFIGURATION'] =
TestUtils.configurationDir(testCase.configuration);
Future processFuture = Process.start(command.executable,
@@ -890,12 +1059,8 @@ class RunningProcess {
processFuture.then((Process p) {
process = p;
process.onExit = processExitHandler;
- var stdoutStringStream = new StringInputStream(process.stdout);
- var stderrStringStream = new StringInputStream(process.stderr);
- stdoutStringStream.onLine =
- makeReadHandler(stdoutStringStream, stdout);
- stderrStringStream.onLine =
- makeReadHandler(stderrStringStream, stderr);
+ drainStream(process.stdout, stdout);
+ drainStream(process.stderr, stderr);
if (timeoutTimer == null) {
// Create one timeout timer when starting test case, remove it at
// the end.
@@ -951,8 +1116,8 @@ class BatchRunnerProcess {
StringInputStream _stderrStream;
TestCase _currentTest;
- List<String> _testStdout;
- List<String> _testStderr;
+ List<int> _testStdout;
+ List<int> _testStderr;
String _status;
bool _stdoutDrained = false;
bool _stderrDrained = false;
@@ -1029,9 +1194,15 @@ class BatchRunnerProcess {
_stdoutDrained = false;
_stderrDrained = false;
_ignoreStreams = new MutableValue<bool>(false); // Captured by closures.
- _stdoutStream.onLine = _readStdout(_stdoutStream, _testStdout);
- _stderrStream.onLine = _readStderr(_stderrStream, _testStderr);
+ _readStdout(_stdoutStream, _testStdout);
+ _readStderr(_stderrStream, _testStderr);
_timer = new Timer(testCase.timeout * 1000, _timeoutHandler);
+
+ if (testCase.commands.last.environment != null) {
+ print("Warning: command.environment != null, but we don't support custom "
+ "environments for batch runner tests!");
+ }
+
var line = _createArgumentsLine(testCase.batchTestArguments);
_process.stdin.onError = (err) {
print('Error on batch runner input stream stdin');
@@ -1081,9 +1252,9 @@ class BatchRunnerProcess {
if (_stderrDrained) _reportResult();
}
- VoidFunction _readStdout(StringInputStream stream, List<String> buffer) {
+ void _readStdout(StringInputStream stream, List<int> buffer) {
var ignoreStreams = _ignoreStreams; // Capture this mutable object.
- void reader() {
+ void onLineHandler() {
if (ignoreStreams.value) {
while (stream.readLine() != null) {
// Do nothing.
@@ -1100,7 +1271,7 @@ class BatchRunnerProcess {
} else if (line.startsWith('>>> ')) {
throw new Exception('Unexpected command from dartc batch runner.');
} else {
- buffer.add(line);
+ buffer.addAll("$line\n".charCodes);
}
line = stream.readLine();
}
@@ -1109,12 +1280,12 @@ class BatchRunnerProcess {
_stdoutDone();
}
}
- return reader;
+ stream.onLine = onLineHandler;
}
- VoidFunction _readStderr(StringInputStream stream, List<String> buffer) {
+ void _readStderr(StringInputStream stream, List<int> buffer) {
var ignoreStreams = _ignoreStreams; // Capture this mutable object.
- void reader() {
+ void onLineHandler() {
if (ignoreStreams.value) {
while (stream.readLine() != null) {
// Do nothing.
@@ -1127,12 +1298,12 @@ class BatchRunnerProcess {
if (line.startsWith('>>> EOF STDERR')) {
_stderrDone();
} else {
- buffer.add(line);
+ buffer.addAll("$line\n".charCodes);
}
line = stream.readLine();
}
}
- return reader;
+ stream.onLine = onLineHandler;
}
ExitCodeEvent makeExitHandler(String status) {

Powered by Google App Engine
This is Rietveld 408576698