1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/**
6 * Tests that an observation matches the expected value.
7 * @param {Object} expected The expected value.
8 * @param {Object} observed The actual value.
9 * @param {string=} opt_message Optional message to include with a test
10 *     failure.
11 */
12function assertEquals(expected, observed, opt_message) {
13  if (observed !== expected) {
14    var message = 'Assertion Failed\n  Observed: ' + observed +
15        '\n  Expected: ' + expected;
16    if (opt_message)
17      message = message + '\n  ' + opt_message;
18    throw new Error(message);
19  }
20}
21
22/**
23 * Verifies that a test result is true.
24 * @param {boolean} observed The observed value.
25 * @param {string=} opt_message Optional message to include with a test
26 *     failure.
27 */
28function assertTrue(observed, opt_message) {
29  assertEquals(true, observed, opt_message);
30}
31
32/**
33 * Verifies that a test result is false.
34 * @param {boolean} observed The observed value.
35 * @param {string=} opt_message Optional message to include with a test
36 *     failure.
37 */
38function assertFalse(observed, opt_message) {
39  assertEquals(false, observed, opt_message);
40}
41
42/**
43 * Verifies that the observed and reference values differ.
44 * @param {Object} reference The target value for comparison.
45 * @param {Object} observed The test result.
46 * @param {string=} opt_message Optional message to include with a test
47 *     failure.
48 */
49function assertNotEqual(reference, observed, opt_message) {
50  if (observed === reference) {
51    var message = 'Assertion Failed\n  Observed: ' + observed +
52        '\n  Reference: ' + reference;
53    if (opt_message)
54      message = message + '\n  ' + opt_message;
55    throw new Error(message);
56  }
57}
58
59/**
60 * Verifies that a test evaluation results in an exception.
61 * @param {!Function} f The test function.
62 */
63function assertThrows(f) {
64  var triggeredError = false;
65  try {
66    f();
67  } catch(err) {
68    triggeredError = true;
69  }
70  if (!triggeredError)
71    throw new Error('Assertion Failed: throw expected.');
72}
73
74/**
75 * Verifies that the contents of the expected and observed arrays match.
76 * @param {!Array} expected The expected result.
77 * @param {!Array} observed The actual result.
78 */
79function assertArrayEquals(expected, observed) {
80  var v1 = Array.prototype.slice.call(expected);
81  var v2 = Array.prototype.slice.call(observed);
82  var equal = v1.length == v2.length;
83  if (equal) {
84    for (var i = 0; i < v1.length; i++) {
85      if (v1[i] !== v2[i]) {
86        equal = false;
87        break;
88      }
89    }
90  }
91  if (!equal) {
92    var message =
93       ['Assertion Failed', 'Observed: ' + v2, 'Expected: ' + v1].join('\n  ');
94    throw new Error(message);
95  }
96}
97
98/**
99 * Verifies that the expected and observed result have the same content.
100 * @param {*} expected The expected result.
101 * @param {*} observed The actual result.
102 */
103function assertDeepEquals(expected, observed, opt_message) {
104  if (typeof expected == 'object' && expected != null) {
105    assertNotEqual(null, observed);
106    for (var key in expected) {
107      assertTrue(key in observed, opt_message);
108      assertDeepEquals(expected[key], observed[key], opt_message);
109    }
110    for (var key in observed) {
111      assertTrue(key in expected, opt_message);
112    }
113  } else {
114    assertEquals(expected, observed, opt_message);
115  }
116}
117
118/**
119 * Defines runTests.
120 */
121(function(exports) {
122  /**
123   * List of test cases.
124   * @type {Array.<string>} List of function names for tests to run.
125   */
126  var testCases = [];
127
128  /**
129   * Indicates if all tests have run successfully.
130   * @type {boolean}
131   */
132  var cleanTestRun = true;
133
134  /**
135   * Armed during setup of a test to call the matching tear down code.
136   * @type {Function}
137   */
138  var pendingTearDown = null;
139
140  /**
141   * Runs all functions starting with test and reports success or
142   * failure of the test suite.
143   */
144  function runTests() {
145    for (var name in window) {
146      if (typeof window[name] == 'function' && /^test/.test(name))
147        testCases.push(name);
148    }
149    if (!testCases.length) {
150      console.error('Failed to find test cases.');
151      cleanTestRun = false;
152    }
153    continueTesting();
154  }
155
156  /**
157   * Runs the next test in the queue. Reports the test results if the queue is
158   * empty.
159   * @param {boolean=} opt_asyncTestFailure Optional parameter indicated if the
160   *     last asynchronous test failed.
161   */
162  function continueTesting(opt_asyncTestFailure) {
163    if (opt_asyncTestFailure)
164      cleanTestRun = false;
165    var done = false;
166    if (pendingTearDown) {
167      pendingTearDown();
168      pendingTearDown = null;
169    }
170    if (testCases.length > 0) {
171      var fn = testCases.pop();
172      var isAsyncTest = window[fn].length;
173      try {
174        if (window.setUp)
175          window.setUp();
176        pendingTearDown = window.tearDown;
177        window[fn](continueTesting);
178      } catch(err) {
179        console.error('Failure in test ' + fn + '\n' + err);
180        console.log(err.stack);
181        cleanTestRun = false;
182      }
183      // Asynchronous tests must manually call continueTesting when complete.
184      if (!isAsyncTest)
185        continueTesting();
186    } else {
187      done = true;
188      endTests(cleanTestRun);
189    }
190    if (!done) {
191      domAutomationController.setAutomationId(1);
192      domAutomationController.send('PENDING');
193    }
194  };
195
196  exports.runTests = runTests;
197})(this);
198
199/**
200 * Signals completion of a test.
201 * @param {boolean} success Indicates if the test completed successfully.
202 */
203function endTests(success) {
204  domAutomationController.setAutomationId(1);
205  domAutomationController.send(success ? 'SUCCESS' : 'FAILURE');
206}
207
208window.onerror = function() {
209  endTests(false);
210};
211