aboutsummaryrefslogtreecommitdiffstats
path: root/web/src/vendor/benchmark/test
diff options
context:
space:
mode:
authorAldo Cortesi <aldo@nullcube.com>2014-09-10 14:22:26 +1200
committerAldo Cortesi <aldo@nullcube.com>2014-09-10 14:23:10 +1200
commit0510c9b111aed03d0d3680db63614d50f231745c (patch)
tree0d2fec5d46a3cb984a8b12e36db2f44a1a8eaa5a /web/src/vendor/benchmark/test
parent76982937a68a2adaf96ec2d258e369d7c871a609 (diff)
downloadmitmproxy-0510c9b111aed03d0d3680db63614d50f231745c.tar.gz
mitmproxy-0510c9b111aed03d0d3680db63614d50f231745c.tar.bz2
mitmproxy-0510c9b111aed03d0d3680db63614d50f231745c.zip
Client-side framework for web application
Diffstat (limited to 'web/src/vendor/benchmark/test')
-rw-r--r--web/src/vendor/benchmark/test/benchmark.air/.actionScriptProperties24
-rw-r--r--web/src/vendor/benchmark/test/benchmark.air/.flexProperties2
-rw-r--r--web/src/vendor/benchmark/test/benchmark.air/.project24
-rw-r--r--web/src/vendor/benchmark/test/benchmark.air/README.md6
-rw-r--r--web/src/vendor/benchmark/test/benchmark.air/bin-debug/air.swfbin0 -> 965966 bytes
-rw-r--r--web/src/vendor/benchmark/test/benchmark.air/src/air-app.xml16
-rw-r--r--web/src/vendor/benchmark/test/benchmark.air/src/air.mxml7
-rw-r--r--web/src/vendor/benchmark/test/benchmark.air/src/index.html37
-rw-r--r--web/src/vendor/benchmark/test/index.html85
-rwxr-xr-xweb/src/vendor/benchmark/test/run-test.sh9
-rw-r--r--web/src/vendor/benchmark/test/test.js2074
11 files changed, 2284 insertions, 0 deletions
diff --git a/web/src/vendor/benchmark/test/benchmark.air/.actionScriptProperties b/web/src/vendor/benchmark/test/benchmark.air/.actionScriptProperties
new file mode 100644
index 00000000..53a44ecd
--- /dev/null
+++ b/web/src/vendor/benchmark/test/benchmark.air/.actionScriptProperties
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<actionScriptProperties analytics="false" mainApplicationPath="air.mxml" projectUUID="f1d53a64-add9-48c3-9e81-bd3b388963e7" version="10">
+ <compiler additionalCompilerArguments="-locale en_US" autoRSLOrdering="true" copyDependentFiles="true" fteInMXComponents="false" generateAccessible="true" htmlExpressInstall="true" htmlGenerate="false" htmlHistoryManagement="false" htmlPlayerVersionCheck="true" includeNetmonSwc="false" outputFolderPath="bin-debug" removeUnusedRSL="true" sourceFolderPath="src" strict="true" targetPlayerVersion="0.0.0" useApolloConfig="true" useDebugRSLSwfs="true" verifyDigests="true" warn="true">
+ <compilerSourcePath/>
+ <libraryPath defaultLinkType="0"/>
+ <sourceAttachmentPath/>
+ </compiler>
+ <applications>
+ <application path="air.mxml">
+ <airExcludes/>
+ </application>
+ </applications>
+ <modules/>
+ <buildCSSFiles/>
+ <flashCatalyst validateFlashCatalystCompatibility="false"/>
+ <buildTargets>
+ <buildTarget buildTargetName="default">
+ <airSettings airCertificatePath="" airTimestamp="true" version="1">
+ <airExcludes/>
+ </airSettings>
+ <actionScriptSettings version="1"/>
+ </buildTarget>
+ </buildTargets>
+</actionScriptProperties>
diff --git a/web/src/vendor/benchmark/test/benchmark.air/.flexProperties b/web/src/vendor/benchmark/test/benchmark.air/.flexProperties
new file mode 100644
index 00000000..f2072112
--- /dev/null
+++ b/web/src/vendor/benchmark/test/benchmark.air/.flexProperties
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<flexProperties enableServiceManager="false" flexServerFeatures="0" flexServerType="0" toolCompile="true" useServerFlexSDK="false" version="2"/>
diff --git a/web/src/vendor/benchmark/test/benchmark.air/.project b/web/src/vendor/benchmark/test/benchmark.air/.project
new file mode 100644
index 00000000..98fe0436
--- /dev/null
+++ b/web/src/vendor/benchmark/test/benchmark.air/.project
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>benchmark.air</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.adobe.flexbuilder.project.flexbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.adobe.flexbuilder.project.apollobuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.adobe.flexbuilder.project.apollonature</nature>
+ <nature>com.adobe.flexbuilder.project.flexnature</nature>
+ <nature>com.adobe.flexbuilder.project.actionscriptnature</nature>
+ </natures>
+</projectDescription>
diff --git a/web/src/vendor/benchmark/test/benchmark.air/README.md b/web/src/vendor/benchmark/test/benchmark.air/README.md
new file mode 100644
index 00000000..a304b6fa
--- /dev/null
+++ b/web/src/vendor/benchmark/test/benchmark.air/README.md
@@ -0,0 +1,6 @@
+# Instructions
+
+Before importing the Flash Builder project please perform the following steps:
+
+ 1. Copy/paste `benchmark.js` into the `src` folder.
+ 2. Copy/paste `AIRIntrospector.js` from some place like `C:\Program Files\Adobe\Adobe Flash Builder 4.5\sdks\4.5.0\frameworks\libs\air\AIRIntrospector.js` into the `src` folder.
diff --git a/web/src/vendor/benchmark/test/benchmark.air/bin-debug/air.swf b/web/src/vendor/benchmark/test/benchmark.air/bin-debug/air.swf
new file mode 100644
index 00000000..53399542
--- /dev/null
+++ b/web/src/vendor/benchmark/test/benchmark.air/bin-debug/air.swf
Binary files differ
diff --git a/web/src/vendor/benchmark/test/benchmark.air/src/air-app.xml b/web/src/vendor/benchmark/test/benchmark.air/src/air-app.xml
new file mode 100644
index 00000000..0ef97076
--- /dev/null
+++ b/web/src/vendor/benchmark/test/benchmark.air/src/air-app.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<application xmlns="http://ns.adobe.com/air/application/2.6">
+ <id>air</id>
+ <filename>air</filename>
+ <name>air</name>
+ <versionNumber>0.0.0</versionNumber>
+ <initialWindow>
+ <content>index.html</content>
+ <resizable>true</resizable>
+ <width>320</width>
+ <height>240</height>
+ <autoOrients>false</autoOrients>
+ <fullScreen>false</fullScreen>
+ <visible>true</visible>
+ </initialWindow>
+</application>
diff --git a/web/src/vendor/benchmark/test/benchmark.air/src/air.mxml b/web/src/vendor/benchmark/test/benchmark.air/src/air.mxml
new file mode 100644
index 00000000..c2c46603
--- /dev/null
+++ b/web/src/vendor/benchmark/test/benchmark.air/src/air.mxml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
+ xmlns:s="library://ns.adobe.com/flex/spark"
+ xmlns:mx="library://ns.adobe.com/flex/mx">
+ <fx:Declarations>
+ </fx:Declarations>
+</s:WindowedApplication>
diff --git a/web/src/vendor/benchmark/test/benchmark.air/src/index.html b/web/src/vendor/benchmark/test/benchmark.air/src/index.html
new file mode 100644
index 00000000..1a1eb196
--- /dev/null
+++ b/web/src/vendor/benchmark/test/benchmark.air/src/index.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Benchmark.js Adobe AIR Test Suite</title>
+ <script src="AIRIntrospector.js"></script>
+ <script>
+ var console = air.Introspector.Console;
+ </script>
+ <script src="benchmark.js"></script>
+ </head>
+ <body>
+ <script>
+ (function() {
+ var suite = new Benchmark.Suite;
+
+ // add tests
+ suite.add('RegExp#test', function() {
+ /o/.test('Hello World!');
+ })
+ .add('String#indexOf', function() {
+ 'Hello World!'.indexOf('o') > -1;
+ })
+ // add listeners
+ .on('cycle', function(event) {
+ console.log(event.target);
+ })
+ .on('complete', function() {
+ console.log('Fastest is ' + this.filter('fastest').pluck('name'));
+ })
+ // don't run async to avoid JavaScript security errors
+ // http://help.adobe.com/en_US/AIR/1.5/devappshtml/WS5b3ccc516d4fbf351e63e3d118666ade46-7f0e.html
+ .run({ 'async': false });
+ }());
+ </script>
+ </body>
+</html>
diff --git a/web/src/vendor/benchmark/test/index.html b/web/src/vendor/benchmark/test/index.html
new file mode 100644
index 00000000..9762fb69
--- /dev/null
+++ b/web/src/vendor/benchmark/test/index.html
@@ -0,0 +1,85 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Benchmark.js Test Suite</title>
+ <link rel="stylesheet" href="../vendor/qunit/qunit/qunit.css">
+ <style>
+ applet {
+ position: absolute;
+ left: -9999em;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="qunit"></div>
+ <script src="../benchmark.js"></script>
+ <script src="../vendor/platform.js/platform.js"></script>
+ <script>
+ (function() {
+ var hasOwnProperty = function hasOwnProperty(key) {
+ var parent = (this.constructor || Object).prototype;
+ return key in this && !(key in parent && this[key] === parent[key]);
+ };
+ if (typeof {}.hasOwnProperty != 'function') {
+ // redefine for Safari 2, else use the less accurate fallback for others
+ if ({}.__proto__ == Object.prototype) {
+ hasOwnProperty = function hasOwnProperty(key) {
+ var result;
+ this.__proto__ = [this.__proto__, this.__proto__ = null, result = key in this][0];
+ return result;
+ };
+ }
+ Object.prototype.hasOwnProperty = hasOwnProperty;
+ }
+ }());
+
+ // load the nanosecond timer
+ if (!/[?&]nojava=true(?:&|$)/.test(location.search)) {
+ document.write('<applet code="nano" archive="../nano.jar"></applet>');
+ }
+
+ // avoid syntax errors for `QUnit.throws` in older Firefoxes
+ document.write(platform.name == 'Firefox' && /^1\b/.test(platform.version)
+ ? '<script src="../vendor/qunit/qunit/qunit-1.8.0.js"><\/script>'
+ : '<script src="../vendor/qunit/qunit/qunit.js"><\/script>'
+ );
+ </script>
+ <script>
+ // load test.js if not using require.js
+ document.write(/[?&]norequire=true(?:&|$)/.test(location.search)
+ ? '<script src="test.js"><\/script>'
+ : '<script src="../vendor/requirejs/require.js"><\/script>'
+ );
+ </script>
+ <script>
+ // load Benchmark as a module
+ var Benchmark2;
+
+ window.require && require({
+ 'baseUrl': '../vendor/requirejs/',
+ 'urlArgs': 't=' + (+new Date),
+ 'paths': {
+ 'benchmark': '../../benchmark',
+ 'platform': '../platform.js/platform'
+ }
+ },
+ ['benchmark', 'platform'], function(Benchmark, platform) {
+ Benchmark2 = Benchmark;
+ Benchmark2.platform = platform;
+ require(['test.js']);
+ });
+
+ // set a more readable browser name
+ window.onload = function() {
+ var timeoutId = setInterval(function() {
+ var ua = document.getElementById('qunit-userAgent');
+ if (ua) {
+ ua.innerHTML = platform;
+ clearInterval(timeoutId);
+ }
+ }, 15);
+ };
+ </script>
+ </body>
+</html>
diff --git a/web/src/vendor/benchmark/test/run-test.sh b/web/src/vendor/benchmark/test/run-test.sh
new file mode 100755
index 00000000..43424e43
--- /dev/null
+++ b/web/src/vendor/benchmark/test/run-test.sh
@@ -0,0 +1,9 @@
+cd "$(dirname "$0")"
+for cmd in rhino ringo narwhal node; do
+ echo ""
+ echo "Testing in $cmd..."
+ $cmd test.js
+done
+echo ""
+echo "Testing in a browser..."
+open index.html
diff --git a/web/src/vendor/benchmark/test/test.js b/web/src/vendor/benchmark/test/test.js
new file mode 100644
index 00000000..d6944940
--- /dev/null
+++ b/web/src/vendor/benchmark/test/test.js
@@ -0,0 +1,2074 @@
+;(function(window, undefined) {
+ 'use strict';
+
+ /** Use a single load function */
+ var load = typeof require == 'function' ? require : window.load;
+
+ /** The `platform` object to check */
+ var platform =
+ window.platform ||
+ load('../vendor/platform.js/platform.js') ||
+ window.platform;
+
+ /** The unit testing framework */
+ var QUnit =
+ window.QUnit || (
+ window.setTimeout || (window.addEventListener = window.setTimeout = / /),
+ window.QUnit = load('../vendor/qunit/qunit/qunit' + (platform.name == 'Narwhal' ? '-1.8.0' : '') + '.js') || window.QUnit,
+ load('../vendor/qunit-clib/qunit-clib.js'),
+ (window.addEventListener || 0).test && delete window.addEventListener,
+ window.QUnit
+ );
+
+ /** The `Benchmark` constructor to test */
+ var Benchmark =
+ window.Benchmark || (
+ Benchmark = load('../benchmark.js') || window.Benchmark,
+ Benchmark.Benchmark || Benchmark
+ );
+
+ /** API shortcut */
+ var forOwn = Benchmark.forOwn;
+
+ /** Used to get property descriptors */
+ var getDescriptor = Object.getOwnPropertyDescriptor;
+
+ /** Used to set property descriptors */
+ var setDescriptor = Object.defineProperty;
+
+ /** Shortcut used to convert array-like objects to arrays */
+ var slice = [].slice;
+
+ /** Used to resolve a value's internal [[Class]] */
+ var toString = {}.toString;
+
+ /** Used to check problem JScript properties (a.k.a. the [[DontEnum]] bug) */
+ var shadowed = {
+ 'constructor': 1,
+ 'hasOwnProperty': 2,
+ 'isPrototypeOf': 3,
+ 'propertyIsEnumerable': 4,
+ 'toLocaleString': 5,
+ 'toString': 6,
+ 'valueOf': 7
+ };
+
+ /** Used to flag environments/features */
+ var support = {
+ 'descriptors': !!function() {
+ try {
+ var o = {};
+ return (setDescriptor(o, o, o), 'value' in getDescriptor(o, o));
+ } catch(e) { }
+ }()
+ };
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Skips a given number of tests with a passing result.
+ *
+ * @private
+ * @param {Number} [count=1] The number of tests to skip.
+ */
+ function skipTest(count) {
+ count || (count = 1);
+ while (count--) {
+ ok(true, 'test skipped');
+ }
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // init Benchmark.options.minTime
+ Benchmark(function() { throw 0; }).run();
+
+ // set a shorter max time
+ Benchmark.options.maxTime = Benchmark.options.minTime * 5;
+
+ // explicitly call `QUnit.module()` instead of `module()`
+ // in case we are in a CLI environment
+ QUnit.module('Benchmark');
+
+ (function() {
+ test('has the default `Benchmark.platform` value', function() {
+ if (window.document) {
+ equal(String(Benchmark.platform), navigator.userAgent);
+ } else {
+ skipTest(1)
+ }
+ });
+
+ test('supports loading Benchmark.js as a module', function() {
+ if (window.document && window.require) {
+ equal((Benchmark2 || {}).version, Benchmark.version);
+ } else {
+ skipTest(1)
+ }
+ });
+
+ test('supports loading Platform.js as a module', function() {
+ if (window.document && window.require) {
+ var platform = (Benchmark2 || {}).platform || {};
+ equal(typeof platform.name, 'string');
+ } else {
+ skipTest(1)
+ }
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark constructor');
+
+ (function() {
+ test('creates a new instance when called without the `new` operator', function() {
+ ok(Benchmark() instanceof Benchmark);
+ });
+
+ test('supports passing an options object', function() {
+ var bench = Benchmark({ 'name': 'foo', 'fn': function() { } });
+ ok(bench.fn && bench.name == 'foo');
+ });
+
+ test('supports passing a "name" and "fn" argument', function() {
+ var bench = Benchmark('foo', function() { });
+ ok(bench.fn && bench.name == 'foo');
+ });
+
+ test('supports passing a "name" argument and an options object', function() {
+ var bench = Benchmark('foo', { 'fn': function() { } });
+ ok(bench.fn && bench.name == 'foo');
+ });
+
+ test('supports passing a "name" argument and an options object', function() {
+ var bench = Benchmark('foo', function() { }, { 'id': 'bar' });
+ ok(bench.fn && bench.name == 'foo' && bench.id == 'bar');
+ });
+
+ test('supports passing an empy string for the "fn" options property', function() {
+ var bench = Benchmark({ 'fn': '' }).run();
+ ok(!bench.error);
+ });
+
+ test('detects dead code', function() {
+ var bench = Benchmark(function() { }).run();
+ ok(/setup\(\)/.test(bench.compiled) ? !bench.error : bench.error);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark compilation');
+
+ (function() {
+ test('compiles using the default `Function#toString`', function() {
+ var bench = Benchmark({
+ 'setup': function() { var a = 1; },
+ 'fn': function() { throw a; },
+ 'teardown': function() { a = 2; }
+ }).run();
+
+ var compiled = bench.compiled;
+ if (/setup\(\)/.test(compiled)) {
+ skipTest();
+ }
+ else {
+ ok(/var a\s*=\s*1/.test(compiled) && /throw a/.test(compiled) && /a\s*=\s*2/.test(compiled));
+ }
+ });
+
+ test('compiles using a custom "toString" method', function() {
+ var bench = Benchmark({
+ 'setup': function() { },
+ 'fn': function() { },
+ 'teardown': function() { }
+ });
+
+ bench.setup.toString = function() { return 'var a = 1;' };
+ bench.fn.toString = function() { return 'throw a;' };
+ bench.teardown.toString = function() { return 'a = 2;' };
+ bench.run();
+
+ var compiled = bench.compiled;
+ if (/setup\(\)/.test(compiled)) {
+ skipTest();
+ }
+ else {
+ ok(/var a\s*=\s*1/.test(compiled) && /throw a/.test(compiled) && /a\s*=\s*2/.test(compiled));
+ }
+ });
+
+ test('compiles using a string value', function() {
+ var bench = Benchmark({
+ 'setup': 'var a = 1;',
+ 'fn': 'throw a;',
+ 'teardown': 'a = 2;'
+ }).run();
+
+ var compiled = bench.compiled;
+ if (/setup\(\)/.test(compiled)) {
+ skipTest();
+ }
+ else {
+ ok(/var a\s*=\s*1/.test(compiled) && /throw a/.test(compiled) && /a\s*=\s*2/.test(compiled));
+ }
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark test binding');
+
+ (function() {
+ var count = 0;
+
+ var tests = {
+ 'inlined "setup", "fn", and "teardown"': (
+ 'if(/ops/.test(this))this._fn=true;'
+ ),
+ 'called "fn" and inlined "setup"/"teardown" reached by error': function() {
+ count++;
+ if (/ops/.test(this)) {
+ this._fn = true;
+ }
+ },
+ 'called "fn" and inlined "setup"/"teardown" reached by `return` statement': function() {
+ if (/ops/.test(this)) {
+ this._fn = true;
+ }
+ return;
+ }
+ };
+
+ forOwn(tests, function(fn, title) {
+ test('has correct binding for ' + title, function() {
+ var bench = Benchmark({
+ 'setup': 'if(/ops/.test(this))this._setup=true;',
+ 'fn': fn,
+ 'teardown': 'if(/ops/.test(this))this._teardown=true;',
+ 'onCycle': function() { this.abort(); }
+ }).run();
+
+ var compiled = bench.compiled;
+ if (/setup\(\)/.test(compiled)) {
+ skipTest(3);
+ }
+ else {
+ ok(bench._setup, 'correct binding for "setup"');
+ ok(bench._fn, 'correct binding for "fn"');
+ ok(bench._teardown, 'correct binding for "teardown"');
+ }
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.deepClone');
+
+ (function() {
+ function createCircularObject() {
+ var result = {
+ 'foo': { 'b': { 'foo': { 'c': { } } } },
+ 'bar': { }
+ };
+
+ result.foo.b.foo.c.foo = result;
+ result.bar.b = result.foo.b;
+ return result;
+ }
+
+ function Klass() {
+ this.a = 1;
+ }
+
+ Klass.prototype = { 'b': 1 };
+
+ var notCloneable = {
+ 'an arguments object': arguments,
+ 'an element': window.document && document.body,
+ 'a function': Klass,
+ 'a Klass instance': new Klass
+ };
+
+ var objects = {
+ 'an array': ['a', 'b', 'c', ''],
+ 'an array-like-object': { '0': 'a', '1': 'b', '2': 'c', '3': '', 'length': 5 },
+ 'boolean': false,
+ 'boolean object': Object(false),
+ 'an object': { 'a': 0, 'b': 1, 'c': 3 },
+ 'an object with object values': { 'a': /a/, 'b': ['B'], 'c': { 'C': 1 } },
+ 'null': null,
+ 'a number': 3,
+ 'a number object': Object(3),
+ 'a regexp': /x/gim,
+ 'a string': 'x',
+ 'a string object': Object('x'),
+ 'undefined': undefined
+ };
+
+ objects['an array'].length = 5;
+
+ forOwn(objects, function(object, key) {
+ test('clones ' + key + ' correctly', function() {
+ var kind = toString.call(object),
+ clone = Benchmark.deepClone(object);
+
+ if (object == null) {
+ equal(clone, object);
+ } else {
+ deepEqual(clone.valueOf(), object.valueOf());
+ }
+ if (object === Object(object)) {
+ ok(clone !== object);
+ } else {
+ skipTest();
+ }
+ });
+ });
+
+ forOwn(notCloneable, function(object, key) {
+ test('does not clone ' + key, function() {
+ ok(Benchmark.deepClone(object) === object);
+ });
+ });
+
+ test('clones using Klass#deepClone', function() {
+ var object = new Klass;
+ Klass.prototype.deepClone = function() { return new Klass; };
+
+ var clone = Benchmark.deepClone(object);
+ ok(clone !== object && clone instanceof Klass);
+
+ delete Klass.prototype.clone;
+ });
+
+ test('clones problem JScript properties', function() {
+ var clone = Benchmark.deepClone(shadowed);
+ deepEqual(clone, shadowed);
+ });
+
+ test('clones string object with custom property', function() {
+ var object = new String('x');
+ object.x = 1;
+
+ var clone = Benchmark.deepClone(object);
+ ok(clone == 'x' && typeof clone == 'object' && clone.x === 1 && toString.call(clone) == '[object String]');
+ });
+
+ test('clones objects with circular references', function() {
+ var object = createCircularObject(),
+ clone = Benchmark.deepClone(object);
+
+ ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c.foo && clone !== object);
+ });
+
+ test('clones non-extensible objects with circular references', function() {
+ if (Object.preventExtensions) {
+ var object = Object.preventExtensions(createCircularObject());
+ Object.preventExtensions(object.bar.b);
+
+ var clone = Benchmark.deepClone(object);
+ ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c.foo && clone !== object);
+ } else {
+ skipTest(1)
+ }
+ });
+
+ test('clones sealed objects with circular references', function() {
+ if (Object.seal) {
+ var object = Object.seal(createCircularObject());
+ Object.seal(object.bar.b);
+
+ var clone = Benchmark.deepClone(object);
+ ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c.foo && clone !== object);
+ } else {
+ skipTest(1)
+ }
+ });
+
+ test('clones frozen objects with circular references', function() {
+ if (Object.freeze) {
+ var object = Object.freeze(createCircularObject());
+ Object.freeze(object.bar.b);
+
+ var clone = Benchmark.deepClone(object);
+ ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c.foo && clone !== object);
+ } else {
+ skipTest(1)
+ }
+ });
+
+ test('clones objects with custom descriptors and circular references', function() {
+ var accessor,
+ descriptor;
+
+ if (support.descriptors) {
+ var object = setDescriptor({}, 'foo', {
+ 'configurable': true,
+ 'value': setDescriptor({}, 'b', {
+ 'writable': true,
+ 'value': setDescriptor({}, 'foo', {
+ 'get': function() { return accessor; },
+ 'set': function(value) { accessor = value; }
+ })
+ })
+ });
+
+ setDescriptor(object, 'bar', { 'value': {} });
+ object.foo.b.foo = { 'c': object };
+ object.bar.b = object.foo.b;
+
+ var clone = Benchmark.deepClone(object);
+ ok(clone !== object &&
+ clone.bar.b === clone.foo.b &&
+ clone !== clone.foo.b.foo.c.foo &&
+ (descriptor = getDescriptor(clone, 'foo')) &&
+ descriptor.configurable && !(descriptor.enumerable && descriptor.writable) &&
+ (descriptor = getDescriptor(clone.foo, 'b')) &&
+ descriptor.writable && !(descriptor.configurable && descriptor.enumerable) &&
+ (descriptor = getDescriptor(clone.foo.b, 'foo')) &&
+ descriptor.get && descriptor.set &&
+ (descriptor = getDescriptor(clone.foo.b, 'foo')) &&
+ !(descriptor.configurable && descriptor.enumerable && descriptor.writable) &&
+ (descriptor = getDescriptor(clone, 'bar')) &&
+ !(descriptor.configurable && descriptor.enumerable && descriptor.writable));
+ }
+ else {
+ skipTest(1)
+ }
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.each');
+
+ (function() {
+ var xpathResult;
+
+ var objects = {
+ 'array': ['a', 'b', 'c', ''],
+ 'array-like-object': { '0': 'a', '1': 'b', '2': 'c', '3': '', 'length': 5 },
+ 'xpath snapshot': null
+ };
+
+ if (window.document && document.evaluate) {
+ xpathResult = [document.documentElement, document.getElementsByTagName('head')[0], document.body];
+ objects['xpath snapshot'] = document.evaluate('//*[self::html or self::head or self::body]', document, null, 7, null);
+ }
+
+ objects.array.length = 5;
+
+ forOwn(objects, function(object, key) {
+ test('passes the correct arguments when passing an ' + key, function() {
+ if (object) {
+ var args
+ Benchmark.each(object, function() {
+ args || (args = slice.call(arguments));
+ });
+
+ if (key == 'xpath snapshot') {
+ ok(args[0] === xpathResult[0]);
+ } else {
+ equal(args[0], 'a');
+ }
+ equal(args[1], 0);
+ ok(args[2] === object);
+ }
+ else {
+ skipTest(3);
+ }
+ });
+
+ test('returns the passed object when passing an ' + key, function() {
+ if (object) {
+ var actual = Benchmark.each(object, function() { });
+ ok(actual === object);
+ }
+ else {
+ skipTest();
+ }
+ });
+
+ test('iterates over all indexes when passing an ' + key, function() {
+ if (object) {
+ var values = [];
+ Benchmark.each(object, function(value) {
+ values.push(value);
+ });
+
+ deepEqual(values, key == 'xpath snapshot' ? xpathResult : ['a', 'b', 'c', '']);
+ }
+ else {
+ skipTest();
+ }
+ });
+
+ test('exits early when returning `false` when passing an ' + key, function() {
+ if (object) {
+ var values = [];
+ Benchmark.each(object, function(value) {
+ values.push(value);
+ return values.length < 2;
+ });
+
+ deepEqual(values, key == 'xpath snapshot' ? xpathResult.slice(0, 2) : ['a', 'b']);
+ }
+ else {
+ skipTest();
+ }
+ });
+ });
+
+ test('passes the third callback argument as an object', function() {
+ var thirdArg;
+ Benchmark.each('hello', function(value, index, object) {
+ thirdArg = object;
+ });
+
+ ok(thirdArg && typeof thirdArg == 'object');
+ });
+
+ test('iterates over strings by index', function() {
+ var values = [];
+ Benchmark.each('hello', function(value) {
+ values.push(value)
+ });
+
+ deepEqual(values, ['h', 'e', 'l', 'l', 'o']);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.extend');
+
+ (function() {
+ test('allows no source argument', function() {
+ var object = {};
+ equal(Benchmark.extend(object), object);
+ });
+
+ test('allows a single source argument', function() {
+ var source = { 'x': 1, 'y': 1 },
+ actual = Benchmark.extend({}, source);
+
+ deepEqual(Benchmark.extend({}, source), { 'x': 1, 'y': 1 });
+ });
+
+ test('allows multiple source arguments', function() {
+ var source1 = { 'x': 1, 'y': 1 },
+ source2 = { 'y': 2, 'z': 2 },
+ actual = Benchmark.extend({}, source1, source2);
+
+ deepEqual(actual, { 'x': 1, 'y': 2, 'z': 2 });
+ });
+
+ test('will add inherited source properties', function() {
+ function Source() { }
+ Source.prototype.x = 1;
+ deepEqual(Benchmark.extend({}, new Source), { 'x': 1 });
+ });
+
+ test('will add problem JScript properties', function() {
+ deepEqual(Benchmark.extend({}, shadowed), shadowed);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.filter');
+
+ (function() {
+ var objects = {
+ 'array': ['a', 'b', 'c', ''],
+ 'array-like-object': { '0': 'a', '1': 'b', '2': 'c', '3': '', 'length': 5 }
+ };
+
+ objects.array.length = 5;
+
+ forOwn(objects, function(object, key) {
+ test('passes the correct arguments when passing an ' + key, function() {
+ var args;
+ Benchmark.filter(object, function() {
+ args || (args = slice.call(arguments));
+ });
+
+ deepEqual(args, ['a', 0, object]);
+ });
+
+ test('produces the correct result when passing an ' + key, function() {
+ var actual = Benchmark.filter(object, function(value, index) {
+ return index > 0;
+ });
+
+ deepEqual(actual, ['b', 'c', '']);
+ });
+
+ test('iterates over sparse ' + key + 's correctly', function() {
+ var actual = Benchmark.filter(object, function(value) {
+ return value === undefined;
+ });
+
+ deepEqual(actual, []);
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.forOwn');
+
+ (function() {
+ function fn() {
+ // no-op
+ }
+
+ function KlassA() {
+ this.a = 1;
+ this.b = 2;
+ this.c = 3;
+ }
+
+ function KlassB() {
+ this.a = 1;
+ this.constructor = 2;
+ this.hasOwnProperty = 3;
+ this.isPrototypeOf = 4;
+ this.propertyIsEnumerable = 5;
+ this.toLocaleString = 6;
+ this.toString = 7;
+ this.valueOf = 8;
+ }
+
+ function KlassC() {
+ // no-op
+ }
+
+ fn.a = 1;
+ fn.b = 2;
+ fn.c = 3;
+
+ KlassC.prototype.a = 1;
+ KlassC.prototype.b = 2;
+ KlassC.prototype.c = 3;
+
+ var objects = {
+ 'an arguments object': arguments,
+ 'a function': fn,
+ 'an object': new KlassA,
+ 'an object shadowing properties on Object.prototype': new KlassB,
+ 'a prototype object': KlassC.prototype,
+ 'a string': 'abc'
+ };
+
+ forOwn(objects, function(object, key) {
+ test('passes the correct arguments when passing ' + key, function() {
+ var args;
+ Benchmark.forOwn(object, function() {
+ args || (args = slice.call(arguments));
+ });
+
+ equal(typeof args[0], key == 'a string' ? 'string' : 'number');
+ equal(typeof args[1], 'string');
+ equal(args[2] && typeof args[2], key == 'a function' ? 'function' : 'object');
+ });
+
+ test('returns the passed object when passing ' + key, function() {
+ var actual = Benchmark.forOwn(object, function() { });
+ deepEqual(actual, object);
+ });
+
+ test('iterates over own properties when passing ' + key, function() {
+ var values = [];
+ Benchmark.forOwn(object, function(value) {
+ values.push(value);
+ });
+
+ if (object instanceof KlassB) {
+ deepEqual(values.sort(), [1, 2, 3, 4, 5, 6, 7, 8]);
+ } else if (key == 'a string') {
+ deepEqual(values, ['a', 'b', 'c']);
+ } else {
+ deepEqual(values.sort(), [1, 2, 3]);
+ }
+ });
+
+ test('exits early when returning `false` when passing ' + key, function() {
+ var values = [];
+ Benchmark.forOwn(object, function(value) {
+ values.push(value);
+ return false;
+ });
+
+ equal(values.length, 1);
+ });
+
+ if (object instanceof KlassB) {
+ test('exits correctly when transitioning to the JScript [[DontEnum]] fix', function() {
+ var values = [];
+ Benchmark.forOwn(object, function(value) {
+ values.push(value);
+ return values.length < 2;
+ });
+
+ equal(values.length, 2);
+ });
+ }
+ });
+ }(1, 2, 3));
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.formatNumber');
+
+ (function() {
+ test('formats a million correctly', function() {
+ equal(Benchmark.formatNumber(1e6), '1,000,000');
+ });
+
+ test('formats less than 100 correctly', function() {
+ equal(Benchmark.formatNumber(23), '23');
+ });
+
+ test('formats numbers with decimal values correctly', function() {
+ equal(Benchmark.formatNumber(1234.56), '1,234.56');
+ });
+
+ test('formats negative numbers correctly', function() {
+ equal(Benchmark.formatNumber(-1234.56), '-1,234.56');
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.hasKey');
+
+ (function() {
+ test('returns `true` for own properties', function() {
+ var object = { 'x': 1 };
+ equal(Benchmark.hasKey(object, 'x'), true);
+ });
+
+ test('returns `false` for inherited properties', function() {
+ equal(Benchmark.hasKey({}, 'toString'), false);
+ });
+
+ test('doesn\'t use an object\'s `hasOwnProperty` method', function() {
+ var object = { 'hasOwnProperty': function() { return true; } };
+ equal(Benchmark.hasKey(object, 'x'), false);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.indexOf');
+
+ (function() {
+ var objects = {
+ 'array': ['a', 'b', 'c', ''],
+ 'array-like-object': { '0': 'a', '1': 'b', '2': 'c', '3': '', 'length': 5 }
+ };
+
+ objects.array.length = 5;
+
+ forOwn(objects, function(object, key) {
+ test('produces the correct result when passing an ' + key, function() {
+ equal(Benchmark.indexOf(object, 'b'), 1);
+ });
+
+ test('matches values by strict equality when passing an ' + key, function() {
+ equal(Benchmark.indexOf(object, new String('b')), -1);
+ });
+
+ test('iterates over sparse ' + key + 's correctly', function() {
+ equal(Benchmark.indexOf(object, undefined), -1);
+ });
+ });
+
+ test('searches from the given `fromIndex`', function() {
+ var array = ['a', 'b', 'c', 'a'];
+ equal(Benchmark.indexOf(array, 'a', 1), 3);
+ });
+
+ test('handles extreme negative `fromIndex` values correctly', function() {
+ var array = ['a'];
+ array['-1'] = 'z';
+ equal(Benchmark.indexOf(array, 'z', -2), -1);
+ });
+
+ test('handles extreme positive `fromIndex` values correctly', function() {
+ var object = { '0': 'a', '1': 'b', '2': 'c', 'length': 2 };
+ equal(Benchmark.indexOf(object, 'c', 2), -1);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.interpolate');
+
+ (function() {
+ test('replaces tokens correctly', function() {
+ var actual = Benchmark.interpolate('#{greeting} #{location}.', {
+ 'greeting': 'Hello',
+ 'location': 'world'
+ });
+
+ equal(actual, 'Hello world.');
+ });
+
+ test('ignores inherited object properties', function() {
+ var actual = Benchmark.interpolate('x#{toString}', {});
+ equal(actual, 'x#{toString}');
+ });
+
+ test('allows for no template object', function() {
+ var actual = Benchmark.interpolate('x');
+ equal(actual, 'x');
+ });
+
+ test('replaces duplicate tokens', function() {
+ var actual = Benchmark.interpolate('#{x}#{x}#{x}', { 'x': 'a' });
+ equal(actual, 'aaa');
+ });
+
+ test('handles keys containing RegExp special characters', function() {
+ var actual = Benchmark.interpolate('#{.*+?^=!:${}()|[]\\/}', { '.*+?^=!:${}()|[]\\/': 'x' });
+ equal(actual, 'x');
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.invoke');
+
+ (function() {
+ var objects = {
+ 'array': ['a', ['b'], 'c', null],
+ 'array-like-object': { '0': 'a', '1': ['b'], '2': 'c', '3': null, 'length': 5 }
+ };
+
+ objects.array.length = 5;
+
+ forOwn(objects, function(object, key) {
+ test('produces the correct result when passing an ' + key, function() {
+ var actual = Benchmark.invoke(object, 'concat');
+ deepEqual(actual, ['a', ['b'], 'c', undefined, undefined]);
+ equal('4' in actual, false);
+ });
+
+ test('passes the correct arguments to the invoked method when passing an ' + key, function() {
+ var actual = Benchmark.invoke(object, 'concat', 'x', 'y', 'z');
+ deepEqual(actual, ['axyz', ['b', 'x', 'y', 'z'], 'cxyz', undefined, undefined]);
+ equal('4' in actual, false);
+ });
+
+ test('handles options object with callbacks correctly when passing an ' + key, function() {
+ function callback() {
+ callbacks.push(slice.call(arguments));
+ }
+
+ var callbacks = [];
+ var actual = Benchmark.invoke(object, {
+ 'name': 'concat',
+ 'args': ['x', 'y', 'z'],
+ 'onStart': callback,
+ 'onCycle': callback,
+ 'onComplete': callback
+ });
+
+ deepEqual(actual, ['axyz', ['b', 'x', 'y', 'z'], 'cxyz', undefined, undefined]);
+ equal('4' in actual, false);
+
+ equal(callbacks[0].length, 1);
+ equal(callbacks[0][0].target, 'a');
+ deepEqual(callbacks[0][0].currentTarget, object);
+ equal(callbacks[0][0].type, 'start');
+ equal(callbacks[1][0].type, 'cycle');
+ equal(callbacks[5][0].type, 'complete');
+ });
+
+ test('supports queuing when passing an ' + key, function() {
+ var lengths = [];
+ var actual = Benchmark.invoke(object, {
+ 'name': 'concat',
+ 'queued': true,
+ 'args': 'x',
+ 'onCycle': function() {
+ lengths.push(object.length);
+ }
+ });
+
+ deepEqual(lengths, [5, 4, 3, 2]);
+ deepEqual(actual, ['ax', ['b', 'x'], 'cx', undefined, undefined]);
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.join');
+
+ (function() {
+ var objects = {
+ 'array': ['a', 'b', ''],
+ 'array-like-object': { '0': 'a', '1': 'b', '2': '', 'length': 4 },
+ 'object': { 'a': '0', 'b': '1', '': '2' }
+ };
+
+ objects.array.length = 4;
+
+ forOwn(objects, function(object, key) {
+ test('joins correctly using the default separator when passing an ' + key, function() {
+ equal(Benchmark.join(object), key == 'object' ? 'a: 0,b: 1,: 2' : 'a,b,');
+ });
+
+ test('joins correctly using a custom separator when passing an ' + key, function() {
+ equal(Benchmark.join(object, '+', '@'), key == 'object' ? 'a@0+b@1+@2' : 'a+b+');
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.map');
+
+ (function() {
+ var objects = {
+ 'array': ['a', 'b', 'c', ''],
+ 'array-like-object': { '0': 'a', '1': 'b', '2': 'c', '3': '', 'length': 5 }
+ };
+
+ objects.array.length = 5;
+
+ forOwn(objects, function(object, key) {
+ test('passes the correct arguments when passing an ' + key, function() {
+ var args;
+ Benchmark.map(object, function() {
+ args || (args = slice.call(arguments));
+ });
+
+ deepEqual(args, ['a', 0, object]);
+ });
+
+ test('produces the correct result when passing an ' + key, function() {
+ var actual = Benchmark.map(object, function(value, index) {
+ return value + index;
+ });
+
+ deepEqual(actual, ['a0', 'b1', 'c2', '3', undefined]);
+ equal('4' in actual, false);
+ });
+
+ test('produces an array of the correct length for sparse ' + key + 's', function() {
+ equal(Benchmark.map(object, function() { }).length, 5);
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.pluck');
+
+ (function() {
+ var objects = {
+ 'array': [{ '_': 'a' }, { '_': 'b' }, { '_': 'c' }, null],
+ 'array-like-object': { '0': { '_': 'a' }, '1': { '_': 'b' }, '2': { '_': 'c' }, '3': null, 'length': 5 }
+ };
+
+ objects.array.length = 5;
+
+ forOwn(objects, function(object, key) {
+ test('produces the correct result when passing an ' + key, function() {
+ var actual = Benchmark.pluck(object, '_');
+ deepEqual(actual, ['a', 'b', 'c', undefined, undefined]);
+ equal('4' in actual, false);
+ });
+
+ test('produces the correct result for non-existent keys when passing an ' + key, function() {
+ var actual = Benchmark.pluck(object, 'non-existent');
+ deepEqual(actual, [undefined, undefined, undefined, undefined, undefined]);
+ equal('4' in actual, false);
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.reduce');
+
+ (function() {
+ var objects = {
+ 'array': ['b', 'c', ''],
+ 'array-like-object': { '0': 'b', '1': 'c', '2': '', 'length': 4 }
+ };
+
+ objects.array.length = 4;
+
+ forOwn(objects, function(object, key) {
+ test('passes the correct arguments when passing an ' + key, function() {
+ var args;
+ Benchmark.reduce(object, function() {
+ args || (args = slice.call(arguments));
+ }, 'a');
+
+ deepEqual(args, ['a', 'b', 0, object]);
+ });
+
+ test('accumulates correctly when passing an ' + key, function() {
+ var actual = Benchmark.reduce(object, function(string, value) {
+ return string + value;
+ }, 'a');
+
+ equal(actual, 'abc');
+ });
+
+ test('handles arguments with no initial value correctly when passing an ' + key, function() {
+ var args;
+ Benchmark.reduce(object, function() {
+ args || (args = slice.call(arguments));
+ });
+
+ deepEqual(args, ['b', 'c', 1, object]);
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark#clone');
+
+ (function() {
+ var bench = Benchmark(function() { this.count += 0; }).run();
+
+ test('produces the correct result passing no arguments', function() {
+ var clone = bench.clone();
+ deepEqual(clone, bench);
+ ok(clone.stats != bench.stats && clone.times != bench.times && clone.options != bench.options);
+ });
+
+ test('produces the correct result passing a data object', function() {
+ var clone = bench.clone({ 'fn': '', 'name': 'foo' });
+ ok(clone.fn === '' && clone.options.fn === '');
+ ok(clone.name == 'foo' && clone.options.name == 'foo');
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark#run');
+
+ (function() {
+ var data = { 'onComplete': 0, 'onCycle': 0, 'onStart': 0 };
+
+ var bench = Benchmark({
+ 'fn': function() {
+ this.count += 0;
+ },
+ 'onStart': function() {
+ data.onStart++;
+ },
+ 'onComplete': function() {
+ data.onComplete++;
+ }
+ })
+ .run();
+
+ test('onXYZ callbacks should not be triggered by internal benchmark clones', function() {
+ equal(data.onStart, 1);
+ equal(data.onComplete, 1);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ forOwn({
+ 'Benchmark': Benchmark,
+ 'Benchmark.Suite': Benchmark.Suite
+ },
+ function(Constructor, namespace) {
+
+ QUnit.module(namespace + '#emit');
+
+ (function() {
+ test('emits passed arguments', function() {
+ var args,
+ object = Constructor();
+
+ object.on('args', function() { args = slice.call(arguments, 1); });
+ object.emit('args', 'a', 'b', 'c');
+ deepEqual(args, ['a', 'b', 'c']);
+ });
+
+ test('emits with no listeners', function() {
+ var event = Benchmark.Event('empty'),
+ object = Constructor();
+
+ object.emit(event);
+ equal(event.cancelled, false);
+ });
+
+ test('emits with an event type of "toString"', function() {
+ var event = Benchmark.Event('toString'),
+ object = Constructor();
+
+ object.emit(event);
+ equal(event.cancelled, false);
+ });
+
+ test('returns the last listeners returned value', function() {
+ var event = Benchmark.Event('result'),
+ object = Constructor();
+
+ object.on('result', function() { return 'x'; });
+ object.on('result', function() { return 'y'; });
+ equal(object.emit(event), 'y');
+ });
+
+ test('aborts the emitters listener iteration when `event.aborted` is `true`', function() {
+ var event = Benchmark.Event('aborted'),
+ object = Constructor();
+
+ object.on('aborted', function(event) {
+ event.aborted = true;
+ return false;
+ });
+
+ object.on('aborted', function(event) {
+ // should not get here
+ event.aborted = false;
+ return true;
+ });
+
+ equal(object.emit(event), false);
+ equal(event.aborted, true);
+ });
+
+ test('cancels the event if a listener explicitly returns `false`', function() {
+ var event = Benchmark.Event('cancel'),
+ object = Constructor();
+
+ object.on('cancel', function() { return false; });
+ object.on('cancel', function() { return true; });
+ object.emit(event);
+ equal(event.cancelled, true);
+ });
+
+ test('uses a shallow clone of the listeners when emitting', function() {
+ var event,
+ listener2 = function(eventObject) { eventObject.listener2 = true },
+ object = Constructor();
+
+ object.on('shallowclone', function(eventObject) {
+ event = eventObject;
+ object.off(event.type, listener2);
+ })
+ .on('shallowclone', listener2)
+ .emit('shallowclone');
+
+ ok(event.listener2);
+ });
+
+ test('emits a custom event object', function() {
+ var event = Benchmark.Event('custom'),
+ object = Constructor();
+
+ object.on('custom', function(eventObject) { eventObject.touched = true; });
+ object.emit(event);
+ ok(event.touched);
+ });
+
+ test('sets `event.result` correctly', function() {
+ var event = Benchmark.Event('result'),
+ object = Constructor();
+
+ object.on('result', function() { return 'x'; });
+ object.emit(event);
+ equal(event.result, 'x');
+ });
+
+ test('sets `event.type` correctly', function() {
+ var event,
+ object = Constructor();
+
+ object.on('type', function(eventObj) {
+ event = eventObj;
+ });
+
+ object.emit('type');
+ equal(event.type, 'type');
+ });
+ }());
+
+ /*------------------------------------------------------------------------*/
+
+ QUnit.module(namespace + '#listeners');
+
+ (function() {
+ test('returns the correct listeners', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x', listener);
+ deepEqual(object.listeners('x'), [listener]);
+ });
+
+ test('returns an array and initializes previously uninitialized listeners', function() {
+ var object = Constructor();
+ deepEqual(object.listeners('x'), []);
+ deepEqual(object.events, { 'x': [] });
+ });
+ }());
+
+ /*------------------------------------------------------------------------*/
+
+ QUnit.module(namespace + '#off');
+
+ (function() {
+ test('returns the benchmark', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x', listener);
+ equal(object.off('x', listener), object);
+ });
+
+ test('will ignore inherited properties of the event cache', function() {
+ var Dummy = function() { },
+ listener = function() { },
+ object = Constructor();
+
+ Dummy.prototype.x = [listener];
+ object.events = new Dummy;
+
+ object.off('x', listener);
+ deepEqual(object.events.x, [listener]);
+ });
+
+ test('handles an event type and listener', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x', listener);
+ object.off('x', listener);
+ deepEqual(object.events.x, []);
+ });
+
+ test('handles unregistering duplicate listeners', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x', listener);
+ object.on('x', listener);
+
+ var events = object.events;
+ object.off('x', listener);
+ deepEqual(events.x, [listener]);
+
+ object.off('x', listener);
+ deepEqual(events.x, []);
+ });
+
+ test('handles a non-registered listener', function() {
+ var object = Constructor();
+ object.off('x', function() { });
+ equal(object.events, undefined);
+ });
+
+ test('handles space separated event type and listener', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x', listener);
+ object.on('y', listener);
+
+ var events = object.events;
+ object.off('x y', listener);
+ deepEqual(events.x, []);
+ deepEqual(events.y, []);
+ });
+
+ test('handles space separated event type and no listener', function() {
+ var listener1 = function() { },
+ listener2 = function() { },
+ object = Constructor();
+
+ object.on('x', listener1);
+ object.on('y', listener2);
+
+ var events = object.events;
+ object.off('x y');
+ deepEqual(events.x, []);
+ deepEqual(events.y, []);
+ });
+
+ test('handles no arguments', function() {
+ var listener1 = function() { },
+ listener2 = function() { },
+ listener3 = function() { },
+ object = Constructor();
+
+ object.on('x', listener1);
+ object.on('y', listener2);
+ object.on('z', listener3);
+
+ var events = object.events;
+ object.off();
+ deepEqual(events.x, []);
+ deepEqual(events.y, []);
+ deepEqual(events.z, []);
+ });
+ }());
+
+ /*------------------------------------------------------------------------*/
+
+ QUnit.module(namespace + '#on');
+
+ (function() {
+ test('returns the benchmark', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ equal(object.on('x', listener), object);
+ });
+
+ test('will ignore inherited properties of the event cache', function() {
+ var Dummy = function() { },
+ listener1 = function() { },
+ listener2 = function() { },
+ object = Constructor();
+
+ Dummy.prototype.x = [listener1];
+ object.events = new Dummy;
+
+ object.on('x', listener2);
+ deepEqual(object.events.x, [listener2]);
+ });
+
+ test('handles an event type and listener', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x', listener);
+ deepEqual(object.events.x, [listener]);
+ });
+
+ test('handles registering duplicate listeners', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x', listener);
+ object.on('x', listener);
+ deepEqual(object.events.x, [listener, listener]);
+ });
+
+ test('handles space separated event type and listener', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x y', listener);
+
+ var events = object.events;
+ deepEqual(events.x, [listener]);
+ deepEqual(events.y, [listener]);
+ });
+ }());
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite#abort');
+
+ (function() {
+ test('igores abort calls when the suite isn\'t running', function() {
+ var fired = false;
+ var suite = Benchmark.Suite('suite', {
+ 'onAbort': function() { fired = true; }
+ });
+
+ suite.add('foo', function() { });
+ suite.abort();
+ equal(fired, false);
+ });
+
+ test('ignores abort calls from `Benchmark.Suite#reset` when the suite isn\'t running', function() {
+ var fired = false;
+ var suite = Benchmark.Suite('suite', {
+ 'onAbort': function() { fired = true; }
+ });
+
+ suite.add('foo', function() { });
+ suite.reset();
+ equal(fired, false);
+ });
+
+ asyncTest('emits an abort event when running', function() {
+ var fired = false;
+
+ Benchmark.Suite({
+ 'onAbort': function() { fired = true; }
+ })
+ .on('start', function() {
+ this.abort();
+ })
+ .on('complete', function() {
+ ok(fired);
+ QUnit.start();
+ })
+ .add(function(){ })
+ .run({ 'async': true });
+ });
+
+ asyncTest('emits an abort event after calling `Benchmark.Suite#reset`', function() {
+ var fired = false;
+
+ Benchmark.Suite({
+ 'onAbort': function() { fired = true; }
+ })
+ .on('start', function() {
+ this.reset();
+ })
+ .on('complete', function() {
+ ok(fired);
+ QUnit.start();
+ })
+ .add(function(){ })
+ .run({ 'async': true });
+ });
+
+ asyncTest('should abort deferred benchmark', function() {
+ var fired = false,
+ suite = Benchmark.Suite();
+
+ suite.on('complete', function() {
+ equal(fired, false);
+ QUnit.start();
+ })
+ .add('a', {
+ 'defer': true,
+ 'fn': function(deferred) {
+ // avoid test inlining
+ suite.name;
+ // delay resolve
+ setTimeout(function() {
+ deferred.resolve();
+ suite.abort();
+ }, 10);
+ }
+ })
+ .add('b', {
+ 'defer': true,
+ 'fn': function(deferred) {
+ // avoid test inlining
+ suite.name;
+ // delay resolve
+ setTimeout(function() {
+ deferred.resolve();
+ fired = true;
+ }, 10);
+ }
+ })
+ .run();
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite#concat');
+
+ (function() {
+ var args = arguments;
+
+ test('doesn\'t treat an arguments object like an array', function() {
+ var suite = Benchmark.Suite();
+ deepEqual(suite.concat(args), [args]);
+ });
+
+ test('flattens array arguments', function() {
+ var suite = Benchmark.Suite();
+ deepEqual(suite.concat([1, 2], 3, [4, 5]), [1, 2, 3, 4, 5]);
+ });
+
+ test('supports concating sparse arrays', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[2] = 2;
+ suite.length = 3;
+
+ var actual = suite.concat(3);
+ deepEqual(actual, [0, undefined, 2, 3]);
+ equal('1' in actual, false);
+ });
+
+ test('supports sparse arrays as arguments', function() {
+ var suite = Benchmark.Suite(),
+ sparse = [];
+
+ sparse[0] = 0;
+ sparse[2] = 2;
+ sparse.length = 3;
+
+ var actual = suite.concat(sparse);
+ deepEqual(actual, [0, undefined, 2]);
+ equal('1' in actual, false);
+ });
+
+ test('creates a new array', function() {
+ var suite = Benchmark.Suite();
+ ok(suite.concat(1) !== suite);
+ });
+ }(1, 2, 3));
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite#reverse');
+
+ (function() {
+ test('reverses the element order', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 1;
+ suite.length = 2;
+
+ var actual = suite.reverse();
+ equal(actual, suite);
+ deepEqual(slice.call(actual), [1, 0]);
+ });
+
+ test('supports reversing sparse arrays', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[2] = 2;
+ suite.length = 3;
+
+ var actual = suite.reverse();
+ equal(actual, suite);
+ deepEqual(slice.call(actual), [2, undefined, 0]);
+ equal('1' in actual, false);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite#shift');
+
+ (function() {
+ test('removes the first element', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 1;
+ suite.length = 2;
+
+ var actual = suite.shift();
+ equal(actual, 0);
+ deepEqual(slice.call(suite), [1]);
+ });
+
+ test('shifts an object with no elements', function() {
+ var suite = Benchmark.Suite(),
+ actual = suite.shift();
+
+ equal(actual, undefined);
+ deepEqual(slice.call(suite), []);
+ });
+
+ test('should have no elements when length is 0 after shift', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite.length = 1;
+ suite.shift();
+
+ // ensure element is removed
+ equal('0' in suite, false);
+ equal(suite.length, 0);
+ });
+
+ test('supports shifting sparse arrays', function() {
+ var suite = Benchmark.Suite();
+ suite[1] = 1;
+ suite[3] = 3;
+ suite.length = 4;
+
+ var actual = suite.shift();
+ equal(actual, undefined);
+ deepEqual(slice.call(suite), [1, undefined, 3]);
+ equal('1' in suite, false);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite#slice');
+
+ (function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 1;
+ suite[2] = 2;
+ suite[3] = 3;
+ suite.length = 4;
+
+ test('works with no arguments', function() {
+ var actual = suite.slice();
+ deepEqual(actual, [0, 1, 2, 3]);
+ ok(suite !== actual);
+ });
+
+ test('works with positive `start` argument', function() {
+ var actual = suite.slice(2);
+ deepEqual(actual, [2, 3]);
+ ok(suite !== actual);
+ });
+
+ test('works with positive `start` and `end` arguments', function() {
+ var actual = suite.slice(1, 3);
+ deepEqual(actual, [1, 2]);
+ ok(suite !== actual);
+ });
+
+ test('works with `end` values exceeding length', function() {
+ var actual = suite.slice(1, 10);
+ deepEqual(actual, [1, 2, 3]);
+ ok(suite !== actual);
+ });
+
+ test('works with negative `start` and `end` arguments', function() {
+ var actual = suite.slice(-3, -1);
+ deepEqual(actual, [1, 2]);
+ ok(suite !== actual);
+ });
+
+ test('works with an extreme negative `end` value', function() {
+ var actual = suite.slice(1, -10);
+ deepEqual(actual, []);
+ equal('-1' in actual, false);
+ ok(suite !== actual);
+ });
+
+ test('supports slicing sparse arrays', function() {
+ var sparse = Benchmark.Suite();
+ sparse[1] = 1;
+ sparse[3] = 3;
+ sparse.length = 4;
+
+ var actual = sparse.slice(0, 2);
+ deepEqual(actual, [undefined, 1]);
+ equal('0' in actual, false);
+
+ actual = sparse.slice(1);
+ deepEqual(actual, [1, undefined, 3]);
+ equal('1' in actual, false);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite#splice');
+
+ (function() {
+ test('works with no arguments', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite.length = 1;
+
+ var actual = suite.splice();
+ deepEqual(actual, []);
+ deepEqual(slice.call(suite), [0]);
+ });
+
+ test('works with only the `start` argument', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 1;
+ suite.length = 2;
+
+ var actual = suite.splice(1);
+ deepEqual(actual, [1]);
+ deepEqual(slice.call(suite), [0]);
+ });
+
+ test('should have no elements when length is 0 after splice', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite.length = 1
+ suite.splice(0, 1);
+
+ // ensure element is removed
+ equal('0' in suite, false);
+ equal(suite.length, 0);
+ });
+
+ test('works with positive `start` argument', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 3;
+ suite.length = 2;
+
+ var actual = suite.splice(1, 0, 1, 2);
+ deepEqual(actual, []);
+ deepEqual(slice.call(suite), [0, 1, 2, 3]);
+ });
+
+ test('works with positive `start` and `deleteCount` arguments', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 3;
+ suite.length = 2;
+
+ var actual = suite.splice(1, 1, 1, 2);
+ deepEqual(actual, [3]);
+ deepEqual(slice.call(suite), [0, 1, 2]);
+ });
+
+ test('works with `deleteCount` values exceeding length', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 3;
+ suite.length = 2;
+
+ var actual = suite.splice(1, 10, 1, 2);
+ deepEqual(actual, [3]);
+ deepEqual(slice.call(suite), [0, 1, 2]);
+ });
+
+ test('works with negative `start` and `deleteCount` arguments', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 3;
+ suite.length = 2;
+
+ var actual = suite.splice(-1, -1, 1, 2);
+ deepEqual(actual, []);
+ deepEqual(slice.call(suite), [0, 1, 2, 3]);
+ });
+
+ test('works with an extreme negative `deleteCount` value', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 3;
+ suite.length = 2;
+
+ var actual = suite.splice(0, -10, 1, 2);
+ deepEqual(actual, []);
+ deepEqual(slice.call(suite), [1, 2, 0, 3]);
+ });
+
+ test('supports splicing sparse arrays', function() {
+ var suite = Benchmark.Suite();
+ suite[1] = 1;
+ suite[3] = 3;
+ suite.length = 4;
+
+ var actual = suite.splice(1, 2, 1, 2);
+ deepEqual(actual, [1, undefined]);
+ equal(actual.length, 2);
+ deepEqual(slice.call(suite), [undefined, 1, 2, 3]);
+ equal('0' in suite, false);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite#unshift');
+
+ (function() {
+ test('adds a first element', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 1;
+ suite.length = 1;
+
+ var actual = suite.unshift(0);
+ equal(actual, 2);
+ deepEqual(slice.call(suite), [0, 1]);
+ });
+
+ test('adds multiple elements to the front', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 3;
+ suite.length = 1;
+
+ var actual = suite.unshift(0, 1, 2);
+ equal(actual, 4);
+ deepEqual(slice.call(suite), [0, 1, 2, 3]);
+ });
+
+ test('supports unshifting sparse arrays', function() {
+ var suite = Benchmark.Suite();
+ suite[1] = 2;
+ suite.length = 2;
+
+ var actual = suite.unshift(0);
+ equal(actual, 3);
+ deepEqual(slice.call(suite), [0, undefined, 2]);
+ equal('1' in suite, false);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite filtered results onComplete');
+
+ (function() {
+ var count = 0,
+ suite = Benchmark.Suite();
+
+ suite.add('a', function() {
+ count++;
+ })
+ .add('b', function() {
+ for (var i = 0; i < 1e6; i++) {
+ count++;
+ }
+ })
+ .add('c', function() {
+ throw new TypeError;
+ });
+
+ asyncTest('should filter by fastest', function() {
+ suite.on('complete', function() {
+ suite.off();
+ deepEqual(this.filter('fastest').pluck('name'), ['a']);
+ QUnit.start();
+ })
+ .run({ 'async': true });
+ });
+
+ asyncTest('should filter by slowest', function() {
+ suite.on('complete', function() {
+ suite.off();
+ deepEqual(this.filter('slowest').pluck('name'), ['b']);
+ QUnit.start();
+ })
+ .run({ 'async': true });
+ });
+
+ asyncTest('should filter by successful', function() {
+ suite.on('complete', function() {
+ suite.off();
+ deepEqual(this.filter('successful').pluck('name'), ['a', 'b']);
+ QUnit.start();
+ })
+ .run({ 'async': true });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite event flow');
+
+ (function() {
+ var events = [],
+ callback = function(event) { events.push(event); };
+
+ var suite = Benchmark.Suite('suite', {
+ 'onAdd': callback,
+ 'onAbort': callback,
+ 'onClone': callback,
+ 'onError': callback,
+ 'onStart': callback,
+ 'onCycle': callback,
+ 'onComplete': callback,
+ 'onReset': callback
+ })
+ .add('bench', function() {
+ throw null;
+ }, {
+ 'onAbort': callback,
+ 'onClone': callback,
+ 'onError': callback,
+ 'onStart': callback,
+ 'onCycle': callback,
+ 'onComplete': callback,
+ 'onReset': callback
+ })
+ .run({ 'async': false });
+
+ // first Suite#onAdd
+ test('should emit the suite "add" event first', function() {
+ var event = events[0];
+ ok(event.type == 'add' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+ });
+
+ // next we start the Suite because no reset was needed
+ test('should emit the suite "start" event', function() {
+ var event = events[1];
+ ok(event.type == 'start' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+ });
+
+ // and so start the first benchmark
+ test('should emit the benchmark "start" event', function() {
+ var event = events[2];
+ ok(event.type == 'start' && event.currentTarget.name == 'bench');
+ });
+
+ // oh no! we abort because of an error
+ test('should emit the benchmark "error" event', function() {
+ var event = events[3];
+ ok(event.type == 'error' && event.currentTarget.name == 'bench');
+ });
+
+ // benchmark error triggered
+ test('should emit the benchmark "abort" event', function() {
+ var event = events[4];
+ ok(event.type == 'abort' && event.currentTarget.name == 'bench');
+ });
+
+ // we reset the benchmark as part of the abort
+ test('should emit the benchmark "reset" event', function() {
+ var event = events[5];
+ ok(event.type == 'reset' && event.currentTarget.name == 'bench');
+ });
+
+ // benchmark is cycle is finished
+ test('should emit the benchmark "cycle" event', function() {
+ var event = events[6];
+ ok(event.type == 'cycle' && event.currentTarget.name == 'bench');
+ });
+
+ // benchmark is complete
+ test('should emit the benchmark "complete" event', function() {
+ var event = events[7];
+ ok(event.type == 'complete' && event.currentTarget.name == 'bench');
+ });
+
+ // the benchmark error triggers a Suite error
+ test('should emit the suite "error" event', function() {
+ var event = events[8];
+ ok(event.type == 'error' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+ });
+
+ // the Suite cycle finishes
+ test('should emit the suite "cycle" event', function() {
+ var event = events[9];
+ ok(event.type == 'cycle' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+ });
+
+ // the Suite completes
+ test('finally it should emit the suite "complete" event', function() {
+ var event = events[10];
+ ok(event.type == 'complete' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+ });
+
+ test('emitted all expected events', function() {
+ ok(events.length == 11);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Deferred benchmarks');
+
+ (function() {
+ asyncTest('should run a deferred benchmark correctly', function() {
+ Benchmark(function(deferred) {
+ setTimeout(function() { deferred.resolve(); }, 1e3);
+ }, {
+ 'defer': true,
+ 'onComplete': function() {
+ equal(this.hz.toFixed(0), 1);
+ QUnit.start();
+ }
+ })
+ .run();
+ });
+
+ asyncTest('should run with string values for "fn", "setup", and "teardown"', function() {
+ Benchmark({
+ 'defer': true,
+ 'setup': 'var x = [3, 2, 1];',
+ 'fn': 'setTimeout(function() { x.sort(); deferred.resolve(); }, 10);',
+ 'teardown': 'x.length = 0;',
+ 'onComplete': function() {
+ ok(true);
+ QUnit.start();
+ }
+ })
+ .run();
+ });
+
+ asyncTest('should run recursively', function() {
+ Benchmark({
+ 'defer': true,
+ 'setup': 'var x = [3, 2, 1];',
+ 'fn': 'for (var i = 0; i < 100; i++) x[ i % 2 ? "sort" : "reverse" ](); deferred.resolve();',
+ 'teardown': 'x.length = 0;',
+ 'onComplete': function() {
+ ok(true);
+ QUnit.start();
+ }
+ })
+ .run();
+ });
+
+ asyncTest('should execute "setup", "fn", and "teardown" in correct order', function() {
+ var fired = [];
+
+ Benchmark({
+ 'defer': true,
+ 'setup': function() {
+ fired.push('setup');
+ },
+ 'fn': function(deferred) {
+ fired.push('fn');
+ setTimeout(function() { deferred.resolve(); }, 10);
+ },
+ 'teardown': function() {
+ fired.push('teardown');
+ },
+ 'onComplete': function() {
+ var actual = fired.join().replace(/(fn,)+/g, '$1').replace(/(setup,fn,teardown(?:,|$))+/, '$1');
+ equal(actual, 'setup,fn,teardown');
+ QUnit.start();
+ }
+ })
+ .run();
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.deepClone');
+
+ (function() {
+ asyncTest('avoids call stack limits', function() {
+ var result,
+ count = 0,
+ object = {},
+ recurse = function() { count++; recurse(); };
+
+ setTimeout(function() {
+ ok(result, 'avoids call stack limits (stack limit is ' + (count - 1) + ')');
+ QUnit.start();
+ }, 15);
+
+ if (toString.call(window.java) == '[object JavaPackage]') {
+ // Java throws uncatchable errors on call stack overflows, so to avoid
+ // them I chose a number higher than Rhino's call stack limit without
+ // dynamically testing for the actual limit
+ count = 3e3;
+ } else {
+ try { recurse(); } catch(e) { }
+ }
+
+ // exceed limit
+ count++;
+ for (var i = 0, sub = object; i <= count; i++) {
+ sub = sub[i] = {};
+ }
+
+ try {
+ for (var i = 0, sub = Benchmark.deepClone(object); sub = sub[i]; i++) { }
+ result = --i == count;
+ } catch(e) { }
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ // explicitly call `QUnit.start()` for Narwhal, Rhino, and RingoJS
+ if (!window.document) {
+ QUnit.start();
+ }
+}(typeof global == 'object' && global || this));