15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function $(id) {
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return document.getElementById(id);
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function createNaClEmbed(args) {
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var fallback = function(value, default_value) {
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return value !== undefined ? value : default_value;
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var embed = document.createElement('embed');
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  embed.id = args.id;
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  embed.src = args.src;
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  embed.type = fallback(args.type, 'application/x-nacl');
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // JavaScript inconsistency: this is equivalent to class=... in HTML.
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  embed.className = fallback(args.className, 'naclModule');
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  embed.width = fallback(args.width, 0);
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  embed.height = fallback(args.height, 0);
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return embed;
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function decodeURIArgs(encoded) {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var args = {};
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (encoded.length > 0) {
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var pairs = encoded.replace(/\+/g, ' ').split('&');
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var p = 0; p < pairs.length; p++) {
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var pair = pairs[p].split('=');
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (pair.length != 2) {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        throw "Malformed argument key/value pair: '" + pairs[p] + "'";
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      args[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return args;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function addDefaultsToArgs(defaults, args) {
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var key in defaults) {
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!(key in args)) {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      args[key] = defaults[key];
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Return a dictionary of arguments for the test.  These arguments are passed
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// in the query string of the main page's URL.  Any time this function is used,
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// default values should be provided for every argument.  In some cases a test
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// may be run without an expected query string (manual testing, for example.)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Careful: all the keys and values in the dictionary are strings.  You will
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// need to manually parse any non-string values you wish to use.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function getTestArguments(defaults) {
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var encoded = window.location.search.substring(1);
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var args = decodeURIArgs(encoded);
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (defaults !== undefined) {
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    addDefaultsToArgs(defaults, args);
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return args;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function exceptionToLogText(e) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (typeof e == 'object' && 'message' in e && 'stack' in e) {
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return e.message + '\n' + e.stack.toString();
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (typeof(e) == 'string') {
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return e;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return toString(e)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Logs test results to the server using URL-encoded RPC.
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Also logs the same test results locally into the DOM.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function RPCWrapper() {
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Work around how JS binds 'this'
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var this_ = this;
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // It is assumed RPC will work unless proven otherwise.
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.rpc_available = true;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set to true if any test fails.
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.ever_failed = false;
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Async calls can make it faster, but it can also change order of events.
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.async = false;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Called if URL-encoded RPC gets a 404, can't find the server, etc.
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function handleRPCFailure(name, message) {
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This isn't treated as a testing error - the test can be run without a
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // web server that understands RPC.
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this_.logLocal('RPC failure for ' + name + ': ' + message + ' - If you ' +
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   'are running this test manually, this is not a problem.',
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   'gray');
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this_.disableRPC();
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function handleRPCResponse(name, req) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (req.status == 200) {
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (req.responseText == 'Die, please') {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // TODO(eugenis): this does not end the browser process on Mac.
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        window.close();
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (req.responseText != 'OK') {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this_.logLocal('Unexpected RPC response to ' + name + ': \'' +
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       req.responseText + '\' - If you are running this test ' +
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'manually, this is not a problem.', 'gray');
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this_.disableRPC();
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      handleRPCFailure(name, req.status.toString());
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Performs a URL-encoded RPC call, given a function name and a dictionary
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // (actually just an object - it's a JS idiom) of parameters.
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function rpcCall(name, params) {
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (window.domAutomationController !== undefined) {
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Running as a Chrome browser_test.
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var msg = {type: name};
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var pname in params) {
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        msg[pname] = params[pname];
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      domAutomationController.setAutomationId(0);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      domAutomationController.send(JSON.stringify(msg));
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (this_.rpc_available) {
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Construct the URL for the RPC request.
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var args = [];
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var pname in params) {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        pvalue = params[pname];
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        args.push(encodeURIComponent(pname) + '=' + encodeURIComponent(pvalue));
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var url = '/TESTER/' + name + '?' + args.join('&');
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var req = new XMLHttpRequest();
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Async result handler
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this_.async) {
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        req.onreadystatechange = function() {
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (req.readyState == XMLHttpRequest.DONE) {
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            handleRPCResponse(name, req);
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      try {
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        req.open('GET', url, this_.async);
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        req.send();
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!this_.async) {
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          handleRPCResponse(name, req);
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } catch (err) {
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        handleRPCFailure(name, err.toString());
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Pretty prints an error into the DOM.
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.logLocalError = function(message) {
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.logLocal(message, 'red');
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.visualError();
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If RPC isn't working, disable it to stop error message spam.
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.disableRPC = function() {
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (this.rpc_available) {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.rpc_available = false;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.logLocal('Disabling RPC', 'gray');
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.startup = function() {
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(ncbray) move into test runner
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.num_passed = 0;
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.num_failed = 0;
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.num_errors = 0;
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this._log('[STARTUP]');
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.shutdown = function() {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (this.num_passed == 0 && this.num_failed == 0 && this.num_errors == 0) {
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.client_error('No tests were run. This may be a bug.');
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var full_message = '[SHUTDOWN] ';
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    full_message += this.num_passed + ' passed';
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    full_message += ', ' + this.num_failed + ' failed';
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    full_message += ', ' + this.num_errors + ' errors';
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.logLocal(full_message);
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rpcCall('Shutdown', {message: full_message, passed: !this.ever_failed});
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (this.ever_failed) {
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.localOutput.style.border = '2px solid #FF0000';
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.localOutput.style.border = '2px solid #00FF00';
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.ping = function() {
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rpcCall('Ping', {});
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.heartbeat = function() {
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rpcCall('JavaScriptIsAlive', {});
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.client_error = function(message) {
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.num_errors += 1;
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.visualError();
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var full_message = '\n[CLIENT_ERROR] ' + exceptionToLogText(message)
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The client error could have been generated by logging - be careful.
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try {
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this._log(full_message, 'red');
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } catch (err) {
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // There's not much that can be done, at this point.
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.begin = function(test_name) {
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var full_message = '[' + test_name + ' BEGIN]'
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this._log(full_message, 'blue');
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this._log = function(message, color, from_completed_test) {
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (typeof(message) != 'string') {
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      message = toString(message);
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // For event-driven tests, output may come after the test has finished.
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Display this in a special way to assist debugging.
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (from_completed_test) {
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      color = 'orange';
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      message = 'completed test: ' + message;
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.logLocal(message, color);
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rpcCall('TestLog', {message: message});
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.log = function(test_name, message, from_completed_test) {
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (message == undefined) {
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // This is a log message that is not assosiated with a test.
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // What we though was the test name is actually the message.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this._log(test_name);
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (typeof(message) != 'string') {
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        message = toString(message);
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var full_message = '[' + test_name + ' LOG] ' + message;
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this._log(full_message, 'black', from_completed_test);
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.fail = function(test_name, message, from_completed_test) {
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.num_failed += 1;
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.visualError();
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var full_message = '[' + test_name + ' FAIL] ' + message
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this._log(full_message, 'red', from_completed_test);
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.exception = function(test_name, err, from_completed_test) {
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.num_errors += 1;
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.visualError();
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var message = exceptionToLogText(err);
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var full_message = '[' + test_name + ' EXCEPTION] ' + message;
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this._log(full_message, 'purple', from_completed_test);
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.pass = function(test_name, from_completed_test) {
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.num_passed += 1;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var full_message = '[' + test_name + ' PASS]';
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this._log(full_message, 'green', from_completed_test);
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.blankLine = function() {
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this._log('');
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Allows users to log time data that will be parsed and re-logged
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for chrome perf-bot graphs / performance regression testing.
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // See: native_client/tools/process_perf_output.py
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.logTimeData = function(event, timeMS) {
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.log('NaClPerf [' + event + '] ' + timeMS + ' millisecs');
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.visualError = function() {
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Changing the color is defered until testing is done
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.ever_failed = true;
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.logLineLocal = function(text, color) {
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    text = text.replace(/\s+$/, '');
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (text == '') {
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.localOutput.appendChild(document.createElement('br'));
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var mNode = document.createTextNode(text);
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var div = document.createElement('div');
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Preserve whitespace formatting.
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      div.style['white-space'] = 'pre';
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (color != undefined) {
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        div.style.color = color;
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      div.appendChild(mNode);
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.localOutput.appendChild(div);
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.logLocal = function(message, color) {
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var lines = message.split('\n');
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < lines.length; i++) {
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.logLineLocal(lines[i], color);
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create a place in the page to output test results
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.localOutput = document.createElement('div');
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.localOutput.id = 'testresults';
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.localOutput.style.border = '2px solid #0000FF';
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.localOutput.style.padding = '10px';
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  document.body.appendChild(this.localOutput);
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// BEGIN functions for testing
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function fail(message, info, test_status) {
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var parts = [];
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (message != undefined) {
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parts.push(message);
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (info != undefined) {
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parts.push('(' + info + ')');
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var full_message = parts.join(' ');
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (test_status !== undefined) {
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // New-style test
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test_status.fail(full_message);
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Old-style test
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    throw {type: 'test_fail', message: full_message};
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function assert(condition, message, test_status) {
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!condition) {
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fail(message, toString(condition), test_status);
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This is accepted best practice for checking if an object is an array.
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function isArray(obj) {
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return Object.prototype.toString.call(obj) === '[object Array]';
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function toString(obj) {
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (typeof(obj) == 'string') {
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '\'' + obj + '\'';
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try {
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return obj.toString();
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } catch (err) {
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try {
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Arrays should do this automatically, but there is a known bug where
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // NaCl gets array types wrong.  .toString will fail on these objects.
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return obj.join(',');
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } catch (err) {
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (obj == undefined) {
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return 'undefined';
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // There is no way to create a textual representation of this object.
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return '[UNPRINTABLE]';
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Old-style, but new-style tests use it indirectly.
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// (The use of the "test" parameter indicates a new-style test.  This is a
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// temporary hack to avoid code duplication.)
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function assertEqual(a, b, message, test_status) {
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (isArray(a) && isArray(b)) {
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assertArraysEqual(a, b, message, test_status);
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (a !== b) {
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fail(message, toString(a) + ' != ' + toString(b), test_status);
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Old-style, but new-style tests use it indirectly.
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// (The use of the "test" parameter indicates a new-style test.  This is a
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// temporary hack to avoid code duplication.)
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function assertArraysEqual(a, b, message, test_status) {
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var dofail = function() {
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fail(message, toString(a) + ' != ' + toString(b), test_status);
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (a.length != b.length) {
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dofail();
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var i = 0; i < a.length; i++) {
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (a[i] !== b[i]) {
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dofail();
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Ideally there'd be some way to identify what exception was thrown, but JS
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// exceptions are fairly ad-hoc.
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(ncbray) allow manual validation of exception types?
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function assertRaises(func, message, test_status) {
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try {
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    func();
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } catch (err) {
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fail(message, 'did not raise', test_status);
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// END functions for testing
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function haltAsyncTest() {
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  throw {type: 'test_halt'};
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function begins_with(s, prefix) {
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (s.length >= prefix.length) {
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return s.substr(0, prefix.length) == prefix;
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function ends_with(s, suffix) {
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (s.length >= suffix.length) {
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return s.substr(s.length - suffix.length, suffix.length) == suffix;
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function embed_name(embed) {
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (embed.name != undefined) {
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (embed.id != undefined) {
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return embed.name + ' / ' + embed.id;
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return embed.name;
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (embed.id != undefined) {
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return embed.id;
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '[no name]';
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
469d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// Write data to the filesystem. This will only work if the browser_tester was
470d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// initialized with --output_dir.
471d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)function outputFile(name, data, onload, onerror) {
472d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  var xhr = new XMLHttpRequest();
473d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  xhr.onload = onload;
474d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  xhr.onerror = onerror;
475d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  xhr.open('POST', name, true);
476d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  xhr.send(data);
477d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
478d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
479d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Webkit Bug Workaround
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// THIS SHOULD BE REMOVED WHEN Webkit IS FIXED
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// http://code.google.com/p/nativeclient/issues/detail?id=2428
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// http://code.google.com/p/chromium/issues/detail?id=103588
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function ForcePluginLoadOnTimeout(elem, tester, timeout) {
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tester.log('Registering ForcePluginLoadOnTimeout ' +
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '(Bugs: NaCl 2428, Chrome 103588)');
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var started_loading = elem.readyState !== undefined;
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Remember that the plugin started loading - it may be unloaded by the time
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the callback fires.
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elem.addEventListener('load', function() {
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    started_loading = true;
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }, true);
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check that the plugin has at least started to load after "timeout" seconds,
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // otherwise reload the page.
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  setTimeout(function() {
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!started_loading) {
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ForceNaClPluginReload(elem, tester);
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }, timeout);
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function ForceNaClPluginReload(elem, tester) {
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (elem.readyState === undefined) {
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tester.log('WARNING: WebKit plugin-not-loading error detected; reloading.');
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    window.location.reload();
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function NaClWaiter(body_element) {
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Work around how JS binds 'this'
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var this_ = this;
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var embedsToWaitFor = [];
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // embedsLoaded contains list of embeds that have dispatched the
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 'loadend' progress event.
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.embedsLoaded = [];
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.is_loaded = function(embed) {
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < this_.embedsLoaded.length; ++i) {
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this_.embedsLoaded[i] === embed) {
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return true;
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return (embed.readyState == 4) && !this_.has_errored(embed);
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.has_errored = function(embed) {
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var msg = embed.lastError;
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return embed.lastError != undefined && embed.lastError != '';
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If an argument was passed, it is the body element for registering
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // event listeners for the 'loadend' event type.
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (body_element != undefined) {
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var eventListener = function(e) {
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (e.type == 'loadend') {
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this_.embedsLoaded.push(e.target);
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    body_element.addEventListener('loadend', eventListener, true);
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Takes an arbitrary number of arguments.
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.waitFor = function() {
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i< arguments.length; i++) {
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      embedsToWaitFor.push(arguments[i]);
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.run = function(doneCallback, pingCallback) {
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.doneCallback = doneCallback;
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.pingCallback = pingCallback;
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Wait for up to forty seconds for the nexes to load.
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(ncbray) use error handling mechanisms (when they are implemented)
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // rather than a timeout.
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.totalWait = 0;
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.maxTotalWait = 40000;
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.retryWait = 10;
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.waitForPlugins();
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.waitForPlugins = function() {
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var errored = [];
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var loaded = [];
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var waiting = [];
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < embedsToWaitFor.length; i++) {
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      try {
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var e = embedsToWaitFor[i];
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (this.has_errored(e)) {
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          errored.push(e);
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else if (this.is_loaded(e)) {
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          loaded.push(e);
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          waiting.push(e);
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } catch(err) {
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If the module is badly horked, touching lastError, etc, may except.
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errored.push(err);
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.totalWait += this.retryWait;
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (waiting.length == 0) {
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.doneCallback(loaded, errored);
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (this.totalWait >= this.maxTotalWait) {
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Timeouts are considered errors.
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.doneCallback(loaded, errored.concat(waiting));
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      setTimeout(function() { this_.waitForPlugins(); }, this.retryWait);
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Capped exponential backoff
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.retryWait += this.retryWait/2;
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Paranoid: does setTimeout like floating point numbers?
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.retryWait = Math.round(this.retryWait);
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.retryWait > 100)
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.retryWait = 100;
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Prevent the server from thinking the test has died.
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.pingCallback)
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.pingCallback();
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6117dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochfunction logLoadStatus(rpc, load_errors_are_test_errors,
6127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                       exit_cleanly_is_an_error, loaded, waiting) {
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var i = 0; i < loaded.length; i++) {
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rpc.log(embed_name(loaded[i]) + ' loaded');
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Be careful when interacting with horked nexes.
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var getCarefully = function (callback) {
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try {
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return callback();
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } catch (err) {
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return '<exception>';
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var errored = false;
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var j = 0; j < waiting.length; j++) {
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Workaround for WebKit layout bug that caused the NaCl plugin to not
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // load.  If we see that the plugin is not loaded after a timeout, we
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // forcibly reload the page, thereby triggering layout.  Re-running
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // layout should make WebKit instantiate the plugin.  NB: this could
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // make the JavaScript-based code go into an infinite loop if the
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // WebKit bug becomes deterministic or the NaCl plugin fails after
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // loading, but the browser_tester.py code will timeout the test.
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // http://code.google.com/p/nativeclient/issues/detail?id=2428
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (waiting[j].readyState == undefined) {
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // alert('Woot');  // -- for manual debugging
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rpc.log('WARNING: WebKit plugin-not-loading error detected; reloading.');
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      window.location.reload();
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw "reload NOW";
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var name = getCarefully(function(){
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return embed_name(waiting[j]);
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      });
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var ready = getCarefully(function(){
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var readyStateString =
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ['UNSENT', 'OPENED', 'HEADERS_RECEIVED', 'LOADING', 'DONE'];
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // An undefined index value will return and undefined result.
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return readyStateString[waiting[j].readyState];
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      });
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var last = getCarefully(function(){
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return toString(waiting[j].lastError);
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      });
6557dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (!exit_cleanly_is_an_error) {
6567dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      // For some tests (e.g. the NaCl SDK examples) it is OK if the test
6577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      // exits cleanly when we are waiting for it to load.
6587dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      //
6597dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      // In this case, "exiting cleanly" means returning 0 from main, or
6607dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      // calling exit(0). When this happens, the module "crashes" by posting
6617dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      // the "crash" message, but it also assigns an exitStatus.
6627dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      //
6637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      // A real crash produces an exitStatus of -1, and if the module is still
6647dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      // running its exitStatus will be undefined.
6657dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      var exitStatus = getCarefully(function() {
6667dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        if (ready === 'DONE') {
6677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          return waiting[j].exitStatus;
6687dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        } else {
6697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          return -1;
6707dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        }
6717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      });
6727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
6737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      if (exitStatus === 0) {
6747dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        continue;
6757dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      }
6767dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    }
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var msg = (name + ' did not load. Status: ' + ready + ' / ' + last);
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (load_errors_are_test_errors) {
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rpc.client_error(msg);
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      errored = true;
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rpc.log(msg);
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return errored;
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Contains the state for a single test.
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function TestStatus(tester, name, async) {
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Work around how JS binds 'this'
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var this_ = this;
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.tester = tester;
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.name = name;
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.async = async;
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.running = true;
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.log = function(message) {
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.tester.rpc.log(this.name, toString(message), !this.running);
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.pass = function() {
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO raise if not running.
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.tester.rpc.pass(this.name, !this.running);
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this._done();
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    haltAsyncTest();
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.fail = function(message) {
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.tester.rpc.fail(this.name, message, !this.running);
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this._done();
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    haltAsyncTest();
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this._done = function() {
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (this.running) {
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.running = false;
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.tester.testDone(this);
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.assert = function(condition, message) {
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert(condition, message, this);
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.assertEqual = function(a, b, message) {
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assertEqual(a, b, message, this);
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.callbackWrapper = function(callback, args) {
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // A stale callback?
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!this.running)
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (args === undefined)
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      args = [];
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try {
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      callback.apply(undefined, args);
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } catch (err) {
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (typeof err == 'object' && 'type' in err) {
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (err.type == 'test_halt') {
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // New-style test
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // If we get this exception, we can assume any callbacks or next
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // tests have already been scheduled.
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return;
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else if (err.type == 'test_fail') {
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // Old-style test
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // A special exception that terminates the test with a failure
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.tester.rpc.fail(this.name, err.message, !this.running);
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this._done();
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return;
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // This is not a special type of exception, it is an error.
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.tester.rpc.exception(this.name, err, !this.running);
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this._done();
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // A normal exit.  Should we move on to the next test?
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Async tests do not move on without an explicit pass.
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!this.async) {
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.tester.rpc.pass(this.name);
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this._done();
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Async callbacks should be wrapped so the tester can catch unexpected
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // exceptions.
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.wrap = function(callback) {
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return function() {
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this_.callbackWrapper(callback, arguments);
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.setTimeout = function(callback, time) {
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    setTimeout(this.wrap(callback), time);
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.waitForCallback = function(callbackName, expectedCalls) {
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.log('Waiting for ' + expectedCalls + ' invocations of callback: '
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               + callbackName);
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var gotCallbacks = 0;
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Deliberately global - this is what the nexe expects.
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(ncbray): consider returning this function, so the test has more
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // flexibility. For example, in the test one could count to N
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // using a different callback before calling _this_ callback, and
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // continuing the test. Also, consider calling user-supplied callback
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // when done waiting.
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    window[callbackName] = this.wrap(function() {
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++gotCallbacks;
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this_.log('Received callback ' + gotCallbacks);
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (gotCallbacks == expectedCalls) {
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this_.log("Done waiting");
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this_.pass();
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // HACK
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        haltAsyncTest();
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    });
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // HACK if this function is used in a non-async test, make sure we don't
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // spuriously pass.  Throwing this exception forces us to behave like an
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // async test.
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    haltAsyncTest();
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This function takes an array of messages and asserts that the nexe
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // calls PostMessage with each of these messages, in order.
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Arguments:
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   plugin - The DOM object for the NaCl plugin
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   messages - An array of expected responses
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   callback - An optional callback function that takes the current message
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //              string as an argument
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.expectMessageSequence = function(plugin, messages, callback) {
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.assert(messages.length > 0, 'Must provide at least one message');
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var local_messages = messages.slice();
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var listener = function(message) {
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (message.data.indexOf('@:') == 0) {
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // skip debug messages
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this_.log('DEBUG: ' + message.data.substr(2));
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this_.assertEqual(message.data, local_messages.shift());
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (callback !== undefined) {
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          callback(message.data);
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (local_messages.length == 0) {
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this_.pass();
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this_.expectEvent(plugin, 'message', listener);
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.expectEvent(plugin, 'message', listener);
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.expectEvent = function(src, event_type, listener) {
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var wrapper = this.wrap(function(e) {
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      src.removeEventListener(event_type, wrapper, false);
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      listener(e);
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    });
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    src.addEventListener(event_type, wrapper, false);
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function Tester(body_element) {
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Work around how JS binds 'this'
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var this_ = this;
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The tests being run.
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var tests = [];
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.rpc = new RPCWrapper();
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.waiter = new NaClWaiter(body_element);
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var load_errors_are_test_errors = true;
8587dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  var exit_cleanly_is_an_error = true;
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var parallel = false;
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // BEGIN public interface
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.loadErrorsAreOK = function() {
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    load_errors_are_test_errors = false;
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8707dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  this.exitCleanlyIsOK = function() {
8717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    exit_cleanly_is_an_error = false;
8727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  };
8737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.log = function(message) {
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.rpc.log(message);
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If this kind of test exits cleanly, it passes
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.addTest = function(name, testFunction) {
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tests.push({name: name, callback: testFunction, async: false});
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This kind of test does not pass until "pass" is explicitly called.
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.addAsyncTest = function(name, testFunction) {
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tests.push({name: name, callback: testFunction, async: true});
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.run = function() {
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.rpc.startup();
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.startHeartbeat();
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.waiter.run(
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function(loaded, waiting) {
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var errored = logLoadStatus(this_.rpc, load_errors_are_test_errors,
8947dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                                    exit_cleanly_is_an_error,
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    loaded, waiting);
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (errored) {
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this_.rpc.blankLine();
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this_.rpc.log('A nexe load error occured, aborting testing.');
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this_._done();
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this_.startTesting();
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function() {
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this_.rpc.ping();
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    );
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.runParallel = function() {
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parallel = true;
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.run();
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Takes an arbitrary number of arguments.
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.waitFor = function() {
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i< arguments.length; i++) {
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.waiter.waitFor(arguments[i]);
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // END public interface
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.startHeartbeat = function() {
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var rpc = this.rpc;
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var heartbeat = function() {
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rpc.heartbeat();
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      setTimeout(heartbeat, 500);
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    heartbeat();
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.launchTest = function(testIndex) {
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var testDecl = tests[testIndex];
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var currentTest = new TestStatus(this, testDecl.name, testDecl.async);
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    setTimeout(currentTest.wrap(function() {
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this_.rpc.blankLine();
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this_.rpc.begin(currentTest.name);
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      testDecl.callback(currentTest);
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }), 0);
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this._done = function() {
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.rpc.blankLine();
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.rpc.shutdown();
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.startTesting = function() {
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tests.length == 0) {
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // No tests specified.
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this._done();
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.testCount = 0;
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (parallel) {
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Launch all tests.
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < tests.length; i++) {
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.launchTest(i);
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Launch the first test.
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.launchTest(0);
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.testDone = function(test) {
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.testCount += 1;
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (this.testCount < tests.length) {
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!parallel) {
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Move on to the next test if they're being run one at a time.
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.launchTest(this.testCount);
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this._done();
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
981