/public/javascripts/dojo/release/dojo/util/doh/runner.js

http://enginey.googlecode.com/ · JavaScript · 1053 lines · 784 code · 98 blank · 171 comment · 197 complexity · fc927fab4e5e5573211f0f5d4c52b26d MD5 · raw file

  1. // package system gunk.
  2. try{
  3. dojo.provide("doh.runner");
  4. }catch(e){
  5. if(!this["doh"]){
  6. doh = {};
  7. }
  8. }
  9. //
  10. // Utility Functions and Classes
  11. //
  12. doh.selfTest = false;
  13. doh.global = this;
  14. doh.hitch = function(/*Object*/thisObject, /*Function|String*/method /*, ...*/){
  15. var args = [];
  16. for(var x=2; x<arguments.length; x++){
  17. args.push(arguments[x]);
  18. }
  19. var fcn = ((typeof method == "string") ? thisObject[method] : method) || function(){};
  20. return function(){
  21. var ta = args.concat([]); // make a copy
  22. for(var x=0; x<arguments.length; x++){
  23. ta.push(arguments[x]);
  24. }
  25. return fcn.apply(thisObject, ta); // Function
  26. };
  27. }
  28. doh._mixin = function(/*Object*/ obj, /*Object*/ props){
  29. // summary:
  30. // Adds all properties and methods of props to obj. This addition is
  31. // "prototype extension safe", so that instances of objects will not
  32. // pass along prototype defaults.
  33. var tobj = {};
  34. for(var x in props){
  35. // the "tobj" condition avoid copying properties in "props"
  36. // inherited from Object.prototype. For example, if obj has a custom
  37. // toString() method, don't overwrite it with the toString() method
  38. // that props inherited from Object.protoype
  39. if((typeof tobj[x] == "undefined") || (tobj[x] != props[x])){
  40. obj[x] = props[x];
  41. }
  42. }
  43. // IE doesn't recognize custom toStrings in for..in
  44. if( this["document"]
  45. && document.all
  46. && (typeof props["toString"] == "function")
  47. && (props["toString"] != obj["toString"])
  48. && (props["toString"] != tobj["toString"])
  49. ){
  50. obj.toString = props.toString;
  51. }
  52. return obj; // Object
  53. }
  54. doh.mixin = function(/*Object*/obj, /*Object...*/props){
  55. // summary: Adds all properties and methods of props to obj.
  56. for(var i=1, l=arguments.length; i<l; i++){
  57. doh._mixin(obj, arguments[i]);
  58. }
  59. return obj; // Object
  60. }
  61. doh.extend = function(/*Object*/ constructor, /*Object...*/ props){
  62. // summary:
  63. // Adds all properties and methods of props to constructor's
  64. // prototype, making them available to all instances created with
  65. // constructor.
  66. for(var i=1, l=arguments.length; i<l; i++){
  67. doh._mixin(constructor.prototype, arguments[i]);
  68. }
  69. return constructor; // Object
  70. }
  71. doh._line = "------------------------------------------------------------";
  72. /*
  73. doh._delegate = function(obj, props){
  74. // boodman-crockford delegation
  75. function TMP(){};
  76. TMP.prototype = obj;
  77. var tmp = new TMP();
  78. if(props){
  79. dojo.lang.mixin(tmp, props);
  80. }
  81. return tmp;
  82. }
  83. */
  84. doh.debug = function(){
  85. // summary:
  86. // takes any number of arguments and sends them to whatever debugging
  87. // or logging facility is available in this environment
  88. // YOUR TEST RUNNER NEEDS TO IMPLEMENT THIS
  89. }
  90. doh._AssertFailure = function(msg, hint){
  91. // idea for this as way of dis-ambiguating error types is from JUM.
  92. // The JUM is dead! Long live the JUM!
  93. if(!(this instanceof doh._AssertFailure)){
  94. return new doh._AssertFailure(msg);
  95. }
  96. if(hint){
  97. msg = (new String(msg||""))+" with hint: \n\t\t"+(new String(hint)+"\n");
  98. }
  99. this.message = new String(msg||"");
  100. return this;
  101. }
  102. doh._AssertFailure.prototype = new Error();
  103. doh._AssertFailure.prototype.constructor = doh._AssertFailure;
  104. doh._AssertFailure.prototype.name = "doh._AssertFailure";
  105. doh.Deferred = function(canceller){
  106. this.chain = [];
  107. this.id = this._nextId();
  108. this.fired = -1;
  109. this.paused = 0;
  110. this.results = [null, null];
  111. this.canceller = canceller;
  112. this.silentlyCancelled = false;
  113. };
  114. doh.extend(doh.Deferred, {
  115. getTestCallback: function(cb, scope){
  116. var _this = this;
  117. return function(){
  118. try{
  119. cb.apply(scope||doh.global||_this, arguments);
  120. }catch(e){
  121. _this.errback(e);
  122. return;
  123. }
  124. _this.callback(true);
  125. };
  126. },
  127. getFunctionFromArgs: function(){
  128. var a = arguments;
  129. if((a[0])&&(!a[1])){
  130. if(typeof a[0] == "function"){
  131. return a[0];
  132. }else if(typeof a[0] == "string"){
  133. return doh.global[a[0]];
  134. }
  135. }else if((a[0])&&(a[1])){
  136. return doh.hitch(a[0], a[1]);
  137. }
  138. return null;
  139. },
  140. makeCalled: function() {
  141. var deferred = new doh.Deferred();
  142. deferred.callback();
  143. return deferred;
  144. },
  145. _nextId: (function(){
  146. var n = 1;
  147. return function(){ return n++; };
  148. })(),
  149. cancel: function(){
  150. if(this.fired == -1){
  151. if (this.canceller){
  152. this.canceller(this);
  153. }else{
  154. this.silentlyCancelled = true;
  155. }
  156. if(this.fired == -1){
  157. this.errback(new Error("Deferred(unfired)"));
  158. }
  159. }else if(this.fired == 0 &&
  160. (this.results[0] instanceof doh.Deferred)){
  161. this.results[0].cancel();
  162. }
  163. },
  164. _pause: function(){
  165. this.paused++;
  166. },
  167. _unpause: function(){
  168. this.paused--;
  169. if ((this.paused == 0) && (this.fired >= 0)) {
  170. this._fire();
  171. }
  172. },
  173. _continue: function(res){
  174. this._resback(res);
  175. this._unpause();
  176. },
  177. _resback: function(res){
  178. this.fired = ((res instanceof Error) ? 1 : 0);
  179. this.results[this.fired] = res;
  180. this._fire();
  181. },
  182. _check: function(){
  183. if(this.fired != -1){
  184. if(!this.silentlyCancelled){
  185. throw new Error("already called!");
  186. }
  187. this.silentlyCancelled = false;
  188. return;
  189. }
  190. },
  191. callback: function(res){
  192. this._check();
  193. this._resback(res);
  194. },
  195. errback: function(res){
  196. this._check();
  197. if(!(res instanceof Error)){
  198. res = new Error(res);
  199. }
  200. this._resback(res);
  201. },
  202. addBoth: function(cb, cbfn){
  203. var enclosed = this.getFunctionFromArgs(cb, cbfn);
  204. if(arguments.length > 2){
  205. enclosed = doh.hitch(null, enclosed, arguments, 2);
  206. }
  207. return this.addCallbacks(enclosed, enclosed);
  208. },
  209. addCallback: function(cb, cbfn){
  210. var enclosed = this.getFunctionFromArgs(cb, cbfn);
  211. if(arguments.length > 2){
  212. enclosed = doh.hitch(null, enclosed, arguments, 2);
  213. }
  214. return this.addCallbacks(enclosed, null);
  215. },
  216. addErrback: function(cb, cbfn){
  217. var enclosed = this.getFunctionFromArgs(cb, cbfn);
  218. if(arguments.length > 2){
  219. enclosed = doh.hitch(null, enclosed, arguments, 2);
  220. }
  221. return this.addCallbacks(null, enclosed);
  222. },
  223. addCallbacks: function(cb, eb){
  224. this.chain.push([cb, eb]);
  225. if(this.fired >= 0){
  226. this._fire();
  227. }
  228. return this;
  229. },
  230. _fire: function(){
  231. var chain = this.chain;
  232. var fired = this.fired;
  233. var res = this.results[fired];
  234. var self = this;
  235. var cb = null;
  236. while(chain.length > 0 && this.paused == 0){
  237. // Array
  238. var pair = chain.shift();
  239. var f = pair[fired];
  240. if(f == null){
  241. continue;
  242. }
  243. try {
  244. res = f(res);
  245. fired = ((res instanceof Error) ? 1 : 0);
  246. if(res instanceof doh.Deferred){
  247. cb = function(res){
  248. self._continue(res);
  249. };
  250. this._pause();
  251. }
  252. }catch(err){
  253. fired = 1;
  254. res = err;
  255. }
  256. }
  257. this.fired = fired;
  258. this.results[fired] = res;
  259. if((cb)&&(this.paused)){
  260. res.addBoth(cb);
  261. }
  262. }
  263. });
  264. //
  265. // State Keeping and Reporting
  266. //
  267. doh._testCount = 0;
  268. doh._groupCount = 0;
  269. doh._errorCount = 0;
  270. doh._failureCount = 0;
  271. doh._currentGroup = null;
  272. doh._currentTest = null;
  273. doh._paused = true;
  274. doh._init = function(){
  275. this._currentGroup = null;
  276. this._currentTest = null;
  277. this._errorCount = 0;
  278. this._failureCount = 0;
  279. this.debug(this._testCount, "tests to run in", this._groupCount, "groups");
  280. }
  281. // doh._urls = [];
  282. doh._groups = {};
  283. //
  284. // Test Registration
  285. //
  286. doh.registerTestNs = function(/*String*/ group, /*Object*/ ns){
  287. // summary:
  288. // adds the passed namespace object to the list of objects to be
  289. // searched for test groups. Only "public" functions (not prefixed
  290. // with "_") will be added as tests to be run. If you'd like to use
  291. // fixtures (setUp(), tearDown(), and runTest()), please use
  292. // registerTest() or registerTests().
  293. for(var x in ns){
  294. if( (x.charAt(0) != "_") &&
  295. (typeof ns[x] == "function") ){
  296. this.registerTest(group, ns[x]);
  297. }
  298. }
  299. }
  300. doh._testRegistered = function(group, fixture){
  301. // slot to be filled in
  302. }
  303. doh._groupStarted = function(group){
  304. // slot to be filled in
  305. }
  306. doh._groupFinished = function(group, success){
  307. // slot to be filled in
  308. }
  309. doh._testStarted = function(group, fixture){
  310. // slot to be filled in
  311. }
  312. doh._testFinished = function(group, fixture, success){
  313. // slot to be filled in
  314. }
  315. doh.registerGroup = function( /*String*/ group,
  316. /*Array||Function||Object*/ tests,
  317. /*Function*/ setUp,
  318. /*Function*/ tearDown){
  319. // summary:
  320. // registers an entire group of tests at once and provides a setUp and
  321. // tearDown facility for groups. If you call this method with only
  322. // setUp and tearDown parameters, they will replace previously
  323. // installed setUp or tearDown functions for the group with the new
  324. // methods.
  325. // group:
  326. // string name of the group
  327. // tests:
  328. // either a function or an object or an array of functions/objects. If
  329. // an object, it must contain at *least* a "runTest" method, and may
  330. // also contain "setUp" and "tearDown" methods. These will be invoked
  331. // on either side of the "runTest" method (respectively) when the test
  332. // is run. If an array, it must contain objects matching the above
  333. // description or test functions.
  334. // setUp: a function for initializing the test group
  335. // tearDown: a function for initializing the test group
  336. if(tests){
  337. this.register(group, tests);
  338. }
  339. if(setUp){
  340. this._groups[group].setUp = setUp;
  341. }
  342. if(tearDown){
  343. this._groups[group].tearDown = tearDown;
  344. }
  345. }
  346. doh._getTestObj = function(group, test){
  347. var tObj = test;
  348. if(typeof test == "string"){
  349. if(test.substr(0, 4)=="url:"){
  350. return this.registerUrl(group, test);
  351. }else{
  352. tObj = {
  353. name: test.replace("/\s/g", "_") // FIXME: bad escapement
  354. };
  355. tObj.runTest = new Function("t", test);
  356. }
  357. }else if(typeof test == "function"){
  358. // if we didn't get a fixture, wrap the function
  359. tObj = { "runTest": test };
  360. if(test["name"]){
  361. tObj.name = test.name;
  362. }else{
  363. try{
  364. var fStr = "function ";
  365. var ts = tObj.runTest+"";
  366. if(0 <= ts.indexOf(fStr)){
  367. tObj.name = ts.split(fStr)[1].split("(", 1)[0];
  368. }
  369. // doh.debug(tObj.runTest.toSource());
  370. }catch(e){
  371. }
  372. }
  373. // FIXME: try harder to get the test name here
  374. }
  375. return tObj;
  376. }
  377. doh.registerTest = function(/*String*/ group, /*Function||Object*/ test){
  378. // summary:
  379. // add the provided test function or fixture object to the specified
  380. // test group.
  381. // group:
  382. // string name of the group to add the test to
  383. // test:
  384. // either a function or an object. If an object, it must contain at
  385. // *least* a "runTest" method, and may also contain "setUp" and
  386. // "tearDown" methods. These will be invoked on either side of the
  387. // "runTest" method (respectively) when the test is run.
  388. if(!this._groups[group]){
  389. this._groupCount++;
  390. this._groups[group] = [];
  391. this._groups[group].inFlight = 0;
  392. }
  393. var tObj = this._getTestObj(group, test);
  394. if(!tObj){ return null; }
  395. this._groups[group].push(tObj);
  396. this._testCount++;
  397. this._testRegistered(group, tObj);
  398. return tObj;
  399. }
  400. doh.registerTests = function(/*String*/ group, /*Array*/ testArr){
  401. // summary:
  402. // registers a group of tests, treating each element of testArr as
  403. // though it were being (along with group) passed to the registerTest
  404. // method.
  405. for(var x=0; x<testArr.length; x++){
  406. this.registerTest(group, testArr[x]);
  407. }
  408. }
  409. // FIXME: move implementation to _browserRunner?
  410. doh.registerUrl = function( /*String*/ group,
  411. /*String*/ url,
  412. /*Integer*/ timeout){
  413. this.debug("ERROR:");
  414. this.debug("\tNO registerUrl() METHOD AVAILABLE.");
  415. // this._urls.push(url);
  416. }
  417. doh.registerString = function(group, str){
  418. }
  419. // FIXME: remove the doh.add alias SRTL.
  420. doh.register = doh.add = function(groupOrNs, testOrNull){
  421. // summary:
  422. // "magical" variant of registerTests, registerTest, and
  423. // registerTestNs. Will accept the calling arguments of any of these
  424. // methods and will correctly guess the right one to register with.
  425. if( (arguments.length == 1)&&
  426. (typeof groupOrNs == "string") ){
  427. if(groupOrNs.substr(0, 4)=="url:"){
  428. this.registerUrl(groupOrNs);
  429. }else{
  430. this.registerTest("ungrouped", groupOrNs);
  431. }
  432. }
  433. if(arguments.length == 1){
  434. this.debug("invalid args passed to doh.register():", groupOrNs, ",", testOrNull);
  435. return;
  436. }
  437. if(typeof testOrNull == "string"){
  438. if(testOrNull.substr(0, 4)=="url:"){
  439. this.registerUrl(testOrNull);
  440. }else{
  441. this.registerTest(groupOrNs, testOrNull);
  442. }
  443. // this.registerTestNs(groupOrNs, testOrNull);
  444. return;
  445. }
  446. if(doh._isArray(testOrNull)){
  447. this.registerTests(groupOrNs, testOrNull);
  448. return;
  449. }
  450. this.registerTest(groupOrNs, testOrNull);
  451. };
  452. doh.registerDocTests = function(module){
  453. // no-op for when Dojo isn't loaded into the page
  454. this.debug("registerDocTests() requires dojo to be loaded into the environment. Skipping doctest set for module:", module);
  455. };
  456. (function(){
  457. if(typeof dojo != "undefined"){
  458. try{
  459. dojo.require("dojox.testing.DocTest");
  460. }catch(e){
  461. // if the DocTest module isn't available (e.g., the build we're
  462. // running from doesn't include it), stub it out and log the error
  463. console.debug(e);
  464. doh.registerDocTests = function(){}
  465. return;
  466. }
  467. doh.registerDocTests = function(module){
  468. // summary:
  469. // Get all the doctests from the given module and register each of them
  470. // as a single test case here.
  471. //
  472. var docTest = new dojox.testing.DocTest();
  473. var docTests = docTest.getTests(module);
  474. var len = docTests.length;
  475. var tests = [];
  476. for (var i=0; i<len; i++){
  477. var test = docTests[i];
  478. // Extract comment on first line and add to test name.
  479. var comment = "";
  480. if (test.commands.length && test.commands[0].indexOf("//")!=-1) {
  481. var parts = test.commands[0].split("//");
  482. comment = ", "+parts[parts.length-1]; // Get all after the last //, so we dont get trapped by http:// or alikes :-).
  483. }
  484. tests.push({
  485. runTest: (function(test){
  486. return function(t){
  487. var r = docTest.runTest(test.commands, test.expectedResult);
  488. t.assertTrue(r.success);
  489. }
  490. })(test),
  491. name:"Line "+test.line+comment
  492. }
  493. );
  494. }
  495. this.register("DocTests: "+module, tests);
  496. }
  497. }
  498. })();
  499. //
  500. // Assertions and In-Test Utilities
  501. //
  502. doh.t = doh.assertTrue = function(/*Object*/ condition, /*String?*/ hint){
  503. // summary:
  504. // is the passed item "truthy"?
  505. if(arguments.length < 1){
  506. throw new doh._AssertFailure("assertTrue failed because it was not passed at least 1 argument");
  507. }
  508. if(!eval(condition)){
  509. throw new doh._AssertFailure("assertTrue('" + condition + "') failed", hint);
  510. }
  511. }
  512. doh.f = doh.assertFalse = function(/*Object*/ condition, /*String?*/ hint){
  513. // summary:
  514. // is the passed item "falsey"?
  515. if(arguments.length < 1){
  516. throw new doh._AssertFailure("assertFalse failed because it was not passed at least 1 argument");
  517. }
  518. if(eval(condition)){
  519. throw new doh._AssertFailure("assertFalse('" + condition + "') failed", hint);
  520. }
  521. }
  522. doh.e = doh.assertError = function(/*Error object*/expectedError, /*Object*/scope, /*String*/functionName, /*Array*/args, /*String?*/ hint){
  523. // summary:
  524. // Test for a certain error to be thrown by the given function.
  525. // example:
  526. // t.assertError(dojox.data.QueryReadStore.InvalidAttributeError, store, "getValue", [item, "NOT THERE"]);
  527. // t.assertError(dojox.data.QueryReadStore.InvalidItemError, store, "getValue", ["not an item", "NOT THERE"]);
  528. try{
  529. scope[functionName].apply(scope, args);
  530. }catch (e){
  531. if(e instanceof expectedError){
  532. return true;
  533. }else{
  534. throw new doh._AssertFailure("assertError() failed:\n\texpected error\n\t\t"+expectedError+"\n\tbut got\n\t\t"+e+"\n\n", hint);
  535. }
  536. }
  537. throw new doh._AssertFailure("assertError() failed:\n\texpected error\n\t\t"+expectedError+"\n\tbut no error caught\n\n", hint);
  538. }
  539. doh.is = doh.assertEqual = function(/*Object*/ expected, /*Object*/ actual, /*String?*/ hint){
  540. // summary:
  541. // are the passed expected and actual objects/values deeply
  542. // equivalent?
  543. // Compare undefined always with three equal signs, because undefined==null
  544. // is true, but undefined===null is false.
  545. if((expected === undefined)&&(actual === undefined)){
  546. return true;
  547. }
  548. if(arguments.length < 2){
  549. throw doh._AssertFailure("assertEqual failed because it was not passed 2 arguments");
  550. }
  551. if((expected === actual)||(expected == actual)){
  552. return true;
  553. }
  554. if( (this._isArray(expected) && this._isArray(actual))&&
  555. (this._arrayEq(expected, actual)) ){
  556. return true;
  557. }
  558. if( ((typeof expected == "object")&&((typeof actual == "object")))&&
  559. (this._objPropEq(expected, actual)) ){
  560. return true;
  561. }
  562. throw new doh._AssertFailure("assertEqual() failed:\n\texpected\n\t\t"+expected+"\n\tbut got\n\t\t"+actual+"\n\n", hint);
  563. }
  564. doh.isNot = doh.assertNotEqual = function(/*Object*/ notExpected, /*Object*/ actual, /*String?*/ hint){
  565. // summary:
  566. // are the passed notexpected and actual objects/values deeply
  567. // not equivalent?
  568. // Compare undefined always with three equal signs, because undefined==null
  569. // is true, but undefined===null is false.
  570. if((notExpected === undefined)&&(actual === undefined)){
  571. throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
  572. }
  573. if(arguments.length < 2){
  574. throw doh._AssertFailure("assertEqual failed because it was not passed 2 arguments");
  575. }
  576. if((notExpected === actual)||(notExpected == actual)){
  577. throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
  578. }
  579. if( (this._isArray(notExpected) && this._isArray(actual))&&
  580. (this._arrayEq(notExpected, actual)) ){
  581. throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
  582. }
  583. if( ((typeof notExpected == "object")&&((typeof actual == "object")))&&
  584. (this._objPropEq(notExpected, actual)) ){
  585. throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
  586. }
  587. return true;
  588. }
  589. doh._arrayEq = function(expected, actual){
  590. if(expected.length != actual.length){ return false; }
  591. // FIXME: we're not handling circular refs. Do we care?
  592. for(var x=0; x<expected.length; x++){
  593. if(!doh.assertEqual(expected[x], actual[x])){ return false; }
  594. }
  595. return true;
  596. }
  597. doh._objPropEq = function(expected, actual){
  598. if(expected instanceof Date){
  599. return actual instanceof Date && expected.getTime()==actual.getTime();
  600. }
  601. var x;
  602. // Make sure ALL THE SAME properties are in both objects!
  603. for(x in actual){ // Lets check "actual" here, expected is checked below.
  604. if(expected[x] === undefined){
  605. return false;
  606. }
  607. };
  608. for(x in expected){
  609. if(!doh.assertEqual(expected[x], actual[x])){
  610. return false;
  611. }
  612. }
  613. return true;
  614. }
  615. doh._isArray = function(it){
  616. return (it && it instanceof Array || typeof it == "array" ||
  617. (
  618. !!doh.global["dojo"] &&
  619. doh.global["dojo"]["NodeList"] !== undefined &&
  620. it instanceof doh.global["dojo"]["NodeList"]
  621. )
  622. );
  623. }
  624. //
  625. // Runner-Wrapper
  626. //
  627. doh._setupGroupForRun = function(/*String*/ groupName, /*Integer*/ idx){
  628. var tg = this._groups[groupName];
  629. this.debug(this._line);
  630. this.debug("GROUP", "\""+groupName+"\"", "has", tg.length, "test"+((tg.length > 1) ? "s" : "")+" to run");
  631. }
  632. doh._handleFailure = function(groupName, fixture, e){
  633. // this.debug("FAILED test:", fixture.name);
  634. // mostly borrowed from JUM
  635. this._groups[groupName].failures++;
  636. var out = "";
  637. if(e instanceof this._AssertFailure){
  638. this._failureCount++;
  639. if(e["fileName"]){ out += e.fileName + ':'; }
  640. if(e["lineNumber"]){ out += e.lineNumber + ' '; }
  641. out += e+": "+e.message;
  642. this.debug("\t_AssertFailure:", out);
  643. }else{
  644. this._errorCount++;
  645. }
  646. this.debug(e);
  647. if(fixture.runTest["toSource"]){
  648. var ss = fixture.runTest.toSource();
  649. this.debug("\tERROR IN:\n\t\t", ss);
  650. }else{
  651. this.debug("\tERROR IN:\n\t\t", fixture.runTest);
  652. }
  653. if(e.rhinoException){
  654. e.rhinoException.printStackTrace();
  655. }else if(e.javaException){
  656. e.javaException.printStackTrace();
  657. }
  658. }
  659. try{
  660. setTimeout(function(){}, 0);
  661. }catch(e){
  662. setTimeout = function(func){
  663. return func();
  664. }
  665. }
  666. doh._runFixture = function(groupName, fixture){
  667. var tg = this._groups[groupName];
  668. this._testStarted(groupName, fixture);
  669. var threw = false;
  670. var err = null;
  671. // run it, catching exceptions and reporting them
  672. try{
  673. // let doh reference "this.group.thinger..." which can be set by
  674. // another test or group-level setUp function
  675. fixture.group = tg;
  676. // only execute the parts of the fixture we've got
  677. if(fixture["setUp"]){ fixture.setUp(this); }
  678. if(fixture["runTest"]){ // should we error out of a fixture doesn't have a runTest?
  679. fixture.startTime = new Date();
  680. var ret = fixture.runTest(this);
  681. fixture.endTime = new Date();
  682. // if we get a deferred back from the test runner, we know we're
  683. // gonna wait for an async result. It's up to the test code to trap
  684. // errors and give us an errback or callback.
  685. if(ret instanceof doh.Deferred){
  686. tg.inFlight++;
  687. ret.groupName = groupName;
  688. ret.fixture = fixture;
  689. ret.addErrback(function(err){
  690. doh._handleFailure(groupName, fixture, err);
  691. });
  692. var retEnd = function(){
  693. if(fixture["tearDown"]){ fixture.tearDown(doh); }
  694. tg.inFlight--;
  695. if((!tg.inFlight)&&(tg.iterated)){
  696. doh._groupFinished(groupName, (!tg.failures));
  697. }
  698. doh._testFinished(groupName, fixture, ret.results[0]);
  699. if(doh._paused){
  700. doh.run();
  701. }
  702. }
  703. var timer = setTimeout(function(){
  704. // ret.cancel();
  705. // retEnd();
  706. ret.errback(new Error("test timeout in "+fixture.name.toString()));
  707. }, fixture["timeout"]||1000);
  708. ret.addBoth(function(arg){
  709. clearTimeout(timer);
  710. retEnd();
  711. });
  712. if(ret.fired < 0){
  713. doh.pause();
  714. }
  715. return ret;
  716. }
  717. }
  718. if(fixture["tearDown"]){ fixture.tearDown(this); }
  719. }catch(e){
  720. threw = true;
  721. err = e;
  722. if(!fixture.endTime){
  723. fixture.endTime = new Date();
  724. }
  725. }
  726. var d = new doh.Deferred();
  727. setTimeout(this.hitch(this, function(){
  728. if(threw){
  729. this._handleFailure(groupName, fixture, err);
  730. }
  731. this._testFinished(groupName, fixture, (!threw));
  732. if((!tg.inFlight)&&(tg.iterated)){
  733. doh._groupFinished(groupName, (!tg.failures));
  734. }else if(tg.inFlight > 0){
  735. setTimeout(this.hitch(this, function(){
  736. doh.runGroup(groupName); // , idx);
  737. }), 100);
  738. this._paused = true;
  739. }
  740. if(doh._paused){
  741. doh.run();
  742. }
  743. }), 30);
  744. doh.pause();
  745. return d;
  746. }
  747. doh._testId = 0;
  748. doh.runGroup = function(/*String*/ groupName, /*Integer*/ idx){
  749. // summary:
  750. // runs the specified test group
  751. // the general structure of the algorithm is to run through the group's
  752. // list of doh, checking before and after each of them to see if we're in
  753. // a paused state. This can be caused by the test returning a deferred or
  754. // the user hitting the pause button. In either case, we want to halt
  755. // execution of the test until something external to us restarts it. This
  756. // means we need to pickle off enough state to pick up where we left off.
  757. // FIXME: need to make fixture execution async!!
  758. var tg = this._groups[groupName];
  759. if(tg.skip === true){ return; }
  760. if(this._isArray(tg)){
  761. if(idx<=tg.length){
  762. if((!tg.inFlight)&&(tg.iterated == true)){
  763. if(tg["tearDown"]){ tg.tearDown(this); }
  764. doh._groupFinished(groupName, (!tg.failures));
  765. return;
  766. }
  767. }
  768. if(!idx){
  769. tg.inFlight = 0;
  770. tg.iterated = false;
  771. tg.failures = 0;
  772. }
  773. doh._groupStarted(groupName);
  774. if(!idx){
  775. this._setupGroupForRun(groupName, idx);
  776. if(tg["setUp"]){ tg.setUp(this); }
  777. }
  778. for(var y=(idx||0); y<tg.length; y++){
  779. if(this._paused){
  780. this._currentTest = y;
  781. // this.debug("PAUSED at:", tg[y].name, this._currentGroup, this._currentTest);
  782. return;
  783. }
  784. doh._runFixture(groupName, tg[y]);
  785. if(this._paused){
  786. this._currentTest = y+1;
  787. if(this._currentTest == tg.length){
  788. tg.iterated = true;
  789. }
  790. // this.debug("PAUSED at:", tg[y].name, this._currentGroup, this._currentTest);
  791. return;
  792. }
  793. }
  794. tg.iterated = true;
  795. if(!tg.inFlight){
  796. if(tg["tearDown"]){ tg.tearDown(this); }
  797. doh._groupFinished(groupName, (!tg.failures));
  798. }
  799. }
  800. }
  801. doh._onEnd = function(){}
  802. doh._report = function(){
  803. // summary:
  804. // a private method to be implemented/replaced by the "locally
  805. // appropriate" test runner
  806. // this.debug("ERROR:");
  807. // this.debug("\tNO REPORTING OUTPUT AVAILABLE.");
  808. // this.debug("\tIMPLEMENT doh._report() IN YOUR TEST RUNNER");
  809. this.debug(this._line);
  810. this.debug("| TEST SUMMARY:");
  811. this.debug(this._line);
  812. this.debug("\t", this._testCount, "tests in", this._groupCount, "groups");
  813. this.debug("\t", this._errorCount, "errors");
  814. this.debug("\t", this._failureCount, "failures");
  815. }
  816. doh.togglePaused = function(){
  817. this[(this._paused) ? "run" : "pause"]();
  818. }
  819. doh.pause = function(){
  820. // summary:
  821. // halt test run. Can be resumed.
  822. this._paused = true;
  823. }
  824. doh.run = function(){
  825. // summary:
  826. // begins or resumes the test process.
  827. // this.debug("STARTING");
  828. this._paused = false;
  829. var cg = this._currentGroup;
  830. var ct = this._currentTest;
  831. var found = false;
  832. if(!cg){
  833. this._init(); // we weren't paused
  834. found = true;
  835. }
  836. this._currentGroup = null;
  837. this._currentTest = null;
  838. for(var x in this._groups){
  839. if(
  840. ( (!found)&&(x == cg) )||( found )
  841. ){
  842. if(this._paused){ return; }
  843. this._currentGroup = x;
  844. if(!found){
  845. found = true;
  846. this.runGroup(x, ct);
  847. }else{
  848. this.runGroup(x);
  849. }
  850. if(this._paused){ return; }
  851. }
  852. }
  853. this._currentGroup = null;
  854. this._currentTest = null;
  855. this._paused = false;
  856. this._onEnd();
  857. this._report();
  858. }
  859. tests = doh;
  860. (function(){
  861. // scope protection
  862. var x;
  863. try{
  864. if(typeof dojo != "undefined"){
  865. dojo.platformRequire({
  866. browser: ["doh._browserRunner"],
  867. rhino: ["doh._rhinoRunner"],
  868. spidermonkey: ["doh._rhinoRunner"]
  869. });
  870. var _shouldRequire = dojo.isBrowser ? (dojo.global == dojo.global["parent"]) : true;
  871. if(_shouldRequire){
  872. if(dojo.isBrowser){
  873. dojo.addOnLoad(function(){
  874. if (dojo.global.registerModulePath){
  875. dojo.forEach(dojo.global.registerModulePath, function(m){
  876. dojo.registerModulePath(m[0], m[1]);
  877. });
  878. }
  879. if(dojo.byId("testList")){
  880. var _tm = ( (dojo.global.testModule && dojo.global.testModule.length) ? dojo.global.testModule : "dojo.tests.module");
  881. dojo.forEach(_tm.split(","), dojo.require, dojo);
  882. setTimeout(function(){
  883. doh.run();
  884. }, 500);
  885. }
  886. });
  887. }else{
  888. // dojo.require("doh._base");
  889. }
  890. }
  891. }else{
  892. if(typeof load == "function" &&
  893. (typeof Packages == "function" || typeof Packages == "object")){
  894. throw new Error();
  895. }else if(typeof load == "function"){
  896. throw new Error();
  897. }
  898. if(this["document"]){
  899. // if we survived all of that, we're probably in a browser but
  900. // don't have Dojo handy. Load _browserRunner.js using a
  901. // document.write() call.
  902. // find runner.js, load _browserRunner relative to it
  903. var scripts = document.getElementsByTagName("script");
  904. for(x=0; x<scripts.length; x++){
  905. var s = scripts[x].src;
  906. if(s && (s.substr(s.length - 9) == "runner.js")){
  907. document.write("<scri"+"pt src='" + s.substr(0, s.length - 9)
  908. + "_browserRunner.js' type='text/javascript'></scr"+"ipt>");
  909. }
  910. }
  911. }
  912. }
  913. }catch(e){
  914. print("\n"+doh._line);
  915. print("The Dojo Unit Test Harness, $Rev$");
  916. print("Copyright (c) 2007, The Dojo Foundation, All Rights Reserved");
  917. print(doh._line, "\n");
  918. load("_rhinoRunner.js");
  919. try{
  920. var dojoUrl = "../../dojo/dojo.js";
  921. var testUrl = "";
  922. var testModule = "dojo.tests.module";
  923. for(x=0; x<arguments.length; x++){
  924. if(arguments[x].indexOf("=") > 0){
  925. var tp = arguments[x].split("=");
  926. if(tp[0] == "dojoUrl"){
  927. dojoUrl = tp[1];
  928. }
  929. if(tp[0] == "testUrl"){
  930. testUrl = tp[1];
  931. }
  932. if(tp[0] == "testModule"){
  933. testModule = tp[1];
  934. }
  935. }
  936. }
  937. if(dojoUrl.length){
  938. if(!this["djConfig"]){
  939. djConfig = {};
  940. }
  941. djConfig.baseUrl = dojoUrl.split("dojo.js")[0];
  942. load(dojoUrl);
  943. }
  944. if(testUrl.length){
  945. load(testUrl);
  946. }
  947. if(testModule.length){
  948. dojo.forEach(testModule.split(","), dojo.require, dojo);
  949. }
  950. }catch(e){
  951. print("An exception occurred: " + e);
  952. }
  953. doh.run();
  954. }
  955. }).apply(this, typeof arguments != "undefined" ? arguments : [null]);