Index: chrome/third_party/mock4js/mock4js.js |
diff --git a/chrome/third_party/mock4js/mock4js.js b/chrome/third_party/mock4js/mock4js.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..933e0a658bb1f0bb5bc8bf6a3569b4556c1cae75 |
--- /dev/null |
+++ b/chrome/third_party/mock4js/mock4js.js |
@@ -0,0 +1,625 @@ |
+/** |
+ * Mock4JS 0.2 |
+ * http://mock4js.sourceforge.net/ |
+ */ |
+ |
+Mock4JS = { |
+ _mocksToVerify: [], |
+ _convertToConstraint: function(constraintOrValue) { |
+ if(constraintOrValue.argumentMatches) { |
+ return constraintOrValue; // it's already an ArgumentMatcher |
+ } else { |
+ return new MatchExactly(constraintOrValue); // default to eq(...) |
+ } |
+ }, |
+ addMockSupport: function(object) { |
+ // mock creation |
+ object.mock = function(mockedType) { |
+ if(!mockedType) { |
+ throw new Mock4JSException("Cannot create mock: type to mock cannot be found or is null"); |
+ } |
+ var newMock = new Mock(mockedType); |
+ Mock4JS._mocksToVerify.push(newMock); |
+ return newMock; |
+ } |
+ |
+ // syntactic sugar for expects() |
+ object.once = function() { |
+ return new CallCounter(1); |
+ } |
+ object.never = function() { |
+ return new CallCounter(0); |
+ } |
+ object.exactly = function(expectedCallCount) { |
+ return new CallCounter(expectedCallCount); |
+ } |
+ object.atLeastOnce = function() { |
+ return new InvokeAtLeastOnce(); |
+ } |
+ |
+ // syntactic sugar for argument expectations |
+ object.ANYTHING = new MatchAnything(); |
+ object.NOT_NULL = new MatchAnythingBut(new MatchExactly(null)); |
+ object.NOT_UNDEFINED = new MatchAnythingBut(new MatchExactly(undefined)); |
+ object.eq = function(expectedValue) { |
+ return new MatchExactly(expectedValue); |
+ } |
+ object.not = function(valueNotExpected) { |
+ var argConstraint = Mock4JS._convertToConstraint(valueNotExpected); |
+ return new MatchAnythingBut(argConstraint); |
+ } |
+ object.and = function() { |
+ var constraints = []; |
+ for(var i=0; i<arguments.length; i++) { |
+ constraints[i] = Mock4JS._convertToConstraint(arguments[i]); |
+ } |
+ return new MatchAllOf(constraints); |
+ } |
+ object.or = function() { |
+ var constraints = []; |
+ for(var i=0; i<arguments.length; i++) { |
+ constraints[i] = Mock4JS._convertToConstraint(arguments[i]); |
+ } |
+ return new MatchAnyOf(constraints); |
+ } |
+ object.stringContains = function(substring) { |
+ return new MatchStringContaining(substring); |
+ } |
+ |
+ // syntactic sugar for will() |
+ object.returnValue = function(value) { |
+ return new ReturnValueAction(value); |
+ } |
+ object.throwException = function(exception) { |
+ return new ThrowExceptionAction(exception); |
+ } |
+ }, |
+ clearMocksToVerify: function() { |
+ Mock4JS._mocksToVerify = []; |
+ }, |
+ verifyAllMocks: function() { |
+ for(var i=0; i<Mock4JS._mocksToVerify.length; i++) { |
+ Mock4JS._mocksToVerify[i].verify(); |
+ } |
+ } |
+} |
+ |
+Mock4JSUtil = { |
+ hasFunction: function(obj, methodName) { |
+ return typeof obj == 'object' && typeof obj[methodName] == 'function'; |
+ }, |
+ join: function(list) { |
+ var result = ""; |
+ for(var i=0; i<list.length; i++) { |
+ var item = list[i]; |
+ if(Mock4JSUtil.hasFunction(item, "describe")) { |
+ result += item.describe(); |
+ } |
+ else if(typeof list[i] == 'string') { |
+ result += "\""+list[i]+"\""; |
+ } else { |
+ result += list[i]; |
+ } |
+ |
+ if(i<list.length-1) result += ", "; |
+ } |
+ return result; |
+ } |
+} |
+ |
+Mock4JSException = function(message) { |
+ this.message = message; |
+} |
+ |
+Mock4JSException.prototype = { |
+ toString: function() { |
+ return this.message; |
+ } |
+} |
+ |
+/** |
+ * Assert function that makes use of the constraint methods |
+ */ |
+assertThat = function(expected, argumentMatcher) { |
+ if(!argumentMatcher.argumentMatches(expected)) { |
+ fail("Expected '"+expected+"' to be "+argumentMatcher.describe()); |
+ } |
+} |
+ |
+/** |
+ * CallCounter |
+ */ |
+function CallCounter(expectedCount) { |
+ this._expectedCallCount = expectedCount; |
+ this._actualCallCount = 0; |
+} |
+ |
+CallCounter.prototype = { |
+ addActualCall: function() { |
+ this._actualCallCount++; |
+ if(this._actualCallCount > this._expectedCallCount) { |
+ throw new Mock4JSException("unexpected invocation"); |
+ } |
+ }, |
+ |
+ verify: function() { |
+ if(this._actualCallCount < this._expectedCallCount) { |
+ throw new Mock4JSException("expected method was not invoked the expected number of times"); |
+ } |
+ }, |
+ |
+ describe: function() { |
+ if(this._expectedCallCount == 0) { |
+ return "not expected"; |
+ } else if(this._expectedCallCount == 1) { |
+ var msg = "expected once"; |
+ if(this._actualCallCount >= 1) { |
+ msg += " and has been invoked"; |
+ } |
+ return msg; |
+ } else { |
+ var msg = "expected "+this._expectedCallCount+" times"; |
+ if(this._actualCallCount > 0) { |
+ msg += ", invoked "+this._actualCallCount + " times"; |
+ } |
+ return msg; |
+ } |
+ } |
+} |
+ |
+function InvokeAtLeastOnce() { |
+ this._hasBeenInvoked = false; |
+} |
+ |
+InvokeAtLeastOnce.prototype = { |
+ addActualCall: function() { |
+ this._hasBeenInvoked = true; |
+ }, |
+ |
+ verify: function() { |
+ if(this._hasBeenInvoked === false) { |
+ throw new Mock4JSException(describe()); |
+ } |
+ }, |
+ |
+ describe: function() { |
+ var desc = "expected at least once"; |
+ if(this._hasBeenInvoked) desc+=" and has been invoked"; |
+ return desc; |
+ } |
+} |
+ |
+/** |
+ * ArgumentMatchers |
+ */ |
+ |
+function MatchExactly(expectedValue) { |
+ this._expectedValue = expectedValue; |
+} |
+ |
+MatchExactly.prototype = { |
+ argumentMatches: function(actualArgument) { |
+ if(this._expectedValue instanceof Array) { |
+ if(!(actualArgument instanceof Array)) return false; |
+ if(this._expectedValue.length != actualArgument.length) return false; |
+ for(var i=0; i<this._expectedValue.length; i++) { |
+ if(this._expectedValue[i] != actualArgument[i]) return false; |
+ } |
+ return true; |
+ } else { |
+ return this._expectedValue == actualArgument; |
+ } |
+ }, |
+ describe: function() { |
+ if(typeof this._expectedValue == "string") { |
+ return "eq(\""+this._expectedValue+"\")"; |
+ } else { |
+ return "eq("+this._expectedValue+")"; |
+ } |
+ } |
+} |
+ |
+function MatchAnything() { |
+} |
+ |
+MatchAnything.prototype = { |
+ argumentMatches: function(actualArgument) { |
+ return true; |
+ }, |
+ describe: function() { |
+ return "ANYTHING"; |
+ } |
+} |
+ |
+function MatchAnythingBut(matcherToNotMatch) { |
+ this._matcherToNotMatch = matcherToNotMatch; |
+} |
+ |
+MatchAnythingBut.prototype = { |
+ argumentMatches: function(actualArgument) { |
+ return !this._matcherToNotMatch.argumentMatches(actualArgument); |
+ }, |
+ describe: function() { |
+ return "not("+this._matcherToNotMatch.describe()+")"; |
+ } |
+} |
+ |
+function MatchAllOf(constraints) { |
+ this._constraints = constraints; |
+} |
+ |
+ |
+MatchAllOf.prototype = { |
+ argumentMatches: function(actualArgument) { |
+ for(var i=0; i<this._constraints.length; i++) { |
+ var constraint = this._constraints[i]; |
+ if(!constraint.argumentMatches(actualArgument)) return false; |
+ } |
+ return true; |
+ }, |
+ describe: function() { |
+ return "and("+Mock4JSUtil.join(this._constraints)+")"; |
+ } |
+} |
+ |
+function MatchAnyOf(constraints) { |
+ this._constraints = constraints; |
+} |
+ |
+MatchAnyOf.prototype = { |
+ argumentMatches: function(actualArgument) { |
+ for(var i=0; i<this._constraints.length; i++) { |
+ var constraint = this._constraints[i]; |
+ if(constraint.argumentMatches(actualArgument)) return true; |
+ } |
+ return false; |
+ }, |
+ describe: function() { |
+ return "or("+Mock4JSUtil.join(this._constraints)+")"; |
+ } |
+} |
+ |
+ |
+function MatchStringContaining(stringToLookFor) { |
+ this._stringToLookFor = stringToLookFor; |
+} |
+ |
+MatchStringContaining.prototype = { |
+ argumentMatches: function(actualArgument) { |
+ if(typeof actualArgument != 'string') throw new Mock4JSException("stringContains() must be given a string, actually got a "+(typeof actualArgument)); |
+ return (actualArgument.indexOf(this._stringToLookFor) != -1); |
+ }, |
+ describe: function() { |
+ return "a string containing \""+this._stringToLookFor+"\""; |
+ } |
+} |
+ |
+ |
+/** |
+ * StubInvocation |
+ */ |
+function StubInvocation(expectedMethodName, expectedArgs, actionSequence) { |
+ this._expectedMethodName = expectedMethodName; |
+ this._expectedArgs = expectedArgs; |
+ this._actionSequence = actionSequence; |
+} |
+ |
+StubInvocation.prototype = { |
+ matches: function(invokedMethodName, invokedMethodArgs) { |
+ if (invokedMethodName != this._expectedMethodName) { |
+ return false; |
+ } |
+ |
+ if (invokedMethodArgs.length != this._expectedArgs.length) { |
+ return false; |
+ } |
+ |
+ for(var i=0; i<invokedMethodArgs.length; i++) { |
+ var expectedArg = this._expectedArgs[i]; |
+ var invokedArg = invokedMethodArgs[i]; |
+ if(!expectedArg.argumentMatches(invokedArg)) { |
+ return false; |
+ } |
+ } |
+ |
+ return true; |
+ }, |
+ |
+ invoked: function() { |
+ try { |
+ return this._actionSequence.invokeNextAction(); |
+ } catch(e) { |
+ if(e instanceof Mock4JSException) { |
+ throw new Mock4JSException(this.describeInvocationNameAndArgs()+" - "+e.message); |
+ } else { |
+ throw e; |
+ } |
+ } |
+ }, |
+ |
+ will: function() { |
+ this._actionSequence.addAll.apply(this._actionSequence, arguments); |
+ }, |
+ |
+ describeInvocationNameAndArgs: function() { |
+ return this._expectedMethodName+"("+Mock4JSUtil.join(this._expectedArgs)+")"; |
+ }, |
+ |
+ describe: function() { |
+ return "stub: "+this.describeInvocationNameAndArgs(); |
+ }, |
+ |
+ verify: function() { |
+ } |
+} |
+ |
+/** |
+ * ExpectedInvocation |
+ */ |
+function ExpectedInvocation(expectedMethodName, expectedArgs, expectedCallCounter) { |
+ this._stubInvocation = new StubInvocation(expectedMethodName, expectedArgs, new ActionSequence()); |
+ this._expectedCallCounter = expectedCallCounter; |
+} |
+ |
+ExpectedInvocation.prototype = { |
+ matches: function(invokedMethodName, invokedMethodArgs) { |
+ try { |
+ return this._stubInvocation.matches(invokedMethodName, invokedMethodArgs); |
+ } catch(e) { |
+ throw new Mock4JSException("method "+this._stubInvocation.describeInvocationNameAndArgs()+": "+e.message); |
+ } |
+ }, |
+ |
+ invoked: function() { |
+ try { |
+ this._expectedCallCounter.addActualCall(); |
+ } catch(e) { |
+ throw new Mock4JSException(e.message+": "+this._stubInvocation.describeInvocationNameAndArgs()); |
+ } |
+ return this._stubInvocation.invoked(); |
+ }, |
+ |
+ will: function() { |
+ this._stubInvocation.will.apply(this._stubInvocation, arguments); |
+ }, |
+ |
+ describe: function() { |
+ return this._expectedCallCounter.describe()+": "+this._stubInvocation.describeInvocationNameAndArgs(); |
+ }, |
+ |
+ verify: function() { |
+ try { |
+ this._expectedCallCounter.verify(); |
+ } catch(e) { |
+ throw new Mock4JSException(e.message+": "+this._stubInvocation.describeInvocationNameAndArgs()); |
+ } |
+ } |
+} |
+ |
+/** |
+ * MethodActions |
+ */ |
+function ReturnValueAction(valueToReturn) { |
+ this._valueToReturn = valueToReturn; |
+} |
+ |
+ReturnValueAction.prototype = { |
+ invoke: function() { |
+ return this._valueToReturn; |
+ }, |
+ describe: function() { |
+ return "returns "+this._valueToReturn; |
+ } |
+} |
+ |
+function ThrowExceptionAction(exceptionToThrow) { |
+ this._exceptionToThrow = exceptionToThrow; |
+} |
+ |
+ThrowExceptionAction.prototype = { |
+ invoke: function() { |
+ throw this._exceptionToThrow; |
+ }, |
+ describe: function() { |
+ return "throws "+this._exceptionToThrow; |
+ } |
+} |
+ |
+function ActionSequence() { |
+ this._ACTIONS_NOT_SETUP = "_ACTIONS_NOT_SETUP"; |
+ this._actionSequence = this._ACTIONS_NOT_SETUP; |
+ this._indexOfNextAction = 0; |
+} |
+ |
+ActionSequence.prototype = { |
+ invokeNextAction: function() { |
+ if(this._actionSequence === this._ACTIONS_NOT_SETUP) { |
+ return; |
+ } else { |
+ if(this._indexOfNextAction >= this._actionSequence.length) { |
+ throw new Mock4JSException("no more values to return"); |
+ } else { |
+ var action = this._actionSequence[this._indexOfNextAction]; |
+ this._indexOfNextAction++; |
+ return action.invoke(); |
+ } |
+ } |
+ }, |
+ |
+ addAll: function() { |
+ this._actionSequence = []; |
+ for(var i=0; i<arguments.length; i++) { |
+ if(typeof arguments[i] != 'object' && arguments[i].invoke === undefined) { |
+ throw new Error("cannot add a method action that does not have an invoke() method"); |
+ } |
+ this._actionSequence.push(arguments[i]); |
+ } |
+ } |
+} |
+ |
+function StubActionSequence() { |
+ this._ACTIONS_NOT_SETUP = "_ACTIONS_NOT_SETUP"; |
+ this._actionSequence = this._ACTIONS_NOT_SETUP; |
+ this._indexOfNextAction = 0; |
+} |
+ |
+StubActionSequence.prototype = { |
+ invokeNextAction: function() { |
+ if(this._actionSequence === this._ACTIONS_NOT_SETUP) { |
+ return; |
+ } else if(this._actionSequence.length == 1) { |
+ // if there is only one method action, keep doing that on every invocation |
+ return this._actionSequence[0].invoke(); |
+ } else { |
+ if(this._indexOfNextAction >= this._actionSequence.length) { |
+ throw new Mock4JSException("no more values to return"); |
+ } else { |
+ var action = this._actionSequence[this._indexOfNextAction]; |
+ this._indexOfNextAction++; |
+ return action.invoke(); |
+ } |
+ } |
+ }, |
+ |
+ addAll: function() { |
+ this._actionSequence = []; |
+ for(var i=0; i<arguments.length; i++) { |
+ if(typeof arguments[i] != 'object' && arguments[i].invoke === undefined) { |
+ throw new Error("cannot add a method action that does not have an invoke() method"); |
+ } |
+ this._actionSequence.push(arguments[i]); |
+ } |
+ } |
+} |
+ |
+ |
+/** |
+ * Mock |
+ */ |
+function Mock(mockedType) { |
+ if(mockedType === undefined || mockedType.prototype === undefined) { |
+ throw new Mock4JSException("Unable to create Mock: must create Mock using a class not prototype, eg. 'new Mock(TypeToMock)' or using the convenience method 'mock(TypeToMock)'"); |
+ } |
+ this._mockedType = mockedType.prototype; |
+ this._expectedCallCount; |
+ this._isRecordingExpectations = false; |
+ this._expectedInvocations = []; |
+ |
+ // setup proxy |
+ var IntermediateClass = new Function(); |
+ IntermediateClass.prototype = mockedType.prototype; |
+ var ChildClass = new Function(); |
+ ChildClass.prototype = new IntermediateClass(); |
+ this._proxy = new ChildClass(); |
+ this._proxy.mock = this; |
+ |
+ for(property in mockedType.prototype) { |
+ if(this._isPublicMethod(mockedType.prototype, property)) { |
+ var publicMethodName = property; |
+ this._proxy[publicMethodName] = this._createMockedMethod(publicMethodName); |
+ this[publicMethodName] = this._createExpectationRecordingMethod(publicMethodName); |
+ } |
+ } |
+} |
+ |
+Mock.prototype = { |
+ |
+ proxy: function() { |
+ return this._proxy; |
+ }, |
+ |
+ expects: function(expectedCallCount) { |
+ this._expectedCallCount = expectedCallCount; |
+ this._isRecordingExpectations = true; |
+ this._isRecordingStubs = false; |
+ return this; |
+ }, |
+ |
+ stubs: function() { |
+ this._isRecordingExpectations = false; |
+ this._isRecordingStubs = true; |
+ return this; |
+ }, |
+ |
+ verify: function() { |
+ for(var i=0; i<this._expectedInvocations.length; i++) { |
+ var expectedInvocation = this._expectedInvocations[i]; |
+ try { |
+ expectedInvocation.verify(); |
+ } catch(e) { |
+ var failMsg = e.message+this._describeMockSetup(); |
+ throw new Mock4JSException(failMsg); |
+ } |
+ } |
+ }, |
+ |
+ _isPublicMethod: function(mockedType, property) { |
+ try { |
+ var isMethod = typeof(mockedType[property]) == 'function'; |
+ var isPublic = property.charAt(0) != "_"; |
+ return isMethod && isPublic; |
+ } catch(e) { |
+ return false; |
+ } |
+ }, |
+ |
+ _createExpectationRecordingMethod: function(methodName) { |
+ return function() { |
+ // ensure all arguments are instances of ArgumentMatcher |
+ var expectedArgs = []; |
+ for(var i=0; i<arguments.length; i++) { |
+ if(arguments[i] !== null && arguments[i] !== undefined && arguments[i].argumentMatches) { |
+ expectedArgs[i] = arguments[i]; |
+ } else { |
+ expectedArgs[i] = new MatchExactly(arguments[i]); |
+ } |
+ } |
+ |
+ // create stub or expected invocation |
+ var expectedInvocation; |
+ if(this._isRecordingExpectations) { |
+ expectedInvocation = new ExpectedInvocation(methodName, expectedArgs, this._expectedCallCount); |
+ } else { |
+ expectedInvocation = new StubInvocation(methodName, expectedArgs, new StubActionSequence()); |
+ } |
+ |
+ this._expectedInvocations.push(expectedInvocation); |
+ |
+ this._isRecordingExpectations = false; |
+ this._isRecordingStubs = false; |
+ return expectedInvocation; |
+ } |
+ }, |
+ |
+ _createMockedMethod: function(methodName) { |
+ return function() { |
+ // go through expectation list backwards to ensure later expectations override earlier ones |
+ for(var i=this.mock._expectedInvocations.length-1; i>=0; i--) { |
+ var expectedInvocation = this.mock._expectedInvocations[i]; |
+ if(expectedInvocation.matches(methodName, arguments)) { |
+ try { |
+ return expectedInvocation.invoked(); |
+ } catch(e) { |
+ if(e instanceof Mock4JSException) { |
+ throw new Mock4JSException(e.message+this.mock._describeMockSetup()); |
+ } else { |
+ // the user setup the mock to throw a specific error, so don't modify the message |
+ throw e; |
+ } |
+ } |
+ } |
+ } |
+ var failMsg = "unexpected invocation: "+methodName+"("+Mock4JSUtil.join(arguments)+")"+this.mock._describeMockSetup(); |
+ throw new Mock4JSException(failMsg); |
+ }; |
+ }, |
+ |
+ _describeMockSetup: function() { |
+ var msg = "\nAllowed:"; |
+ for(var i=0; i<this._expectedInvocations.length; i++) { |
+ var expectedInvocation = this._expectedInvocations[i]; |
+ msg += "\n" + expectedInvocation.describe(); |
+ } |
+ return msg; |
+ } |
+} |