expectedfailures.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // Copyright 2008 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. * @fileoverview Helper class to allow for expected unit test failures.
  16. *
  17. * @author robbyw@google.com (Robby Walker)
  18. */
  19. goog.setTestOnly('goog.testing.ExpectedFailures');
  20. goog.provide('goog.testing.ExpectedFailures');
  21. goog.require('goog.asserts');
  22. goog.require('goog.debug.DivConsole');
  23. goog.require('goog.dom');
  24. goog.require('goog.dom.TagName');
  25. goog.require('goog.events');
  26. goog.require('goog.events.EventType');
  27. goog.require('goog.log');
  28. goog.require('goog.style');
  29. goog.require('goog.testing.JsUnitException');
  30. goog.require('goog.testing.TestCase');
  31. goog.require('goog.testing.asserts');
  32. /**
  33. * Helper class for allowing some unit tests to fail, particularly designed to
  34. * mark tests that should be fixed on a given browser.
  35. *
  36. * <pre>
  37. * var expectedFailures = new goog.testing.ExpectedFailures();
  38. *
  39. * function tearDown() {
  40. * expectedFailures.handleTearDown();
  41. * }
  42. *
  43. * function testSomethingThatBreaksInWebKit() {
  44. * expectedFailures.expectFailureFor(goog.userAgent.WEBKIT);
  45. *
  46. * try {
  47. * ...
  48. * assert(somethingThatFailsInWebKit);
  49. * ...
  50. * } catch (e) {
  51. * expectedFailures.handleException(e);
  52. * }
  53. * }
  54. * </pre>
  55. *
  56. * @constructor
  57. * @final
  58. */
  59. goog.testing.ExpectedFailures = function() {
  60. goog.testing.ExpectedFailures.setUpConsole_();
  61. this.reset_();
  62. };
  63. /**
  64. * The lazily created debugging console.
  65. * @type {goog.debug.DivConsole?}
  66. * @private
  67. */
  68. goog.testing.ExpectedFailures.console_ = null;
  69. /**
  70. * Logger for the expected failures.
  71. * @type {goog.log.Logger}
  72. * @private
  73. */
  74. goog.testing.ExpectedFailures.prototype.logger_ =
  75. goog.log.getLogger('goog.testing.ExpectedFailures');
  76. /**
  77. * Whether or not we are expecting failure.
  78. * @type {boolean}
  79. * @private
  80. */
  81. goog.testing.ExpectedFailures.prototype.expectingFailure_;
  82. /**
  83. * The string to emit upon an expected failure.
  84. * @type {string}
  85. * @private
  86. */
  87. goog.testing.ExpectedFailures.prototype.failureMessage_;
  88. /**
  89. * An array of suppressed failures.
  90. * @type {Array<!Error>}
  91. * @private
  92. */
  93. goog.testing.ExpectedFailures.prototype.suppressedFailures_;
  94. /**
  95. * Sets up the debug console, if it isn't already set up.
  96. * @private
  97. */
  98. goog.testing.ExpectedFailures.setUpConsole_ = function() {
  99. if (!goog.testing.ExpectedFailures.console_) {
  100. var xButton = goog.dom.createDom(
  101. goog.dom.TagName.DIV, {
  102. 'style': 'position: absolute; border-left:1px solid #333;' +
  103. 'border-bottom:1px solid #333; right: 0; top: 0; width: 1em;' +
  104. 'height: 1em; cursor: pointer; background-color: #cde;' +
  105. 'text-align: center; color: black'
  106. },
  107. 'X');
  108. var div = goog.dom.createDom(
  109. goog.dom.TagName.DIV, {
  110. 'style': 'position: absolute; border: 1px solid #333; right: 10px;' +
  111. 'top : 10px; width: 400px; display: none'
  112. },
  113. xButton);
  114. document.body.appendChild(div);
  115. goog.events.listen(xButton, goog.events.EventType.CLICK, function() {
  116. goog.style.setElementShown(div, false);
  117. });
  118. goog.testing.ExpectedFailures.console_ = new goog.debug.DivConsole(div);
  119. goog.log.addHandler(
  120. goog.testing.ExpectedFailures.prototype.logger_,
  121. goog.bind(goog.style.setElementShown, null, div, true));
  122. goog.log.addHandler(
  123. goog.testing.ExpectedFailures.prototype.logger_,
  124. goog.bind(
  125. goog.testing.ExpectedFailures.console_.addLogRecord,
  126. goog.testing.ExpectedFailures.console_));
  127. }
  128. };
  129. /**
  130. * Register to expect failure for the given condition. Multiple calls to this
  131. * function act as a boolean OR. The first applicable message will be used.
  132. * @param {boolean} condition Whether to expect failure.
  133. * @param {string=} opt_message Descriptive message of this expected failure.
  134. */
  135. goog.testing.ExpectedFailures.prototype.expectFailureFor = function(
  136. condition, opt_message) {
  137. this.expectingFailure_ = this.expectingFailure_ || condition;
  138. if (condition) {
  139. this.failureMessage_ = this.failureMessage_ || opt_message || '';
  140. }
  141. };
  142. /**
  143. * Determines if the given exception was expected.
  144. * @param {Object} ex The exception to check.
  145. * @return {boolean} Whether the exception was expected.
  146. */
  147. goog.testing.ExpectedFailures.prototype.isExceptionExpected = function(ex) {
  148. return this.expectingFailure_ && ex instanceof goog.testing.JsUnitException;
  149. };
  150. /**
  151. * Handle an exception, suppressing it if it is a unit test failure that we
  152. * expected.
  153. * @param {Error} ex The exception to handle.
  154. */
  155. goog.testing.ExpectedFailures.prototype.handleException = function(ex) {
  156. if (this.isExceptionExpected(ex)) {
  157. goog.asserts.assertInstanceof(ex, goog.testing.JsUnitException);
  158. goog.log.info(
  159. this.logger_, 'Suppressing test failure in ' +
  160. goog.testing.TestCase.currentTestName + ':' +
  161. (this.failureMessage_ ? '\n(' + this.failureMessage_ + ')' : ''),
  162. ex);
  163. this.suppressedFailures_.push(ex);
  164. goog.testing.TestCase.invalidateAssertionException(ex);
  165. return;
  166. }
  167. // Rethrow the exception if we weren't expecting it or if it is a normal
  168. // exception.
  169. throw ex;
  170. };
  171. /**
  172. * Run the given function, catching any expected failures.
  173. * @param {Function} func The function to run.
  174. * @param {boolean=} opt_lenient Whether to ignore if the expected failures
  175. * didn't occur. In this case a warning will be logged in handleTearDown.
  176. */
  177. goog.testing.ExpectedFailures.prototype.run = function(func, opt_lenient) {
  178. try {
  179. func();
  180. } catch (ex) {
  181. this.handleException(ex);
  182. }
  183. if (!opt_lenient && this.expectingFailure_ &&
  184. !this.suppressedFailures_.length) {
  185. fail(this.getExpectationMessage_());
  186. }
  187. };
  188. /**
  189. * @return {string} A warning describing an expected failure that didn't occur.
  190. * @private
  191. */
  192. goog.testing.ExpectedFailures.prototype.getExpectationMessage_ = function() {
  193. return 'Expected a test failure in \'' +
  194. goog.testing.TestCase.currentTestName + '\' but the test passed.';
  195. };
  196. /**
  197. * Handle the tearDown phase of a test, alerting the user if an expected test
  198. * was not suppressed.
  199. */
  200. goog.testing.ExpectedFailures.prototype.handleTearDown = function() {
  201. if (this.expectingFailure_ && !this.suppressedFailures_.length) {
  202. goog.log.warning(this.logger_, this.getExpectationMessage_());
  203. }
  204. this.reset_();
  205. };
  206. /**
  207. * Reset internal state.
  208. * @private
  209. */
  210. goog.testing.ExpectedFailures.prototype.reset_ = function() {
  211. this.expectingFailure_ = false;
  212. this.failureMessage_ = '';
  213. this.suppressedFailures_ = [];
  214. };