123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736 |
- // 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.
- /**
- * @fileoverview Unit tests for goog.functions.
- */
- goog.provide('goog.functionsTest');
- goog.setTestOnly('goog.functionsTest');
- goog.require('goog.array');
- goog.require('goog.functions');
- goog.require('goog.testing.MockClock');
- goog.require('goog.testing.PropertyReplacer');
- goog.require('goog.testing.jsunit');
- goog.require('goog.testing.recordFunction');
- var fTrue = makeCallOrderLogger('fTrue', true);
- var gFalse = makeCallOrderLogger('gFalse', false);
- var hTrue = makeCallOrderLogger('hTrue', true);
- var stubs = new goog.testing.PropertyReplacer();
- function setUp() {
- callOrder = [];
- }
- function tearDown() {
- stubs.reset();
- }
- var foo = 'global';
- var obj = {foo: 'obj'};
- function getFoo(arg1, arg2) {
- return {foo: this.foo, arg1: arg1, arg2: arg2};
- }
- function testTrue() {
- assertTrue(goog.functions.TRUE());
- }
- function testFalse() {
- assertFalse(goog.functions.FALSE());
- }
- function testLock() {
- function add(var_args) {
- var result = 0;
- for (var i = 0; i < arguments.length; i++) {
- result += arguments[i];
- }
- return result;
- }
- assertEquals(6, add(1, 2, 3));
- assertEquals(0, goog.functions.lock(add)(1, 2, 3));
- assertEquals(3, goog.functions.lock(add, 2)(1, 2, 3));
- assertEquals(6, goog.partial(add, 1, 2)(3));
- assertEquals(3, goog.functions.lock(goog.partial(add, 1, 2))(3));
- }
- function testNth() {
- assertEquals(1, goog.functions.nth(0)(1));
- assertEquals(2, goog.functions.nth(1)(1, 2));
- assertEquals('a', goog.functions.nth(0)('a', 'b'));
- assertEquals(undefined, goog.functions.nth(0)());
- assertEquals(undefined, goog.functions.nth(1)(true));
- assertEquals(undefined, goog.functions.nth(-1)());
- }
- function testPartialRight() {
- var f = function(x, y) { return x / y; };
- var g = goog.functions.partialRight(f, 2);
- assertEquals(2, g(4));
- var h = goog.functions.partialRight(f, 4, 2);
- assertEquals(2, h());
- var i = goog.functions.partialRight(f);
- assertEquals(2, i(4, 2));
- }
- function testPartialRightUsesGlobal() {
- var f = function(x, y) {
- assertEquals(goog.global, this);
- return x / y;
- };
- var g = goog.functions.partialRight(f, 2);
- var h = goog.functions.partialRight(g, 4);
- assertEquals(2, h());
- }
- function testPartialRightWithCall() {
- var obj = {};
- var f = function(x, y) {
- assertEquals(obj, this);
- return x / y;
- };
- var g = goog.functions.partialRight(f, 2);
- var h = goog.functions.partialRight(g, 4);
- assertEquals(2, h.call(obj));
- }
- function testPartialRightAndBind() {
- // This ensures that this "survives" through a partialRight.
- var p = goog.functions.partialRight(getFoo, 'dog');
- var b = goog.bind(p, obj, 'hot');
- var res = b();
- assertEquals(obj.foo, res.foo);
- assertEquals('hot', res.arg1);
- assertEquals('dog', res.arg2);
- }
- function testBindAndPartialRight() {
- // This ensures that this "survives" through a partialRight.
- var b = goog.bind(getFoo, obj, 'hot');
- var p = goog.functions.partialRight(b, 'dog');
- var res = p();
- assertEquals(obj.foo, res.foo);
- assertEquals('hot', res.arg1);
- assertEquals('dog', res.arg2);
- }
- function testPartialRightMultipleCalls() {
- var f = goog.testing.recordFunction();
- var a = goog.functions.partialRight(f, 'foo');
- var b = goog.functions.partialRight(a, 'bar');
- a();
- a();
- b();
- b();
- assertEquals(4, f.getCallCount());
- var calls = f.getCalls();
- assertArrayEquals(['foo'], calls[0].getArguments());
- assertArrayEquals(['foo'], calls[1].getArguments());
- assertArrayEquals(['bar', 'foo'], calls[2].getArguments());
- assertArrayEquals(['bar', 'foo'], calls[3].getArguments());
- }
- function testIdentity() {
- assertEquals(3, goog.functions.identity(3));
- assertEquals(3, goog.functions.identity(3, 4, 5, 6));
- assertEquals('Hi there', goog.functions.identity('Hi there'));
- assertEquals(null, goog.functions.identity(null));
- assertEquals(undefined, goog.functions.identity());
- var arr = [1, 'b', null];
- assertEquals(arr, goog.functions.identity(arr));
- var obj = {a: 'ay', b: 'bee', c: 'see'};
- assertEquals(obj, goog.functions.identity(obj));
- }
- function testConstant() {
- assertEquals(3, goog.functions.constant(3)());
- assertEquals(undefined, goog.functions.constant()());
- }
- function testError() {
- var f = goog.functions.error('x');
- var e = assertThrows(
- 'A function created by goog.functions.error must throw an error', f);
- assertEquals('x', e.message);
- }
- function testFail() {
- var obj = {};
- var f = goog.functions.fail(obj);
- var e = assertThrows(
- 'A function created by goog.functions.raise must throw its input', f);
- assertEquals(obj, e);
- }
- function testCompose() {
- var add2 = function(x) { return x + 2; };
- var doubleValue = function(x) { return x * 2; };
- assertEquals(6, goog.functions.compose(doubleValue, add2)(1));
- assertEquals(4, goog.functions.compose(add2, doubleValue)(1));
- assertEquals(6, goog.functions.compose(add2, add2, doubleValue)(1));
- assertEquals(
- 12, goog.functions.compose(doubleValue, add2, add2, doubleValue)(1));
- assertUndefined(goog.functions.compose()(1));
- assertEquals(3, goog.functions.compose(add2)(1));
- var add2Numbers = function(x, y) { return x + y; };
- assertEquals(17, goog.functions.compose(add2Numbers)(10, 7));
- assertEquals(34, goog.functions.compose(doubleValue, add2Numbers)(10, 7));
- }
- function testAdd() {
- assertUndefined(goog.functions.sequence()());
- assertCallOrderAndReset([]);
- assert(goog.functions.sequence(fTrue)());
- assertCallOrderAndReset(['fTrue']);
- assertFalse(goog.functions.sequence(fTrue, gFalse)());
- assertCallOrderAndReset(['fTrue', 'gFalse']);
- assert(goog.functions.sequence(fTrue, gFalse, hTrue)());
- assertCallOrderAndReset(['fTrue', 'gFalse', 'hTrue']);
- assert(goog.functions.sequence(goog.functions.identity)(true));
- assertFalse(goog.functions.sequence(goog.functions.identity)(false));
- }
- function testAnd() {
- // the return value is unspecified for an empty and
- goog.functions.and()();
- assertCallOrderAndReset([]);
- assert(goog.functions.and(fTrue)());
- assertCallOrderAndReset(['fTrue']);
- assertFalse(goog.functions.and(fTrue, gFalse)());
- assertCallOrderAndReset(['fTrue', 'gFalse']);
- assertFalse(goog.functions.and(fTrue, gFalse, hTrue)());
- assertCallOrderAndReset(['fTrue', 'gFalse']);
- assert(goog.functions.and(goog.functions.identity)(true));
- assertFalse(goog.functions.and(goog.functions.identity)(false));
- }
- function testOr() {
- // the return value is unspecified for an empty or
- goog.functions.or()();
- assertCallOrderAndReset([]);
- assert(goog.functions.or(fTrue)());
- assertCallOrderAndReset(['fTrue']);
- assert(goog.functions.or(fTrue, gFalse)());
- assertCallOrderAndReset(['fTrue']);
- assert(goog.functions.or(fTrue, gFalse, hTrue)());
- assertCallOrderAndReset(['fTrue']);
- assert(goog.functions.or(goog.functions.identity)(true));
- assertFalse(goog.functions.or(goog.functions.identity)(false));
- }
- function testNot() {
- assertTrue(goog.functions.not(gFalse)());
- assertCallOrderAndReset(['gFalse']);
- assertTrue(goog.functions.not(goog.functions.identity)(false));
- assertFalse(goog.functions.not(goog.functions.identity)(true));
- var f = function(a, b) {
- assertEquals(1, a);
- assertEquals(2, b);
- return false;
- };
- assertTrue(goog.functions.not(f)(1, 2));
- }
- function testCreate(expectedArray) {
- var tempConstructor = function(a, b) {
- this.foo = a;
- this.bar = b;
- };
- var factory = goog.partial(goog.functions.create, tempConstructor, 'baz');
- var instance = factory('qux');
- assert(instance instanceof tempConstructor);
- assertEquals(instance.foo, 'baz');
- assertEquals(instance.bar, 'qux');
- }
- function testWithReturnValue() {
- var obj = {};
- var f = function(a, b) {
- assertEquals(obj, this);
- assertEquals(1, a);
- assertEquals(2, b);
- };
- assertTrue(goog.functions.withReturnValue(f, true).call(obj, 1, 2));
- assertFalse(goog.functions.withReturnValue(f, false).call(obj, 1, 2));
- }
- function testEqualTo() {
- assertTrue(goog.functions.equalTo(42)(42));
- assertFalse(goog.functions.equalTo(42)(13));
- assertFalse(goog.functions.equalTo(42)('a string'));
- assertFalse(goog.functions.equalTo(42)('42'));
- assertTrue(goog.functions.equalTo(42, true)('42'));
- assertTrue(goog.functions.equalTo(0)(0));
- assertFalse(goog.functions.equalTo(0)(''));
- assertFalse(goog.functions.equalTo(0)(1));
- assertTrue(goog.functions.equalTo(0, true)(0));
- assertTrue(goog.functions.equalTo(0, true)(''));
- assertFalse(goog.functions.equalTo(0, true)(1));
- }
- function makeCallOrderLogger(name, returnValue) {
- return function() {
- callOrder.push(name);
- return returnValue;
- };
- }
- function assertCallOrderAndReset(expectedArray) {
- assertArrayEquals(expectedArray, callOrder);
- callOrder = [];
- }
- function testCacheReturnValue() {
- var returnFive = function() { return 5; };
- var recordedReturnFive = goog.testing.recordFunction(returnFive);
- var cachedRecordedReturnFive =
- goog.functions.cacheReturnValue(recordedReturnFive);
- assertEquals(0, recordedReturnFive.getCallCount());
- assertEquals(5, cachedRecordedReturnFive());
- assertEquals(1, recordedReturnFive.getCallCount());
- assertEquals(5, cachedRecordedReturnFive());
- assertEquals(1, recordedReturnFive.getCallCount());
- }
- function testCacheReturnValueFlagEnabled() {
- var count = 0;
- var returnIncrementingInteger = function() {
- count++;
- return count;
- };
- var recordedFunction = goog.testing.recordFunction(returnIncrementingInteger);
- var cachedRecordedFunction =
- goog.functions.cacheReturnValue(recordedFunction);
- assertEquals(0, recordedFunction.getCallCount());
- assertEquals(1, cachedRecordedFunction());
- assertEquals(1, recordedFunction.getCallCount());
- assertEquals(1, cachedRecordedFunction());
- assertEquals(1, recordedFunction.getCallCount());
- assertEquals(1, cachedRecordedFunction());
- }
- function testCacheReturnValueFlagDisabled() {
- stubs.set(goog.functions, 'CACHE_RETURN_VALUE', false);
- var count = 0;
- var returnIncrementingInteger = function() {
- count++;
- return count;
- };
- var recordedFunction = goog.testing.recordFunction(returnIncrementingInteger);
- var cachedRecordedFunction =
- goog.functions.cacheReturnValue(recordedFunction);
- assertEquals(0, recordedFunction.getCallCount());
- assertEquals(1, cachedRecordedFunction());
- assertEquals(1, recordedFunction.getCallCount());
- assertEquals(2, cachedRecordedFunction());
- assertEquals(2, recordedFunction.getCallCount());
- assertEquals(3, cachedRecordedFunction());
- }
- function testOnce() {
- var recordedFunction = goog.testing.recordFunction();
- var f = goog.functions.once(recordedFunction);
- assertEquals(0, recordedFunction.getCallCount());
- f();
- assertEquals(1, recordedFunction.getCallCount());
- f();
- assertEquals(1, recordedFunction.getCallCount());
- }
- function testDebounce() {
- // Encoded sequences of commands to perform mapped to expected # of calls.
- // f: fire
- // w: wait (for the timer to elapse)
- assertAsyncDecoratorCommandSequenceCalls(goog.functions.debounce, {
- 'f': 0,
- 'ff': 0,
- 'fff': 0,
- 'fw': 1,
- 'ffw': 1,
- 'fffw': 1,
- 'fwffwf': 2,
- 'ffwwwffwwfwf': 3
- });
- }
- function testDebounceScopeBinding() {
- var interval = 500;
- var mockClock = new goog.testing.MockClock(true);
- var x = {'y': 0};
- goog.functions.debounce(function() { ++this['y']; }, interval, x)();
- assertEquals(0, x['y']);
- mockClock.tick(interval);
- assertEquals(1, x['y']);
- mockClock.uninstall();
- }
- function testDebounceArgumentBinding() {
- var interval = 500;
- var mockClock = new goog.testing.MockClock(true);
- var calls = 0;
- var debouncedFn = goog.functions.debounce(function(a, b, c) {
- ++calls;
- assertEquals(3, a);
- assertEquals('string', b);
- assertEquals(false, c);
- }, interval);
- debouncedFn(3, 'string', false);
- mockClock.tick(interval);
- assertEquals(1, calls);
- // goog.functions.debounce should always pass the last arguments passed to the
- // decorator into the decorated function, even if called multiple times.
- debouncedFn();
- mockClock.tick(interval / 2);
- debouncedFn(8, null, true);
- debouncedFn(3, 'string', false);
- mockClock.tick(interval);
- assertEquals(2, calls);
- mockClock.uninstall();
- }
- function testDebounceArgumentAndScopeBinding() {
- var interval = 500;
- var mockClock = new goog.testing.MockClock(true);
- var x = {'calls': 0};
- var debouncedFn = goog.functions.debounce(function(a, b, c) {
- ++this['calls'];
- assertEquals(3, a);
- assertEquals('string', b);
- assertEquals(false, c);
- }, interval, x);
- debouncedFn(3, 'string', false);
- mockClock.tick(interval);
- assertEquals(1, x['calls']);
- // goog.functions.debounce should always pass the last arguments passed to the
- // decorator into the decorated function, even if called multiple times.
- debouncedFn();
- mockClock.tick(interval / 2);
- debouncedFn(8, null, true);
- debouncedFn(3, 'string', false);
- mockClock.tick(interval);
- assertEquals(2, x['calls']);
- mockClock.uninstall();
- }
- function testThrottle() {
- // Encoded sequences of commands to perform mapped to expected # of calls.
- // f: fire
- // w: wait (for the timer to elapse)
- assertAsyncDecoratorCommandSequenceCalls(goog.functions.throttle, {
- 'f': 1,
- 'ff': 1,
- 'fff': 1,
- 'fw': 1,
- 'ffw': 2,
- 'fwf': 2,
- 'fffw': 2,
- 'fwfff': 2,
- 'fwfffw': 3,
- 'fwffwf': 3,
- 'ffwf': 2,
- 'ffwff': 2,
- 'ffwfw': 3,
- 'ffwffwf': 3,
- 'ffwffwff': 3,
- 'ffwffwffw': 4,
- 'ffwwwffwwfw': 5,
- 'ffwwwffwwfwf': 6
- });
- }
- function testThrottleScopeBinding() {
- var interval = 500;
- var mockClock = new goog.testing.MockClock(true);
- var x = {'y': 0};
- goog.functions.throttle(function() { ++this['y']; }, interval, x)();
- assertEquals(1, x['y']);
- mockClock.uninstall();
- }
- function testThrottleArgumentBinding() {
- var interval = 500;
- var mockClock = new goog.testing.MockClock(true);
- var calls = 0;
- var throttledFn = goog.functions.throttle(function(a, b, c) {
- ++calls;
- assertEquals(3, a);
- assertEquals('string', b);
- assertEquals(false, c);
- }, interval);
- throttledFn(3, 'string', false);
- assertEquals(1, calls);
- // goog.functions.throttle should always pass the last arguments passed to the
- // decorator into the decorated function, even if called multiple times.
- throttledFn();
- mockClock.tick(interval / 2);
- throttledFn(8, null, true);
- throttledFn(3, 'string', false);
- mockClock.tick(interval);
- assertEquals(2, calls);
- mockClock.uninstall();
- }
- function testThrottleArgumentAndScopeBinding() {
- var interval = 500;
- var mockClock = new goog.testing.MockClock(true);
- var x = {'calls': 0};
- var throttledFn = goog.functions.throttle(function(a, b, c) {
- ++this['calls'];
- assertEquals(3, a);
- assertEquals('string', b);
- assertEquals(false, c);
- }, interval, x);
- throttledFn(3, 'string', false);
- assertEquals(1, x['calls']);
- // goog.functions.throttle should always pass the last arguments passed to the
- // decorator into the decorated function, even if called multiple times.
- throttledFn();
- mockClock.tick(interval / 2);
- throttledFn(8, null, true);
- throttledFn(3, 'string', false);
- mockClock.tick(interval);
- assertEquals(2, x['calls']);
- mockClock.uninstall();
- }
- function testRateLimit() {
- // Encoded sequences of commands to perform mapped to expected # of calls.
- // f: fire
- // w: wait (for the timer to elapse)
- assertAsyncDecoratorCommandSequenceCalls(goog.functions.rateLimit, {
- 'f': 1,
- 'ff': 1,
- 'fff': 1,
- 'fw': 1,
- 'ffw': 1,
- 'fwf': 2,
- 'fffw': 1,
- 'fwfff': 2,
- 'fwfffw': 2,
- 'fwffwf': 3,
- 'ffwf': 2,
- 'ffwff': 2,
- 'ffwfw': 2,
- 'ffwffwf': 3,
- 'ffwffwff': 3,
- 'ffwffwffw': 3,
- 'ffwwwffwwfw': 3,
- 'ffwwwffwwfwf': 4
- });
- }
- function testRateLimitScopeBinding() {
- var interval = 500;
- var mockClock = new goog.testing.MockClock(true);
- var x = {'y': 0};
- goog.functions.rateLimit(function() {
- ++this['y'];
- }, interval, x)();
- assertEquals(1, x['y']);
- mockClock.uninstall();
- }
- function testRateLimitArgumentBinding() {
- var interval = 500;
- var mockClock = new goog.testing.MockClock(true);
- var calls = 0;
- var rateLimitedFn = goog.functions.rateLimit(function(a, b, c) {
- ++calls;
- assertEquals(3, a);
- assertEquals('string', b);
- assertEquals(false, c);
- }, interval);
- rateLimitedFn(3, 'string', false);
- assertEquals(1, calls);
- // goog.functions.rateLimit should always pass the first arguments passed to
- // the
- // decorator into the decorated function, even if called multiple times.
- rateLimitedFn();
- mockClock.tick(interval / 2);
- rateLimitedFn(8, null, true);
- mockClock.tick(interval);
- rateLimitedFn(3, 'string', false);
- assertEquals(2, calls);
- mockClock.uninstall();
- }
- function testRateLimitArgumentAndScopeBinding() {
- var interval = 500;
- var mockClock = new goog.testing.MockClock(true);
- var x = {'calls': 0};
- var rateLimitedFn = goog.functions.rateLimit(function(a, b, c) {
- ++this['calls'];
- assertEquals(3, a);
- assertEquals('string', b);
- assertEquals(false, c);
- }, interval, x);
- rateLimitedFn(3, 'string', false);
- assertEquals(1, x['calls']);
- // goog.functions.rateLimit should always pass the last arguments passed to
- // the
- // decorator into the decorated function, even if called multiple times.
- rateLimitedFn();
- mockClock.tick(interval / 2);
- rateLimitedFn(8, null, true);
- mockClock.tick(interval);
- rateLimitedFn(3, 'string', false);
- assertEquals(2, x['calls']);
- mockClock.uninstall();
- }
- /**
- * Wraps a {@code goog.testing.recordFunction} with the specified decorator and
- * executes a list of command sequences, asserting that in each case the
- * decorated function is called the expected number of times.
- * @param {function():*} decorator The async decorator to test.
- * @param {!Object.<string, number>} expectedCommandSequenceCalls An object
- * mapping string command sequences (where 'f' is 'fire' and 'w' is 'wait')
- * to the number times we expect a decorated function to be called during
- * the execution of those commands.
- */
- function assertAsyncDecoratorCommandSequenceCalls(
- decorator, expectedCommandSequenceCalls) {
- var interval = 500;
- var mockClock = new goog.testing.MockClock(true);
- for (var commandSequence in expectedCommandSequenceCalls) {
- var recordedFunction = goog.testing.recordFunction();
- var f = decorator(recordedFunction, interval);
- for (var i = 0; i < commandSequence.length; ++i) {
- switch (commandSequence[i]) {
- case 'f':
- f();
- break;
- case 'w':
- mockClock.tick(interval);
- break;
- }
- }
- var expectedCalls = expectedCommandSequenceCalls[commandSequence];
- assertEquals(
- 'Expected ' + expectedCalls + ' calls for command sequence "' +
- commandSequence + '" (' +
- goog.array
- .map(
- commandSequence,
- function(command) {
- switch (command) {
- case 'f':
- return 'fire';
- case 'w':
- return 'wait';
- }
- })
- .join(' -> ') +
- ')',
- expectedCalls, recordedFunction.getCallCount());
- }
- mockClock.uninstall();
- }
|