Changeset 7589

Show
Ignore:
Timestamp:
03/14/07 04:58:36 (22 months ago)
Author:
alex
Message:

implements async test harness. Refs #2550

Location:
dojo/trunk/tests
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • dojo/trunk/tests/runner.html

    r7580 r7589  
    6868                        #play, #pause { 
    6969                                font-family: Webdings; 
    70                                 font-size: 2.0em; 
     70                                font-size: 1.4em; 
    7171                                border: 1px solid #DEDEDE; 
    7272                                cursor: pointer; 
     
    112112                                float: left; 
    113113                                background-color: #DEDEDE; 
     114                        } 
     115 
     116                        tr.inProgress { 
     117                                background-color: #85afde; 
     118                        } 
     119 
     120                        tr.success { 
     121                                background-color: #7cdea7; 
     122                        } 
     123 
     124                        tr.failure { 
     125                                background-color: #de827b; 
    114126                        } 
    115127                </style> 
     
    137149                                <td width="30%" class="header"> 
    138150                                        <span id="toggleButtons" onclick="tests.togglePaused();"> 
    139                                                 <span id="play">&#052;</span> 
    140                                                 <span id="pause" style="display: none;">&#059;</span> 
     151                                                <button id="play">&#052;</button> 
     152                                                <button id="pause" style="display: none;">&#059;</button> 
    141153                                        </span> 
    142154                                        <span id="runningStatus"> 
     
    146158                                </td> 
    147159                                <td width="*" class="header"> 
    148                                         blah 
     160                                        <button>Test Page</button> 
     161                                        <button>Log</button> 
    149162                                </td> 
    150163                        </tr> 
     
    161174                                                                        </th> 
    162175                                                                        <th width="*" style="text-align: left;">test</th> 
    163                                                                         <th width="50">pass/fail</th> 
    164176                                                                        <th width="50">time</th> 
    165177                                                                </tr> 
     
    171183                                                                        </td> 
    172184                                                                        <td>group name</td> 
    173                                                                         <td>pass</td> 
    174185                                                                        <td>10ms</td> 
    175186                                                                </tr> 
     
    177188                                                                        <td>&nbsp;</td> 
    178189                                                                        <td style="padding-left: 20px;">test name</td> 
    179                                                                         <td>pass</td> 
    180190                                                                        <td>10ms</td> 
    181191                                                                </tr> 
  • dojo/trunk/tests/runner.js

    r7580 r7589  
    1313// Utility Functions and Classes 
    1414// 
     15 
     16tests.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) || function(){}; 
     22        return function(){ 
     23                var ta = args.concat([]); // make a copy 
     24                for(var x=0; x<arguments.length; x++){ 
     25                        ta.push(arguments[x]); 
     26                } 
     27                return fcn.apply(thisObject, ta); // Function 
     28        }; 
     29} 
     30 
     31tests._mixin = function(/*Object*/ obj, /*Object*/ props){ 
     32        // summary: 
     33        //              Adds all properties and methods of props to obj. This addition is 
     34        //              "prototype extension safe", so that instances of objects will not 
     35        //              pass along prototype defaults. 
     36        var tobj = {}; 
     37        for(var x in props){ 
     38                // the "tobj" condition avoid copying properties in "props" 
     39                // inherited from Object.prototype.  For example, if obj has a custom 
     40                // toString() method, don't overwrite it with the toString() method 
     41                // that props inherited from Object.protoype 
     42                if((typeof tobj[x] == "undefined") || (tobj[x] != props[x])){ 
     43                        obj[x] = props[x]; 
     44                } 
     45        } 
     46        // IE doesn't recognize custom toStrings in for..in 
     47        if(     this["document"]  
     48                && document.all 
     49                && (typeof props["toString"] == "function") 
     50                && (props["toString"] != obj["toString"]) 
     51                && (props["toString"] != tobj["toString"]) 
     52        ){ 
     53                obj.toString = props.toString; 
     54        } 
     55        return obj; // Object 
     56} 
     57 
     58tests.mixin = function(/*Object*/obj, /*Object...*/props){ 
     59        // summary:     Adds all properties and methods of props to obj.  
     60        for(var i=1, l=arguments.length; i<l; i++){ 
     61                tests._mixin(obj, arguments[i]); 
     62        } 
     63        return obj; // Object 
     64} 
     65 
     66tests.extend = function(/*Object*/ constructor, /*Object...*/ props){ 
     67        // summary: 
     68        //              Adds all properties and methods of props to constructor's 
     69        //              prototype, making them available to all instances created with 
     70        //              constructor. 
     71        for(var i=1, l=arguments.length; i<l; i++){ 
     72                tests._mixin(constructor.prototype, arguments[i]); 
     73        } 
     74        return constructor; // Object 
     75} 
     76 
    1577 
    1678tests._line = "------------------------------------------------------------"; 
     
    51113tests._AssertFailure.prototype.name = "tests._AssertFailure"; 
    52114 
     115tests.Deferred = function(canceller){ 
     116        this.chain = []; 
     117        this.id = this._nextId(); 
     118        this.fired = -1; 
     119        this.paused = 0; 
     120        this.results = [null, null]; 
     121        this.canceller = canceller; 
     122        this.silentlyCancelled = false; 
     123}; 
     124 
     125tests.extend(tests.Deferred, { 
     126        getFunctionFromArgs: function(){ 
     127                var a = arguments; 
     128                if((a[0])&&(!a[1])){ 
     129                        if(typeof a[0] == "function"){ 
     130                                return a[0]; 
     131                        }else if(typeof a[0] == "string"){ 
     132                                return dj_global[a[0]]; 
     133                        } 
     134                }else if((a[0])&&(a[1])){ 
     135                        return tests.hitch(a[0], a[1]); 
     136                } 
     137                return null; 
     138        }, 
     139 
     140        makeCalled: function() { 
     141                var deferred = new tests.Deferred(); 
     142                deferred.callback(); 
     143                return deferred; 
     144        }, 
     145 
     146        _nextId: (function(){ 
     147                var n = 1; 
     148                return function(){ return n++; }; 
     149        })(), 
     150 
     151        cancel: function(){ 
     152                if(this.fired == -1){ 
     153                        if (this.canceller){ 
     154                                this.canceller(this); 
     155                        }else{ 
     156                                this.silentlyCancelled = true; 
     157                        } 
     158                        if(this.fired == -1){ 
     159                                this.errback(new Error("Deferred(unfired)")); 
     160                        } 
     161                }else if(       (this.fired == 0)&& 
     162                                        (this.results[0] instanceof tests.Deferred)){ 
     163                        this.results[0].cancel(); 
     164                } 
     165        }, 
     166                         
     167 
     168        _pause: function(){ 
     169                this.paused++; 
     170        }, 
     171 
     172        _unpause: function(){ 
     173                this.paused--; 
     174                if ((this.paused == 0) && (this.fired >= 0)) { 
     175                        this._fire(); 
     176                } 
     177        }, 
     178 
     179        _continue: function(res){ 
     180                this._resback(res); 
     181                this._unpause(); 
     182        }, 
     183 
     184        _resback: function(res){ 
     185                this.fired = ((res instanceof Error) ? 1 : 0); 
     186                this.results[this.fired] = res; 
     187                this._fire(); 
     188        }, 
     189 
     190        _check: function(){ 
     191                if(this.fired != -1){ 
     192                        if(!this.silentlyCancelled){ 
     193                                tests.raise("already called!"); 
     194                        } 
     195                        this.silentlyCancelled = false; 
     196                        return; 
     197                } 
     198        }, 
     199 
     200        callback: function(res){ 
     201                this._check(); 
     202                this._resback(res); 
     203        }, 
     204 
     205        errback: function(res){ 
     206                this._check(); 
     207                if(!(res instanceof Error)){ 
     208                        res = new Error(res); 
     209                } 
     210                this._resback(res); 
     211        }, 
     212 
     213        addBoth: function(cb, cbfn){ 
     214                var enclosed = this.getFunctionFromArgs(cb, cbfn); 
     215                if(arguments.length > 2){ 
     216                        enclosed = tests.hitch(null, enclosed, arguments, 2); 
     217                } 
     218                return this.addCallbacks(enclosed, enclosed); 
     219        }, 
     220 
     221        addCallback: function(cb, cbfn){ 
     222                var enclosed = this.getFunctionFromArgs(cb, cbfn); 
     223                if(arguments.length > 2){ 
     224                        enclosed = tests.hitch(null, enclosed, arguments, 2); 
     225                } 
     226                return this.addCallbacks(enclosed, null); 
     227        }, 
     228 
     229        addErrback: function(cb, cbfn){ 
     230                var enclosed = this.getFunctionFromArgs(cb, cbfn); 
     231                if(arguments.length > 2){ 
     232                        enclosed = tests.hitch(null, enclosed, arguments, 2); 
     233                } 
     234                return this.addCallbacks(null, enclosed); 
     235        }, 
     236 
     237        addCallbacks: function(cb, eb){ 
     238                this.chain.push([cb, eb]) 
     239                if(this.fired >= 0){ 
     240                        this._fire(); 
     241                } 
     242                return this; 
     243        }, 
     244 
     245        _fire: function(){ 
     246                var chain = this.chain; 
     247                var fired = this.fired; 
     248                var res = this.results[fired]; 
     249                var self = this; 
     250                var cb = null; 
     251                while (chain.length > 0 && this.paused == 0) { 
     252                        // Array 
     253                        var pair = chain.shift(); 
     254                        var f = pair[fired]; 
     255                        if (f == null) { 
     256                                continue; 
     257                        } 
     258                        try { 
     259                                res = f(res); 
     260                                fired = ((res instanceof Error) ? 1 : 0); 
     261                                if(res instanceof tests.Deferred) { 
     262                                        cb = function(res){ 
     263                                                self._continue(res); 
     264                                        } 
     265                                        this._pause(); 
     266                                } 
     267                        }catch(err){ 
     268                                fired = 1; 
     269                                res = err; 
     270                        } 
     271                } 
     272                this.fired = fired; 
     273                this.results[fired] = res; 
     274                if((cb)&&(this.paused)){ 
     275                        res.addBoth(cb); 
     276                } 
     277        } 
     278}); 
     279 
    53280// 
    54281// State Keeping and Reporting 
     
    58285tests._errorCount = 0; 
    59286tests._failureCount = 0; 
    60 tests._passedCount = 0; 
    61287tests._currentGroup = null; 
    62288tests._currentTest = null; 
     
    68294        this._errorCount = 0; 
    69295        this._failureCount = 0; 
    70         this._passedCount = 0; 
    71296} 
    72297 
     
    126351        if(!this._groups[group]){ 
    127352                this._groups[group] = []; 
     353                this._groups[group].inFlight = 0; 
    128354        } 
    129355        var tObj = test; 
     
    256482 
    257483tests._setupGroupForRun = function(/*String*/ groupName, /*Integer*/ idx){ 
    258 } 
    259  
     484        var tg = this._groups[groupName]; 
     485        this.debug(this._line, "\nGROUP", "\""+groupName+"\"", "has", tg.length, "test"+((tg.length > 1) ? "s" : "")+" to run"); 
     486} 
     487 
     488tests._handleFailure = function(groupName, fixture, e){ 
     489        // this.debug("FAILED test:", fixture.name); 
     490        // mostly borrowed from JUM 
     491        this._groups[groupName].failures++; 
     492        var out = ""; 
     493        if(e instanceof this._AssertFailure){ 
     494                this._failureCount++; 
     495                if(e["fileName"]){ out += e.fileName + ':'; } 
     496                if(e["lineNumber"]){ out += e.lineNumber + ' '; } 
     497                out += e.message; 
     498                this.debug("\t_AssertFailure:", out); 
     499        }else{ 
     500                this._errorCount++; 
     501        } 
     502        if(fixture.runTest["toSource"]){ 
     503                var ss = fixture.runTest.toSource(); 
     504                this.debug("\tERROR IN:\n\t\t", ss); 
     505        } 
     506} 
     507 
     508tests._runFixture = function(groupName, fixture){ 
     509        var tg = this._groups[groupName]; 
     510        this._testStarted(groupName, fixture); 
     511        var threw = false; 
     512        // run it, catching exceptions and reporting them 
     513        try{ 
     514                if(fixture["setUp"]){ fixture.setUp(this); } 
     515                if(fixture["runTest"]){  
     516                        var ret = fixture.runTest(this);  
     517                        // if we get a deferred back from the test runner, we know we're 
     518                        // gonna wait for an async result. It's up to the test code to trap 
     519                        // errors and give us an errback or callback. 
     520                        if(ret instanceof this.Deferred){ 
     521 
     522                                tg.inFlight++; 
     523 
     524                                ret.addErrback(function(err){ 
     525                                        tests._handleFailure(groupName, fixture, err); 
     526                                }); 
     527 
     528                                var retEnd = function(){ 
     529                                        if(fixture["tearDown"]){ fixture.tearDown(tests); } 
     530                                        tg.inFlight--; 
     531                                        if((!tg.inFlight)&&(tg.iterated)){ 
     532                                                tests._groupFinished(groupName, (!tg.failures)); 
     533                                        } 
     534                                        tests._testFinished(groupName, fixture, ret.results[0]); 
     535                                } 
     536 
     537                                var timer = setTimeout(function(){ 
     538                                        ret.cancel(); 
     539                                        retEnd(); 
     540                                }, fixture["timeout"]||500); 
     541 
     542                                ret.addBoth(function(arg){ 
     543                                        clearTimeout(timer); 
     544                                        retEnd(); 
     545                                }); 
     546                                return ret; 
     547                        } 
     548                } 
     549                if(fixture["tearDown"]){ fixture.tearDown(this); } 
     550        }catch(e){ 
     551                threw = true; 
     552                this._handleFailure(groupName, fixture, e); 
     553        } 
     554        this._testFinished(groupName, fixture, (!threw)); 
     555} 
     556 
     557tests._testId = 0; 
    260558tests.runGroup = function(/*String*/ groupName, /*Integer*/ idx){ 
    261559        // summary: 
    262560        //              runs the specified test group 
    263561 
     562        // FIXME: we need group status and individual tests to be async-able. Would 
     563        // preferr, perhaps, avoiding use of the full Deferred system, but we'll 
     564        // see what we can get away w/. 
    264565        var tg = this._groups[groupName]; 
    265566        if(tg.skip === true){ return; } 
    266567        if(this._isArray(tg)){ 
     568                tg.inFlight = 0; 
     569                tg.iterated = false; 
     570                tg.failures = 0; 
     571                tests._groupStarted(groupName); 
    267572                this._setupGroupForRun(groupName, idx); 
    268                 this.debug(this._line, "\nGROUP", "\""+groupName+"\"", "has", tg.length, "test"+((tg.length > 1) ? "s" : "")+" to run"); 
    269573                for(var y=(idx||0); y<tg.length; y++){ 
    270574                        if(this._paused){ 
     
    273577                                return; 
    274578                        } 
    275                         var tt = tg[y]; 
    276                         var threw = false; 
    277                         // run it, catching exceptions and reporting them 
    278                         try{ 
    279                                 if(tt["setUp"]){ tt.setUp(this); } 
    280                                 if(tt["runTest"]){ tt.runTest(this); } 
    281                                 if(tt["tearDown"]){ tt.tearDown(this); } 
    282                                 this._passedCount++; 
    283                         }catch(e){ 
    284                                 var threw = true; 
    285                                 this.debug("FAILED test:", y); 
    286                                 // mostly borrowed from JUM 
    287                                 var out = ""; 
    288                                 if(e instanceof this._AssertFailure){ 
    289                                         this._failureCount++; 
    290                                         if(e["fileName"]){ out += e.fileName + ':'; } 
    291                                         if(e["lineNumber"]){ out += e.lineNumber + ' '; } 
    292                                         out += e.message; 
    293                                         this.debug("\t_AssertFailure:", out); 
    294                                 }else{ 
    295                                         this._errorCount++; 
    296                                 } 
    297                                 // this.debug(e); 
    298                                 if(tt.runTest["toSource"]){ 
    299                                         var ss = tt.runTest.toSource(); 
    300                                         // var ss = tt.runTest.toSource().split("{", 2)[1]; 
    301                                         // ss = ss.substr(0, ss.lastIndexOf("}")); 
    302                                         this.debug("\tERROR IN:\n\t\t", ss); 
    303                                 } 
    304                                 /* 
    305                                 for(var x in e){ 
    306                                         this.debug("\t", x, ":", x[e]); 
    307                                 } 
    308                                 throw e; 
    309                                 */ 
    310                         } 
    311                         if(!threw){ 
    312                                 this.debug("PASSED test:", y); 
    313                         } 
     579                        tests._runFixture(groupName, tg[y]); 
     580                } 
     581                tg.iterated = true; 
     582                if(!tg.inFlight){ 
     583                        tests._groupFinished(groupName, (!tg.failures)); 
    314584                } 
    315585        } 
  • dojo/trunk/tests/_base.js

    r7580 r7589  
    5353); 
    5454 
    55 tests.add("tests._base",  
    56         [ 
    57                 function dojoIsAvailable(t){ 
    58                         t.isTrue(testGlobal["dojo"]); 
    59                 } 
    60         ] 
    61 ); 
     55if(this["dojo"]){ 
     56        tests.add("tests._base",  
     57                [ 
     58                        function dojoIsAvailable(t){ 
     59                                t.isTrue(testGlobal["dojo"]); 
     60                        } 
     61                ] 
     62        ); 
     63} 
     64 
     65if(this["setTimeout"]){ 
     66        // a stone-stupid async test 
     67        tests.add("tests.async",  
     68                [ 
     69                        { 
     70                                name: "deferredSuccess", 
     71                                runTest: function(t){ 
     72                                        var d = new tests.Deferred(); 
     73                                        setTimeout(function(){ 
     74                                                d.callback(true); 
     75                                        }, 50); 
     76                                        return d; 
     77                                } 
     78                        }, 
     79                        { 
     80                                name: "deferredFailure", 
     81                                runTest: function(t){ 
     82                                        var d = new tests.Deferred(); 
     83                                        setTimeout(function(){ 
     84                                                d.errback(new Error("hrm...")); 
     85                                        }, 50); 
     86                                        return d; 
     87                                } 
     88                        }, 
     89                        { 
     90                                name: "timeoutFailure", 
     91                                timeout: 50, 
     92                                runTest: function(t){ 
     93                                        // timeout of 50 
     94                                        var d = new tests.Deferred(); 
     95                                        setTimeout(function(){ 
     96                                                d.callback(true); 
     97                                        }, 100); 
     98                                        return d; 
     99                                } 
     100                        } 
     101                ] 
     102        ); 
     103} 
  • dojo/trunk/tests/_browserRunner.js

    r7580 r7589  
    22        dojo.provide("tests._browserRunner"); 
    33} 
     4 
     5// FIXME: need to add prompting for monkey-do testing 
     6// FIXME: need to implement progress bar 
     7// FIXME: need to implement errors in progress bar 
     8// FIXME: need to implement run/log tabs 
    49 
    510(function(){ 
     
    6469                tds[1].innerHTML = group; 
    6570                tds[2].innerHTML = ""; 
    66                 tds[3].innerHTML = ""; 
    6771 
    6872                tb.appendChild(tg); 
     
    7781                tds[1].innerHTML = fixture.name; 
    7882                tds[2].innerHTML = ""; 
    79                 tds[3].innerHTML = ""; 
    8083 
    8184                var nn = (cgn.__lastFixture||cgn.__groupNode).nextSibling; 
     
    8992 
    9093        var getFixtureNode = function(group, fixture){ 
     94                if(groupNodes[group]){ 
     95                        return groupNodes[group][fixture.name]; 
     96                } 
     97                return null; 
    9198        } 
    9299 
    93100        var getGroupNode = function(group){ 
    94101                if(groupNodes[group]){ 
    95                 } 
     102                        return groupNodes[group].__groupNode; 
     103                } 
     104                return null; 
    96105        } 
    97106 
     
    123132        tests._testRegistered = tests._updateTestList; 
    124133 
    125         // FIXME: need to add prompting for monkey-do testing 
     134        tests._groupStarted = function(group){ 
     135                getGroupNode(group).className = "inProgress"; 
     136        } 
     137 
     138        tests._groupFinished = function(group, success){ 
     139                getGroupNode(group).className = (success) ? "success" : "failure"; 
     140        } 
     141