runner.js 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499
  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(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, hint);
  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. getTestErrback: function(cb, scope){
  116. // summary: Replaces outer getTextCallback's in nested situations to avoid multiple callback(true)'s
  117. var _this = this;
  118. return function(){
  119. try{
  120. cb.apply(scope||doh.global||_this, arguments);
  121. }catch(e){
  122. _this.errback(e);
  123. }
  124. };
  125. },
  126. getTestCallback: function(cb, scope){
  127. var _this = this;
  128. return function(){
  129. try{
  130. cb.apply(scope||doh.global||_this, arguments);
  131. }catch(e){
  132. _this.errback(e);
  133. return;
  134. }
  135. _this.callback(true);
  136. };
  137. },
  138. getFunctionFromArgs: function(){
  139. var a = arguments;
  140. if((a[0])&&(!a[1])){
  141. if(typeof a[0] == "function"){
  142. return a[0];
  143. }else if(typeof a[0] == "string"){
  144. return doh.global[a[0]];
  145. }
  146. }else if((a[0])&&(a[1])){
  147. return doh.hitch(a[0], a[1]);
  148. }
  149. return null;
  150. },
  151. makeCalled: function() {
  152. var deferred = new doh.Deferred();
  153. deferred.callback();
  154. return deferred;
  155. },
  156. _nextId: (function(){
  157. var n = 1;
  158. return function(){ return n++; };
  159. })(),
  160. cancel: function(){
  161. if(this.fired == -1){
  162. if (this.canceller){
  163. this.canceller(this);
  164. }else{
  165. this.silentlyCancelled = true;
  166. }
  167. if(this.fired == -1){
  168. this.errback(new Error("Deferred(unfired)"));
  169. }
  170. }else if(this.fired == 0 &&
  171. (this.results[0] instanceof doh.Deferred)){
  172. this.results[0].cancel();
  173. }
  174. },
  175. _pause: function(){
  176. this.paused++;
  177. },
  178. _unpause: function(){
  179. this.paused--;
  180. if ((this.paused == 0) && (this.fired >= 0)) {
  181. this._fire();
  182. }
  183. },
  184. _continue: function(res){
  185. this._resback(res);
  186. this._unpause();
  187. },
  188. _resback: function(res){
  189. this.fired = ((res instanceof Error) ? 1 : 0);
  190. this.results[this.fired] = res;
  191. this._fire();
  192. },
  193. _check: function(){
  194. if(this.fired != -1){
  195. if(!this.silentlyCancelled){
  196. throw new Error("already called!");
  197. }
  198. this.silentlyCancelled = false;
  199. return;
  200. }
  201. },
  202. callback: function(res){
  203. this._check();
  204. this._resback(res);
  205. },
  206. errback: function(res){
  207. this._check();
  208. if(!(res instanceof Error)){
  209. res = new Error(res);
  210. }
  211. this._resback(res);
  212. },
  213. addBoth: function(cb, cbfn){
  214. var enclosed = this.getFunctionFromArgs(cb, cbfn);
  215. if(arguments.length > 2){
  216. enclosed = doh.hitch(null, enclosed, arguments, 2);
  217. }
  218. return this.addCallbacks(enclosed, enclosed);
  219. },
  220. addCallback: function(cb, cbfn){
  221. var enclosed = this.getFunctionFromArgs(cb, cbfn);
  222. if(arguments.length > 2){
  223. enclosed = doh.hitch(null, enclosed, arguments, 2);
  224. }
  225. return this.addCallbacks(enclosed, null);
  226. },
  227. addErrback: function(cb, cbfn){
  228. var enclosed = this.getFunctionFromArgs(cb, cbfn);
  229. if(arguments.length > 2){
  230. enclosed = doh.hitch(null, enclosed, arguments, 2);
  231. }
  232. return this.addCallbacks(null, enclosed);
  233. },
  234. addCallbacks: function(cb, eb){
  235. this.chain.push([cb, eb]);
  236. if(this.fired >= 0){
  237. this._fire();
  238. }
  239. return this;
  240. },
  241. _fire: function(){
  242. var chain = this.chain;
  243. var fired = this.fired;
  244. var res = this.results[fired];
  245. var self = this;
  246. var cb = null;
  247. while(chain.length > 0 && this.paused == 0){
  248. // Array
  249. var pair = chain.shift();
  250. var f = pair[fired];
  251. if(f == null){
  252. continue;
  253. }
  254. try {
  255. res = f(res);
  256. fired = ((res instanceof Error) ? 1 : 0);
  257. if(res instanceof doh.Deferred){
  258. cb = function(res){
  259. self._continue(res);
  260. };
  261. this._pause();
  262. }
  263. }catch(err){
  264. fired = 1;
  265. res = err;
  266. }
  267. }
  268. this.fired = fired;
  269. this.results[fired] = res;
  270. if((cb)&&(this.paused)){
  271. res.addBoth(cb);
  272. }
  273. }
  274. });
  275. //
  276. // State Keeping and Reporting
  277. //
  278. doh._testCount = 0;
  279. doh._groupCount = 0;
  280. doh._errorCount = 0;
  281. doh._failureCount = 0;
  282. doh._currentGroup = null;
  283. doh._currentTest = null;
  284. doh._paused = true;
  285. doh._init = function(){
  286. this._currentGroup = null;
  287. this._currentTest = null;
  288. this._errorCount = 0;
  289. this._failureCount = 0;
  290. this.debug(this._testCount, "tests to run in", this._groupCount, "groups");
  291. }
  292. // doh._urls = [];
  293. doh._groups = {};
  294. //
  295. // Test Registration
  296. //
  297. doh.registerTestNs = function(/*String*/ group, /*Object*/ ns){
  298. // summary:
  299. // adds the passed namespace object to the list of objects to be
  300. // searched for test groups. Only "public" functions (not prefixed
  301. // with "_") will be added as tests to be run. If you'd like to use
  302. // fixtures (setUp(), tearDown(), and runTest()), please use
  303. // registerTest() or registerTests().
  304. for(var x in ns){
  305. if( (x.charAt(0) != "_") &&
  306. (typeof ns[x] == "function") ){
  307. this.registerTest(group, ns[x]);
  308. }
  309. }
  310. }
  311. doh._testRegistered = function(group, fixture){
  312. // slot to be filled in
  313. }
  314. doh._groupStarted = function(group){
  315. // slot to be filled in
  316. }
  317. doh._groupFinished = function(group, success){
  318. // slot to be filled in
  319. }
  320. doh._testStarted = function(group, fixture){
  321. // slot to be filled in
  322. }
  323. doh._testFinished = function(group, fixture, success){
  324. // slot to be filled in
  325. }
  326. doh.registerGroup = function( /*String*/ group,
  327. /*Array||Function||Object*/ tests,
  328. /*Function*/ setUp,
  329. /*Function*/ tearDown,
  330. /*String*/ type){
  331. // summary:
  332. // registers an entire group of tests at once and provides a setUp and
  333. // tearDown facility for groups. If you call this method with only
  334. // setUp and tearDown parameters, they will replace previously
  335. // installed setUp or tearDown functions for the group with the new
  336. // methods.
  337. // group:
  338. // string name of the group
  339. // tests:
  340. // either a function or an object or an array of functions/objects. If
  341. // an object, it must contain at *least* a "runTest" method, and may
  342. // also contain "setUp" and "tearDown" methods. These will be invoked
  343. // on either side of the "runTest" method (respectively) when the test
  344. // is run. If an array, it must contain objects matching the above
  345. // description or test functions.
  346. // setUp: a function for initializing the test group
  347. // tearDown: a function for initializing the test group
  348. // type: The type of tests these are, such as a group of performance tests
  349. // null/undefied are standard DOH tests, the valye 'perf' enables
  350. // registering them as performance tests.
  351. if(tests){
  352. this.register(group, tests, type);
  353. }
  354. if(setUp){
  355. this._groups[group].setUp = setUp;
  356. }
  357. if(tearDown){
  358. this._groups[group].tearDown = tearDown;
  359. }
  360. }
  361. doh._getTestObj = function(group, test, type){
  362. var tObj = test;
  363. if(typeof test == "string"){
  364. if(test.substr(0, 4)=="url:"){
  365. return this.registerUrl(group, test);
  366. }else{
  367. tObj = {
  368. name: test.replace("/\s/g", "_") // FIXME: bad escapement
  369. };
  370. tObj.runTest = new Function("t", test);
  371. }
  372. }else if(typeof test == "function"){
  373. // if we didn't get a fixture, wrap the function
  374. tObj = { "runTest": test };
  375. if(test["name"]){
  376. tObj.name = test.name;
  377. }else{
  378. try{
  379. var fStr = "function ";
  380. var ts = tObj.runTest+"";
  381. if(0 <= ts.indexOf(fStr)){
  382. tObj.name = ts.split(fStr)[1].split("(", 1)[0];
  383. }
  384. // doh.debug(tObj.runTest.toSource());
  385. }catch(e){
  386. }
  387. }
  388. // FIXME: try harder to get the test name here
  389. }
  390. //Augment the test with some specific options to make it identifiable as a
  391. //particular type of test so it can be executed properly.
  392. if(type === "perf" || tObj.testType === "perf"){
  393. tObj.testType = "perf";
  394. //Build an object on the root DOH class to contain all the test results.
  395. //Cache it on the test object for quick lookup later for results storage.
  396. if(!doh.perfTestResults){
  397. doh.perfTestResults = {};
  398. doh.perfTestResults[group] = {};
  399. }
  400. if(!doh.perfTestResults[group]){
  401. doh.perfTestResults[group] = {};
  402. }
  403. if(!doh.perfTestResults[group][tObj.name]){
  404. doh.perfTestResults[group][tObj.name] = {};
  405. }
  406. tObj.results = doh.perfTestResults[group][tObj.name];
  407. //If it's not set, then set the trial duration
  408. //default to 100ms.
  409. if(!("trialDuration" in tObj)){
  410. tObj.trialDuration = 100;
  411. }
  412. //If it's not set, then set the delay between trial runs to 100ms
  413. //default to 100ms to allow for GC and to make IE happy.
  414. if(!("trialDelay" in tObj)){
  415. tObj.trialDelay = 100;
  416. }
  417. //If it's not set, then set number of times a trial is run to 10.
  418. if(!("trialIterations" in tObj)){
  419. tObj.trialIterations = 10;
  420. }
  421. }
  422. return tObj;
  423. }
  424. doh.registerTest = function(/*String*/ group, /*Function||Object*/ test, /*String*/ type){
  425. // summary:
  426. // add the provided test function or fixture object to the specified
  427. // test group.
  428. // group:
  429. // string name of the group to add the test to
  430. // test:
  431. // either a function or an object. If an object, it must contain at
  432. // *least* a "runTest" method, and may also contain "setUp" and
  433. // "tearDown" methods. These will be invoked on either side of the
  434. // "runTest" method (respectively) when the test is run.
  435. // type:
  436. // An identifier denoting the type of testing that the test performs, such
  437. // as a performance test. If null, defaults to regular DOH test.
  438. if(!this._groups[group]){
  439. this._groupCount++;
  440. this._groups[group] = [];
  441. this._groups[group].inFlight = 0;
  442. }
  443. var tObj = this._getTestObj(group, test, type);
  444. if(!tObj){ return null; }
  445. this._groups[group].push(tObj);
  446. this._testCount++;
  447. this._testRegistered(group, tObj);
  448. return tObj;
  449. }
  450. doh.registerTests = function(/*String*/ group, /*Array*/ testArr, /*String*/ type){
  451. // summary:
  452. // registers a group of tests, treating each element of testArr as
  453. // though it were being (along with group) passed to the registerTest
  454. // method. It also uses the type to decide how the tests should
  455. // behave, by defining the type of tests these are, such as performance tests
  456. for(var x=0; x<testArr.length; x++){
  457. this.registerTest(group, testArr[x], type);
  458. }
  459. }
  460. // FIXME: move implementation to _browserRunner?
  461. doh.registerUrl = function( /*String*/ group,
  462. /*String*/ url,
  463. /*Integer*/ timeout,
  464. /*String*/ type){
  465. this.debug("ERROR:");
  466. this.debug("\tNO registerUrl() METHOD AVAILABLE.");
  467. // this._urls.push(url);
  468. }
  469. doh.registerString = function(group, str, type){
  470. }
  471. // FIXME: remove the doh.add alias SRTL.
  472. doh.register = doh.add = function(groupOrNs, testOrNull, type){
  473. // summary:
  474. // "magical" variant of registerTests, registerTest, and
  475. // registerTestNs. Will accept the calling arguments of any of these
  476. // methods and will correctly guess the right one to register with.
  477. if( (arguments.length == 1)&&
  478. (typeof groupOrNs == "string") ){
  479. if(groupOrNs.substr(0, 4)=="url:"){
  480. this.registerUrl(groupOrNs, null, null, type);
  481. }else{
  482. this.registerTest("ungrouped", groupOrNs, type);
  483. }
  484. }
  485. if(arguments.length == 1){
  486. this.debug("invalid args passed to doh.register():", groupOrNs, ",", testOrNull);
  487. return;
  488. }
  489. if(typeof testOrNull == "string"){
  490. if(testOrNull.substr(0, 4)=="url:"){
  491. this.registerUrl(testOrNull, null, null, type);
  492. }else{
  493. this.registerTest(groupOrNs, testOrNull, type);
  494. }
  495. // this.registerTestNs(groupOrNs, testOrNull);
  496. return;
  497. }
  498. if(doh._isArray(testOrNull)){
  499. this.registerTests(groupOrNs, testOrNull, type);
  500. return;
  501. }
  502. this.registerTest(groupOrNs, testOrNull, type);
  503. };
  504. doh.registerDocTests = function(module){
  505. // no-op for when Dojo isn't loaded into the page
  506. this.debug("registerDocTests() requires dojo to be loaded into the environment. Skipping doctest set for module:", module);
  507. };
  508. (function(){
  509. if(typeof dojo != "undefined"){
  510. try{
  511. dojo.require("dojox.testing.DocTest");
  512. }catch(e){
  513. // if the DocTest module isn't available (e.g., the build we're
  514. // running from doesn't include it), stub it out and log the error
  515. console.debug(e);
  516. doh.registerDocTests = function(){}
  517. return;
  518. }
  519. doh.registerDocTests = function(module){
  520. // summary:
  521. // Get all the doctests from the given module and register each of them
  522. // as a single test case here.
  523. //
  524. var docTest = new dojox.testing.DocTest();
  525. var docTests = docTest.getTests(module);
  526. var len = docTests.length;
  527. var tests = [];
  528. for (var i=0; i<len; i++){
  529. var test = docTests[i];
  530. // Extract comment on first line and add to test name.
  531. var comment = "";
  532. if (test.commands.length && test.commands[0].indexOf("//")!=-1) {
  533. var parts = test.commands[0].split("//");
  534. comment = ", "+parts[parts.length-1]; // Get all after the last //, so we dont get trapped by http:// or alikes :-).
  535. }
  536. tests.push({
  537. runTest: (function(test){
  538. return function(t){
  539. var r = docTest.runTest(test.commands, test.expectedResult);
  540. t.assertTrue(r.success);
  541. }
  542. })(test),
  543. name:"Line "+test.line+comment
  544. }
  545. );
  546. }
  547. this.register("DocTests: "+module, tests);
  548. }
  549. }
  550. })();
  551. //
  552. // Assertions and In-Test Utilities
  553. //
  554. doh.t = doh.assertTrue = function(/*Object*/ condition, /*String?*/ hint){
  555. // summary:
  556. // is the passed item "truthy"?
  557. if(arguments.length < 1){
  558. throw new doh._AssertFailure("assertTrue failed because it was not passed at least 1 argument");
  559. }
  560. if(!eval(condition)){
  561. throw new doh._AssertFailure("assertTrue('" + condition + "') failed", hint);
  562. }
  563. }
  564. doh.f = doh.assertFalse = function(/*Object*/ condition, /*String?*/ hint){
  565. // summary:
  566. // is the passed item "falsey"?
  567. if(arguments.length < 1){
  568. throw new doh._AssertFailure("assertFalse failed because it was not passed at least 1 argument");
  569. }
  570. if(eval(condition)){
  571. throw new doh._AssertFailure("assertFalse('" + condition + "') failed", hint);
  572. }
  573. }
  574. doh.e = doh.assertError = function(/*Error object*/expectedError, /*Object*/scope, /*String*/functionName, /*Array*/args, /*String?*/ hint){
  575. // summary:
  576. // Test for a certain error to be thrown by the given function.
  577. // example:
  578. // t.assertError(dojox.data.QueryReadStore.InvalidAttributeError, store, "getValue", [item, "NOT THERE"]);
  579. // t.assertError(dojox.data.QueryReadStore.InvalidItemError, store, "getValue", ["not an item", "NOT THERE"]);
  580. try{
  581. scope[functionName].apply(scope, args);
  582. }catch (e){
  583. if(e instanceof expectedError){
  584. return true;
  585. }else{
  586. throw new doh._AssertFailure("assertError() failed:\n\texpected error\n\t\t"+expectedError+"\n\tbut got\n\t\t"+e+"\n\n", hint);
  587. }
  588. }
  589. throw new doh._AssertFailure("assertError() failed:\n\texpected error\n\t\t"+expectedError+"\n\tbut no error caught\n\n", hint);
  590. }
  591. doh.is = doh.assertEqual = function(/*Object*/ expected, /*Object*/ actual, /*String?*/ hint){
  592. // summary:
  593. // are the passed expected and actual objects/values deeply
  594. // equivalent?
  595. // Compare undefined always with three equal signs, because undefined==null
  596. // is true, but undefined===null is false.
  597. if((expected === undefined)&&(actual === undefined)){
  598. return true;
  599. }
  600. if(arguments.length < 2){
  601. throw doh._AssertFailure("assertEqual failed because it was not passed 2 arguments");
  602. }
  603. if((expected === actual)||(expected == actual)||
  604. ( typeof expected == "number" && typeof actual == "number" && isNaN(expected) && isNaN(actual) )){
  605. return true;
  606. }
  607. if( (this._isArray(expected) && this._isArray(actual))&&
  608. (this._arrayEq(expected, actual)) ){
  609. return true;
  610. }
  611. if( ((typeof expected == "object")&&((typeof actual == "object")))&&
  612. (this._objPropEq(expected, actual)) ){
  613. return true;
  614. }
  615. throw new doh._AssertFailure("assertEqual() failed:\n\texpected\n\t\t"+expected+"\n\tbut got\n\t\t"+actual+"\n\n", hint);
  616. }
  617. doh.isNot = doh.assertNotEqual = function(/*Object*/ notExpected, /*Object*/ actual, /*String?*/ hint){
  618. // summary:
  619. // are the passed notexpected and actual objects/values deeply
  620. // not equivalent?
  621. // Compare undefined always with three equal signs, because undefined==null
  622. // is true, but undefined===null is false.
  623. if((notExpected === undefined)&&(actual === undefined)){
  624. throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
  625. }
  626. if(arguments.length < 2){
  627. throw doh._AssertFailure("assertEqual failed because it was not passed 2 arguments");
  628. }
  629. if((notExpected === actual)||(notExpected == actual)){
  630. throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
  631. }
  632. if( (this._isArray(notExpected) && this._isArray(actual))&&
  633. (this._arrayEq(notExpected, actual)) ){
  634. throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
  635. }
  636. if( ((typeof notExpected == "object")&&((typeof actual == "object")))&&
  637. (this._objPropEq(notExpected, actual)) ){
  638. throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
  639. }
  640. return true;
  641. }
  642. doh._arrayEq = function(expected, actual){
  643. if(expected.length != actual.length){ return false; }
  644. // FIXME: we're not handling circular refs. Do we care?
  645. for(var x=0; x<expected.length; x++){
  646. if(!doh.assertEqual(expected[x], actual[x])){ return false; }
  647. }
  648. return true;
  649. }
  650. doh._objPropEq = function(expected, actual){
  651. // Degenerate case: if they are both null, then their "properties" are equal.
  652. if(expected === null && actual === null){
  653. return true;
  654. }
  655. // If only one is null, they aren't equal.
  656. if(expected === null || actual === null){
  657. return false;
  658. }
  659. if(expected instanceof Date){
  660. return actual instanceof Date && expected.getTime()==actual.getTime();
  661. }
  662. var x;
  663. // Make sure ALL THE SAME properties are in both objects!
  664. for(x in actual){ // Lets check "actual" here, expected is checked below.
  665. if(expected[x] === undefined){
  666. return false;
  667. }
  668. };
  669. for(x in expected){
  670. if(!doh.assertEqual(expected[x], actual[x])){
  671. return false;
  672. }
  673. }
  674. return true;
  675. }
  676. doh._isArray = function(it){
  677. return (it && it instanceof Array || typeof it == "array" ||
  678. (
  679. !!doh.global["dojo"] &&
  680. doh.global["dojo"]["NodeList"] !== undefined &&
  681. it instanceof doh.global["dojo"]["NodeList"]
  682. )
  683. );
  684. }
  685. //
  686. // Runner-Wrapper
  687. //
  688. doh._setupGroupForRun = function(/*String*/ groupName, /*Integer*/ idx){
  689. var tg = this._groups[groupName];
  690. this.debug(this._line);
  691. this.debug("GROUP", "\""+groupName+"\"", "has", tg.length, "test"+((tg.length > 1) ? "s" : "")+" to run");
  692. }
  693. doh._handleFailure = function(groupName, fixture, e){
  694. // this.debug("FAILED test:", fixture.name);
  695. // mostly borrowed from JUM
  696. this._groups[groupName].failures++;
  697. var out = "";
  698. if(e instanceof this._AssertFailure){
  699. this._failureCount++;
  700. if(e["fileName"]){ out += e.fileName + ':'; }
  701. if(e["lineNumber"]){ out += e.lineNumber + ' '; }
  702. out += e+": "+e.message;
  703. this.debug("\t_AssertFailure:", out);
  704. }else{
  705. this._errorCount++;
  706. }
  707. this.debug(e);
  708. if(fixture.runTest["toSource"]){
  709. var ss = fixture.runTest.toSource();
  710. this.debug("\tERROR IN:\n\t\t", ss);
  711. }else{
  712. this.debug("\tERROR IN:\n\t\t", fixture.runTest);
  713. }
  714. if(e.rhinoException){
  715. e.rhinoException.printStackTrace();
  716. }else if(e.javaException){
  717. e.javaException.printStackTrace();
  718. }
  719. }
  720. //Assume a setTimeout implementation that is synchronous, so that
  721. //the Node and Rhino envs work similar to each other. Node defines
  722. //a setTimeout, so testing for setTimeout is not enough, each environment
  723. //adapter should set this value accordingly.
  724. doh.setTimeout = function(func){
  725. return func();
  726. };
  727. doh._runPerfFixture = function(/*String*/groupName, /*Object*/fixture){
  728. // summary:
  729. // This function handles how to execute a 'performance' test
  730. // which is different from a straight UT style test. These
  731. // will often do numerous iterations of the same operation and
  732. // gather execution statistics about it, like max, min, average,
  733. // etc. It makes use of the already in place DOH deferred test
  734. // handling since it is a good idea to put a pause inbetween each
  735. // iteration to allow for GC cleanup and the like.
  736. //
  737. // groupName:
  738. // The test group that contains this performance test.
  739. // fixture:
  740. // The performance test fixture.
  741. var tg = this._groups[groupName];
  742. fixture.startTime = new Date();
  743. //Perf tests always need to act in an async manner as there is a
  744. //number of iterations to flow through.
  745. var def = new doh.Deferred();
  746. tg.inFlight++;
  747. def.groupName = groupName;
  748. def.fixture = fixture;
  749. def.addErrback(function(err){
  750. doh._handleFailure(groupName, fixture, err);
  751. });
  752. //Set up the finalizer.
  753. var retEnd = function(){
  754. if(fixture["tearDown"]){ fixture.tearDown(doh); }
  755. tg.inFlight--;
  756. if((!tg.inFlight)&&(tg.iterated)){
  757. doh._groupFinished(groupName, !tg.failures);
  758. }
  759. doh._testFinished(groupName, fixture, def.results[0]);
  760. if(doh._paused){
  761. doh.run();
  762. }
  763. };
  764. //Since these can take who knows how long, we don't want to timeout
  765. //unless explicitly set
  766. var timer;
  767. var to = fixture.timeout;
  768. if(to > 0) {
  769. timer = doh.setTimeout(function(){
  770. // ret.cancel();
  771. // retEnd();
  772. def.errback(new Error("test timeout in "+fixture.name.toString()));
  773. }, to);
  774. }
  775. //Set up the end calls to the test into the deferred we'll return.
  776. def.addBoth(function(arg){
  777. if(timer){
  778. clearTimeout(timer);
  779. }
  780. retEnd();
  781. });
  782. //Okay, now set up the timing loop for the actual test.
  783. //This is down as an async type test where there is a delay
  784. //between each execution to allow for GC time, etc, so the GC
  785. //has less impact on the tests.
  786. var res = fixture.results;
  787. res.trials = [];
  788. //Try to figure out how many calls are needed to hit a particular threshold.
  789. var itrDef = doh._calcTrialIterations(groupName, fixture);
  790. itrDef.addErrback(function(err){
  791. fixture.endTime = new Date();
  792. def.errback(err);
  793. });
  794. //Blah, since tests can be deferred, the actual run has to be deferred until after
  795. //we know how many iterations to run. This is just plain ugly.
  796. itrDef.addCallback(function(iterations){
  797. if(iterations){
  798. var countdown = fixture.trialIterations;
  799. doh.debug("TIMING TEST: [" + fixture.name +
  800. "]\n\t\tITERATIONS PER TRIAL: " +
  801. iterations + "\n\tTRIALS: " +
  802. countdown);
  803. //Figure out how many times we want to run our 'trial'.
  804. //Where each trial consists of 'iterations' of the test.
  805. var trialRunner = function() {
  806. //Set up our function to execute a block of tests
  807. var start = new Date();
  808. var tTimer = new doh.Deferred();
  809. var tCountdown = iterations;
  810. var tState = {
  811. countdown: iterations
  812. };
  813. var testRunner = function(state){
  814. while(state){
  815. try{
  816. state.countdown--;
  817. if(state.countdown){
  818. var ret = fixture.runTest(doh);
  819. if(ret instanceof doh.Deferred){
  820. //Deferreds have to be handled async,
  821. //otherwise we just keep looping.
  822. var atState = {
  823. countdown: state.countdown
  824. };
  825. ret.addCallback(function(){
  826. testRunner(atState);
  827. });
  828. ret.addErrback(function(err) {
  829. doh._handleFailure(groupName, fixture, err);
  830. fixture.endTime = new Date();
  831. def.errback(err);
  832. });
  833. state = null;
  834. }
  835. }else{
  836. tTimer.callback(new Date());
  837. state = null;
  838. }
  839. }catch(err){
  840. fixture.endTime = new Date();
  841. tTimer.errback(err);
  842. }
  843. }
  844. };
  845. tTimer.addCallback(function(end){
  846. //Figure out the results and try to factor out function call costs.
  847. var tResults = {
  848. trial: (fixture.trialIterations - countdown),
  849. testIterations: iterations,
  850. executionTime: (end.getTime() - start.getTime()),
  851. average: (end.getTime() - start.getTime())/iterations
  852. };
  853. res.trials.push(tResults);
  854. doh.debug("\n\t\tTRIAL #: " +
  855. tResults.trial + "\n\tTIME: " +
  856. tResults.executionTime + "ms.\n\tAVG TEST TIME: " +
  857. (tResults.executionTime/tResults.testIterations) + "ms.");
  858. //Okay, have we run all the trials yet?
  859. countdown--;
  860. if(countdown){
  861. doh.setTimeout(trialRunner, fixture.trialDelay);
  862. }else{
  863. //Okay, we're done, lets compute some final performance results.
  864. var t = res.trials;
  865. //We're done.
  866. fixture.endTime = new Date();
  867. def.callback(true);
  868. }
  869. });
  870. tTimer.addErrback(function(err){
  871. fixture.endTime = new Date();
  872. def.errback(err);
  873. });
  874. testRunner(tState);
  875. };
  876. trialRunner();
  877. }
  878. });
  879. //Set for a pause, returned the deferred.
  880. if(def.fired < 0){
  881. doh.pause();
  882. }
  883. return def;
  884. };
  885. doh._calcTrialIterations = function(/*String*/ groupName, /*Object*/ fixture){
  886. // summary:
  887. // This function determines the rough number of iterations to
  888. // use to reach a particular MS threshold. This returns a deferred
  889. // since tests can theoretically by async. Async tests aren't going to
  890. // give great perf #s, though.
  891. // The callback is passed the # of iterations to hit the requested
  892. // threshold.
  893. //
  894. // fixture:
  895. // The test fixture we want to calculate iterations for.
  896. var def = new doh.Deferred();
  897. var calibrate = function () {
  898. var testFunc = fixture.runTest;
  899. //Set the initial state. We have to do this as a loop instead
  900. //of a recursive function. Otherwise, it blows the call stack
  901. //on some browsers.
  902. var iState = {
  903. start: new Date(),
  904. curIter: 0,
  905. iterations: 5
  906. };
  907. var handleIteration = function(state){
  908. while(state){
  909. if(state.curIter < state.iterations){
  910. try{
  911. var ret = testFunc(doh);
  912. if(ret instanceof doh.Deferred){
  913. var aState = {
  914. start: state.start,
  915. curIter: state.curIter + 1,
  916. iterations: state.iterations
  917. };
  918. ret.addCallback(function(){
  919. handleIteration(aState);
  920. });
  921. ret.addErrback(function(err) {
  922. fixture.endTime = new Date();
  923. def.errback(err);
  924. });
  925. state = null;
  926. }else{
  927. state.curIter++;
  928. }
  929. }catch(err){
  930. fixture.endTime = new Date();
  931. def.errback(err);
  932. return;
  933. }
  934. }else{
  935. var end = new Date();
  936. var totalTime = (end.getTime() - state.start.getTime());
  937. if(totalTime < fixture.trialDuration){
  938. var nState = {
  939. iterations: state.iterations * 2,
  940. curIter: 0
  941. }
  942. state = null;
  943. doh.setTimeout(function(){
  944. nState.start = new Date();
  945. handleIteration(nState);
  946. }, 50);
  947. }else{
  948. var itrs = state.iterations;
  949. doh.setTimeout(function(){def.callback(itrs)}, 50);
  950. state = null;
  951. }
  952. }
  953. }
  954. };
  955. handleIteration(iState);
  956. };
  957. doh.setTimeout(calibrate, 10);
  958. return def;
  959. };
  960. doh._runRegFixture = function(/*String*/groupName, /*Object*/fixture){
  961. // summary:
  962. // Function to run a generic doh test. These are not
  963. // specialized tests, like performance groups and such.
  964. //
  965. // groupName:
  966. // The groupName of the test.
  967. // fixture:
  968. // The test fixture to execute.
  969. var tg = this._groups[groupName];
  970. fixture.startTime = new Date();
  971. var ret = fixture.runTest(this);
  972. fixture.endTime = new Date();
  973. // if we get a deferred back from the test runner, we know we're
  974. // gonna wait for an async result. It's up to the test code to trap
  975. // errors and give us an errback or callback.
  976. if(ret instanceof doh.Deferred){
  977. tg.inFlight++;
  978. ret.groupName = groupName;
  979. ret.fixture = fixture;
  980. ret.addErrback(function(err){
  981. doh._handleFailure(groupName, fixture, err);
  982. });
  983. var retEnd = function(){
  984. if(fixture["tearDown"]){ fixture.tearDown(doh); }
  985. tg.inFlight--;
  986. if((!tg.inFlight)&&(tg.iterated)){
  987. doh._groupFinished(groupName, !tg.failures);
  988. }
  989. doh._testFinished(groupName, fixture, ret.results[0]);
  990. if(doh._paused){
  991. doh.run();
  992. }
  993. }
  994. var timer = doh.setTimeout(function(){
  995. // ret.cancel();
  996. // retEnd();
  997. ret.errback(new Error("test timeout in "+fixture.name.toString()));
  998. }, fixture["timeout"]||1000);
  999. ret.addBoth(function(arg){
  1000. clearTimeout(timer);
  1001. retEnd();
  1002. });
  1003. if(ret.fired < 0){
  1004. doh.pause();
  1005. }
  1006. return ret;
  1007. }
  1008. };
  1009. doh._runFixture = function(groupName, fixture){
  1010. var tg = this._groups[groupName];
  1011. this._testStarted(groupName, fixture);
  1012. var threw = false;
  1013. var err = null;
  1014. // run it, catching exceptions and reporting them
  1015. try{
  1016. // let doh reference "this.group.thinger..." which can be set by
  1017. // another test or group-level setUp function
  1018. fixture.group = tg;
  1019. // only execute the parts of the fixture we've got
  1020. if(fixture["setUp"]){ fixture.setUp(this); }
  1021. if(fixture["runTest"]){ // should we error out of a fixture doesn't have a runTest?
  1022. if(fixture.testType === "perf"){
  1023. //Always async deferred, so return it.
  1024. return doh._runPerfFixture(groupName, fixture);
  1025. }else{
  1026. //May or may not by async.
  1027. var ret = doh._runRegFixture(groupName, fixture);
  1028. if(ret){
  1029. return ret;
  1030. }
  1031. }
  1032. }
  1033. if(fixture["tearDown"]){ fixture.tearDown(this); }
  1034. }catch(e){
  1035. threw = true;
  1036. err = e;
  1037. if(!fixture.endTime){
  1038. fixture.endTime = new Date();
  1039. }
  1040. }
  1041. var d = new doh.Deferred();
  1042. doh.setTimeout(this.hitch(this, function(){
  1043. if(threw){
  1044. this._handleFailure(groupName, fixture, err);
  1045. }
  1046. this._testFinished(groupName, fixture, !threw);
  1047. if((!tg.inFlight)&&(tg.iterated)){
  1048. doh._groupFinished(groupName, !tg.failures);
  1049. }else if(tg.inFlight > 0){
  1050. doh.setTimeout(this.hitch(this, function(){
  1051. doh.runGroup(groupName); // , idx);
  1052. }), 100);
  1053. this._paused = true;
  1054. }
  1055. if(doh._paused){
  1056. doh.run();
  1057. }
  1058. }), 30);
  1059. doh.pause();
  1060. return d;
  1061. }
  1062. doh._testId = 0;
  1063. doh.runGroup = function(/*String*/ groupName, /*Integer*/ idx){
  1064. // summary:
  1065. // runs the specified test group
  1066. // the general structure of the algorithm is to run through the group's
  1067. // list of doh, checking before and after each of them to see if we're in
  1068. // a paused state. This can be caused by the test returning a deferred or
  1069. // the user hitting the pause button. In either case, we want to halt
  1070. // execution of the test until something external to us restarts it. This
  1071. // means we need to pickle off enough state to pick up where we left off.
  1072. // FIXME: need to make fixture execution async!!
  1073. var tg = this._groups[groupName];
  1074. if(tg.skip === true){ return; }
  1075. if(this._isArray(tg)){
  1076. if(idx<=tg.length){
  1077. if((!tg.inFlight)&&(tg.iterated == true)){
  1078. if(tg["tearDown"]){ tg.tearDown(this); }
  1079. doh._groupFinished(groupName, !tg.failures);
  1080. return;
  1081. }
  1082. }
  1083. if(!idx){
  1084. tg.inFlight = 0;
  1085. tg.iterated = false;
  1086. tg.failures = 0;
  1087. }
  1088. doh._groupStarted(groupName);
  1089. if(!idx){
  1090. this._setupGroupForRun(groupName, idx);
  1091. if(tg["setUp"]){ tg.setUp(this); }
  1092. }
  1093. for(var y=(idx||0); y<tg.length; y++){
  1094. if(this._paused){
  1095. this._currentTest = y;
  1096. // this.debug("PAUSED at:", tg[y].name, this._currentGroup, this._currentTest);
  1097. return;
  1098. }
  1099. doh._runFixture(groupName, tg[y]);
  1100. if(this._paused){
  1101. this._currentTest = y+1;
  1102. if(this._currentTest == tg.length){
  1103. tg.iterated = true;
  1104. }
  1105. // this.debug("PAUSED at:", tg[y].name, this._currentGroup, this._currentTest);
  1106. return;
  1107. }
  1108. }
  1109. tg.iterated = true;
  1110. if(!tg.inFlight){
  1111. if(tg["tearDown"]){ tg.tearDown(this); }
  1112. doh._groupFinished(groupName, !tg.failures);
  1113. }
  1114. }
  1115. }
  1116. doh._onEnd = function(){}
  1117. doh._report = function(){
  1118. // summary:
  1119. // a private method to be implemented/replaced by the "locally
  1120. // appropriate" test runner
  1121. // this.debug("ERROR:");
  1122. // this.debug("\tNO REPORTING OUTPUT AVAILABLE.");
  1123. // this.debug("\tIMPLEMENT doh._report() IN YOUR TEST RUNNER");
  1124. this.debug(this._line);
  1125. this.debug("| TEST SUMMARY:");
  1126. this.debug(this._line);
  1127. this.debug("\t", this._testCount, "tests in", this._groupCount, "groups");
  1128. this.debug("\t", this._errorCount, "errors");
  1129. this.debug("\t", this._failureCount, "failures");
  1130. }
  1131. doh.togglePaused = function(){
  1132. this[(this._paused) ? "run" : "pause"]();
  1133. }
  1134. doh.pause = function(){
  1135. // summary:
  1136. // halt test run. Can be resumed.
  1137. this._paused = true;
  1138. }
  1139. doh.run = function(){
  1140. // summary:
  1141. // begins or resumes the test process.
  1142. // this.debug("STARTING");
  1143. this._paused = false;
  1144. var cg = this._currentGroup;
  1145. var ct = this._currentTest;
  1146. var found = false;
  1147. if(!cg){
  1148. this._init(); // we weren't paused
  1149. found = true;
  1150. }
  1151. this._currentGroup = null;
  1152. this._currentTest = null;
  1153. for(var x in this._groups){
  1154. if(
  1155. ( (!found)&&(x == cg) )||( found )
  1156. ){
  1157. if(this._paused){ return; }
  1158. this._currentGroup = x;
  1159. if(!found){
  1160. found = true;
  1161. this.runGroup(x, ct);
  1162. }else{
  1163. this.runGroup(x);
  1164. }
  1165. if(this._paused){ return; }
  1166. }
  1167. }
  1168. this._currentGroup = null;
  1169. this._currentTest = null;
  1170. this._paused = false;
  1171. this._onEnd();
  1172. this._report();
  1173. }
  1174. //Statistics functions to handle computing performance metrics.
  1175. //Taken from dojox.math
  1176. // basic statistics
  1177. doh.standardDeviation = function(/* Number[] */a){
  1178. // summary:
  1179. // Returns the standard deviation of the passed arguments.
  1180. return Math.sqrt(this.variance(a)); // Number
  1181. };
  1182. doh.variance = function(/* Number[] */a){
  1183. // summary:
  1184. // Find the variance in the passed array of numbers.
  1185. var mean=0, squares=0;
  1186. dojo.forEach(a, function(item){
  1187. mean+=item;
  1188. squares+=Math.pow(item,2);
  1189. });
  1190. return (squares/a.length)-Math.pow(mean/a.length, 2); // Number
  1191. };
  1192. doh.mean = function(/* Number[] */a){
  1193. // summary:
  1194. // Returns the mean value in the passed array.
  1195. var t=0;
  1196. dojo.forEach(a, function(v){
  1197. t += v;
  1198. });
  1199. return t / Math.max(a.length, 1); // Number
  1200. };
  1201. doh.min = function(/* Number[] */a){
  1202. // summary:
  1203. // Returns the min value in the passed array.
  1204. return Math.min.apply(null, a); // Number
  1205. };
  1206. doh.max = function(/* Number[] */a){
  1207. // summary:
  1208. // Returns the max value in the passed array.
  1209. return Math.max.apply(null, a); // Number
  1210. },
  1211. doh.median= function(/* Number[] */a){
  1212. // summary:
  1213. // Returns the value closest to the middle from a sorted version of the passed array.
  1214. return a.slice(0).sort()[Math.ceil(a.length/2)-1]; // Number
  1215. },
  1216. doh.mode = function(/* Number[] */a){
  1217. // summary:
  1218. // Returns the mode from the passed array (number that appears the most often).
  1219. // This is not the most efficient method, since it requires a double scan, but
  1220. // is ensures accuracy.
  1221. var o = {}, r = 0, m = Number.MIN_VALUE;
  1222. dojo.forEach(a, function(v){
  1223. (o[v]!==undefined)?o[v]++:o[v]=1;
  1224. });
  1225. // we did the lookup map because we need the number that appears the most.
  1226. for(var p in o){
  1227. if(m < o[p]){
  1228. m = o[p], r = p;
  1229. }
  1230. }
  1231. return r; // Number
  1232. };
  1233. doh.average = function(/* Number [] */ a){
  1234. var i;
  1235. var s = 0;
  1236. for(i = 0; i < a.length; i++){
  1237. s += a[i];
  1238. }
  1239. return s/a.length;
  1240. }
  1241. tests = doh;
  1242. if (typeof skipDohSetup === "undefined") {
  1243. (function(){
  1244. // scope protection
  1245. var x;
  1246. try{
  1247. if(typeof dojo != "undefined"){
  1248. dojo.platformRequire({
  1249. browser: ["doh._browserRunner"],
  1250. rhino: ["doh._rhinoRunner"],
  1251. spidermonkey: ["doh._rhinoRunner"]
  1252. });
  1253. try{
  1254. var _shouldRequire = dojo.isBrowser ? (dojo.global == dojo.global["parent"] || !Boolean(dojo.global.parent.doh) ) : true;
  1255. }catch(e){
  1256. //can't access dojo.global.parent.doh, then we need to do require
  1257. _shouldRequire = true;
  1258. }
  1259. if(_shouldRequire){
  1260. if(dojo.isBrowser){
  1261. dojo.addOnLoad(function(){
  1262. if (dojo.global.registerModulePath){
  1263. dojo.forEach(dojo.global.registerModulePath, function(m){
  1264. dojo.registerModulePath(m[0], m[1]);
  1265. });
  1266. }
  1267. if(dojo.byId("testList")){
  1268. var _tm = ( (dojo.global.testModule && dojo.global.testModule.length) ? dojo.global.testModule : "dojo.tests.module");
  1269. dojo.forEach(_tm.split(","), dojo.require, dojo);
  1270. doh.setTimeout(function(){
  1271. doh.run();
  1272. }, 500);
  1273. }
  1274. });
  1275. }else{
  1276. // dojo.require("doh._base");
  1277. }
  1278. }
  1279. }else{
  1280. if(typeof load == "function" &&
  1281. (typeof Packages == "function" || typeof Packages == "object")){
  1282. throw new Error();
  1283. }else if(typeof load == "function"){
  1284. throw new Error();
  1285. }
  1286. if(this["document"]){
  1287. /*
  1288. // if we survived all of that, we're probably in a browser but
  1289. // don't have Dojo handy. Load _browserRunner.js using a
  1290. // document.write() call.
  1291. // find runner.js, load _browserRunner relative to it
  1292. var scripts = document.getElementsByTagName("script"), runnerFile;
  1293. for(x=0; x<scripts.length; x++){
  1294. var s = scripts[x].src;
  1295. if(s){
  1296. if(!runnerFile && s.substr(s.length - 9) == "runner.js"){
  1297. runnerFile = s;
  1298. }else if(s.substr(s.length - 17) == "_browserRunner.js"){
  1299. runnerFile = null;
  1300. break;
  1301. }
  1302. }
  1303. }
  1304. if(runnerFile){
  1305. document.write("<scri"+"pt src='" + runnerFile.substr(0, runnerFile.length - 9)
  1306. + "_browserRunner.js' type='text/javascript'></scr"+"ipt>");
  1307. }
  1308. */
  1309. }
  1310. }
  1311. }catch(e){
  1312. print("\n"+doh._line);
  1313. print("The Dojo Unit Test Harness, $Rev: 20389 $");
  1314. print("Copyright (c) 2009, The Dojo Foundation, All Rights Reserved");
  1315. print(doh._line, "\n");
  1316. try{
  1317. var dojoUrl = "../../dojo/dojo.js";
  1318. var testUrl = "";
  1319. var testModule = "dojo.tests.module";
  1320. var dohBase = "";
  1321. for(x=0; x<arguments.length; x++){
  1322. if(arguments[x].indexOf("=") > 0){
  1323. var tp = arguments[x].split("=");
  1324. if(tp[0] == "dohBase"){
  1325. dohBase = tp[1];
  1326. //Convert slashes to unix style and make sure properly
  1327. //ended.
  1328. dohBase = dohBase.replace(/\\/g, "/");
  1329. if(dohBase.charAt(dohBase.length - 1) != "/"){
  1330. dohBase += "/";
  1331. }
  1332. }
  1333. if(tp[0] == "dojoUrl"){
  1334. dojoUrl = tp[1];
  1335. }
  1336. if(tp[0] == "testUrl"){
  1337. testUrl = tp[1];
  1338. }
  1339. if(tp[0] == "testModule"){
  1340. testModule = tp[1];
  1341. }
  1342. }
  1343. }
  1344. load(dohBase + "_rhinoRunner.js");
  1345. if(dojoUrl.length){
  1346. if(!this["djConfig"]){
  1347. djConfig = {};
  1348. }
  1349. djConfig.baseUrl = dojoUrl.split("dojo.js")[0];
  1350. load(dojoUrl);
  1351. }
  1352. if(testUrl.length){
  1353. load(testUrl);
  1354. }
  1355. if(testModule.length){
  1356. dojo.forEach(testModule.split(","), dojo.require, dojo);
  1357. }
  1358. }catch(e){
  1359. print("An exception occurred: " + e);
  1360. }
  1361. doh.run();
  1362. }
  1363. }).apply(this, typeof arguments != "undefined" ? arguments : [null]);
  1364. }