Browse Source

Tests: Added more tests for src/core/*

moraxy 7 years ago
parent
commit
4cd6d451b1

+ 122 - 0
test/unit/src/core/BufferAttribute.js

@@ -115,3 +115,125 @@ QUnit.test( "clone" , function( assert ) {
 		assert.ok( attr.array[i] === attrCopy.array[i], 'array item is equal' );
 		assert.ok( attr.array[i] === attrCopy.array[i], 'array item is equal' );
 	}
 	}
 });
 });
+
+QUnit.test( "constructor exception", function ( assert ) {
+
+	assert.throws(
+		function () {
+
+			var a = new THREE.BufferAttribute( [ 1, 2, 3, 4 ], 2, false );
+
+		},
+		/array should be a Typed Array/,
+		"Calling constructor with a simple array throws Error"
+	);
+
+} );
+
+QUnit.test( "setArray", function ( assert ) {
+
+	var f32a = new Float32Array( [ 1, 2, 3, 4 ] );
+	var a = new THREE.BufferAttribute( f32a, 2, false );
+
+	a.setArray( f32a, 2 );
+
+	assert.strictEqual( a.count, 2, "Check item count" );
+	assert.strictEqual( a.array, f32a, "Check array" );
+
+	assert.throws(
+		function () {
+
+			a.setArray( [ 1, 2, 3, 4 ] );
+
+		},
+		/array should be a Typed Array/,
+		"Calling setArray with a simple array throws Error"
+	);
+
+} );
+
+QUnit.test( "copyArray", function ( assert ) {
+
+	var f32a = new Float32Array( [ 5, 6, 7, 8 ] );
+	var a = new THREE.BufferAttribute( new Float32Array( [ 1, 2, 3, 4 ] ), 2, false );
+
+	a.copyArray( f32a );
+
+	assert.deepEqual( a.array, f32a, "Check array has new values" );
+
+} );
+
+QUnit.test( "set", function ( assert ) {
+
+	var f32a = new Float32Array( [ 1, 2, 3, 4 ] );
+	var a = new THREE.BufferAttribute( f32a, 2, false );
+	var expected = new Float32Array( [ 9, 2, 8, 4 ] );
+
+	a.set( [ 9 ] );
+	a.set( [ 8 ], 2 );
+
+	assert.deepEqual( a.array, expected, "Check array has expected values" );
+
+} );
+
+QUnit.test( "set[X, Y, Z, W, XYZ, XYZW]/get[X, Y, Z, W]", function ( assert ) {
+
+	var f32a = new Float32Array( [ 1, 2, 3, 4, 5, 6, 7, 8 ] );
+	var a = new THREE.BufferAttribute( f32a, 4, false );
+	var expected = new Float32Array( [ 1, 2, - 3, - 4, - 5, - 6, 7, 8 ] );
+
+	a.setX( 1, a.getX( 1 ) * - 1 );
+	a.setY( 1, a.getY( 1 ) * - 1 );
+	a.setZ( 0, a.getZ( 0 ) * - 1 );
+	a.setW( 0, a.getW( 0 ) * - 1 );
+
+	assert.deepEqual( a.array, expected, "Check all set* calls set the correct values" );
+
+} );
+
+QUnit.test( "setXY", function ( assert ) {
+
+	var f32a = new Float32Array( [ 1, 2, 3, 4 ] );
+	var a = new THREE.BufferAttribute( f32a, 2, false );
+	var expected = new Float32Array( [ - 1, - 2, 3, 4 ] );
+
+	a.setXY( 0, - 1, - 2 );
+
+	assert.deepEqual( a.array, expected, "Check for the correct values" );
+
+} );
+
+QUnit.test( "setXYZ", function ( assert ) {
+
+	var f32a = new Float32Array( [ 1, 2, 3, 4, 5, 6 ] );
+	var a = new THREE.BufferAttribute( f32a, 3, false );
+	var expected = new Float32Array( [ 1, 2, 3, - 4, - 5, - 6 ] );
+
+	a.setXYZ( 1, - 4, - 5, - 6 );
+
+	assert.deepEqual( a.array, expected, "Check for the correct values" );
+
+} );
+
+QUnit.test( "setXYZW", function ( assert ) {
+
+	var f32a = new Float32Array( [ 1, 2, 3, 4 ] );
+	var a = new THREE.BufferAttribute( f32a, 4, false );
+	var expected = new Float32Array( [ - 1, - 2, - 3, - 4 ] );
+
+	a.setXYZW( 0, - 1, - 2, - 3, - 4 );
+
+	assert.deepEqual( a.array, expected, "Check for the correct values" );
+
+} );
+
+QUnit.test( "onUpload", function ( assert ) {
+
+	var a = new THREE.BufferAttribute();
+	var func = function () { };
+
+	a.onUpload( func );
+
+	assert.strictEqual( a.onUploadCallback, func, "Check callback was set properly" );
+
+} );

+ 526 - 0
test/unit/src/core/BufferGeometry.js

@@ -6,6 +6,31 @@ QUnit.module( "BufferGeometry" );
 
 
 var DegToRad = Math.PI / 180;
 var DegToRad = Math.PI / 180;
 
 
+var bufferAttributeEquals = function ( a, b, tolerance ) {
+
+	tolerance = tolerance || 0.0001;
+
+	if ( a.count !== b.count || a.itemSize !== b.itemSize ) {
+
+		return false;
+
+	}
+
+	for ( var i = 0, il = a.count * a.itemSize; i < il; i ++ ) {
+
+		var delta = a[ i ] - b[ i ];
+		if ( delta > tolerance ) {
+
+			return false;
+
+		}
+
+	}
+
+	return true;
+
+};
+
 QUnit.test( "add / delete Attribute", function( assert ) {
 QUnit.test( "add / delete Attribute", function( assert ) {
 	var geometry = new THREE.BufferGeometry();
 	var geometry = new THREE.BufferGeometry();
 	var attributeName = "position";
 	var attributeName = "position";
@@ -309,3 +334,504 @@ QUnit.test( "copy" , function( assert ) {
 		}
 		}
 	});
 	});
 });
 });
+
+QUnit.test( "setIndex/getIndex", function ( assert ) {
+
+	var a = new THREE.BufferGeometry();
+	var uint16 = [ 1, 2, 3 ];
+	var uint32 = [ 65535, 65536, 65537 ];
+	var str = "foo";
+
+	a.setIndex( uint16 );
+	assert.ok( a.getIndex() instanceof THREE.Uint16BufferAttribute, "Index has the right type" );
+	assert.deepEqual( a.getIndex().array, new Uint16Array( uint16 ), "Small index gets stored correctly" );
+
+	a.setIndex( uint32 );
+	assert.ok( a.getIndex() instanceof THREE.Uint32BufferAttribute, "Index has the right type" );
+	assert.deepEqual( a.getIndex().array, new Uint32Array( uint32 ), "Large index gets stored correctly" );
+
+	a.setIndex( str );
+	assert.strictEqual( a.getIndex(), str, "Weird index gets stored correctly" );
+
+} );
+
+QUnit.test( "addGroup", function ( assert ) {
+
+	var a = new THREE.BufferGeometry();
+	var expected = [
+		{ start: 0, count: 1, materialIndex: 0 },
+		{ start: 1, count: 2, materialIndex: 2 }
+	];
+
+	a.addGroup( 0, 1, 0 );
+	a.addGroup( 1, 2, 2 );
+
+	assert.deepEqual( a.groups, expected, "Check groups were stored correctly and in order" );
+
+	a.clearGroups();
+	assert.strictEqual( a.groups.length, 0, "Check groups were deleted correctly" );
+
+} );
+
+QUnit.test( "setDrawRange", function ( assert ) {
+
+	var a = new THREE.BufferGeometry();
+
+	a.setDrawRange( 1.0, 7 );
+
+	assert.deepEqual( a.drawRange, { start: 1, count: 7 }, "Check draw range was stored correctly" );
+
+} );
+
+QUnit.test( "lookAt", function ( assert ) {
+
+	var a = new THREE.BufferGeometry();
+	var vertices = new Float32Array( [
+		- 1.0, - 1.0, 1.0,
+		1.0, - 1.0, 1.0,
+		1.0, 1.0, 1.0,
+
+		1.0, 1.0, 1.0,
+		- 1.0, 1.0, 1.0,
+		- 1.0, - 1.0, 1.0
+	] );
+	a.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+
+	var sqrt = Math.sqrt( 2 );
+	var expected = new Float32Array( [
+		1, 0, - sqrt,
+		- 1, 0, - sqrt,
+		- 1, sqrt, 0,
+
+		- 1, sqrt, 0,
+		1, sqrt, 0,
+		1, 0, - sqrt
+	] );
+
+	a.lookAt( new THREE.Vector3( 0, 1, - 1 ) );
+
+	assert.ok( bufferAttributeEquals( a.attributes.position.array, expected ), "Rotation is correct" );
+
+} );
+
+QUnit.test( "fromGeometry/fromDirectGeometry", function ( assert ) {
+
+	var a = new THREE.BufferGeometry();
+	// BoxGeometry is a bit too simple but works fine in a pinch
+	// var b = new THREE.BoxGeometry( 1, 1, 1 );
+	// b.mergeVertices();
+	// b.computeVertexNormals();
+	// b.computeBoundingBox();
+	// b.computeBoundingSphere();
+	var asyncDone = assert.async(); // tell QUnit we're done with asserts
+
+	var loader = new THREE.JSONLoader();
+	loader.load( "../../examples/models/skinned/simple/simple.js", function ( modelGeometry ) {
+
+		a.fromGeometry( modelGeometry );
+
+		var attr;
+		var geometry = new THREE.DirectGeometry().fromGeometry( modelGeometry );
+
+		var positions = new Float32Array( geometry.vertices.length * 3 );
+		attr = new THREE.BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices );
+		assert.ok( bufferAttributeEquals( a.attributes.position, attr ), "Vertices are identical" );
+
+		if ( geometry.normals.length > 0 ) {
+
+			var normals = new Float32Array( geometry.normals.length * 3 );
+			attr = new THREE.BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals );
+			assert.ok( bufferAttributeEquals( a.attributes.normal, attr ), "Normals are identical" );
+
+		}
+
+		if ( geometry.colors.length > 0 ) {
+
+			var colors = new Float32Array( geometry.colors.length * 3 );
+			attr = new THREE.BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors );
+			assert.ok( bufferAttributeEquals( a.attributes.color, attr ), "Colors are identical" );
+
+		}
+
+		if ( geometry.uvs.length > 0 ) {
+
+			var uvs = new Float32Array( geometry.uvs.length * 2 );
+			attr = new THREE.BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs );
+			assert.ok( bufferAttributeEquals( a.attributes.uv, attr ), "UVs are identical" );
+
+		}
+
+		if ( geometry.uvs2.length > 0 ) {
+
+			var uvs2 = new Float32Array( geometry.uvs2.length * 2 );
+			attr = new THREE.BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 );
+			assert.ok( bufferAttributeEquals( a.attributes.uv2, attr ), "UV2s are identical" );
+
+		}
+
+		if ( geometry.indices.length > 0 ) {
+
+			var TypeArray = THREE.arrayMax( geometry.indices ) > 65535 ? Uint32Array : Uint16Array;
+			var indices = new TypeArray( geometry.indices.length * 3 );
+			attr = new THREE.BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices );
+			assert.ok( bufferAttributeEquals( a.indices, attr ), "Indices are identical" );
+
+		}
+
+		// groups
+		assert.deepEqual( a.groups, geometry.groups, "Groups are identical" );
+
+		// morphs
+		if ( geometry.morphTargets !== undefined ) {
+
+			for ( var name in geometry.morphTargets ) {
+
+				var morphTargets = geometry.morphTargets[ name ];
+
+				for ( var i = 0, l = morphTargets.length; i < l; i ++ ) {
+
+					var morphTarget = morphTargets[ i ];
+
+					attr = new THREE.Float32BufferAttribute( morphTarget.length * 3, 3 );
+					attr.copyVector3sArray( morphTarget );
+
+					assert.ok(
+						bufferAttributeEquals( a.morphAttributes[ name ][ i ], attr ),
+						"MorphTargets #" + i + " are identical"
+					);
+
+				}
+
+			}
+
+		}
+
+		// skinning
+		if ( geometry.skinIndices.length > 0 ) {
+
+			attr = new THREE.Float32BufferAttribute( geometry.skinIndices.length * 4, 4 );
+			attr.copyVector4sArray( geometry.skinIndices );
+			assert.ok( bufferAttributeEquals( a.attributes.skinIndex, attr ), "SkinIndices are identical" );
+
+		}
+
+		if ( geometry.skinWeights.length > 0 ) {
+
+			attr = new THREE.Float32BufferAttribute( geometry.skinWeights.length * 4, 4 );
+			attr.copyVector4sArray( geometry.skinWeights );
+			assert.ok( bufferAttributeEquals( a.attributes.skinWeight, attr ), "SkinWeights are identical" );
+
+		}
+
+		// TODO
+		// DirectGeometry doesn't actually copy boundingSphere and boundingBox yet,
+		// so they're always null
+		if ( geometry.boundingSphere !== null ) {
+
+			assert.ok( a.boundingSphere.equals( geometry.boundingSphere ), "BoundingSphere is identical" );
+
+		}
+
+		if ( geometry.boundingBox !== null ) {
+
+			assert.ok( a.boundingBox.equals( geometry.boundingBox ), "BoundingBox is identical" );
+
+		}
+
+		asyncDone();
+
+	} );
+
+} );
+
+QUnit.test( "clone", function ( assert ) {
+
+	var a = new THREE.BufferGeometry();
+	a.addAttribute( "attribute1", new THREE.BufferAttribute( new Float32Array( [ 1, 2, 3, 4, 5, 6 ] ), 3 ) );
+	a.addAttribute( "attribute2", new THREE.BufferAttribute( new Float32Array( [ 0, 1, 3, 5, 6 ] ), 1 ) );
+	a.addGroup( 0, 1, 2 );
+	a.computeBoundingBox();
+	a.computeBoundingSphere();
+	a.setDrawRange( 0, 1 );
+	var b = a.clone();
+
+	assert.notEqual( a, b, "A new object was created" );
+	assert.notEqual( a.id, b.id, "New object has a different GUID" );
+
+	assert.strictEqual(
+		Object.keys( a.attributes ).count, Object.keys( b.attributes ).count,
+		"Both objects have the same amount of attributes"
+	);
+	assert.ok(
+		bufferAttributeEquals( a.getAttribute( "attribute1" ), b.getAttribute( "attribute1" ) ),
+		"First attributes buffer is identical"
+	);
+	assert.ok(
+		bufferAttributeEquals( a.getAttribute( "attribute2" ), b.getAttribute( "attribute2" ) ),
+		"Second attributes buffer is identical"
+	);
+
+	assert.deepEqual( a.groups, b.groups, "Groups are identical" );
+
+	assert.ok( a.boundingBox.equals( b.boundingBox ), "BoundingBoxes are equal" );
+	assert.ok( a.boundingSphere.equals( b.boundingSphere ), "BoundingSpheres are equal" );
+
+	assert.strictEqual( a.drawRange.start, b.drawRange.start, "DrawRange start is identical" );
+	assert.strictEqual( a.drawRange.count, b.drawRange.count, "DrawRange count is identical" );
+
+} );
+
+QUnit.test( "computeVertexNormals (indexed)", function ( assert ) {
+
+	var sqrt = 0.5 * Math.sqrt( 2 );
+	var normal = new THREE.BufferAttribute( new Float32Array( [
+		- 1, 0, 0, - 1, 0, 0, - 1, 0, 0,
+		sqrt, sqrt, 0, sqrt, sqrt, 0, sqrt, sqrt, 0,
+		- 1, 0, 0
+	] ), 3 );
+	var position = new THREE.BufferAttribute( new Float32Array( [
+		0.5, 0.5, 0.5, 0.5, 0.5, - 0.5, 0.5, - 0.5, 0.5,
+		0.5, - 0.5, - 0.5, - 0.5, 0.5, - 0.5, - 0.5, 0.5, 0.5,
+		- 0.5, - 0.5, - 0.5
+	] ), 3 );
+	var index = new THREE.BufferAttribute( new Uint16Array( [
+		0, 2, 1, 2, 3, 1, 4, 6, 5, 6, 7, 5
+	] ), 1 );
+
+	var a = new THREE.BufferGeometry();
+	a.addAttribute( "position", position );
+	a.computeVertexNormals();
+	assert.ok(
+		bufferAttributeEquals( normal, a.getAttribute( "normal" ) ),
+		"Regular geometry: first computed normals are correct"
+	);
+
+	// a second time to see if the existing normals get properly deleted
+	a.computeVertexNormals();
+	assert.ok(
+		bufferAttributeEquals( normal, a.getAttribute( "normal" ) ),
+		"Regular geometry: second computed normals are correct"
+	);
+
+	// indexed geometry
+	a = new THREE.BufferGeometry();
+	a.addAttribute( "position", position );
+	a.setIndex( index );
+	a.computeVertexNormals();
+	assert.ok( bufferAttributeEquals( normal, a.getAttribute( "normal" ) ), "Indexed geometry: computed normals are correct" );
+
+} );
+
+QUnit.test( "toJSON", function ( assert ) {
+
+	var index = new THREE.BufferAttribute( new Uint16Array( [ 0, 1, 2, 3 ] ), 1 );
+	var attribute1 = new THREE.BufferAttribute( new Uint16Array( [ 1, 3, 5, 7 ] ), 1 );
+	var a = new THREE.BufferGeometry();
+	a.name = "JSONtest";
+	// a.parameters = { "placeholder": 0 };
+	a.addAttribute( "attribute1", attribute1 );
+	a.setIndex( index );
+	a.addGroup( 0, 1, 2 );
+	a.boundingSphere = new THREE.Sphere( new THREE.Vector3( x, y, z ), 0.5 );
+	var j = a.toJSON();
+
+	var gold = {
+		"metadata": { "version": 4.5, "type": "BufferGeometry", "generator": "BufferGeometry.toJSON" },
+		"uuid": a.uuid,
+		"type": "BufferGeometry",
+		"name": "JSONtest",
+		"data": {
+			"attributes": {
+				"attribute1": {
+					"itemSize": 1,
+					"type": "Uint16Array",
+					"array": [ 1, 3, 5, 7 ],
+					"normalized": false
+				}
+			},
+			"index": { "type": "Uint16Array", "array": [ 0, 1, 2, 3 ] },
+			"groups": [ { "start": 0, "count": 1, "materialIndex": 2 } ],
+			"boundingSphere": { "center": [ 2, 3, 4 ], "radius": 0.5 }
+		}
+	};
+
+	assert.deepEqual( j, gold, "Generated JSON is as expected" );
+
+} );
+
+function comparePositions( pos, v ) {
+
+	return (
+		pos[ 0 ] === v[ 0 ].x && pos[ 1 ] === v[ 0 ].y && pos[ 2 ] === v[ 0 ].z &&
+		pos[ 3 ] === v[ 1 ].x && pos[ 4 ] === v[ 1 ].y && pos[ 5 ] === v[ 1 ].z &&
+		pos[ 6 ] === v[ 2 ].x && pos[ 7 ] === v[ 2 ].y && pos[ 8 ] === v[ 2 ].z
+	);
+
+}
+
+function compareColors( col, c ) {
+
+	return (
+		col[ 0 ] === c[ 0 ].r && col[ 1 ] === c[ 0 ].g && col[ 2 ] === c[ 0 ].b &&
+		col[ 3 ] === c[ 1 ].r && col[ 4 ] === c[ 1 ].g && col[ 5 ] === c[ 1 ].b &&
+		col[ 6 ] === c[ 2 ].r && col[ 7 ] === c[ 2 ].g && col[ 8 ] === c[ 2 ].b
+	);
+
+}
+
+function compareUvs( uvs, u ) {
+
+	return (
+		uvs[ 0 ] === u[ 0 ].x && uvs[ 1 ] === u[ 0 ].y &&
+		uvs[ 2 ] === u[ 1 ].x && uvs[ 3 ] === u[ 1 ].y &&
+		uvs[ 4 ] === u[ 2 ].x && uvs[ 5 ] === u[ 2 ].y
+	);
+
+}
+
+QUnit.test( "setFromObject (more)", function ( assert ) {
+
+	var lineGeo = new THREE.Geometry();
+	lineGeo.vertices.push(
+		new THREE.Vector3( - 10, 0, 0 ),
+		new THREE.Vector3( 0, 10, 0 ),
+		new THREE.Vector3( 10, 0, 0 )
+	);
+
+	lineGeo.colors.push(
+		new THREE.Color( 1, 0, 0 ),
+		new THREE.Color( 0, 1, 0 ),
+		new THREE.Color( 0, 0, 1 )
+	);
+
+	lineGeo.computeBoundingBox();
+	lineGeo.computeBoundingSphere();
+
+	var line = new THREE.Line( lineGeo );
+	var geometry = new THREE.BufferGeometry().setFromObject( line );
+
+	assert.ok( geometry.boundingBox.equals( lineGeo.boundingBox ), "BoundingBox was set correctly" );
+	assert.ok( geometry.boundingSphere.equals( lineGeo.boundingSphere ), "BoundingSphere was set correctly" );
+
+	var pos = geometry.attributes.position.array;
+	var col = geometry.attributes.color.array;
+	var v = lineGeo.vertices;
+	var c = lineGeo.colors;
+
+	// adapted from setFromObject test (way up)
+	assert.notStrictEqual( pos, undefined, "Position attribute exists" );
+	assert.strictEqual( v.length * 3, pos.length, "Vertex arrays have the same size" );
+	assert.strictEqual( geometry.attributes.position.count, 3, "Correct number of vertices" );
+	assert.ok( comparePositions( pos, v ), "Positions are identical" );
+
+	assert.notStrictEqual( col, undefined, "Color attribute exists" );
+	assert.strictEqual( c.length * 3, col.length, "Color arrays have the same size" );
+	assert.strictEqual( geometry.attributes.color.count, 3, "Correct number of colors" );
+	assert.ok( compareColors( col, c ), "Colors are identical" );
+
+	// setFromObject with a Mesh as object
+	lineGeo.faces.push( new THREE.Face3( 0, 1, 2 ) );
+	var lineMesh = new THREE.Mesh( lineGeo );
+	geometry = new THREE.BufferGeometry().setFromObject( lineMesh );
+
+	// no colors
+	pos = geometry.attributes.position.array;
+	v = lineGeo.vertices;
+
+	assert.notStrictEqual( pos, undefined, "Mesh: position attribute exists" );
+	assert.strictEqual( v.length * 3, pos.length, "Mesh: vertex arrays have the same size" );
+	assert.strictEqual( geometry.attributes.position.count, 3, "Mesh: correct number of vertices" );
+	assert.ok( comparePositions( pos, v ), "Mesh: positions are identical" );
+
+} );
+
+QUnit.test( "updateFromObject", function ( assert ) {
+
+	var geo = new THREE.Geometry();
+
+	geo.vertices.push(
+		new THREE.Vector3( - 10, 0, 0 ),
+		new THREE.Vector3( 0, 10, 0 ),
+		new THREE.Vector3( 10, 0, 0 )
+	);
+
+	geo.faces.push( new THREE.Face3( 0, 1, 2 ) );
+
+	geo.faces[ 0 ].vertexColors.push(
+		new THREE.Color( 1, 0, 0 ),
+		new THREE.Color( 0, 1, 0 ),
+		new THREE.Color( 0, 0, 1 )
+	);
+
+	geo.faceVertexUvs[ 0 ] = [[
+		new THREE.Vector2( 0, 0 ),
+		new THREE.Vector2( 1, 0 ),
+		new THREE.Vector2( 1, 1 )
+	]];
+
+	geo.computeFaceNormals();
+	geo.computeVertexNormals();
+
+	geo.verticesNeedUpdate = true;
+	geo.normalsNeedUpdate = true;
+	geo.colorsNeedUpdate = true;
+	geo.uvsNeedUpdate = true;
+	geo.groupsNeedUpdate = true;
+
+	var mesh = new THREE.Mesh( geo );
+	var geometry = new THREE.BufferGeometry();
+
+	geometry.updateFromObject( mesh ); // first call to create the underlying structure (DirectGeometry)
+	geometry.updateFromObject( mesh ); // second time to actually go thru the motions and update
+
+	var pos = geometry.attributes.position.array;
+	var col = geometry.attributes.color.array;
+	var norm = geometry.attributes.normal.array;
+	var uvs = geometry.attributes.uv.array;
+	var v = geo.vertices;
+	var c = geo.faces[ 0 ].vertexColors;
+	var n = geo.faces[ 0 ].vertexNormals;
+	var u = geo.faceVertexUvs[ 0 ][ 0 ];
+
+	assert.notStrictEqual( pos, undefined, "Position attribute exists" );
+	assert.strictEqual( v.length * 3, pos.length, "Both arrays have the same size" );
+	assert.strictEqual( geometry.attributes.position.count, v.length, "Correct number of vertices" );
+	assert.ok( comparePositions( pos, v ), "Positions are identical" );
+
+	assert.notStrictEqual( col, undefined, "Color attribute exists" );
+	assert.strictEqual( c.length * 3, col.length, "Both arrays have the same size" );
+	assert.strictEqual( geometry.attributes.color.count, c.length, "Correct number of colors" );
+	assert.ok( compareColors( col, c ), "Colors are identical" );
+
+	assert.notStrictEqual( norm, undefined, "Normal attribute exists" );
+	assert.strictEqual( n.length * 3, norm.length, "Both arrays have the same size" );
+	assert.strictEqual( geometry.attributes.normal.count, n.length, "Correct number of normals" );
+	assert.ok( comparePositions( norm, n ), "Normals are identical" );
+
+	assert.notStrictEqual( uvs, undefined, "UV attribute exists" );
+	assert.strictEqual( u.length * 2, uvs.length, "Both arrays have the same size" );
+	assert.strictEqual( geometry.attributes.uv.count, u.length, "Correct number of UV coordinates" );
+	assert.ok( compareUvs( uvs, u ), "UVs are identical" );
+
+} );
+
+QUnit.test( "toNonIndexed", function ( assert ) {
+
+	var geometry = new THREE.BufferGeometry();
+	var vertices = new Float32Array( [
+		0.5, 0.5, 0.5, 0.5, 0.5, - 0.5, 0.5, - 0.5, 0.5, 0.5, - 0.5, - 0.5
+	] );
+	var index = new THREE.BufferAttribute( new Uint16Array( [ 0, 2, 1, 2, 3, 1 ] ) );
+	var expected = new Float32Array( [
+		0.5, 0.5, 0.5, 0.5, - 0.5, 0.5, 0.5, 0.5, - 0.5,
+		0.5, - 0.5, 0.5, 0.5, - 0.5, - 0.5, 0.5, 0.5, - 0.5
+	] );
+
+	geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+	geometry.setIndex( index );
+
+	var nonIndexed = geometry.toNonIndexed();
+
+	assert.deepEqual( nonIndexed.getAttribute( "position" ).array, expected, "Expected vertices" );
+
+} );

+ 221 - 3
test/unit/src/core/DirectGeometry.js

@@ -1,6 +1,224 @@
 /**
 /**
- * @author TristanVALCKE / https://github.com/TristanVALCKE
+ * @author moraxy / https://github.com/moraxy
  */
  */
 
 
-//Todo
-console.warn("Todo: Unit tests of DirectGeometry")
+QUnit.module( "DirectGeometry" );
+
+QUnit.test( "computeGroups", function ( assert ) {
+
+	var a = new THREE.DirectGeometry();
+	var b = new THREE.Geometry();
+	var expected = [
+		{ start: 0, materialIndex: 0, count: 3 },
+		{ start: 3, materialIndex: 1, count: 3 },
+		{ start: 6, materialIndex: 2, count: 6 }
+	];
+
+	// we only care for materialIndex
+	b.faces.push(
+		new THREE.Face3( 0, 0, 0, undefined, undefined, 0 ),
+		new THREE.Face3( 0, 0, 0, undefined, undefined, 1 ),
+		new THREE.Face3( 0, 0, 0, undefined, undefined, 2 ),
+		new THREE.Face3( 0, 0, 0, undefined, undefined, 2 )
+	);
+
+	a.computeGroups( b );
+
+	assert.deepEqual( a.groups, expected, "Groups are as expected" );
+
+} );
+
+QUnit.test( "fromGeometry", function ( assert ) {
+
+	var a = new THREE.DirectGeometry();
+
+	var asyncDone = assert.async(); // tell QUnit when we're done with async stuff
+
+	var loader = new THREE.JSONLoader();
+	loader.load( "../../examples/models/skinned/simple/simple.js", function ( geometry ) {
+
+		a.fromGeometry( geometry );
+
+		var tmp = new THREE.DirectGeometry();
+		tmp.computeGroups( geometry );
+		assert.deepEqual( a.groups, tmp.groups, "Check groups" );
+
+		var morphTargets = geometry.morphTargets;
+		var morphTargetsLength = morphTargets.length;
+
+		var morphTargetsPosition;
+
+		if ( morphTargetsLength > 0 ) {
+
+			morphTargetsPosition = [];
+
+			for ( var i = 0; i < morphTargetsLength; i ++ ) {
+
+				morphTargetsPosition[ i ] = [];
+
+			}
+
+			morphTargets.position = morphTargetsPosition;
+
+		}
+
+		var morphNormals = geometry.morphNormals;
+		var morphNormalsLength = morphNormals.length;
+
+		var morphTargetsNormal;
+
+		if ( morphNormalsLength > 0 ) {
+
+			morphTargetsNormal = [];
+
+			for ( var i = 0; i < morphNormalsLength; i ++ ) {
+
+				morphTargetsNormal[ i ] = [];
+
+			}
+
+			morphTargets.normal = morphTargetsNormal;
+
+		}
+
+		var vertices = [];
+		var normals = [];
+		var colors = [];
+		var uvs = [];
+		var uvs2 = [];
+		var skinIndices = [];
+		var skinWeights = [];
+
+		var hasFaceVertexUv = geometry.faceVertexUvs[ 0 ] && geometry.faceVertexUvs[ 0 ].length > 0;
+		var hasFaceVertexUv2 = geometry.faceVertexUvs[ 1 ] && geometry.faceVertexUvs[ 1 ].length > 0;
+
+		var hasSkinIndices = geometry.skinIndices.length === geometry.vertices.length;
+		var hasSkinWeights = geometry.skinWeights.length === geometry.vertices.length;
+
+		for ( var i = 0; i < geometry.faces.length; i ++ ) {
+
+			var face = geometry.faces[ i ];
+
+			vertices.push(
+				geometry.vertices[ face.a ],
+				geometry.vertices[ face.b ],
+				geometry.vertices[ face.c ]
+			);
+
+			var vertexNormals = face.vertexNormals;
+
+			if ( vertexNormals.length === 3 ) {
+
+				normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] );
+
+			} else {
+
+				normals.push( face.normal, face.normal, face.normal );
+
+			}
+
+			var vertexColors = face.vertexColors;
+
+			if ( vertexColors.length === 3 ) {
+
+				colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] );
+
+			} else {
+
+				colors.push( face.color, face.color, face.color );
+
+			}
+
+			if ( hasFaceVertexUv === true ) {
+
+				var vertexUvs = geometry.faceVertexUvs[ 0 ][ i ];
+
+				if ( vertexUvs !== undefined ) {
+
+					uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );
+
+				} else {
+
+					uvs.push( new Vector2(), new Vector2(), new Vector2() );
+
+				}
+
+			}
+
+			if ( hasFaceVertexUv2 === true ) {
+
+				var vertexUvs = geometry.faceVertexUvs[ 1 ][ i ];
+
+				if ( vertexUvs !== undefined ) {
+
+					uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );
+
+				} else {
+
+					uvs2.push( new Vector2(), new Vector2(), new Vector2() );
+
+				}
+
+			}
+
+			// morphs
+
+			for ( var j = 0; j < morphTargetsLength; j ++ ) {
+
+				var morphTarget = morphTargets[ j ].vertices;
+
+				morphTargetsPosition[ j ].push(
+					morphTarget[ face.a ],
+					morphTarget[ face.b ],
+					morphTarget[ face.c ]
+				);
+
+			}
+
+			for ( var j = 0; j < morphNormalsLength; j ++ ) {
+
+				var morphNormal = morphNormals[ j ].vertexNormals[ i ];
+
+				morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c );
+
+			}
+
+			// skins
+
+			if ( hasSkinIndices ) {
+
+				skinIndices.push(
+					geometry.skinIndices[ face.a ],
+					geometry.skinIndices[ face.b ],
+					geometry.skinIndices[ face.c ]
+				);
+
+			}
+
+			if ( hasSkinWeights ) {
+
+				skinWeights.push(
+					geometry.skinWeights[ face.a ],
+					geometry.skinWeights[ face.b ],
+					geometry.skinWeights[ face.c ]
+				);
+
+			}
+
+		}
+
+		assert.deepEqual( a.vertices, vertices, "Vertices are identical" );
+		assert.deepEqual( a.normals, normals, "Normals are identical" );
+		assert.deepEqual( a.colors, colors, "Colors are identical" );
+		assert.deepEqual( a.uvs, uvs, "UV coordinates are identical" );
+		assert.deepEqual( a.uvs2, uvs2, "UV(2) coordinates are identical" );
+		assert.deepEqual( a.skinIndices, skinIndices, "SkinIndices are identical" );
+		assert.deepEqual( a.skinWeights, skinWeights, "SkinWeights are identical" );
+		assert.deepEqual( a.morphTargetsPosition, morphTargetsPosition, "MorphTargets (Position) are identical" );
+		assert.deepEqual( a.morphTargetsNormal, morphTargetsNormal, "MorphTargets (Normals) are identical" );
+
+		asyncDone();
+
+	} );
+
+} );

+ 254 - 0
test/unit/src/core/Geometry.js

@@ -104,3 +104,257 @@ function getGeometryByParams( x1, y1, z1, x2, y2, z2, x3, y3, z3 ) {
 function getGeometry() {
 function getGeometry() {
 	return getGeometryByParams( -0.5, 0, 0, 0.5, 0, 0, 0, 1, 0 );
 	return getGeometryByParams( -0.5, 0, 0, 0.5, 0, 0, 0, 1, 0 );
 }
 }
+
+QUnit.test( "applyMatrix", function ( assert ) {
+
+	var geometry = getGeometry();
+	geometry.faces.push( new THREE.Face3( 0, 1, 2 ) );
+	var m = new THREE.Matrix4();
+	var expectedVerts = [
+		new THREE.Vector3( 1.5, 3, 4 ),
+		new THREE.Vector3( 2.5, 3, 4 ),
+		new THREE.Vector3( 2, 3, 5 )
+	];
+	var v1, v2, v3;
+
+	m.makeRotationX( Math.PI / 2 );
+	m.setPosition( new THREE.Vector3( x, y, z ) );
+
+	geometry.applyMatrix( m );
+
+	v0 = geometry.vertices[ 0 ];
+	v1 = geometry.vertices[ 1 ];
+	v2 = geometry.vertices[ 2 ];
+	assert.ok(
+		Math.abs( v0.x - expectedVerts[ 0 ].x ) <= eps &&
+		Math.abs( v0.y - expectedVerts[ 0 ].y ) <= eps &&
+		Math.abs( v0.z - expectedVerts[ 0 ].z ) <= eps,
+		"First vertex is as expected"
+	);
+	assert.ok(
+		Math.abs( v1.x - expectedVerts[ 1 ].x ) <= eps &&
+		Math.abs( v1.y - expectedVerts[ 1 ].y ) <= eps &&
+		Math.abs( v1.z - expectedVerts[ 1 ].z ) <= eps,
+		"Second vertex is as expected"
+	);
+	assert.ok(
+		Math.abs( v2.x - expectedVerts[ 2 ].x ) <= eps &&
+		Math.abs( v2.y - expectedVerts[ 2 ].y ) <= eps &&
+		Math.abs( v2.z - expectedVerts[ 2 ].z ) <= eps,
+		"Third vertex is as expected"
+	);
+
+} );
+
+QUnit.test( "translate", function ( assert ) {
+
+	var a = getGeometry();
+	var expected = [
+		new THREE.Vector3( - 2.5, 3, - 4 ),
+		new THREE.Vector3( - 1.5, 3, - 4 ),
+		new THREE.Vector3( - 2, 4, - 4 )
+	];
+	var v;
+
+	a.translate( - x, y, - z );
+
+	for ( var i = 0; i < a.vertices.length; i ++ ) {
+
+		v = a.vertices[ i ];
+		assert.ok(
+			Math.abs( v.x - expected[ i ].x ) <= eps &&
+			Math.abs( v.y - expected[ i ].y ) <= eps &&
+			Math.abs( v.z - expected[ i ].z ) <= eps,
+			"Vertex #" + i + " was translated as expected"
+		);
+
+	}
+
+} );
+
+QUnit.test( "lookAt", function ( assert ) {
+
+	var a = getGeometry();
+	var expected = [
+		new THREE.Vector3( - 0.5, 0, 0 ),
+		new THREE.Vector3( 0.5, 0, 0 ),
+		new THREE.Vector3( 0, 0.5 * Math.sqrt( 2 ), 0.5 * Math.sqrt( 2 ) )
+	];
+
+	a.lookAt( new THREE.Vector3( 0, - 1, 1 ) );
+
+	for ( var i = 0; i < a.vertices.length; i ++ ) {
+
+		v = a.vertices[ i ];
+		assert.ok(
+			Math.abs( v.x - expected[ i ].x ) <= eps &&
+			Math.abs( v.y - expected[ i ].y ) <= eps &&
+			Math.abs( v.z - expected[ i ].z ) <= eps,
+			"Vertex #" + i + " was adjusted as expected"
+		);
+
+	}
+
+} );
+
+QUnit.test( "scale", function ( assert ) {
+
+	var a = getGeometry();
+	var expected = [
+		new THREE.Vector3( - 1, 0, 0 ),
+		new THREE.Vector3( 1, 0, 0 ),
+		new THREE.Vector3( 0, 3, 0 )
+	];
+	var v;
+
+	a.scale( 2, 3, 4 );
+
+	for ( var i = 0; i < a.vertices.length; i ++ ) {
+
+		v = a.vertices[ i ];
+		assert.ok(
+			Math.abs( v.x - expected[ i ].x ) <= eps &&
+			Math.abs( v.y - expected[ i ].y ) <= eps &&
+			Math.abs( v.z - expected[ i ].z ) <= eps,
+			"Vertex #" + i + " was scaled as expected"
+		);
+
+	}
+
+} );
+
+QUnit.test( "normalize (actual)", function ( assert ) {
+
+	var a = getGeometry();
+	var sqrt = 0.5 * Math.sqrt( 2 );
+	var expected = [
+		new THREE.Vector3( - sqrt, - sqrt, 0 ),
+		new THREE.Vector3( sqrt, - sqrt, 0 ),
+		new THREE.Vector3( 0, sqrt, 0 )
+	];
+	var v;
+
+	a.normalize();
+
+	for ( var i = 0; i < a.vertices.length; i ++ ) {
+
+		v = a.vertices[ i ];
+		assert.ok(
+			Math.abs( v.x - expected[ i ].x ) <= eps &&
+			Math.abs( v.y - expected[ i ].y ) <= eps &&
+			Math.abs( v.z - expected[ i ].z ) <= eps,
+			"Vertex #" + i + " was normalized as expected"
+		);
+
+	}
+
+} );
+
+QUnit.test( "toJSON", function ( assert ) {
+
+	var a = getGeometry();
+	var gold = {
+		"metadata": {
+			"version": 4.5,
+			"type": "Geometry",
+			"generator": "Geometry.toJSON"
+		},
+		"uuid": null,
+		"type": "Geometry",
+		"data": {
+			"vertices": [ - 0.5, 0, 0, 0.5, 0, 0, 0, 1, 0 ],
+			"normals": [ 0, 0, 1 ],
+			"faces": [ 50, 0, 1, 2, 0, 0, 0, 0, 0 ]
+		}
+	};
+	var json;
+
+	a.faces.push( new THREE.Face3( 0, 1, 2 ) );
+	a.computeFaceNormals();
+	a.computeVertexNormals();
+
+	json = a.toJSON();
+	json.uuid = null;
+	assert.deepEqual( json, gold, "Generated JSON is as expected" );
+
+} );
+
+QUnit.test( "mergeVertices", function ( assert ) {
+
+	var a = new THREE.Geometry();
+	var b = new THREE.BoxBufferGeometry( 1, 1, 1 );
+	var verts, faces, removed;
+
+	a.fromBufferGeometry( b );
+
+	removed = a.mergeVertices();
+	verts = a.vertices.length;
+	faces = a.faces.length;
+
+	assert.strictEqual( removed, 16, "Removed the expected number of vertices" );
+	assert.strictEqual( verts, 8, "Minimum number of vertices remaining" );
+	assert.strictEqual( faces, 12, "Minimum number of faces remaining" );
+
+} );
+
+QUnit.test( "sortFacesByMaterialIndex", function ( assert ) {
+
+	var box = new THREE.BoxBufferGeometry( 1, 1, 1 );
+	var a = new THREE.Geometry().fromBufferGeometry( box );
+	var expected = [ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 ];
+
+	a.faces.reverse(); // a bit too simple probably, still missing stuff like checking new UVs
+	a.sortFacesByMaterialIndex();
+
+	var indices = [];
+
+	for ( var i = 0; i < a.faces.length; i ++ ) {
+
+		indices.push( a.faces[ i ].materialIndex );
+
+	}
+
+	assert.deepEqual( indices, expected, "Faces in correct order" );
+
+} );
+
+QUnit.test( "computeBoundingBox", function ( assert ) {
+
+	var a = new THREE.DodecahedronGeometry();
+
+	a.computeBoundingBox();
+	assert.strictEqual( a.boundingBox.isEmpty(), false, "Bounding box isn't empty" );
+
+	var allIn = true;
+	for ( var i = 0; i < a.vertices.length; i ++ ) {
+
+		if ( ! a.boundingBox.containsPoint( a.vertices[ i ] ) ) {
+
+			allIn = false;
+
+		}
+
+	}
+	assert.strictEqual( allIn, true, "All vertices are inside the box" );
+
+} );
+
+QUnit.test( "computeBoundingSphere", function ( assert ) {
+
+	var a = new THREE.DodecahedronGeometry();
+
+	a.computeBoundingSphere();
+
+	var allIn = true;
+	for ( var i = 0; i < a.vertices.length; i ++ ) {
+
+		if ( ! a.boundingSphere.containsPoint( a.vertices[ i ] ) ) {
+
+			allIn = false;
+
+		}
+
+	}
+	assert.strictEqual( allIn, true, "All vertices are inside the bounding sphere" );
+
+} );

+ 63 - 0
test/unit/src/core/InterleavedBuffer.js

@@ -43,3 +43,66 @@ QUnit.test( "set" , function( assert ) {
 	instance.set( [0, -1] );
 	instance.set( [0, -1] );
 	assert.ok( instance.array[0] === 0 && instance.array[1] === -1, "replace at first by default" );
 	assert.ok( instance.array[0] === 0 && instance.array[1] === -1, "replace at first by default" );
 });
 });
+
+QUnit.test( "needsUpdate", function ( assert ) {
+
+	var a = new THREE.InterleavedBuffer( new Float32Array( [ 1, 2, 3, 4 ], 2 ) );
+
+	a.needsUpdate = true;
+
+	assert.strictEqual( a.version, 1, "Check version increased" );
+
+} );
+
+QUnit.test( "setArray", function ( assert ) {
+
+	var f32a = new Float32Array( [ 1, 2, 3, 4 ] );
+	var f32b = new Float32Array( [ ] );
+	var a = new THREE.InterleavedBuffer( f32a, 2, false );
+
+	a.setArray( f32a );
+
+	assert.strictEqual( a.count, 2, "Check item count for non-empty array" );
+	assert.strictEqual( a.array, f32a, "Check array itself" );
+
+	a.setArray( f32b );
+
+	assert.strictEqual( a.count, 0, "Check item count for empty array" );
+	assert.strictEqual( a.array, f32b, "Check array itself" );
+
+	assert.throws(
+		function () {
+
+			a.setArray( [ 1, 2, 3, 4 ] );
+
+		},
+		/array should be a Typed Array/,
+		"Calling setArray with a non-typed array throws Error"
+	);
+
+} );
+
+QUnit.test( "copyAt", function ( assert ) {
+
+	var a = new THREE.InterleavedBuffer( new Float32Array( [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ), 3 );
+	var b = new THREE.InterleavedBuffer( new Float32Array( 9 ), 3 );
+	var expected = new Float32Array( [ 4, 5, 6, 7, 8, 9, 1, 2, 3 ] );
+
+	b.copyAt( 1, a, 2 );
+	b.copyAt( 0, a, 1 );
+	b.copyAt( 2, a, 0 );
+
+	assert.deepEqual( b.array, expected, "Check the right values were replaced" );
+
+} );
+
+QUnit.test( "onUpload", function ( assert ) {
+
+	var a = new THREE.InterleavedBuffer();
+	var func = function () { };
+
+	a.onUpload( func );
+
+	assert.strictEqual( a.onUploadCallback, func, "Check callback was set properly" );
+
+} );

+ 98 - 3
test/unit/src/core/Layers.js

@@ -1,6 +1,101 @@
 /**
 /**
- * @author TristanVALCKE / https://github.com/TristanVALCKE
+ * @author moraxy / https://github.com/moraxy
  */
  */
 
 
-//Todo
-console.warn("Todo: Unit tests of Layers")
+QUnit.module( "Layers" );
+
+QUnit.test( "set", function ( assert ) {
+
+	var a = new THREE.Layers();
+
+	a.set( 0 );
+	assert.strictEqual( a.mask, 1, "Set channel 0" );
+
+	a.set( 1 );
+	assert.strictEqual( a.mask, 2, "Set channel 1" );
+
+	a.set( 2 );
+	assert.strictEqual( a.mask, 4, "Set channel 2" );
+
+} );
+
+QUnit.test( "enable", function ( assert ) {
+
+	var a = new THREE.Layers();
+
+	a.set( 0 );
+	a.enable( 0 );
+	assert.strictEqual( a.mask, 1, "Enable channel 0 with mask 0" );
+
+	a.set( 0 );
+	a.enable( 1 );
+	assert.strictEqual( a.mask, 3, "Enable channel 1 with mask 0" );
+
+	a.set( 1 );
+	a.enable( 0 );
+	assert.strictEqual( a.mask, 3, "Enable channel 0 with mask 1" );
+
+	a.set( 1 );
+	a.enable( 1 );
+	assert.strictEqual( a.mask, 2, "Enable channel 1 with mask 1" );
+
+} );
+
+QUnit.test( "disable", function ( assert ) {
+
+	var a = new THREE.Layers();
+
+	a.set( 0 );
+	a.disable( 0 );
+	assert.strictEqual( a.mask, 0, "Disable channel 0 with mask 0" );
+
+	a.set( 0 );
+	a.disable( 1 );
+	assert.strictEqual( a.mask, 1, "Disable channel 1 with mask 0" );
+
+	a.set( 1 );
+	a.disable( 0 );
+	assert.strictEqual( a.mask, 2, "Disable channel 0 with mask 1" );
+
+	a.set( 1 );
+	a.disable( 1 );
+	assert.strictEqual( a.mask, 0, "Disable channel 1 with mask 1" );
+
+} );
+
+QUnit.test( "toggle", function ( assert ) {
+
+	var a = new THREE.Layers();
+
+	a.set( 0 );
+	a.toggle( 0 );
+	assert.strictEqual( a.mask, 0, "Toggle channel 0 with mask 0" );
+
+	a.set( 0 );
+	a.toggle( 1 );
+	assert.strictEqual( a.mask, 3, "Toggle channel 1 with mask 0" );
+
+	a.set( 1 );
+	a.toggle( 0 );
+	assert.strictEqual( a.mask, 3, "Toggle channel 0 with mask 1" );
+
+	a.set( 1 );
+	a.toggle( 1 );
+	assert.strictEqual( a.mask, 0, "Toggle channel 1 with mask 1" );
+
+} );
+
+QUnit.test( "test", function ( assert ) {
+
+	var a = new THREE.Layers();
+	var b = new THREE.Layers();
+
+	assert.ok( a.test( b ), "Start out true" );
+
+	a.set( 1 );
+	assert.notOk( a.test( b ), "Set channel 1 in a and fail the test" );
+
+	b.toggle( 1 );
+	assert.ok( a.test( b ), "Toggle channel 1 in b and pass again" );
+
+} );

+ 400 - 0
test/unit/src/core/Object3D.js

@@ -6,6 +6,24 @@ QUnit.module( "Object3D" );
 
 
 var RadToDeg = 180 / Math.PI;
 var RadToDeg = 180 / Math.PI;
 
 
+var eulerEquals = function ( a, b, tolerance ) {
+
+	tolerance = tolerance || 0.0001;
+
+	if ( a.order != b.order ) {
+
+		return false;
+
+	}
+
+	return (
+		Math.abs( a.x - b.x ) <= tolerance &&
+		Math.abs( a.y - b.y ) <= tolerance &&
+		Math.abs( a.z - b.z ) <= tolerance
+	);
+
+};
+
 QUnit.test( "rotateX" , function( assert ) {
 QUnit.test( "rotateX" , function( assert ) {
 	var obj = new THREE.Object3D();
 	var obj = new THREE.Object3D();
 
 
@@ -80,3 +98,385 @@ QUnit.test( "getWorldRotation" , function( assert ) {
 	obj.lookAt(new THREE.Vector3(1, 0, 0));
 	obj.lookAt(new THREE.Vector3(1, 0, 0));
 	assert.numEqual( obj.getWorldRotation().y * RadToDeg, 90, "y is equal" );
 	assert.numEqual( obj.getWorldRotation().y * RadToDeg, 90, "y is equal" );
 });
 });
+
+QUnit.test( "getObjectById/getObjectByName/getObjectByProperty", function ( assert ) {
+
+	var parent = new THREE.Object3D();
+	var childName = new THREE.Object3D();
+	var childId = new THREE.Object3D(); // id = parent.id + 2
+	var childNothing = new THREE.Object3D();
+
+	parent.prop = true;
+	childName.name = "foo";
+	parent.add( childName, childId, childNothing );
+
+	assert.strictEqual( parent.getObjectByProperty( 'prop', true ), parent, "Get parent by its own property" );
+	assert.strictEqual( parent.getObjectByName( "foo" ), childName, "Get child by name" );
+	assert.strictEqual( parent.getObjectById( parent.id + 2 ), childId, "Get child by Id" );
+	assert.strictEqual(
+		parent.getObjectByProperty( 'no-property', 'no-value' ), undefined,
+		"Unknown property results in undefined"
+	);
+
+} );
+
+QUnit.test( "setRotationFromAxisAngle", function ( assert ) {
+
+	var a = new THREE.Object3D();
+	var axis = new THREE.Vector3( 0, 1, 0 );
+	var angle = Math.PI;
+	var expected = new THREE.Euler( - Math.PI, 0, - Math.PI );
+
+	a.setRotationFromAxisAngle( axis, angle );
+	assert.ok( eulerEquals( a.getWorldRotation(), expected ), "Correct values after rotation" );
+
+	axis.set( 1, 0, 0 );
+	angle = 0;
+	expected.set( 0, 0, 0 );
+
+	a.setRotationFromAxisAngle( axis, angle );
+	assert.ok( eulerEquals( a.getWorldRotation(), expected ), "Correct values after zeroing" );
+
+} );
+
+QUnit.test( "setRotationFromEuler", function ( assert ) {
+
+	var a = new THREE.Object3D();
+	var rotation = new THREE.Euler( THREE.Math.degToRad( 45 ), 0, Math.PI );
+	var expected = rotation.clone(); // bit obvious
+
+	a.setRotationFromEuler( rotation );
+	assert.ok( eulerEquals( a.getWorldRotation(), expected ), "Correct values after rotation" );
+
+} );
+
+QUnit.test( "setRotationFromQuaternion", function ( assert ) {
+
+	var a = new THREE.Object3D();
+	var rotation = new THREE.Quaternion().setFromEuler( new THREE.Euler( Math.PI, 0, - Math.PI ) );
+	var expected = new THREE.Euler( Math.PI, 0, - Math.PI );
+
+	a.setRotationFromQuaternion( rotation );
+	assert.ok( eulerEquals( a.getWorldRotation(), expected ), "Correct values after rotation" );
+
+} );
+
+QUnit.test( "setRotationFromMatrix", function ( assert ) {
+
+	var a = new THREE.Object3D();
+	var m = new THREE.Matrix4();
+	var eye = new THREE.Vector3( 0, 0, 0 );
+	var target = new THREE.Vector3( 0, 1, - 1 );
+	var up = new THREE.Vector3( 0, 1, 0 );
+
+	m.lookAt( eye, target, up );
+	a.setRotationFromMatrix( m );
+	assert.numEqual( a.getWorldRotation().x * RadToDeg, 45, "Correct rotation angle" );
+
+} );
+
+QUnit.test( "copy", function ( assert ) {
+
+	var a = new THREE.Object3D();
+	var b = new THREE.Object3D();
+	var child = new THREE.Object3D();
+	var childChild = new THREE.Object3D();
+
+	a.name = "original";
+	b.name = "to-be-copied";
+
+	b.position.set( x, y, z );
+	b.quaternion.set( x, y, z, w );
+	b.scale.set( 2, 3, 4 );
+
+	// bogus test values
+	b.matrix.set( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 );
+	b.matrixWorld.set( 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 );
+
+	b.matrixAutoUpdate = false;
+	b.matrixWorldNeedsUpdate = true;
+
+	b.layers.mask = 2;
+	b.visible = false;
+
+	b.castShadow = true;
+	b.receiveShadow = true;
+
+	b.frustumCulled = false;
+	b.renderOrder = 1;
+
+	b.userData[ "foo" ] = "bar";
+
+	child.add( childChild );
+	b.add( child );
+
+	assert.notDeepEqual( a, b, "Objects are not equal pre-copy()" );
+	a.copy( b, true );
+
+	// check they're all unique instances
+	assert.ok(
+		a.uuid !== b.uuid &&
+		a.children[ 0 ].uuid !== b.children[ 0 ].uuid &&
+		a.children[ 0 ].children[ 0 ].uuid !== b.children[ 0 ].children[ 0 ].uuid,
+		"UUIDs are all different"
+	);
+
+	// and now fix that
+	a.uuid = b.uuid;
+	a.children[ 0 ].uuid = b.children[ 0 ].uuid;
+	a.children[ 0 ].children[ 0 ].uuid = b.children[ 0 ].children[ 0 ].uuid;
+
+	assert.deepEqual( a, b, "Objects are equal post-copy()" );
+
+} );
+
+QUnit.test( "clone", function ( assert ) {
+
+	var a;
+	var b = new THREE.Object3D();
+
+	assert.strictEqual( a, undefined, "Undefined pre-clone()" );
+
+	a = b.clone();
+	assert.notStrictEqual( a, b, "Defined but seperate instances post-clone()" );
+
+	a.uuid = b.uuid;
+	assert.deepEqual( a, b, "But identical properties" );
+
+} );
+
+QUnit.test( "toJSON", function ( assert ) {
+
+	var a = new THREE.Object3D();
+	var child = new THREE.Object3D();
+	var childChild = new THREE.Object3D();
+
+	a.name = "a's name";
+	a.matrix.set( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 );
+	a.visible = false;
+	a.castShadow = true;
+	a.receiveShadow = true;
+	a.userData[ "foo" ] = "bar";
+
+	child.uuid = "5D4E9AE8-DA61-4912-A575-71A5BE3D72CD";
+	childChild.uuid = "B43854B3-E970-4E85-BD41-AAF8D7BFA189";
+	child.add( childChild );
+	a.add( child );
+
+	var gold = {
+		"metadata": {
+			"version": 4.5,
+			"type": "Object",
+			"generator": "Object3D.toJSON"
+		},
+		"object": {
+			"uuid": "0A1E4F43-CB5B-4097-8F82-DC2969C0B8C2",
+			"type": "Object3D",
+			"name": "a's name",
+			"castShadow": true,
+			"receiveShadow": true,
+			"visible": false,
+			"userData": { "foo": "bar" },
+			"matrix": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
+			"children": [
+				{
+					"uuid": "5D4E9AE8-DA61-4912-A575-71A5BE3D72CD",
+					"type": "Object3D",
+					"matrix": [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
+					"children": [
+						{
+							"uuid": "B43854B3-E970-4E85-BD41-AAF8D7BFA189",
+							"type": "Object3D",
+							"matrix": [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]
+						}
+					]
+				}
+			]
+		}
+	};
+
+	// hacks
+	var out = a.toJSON();
+	out.object.uuid = "0A1E4F43-CB5B-4097-8F82-DC2969C0B8C2";
+
+	assert.deepEqual( out, gold, "JSON is as expected" );
+
+} );
+
+QUnit.test( "add/remove", function ( assert ) {
+
+	var a = new THREE.Object3D();
+	var child1 = new THREE.Object3D();
+	var child2 = new THREE.Object3D();
+
+	assert.strictEqual( a.children.length, 0, "Starts with no children" );
+
+	a.add( child1 );
+	assert.strictEqual( a.children.length, 1, "The first child was added" );
+	assert.strictEqual( a.children[ 0 ], child1, "It's the right one" );
+
+	a.add( child2 );
+	assert.strictEqual( a.children.length, 2, "The second child was added" );
+	assert.strictEqual( a.children[ 1 ], child2, "It's the right one" );
+	assert.strictEqual( a.children[ 0 ], child1, "The first one is still there" );
+
+	a.remove( child1 );
+	assert.strictEqual( a.children.length, 1, "The first child was removed" );
+	assert.strictEqual( a.children[ 0 ], child2, "The second one is still there" );
+
+	a.add( child1 );
+	a.remove( child1, child2 );
+	assert.strictEqual( a.children.length, 0, "Both children were removed at once" );
+
+	child1.add( child2 );
+	assert.strictEqual( child1.children.length, 1, "The second child was added to the first one" );
+	a.add( child2 );
+	assert.strictEqual( a.children.length, 1, "The second one was added to the parent (no remove)" );
+	assert.strictEqual( a.children[ 0 ], child2, "The second one is now the parent's child again" );
+	assert.strictEqual( child1.children.length, 0, "The first one no longer has any children" );
+
+} );
+
+QUnit.test( "applyQuaternion", function ( assert ) {
+
+	var a = new THREE.Object3D();
+	var sqrt = 0.5 * Math.sqrt( 2 );
+	var quat = new THREE.Quaternion( 0, sqrt, 0, sqrt );
+	var expected = new THREE.Quaternion( sqrt / 2, sqrt / 2, 0, 0 );
+
+	a.quaternion.set( 0.25, 0.25, 0.25, 0.25 );
+	a.applyQuaternion( quat );
+
+	assert.ok(
+		Math.abs( a.quaternion.x - expected.x ) <= eps &&
+		Math.abs( a.quaternion.y - expected.y ) <= eps &&
+		Math.abs( a.quaternion.z - expected.z ) <= eps,
+		"Quaternion has the expected values"
+	);
+
+} );
+
+QUnit.test( "applyMatrix", function ( assert ) {
+
+	var a = new THREE.Object3D();
+	var m = new THREE.Matrix4();
+	var expectedPos = new THREE.Vector3( x, y, z );
+	var expectedQuat = new THREE.Quaternion( 0.5 * Math.sqrt( 2 ), 0, 0, 0.5 * Math.sqrt( 2 ) );
+
+	m.makeRotationX( Math.PI / 2 );
+	m.setPosition( new THREE.Vector3( x, y, z ) );
+
+	a.applyMatrix( m );
+
+	assert.deepEqual( a.position, expectedPos, "Position has the expected values" );
+	assert.ok(
+		Math.abs( a.quaternion.x - expectedQuat.x ) <= eps &&
+		Math.abs( a.quaternion.y - expectedQuat.y ) <= eps &&
+		Math.abs( a.quaternion.z - expectedQuat.z ) <= eps,
+		"Quaternion has the expected values"
+	);
+
+} );
+
+QUnit.test( "getWorldPosition", function ( assert ) {
+
+	var a = new THREE.Object3D();
+	var b = new THREE.Object3D();
+	var expectedSingle = new THREE.Vector3( x, y, z );
+	var expectedParent = new THREE.Vector3( x, y, 0 );
+	var expectedChild = new THREE.Vector3( x, y, 7 + ( z - z ) );
+
+	a.translateX( x );
+	a.translateY( y );
+	a.translateZ( z );
+
+	assert.deepEqual(
+		a.getWorldPosition(), expectedSingle,
+		"WorldPosition as expected for single object"
+	);
+
+	// translate child and then parent
+	b.translateZ( 7 );
+	a.add( b );
+	a.translateZ( - z );
+
+	assert.deepEqual( a.getWorldPosition(), expectedParent, "WorldPosition as expected for parent" );
+	assert.deepEqual( b.getWorldPosition(), expectedChild, "WorldPosition as expected for child" );
+
+} );
+
+QUnit.test( "getWorldScale", function ( assert ) {
+
+	var a = new THREE.Object3D();
+	var m = new THREE.Matrix4().makeScale( x, y, z );
+	var expected = new THREE.Vector3( x, y, z );
+
+	a.applyMatrix( m );
+
+	assert.deepEqual( a.getWorldScale(), expected, "WorldScale as expected" );
+
+} );
+
+QUnit.test( "getWorldDirection", function ( assert ) {
+
+	var a = new THREE.Object3D();
+	var expected = new THREE.Vector3( 0, - 0.5 * Math.sqrt( 2 ), 0.5 * Math.sqrt( 2 ) );
+	var dir;
+
+	a.lookAt( new THREE.Vector3( 0, - 1, 1 ) );
+	dir = a.getWorldDirection();
+
+	assert.ok(
+		Math.abs( dir.x - expected.x ) <= eps &&
+		Math.abs( dir.y - expected.y ) <= eps &&
+		Math.abs( dir.z - expected.z ) <= eps,
+		"Direction has the expected values"
+	);
+
+} );
+
+QUnit.test( "traverse/traverseVisible/traverseAncestors", function ( assert ) {
+
+	var a = new THREE.Object3D();
+	var b = new THREE.Object3D();
+	var c = new THREE.Object3D();
+	var d = new THREE.Object3D();
+	var names = [];
+	var expectedNormal = [ "parent", "child", "childchild 1", "childchild 2" ];
+	var expectedVisible = [ "parent", "child", "childchild 2" ];
+	var expectedAncestors = [ "child", "parent" ];
+
+	a.name = "parent";
+	b.name = "child";
+	c.name = "childchild 1";
+	c.visible = false;
+	d.name = "childchild 2";
+
+	b.add( c );
+	b.add( d );
+	a.add( b );
+
+	a.traverse( function ( obj ) {
+
+		names.push( obj.name );
+
+	} );
+	assert.deepEqual( names, expectedNormal, "Traversed objects in expected order" );
+
+	names = [];
+	a.traverseVisible( function ( obj ) {
+
+		names.push( obj.name );
+
+	} );
+	assert.deepEqual( names, expectedVisible, "Traversed visible objects in expected order" );
+
+	names = [];
+	c.traverseAncestors( function ( obj ) {
+
+		names.push( obj.name );
+
+	} );
+	assert.deepEqual( names, expectedAncestors, "Traversed ancestors in expected order" );
+
+} );

+ 33 - 0
test/unit/src/core/Raycaster.js

@@ -117,3 +117,36 @@ function getObjectsToCheck() {
 function getSphere() {
 function getSphere() {
 	return new THREE.Mesh( new THREE.SphereGeometry( 1, 100, 100 ) );
 	return new THREE.Mesh( new THREE.SphereGeometry( 1, 100, 100 ) );
 }
 }
+
+QUnit.test( "set", function ( assert ) {
+
+	var origin = new THREE.Vector3( 0, 0, 0 );
+	var direction = new THREE.Vector3( 0, 0, - 1 );
+	var a = new THREE.Raycaster( origin.clone(), direction.clone() );
+
+	assert.deepEqual( a.ray.origin, origin, "Origin is correct" );
+	assert.deepEqual( a.ray.direction, direction, "Direction is correct" );
+
+	origin.set( 1, 1, 1 );
+	direction.set( - 1, 0, 0 );
+	a.set( origin, direction );
+
+	assert.deepEqual( a.ray.origin, origin, "Origin was set correctly" );
+	assert.deepEqual( a.ray.direction, direction, "Direction was set correctly" );
+
+} );
+
+QUnit.test( "setFromCamera (Orthographic)", function ( assert ) {
+
+	var raycaster = new THREE.Raycaster();
+	var rayOrigin = raycaster.ray.origin;
+	var rayDirection = raycaster.ray.direction;
+	var camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1000 );
+	var expectedOrigin = new THREE.Vector3( 0, 0, 0 );
+	var expectedDirection = new THREE.Vector3( 0, 0, - 1 );
+
+	raycaster.setFromCamera( { x: 0, y: 0 }, camera );
+	assert.deepEqual( rayOrigin, expectedOrigin, "Ray origin has the right coordinates" );
+	assert.deepEqual( rayDirection, expectedDirection, "Camera and Ray are pointing towards -z" );
+
+} );

+ 29 - 3
test/unit/src/core/Uniform.js

@@ -1,6 +1,32 @@
 /**
 /**
- * @author TristanVALCKE / https://github.com/TristanVALCKE
+ * @author moraxy / https://github.com/moraxy
  */
  */
 
 
-//Todo
-console.warn("Todo: Unit tests of Uniform")
+QUnit.module( "Uniform" );
+
+QUnit.test( "constructor", function ( assert ) {
+
+	var a;
+	var b = new THREE.Vector3( x, y, z );
+
+	a = new THREE.Uniform( 5 );
+	assert.strictEqual( a.value, 5, "New constructor works with simple values" );
+
+	a = new THREE.Uniform( b );
+	assert.ok( a.value.equals( b ), "New constructor works with complex values" );
+
+} );
+
+QUnit.test( "clone", function ( assert ) {
+
+	var a = new THREE.Uniform( 23 );
+	var b = a.clone();
+
+	assert.strictEqual( b.value, a.value, "clone() with simple values works" );
+
+	a = new THREE.Uniform( new THREE.Vector3( 1, 2, 3 ) );
+	b = a.clone();
+
+	assert.ok( b.value.equals( a.value ), "clone() with complex values works" );
+
+} );