modulemanager_test.js 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932
  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.module.ModuleManagerTest');
  15. goog.setTestOnly('goog.module.ModuleManagerTest');
  16. goog.require('goog.array');
  17. goog.require('goog.functions');
  18. goog.require('goog.module.BaseModule');
  19. goog.require('goog.module.ModuleManager');
  20. goog.require('goog.testing');
  21. goog.require('goog.testing.MockClock');
  22. goog.require('goog.testing.jsunit');
  23. goog.require('goog.testing.recordFunction');
  24. var clock;
  25. var requestCount = 0;
  26. function tearDown() {
  27. clock.dispose();
  28. }
  29. function setUp() {
  30. clock = new goog.testing.MockClock(true);
  31. requestCount = 0;
  32. }
  33. function getModuleManager(infoMap) {
  34. var mm = new goog.module.ModuleManager();
  35. mm.setAllModuleInfo(infoMap);
  36. mm.isModuleLoaded = function(id) {
  37. return this.getModuleInfo(id).isLoaded();
  38. };
  39. return mm;
  40. }
  41. function createSuccessfulBatchLoader(moduleMgr) {
  42. return {
  43. loadModules: function(
  44. ids, moduleInfoMap, opt_successFn, opt_errFn, opt_timeoutFn) {
  45. requestCount++;
  46. setTimeout(goog.bind(this.onLoad, this, ids.concat(), 0), 5);
  47. },
  48. onLoad: function(ids, idxLoaded) {
  49. moduleMgr.beforeLoadModuleCode(ids[idxLoaded]);
  50. moduleMgr.setLoaded(ids[idxLoaded]);
  51. moduleMgr.afterLoadModuleCode(ids[idxLoaded]);
  52. var idx = idxLoaded + 1;
  53. if (idx < ids.length) {
  54. setTimeout(goog.bind(this.onLoad, this, ids, idx), 2);
  55. }
  56. }
  57. };
  58. }
  59. function createSuccessfulNonBatchLoader(moduleMgr) {
  60. return {
  61. loadModules: function(
  62. ids, moduleInfoMap, opt_successFn, opt_errFn, opt_timeoutFn) {
  63. requestCount++;
  64. setTimeout(function() {
  65. moduleMgr.beforeLoadModuleCode(ids[0]);
  66. moduleMgr.setLoaded(ids[0]);
  67. moduleMgr.afterLoadModuleCode(ids[0]);
  68. if (opt_successFn) {
  69. opt_successFn();
  70. }
  71. }, 5);
  72. }
  73. };
  74. }
  75. function createUnsuccessfulLoader(moduleMgr, status) {
  76. return {
  77. loadModules: function(
  78. ids, moduleInfoMap, opt_successFn, opt_errFn, opt_timeoutFn) {
  79. moduleMgr.beforeLoadModuleCode(ids[0]);
  80. setTimeout(function() { opt_errFn(status); }, 5);
  81. }
  82. };
  83. }
  84. function createUnsuccessfulBatchLoader(moduleMgr, status) {
  85. return {
  86. loadModules: function(
  87. ids, moduleInfoMap, opt_successFn, opt_errFn, opt_timeoutFn) {
  88. setTimeout(function() { opt_errFn(status); }, 5);
  89. }
  90. };
  91. }
  92. function createTimeoutLoader(moduleMgr, status) {
  93. return {
  94. loadModules: function(
  95. ids, moduleInfoMap, opt_successFn, opt_errFn, opt_timeoutFn) {
  96. setTimeout(function() { opt_timeoutFn(status); }, 5);
  97. }
  98. };
  99. }
  100. /**
  101. * Tests loading a module under different conditions i.e. unloaded
  102. * module, already loaded module, module loaded through user initiated
  103. * actions, synchronous callback for a module that has been already
  104. * loaded. Test both batch and non-batch loaders.
  105. */
  106. function testExecOnLoad() {
  107. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  108. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  109. execOnLoad_(mm);
  110. mm = getModuleManager({'a': [], 'b': [], 'c': []});
  111. mm.setLoader(createSuccessfulBatchLoader(mm));
  112. mm.setBatchModeEnabled(true);
  113. execOnLoad_(mm);
  114. }
  115. /**
  116. * Tests execOnLoad with the specified module manager.
  117. * @param {goog.module.ModuleManager} mm The module manager.
  118. */
  119. function execOnLoad_(mm) {
  120. // When module is unloaded, execOnLoad is async.
  121. var execCalled1 = false;
  122. mm.execOnLoad('a', function() { execCalled1 = true; });
  123. assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a'));
  124. assertTrue('module "a" should be loading', mm.isModuleLoading('a'));
  125. assertFalse('execCalled1 should not be set yet', execCalled1);
  126. assertTrue('ModuleManager should be active', mm.isActive());
  127. assertFalse('ModuleManager should not be user active', mm.isUserActive());
  128. clock.tick(5);
  129. assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
  130. assertFalse('module "a" should not be loading', mm.isModuleLoading('a'));
  131. assertTrue('execCalled1 should be set', execCalled1);
  132. assertFalse('ModuleManager should not be active', mm.isActive());
  133. assertFalse('ModuleManager should not be user active', mm.isUserActive());
  134. // When module is already loaded, execOnLoad is still async unless
  135. // specified otherwise.
  136. var execCalled2 = false;
  137. mm.execOnLoad('a', function() { execCalled2 = true; });
  138. assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
  139. assertFalse('module "a" should not be loading', mm.isModuleLoading('a'));
  140. assertFalse('execCalled2 should not be set yet', execCalled2);
  141. clock.tick(5);
  142. assertTrue('execCalled2 should be set', execCalled2);
  143. // When module is unloaded, execOnLoad is async (user active).
  144. var execCalled5 = false;
  145. mm.execOnLoad('c', function() { execCalled5 = true; }, null, null, true);
  146. assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
  147. assertTrue('module "c" should be loading', mm.isModuleLoading('c'));
  148. assertFalse('execCalled1 should not be set yet', execCalled5);
  149. assertTrue('ModuleManager should be active', mm.isActive());
  150. assertTrue('ModuleManager should be user active', mm.isUserActive());
  151. clock.tick(5);
  152. assertTrue('module "c" should be loaded', mm.isModuleLoaded('c'));
  153. assertFalse('module "c" should not be loading', mm.isModuleLoading('c'));
  154. assertTrue('execCalled1 should be set', execCalled5);
  155. assertFalse('ModuleManager should not be active', mm.isActive());
  156. assertFalse('ModuleManager should not be user active', mm.isUserActive());
  157. // When module is already loaded, execOnLoad is still synchronous when
  158. // so specified
  159. var execCalled6 = false;
  160. mm.execOnLoad('c', function() {
  161. execCalled6 = true;
  162. }, undefined, undefined, undefined, true);
  163. assertTrue('module "c" should be loaded', mm.isModuleLoaded('c'));
  164. assertFalse('module "c" should not be loading', mm.isModuleLoading('c'));
  165. assertTrue('execCalled6 should be set', execCalled6);
  166. clock.tick(5);
  167. assertTrue('execCalled6 should still be set', execCalled6);
  168. }
  169. /**
  170. * Test aborting the callback called on module load.
  171. */
  172. function testExecOnLoadAbort() {
  173. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  174. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  175. // When module is unloaded and abort is called, module still gets
  176. // loaded, but callback is cancelled.
  177. var execCalled1 = false;
  178. var callback1 = mm.execOnLoad('b', function() { execCalled1 = true; });
  179. callback1.abort();
  180. clock.tick(5);
  181. assertTrue('module "b" should be loaded', mm.isModuleLoaded('b'));
  182. assertFalse('execCalled3 should not be set', execCalled1);
  183. // When module is already loaded, execOnLoad is still async, so calling
  184. // abort should still cancel the callback.
  185. var execCalled2 = false;
  186. var callback2 = mm.execOnLoad('a', function() { execCalled2 = true; });
  187. callback2.abort();
  188. clock.tick(5);
  189. assertFalse('execCalled2 should not be set', execCalled2);
  190. }
  191. /**
  192. * Test preloading modules and ensure that the before load, after load
  193. * and set load called are called only once per module.
  194. */
  195. function testExecOnLoadWhilePreloadingAndViceVersa() {
  196. var mm = getModuleManager({'c': [], 'd': []});
  197. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  198. execOnLoadWhilePreloadingAndViceVersa_(mm);
  199. mm = getModuleManager({'c': [], 'd': []});
  200. mm.setLoader(createSuccessfulBatchLoader(mm));
  201. mm.setBatchModeEnabled(true);
  202. execOnLoadWhilePreloadingAndViceVersa_(mm);
  203. }
  204. /**
  205. * Perform tests with the specified module manager.
  206. * @param {goog.module.ModuleManager} mm The module manager.
  207. */
  208. function execOnLoadWhilePreloadingAndViceVersa_(mm) {
  209. var mm = getModuleManager({'c': [], 'd': []});
  210. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  211. var origSetLoaded = mm.setLoaded;
  212. var calls = [0, 0, 0];
  213. mm.beforeLoadModuleCode = function(id) { calls[0]++; };
  214. mm.setLoaded = function(id) {
  215. calls[1]++;
  216. origSetLoaded.call(mm, id);
  217. };
  218. mm.afterLoadModuleCode = function(id) { calls[2]++; };
  219. mm.preloadModule('c', 2);
  220. assertFalse('module "c" should not be loading yet', mm.isModuleLoading('c'));
  221. clock.tick(2);
  222. assertTrue('module "c" should now be loading', mm.isModuleLoading('c'));
  223. mm.execOnLoad('c', function() {});
  224. assertTrue('module "c" should still be loading', mm.isModuleLoading('c'));
  225. clock.tick(5);
  226. assertFalse('module "c" should be done loading', mm.isModuleLoading('c'));
  227. assertEquals('beforeLoad should only be called once for "c"', 1, calls[0]);
  228. assertEquals('setLoaded should only be called once for "c"', 1, calls[1]);
  229. assertEquals('afterLoad should only be called once for "c"', 1, calls[2]);
  230. mm.execOnLoad('d', function() {});
  231. assertTrue('module "d" should now be loading', mm.isModuleLoading('d'));
  232. mm.preloadModule('d', 2);
  233. clock.tick(5);
  234. assertFalse('module "d" should be done loading', mm.isModuleLoading('d'));
  235. assertTrue('module "d" should now be loaded', mm.isModuleLoaded('d'));
  236. assertEquals('beforeLoad should only be called once for "d"', 2, calls[0]);
  237. assertEquals('setLoaded should only be called once for "d"', 2, calls[1]);
  238. assertEquals('afterLoad should only be called once for "d"', 2, calls[2]);
  239. }
  240. /**
  241. * Tests that multiple callbacks on the same module don't cause
  242. * confusion about the active state after the module is finally loaded.
  243. */
  244. function testUserInitiatedExecOnLoadEventuallyLeavesManagerIdle() {
  245. var mm = getModuleManager({'c': [], 'd': []});
  246. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  247. var calledBack1 = false;
  248. var calledBack2 = false;
  249. mm.execOnLoad(
  250. 'c', function() { calledBack1 = true; }, undefined, undefined, true);
  251. mm.execOnLoad(
  252. 'c', function() { calledBack2 = true; }, undefined, undefined, true);
  253. mm.load('c');
  254. assertTrue(
  255. 'Manager should be active while waiting for load', mm.isUserActive());
  256. clock.tick(5);
  257. assertTrue('First callback should be called', calledBack1);
  258. assertTrue('Second callback should be called', calledBack2);
  259. assertFalse(
  260. 'Manager should be inactive after loading is complete',
  261. mm.isUserActive());
  262. }
  263. /**
  264. * Tests loading a module by requesting a Deferred object.
  265. */
  266. function testLoad() {
  267. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  268. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  269. var calledBack = false;
  270. var error = null;
  271. var d = mm.load('a');
  272. d.addCallback(function(ctx) { calledBack = true; });
  273. d.addErrback(function(err) { error = err; });
  274. assertFalse(calledBack);
  275. assertNull(error);
  276. assertFalse(mm.isUserActive());
  277. clock.tick(5);
  278. assertTrue(calledBack);
  279. assertNull(error);
  280. }
  281. /**
  282. * Tests loading 2 modules asserting that the loads happen in parallel
  283. * in one unit of time.
  284. */
  285. function testLoad_concurrent() {
  286. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  287. mm.setConcurrentLoadingEnabled(true);
  288. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  289. var calledBack = false;
  290. var error = null;
  291. mm.load('a');
  292. mm.load('b');
  293. assertEquals(2, requestCount);
  294. // Only time for one serialized download.
  295. clock.tick(5);
  296. assertTrue(mm.getModuleInfo('a').isLoaded());
  297. assertTrue(mm.getModuleInfo('b').isLoaded());
  298. }
  299. function testLoad_concurrentSecondIsDepOfFist() {
  300. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  301. mm.setBatchModeEnabled(true);
  302. mm.setConcurrentLoadingEnabled(true);
  303. mm.setLoader(createSuccessfulBatchLoader(mm));
  304. var calledBack = false;
  305. var error = null;
  306. mm.loadMultiple(['a', 'b']);
  307. mm.load('b');
  308. assertEquals('No 2nd request expected', 1, requestCount);
  309. // Only time for one serialized download.
  310. clock.tick(5);
  311. clock.tick(2); // Makes second module come in from batch requst.
  312. assertTrue(mm.getModuleInfo('a').isLoaded());
  313. assertTrue(mm.getModuleInfo('b').isLoaded());
  314. }
  315. function testLoad_nonConcurrent() {
  316. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  317. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  318. var calledBack = false;
  319. var error = null;
  320. mm.load('a');
  321. mm.load('b');
  322. assertEquals(1, requestCount);
  323. // Only time for one serialized download.
  324. clock.tick(5);
  325. assertTrue(mm.getModuleInfo('a').isLoaded());
  326. assertFalse(mm.getModuleInfo('b').isLoaded());
  327. }
  328. function testLoadUnknown() {
  329. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  330. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  331. var e = assertThrows(function() { mm.load('DoesNotExist'); });
  332. assertEquals('Unknown module: DoesNotExist', e.message);
  333. }
  334. /**
  335. * Tests loading multiple modules by requesting a Deferred object.
  336. */
  337. function testLoadMultiple() {
  338. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  339. mm.setBatchModeEnabled(true);
  340. mm.setLoader(createSuccessfulBatchLoader(mm));
  341. var calledBack = false;
  342. var error = null;
  343. var calledBack2 = false;
  344. var error2 = null;
  345. var dMap = mm.loadMultiple(['a', 'b']);
  346. dMap['a'].addCallback(function(ctx) { calledBack = true; });
  347. dMap['a'].addErrback(function(err) { error = err; });
  348. dMap['b'].addCallback(function(ctx) { calledBack2 = true; });
  349. dMap['b'].addErrback(function(err) { error2 = err; });
  350. assertFalse(calledBack);
  351. assertFalse(calledBack2);
  352. clock.tick(5);
  353. assertTrue(calledBack);
  354. assertFalse(calledBack2);
  355. assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
  356. assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
  357. assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
  358. clock.tick(2);
  359. assertTrue(calledBack);
  360. assertTrue(calledBack2);
  361. assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
  362. assertTrue('module "b" should be loaded', mm.isModuleLoaded('b'));
  363. assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
  364. assertNull(error);
  365. assertNull(error2);
  366. }
  367. /**
  368. * Tests loading multiple modules with deps by requesting a Deferred object.
  369. */
  370. function testLoadMultipleWithDeps() {
  371. var mm = getModuleManager({'a': [], 'b': ['c'], 'c': []});
  372. mm.setBatchModeEnabled(true);
  373. mm.setLoader(createSuccessfulBatchLoader(mm));
  374. var calledBack = false;
  375. var error = null;
  376. var calledBack2 = false;
  377. var error2 = null;
  378. var dMap = mm.loadMultiple(['a', 'b']);
  379. dMap['a'].addCallback(function(ctx) { calledBack = true; });
  380. dMap['a'].addErrback(function(err) { error = err; });
  381. dMap['b'].addCallback(function(ctx) { calledBack2 = true; });
  382. dMap['b'].addErrback(function(err) { error2 = err; });
  383. assertFalse(calledBack);
  384. assertFalse(calledBack2);
  385. clock.tick(5);
  386. assertTrue(calledBack);
  387. assertFalse(calledBack2);
  388. assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
  389. assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
  390. assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
  391. clock.tick(2);
  392. assertFalse(calledBack2);
  393. assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
  394. assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
  395. assertTrue('module "c" should be loaded', mm.isModuleLoaded('c'));
  396. clock.tick(2);
  397. assertTrue(calledBack2);
  398. assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
  399. assertTrue('module "b" should be loaded', mm.isModuleLoaded('b'));
  400. assertTrue('module "c" should be loaded', mm.isModuleLoaded('c'));
  401. assertNull(error);
  402. assertNull(error2);
  403. }
  404. /**
  405. * Tests loading multiple modules by requesting a Deferred object when
  406. * a server error occurs.
  407. */
  408. function testLoadMultipleWithErrors() {
  409. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  410. mm.setBatchModeEnabled(true);
  411. mm.setLoader(createUnsuccessfulLoader(mm, 500));
  412. var calledBack = false;
  413. var error = null;
  414. var calledBack2 = false;
  415. var error2 = null;
  416. var calledBack3 = false;
  417. var error3 = null;
  418. var dMap = mm.loadMultiple(['a', 'b', 'c']);
  419. dMap['a'].addCallback(function(ctx) { calledBack = true; });
  420. dMap['a'].addErrback(function(err) { error = err; });
  421. dMap['b'].addCallback(function(ctx) { calledBack2 = true; });
  422. dMap['b'].addErrback(function(err) { error2 = err; });
  423. dMap['c'].addCallback(function(ctx) { calledBack3 = true; });
  424. dMap['c'].addErrback(function(err) { error3 = err; });
  425. assertFalse(calledBack);
  426. assertFalse(calledBack2);
  427. assertFalse(calledBack3);
  428. clock.tick(4);
  429. // A module request is now underway using the unsuccessful loader.
  430. // We substitute a successful loader for future module load requests.
  431. mm.setLoader(createSuccessfulBatchLoader(mm));
  432. clock.tick(1);
  433. assertFalse(calledBack);
  434. assertFalse(calledBack2);
  435. assertFalse(calledBack3);
  436. assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a'));
  437. assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
  438. assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
  439. // Retry should happen after a backoff
  440. clock.tick(5 + mm.getBackOff_());
  441. assertTrue(calledBack);
  442. assertFalse(calledBack2);
  443. assertFalse(calledBack3);
  444. assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
  445. assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
  446. assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
  447. clock.tick(2);
  448. assertTrue(calledBack2);
  449. assertFalse(calledBack3);
  450. assertTrue('module "b" should be loaded', mm.isModuleLoaded('b'));
  451. assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
  452. clock.tick(2);
  453. assertTrue(calledBack3);
  454. assertTrue('module "c" should be loaded', mm.isModuleLoaded('c'));
  455. assertNull(error);
  456. assertNull(error2);
  457. assertNull(error3);
  458. }
  459. /**
  460. * Tests loading multiple modules by requesting a Deferred object when
  461. * consecutive server error occur and the loader falls back to serial
  462. * loads.
  463. */
  464. function testLoadMultipleWithErrorsFallbackOnSerial() {
  465. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  466. mm.setBatchModeEnabled(true);
  467. mm.setLoader(createUnsuccessfulLoader(mm, 500));
  468. var calledBack = false;
  469. var error = null;
  470. var calledBack2 = false;
  471. var error2 = null;
  472. var calledBack3 = false;
  473. var error3 = null;
  474. var dMap = mm.loadMultiple(['a', 'b', 'c']);
  475. dMap['a'].addCallback(function(ctx) { calledBack = true; });
  476. dMap['a'].addErrback(function(err) { error = err; });
  477. dMap['b'].addCallback(function(ctx) { calledBack2 = true; });
  478. dMap['b'].addErrback(function(err) { error2 = err; });
  479. dMap['c'].addCallback(function(ctx) { calledBack3 = true; });
  480. dMap['c'].addErrback(function(err) { error3 = err; });
  481. assertFalse(calledBack);
  482. assertFalse(calledBack2);
  483. assertFalse(calledBack3);
  484. clock.tick(5);
  485. assertFalse(calledBack);
  486. assertFalse(calledBack2);
  487. assertFalse(calledBack3);
  488. assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a'));
  489. assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
  490. assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
  491. // Retry should happen and fail after a backoff
  492. clock.tick(5 + mm.getBackOff_());
  493. assertFalse(calledBack);
  494. assertFalse(calledBack2);
  495. assertFalse(calledBack3);
  496. assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a'));
  497. assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
  498. assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
  499. // A second retry should happen after a backoff
  500. clock.tick(4 + mm.getBackOff_());
  501. // The second retry is now underway using the unsuccessful loader.
  502. // We substitute a successful loader for future module load requests.
  503. mm.setLoader(createSuccessfulBatchLoader(mm));
  504. clock.tick(1);
  505. // A second retry should fail now
  506. assertFalse(calledBack);
  507. assertFalse(calledBack2);
  508. assertFalse(calledBack3);
  509. assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a'));
  510. assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
  511. assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
  512. // Each module should be loaded individually now, each taking 5 ticks
  513. clock.tick(5);
  514. assertTrue(calledBack);
  515. assertFalse(calledBack2);
  516. assertFalse(calledBack3);
  517. assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
  518. assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
  519. assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
  520. clock.tick(5);
  521. assertTrue(calledBack2);
  522. assertFalse(calledBack3);
  523. assertTrue('module "b" should be loaded', mm.isModuleLoaded('b'));
  524. assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
  525. clock.tick(5);
  526. assertTrue(calledBack3);
  527. assertTrue('module "c" should be loaded', mm.isModuleLoaded('c'));
  528. assertNull(error);
  529. assertNull(error2);
  530. assertNull(error3);
  531. }
  532. /**
  533. * Tests loading a module by user action by requesting a Deferred object.
  534. */
  535. function testLoadForUser() {
  536. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  537. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  538. var calledBack = false;
  539. var error = null;
  540. var d = mm.load('a', true);
  541. d.addCallback(function(ctx) { calledBack = true; });
  542. d.addErrback(function(err) { error = err; });
  543. assertFalse(calledBack);
  544. assertNull(error);
  545. assertTrue(mm.isUserActive());
  546. clock.tick(5);
  547. assertTrue(calledBack);
  548. assertNull(error);
  549. }
  550. /**
  551. * Tests that preloading a module calls back the deferred object.
  552. */
  553. function testPreloadDeferredWhenNotLoaded() {
  554. var mm = getModuleManager({'a': []});
  555. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  556. var calledBack = false;
  557. var d = mm.preloadModule('a');
  558. d.addCallback(function(ctx) { calledBack = true; });
  559. // First load should take five ticks.
  560. assertFalse('module "a" should not be loaded yet', calledBack);
  561. clock.tick(5);
  562. assertTrue('module "a" should be loaded', calledBack);
  563. }
  564. /**
  565. * Tests preloading an already loaded module.
  566. */
  567. function testPreloadDeferredWhenLoaded() {
  568. var mm = getModuleManager({'a': []});
  569. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  570. var calledBack = false;
  571. mm.preloadModule('a');
  572. clock.tick(5);
  573. var d = mm.preloadModule('a');
  574. d.addCallback(function(ctx) { calledBack = true; });
  575. // Module is already loaded, should be called back after the setTimeout
  576. // in preloadModule.
  577. assertFalse('deferred for module "a" should not be called yet', calledBack);
  578. clock.tick(1);
  579. assertTrue('module "a" should be loaded', calledBack);
  580. }
  581. /**
  582. * Tests preloading a module that is currently loading.
  583. */
  584. function testPreloadDeferredWhenLoading() {
  585. var mm = getModuleManager({'a': []});
  586. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  587. mm.preloadModule('a');
  588. clock.tick(1);
  589. // 'b' is in the middle of loading, should get called back when it's done.
  590. var calledBack = false;
  591. var d = mm.preloadModule('a');
  592. d.addCallback(function(ctx) { calledBack = true; });
  593. assertFalse('module "a" should not be loaded yet', calledBack);
  594. clock.tick(4);
  595. assertTrue('module "a" should be loaded', calledBack);
  596. }
  597. /**
  598. * Tests that load doesn't trigger another load if a module is already
  599. * preloading.
  600. */
  601. function testLoadWhenPreloading() {
  602. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  603. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  604. var origSetLoaded = mm.setLoaded;
  605. var calls = [0, 0, 0];
  606. mm.beforeLoadModuleCode = function(id) { calls[0]++; };
  607. mm.setLoaded = function(id) {
  608. calls[1]++;
  609. origSetLoaded.call(mm, id);
  610. };
  611. mm.afterLoadModuleCode = function(id) { calls[2]++; };
  612. var calledBack = false;
  613. var error = null;
  614. mm.preloadModule('c', 2);
  615. assertFalse('module "c" should not be loading yet', mm.isModuleLoading('c'));
  616. clock.tick(2);
  617. assertTrue('module "c" should now be loading', mm.isModuleLoading('c'));
  618. var d = mm.load('c');
  619. d.addCallback(function(ctx) { calledBack = true; });
  620. d.addErrback(function(err) { error = err; });
  621. assertTrue('module "c" should still be loading', mm.isModuleLoading('c'));
  622. clock.tick(5);
  623. assertFalse('module "c" should be done loading', mm.isModuleLoading('c'));
  624. assertEquals('beforeLoad should only be called once for "c"', 1, calls[0]);
  625. assertEquals('setLoaded should only be called once for "c"', 1, calls[1]);
  626. assertEquals('afterLoad should only be called once for "c"', 1, calls[2]);
  627. assertTrue(calledBack);
  628. assertNull(error);
  629. }
  630. /**
  631. * Tests that load doesn't trigger another load if a module is already
  632. * preloading.
  633. */
  634. function testLoadMultipleWhenPreloading() {
  635. var mm = getModuleManager({'a': [], 'b': ['d'], 'c': [], 'd': []});
  636. mm.setLoader(createSuccessfulBatchLoader(mm));
  637. mm.setBatchModeEnabled(true);
  638. var origSetLoaded = mm.setLoaded;
  639. var calls = {'a': [0, 0, 0], 'b': [0, 0, 0], 'c': [0, 0, 0], 'd': [0, 0, 0]};
  640. mm.beforeLoadModuleCode = function(id) { calls[id][0]++; };
  641. mm.setLoaded = function(id) {
  642. calls[id][1]++;
  643. origSetLoaded.call(mm, id);
  644. };
  645. mm.afterLoadModuleCode = function(id) { calls[id][2]++; };
  646. var calledBack = false;
  647. var error = null;
  648. var calledBack2 = false;
  649. var error2 = null;
  650. var calledBack3 = false;
  651. var error3 = null;
  652. mm.preloadModule('c', 2);
  653. mm.preloadModule('d', 3);
  654. assertFalse('module "c" should not be loading yet', mm.isModuleLoading('c'));
  655. assertFalse('module "d" should not be loading yet', mm.isModuleLoading('d'));
  656. clock.tick(2);
  657. assertTrue('module "c" should now be loading', mm.isModuleLoading('c'));
  658. clock.tick(1);
  659. assertTrue('module "d" should now be loading', mm.isModuleLoading('d'));
  660. var dMap = mm.loadMultiple(['a', 'b', 'c']);
  661. dMap['a'].addCallback(function(ctx) { calledBack = true; });
  662. dMap['a'].addErrback(function(err) { error = err; });
  663. dMap['b'].addCallback(function(ctx) { calledBack2 = true; });
  664. dMap['b'].addErrback(function(err) { error2 = err; });
  665. dMap['c'].addCallback(function(ctx) { calledBack3 = true; });
  666. dMap['c'].addErrback(function(err) { error3 = err; });
  667. assertTrue('module "a" should be loading', mm.isModuleLoading('a'));
  668. assertTrue('module "b" should be loading', mm.isModuleLoading('b'));
  669. assertTrue('module "c" should still be loading', mm.isModuleLoading('c'));
  670. clock.tick(4);
  671. assertTrue(calledBack3);
  672. assertFalse('module "c" should be done loading', mm.isModuleLoading('c'));
  673. assertTrue('module "d" should still be loading', mm.isModuleLoading('d'));
  674. clock.tick(5);
  675. assertFalse('module "d" should be done loading', mm.isModuleLoading('d'));
  676. assertFalse(calledBack);
  677. assertFalse(calledBack2);
  678. assertTrue('module "a" should still be loading', mm.isModuleLoading('a'));
  679. assertTrue('module "b" should still be loading', mm.isModuleLoading('b'));
  680. clock.tick(7);
  681. assertTrue(calledBack);
  682. assertTrue(calledBack2);
  683. assertFalse('module "a" should be done loading', mm.isModuleLoading('a'));
  684. assertFalse('module "b" should be done loading', mm.isModuleLoading('b'));
  685. assertEquals(
  686. 'beforeLoad should only be called once for "a"', 1, calls['a'][0]);
  687. assertEquals(
  688. 'setLoaded should only be called once for "a"', 1, calls['a'][1]);
  689. assertEquals(
  690. 'afterLoad should only be called once for "a"', 1, calls['a'][2]);
  691. assertEquals(
  692. 'beforeLoad should only be called once for "b"', 1, calls['b'][0]);
  693. assertEquals(
  694. 'setLoaded should only be called once for "b"', 1, calls['b'][1]);
  695. assertEquals(
  696. 'afterLoad should only be called once for "b"', 1, calls['b'][2]);
  697. assertEquals(
  698. 'beforeLoad should only be called once for "c"', 1, calls['c'][0]);
  699. assertEquals(
  700. 'setLoaded should only be called once for "c"', 1, calls['c'][1]);
  701. assertEquals(
  702. 'afterLoad should only be called once for "c"', 1, calls['c'][2]);
  703. assertEquals(
  704. 'beforeLoad should only be called once for "d"', 1, calls['d'][0]);
  705. assertEquals(
  706. 'setLoaded should only be called once for "d"', 1, calls['d'][1]);
  707. assertEquals(
  708. 'afterLoad should only be called once for "d"', 1, calls['d'][2]);
  709. assertNull(error);
  710. assertNull(error2);
  711. assertNull(error3);
  712. }
  713. /**
  714. * Tests that the deferred is still called when loadMultiple loads modules
  715. * that are already preloading.
  716. */
  717. function testLoadMultipleWhenPreloadingSameModules() {
  718. var mm = getModuleManager({'a': [], 'b': ['d'], 'c': [], 'd': []});
  719. mm.setLoader(createSuccessfulBatchLoader(mm));
  720. mm.setBatchModeEnabled(true);
  721. var origSetLoaded = mm.setLoaded;
  722. var calls = {'c': [0, 0, 0], 'd': [0, 0, 0]};
  723. mm.beforeLoadModuleCode = function(id) { calls[id][0]++; };
  724. mm.setLoaded = function(id) {
  725. calls[id][1]++;
  726. origSetLoaded.call(mm, id);
  727. };
  728. mm.afterLoadModuleCode = function(id) { calls[id][2]++; };
  729. var calledBack = false;
  730. var error = null;
  731. var calledBack2 = false;
  732. var error2 = null;
  733. mm.preloadModule('c', 2);
  734. mm.preloadModule('d', 3);
  735. assertFalse('module "c" should not be loading yet', mm.isModuleLoading('c'));
  736. assertFalse('module "d" should not be loading yet', mm.isModuleLoading('d'));
  737. clock.tick(2);
  738. assertTrue('module "c" should now be loading', mm.isModuleLoading('c'));
  739. clock.tick(1);
  740. assertTrue('module "d" should now be loading', mm.isModuleLoading('d'));
  741. var dMap = mm.loadMultiple(['c', 'd']);
  742. dMap['c'].addCallback(function(ctx) { calledBack = true; });
  743. dMap['c'].addErrback(function(err) { error = err; });
  744. dMap['d'].addCallback(function(ctx) { calledBack2 = true; });
  745. dMap['d'].addErrback(function(err) { error2 = err; });
  746. assertTrue('module "c" should still be loading', mm.isModuleLoading('c'));
  747. clock.tick(4);
  748. assertFalse('module "c" should be done loading', mm.isModuleLoading('c'));
  749. assertTrue('module "d" should still be loading', mm.isModuleLoading('d'));
  750. clock.tick(5);
  751. assertFalse('module "d" should be done loading', mm.isModuleLoading('d'));
  752. assertTrue(calledBack);
  753. assertTrue(calledBack2);
  754. assertEquals(
  755. 'beforeLoad should only be called once for "c"', 1, calls['c'][0]);
  756. assertEquals(
  757. 'setLoaded should only be called once for "c"', 1, calls['c'][1]);
  758. assertEquals(
  759. 'afterLoad should only be called once for "c"', 1, calls['c'][2]);
  760. assertEquals(
  761. 'beforeLoad should only be called once for "d"', 1, calls['d'][0]);
  762. assertEquals(
  763. 'setLoaded should only be called once for "d"', 1, calls['d'][1]);
  764. assertEquals(
  765. 'afterLoad should only be called once for "d"', 1, calls['d'][2]);
  766. assertNull(error);
  767. assertNull(error2);
  768. }
  769. /**
  770. * Tests loading a module via load when the module is already
  771. * loaded. The deferred's callback should be called immediately.
  772. */
  773. function testLoadWhenLoaded() {
  774. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  775. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  776. var calledBack = false;
  777. var error = null;
  778. mm.preloadModule('b', 2);
  779. clock.tick(10);
  780. assertFalse('module "b" should be done loading', mm.isModuleLoading('b'));
  781. var d = mm.load('b');
  782. d.addCallback(function(ctx) { calledBack = true; });
  783. d.addErrback(function(err) { error = err; });
  784. assertTrue(calledBack);
  785. assertNull(error);
  786. }
  787. /**
  788. * Tests that the deferred's errbacks are called if the module fails to load.
  789. */
  790. function testLoadWithFailingModule() {
  791. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  792. mm.setLoader(createUnsuccessfulLoader(mm, 401));
  793. mm.registerCallback(
  794. goog.module.ModuleManager.CallbackType.ERROR,
  795. function(callbackType, id, cause) {
  796. assertEquals(
  797. 'Failure cause was not as expected',
  798. goog.module.ModuleManager.FailureType.UNAUTHORIZED, cause);
  799. firedLoadFailed = true;
  800. });
  801. var calledBack = false;
  802. var error = null;
  803. var d = mm.load('a');
  804. d.addCallback(function(ctx) { calledBack = true; });
  805. d.addErrback(function(err) { error = err; });
  806. assertFalse(calledBack);
  807. assertNull(error);
  808. clock.tick(500);
  809. assertFalse(calledBack);
  810. // NOTE: Deferred always calls errbacks with an Error object. For now the
  811. // module manager just passes the FailureType which gets set as the Error
  812. // object's message.
  813. assertEquals(
  814. 'Failure cause was not as expected',
  815. goog.module.ModuleManager.FailureType.UNAUTHORIZED,
  816. Number(error.message));
  817. }
  818. /**
  819. * Tests that the deferred's errbacks are called if a module fails to load.
  820. */
  821. function testLoadMultipleWithFailingModule() {
  822. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  823. mm.setLoader(createUnsuccessfulLoader(mm, 401));
  824. mm.setBatchModeEnabled(true);
  825. mm.registerCallback(
  826. goog.module.ModuleManager.CallbackType.ERROR,
  827. function(callbackType, id, cause) {
  828. assertEquals(
  829. 'Failure cause was not as expected',
  830. goog.module.ModuleManager.FailureType.UNAUTHORIZED, cause);
  831. });
  832. var calledBack11 = false;
  833. var error11 = null;
  834. var calledBack12 = false;
  835. var error12 = null;
  836. var calledBack21 = false;
  837. var error21 = null;
  838. var calledBack22 = false;
  839. var error22 = null;
  840. var dMap = mm.loadMultiple(['a', 'b']);
  841. dMap['a'].addCallback(function(ctx) { calledBack11 = true; });
  842. dMap['a'].addErrback(function(err) { error11 = err; });
  843. dMap['b'].addCallback(function(ctx) { calledBack12 = true; });
  844. dMap['b'].addErrback(function(err) { error12 = err; });
  845. var dMap2 = mm.loadMultiple(['b', 'c']);
  846. dMap2['b'].addCallback(function(ctx) { calledBack21 = true; });
  847. dMap2['b'].addErrback(function(err) { error21 = err; });
  848. dMap2['c'].addCallback(function(ctx) { calledBack22 = true; });
  849. dMap2['c'].addErrback(function(err) { error22 = err; });
  850. assertFalse(calledBack11);
  851. assertFalse(calledBack12);
  852. assertFalse(calledBack21);
  853. assertFalse(calledBack22);
  854. assertNull(error11);
  855. assertNull(error12);
  856. assertNull(error21);
  857. assertNull(error22);
  858. clock.tick(5);
  859. assertFalse(calledBack11);
  860. assertFalse(calledBack12);
  861. assertFalse(calledBack21);
  862. assertFalse(calledBack22);
  863. // NOTE: Deferred always calls errbacks with an Error object. For now the
  864. // module manager just passes the FailureType which gets set as the Error
  865. // object's message.
  866. assertEquals(
  867. 'Failure cause was not as expected',
  868. goog.module.ModuleManager.FailureType.UNAUTHORIZED,
  869. Number(error11.message));
  870. assertEquals(
  871. 'Failure cause was not as expected',
  872. goog.module.ModuleManager.FailureType.UNAUTHORIZED,
  873. Number(error12.message));
  874. // The first deferred of the second load should be called since it asks for
  875. // one of the failed modules.
  876. assertEquals(
  877. 'Failure cause was not as expected',
  878. goog.module.ModuleManager.FailureType.UNAUTHORIZED,
  879. Number(error21.message));
  880. // The last deferred should be dropped so it is neither called back nor an
  881. // error.
  882. assertFalse(calledBack22);
  883. assertNull(error22);
  884. }
  885. /**
  886. * Tests that the right dependencies are cancelled on a loadMultiple failure.
  887. */
  888. function testLoadMultipleWithFailingModuleDependencies() {
  889. var mm =
  890. getModuleManager({'a': [], 'b': [], 'c': ['b'], 'd': ['c'], 'e': []});
  891. mm.setLoader(createUnsuccessfulLoader(mm, 401));
  892. mm.setBatchModeEnabled(true);
  893. var cancelledIds = [];
  894. mm.registerCallback(
  895. goog.module.ModuleManager.CallbackType.ERROR,
  896. function(callbackType, id, cause) {
  897. assertEquals(
  898. 'Failure cause was not as expected',
  899. goog.module.ModuleManager.FailureType.UNAUTHORIZED, cause);
  900. cancelledIds.push(id);
  901. });
  902. var calledBack11 = false;
  903. var error11 = null;
  904. var calledBack12 = false;
  905. var error12 = null;
  906. var calledBack21 = false;
  907. var error21 = null;
  908. var calledBack22 = false;
  909. var error22 = null;
  910. var calledBack23 = false;
  911. var error23 = null;
  912. var dMap = mm.loadMultiple(['a', 'b']);
  913. dMap['a'].addCallback(function(ctx) { calledBack11 = true; });
  914. dMap['a'].addErrback(function(err) { error11 = err; });
  915. dMap['b'].addCallback(function(ctx) { calledBack12 = true; });
  916. dMap['b'].addErrback(function(err) { error12 = err; });
  917. var dMap2 = mm.loadMultiple(['c', 'd', 'e']);
  918. dMap2['c'].addCallback(function(ctx) { calledBack21 = true; });
  919. dMap2['c'].addErrback(function(err) { error21 = err; });
  920. dMap2['d'].addCallback(function(ctx) { calledBack22 = true; });
  921. dMap2['d'].addErrback(function(err) { error22 = err; });
  922. dMap2['e'].addCallback(function(ctx) { calledBack23 = true; });
  923. dMap2['e'].addErrback(function(err) { error23 = err; });
  924. assertFalse(calledBack11);
  925. assertFalse(calledBack12);
  926. assertFalse(calledBack21);
  927. assertFalse(calledBack22);
  928. assertFalse(calledBack23);
  929. assertNull(error11);
  930. assertNull(error12);
  931. assertNull(error21);
  932. assertNull(error22);
  933. assertNull(error23);
  934. clock.tick(5);
  935. assertFalse(calledBack11);
  936. assertFalse(calledBack12);
  937. assertFalse(calledBack21);
  938. assertFalse(calledBack22);
  939. assertFalse(calledBack23);
  940. // NOTE: Deferred always calls errbacks with an Error object. For now the
  941. // module manager just passes the FailureType which gets set as the Error
  942. // object's message.
  943. assertEquals(
  944. 'Failure cause was not as expected',
  945. goog.module.ModuleManager.FailureType.UNAUTHORIZED,
  946. Number(error11.message));
  947. assertEquals(
  948. 'Failure cause was not as expected',
  949. goog.module.ModuleManager.FailureType.UNAUTHORIZED,
  950. Number(error12.message));
  951. // Check that among the failed modules, 'c' and 'd' are also cancelled
  952. // due to dependencies.
  953. assertTrue(goog.array.equals(['a', 'b', 'c', 'd'], cancelledIds.sort()));
  954. }
  955. /**
  956. * Tests that when loading multiple modules, the input array is not modified
  957. * when it has duplicates.
  958. */
  959. function testLoadMultipleWithDuplicates() {
  960. var mm = getModuleManager({'a': [], 'b': []});
  961. mm.setBatchModeEnabled(true);
  962. mm.setLoader(createSuccessfulBatchLoader(mm));
  963. var listWithDuplicates = ['a', 'a', 'b'];
  964. mm.loadMultiple(listWithDuplicates);
  965. assertArrayEquals(
  966. 'loadMultiple should not modify its input', ['a', 'a', 'b'],
  967. listWithDuplicates);
  968. }
  969. /**
  970. * Test loading dependencies transitively.
  971. */
  972. function testLoadingDepsInNonBatchMode1() {
  973. var mm =
  974. getModuleManager({'i': [], 'j': [], 'k': ['j'], 'l': ['i', 'j', 'k']});
  975. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  976. mm.preloadModule('j');
  977. clock.tick(5);
  978. assertTrue('module "j" should be loaded', mm.isModuleLoaded('j'));
  979. assertFalse('module "i" should not be loaded (1)', mm.isModuleLoaded('i'));
  980. assertFalse('module "k" should not be loaded (1)', mm.isModuleLoaded('k'));
  981. assertFalse('module "l" should not be loaded (1)', mm.isModuleLoaded('l'));
  982. // When loading a module in non-batch mode, its dependencies should be
  983. // requested independently, and in dependency order.
  984. mm.preloadModule('l');
  985. clock.tick(5);
  986. assertTrue('module "i" should be loaded', mm.isModuleLoaded('i'));
  987. assertFalse('module "k" should not be loaded (2)', mm.isModuleLoaded('k'));
  988. assertFalse('module "l" should not be loaded (2)', mm.isModuleLoaded('l'));
  989. clock.tick(5);
  990. assertTrue('module "k" should be loaded', mm.isModuleLoaded('k'));
  991. assertFalse('module "l" should not be loaded (3)', mm.isModuleLoaded('l'));
  992. clock.tick(5);
  993. assertTrue('module "l" should be loaded', mm.isModuleLoaded('l'));
  994. }
  995. /**
  996. * Test loading dependencies transitively and in dependency order.
  997. */
  998. function testLoadingDepsInNonBatchMode2() {
  999. var mm = getModuleManager({
  1000. 'h': [],
  1001. 'i': ['h'],
  1002. 'j': ['i'],
  1003. 'k': ['j'],
  1004. 'l': ['i', 'j', 'k'],
  1005. 'm': ['l']
  1006. });
  1007. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  1008. // When loading a module in non-batch mode, its dependencies should be
  1009. // requested independently, and in dependency order. The order in this
  1010. // case should be h,i,j,k,l,m.
  1011. mm.preloadModule('m');
  1012. clock.tick(5);
  1013. assertTrue('module "h" should be loaded', mm.isModuleLoaded('h'));
  1014. assertFalse('module "i" should not be loaded (1)', mm.isModuleLoaded('i'));
  1015. assertFalse('module "j" should not be loaded (1)', mm.isModuleLoaded('j'));
  1016. assertFalse('module "k" should not be loaded (1)', mm.isModuleLoaded('k'));
  1017. assertFalse('module "l" should not be loaded (1)', mm.isModuleLoaded('l'));
  1018. assertFalse('module "m" should not be loaded (1)', mm.isModuleLoaded('m'));
  1019. clock.tick(5);
  1020. assertTrue('module "i" should be loaded', mm.isModuleLoaded('i'));
  1021. assertFalse('module "j" should not be loaded (2)', mm.isModuleLoaded('j'));
  1022. assertFalse('module "k" should not be loaded (2)', mm.isModuleLoaded('k'));
  1023. assertFalse('module "l" should not be loaded (2)', mm.isModuleLoaded('l'));
  1024. assertFalse('module "m" should not be loaded (2)', mm.isModuleLoaded('m'));
  1025. clock.tick(5);
  1026. assertTrue('module "j" should be loaded', mm.isModuleLoaded('j'));
  1027. assertFalse('module "k" should not be loaded (3)', mm.isModuleLoaded('k'));
  1028. assertFalse('module "l" should not be loaded (3)', mm.isModuleLoaded('l'));
  1029. assertFalse('module "m" should not be loaded (3)', mm.isModuleLoaded('m'));
  1030. clock.tick(5);
  1031. assertTrue('module "k" should be loaded', mm.isModuleLoaded('k'));
  1032. assertFalse('module "l" should not be loaded (4)', mm.isModuleLoaded('l'));
  1033. assertFalse('module "m" should not be loaded (4)', mm.isModuleLoaded('m'));
  1034. clock.tick(5);
  1035. assertTrue('module "l" should be loaded', mm.isModuleLoaded('l'));
  1036. assertFalse('module "m" should not be loaded (5)', mm.isModuleLoaded('m'));
  1037. clock.tick(5);
  1038. assertTrue('module "m" should be loaded', mm.isModuleLoaded('m'));
  1039. }
  1040. function testLoadingDepsInBatchMode() {
  1041. var mm =
  1042. getModuleManager({'e': [], 'f': [], 'g': ['f'], 'h': ['e', 'f', 'g']});
  1043. mm.setLoader(createSuccessfulBatchLoader(mm));
  1044. mm.setBatchModeEnabled(true);
  1045. mm.preloadModule('f');
  1046. clock.tick(5);
  1047. assertTrue('module "f" should be loaded', mm.isModuleLoaded('f'));
  1048. assertFalse('module "e" should not be loaded (1)', mm.isModuleLoaded('e'));
  1049. assertFalse('module "g" should not be loaded (1)', mm.isModuleLoaded('g'));
  1050. assertFalse('module "h" should not be loaded (1)', mm.isModuleLoaded('h'));
  1051. // When loading a module in batch mode, its not-yet-loaded dependencies
  1052. // should be requested at the same time, and in dependency order.
  1053. mm.preloadModule('h');
  1054. clock.tick(5);
  1055. assertTrue('module "e" should be loaded', mm.isModuleLoaded('e'));
  1056. assertFalse('module "g" should not be loaded (2)', mm.isModuleLoaded('g'));
  1057. assertFalse('module "h" should not be loaded (2)', mm.isModuleLoaded('h'));
  1058. clock.tick(2);
  1059. assertTrue('module "g" should be loaded', mm.isModuleLoaded('g'));
  1060. assertFalse('module "h" should not be loaded (3)', mm.isModuleLoaded('h'));
  1061. clock.tick(2);
  1062. assertTrue('module "h" should be loaded', mm.isModuleLoaded('h'));
  1063. }
  1064. /**
  1065. * Test unauthorized errors while loading modules.
  1066. */
  1067. function testUnauthorizedLoading() {
  1068. var mm = getModuleManager({'m': [], 'n': [], 'o': ['n']});
  1069. mm.setLoader(createUnsuccessfulLoader(mm, 401));
  1070. // Callback checks for an unauthorized error
  1071. var firedLoadFailed = false;
  1072. mm.registerCallback(
  1073. goog.module.ModuleManager.CallbackType.ERROR,
  1074. function(callbackType, id, cause) {
  1075. assertEquals(
  1076. 'Failure cause was not as expected',
  1077. goog.module.ModuleManager.FailureType.UNAUTHORIZED, cause);
  1078. firedLoadFailed = true;
  1079. });
  1080. mm.execOnLoad('o', function() {});
  1081. assertTrue('module "o" should be loading', mm.isModuleLoading('o'));
  1082. assertTrue('module "n" should be loading', mm.isModuleLoading('n'));
  1083. clock.tick(5);
  1084. assertTrue(
  1085. 'should have called unauthorized module callback', firedLoadFailed);
  1086. assertFalse('module "o" should not be loaded', mm.isModuleLoaded('o'));
  1087. assertFalse('module "o" should not be loading', mm.isModuleLoading('o'));
  1088. assertFalse('module "n" should not be loaded', mm.isModuleLoaded('n'));
  1089. assertFalse('module "n" should not be loading', mm.isModuleLoading('n'));
  1090. }
  1091. /**
  1092. * Test error loading modules which are retried.
  1093. */
  1094. function testErrorLoadingModule() {
  1095. var mm = getModuleManager({'p': ['q'], 'q': [], 'r': ['q', 'p']});
  1096. mm.setLoader(createUnsuccessfulLoader(mm, 500));
  1097. mm.preloadModule('r');
  1098. clock.tick(4);
  1099. // A module request is now underway using the unsuccessful loader.
  1100. // We substitute a successful loader for future module load requests.
  1101. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  1102. clock.tick(1);
  1103. assertFalse('module "q" should not be loaded (1)', mm.isModuleLoaded('q'));
  1104. assertFalse('module "p" should not be loaded (1)', mm.isModuleLoaded('p'));
  1105. assertFalse('module "r" should not be loaded (1)', mm.isModuleLoaded('r'));
  1106. // Failed loads are automatically retried after a backOff.
  1107. clock.tick(5 + mm.getBackOff_());
  1108. assertTrue('module "q" should be loaded', mm.isModuleLoaded('q'));
  1109. assertFalse('module "p" should not be loaded (2)', mm.isModuleLoaded('p'));
  1110. assertFalse('module "r" should not be loaded (2)', mm.isModuleLoaded('r'));
  1111. // A successful load decrements the backOff.
  1112. clock.tick(5);
  1113. assertTrue('module "p" should be loaded', mm.isModuleLoaded('p'));
  1114. assertFalse('module "r" should not be loaded (3)', mm.isModuleLoaded('r'));
  1115. clock.tick(5);
  1116. assertTrue('module "r" should be loaded', mm.isModuleLoaded('r'));
  1117. }
  1118. /**
  1119. * Tests error loading modules which are retried.
  1120. */
  1121. function testErrorLoadingModule_batchMode() {
  1122. var mm = getModuleManager({'p': ['q'], 'q': [], 'r': ['q', 'p']});
  1123. mm.setLoader(createUnsuccessfulBatchLoader(mm, 500));
  1124. mm.setBatchModeEnabled(true);
  1125. mm.preloadModule('r');
  1126. clock.tick(4);
  1127. // A module request is now underway using the unsuccessful loader.
  1128. // We substitute a successful loader for future module load requests.
  1129. mm.setLoader(createSuccessfulBatchLoader(mm));
  1130. clock.tick(1);
  1131. assertFalse('module "q" should not be loaded (1)', mm.isModuleLoaded('q'));
  1132. assertFalse('module "p" should not be loaded (1)', mm.isModuleLoaded('p'));
  1133. assertFalse('module "r" should not be loaded (1)', mm.isModuleLoaded('r'));
  1134. // Failed loads are automatically retried after a backOff.
  1135. clock.tick(5 + mm.getBackOff_());
  1136. assertTrue('module "q" should be loaded', mm.isModuleLoaded('q'));
  1137. clock.tick(2);
  1138. assertTrue('module "p" should not be loaded (2)', mm.isModuleLoaded('p'));
  1139. clock.tick(2);
  1140. assertTrue('module "r" should not be loaded (2)', mm.isModuleLoaded('r'));
  1141. }
  1142. /**
  1143. * Test consecutive errors in loading modules.
  1144. */
  1145. function testConsecutiveErrors() {
  1146. var mm = getModuleManager({'s': []});
  1147. mm.setLoader(createUnsuccessfulLoader(mm, 500));
  1148. // Register an error callback for consecutive failures.
  1149. var firedLoadFailed = false;
  1150. mm.registerCallback(
  1151. goog.module.ModuleManager.CallbackType.ERROR,
  1152. function(callbackType, id, cause) {
  1153. assertEquals(
  1154. 'Failure cause was not as expected',
  1155. goog.module.ModuleManager.FailureType.CONSECUTIVE_FAILURES, cause);
  1156. firedLoadFailed = true;
  1157. });
  1158. mm.preloadModule('s');
  1159. assertFalse('module "s" should not be loaded (0)', mm.isModuleLoaded('s'));
  1160. // Fail twice.
  1161. for (var i = 0; i < 2; i++) {
  1162. clock.tick(5 + mm.getBackOff_());
  1163. assertFalse('module "s" should not be loaded (1)', mm.isModuleLoaded('s'));
  1164. assertFalse('should not fire failed callback (1)', firedLoadFailed);
  1165. }
  1166. // Fail a third time and check that the callback is fired.
  1167. clock.tick(5 + mm.getBackOff_());
  1168. assertFalse('module "s" should not be loaded (2)', mm.isModuleLoaded('s'));
  1169. assertTrue('should have fired failed callback', firedLoadFailed);
  1170. // Check that it doesn't attempt to load the module anymore after it has
  1171. // failed.
  1172. var triedLoad = false;
  1173. mm.setLoader({
  1174. loadModules: function(ids, moduleInfoMap, opt_successFn, opt_errFn) {
  1175. triedLoad = true;
  1176. }
  1177. });
  1178. // Also reset the failed callback flag and make sure it isn't called
  1179. // again.
  1180. firedLoadFailed = false;
  1181. clock.tick(10 + mm.getBackOff_());
  1182. assertFalse('module "s" should not be loaded (3)', mm.isModuleLoaded('s'));
  1183. assertFalse('No more loads should have been tried', triedLoad);
  1184. assertFalse(
  1185. 'The load failed callback should be fired only once', firedLoadFailed);
  1186. }
  1187. /**
  1188. * Test loading errors due to old code.
  1189. */
  1190. function testOldCodeGoneError() {
  1191. var mm = getModuleManager({'s': []});
  1192. mm.setLoader(createUnsuccessfulLoader(mm, 410));
  1193. // Callback checks for an old code failure
  1194. var firedLoadFailed = false;
  1195. mm.registerCallback(
  1196. goog.module.ModuleManager.CallbackType.ERROR,
  1197. function(callbackType, id, cause) {
  1198. assertEquals(
  1199. 'Failure cause was not as expected',
  1200. goog.module.ModuleManager.FailureType.OLD_CODE_GONE, cause);
  1201. firedLoadFailed = true;
  1202. });
  1203. mm.preloadModule('s', 0);
  1204. assertFalse('module "s" should not be loaded (0)', mm.isModuleLoaded('s'));
  1205. clock.tick(5);
  1206. assertFalse('module "s" should not be loaded (1)', mm.isModuleLoaded('s'));
  1207. assertTrue('should have called old code gone callback', firedLoadFailed);
  1208. }
  1209. /**
  1210. * Test timeout.
  1211. */
  1212. function testTimeout() {
  1213. var mm = getModuleManager({'s': []});
  1214. mm.setLoader(createTimeoutLoader(mm));
  1215. // Callback checks for timeout
  1216. var firedTimeout = false;
  1217. mm.registerCallback(
  1218. goog.module.ModuleManager.CallbackType.ERROR,
  1219. function(callbackType, id, cause) {
  1220. assertEquals(
  1221. 'Failure cause was not as expected',
  1222. goog.module.ModuleManager.FailureType.TIMEOUT, cause);
  1223. firedTimeout = true;
  1224. });
  1225. mm.preloadModule('s', 0);
  1226. assertFalse('module "s" should not be loaded (0)', mm.isModuleLoaded('s'));
  1227. clock.tick(5);
  1228. assertFalse('module "s" should not be loaded (1)', mm.isModuleLoaded('s'));
  1229. assertTrue('should have called timeout callback', firedTimeout);
  1230. }
  1231. /**
  1232. * Tests that an error during execOnLoad will trigger the error callback.
  1233. */
  1234. function testExecOnLoadError() {
  1235. // Expect two callbacks, each of which will be called with callback type
  1236. // ERROR, the right module id and failure type INIT_ERROR.
  1237. var errorCallback1 = goog.testing.createFunctionMock('callback1');
  1238. errorCallback1(
  1239. goog.module.ModuleManager.CallbackType.ERROR, 'b',
  1240. goog.module.ModuleManager.FailureType.INIT_ERROR);
  1241. var errorCallback2 = goog.testing.createFunctionMock('callback2');
  1242. errorCallback2(
  1243. goog.module.ModuleManager.CallbackType.ERROR, 'b',
  1244. goog.module.ModuleManager.FailureType.INIT_ERROR);
  1245. errorCallback1.$replay();
  1246. errorCallback2.$replay();
  1247. var mm = new goog.module.ModuleManager();
  1248. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  1249. // Register the first callback before setting the module info map.
  1250. mm.registerCallback(
  1251. goog.module.ModuleManager.CallbackType.ERROR, errorCallback1);
  1252. mm.setAllModuleInfo({'a': [], 'b': [], 'c': []});
  1253. // Register the second callback after setting the module info map.
  1254. mm.registerCallback(
  1255. goog.module.ModuleManager.CallbackType.ERROR, errorCallback2);
  1256. var execOnLoadBCalled = false;
  1257. mm.execOnLoad('b', function() {
  1258. execOnLoadBCalled = true;
  1259. throw new Error();
  1260. });
  1261. assertThrows(function() { clock.tick(5); });
  1262. assertTrue(
  1263. 'execOnLoad should have been called on module b.', execOnLoadBCalled);
  1264. errorCallback1.$verify();
  1265. errorCallback2.$verify();
  1266. }
  1267. /**
  1268. * Tests that an error during execOnLoad will trigger the error callback.
  1269. * Uses setAllModuleInfoString rather than setAllModuleInfo.
  1270. */
  1271. function testExecOnLoadErrorModuleInfoString() {
  1272. // Expect a callback to be called with callback type ERROR, the right module
  1273. // id and failure type INIT_ERROR.
  1274. var errorCallback = goog.testing.createFunctionMock('callback');
  1275. errorCallback(
  1276. goog.module.ModuleManager.CallbackType.ERROR, 'b',
  1277. goog.module.ModuleManager.FailureType.INIT_ERROR);
  1278. errorCallback.$replay();
  1279. var mm = new goog.module.ModuleManager();
  1280. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  1281. // Register the first callback before setting the module info map.
  1282. mm.registerCallback(
  1283. goog.module.ModuleManager.CallbackType.ERROR, errorCallback);
  1284. mm.setAllModuleInfoString('a/b/c');
  1285. var execOnLoadBCalled = false;
  1286. mm.execOnLoad('b', function() {
  1287. execOnLoadBCalled = true;
  1288. throw new Error();
  1289. });
  1290. assertThrows(function() { clock.tick(5); });
  1291. assertTrue(
  1292. 'execOnLoad should have been called on module b.', execOnLoadBCalled);
  1293. errorCallback.$verify();
  1294. }
  1295. /**
  1296. * Make sure ModuleInfo objects in moduleInfoMap_ get disposed.
  1297. */
  1298. function testDispose() {
  1299. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  1300. var moduleInfoA = mm.getModuleInfo('a');
  1301. assertNotNull(moduleInfoA);
  1302. var moduleInfoB = mm.getModuleInfo('b');
  1303. assertNotNull(moduleInfoB);
  1304. var moduleInfoC = mm.getModuleInfo('c');
  1305. assertNotNull(moduleInfoC);
  1306. mm.dispose();
  1307. assertTrue(moduleInfoA.isDisposed());
  1308. assertTrue(moduleInfoB.isDisposed());
  1309. assertTrue(moduleInfoC.isDisposed());
  1310. }
  1311. function testDependencyOrderingWithSimpleDeps() {
  1312. var mm = getModuleManager({
  1313. 'a': ['b', 'c'],
  1314. 'b': ['d'],
  1315. 'c': ['e', 'f'],
  1316. 'd': [],
  1317. 'e': [],
  1318. 'f': []
  1319. });
  1320. var ids = mm.getNotYetLoadedTransitiveDepIds_('a');
  1321. assertDependencyOrder(ids, mm);
  1322. assertArrayEquals(['d', 'e', 'f', 'b', 'c', 'a'], ids);
  1323. }
  1324. function testDependencyOrderingWithCommonDepsInDeps() {
  1325. // Tests to make sure that if dependencies of the root are loaded before
  1326. // their common dependencies.
  1327. var mm = getModuleManager({'a': ['b', 'c'], 'b': ['d'], 'c': ['d'], 'd': []});
  1328. var ids = mm.getNotYetLoadedTransitiveDepIds_('a');
  1329. assertDependencyOrder(ids, mm);
  1330. assertArrayEquals(['d', 'b', 'c', 'a'], ids);
  1331. }
  1332. function testDependencyOrderingWithCommonDepsInRoot1() {
  1333. // Tests the case where a dependency of the root depends on another
  1334. // dependency of the root. Regardless of ordering in the root's
  1335. // deps.
  1336. var mm = getModuleManager({'a': ['b', 'c'], 'b': ['c'], 'c': []});
  1337. var ids = mm.getNotYetLoadedTransitiveDepIds_('a');
  1338. assertDependencyOrder(ids, mm);
  1339. assertArrayEquals(['c', 'b', 'a'], ids);
  1340. }
  1341. function testDependencyOrderingWithCommonDepsInRoot2() {
  1342. // Tests the case where a dependency of the root depends on another
  1343. // dependency of the root. Regardless of ordering in the root's
  1344. // deps.
  1345. var mm = getModuleManager({'a': ['b', 'c'], 'b': [], 'c': ['b']});
  1346. var ids = mm.getNotYetLoadedTransitiveDepIds_('a');
  1347. assertDependencyOrder(ids, mm);
  1348. assertArrayEquals(['b', 'c', 'a'], ids);
  1349. }
  1350. function testDependencyOrderingWithGmailExample() {
  1351. // Real dependency graph taken from gmail.
  1352. var mm = getModuleManager({
  1353. 's': ['dp', 'ml', 'md'],
  1354. 'dp': ['a'],
  1355. 'ml': ['ld', 'm'],
  1356. 'ld': ['a'],
  1357. 'm': ['ad', 'mh', 'n'],
  1358. 'md': ['mh', 'ld'],
  1359. 'a': [],
  1360. 'mh': [],
  1361. 'ad': [],
  1362. 'n': []
  1363. });
  1364. mm.setLoaded('a');
  1365. mm.setLoaded('m');
  1366. mm.setLoaded('n');
  1367. mm.setLoaded('ad');
  1368. mm.setLoaded('mh');
  1369. var ids = mm.getNotYetLoadedTransitiveDepIds_('s');
  1370. assertDependencyOrder(ids, mm);
  1371. assertArrayEquals(['ld', 'dp', 'ml', 'md', 's'], ids);
  1372. }
  1373. function assertDependencyOrder(list, mm) {
  1374. var seen = {};
  1375. for (var i = 0; i < list.length; i++) {
  1376. var id = list[i];
  1377. seen[id] = true;
  1378. var deps = mm.getModuleInfo(id).getDependencies();
  1379. for (var j = 0; j < deps.length; j++) {
  1380. var dep = deps[j];
  1381. assertTrue(
  1382. 'Unresolved dependency [' + dep + '] for [' + id + '].',
  1383. seen[dep] || mm.getModuleInfo(dep).isLoaded());
  1384. }
  1385. }
  1386. }
  1387. function testRegisterInitializationCallback() {
  1388. var initCalled = 0;
  1389. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  1390. mm.setLoader(
  1391. createSuccessfulNonBatchLoaderWithRegisterInitCallback(
  1392. mm, function() { ++initCalled; }));
  1393. execOnLoad_(mm);
  1394. // execOnLoad_ loads modules a and c
  1395. assertTrue(initCalled == 2);
  1396. }
  1397. function createSuccessfulNonBatchLoaderWithRegisterInitCallback(moduleMgr, fn) {
  1398. return {
  1399. loadModules: function(
  1400. ids, moduleInfoMap, opt_successFn, opt_errFn, opt_timeoutFn) {
  1401. moduleMgr.beforeLoadModuleCode(ids[0]);
  1402. moduleMgr.registerInitializationCallback(fn);
  1403. setTimeout(function() {
  1404. moduleMgr.setLoaded(ids[0]);
  1405. moduleMgr.afterLoadModuleCode(ids[0]);
  1406. if (opt_successFn) {
  1407. opt_successFn();
  1408. }
  1409. }, 5);
  1410. }
  1411. };
  1412. }
  1413. function testSetModuleConstructor() {
  1414. var initCalled = 0;
  1415. var mm = getModuleManager({'a': [], 'b': [], 'c': []});
  1416. var info = {
  1417. 'a': {ctor: AModule, count: 0},
  1418. 'b': {ctor: BModule, count: 0},
  1419. 'c': {ctor: CModule, count: 0}
  1420. };
  1421. function AModule() {
  1422. ++info['a'].count;
  1423. goog.module.BaseModule.call(this);
  1424. }
  1425. goog.inherits(AModule, goog.module.BaseModule);
  1426. function BModule() {
  1427. ++info['b'].count;
  1428. goog.module.BaseModule.call(this);
  1429. }
  1430. goog.inherits(BModule, goog.module.BaseModule);
  1431. function CModule() {
  1432. ++info['c'].count;
  1433. goog.module.BaseModule.call(this);
  1434. }
  1435. goog.inherits(CModule, goog.module.BaseModule);
  1436. mm.setLoader(createSuccessfulNonBatchLoaderWithConstructor(mm, info));
  1437. execOnLoad_(mm);
  1438. assertTrue(info['a'].count == 1);
  1439. assertTrue(info['b'].count == 0);
  1440. assertTrue(info['c'].count == 1);
  1441. assertTrue(mm.getModuleInfo('a').getModule() instanceof AModule);
  1442. assertTrue(mm.getModuleInfo('c').getModule() instanceof CModule);
  1443. }
  1444. /**
  1445. * Tests that a call to load the loading module during module initialization
  1446. * doesn't trigger a second load.
  1447. */
  1448. function testLoadWhenInitializing() {
  1449. var mm = getModuleManager({'a': []});
  1450. mm.setLoader(createSuccessfulNonBatchLoader(mm));
  1451. var info = {'a': {ctor: AModule, count: 0}};
  1452. function AModule() {
  1453. ++info['a'].count;
  1454. goog.module.BaseModule.call(this);
  1455. }
  1456. goog.inherits(AModule, goog.module.BaseModule);
  1457. AModule.prototype.initialize = function() { mm.load('a'); };
  1458. mm.setLoader(createSuccessfulNonBatchLoaderWithConstructor(mm, info));
  1459. mm.preloadModule('a');
  1460. clock.tick(5);
  1461. assertEquals(info['a'].count, 1);
  1462. }
  1463. function testErrorInEarlyCallback() {
  1464. var errback = goog.testing.recordFunction();
  1465. var callback = goog.testing.recordFunction();
  1466. var mm = getModuleManager({'a': [], 'b': ['a']});
  1467. mm.getModuleInfo('a').registerEarlyCallback(goog.functions.error('error'));
  1468. mm.getModuleInfo('a').registerCallback(callback);
  1469. mm.getModuleInfo('a').registerErrback(errback);
  1470. mm.setLoader(
  1471. createSuccessfulNonBatchLoaderWithConstructor(
  1472. mm, createModulesFor('a', 'b')));
  1473. mm.preloadModule('b');
  1474. var e = assertThrows(function() { clock.tick(5); });
  1475. assertEquals('error', e.message);
  1476. assertEquals(0, callback.getCallCount());
  1477. assertEquals(1, errback.getCallCount());
  1478. assertEquals(
  1479. goog.module.ModuleManager.FailureType.INIT_ERROR,
  1480. errback.getLastCall().getArguments()[0]);
  1481. assertTrue(mm.getModuleInfo('a').isLoaded());
  1482. assertFalse(mm.getModuleInfo('b').isLoaded());
  1483. clock.tick(5);
  1484. assertTrue(mm.getModuleInfo('b').isLoaded());
  1485. }
  1486. function testErrorInNormalCallback() {
  1487. var earlyCallback = goog.testing.recordFunction();
  1488. var errback = goog.testing.recordFunction();
  1489. var mm = getModuleManager({'a': [], 'b': ['a']});
  1490. mm.getModuleInfo('a').registerEarlyCallback(earlyCallback);
  1491. mm.getModuleInfo('a').registerEarlyCallback(goog.functions.error('error'));
  1492. mm.getModuleInfo('a').registerErrback(errback);
  1493. mm.setLoader(
  1494. createSuccessfulNonBatchLoaderWithConstructor(
  1495. mm, createModulesFor('a', 'b')));
  1496. mm.preloadModule('b');
  1497. var e = assertThrows(function() { clock.tick(10); });
  1498. clock.tick(10);
  1499. assertEquals('error', e.message);
  1500. assertEquals(1, errback.getCallCount());
  1501. assertEquals(
  1502. goog.module.ModuleManager.FailureType.INIT_ERROR,
  1503. errback.getLastCall().getArguments()[0]);
  1504. assertTrue(mm.getModuleInfo('a').isLoaded());
  1505. assertTrue(mm.getModuleInfo('b').isLoaded());
  1506. }
  1507. function testErrorInErrback() {
  1508. var mm = getModuleManager({'a': [], 'b': ['a']});
  1509. mm.getModuleInfo('a').registerCallback(goog.functions.error('error1'));
  1510. mm.getModuleInfo('a').registerErrback(goog.functions.error('error2'));
  1511. mm.setLoader(
  1512. createSuccessfulNonBatchLoaderWithConstructor(
  1513. mm, createModulesFor('a', 'b')));
  1514. mm.preloadModule('a');
  1515. var e = assertThrows(function() { clock.tick(10); });
  1516. assertEquals('error1', e.message);
  1517. var e = assertThrows(function() { clock.tick(10); });
  1518. assertEquals('error2', e.message);
  1519. assertTrue(mm.getModuleInfo('a').isLoaded());
  1520. }
  1521. function createModulesFor(var_args) {
  1522. var result = {};
  1523. for (var i = 0; i < arguments.length; i++) {
  1524. var key = arguments[i];
  1525. result[key] = {ctor: goog.module.BaseModule};
  1526. }
  1527. return result;
  1528. }
  1529. function createSuccessfulNonBatchLoaderWithConstructor(moduleMgr, info) {
  1530. return {
  1531. loadModules: function(
  1532. ids, moduleInfoMap, opt_successFn, opt_errFn, opt_timeoutFn) {
  1533. setTimeout(function() {
  1534. moduleMgr.beforeLoadModuleCode(ids[0]);
  1535. moduleMgr.setModuleConstructor(info[ids[0]].ctor);
  1536. moduleMgr.setLoaded(ids[0]);
  1537. moduleMgr.afterLoadModuleCode(ids[0]);
  1538. if (opt_successFn) {
  1539. opt_successFn();
  1540. }
  1541. }, 5);
  1542. }
  1543. };
  1544. }
  1545. function testInitCallbackInBaseModule() {
  1546. var mm = new goog.module.ModuleManager();
  1547. var called = false;
  1548. var context;
  1549. mm.registerInitializationCallback(function(mcontext) {
  1550. called = true;
  1551. context = mcontext;
  1552. });
  1553. mm.setAllModuleInfo({'a': [], 'b': ['a']});
  1554. assertTrue('Base initialization not called', called);
  1555. assertNull('Context should still be null', context);
  1556. var mm = new goog.module.ModuleManager();
  1557. called = false;
  1558. mm.registerInitializationCallback(function(mcontext) {
  1559. called = true;
  1560. context = mcontext;
  1561. });
  1562. var appContext = {};
  1563. mm.setModuleContext(appContext);
  1564. assertTrue('Base initialization not called after setModuleContext', called);
  1565. assertEquals('Did not receive module context', appContext, context);
  1566. }
  1567. function testSetAllModuleInfoString() {
  1568. var info = 'base/one:0/two:0/three:0,1,2/four:0,3/five:';
  1569. var mm = new goog.module.ModuleManager();
  1570. mm.setAllModuleInfoString(info);
  1571. assertNotNull('Base should exist', mm.getModuleInfo('base'));
  1572. assertNotNull('One should exist', mm.getModuleInfo('one'));
  1573. assertNotNull('Two should exist', mm.getModuleInfo('two'));
  1574. assertNotNull('Three should exist', mm.getModuleInfo('three'));
  1575. assertNotNull('Four should exist', mm.getModuleInfo('four'));
  1576. assertNotNull('Five should exist', mm.getModuleInfo('five'));
  1577. assertArrayEquals(
  1578. ['base', 'one', 'two'], mm.getModuleInfo('three').getDependencies());
  1579. assertArrayEquals(
  1580. ['base', 'three'], mm.getModuleInfo('four').getDependencies());
  1581. assertArrayEquals([], mm.getModuleInfo('five').getDependencies());
  1582. }
  1583. function testSetAllModuleInfoStringWithEmptyString() {
  1584. var mm = new goog.module.ModuleManager();
  1585. var called = false;
  1586. var context;
  1587. mm.registerInitializationCallback(function(mcontext) {
  1588. called = true;
  1589. context = mcontext;
  1590. });
  1591. mm.setAllModuleInfoString('');
  1592. assertTrue('Initialization not called', called);
  1593. }
  1594. function testBackOffAmounts() {
  1595. var mm = new goog.module.ModuleManager();
  1596. assertEquals(0, mm.getBackOff_());
  1597. mm.consecutiveFailures_++;
  1598. assertEquals(5000, mm.getBackOff_());
  1599. mm.consecutiveFailures_++;
  1600. assertEquals(20000, mm.getBackOff_());
  1601. }
  1602. /**
  1603. * Tests that the IDLE callbacks are executed for active->idle transitions
  1604. * after setAllModuleInfoString with currently loading modules.
  1605. */
  1606. function testIdleCallbackWithInitialModules() {
  1607. var callback = goog.testing.recordFunction();
  1608. var mm = new goog.module.ModuleManager();
  1609. mm.setAllModuleInfoString('a', ['a']);
  1610. mm.registerCallback(goog.module.ModuleManager.CallbackType.IDLE, callback);
  1611. assertTrue(mm.isActive());
  1612. mm.beforeLoadModuleCode('a');
  1613. assertEquals(0, callback.getCallCount());
  1614. mm.setLoaded('a');
  1615. mm.afterLoadModuleCode('a');
  1616. assertFalse(mm.isActive());
  1617. assertEquals(1, callback.getCallCount());
  1618. }