text: // Copyright 2008 The Closure Library Authors. All Rights Reserved.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //      http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS-IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  goog.provide('goog.testing.StrictMockTest');
  goog.setTestOnly('goog.testing.StrictMockTest');
  
  goog.require('goog.testing.StrictMock');
  goog.require('goog.testing.jsunit');
  
  // The object that we will be mocking
  var RealObject = function() {};
  
  RealObject.prototype.a = function() {
    fail('real object should never be called');
  };
  
  RealObject.prototype.b = function() {
    fail('real object should never be called');
  };
  
  RealObject.prototype.c = function() {
    fail('real object should never be called');
  };
  
  var mock;
  
  function setUp() {
    var obj = new RealObject();
    mock = new goog.testing.StrictMock(obj);
  }
  
  
  function testMockFunction() {
    var mock = new goog.testing.StrictMock(RealObject);
    mock.a();
    mock.b();
    mock.c();
    mock.$replay();
    mock.a();
    mock.b();
    mock.c();
    mock.$verify();
  
    mock.$reset();
  
    assertThrows(function() {
      mock.x();
    });
  }
  
  
  function testSimpleExpectations() {
    mock.a();
    mock.$replay();
    mock.a();
    mock.$verify();
  
    mock.$reset();
  
    mock.a();
    mock.b();
    mock.a();
    mock.a();
    mock.$replay();
    mock.a();
    mock.b();
    mock.a();
    mock.a();
    mock.$verify();
  }
  
  
  function testFailToSetExpectation() {
    mock.$replay();
    assertThrowsJsUnitException(goog.bind(mock.a, mock));
  
    mock.$reset();
  
    mock.$replay();
    assertThrowsJsUnitException(goog.bind(mock.b, mock));
  }
  
  
  function testUnexpectedCall() {
    mock.a();
    mock.$replay();
    mock.a();
    assertThrowsJsUnitException(goog.bind(mock.a, mock));
  
    mock.$reset();
  
    mock.a();
    mock.$replay();
    assertThrowsJsUnitException(goog.bind(mock.b, mock));
  }
  
  
  function testNotEnoughCalls() {
    mock.a();
    mock.$replay();
    assertThrowsJsUnitException(goog.bind(mock.$verify, mock));
  
    mock.$reset();
  
    mock.a();
    mock.b();
    mock.$replay();
    mock.a();
    assertThrowsJsUnitException(goog.bind(mock.$verify, mock));
  }
  
  
  function testOutOfOrderCalls() {
    mock.a();
    mock.b();
    mock.$replay();
    assertThrowsJsUnitException(goog.bind(mock.b, mock));
  }
  
  
  function testVerify() {
    mock.a();
    mock.$replay();
    mock.a();
    mock.$verify();
  
    mock.$reset();
  
    mock.a();
    mock.$replay();
    assertThrowsJsUnitException(goog.bind(mock.$verify, mock));
  }
  
  
  function testArgumentMatching() {
    mock.a('foo');
    mock.b('bar');
    mock.$replay();
    mock.a('foo');
    assertThrowsJsUnitException(function() {
      mock.b('foo');
    });
  
    mock.$reset();
    mock.a('foo');
    mock.a('bar');
    mock.$replay();
    mock.a('foo');
    mock.a('bar');
    mock.$verify();
  
    mock.$reset();
    mock.a('foo');
    mock.a('bar');
    mock.$replay();
    assertThrowsJsUnitException(function() {
      mock.a('bar');
    });
  }
  
  
  function testReturnValue() {
    mock.a().$returns(5);
    mock.$replay();
  
    assertEquals('Mock should return the right value', 5, mock.a());
  
    mock.$verify();
  }
  
  function testMultipleReturnValues() {
    mock.a().$returns(3);
    mock.a().$returns(2);
    mock.a().$returns(1);
  
    mock.$replay();
  
    assertArrayEquals(
        'Mock should return the right value sequence', [3, 2, 1],
        [mock.a(), mock.a(), mock.a()]);
  
    mock.$verify();
  }
  
  
  function testAtMostOnce() {
    // Zero times SUCCESS.
    mock.a().$atMostOnce();
    mock.$replay();
    mock.$verify();
  
    mock.$reset();
  
    // One time SUCCESS.
    mock.a().$atMostOnce();
    mock.$replay();
    mock.a();
    mock.$verify();
  
    mock.$reset();
  
    // Many times FAIL.
    mock.a().$atMostOnce();
    mock.$replay();
    mock.a();
    assertThrowsJsUnitException(goog.bind(mock.a, mock));
  
    mock.$reset();
  
    // atMostOnce only lasts until a new method is called.
    mock.a().$atMostOnce();
    mock.b();
    mock.a();
    mock.$replay();
    mock.b();
    assertThrowsJsUnitException(goog.bind(mock.$verify, mock));
  }
  
  
  function testAtLeastOnce() {
    // atLeastOnce does not mean zero times
    mock.a().$atLeastOnce();
    mock.$replay();
    assertThrowsJsUnitException(goog.bind(mock.$verify, mock));
  
    mock.$reset();
  
    // atLeastOnce does mean three times
    mock.a().$atLeastOnce();
    mock.$replay();
    mock.a();
    mock.a();
    mock.a();
    mock.$verify();
  
    mock.$reset();
  
    // atLeastOnce only lasts until a new method is called
    mock.a().$atLeastOnce();
    mock.b();
    mock.a();
    mock.$replay();
    mock.a();
    mock.a();
    mock.b();
    mock.a();
    assertThrowsJsUnitException(goog.bind(mock.a, mock));
  }
  
  
  function testAtLeastOnceWithArgs() {
    mock.a('asdf').$atLeastOnce();
    mock.a('qwert');
    mock.$replay();
    mock.a('asdf');
    mock.a('asdf');
    mock.a('qwert');
    mock.$verify();
  
    mock.$reset();
  
    mock.a('asdf').$atLeastOnce();
    mock.a('qwert');
    mock.$replay();
    mock.a('asdf');
    mock.a('asdf');
    assertThrowsJsUnitException(function() {
      mock.a('zxcv');
    });
    assertThrowsJsUnitException(goog.bind(mock.$verify, mock));
  }
  
  
  function testAnyTimes() {
    mock.a().$anyTimes();
    mock.$replay();
    mock.$verify();
  
    mock.$reset();
  
    mock.a().$anyTimes();
    mock.$replay();
    mock.a();
    mock.a();
    mock.a();
    mock.a();
    mock.a();
    mock.$verify();
  }
  
  
  function testAnyTimesWithArguments() {
    mock.a('foo').$anyTimes();
    mock.$replay();
    mock.$verify();
  
    mock.$reset();
  
    mock.a('foo').$anyTimes();
    mock.a('bar').$anyTimes();
    mock.$replay();
    mock.a('foo');
    mock.a('foo');
    mock.a('foo');
    mock.a('bar');
    mock.a('bar');
    mock.$verify();
  }
  
  
  function testZeroTimes() {
    mock.a().$times(0);
    mock.$replay();
    mock.$verify();
  
    mock.$reset();
  
    mock.a().$times(0);
    mock.$replay();
    assertThrowsJsUnitException(function() {
      mock.a();
    });
  }
  
  
  function testZeroTimesWithArguments() {
    mock.a('foo').$times(0);
    mock.$replay();
    mock.$verify();
  
    mock.$reset();
  
    mock.a('foo').$times(0);
    mock.$replay();
    assertThrowsJsUnitException(function() {
      mock.a('foo');
    });
  }
  
  
  function testTooManyCalls() {
    mock.a().$times(2);
    mock.$replay();
    mock.a();
    mock.a();
    assertThrowsJsUnitException(function() {
      mock.a();
    });
  }
  
  
  function testTooManyCallsWithArguments() {
    mock.a('foo').$times(2);
    mock.$replay();
    mock.a('foo');
    mock.a('foo');
    assertThrowsJsUnitException(function() {
      mock.a('foo');
    });
  }
  
  
  function testMultipleSkippedAnyTimes() {
    mock.a().$anyTimes();
    mock.b().$anyTimes();
    mock.c().$anyTimes();
    mock.$replay();
    mock.c();
    mock.$verify();
  }
  
  
  function testMultipleSkippedAnyTimesWithArguments() {
    mock.a('foo').$anyTimes();
    mock.a('bar').$anyTimes();
    mock.a('baz').$anyTimes();
    mock.$replay();
    mock.a('baz');
    mock.$verify();
  }
  
  
  function testVerifyThrows() {
    mock.a(1);
    mock.$replay();
    mock.a(1);
    try {
      mock.a(2);
      fail('bad mock, should fail');
    } catch (ex) {
      // this could be an event handler, for example
    }
    assertThrowsJsUnitException(goog.bind(mock.$verify, mock));
  }
  
  
  function testThrows() {
    mock.a().$throws('exception!');
    mock.$replay();
    assertThrows(goog.bind(mock.a, mock));
    mock.$verify();
  }
  
  
  function testDoes() {
    mock.a(1, 2).$does(function(a, b) { return a + b; });
    mock.$replay();
    assertEquals('Mock should call the function', 3, mock.a(1, 2));
    mock.$verify();
  }
  
  function testErrorMessageForBadArgs() {
    mock.a();
    mock.$anyTimes();
  
    mock.$replay();
  
    var e = assertThrowsJsUnitException(function() {
      mock.a('a');
    });
  
    assertContains('Bad arguments to a()', e.message);
  }
  
  async function testWaitAndVerify() {
    mock.a();
    mock.$replay();
  
    setTimeout(() => {
      mock.a();
    }, 0);
    await mock.$waitAndVerify();
  }
  
  async function testWaitAndVerify_Synchronous() {
    mock.a();
    mock.$replay();
  
    mock.a();
    await mock.$waitAndVerify();
  }
  
  async function testWaitAndVerify_Exception() {
    mock.a();
    mock.$replay();
  
    setTimeout(() => {
      assertThrowsJsUnitException(() => {
        mock.a(false);
      });
    }, 0);
    await assertRejects(mock.$waitAndVerify());
  }
  
  async function testWaitAndVerify_Reset() {
    mock.a();
    mock.$replay();
  
    setTimeout(() => {
      mock.a();
    }, 0);
    await mock.$waitAndVerify();
    mock.$reset();
    mock.a();
    mock.$replay();
  
    setTimeout(() => {
      mock.a();
    }, 0);
    await mock.$waitAndVerify();
  }
  