diff options
Diffstat (limited to 'includes/js/util/doh')
-rw-r--r-- | includes/js/util/doh/LICENSE | 195 | ||||
-rw-r--r-- | includes/js/util/doh/README | 12 | ||||
-rw-r--r-- | includes/js/util/doh/_browserRunner.js | 465 | ||||
-rw-r--r-- | includes/js/util/doh/_rhinoRunner.js | 17 | ||||
-rw-r--r-- | includes/js/util/doh/_sounds/LICENSE | 10 | ||||
-rw-r--r-- | includes/js/util/doh/_sounds/doh.wav | bin | 0 -> 2878 bytes | |||
-rw-r--r-- | includes/js/util/doh/_sounds/dohaaa.wav | bin | 0 -> 14318 bytes | |||
-rw-r--r-- | includes/js/util/doh/_sounds/woohoo.wav | bin | 0 -> 3814 bytes | |||
-rw-r--r-- | includes/js/util/doh/runner.html | 283 | ||||
-rw-r--r-- | includes/js/util/doh/runner.js | 948 | ||||
-rw-r--r-- | includes/js/util/doh/runner.sh | 3 | ||||
-rw-r--r-- | includes/js/util/doh/small_logo.png | bin | 0 -> 1893 bytes |
12 files changed, 1933 insertions, 0 deletions
diff --git a/includes/js/util/doh/LICENSE b/includes/js/util/doh/LICENSE new file mode 100644 index 0000000..3fa2720 --- /dev/null +++ b/includes/js/util/doh/LICENSE @@ -0,0 +1,195 @@ +Dojo is available under *either* the terms of the modified BSD license *or* the +Academic Free License version 2.1. As a recipient of Dojo, you may choose which +license to receive this code under (except as noted in per-module LICENSE +files). Some modules may not be the copyright of the Dojo Foundation. These +modules contain explicit declarations of copyright in both the LICENSE files in +the directories in which they reside and in the code itself. No external +contributions are allowed under licenses which are fundamentally incompatible +with the AFL or BSD licenses that Dojo is distributed under. + +The text of the AFL and BSD licenses is reproduced below. + +------------------------------------------------------------------------------- +The "New" BSD License: +********************** + +Copyright (c) 2005-2008, The Dojo Foundation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the Dojo Foundation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- +The Academic Free License, v. 2.1: +********************************** + +This Academic Free License (the "License") applies to any original work of +authorship (the "Original Work") whose owner (the "Licensor") has placed the +following notice immediately following the copyright notice for the Original +Work: + +Licensed under the Academic Free License version 2.1 + +1) Grant of Copyright License. Licensor hereby grants You a world-wide, +royalty-free, non-exclusive, perpetual, sublicenseable license to do the +following: + +a) to reproduce the Original Work in copies; + +b) to prepare derivative works ("Derivative Works") based upon the Original +Work; + +c) to distribute copies of the Original Work and Derivative Works to the +public; + +d) to perform the Original Work publicly; and + +e) to display the Original Work publicly. + +2) Grant of Patent License. Licensor hereby grants You a world-wide, +royalty-free, non-exclusive, perpetual, sublicenseable license, under patent +claims owned or controlled by the Licensor that are embodied in the Original +Work as furnished by the Licensor, to make, use, sell and offer for sale the +Original Work and Derivative Works. + +3) Grant of Source Code License. The term "Source Code" means the preferred +form of the Original Work for making modifications to it and all available +documentation describing how to modify the Original Work. Licensor hereby +agrees to provide a machine-readable copy of the Source Code of the Original +Work along with each copy of the Original Work that Licensor distributes. +Licensor reserves the right to satisfy this obligation by placing a +machine-readable copy of the Source Code in an information repository +reasonably calculated to permit inexpensive and convenient access by You for as +long as Licensor continues to distribute the Original Work, and by publishing +the address of that information repository in a notice immediately following +the copyright notice that applies to the Original Work. + +4) Exclusions From License Grant. Neither the names of Licensor, nor the names +of any contributors to the Original Work, nor any of their trademarks or +service marks, may be used to endorse or promote products derived from this +Original Work without express prior written permission of the Licensor. Nothing +in this License shall be deemed to grant any rights to trademarks, copyrights, +patents, trade secrets or any other intellectual property of Licensor except as +expressly stated herein. No patent license is granted to make, use, sell or +offer to sell embodiments of any patent claims other than the licensed claims +defined in Section 2. No right is granted to the trademarks of Licensor even if +such marks are included in the Original Work. Nothing in this License shall be +interpreted to prohibit Licensor from licensing under different terms from this +License any Original Work that Licensor otherwise would have a right to +license. + +5) This section intentionally omitted. + +6) Attribution Rights. You must retain, in the Source Code of any Derivative +Works that You create, all copyright, patent or trademark notices from the +Source Code of the Original Work, as well as any notices of licensing and any +descriptive text identified therein as an "Attribution Notice." You must cause +the Source Code for any Derivative Works that You create to carry a prominent +Attribution Notice reasonably calculated to inform recipients that You have +modified the Original Work. + +7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that +the copyright in and to the Original Work and the patent rights granted herein +by Licensor are owned by the Licensor or are sublicensed to You under the terms +of this License with the permission of the contributor(s) of those copyrights +and patent rights. Except as expressly stated in the immediately proceeding +sentence, the Original Work is provided under this License on an "AS IS" BASIS +and WITHOUT WARRANTY, either express or implied, including, without limitation, +the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. +This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No +license to Original Work is granted hereunder except under this disclaimer. + +8) Limitation of Liability. Under no circumstances and under no legal theory, +whether in tort (including negligence), contract, or otherwise, shall the +Licensor be liable to any person for any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License +or the use of the Original Work including, without limitation, damages for loss +of goodwill, work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses. This limitation of liability shall not +apply to liability for death or personal injury resulting from Licensor's +negligence to the extent applicable law prohibits such limitation. Some +jurisdictions do not allow the exclusion or limitation of incidental or +consequential damages, so this exclusion and limitation may not apply to You. + +9) Acceptance and Termination. If You distribute copies of the Original Work or +a Derivative Work, You must make a reasonable effort under the circumstances to +obtain the express assent of recipients to the terms of this License. Nothing +else but this License (or another written agreement between Licensor and You) +grants You permission to create Derivative Works based upon the Original Work +or to exercise any of the rights granted in Section 1 herein, and any attempt +to do so except under the terms of this License (or another written agreement +between Licensor and You) is expressly prohibited by U.S. copyright law, the +equivalent laws of other countries, and by international treaty. Therefore, by +exercising any of the rights granted to You in Section 1 herein, You indicate +Your acceptance of this License and all of its terms and conditions. + +10) Termination for Patent Action. This License shall terminate automatically +and You may no longer exercise any of the rights granted to You by this License +as of the date You commence an action, including a cross-claim or counterclaim, +against Licensor or any licensee alleging that the Original Work infringes a +patent. This termination provision shall not apply for an action alleging +patent infringement by combinations of the Original Work with other software or +hardware. + +11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this +License may be brought only in the courts of a jurisdiction wherein the +Licensor resides or in which Licensor conducts its primary business, and under +the laws of that jurisdiction excluding its conflict-of-law provisions. The +application of the United Nations Convention on Contracts for the International +Sale of Goods is expressly excluded. Any use of the Original Work outside the +scope of this License or after its termination shall be subject to the +requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et +seq., the equivalent laws of other countries, and international treaty. This +section shall survive the termination of this License. + +12) Attorneys Fees. In any action to enforce the terms of this License or +seeking damages relating thereto, the prevailing party shall be entitled to +recover its costs and expenses, including, without limitation, reasonable +attorneys' fees and costs incurred in connection with such action, including +any appeal of such action. This section shall survive the termination of this +License. + +13) Miscellaneous. This License represents the complete agreement concerning +the subject matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent necessary to +make it enforceable. + +14) Definition of "You" in This License. "You" throughout this License, whether +in upper or lower case, means an individual or a legal entity exercising rights +under, and complying with all of the terms of, this License. For legal +entities, "You" includes any entity that controls, is controlled by, or is +under common control with you. For purposes of this definition, "control" means +(i) the power, direct or indirect, to cause the direction or management of such +entity, whether by contract or otherwise, or (ii) ownership of fifty percent +(50%) or more of the outstanding shares, or (iii) beneficial ownership of such +entity. + +15) Right to Use. You may use the Original Work in all ways not otherwise +restricted or conditioned by this License or by law, and Licensor promises not +to interfere with or be responsible for such uses by You. + +This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved. +Permission is hereby granted to copy and distribute this license without +modification. This license may not be modified without the express written +permission of its copyright owner. diff --git a/includes/js/util/doh/README b/includes/js/util/doh/README new file mode 100644 index 0000000..c4439dd --- /dev/null +++ b/includes/js/util/doh/README @@ -0,0 +1,12 @@ +DOH may be run standalone by issuing a command like the following: + +java -jar ../shrinksafe/custom_rhino.jar runner.js testModule=tests.colors + +where the testModule argument is optional and custom_rhino.jar is just a +convenient copy of the Rhino JavaScript engine -- the custom patch is not +required. + +Optional arguments include: + * dojoUrl - specifies the location of dojo.js + * testUrl - specifies a Javascript file to load with initialization code + * testModule - specifies a test module in the dojo package namespace diff --git a/includes/js/util/doh/_browserRunner.js b/includes/js/util/doh/_browserRunner.js new file mode 100644 index 0000000..9e9e3f3 --- /dev/null +++ b/includes/js/util/doh/_browserRunner.js @@ -0,0 +1,465 @@ +if(window["dojo"]){ + dojo.provide("doh._browserRunner"); +} + +// FIXME: need to add prompting for monkey-do testing +// FIXME: need to implement progress bar +// FIXME: need to implement errors in progress bar + +(function(){ + if(window.parent == window){ + // we're the top-dog window. + + // borrowed from Dojo, etc. + var byId = function(id){ + return document.getElementById(id); + } + + var _addOnEvt = function( type, // string + refOrName, // function or string + scope){ // object, defaults is window + + if(!scope){ scope = window; } + + var funcRef = refOrName; + if(typeof refOrName == "string"){ + funcRef = scope[refOrName]; + } + var enclosedFunc = function(){ return funcRef.apply(scope, arguments); }; + + if((window["dojo"])&&(type == "load")){ + dojo.addOnLoad(enclosedFunc); + }else{ + if(window["attachEvent"]){ + window.attachEvent("on"+type, enclosedFunc); + }else if(window["addEventListener"]){ + window.addEventListener(type, enclosedFunc, false); + }else if(document["addEventListener"]){ + document.addEventListener(type, enclosedFunc, false); + } + } + }; + + // + // Over-ride or implement base runner.js-provided methods + // + var _logBacklog = []; + var sendToLogPane = function(args, skip){ + var msg = ""; + for(var x=0; x<args.length; x++){ + msg += " "+args[x]; + } + // workarounds for IE. Wheeee!!! + msg = msg.replace("\t", " "); + msg = msg.replace(" ", " "); + msg = msg.replace("\n", "<br> "); + if(!byId("logBody")){ + _logBacklog.push(msg); + return; + }else if((_logBacklog.length)&&(!skip)){ + var tm; + while(tm=_logBacklog.shift()){ + sendToLogPane(tm, true); + } + } + var tn = document.createElement("div"); + tn.innerHTML = msg; + byId("logBody").appendChild(tn); + } + + doh._init = (function(oi){ + return function(){ + var lb = byId("logBody"); + if(lb){ + // clear the console before each run + while(lb.firstChild){ + lb.removeChild(lb.firstChild); + } + } + oi.apply(doh, arguments); + } + })(doh._init); + + if(this["opera"] && opera.postError){ + doh.debug = function(){ + var msg = ""; + for(var x=0; x<arguments.length; x++){ + msg += " "+arguments[x]; + } + sendToLogPane([msg]); + opera.postError("DEBUG:"+msg); + } + }else if(window["console"]){ + if(console.info){ + doh.debug = function(){ + sendToLogPane.call(window, arguments); + console.debug.apply(console, arguments); + } + }else{ + doh.debug = function(){ + var msg = ""; + for(var x=0; x<arguments.length; x++){ + msg += " "+arguments[x]; + } + sendToLogPane([msg]); + console.log("DEBUG:"+msg); + } + } + }else{ + doh.debug = function(){ + sendToLogPane.call(window, arguments); + } + } + + var loaded = false; + var groupTemplate = null; + var testTemplate = null; + + var groupNodes = {}; + + var _groupTogglers = {}; + + var _getGroupToggler = function(group, toggle){ + if(_groupTogglers[group]){ return _groupTogglers[group]; } + var rolledUp = true; + return _groupTogglers[group] = function(evt, forceOpen){ + var nodes = groupNodes[group].__items; + if(rolledUp||forceOpen){ + rolledUp = false; + for(var x=0; x<nodes.length; x++){ + nodes[x].style.display = ""; + } + toggle.innerHTML = "6"; + }else{ + rolledUp = true; + for(var x=0; x<nodes.length; x++){ + nodes[x].style.display = "none"; + } + toggle.innerHTML = "4"; + } + }; + } + + var addGroupToList = function(group){ + if(!byId("testList")){ return; } + var tb = byId("testList").tBodies[0]; + var tg = groupTemplate.cloneNode(true); + var tds = tg.getElementsByTagName("td"); + var toggle = tds[0]; + toggle.onclick = _getGroupToggler(group, toggle); + var cb = tds[1].getElementsByTagName("input")[0]; + cb.group = group; + cb.onclick = function(evt){ + doh._groups[group].skip = (!this.checked); + } + tds[2].innerHTML = group; + tds[3].innerHTML = ""; + + tb.appendChild(tg); + return tg; + } + + var addFixtureToList = function(group, fixture){ + if(!testTemplate){ return; } + var cgn = groupNodes[group]; + if(!cgn["__items"]){ cgn.__items = []; } + var tn = testTemplate.cloneNode(true); + var tds = tn.getElementsByTagName("td"); + + tds[2].innerHTML = fixture.name; + tds[3].innerHTML = ""; + + var nn = (cgn.__lastFixture||cgn.__groupNode).nextSibling; + if(nn){ + nn.parentNode.insertBefore(tn, nn); + }else{ + cgn.__groupNode.parentNode.appendChild(tn); + } + // FIXME: need to make group display toggleable!! + tn.style.display = "none"; + cgn.__items.push(tn); + return cgn.__lastFixture = tn; + } + + var getFixtureNode = function(group, fixture){ + if(groupNodes[group]){ + return groupNodes[group][fixture.name]; + } + return null; + } + + var getGroupNode = function(group){ + if(groupNodes[group]){ + return groupNodes[group].__groupNode; + } + return null; + } + + var updateBacklog = []; + doh._updateTestList = function(group, fixture, unwindingBacklog){ + if(!loaded){ + if(group && fixture){ + updateBacklog.push([group, fixture]); + } + return; + }else if((updateBacklog.length)&&(!unwindingBacklog)){ + var tr; + while(tr=updateBacklog.shift()){ + doh._updateTestList(tr[0], tr[1], true); + } + } + if(group && fixture){ + if(!groupNodes[group]){ + groupNodes[group] = { + "__groupNode": addGroupToList(group) + }; + } + if(!groupNodes[group][fixture.name]){ + groupNodes[group][fixture.name] = addFixtureToList(group, fixture) + } + } + } + + doh._testRegistered = doh._updateTestList; + + doh._groupStarted = function(group){ + // console.debug("_groupStarted", group); + var gn = getGroupNode(group); + if(gn){ + gn.className = "inProgress"; + } + } + + doh._groupFinished = function(group, success){ + // console.debug("_groupFinished", group); + var gn = getGroupNode(group); + if(gn){ + gn.className = (success) ? "success" : "failure"; + } + } + + doh._testStarted = function(group, fixture){ + // console.debug("_testStarted", group, fixture.name); + var fn = getFixtureNode(group, fixture); + if(fn){ + fn.className = "inProgress"; + } + } + + var _nameTimes = {}; + var _playSound = function(name){ + if(byId("hiddenAudio") && byId("audio") && byId("audio").checked){ + // console.debug("playing:", name); + var nt = _nameTimes[name]; + // only play sounds once every second or so + if((!nt)||(((new Date)-nt) > 700)){ + _nameTimes[name] = new Date(); + var tc = document.createElement("span"); + byId("hiddenAudio").appendChild(tc); + tc.innerHTML = '<embed src="_sounds/'+name+'.wav" autostart="true" loop="false" hidden="true" width="1" height="1"></embed>'; + } + } + } + + doh._testFinished = function(group, fixture, success){ + var fn = getFixtureNode(group, fixture); + if(fn){ + fn.getElementsByTagName("td")[3].innerHTML = (fixture.endTime-fixture.startTime)+"ms"; + fn.className = (success) ? "success" : "failure"; + + if(!success){ + _playSound("doh"); + var gn = getGroupNode(group); + if(gn){ + gn.className = "failure"; + _getGroupToggler(group)(null, true); + } + } + } + this.debug(((success) ? "PASSED" : "FAILED"), "test:", fixture.name); + } + + // FIXME: move implementation to _browserRunner? + doh.registerUrl = function( /*String*/ group, + /*String*/ url, + /*Integer*/ timeout){ + var tg = new String(group); + this.register(group, { + name: url, + setUp: function(){ + doh.currentGroupName = tg; + doh.currentGroup = this; + doh.currentUrl = url; + this.d = new doh.Deferred(); + doh.currentTestDeferred = this.d; + showTestPage(); + byId("testBody").src = url; + }, + timeout: timeout||10000, // 10s + // timeout: timeout||1000, // 10s + runTest: function(){ + // FIXME: implement calling into the url's groups here!! + return this.d; + }, + tearDown: function(){ + doh.currentGroupName = null; + doh.currentGroup = null; + doh.currentTestDeferred = null; + doh.currentUrl = null; + // this.d.errback(false); + // byId("testBody").src = "about:blank"; + showLogPage(); + } + }); + } + + // + // Utility code for runner.html + // + // var isSafari = navigator.appVersion.indexOf("Safari") >= 0; + var tabzidx = 1; + var _showTab = function(toShow, toHide){ + // FIXME: I don't like hiding things this way. + byId(toHide).style.display = "none"; + with(byId(toShow).style){ + display = ""; + zIndex = ++tabzidx; + } + } + + showTestPage = function(){ + _showTab("testBody", "logBody"); + } + + showLogPage = function(){ + _showTab("logBody", "testBody"); + } + + var runAll = true; + toggleRunAll = function(){ + // would be easier w/ query...sigh + runAll = (!runAll); + if(!byId("testList")){ return; } + var tb = byId("testList").tBodies[0]; + var inputs = tb.getElementsByTagName("input"); + var x=0; var tn; + while(tn=inputs[x++]){ + tn.checked = runAll; + doh._groups[tn.group].skip = (!runAll); + } + } + + var listHeightTimer = null; + var setListHeight = function(){ + if(listHeightTimer){ + clearTimeout(listHeightTimer); + } + var tl = byId("testList"); + if(!tl){ return; } + listHeightTimer = setTimeout(function(){ + tl.style.display = "none"; + tl.style.display = ""; + + }, 10); + } + + _addOnEvt("resize", setListHeight); + _addOnEvt("load", setListHeight); + _addOnEvt("load", function(){ + if(loaded){ return; } + loaded = true; + groupTemplate = byId("groupTemplate"); + if(!groupTemplate){ + // make sure we've got an ammenable DOM structure + return; + } + groupTemplate.parentNode.removeChild(groupTemplate); + groupTemplate.style.display = ""; + testTemplate = byId("testTemplate"); + testTemplate.parentNode.removeChild(testTemplate); + testTemplate.style.display = ""; + doh._updateTestList(); + }); + + _addOnEvt("load", + function(){ + doh._onEnd = function(){ + if(doh._failureCount == 0){ + doh.debug("WOOHOO!!"); + _playSound("woohoo"); + }else{ + console.debug("doh._failureCount:", doh._failureCount); + } + if(byId("play")){ + toggleRunning(); + } + } + if(!byId("play")){ + // make sure we've got an ammenable DOM structure + return; + } + var isRunning = false; + var toggleRunning = function(){ + // ugg, this would be so much better w/ dojo.query() + if(isRunning){ + byId("play").style.display = byId("pausedMsg").style.display = ""; + byId("playingMsg").style.display = byId("pause").style.display = "none"; + isRunning = false; + }else{ + byId("play").style.display = byId("pausedMsg").style.display = "none"; + byId("playingMsg").style.display = byId("pause").style.display = ""; + isRunning = true; + } + } + doh.run = (function(oldRun){ + return function(){ + if(!doh._currentGroup){ + toggleRunning(); + } + return oldRun.apply(doh, arguments); + } + })(doh.run); + var btns = byId("toggleButtons").getElementsByTagName("span"); + var node; var idx=0; + while(node=btns[idx++]){ + node.onclick = toggleRunning; + } + } + ); + }else{ + // we're in an iframe environment. Time to mix it up a bit. + + _doh = window.parent.doh; + var _thisGroup = _doh.currentGroupName; + var _thisUrl = _doh.currentUrl; + if(_thisGroup){ + doh._testRegistered = function(group, tObj){ + _doh._updateTestList(_thisGroup, tObj); + } + doh._onEnd = function(){ + _doh._errorCount += doh._errorCount; + _doh._failureCount += doh._failureCount; + _doh._testCount += doh._testCount; + // should we be really adding raw group counts? + _doh._groupCount += doh._groupCount; + _doh.currentTestDeferred.callback(true); + } + var otr = doh._getTestObj; + doh._getTestObj = function(){ + var tObj = otr.apply(doh, arguments); + tObj.name = _thisUrl+"::"+arguments[0]+"::"+tObj.name; + return tObj; + } + doh.debug = doh.hitch(_doh, "debug"); + doh.registerUrl = doh.hitch(_doh, "registerUrl"); + doh._testStarted = function(group, fixture){ + _doh._testStarted(_thisGroup, fixture); + } + doh._testFinished = function(g, f, s){ + _doh._testFinished(_thisGroup, f, s); + } + doh._report = function(){}; + } + } + +})(); diff --git a/includes/js/util/doh/_rhinoRunner.js b/includes/js/util/doh/_rhinoRunner.js new file mode 100644 index 0000000..ae47597 --- /dev/null +++ b/includes/js/util/doh/_rhinoRunner.js @@ -0,0 +1,17 @@ +if(this["dojo"]){ + dojo.provide("doh._rhinoRunner"); +} + +doh.debug = print; + +// Override the doh._report method to make it quit with an +// appropriate exit code in case of test failures. +(function(){ + var oldReport = doh._report; + doh._report = function(){ + oldReport.apply(doh, arguments); + if(this._failureCount > 0 || this._errorCount > 0){ + quit(1); + } + } +})(); diff --git a/includes/js/util/doh/_sounds/LICENSE b/includes/js/util/doh/_sounds/LICENSE new file mode 100644 index 0000000..e8e11d4 --- /dev/null +++ b/includes/js/util/doh/_sounds/LICENSE @@ -0,0 +1,10 @@ +License Disclaimer: + +All contents of this directory are Copyright (c) the Dojo Foundation, with the +following exceptions: +------------------------------------------------------------------------------- + +woohoo.wav, doh.wav, dohaaa.wav: + * Copyright original authors. + Copied from: + http://simpson-homer.com/homer-simpson-soundboard.html diff --git a/includes/js/util/doh/_sounds/doh.wav b/includes/js/util/doh/_sounds/doh.wav Binary files differnew file mode 100644 index 0000000..5e8a583 --- /dev/null +++ b/includes/js/util/doh/_sounds/doh.wav diff --git a/includes/js/util/doh/_sounds/dohaaa.wav b/includes/js/util/doh/_sounds/dohaaa.wav Binary files differnew file mode 100644 index 0000000..2220921 --- /dev/null +++ b/includes/js/util/doh/_sounds/dohaaa.wav diff --git a/includes/js/util/doh/_sounds/woohoo.wav b/includes/js/util/doh/_sounds/woohoo.wav Binary files differnew file mode 100644 index 0000000..eb69217 --- /dev/null +++ b/includes/js/util/doh/_sounds/woohoo.wav diff --git a/includes/js/util/doh/runner.html b/includes/js/util/doh/runner.html new file mode 100644 index 0000000..dbcd68c --- /dev/null +++ b/includes/js/util/doh/runner.html @@ -0,0 +1,283 @@ +<html>
+ <!--
+ NOTE: we are INTENTIONALLY in quirks mode. It makes it much easier to
+ get a "full screen" UI w/ straightforward CSS.
+ -->
+ <!--
+ // TODO: implement global progress bar
+ // TODO: provide a UI for prompted tests
+ -->
+ <head>
+ <title>The Dojo Unit Test Harness, $Rev$</title>
+ <script type="text/javascript">
+ window.dojoUrl = "../../dojo/dojo.js";
+ window.testUrl = "";
+ window.testModule = "";
+
+ // parse out our test URL and our Dojo URL from the query string
+ var qstr = window.location.search.substr(1);
+ if(qstr.length){
+ var qparts = qstr.split("&");
+ for(var x=0; x<qparts.length; x++){
+ var tp = qparts[x].split("=");
+ if(tp[0] == "dojoUrl"){
+ window.dojoUrl = tp[1];
+ }
+ if(tp[0] == "testUrl"){
+ window.testUrl = tp[1];
+ }
+ if(tp[0] == "testModule"){
+ window.testModule = tp[1];
+ }
+ }
+ }
+
+ document.write("<scr"+"ipt type='text/javascript' djConfig='isDebug: true' src='"+dojoUrl+"'></scr"+"ipt>");
+ </script>
+ <script type="text/javascript">
+ try{
+ dojo.require("doh.runner");
+ }catch(e){
+ document.write("<scr"+"ipt type='text/javascript' src='runner.js'></scr"+"ipt>");
+ document.write("<scr"+"ipt type='text/javascript' src='_browserRunner.js'></scr"+"ipt>");
+ }
+ if(testUrl.length){
+ document.write("<scr"+"ipt type='text/javascript' src='"+testUrl+".js'></scr"+"ipt>");
+ }
+ </script>
+ <style type="text/css">
+ @import "../../dojo/resources/dojo.css";
+ /*
+ body {
+ margin: 0px;
+ padding: 0px;
+ font-size: 13px;
+ color: #292929;
+ font-family: Myriad, Lucida Grande, Bitstream Vera Sans, Arial, Helvetica, sans-serif;
+ *font-size: small;
+ *font: x-small;
+ }
+
+ th, td {
+ font-size: 13px;
+ color: #292929;
+ font-family: Myriad, Lucida Grande, Bitstream Vera Sans, Arial, Helvetica, sans-serif;
+ font-weight: normal;
+ }
+
+ * body {
+ line-height: 1.25em;
+ }
+
+ table {
+ border-collapse: collapse;
+ }
+ */
+
+ #testLayout {
+ position: relative;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+ height: 100%;
+ border: 1px solid black;
+ border: 0px;
+ }
+
+ .tabBody {
+ margin: 0px;
+ padding: 0px;
+ /*
+ border: 1px solid black;
+ */
+ background-color: #DEDEDE;
+ border: 0px;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ overflow: auto;
+ }
+
+ #logBody {
+ padding-left: 5px;
+ padding-top: 5px;
+ font-family: Monaco, monospace;
+ font-size: 11px;
+ white-space: pre;
+ }
+
+ #progressOuter {
+ background:#e9e9e9 url("http://svn.dojotoolkit.org/dojo/dijit/trunk/themes/tundra/images/dojoTundraGradientBg.png") repeat-x 0 0;
+ /*
+ border-color: #e8e8e8;
+ */
+ }
+
+ #progressInner {
+ background: blue;
+ width: 0%;
+ position: relative;
+ left: 0px;
+ top: 0px;
+ height: 100%;
+ }
+
+ #play, #pause {
+ font-family: Webdings;
+ font-size: 1.4em;
+ border: 1px solid #DEDEDE;
+ cursor: pointer;
+ padding-right: 0.5em;
+ }
+
+ .header {
+ border: 1px solid #DEDEDE;
+ }
+
+ button.tab {
+ border-width: 1px 1px 0px 1px;
+ border-style: solid;
+ border-color: #DEDEDE;
+ margin-right: 5px;
+ }
+
+ #testListContainer {
+ /*
+ border: 1px solid black;
+ */
+ position: relative;
+ height: 99%;
+ width: 100%;
+ overflow: auto;
+ }
+
+ #testList {
+ border-collapse: collapse;
+ position: absolute;
+ left: 0px;
+ width: 100%;
+ }
+
+ #testList > tbody > tr > td {
+ border-bottom: 1px solid #DEDEDE;
+ border-right : 1px solid #DEDEDE;
+ padding: 3px;
+ }
+
+ #testListHeader th {
+ border-bottom: 1px solid #DEDEDE;
+ border-right : 1px solid #DEDEDE;
+ padding: 3px;
+ font-weight: bolder;
+ font-style: italic;
+ }
+
+ #toggleButtons {
+ float: left;
+ background-color: #DEDEDE;
+ }
+
+ tr.inProgress {
+ background-color: #85afde;
+ }
+
+ tr.success {
+ background-color: #7cdea7;
+ }
+
+ tr.failure {
+ background-color: #de827b;
+ }
+ </style>
+ </head>
+ <body>
+ <table id="testLayout" cellpadding="0" cellspacing="0" style="margin: 0;">
+ <tr valign="top" height="40">
+ <td colspan="2" id="logoBar">
+ <h3 style="margin: 5px 5px 0px 5px; float: left;">D.O.H.: The Dojo Objective Harness</h3>
+ <img src="small_logo.png" height="40" style="margin: 0px 5px 0px 5px; float: right;">
+ <span style="margin: 10px 5px 0px 5px; float: right;">
+ <input type="checkbox" id="audio" name="audio">
+ <label for="audio">sounds?</label>
+ </span>
+ </td>
+ </tr>
+ <!--
+ <tr valign="top" height="10">
+ <td colspan="2" id="progressOuter">
+ <div id="progressInner">blah</div>
+ </td>
+ </tr>
+ -->
+ <tr valign="top" height="30">
+ <td width="30%" class="header">
+ <span id="toggleButtons" onclick="doh.togglePaused();">
+ <button id="play">4</button>
+ <button id="pause" style="display: none;">;</button>
+ </span>
+ <span id="runningStatus">
+ <span id="pausedMsg">Stopped</span>
+ <span id="playingMsg" style="display: none;">Tests Running</span>
+ </span>
+ </td>
+ <td width="*" class="header" valign="bottom">
+ <button class="tab" onclick="showTestPage();">Test Page</button>
+ <button class="tab" onclick="showLogPage();">Log</button>
+ </td>
+ </tr>
+ <tr valign="top" style="border: 0; padding: 0; margin: 0;">
+ <td height="100%" style="border: 0; padding: 0; margin: 0;">
+ <div id="testListContainer">
+ <table cellpadding="0" cellspacing="0" border="0"
+ width="100%" id="testList" style="margin: 0;">
+ <thead>
+ <tr id="testListHeader" style="border: 0; padding: 0; margin: 0;" >
+ <th> </th>
+ <th width="20">
+ <input type="checkbox" checked
+ onclick="toggleRunAll();">
+ </th>
+ <th width="*" style="text-align: left;">test</th>
+ <th width="50">time</th>
+ </tr>
+ </thead>
+ <tbody valign="top">
+ <tr id="groupTemplate" style="display: none;">
+ <td style="font-family: Webdings; width: 15px;">4</td>
+ <td>
+ <input type="checkbox" checked>
+ </td>
+ <td>group name</td>
+ <td>10ms</td>
+ </tr>
+ <tr id="testTemplate" style="display: none;">
+ <td> </td>
+ <td> </td>
+ <td style="padding-left: 20px;">test name</td>
+ <td>10ms</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </td>
+ <td>
+ <div style="position: relative; width: 99%; height: 100%; top: 0px; left: 0px;">
+ <div class="tabBody"
+ style="z-index: 1;">
+<pre id="logBody"></pre>
+ </div>
+ <iframe id="testBody" class="tabBody"
+ style="z-index: 0;"></iframe>
+ <!--
+ src="http://redesign.dojotoolkit.org"></iframe>
+ -->
+ </div>
+ </td>
+ </tr>
+ </table>
+ <span id="hiddenAudio"></span>
+ </body>
+</html>
+
diff --git a/includes/js/util/doh/runner.js b/includes/js/util/doh/runner.js new file mode 100644 index 0000000..f5e47e1 --- /dev/null +++ b/includes/js/util/doh/runner.js @@ -0,0 +1,948 @@ +// FIXME: need to add async tests +// FIXME: need to handle URL wrapping and test registration/running from URLs + +// package system gunk. +try{ + dojo.provide("doh.runner"); +}catch(e){ + if(!this["doh"]){ + doh = {}; + } +} + +// +// Utility Functions and Classes +// + +doh.selfTest = false; + +doh.hitch = function(/*Object*/thisObject, /*Function|String*/method /*, ...*/){ + var args = []; + for(var x=2; x<arguments.length; x++){ + args.push(arguments[x]); + } + var fcn = ((typeof method == "string") ? thisObject[method] : method) || function(){}; + return function(){ + var ta = args.concat([]); // make a copy + for(var x=0; x<arguments.length; x++){ + ta.push(arguments[x]); + } + return fcn.apply(thisObject, ta); // Function + }; +} + +doh._mixin = function(/*Object*/ obj, /*Object*/ props){ + // summary: + // Adds all properties and methods of props to obj. This addition is + // "prototype extension safe", so that instances of objects will not + // pass along prototype defaults. + var tobj = {}; + for(var x in props){ + // the "tobj" condition avoid copying properties in "props" + // inherited from Object.prototype. For example, if obj has a custom + // toString() method, don't overwrite it with the toString() method + // that props inherited from Object.protoype + if((typeof tobj[x] == "undefined") || (tobj[x] != props[x])){ + obj[x] = props[x]; + } + } + // IE doesn't recognize custom toStrings in for..in + if( this["document"] + && document.all + && (typeof props["toString"] == "function") + && (props["toString"] != obj["toString"]) + && (props["toString"] != tobj["toString"]) + ){ + obj.toString = props.toString; + } + return obj; // Object +} + +doh.mixin = function(/*Object*/obj, /*Object...*/props){ + // summary: Adds all properties and methods of props to obj. + for(var i=1, l=arguments.length; i<l; i++){ + doh._mixin(obj, arguments[i]); + } + return obj; // Object +} + +doh.extend = function(/*Object*/ constructor, /*Object...*/ props){ + // summary: + // Adds all properties and methods of props to constructor's + // prototype, making them available to all instances created with + // constructor. + for(var i=1, l=arguments.length; i<l; i++){ + doh._mixin(constructor.prototype, arguments[i]); + } + return constructor; // Object +} + + +doh._line = "------------------------------------------------------------"; + +/* +doh._delegate = function(obj, props){ + // boodman-crockford delegation + function TMP(){}; + TMP.prototype = obj; + var tmp = new TMP(); + if(props){ + dojo.lang.mixin(tmp, props); + } + return tmp; +} +*/ + +doh.debug = function(){ + // summary: + // takes any number of arguments and sends them to whatever debugging + // or logging facility is available in this environment + + // YOUR TEST RUNNER NEEDS TO IMPLEMENT THIS +} + +doh._AssertFailure = function(msg){ + // idea for this as way of dis-ambiguating error types is from JUM. + // The JUM is dead! Long live the JUM! + + if(!(this instanceof doh._AssertFailure)){ + return new doh._AssertFailure(msg); + } + this.message = new String(msg||""); + return this; +} +doh._AssertFailure.prototype = new Error(); +doh._AssertFailure.prototype.constructor = doh._AssertFailure; +doh._AssertFailure.prototype.name = "doh._AssertFailure"; + +doh.Deferred = function(canceller){ + this.chain = []; + this.id = this._nextId(); + this.fired = -1; + this.paused = 0; + this.results = [null, null]; + this.canceller = canceller; + this.silentlyCancelled = false; +}; + +doh.extend(doh.Deferred, { + getTestCallback: function(cb, scope){ + var _this = this; + return function(){ + try{ + cb.apply(scope||dojo.global||_this, arguments); + }catch(e){ + _this.errback(e); + return; + } + _this.callback(true); + } + }, + + getFunctionFromArgs: function(){ + var a = arguments; + if((a[0])&&(!a[1])){ + if(typeof a[0] == "function"){ + return a[0]; + }else if(typeof a[0] == "string"){ + return dojo.global[a[0]]; + } + }else if((a[0])&&(a[1])){ + return doh.hitch(a[0], a[1]); + } + return null; + }, + + makeCalled: function() { + var deferred = new doh.Deferred(); + deferred.callback(); + return deferred; + }, + + _nextId: (function(){ + var n = 1; + return function(){ return n++; }; + })(), + + cancel: function(){ + if(this.fired == -1){ + if (this.canceller){ + this.canceller(this); + }else{ + this.silentlyCancelled = true; + } + if(this.fired == -1){ + this.errback(new Error("Deferred(unfired)")); + } + }else if( (this.fired == 0)&& + (this.results[0] instanceof doh.Deferred)){ + this.results[0].cancel(); + } + }, + + + _pause: function(){ + this.paused++; + }, + + _unpause: function(){ + this.paused--; + if ((this.paused == 0) && (this.fired >= 0)) { + this._fire(); + } + }, + + _continue: function(res){ + this._resback(res); + this._unpause(); + }, + + _resback: function(res){ + this.fired = ((res instanceof Error) ? 1 : 0); + this.results[this.fired] = res; + this._fire(); + }, + + _check: function(){ + if(this.fired != -1){ + if(!this.silentlyCancelled){ + throw new Error("already called!"); + } + this.silentlyCancelled = false; + return; + } + }, + + callback: function(res){ + this._check(); + this._resback(res); + }, + + errback: function(res){ + this._check(); + if(!(res instanceof Error)){ + res = new Error(res); + } + this._resback(res); + }, + + addBoth: function(cb, cbfn){ + var enclosed = this.getFunctionFromArgs(cb, cbfn); + if(arguments.length > 2){ + enclosed = doh.hitch(null, enclosed, arguments, 2); + } + return this.addCallbacks(enclosed, enclosed); + }, + + addCallback: function(cb, cbfn){ + var enclosed = this.getFunctionFromArgs(cb, cbfn); + if(arguments.length > 2){ + enclosed = doh.hitch(null, enclosed, arguments, 2); + } + return this.addCallbacks(enclosed, null); + }, + + addErrback: function(cb, cbfn){ + var enclosed = this.getFunctionFromArgs(cb, cbfn); + if(arguments.length > 2){ + enclosed = doh.hitch(null, enclosed, arguments, 2); + } + return this.addCallbacks(null, enclosed); + }, + + addCallbacks: function(cb, eb){ + this.chain.push([cb, eb]) + if(this.fired >= 0){ + this._fire(); + } + return this; + }, + + _fire: function(){ + var chain = this.chain; + var fired = this.fired; + var res = this.results[fired]; + var self = this; + var cb = null; + while (chain.length > 0 && this.paused == 0){ + // Array + var pair = chain.shift(); + var f = pair[fired]; + if(f == null){ + continue; + } + try { + res = f(res); + fired = ((res instanceof Error) ? 1 : 0); + if(res instanceof doh.Deferred){ + cb = function(res){ + self._continue(res); + } + this._pause(); + } + }catch(err){ + fired = 1; + res = err; + } + } + this.fired = fired; + this.results[fired] = res; + if((cb)&&(this.paused)){ + res.addBoth(cb); + } + } +}); + +// +// State Keeping and Reporting +// + +doh._testCount = 0; +doh._groupCount = 0; +doh._errorCount = 0; +doh._failureCount = 0; +doh._currentGroup = null; +doh._currentTest = null; +doh._paused = true; + +doh._init = function(){ + this._currentGroup = null; + this._currentTest = null; + this._errorCount = 0; + this._failureCount = 0; + this.debug(this._testCount, "tests to run in", this._groupCount, "groups"); +} + +// doh._urls = []; +doh._groups = {}; + +// +// Test Registration +// + +doh.registerTestNs = function(/*String*/ group, /*Object*/ ns){ + // summary: + // adds the passed namespace object to the list of objects to be + // searched for test groups. Only "public" functions (not prefixed + // with "_") will be added as tests to be run. If you'd like to use + // fixtures (setUp(), tearDown(), and runTest()), please use + // registerTest() or registerTests(). + for(var x in ns){ + if( (x.charAt(0) != "_") && + (typeof ns[x] == "function") ){ + this.registerTest(group, ns[x]); + } + } +} + +doh._testRegistered = function(group, fixture){ + // slot to be filled in +} + +doh._groupStarted = function(group){ + // slot to be filled in +} + +doh._groupFinished = function(group, success){ + // slot to be filled in +} + +doh._testStarted = function(group, fixture){ + // slot to be filled in +} + +doh._testFinished = function(group, fixture, success){ + // slot to be filled in +} + +doh.registerGroup = function( /*String*/ group, + /*Array||Function||Object*/ tests, + /*Function*/ setUp, + /*Function*/ tearDown){ + // summary: + // registers an entire group of tests at once and provides a setUp and + // tearDown facility for groups. If you call this method with only + // setUp and tearDown parameters, they will replace previously + // installed setUp or tearDown functions for the group with the new + // methods. + // group: + // string name of the group + // tests: + // either a function or an object or an array of functions/objects. If + // an object, it must contain at *least* a "runTest" method, and may + // also contain "setUp" and "tearDown" methods. These will be invoked + // on either side of the "runTest" method (respectively) when the test + // is run. If an array, it must contain objects matching the above + // description or test functions. + // setUp: a function for initializing the test group + // tearDown: a function for initializing the test group + if(tests){ + this.register(group, tests); + } + if(setUp){ + this._groups[group].setUp = setUp; + } + if(tearDown){ + this._groups[group].tearDown = tearDown; + } +} + +doh._getTestObj = function(group, test){ + var tObj = test; + if(typeof test == "string"){ + if(test.substr(0, 4)=="url:"){ + return this.registerUrl(group, test); + }else{ + tObj = { + name: test.replace("/\s/g", "_") + }; + tObj.runTest = new Function("t", test); + } + }else if(typeof test == "function"){ + // if we didn't get a fixture, wrap the function + tObj = { "runTest": test }; + if(test["name"]){ + tObj.name = test.name; + }else{ + try{ + var fStr = "function "; + var ts = tObj.runTest+""; + if(0 <= ts.indexOf(fStr)){ + tObj.name = ts.split(fStr)[1].split("(", 1)[0]; + } + // doh.debug(tObj.runTest.toSource()); + }catch(e){ + } + } + // FIXME: try harder to get the test name here + } + return tObj; +} + +doh.registerTest = function(/*String*/ group, /*Function||Object*/ test){ + // summary: + // add the provided test function or fixture object to the specified + // test group. + // group: + // string name of the group to add the test to + // test: + // either a function or an object. If an object, it must contain at + // *least* a "runTest" method, and may also contain "setUp" and + // "tearDown" methods. These will be invoked on either side of the + // "runTest" method (respectively) when the test is run. + if(!this._groups[group]){ + this._groupCount++; + this._groups[group] = []; + this._groups[group].inFlight = 0; + } + var tObj = this._getTestObj(group, test); + if(!tObj){ return; } + this._groups[group].push(tObj); + this._testCount++; + this._testRegistered(group, tObj); + return tObj; +} + +doh.registerTests = function(/*String*/ group, /*Array*/ testArr){ + // summary: + // registers a group of tests, treating each element of testArr as + // though it were being (along with group) passed to the registerTest + // method. + for(var x=0; x<testArr.length; x++){ + this.registerTest(group, testArr[x]); + } +} + +// FIXME: move implementation to _browserRunner? +doh.registerUrl = function( /*String*/ group, + /*String*/ url, + /*Integer*/ timeout){ + this.debug("ERROR:"); + this.debug("\tNO registerUrl() METHOD AVAILABLE."); + // this._urls.push(url); +} + +doh.registerString = function(group, str){ +} + +// FIXME: remove the doh.add alias SRTL. +doh.register = doh.add = function(groupOrNs, testOrNull){ + // summary: + // "magical" variant of registerTests, registerTest, and + // registerTestNs. Will accept the calling arguments of any of these + // methods and will correctly guess the right one to register with. + if( (arguments.length == 1)&& + (typeof groupOrNs == "string") ){ + if(groupOrNs.substr(0, 4)=="url:"){ + this.registerUrl(groupOrNs); + }else{ + this.registerTest("ungrouped", groupOrNs); + } + } + if(arguments.length == 1){ + this.debug("invalid args passed to doh.register():", groupOrNs, ",", testOrNull); + return; + } + if(typeof testOrNull == "string"){ + if(testOrNull.substr(0, 4)=="url:"){ + this.registerUrl(testOrNull); + }else{ + this.registerTest(groupOrNs, testOrNull); + } + // this.registerTestNs(groupOrNs, testOrNull); + return; + } + if(doh._isArray(testOrNull)){ + this.registerTests(groupOrNs, testOrNull); + return; + } + this.registerTest(groupOrNs, testOrNull); +} + +// +// Assertions and In-Test Utilities +// + +doh.t = doh.assertTrue = function(/*Object*/ condition){ + // summary: + // is the passed item "truthy"? + if(arguments.length != 1){ + throw doh._AssertFailure("assertTrue failed because it was not passed exactly 1 argument"); + } + if(!eval(condition)){ + throw doh._AssertFailure("assertTrue('" + condition + "') failed"); + } +} + +doh.f = doh.assertFalse = function(/*Object*/ condition){ + // summary: + // is the passed item "falsey"? + if(arguments.length != 1){ + throw doh._AssertFailure("assertFalse failed because it was not passed exactly 1 argument"); + } + if(eval(condition)){ + throw doh._AssertFailure("assertFalse('" + condition + "') failed"); + } +} + +doh.e = doh.assertError = function(/*Error object*/expectedError, /*Object*/scope, /*String*/functionName, /*Array*/args){ + // summary: + // Test for a certain error to be thrown by the given function. + // example: + // t.assertError(dojox.data.QueryReadStore.InvalidAttributeError, store, "getValue", [item, "NOT THERE"]); + // t.assertError(dojox.data.QueryReadStore.InvalidItemError, store, "getValue", ["not an item", "NOT THERE"]); + try{ + scope[functionName].apply(scope, args); + }catch (e){ + if(e instanceof expectedError){ + return true; + }else{ + throw new doh._AssertFailure("assertError() failed:\n\texpected error\n\t\t"+expectedError+"\n\tbut got\n\t\t"+e+"\n\n"); + } + } + throw new doh._AssertFailure("assertError() failed:\n\texpected error\n\t\t"+expectedError+"\n\tbut no error caught\n\n"); +} + + +doh.is = doh.assertEqual = function(/*Object*/ expected, /*Object*/ actual){ + // summary: + // are the passed expected and actual objects/values deeply + // equivalent? + + // Compare undefined always with three equal signs, because undefined==null + // is true, but undefined===null is false. + if((expected === undefined)&&(actual === undefined)){ + return true; + } + if(arguments.length < 2){ + throw doh._AssertFailure("assertEqual failed because it was not passed 2 arguments"); + } + if((expected === actual)||(expected == actual)){ + return true; + } + if( (this._isArray(expected) && this._isArray(actual))&& + (this._arrayEq(expected, actual)) ){ + return true; + } + if( ((typeof expected == "object")&&((typeof actual == "object")))&& + (this._objPropEq(expected, actual)) ){ + return true; + } + throw new doh._AssertFailure("assertEqual() failed:\n\texpected\n\t\t"+expected+"\n\tbut got\n\t\t"+actual+"\n\n"); +} + +doh._arrayEq = function(expected, actual){ + if(expected.length != actual.length){ return false; } + // FIXME: we're not handling circular refs. Do we care? + for(var x=0; x<expected.length; x++){ + if(!doh.assertEqual(expected[x], actual[x])){ return false; } + } + return true; +} + +doh._objPropEq = function(expected, actual){ + if(expected instanceof Date){ + return actual instanceof Date && expected.getTime()==actual.getTime(); + } + // Make sure ALL THE SAME properties are in both objects! + for(var x in actual){ // Lets check "actual" here, expected is checked below. + if(expected[x] === undefined){ + return false; + } + }; + + for(var x in expected){ + if(!doh.assertEqual(expected[x], actual[x])){ + return false; + } + } + return true; +} + +doh._isArray = function(it){ + return (it && it instanceof Array || typeof it == "array" || (dojo["NodeList"] !== undefined && it instanceof dojo.NodeList)); +} + +// +// Runner-Wrapper +// + +doh._setupGroupForRun = function(/*String*/ groupName, /*Integer*/ idx){ + var tg = this._groups[groupName]; + this.debug(this._line); + this.debug("GROUP", "\""+groupName+"\"", "has", tg.length, "test"+((tg.length > 1) ? "s" : "")+" to run"); +} + +doh._handleFailure = function(groupName, fixture, e){ + // this.debug("FAILED test:", fixture.name); + // mostly borrowed from JUM + this._groups[groupName].failures++; + var out = ""; + if(e instanceof this._AssertFailure){ + this._failureCount++; + if(e["fileName"]){ out += e.fileName + ':'; } + if(e["lineNumber"]){ out += e.lineNumber + ' '; } + out += e+": "+e.message; + this.debug("\t_AssertFailure:", out); + }else{ + this._errorCount++; + } + this.debug(e); + if(fixture.runTest["toSource"]){ + var ss = fixture.runTest.toSource(); + this.debug("\tERROR IN:\n\t\t", ss); + }else{ + this.debug("\tERROR IN:\n\t\t", fixture.runTest); + } + + if(e.rhinoException){ + e.rhinoException.printStackTrace(); + }else if(e.javaException){ + e.javaException.printStackTrace(); + } +} + +try{ + setTimeout(function(){}, 0); +}catch(e){ + setTimeout = function(func){ + return func(); + } +} + +doh._runFixture = function(groupName, fixture){ + var tg = this._groups[groupName]; + this._testStarted(groupName, fixture); + var threw = false; + var err = null; + // run it, catching exceptions and reporting them + try{ + // let doh reference "this.group.thinger..." which can be set by + // another test or group-level setUp function + fixture.group = tg; + // only execute the parts of the fixture we've got + if(fixture["setUp"]){ fixture.setUp(this); } + if(fixture["runTest"]){ // should we error out of a fixture doesn't have a runTest? + fixture.startTime = new Date(); + var ret = fixture.runTest(this); + fixture.endTime = new Date(); + // if we get a deferred back from the test runner, we know we're + // gonna wait for an async result. It's up to the test code to trap + // errors and give us an errback or callback. + if(ret instanceof doh.Deferred){ + + tg.inFlight++; + ret.groupName = groupName; + ret.fixture = fixture; + + ret.addErrback(function(err){ + doh._handleFailure(groupName, fixture, err); + }); + + var retEnd = function(){ + if(fixture["tearDown"]){ fixture.tearDown(doh); } + tg.inFlight--; + if((!tg.inFlight)&&(tg.iterated)){ + doh._groupFinished(groupName, (!tg.failures)); + } + doh._testFinished(groupName, fixture, ret.results[0]); + if(doh._paused){ + doh.run(); + } + } + + var timer = setTimeout(function(){ + // ret.cancel(); + // retEnd(); + ret.errback(new Error("test timeout in "+fixture.name.toString())); + }, fixture["timeout"]||1000); + + ret.addBoth(function(arg){ + clearTimeout(timer); + retEnd(); + }); + if(ret.fired < 0){ + doh.pause(); + } + return ret; + } + } + if(fixture["tearDown"]){ fixture.tearDown(this); } + }catch(e){ + threw = true; + err = e; + if(!fixture.endTime){ + fixture.endTime = new Date(); + } + } + var d = new doh.Deferred(); + setTimeout(this.hitch(this, function(){ + if(threw){ + this._handleFailure(groupName, fixture, err); + } + this._testFinished(groupName, fixture, (!threw)); + + if((!tg.inFlight)&&(tg.iterated)){ + doh._groupFinished(groupName, (!tg.failures)); + }else if(tg.inFlight > 0){ + setTimeout(this.hitch(this, function(){ + doh.runGroup(groupName); // , idx); + }), 100); + this._paused = true; + } + if(doh._paused){ + doh.run(); + } + }), 30); + doh.pause(); + return d; +} + +doh._testId = 0; +doh.runGroup = function(/*String*/ groupName, /*Integer*/ idx){ + // summary: + // runs the specified test group + + // the general structure of the algorithm is to run through the group's + // list of doh, checking before and after each of them to see if we're in + // a paused state. This can be caused by the test returning a deferred or + // the user hitting the pause button. In either case, we want to halt + // execution of the test until something external to us restarts it. This + // means we need to pickle off enough state to pick up where we left off. + + // FIXME: need to make fixture execution async!! + + var tg = this._groups[groupName]; + if(tg.skip === true){ return; } + if(this._isArray(tg)){ + if(idx<=tg.length){ + if((!tg.inFlight)&&(tg.iterated == true)){ + if(tg["tearDown"]){ tg.tearDown(this); } + doh._groupFinished(groupName, (!tg.failures)); + return; + } + } + if(!idx){ + tg.inFlight = 0; + tg.iterated = false; + tg.failures = 0; + } + doh._groupStarted(groupName); + if(!idx){ + this._setupGroupForRun(groupName, idx); + if(tg["setUp"]){ tg.setUp(this); } + } + for(var y=(idx||0); y<tg.length; y++){ + if(this._paused){ + this._currentTest = y; + // this.debug("PAUSED at:", tg[y].name, this._currentGroup, this._currentTest); + return; + } + doh._runFixture(groupName, tg[y]); + if(this._paused){ + this._currentTest = y+1; + if(this._currentTest == tg.length){ + tg.iterated = true; + } + // this.debug("PAUSED at:", tg[y].name, this._currentGroup, this._currentTest); + return; + } + } + tg.iterated = true; + if(!tg.inFlight){ + if(tg["tearDown"]){ tg.tearDown(this); } + doh._groupFinished(groupName, (!tg.failures)); + } + } +} + +doh._onEnd = function(){} + +doh._report = function(){ + // summary: + // a private method to be implemented/replaced by the "locally + // appropriate" test runner + + // this.debug("ERROR:"); + // this.debug("\tNO REPORTING OUTPUT AVAILABLE."); + // this.debug("\tIMPLEMENT doh._report() IN YOUR TEST RUNNER"); + + this.debug(this._line); + this.debug("| TEST SUMMARY:"); + this.debug(this._line); + this.debug("\t", this._testCount, "tests in", this._groupCount, "groups"); + this.debug("\t", this._errorCount, "errors"); + this.debug("\t", this._failureCount, "failures"); +} + +doh.togglePaused = function(){ + this[(this._paused) ? "run" : "pause"](); +} + +doh.pause = function(){ + // summary: + // halt test run. Can be resumed. + this._paused = true; +} + +doh.run = function(){ + // summary: + // begins or resumes the test process. + // this.debug("STARTING"); + this._paused = false; + var cg = this._currentGroup; + var ct = this._currentTest; + var found = false; + if(!cg){ + this._init(); // we weren't paused + found = true; + } + this._currentGroup = null; + this._currentTest = null; + + for(var x in this._groups){ + if( + ( (!found)&&(x == cg) )||( found ) + ){ + if(this._paused){ return; } + this._currentGroup = x; + if(!found){ + found = true; + this.runGroup(x, ct); + }else{ + this.runGroup(x); + } + if(this._paused){ return; } + } + } + this._currentGroup = null; + this._currentTest = null; + this._paused = false; + this._onEnd(); + this._report(); +} + +tests = doh; + +(function(){ + // scop protection + try{ + if(typeof dojo != "undefined"){ + dojo.platformRequire({ + browser: ["doh._browserRunner"], + rhino: ["doh._rhinoRunner"], + spidermonkey: ["doh._rhinoRunner"] + }); + var _shouldRequire = (dojo.isBrowser) ? (dojo.global == dojo.global["parent"]) : true; + if(_shouldRequire){ + if(dojo.isBrowser){ + dojo.addOnLoad(function(){ + if(dojo.byId("testList")){ + var _tm = ( (dojo.global.testModule && dojo.global.testModule.length) ? dojo.global.testModule : "dojo.tests.module"); + dojo.forEach(_tm.split(","), dojo.require, dojo); + setTimeout(function(){ + doh.run(); + }, 500); + } + }); + }else{ + // dojo.require("doh._base"); + } + } + }else{ + if( + (typeof load == "function")&& + ( (typeof Packages == "function")|| + (typeof Packages == "object") ) + ){ + throw new Error(); + }else if(typeof load == "function"){ + throw new Error(); + } + } + }catch(e){ + print("\n"+doh._line); + print("The Dojo Unit Test Harness, $Rev$"); + print("Copyright (c) 2007, The Dojo Foundation, All Rights Reserved"); + print(doh._line, "\n"); + + load("_rhinoRunner.js"); + + try{ + var dojoUrl = "../../dojo/dojo.js"; + var testUrl = ""; + var testModule = "dojo.tests.module"; + for(var x=0; x<arguments.length; x++){ + if(arguments[x].indexOf("=") > 0){ + var tp = arguments[x].split("="); + if(tp[0] == "dojoUrl"){ + dojoUrl = tp[1]; + } + if(tp[0] == "testUrl"){ + testUrl = tp[1]; + } + if(tp[0] == "testModule"){ + testModule = tp[1]; + } + } + } + if(dojoUrl.length){ + if(!this["djConfig"]){ + djConfig = {}; + } + djConfig.baseUrl = dojoUrl.split("dojo.js")[0]; + load(dojoUrl); + } + if(testUrl.length){ + load(testUrl); + } + if(testModule.length){ + dojo.forEach(testModule.split(","), dojo.require, dojo); + } + }catch(e){ + print("An exception occurred: " + e); + } + + doh.run(); + } +}).apply(this, typeof arguments != "undefined" ? arguments : [null]); diff --git a/includes/js/util/doh/runner.sh b/includes/js/util/doh/runner.sh new file mode 100644 index 0000000..21b5cf7 --- /dev/null +++ b/includes/js/util/doh/runner.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +java -jar ../shrinksafe/custom_rhino.jar runner.js "$@" diff --git a/includes/js/util/doh/small_logo.png b/includes/js/util/doh/small_logo.png Binary files differnew file mode 100644 index 0000000..2fda23c --- /dev/null +++ b/includes/js/util/doh/small_logo.png |