function TestStatus(tester, name, async) {
  // Work around how JS binds 'this'
  var this_ = this;
  this.tester = tester;
  this.name = name;
  this.async = async;
  this.running = true;

  this.log = function(message) {
    this.tester.rpc.log(this.name, toString(message), !this.running);
  }

  this.pass = function() {
    // TODO raise if not running.
    this.tester.rpc.pass(this.name, !this.running);
    this._done();
    haltAsyncTest();
  }

  this.fail = function(message) {
    this.tester.rpc.fail(this.name, message, !this.running);
    this._done();
    haltAsyncTest();
  }

  this._done = function() {
    if (this.running) {
      this.running = false;
      this.tester.testDone(this);
    }
  }

  this.assert = function(condition, message) {
    assert(condition, message, this);
  }

  this.assertEqual = function(a, b, message) {
    assertEqual(a, b, message, this);
  }

  this.assertRegexMatches = function(a, b, message) {
    assertRegexMatches(a, b, message, this);
  }

  this.callbackWrapper = function(callback, args) {
    // A stale callback?
    if (!this.running)
      return;

    if (args === undefined)
      args = [];

    try {
      callback.apply(undefined, args);
    } catch (err) {
      if (typeof err == 'object' && 'type' in err) {
        if (err.type == 'test_halt') {
          // New-style test
          // If we get this exception, we can assume any callbacks or next
          // tests have already been scheduled.
          return;
        } else if (err.type == 'test_fail') {
          // Old-style test
          // A special exception that terminates the test with a failure
          this.tester.rpc.fail(this.name, err.message, !this.running);
          this._done();
          return;
        }
      }
      // This is not a special type of exception, it is an error.
      this.tester.rpc.exception(this.name, err, !this.running);
      this._done();
      return;
    }

    // A normal exit.  Should we move on to the next test?
    // Async tests do not move on without an explicit pass.
    if (!this.async) {
      this.tester.rpc.pass(this.name);
      this._done();
    }
  }

  // Async callbacks should be wrapped so the tester can catch unexpected
  // exceptions.
  this.wrap = function(callback) {
    return function() {
      this_.callbackWrapper(callback, arguments);
    };
  }

  this.setTimeout = function(callback, time) {
    setTimeout(this.wrap(callback), time);
  }

  this.waitForCallback = function(callbackName, expectedCalls) {
    this.log('Waiting for ' + expectedCalls + ' invocations of callback: '
               + callbackName);
    var gotCallbacks = 0;

    // Deliberately global - this is what the nexe expects.
    // TODO(ncbray): consider returning this function, so the test has more
    // flexibility. For example, in the test one could count to N
    // using a different callback before calling _this_ callback, and
    // continuing the test. Also, consider calling user-supplied callback
    // when done waiting.
    window[callbackName] = this.wrap(function() {
      ++gotCallbacks;
      this_.log('Received callback ' + gotCallbacks);
      if (gotCallbacks == expectedCalls) {
        this_.log("Done waiting");
        this_.pass();
      } else {
        // HACK
        haltAsyncTest();
      }
    });

    // HACK if this function is used in a non-async test, make sure we don't
    // spuriously pass.  Throwing this exception forces us to behave like an
    // async test.
    haltAsyncTest();
  }

  // This function takes an array of messages and asserts that the nexe
  // calls PostMessage with each of these messages, in order.
  // Arguments:
  //   plugin - The DOM object for the NaCl plugin
  //   messages - An array of expected responses
  //   callback - An optional callback function that takes the current message
  //              string as an argument
  this.expectMessageSequence = function(plugin, messages, callback) {
    this.assert(messages.length > 0, 'Must provide at least one message');
    var local_messages = messages.slice();
    var listener = function(message) {
      if (message.data.indexOf('@:') == 0) {
        // skip debug messages
        this_.log('DEBUG: ' + message.data.substr(2));
      } else {
        this_.assertEqual(message.data, local_messages.shift());
        if (callback !== undefined) {
          callback(message.data);
        }
      }
      if (local_messages.length == 0) {
        this_.pass();
      } else {
        this_.expectEvent(plugin, 'message', listener);
      }
    }
    this.expectEvent(plugin, 'message', listener);
  }

  this.expectEvent = function(src, event_type, listener) {
    var wrapper = this.wrap(function(e) {
      src.removeEventListener(event_type, wrapper, false);
      listener(e);
    });
    src.addEventListener(event_type, wrapper, false);
  }
}
// Contains the state for a single test.
Contains the state for a single test.
Contains the state for a single test .
function TestStatus(tester, name, async) {
  var this_ = this;
  this.tester = tester;
  this.name = name;
  this.async = async;
  this.running = true;
  this.log = function(message) {
    this.tester.rpc.log(this.name, toString(message), !this.running);
  }
  this.pass = function() {
    this.tester.rpc.pass(this.name, !this.running);
    this._done();
    haltAsyncTest();
  }
  this.fail = function(message) {
    this.tester.rpc.fail(this.name, message, !this.running);
    this._done();
    haltAsyncTest();
  }
  this._done = function() {
    if (this.running) {
      this.running = false;
      this.tester.testDone(this);
    }
  }
  this.assert = function(condition, message) {
    assert(condition, message, this);
  }
  this.assertEqual = function(a, b, message) {
    assertEqual(a, b, message, this);
  }
  this.assertRegexMatches = function(a, b, message) {
    assertRegexMatches(a, b, message, this);
  }
  this.callbackWrapper = function(callback, args) {
    if (!this.running)
      return;
    if (args === undefined)
      args = [];
    try {
      callback.apply(undefined, args);
    } catch (err) {
      if (typeof err == 'object' && 'type' in err) {
        if (err.type == 'test_halt') {
          return;
        } else if (err.type == 'test_fail') {
          this.tester.rpc.fail(this.name, err.message, !this.running);
          this._done();
          return;
        }
      }
      this.tester.rpc.exception(this.name, err, !this.running);
      this._done();
      return;
    }
    if (!this.async) {
      this.tester.rpc.pass(this.name);
      this._done();
    }
  }
  this.wrap = function(callback) {
    return function() {
      this_.callbackWrapper(callback, arguments);
    };
  }
  this.setTimeout = function(callback, time) {
    setTimeout(this.wrap(callback), time);
  }
  this.waitForCallback = function(callbackName, expectedCalls) {
    this.log('Waiting for ' + expectedCalls + ' invocations of callback: '
               + callbackName);
    var gotCallbacks = 0;
    window[callbackName] = this.wrap(function() {
      ++gotCallbacks;
      this_.log('Received callback ' + gotCallbacks);
      if (gotCallbacks == expectedCalls) {
        this_.log("Done waiting");
        this_.pass();
      } else {
        haltAsyncTest();
      }
    });
     if this function is used in a non-async test, make sure we don't
    haltAsyncTest();
  }
  this.expectMessageSequence = function(plugin, messages, callback) {
    this.assert(messages.length > 0, 'Must provide at least one message');
    var local_messages = messages.slice();
    var listener = function(message) {
      if (message.data.indexOf('@:') == 0) {
        this_.log('DEBUG: ' + message.data.substr(2));
      } else {
        this_.assertEqual(message.data, local_messages.shift());
        if (callback !== undefined) {
          callback(message.data);
        }
      }
      if (local_messages.length == 0) {
        this_.pass();
      } else {
        this_.expectEvent(plugin, 'message', listener);
      }
    }
    this.expectEvent(plugin, 'message', listener);
  }
  this.expectEvent = function(src, event_type, listener) {
    var wrapper = this.wrap(function(e) {
      src.removeEventListener(event_type, wrapper, false);
      listener(e);
    });
    src.addEventListener(event_type, wrapper, false);
  }
}
function TestStatus ( tester , name , async ) { var this_ = this ; this . tester = tester ; this . name = name ; this . async = async ; this . running = true ; this . log = function ( message ) { this . tester . rpc . log ( this . name , toString ( message ) , ! this . running ) ; } this . pass = function ( ) { this . tester . rpc . pass ( this . name , ! this . running ) ; this . _done ( ) ; haltAsyncTest ( ) ; } this . fail = function ( message ) { this . tester . rpc . fail ( this . name , message , ! this . running ) ; this . _done ( ) ; haltAsyncTest ( ) ; } this . _done = function ( ) { if ( this . running ) { this . running = false ; this . tester . testDone ( this ) ; } } this . assert = function ( condition , message ) { assert ( condition , message , this ) ; } this . assertEqual = function ( a , b , message ) { assertEqual ( a , b , message , this ) ; } this . assertRegexMatches = function ( a , b , message ) { assertRegexMatches ( a , b , message , this ) ; } this . callbackWrapper = function ( callback , args ) { if ( ! this . running ) return ; if ( args === undefined ) args = [ ] ; try { callback . apply ( undefined , args ) ; } catch ( err ) { if ( typeof err == 'object' && 'type' in err ) { if ( err . type == 'test_halt' ) { return ; } else if ( err . type == 'test_fail' ) { this . tester . rpc . fail ( this . name , err . message , ! this . running ) ; this . _done ( ) ; return ; } } this . tester . rpc . exception ( this . name , err , ! this . running ) ; this . _done ( ) ; return ; } if ( ! this . async ) { this . tester . rpc . pass ( this . name ) ; this . _done ( ) ; } } this . wrap = function ( callback ) { return function ( ) { this_ . callbackWrapper ( callback , arguments ) ; } ; } this . setTimeout = function ( callback , time ) { setTimeout ( this . wrap ( callback ) , time ) ; } this . waitForCallback = function ( callbackName , expectedCalls ) { this . log ( 'Waiting for ' + expectedCalls + ' invocations of callback: ' + callbackName ) ; var gotCallbacks = 0 ; window [ callbackName ] = this . wrap ( function ( ) { ++ gotCallbacks ; this_ . log ( 'Received callback ' + gotCallbacks ) ; if ( gotCallbacks == expectedCalls ) { this_ . log ( "Done waiting" ) ; this_ . pass ( ) ; } else { haltAsyncTest ( ) ; } } ) ; haltAsyncTest ( ) ; } this . expectMessageSequence = function ( plugin , messages , callback ) { this . assert ( messages . length > 0 , 'Must provide at least one message' ) ; var local_messages = messages . slice ( ) ; var listener = function ( message ) { if ( message . data . indexOf ( '@:' ) == 0 ) { this_ . log ( 'DEBUG: ' + message . data . substr ( 2 ) ) ; } else { this_ . assertEqual ( message . data , local_messages . shift ( ) ) ; if ( callback !== undefined ) { callback ( message . data ) ; } } if ( local_messages . length == 0 ) { this_ . pass ( ) ; } else { this_ . expectEvent ( plugin , 'message' , listener ) ; } } this . expectEvent ( plugin , 'message' , listener ) ; } this . expectEvent = function ( src , event_type , listener ) { var wrapper = this . wrap ( function ( e ) { src . removeEventListener ( event_type , wrapper , false ) ; listener ( e ) ; } ) ; src . addEventListener ( event_type , wrapper , false ) ; } }
Contains the state for a single test.
Contains the state for a single test .
// Work around how JS binds 'this' // TODO raise if not running. // A stale callback? // New-style test // If we get this exception, we can assume any callbacks or next // tests have already been scheduled. // Old-style test // A special exception that terminates the test with a failure // This is not a special type of exception, it is an error. // A normal exit.  Should we move on to the next test? // Async tests do not move on without an explicit pass. // Async callbacks should be wrapped so the tester can catch unexpected // exceptions. // Deliberately global - this is what the nexe expects. // TODO(ncbray): consider returning this function, so the test has more // flexibility. For example, in the test one could count to N // using a different callback before calling _this_ callback, and // continuing the test. Also, consider calling user-supplied callback // when done waiting. // HACK // HACK if this function is used in a non-async test, make sure we don't // spuriously pass.  Throwing this exception forces us to behave like an // async test. // This function takes an array of messages and asserts that the nexe // calls PostMessage with each of these messages, in order. // Arguments: //   plugin - The DOM object for the NaCl plugin //   messages - An array of expected responses //   callback - An optional callback function that takes the current message //              string as an argument // skip debug messages