| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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 // test_custom_bindings.js | |
| 6 // mini-framework for ExtensionApiTest browser tests | |
| 7 | |
| 8 var binding = require('binding').Binding.create('test'); | |
| 9 | |
| 10 var chrome = requireNative('chrome').GetChrome(); | |
| 11 var GetExtensionAPIDefinitionsForTest = | |
| 12 requireNative('apiDefinitions').GetExtensionAPIDefinitionsForTest; | |
| 13 var GetAvailability = requireNative('v8_context').GetAvailability; | |
| 14 var GetAPIFeatures = requireNative('test_features').GetAPIFeatures; | |
| 15 var uncaughtExceptionHandler = require('uncaught_exception_handler'); | |
| 16 var userGestures = requireNative('user_gestures'); | |
| 17 | |
| 18 binding.registerCustomHook(function(api) { | |
| 19 var chromeTest = api.compiledApi; | |
| 20 var apiFunctions = api.apiFunctions; | |
| 21 | |
| 22 chromeTest.tests = chromeTest.tests || []; | |
| 23 | |
| 24 var currentTest = null; | |
| 25 var lastTest = null; | |
| 26 var testsFailed = 0; | |
| 27 var testCount = 1; | |
| 28 var failureException = 'chrome.test.failure'; | |
| 29 | |
| 30 // Helper function to get around the fact that function names in javascript | |
| 31 // are read-only, and you can't assign one to anonymous functions. | |
| 32 function testName(test) { | |
| 33 return test ? (test.name || test.generatedName) : "(no test)"; | |
| 34 } | |
| 35 | |
| 36 function testDone() { | |
| 37 // Use setTimeout here to allow previous test contexts to be | |
| 38 // eligible for garbage collection. | |
| 39 setTimeout(chromeTest.runNextTest, 0); | |
| 40 } | |
| 41 | |
| 42 function allTestsDone() { | |
| 43 if (testsFailed == 0) { | |
| 44 chromeTest.notifyPass(); | |
| 45 } else { | |
| 46 chromeTest.notifyFail('Failed ' + testsFailed + ' of ' + | |
| 47 testCount + ' tests'); | |
| 48 } | |
| 49 } | |
| 50 | |
| 51 var pendingCallbacks = 0; | |
| 52 | |
| 53 apiFunctions.setHandleRequest('callbackAdded', function() { | |
| 54 pendingCallbacks++; | |
| 55 | |
| 56 var called = null; | |
| 57 return function() { | |
| 58 if (called != null) { | |
| 59 var redundantPrefix = 'Error\n'; | |
| 60 chrome.test.fail( | |
| 61 'Callback has already been run. ' + | |
| 62 'First call:\n' + | |
| 63 $String.slice(called, redundantPrefix.length) + '\n' + | |
| 64 'Second call:\n' + | |
| 65 $String.slice(new Error().stack, redundantPrefix.length)); | |
| 66 } | |
| 67 called = new Error().stack; | |
| 68 | |
| 69 pendingCallbacks--; | |
| 70 if (pendingCallbacks == 0) { | |
| 71 chromeTest.succeed(); | |
| 72 } | |
| 73 }; | |
| 74 }); | |
| 75 | |
| 76 apiFunctions.setHandleRequest('runNextTest', function() { | |
| 77 // There may have been callbacks which were interrupted by failure | |
| 78 // exceptions. | |
| 79 pendingCallbacks = 0; | |
| 80 | |
| 81 lastTest = currentTest; | |
| 82 currentTest = chromeTest.tests.shift(); | |
| 83 | |
| 84 if (!currentTest) { | |
| 85 allTestsDone(); | |
| 86 return; | |
| 87 } | |
| 88 | |
| 89 try { | |
| 90 chromeTest.log("( RUN ) " + testName(currentTest)); | |
| 91 uncaughtExceptionHandler.setHandler(function(message, e) { | |
| 92 if (e !== failureException) | |
| 93 chromeTest.fail('uncaught exception: ' + message); | |
| 94 }); | |
| 95 currentTest.call(); | |
| 96 } catch (e) { | |
| 97 uncaughtExceptionHandler.handle(e.message, e); | |
| 98 } | |
| 99 }); | |
| 100 | |
| 101 apiFunctions.setHandleRequest('fail', function(message) { | |
| 102 chromeTest.log("( FAILED ) " + testName(currentTest)); | |
| 103 | |
| 104 var stack = {}; | |
| 105 Error.captureStackTrace(stack, chromeTest.fail); | |
| 106 | |
| 107 if (!message) | |
| 108 message = "FAIL (no message)"; | |
| 109 | |
| 110 message += "\n" + stack.stack; | |
| 111 console.log("[FAIL] " + testName(currentTest) + ": " + message); | |
| 112 testsFailed++; | |
| 113 testDone(); | |
| 114 | |
| 115 // Interrupt the rest of the test. | |
| 116 throw failureException; | |
| 117 }); | |
| 118 | |
| 119 apiFunctions.setHandleRequest('succeed', function() { | |
| 120 console.log("[SUCCESS] " + testName(currentTest)); | |
| 121 chromeTest.log("( SUCCESS )"); | |
| 122 testDone(); | |
| 123 }); | |
| 124 | |
| 125 apiFunctions.setHandleRequest('assertTrue', function(test, message) { | |
| 126 chromeTest.assertBool(test, true, message); | |
| 127 }); | |
| 128 | |
| 129 apiFunctions.setHandleRequest('assertFalse', function(test, message) { | |
| 130 chromeTest.assertBool(test, false, message); | |
| 131 }); | |
| 132 | |
| 133 apiFunctions.setHandleRequest('assertBool', | |
| 134 function(test, expected, message) { | |
| 135 if (test !== expected) { | |
| 136 if (typeof(test) == "string") { | |
| 137 if (message) | |
| 138 message = test + "\n" + message; | |
| 139 else | |
| 140 message = test; | |
| 141 } | |
| 142 chromeTest.fail(message); | |
| 143 } | |
| 144 }); | |
| 145 | |
| 146 apiFunctions.setHandleRequest('checkDeepEq', function(expected, actual) { | |
| 147 if ((expected === null) != (actual === null)) | |
| 148 return false; | |
| 149 | |
| 150 if (expected === actual) | |
| 151 return true; | |
| 152 | |
| 153 if (typeof(expected) !== typeof(actual)) | |
| 154 return false; | |
| 155 | |
| 156 for (var p in actual) { | |
| 157 if ($Object.hasOwnProperty(actual, p) && | |
| 158 !$Object.hasOwnProperty(expected, p)) { | |
| 159 return false; | |
| 160 } | |
| 161 } | |
| 162 for (var p in expected) { | |
| 163 if ($Object.hasOwnProperty(expected, p) && | |
| 164 !$Object.hasOwnProperty(actual, p)) { | |
| 165 return false; | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 for (var p in expected) { | |
| 170 var eq = true; | |
| 171 switch (typeof(expected[p])) { | |
| 172 case 'object': | |
| 173 eq = chromeTest.checkDeepEq(expected[p], actual[p]); | |
| 174 break; | |
| 175 case 'function': | |
| 176 eq = (typeof(actual[p]) != 'undefined' && | |
| 177 expected[p].toString() == actual[p].toString()); | |
| 178 break; | |
| 179 default: | |
| 180 eq = (expected[p] == actual[p] && | |
| 181 typeof(expected[p]) == typeof(actual[p])); | |
| 182 break; | |
| 183 } | |
| 184 if (!eq) | |
| 185 return false; | |
| 186 } | |
| 187 return true; | |
| 188 }); | |
| 189 | |
| 190 apiFunctions.setHandleRequest('assertEq', | |
| 191 function(expected, actual, message) { | |
| 192 var error_msg = "API Test Error in " + testName(currentTest); | |
| 193 if (message) | |
| 194 error_msg += ": " + message; | |
| 195 if (typeof(expected) == 'object') { | |
| 196 if (!chromeTest.checkDeepEq(expected, actual)) { | |
| 197 // Note: these JSON.stringify calls may fail in tests that explicitly | |
| 198 // override JSON.stringfy, so surround in try-catch. | |
| 199 try { | |
| 200 error_msg += "\nActual: " + JSON.stringify(actual) + | |
| 201 "\nExpected: " + JSON.stringify(expected); | |
| 202 } catch (e) {} | |
| 203 chromeTest.fail(error_msg); | |
| 204 } | |
| 205 return; | |
| 206 } | |
| 207 if (expected != actual) { | |
| 208 chromeTest.fail(error_msg + | |
| 209 "\nActual: " + actual + "\nExpected: " + expected); | |
| 210 } | |
| 211 if (typeof(expected) != typeof(actual)) { | |
| 212 chromeTest.fail(error_msg + | |
| 213 " (type mismatch)\nActual Type: " + typeof(actual) + | |
| 214 "\nExpected Type:" + typeof(expected)); | |
| 215 } | |
| 216 }); | |
| 217 | |
| 218 apiFunctions.setHandleRequest('assertNoLastError', function() { | |
| 219 if (chrome.runtime.lastError != undefined) { | |
| 220 chromeTest.fail("lastError.message == " + | |
| 221 chrome.runtime.lastError.message); | |
| 222 } | |
| 223 }); | |
| 224 | |
| 225 apiFunctions.setHandleRequest('assertLastError', function(expectedError) { | |
| 226 chromeTest.assertEq(typeof(expectedError), 'string'); | |
| 227 chromeTest.assertTrue(chrome.runtime.lastError != undefined, | |
| 228 "No lastError, but expected " + expectedError); | |
| 229 chromeTest.assertEq(expectedError, chrome.runtime.lastError.message); | |
| 230 }); | |
| 231 | |
| 232 apiFunctions.setHandleRequest('assertThrows', | |
| 233 function(fn, self, args, message) { | |
| 234 chromeTest.assertTrue(typeof fn == 'function'); | |
| 235 try { | |
| 236 fn.apply(self, args); | |
| 237 chromeTest.fail('Did not throw error: ' + fn); | |
| 238 } catch (e) { | |
| 239 if (e != failureException && message !== undefined) { | |
| 240 if (message instanceof RegExp) { | |
| 241 chromeTest.assertTrue(message.test(e.message), | |
| 242 e.message + ' should match ' + message) | |
| 243 } else { | |
| 244 chromeTest.assertEq(message, e.message); | |
| 245 } | |
| 246 } | |
| 247 } | |
| 248 }); | |
| 249 | |
| 250 function safeFunctionApply(func, args) { | |
| 251 try { | |
| 252 if (func) | |
| 253 return $Function.apply(func, undefined, args); | |
| 254 } catch (e) { | |
| 255 var msg = "uncaught exception " + e; | |
| 256 chromeTest.fail(msg); | |
| 257 } | |
| 258 }; | |
| 259 | |
| 260 // Wrapper for generating test functions, that takes care of calling | |
| 261 // assertNoLastError() and (optionally) succeed() for you. | |
| 262 apiFunctions.setHandleRequest('callback', function(func, expectedError) { | |
| 263 if (func) { | |
| 264 chromeTest.assertEq(typeof(func), 'function'); | |
| 265 } | |
| 266 var callbackCompleted = chromeTest.callbackAdded(); | |
| 267 | |
| 268 return function() { | |
| 269 if (expectedError == null) { | |
| 270 chromeTest.assertNoLastError(); | |
| 271 } else { | |
| 272 chromeTest.assertLastError(expectedError); | |
| 273 } | |
| 274 | |
| 275 var result; | |
| 276 if (func) { | |
| 277 result = safeFunctionApply(func, arguments); | |
| 278 } | |
| 279 | |
| 280 callbackCompleted(); | |
| 281 return result; | |
| 282 }; | |
| 283 }); | |
| 284 | |
| 285 apiFunctions.setHandleRequest('listenOnce', function(event, func) { | |
| 286 var callbackCompleted = chromeTest.callbackAdded(); | |
| 287 var listener = function() { | |
| 288 event.removeListener(listener); | |
| 289 safeFunctionApply(func, arguments); | |
| 290 callbackCompleted(); | |
| 291 }; | |
| 292 event.addListener(listener); | |
| 293 }); | |
| 294 | |
| 295 apiFunctions.setHandleRequest('listenForever', function(event, func) { | |
| 296 var callbackCompleted = chromeTest.callbackAdded(); | |
| 297 | |
| 298 var listener = function() { | |
| 299 safeFunctionApply(func, arguments); | |
| 300 }; | |
| 301 | |
| 302 var done = function() { | |
| 303 event.removeListener(listener); | |
| 304 callbackCompleted(); | |
| 305 }; | |
| 306 | |
| 307 event.addListener(listener); | |
| 308 return done; | |
| 309 }); | |
| 310 | |
| 311 apiFunctions.setHandleRequest('callbackPass', function(func) { | |
| 312 return chromeTest.callback(func); | |
| 313 }); | |
| 314 | |
| 315 apiFunctions.setHandleRequest('callbackFail', function(expectedError, func) { | |
| 316 return chromeTest.callback(func, expectedError); | |
| 317 }); | |
| 318 | |
| 319 apiFunctions.setHandleRequest('runTests', function(tests) { | |
| 320 chromeTest.tests = tests; | |
| 321 testCount = chromeTest.tests.length; | |
| 322 chromeTest.runNextTest(); | |
| 323 }); | |
| 324 | |
| 325 apiFunctions.setHandleRequest('getApiDefinitions', function() { | |
| 326 return GetExtensionAPIDefinitionsForTest(); | |
| 327 }); | |
| 328 | |
| 329 apiFunctions.setHandleRequest('getApiFeatures', function() { | |
| 330 return GetAPIFeatures(); | |
| 331 }); | |
| 332 | |
| 333 apiFunctions.setHandleRequest('isProcessingUserGesture', function() { | |
| 334 return userGestures.IsProcessingUserGesture(); | |
| 335 }); | |
| 336 | |
| 337 apiFunctions.setHandleRequest('runWithUserGesture', function(callback) { | |
| 338 chromeTest.assertEq(typeof(callback), 'function'); | |
| 339 return userGestures.RunWithUserGesture(callback); | |
| 340 }); | |
| 341 | |
| 342 apiFunctions.setHandleRequest('runWithoutUserGesture', function(callback) { | |
| 343 chromeTest.assertEq(typeof(callback), 'function'); | |
| 344 return userGestures.RunWithoutUserGesture(callback); | |
| 345 }); | |
| 346 | |
| 347 apiFunctions.setHandleRequest('setExceptionHandler', function(callback) { | |
| 348 chromeTest.assertEq(typeof(callback), 'function'); | |
| 349 uncaughtExceptionHandler.setHandler(callback); | |
| 350 }); | |
| 351 }); | |
| 352 | |
| 353 exports.binding = binding.generate(); | |
| OLD | NEW |