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