Skip to content

Commit 82d84ea

Browse files
author
Nick Reid
committed
Rollforward of cl/211538380: Add a $waitAndVerify() function
NEW: Assign goog.getUid to a local private const var in Set, preventing the global mocking behavior of some tests from impacting Set. RELNOTES: Add a $waitAndVerify() function to MockInterface and a $waitAndVerifyAll() function to MockControl that returns a goog.Promise that resolves when expectations have been registered. Automated g4 rollback of changelist 211629440. *** Reason for rollback *** Rollforward by not allowing goog.getUid to be mocked for Set. *** Original change description *** Automated g4 rollback of changelist 211538380. *** Reason for rollback *** Broken builds. *** Original change description *** Add a $waitAndVerify() function to MockInterface and a $waitAndVerifyAll() function to MockControl that returns a goog.Promise that resolves when expectations have been registered. RELNOTES: same *** *** ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=211661074
1 parent 6d3f93c commit 82d84ea

File tree

10 files changed

+267
-12
lines changed

10 files changed

+267
-12
lines changed

closure/goog/deps.js

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

closure/goog/structs/set.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ goog.require('goog.structs');
3030
goog.require('goog.structs.Collection');
3131
goog.require('goog.structs.Map');
3232

33-
34-
3533
/**
3634
* A set that can contain both primitives and objects. Adding and removing
3735
* elements is O(1). Primitives are treated as identical if they have the same
@@ -56,6 +54,12 @@ goog.structs.Set = function(opt_values) {
5654
}
5755
};
5856

57+
/**
58+
* A function that returns a unique id.
59+
* @private @const {function(?Object): number}
60+
*/
61+
goog.structs.Set.getUid_ = goog.getUid;
62+
5963

6064
/**
6165
* Obtains a unique key for an element of the set. Primitives will yield the
@@ -68,7 +72,7 @@ goog.structs.Set = function(opt_values) {
6872
goog.structs.Set.getKey_ = function(val) {
6973
var type = typeof val;
7074
if (type == 'object' && val || type == 'function') {
71-
return 'o' + goog.getUid(/** @type {Object} */ (val));
75+
return 'o' + goog.structs.Set.getUid_(/** @type {Object} */ (val));
7276
} else {
7377
return type.substr(0, 1) + val;
7478
}

closure/goog/testing/loosemock.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ goog.provide('goog.testing.LooseExpectationCollection');
2121
goog.provide('goog.testing.LooseMock');
2222

2323
goog.require('goog.array');
24+
goog.require('goog.asserts');
2425
goog.require('goog.structs.Map');
26+
goog.require('goog.structs.Set');
2527
goog.require('goog.testing.Mock');
2628

2729

@@ -93,6 +95,9 @@ goog.testing.LooseMock = function(
9395
*/
9496
this.$expectations_ = new goog.structs.Map();
9597

98+
/** @private {!goog.structs.Set<!goog.testing.MockExpectation>} */
99+
this.awaitingExpectations_ = new goog.structs.Set();
100+
96101
/**
97102
* The calls that have been made; we cache them to verify at the end. Each
98103
* element is an array where the first element is the name, and the second
@@ -134,6 +139,9 @@ goog.testing.LooseMock.prototype.$recordExpectation = function() {
134139

135140
var collection = this.$expectations_.get(this.$pendingExpectation.name);
136141
collection.addExpectation(this.$pendingExpectation);
142+
if (this.$pendingExpectation) {
143+
this.awaitingExpectations_.add(this.$pendingExpectation);
144+
}
137145
};
138146

139147

@@ -172,6 +180,10 @@ goog.testing.LooseMock.prototype.$recordCall = function(name, args) {
172180
matchingExpectation.maxCalls + ' but was: ' +
173181
matchingExpectation.actualCalls);
174182
}
183+
if (matchingExpectation.actualCalls >= matchingExpectation.minCalls) {
184+
this.awaitingExpectations_.remove(matchingExpectation);
185+
this.maybeFinishedWithExpectations_();
186+
}
175187

176188
this.$calls_.push([name, args]);
177189
return this.$do(matchingExpectation, args);
@@ -183,6 +195,7 @@ goog.testing.LooseMock.prototype.$reset = function() {
183195
goog.testing.LooseMock.superClass_.$reset.call(this);
184196

185197
this.$expectations_ = new goog.structs.Map();
198+
this.awaitingExpectations_ = new goog.structs.Set();
186199
this.$calls_ = [];
187200
};
188201

@@ -223,6 +236,34 @@ goog.testing.LooseMock.prototype.$replay = function() {
223236
};
224237

225238

239+
/** @override */
240+
goog.testing.LooseMock.prototype.$waitAndVerify = function() {
241+
var keys = this.$expectations_.getKeys();
242+
for (var i = 0; i < keys.length; i++) {
243+
var expectations = this.$expectations_.get(keys[i]).getExpectations();
244+
for (var j = 0; j < expectations.length; j++) {
245+
var expectation = expectations[j];
246+
goog.asserts.assert(
247+
!isFinite(expectation.maxCalls) ||
248+
expectation.minCalls == expectation.maxCalls,
249+
'Mock expectations cannot have a loose number of expected calls to ' +
250+
'use $waitAndVerify.');
251+
}
252+
}
253+
var promise = goog.testing.LooseMock.base(this, '$waitAndVerify');
254+
this.maybeFinishedWithExpectations_();
255+
return promise;
256+
};
257+
258+
/**
259+
* @private
260+
*/
261+
goog.testing.LooseMock.prototype.maybeFinishedWithExpectations_ = function() {
262+
if (this.awaitingExpectations_.isEmpty() && this.waitingForExpectations) {
263+
this.waitingForExpectations.resolve();
264+
}
265+
};
266+
226267
/** @override */
227268
goog.testing.LooseMock.prototype.$verify = function() {
228269
goog.testing.LooseMock.superClass_.$verify.call(this);

closure/goog/testing/loosemock_test.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,51 @@ function testErrorMessageForBadArgs() {
290290

291291
assertContains('Bad arguments to a()', e.message);
292292
}
293+
294+
async function testWaitAndVerify() {
295+
mock.a();
296+
mock.$replay();
297+
298+
setTimeout(() => {
299+
mock.a();
300+
}, 0);
301+
await mock.$waitAndVerify();
302+
}
303+
304+
async function testWaitAndVerify_Synchronous() {
305+
mock.a();
306+
mock.$replay();
307+
308+
mock.a();
309+
await mock.$waitAndVerify();
310+
}
311+
312+
async function testWaitAndVerify_Exception() {
313+
mock.a();
314+
mock.$replay();
315+
316+
setTimeout(() => {
317+
assertThrowsJsUnitException(() => {
318+
mock.a(false);
319+
});
320+
}, 0);
321+
await assertRejects(mock.$waitAndVerify());
322+
}
323+
324+
async function testWaitAndVerify_Reset() {
325+
mock.a();
326+
mock.$replay();
327+
328+
setTimeout(() => {
329+
mock.a();
330+
}, 0);
331+
await mock.$waitAndVerify();
332+
mock.$reset();
333+
mock.a();
334+
mock.$replay();
335+
336+
setTimeout(() => {
337+
mock.a();
338+
}, 0);
339+
await mock.$waitAndVerify();
340+
}

closure/goog/testing/mock.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,11 @@ goog.setTestOnly('goog.testing.Mock');
3939
goog.provide('goog.testing.Mock');
4040
goog.provide('goog.testing.MockExpectation');
4141

42+
goog.require('goog.Promise');
4243
goog.require('goog.array');
44+
goog.require('goog.asserts');
4345
goog.require('goog.object');
46+
goog.require('goog.promise.Resolver');
4447
goog.require('goog.testing.JsUnitException');
4548
goog.require('goog.testing.MockInterface');
4649
goog.require('goog.testing.mockmatchers');
@@ -193,6 +196,9 @@ goog.testing.Mock = function(
193196
this.$initializeFunctions_(objectToMock);
194197
}
195198
this.$argumentListVerifiers_ = {};
199+
200+
/** @protected {?goog.promise.Resolver<undefined>} */
201+
this.waitingForExpectations = null;
196202
};
197203

198204

@@ -543,6 +549,9 @@ goog.testing.Mock.prototype.$reset = function() {
543549
this.$recording_ = true;
544550
this.$threwException_ = null;
545551
delete this.$pendingExpectation;
552+
if (this.waitingForExpectations) {
553+
this.waitingForExpectations = null;
554+
}
546555
};
547556

548557

@@ -569,6 +578,9 @@ goog.testing.Mock.prototype.$throwException = function(comment, opt_message) {
569578
* @protected
570579
*/
571580
goog.testing.Mock.prototype.$recordAndThrow = function(ex, rethrow) {
581+
if (this.waitingForExpectations) {
582+
this.waitingForExpectations.resolve();
583+
}
572584
// If it's an assert exception, record it.
573585
if (ex['isJsUnitException']) {
574586
if (!this.$threwException_) {
@@ -590,6 +602,28 @@ goog.testing.Mock.prototype.$recordAndThrow = function(ex, rethrow) {
590602
};
591603

592604

605+
/** @override */
606+
goog.testing.Mock.prototype.$waitAndVerify = function() {
607+
goog.asserts.assert(
608+
!this.$recording_,
609+
'$waitAndVerify should be called after recording calls.');
610+
this.waitingForExpectations = goog.Promise.withResolver();
611+
var verify = goog.bind(this.$verify, this);
612+
return this.waitingForExpectations.promise.then(function() {
613+
return new goog.Promise(function(resolve, reject) {
614+
setTimeout(function() {
615+
try {
616+
verify();
617+
} catch (e) {
618+
reject(e);
619+
}
620+
resolve();
621+
}, 0);
622+
});
623+
});
624+
};
625+
626+
593627
/**
594628
* Verify that all of the expectations were met. Should be overridden by
595629
* subclasses.

closure/goog/testing/mock_test.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,4 +345,22 @@ function testMockEs6ClassStaticMethods() {
345345
assertEquals('a', mock.a());
346346
assertEquals('apply', mock.apply());
347347
mockControl.$verifyAll();
348-
}
348+
}
349+
350+
async function testLooseMockAsynchronousVerify() {
351+
const mockControl = new goog.testing.MockControl();
352+
const looseMock = mockControl.createLooseMock(RealObject);
353+
looseMock.a().$returns('a');
354+
355+
const strictMock = mockControl.createStrictMock(RealObject);
356+
strictMock.a().$returns('a');
357+
358+
mockControl.$replayAll();
359+
setTimeout(() => {
360+
looseMock.a();
361+
}, 0);
362+
setTimeout(() => {
363+
strictMock.a();
364+
}, 0);
365+
await mockControl.$waitAndVerifyAll();
366+
}

closure/goog/testing/mockcontrol.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
goog.setTestOnly('goog.testing.MockControl');
3131
goog.provide('goog.testing.MockControl');
3232

33+
goog.require('goog.Promise');
3334
goog.require('goog.array');
3435
goog.require('goog.testing');
3536
goog.require('goog.testing.LooseMock');
@@ -80,6 +81,18 @@ goog.testing.MockControl.prototype.$resetAll = function() {
8081
};
8182

8283

84+
/**
85+
* Returns a Promise that resolves when all of the controlled mocks have
86+
* finished and verified.
87+
* @return {!goog.Promise<!Array<undefined>>}
88+
*/
89+
goog.testing.MockControl.prototype.$waitAndVerifyAll = function() {
90+
return goog.Promise.all(goog.array.map(this.mocks_, function(m) {
91+
return m.$waitAndVerify();
92+
}));
93+
};
94+
95+
8396
/**
8497
* Calls verify on each controlled mock.
8598
*/

closure/goog/testing/mockinterface.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
goog.setTestOnly('goog.testing.MockInterface');
2121
goog.provide('goog.testing.MockInterface');
2222

23+
goog.require('goog.Promise');
24+
2325

2426

2527
/** @interface */
@@ -40,6 +42,13 @@ goog.testing.MockInterface.prototype.$replay = function() {};
4042
goog.testing.MockInterface.prototype.$reset = function() {};
4143

4244

45+
/**
46+
* Waits for the Mock to gather expectations and then performs verify.
47+
* @return {!goog.Promise<undefined>}
48+
*/
49+
goog.testing.MockInterface.prototype.$waitAndVerify = function() {};
50+
51+
4352
/**
4453
* Assert that the expected function calls match the actual calls.
4554
*/

0 commit comments

Comments
 (0)