diff options
-rw-r--r-- | docs/examples/index.html | 1 | ||||
-rw-r--r-- | docs/index.html | 39 | ||||
-rw-r--r-- | jarmon/jarmon.js | 88 | ||||
-rw-r--r-- | jarmon/jarmon.test.js | 312 | ||||
-rw-r--r-- | jarmonbuild/commands.py | 112 | ||||
-rw-r--r-- | test.html | 24 | ||||
-rw-r--r-- | testfile.bin | bin | 0 -> 1 bytes |
7 files changed, 529 insertions, 47 deletions
diff --git a/docs/examples/index.html b/docs/examples/index.html index 9d12389..3debd18 100644 --- a/docs/examples/index.html +++ b/docs/examples/index.html @@ -208,6 +208,7 @@ </div> <div class="chart-container"> <h2 class="title"></h2> + <div class="error"></div> <div class="chart"></div> <div class="graph-legend"></div> </div> diff --git a/docs/index.html b/docs/index.html index 1d57d49..b5ea14a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -10,6 +10,7 @@ <li><a href="#browser-compatibility">Browser Compatibility</a></li> <li><a href="#api-documentation">API documentation</a></li> <li><a href="#release-process">Release process and tools</a></li> + <li><a href="#automated-tests">Running automated tests</a></li> </ol> <h2 id="browser-compatibility">Browser Compatibility</h2> <p>Jarmon depends upon the <a href="http://code.google.com/p/flot/">Flot @@ -46,7 +47,7 @@ <dd>This is the default IE compatibility workaround. Jarmon bundles the <a href="http://code.google.com/p/explorercanvas/">excanvas library</a> which translates canvas api calls into VML, but this is much slower - than the native canvas support in other browsers. In the absense of + than the native canvas support in other browsers. In the absence of other workarounds, Jarmon will transparently use the excanvas library. <dt>Chrome Frame @@ -92,3 +93,39 @@ out the source code, then issue the <code>./bin/build -V 10.8 release</code> command from within the source tree.</p> + + <h2 id="automated-tests">Running automated tests</h2> + <p>Jarmon includes unit tests which are used to exercise the library + code, to check for compatibility with various platforms and web client + software and to check for regressions as the library evolves.</p> + <p>The unit tests are designed to be run inside a web browser using the + <a href="http://developer.yahoo.com/yui/yuitest/">YUI test harness</a>.</p> + <ol> + <li>Generate test data by running the following command + <code>./bin/build testdata</code> from within the source tree. + <li>Start a web server to serve the test page. Eg + <code>$ nginx -p . -c docs/examples/nginx.conf.example</code></li> + <li>Use a web browser to load + <a href="http://localhost:8080/test.html">the test page</a> + (<a href="http://localhost:8080/test.html"> + http://localhost:8080/test.html</a>)</li> + </ol> + <h3>Measure code coverage</h3> + <p>There are number of tools available to measure the + <a href="http://en.wikipedia.org/wiki/Code_coverage">code coverage</a> of + Javascript unit tests.</p> + <p>One such tool is <a href="http://siliconforks.com/jscoverage/"> + JSCoverage</a>. It is very easy to install JSCoverage on a Debian / Ubuntu + system and use it to measure the coverage of the Jarmon unit tests.</p> + <ol> + <li><code>sudo aptitude install jscoverage</code> - install + jscoverage.</li> + <li><code>jscoverage-server</code> - run the JSCoverage web server from + the root of the Jarmon source tree.</li> + <li>Visit <a href="http://localhost:8080/jscoverage.html?test.html"> + http://localhost:8080/jscoverage.html?test.html</a> to run the unit + tests through the JSCoverage server. The server will automatically + <a href="http://en.wikipedia.org/wiki/Instrumentation_%28computer_programming%29"> + instrument</a> the Jarmon code and present a web interface which reports + how much of the code has been exercised.</li> + </ol> diff --git a/jarmon/jarmon.js b/jarmon/jarmon.js index 44c4dad..406c4d1 100644 --- a/jarmon/jarmon.js +++ b/jarmon/jarmon.js @@ -27,6 +27,7 @@ if(typeof jarmon == 'undefined') { var jarmon = {}; } + jarmon.downloadBinary = function(url) { /** * Download a binary file asynchronously using the jQuery.ajax function @@ -141,31 +142,36 @@ jarmon.RrdQuery = function(rrd, unit) { this.unit = unit; }; -jarmon.RrdQuery.prototype.getData = function(startTime, endTime, dsId, cfName) { +jarmon.RrdQuery.prototype.getData = function(startTimeJs, endTimeJs, dsId, cfName) { /** * Generate a Flot compatible data object containing rows between start and * end time. The rows are taken from the first RRA whose data spans the * requested time range. * * @method getData - * @param startTime {Number} start timestamp - * @param endTime {Number} end timestamp + * @param startTimeJs {Number} start timestamp in microseconds + * @param endTimeJs {Number} end timestamp in microseconds * @param dsId {Variant} identifier of the RRD datasource (string or number) * @param cfName {String} The name of an RRD consolidation function (CF) * eg AVERAGE, MIN, MAX * @return {Object} A Flot compatible data series * eg label: '', data: [], unit: '' **/ - var startTimestamp = startTime/1000; + if (startTimeJs >= endTimeJs) { + throw RangeError( + ['starttime must be less than endtime. ', + 'starttime: ', startTimeJs, + 'endtime: ', endTimeJs].join('')); + } + + var startTime = startTimeJs/1000; var lastUpdated = this.rrd.getLastUpdate(); - var endTimestamp = lastUpdated; - if(endTime) { - endTimestamp = endTime/1000; - // If end time stamp is beyond the range of this rrd then reset it - if(lastUpdated < endTimestamp) { - endTimestamp = lastUpdated; - } + + // default endTime to the last updated time (quantized to rrd step boundry) + var endTime = lastUpdated - lastUpdated%this.rrd.getMinStep(); + if(endTimeJs) { + endTime = endTimeJs/1000; } if(dsId == null) { @@ -177,7 +183,7 @@ jarmon.RrdQuery.prototype.getData = function(startTime, endTime, dsId, cfName) { cfName = 'AVERAGE'; } - var rra, step, rraRowCount, firstUpdated; + var rra, step, rraRowCount, lastRowTime, firstRowTime; for(var i=0; i<this.rrd.getNrRRAs(); i++) { // Look through all RRAs looking for the most suitable @@ -191,29 +197,53 @@ jarmon.RrdQuery.prototype.getData = function(startTime, endTime, dsId, cfName) { step = rra.getStep(); rraRowCount = rra.getNrRows(); - firstUpdated = lastUpdated - (rraRowCount - 1) * step; + lastRowTime = lastUpdated-lastUpdated%step; + firstRowTime = lastRowTime - rraRowCount * step; + // We assume that the RRAs are listed in ascending order of time range, // therefore the first RRA which contains the range minimum should give // the highest resolution data for this range. - if(firstUpdated <= startTimestamp) { + if(firstRowTime <= startTime) { break; } } // If we got to the end of the loop without ever defining step, it means // that the CF check never succeded. if(!step) { - throw new Error('Unrecognised consolidation function: ' + cfName); + throw TypeError('Unrecognised consolidation function: ' + cfName); } - var startRow = rraRowCount - parseInt((lastUpdated - Math.max(startTimestamp, firstUpdated))/step) - 1; - var endRow = rraRowCount - parseInt((lastUpdated - endTimestamp)/step) - 1; - var flotData = []; - var timestamp = firstUpdated + (startRow - 1) * step; var dsIndex = ds.getIdx(); - for (var i=startRow; i<=endRow; i++) { - flotData.push([timestamp*1000.0, rra.getEl(i, dsIndex)]); - timestamp += step; + + var startRowTime = Math.max(firstRowTime, startTime - startTime%step); + var endRowTime = Math.min(lastRowTime, endTime - endTime%step); + // If RRD exists, but hasn't been updated then the start time might end up + // being higher than the end time (which is capped at the last row time of + // the chosen RRA, so cap startTime at endTime...if you see what I mean) + startRowTime = Math.min(startRowTime, endRowTime); + + /* + console.log('FRT: ', new Date(firstRowTime*1000)); + console.log('LRT: ', new Date(lastRowTime*1000)); + console.log('SRT: ', new Date(startRowTime*1000)); + console.log('ERT: ', new Date(endRowTime*1000)); + console.log('DIFF: ', (lastRowTime - startRowTime) / step); + console.log('ROWS: ', rraRowCount); + */ + + var startRowIndex = rraRowCount - (lastRowTime - startRowTime) / step; + var endRowIndex = rraRowCount - (lastRowTime - endRowTime) / step; + + //console.log('SRI: ', startRowIndex); + //console.log('ERI: ', endRowIndex); + + var val; + var timestamp = startRowTime; + for(var i=startRowIndex; i<endRowIndex; i++) { + val = rra.getEl(i, dsIndex) + flotData.push([timestamp*1000.0, val]); + timestamp += step } // Now get the date of the earliest record in entire rrd file, ie that of @@ -240,7 +270,7 @@ jarmon.RrdQuery.prototype.getData = function(startTime, endTime, dsId, cfName) { jarmon.RrdQueryRemote = function(url, unit, downloader) { this.url = url; this.unit = unit; - this.downloader = downloader; + this.downloader = downloader || jarmon.downloadBinary; this.lastUpdate = 0; this._download = null; }; @@ -498,6 +528,9 @@ jarmon.Chart.prototype.draw = function() { return MochiKit.Async.gatherResults(results) .addCallback( function(self, data) { + // Clear any previous error messages. + self.template.find('.error').empty().hide(); + var i, label, disabled = []; unit = ''; for(i=0; i<data.length; i++) { @@ -514,7 +547,7 @@ jarmon.Chart.prototype.draw = function() { } } - $.plot(self.template.find('.chart'), data, self.options); + $.plot(self.template.find('.chart').empty().show(), data, self.options); var yaxisUnitLabel = $('<div>').text(self.siPrefix + unit) .css({width: '100px', @@ -533,7 +566,7 @@ jarmon.Chart.prototype.draw = function() { // table is useful as it generates an optimum label element // width which we can copy to the new divs + a little extra // to accomodate the color box - var legend = self.template.find('.graph-legend'); + var legend = self.template.find('.graph-legend').show(); legend.empty(); self.template.find('.legendLabel') .each(function(i, el) { @@ -562,7 +595,10 @@ jarmon.Chart.prototype.draw = function() { }, this) .addErrback( function(self, failure) { - self.template.text('error: ' + failure.message); + self.template.find('.chart').empty().hide(); + self.template.find('.graph-legend').empty().hide(); + self.template.find('.error').text('error: ' + failure.message); + }, this) .addBoth( function(self, res) { diff --git a/jarmon/jarmon.test.js b/jarmon/jarmon.test.js new file mode 100644 index 0000000..c838023 --- /dev/null +++ b/jarmon/jarmon.test.js @@ -0,0 +1,312 @@ +/* Copyright (c) 2010 Richard Wall <richard (at) the-moon.net> + * See LICENSE for details. + * + * Unit tests for Jarmon + **/ + +YUI({ logInclude: { TestRunner: true } }).use('console', 'test', function(Y) { + Y.Test.Runner.add(new Y.Test.Case({ + name: "jarmon.downloadBinary", + + test_urlNotFound: function () { + /** + * When url cannot be found, the deferred should errback with status + * 404. + **/ + var d = new jarmon.downloadBinary('non-existent-file.html'); + d.addBoth( + function(self, ret) { + self.resume(function() { + Y.Assert.isInstanceOf(Error, ret); + Y.Assert.areEqual(404, ret.message); + }); + }, this); + + this.wait(); + }, + + test_urlFound: function () { + /** + * When url is found, the deferred should callback with an instance + * of javascriptrrd.BinaryFile + **/ + var d = new jarmon.downloadBinary('testfile.bin'); + d.addBoth( + function(self, ret) { + self.resume(function() { + Y.Assert.isInstanceOf(BinaryFile, ret); + Y.Assert.areEqual(String.fromCharCode(0), ret.getRawData()); + }); + }, this); + + this.wait(); + } + + })); + + var RRD_STEP = 10; + var RRD_DSNAME = 'speed'; + var RRD_DSINDEX = 0; + var RRD_RRACOUNT = 2; + var RRD_RRAROWS = 12; + var RRD_STARTTIME = new Date('1 jan 1980 00:00:00').getTime(); + var RRD_ENDTIME = new Date('1 jan 1980 00:02:00').getTime(); + + Y.Test.Runner.add(new Y.Test.Case({ + name: "jarmon.RRDFile", + + setUp: function() { + this.d = new jarmon.downloadBinary('build/test.rrd') + .addCallback( + function(self, binary) { + return new RRDFile(binary); + }, this) + .addErrback( + function(ret) { + console.log(ret); + }); + }, + + test_getLastUpdate: function () { + /** + * The generated rrd file should have a lastupdate date of + * 1980-01-01 00:50:01 + **/ + this.d.addCallback( + function(self, rrd) { + self.resume(function() { + Y.Assert.areEqual( + RRD_ENDTIME/1000+1, rrd.getLastUpdate()); + }); + }, this); + this.wait(); + }, + + test_getDSIndex: function () { + /** + * The generated rrd file should have a single DS whose name is + * 'speed'. A RangeError is thrown if the requested index or dsName + * doesnt exist. + **/ + this.d.addCallback( + function(self, rrd) { + self.resume(function() { + Y.Assert.areEqual(RRD_DSNAME, rrd.getDS(0).getName()); + Y.Assert.areEqual( + RRD_DSINDEX, rrd.getDS('speed').getIdx()); + var error = null; + try { + rrd.getDS(RRD_DSINDEX+1); + } catch(e) { + error = e; + } + Y.assert(error instanceof RangeError); + }); + }, this); + this.wait(); + }, + + test_getNrRRAs: function () { + /** + * The generated rrd file should have a single RRA + **/ + this.d.addCallback( + function(self, rrd) { + self.resume(function() { + Y.Assert.areEqual(RRD_RRACOUNT, rrd.getNrRRAs()); + }); + }, this); + this.wait(); + }, + + test_getRRA: function () { + /** + * The generated rrd file should have a single RRA using AVERAGE + * consolidation, step=10, rows=6 and values 0-5 + * rra.getEl throws a RangeError if asked for row which doesn't + * exist. + **/ + this.d.addCallback( + function(self, rrd) { + self.resume(function() { + var rra = rrd.getRRA(0); + Y.Assert.areEqual('AVERAGE', rra.getCFName()); + Y.Assert.areEqual(RRD_STEP, rra.getStep()); + Y.Assert.areEqual(RRD_RRAROWS, rra.getNrRows()); + for(var i=0; i<RRD_RRAROWS; i++) { + Y.Assert.areEqual(i, rra.getEl(i, RRD_DSINDEX)); + } + var error = null + try { + rra.getEl(RRD_RRAROWS+1, 0); + } catch(e) { + error = e; + } + Y.assert(error instanceof RangeError); + }); + }, this); + this.wait(); + } + })); + + Y.Test.Runner.add(new Y.Test.Case({ + name: "jarmon.RrdQuery", + + setUp: function() { + this.d = new jarmon.downloadBinary('build/test.rrd') + .addCallback( + function(self, binary) { + return new RRDFile(binary); + }, this) + .addErrback( + function(ret) { + console.log(ret); + }); + }, + + test_getDataTimeRangeOverlapError: function () { + /** + * The starttime must be less than the endtime + **/ + this.d.addCallback( + function(self, rrd) { + self.resume(function() { + var rq = new jarmon.RrdQuery(rrd, ''); + var error = null; + try { + rq.getData(1, 0); + } catch(e) { + error = e; + } + Y.Assert.isInstanceOf(RangeError, error); + }); + }, this); + this.wait(); + }, + + + test_getDataUnknownCfError: function () { + /** + * Error is raised if the rrd file doesn't contain an RRA with the + * requested consolidation function (CF) + **/ + this.d.addCallback( + function(self, rrd) { + self.resume(function() { + var rq = new jarmon.RrdQuery(rrd, ''); + var error = null; + try { + rq.getData(RRD_STARTTIME, RRD_ENDTIME, 0, 'FOO'); + } catch(e) { + error = e; + } + Y.Assert.isInstanceOf(TypeError, error); + }); + }, this); + this.wait(); + }, + + + test_getData: function () { + /** + * The generated rrd file should have values 0-9 at 300s intervals + * starting at 1980-01-01 00:00:00 + * Result should include a data points with times > starttime and + * <= endTime + **/ + this.d.addCallback( + function(self, rrd) { + self.resume(function() { + var rq = new jarmon.RrdQuery(rrd, ''); + + // We request data starting 1 STEP +1s after the RRD file + // first val and ending 1 STEP -1s before the RRD last val + // ie one step within the RRD file, but 1s away from the + // step boundary to test the quantisation of the + // requested time range. + var data = rq.getData( + RRD_STARTTIME + (RRD_STEP+1) * 1000, + RRD_ENDTIME - (RRD_STEP-1) * 1000); + + // so we expect two less rows than the total rows in the + // file. + Y.Assert.areEqual(RRD_RRAROWS-2, data.data.length); + + // The value of the first returned row should be the + // second value in the RRD file (starts at value 0) + Y.Assert.areEqual(1, data.data[0][1]); + + // The value of the last returned row should be the + // 10 value in the RRD file (starts at value 0) + Y.Assert.areEqual(10, data.data[data.data.length-1][1]); + + // The timestamp of the first returned row should be + // exactly one step after the start of the RRD file + Y.Assert.areEqual( + RRD_STARTTIME+RRD_STEP*1000, data.data[0][0]); + + // RRD_ENDTIME is on a step boundary and is therfore + // actually the start time of a new row + // So when we ask for endTime = RRD_ENDTIME-STEP-1 we + // actually get data up to the 2nd to last RRD row. + Y.Assert.areEqual( + RRD_ENDTIME-RRD_STEP*1000*2, + data.data[data.data.length-1][0]); + }); + }, this); + this.wait(); + }, + + test_getDataUnknownValues: function () { + /** + * If the requested time range is outside the range of the RRD file + * we should not get any values back + **/ + this.d.addCallback( + function(self, rrd) { + self.resume(function() { + var rq = new jarmon.RrdQuery(rrd, ''); + var data = rq.getData(RRD_ENDTIME, RRD_ENDTIME+1000); + Y.Assert.areEqual(0, data.data.length); + }); + }, this); + this.wait(); + } + + })); + + + Y.Test.Runner.add(new Y.Test.Case({ + name: "jarmon.Chart", + + test_draw: function () { + /** + * Test that a rendered chart has the correct dimensions, legend, + * axis, labels etc + **/ + var $tpl = $('<div><div class="chart"></div></div>').appendTo($('body')); + var c = new jarmon.Chart($tpl, jarmon.Chart.BASE_OPTIONS); + // + c.options.xaxis.tzoffset = 0; + c.addData('speed', new jarmon.RrdQueryRemote('build/test.rrd', 'm/s'), true); + var d = c.setTimeRange(RRD_STARTTIME, RRD_ENDTIME); + d.addCallback( + function(self) { + self.resume(function() { + // TODO: write useful tests + }); + }, this); + this.wait(); + }, + })); + + + //initialize the console + var yconsole = new Y.Console({ + newestOnTop: false, + width:'600px', + height: '400px' + }); + yconsole.render('#log'); + Y.Test.Runner.run(); +}); diff --git a/jarmonbuild/commands.py b/jarmonbuild/commands.py index 5071a46..9ababd9 100644 --- a/jarmonbuild/commands.py +++ b/jarmonbuild/commands.py @@ -8,7 +8,9 @@ import logging import os import shutil import sys +import time +from datetime import datetime from optparse import OptionParser from subprocess import check_call, PIPE from tempfile import gettempdir @@ -34,13 +36,9 @@ class BuildError(Exception): class BuildCommand(object): - def __init__(self, buildversion, log=None): - self.buildversion = buildversion - if log is not None: - self.log = log - else: - self.log = logging.getLogger( - '%s.%s' % (__name__, self.__class__.__name__)) + def __init__(self): + self.log = logging.getLogger( + '%s.%s' % (__name__, self.__class__.__name__)) self.workingbranch_dir = os.path.abspath( os.path.join(os.path.dirname(__file__), '..')) @@ -60,12 +58,26 @@ class BuildApidocsCommand(BuildCommand): Download YUI Doc and use it to generate apidocs for jarmon """ + command_name = 'apidocs' + def main(self, argv): """ The main entry point for the build-apidocs command @param argv: The list of arguments passed to the build-apidocs command """ + + parser = OptionParser( + usage='build [options] %s VERSION' % (self.command_name,)) + parser.disable_interspersed_args() + options, args = parser.parse_args(argv) + + if len(args) != 1: + parser.error('Wrong number of arguments. This command expects a ' + 'version number only.') + + buildversion = args[0] + tmpdir = gettempdir() workingbranch_dir = self.workingbranch_dir build_dir = self.build_dir @@ -131,7 +143,7 @@ class BuildApidocsCommand(BuildCommand): '--template=%s' % ( os.path.join( workingbranch_dir, 'jarmonbuild', 'yuidoc_template'),), - '--version=%s' % (self.buildversion,), + '--version=%s' % (buildversion,), '--project=%s' % (JARMON_PROJECT_TITLE,), '--projecturl=%s' % (JARMON_PROJECT_URL,) ), stdout=PIPE, stderr=PIPE,) @@ -145,7 +157,21 @@ class BuildReleaseCommand(BuildCommand): upload to Launchpad. """ + command_name = 'release' + def main(self, argv): + + parser = OptionParser( + usage='build [options] %s VERSION' % (self.command_name,)) + parser.disable_interspersed_args() + options, args = parser.parse_args(argv) + + if len(args) != 1: + parser.error('Wrong number of arguments. This command expects a ' + 'version number only.') + + buildversion = args[0] + workingbranch_dir = self.workingbranch_dir build_dir = self.build_dir @@ -167,11 +193,11 @@ class BuildReleaseCommand(BuildCommand): self.log.debug('Generate apidocs') - BuildApidocsCommand(buildversion=self.buildversion).main(argv) + BuildApidocsCommand().main([buildversion]) self.log.debug('Generate archive') - archive_root = 'jarmon-%s' % (self.buildversion,) + archive_root = 'jarmon-%s' % (buildversion,) prefix_len = len(build_dir) + 1 z = ZipFile('%s.zip' % (archive_root,), 'w', ZIP_DEFLATED) try: @@ -185,11 +211,54 @@ class BuildReleaseCommand(BuildCommand): z.close() -# The available sub commands -build_commands = { - 'apidocs': BuildApidocsCommand, - 'release': BuildReleaseCommand, -} +class BuildTestDataCommand(BuildCommand): + """ + Create data for use in unittests + """ + + command_name = 'testdata' + + def main(self, argv): + """ + Create an RRD file with values 0-9 entered at 1 second intervals from + 1980-01-01 00:00:00 (the first date that rrdtool allows) + """ + from datetime import datetime + from pyrrd.rrd import DataSource, RRA, RRD + start = int(datetime(1980, 1, 1, 0, 0).strftime('%s')) + dss = [] + rras = [] + filename = os.path.join(self.build_dir, 'test.rrd') + + rows = 12 + step = 10 + + dss.append(DataSource(dsName='speed', dsType='GAUGE', heartbeat=2*step)) + rras.append(RRA(cf='AVERAGE', xff=0.5, steps=1, rows=rows)) + rras.append(RRA(cf='AVERAGE', xff=0.5, steps=12, rows=rows)) + my_rrd = RRD(filename, ds=dss, rra=rras, start=start, step=step) + my_rrd.create() + + for i, t in enumerate(range(start+step, start+step+(rows*step), step)): + self.log.debug('DATA: %s %s (%s)' % (t, i, datetime.fromtimestamp(t))) + my_rrd.bufferValue(t, i) + + # Add further data 1 second later to demonstrate that the rrd + # lastupdatetime does not necessarily fall on a step boundary + t += 1 + i += 1 + self.log.debug('DATA: %s %s (%s)' % (t, i, datetime.fromtimestamp(t))) + my_rrd.bufferValue(t, i) + + my_rrd.update() + + +# The available subcommands +SUBCOMMAND_HANDLERS = [ + BuildApidocsCommand, + BuildReleaseCommand, + BuildTestDataCommand, +] def main(argv=sys.argv[1:]): @@ -197,10 +266,13 @@ def main(argv=sys.argv[1:]): The root build command which dispatches to various subcommands for eg building apidocs and release zip files. """ - parser = OptionParser(usage='%prog [options] SUBCOMMAND [options]') - parser.add_option( - '-V', '--build-version', dest='buildversion', default='0', - metavar='BUILDVERSION', help='Specify the build version') + + build_commands = dict( + (handler.command_name, handler) for handler in SUBCOMMAND_HANDLERS) + + parser = OptionParser( + usage='build [options] SUBCOMMAND [options]', + description='Available subcommands are: %r' % (build_commands.keys(),)) parser.add_option( '-d', '--debug', action='store_true', default=False, dest='debug', help='Print verbose debug log to stderr') @@ -236,4 +308,4 @@ def main(argv=sys.argv[1:]): log.setLevel(logging.DEBUG) ch.setLevel(logging.DEBUG) - command_factory(buildversion=options.buildversion).main(argv=args) + command_factory().main(argv=args) diff --git a/test.html b/test.html new file mode 100644 index 0000000..e8508f6 --- /dev/null +++ b/test.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Jarmon Unit Test Runner</title> + <link rel="stylesheet" type="text/css" + href="http://developer.yahoo.com/yui/3/assets/yui.css"/> + <link rel="stylesheet" type="text/css" + href="http://yui.yahooapis.com/3.1.1/build/cssfonts/fonts-min.css"/> + <style type='text/css'> + .chart { + width: 500px; + height: 100px; + } + </style> + <script src="http://yui.yahooapis.com/3.1.1/build/yui/yui-min.js"></script> + <script src="docs/examples/assets/js/dependencies.js"></script> + <script src="jarmon/jarmon.js"></script> + <script src="jarmon/jarmon.test.js"></script> + </head> + <body class="yui3-skin-sam yui-skin-sam"> + <div id="log"></div> + </body> +</html> diff --git a/testfile.bin b/testfile.bin Binary files differnew file mode 100644 index 0000000..f76dd23 --- /dev/null +++ b/testfile.bin |