瀏覽代碼

Add unit tests

Covers construction, cloning, json import/export. Use lodash from npm.
dubejf 10 年之前
父節點
當前提交
faa8745f58
共有 28 個文件被更改,包括 937 次插入10 次删除
  1. 2 1
      package.json
  2. 177 0
      test/unit/SmartComparer.js
  3. 4 4
      test/unit/extras/ImageUtils.test.js
  4. 38 0
      test/unit/extras/geometries/BoxGeometry.tests.js
  5. 38 0
      test/unit/extras/geometries/CircleBufferGeometry.tests.js
  6. 38 0
      test/unit/extras/geometries/CircleGeometry.tests.js
  7. 46 0
      test/unit/extras/geometries/CylinderGeometry.tests.js
  8. 34 0
      test/unit/extras/geometries/DodecahedronGeometry.tests.js
  9. 1 0
      test/unit/extras/geometries/ExtrudeGeometry.tests.js
  10. 34 0
      test/unit/extras/geometries/IcosahedronGeometry.tests.js
  11. 1 0
      test/unit/extras/geometries/LatheGeometry.tests.js
  12. 34 0
      test/unit/extras/geometries/OctahedronGeometry.tests.js
  13. 1 0
      test/unit/extras/geometries/ParametricGeometry.tests.js
  14. 38 0
      test/unit/extras/geometries/PlaneBufferGeometry.tests.js
  15. 38 0
      test/unit/extras/geometries/PlaneGeometry.tests.js
  16. 1 0
      test/unit/extras/geometries/PolyhedronGeometry.tests.js
  17. 42 0
      test/unit/extras/geometries/RingGeometry.tests.js
  18. 1 0
      test/unit/extras/geometries/ShapeGeometry.tests.js
  19. 44 0
      test/unit/extras/geometries/SphereBufferGeometry.tests.js
  20. 44 0
      test/unit/extras/geometries/SphereGeometry.tests.js
  21. 34 0
      test/unit/extras/geometries/TetrahedronGeometry.tests.js
  22. 1 0
      test/unit/extras/geometries/TextGeometry.tests.js
  23. 40 0
      test/unit/extras/geometries/TorusGeometry.tests.js
  24. 42 0
      test/unit/extras/geometries/TorusKnotGeometry.tests.js
  25. 1 0
      test/unit/extras/geometries/TubeGeometry.tests.js
  26. 1 0
      test/unit/extras/geometries/WireframeGeometry.tests.js
  27. 130 2
      test/unit/qunit-utils.js
  28. 32 3
      test/unit/unittests_three.html

+ 2 - 1
package.json

@@ -28,6 +28,7 @@
   },
   "homepage": "http://threejs.org/",
   "devDependencies": {
-    "jscs": "^1.13.1"
+    "jscs": "^1.13.1",
+    "lodash": "^3.10.0"
   }
 }

+ 177 - 0
test/unit/SmartComparer.js

@@ -0,0 +1,177 @@
+// Smart comparison of three.js objects.
+// Identifies significant differences between two objects.
+// Performs deep comparison.
+// Comparison stops after the first difference is found.
+// Provides an explanation for the failure.
+function SmartComparer() {
+	'use strict';
+
+	// Diagnostic message, when comparison fails.
+	var message;
+
+	// Keys to skip during object comparison.
+	var omitKeys = [ 'id', 'uuid' ];
+
+	return {
+
+		areEqual: areEqual,
+
+		getDiagnostic: function() {
+
+			return message;
+
+		}
+
+	};
+
+
+	// val1 - first value to compare (typically the actual value)
+	// val2 - other value to compare (typically the expected value)
+	function areEqual( val1, val2 ) {
+
+		// Values are strictly equal.
+		if ( val1 === val2 ) return true;
+
+		// Null or undefined values.
+		/* jshint eqnull:true */
+		if ( val1 == null || val2 == null ) {
+
+			if ( val1 != val2 ) {
+
+				return makeFail( 'One value is undefined or null', val1, val2 );
+
+			}
+
+			// Both null / undefined.
+			return true;
+		}
+
+		// Don't compare functions.
+		if ( _.isFunction( val1 ) && _.isFunction( val2 ) ) return true;
+
+		// Array comparison.
+		var arrCmp = compareArrays( val1, val2 );
+		if ( arrCmp !== undefined ) return arrCmp;
+
+		// Has custom equality comparer.
+		if ( val1.equals ) {
+
+			if ( val1.equals( val2 ) ) return true;
+
+			return makeFail( 'Comparison with .equals method returned false' );
+
+		}
+
+		// Object comparison.
+		var objCmp = compareObjects( val1, val2 );
+		if ( objCmp !== undefined ) return objCmp;
+
+		// if (JSON.stringify( val1 ) == JSON.stringify( val2 ) ) return true;
+
+		// Continue with default comparison.
+		if ( _.isEqual( val1, val2 ) ) return true;
+
+		// Object differs (unknown reason).
+		return makeFail( 'Values differ', val1, val2 );
+	}
+
+
+	function compareArrays( val1, val2 ) {
+
+		var isArr1 = Array.isArray( val1 );
+		var isArr2 = Array.isArray( val2 );
+
+		// Compare type.
+		if ( isArr1 !== isArr2 ) return makeFail( 'Values are not both arrays' );
+
+		// Not arrays. Continue.
+		if ( !isArr1 ) return undefined;
+
+		// Compare length.
+		var N1 = val1.length;
+		var N2 = val2.length;
+		if ( N1 !== val2.length ) return makeFail( 'Array length differs', N1, N2 );
+
+		// Compare content at each index.
+		for ( var i = 0; i < N1; i ++ ) {
+
+			var cmp = areEqual( val1[ i ], val2[ i ] );
+			if ( !cmp )	return addContext( 'array index "' + i + '"' );
+
+		}
+
+		// Arrays are equal.
+		return true;
+	}
+
+
+	function compareObjects( val1, val2 ) {
+
+		var isObj1 = _.isObject( val1 );
+		var isObj2 = _.isObject( val2 );
+
+		// Compare type.
+		if ( isObj1 !== isObj2 ) return makeFail( 'Values are not both objects' );
+
+		// Not objects. Continue.
+		if ( !isObj1 ) return undefined;
+
+		// Compare keys.
+		var keys1 = _( val1 ).keys().difference( omitKeys ).value();
+		var keys2 = _( val2 ).keys().difference( omitKeys ).value();
+
+		var missingActual = _.difference( keys1, keys2 );
+		if ( missingActual.length !== 0 ) {
+
+			return makeFail( 'Property "' + missingActual[0] + '" is unexpected.' );
+
+		}
+
+		var missingExpected = _.difference( keys2, keys1 );
+		if ( missingExpected.length !== 0 ) {
+
+			return makeFail( 'Property "' + missingExpected[0] + '" is missing.' );
+
+		}
+
+		// Keys are the same. For each key, compare content until a difference is found.
+		var hadDifference = _.any( keys1, function ( key ) {
+
+			var prop1 = val1[key];
+			var prop2 = val2[key];
+
+			// Compare property content.
+			var eq = areEqual( prop1, prop2 );
+
+			// In case of failure, an message should already be set.
+			// Add context to low level message.
+			if ( !eq ) addContext( 'property "' + key + '"' );
+			return !eq;
+
+		});
+
+		return ! hadDifference;
+
+	}
+
+
+	function makeFail( msg, val1, val2 ) {
+
+		message = msg;
+		if ( arguments.length > 1) message += " (" + val1 + " vs " + val2 + ")";
+
+		return false;
+
+	}
+
+	function addContext( msg ) {
+
+		// There should already be a validation message. Add more context to it.
+		message = message || "Error";
+		message += ", at " + msg;
+
+		return false;
+
+	}
+
+}

+ 4 - 4
test/unit/extras/ImageUtils.test.js

@@ -63,13 +63,13 @@ QUnit.test( "test cached texture", function( assert ) {
 
 		assert.ok( rtex1.image !== undefined, "texture 1 image is loaded" );
 		assert.equal( rtex1, tex1, "texture 1 callback is equal to return" );
-		
+
 		var rtex2 = THREE.ImageUtils.loadTexture( good_url, undefined, function ( tex2 ) {
-			
+
 			assert.ok( rtex2 !== undefined, "cached callback is async" );
 			assert.ok( rtex2.image !== undefined, "texture 2 image is loaded" );
 			assert.equal( rtex2, tex2, "texture 2 callback is equal to return" );
-			
+
 			done();
 
 		});
@@ -77,7 +77,7 @@ QUnit.test( "test cached texture", function( assert ) {
 		assert.ok( rtex2, "texture 2 return is defined" );
 
 	});
-	
+
 	assert.ok( rtex1, "texture 1 return is defined" );
 	assert.ok( rtex1.image === undefined, "texture 1 image is not loaded" );
 

+ 38 - 0
test/unit/extras/geometries/BoxGeometry.tests.js

@@ -0,0 +1,38 @@
+(function () {
+
+	'use strict';
+
+	var parameters = {
+		width: 10,
+		height: 20,
+		depth: 30,
+		widthSegments: 2,
+		heightSegments: 3,
+		depthSegments: 4,
+	};
+
+	var geometries;
+	var box, cube, boxWithSegments;
+
+	QUnit.module( "Extras - Geometries - BoxGeometry", {
+
+		beforeEach: function() {
+
+			box = new THREE.BoxGeometry( parameters.width, parameters.height, parameters.depth );
+			cube = new THREE.CubeGeometry( parameters.width, parameters.height, parameters.depth );
+			boxWithSegments = new THREE.BoxGeometry( parameters.width, parameters.height, parameters.depth,
+													 parameters.widthSegments, parameters.heightSegments, parameters.depthSegments );
+
+			geometries = [ box, cube, boxWithSegments ];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 38 - 0
test/unit/extras/geometries/CircleBufferGeometry.tests.js

@@ -0,0 +1,38 @@
+(function () {
+
+	'use strict';
+
+	var parameters = {
+		radius: 10,
+		segments: 20,
+		thetaStart: 0.1,
+		thetaLength: 0.2
+	};
+
+	var geometries;
+
+	QUnit.module( "Extras - Geometries - CircleBufferGeometry", {
+
+		beforeEach: function() {
+
+			geometries = [
+
+				new THREE.CircleBufferGeometry(),
+				new THREE.CircleBufferGeometry( parameters.radius ),
+				new THREE.CircleBufferGeometry( parameters.radius, parameters.segments ),
+				new THREE.CircleBufferGeometry( parameters.radius, parameters.segments, parameters.thetaStart ),
+				new THREE.CircleBufferGeometry( parameters.radius, parameters.segments, parameters.thetaStart, parameters.thetaLength ),
+
+			];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 38 - 0
test/unit/extras/geometries/CircleGeometry.tests.js

@@ -0,0 +1,38 @@
+(function () {
+
+	'use strict';
+
+	var parameters = {
+		radius: 10,
+		segments: 20,
+		thetaStart: 0.1,
+		thetaLength: 0.2
+	};
+
+	var geometries;
+
+	QUnit.module( "Extras - Geometries - CircleGeometry", {
+
+		beforeEach: function() {
+
+			geometries = [
+
+				new THREE.CircleGeometry(),
+				new THREE.CircleGeometry( parameters.radius ),
+				new THREE.CircleGeometry( parameters.radius, parameters.segments ),
+				new THREE.CircleGeometry( parameters.radius, parameters.segments, parameters.thetaStart ),
+				new THREE.CircleGeometry( parameters.radius, parameters.segments, parameters.thetaStart, parameters.thetaLength ),
+
+			];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 46 - 0
test/unit/extras/geometries/CylinderGeometry.tests.js

@@ -0,0 +1,46 @@
+(function () {
+
+	'use strict';
+
+	var parameters = {
+		radiusTop: 10,
+		radiusBottom: 20,
+		height: 30,
+		radialSegments: 20,
+		heightSegments: 30,
+		openEnded: true,
+		thetaStart: 0.1,
+		thetaLength: 2.0,
+	};
+
+	var geometries;
+
+	QUnit.module( "Extras - Geometries - CylinderGeometry", {
+
+		beforeEach: function() {
+
+			geometries = [
+
+				new THREE.CylinderGeometry(),
+				new THREE.CylinderGeometry( parameters.radiusTop ),
+				new THREE.CylinderGeometry( parameters.radiusTop, parameters.radiusBottom ),
+				new THREE.CylinderGeometry( parameters.radiusTop, parameters.radiusBottom, parameters.height ),
+				new THREE.CylinderGeometry( parameters.radiusTop, parameters.radiusBottom, parameters.height, parameters.radialSegments ),
+				new THREE.CylinderGeometry( parameters.radiusTop, parameters.radiusBottom, parameters.height, parameters.radialSegments, parameters.heightSegments ),
+				new THREE.CylinderGeometry( parameters.radiusTop, parameters.radiusBottom, parameters.height, parameters.radialSegments, parameters.heightSegments, parameters.openEnded ),
+				new THREE.CylinderGeometry( parameters.radiusTop, parameters.radiusBottom, parameters.height, parameters.radialSegments, parameters.heightSegments, parameters.openEnded, parameters.thetaStart ),
+				new THREE.CylinderGeometry( parameters.radiusTop, parameters.radiusBottom, parameters.height, parameters.radialSegments, parameters.heightSegments, parameters.openEnded, parameters.thetaStart, parameters.thetaLength ),
+
+			];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 34 - 0
test/unit/extras/geometries/DodecahedronGeometry.tests.js

@@ -0,0 +1,34 @@
+(function () {
+
+	'use strict';
+
+	var parameters = {
+		radius: 10,
+		detail: undefined
+	};
+
+	var geometries;
+
+	QUnit.module( "Extras - Geometries - DodecahedronGeometry", {
+
+		beforeEach: function() {
+
+			geometries = [
+
+				new THREE.DodecahedronGeometry(),
+				new THREE.DodecahedronGeometry( parameters.radius ),
+				new THREE.DodecahedronGeometry( parameters.radius, parameters.detail ),
+
+			];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 1 - 0
test/unit/extras/geometries/ExtrudeGeometry.tests.js

@@ -0,0 +1 @@
+// TODO

+ 34 - 0
test/unit/extras/geometries/IcosahedronGeometry.tests.js

@@ -0,0 +1,34 @@
+(function () {
+
+	'use strict';
+
+	var parameters = {
+		radius: 10,
+		detail: undefined
+	};
+
+	var geometries;
+
+	QUnit.module( "Extras - Geometries - IcosahedronGeometry", {
+
+		beforeEach: function() {
+
+			geometries = [
+
+				new THREE.IcosahedronGeometry(),
+				new THREE.IcosahedronGeometry( parameters.radius ),
+				new THREE.IcosahedronGeometry( parameters.radius, parameters.detail ),
+
+			];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 1 - 0
test/unit/extras/geometries/LatheGeometry.tests.js

@@ -0,0 +1 @@
+// TODO

+ 34 - 0
test/unit/extras/geometries/OctahedronGeometry.tests.js

@@ -0,0 +1,34 @@
+(function() {
+
+	'use strict';
+
+	var parameters = {
+		radius: 10,
+		detail: undefined
+	};
+
+	var geometries;
+
+	QUnit.module( "Extras - Geometries - OctahedronGeometry", {
+
+		beforeEach: function() {
+
+			geometries = [
+
+				new THREE.OctahedronGeometry(),
+				new THREE.OctahedronGeometry( parameters.radius ),
+				new THREE.OctahedronGeometry( parameters.radius, parameters.detail ),
+
+			];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 1 - 0
test/unit/extras/geometries/ParametricGeometry.tests.js

@@ -0,0 +1 @@
+// TODO

+ 38 - 0
test/unit/extras/geometries/PlaneBufferGeometry.tests.js

@@ -0,0 +1,38 @@
+(function () {
+
+	'use strict';
+
+	var parameters = {
+		width: 10,
+		height: 30,
+		widthSegments: 3,
+		heightSegments: 5
+	};
+
+	var geometries;
+
+	QUnit.module( "Extras - Geometries - PlaneBufferGeometry", {
+
+		beforeEach: function() {
+
+			geometries = [
+
+				new THREE.PlaneBufferGeometry(),
+				new THREE.PlaneBufferGeometry( parameters.width ),
+				new THREE.PlaneBufferGeometry( parameters.width, parameters.height ),
+				new THREE.PlaneBufferGeometry( parameters.width, parameters.height, parameters.widthSegments ),
+				new THREE.PlaneBufferGeometry( parameters.width, parameters.height, parameters.widthSegments, parameters.heightSegments ),
+
+			];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 38 - 0
test/unit/extras/geometries/PlaneGeometry.tests.js

@@ -0,0 +1,38 @@
+(function () {
+
+	'use strict';
+
+	var parameters = {
+		width: 10,
+		height: 30,
+		widthSegments: 3,
+		heightSegments: 5
+	};
+
+	var geometries;
+
+	QUnit.module( "Extras - Geometries - PlaneGeometry", {
+
+		beforeEach: function() {
+
+			geometries = [
+
+				new THREE.PlaneGeometry(),
+				new THREE.PlaneGeometry( parameters.width ),
+				new THREE.PlaneGeometry( parameters.width, parameters.height ),
+				new THREE.PlaneGeometry( parameters.width, parameters.height, parameters.widthSegments ),
+				new THREE.PlaneGeometry( parameters.width, parameters.height, parameters.widthSegments, parameters.heightSegments ),
+
+			];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 1 - 0
test/unit/extras/geometries/PolyhedronGeometry.tests.js

@@ -0,0 +1 @@
+// TODO

+ 42 - 0
test/unit/extras/geometries/RingGeometry.tests.js

@@ -0,0 +1,42 @@
+(function () {
+
+	'use strict';
+
+	var parameters = {
+		innerRadius: 10,
+		outerRadius: 60,
+		thetaSegments: 12,
+		phiSegments: 14,
+		thetaStart: 0.1,
+		thetaLength: 2.0
+	};
+
+	var geometries;
+
+	QUnit.module( "Extras - Geometries - RingGeometry", {
+
+		beforeEach: function() {
+
+			geometries = [
+
+				new THREE.RingGeometry(),
+				new THREE.RingGeometry( parameters.innerRadius ),
+				new THREE.RingGeometry( parameters.innerRadius, parameters.outerRadius ),
+				new THREE.RingGeometry( parameters.innerRadius, parameters.outerRadius, parameters.thetaSegments ),
+				new THREE.RingGeometry( parameters.innerRadius, parameters.outerRadius, parameters.thetaSegments, parameters.phiSegments ),
+				new THREE.RingGeometry( parameters.innerRadius, parameters.outerRadius, parameters.thetaSegments, parameters.phiSegments, parameters.thetaStart ),
+				new THREE.RingGeometry( parameters.innerRadius, parameters.outerRadius, parameters.thetaSegments, parameters.phiSegments, parameters.thetaStart, parameters.thetaLength ),
+
+			];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 1 - 0
test/unit/extras/geometries/ShapeGeometry.tests.js

@@ -0,0 +1 @@
+// TODO

+ 44 - 0
test/unit/extras/geometries/SphereBufferGeometry.tests.js

@@ -0,0 +1,44 @@
+(function () {
+
+	'use strict';
+
+	var parameters = {
+		radius: 10,
+		widthSegments: 20,
+		heightSegments: 30,
+		phiStart: 0.5,
+		phiLength: 1.0,
+		thetaStart: 0.4,
+		thetaLength: 2.0,
+	};
+
+	var geometries;
+
+	QUnit.module( "Extras - Geometries - SphereBufferGeometry", {
+
+		beforeEach: function() {
+
+			geometries = [
+
+				new THREE.SphereBufferGeometry(),
+				new THREE.SphereBufferGeometry( parameters.radius ),
+				new THREE.SphereBufferGeometry( parameters.radius, parameters.widthSegments ),
+				new THREE.SphereBufferGeometry( parameters.radius, parameters.widthSegments, parameters.heightSegments ),
+				new THREE.SphereBufferGeometry( parameters.radius, parameters.widthSegments, parameters.heightSegments, parameters.phiStart ),
+				new THREE.SphereBufferGeometry( parameters.radius, parameters.widthSegments, parameters.heightSegments, parameters.phiStart, parameters.phiLength ),
+				new THREE.SphereBufferGeometry( parameters.radius, parameters.widthSegments, parameters.heightSegments, parameters.phiStart, parameters.phiLength, parameters.thetaStart ),
+				new THREE.SphereBufferGeometry( parameters.radius, parameters.widthSegments, parameters.heightSegments, parameters.phiStart, parameters.phiLength, parameters.thetaStart, parameters.thetaLength ),
+
+			];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 44 - 0
test/unit/extras/geometries/SphereGeometry.tests.js

@@ -0,0 +1,44 @@
+(function () {
+
+	'use strict';
+
+	var parameters = {
+		radius: 10,
+		widthSegments: 20,
+		heightSegments: 30,
+		phiStart: 0.5,
+		phiLength: 1.0,
+		thetaStart: 0.4,
+		thetaLength: 2.0,
+	};
+
+	var geometries;
+
+	QUnit.module( "Extras - Geometries - SphereGeometry", {
+
+		beforeEach: function() {
+
+			geometries = [
+
+				new THREE.SphereGeometry(),
+				new THREE.SphereGeometry( parameters.radius ),
+				new THREE.SphereGeometry( parameters.radius, parameters.widthSegments ),
+				new THREE.SphereGeometry( parameters.radius, parameters.widthSegments, parameters.heightSegments ),
+				new THREE.SphereGeometry( parameters.radius, parameters.widthSegments, parameters.heightSegments, parameters.phiStart ),
+				new THREE.SphereGeometry( parameters.radius, parameters.widthSegments, parameters.heightSegments, parameters.phiStart, parameters.phiLength ),
+				new THREE.SphereGeometry( parameters.radius, parameters.widthSegments, parameters.heightSegments, parameters.phiStart, parameters.phiLength, parameters.thetaStart ),
+				new THREE.SphereGeometry( parameters.radius, parameters.widthSegments, parameters.heightSegments, parameters.phiStart, parameters.phiLength, parameters.thetaStart, parameters.thetaLength ),
+
+			];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 34 - 0
test/unit/extras/geometries/TetrahedronGeometry.tests.js

@@ -0,0 +1,34 @@
+(function () {
+
+	'use strict';
+
+	var parameters = {
+		radius: 10,
+		detail: undefined
+	};
+
+	var geometries;
+
+	QUnit.module( "Extras - Geometries - TetrahedronGeometry", {
+
+		beforeEach: function() {
+
+			geometries = [
+
+				new THREE.TetrahedronGeometry(),
+				new THREE.TetrahedronGeometry( parameters.radius ),
+				new THREE.TetrahedronGeometry( parameters.radius, parameters.detail ),
+
+			];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 1 - 0
test/unit/extras/geometries/TextGeometry.tests.js

@@ -0,0 +1 @@
+// TODO

+ 40 - 0
test/unit/extras/geometries/TorusGeometry.tests.js

@@ -0,0 +1,40 @@
+(function () {
+
+	'use strict';
+
+	var parameters = {
+		radius: 10,
+		tube: 20,
+		radialSegments: 30,
+		tubularSegments: 10,
+		arc: 2.0,
+	};
+
+	var geometries;
+
+	QUnit.module( "Extras - Geometries - TorusGeometry", {
+
+		beforeEach: function() {
+
+			geometries = [
+
+				new THREE.TorusGeometry(),
+				new THREE.TorusGeometry( parameters.radius ),
+				new THREE.TorusGeometry( parameters.radius, parameters.tube ),
+				new THREE.TorusGeometry( parameters.radius, parameters.tube, parameters.radialSegments ),
+				new THREE.TorusGeometry( parameters.radius, parameters.tube, parameters.radialSegments, parameters.tubularSegments ),
+				new THREE.TorusGeometry( parameters.radius, parameters.tube, parameters.radialSegments, parameters.tubularSegments, parameters.arc ),
+
+			];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 42 - 0
test/unit/extras/geometries/TorusKnotGeometry.tests.js

@@ -0,0 +1,42 @@
+(function () {
+
+	'use strict';
+
+	var parameters = {
+		radius: 10,
+		tube: 20,
+		radialSegments: 30,
+		tubularSegments: 10,
+		p: 3,
+		q: 2,
+		heightScale: 2.0
+	};
+
+	var geometries;
+
+	QUnit.module( "Extras - Geometries - TorusKnotGeometry", {
+
+		beforeEach: function() {
+
+			geometries = [
+
+				new THREE.TorusKnotGeometry(),
+				new THREE.TorusKnotGeometry( parameters.radius ),
+				new THREE.TorusKnotGeometry( parameters.radius, parameters.tube ),
+				new THREE.TorusKnotGeometry( parameters.radius, parameters.tube, parameters.radialSegments ),
+				new THREE.TorusKnotGeometry( parameters.radius, parameters.tube, parameters.radialSegments, parameters.tubularSegments ),
+				new THREE.TorusKnotGeometry( parameters.radius, parameters.tube, parameters.radialSegments, parameters.tubularSegments, parameters.p, parameters.q, parameters.heightScale ),
+
+			];
+
+		}
+
+	});
+
+	QUnit.test( "standard geometry tests", function( assert ) {
+
+		runStdGeometryTests( assert, geometries );
+
+	});
+
+})();

+ 1 - 0
test/unit/extras/geometries/TubeGeometry.tests.js

@@ -0,0 +1 @@
+// TODO

+ 1 - 0
test/unit/extras/geometries/WireframeGeometry.tests.js

@@ -0,0 +1 @@
+// TODO

+ 130 - 2
test/unit/qunit-utils.js

@@ -5,13 +5,141 @@
 QUnit.assert.success = function( message ) {
 
 	// Equivalent to assert( true, message );
-	QUnit.assert.push( true, undefined, undefined, message ); 
+	QUnit.assert.push( true, undefined, undefined, message );
 
 };
 
 QUnit.assert.fail = function( message ) {
 
 	// Equivalent to assert( false, message );
-	QUnit.assert.push( false, undefined, undefined, message ); 
+	QUnit.assert.push( false, undefined, undefined, message );
 
 };
+
+QUnit.assert.numEqual = function( actual, expected, message ) {
+
+	var diff = Math.abs(actual - expected);
+	message = message || ( actual + " should be equal to " + expected );
+	QUnit.assert.push( diff < 0.1, actual, expected, message );
+
+};
+
+QUnit.assert.equalKey = function( obj, ref, key ) {
+
+	var actual = obj[key];
+	var expected = ref[key];
+	var message = actual + ' should be equal to ' + expected + ' for key "' + key + '"';
+	QUnit.assert.push( actual == expected, actual, expected, message );
+
+};
+
+QUnit.assert.smartEqual = function( actual, expected, message ) {
+
+	var cmp = new SmartComparer();
+
+	var same = cmp.areEqual(actual, expected);
+	var msg = cmp.getDiagnostic() || message;
+
+	QUnit.assert.push( same, actual, expected, msg );
+
+};
+
+
+
+//
+//	GEOMETRY TEST HELPERS
+//
+
+function checkGeometryClone( geom ) {
+
+	// Clone
+	var copy = geom.clone();
+	QUnit.assert.notEqual( copy.uuid, geom.uuid, "clone uuid should differ from original" );
+	QUnit.assert.notEqual( copy.id, geom.id, "clone id should differ from original" );
+	QUnit.assert.smartEqual( copy, geom, "clone is equal to original" );
+
+
+	// json round trip with clone
+	checkGeometryJsonRoundtrip( copy );
+
+}
+
+// Compare json file with its source geometry.
+function checkGeometryJsonWriting( geom, json ) {
+
+	QUnit.assert.equal( json.metadata.version, "4.4", "check metadata version" );
+	QUnit.assert.equalKey( geom, json, 'type' );
+	QUnit.assert.equalKey( geom, json, 'uuid' );
+	QUnit.assert.equal( json.id, undefined, "should not persist id" );
+
+	// All parameters from geometry should be persisted.
+	_.forOwn( geom.parameters, function ( val, key ) {
+
+		QUnit.assert.equalKey( geom.parameters, json, key );
+
+	});
+
+	// All parameters from json should be transfered to the geometry.
+	// json is flat. Ignore first level json properties that are not parameters.
+	var notParameters = [ "metadata", "uuid", "type" ];
+	_.forOwn( json, function ( val, key ) {
+
+		if ( notParameters.indexOf( key) === -1 ) QUnit.assert.equalKey( geom.parameters, json, key );
+
+	});
+
+}
+
+// Check parsing and reconstruction of json geometry
+function checkGeometryJsonReading( json, geom ) {
+
+	var wrap = [ json ];
+
+	var loader = new THREE.ObjectLoader();
+	var output = loader.parseGeometries( wrap );
+
+	QUnit.assert.ok( output[geom.uuid], 'geometry matching source uuid not in output' );
+	QUnit.assert.smartEqual( output[geom.uuid], geom, 'Reconstruct geometry from ObjectLoader' );
+
+}
+
+// Verify geom -> json -> geom
+function checkGeometryJsonRoundtrip( geom ) {
+
+	var json = geom.toJSON();
+	checkGeometryJsonWriting( geom, json );
+	checkGeometryJsonReading( json, geom );
+
+}
+
+// Look for undefined and NaN values in numerical fieds.
+function checkFinite( geom ) {
+
+	var isNotFinite = _.any( geom.vertices, function ( v ) {
+
+		return ! ( _.isFinite( v.x ) || _.isFinite( v.y ) || _.isFinite( v.z ) );
+
+	});
+
+	// TODO Buffers, normal, etc.
+
+	QUnit.assert.ok( isNotFinite === false, "contains non-finite coordinates" );
+
+}
+
+// Run common geometry tests.
+function runStdGeometryTests( assert, geometries ) {
+
+	_.each( geometries, function( geom ) {
+
+		checkFinite( geom );
+
+		// Clone
+		checkGeometryClone( geom );
+
+		// json round trip
+		checkGeometryJsonRoundtrip( geom );
+
+	});
+
+}

+ 32 - 3
test/unit/unittests_three.html

@@ -7,9 +7,11 @@
 </head>
 <body>
   <div id="qunit"></div>
+  <script src="../../node_modules/lodash/index.js"></script>
   <script src="qunit-1.18.0.js"></script>
   <script src="qunit-utils.js"></script>
-  
+  <script src="SmartComparer.js"></script>
+
   <!-- add sources to test below -->
 
   <script src="../../build/three.js"></script>
@@ -33,11 +35,38 @@
   <script src="math/Matrix3.js"></script>
   <script src="math/Matrix4.js"></script>
   <script src="math/Frustum.js"></script>
-  
+
   <script src="geometry/EdgesGeometry.js"></script>
   <script src="extras/ImageUtils.test.js"></script>
-   
+
+  <script src="extras/geometries/BoxGeometry.tests.js"></script>
+  <script src="extras/geometries/CircleBufferGeometry.tests.js"></script>
+  <script src="extras/geometries/CircleGeometry.tests.js"></script>
+  <script src="extras/geometries/CylinderGeometry.tests.js"></script>
+  <script src="extras/geometries/DodecahedronGeometry.tests.js"></script>
+  <script src="extras/geometries/ExtrudeGeometry.tests.js"></script>
+  <script src="extras/geometries/IcosahedronGeometry.tests.js"></script>
+  <script src="extras/geometries/LatheGeometry.tests.js"></script>
+  <script src="extras/geometries/OctahedronGeometry.tests.js"></script>
+  <script src="extras/geometries/ParametricGeometry.tests.js"></script>
+  <script src="extras/geometries/PlaneBufferGeometry.tests.js"></script>
+  <script src="extras/geometries/PlaneGeometry.tests.js"></script>
+  <script src="extras/geometries/PolyhedronGeometry.tests.js"></script>
+  <script src="extras/geometries/RingGeometry.tests.js"></script>
+  <script src="extras/geometries/ShapeGeometry.tests.js"></script>
+  <script src="extras/geometries/SphereBufferGeometry.tests.js"></script>
+  <script src="extras/geometries/SphereGeometry.tests.js"></script>
+  <script src="extras/geometries/TetrahedronGeometry.tests.js"></script>
+  <script src="extras/geometries/TextGeometry.tests.js"></script>
+  <script src="extras/geometries/TorusGeometry.tests.js"></script>
+  <script src="extras/geometries/TorusKnotGeometry.tests.js"></script>
+  <script src="extras/geometries/TubeGeometry.tests.js"></script>
+  <script src="extras/geometries/WireframeGeometry.tests.js"></script>
+
+
   <!-- for debug output -->
+  <!--
   <script src="../../examples/js/controls/OrbitControls.js"></script>
+  -->
 </body>
 </html>