errorhandler_test.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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. goog.provide('goog.debug.ErrorHandlerTest');
  15. goog.setTestOnly('goog.debug.ErrorHandlerTest');
  16. goog.require('goog.debug.ErrorHandler');
  17. goog.require('goog.testing.MockControl');
  18. goog.require('goog.testing.jsunit');
  19. var oldGetObjectByName;
  20. // provide our own window that implements our instrumented and
  21. // immediate-call versions of setTimeout and setInterval
  22. var fakeWin = {};
  23. var errorHandler;
  24. var mockControl;
  25. function badTimer() {
  26. arguments.callee.called = true;
  27. throw 'die die die';
  28. }
  29. function setUp() {
  30. mockControl = new goog.testing.MockControl();
  31. // On IE, globalEval happens async. So make it synchronous.
  32. goog.globalEval = function(str) { eval(str); };
  33. oldGetObjectByName = goog.getObjectByName;
  34. goog.getObjectByName = function(name) {
  35. if (name == 'window') {
  36. return fakeWin;
  37. } else {
  38. return oldGetObjectByName(name);
  39. }
  40. };
  41. fakeWin.setTimeout = function(fn, time) {
  42. fakeWin.setTimeout.called = true;
  43. fakeWin.setTimeout.that = this;
  44. if (goog.isString(fn)) {
  45. eval(fn);
  46. } else {
  47. fn.apply(this, Array.prototype.slice.call(arguments, 2));
  48. }
  49. };
  50. fakeWin.setInterval = function(fn, time) {
  51. fakeWin.setInterval.called = true;
  52. fakeWin.setInterval.that = this;
  53. if (goog.isString(fn)) {
  54. eval(fn);
  55. } else {
  56. fn.apply(this, Array.prototype.slice.call(arguments, 2));
  57. }
  58. };
  59. fakeWin.requestAnimationFrame = function(fn) {
  60. fakeWin.requestAnimationFrame.called = true;
  61. fakeWin.requestAnimationFrame.that = this;
  62. fn();
  63. };
  64. // just record the exception in the error handler when it happens
  65. errorHandler = new goog.debug.ErrorHandler(function(ex) { this.ex = ex; });
  66. }
  67. function tearDown() {
  68. mockControl.$tearDown();
  69. goog.dispose(errorHandler);
  70. errorHandler = null;
  71. goog.getObjectByName = oldGetObjectByName;
  72. delete badTimer['__protected__'];
  73. }
  74. function testWrapSetTimeout() {
  75. errorHandler.protectWindowSetTimeout();
  76. var caught;
  77. try {
  78. fakeWin.setTimeout(badTimer, 3);
  79. } catch (ex) {
  80. caught = ex;
  81. }
  82. assertSetTimeoutError(caught);
  83. }
  84. function testWrapSetTimeoutWithoutException() {
  85. errorHandler.protectWindowSetTimeout();
  86. fakeWin.setTimeout(function(x, y) {
  87. assertEquals('test', x);
  88. assertEquals(7, y);
  89. }, 3, 'test', 7);
  90. }
  91. function testWrapSetTimeoutWithString() {
  92. errorHandler.protectWindowSetTimeout();
  93. var caught;
  94. try {
  95. fakeWin.setTimeout('badTimer()', 3);
  96. } catch (ex) {
  97. caught = ex;
  98. }
  99. assertSetTimeoutError(caught);
  100. }
  101. function testWrapSetInterval() {
  102. errorHandler.protectWindowSetInterval();
  103. var caught;
  104. try {
  105. fakeWin.setInterval(badTimer, 3);
  106. } catch (ex) {
  107. caught = ex;
  108. }
  109. assertSetIntervalError(caught);
  110. }
  111. function testWrapSetIntervalWithoutException() {
  112. errorHandler.protectWindowSetInterval();
  113. fakeWin.setInterval(function(x, y) {
  114. assertEquals('test', x);
  115. assertEquals(7, y);
  116. }, 3, 'test', 7);
  117. }
  118. function testWrapSetIntervalWithString() {
  119. errorHandler.protectWindowSetInterval();
  120. var caught;
  121. try {
  122. fakeWin.setInterval('badTimer()', 3);
  123. } catch (ex) {
  124. caught = ex;
  125. }
  126. assertSetIntervalError(caught);
  127. }
  128. function testWrapRequestAnimationFrame() {
  129. errorHandler.protectWindowRequestAnimationFrame();
  130. var caught;
  131. try {
  132. fakeWin.requestAnimationFrame(badTimer);
  133. } catch (ex) {
  134. caught = ex;
  135. }
  136. assertRequestAnimationFrameError(caught);
  137. }
  138. function testDisposal() {
  139. fakeWin = goog.getObjectByName('window');
  140. var originalSetTimeout = fakeWin.setTimeout;
  141. var originalSetInterval = fakeWin.setInterval;
  142. errorHandler.protectWindowSetTimeout();
  143. errorHandler.protectWindowSetInterval();
  144. assertNotEquals(originalSetTimeout, fakeWin.setTimeout);
  145. assertNotEquals(originalSetInterval, fakeWin.setInterval);
  146. errorHandler.dispose();
  147. assertEquals(originalSetTimeout, fakeWin.setTimeout);
  148. assertEquals(originalSetInterval, fakeWin.setInterval);
  149. }
  150. function testUnwrap() {
  151. var fn = function() {};
  152. var wrappedFn = errorHandler.wrap(fn);
  153. assertNotEquals(wrappedFn, fn);
  154. assertEquals(fn, errorHandler.unwrap(fn));
  155. assertEquals(fn, errorHandler.unwrap(wrappedFn));
  156. }
  157. function testStackPreserved() {
  158. var e;
  159. var hasStacks;
  160. function specialFunctionName() {
  161. var e = Error();
  162. hasStacks = !!e.stack;
  163. throw e;
  164. }
  165. var wrappedFn = errorHandler.wrap(specialFunctionName);
  166. try {
  167. wrappedFn();
  168. } catch (exception) {
  169. e = exception;
  170. }
  171. assertTrue(!!e);
  172. if (hasStacks) {
  173. assertContains('specialFunctionName', e.stack);
  174. }
  175. }
  176. function testGetProtectedFunction() {
  177. var fn = function() { throw new Error('Foo'); };
  178. var protectedFn = errorHandler.getProtectedFunction(fn);
  179. var e = assertThrows(protectedFn);
  180. assertTrue(e instanceof goog.debug.ErrorHandler.ProtectedFunctionError);
  181. assertEquals('Foo', e.cause.message);
  182. }
  183. function testGetProtectedFunctionNullError() {
  184. var fn = function() { throw null; };
  185. var protectedFn = errorHandler.getProtectedFunction(fn);
  186. var e = assertThrows(protectedFn);
  187. assertTrue(e instanceof goog.debug.ErrorHandler.ProtectedFunctionError);
  188. assertNull(e.cause);
  189. }
  190. function testGetProtectedFunction_withoutWrappedErrors() {
  191. var shouldCallErrorLog = !!Error.captureStackTrace;
  192. if (shouldCallErrorLog) {
  193. mockControl.createMethodMock(goog.global.console, 'error');
  194. }
  195. errorHandler.setWrapErrors(false);
  196. var fn = function() {
  197. var e = new Error('Foo');
  198. e.stack = 'STACK';
  199. throw e;
  200. };
  201. var protectedFn = errorHandler.getProtectedFunction(fn);
  202. if (shouldCallErrorLog) {
  203. goog.global.console.error('Foo', 'STACK');
  204. }
  205. mockControl.$replayAll();
  206. var e = assertThrows(protectedFn);
  207. mockControl.$verifyAll();
  208. assertTrue(e instanceof Error);
  209. assertEquals('Foo', e.message);
  210. assertEquals(e.stack, 'STACK');
  211. }
  212. function testGetProtectedFunction_withoutWrappedErrorsWithMessagePrefix() {
  213. errorHandler.setWrapErrors(false);
  214. errorHandler.setPrefixErrorMessages(true);
  215. var fn = function() { throw new Error('Foo'); };
  216. var protectedFn = errorHandler.getProtectedFunction(fn);
  217. var e = assertThrows(protectedFn);
  218. assertTrue(e instanceof Error);
  219. assertEquals(
  220. goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX + 'Foo',
  221. e.message);
  222. var stringError = function() { throw 'String'; };
  223. protectedFn = errorHandler.getProtectedFunction(stringError);
  224. e = assertThrows(protectedFn);
  225. assertEquals('string', typeof e);
  226. assertEquals(
  227. goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX + 'String',
  228. e);
  229. }
  230. function testProtectedFunction_infiniteLoop() {
  231. var numErrors = 0;
  232. var errorHandler = new goog.debug.ErrorHandler(function(ex) { numErrors++; });
  233. errorHandler.protectWindowSetTimeout();
  234. fakeWin.setTimeout(function() { fakeWin.setTimeout(badTimer, 3); }, 3);
  235. assertEquals(
  236. 'Error handler should only have been executed once.', 1, numErrors);
  237. }
  238. function assertSetTimeoutError(caught) {
  239. assertMethodCalledHelper('setTimeout', caught);
  240. }
  241. function assertSetIntervalError(caught) {
  242. assertMethodCalledHelper('setInterval', caught);
  243. }
  244. function assertRequestAnimationFrameError(caught) {
  245. assertMethodCalledHelper('requestAnimationFrame', caught);
  246. }
  247. function assertMethodCalledHelper(method, caught) {
  248. assertTrue('exception not thrown', !!caught);
  249. assertEquals(
  250. 'exception not caught by error handler', caught.cause, errorHandler.ex);
  251. assertTrue('fake ' + method + ' not called', !!fakeWin[method].called);
  252. assertTrue(
  253. '"this" not passed to original ' + method,
  254. fakeWin[method].that === fakeWin);
  255. }