# lib/goog/testing/testcase.js

1 | // Copyright 2007 The Closure Library Authors. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS-IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /**
16 | * @fileoverview A class representing a set of test functions to be run.
17 | *
18 | * Testing code should not have dependencies outside of goog.testing so as to
19 | * reduce the chance of masking missing dependencies.
20 | *
21 | * This file does not compile correctly with --collapse_properties. Use
22 | * --property_renaming=ALL_UNQUOTED instead.
23 | *
24 | */
25 |
26 | goog.provide('goog.testing.TestCase');
27 | goog.provide('goog.testing.TestCase.Error');
28 | goog.provide('goog.testing.TestCase.Order');
29 | goog.provide('goog.testing.TestCase.Result');
30 | goog.provide('goog.testing.TestCase.Test');
31 |
32 | goog.require('goog.Promise');
33 | goog.require('goog.Thenable');
34 | goog.require('goog.object');
35 | goog.require('goog.testing.asserts');
36 | goog.require('goog.testing.stacktrace');
37 |
38 |
39 |
40 | /**
41 | * A class representing a JsUnit test case. A TestCase is made up of a number
42 | * of test functions which can be run. Individual test cases can override the
43 | * following functions to set up their test environment:
44 | * - runTests - completely override the test's runner
45 | * - setUpPage - called before any of the test functions are run
46 | * - tearDownPage - called after all tests are finished
47 | * - setUp - called before each of the test functions
48 | * - tearDown - called after each of the test functions
49 | * - shouldRunTests - called before a test run, all tests are skipped if it
50 | * returns false. Can be used to disable tests on browsers
51 | * where they aren't expected to pass.
52 | *
53 | * Use {@link #autoDiscoverLifecycle} and {@link #autoDiscoverTests}
54 | *
55 | * @param {string=} opt_name The name of the test case, defaults to
56 | * 'Untitled Test Case'.
57 | * @constructor
58 | */
59 | goog.testing.TestCase = function(opt_name) {
60 | /**
61 | * A name for the test case.
62 | * @type {string}
63 | * @private
64 | */
65 | this.name_ = opt_name || 'Untitled Test Case';
66 |
67 | /**
68 | * Array of test functions that can be executed.
69 | * @type {!Array<!goog.testing.TestCase.Test>}
70 | * @private
71 | */
72 | this.tests_ = [];
73 |
74 | /**
75 | * Set of test names and/or indices to execute, or null if all tests should
76 | * be executed.
77 | *
78 | * Indices are included to allow automation tools to run a subset of the
79 | * tests without knowing the exact contents of the test file.
80 | *
81 | * Indices should only be used with SORTED ordering.
82 | *
83 | * Example valid values:
84 | * <ul>
85 | * <li>[testName]
86 | * <li>[testName1, testName2]
87 | * <li>[2] - will run the 3rd test in the order specified
88 | * <li>[1,3,5]
89 | * <li>[testName1, testName2, 3, 5] - will work
90 | * <ul>
91 | * @type {Object}
92 | * @private
93 | */
94 | this.testsToRun_ = null;
95 |
96 | /** @private {function(!goog.testing.TestCase.Result)} */
97 | this.runNextTestCallback_ = goog.nullFunction;
98 |
99 | /**
100 | * The number of {@link runNextTest_} frames currently on the stack.
101 | * When this exceeds {@link MAX_STACK_DEPTH_}, test execution is rescheduled
102 | * for a later tick of the event loop.
103 | * @see {finishTestInvocation_}
104 | * @private {number}
105 | */
106 | this.depth_ = 0;
107 |
108 | /** @private {goog.testing.TestCase.Test} */
109 | this.curTest_ = null;
110 |
111 | var search = '';
112 | if (goog.global.location) {
113 | search = goog.global.location.search;
114 | }
115 |
116 | // Parse the 'runTests' query parameter into a set of test names and/or
117 | // test indices.
118 | var runTestsMatch = search.match(/(?:\?|&)runTests=([^?&]+)/i);
119 | if (runTestsMatch) {
120 | this.testsToRun_ = {};
121 | var arr = runTestsMatch[1].split(',');
122 | for (var i = 0, len = arr.length; i < len; i++) {
123 | this.testsToRun_[arr[i]] = 1;
124 | }
125 | }
126 |
127 | // Checks the URL for a valid order param.
128 | var orderMatch = search.match(/(?:\?|&)order=(natural|random|sorted)/i);
129 | if (orderMatch) {
130 | this.order = orderMatch[1];
131 | }
132 |
133 | /**
134 | * Object used to encapsulate the test results.
135 | * @type {!goog.testing.TestCase.Result}
136 | * @protected
137 | * @suppress {underscore|visibility}
138 | */
139 | this.result_ = new goog.testing.TestCase.Result(this);
140 |
141 | // This silences a compiler warning from the legacy property check, which
142 | // is deprecated. It idly writes to testRunner properties that are used
143 | // in this file.
144 | var testRunnerMethods = {isFinished: true, hasErrors: true};
145 | };
146 |
147 |
148 | /**
149 | * The order to run the auto-discovered tests.
150 | * @enum {string}
151 | */
152 | goog.testing.TestCase.Order = {
153 | /**
154 | * This is browser dependent and known to be different in FF and Safari
155 | * compared to others.
156 | */
157 | NATURAL: 'natural',
158 |
159 | /** Random order. */
160 | RANDOM: 'random',
161 |
162 | /** Sorted based on the name. */
163 | SORTED: 'sorted'
164 | };
165 |
166 |
167 | /**
168 | * @return {string} The name of the test.
169 | */
170 | goog.testing.TestCase.prototype.getName = function() {
171 | return this.name_;
172 | };
173 |
174 |
175 | /**
176 | * The maximum amount of time in milliseconds that the test case can take
177 | * before it is forced to yield and reschedule. This prevents the test runner
178 | * from blocking the browser and potentially hurting the test harness.
179 | * @type {number}
180 | */
181 | goog.testing.TestCase.maxRunTime = 200;
182 |
183 |
184 | /**
185 | * The maximum number of {@link runNextTest_} frames that can be on the stack
186 | * before the test case is forced to yield and reschedule. Although modern
187 | * browsers can handle thousands of stack frames, this is set conservatively
188 | * because maximum stack depth has never been standardized, and engine-specific
189 | * techniques like tail cail optimization can affect the exact depth.
190 | * @private @const
191 | */
192 | goog.testing.TestCase.MAX_STACK_DEPTH_ = 100;
193 |
194 |
195 | /**
196 | * The order to run the auto-discovered tests in.
197 | * @type {string}
198 | */
199 | goog.testing.TestCase.prototype.order = goog.testing.TestCase.Order.SORTED;
200 |
201 |
202 | /**
203 | * Save a reference to {@code window.setTimeout}, so any code that overrides the
204 | * default behavior (the MockClock, for example) doesn't affect our runner.
205 | * @type {function((Function|string), number, *=): number}
206 | * @private
207 | */
208 | goog.testing.TestCase.protectedSetTimeout_ = goog.global.setTimeout;
209 |
210 |
211 | /**
212 | * Save a reference to {@code window.clearTimeout}, so any code that overrides
213 | * the default behavior (e.g. MockClock) doesn't affect our runner.
214 | * @type {function((null|number|undefined)): void}
215 | * @private
216 | */
217 | goog.testing.TestCase.protectedClearTimeout_ = goog.global.clearTimeout;
218 |
219 |
220 | /**
221 | * Save a reference to {@code window.Date}, so any code that overrides
222 | * the default behavior doesn't affect our runner.
223 | * @type {function(new: Date)}
224 | * @private
225 | */
226 | goog.testing.TestCase.protectedDate_ = Date;
227 |
228 |
229 | /**
230 | * Saved string referencing goog.global.setTimeout's string serialization. IE
231 | * sometimes fails to uphold equality for setTimeout, but the string version
232 | * stays the same.
233 | * @type {string}
234 | * @private
235 | */
236 | goog.testing.TestCase.setTimeoutAsString_ = String(goog.global.setTimeout);
237 |
238 |
239 | /**
240 | * TODO(user) replace this with prototype.currentTest.
241 | * Name of the current test that is running, or null if none is running.
242 | * @type {?string}
243 | */
244 | goog.testing.TestCase.currentTestName = null;
245 |
246 |
247 | /**
248 | * Avoid a dependency on goog.userAgent and keep our own reference of whether
249 | * the browser is IE.
250 | * @type {boolean}
251 | */
252 | goog.testing.TestCase.IS_IE = typeof opera == 'undefined' &&
253 | !!goog.global.navigator &&
254 | goog.global.navigator.userAgent.indexOf('MSIE') != -1;
255 |
256 |
257 | /**
258 | * Exception object that was detected before a test runs.
259 | * @type {*}
260 | * @protected
261 | */
262 | goog.testing.TestCase.prototype.exceptionBeforeTest;
263 |
264 |
265 | /**
266 | * Whether the test case has ever tried to execute.
267 | * @type {boolean}
268 | */
269 | goog.testing.TestCase.prototype.started = false;
270 |
271 |
272 | /**
273 | * Whether the test case is running.
274 | * @type {boolean}
275 | */
276 | goog.testing.TestCase.prototype.running = false;
277 |
278 |
279 | /**
280 | * Timestamp for when the test was started.
281 | * @type {number}
282 | * @private
283 | */
284 | goog.testing.TestCase.prototype.startTime_ = 0;
285 |
286 |
287 | /**
288 | * Time since the last batch of tests was started, if batchTime exceeds
289 | * {@link #maxRunTime} a timeout will be used to stop the tests blocking the
290 | * browser and a new batch will be started.
291 | * @type {number}
292 | * @private
293 | */
294 | goog.testing.TestCase.prototype.batchTime_ = 0;
295 |
296 |
297 | /**
298 | * Pointer to the current test.
299 | * @type {number}
300 | * @private
301 | */
302 | goog.testing.TestCase.prototype.currentTestPointer_ = 0;
303 |
304 |
305 | /**
306 | * Optional callback that will be executed when the test has finalized.
307 | * @type {Function}
308 | * @private
309 | */
310 | goog.testing.TestCase.prototype.onCompleteCallback_ = null;
311 |
312 |
313 | /**
314 | * Adds a new test to the test case.
315 | * @param {goog.testing.TestCase.Test} test The test to add.
316 | */
317 | goog.testing.TestCase.prototype.add = function(test) {
318 | if (this.started) {
319 | throw Error('Tests cannot be added after execute() has been called. ' +
320 | 'Test: ' + test.name);
321 | }
322 |
323 | this.tests_.push(test);
324 | };
325 |
326 |
327 | /**
328 | * Creates and adds a new test.
329 | *
330 | * Convenience function to make syntax less awkward when not using automatic
331 | * test discovery.
332 | *
333 | * @param {string} name The test name.
334 | * @param {!Function} ref Reference to the test function.
335 | * @param {!Object=} opt_scope Optional scope that the test function should be
336 | * called in.
337 | */
338 | goog.testing.TestCase.prototype.addNewTest = function(name, ref, opt_scope) {
339 | var test = new goog.testing.TestCase.Test(name, ref, opt_scope || this);
340 | this.add(test);
341 | };
342 |
343 |
344 | /**
345 | * Sets the tests.
346 | * @param {!Array<goog.testing.TestCase.Test>} tests A new test array.
347 | * @protected
348 | */
349 | goog.testing.TestCase.prototype.setTests = function(tests) {
350 | this.tests_ = tests;
351 | };
352 |
353 |
354 | /**
355 | * Gets the tests.
356 | * @return {!Array<goog.testing.TestCase.Test>} The test array.
357 | */
358 | goog.testing.TestCase.prototype.getTests = function() {
359 | return this.tests_;
360 | };
361 |
362 |
363 | /**
364 | * Returns the number of tests contained in the test case.
365 | * @return {number} The number of tests.
366 | */
367 | goog.testing.TestCase.prototype.getCount = function() {
368 | return this.tests_.length;
369 | };
370 |
371 |
372 | /**
373 | * Returns the number of tests actually run in the test case, i.e. subtracting
374 | * any which are skipped.
375 | * @return {number} The number of un-ignored tests.
376 | */
377 | goog.testing.TestCase.prototype.getActuallyRunCount = function() {
378 | return this.testsToRun_ ? goog.object.getCount(this.testsToRun_) : 0;
379 | };
380 |
381 |
382 | /**
383 | * Returns the current test and increments the pointer.
384 | * @return {goog.testing.TestCase.Test} The current test case.
385 | */
386 | goog.testing.TestCase.prototype.next = function() {
387 | var test;
388 | while ((test = this.tests_[this.currentTestPointer_++])) {
389 | if (!this.testsToRun_ || this.testsToRun_[test.name] ||
390 | this.testsToRun_[this.currentTestPointer_ - 1]) {
391 | return test;
392 | }
393 | }
394 | return null;
395 | };
396 |
397 |
398 | /**
399 | * Resets the test case pointer, so that next returns the first test.
400 | */
401 | goog.testing.TestCase.prototype.reset = function() {
402 | this.currentTestPointer_ = 0;
403 | this.result_ = new goog.testing.TestCase.Result(this);
404 | };
405 |
406 |
407 | /**
408 | * Sets the callback function that should be executed when the tests have
409 | * completed.
410 | * @param {Function} fn The callback function.
411 | */
412 | goog.testing.TestCase.prototype.setCompletedCallback = function(fn) {
413 | this.onCompleteCallback_ = fn;
414 | };
415 |
416 |
417 | /**
418 | * Can be overridden in test classes to indicate whether the tests in a case
419 | * should be run in that particular situation. For example, this could be used
420 | * to stop tests running in a particular browser, where browser support for
421 | * the class under test was absent.
422 | * @return {boolean} Whether any of the tests in the case should be run.
423 | */
424 | goog.testing.TestCase.prototype.shouldRunTests = function() {
425 | return true;
426 | };
427 |
428 |
429 | /**
430 | * Executes the tests, yielding asynchronously if execution time exceeds
431 | * {@link maxRunTime}. There is no guarantee that the test case has finished
432 | * once this method has returned. To be notified when the test case
433 | * has finished, use {@link #setCompletedCallback} or
434 | * {@link #runTestsReturningPromise}.
435 | */
436 | goog.testing.TestCase.prototype.execute = function() {
437 | if (!this.prepareForRun_()) {
438 | return;
439 | }
440 | this.log('Starting tests: ' + this.name_);
441 | this.cycleTests();
442 | };
443 |
444 |
445 | /**
446 | * Sets up the internal state of the test case for a run.
447 | * @return {boolean} If false, preparation failed because the test case
448 | * is not supposed to run in the present environment.
449 | * @private
450 | */
451 | goog.testing.TestCase.prototype.prepareForRun_ = function() {
452 | this.started = true;
453 | this.reset();
454 | this.startTime_ = this.now();
455 | this.running = true;
456 | this.result_.totalCount = this.getCount();
457 | if (!this.shouldRunTests()) {
458 | this.log('shouldRunTests() returned false, skipping these tests.');
459 | this.result_.testSuppressed = true;
460 | this.finalize();
461 | return false;
462 | }
463 | return true;
464 | };
465 |
466 |
467 | /**
468 | * Finalizes the test case, called when the tests have finished executing.
469 | */
470 | goog.testing.TestCase.prototype.finalize = function() {
471 | this.saveMessage('Done');
472 |
473 | this.tearDownPage();
474 |
475 | var restoredSetTimeout =
476 | goog.testing.TestCase.protectedSetTimeout_ == goog.global.setTimeout &&
477 | goog.testing.TestCase.protectedClearTimeout_ == goog.global.clearTimeout;
478 | if (!restoredSetTimeout && goog.testing.TestCase.IS_IE &&
479 | String(goog.global.setTimeout) ==
480 | goog.testing.TestCase.setTimeoutAsString_) {
481 | // In strange cases, IE's value of setTimeout *appears* to change, but
482 | // the string representation stays stable.
483 | restoredSetTimeout = true;
484 | }
485 |
486 | if (!restoredSetTimeout) {
487 | var message = 'ERROR: Test did not restore setTimeout and clearTimeout';
488 | this.saveMessage(message);
489 | var err = new goog.testing.TestCase.Error(this.name_, message);
490 | this.result_.errors.push(err);
491 | }
492 | goog.global.clearTimeout = goog.testing.TestCase.protectedClearTimeout_;
493 | goog.global.setTimeout = goog.testing.TestCase.protectedSetTimeout_;
494 | this.endTime_ = this.now();
495 | this.running = false;
496 | this.result_.runTime = this.endTime_ - this.startTime_;
497 | this.result_.numFilesLoaded = this.countNumFilesLoaded_();
498 | this.result_.complete = true;
499 |
500 | this.log(this.result_.getSummary());
501 | if (this.result_.isSuccess()) {
502 | this.log('Tests complete');
503 | } else {
504 | this.log('Tests Failed');
505 | }
506 | if (this.onCompleteCallback_) {
507 | var fn = this.onCompleteCallback_;
508 | // Execute's the completed callback in the context of the global object.
509 | fn();
510 | this.onCompleteCallback_ = null;
511 | }
512 | };
513 |
514 |
515 | /**
516 | * Saves a message to the result set.
517 | * @param {string} message The message to save.
518 | */
519 | goog.testing.TestCase.prototype.saveMessage = function(message) {
520 | this.result_.messages.push(this.getTimeStamp_() + ' ' + message);
521 | };
522 |
523 |
524 | /**
525 | * @return {boolean} Whether the test case is running inside the multi test
526 | * runner.
527 | */
528 | goog.testing.TestCase.prototype.isInsideMultiTestRunner = function() {
529 | var top = goog.global['top'];
530 | return top && typeof top['_allTests'] != 'undefined';
531 | };
532 |
533 |
534 | /**
535 | * Logs an object to the console, if available.
536 | * @param {*} val The value to log. Will be ToString'd.
537 | */
538 | goog.testing.TestCase.prototype.log = function(val) {
539 | if (!this.isInsideMultiTestRunner() && goog.global.console) {
540 | if (typeof val == 'string') {
541 | val = this.getTimeStamp_() + ' : ' + val;
542 | }
543 | if (val instanceof Error && val.stack) {
544 | // Chrome does console.log asynchronously in a different process
545 | // (http://code.google.com/p/chromium/issues/detail?id=50316).
546 | // This is an acute problem for Errors, which almost never survive.
547 | // Grab references to the immutable strings so they survive.
548 | goog.global.console.log(val, val.message, val.stack);
549 | // TODO(gboyer): Consider for Chrome cloning any object if we can ensure
550 | // there are no circular references.
551 | } else {
552 | goog.global.console.log(val);
553 | }
554 | }
555 | };
556 |
557 |
558 | /**
559 | * @return {boolean} Whether the test was a success.
560 | */
561 | goog.testing.TestCase.prototype.isSuccess = function() {
562 | return !!this.result_ && this.result_.isSuccess();
563 | };
564 |
565 |
566 | /**
567 | * Returns a string detailing the results from the test.
568 | * @param {boolean=} opt_verbose If true results will include data about all
569 | * tests, not just what failed.
570 | * @return {string} The results from the test.
571 | */
572 | goog.testing.TestCase.prototype.getReport = function(opt_verbose) {
573 | var rv = [];
574 |
575 | if (this.running) {
576 | rv.push(this.name_ + ' [RUNNING]');
577 | } else {
578 | var label = this.result_.isSuccess() ? 'PASSED' : 'FAILED';
579 | rv.push(this.name_ + ' [' + label + ']');
580 | }
581 |
582 | if (goog.global.location) {
583 | rv.push(this.trimPath_(goog.global.location.href));
584 | }
585 |
586 | rv.push(this.result_.getSummary());
587 |
588 | if (opt_verbose) {
589 | rv.push('.', this.result_.messages.join('\n'));
590 | } else if (!this.result_.isSuccess()) {
591 | rv.push(this.result_.errors.join('\n'));
592 | }
593 |
594 | rv.push(' ');
595 |
596 | return rv.join('\n');
597 | };
598 |
599 |
600 | /**
601 | * Returns the test results.
602 | * @return {!goog.testing.TestCase.Result}
603 | * @package
604 | */
605 | goog.testing.TestCase.prototype.getResult = function() {
606 | return this.result_;
607 | };
608 |
609 |
610 | /**
611 | * Returns the amount of time it took for the test to run.
612 | * @return {number} The run time, in milliseconds.
613 | */
614 | goog.testing.TestCase.prototype.getRunTime = function() {
615 | return this.result_.runTime;
616 | };
617 |
618 |
619 | /**
620 | * Returns the number of script files that were loaded in order to run the test.
621 | * @return {number} The number of script files.
622 | */
623 | goog.testing.TestCase.prototype.getNumFilesLoaded = function() {
624 | return this.result_.numFilesLoaded;
625 | };
626 |
627 |
628 | /**
629 | * Returns the test results object: a map from test names to a list of test
630 | * failures (if any exist).
631 | * @return {!Object<string, !Array<string>>} Tests results object.
632 | */
633 | goog.testing.TestCase.prototype.getTestResults = function() {
634 | return this.result_.resultsByName;
635 | };
636 |
637 |
638 | /**
639 | * Executes each of the tests, yielding asynchronously if execution time
640 | * exceeds {@link #maxRunTime}. There is no guarantee that the test case
641 | * has finished execution once this method has returned.
642 | * To be notified when the test case has finished execution, use
643 | * {@link #setCompletedCallback} or {@link #runTestsReturningPromise}.
644 | *
645 | * Overridable by the individual test case. This allows test cases to defer
646 | * when the test is actually started. If overridden, finalize must be called
647 | * by the test to indicate it has finished.
648 | */
649 | goog.testing.TestCase.prototype.runTests = function() {
650 | try {
651 | this.setUpPage();
652 | } catch (e) {
653 | this.exceptionBeforeTest = e;
654 | }
655 | this.execute();
656 | };
657 |
658 |
659 | /**
660 | * Executes each of the tests, returning a promise that resolves with the
661 | * test results once they are done running.
662 | * @return {!IThenable.<!goog.testing.TestCase.Result>}
663 | * @final
664 | * @package
665 | */
666 | goog.testing.TestCase.prototype.runTestsReturningPromise = function() {
667 | try {
668 | this.setUpPage();
669 | } catch (e) {
670 | this.exceptionBeforeTest = e;
671 | }
672 | if (!this.prepareForRun_()) {
673 | return goog.Promise.resolve(this.result_);
674 | }
675 | this.log('Starting tests: ' + this.name_);
676 | this.saveMessage('Start');
677 | this.batchTime_ = this.now();
678 | return new goog.Promise(function(resolve) {
679 | this.runNextTestCallback_ = resolve;
680 | this.runNextTest_();
681 | }, this);
682 | };
683 |
684 |
685 | /**
686 | * Executes the next test method synchronously or with promises, depending on
687 | * the test method's return value.
688 | *
689 | * If the test method returns a promise, the next test method will run once
690 | * the promise is resolved or rejected. If the test method does not
691 | * return a promise, it is assumed to be synchronous, and execution proceeds
692 | * immediately to the next test method. This means that test cases can run
693 | * partially synchronously and partially asynchronously, depending on
694 | * the return values of their test methods. In particular, a test case
695 | * executes synchronously until the first promise is returned from a
696 | * test method (or until a resource limit is reached; see
697 | * {@link finishTestInvocation_}).
698 | * @private
699 | */
700 | goog.testing.TestCase.prototype.runNextTest_ = function() {
701 | this.curTest_ = this.next();
702 | if (!this.curTest_ || !this.running) {
703 | this.finalize();
704 | this.runNextTestCallback_(this.result_);
705 | return;
706 | }
707 | this.result_.runCount++;
708 | this.log('Running test: ' + this.curTest_.name);
709 | if (this.maybeFailTestEarly(this.curTest_)) {
710 | this.finishTestInvocation_();
711 | return;
712 | }
713 | goog.testing.TestCase.currentTestName = this.curTest_.name;
714 | this.invokeTestFunction_(
715 | this.setUp, this.safeRunTest_, this.safeTearDown_);
716 | };
717 |
718 |
719 | /**
720 | * Calls the given test function, handling errors appropriately.
721 | * @private
722 | */
723 | goog.testing.TestCase.prototype.safeRunTest_ = function() {
724 | this.invokeTestFunction_(
725 | goog.bind(this.curTest_.ref, this.curTest_.scope),
726 | this.safeTearDown_,
727 | this.safeTearDown_);
728 | };
729 |
730 |
731 | /**
732 | * Calls {@link tearDown}, handling errors appropriately.
733 | * @param {*=} opt_error Error associated with the test, if any.
734 | * @private
735 | */
736 | goog.testing.TestCase.prototype.safeTearDown_ = function(opt_error) {
737 | if (arguments.length == 1) {
738 | this.doError(this.curTest_, opt_error);
739 | }
740 | this.invokeTestFunction_(
741 | this.tearDown, this.finishTestInvocation_, this.finishTestInvocation_);
742 | };
743 |
744 |
745 | /**
746 | * Calls the given {@code fn}, then calls either {@code onSuccess} or
747 | * {@code onFailure}, either synchronously or using promises, depending on
748 | * {@code fn}'s return value.
749 | *
750 | * If {@code fn} throws an exception, {@code onFailure} is called immediately
751 | * with the exception.
752 | *
753 | * If {@code fn} returns a promise, and the promise is eventually resolved,
754 | * {@code onSuccess} is called with no arguments. If the promise is eventually
755 | * rejected, {@code onFailure} is called with the rejection reason.
756 | *
757 | * Otherwise, if {@code fn} neither returns a promise nor throws an exception,
758 | * {@code onSuccess} is called immediately with no arguments.
759 | *
760 | * {@code fn}, {@code onSuccess}, and {@code onFailure} are all called with
761 | * the TestCase instance as the method receiver.
762 | *
763 | * @param {function()} fn The function to call.
764 | * @param {function()} onSuccess Success callback.
765 | * @param {function(*)} onFailure Failure callback.
766 | * @private
767 | */
768 | goog.testing.TestCase.prototype.invokeTestFunction_ = function(
769 | fn, onSuccess, onFailure) {
770 | try {
771 | var retval = fn.call(this);
772 | if (goog.Thenable.isImplementedBy(retval) ||
773 | goog.isFunction(retval && retval['then'])) {
774 | var self = this;
775 | retval.then(
776 | function() { onSuccess.call(self); },
777 | function(e) { onFailure.call(self, e); });
778 | } else {
779 | onSuccess.call(this);
780 | }
781 | } catch (e) {
782 | onFailure.call(this, e);
783 | }
784 | };
785 |
786 |
787 | /**
788 | * Finishes up bookkeeping for the current test function, and schedules
789 | * the next test function to run, either immediately or asychronously.
790 | * @param {*=} opt_error Optional error resulting from the test invocation.
791 | * @private
792 | */
793 | goog.testing.TestCase.prototype.finishTestInvocation_ = function(opt_error) {
794 | if (arguments.length == 1) {
795 | this.doError(this.curTest_, opt_error);
796 | }
797 |
798 | // If no errors have been recorded for the test, it is a success.
799 | if (!(this.curTest_.name in this.result_.resultsByName) ||
800 | !this.result_.resultsByName[this.curTest_.name].length) {
801 | this.doSuccess(this.curTest_);
802 | }
803 |
804 | goog.testing.TestCase.currentTestName = null;
805 |
806 | // If the test case has consumed too much time or stack space,
807 | // yield to avoid blocking the browser. Otherwise, proceed to the next test.
808 | if (this.depth_ > goog.testing.TestCase.MAX_STACK_DEPTH_ ||
809 | this.now() - this.batchTime_ > goog.testing.TestCase.maxRunTime) {
810 | this.saveMessage('Breaking async');
811 | this.batchTime_ = this.now();
812 | this.depth_ = 0;
813 | this.timeout(goog.bind(this.runNextTest_, this), 0);
814 | } else {
815 | ++this.depth_;
816 | this.runNextTest_();
817 | }
818 | };
819 |
820 |
821 | /**
822 | * Reorders the tests depending on the {@code order} field.
823 | * @param {Array<goog.testing.TestCase.Test>} tests An array of tests to
824 | * reorder.
825 | * @private
826 | */
827 | goog.testing.TestCase.prototype.orderTests_ = function(tests) {
828 | switch (this.order) {
829 | case goog.testing.TestCase.Order.RANDOM:
830 | // Fisher-Yates shuffle
831 | var i = tests.length;
832 | while (i > 1) {
833 | // goog.math.randomInt is inlined to reduce dependencies.
834 | var j = Math.floor(Math.random() * i); // exclusive
835 | i--;
836 | var tmp = tests[i];
837 | tests[i] = tests[j];
838 | tests[j] = tmp;
839 | }
840 | break;
841 |
842 | case goog.testing.TestCase.Order.SORTED:
843 | tests.sort(function(t1, t2) {
844 | if (t1.name == t2.name) {
845 | return 0;
846 | }
847 | return t1.name < t2.name ? -1 : 1;
848 | });
849 | break;
850 |
851 | // Do nothing for NATURAL.
852 | }
853 | };
854 |
855 |
856 | /**
857 | * Gets list of objects that potentially contain test cases. For IE 8 and below,
858 | * this is the global "this" (for properties set directly on the global this or
859 | * window) and the RuntimeObject (for global variables and functions). For all
860 | * other browsers, the array simply contains the global this.
861 | *
862 | * @param {string=} opt_prefix An optional prefix. If specified, only get things
863 | * under this prefix. Note that the prefix is only honored in IE, since it
864 | * supports the RuntimeObject:
865 | * http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx
866 | * TODO: Remove this option.
867 | * @return {!Array<!Object>} A list of objects that should be inspected.
868 | */
869 | goog.testing.TestCase.prototype.getGlobals = function(opt_prefix) {
870 | return goog.testing.TestCase.getGlobals(opt_prefix);
871 | };
872 |
873 |
874 | /**
875 | * Gets list of objects that potentially contain test cases. For IE 8 and below,
876 | * this is the global "this" (for properties set directly on the global this or
877 | * window) and the RuntimeObject (for global variables and functions). For all
878 | * other browsers, the array simply contains the global this.
879 | *
880 | * @param {string=} opt_prefix An optional prefix. If specified, only get things
881 | * under this prefix. Note that the prefix is only honored in IE, since it
882 | * supports the RuntimeObject:
883 | * http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx
884 | * TODO: Remove this option.
885 | * @return {!Array<!Object>} A list of objects that should be inspected.
886 | */
887 | goog.testing.TestCase.getGlobals = function(opt_prefix) {
888 | // Look in the global scope for most browsers, on IE we use the little known
889 | // RuntimeObject which holds references to all globals. We reference this
890 | // via goog.global so that there isn't an aliasing that throws an exception
891 | // in Firefox.
892 | return typeof goog.global['RuntimeObject'] != 'undefined' ?
893 | [goog.global['RuntimeObject']((opt_prefix || '') + '*'), goog.global] :
894 | [goog.global];
895 | };
896 |
897 |
898 | /**
899 | * Gets called before any tests are executed. Can be overridden to set up the
900 | * environment for the whole test case.
901 | */
902 | goog.testing.TestCase.prototype.setUpPage = function() {};
903 |
904 |
905 | /**
906 | * Gets called after all tests have been executed. Can be overridden to tear
907 | * down the entire test case.
908 | */
909 | goog.testing.TestCase.prototype.tearDownPage = function() {};
910 |
911 |
912 | /**
913 | * Gets called before every goog.testing.TestCase.Test is been executed. Can be
914 | * overridden to add set up functionality to each test.
915 | */
916 | goog.testing.TestCase.prototype.setUp = function() {};
917 |
918 |
919 | /**
920 | * Gets called after every goog.testing.TestCase.Test has been executed. Can be
921 | * overriden to add tear down functionality to each test.
922 | */
923 | goog.testing.TestCase.prototype.tearDown = function() {};
924 |
925 |
926 | /**
927 | * @return {string} The function name prefix used to auto-discover tests.
928 | * @protected
929 | */
930 | goog.testing.TestCase.prototype.getAutoDiscoveryPrefix = function() {
931 | return 'test';
932 | };
933 |
934 |
935 | /**
936 | * @return {number} Time since the last batch of tests was started.
937 | * @protected
938 | */
939 | goog.testing.TestCase.prototype.getBatchTime = function() {
940 | return this.batchTime_;
941 | };
942 |
943 |
944 | /**
945 | * @param {number} batchTime Time since the last batch of tests was started.
946 | * @protected
947 | */
948 | goog.testing.TestCase.prototype.setBatchTime = function(batchTime) {
949 | this.batchTime_ = batchTime;
950 | };
951 |
952 |
953 | /**
954 | * Creates a {@code goog.testing.TestCase.Test} from an auto-discovered
955 | * function.
956 | * @param {string} name The name of the function.
957 | * @param {function() : void} ref The auto-discovered function.
958 | * @return {!goog.testing.TestCase.Test} The newly created test.
959 | * @protected
960 | */
961 | goog.testing.TestCase.prototype.createTestFromAutoDiscoveredFunction =
962 | function(name, ref) {
963 | return new goog.testing.TestCase.Test(name, ref, goog.global);
964 | };
965 |
966 |
967 | /**
968 | * Adds any functions defined in the global scope that correspond to
969 | * lifecycle events for the test case. Overrides setUp, tearDown, setUpPage,
970 | * tearDownPage and runTests if they are defined.
971 | */
972 | goog.testing.TestCase.prototype.autoDiscoverLifecycle = function() {
973 | if (goog.global['setUp']) {
974 | this.setUp = goog.bind(goog.global['setUp'], goog.global);
975 | }
976 | if (goog.global['tearDown']) {
977 | this.tearDown = goog.bind(goog.global['tearDown'], goog.global);
978 | }
979 | if (goog.global['setUpPage']) {
980 | this.setUpPage = goog.bind(goog.global['setUpPage'], goog.global);
981 | }
982 | if (goog.global['tearDownPage']) {
983 | this.tearDownPage = goog.bind(goog.global['tearDownPage'], goog.global);
984 | }
985 | if (goog.global['runTests']) {
986 | this.runTests = goog.bind(goog.global['runTests'], goog.global);
987 | }
988 | if (goog.global['shouldRunTests']) {
989 | this.shouldRunTests = goog.bind(goog.global['shouldRunTests'], goog.global);
990 | }
991 | };
992 |
993 |
994 | /**
995 | * Adds any functions defined in the global scope that are prefixed with "test"
996 | * to the test case.
997 | */
998 | goog.testing.TestCase.prototype.autoDiscoverTests = function() {
999 | var prefix = this.getAutoDiscoveryPrefix();
1000 | var testSources = this.getGlobals(prefix);
1001 |
1002 | var foundTests = [];
1003 |
1004 | for (var i = 0; i < testSources.length; i++) {
1005 | var testSource = testSources[i];
1006 | for (var name in testSource) {
1007 | if ((new RegExp('^' + prefix)).test(name)) {
1008 | var ref;
1009 | try {
1010 | ref = testSource[name];
1011 | } catch (ex) {
1012 | // NOTE(brenneman): When running tests from a file:// URL on Firefox
1013 | // 3.5 for Windows, any reference to goog.global.sessionStorage raises
1014 | // an "Operation is not supported" exception. Ignore any exceptions
1015 | // raised by simply accessing global properties.
1016 | ref = undefined;
1017 | }
1018 |
1019 | if (goog.isFunction(ref)) {
1020 | foundTests.push(this.createTestFromAutoDiscoveredFunction(name, ref));
1021 | }
1022 | }
1023 | }
1024 | }
1025 |
1026 | this.orderTests_(foundTests);
1027 |
1028 | for (var i = 0; i < foundTests.length; i++) {
1029 | this.add(foundTests[i]);
1030 | }
1031 |
1032 | this.log(this.getCount() + ' tests auto-discovered');
1033 |
1034 | // TODO(user): Do this as a separate call. Unfortunately, a lot of projects
1035 | // currently override autoDiscoverTests and expect lifecycle events to be
1036 | // registered as a part of this call.
1037 | this.autoDiscoverLifecycle();
1038 | };
1039 |
1040 |
1041 | /**
1042 | * Checks to see if the test should be marked as failed before it is run.
1043 | *
1044 | * If there was an error in setUpPage, we treat that as a failure for all tests
1045 | * and mark them all as having failed.
1046 | *
1047 | * @param {goog.testing.TestCase.Test} testCase The current test case.
1048 | * @return {boolean} Whether the test was marked as failed.
1049 | * @protected
1050 | */
1051 | goog.testing.TestCase.prototype.maybeFailTestEarly = function(testCase) {
1052 | if (this.exceptionBeforeTest) {
1053 | // We just use the first error to report an error on a failed test.
1054 | this.curTest_.name = 'setUpPage for ' + this.curTest_.name;
1055 | this.doError(this.curTest_, this.exceptionBeforeTest);
1056 | return true;
1057 | }
1058 | return false;
1059 | };
1060 |
1061 |
1062 | /**
1063 | * Cycles through the tests, yielding asynchronously if the execution time
1064 | * execeeds {@link #maxRunTime}. In particular, there is no guarantee that
1065 | * the test case has finished execution once this method has returned.
1066 | * To be notified when the test case has finished execution, use
1067 | * {@link #setCompletedCallback} or {@link #runTestsReturningPromise}.
1068 | */
1069 | goog.testing.TestCase.prototype.cycleTests = function() {
1070 | this.saveMessage('Start');
1071 | this.batchTime_ = this.now();
1072 | if (this.running) {
1073 | this.runNextTestCallback_ = goog.nullFunction;
1074 | // Kick off the tests. runNextTest_ will schedule all of the tests,
1075 | // using a mixture of synchronous and asynchronous strategies.
1076 | this.runNextTest_();
1077 | }
1078 | };
1079 |
1080 |
1081 | /**
1082 | * Counts the number of files that were loaded for dependencies that are
1083 | * required to run the test.
1084 | * @return {number} The number of files loaded.
1085 | * @private
1086 | */
1087 | goog.testing.TestCase.prototype.countNumFilesLoaded_ = function() {
1088 | var scripts = document.getElementsByTagName('script');
1089 | var count = 0;
1090 | for (var i = 0, n = scripts.length; i < n; i++) {
1091 | if (scripts[i].src) {
1092 | count++;
1093 | }
1094 | }
1095 | return count;
1096 | };
1097 |
1098 |
1099 | /**
1100 | * Calls a function after a delay, using the protected timeout.
1101 | * @param {Function} fn The function to call.
1102 | * @param {number} time Delay in milliseconds.
1103 | * @return {number} The timeout id.
1104 | * @protected
1105 | */
1106 | goog.testing.TestCase.prototype.timeout = function(fn, time) {
1107 | // NOTE: invoking protectedSetTimeout_ as a member of goog.testing.TestCase
1108 | // would result in an Illegal Invocation error. The method must be executed
1109 | // with the global context.
1110 | var protectedSetTimeout = goog.testing.TestCase.protectedSetTimeout_;
1111 | return protectedSetTimeout(fn, time);
1112 | };
1113 |
1114 |
1115 | /**
1116 | * Clears a timeout created by {@code this.timeout()}.
1117 | * @param {number} id A timeout id.
1118 | * @protected
1119 | */
1120 | goog.testing.TestCase.prototype.clearTimeout = function(id) {
1121 | // NOTE: see execution note for protectedSetTimeout above.
1122 | var protectedClearTimeout = goog.testing.TestCase.protectedClearTimeout_;
1123 | protectedClearTimeout(id);
1124 | };
1125 |
1126 |
1127 | /**
1128 | * @return {number} The current time in milliseconds, don't use goog.now as some
1129 | * tests override it.
1130 | * @protected
1131 | */
1132 | goog.testing.TestCase.prototype.now = function() {
1133 | // Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223.
1134 | var protectedDate = goog.testing.TestCase.protectedDate_;
1135 | return new protectedDate().getTime();
1136 | };
1137 |
1138 |
1139 | /**
1140 | * Returns the current time.
1141 | * @return {string} HH:MM:SS.
1142 | * @private
1143 | */
1144 | goog.testing.TestCase.prototype.getTimeStamp_ = function() {
1145 | // Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223.
1146 | var protectedDate = goog.testing.TestCase.protectedDate_;
1147 | var d = new protectedDate();
1148 |
1149 | // Ensure millis are always 3-digits
1150 | var millis = '00' + d.getMilliseconds();
1151 | millis = millis.substr(millis.length - 3);
1152 |
1153 | return this.pad_(d.getHours()) + ':' + this.pad_(d.getMinutes()) + ':' +
1154 | this.pad_(d.getSeconds()) + '.' + millis;
1155 | };
1156 |
1157 |
1158 | /**
1159 | * Pads a number to make it have a leading zero if it's less than 10.
1160 | * @param {number} number The number to pad.
1161 | * @return {string} The resulting string.
1162 | * @private
1163 | */
1164 | goog.testing.TestCase.prototype.pad_ = function(number) {
1165 | return number < 10 ? '0' + number : String(number);
1166 | };
1167 |
1168 |
1169 | /**
1170 | * Trims a path to be only that after google3.
1171 | * @param {string} path The path to trim.
1172 | * @return {string} The resulting string.
1173 | * @private
1174 | */
1175 | goog.testing.TestCase.prototype.trimPath_ = function(path) {
1176 | return path.substring(path.indexOf('google3') + 8);
1177 | };
1178 |
1179 |
1180 | /**
1181 | * Handles a test that passed.
1182 | * @param {goog.testing.TestCase.Test} test The test that passed.
1183 | * @protected
1184 | */
1185 | goog.testing.TestCase.prototype.doSuccess = function(test) {
1186 | this.result_.successCount++;
1187 | // An empty list of error messages indicates that the test passed.
1188 | // If we already have a failure for this test, do not set to empty list.
1189 | if (!(test.name in this.result_.resultsByName)) {
1190 | this.result_.resultsByName[test.name] = [];
1191 | }
1192 | var message = test.name + ' : PASSED';
1193 | this.saveMessage(message);
1194 | this.log(message);
1195 | };
1196 |
1197 |
1198 | /**
1199 | * Handles a test that failed.
1200 | * @param {goog.testing.TestCase.Test} test The test that failed.
1201 | * @param {*=} opt_e The exception object associated with the
1202 | * failure or a string.
1203 | * @protected
1204 | */
1205 | goog.testing.TestCase.prototype.doError = function(test, opt_e) {
1206 | var message = test.name + ' : FAILED';
1207 | this.log(message);
1208 | this.saveMessage(message);
1209 | var err = this.logError(test.name, opt_e);
1210 | this.result_.errors.push(err);
1211 | if (test.name in this.result_.resultsByName) {
1212 | this.result_.resultsByName[test.name].push(err.toString());
1213 | } else {
1214 | this.result_.resultsByName[test.name] = [err.toString()];
1215 | }
1216 | };
1217 |
1218 |
1219 | /**
1220 | * @param {string} name Failed test name.
1221 | * @param {*=} opt_e The exception object associated with the
1222 | * failure or a string.
1223 | * @return {!goog.testing.TestCase.Error} Error object.
1224 | */
1225 | goog.testing.TestCase.prototype.logError = function(name, opt_e) {
1226 | var errMsg = null;
1227 | var stack = null;
1228 | if (opt_e) {
1229 | this.log(opt_e);
1230 | if (goog.isString(opt_e)) {
1231 | errMsg = opt_e;
1232 | } else {
1233 | errMsg = opt_e.message || opt_e.description || opt_e.toString();
1234 | stack = opt_e.stack ? goog.testing.stacktrace.canonicalize(opt_e.stack) :
1235 | opt_e['stackTrace'];
1236 | }
1237 | } else {
1238 | errMsg = 'An unknown error occurred';
1239 | }
1240 | var err = new goog.testing.TestCase.Error(name, errMsg, stack);
1241 |
1242 | // Avoid double logging.
1243 | if (!opt_e || !opt_e['isJsUnitException'] ||
1244 | !opt_e['loggedJsUnitException']) {
1245 | this.saveMessage(err.toString());
1246 | }
1247 | if (opt_e && opt_e['isJsUnitException']) {
1248 | opt_e['loggedJsUnitException'] = true;
1249 | }
1250 |
1251 | return err;
1252 | };
1253 |
1254 |
1255 |
1256 | /**
1257 | * A class representing a single test function.
1258 | * @param {string} name The test name.
1259 | * @param {Function} ref Reference to the test function.
1260 | * @param {Object=} opt_scope Optional scope that the test function should be
1261 | * called in.
1262 | * @constructor
1263 | */
1264 | goog.testing.TestCase.Test = function(name, ref, opt_scope) {
1265 | /**
1266 | * The name of the test.
1267 | * @type {string}
1268 | */
1269 | this.name = name;
1270 |
1271 | /**
1272 | * Reference to the test function.
1273 | * @type {Function}
1274 | */
1275 | this.ref = ref;
1276 |
1277 | /**
1278 | * Scope that the test function should be called in.
1279 | * @type {Object}
1280 | */
1281 | this.scope = opt_scope || null;
1282 | };
1283 |
1284 |
1285 | /**
1286 | * Executes the test function.
1287 | * @package
1288 | */
1289 | goog.testing.TestCase.Test.prototype.execute = function() {
1290 | this.ref.call(this.scope);
1291 | };
1292 |
1293 |
1294 |
1295 | /**
1296 | * A class for representing test results. A bag of public properties.
1297 | * @param {goog.testing.TestCase} testCase The test case that owns this result.
1298 | * @constructor
1299 | * @final
1300 | */
1301 | goog.testing.TestCase.Result = function(testCase) {
1302 | /**
1303 | * The test case that owns this result.
1304 | * @type {goog.testing.TestCase}
1305 | * @private
1306 | */
1307 | this.testCase_ = testCase;
1308 |
1309 | /**
1310 | * Total number of tests that should have been run.
1311 | * @type {number}
1312 | */
1313 | this.totalCount = 0;
1314 |
1315 | /**
1316 | * Total number of tests that were actually run.
1317 | * @type {number}
1318 | */
1319 | this.runCount = 0;
1320 |
1321 | /**
1322 | * Number of successful tests.
1323 | * @type {number}
1324 | */
1325 | this.successCount = 0;
1326 |
1327 | /**
1328 | * The amount of time the tests took to run.
1329 | * @type {number}
1330 | */
1331 | this.runTime = 0;
1332 |
1333 | /**
1334 | * The number of files loaded to run this test.
1335 | * @type {number}
1336 | */
1337 | this.numFilesLoaded = 0;
1338 |
1339 | /**
1340 | * Whether this test case was suppressed by shouldRunTests() returning false.
1341 | * @type {boolean}
1342 | */
1343 | this.testSuppressed = false;
1344 |
1345 | /**
1346 | * Test results for each test that was run. The test name is always added
1347 | * as the key in the map, and the array of strings is an optional list
1348 | * of failure messages. If the array is empty, the test passed. Otherwise,
1349 | * the test failed.
1350 | * @type {!Object<string, !Array<string>>}
1351 | */
1352 | this.resultsByName = {};
1353 |
1354 | /**
1355 | * Errors encountered while running the test.
1356 | * @type {!Array<goog.testing.TestCase.Error>}
1357 | */
1358 | this.errors = [];
1359 |
1360 | /**
1361 | * Messages to show the user after running the test.
1362 | * @type {!Array<string>}
1363 | */
1364 | this.messages = [];
1365 |
1366 | /**
1367 | * Whether the tests have completed.
1368 | * @type {boolean}
1369 | */
1370 | this.complete = false;
1371 | };
1372 |
1373 |
1374 | /**
1375 | * @return {boolean} Whether the test was successful.
1376 | */
1377 | goog.testing.TestCase.Result.prototype.isSuccess = function() {
1378 | return this.complete && this.errors.length == 0;
1379 | };
1380 |
1381 |
1382 | /**
1383 | * @return {string} A summary of the tests, including total number of tests that
1384 | * passed, failed, and the time taken.
1385 | */
1386 | goog.testing.TestCase.Result.prototype.getSummary = function() {
1387 | var summary = this.runCount + ' of ' + this.totalCount + ' tests run in ' +
1388 | this.runTime + 'ms.\n';
1389 | if (this.testSuppressed) {
1390 | summary += 'Tests not run because shouldRunTests() returned false.';
1391 | } else {
1392 | var failures = this.totalCount - this.successCount;
1393 | var suppressionMessage = '';
1394 |
1395 | var countOfRunTests = this.testCase_.getActuallyRunCount();
1396 | if (countOfRunTests) {
1397 | failures = countOfRunTests - this.successCount;
1398 | suppressionMessage = ', ' +
1399 | (this.totalCount - countOfRunTests) + ' suppressed by querystring';
1400 | }
1401 | summary += this.successCount + ' passed, ' +
1402 | failures + ' failed' + suppressionMessage + '.\n' +
1403 | Math.round(this.runTime / this.runCount) + ' ms/test. ' +
1404 | this.numFilesLoaded + ' files loaded.';
1405 | }
1406 |
1407 | return summary;
1408 | };
1409 |
1410 |
1411 | /**
1412 | * Initializes the given test case with the global test runner 'G_testRunner'.
1413 | * @param {goog.testing.TestCase} testCase The test case to install.
1414 | */
1415 | goog.testing.TestCase.initializeTestRunner = function(testCase) {
1416 | testCase.autoDiscoverTests();
1417 | var gTestRunner = goog.global['G_testRunner'];
1418 | if (gTestRunner) {
1419 | gTestRunner['initialize'](testCase);
1420 | } else {
1421 | throw Error('G_testRunner is undefined. Please ensure goog.testing.jsunit' +
1422 | ' is included.');
1423 | }
1424 | };
1425 |
1426 |
1427 |
1428 | /**
1429 | * A class representing an error thrown by the test
1430 | * @param {string} source The name of the test which threw the error.
1431 | * @param {string} message The error message.
1432 | * @param {string=} opt_stack A string showing the execution stack.
1433 | * @constructor
1434 | * @final
1435 | */
1436 | goog.testing.TestCase.Error = function(source, message, opt_stack) {
1437 | /**
1438 | * The name of the test which threw the error.
1439 | * @type {string}
1440 | */
1441 | this.source = source;
1442 |
1443 | /**
1444 | * Reference to the test function.
1445 | * @type {string}
1446 | */
1447 | this.message = message;
1448 |
1449 | /**
1450 | * Scope that the test function should be called in.
1451 | * @type {?string}
1452 | */
1453 | this.stack = opt_stack || null;
1454 | };
1455 |
1456 |
1457 | /**
1458 | * Returns a string representing the error object.
1459 | * @return {string} A string representation of the error.
1460 | * @override
1461 | */
1462 | goog.testing.TestCase.Error.prototype.toString = function() {
1463 | return 'ERROR in ' + this.source + '\n' +
1464 | this.message + (this.stack ? '\n' + this.stack : '');
1465 | };