summaryrefslogtreecommitdiffstats
path: root/o3d/tests
diff options
context:
space:
mode:
Diffstat (limited to 'o3d/tests')
-rw-r--r--o3d/tests/archive_files/BumpReflect.fx99
-rw-r--r--o3d/tests/archive_files/bogus.tar.gz4
-rw-r--r--o3d/tests/archive_files/keyboard.jpgbin0 -> 37521 bytes
-rw-r--r--o3d/tests/archive_files/keyboard.jpg.gzbin0 -> 29434 bytes
-rw-r--r--o3d/tests/archive_files/perc.aifbin0 -> 155526 bytes
-rw-r--r--o3d/tests/archive_files/test1.tarbin0 -> 10240 bytes
-rw-r--r--o3d/tests/archive_files/test1.tar.gzbin0 -> 248 bytes
-rw-r--r--o3d/tests/archive_files/test2.tar.gzbin0 -> 148746 bytes
-rw-r--r--o3d/tests/basic_system_test/basic_system_test.cc340
-rw-r--r--o3d/tests/basic_system_test/reference_frames/frame_capture0.pngbin0 -> 2454 bytes
-rw-r--r--o3d/tests/basic_system_test/reference_frames/frame_capture1.pngbin0 -> 2738 bytes
-rw-r--r--o3d/tests/basic_system_test/reference_frames/frame_capture2.pngbin0 -> 2735 bytes
-rw-r--r--o3d/tests/basic_system_test/reference_frames/frame_capture3.pngbin0 -> 2719 bytes
-rw-r--r--o3d/tests/basic_system_test/reference_frames/frame_capture4.pngbin0 -> 2764 bytes
-rw-r--r--o3d/tests/basic_system_test/reference_stream.csv242
-rw-r--r--o3d/tests/bitmap_test/5kx5k.ddsbin0 -> 4096 bytes
-rw-r--r--o3d/tests/bitmap_test/5kx5k.jpgbin0 -> 147286 bytes
-rw-r--r--o3d/tests/bitmap_test/5kx5k.pngbin0 -> 84347 bytes
-rw-r--r--o3d/tests/bitmap_test/5kx5k.tgabin0 -> 4096 bytes
-rw-r--r--o3d/tests/bitmap_test/dds-dxt1-256x256-alpha.ddsbin0 -> 32896 bytes
-rw-r--r--o3d/tests/bitmap_test/dds-dxt1-256x256-mipmap.ddsbin0 -> 43832 bytes
-rw-r--r--o3d/tests/bitmap_test/dds-dxt1-256x256.ddsbin0 -> 32896 bytes
-rw-r--r--o3d/tests/bitmap_test/dds-dxt3-256x256-alpha.ddsbin0 -> 65664 bytes
-rw-r--r--o3d/tests/bitmap_test/dds-dxt3-256x256-mipmap.ddsbin0 -> 87536 bytes
-rw-r--r--o3d/tests/bitmap_test/dds-dxt5-256x256-alpha.ddsbin0 -> 65664 bytes
-rw-r--r--o3d/tests/bitmap_test/dds-dxt5-256x256-mipmap.ddsbin0 -> 87536 bytes
-rw-r--r--o3d/tests/bitmap_test/gif-256x256-interlaced.gifbin0 -> 35523 bytes
-rw-r--r--o3d/tests/bitmap_test/gif-256x256.gifbin0 -> 30735 bytes
-rw-r--r--o3d/tests/bitmap_test/jpeg-256x256.jpgbin0 -> 48355 bytes
-rw-r--r--o3d/tests/bitmap_test/png-256x256-24bit-interlaced.pngbin0 -> 76052 bytes
-rw-r--r--o3d/tests/bitmap_test/png-256x256-24bit.pngbin0 -> 52647 bytes
-rw-r--r--o3d/tests/bitmap_test/png-256x256-32bit.pngbin0 -> 52154 bytes
-rw-r--r--o3d/tests/bitmap_test/png-256x256-8bit-palette-alpha.pngbin0 -> 18474 bytes
-rw-r--r--o3d/tests/bitmap_test/png-256x256-8bit-palette.pngbin0 -> 18216 bytes
-rw-r--r--o3d/tests/bitmap_test/test_source.psdbin0 -> 1742489 bytes
-rw-r--r--o3d/tests/bitmap_test/tga-256x256-24bit.tgabin0 -> 196652 bytes
-rw-r--r--o3d/tests/bitmap_test/tga-256x256-32bit.tgabin0 -> 262188 bytes
-rw-r--r--o3d/tests/build.scons706
-rw-r--r--o3d/tests/common/build.scons55
-rw-r--r--o3d/tests/common/cross/main.cc45
-rw-r--r--o3d/tests/common/cross/test_utils.cc68
-rw-r--r--o3d/tests/common/cross/test_utils.h51
-rw-r--r--o3d/tests/common/linux/testing_common.cc169
-rw-r--r--o3d/tests/common/mac/testing_common.cc134
-rw-r--r--o3d/tests/common/win/dxcapture.cc140
-rw-r--r--o3d/tests/common/win/dxcapture.h68
-rw-r--r--o3d/tests/common/win/system_test.h82
-rw-r--r--o3d/tests/common/win/testing_common.cc202
-rw-r--r--o3d/tests/common/win/testing_common.h66
-rw-r--r--o3d/tests/keyboard_info.html126
-rw-r--r--o3d/tests/package_tests.bat37
-rw-r--r--o3d/tests/presubmit_tests.bat35
-rw-r--r--o3d/tests/selenium/__init__.py0
-rw-r--r--o3d/tests/selenium/javascript_unit_test_list.txt88
-rw-r--r--o3d/tests/selenium/javascript_unit_tests.py304
-rw-r--r--o3d/tests/selenium/main.py954
-rw-r--r--o3d/tests/selenium/pulse_testrunner.py149
-rw-r--r--o3d/tests/selenium/run.bat34
-rw-r--r--o3d/tests/selenium/sample_list.txt118
-rw-r--r--o3d/tests/selenium/samples_tests.py528
-rw-r--r--o3d/tests/selenium/selenium_constants.py67
-rw-r--r--o3d/tests/selenium/selenium_utilities.py287
-rw-r--r--o3d/tests/selenium/tests/assets/archive.o3dtgzbin0 -> 308669 bytes
-rw-r--r--o3d/tests/selenium/tests/base-test.html81
-rw-r--r--o3d/tests/selenium/tests/culling-zsort-test.html459
-rw-r--r--o3d/tests/selenium/tests/drawshapes.html395
-rw-r--r--o3d/tests/selenium/tests/effect-import-test.html154
-rw-r--r--o3d/tests/selenium/tests/event-test.html284
-rw-r--r--o3d/tests/selenium/tests/features-test.html157
-rw-r--r--o3d/tests/selenium/tests/init-status-test.html117
-rw-r--r--o3d/tests/selenium/tests/math-test.html861
-rw-r--r--o3d/tests/selenium/tests/no-rendergraph.html80
-rw-r--r--o3d/tests/selenium/tests/non-cachable-params.html250
-rw-r--r--o3d/tests/selenium/tests/offscreen-test.html158
-rw-r--r--o3d/tests/selenium/tests/ownership-test.html210
-rw-r--r--o3d/tests/selenium/tests/pixel-perfection.html337
-rw-r--r--o3d/tests/selenium/tests/quaternion-test.html335
-rw-r--r--o3d/tests/selenium/tests/render-test.html136
-rw-r--r--o3d/tests/selenium/tests/serialization-test.html1376
-rw-r--r--o3d/tests/selenium/tests/test-test.html368
-rw-r--r--o3d/tests/selenium/tests/type-test.html166
-rw-r--r--o3d/tests/selenium/tests/util-test.html111
-rw-r--r--o3d/tests/selenium/tests/v8-test.html772
-rw-r--r--o3d/tests/selenium/tests/version-check-test.html90
-rw-r--r--o3d/tests/selenium/tests/window-overlap-test.html119
-rw-r--r--o3d/tests/selenium/tests/window-overlap-top.html63
-rw-r--r--o3d/tests/test_driver.bat33
-rw-r--r--o3d/tests/test_driver.py433
-rw-r--r--o3d/tests/testing_framework_hardware.PIXExp2
-rw-r--r--o3d/tests/testing_framework_reference.PIXExp2
90 files changed, 12747 insertions, 0 deletions
diff --git a/o3d/tests/archive_files/BumpReflect.fx b/o3d/tests/archive_files/BumpReflect.fx
new file mode 100644
index 0000000..684bc6d
--- /dev/null
+++ b/o3d/tests/archive_files/BumpReflect.fx
@@ -0,0 +1,99 @@
+float4x4 world : World;
+float4x4 worldInverseTranspose : WorldInverseTranspose;
+float4x4 worldViewProj : WorldViewProjection;
+float4x4 viewInverse : ViewInverse;
+
+////////////////
+// tweakables //
+////////////////
+
+float BumpHeight <
+ string UIName = "Bump Factor";
+ float UIMin = 0.0;
+ float UIMax = 2.0;
+ float UIStep = 0.1;
+> = 0.2;
+
+texture normalMap : Normal <
+ string UIName = "Normal Map";
+ string name = "NMP_Ripples2_512.dds";
+ string type = "2D";
+>;
+
+sampler2D NormalSampler = sampler_state {
+ Texture = <normalMap>;
+ MinFilter = Linear;
+ MagFilter = Linear;
+ MipFilter = Linear;
+};
+
+texture envMap : Environment <
+ string UIName = "Cube Map";
+ string type = "CUBE";
+ string name = "sunol_cubemap.dds";
+>;
+
+samplerCUBE EnvSampler = sampler_state {
+ Texture = <envMap>;
+ MinFilter = Linear;
+ MipFilter = Linear;
+ MagFilter = Linear;
+// WrapS = ClampToEdge;
+// WrapT = ClampToEdge;
+};
+
+////////////////
+// structures //
+////////////////
+
+struct a2v
+{
+ float4 Position : POSITION; //in object space
+ float3 TexCoord : TEXCOORD0;
+ float3 Tangent : TANGENT; // TANGENT; // ATTR14; // in object space
+ float3 Binormal : BINORMAL; // BINORMAL; // ATTR15; // in object space
+ float3 Normal : NORMAL; //in object space
+};
+
+struct v2f
+{
+ float4 Position : POSITION; //in projection space
+ float2 TexCoord : TEXCOORD0;
+ float3 worldEyeVec : TEXCOORD1;
+ float3 WorldNormal : TEXCOORD2;
+ float3 WorldTangent : TEXCOORD3;
+ float3 WorldBinorm : TEXCOORD4;
+};
+
+/////////////
+// shaders //
+/////////////
+
+v2f BumpReflectVS(a2v IN)
+{
+ v2f OUT;
+ OUT.Position = mul(IN.Position, worldViewProj);
+ OUT.TexCoord.xy = IN.TexCoord.xy;
+ OUT.WorldNormal = mul(float4(IN.Normal, 0), worldInverseTranspose).xyz;
+ OUT.WorldTangent = mul(float4(IN.Tangent, 0), worldInverseTranspose).xyz;
+ OUT.WorldBinorm = mul(float4(IN.Binormal, 0), worldInverseTranspose).xyz;
+ float3 worldPos = mul(IN.Position, world).xyz;
+ OUT.worldEyeVec = normalize(worldPos - viewInverse[3].xyz);
+ return OUT;
+}
+
+float4 BumpReflectPS(v2f IN) : COLOR {
+ float2 bump = (tex2D(NormalSampler, IN.TexCoord.xy).xy * 2 - 1) * BumpHeight;
+ float3 normal = normalize(IN.WorldNormal);
+ float3 tangent = normalize(IN.WorldTangent);
+ float3 binormal = normalize(IN.WorldBinorm);
+ float3 nb = normal + bump.x * tangent + bump.y * binormal;
+ nb = normalize(nb);
+ float3 worldEyeVec = normalize(IN.worldEyeVec);
+ float3 lookup = reflect(worldEyeVec, nb);
+ return texCUBE(EnvSampler, float4(lookup, 1).xyz);
+}
+
+// #o3d VertexShaderEntryPoint BumpReflectVS
+// #o3d PixelShaderEntryPoint BumpReflectPS
+
diff --git a/o3d/tests/archive_files/bogus.tar.gz b/o3d/tests/archive_files/bogus.tar.gz
new file mode 100644
index 0000000..989c181
--- /dev/null
+++ b/o3d/tests/archive_files/bogus.tar.gz
@@ -0,0 +1,4 @@
+fjdfijeifjeijfiejfijef
+efifjeijfe
+efislja;djfa
+efkejslfra
diff --git a/o3d/tests/archive_files/keyboard.jpg b/o3d/tests/archive_files/keyboard.jpg
new file mode 100644
index 0000000..1fdba53
--- /dev/null
+++ b/o3d/tests/archive_files/keyboard.jpg
Binary files differ
diff --git a/o3d/tests/archive_files/keyboard.jpg.gz b/o3d/tests/archive_files/keyboard.jpg.gz
new file mode 100644
index 0000000..18ee068
--- /dev/null
+++ b/o3d/tests/archive_files/keyboard.jpg.gz
Binary files differ
diff --git a/o3d/tests/archive_files/perc.aif b/o3d/tests/archive_files/perc.aif
new file mode 100644
index 0000000..fc40178
--- /dev/null
+++ b/o3d/tests/archive_files/perc.aif
Binary files differ
diff --git a/o3d/tests/archive_files/test1.tar b/o3d/tests/archive_files/test1.tar
new file mode 100644
index 0000000..fe1e22d
--- /dev/null
+++ b/o3d/tests/archive_files/test1.tar
Binary files differ
diff --git a/o3d/tests/archive_files/test1.tar.gz b/o3d/tests/archive_files/test1.tar.gz
new file mode 100644
index 0000000..9767501
--- /dev/null
+++ b/o3d/tests/archive_files/test1.tar.gz
Binary files differ
diff --git a/o3d/tests/archive_files/test2.tar.gz b/o3d/tests/archive_files/test2.tar.gz
new file mode 100644
index 0000000..4986e73
--- /dev/null
+++ b/o3d/tests/archive_files/test2.tar.gz
Binary files differ
diff --git a/o3d/tests/basic_system_test/basic_system_test.cc b/o3d/tests/basic_system_test/basic_system_test.cc
new file mode 100644
index 0000000..b37ac8e8
--- /dev/null
+++ b/o3d/tests/basic_system_test/basic_system_test.cc
@@ -0,0 +1,340 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// Basic O3D system test for constructing and rendering geometry.
+
+#include <math.h>
+
+#include "core/cross/client.h"
+#include "core/cross/counter.h"
+#include "core/cross/draw_context.h"
+#include "core/cross/draw_pass.h"
+#include "core/cross/draw_list.h"
+#include "core/cross/primitive.h"
+#include "core/cross/stream_bank.h"
+#include "core/cross/tree_traversal.h"
+#include "core/cross/clear_buffer.h"
+#include "core/cross/math_utilities.h"
+#include "import/cross/raw_data.h"
+#include "tests/common/win/testing_common.h"
+#include "tests/common/win/system_test.h"
+
+namespace o3d {
+
+float DegreesToRadians(float degrees) {
+ return degrees * kPi / 180.f;
+}
+
+const char kShaderString[] =
+"// World View Projection matrix that will transform the input vertices \n"
+"// to screen space. \n"
+"float4x4 worldViewProjection : WorldViewProjection; \n"
+" \n"
+"// input parameters for our vertex shader \n"
+"struct VertexShaderInput { \n"
+" float4 position : POSITION; \n"
+"}; \n"
+" \n"
+"// input parameters for our pixel shader \n"
+"struct PixelShaderInput { \n"
+" float4 position : POSITION; \n"
+"}; \n"
+"/** \n"
+"* The vertex shader simply transforms the input vertices to screen space. \n"
+"*/ \n"
+"PixelShaderInput vertexShaderFunction(VertexShaderInput input) { \n"
+" PixelShaderInput output; \n"
+" \n"
+" // Multiply the vertex positions by the worldViewProjection matrix to \n"
+" // transform them to screen space. \n"
+" output.position = mul(input.position, worldViewProjection); \n"
+" return output; \n"
+"} \n"
+" \n"
+"/** \n"
+"* This pixel shader just returns the color red. \n"
+"*/ \n"
+"float4 pixelShaderFunction(PixelShaderInput input): COLOR { \n"
+" return float4(1, 0, 0, 1); // Red. \n"
+"} \n"
+" \n"
+"// Here we tell our effect file *which* functions are \n"
+"// our vertex and pixel shaders. \n"
+" \n"
+"// #o3d VertexShaderEntryPoint vertexShaderFunction \n"
+"// #o3d PixelShaderEntryPoint pixelShaderFunction \n"
+"// #o3d MatrixLoadOrder RowMajor \n";
+
+// System-test class for basic o3d geometry construction and render
+// functionality.
+class BasicSystemTest : public testing::Test {
+ protected:
+ BasicSystemTest()
+ : object_manager_(g_service_locator) {}
+
+ virtual void SetUp();
+ virtual void TearDown();
+
+ Client* client() {
+ return client_;
+ }
+
+ Pack* pack() {
+ return pack_;
+ }
+
+ DrawContext* context() {
+ return context_;
+ }
+
+ DrawList* opaque_draw_list() {
+ return opaque_draw_list_;
+ }
+
+ DrawList* transparent_draw_list() {
+ return transparent_draw_list_;
+ }
+
+ // Constructs a cube, and returns the transform node under which
+ // the new cube shape resides.
+ void CreateCube(Material::Ref material, Transform::Ref* transform);
+
+ ServiceDependency<ObjectManager> object_manager_;
+
+ private:
+ Client *client_;
+ Pack* pack_;
+ DrawContext* context_;
+ DrawList* opaque_draw_list_;
+ DrawList* transparent_draw_list_;
+};
+
+void BasicSystemTest::SetUp() {
+ client_ = new o3d::Client(g_service_locator);
+ client_->Init();
+
+ pack_ = object_manager_->CreatePack();
+
+ Transform* root = client_->root();
+
+ // Creates a clear buffer render node and parents it to the root.
+ ClearBuffer* clear_buffer = pack_->Create<ClearBuffer>();
+ clear_buffer->set_priority(0);
+ clear_buffer->set_clear_color(Float4(0.5f, 0.5f, 0.5f, 1.0f));
+ clear_buffer->SetParent(client_->render_graph_root());
+
+ // Create DrawLists
+ opaque_draw_list_ = pack_->Create<DrawList>();
+ transparent_draw_list_ = pack_->Create<DrawList>();
+
+ // Create DrawContext.
+ context_ = pack_->Create<DrawContext>();
+
+ // Creates a TreeTraversal and parents it to the root.
+ TreeTraversal* tree_traversal = pack_->Create<TreeTraversal>();
+ tree_traversal->set_priority(1);
+ tree_traversal->SetParent(client_->render_graph_root());
+
+ // Creates a DrawPass for opaque shapes.
+ DrawPass* opaque_draw_pass = pack_->Create<DrawPass>();
+ opaque_draw_pass->set_priority(2);
+ opaque_draw_pass->set_draw_list(opaque_draw_list_);
+ opaque_draw_pass->SetParent(client_->render_graph_root());
+
+ // Creates a DrawPass for transparent shapes.
+ DrawPass* transparent_draw_pass = pack_->Create<DrawPass>();
+ transparent_draw_pass->set_priority(3);
+ transparent_draw_pass->set_draw_list(transparent_draw_list_);
+ transparent_draw_pass->SetParent(client_->render_graph_root());
+
+ // Register the passlists and drawcontext with the TreeTraversal
+ tree_traversal->RegisterDrawList(opaque_draw_list_, context_, true);
+ tree_traversal->RegisterDrawList(transparent_draw_list_, context_, true);
+ tree_traversal->set_transform(root);
+
+ const Point3 eye(0.0f, 1.0f, 5.0f);
+ const Point3 target(0.0f, 0.0f, 0.0f);
+ const Vector3 up(0.0f, 1.0f, 0.0f);
+
+ const Matrix4 perspective_matrix = Vectormath::Aos::CreatePerspectiveMatrix(
+ DegreesToRadians(60.0f),
+ 1.0f,
+ 1.0f,
+ 1000.0f);
+ const Matrix4 view_matrix = Matrix4::lookAt(eye, target, up);
+
+ context_->set_view(view_matrix);
+ context_->set_projection(perspective_matrix);
+}
+
+void BasicSystemTest::TearDown() {
+ // Force another render to make the stream capture end.
+ client()->RenderClient();
+
+ pack_->Destroy();
+ delete client_;
+}
+
+void BasicSystemTest::CreateCube(Material::Ref material,
+ Transform::Ref* transform) {
+ Shape::Ref cube_shape(pack()->Create<Shape>());
+ ASSERT_FALSE(cube_shape.IsNull());
+
+ Transform::Ref cube_xform(pack()->Create<Transform>());
+ ASSERT_FALSE(cube_xform.IsNull());
+
+ // Create the Primitive that will contain the geometry data for
+ // the cube.
+ Primitive::Ref cube_primitive(pack()->Create<Primitive>());
+ ASSERT_FALSE(cube_primitive.IsNull());
+
+ // Create a StreamBank to hold the streams of vertex data.
+ StreamBank::Ref stream_bank(pack()->Create<StreamBank>());
+ ASSERT_FALSE(stream_bank.IsNull());
+
+ // Assign the material that was passed in to the primitive.
+ cube_primitive->set_material(material);
+
+ // Assign the Primitive to the Shape.
+ cube_primitive->SetOwner(cube_shape);
+
+ // Assign the StreamBank to the Primitive.
+ cube_primitive->set_stream_bank(stream_bank);
+
+ cube_primitive->set_primitive_type(Primitive::TRIANGLELIST);
+ cube_primitive->set_number_primitives(12); // 12 triangles
+ cube_primitive->set_number_vertices(8); // 8 vertices in total
+
+ cube_primitive->CreateDrawElement(pack(), NULL);
+
+ static const float kPositionArray[][3] = {
+ {-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},
+ {0.5, -0.5, -0.5}
+ };
+
+ static const unsigned int kIndicesArray[] = {
+ 0, 1, 2, // face 1
+ 2, 1, 3,
+ 2, 3, 4, // face 2
+ 4, 3, 5,
+ 4, 5, 6, // face 3
+ 6, 5, 7,
+ 6, 7, 0, // face 4
+ 0, 7, 1,
+ 1, 7, 3, // face 5
+ 3, 7, 5,
+ 6, 0, 4, // face 6
+ 4, 0, 2
+ };
+
+ static const unsigned int kNumComponents = arraysize(kPositionArray[0]);
+ static const unsigned int kNumElements = arraysize(kPositionArray);
+ static const unsigned int kStride = kNumComponents;
+
+ VertexBuffer::Ref positions_buffer(pack()->Create<VertexBuffer>());
+
+ FloatField::Ref positions_field(positions_buffer->CreateField(
+ FloatField::GetApparentClass(),
+ 3));
+ ASSERT_FALSE(positions_field.IsNull());
+
+ ASSERT_TRUE(positions_buffer->AllocateElements(kNumElements));
+ positions_field->SetFromFloats(&kPositionArray[0][0], kStride, 0,
+ kNumElements);
+
+ IndexBuffer::Ref index_buffer(pack()->Create<IndexBuffer>());
+ ASSERT_FALSE(index_buffer.IsNull());
+
+ static const unsigned int kNumIndices = arraysize(kIndicesArray);
+ ASSERT_TRUE(index_buffer->AllocateElements(kNumIndices));
+ index_buffer->index_field()->SetFromUInt32s(&kIndicesArray[0], 1,
+ 0, kNumIndices);
+
+ // Associate the positions Buffer with the StreamBank.
+ stream_bank->SetVertexStream(
+ Stream::POSITION, // semantic: This stream stores vertex positions
+ 0, // semantic index: First (and only) position stream
+ positions_field, // field: the field this stream uses.
+ 0); // start_index: How many elements to skip in the
+ // field.
+
+ // Associate the triangle indices Buffer with the primitive.
+ cube_primitive->set_index_buffer(index_buffer);
+
+ cube_xform->AddShape(cube_shape);
+ *transform = cube_xform;
+}
+
+TEST_F(BasicSystemTest, BasicSystemTestCase) {
+ ASSERT_FALSE(client()->root() == NULL);
+
+ Transform *spin_transform = pack()->Create<Transform>();
+ ASSERT_FALSE(spin_transform == NULL);
+
+ Transform* root = client()->root();
+ spin_transform->SetParent(root);
+
+ Material::Ref cube_material(pack()->Create<Material>());
+ ASSERT_FALSE(cube_material.IsNull());
+ cube_material->set_draw_list(opaque_draw_list());
+
+ Effect::Ref effect(pack()->Create<Effect>());
+ effect->LoadFromFXString(kShaderString);
+ cube_material->set_effect(effect);
+
+ Transform::Ref cube_xform;
+ CreateCube(cube_material, &cube_xform);
+ ASSERT_FALSE(cube_xform.IsNull());
+ cube_xform->SetParent(spin_transform);
+
+ ASSERT_FALSE(spin_transform->GetChildren()[0] == NULL);
+
+ // Assert that 5 rendered frames generate both the correct command streams,
+ // and framebuffer contents.
+ BEGIN_ASSERT_STREAM_CAPTURE();
+ for (int frame_count = 0; frame_count < 5; ++frame_count) {
+ client()->RenderClient();
+ ASSERT_FRAMEBUFFER();
+ Matrix4 mat(Matrix4::rotationY(static_cast<float>(frame_count) * 2 *
+ static_cast<float>(M_PI) / 5.0f));
+ spin_transform->set_local_matrix(mat);
+ }
+ END_ASSERT_STREAM_CAPTURE();
+}
+
+} // namespace o3d
diff --git a/o3d/tests/basic_system_test/reference_frames/frame_capture0.png b/o3d/tests/basic_system_test/reference_frames/frame_capture0.png
new file mode 100644
index 0000000..cfe991f
--- /dev/null
+++ b/o3d/tests/basic_system_test/reference_frames/frame_capture0.png
Binary files differ
diff --git a/o3d/tests/basic_system_test/reference_frames/frame_capture1.png b/o3d/tests/basic_system_test/reference_frames/frame_capture1.png
new file mode 100644
index 0000000..3f7baeb
--- /dev/null
+++ b/o3d/tests/basic_system_test/reference_frames/frame_capture1.png
Binary files differ
diff --git a/o3d/tests/basic_system_test/reference_frames/frame_capture2.png b/o3d/tests/basic_system_test/reference_frames/frame_capture2.png
new file mode 100644
index 0000000..b498023
--- /dev/null
+++ b/o3d/tests/basic_system_test/reference_frames/frame_capture2.png
Binary files differ
diff --git a/o3d/tests/basic_system_test/reference_frames/frame_capture3.png b/o3d/tests/basic_system_test/reference_frames/frame_capture3.png
new file mode 100644
index 0000000..fd5719c
--- /dev/null
+++ b/o3d/tests/basic_system_test/reference_frames/frame_capture3.png
Binary files differ
diff --git a/o3d/tests/basic_system_test/reference_frames/frame_capture4.png b/o3d/tests/basic_system_test/reference_frames/frame_capture4.png
new file mode 100644
index 0000000..2fff834
--- /dev/null
+++ b/o3d/tests/basic_system_test/reference_frames/frame_capture4.png
Binary files differ
diff --git a/o3d/tests/basic_system_test/reference_stream.csv b/o3d/tests/basic_system_test/reference_stream.csv
new file mode 100644
index 0000000..eb38ab4
--- /dev/null
+++ b/o3d/tests/basic_system_test/reference_stream.csv
@@ -0,0 +1,242 @@
+Event Type,EID,Event,StartTime,Frame,Duration,FPS
+Session Start,1,Start Session,0,,0,
+Process Start,2,Start Process,0,,0,
+Frame,3,Frame 2,30598216619,2,2655979068,0.4
+Call,4,D3DPERF_GetStatus(),30598284778,,,
+User Marker,5,User Marker: Frame_Capture: 306 : tests\basic_system_test\import_test.cc,31097816048,,0,
+User Marker,6,User Marker: CaptureScreenContents,31097864253,,0,
+Call,7,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x003EBEC4 --> 0x041F4D40)",31097951090,,,
+Call,8,<0x041F4040> IDirect3DDevice9::GetDepthStencilSurface(0x003EBEC8 --> 0x041C7D68),31097999717,,,
+Call,9,<0x041F4040> IDirect3DDevice9::BeginScene(),31098043720,,,
+Call,10,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F3BC --> 0x041F4D40)",31098090625,,,
+Call,11,<0x041F4D40> IDirect3DSurface9::GetDesc(0x0012F394),31098134913,,,
+Call,12,<0x041F4D40> IDirect3DSurface9::Release(),31098179695,,,
+Call,13,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F3BC --> 0x041F4D40)",31098233507,,,
+Call,14,<0x041F4D40> IDirect3DSurface9::GetDesc(0x0012F394),31098277332,,,
+Call,15,<0x041F4D40> IDirect3DSurface9::Release(),31098320525,,,
+Call,16,<0x041F4040> IDirect3DDevice9::SetViewport(0x0012F39C),31098365740,,,
+Call,17,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F6C8 --> 0x041F4D40)",31098432568,,,
+Call,18,<0x041F4040> IDirect3DDevice9::GetDepthStencilSurface(0x0012F6BC --> 0x041C7D68),31098476648,,,
+Call,19,"<0x041F4040> IDirect3DDevice9::Clear(0x00000000, NULL, 0x00000007, D3DCOLOR_ARGB(0xff,0x7f,0x7f,0x7f), 1.000f, 0x00000000)",31098521071,,,
+Call,20,<0x041C7D68> IDirect3DSurface9::Release(),31098568276,,,
+Call,21,<0x041F4D40> IDirect3DSurface9::Release(),31098611436,,,
+Call,22,<0x041F4040> IDirect3DDevice9::SetIndices(0x041C85B8),31098727017,,,
+Call,23,"<0x041F4040> IDirect3DDevice9::SetStreamSource(0, 0x041C84E8, 0, 12)",31098885293,,,
+Call,24,<0x041F4040> IDirect3DDevice9::SetVertexDeclaration(0x041C8820),31098945447,,,
+Call,25,"<0x041C8298> ID3DXEffect::SetMatrix(0xFAC1DD7B, 0x0012EECC)",31099004241,,,
+Call,26,<0x041C8298> ID3DXEffect::GetTechnique(0),31099061461,,,
+Call,27,<0x041C8298> ID3DXEffect::SetTechnique(0xFAC1D0CB),31099114543,,,
+Call,28,"<0x041C8298> ID3DXEffect::Begin(0x0012EF60, 0x00000000)",31099167480,,,
+Call,29,<0x041CACF0> IDirect3DStateBlock9::Capture(),31099185394,,,
+Call,30,<0x041C9AB8> IDirect3DStateBlock9::Capture(),31099249010,,,
+Call,31,<0x041C94E8> IDirect3DStateBlock9::Capture(),31099306834,,,
+Call,32,<0x041CA088> IDirect3DStateBlock9::Capture(),31099362781,,,
+Call,33,<0x041C8948> IDirect3DStateBlock9::Capture(),31099417686,,,
+Call,34,<0x041C8298> ID3DXEffect::BeginPass(0),31099514400,,,
+Call,35,<0x041F4040> IDirect3DDevice9::SetVertexShader(0x041C81E0),31099533107,,,
+Call,36,"<0x041F4040> IDirect3DDevice9::SetVertexShaderConstantF(0, 0x053E22E0, 4)",31099588736,,,
+Call,37,<0x041F4040> IDirect3DDevice9::SetPixelShader(0x041C8128),31099644021,,,
+Call,38,"<0x041F4040> IDirect3DDevice9::DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12)",31099736770,,,
+Call,39,<0x041C8298> ID3DXEffect::EndPass(),31099825615,,,
+Call,40,<0x041C8298> ID3DXEffect::End(),31099887465,,,
+Call,41,<0x041C8948> IDirect3DStateBlock9::Apply(),31099905619,,,
+Call,42,<0x041CA088> IDirect3DStateBlock9::Apply(),31099959011,,,
+Call,43,<0x041C94E8> IDirect3DStateBlock9::Apply(),31100002488,,,
+Call,44,<0x041C9AB8> IDirect3DStateBlock9::Apply(),31100045426,,,
+Call,45,<0x041CACF0> IDirect3DStateBlock9::Apply(),31100090448,,,
+Call,46,<0x041F4040> IDirect3DDevice9::EndScene(),31100181906,,,
+Call,47,<0x041F4D40> IDirect3DSurface9::Release(),31100226786,,,
+Call,48,<0x041C7D68> IDirect3DSurface9::Release(),31100271023,,,
+Call,49,"<0x041F4040> IDirect3DDevice9::Present(NULL, NULL, NULL, NULL)",31100330032,,,
+Frame,50,Frame 3,33254239772,3,2655502064,0.4
+Call,51,D3DPERF_GetStatus(),33254341389,,,
+User Marker,52,User Marker: Frame_Capture: 306 : tests\basic_system_test\import_test.cc,33754093579,,0,
+User Marker,53,User Marker: CaptureScreenContents,33754168712,,0,
+Call,54,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x003EBEC4 --> 0x041F4D40)",33754242875,,,
+Call,55,<0x041F4040> IDirect3DDevice9::GetDepthStencilSurface(0x003EBEC8 --> 0x041C7D68),33754291635,,,
+Call,56,<0x041F4040> IDirect3DDevice9::BeginScene(),33754335630,,,
+Call,57,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F3BC --> 0x041F4D40)",33754382330,,,
+Call,58,<0x041F4D40> IDirect3DSurface9::GetDesc(0x0012F394),33754426625,,,
+Call,59,<0x041F4D40> IDirect3DSurface9::Release(),33754471340,,,
+Call,60,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F3BC --> 0x041F4D40)",33754516463,,,
+Call,61,<0x041F4D40> IDirect3DSurface9::GetDesc(0x0012F394),33754560173,,,
+Call,62,<0x041F4D40> IDirect3DSurface9::Release(),33754603635,,,
+Call,63,<0x041F4040> IDirect3DDevice9::SetViewport(0x0012F39C),33754648955,,,
+Call,64,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F6C8 --> 0x041F4D40)",33754715152,,,
+Call,65,<0x041F4040> IDirect3DDevice9::GetDepthStencilSurface(0x0012F6BC --> 0x041C7D68),33754758979,,,
+Call,66,"<0x041F4040> IDirect3DDevice9::Clear(0x00000000, NULL, 0x00000007, D3DCOLOR_ARGB(0xff,0x7f,0x7f,0x7f), 1.000f, 0x00000000)",33754803029,,,
+Call,67,<0x041C7D68> IDirect3DSurface9::Release(),33754858946,,,
+Call,68,<0x041F4D40> IDirect3DSurface9::Release(),33754902321,,,
+Call,69,<0x041F4040> IDirect3DDevice9::SetIndices(0x041C85B8),33755087163,,,
+Call,70,"<0x041F4040> IDirect3DDevice9::SetStreamSource(0, 0x041C84E8, 0, 12)",33755142683,,,
+Call,71,<0x041F4040> IDirect3DDevice9::SetVertexDeclaration(0x041C8820),33755189392,,,
+Call,72,"<0x041C8298> ID3DXEffect::SetMatrix(0xFAC1DD7B, 0x0012EECC)",33755236937,,,
+Call,73,<0x041C8298> ID3DXEffect::GetTechnique(0),33755282727,,,
+Call,74,<0x041C8298> ID3DXEffect::SetTechnique(0xFAC1D0CB),33755325955,,,
+Call,75,"<0x041C8298> ID3DXEffect::Begin(0x0012EF60, 0x00000000)",33755369875,,,
+Call,76,<0x041CACF0> IDirect3DStateBlock9::Capture(),33755384392,,,
+Call,77,<0x041C9AB8> IDirect3DStateBlock9::Capture(),33755431049,,,
+Call,78,<0x041C94E8> IDirect3DStateBlock9::Capture(),33755475384,,,
+Call,79,<0x041CA088> IDirect3DStateBlock9::Capture(),33755518354,,,
+Call,80,<0x041C8948> IDirect3DStateBlock9::Capture(),33755561217,,,
+Call,81,<0x041C8298> ID3DXEffect::BeginPass(0),33755635633,,,
+Call,82,<0x041F4040> IDirect3DDevice9::SetVertexShader(0x041C81E0),33755651344,,,
+Call,83,"<0x041F4040> IDirect3DDevice9::SetVertexShaderConstantF(0, 0x053E22E0, 4)",33755697184,,,
+Call,84,<0x041F4040> IDirect3DDevice9::SetPixelShader(0x041C8128),33755742764,,,
+Call,85,"<0x041F4040> IDirect3DDevice9::DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12)",33755818852,,,
+Call,86,<0x041C8298> ID3DXEffect::EndPass(),33755867400,,,
+Call,87,<0x041C8298> ID3DXEffect::End(),33755910380,,,
+Call,88,<0x041C8948> IDirect3DStateBlock9::Apply(),33755923289,,,
+Call,89,<0x041CA088> IDirect3DStateBlock9::Apply(),33755988713,,,
+Call,90,<0x041C94E8> IDirect3DStateBlock9::Apply(),33756051677,,,
+Call,91,<0x041C9AB8> IDirect3DStateBlock9::Apply(),33756095407,,,
+Call,92,<0x041CACF0> IDirect3DStateBlock9::Apply(),33756140752,,,
+Call,93,<0x041F4040> IDirect3DDevice9::EndScene(),33756230874,,,
+Call,94,<0x041F4D40> IDirect3DSurface9::Release(),33756275152,,,
+Call,95,<0x041C7D68> IDirect3DSurface9::Release(),33756318974,,,
+Call,96,"<0x041F4040> IDirect3DDevice9::Present(NULL, NULL, NULL, NULL)",33756374044,,,
+Frame,97,Frame 4,35909807940,4,2701946354,0.4
+Call,98,D3DPERF_GetStatus(),35909909042,,,
+User Marker,99,User Marker: Frame_Capture: 306 : tests\basic_system_test\import_test.cc,36409267444,,0,
+User Marker,100,User Marker: CaptureScreenContents,36409356534,,0,
+Call,101,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x003EBEC4 --> 0x041F4D40)",36409433720,,,
+Call,102,<0x041F4040> IDirect3DDevice9::GetDepthStencilSurface(0x003EBEC8 --> 0x041C7D68),36409482529,,,
+Call,103,<0x041F4040> IDirect3DDevice9::BeginScene(),36409526599,,,
+Call,104,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F3BC --> 0x041F4D40)",36409573357,,,
+Call,105,<0x041F4D40> IDirect3DSurface9::GetDesc(0x0012F394),36409617609,,,
+Call,106,<0x041F4D40> IDirect3DSurface9::Release(),36409662462,,,
+Call,107,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F3BC --> 0x041F4D40)",36409707537,,,
+Call,108,<0x041F4D40> IDirect3DSurface9::GetDesc(0x0012F394),36409750972,,,
+Call,109,<0x041F4D40> IDirect3DSurface9::Release(),36409794277,,,
+Call,110,<0x041F4040> IDirect3DDevice9::SetViewport(0x0012F39C),36409839547,,,
+Call,111,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F6C8 --> 0x041F4D40)",36409906133,,,
+Call,112,<0x041F4040> IDirect3DDevice9::GetDepthStencilSurface(0x0012F6BC --> 0x041C7D68),36409950343,,,
+Call,113,"<0x041F4040> IDirect3DDevice9::Clear(0x00000000, NULL, 0x00000007, D3DCOLOR_ARGB(0xff,0x7f,0x7f,0x7f), 1.000f, 0x00000000)",36409994496,,,
+Call,114,<0x041C7D68> IDirect3DSurface9::Release(),36410040548,,,
+Call,115,<0x041F4D40> IDirect3DSurface9::Release(),36410083603,,,
+Call,116,<0x041F4040> IDirect3DDevice9::SetIndices(0x041C85B8),36410199387,,,
+Call,117,"<0x041F4040> IDirect3DDevice9::SetStreamSource(0, 0x041C84E8, 0, 12)",36410296534,,,
+Call,118,<0x041F4040> IDirect3DDevice9::SetVertexDeclaration(0x041C8820),36410343771,,,
+Call,119,"<0x041C8298> ID3DXEffect::SetMatrix(0xFAC1DD7B, 0x0012EECC)",36410391106,,,
+Call,120,<0x041C8298> ID3DXEffect::GetTechnique(0),36410437171,,,
+Call,121,<0x041C8298> ID3DXEffect::SetTechnique(0xFAC1D0CB),36410573271,,,
+Call,122,"<0x041C8298> ID3DXEffect::Begin(0x0012EF60, 0x00000000)",36410618823,,,
+Call,123,<0x041CACF0> IDirect3DStateBlock9::Capture(),36410633515,,,
+Call,124,<0x041C9AB8> IDirect3DStateBlock9::Capture(),36410680440,,,
+Call,125,<0x041C94E8> IDirect3DStateBlock9::Capture(),36410725060,,,
+Call,126,<0x041CA088> IDirect3DStateBlock9::Capture(),36410768165,,,
+Call,127,<0x041C8948> IDirect3DStateBlock9::Capture(),36410811430,,,
+Call,128,<0x041C8298> ID3DXEffect::BeginPass(0),36410886131,,,
+Call,129,<0x041F4040> IDirect3DDevice9::SetVertexShader(0x041C81E0),36410901798,,,
+Call,130,"<0x041F4040> IDirect3DDevice9::SetVertexShaderConstantF(0, 0x053E22E0, 4)",36410947800,,,
+Call,131,<0x041F4040> IDirect3DDevice9::SetPixelShader(0x041C8128),36410993338,,,
+Call,132,"<0x041F4040> IDirect3DDevice9::DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12)",36411069298,,,
+Call,133,<0x041C8298> ID3DXEffect::EndPass(),36411117723,,,
+Call,134,<0x041C8298> ID3DXEffect::End(),36411160953,,,
+Call,135,<0x041C8948> IDirect3DStateBlock9::Apply(),36411174055,,,
+Call,136,<0x041CA088> IDirect3DStateBlock9::Apply(),36411299753,,,
+Call,137,<0x041C94E8> IDirect3DStateBlock9::Apply(),36411354450,,,
+Call,138,<0x041C9AB8> IDirect3DStateBlock9::Apply(),36411407984,,,
+Call,139,<0x041CACF0> IDirect3DStateBlock9::Apply(),36411463799,,,
+Call,140,<0x041F4040> IDirect3DDevice9::EndScene(),36411573417,,,
+Call,141,<0x041F4D40> IDirect3DSurface9::Release(),36411627859,,,
+Call,142,<0x041C7D68> IDirect3DSurface9::Release(),36411680366,,,
+Call,143,"<0x041F4040> IDirect3DDevice9::Present(NULL, NULL, NULL, NULL)",36411737183,,,
+Frame,144,Frame 5,38611822165,5,2704679664,0.4
+Call,145,D3DPERF_GetStatus(),38611921000,,,
+User Marker,146,User Marker: Frame_Capture: 306 : tests\basic_system_test\import_test.cc,39111379596,,0,
+User Marker,147,User Marker: CaptureScreenContents,39111440170,,0,
+Call,148,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x003EBEC4 --> 0x041F4D40)",39111536980,,,
+Call,149,<0x041F4040> IDirect3DDevice9::GetDepthStencilSurface(0x003EBEC8 --> 0x041C7D68),39111596751,,,
+Call,150,<0x041F4040> IDirect3DDevice9::BeginScene(),39111652673,,,
+Call,151,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F3BC --> 0x041F4D40)",39111709028,,,
+Call,152,<0x041F4D40> IDirect3DSurface9::GetDesc(0x0012F394),39111762947,,,
+Call,153,<0x041F4D40> IDirect3DSurface9::Release(),39111820274,,,
+Call,154,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F3BC --> 0x041F4D40)",39111884080,,,
+Call,155,<0x041F4D40> IDirect3DSurface9::GetDesc(0x0012F394),39111938522,,,
+Call,156,<0x041F4D40> IDirect3DSurface9::Release(),39111981857,,,
+Call,157,<0x041F4040> IDirect3DDevice9::SetViewport(0x0012F39C),39112027207,,,
+Call,158,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F6C8 --> 0x041F4D40)",39112093744,,,
+Call,159,<0x041F4040> IDirect3DDevice9::GetDepthStencilSurface(0x0012F6BC --> 0x041C7D68),39112137584,,,
+Call,160,"<0x041F4040> IDirect3DDevice9::Clear(0x00000000, NULL, 0x00000007, D3DCOLOR_ARGB(0xff,0x7f,0x7f,0x7f), 1.000f, 0x00000000)",39112181704,,,
+Call,161,<0x041C7D68> IDirect3DSurface9::Release(),39112227771,,,
+Call,162,<0x041F4D40> IDirect3DSurface9::Release(),39112270774,,,
+Call,163,<0x041F4040> IDirect3DDevice9::SetIndices(0x041C85B8),39112405776,,,
+Call,164,"<0x041F4040> IDirect3DDevice9::SetStreamSource(0, 0x041C84E8, 0, 12)",39112461208,,,
+Call,165,<0x041F4040> IDirect3DDevice9::SetVertexDeclaration(0x041C8820),39112507971,,,
+Call,166,"<0x041C8298> ID3DXEffect::SetMatrix(0xFAC1DD7B, 0x0012EECC)",39112555123,,,
+Call,167,<0x041C8298> ID3DXEffect::GetTechnique(0),39112601068,,,
+Call,168,<0x041C8298> ID3DXEffect::SetTechnique(0xFAC1D0CB),39112644503,,,
+Call,169,"<0x041C8298> ID3DXEffect::Begin(0x0012EF60, 0x00000000)",39112688565,,,
+Call,170,<0x041CACF0> IDirect3DStateBlock9::Capture(),39112703112,,,
+Call,171,<0x041C9AB8> IDirect3DStateBlock9::Capture(),39112749709,,,
+Call,172,<0x041C94E8> IDirect3DStateBlock9::Capture(),39112794005,,,
+Call,173,<0x041CA088> IDirect3DStateBlock9::Capture(),39112837254,,,
+Call,174,<0x041C8948> IDirect3DStateBlock9::Capture(),39112880402,,,
+Call,175,<0x041C8298> ID3DXEffect::BeginPass(0),39112955206,,,
+Call,176,<0x041F4040> IDirect3DDevice9::SetVertexShader(0x041C81E0),39112970822,,,
+Call,177,"<0x041F4040> IDirect3DDevice9::SetVertexShaderConstantF(0, 0x053E22E0, 4)",39113026769,,,
+Call,178,<0x041F4040> IDirect3DDevice9::SetPixelShader(0x041C8128),39113072436,,,
+Call,179,"<0x041F4040> IDirect3DDevice9::DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12)",39113148480,,,
+Call,180,<0x041C8298> ID3DXEffect::EndPass(),39113196757,,,
+Call,181,<0x041C8298> ID3DXEffect::End(),39113239885,,,
+Call,182,<0x041C8948> IDirect3DStateBlock9::Apply(),39113252804,,,
+Call,183,<0x041CA088> IDirect3DStateBlock9::Apply(),39113297209,,,
+Call,184,<0x041C94E8> IDirect3DStateBlock9::Apply(),39113370402,,,
+Call,185,<0x041C9AB8> IDirect3DStateBlock9::Apply(),39113423302,,,
+Call,186,<0x041CACF0> IDirect3DStateBlock9::Apply(),39113468437,,,
+Call,187,<0x041F4040> IDirect3DDevice9::EndScene(),39113556772,,,
+Call,188,<0x041F4D40> IDirect3DSurface9::Release(),39113600702,,,
+Call,189,<0x041C7D68> IDirect3DSurface9::Release(),39113644717,,,
+Call,190,"<0x041F4040> IDirect3DDevice9::Present(NULL, NULL, NULL, NULL)",39113702049,,,
+Frame,191,Frame 6,41316567158,6,2768584865,0.4
+Call,192,D3DPERF_GetStatus(),41316651078,,,
+User Marker,193,User Marker: Frame_Capture: 306 : tests\basic_system_test\import_test.cc,41816443226,,0,
+User Marker,194,User Marker: CaptureScreenContents,41816519169,,0,
+Call,195,D3DPERF_GetStatus(),41816586445,,,
+User Marker,196,User Marker: EndCommandStreamCapture,41816632693,,0,
+Call,197,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x003EBEC4 --> 0x041F4D40)",41816688500,,,
+Call,198,<0x041F4040> IDirect3DDevice9::GetDepthStencilSurface(0x003EBEC8 --> 0x041C7D68),41816735932,,,
+Call,199,<0x041F4040> IDirect3DDevice9::BeginScene(),41816779852,,,
+Call,200,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F558 --> 0x041F4D40)",41816826552,,,
+Call,201,<0x041F4D40> IDirect3DSurface9::GetDesc(0x0012F530),41816870777,,,
+Call,202,<0x041F4D40> IDirect3DSurface9::Release(),41816915274,,,
+Call,203,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F558 --> 0x041F4D40)",41816960532,,,
+Call,204,<0x041F4D40> IDirect3DSurface9::GetDesc(0x0012F530),41817004137,,,
+Call,205,<0x041F4D40> IDirect3DSurface9::Release(),41817047360,,,
+Call,206,<0x041F4040> IDirect3DDevice9::SetViewport(0x0012F538),41817092544,,,
+Call,207,"<0x041F4040> IDirect3DDevice9::GetRenderTarget(0x00000000, 0x0012F864 --> 0x041F4D40)",41817159118,,,
+Call,208,<0x041F4040> IDirect3DDevice9::GetDepthStencilSurface(0x0012F858 --> 0x041C7D68),41817203298,,,
+Call,209,"<0x041F4040> IDirect3DDevice9::Clear(0x00000000, NULL, 0x00000007, D3DCOLOR_ARGB(0xff,0x7f,0x7f,0x7f), 1.000f, 0x00000000)",41817247511,,,
+Call,210,<0x041C7D68> IDirect3DSurface9::Release(),41817293471,,,
+Call,211,<0x041F4D40> IDirect3DSurface9::Release(),41817336286,,,
+Call,212,<0x041F4040> IDirect3DDevice9::SetIndices(0x041C85B8),41817458434,,,
+Call,213,"<0x041F4040> IDirect3DDevice9::SetStreamSource(0, 0x041C84E8, 0, 12)",41817509654,,,
+Call,214,<0x041F4040> IDirect3DDevice9::SetVertexDeclaration(0x041C8820),41817556511,,,
+Call,215,"<0x041C8298> ID3DXEffect::SetMatrix(0xFAC1DD7B, 0x0012F068)",41817604223,,,
+Call,216,<0x041C8298> ID3DXEffect::GetTechnique(0),41817649908,,,
+Call,217,<0x041C8298> ID3DXEffect::SetTechnique(0xFAC1D0CB),41817693486,,,
+Call,218,"<0x041C8298> ID3DXEffect::Begin(0x0012F0FC, 0x00000000)",41817737806,,,
+Call,219,<0x041CACF0> IDirect3DStateBlock9::Capture(),41817752540,,,
+Call,220,<0x041C9AB8> IDirect3DStateBlock9::Capture(),41817799022,,,
+Call,221,<0x041C94E8> IDirect3DStateBlock9::Capture(),41817843227,,,
+Call,222,<0x041CA088> IDirect3DStateBlock9::Capture(),41817886413,,,
+Call,223,<0x041C8948> IDirect3DStateBlock9::Capture(),41817929598,,,
+Call,224,<0x041C8298> ID3DXEffect::BeginPass(0),41818003948,,,
+Call,225,<0x041F4040> IDirect3DDevice9::SetVertexShader(0x041C81E0),41818019610,,,
+Call,226,"<0x041F4040> IDirect3DDevice9::SetVertexShaderConstantF(0, 0x053E22E0, 4)",41818065208,,,
+Call,227,<0x041F4040> IDirect3DDevice9::SetPixelShader(0x041C8128),41818110687,,,
+Call,228,"<0x041F4040> IDirect3DDevice9::DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12)",41818186683,,,
+Call,229,<0x041C8298> ID3DXEffect::EndPass(),41818234970,,,
+Call,230,<0x041C8298> ID3DXEffect::End(),41818278298,,,
+Call,231,<0x041C8948> IDirect3DStateBlock9::Apply(),41818291347,,,
+Call,232,<0x041CA088> IDirect3DStateBlock9::Apply(),41818378780,,,
+Call,233,<0x041C94E8> IDirect3DStateBlock9::Apply(),41818435942,,,
+Call,234,<0x041C9AB8> IDirect3DStateBlock9::Apply(),41818479202,,,
+Call,235,<0x041CACF0> IDirect3DStateBlock9::Apply(),41818523972,,,
+Call,236,<0x041F4040> IDirect3DDevice9::EndScene(),41818612302,,,
+Call,237,<0x041F4D40> IDirect3DSurface9::Release(),41818656510,,,
+Call,238,<0x041C7D68> IDirect3DSurface9::Release(),41818700222,,,
+Call,239,"<0x041F4040> IDirect3DDevice9::Present(NULL, NULL, NULL, NULL)",41818754391,,,
+Process End,240,End Process,44103702736,,0,
+Session End,241,End Session,44103702736,,0,
diff --git a/o3d/tests/bitmap_test/5kx5k.dds b/o3d/tests/bitmap_test/5kx5k.dds
new file mode 100644
index 0000000..626845b
--- /dev/null
+++ b/o3d/tests/bitmap_test/5kx5k.dds
Binary files differ
diff --git a/o3d/tests/bitmap_test/5kx5k.jpg b/o3d/tests/bitmap_test/5kx5k.jpg
new file mode 100644
index 0000000..2395bb3
--- /dev/null
+++ b/o3d/tests/bitmap_test/5kx5k.jpg
Binary files differ
diff --git a/o3d/tests/bitmap_test/5kx5k.png b/o3d/tests/bitmap_test/5kx5k.png
new file mode 100644
index 0000000..458ac24
--- /dev/null
+++ b/o3d/tests/bitmap_test/5kx5k.png
Binary files differ
diff --git a/o3d/tests/bitmap_test/5kx5k.tga b/o3d/tests/bitmap_test/5kx5k.tga
new file mode 100644
index 0000000..2db7349
--- /dev/null
+++ b/o3d/tests/bitmap_test/5kx5k.tga
Binary files differ
diff --git a/o3d/tests/bitmap_test/dds-dxt1-256x256-alpha.dds b/o3d/tests/bitmap_test/dds-dxt1-256x256-alpha.dds
new file mode 100644
index 0000000..79c1c19
--- /dev/null
+++ b/o3d/tests/bitmap_test/dds-dxt1-256x256-alpha.dds
Binary files differ
diff --git a/o3d/tests/bitmap_test/dds-dxt1-256x256-mipmap.dds b/o3d/tests/bitmap_test/dds-dxt1-256x256-mipmap.dds
new file mode 100644
index 0000000..921cab3
--- /dev/null
+++ b/o3d/tests/bitmap_test/dds-dxt1-256x256-mipmap.dds
Binary files differ
diff --git a/o3d/tests/bitmap_test/dds-dxt1-256x256.dds b/o3d/tests/bitmap_test/dds-dxt1-256x256.dds
new file mode 100644
index 0000000..f5e60e2
--- /dev/null
+++ b/o3d/tests/bitmap_test/dds-dxt1-256x256.dds
Binary files differ
diff --git a/o3d/tests/bitmap_test/dds-dxt3-256x256-alpha.dds b/o3d/tests/bitmap_test/dds-dxt3-256x256-alpha.dds
new file mode 100644
index 0000000..f7086b4
--- /dev/null
+++ b/o3d/tests/bitmap_test/dds-dxt3-256x256-alpha.dds
Binary files differ
diff --git a/o3d/tests/bitmap_test/dds-dxt3-256x256-mipmap.dds b/o3d/tests/bitmap_test/dds-dxt3-256x256-mipmap.dds
new file mode 100644
index 0000000..a72239c
--- /dev/null
+++ b/o3d/tests/bitmap_test/dds-dxt3-256x256-mipmap.dds
Binary files differ
diff --git a/o3d/tests/bitmap_test/dds-dxt5-256x256-alpha.dds b/o3d/tests/bitmap_test/dds-dxt5-256x256-alpha.dds
new file mode 100644
index 0000000..388d000
--- /dev/null
+++ b/o3d/tests/bitmap_test/dds-dxt5-256x256-alpha.dds
Binary files differ
diff --git a/o3d/tests/bitmap_test/dds-dxt5-256x256-mipmap.dds b/o3d/tests/bitmap_test/dds-dxt5-256x256-mipmap.dds
new file mode 100644
index 0000000..45b6869
--- /dev/null
+++ b/o3d/tests/bitmap_test/dds-dxt5-256x256-mipmap.dds
Binary files differ
diff --git a/o3d/tests/bitmap_test/gif-256x256-interlaced.gif b/o3d/tests/bitmap_test/gif-256x256-interlaced.gif
new file mode 100644
index 0000000..7d8498e
--- /dev/null
+++ b/o3d/tests/bitmap_test/gif-256x256-interlaced.gif
Binary files differ
diff --git a/o3d/tests/bitmap_test/gif-256x256.gif b/o3d/tests/bitmap_test/gif-256x256.gif
new file mode 100644
index 0000000..fa81387
--- /dev/null
+++ b/o3d/tests/bitmap_test/gif-256x256.gif
Binary files differ
diff --git a/o3d/tests/bitmap_test/jpeg-256x256.jpg b/o3d/tests/bitmap_test/jpeg-256x256.jpg
new file mode 100644
index 0000000..ae4448c
--- /dev/null
+++ b/o3d/tests/bitmap_test/jpeg-256x256.jpg
Binary files differ
diff --git a/o3d/tests/bitmap_test/png-256x256-24bit-interlaced.png b/o3d/tests/bitmap_test/png-256x256-24bit-interlaced.png
new file mode 100644
index 0000000..37663d4
--- /dev/null
+++ b/o3d/tests/bitmap_test/png-256x256-24bit-interlaced.png
Binary files differ
diff --git a/o3d/tests/bitmap_test/png-256x256-24bit.png b/o3d/tests/bitmap_test/png-256x256-24bit.png
new file mode 100644
index 0000000..126dab8
--- /dev/null
+++ b/o3d/tests/bitmap_test/png-256x256-24bit.png
Binary files differ
diff --git a/o3d/tests/bitmap_test/png-256x256-32bit.png b/o3d/tests/bitmap_test/png-256x256-32bit.png
new file mode 100644
index 0000000..8f858f9
--- /dev/null
+++ b/o3d/tests/bitmap_test/png-256x256-32bit.png
Binary files differ
diff --git a/o3d/tests/bitmap_test/png-256x256-8bit-palette-alpha.png b/o3d/tests/bitmap_test/png-256x256-8bit-palette-alpha.png
new file mode 100644
index 0000000..35b61a9
--- /dev/null
+++ b/o3d/tests/bitmap_test/png-256x256-8bit-palette-alpha.png
Binary files differ
diff --git a/o3d/tests/bitmap_test/png-256x256-8bit-palette.png b/o3d/tests/bitmap_test/png-256x256-8bit-palette.png
new file mode 100644
index 0000000..7134cf7
--- /dev/null
+++ b/o3d/tests/bitmap_test/png-256x256-8bit-palette.png
Binary files differ
diff --git a/o3d/tests/bitmap_test/test_source.psd b/o3d/tests/bitmap_test/test_source.psd
new file mode 100644
index 0000000..cd67ce4
--- /dev/null
+++ b/o3d/tests/bitmap_test/test_source.psd
Binary files differ
diff --git a/o3d/tests/bitmap_test/tga-256x256-24bit.tga b/o3d/tests/bitmap_test/tga-256x256-24bit.tga
new file mode 100644
index 0000000..5619323
--- /dev/null
+++ b/o3d/tests/bitmap_test/tga-256x256-24bit.tga
Binary files differ
diff --git a/o3d/tests/bitmap_test/tga-256x256-32bit.tga b/o3d/tests/bitmap_test/tga-256x256-32bit.tga
new file mode 100644
index 0000000..1d20f8c
--- /dev/null
+++ b/o3d/tests/bitmap_test/tga-256x256-32bit.tga
Binary files differ
diff --git a/o3d/tests/build.scons b/o3d/tests/build.scons
new file mode 100644
index 0000000..c6b41a9
--- /dev/null
+++ b/o3d/tests/build.scons
@@ -0,0 +1,706 @@
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+import os
+import sys
+
+Import('env')
+
+env['COMPONENT_TEST_SUBSYSTEM_WINDOWS'] = True
+
+if env.Bit('windows'):
+ # gmock on MSVC currently has 3 issues:
+ # - it needs <tuple>, that MSVC doesn't have. We could use boost for that
+ # - the 1.0.0 version triggers a bug in MSVC where function-local template
+ # types cause duplicate symbols. It may be fixed in a more recent release.
+ # - on MSVC, it triggers a bug where it gets confused between chrome's and
+ # gmock's version of down_cast.
+ use_gmock = False
+else:
+ use_gmock = True
+
+gtest_env = env.Clone()
+gtest_env.Append(
+ CPPPATH = ['$GTEST_DIR/include',
+ '$GTEST_DIR',
+ ]
+)
+gtest_sources = [
+ 'gtest-all',
+]
+gtest_objs = [gtest_env.ComponentObject(name, '$GTEST_DIR/src/%s.cc' % name)
+ for name in gtest_sources]
+
+if use_gmock:
+ gmock_env = gtest_env.Clone()
+ gmock_env.Append(CPPPATH = ['$GMOCK_DIR/include',
+ '$GMOCK_DIR'])
+ gmock_sources = ['gmock-all']
+ gtest_objs += [gmock_env.ComponentObject(name, '$GMOCK_DIR/src/%s.cc' % name)
+ for name in gmock_sources]
+gtest_lib = gtest_env.Library('gtest', gtest_objs)
+
+env.Append(
+ CPPDEFINES = [
+ '_USE_MATH_DEFINES',
+ ],
+ CPPPATH = [
+ env.Dir('$COLLADA_DIR'),
+ env.Dir('$COLLADA_DIR/LibXML/include'),
+ env.Dir('$CG_DIR/include'),
+ # Headers for Antlr runtime library
+ env.Dir('$ANTLRLIBC_DIR/include'),
+ # Headers generated by Antlr for Technique grammar
+ env.Dir('$OBJ_ROOT/compiler/technique'),
+ '$GTEST_DIR/include',
+ '$GMOCK_DIR/include',
+ ],
+ LIBPATH=[
+ env.Dir('.'),
+ env.Dir('$NACL_LIB_DIR'),
+ env.Dir('$OBJ_ROOT/compiler/technique'), # For technique library
+ env.Dir('$OBJ_ROOT/compiler/antlr'), # For antlr3c library
+ ],
+)
+# TODO: rearrange coverage so this can be an append.
+
+# NOTE: Do not rearrange or sort these -- order is
+# (apparently) important here on Linux.
+env.Prepend(
+ LIBS = [
+ 'testing_common',
+ 'o3dImportNoConditioner',
+ 'o3dCore',
+ 'o3dImport',
+ 'o3dArchive',
+ 'o3dCorePlatform',
+ 'o3dSerializer',
+ 'o3dUtils',
+ 'o3d_base',
+ 'FColladaU',
+ 'google_nacl_imc',
+ 'technique',
+ 'antlr3c',
+ 'gtest',
+ 'skia'
+ ] + env['ICU_LIBS'],
+)
+
+# Next, append renderer specific libraries to the linker environment
+env.Append(
+ CPPPATH = env['RENDERER_INCLUDE_PATH'],
+ LIBPATH = [
+ '$CG_DIR/lib',
+ ] + env['RENDERER_LIB_PATH'],
+ LIBS = env['RENDERER_LIBS'],
+)
+
+run_env = env.Clone()
+if env.Bit('linux'):
+ run_env.Append(ENV = os.environ)
+ run_env.Append(
+ ENV = {'LD_LIBRARY_PATH': run_env.Dir('$ARTIFACTS_DIR').abspath}
+ )
+
+run_env.Append(
+ ENV = {'MOZ_PLUGIN_PATH': run_env.Dir('$ARTIFACTS_DIR').abspath,
+ 'MOZ_CRASHREPORTER_DISABLE': '1'},
+ PYTHONPATH = ['$GFLAGS_DIR/python',
+ '$SELENIUM_PYTHON_DIR']
+)
+
+# Install runtime files required for the build type.
+gl_requirements = []
+if env.Bit('windows'):
+ # The install step for these lives in import/build.scons. the
+ # import library always needs the Cg DLLs and executable, even on
+ # D3D.
+ gl_requirements += [
+ '$ARTIFACTS_DIR/cg.dll',
+ '$ARTIFACTS_DIR/cgGL.dll',
+ '$ARTIFACTS_DIR/glew32.dll',
+ ]
+
+# TODO: shouldn't need this.
+# Adding extra line for cleaner formatting.
+env.Help('\n')
+
+# -------------------------------------------------------------------------
+# Unit tests
+
+# The basic unit tests
+tests = [
+ 'base/cross/bits_test.cc',
+ 'compiler/technique/technique_parser_test.cc',
+ 'core/cross/bitmap_test.cc',
+ 'core/cross/bounding_box_test.cc',
+ 'core/cross/buffer_test.cc',
+ 'core/cross/class_manager_test.cc',
+ 'core/cross/client_test.cc',
+ 'core/cross/counter_test.cc',
+ 'core/cross/curve_test.cc',
+ 'core/cross/draw_element_test.cc',
+ 'core/cross/draw_list_test.cc',
+ 'core/cross/draw_pass_test.cc',
+ 'core/cross/effect_test.cc',
+ 'core/cross/element_test.cc',
+ 'core/cross/fake_vertex_source.cc',
+ 'core/cross/features_test.cc',
+ 'core/cross/field_test.cc',
+ 'core/cross/float_n_test.cc',
+ 'core/cross/function_test.cc',
+ 'core/cross/material_test.cc',
+ 'core/cross/math_utilities_test.cc',
+ 'core/cross/matrix4_axis_rotation_test.cc',
+ 'core/cross/matrix4_composition_test.cc',
+ 'core/cross/matrix4_scale_test.cc',
+ 'core/cross/matrix4_translation_test.cc',
+# Test disabled, pending investigation into NaCl API.
+# 'core/cross/message_queue_test.cc',
+ 'core/cross/object_base_test.cc',
+ 'core/cross/pack_test.cc',
+ 'core/cross/param_array_test.cc',
+ 'core/cross/param_object_test.cc',
+ 'core/cross/param_operation_test.cc',
+ 'core/cross/param_test.cc',
+ 'core/cross/performance_timer_test.cc',
+ 'core/cross/primitive_test.cc',
+ 'core/cross/ray_intersection_info_test.cc',
+ 'core/cross/render_node_test.cc',
+ 'core/cross/renderer_test.cc',
+ 'core/cross/service_locator_test.cc',
+ 'core/cross/shape_test.cc',
+ 'core/cross/skin_test.cc',
+ 'core/cross/smart_ptr_test.cc',
+ 'core/cross/state_set_test.cc',
+ 'core/cross/state_test.cc',
+ 'core/cross/stream_bank_test.cc',
+ 'core/cross/stream_test.cc',
+ 'core/cross/transform_test.cc',
+ 'core/cross/tree_traversal_test.cc',
+ 'core/cross/vector_map_test.cc',
+ 'core/cross/vertex_source_test.cc',
+ 'core/cross/visitor_base_test.cc',
+ 'core/cross/weak_ptr_test.cc',
+ 'import/cross/gz_compressor_test.cc',
+ 'import/cross/gz_decompressor_test.cc',
+ 'import/cross/memory_buffer_test.cc',
+ 'import/cross/memory_stream_test.cc',
+ 'import/cross/raw_data_test.cc',
+ 'import/cross/tar_generator_test.cc',
+ 'import/cross/tar_processor_test.cc',
+ 'import/cross/targz_generator_test.cc',
+ 'import/cross/targz_processor_test.cc',
+ 'serializer/cross/serializer_test.cc',
+ 'tests/common/cross/test_utils.cc',
+ 'utils/cross/file_path_utils_test.cc',
+ 'utils/cross/file_text_reader_test.cc',
+ 'utils/cross/json_writer_test.cc',
+ 'utils/cross/string_reader_test.cc',
+ 'utils/cross/string_writer_test.cc',
+ 'utils/cross/temporary_file_test.cc',
+]
+
+if env['TARGET_PLATFORM']=='WINDOWS':
+ tests += [
+ # These are temporarily just being run for windows but should be
+ # moved to global when logging is working for the mac.
+ 'plugin/cross/plugin_logging_test.cc',
+ 'statsreport/aggregator-win32_unittest.cc',
+ 'statsreport/aggregator_unittest.cc',
+ 'statsreport/formatter_unittest.cc',
+ 'statsreport/metrics_unittest.cc',
+ 'statsreport/persistent_iterator-win32_unittest.cc',
+ 'breakpad/win/bluescreen_detector_test.cc',
+ ]
+ env.Append(
+ LINKFLAGS=['/SUBSYSTEM:WINDOWS'],
+ LIBS=[
+ 'o3dStatsreport_Common',
+ 'o3dStatsreport',
+ 'o3dPlugin_logging',
+ 'o3dBreakpad',
+ 'shlwapi',
+ ]
+ )
+
+# add command buffer renderer unit tests only on that renderer platform.
+if 'RENDERER_CB' in env['CPPDEFINES']:
+ tests += [
+ 'command_buffer/client/cross/id_allocator_test.cc',
+ 'command_buffer/common/cross/bitfield_helpers_test.cc',
+ 'command_buffer/service/cross/effect_utils_test.cc',
+ 'command_buffer/service/cross/resource_test.cc',
+ ]
+ if use_gmock:
+ tests += [
+ 'command_buffer/client/cross/cmd_buffer_helper_test.cc',
+ 'command_buffer/client/cross/fenced_allocator_test.cc',
+ 'command_buffer/client/cross/buffer_sync_proxy_test.cc',
+ 'command_buffer/service/cross/buffer_rpc_test.cc',
+ 'command_buffer/service/cross/cmd_buffer_engine_test.cc',
+ 'command_buffer/service/cross/cmd_parser_test.cc',
+ ]
+
+unit_tests = env.Program('unit_tests', tests)
+
+# After building the test executable, force the windows executable to output
+# stdout to a console window
+# TODO: if WinMain/main is handled differently this isn't needed.
+if env['TARGET_PLATFORM'] == 'WINDOWS':
+ blessed = env.AddPostAction(unit_tests, 'editbin /SUBSYSTEM:CONSOLE $TARGET')
+else:
+ blessed = unit_tests
+
+# Install to artifacts.
+unit_tests_install = env.Replicate('$ARTIFACTS_DIR', blessed)
+
+# Install any data required by the basic unit-tests
+unit_tests_req = []
+
+env['TEST_DATA_DIR'] = '$SCONSTRUCT_DIR/import/test_data'
+unit_tests_req += env.Replicate(
+ '$ARTIFACTS_DIR/unittest_data',
+ [
+ '$TEST_DATA_DIR/crate.dae',
+ '$TEST_DATA_DIR/crate.jpg',
+ '$TEST_DATA_DIR/rock01.tga',
+ '$TEST_DATA_DIR/rock02.tga',
+ ])
+
+env['TECHNIQUE_TEST_DATA_DIR'] = '$SCONSTRUCT_DIR/compiler/technique/test_data'
+unit_tests_req += env.Replicate(
+ '$ARTIFACTS_DIR/unittest_data',
+ [
+ '$TECHNIQUE_TEST_DATA_DIR/fur.fx',
+ '$TECHNIQUE_TEST_DATA_DIR/lambert.fx',
+ '$TECHNIQUE_TEST_DATA_DIR/noshader.fx',
+ '$TECHNIQUE_TEST_DATA_DIR/notechnique.fx',
+ '$TECHNIQUE_TEST_DATA_DIR/sampler_test.fx',
+ '$TECHNIQUE_TEST_DATA_DIR/shadow_map.fx',
+ '$TECHNIQUE_TEST_DATA_DIR/simple.fx',
+ ])
+
+unit_tests_req += env.Replicate(
+ '$ARTIFACTS_DIR/bitmap_test',
+ '$SCONSTRUCT_DIR/tests/bitmap_test/*')
+
+unit_tests_req += env.Replicate(
+ '$ARTIFACTS_DIR/archive_files',
+ '$SCONSTRUCT_DIR/tests/archive_files/*')
+
+# Also require gl related libraries based on variant.
+unit_tests_req += gl_requirements
+
+# Add requirements for unit tests.
+env.Requires(unit_tests_install, unit_tests_req)
+
+# TODO: temporarily continue to use aliases to run tests.
+# This need to eventually switch to use ComponentTestProgram once
+# other sections have been adjusted.
+env['PROGRAM_NAME'] = unit_tests_install[0].path
+env['PROGRAM_BASENAME'] = os.path.join('$ARTIFACTS_DIR', 'unit_tests')
+env.AlwaysBuild(run_env.Alias('unit_tests', unit_tests_install,
+ env.subst('$COMPONENT_TEST_CMDLINE')))
+
+# Add help.
+env.Help('unit_tests: run unit tests\n')
+
+if env.Bit('mac'):
+ env['SHLINKFLAGS'] = ['-F$CG_DIR']
+ env['SHLIBPREFIX'] = ['']
+ env['SHLIBSUFFIX'] = ['']
+
+ env.Append(
+ FRAMEWORKS = [
+ 'AGL',
+ 'Accelerate',
+ 'AudioToolbox',
+ 'AudioUnit',
+ 'Carbon',
+ 'Cg',
+ 'CoreAudio',
+ 'Foundation',
+ 'GLUT',
+ 'OpenAL',
+ 'OpenGL',
+ ],
+ CCFLAGS = ['-F$CG_DIR'],
+ LINKFLAGS = ['-F$CG_DIR'],
+ SHLINKFLAGS = ['-F$CG_DIR']
+ )
+
+ env.AddPostAction(
+ unit_tests_install,
+ [
+ # Change install name reference
+ # make Cg.framework point to copy in $THIRD_PARTY
+ '/usr/bin/install_name_tool '
+ '-change @executable_path/../Library/Frameworks/Cg.framework/Cg '
+ '"$CG_DIR/Cg.framework/Cg" '
+ '"$ARTIFACTS_DIR/unit_tests"'
+ ])
+
+
+#----------------------------------------------------------------------------
+# Install the perceptual diff tool and its requirements.
+
+installed_pdiff = None
+pdiff = None
+pdiff_requirements = []
+if env.Bit('windows'):
+ # Install the perceptual-diff utility for lossy image comparisons
+ pdiff = '$PDIFF_DIR/bin/win/perceptualdiff.exe'
+ # perceptualdiff.exe also needs FreeImage.dll to run
+ pdiff_requirements = env.Replicate('$ARTIFACTS_DIR',
+ '$PDIFF_DIR/bin/win/FreeImage.dll')
+elif env.Bit("linux"):
+ pdiff = '$PDIFF_DIR/bin/linux/perceptualdiff'
+elif env.Bit("mac"):
+ pdiff = '$PDIFF_DIR/bin/mac/perceptualdiff'
+
+if pdiff:
+ installed_pdiff = env.Replicate('$ARTIFACTS_DIR', pdiff)
+ run_env["PDIFF_PATH"] = installed_pdiff[0]
+ env.Requires(installed_pdiff, pdiff_requirements)
+
+# -------------------------------------------------------------------------
+# System tests
+
+# Only define and run the system tests if they are enabled.
+if ARGUMENTS.get('SYSTEM_TESTS_ENABLED', False):
+ # Build the system tests
+ system_testprog = env.Program('system_tests',
+ 'basic_system_test/basic_system_test.cc')
+
+ # After building the test executable, force the windows executable to output
+ # stdout to a console window
+ if env.Bit('windows'):
+ blessed = env.AddPostAction(system_testprog,
+ 'editbin /SUBSYSTEM:CONSOLE $TARGET')
+ else:
+ blessed = system_testprog
+ system_tests_install = env.Replicate('$ARTIFACTS_DIR', blessed)
+
+ # Gather requirements for system tests.
+ system_tests_req = []
+
+ # TODO : The system tests are currently limited to Direct3D.
+ if env.Bit('windows') and 'RENDERER_D3D9' in env['CPPDEFINES']:
+ system_tests_req += env.Replicate(
+ '$ARTIFACTS_DIR',
+ ['test_driver.bat', 'test_driver.py'])
+
+ # Install the assets required for the basic system test.
+ system_tests_req += env.Replicate(
+ '$ARTIFACTS_DIR/basic_system_test/reference_frames',
+ 'basic_system_test/reference_frames/*')
+ system_tests_req += env.Replicate(
+ '$ARTIFACTS_DIR/basic_system_test',
+ 'basic_system_test/reference_stream.csv')
+
+ # Copy the PIX utility and experiment file to the testing build directory.
+ if env.Bit('windows') and 'RENDERER_D3D9' in env['CPPDEFINES']:
+ # Replicate a script which will package all of the files required,
+ # for D3D9-tests.
+ pix_requirements = env.Replicate(
+ '$ARTIFACTS_DIR',
+ [
+ '$DIRECTX9_DIR/utilities/bin/x86/PIXWin.exe',
+ '$DIRECTX9_DIR/utilities/bin/x86/PIXHelper.dll',
+ '$DIRECTX9_DIR/utilities/bin/x86/PIXAdmin.exe',
+ '$DIRECTX9_DIR/utilities/bin/x86/Detoured.dll',
+ '$DIRECTX9_DIR/runtime/x86/d3dref9.dll',
+ 'testing_framework_reference.PIXExp',
+ 'testing_framework_hardware.PIXExp',
+ 'package_tests.bat',
+ ])
+ system_tests_req += pix_requirements
+
+ if installed_pdiff:
+ system_tests_req += installed_pdiff
+
+ # Also require gl related libraries based on variant.
+ system_tests_req += gl_requirements
+
+ # Add requirements for system_tests.
+ env.Requires(system_tests_install, system_tests_req)
+
+ # Get command line flags for tests driver.
+ if int(ARGUMENTS.get('hardware', 1)):
+ env.Replace(TEST_DRIVER_FLAGS = '-hardware')
+
+ # Create an alias for the execution of the system tests.
+ env.Alias('system_tests')
+ # Only add actual step if d3d mode (for now).
+ if 'RENDERER_D3D9' in env['CPPDEFINES']:
+ if env.get('COVERAGE_ENABLED'):
+ # TODO: temporarily continue to use aliases to run tests.
+ # This need to eventually switch to use ComponentTestProgram once
+ # other sections have been adjusted.
+ # When running coverage, don't run system_tests thru test_driver.py
+ env['PROGRAM_NAME'] = system_tests_install[0].path
+ env['PROGRAM_BASENAME'] = os.path.join('$ARTIFACTS_DIR', 'system_tests')
+ env.AlwaysBuild(run_env.Alias(
+ 'system_tests', system_tests_install,
+ env.subst('$COMPONENT_TEST_CMDLINE')))
+ else:
+ env.AlwaysBuild(run_env.Alias(
+ 'system_tests', system_tests_install,
+ 'cd $ARTIFACTS_DIR && ' + \
+ sys.executable + ' $ARTIFACTS_DIR/test_driver.py $TEST_DRIVER_FLAGS'))
+ # Add help.
+ env.Help('system_tests: run system tests\n')
+
+
+#----------------------------------------------------------------------------
+# Selenium Tests
+
+run_env.Replace(
+ SELENIUM_VERBOSE = ARGUMENTS.get('SELENIUM_VERBOSE', '0'),
+ SELENIUM_SERVER_JAR = '$SELENIUM_JAVA_DIR/server/selenium-server.jar',
+ SELENIUM_TEST_PREFIX = ARGUMENTS.get('SELENIUM_TEST_PREFIX', 'Test'),
+ SELENIUM_TEST_SUFFIXES = ARGUMENTS.get('SELENIUM_TEST_SUFFIXES',
+ 'small,medium,large'),
+ SELENIUM_CAPTURE_SCREENSHOTS = ARGUMENTS.get(
+ 'SELENIUM_CAPTURE_SCREENSHOTS',
+ ''),
+ SELENIUM_FLAGS = ('--verbose=$SELENIUM_VERBOSE '
+ '--selenium_server="$SELENIUM_SERVER_JAR" '
+ '--testprefix=$SELENIUM_TEST_PREFIX '
+ '--testsuffixes=$SELENIUM_TEST_SUFFIXES '
+ '--screencompare=$PDIFF_PATH '
+ '$SELENIUM_CAPTURE_SCREENSHOTS '
+ '$SELENIUM_EXTRA_FLAGS'),
+)
+
+if run_env.Bit('windows'):
+ from SCons.Platform.win32 import get_program_files_dir
+ path_contents = '%s/Mozilla Firefox;%s' % (
+ get_program_files_dir(),
+ run_env.Dir('$ARTIFACTS_DIR').abspath)
+ run_env.Append(
+ ENV = {'PATH': '%s/Mozilla Firefox;%s' % (
+ get_program_files_dir(),
+ run_env.Dir('$ARTIFACTS_DIR').abspath)},
+ )
+ run_env.Append(
+ ENV = {'USERPROFILE': os.environ['USERPROFILE']}
+ )
+
+# The Macintosh Firefox apparently does not support the MOZ_PLUGIN_PATH.
+# On the Mac we can run a hermetic version of Firefox and allow it to find
+# the O3D plugin by adding a symlink to it inside the plugins directory
+# inside the Firefox.app bundle. We then instruct selenium to run this
+# specific version of Firefox. All of this avoids having to install
+# the plugin in a system directory which could be bad on
+# auto-build machines, etc.
+plugin_path = '$ARTIFACTS_DIR/${LIBPREFIX}npo3dautoplugin$SHLIBSUFFIX'
+browser_path = ''
+plugin_install_steps = []
+cleanup_steps = []
+if run_env.Bit('mac'):
+ plugin_path = '$ARTIFACTS_DIR/O3D.plugin'
+ if run_env['MAC_HERMETIC_FIREFOX_DIR']:
+ run_env['MAC_FIREFOX_DIR'] = '$MAC_HERMETIC_FIREFOX_DIR'
+ run_env['MAC_FIREFOX_APP'] = '$MAC_FIREFOX_DIR/Firefox.app'
+ run_env['MAC_FIREFOX_TGZ'] = '$MAC_FIREFOX_DIR/firefox.tgz'
+ browser_path = '$MAC_FIREFOX_APP/Contents/MacOS/firefox-bin'
+ plugin_install_steps = [
+ 'rm -rf "$MAC_FIREFOX_APP"',
+ 'cd $MAC_FIREFOX_DIR && tar xfz "$MAC_FIREFOX_TGZ"',
+ 'ln -fs "' + plugin_path + '" "$MAC_FIREFOX_APP/Contents/MacOS/plugins"',
+ ]
+ cleanup_steps = [
+ 'rm -rf "$MAC_FIREFOX_APP"',
+ ]
+
+def DeferSelenium(env):
+ run_selenium_firefox = run_env.Alias(
+ 'selenium_firefox',
+ [plugin_path, env.GetPublished('samples', 'asset_files')],
+ # TODO: have a cleaner way to do this.
+ [Delete('$ARTIFACTS_DIR/selenium/screenshots_firefox'),
+ Mkdir('$ARTIFACTS_DIR/selenium/screenshots_firefox')] +
+ plugin_install_steps +
+ [run_env.Python([
+ 'tests/selenium/main.py',
+ '$SELENIUM_FLAGS',
+ '--browser=*firefox',
+ '--browserpath="' + browser_path + '"',
+ '--referencedir=$TEST_REFERENCE_IMAGES',
+ '--screenshotsdir=$ARTIFACTS_DIR/selenium/screenshots_firefox'])] +
+ cleanup_steps,
+ )
+
+ if run_env.Bit('windows'):
+ run_selenium_ie = run_env.Alias(
+ 'selenium_ie',
+ ['$ARTIFACTS_DIR/${LIBPREFIX}npo3dautoplugin$SHLIBSUFFIX',
+ env.GetPublished('samples', 'asset_files')],
+ # TODO: have a cleaner way to do this.
+ [Delete('$ARTIFACTS_DIR/selenium/screenshots_ie'),
+ Mkdir('$ARTIFACTS_DIR/selenium/screenshots_ie'),
+ run_env.Python([
+ 'tests/selenium/main.py',
+ '$SELENIUM_FLAGS',
+ '--browser=*iexplore',
+ '--servertimeout=80',
+ '--referencedir=$TEST_REFERENCE_IMAGES',
+ '--screenshotsdir=$ARTIFACTS_DIR/selenium/screenshots_ie'])],
+ )
+ run_env.Requires(run_selenium_ie, '$IE_PLUGIN_DIR')
+
+ run_selenium_chrome = run_env.Alias(
+ 'selenium_chrome',
+ ['$ARTIFACTS_DIR/${LIBPREFIX}npo3dautoplugin$SHLIBSUFFIX',
+ env.GetPublished('samples', 'asset_files')],
+ # TODO: have a cleaner way to do this.
+ [Delete('$ARTIFACTS_DIR/selenium/screenshots_chrome'),
+ Mkdir('$ARTIFACTS_DIR/selenium/screenshots_chrome'),
+ run_env.Python([
+ 'tests/selenium/main.py',
+ '$SELENIUM_FLAGS',
+ '--browser=*googlechrome',
+ '--referencedir=$TEST_REFERENCE_IMAGES',
+ '--screenshotsdir=$ARTIFACTS_DIR/selenium/screenshots_chrome'])],
+ )
+ # Chrome requires that the plug-in be located in the install path.
+ env.Requires(run_selenium_chrome, env.Alias('install'))
+
+ if installed_pdiff:
+ env.Requires(run_selenium_firefox, installed_pdiff)
+ if run_env.Bit('windows'):
+ env.Requires(run_selenium_ie, installed_pdiff)
+ env.Requires(run_selenium_chrome, installed_pdiff)
+
+ env.AlwaysBuild(run_selenium_firefox)
+ if run_env.Bit('windows'):
+ env.AlwaysBuild(run_selenium_ie)
+ env.AlwaysBuild(run_selenium_chrome)
+
+ run_env.Alias('selenium', run_selenium_firefox)
+
+run_env.Defer(DeferSelenium)
+
+# Add help.
+env.Help('selenium_{firefox|ie|chrome): run selenium tests in the given browser\n')
+
+
+# Add generation of lump for hive selenium tests.
+if env.AnyBits('mac', 'windows'):
+ selenium_env = env.Clone()
+ selenium_env.AppendENVPath('PATH',
+ os.environ.get('PRESCONS_PATH', '').split(';'))
+
+ # Pull in all pulse related environment variables.
+ for k in os.environ:
+ if k.startswith('PULSE_'):
+ selenium_env['ENV'][k] = os.environ[k]
+
+ selenium_env['PREP_SELENIUM_ARCHIVE'] = (
+ '$TARGET_ROOT/${PREP_SELENIUM_ARCHIVE_NAME}.zip')
+
+ if int(ARGUMENTS.get('O3D_ENABLE_BREAKPAD', 0)):
+ selenium_env['O3D_BRANCH'] = 'release'
+ else:
+ selenium_env['O3D_BRANCH'] = 'trunk'
+
+ if env.Bit('mac'):
+ selenium_env['PREP_SELENIUM_ARCHIVE_NAME'] = 'O3Dtests_mac'
+ selenium_env['GNU_SETUP_EXTRAS'] = ''
+ selenium_env['SETUP_SCRIPTS'] = (
+ 'export PYTHONPATH=' + env.Dir('$MAIN_DIR/scripts').abspath)
+ else:
+ selenium_env['PREP_SELENIUM_ARCHIVE_NAME'] = 'O3Dtests'
+ selenium_env['GNU_SETUP_EXTRAS'] = r'..\third_party\gnu\setup_env.bat && '
+ selenium_env['SETUP_SCRIPTS'] = (
+ 'set PYTHONPATH=' + env.Dir('$MAIN_DIR/scripts').abspath)
+
+ selenium_env.AlwaysBuild(selenium_env.Alias('prep_selenium', [],
+ [Delete('$TARGET_ROOT/${PREP_SELENIUM_ARCHIVE_NAME}'),
+ '$SETUP_SCRIPTS && '
+ 'cd tests && cd lab_automation && ' +
+ sys.executable + ' prepare_selenium_tests.py '
+ '$TARGET_ROOT/${PREP_SELENIUM_ARCHIVE_NAME}',
+ Delete('$PREP_SELENIUM_ARCHIVE'),
+ '${GNU_SETUP_EXTRAS}'
+ 'cd $TARGET_ROOT && '
+ 'zip -9 -r $PREP_SELENIUM_ARCHIVE ${PREP_SELENIUM_ARCHIVE_NAME}',
+ ]))
+
+
+
+#--------------------------------------------------------------------------
+# General stuff
+
+presubmit_tests = [
+ env.Alias('unit_tests'),
+]
+
+if ARGUMENTS.get('SYSTEM_TESTS_ENABLED', False):
+ presubmit_tests += [
+ env.Alias('system_tests'),
+ ]
+
+# TODO: Make presubmit run selenium once the OpenGL build is
+# completely working.
+if 'RENDERER_D3D9' in env['CPPDEFINES']:
+ presubmit_tests += [
+ env.Alias('selenium_firefox'),
+ ]
+
+env.Alias('presubmit', presubmit_tests)
+env.Help('presubmit: run unit tests and selenium integration tests.\n')
+
+# Add an alias for what to run on Pulse.
+pulse_tests = [env.Alias('presubmit')]
+# OpenGL tests don't work with selenium yet.
+if 'RENDERER_D3D9' in env['CPPDEFINES']:
+ pulse_tests += [env.Alias('selenium_firefox'),
+ env.Alias('selenium_ie')]
+# Add selenium for mac.
+if env.Bit('mac'):
+ pulse_tests += [env.Alias('selenium')]
+
+env.Alias('pulse_tests', pulse_tests)
+env.Help('pulse_tests: the test target that automatic builds use.\n')
+
+# TODO: shouldn't need this.
+# Adding extra line for cleaner formatting.
+env.Help('\n')
+
+
+#--------------------------------------------------------------------------
+# Other odds and ends
+
+# Install presubmit_tests batch file.
+# TODO: is this really needed anymore?
+env.Replicate('$ARTIFACTS_DIR', 'presubmit_tests.bat')
diff --git a/o3d/tests/common/build.scons b/o3d/tests/common/build.scons
new file mode 100644
index 0000000..8add24f
--- /dev/null
+++ b/o3d/tests/common/build.scons
@@ -0,0 +1,55 @@
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+Import('env')
+
+env.Append(CPPPATH = ['$RENDERER_INCLUDE_PATH'])
+
+inputs = [
+ "cross/main.cc",
+]
+
+if env.Bit('windows'):
+ inputs += [
+ "win/dxcapture.cc",
+ "win/testing_common.cc",
+ ]
+
+if env.Bit('linux'):
+ inputs += [
+ "linux/testing_common.cc",
+ ]
+
+if env.Bit('mac'):
+ inputs += [
+ "mac/testing_common.cc",
+ ]
+
+o3dcore_lib = env.ComponentLibrary("testing_common", inputs)
diff --git a/o3d/tests/common/cross/main.cc b/o3d/tests/common/cross/main.cc
new file mode 100644
index 0000000..b4bdbd2
--- /dev/null
+++ b/o3d/tests/common/cross/main.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// defines the common main() for all unit tests
+#include <build/build_config.h>
+#include "gtest/gtest.h"
+
+#ifdef OS_WIN
+int test_main(int argc, wchar_t **argv) {
+#else
+int test_main(int argc, char **argv) {
+#endif
+ testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/o3d/tests/common/cross/test_utils.cc b/o3d/tests/common/cross/test_utils.cc
new file mode 100644
index 0000000..d34b28f
--- /dev/null
+++ b/o3d/tests/common/cross/test_utils.cc
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// useful utility functions for unit/functional testing
+
+#include "tests/common/cross/test_utils.h"
+#include <sys/stat.h>
+#include "core/cross/client.h"
+#include "import/cross/targz_processor.h"
+#include "tests/common/win/testing_common.h"
+
+namespace test_utils {
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+uint8 *ReadFile(o3d::String file, size_t *data_size) {
+ FILE *fp = fopen(file.c_str(), "rb");
+
+ if (fp == NULL) return NULL;
+
+ // Figure out the file length
+ struct stat file_info;
+ stat(file.c_str(), &file_info);
+ size_t file_size = file_info.st_size;
+
+ // Read it all into memory
+ // TODO: this should be new[] instead of malloc.
+ uint8 *data = static_cast<uint8*>(malloc(file_size));
+ DCHECK(data);
+
+ int bytes_read = fread(data, 1, file_size, fp);
+ fclose(fp);
+ DCHECK_EQ(bytes_read, file_size);
+
+ // Return file size and data
+ if (data_size) *data_size = file_size;
+ return data;
+}
+
+} // namespace test_utils
diff --git a/o3d/tests/common/cross/test_utils.h b/o3d/tests/common/cross/test_utils.h
new file mode 100644
index 0000000..1d3c54f
--- /dev/null
+++ b/o3d/tests/common/cross/test_utils.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// useful utility functions for unit/functional testing
+
+#ifndef O3D_TESTS_COMMON_CROSS_TEST_UTILS_H_
+#define O3D_TESTS_COMMON_CROSS_TEST_UTILS_H_
+
+#include "core/cross/client.h"
+#include "tests/common/win/testing_common.h"
+
+namespace test_utils {
+
+// Reads the given file into memory and returns a pointer
+// to its contents. The file size is returned in |data_size|
+// The caller is responsible for calling free()
+// NULL is returned if there's an error
+uint8 *ReadFile(o3d::String file, size_t *data_size);
+
+} // namespace test_utils
+
+#endif // O3D_TESTS_COMMON_CROSS_TEST_UTILS_H_
diff --git a/o3d/tests/common/linux/testing_common.cc b/o3d/tests/common/linux/testing_common.cc
new file mode 100644
index 0000000..318b278
--- /dev/null
+++ b/o3d/tests/common/linux/testing_common.cc
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// Contains windows-specific code for setting up the Client object
+// used in the unit tests. Defines WinMain and a WindowProc for running
+// the GUnit tests
+
+#include "core/cross/class_manager.h"
+#include "core/cross/evaluation_counter.h"
+#include "core/cross/install_check.h"
+#include "core/cross/features.h"
+#include "core/cross/object_manager.h"
+#include "core/cross/profiler.h"
+#include "core/cross/renderer.h"
+#include "core/cross/renderer_platform.h"
+#include "core/cross/service_locator.h"
+#include "core/cross/types.h"
+
+o3d::String *g_program_path = NULL;
+o3d::String *g_program_name = NULL;
+o3d::Renderer* g_renderer = NULL;
+o3d::ServiceLocator* g_service_locator = NULL;
+o3d::DisplayWindow* g_display_window = NULL;
+
+Display *g_display = NULL;
+Window g_window = 0;
+
+static char kOffScreenRenderer[] = "O3D_D3D9_OFF_SCREEN";
+
+extern int test_main(int argc, char **argv);
+
+int main(int argc, char *argv[]) {
+ std::string error;
+ if (!o3d::RendererInstallCheck(&error)) {
+ return false;
+ }
+
+ std::string program_path(argv[0]);
+ std::string program_name;
+
+ // Remove all characters starting with last '/'.
+ size_t backslash_pos = program_path.rfind('/');
+ if (backslash_pos != o3d::String::npos) {
+ program_name = program_path.substr(backslash_pos + 1);
+ if (backslash_pos == 0) {
+ program_path = "/";
+ } else {
+ program_path.erase(backslash_pos);
+ }
+ } else {
+ program_name = program_path;
+ program_path = ".";
+ }
+
+ g_program_path = &program_path;
+ g_program_name = &program_name;
+
+ o3d::ServiceLocator service_locator;
+ g_service_locator = &service_locator;
+
+ o3d::EvaluationCounter evaluation_counter(g_service_locator);
+ o3d::ClassManager class_manager(g_service_locator);
+ o3d::ObjectManager object_manager(g_service_locator);
+ o3d::Profiler profiler(g_service_locator);
+ o3d::Features features(g_service_locator);
+
+ // create a renderer device based on the current platform
+ g_renderer = o3d::Renderer::CreateDefaultRenderer(g_service_locator);
+
+ g_display = ::XOpenDisplay(0);
+ int attribs[] = {
+ GLX_RGBA,
+ GLX_DOUBLEBUFFER,
+ GLX_RED_SIZE, 1,
+ GLX_GREEN_SIZE, 1,
+ GLX_BLUE_SIZE, 1,
+ None
+ };
+ XVisualInfo *visualInfo = ::glXChooseVisual(g_display,
+ DefaultScreen(g_display),
+ attribs);
+ Window root_window = RootWindow(g_display, visualInfo->screen);
+ Colormap colorMap = ::XCreateColormap(g_display,
+ root_window,
+ visualInfo->visual,
+ AllocNone);
+
+ XSetWindowAttributes windowAttributes;
+ windowAttributes.colormap = colorMap;
+ windowAttributes.border_pixel = 0;
+ windowAttributes.event_mask = StructureNotifyMask;
+ g_window = ::XCreateWindow(g_display,
+ root_window,
+ 0, 0, 800, 600,
+ 0,
+ visualInfo->depth,
+ InputOutput,
+ visualInfo->visual,
+ CWBorderPixel|CWColormap|CWEventMask,
+ &windowAttributes);
+ ::XFree(visualInfo);
+ ::XMapWindow(g_display, g_window);
+ ::XSync(g_display, True);
+
+ o3d::DisplayWindowLinux* display_window =
+ new o3d::DisplayWindowLinux();
+ display_window->set_display(g_display);
+ display_window->set_window(g_window);
+ g_display_window = display_window;
+
+ // Initialize the renderer for off-screen rendering if kOffScreenRenderer
+ // is in the environment.
+ bool success;
+ if (getenv(kOffScreenRenderer)) {
+ success =
+ g_renderer->Init(*g_display_window, true) == o3d::Renderer::SUCCESS;
+ } else {
+ success =
+ g_renderer->Init(*g_display_window, false) == o3d::Renderer::SUCCESS;
+ }
+
+ int ret = EXIT_FAILURE;
+ if (!success) {
+ ::fprintf(stdout, "Failed to initialize renderer\n");
+ } else {
+ ret = test_main(argc, argv);
+ }
+
+ g_renderer->Destroy();
+ delete g_renderer;
+ g_renderer = NULL;
+
+ delete display_window;
+ g_display_window = NULL;
+ g_program_path = NULL;
+ g_program_name = NULL;
+ ::XCloseDisplay(g_display);
+
+ return ret;
+}
diff --git a/o3d/tests/common/mac/testing_common.cc b/o3d/tests/common/mac/testing_common.cc
new file mode 100644
index 0000000..e102fc8
--- /dev/null
+++ b/o3d/tests/common/mac/testing_common.cc
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// Contains macintosh-specific code for setting up the Client object
+// used in the unit tests.
+
+#include <OpenGL/OpenGL.h>
+#include <OpenGL/gl.h>
+#include <OpenGL/glu.h>
+#include <GLUT/glut.h>
+#include <AGL/agl.h>
+
+#include "core/cross/class_manager.h"
+#include "core/cross/evaluation_counter.h"
+#include "core/cross/install_check.h"
+#include "core/cross/features.h"
+#include "core/cross/object_manager.h"
+#include "core/cross/profiler.h"
+#include "core/cross/renderer.h"
+#include "core/cross/renderer_platform.h"
+#include "core/cross/service_locator.h"
+#include "core/cross/types.h"
+
+o3d::String *g_program_path = NULL;
+o3d::String *g_program_name = NULL;
+o3d::Renderer* g_renderer = NULL;
+o3d::ServiceLocator* g_service_locator = NULL;
+o3d::DisplayWindow* g_display_window = NULL;
+
+const unsigned int kWindowWidth = 800;
+const unsigned int kWindowHeight = 600;
+
+extern int test_main(int argc, char **argv);
+
+int main(int argc, char *argv[]) {
+ std::string error;
+ if (!o3d::RendererInstallCheck(&error)) {
+ return false;
+ }
+
+ std::string program_path(argv[0]);
+ std::string program_name;
+
+ // Remove all characters starting with last '/'.
+ size_t backslash_pos = program_path.rfind('/');
+ if (backslash_pos != o3d::String::npos) {
+ program_name = program_path.substr(backslash_pos + 1);
+ if (backslash_pos == 0) {
+ program_path = "/";
+ } else {
+ program_path.erase(backslash_pos);
+ }
+ } else {
+ program_name = program_path;
+ program_path = ".";
+ }
+
+ g_program_path = &program_path;
+ g_program_name = &program_name;
+
+ o3d::ServiceLocator service_locator;
+ g_service_locator = &service_locator;
+
+ o3d::EvaluationCounter evaluation_counter(g_service_locator);
+ o3d::ClassManager class_manager(g_service_locator);
+ o3d::ObjectManager object_manager(g_service_locator);
+ o3d::Profiler profiler(g_service_locator);
+ o3d::Features features(g_service_locator);
+
+ // create a renderer device based on the current platform
+ g_renderer = o3d::Renderer::CreateDefaultRenderer(g_service_locator);
+
+ glutInit(&argc, argv);
+ glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL);
+ glutInitWindowSize(kWindowWidth, kWindowHeight);
+ glutInitWindowPosition(100, 100);
+ glutCreateWindow(argv[0]);
+
+ // Give the renderer the CGL context
+ CGLContextObj cgl_context = CGLGetCurrentContext();
+ o3d::DisplayWindowMac* display_window = new o3d::DisplayWindowMac();
+ g_display_window = display_window;
+ display_window->set_agl_context(NULL);
+ display_window->set_cgl_context(cgl_context);
+ bool success = g_renderer->Init(*g_display_window,
+ false) == o3d::Renderer::SUCCESS;
+
+ int ret = EXIT_FAILURE;
+ if (!success) {
+ ::fprintf(stdout, "Failed to initialize renderer\n");
+ } else {
+ ret = test_main(argc, argv);
+ }
+
+ g_renderer->Destroy();
+ delete g_renderer;
+ g_renderer = NULL;
+
+ delete display_window;
+ g_display_window = NULL;
+ g_program_path = NULL;
+ g_program_name = NULL;
+
+ return ret;
+}
diff --git a/o3d/tests/common/win/dxcapture.cc b/o3d/tests/common/win/dxcapture.cc
new file mode 100644
index 0000000..0afb91b
--- /dev/null
+++ b/o3d/tests/common/win/dxcapture.cc
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// File containing implementation routines for the DirectX testing framework
+// framebuffer and command stream capture routines.
+
+#include "tests/common/win/dxcapture.h"
+
+#include <d3dx9.h>
+
+#include "core/cross/types.h"
+#include "core/win/d3d9/d3d_entry_points.h"
+#include "core/win/d3d9/renderer_d3d9.h"
+#include "core/win/d3d9/utils_d3d9.h"
+#include "tests/common/win/testing_common.h"
+
+namespace directx_capture {
+
+void StartCommandCapture(const wchar_t* stream_name) {
+#if defined(RENDERER_D3D9)
+ if (D3DPERF_GetStatus()) {
+ D3DPERF_SetMarker(0, stream_name);
+ D3DPERF_SetMarker(0, L"BeginCommandStreamCapture");
+ }
+#endif
+}
+
+void EndCommandCapture() {
+#if defined(RENDERER_D3D9)
+ if (D3DPERF_GetStatus()) {
+ D3DPERF_SetMarker(0, L"EndCommandStreamCapture");
+ }
+#endif
+}
+
+void CaptureFramebuffer(const wchar_t* buffer_metadata) {
+#if defined(RENDERER_D3D9)
+ // Keep track of the invocation count for output file-naming purposes.
+ static int g_call_count = 0;
+ ++g_call_count;
+
+ // If PIX is present, then send a message to PIX requesting a framebuffer
+ // capture.
+ if (D3DPERF_GetStatus()) {
+ ::Sleep(500);
+ D3DPERF_SetMarker(0, buffer_metadata);
+ D3DPERF_SetMarker(0, L"CaptureScreenContents");
+ } else {
+ // Otherwise, explicitly read the contents of the buffer into system memory
+ // and story a .png file.
+ o3d::RendererD3D9* d3d9_renderer =
+ down_cast<o3d::RendererD3D9*>(g_renderer);
+ LPDIRECT3DDEVICE9 device = d3d9_renderer->d3d_device();
+ IDirect3DSurface9* system_surface = NULL;
+ IDirect3DSurface9* current_surface = NULL;
+
+ HR(device->GetRenderTarget(0, &current_surface));
+ D3DSURFACE_DESC surface_description;
+ HR(current_surface->GetDesc(&surface_description));
+
+ // Construct an intermediate surface with multi-sampling disabled.
+ // This surface is required because GetRenderTargetData(...) will fail
+ // for multi-sampled targets. One must first down-sample to a
+ // non-multi-sample buffer, and then copy from that intermediate buffer
+ // to a main memory surface.
+ IDirect3DSurface9* intermediate_target;
+ HR(device->CreateRenderTarget(surface_description.Width,
+ surface_description.Height,
+ surface_description.Format,
+ D3DMULTISAMPLE_NONE,
+ 0,
+ FALSE,
+ &intermediate_target,
+ NULL));
+
+ HR(device->StretchRect(current_surface,
+ NULL,
+ intermediate_target,
+ NULL,
+ D3DTEXF_NONE));
+
+ HR(device->CreateOffscreenPlainSurface(surface_description.Width,
+ surface_description.Height,
+ surface_description.Format,
+ D3DPOOL_SYSTEMMEM,
+ &system_surface,
+ NULL));
+
+ HR(device->GetRenderTargetData(intermediate_target, system_surface));
+
+ const o3d::String file_name(*g_program_name +
+ o3d::String("_") +
+ UintToString(g_call_count) +
+ o3d::String(".png"));
+
+ std::wstring file_name_utf16 = UTF8ToWide(file_name);
+
+ HR(o3d::D3DXSaveSurfaceToFile(file_name_utf16.c_str(),
+ D3DXIFF_PNG,
+ system_surface,
+ NULL,
+ NULL));
+
+ system_surface->Release();
+ intermediate_target->Release();
+ current_surface->Release();
+ }
+#endif
+}
+
+} // end namespace directx_capture
diff --git a/o3d/tests/common/win/dxcapture.h b/o3d/tests/common/win/dxcapture.h
new file mode 100644
index 0000000..c62ce2c
--- /dev/null
+++ b/o3d/tests/common/win/dxcapture.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// File exposing a set functions to capture the direct-X command
+// stream and frame-buffer contents. This class is to be used for
+// regression-testing purposes.
+
+#ifndef O3D_TESTS_COMMON_WIN_DXCAPTURE_H__
+#define O3D_TESTS_COMMON_WIN_DXCAPTURE_H__
+
+// Singleton class encapsulating functionality to capture DX command streams
+// and framebuffer contents.
+namespace directx_capture {
+
+// Routine to inform the testing framework that graphics command logs
+// should be captured. The contents of stream_name will be embedded in the
+// logs for ease of human readability.
+// NOTE: Requires that the executable be invoked through PIX. Fn is a no-op
+// when the PIX environment is not present.
+void StartCommandCapture(const wchar_t* stream_name);
+
+// Invoke to disable stream capture. Note that the start/end routines
+// are NOT re-entrant. One cannot nest stream captures.
+// NOTE: Requires that the executable be invoked through PIX. Fn is a no-op
+// when the PIX environment is not present.
+void EndCommandCapture();
+
+// Invoke to capture the current contents of the framebuffer. If stream
+// capture is active, the contents of buffer_metadata will be written to the
+// logs.
+// NOTE: If PIX is present, then the frame-buffer is captured and stored by
+// PIX according to the PIXRun file. Otherwise, the contents of the current
+// render target surface are saved explicitly by the code. The meta-data
+// is ignored when PIX is not present.
+void CaptureFramebuffer(const wchar_t* buffer_metadata);
+
+} // end namespace directx_capture
+
+#endif // O3D_TESTS_COMMON_WIN_DXCAPTURE_H__
diff --git a/o3d/tests/common/win/system_test.h b/o3d/tests/common/win/system_test.h
new file mode 100644
index 0000000..65e060c
--- /dev/null
+++ b/o3d/tests/common/win/system_test.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// File containing assert-macros for use in O3D rendering system-tests.
+
+#ifndef O3D_TESTS_COMMON_WIN_SYSTEM_TEST_H__
+#define O3D_TESTS_COMMON_WIN_SYSTEM_TEST_H__
+
+#ifdef OS_WIN
+
+#include "tests/common/win/dxcapture.h"
+
+// A helper-macro to widen strings.
+#define WIDEN_STRING2(x) L##x
+#define WIDEN_STRING(x) WIDEN_STRING2(x)
+
+// Initiates capture of the DX command stream. The start of the captured
+// stream will contain a message indicating the values of the line and
+// file arguments.
+inline void BeginStreamCapture(int line, const wchar_t* file) {
+ wchar_t debug_buffer[2048];
+ _snwprintf_s(debug_buffer, 2048, L"Stream_Capture: %i : %s", line, file);
+ directx_capture::StartCommandCapture(debug_buffer);
+}
+
+// Places a debug marker in the graphics command stream containing
+// the values of line and file, and requests that the current framebuffer
+// contents be captured.
+inline void CaptureFrameBuffer(int line, const wchar_t* file) {
+ wchar_t debug_buffer[2048];
+ _snwprintf_s(debug_buffer, 2048, L"Frame_Capture: %i : %s", line, file);
+ directx_capture::CaptureFramebuffer(debug_buffer);
+}
+
+// Macros placing stream-capture begin-end waypoints in the testing application.
+#define BEGIN_ASSERT_STREAM_CAPTURE() \
+ BeginStreamCapture(__LINE__, WIDEN_STRING(__FILE__))
+
+#define END_ASSERT_STREAM_CAPTURE() \
+ directx_capture::EndCommandCapture()
+
+#define ASSERT_FRAMEBUFFER() \
+ CaptureFrameBuffer(__LINE__, WIDEN_STRING(__FILE__))
+
+#else
+
+#define BEGIN_ASSERT_STREAM_CAPTURE() do {} while (0)
+#define END_ASSERT_STREAM_CAPTURE() do {} while (0)
+#define ASSERT_FRAMEBUFFER() do {} while (0)
+
+#endif
+
+#endif // O3D_TESTS_COMMON_WIN_SYSTEM_TEST_H__
diff --git a/o3d/tests/common/win/testing_common.cc b/o3d/tests/common/win/testing_common.cc
new file mode 100644
index 0000000..e51fc6a
--- /dev/null
+++ b/o3d/tests/common/win/testing_common.cc
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// Contains windows-specific code for setting up the Client object
+// used in the unit tests. Defines WinMain and a WindowProc for running
+// the GUnit tests
+
+#include <windows.h>
+#include <Shellapi.h>
+#include <d3dx9.h>
+
+#include "tests/common/win/testing_common.h"
+#include "core/cross/install_check.h"
+#include "core/cross/service_locator.h"
+#include "core/cross/evaluation_counter.h"
+#include "core/cross/class_manager.h"
+#include "core/cross/features.h"
+#include "core/cross/object_manager.h"
+#include "core/cross/profiler.h"
+#include "core/cross/renderer.h"
+#include "core/cross/renderer_platform.h"
+#include "core/cross/types.h"
+
+o3d::ServiceLocator* g_service_locator = NULL;
+o3d::DisplayWindow* g_display_window = NULL;
+
+HWND g_window_handle = NULL;
+
+o3d::String *g_program_path = NULL;
+o3d::String *g_program_name = NULL;
+o3d::Renderer* g_renderer = NULL;
+
+static wchar_t kOffScreenRenderer[] = L"O3D_D3D9_OFF_SCREEN";
+
+extern int test_main(int argc, wchar_t **argv);
+
+LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+ return true;
+}
+
+// Handles some errors that would typically cause an OS dialog box to appear.
+LONG WINAPI LocalUnhandledExceptionFilter(EXCEPTION_POINTERS* pep) {
+ fprintf(stdout, "ERROR: Unhandled Exception\n");
+ exit(EXIT_FAILURE);
+}
+
+// Main entry point for the app. Creates a new window, and calls main()
+int WINAPI WinMain(HINSTANCE instance,
+ HINSTANCE prev_instance,
+ LPSTR cmd_line,
+ int n_cmd_show) {
+ // Turn off some of the OS error dialogs.
+ ::SetUnhandledExceptionFilter(LocalUnhandledExceptionFilter);
+
+ std::string error;
+ if (!o3d::RendererInstallCheck(&error)) {
+ return false;
+ }
+ WNDCLASSEX wc = {0};
+
+ wc.lpszClassName = L"MY_WINDOWS_CLASS";
+ wc.cbSize = sizeof(WNDCLASSEX);
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.lpfnWndProc = ::WindowProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = instance;
+ wc.hIcon = ::LoadIcon(instance, IDI_APPLICATION);
+ wc.hIconSm = NULL;
+ wc.hCursor = ::LoadCursor(instance, IDC_ARROW);
+ wc.hbrBackground = static_cast<HBRUSH>(::GetStockObject(BLACK_BRUSH));
+ wc.lpszMenuName = NULL;
+
+ if (!::RegisterClassEx(&wc))
+ return false;
+
+ g_window_handle = ::CreateWindowExW(NULL,
+ wc.lpszClassName,
+ L"",
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ 512,
+ 512,
+ 0,
+ 0,
+ instance,
+ 0);
+
+ if (g_window_handle == NULL)
+ return false;
+
+ static wchar_t program_filename[512];
+ ::GetModuleFileNameW(NULL, program_filename, sizeof(program_filename));
+ program_filename[511] = 0;
+
+ std::wstring program_path(program_filename);
+ std::wstring program_name;
+
+ // Remove all characters starting with last '\'.
+ size_t backslash_pos = program_path.rfind('\\');
+ if (backslash_pos != o3d::String::npos) {
+ program_name = &program_filename[backslash_pos + 1];
+ program_path.erase(backslash_pos);
+ } else {
+ program_name = program_path;
+ }
+
+ o3d::String program_path_utf8 = WideToUTF8(program_path);
+ o3d::String program_name_utf8 = WideToUTF8(program_name);
+ g_program_path = &program_path_utf8;
+ g_program_name = &program_name_utf8;
+
+ o3d::ServiceLocator service_locator;
+ g_service_locator = &service_locator;
+
+ o3d::EvaluationCounter evaluation_counter(g_service_locator);
+ o3d::ClassManager class_manager(g_service_locator);
+ o3d::ObjectManager object_manager(g_service_locator);
+ o3d::Profiler profiler(g_service_locator);
+ o3d::Features features(g_service_locator);
+
+ // create a renderer device based on the current platform
+ g_renderer = o3d::Renderer::CreateDefaultRenderer(g_service_locator);
+
+ // Initialize the renderer for off-screen rendering if kOffScreenRenderer
+ // is in the environment.
+ bool success;
+ o3d::DisplayWindowWindows* display_window =
+ new o3d::DisplayWindowWindows();
+ display_window = display_window;
+ display_window->set_hwnd(g_window_handle);
+ g_display_window = display_window;
+ bool offscreen = ::GetEnvironmentVariableW(kOffScreenRenderer, NULL, 0);
+ if (offscreen) {
+ success =
+ g_renderer->Init(*g_display_window, true) == o3d::Renderer::SUCCESS;
+ } else {
+ success =
+ g_renderer->Init(*g_display_window, false) == o3d::Renderer::SUCCESS;
+ if (success) {
+ ::SetWindowPos(g_window_handle, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE);
+ ::ShowWindow(g_window_handle, SW_SHOWNORMAL);
+ }
+ }
+
+ int ret = EXIT_FAILURE;
+ if (!success) {
+ if (offscreen) {
+ ::fprintf(stdout, "Failed to initialize OFFSCREEN renderer\n");
+ } else {
+ ::fprintf(stdout, "Failed to initialize on screen renderer\n");
+ }
+ } else {
+ // Invoke the main entry point with the command-line arguments
+ int arg_count;
+ LPWSTR *arg_values = ::CommandLineToArgvW(::GetCommandLine(), &arg_count);
+ ret = ::test_main(arg_count, arg_values);
+ ::LocalFree(arg_values);
+ g_renderer->Destroy();
+ }
+
+ delete g_renderer;
+ g_renderer = NULL;
+
+ delete display_window;
+ g_display_window = NULL;
+ g_program_path = NULL;
+ g_program_name = NULL;
+
+ return ret;
+}
diff --git a/o3d/tests/common/win/testing_common.h b/o3d/tests/common/win/testing_common.h
new file mode 100644
index 0000000..1b282f3
--- /dev/null
+++ b/o3d/tests/common/win/testing_common.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// Set of external declarations for global objects required for O3D
+// testing.
+
+#ifndef O3D_TESTS_COMMON_WIN_TESTING_COMMON_H__
+#define O3D_TESTS_COMMON_WIN_TESTING_COMMON_H__
+
+#include "core/cross/types.h"
+#include "gtest/gtest.h"
+
+namespace o3d {
+class ServiceLocator;
+class Renderer;
+}
+
+extern o3d::ServiceLocator* g_service_locator;
+
+// g_renderer should be declared in a separate .cc file. The
+// code in this file must remain platform agnostic.
+extern o3d::Renderer* g_renderer;
+
+// Path to the executable, used to load files relative to it.
+extern o3d::String *g_program_path;
+
+// the window handle used to create the current window, used to instance a
+// specific Renderer class.
+#if defined(OS_WIN)
+extern HWND g_window_handle;
+#endif
+// Un-qualified name of the executable, stripped of all path information.
+// Note that the executable extension is included in this string.
+extern o3d::String* g_program_name;
+
+
+#endif // O3D_TESTS_COMMON_WIN_TESTING_COMMON_H__
diff --git a/o3d/tests/keyboard_info.html b/o3d/tests/keyboard_info.html
new file mode 100644
index 0000000..8e737da
--- /dev/null
+++ b/o3d/tests/keyboard_info.html
@@ -0,0 +1,126 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <title>Keyboard Events</title>
+
+ <style>
+ div.debug {
+ border:thin dotted gray;
+ padding:1em;
+ height:150px;
+ overflow:scroll;
+ }
+ </style>
+
+
+<script type="text/javascript" charset="utf-8">
+
+function propsOf(event) {
+ var property, propCollection = "";
+ for(property in event)
+ propCollection += (property + ":" + event[property] + "/");
+ return propCollection;
+}
+
+
+function charValue(c) {
+ return (c == 0) ? 'none' : String.fromCharCode(c);
+}
+
+function keyPropsOf(event) {
+ var props = ['type', 'keyCode', 'charCode', 'keyIdentifier', 'which',
+ 'timeStamp', 'isTrusted'];
+ var property, propCollection = "";
+ for(property in props) {
+ var pName = props[property];
+ propCollection += (pName + ":" + event[pName] + '<br>');
+ }
+ propCollection += 'unicode (char, key): ' + charValue(event.charCode) + ', ' +
+ charValue(event.keyCode);
+ return propCollection;
+}
+
+function setField(the_id, the_value) {
+ var field = document.getElementById(the_id);
+ field.innerHTML = the_value;
+}
+
+function debugLogEvent(in_event, name) {
+ var cb = document.getElementById('checkbox');
+ var event = (in_event) ? in_event : window.event;
+ setField(name, ((cb.checked) ? propsOf(event) : keyPropsOf(event)));
+}
+
+function keyUp(event) {
+ debugLogEvent(event, 'KeyUp_Field');
+}
+
+function keyDown(event) {
+ setField('KeyUp_Field', '');
+ setField('KeyDown_Field', '');
+ setField('KeyPress_Field', '');
+ debugLogEvent(event, 'KeyDown_Field');
+}
+
+
+function keyPressed(event) {
+ debugLogEvent(event, 'KeyPress_Field');
+}
+
+function init() {
+ var field = document.getElementById('KeyDown_Field');
+ field.focus();
+}
+
+window.document.onkeypress = keyPressed;
+window.document.onkeyup = keyUp;
+window.document.onkeydown = keyDown;
+window.onload = init;
+
+</script>
+</head>
+<body>
+
+<div id='KeyDown_Field' class='debug'> </div>
+<div id='KeyPress_Field' class='debug'> </div>
+<div id='KeyUp_Field' class='debug'> </div>
+<form>
+<br>
+<input name="Display All Fields" type="checkbox" id='checkbox'>Display All</input>
+</form>
+
+</body>
+</html>
diff --git a/o3d/tests/package_tests.bat b/o3d/tests/package_tests.bat
new file mode 100644
index 0000000..739f94f
--- /dev/null
+++ b/o3d/tests/package_tests.bat
@@ -0,0 +1,37 @@
+@ECHO OFF
+REM Copyright 2009, Google Inc.
+REM All rights reserved.
+REM
+REM Redistribution and use in source and binary forms, with or without
+REM modification, are permitted provided that the following conditions are
+REM met:
+REM
+REM * Redistributions of source code must retain the above copyright
+REM notice, this list of conditions and the following disclaimer.
+REM * Redistributions in binary form must reproduce the above
+REM copyright notice, this list of conditions and the following disclaimer
+REM in the documentation and/or other materials provided with the
+REM distribution.
+REM * Neither the name of Google Inc. nor the names of its
+REM contributors may be used to endorse or promote products derived from
+REM this software without specific prior written permission.
+REM
+REM THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+REM "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+REM LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+REM A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+REM OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+REM SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+REM LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+REM DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+REM THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+REM (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+REM OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+REM Invoke this script to package all of the dependencies required for the
+REM O3D system tests into a single zip file for easy distribution across
+REM testing machines.
+SET TODAY=%date:~10,4%%date:~4,2%%date:~7,2%
+SET ZIPNAME=_test_distribution.zip
+..\..\..\..\third_party\7zip_423\files\7z.exe a -tzip %TODAY%%ZIPNAME% o3d.msi d3d9ref.dll Detoured.dll import_test Microsoft.VC80.DebugCRT perceptualdiff.exe PIXAdmin.exe PIXHelper.dll PIXWin.exe system_tests.exe testing_framework_hardware.PIXExp testing_framework_reference.PIXExp test_driver.py
diff --git a/o3d/tests/presubmit_tests.bat b/o3d/tests/presubmit_tests.bat
new file mode 100644
index 0000000..137d5a7
--- /dev/null
+++ b/o3d/tests/presubmit_tests.bat
@@ -0,0 +1,35 @@
+REM Copyright 2009, Google Inc.
+REM All rights reserved.
+REM
+REM Redistribution and use in source and binary forms, with or without
+REM modification, are permitted provided that the following conditions are
+REM met:
+REM
+REM * Redistributions of source code must retain the above copyright
+REM notice, this list of conditions and the following disclaimer.
+REM * Redistributions in binary form must reproduce the above
+REM copyright notice, this list of conditions and the following disclaimer
+REM in the documentation and/or other materials provided with the
+REM distribution.
+REM * Neither the name of Google Inc. nor the names of its
+REM contributors may be used to endorse or promote products derived from
+REM this software without specific prior written permission.
+REM
+REM THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+REM "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+REM LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+REM A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+REM OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+REM SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+REM LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+REM DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+REM THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+REM (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+REM OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+REM This test will invoke all of the O3D unit-tests. Please execute
+REM these tests before submitting to P4.
+@echo off
+echo Invoking Tests
+%~dp0\unit_tests.exe
+if exist %~dp0\test_driver.bat call %~dp0\test_driver.bat %*
diff --git a/o3d/tests/selenium/__init__.py b/o3d/tests/selenium/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/o3d/tests/selenium/__init__.py
diff --git a/o3d/tests/selenium/javascript_unit_test_list.txt b/o3d/tests/selenium/javascript_unit_test_list.txt
new file mode 100644
index 0000000..9cfa199
--- /dev/null
+++ b/o3d/tests/selenium/javascript_unit_test_list.txt
@@ -0,0 +1,88 @@
+#
+# Test Requirements:
+#
+# Each sample is expected to have a global variable called g_testResult
+# That starts undefined and is set to true or false when the test is finished.
+#
+# Line Format:
+#
+# TestType test-name options
+#
+# Valid TestTypes are:
+#
+# small
+# medium
+# large
+#
+# options are separated by spaces.
+# screenshot : take a screenshot. You can specify a time with
+# screenshot(seconds) as in screenshot(4), take it at the 4 second mark.
+# You have also specify more than 1 screenshot by specifying more than
+# one screenshot option as in "screenshot(4), screenshot(6.5).
+#
+# To take a screenshot the test must have a global variable "g_client"
+# that is the client plugin object from which to take a screenshot.
+#
+# If the sample is animated, it is expected to have a global variable
+# called g_timeMult that can be set to 0 to stop the animation. All of
+# its animation must be based on a global variable called g_clock, such
+# that setting g_clock to the same value will always produce the same
+# image.
+#
+# Screenshots will not be taking unless g_testResult is true.
+#
+# timeout(milliseconds) : Set the timeout to wait for readiness. Default 5000.
+#
+# client(client_variable_name) : Name of client variable in javascript.
+# Default = "g_client"
+#
+# pdiff_threshold_mac(number_of_pixels_allowed)
+# pdiff_threshold_win(number_of_pixels_allowed)
+# pdiff_threshold_linux(number_of_pixels_allowed)
+# pdiff_threshold(number_of_pixels_allowed) : Number of pixels
+# allowed to be perceptually different before the test fails.
+# "pdiff_threshold" specifies the threshold for all
+# platforms. Platform specific versions override the
+# threshold for that platform. The default threshold is 10 pixels.
+#
+# except(*firefox, *iexplore, *googlechrome) : Name of the browser
+# environment(s) where the test should be skipped.
+# Default = ""
+#
+# NOTE! ----------------------------------------------------------------------
+#
+# Read the sample guidelines
+# http://wiki.corp.google.com/twiki/bin/view/Main/ClientThreeDSampleGuidelines
+#
+#
+small version-check-test
+small event-test
+small test-test
+small serialization-test
+small math-test
+small features-test except(*iexplore)
+small quaternion-test
+small v8-test
+small init-status-test
+small quaternion-test
+small base-test
+small util-test
+small pixel-perfection screenshot pdiff_threshold(2500) pdiff_threshold_mac(3000) except(*iexplore)
+medium offscreen-test
+small no-rendergraph screenshot
+small non-cachable-params screenshot pdiff_threshold(1700)
+small type-test
+small render-test
+# TODO Test disabled, as the behaviour on Chrome does not match other
+# browsers - objects are not deleted properly.
+small ownership-test
+small effect-import-test
+
+# -- tests below this line are tests for which there is a python
+# function to custom run the test. As such, only the 'except' and
+# pdiff_threshold options have any meaning
+
+medium TestStressDrawShapes
+medium TestStressMultiWindow
+large TestStressCullingZSort pdiff_threshold(14100)
+
diff --git a/o3d/tests/selenium/javascript_unit_tests.py b/o3d/tests/selenium/javascript_unit_tests.py
new file mode 100644
index 0000000..6d686ee
--- /dev/null
+++ b/o3d/tests/selenium/javascript_unit_tests.py
@@ -0,0 +1,304 @@
+#!/usr/bin/python2.4
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+"""Runs JavaScript unit tests in Selenium.
+
+NOTE: If you manually write a test in python (vs using the generic test)
+The name of the screenshots must match the name of the test not including
+the suffix. In otherwords, if your test is called TestSampleCustomCameraMedium
+then your screenshots need to be named customcamera1, customcamera2 etc.
+
+This is so when it comes time to compare screenshots we can figure out which
+reference images require a corresponding screenshot. In other words if
+TestSampleCustomCameraMedium is run then we know that any reference file
+named customcamera1_reference.png requires that there be a corresponding
+screenshot.
+"""
+
+
+import selenium_utilities
+
+class JavaScriptUnitTests(selenium_utilities.SeleniumTestCase):
+ """Runs the JavaScript unit tests for the sample utilities."""
+
+ def __init__(self, name, session, browser, test_type=None, sample_path=None,
+ options=None):
+ selenium_utilities.SeleniumTestCase.__init__(
+ self, name, session, browser, test_type, sample_path, options)
+
+ def GenericTest(self):
+ """Generically test a sample.
+
+ Each sample is expected to have a global variable called g_testResult
+ That starts undefined and is set to true or false when the test is finished.
+ """
+ self.RunGenericTest(
+ self.session.browserURL +
+ "/tests/selenium/tests/",
+ "typeof(window.g_testResult) != 'undefined'",
+ "window.g_testResult")
+
+ def TestStressDrawShapes(self):
+ """Tries to draw a large number of shapes for stress testing."""
+
+ # Alias for the selenium session
+ s = self.session
+ s.open(s.browserURL +
+ "/tests/selenium/tests/drawshapes.html")
+
+ # Allow a limited time for the plugin to initialize.
+ s.wait_for_condition("typeof(window.g_client) != 'undefined';", 20000)
+
+ # Sanity checks.
+ self.assertEqual("Drawshape stress test for O3D", s.get_title())
+ self.assertEqual("null", s.get_eval("window.undefined_symbol_xxxyyy"))
+
+ # Check assets for base o3d setup:
+ # 2 nodes in scene graph
+ # Root node and g_parent_transform node
+ self.assertEqual(
+ "2",
+ s.get_eval("window.g_client.root.getTransformsInTree().length"))
+
+ # There are 8 nodes in the render graph.
+ # 1 root render node
+ # 1 viewport
+ # 1 clear buffer
+ # 1 tree traversal
+ # 2 draw passes
+ # 2 StateSets
+ self.assertEqual(
+ "8",
+ s.get_eval("window.g_client.renderGraphRoot."
+ "getRenderNodesInTree().length"))
+
+ # Draw 5 triangles
+ s.type("numShapes", "5")
+ s.click("btnTri")
+
+ # 5 more primitives should get created
+ # (1 for the parent transform)
+ s.wait_for_condition(
+ "window.g_client."
+ "getObjectsByClassName('o3d.Shape').length == 5",
+ 5000)
+ # 5 more primitives nodes should get created
+ self.assertEqual(
+ "5",
+ s.get_eval("window.g_client."
+ "getObjectsByClassName('o3d.Primitive').length"))
+
+ # Draw more triangles
+ s.type("numShapes", "8")
+ for i in range(1, 10):
+ s.click("btnTri")
+ s.wait_for_condition(
+ "window.g_client."
+ "getObjectsByClassName('o3d.Shape').length == %d" % (5 + i * 8),
+ 5000)
+ self.assertEqual(
+ str(5 + i * 8),
+ s.get_eval("window.g_client."
+ "getObjectsByClassName('o3d.Primitive').length"))
+
+ # Clear triangles, reset pack.
+ s.click("btnClear")
+
+ # Check assets for base o3d setup again.
+ self.assertEqual(
+ "2",
+ s.get_eval("window.g_client.root.getTransformsInTree().length"))
+ self.assertEqual(
+ "8",
+ s.get_eval("window.g_client.renderGraphRoot."
+ "getRenderNodesInTree().length"))
+
+ # Now draw lines
+ s.type("numShapes", "5")
+ s.click("btnLines")
+
+ # 5 more shapes should get created
+ # (1 for the parent transform)
+ s.wait_for_condition(
+ "window.g_client."
+ "getObjectsByClassName('o3d.Shape').length == 5",
+ 5000)
+ # 5 more primitives and drawelements should get created
+ self.assertEqual(
+ "5",
+ s.get_eval("window.g_client."
+ "getObjectsByClassName('o3d.Primitive').length"))
+ self.assertEqual(
+ "5",
+ s.get_eval("window.g_client."
+ "getObjectsByClassName('o3d.DrawElement').length"))
+
+ # Draw more lines
+ s.type("numShapes", "11")
+ for i in range(1, 10):
+ s.click("btnLines")
+ s.wait_for_condition(
+ "window.g_client."
+ "getObjectsByClassName('o3d.Shape').length == %d" % (5 + i * 11),
+ 5000)
+ self.assertEqual(
+ str(5 + i * 11),
+ s.get_eval("window.g_client."
+ "getObjectsByClassName('o3d.Primitive').length"))
+
+ # Clear triangles, reset pack.
+ s.click("btnClear")
+
+ # Check assets for base o3d setup again.
+ self.assertEqual(
+ "2",
+ s.get_eval("window.g_client.root.getTransformsInTree().length"))
+ self.assertEqual(
+ "8",
+ s.get_eval("window.g_client.renderGraphRoot."
+ "getRenderNodesInTree().length"))
+
+ # Now draw 1000 triangles
+ s.type("numShapes", "1000")
+ s.click("btnTri")
+
+ # 30 seconds to draw 1000 triangle shapes
+ s.wait_for_condition(
+ "window.g_client."
+ "getObjectsByClassName('o3d.Shape').length == 1000",
+ 30000)
+ # Assert number of primitives
+ self.assertEqual(
+ "1000",
+ s.get_eval("window.g_client."
+ "getObjectsByClassName('o3d.Primitive').length"))
+
+ # Clear triangles, reset pack.
+ s.click("btnClear")
+
+ def TestStressMultiWindow(self):
+ """Opens 5 windows of simpletexture.html."""
+
+ # Alias for the selenium session
+ s = self.session
+
+ # Save the titles of windows so we can reset the current window.
+ # Note: docs of selenium are out of date. We should be able to use ids or
+ # names in select_window below but all of those methods failed.
+ old = s.get_all_window_titles()
+
+ for i in range(1, 5):
+ s.open_window(s.browserURL +
+ "/samples/simpletexture.html",
+ "o3dstress_window%d" % i)
+
+ for i in range(1, 5):
+ s.select_window("o3dstress_window%d" % i)
+ # Allow a limited time for the plugin to initialize.
+ s.wait_for_condition("typeof(window.g_client) != 'undefined';", 30000)
+
+ # Sanity checks.
+ self.assertEqual("Tutorial B3: Textures", s.get_title())
+ self.assertEqual("null", s.get_eval("window.undefined_symbol_xxxyyy"))
+
+ for i in range(1, 5):
+ s.select_window("o3dstress_window%d" % i)
+ s.close()
+
+ # make the old window the current window so the next test will run.
+ s.select_window(old[0])
+
+ def TestStressCullingZSort(self):
+ """Checks culling and zsorting work."""
+
+ # Alias for the selenium session
+ s = self.session
+ s.open(s.browserURL +
+ "/tests/selenium/tests/culling-zsort-test.html")
+
+ # Allow a limited time for the plugin to initialize.
+ s.wait_for_condition("typeof(window.g_client) != 'undefined';", 20000)
+
+ # Sanity checks.
+ self.assertEqual("Culling and ZSorting Test.", s.get_title())
+ self.assertEqual("null", s.get_eval("window.undefined_symbol_xxxyyy"))
+ s.wait_for_condition("window.g_client != null", 10000)
+
+ # Wait for all instances to be created.
+ s.wait_for_condition(
+ "window.g_totalDrawElementsElement.innerHTML == '2'",
+ 40000)
+
+ # Stop animation
+ s.run_script("g_timeMult = 0")
+ s.run_script("g_client.renderMode = g_o3d.Client.RENDERMODE_ON_DEMAND")
+ s.run_script("var g_rendered = false")
+
+ new_data = []
+ culling_reference_data = [[1793, 4472, 3780, 2, 4844, 213, 4631],
+ [1793, 6011, 3854, 2, 7734, 337, 7397],
+ [1793, 3014, 2416, 2, 3400, 121, 3279],
+ [1793, 2501, 2408, 2, 2420, 162, 2258],
+ [1793, 2933, 2914, 2, 2746, 182, 2564],
+ [1793, 2825, 2848, 2, 2604, 171, 2433],
+ [1793, 2933, 2790, 2, 2870, 155, 2715],
+ [1793, 4337, 3004, 2, 5360, 237, 5123]]
+
+ # Take screenshots
+ for clock in range(0, 8):
+ s.run_script("window.g_clock = " + str(clock * 3.14159 * 2.5 + 0.5))
+ self.assertTrue(
+ selenium_utilities.TakeScreenShot(s, self.browser, "window.g_client",
+ "cullingzsort" + str(clock)))
+ s.run_script("g_framesRendered = 0")
+ while int(s.get_eval("window.g_framesRendered")) < 3:
+ s.run_script("window.g_client.render()")
+ data = [s.get_eval("window.g_totalTransformsElement.innerHTML"),
+ s.get_eval("window.g_transformsProcessedElement.innerHTML"),
+ s.get_eval("window.g_transformsCulledElement.innerHTML"),
+ s.get_eval("window.g_totalDrawElementsElement.innerHTML"),
+ s.get_eval("window.g_drawElementsProcessedElement.innerHTML"),
+ s.get_eval("window.g_drawElementsCulledElement.innerHTML"),
+ s.get_eval("window.g_drawElementsRenderedElement.innerHTML")]
+ new_data.append(data)
+ print ", ".join(data)
+
+ # check the results
+ for clock in range(0, 8):
+ for ii in range(0, 7):
+ # comment out the following line and add a "pass" line if you need new
+ # culling reference data.
+ self.assertEqual(int(new_data[clock][ii]),
+ culling_reference_data[clock][ii])
+
+
+if __name__ == "__main__":
+ pass
diff --git a/o3d/tests/selenium/main.py b/o3d/tests/selenium/main.py
new file mode 100644
index 0000000..cb40b95
--- /dev/null
+++ b/o3d/tests/selenium/main.py
@@ -0,0 +1,954 @@
+#!/usr/bin/python2.4
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+"""Selenium tests for the O3D plugin.
+
+Sets up a local Selenium Remote Control server and a static file
+server that serves files off the o3d directory.
+
+Launches browsers to test the local build of the o3d plugin
+and reports results back to the user.
+"""
+
+
+
+import os
+import platform
+import re
+import SimpleHTTPServer
+import socket
+import SocketServer
+import subprocess
+import sys
+import threading
+import time
+import unittest
+import gflags
+import javascript_unit_tests
+# Import custom testrunner for pulse
+import pulse_testrunner
+# TODO: figure out if we can remove this hard-coded version
+import rev2478_mod as selenium
+import samples_tests
+import selenium_constants
+import selenium_utilities
+
+
+if platform.system() == "Windows":
+ default_java_exe = "java.exe"
+else:
+ default_java_exe = "java"
+
+# Commands line flags
+FLAGS = gflags.FLAGS
+gflags.DEFINE_boolean("verbose", False, "verbosity")
+gflags.DEFINE_boolean("screenshots", False, "takes screenshots")
+gflags.DEFINE_string(
+ "java",
+ default_java_exe,
+ "specifies the path to the java executable.")
+gflags.DEFINE_string(
+ "selenium_server",
+ "",
+ "specifies the path to the selenium server jar.")
+gflags.DEFINE_string(
+ "screencompare",
+ "",
+ "specifies the directory in which perceptualdiff resides.\n"
+ "compares screenshots with reference images")
+gflags.DEFINE_string(
+ "screenshotsdir",
+ selenium_constants.DEFAULT_SCREENSHOT_PATH,
+ "specifies the directory in which screenshots will be stored.")
+gflags.DEFINE_string(
+ "referencedir",
+ selenium_constants.DEFAULT_SCREENSHOT_PATH,
+ "Specifies the directory where reference images will be read from.")
+gflags.DEFINE_string(
+ "testprefix", "Test",
+ "specifies the prefix of tests to run")
+gflags.DEFINE_string(
+ "testsuffixes", "",
+ "specifies the suffixes, separated by commas of tests to run")
+gflags.DEFINE_string(
+ "servertimeout", "30",
+ "Specifies the timeout value, in seconds, for the selenium server.")
+
+# Browsers to choose from (for browser flag).
+# use --browser $BROWSER_NAME to run
+# tests for that browser
+gflags.DEFINE_list(
+ "browser",
+ "*firefox",
+ "\n".join(["comma-separated list of browsers to test",
+ "Options:"] +
+ selenium_constants.SELENIUM_BROWSER_SET))
+gflags.DEFINE_string(
+ "browserpath",
+ "",
+ "specifies the path to the browser executable "
+ "(for platforms that don't support MOZ_PLUGIN_PATH)")
+
+TESTING_ROOT = os.path.abspath(os.path.dirname(__file__) + "/..")
+
+
+class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+ """Hook to handle HTTP server requests.
+
+ Functions as a handler for logging and other utility functions.
+ """
+
+ def log_message(self, format, *args):
+ """Logging hook for HTTP server."""
+
+ # For now, just suppress logging.
+ pass
+ # TODO: might be nice to have a verbose option for debugging.
+
+
+class LocalFileHTTPServer(threading.Thread):
+ """Minimal HTTP server that serves local files.
+
+ Members:
+ http_alive: event to signal that http server is up and running
+ http_port: the TCP port the server is using
+ """
+
+ START_PORT = 8100
+ END_PORT = 8105
+
+ def __init__(self, local_root=None):
+ """Initializes the server.
+
+ Initializes the HTTP server to serve static files from the
+ local o3d directory
+
+ Args:
+ local_root: all files below this path are served. If not specified,
+ the current directory is the root.
+ """
+ threading.Thread.__init__(self)
+ self._local_root = local_root
+ self.http_alive = threading.Event()
+ self.http_port = 0
+
+ def run(self):
+ """Runs the HTTP server.
+
+ Server is started on an available port in the range of
+ START_PORT to END_PORT
+ """
+
+ if self._local_root:
+ os.chdir(self._local_root)
+
+ for self.http_port in range(self.START_PORT, self.END_PORT):
+ # Attempt to start the server
+ try:
+ httpd = SocketServer.TCPServer(("", self.http_port),
+ MyRequestHandler)
+ except socket.error:
+ # Server didn't manage to start up, try another port.
+ pass
+ else:
+ self.http_alive.set()
+ httpd.serve_forever()
+
+ if not self.http_alive.isSet():
+ print("No available port found for HTTP server in the range %d to %d."
+ % (self.START_PORT, self.END_PORT))
+
+ @staticmethod
+ def StartServer(local_root=None):
+ """Create and start a LocalFileHTTPServer on a separate thread.
+
+ Args:
+ local_root: serve all static files below this directory. If not
+ specified, the current directory is the root.
+
+ Returns:
+ http_server: LocalFileHTTPServer() object
+ """
+
+ # Start up the Selenium Remote Control server
+ http_server = LocalFileHTTPServer(local_root)
+ http_server.setDaemon(True)
+ http_server.start()
+
+ # Wait till the Selenium RC Server is up
+ http_server.http_alive.wait()
+
+ print "LocalFileHTTPServer started on port %d" % http_server.http_port
+
+ return http_server
+
+
+class SeleniumRemoteControl(threading.Thread):
+ """A thread that launches the Selenium Remote Control server.
+
+ The Remote Control server allows us to launch a browser and remotely
+ control it from a script.
+
+ Members:
+ selenium_alive: event to signal that selenium server is up and running
+ selenium_port: the TCP port the server is using
+ process: the subprocess.Popen instance for the server
+ """
+
+ START_PORT = 5430
+ END_PORT = 5535
+
+ def __init__(self, verbose, java_path, selenium_server, server_timeout):
+ """Initializes the SeleniumRemoteControl class.
+
+ Args:
+ verbose: boolean verbose flag
+ java_path: path to java used to run selenium.
+ selenium_server: path to jar containing selenium server.
+ server_timeout: server timeout value, in seconds.
+ """
+ self.selenium_alive = threading.Event()
+ self.selenium_port = 0
+ self.verbose = verbose
+ self.java_path = java_path
+ self.selenium_server = selenium_server
+ self.timeout = server_timeout
+ threading.Thread.__init__(self)
+
+ def run(self):
+ """Starts the selenium server.
+
+ Server is started on an available port in the range of
+ START_PORT to END_PORT
+ """
+
+ for self.selenium_port in range(self.START_PORT, self.END_PORT):
+ # Attempt to start the selenium RC server from java
+ self.process = subprocess.Popen(
+ [self.java_path, "-jar", self.selenium_server, "-multiWindow",
+ "-port", str(self.selenium_port), "-timeout", self.timeout],
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+ for unused_i in range(1, 10):
+ server_msg = self.process.stdout.readline()
+ if self.verbose and server_msg is not None:
+ # log if verbose flag is on
+ print "sel_serv:" + server_msg
+
+ # This status message indicates that the server has done
+ # a bind to the port self.selenium_port successfully.
+ if server_msg.find("INFO - Started SocketListener") != -1:
+ self.selenium_alive.set()
+ break
+
+ # Error starting server on this port, try the next port.
+ if not self.selenium_alive.isSet():
+ continue
+
+ # Loop and read from stdout
+ while self.process.poll() is None:
+ server_msg = self.process.stdout.readline()
+ if self.verbose and server_msg is not None:
+ # log if verbose flag is on
+ print "sel_serv:" + server_msg
+
+ # Finish.
+ break
+
+ if not self.selenium_alive.isSet():
+ print("No available port found for Selenium RC Server "
+ "in the range %d to %d."
+ % (self.START_PORT, self.END_PORT))
+
+ @staticmethod
+ def StartServer(verbose, java_path, selenium_server, server_timeout):
+ """Create and start the Selenium RC Server on a separate thread.
+
+ Args:
+ verbose: boolean verbose flag
+ java_path: path to java used to run selenium.
+ selenium_server: path to jar containing selenium server.
+ server_timeout: server timeout value, in seconds
+
+ Returns:
+ selenium_server: SeleniumRemoteControl() object
+ """
+
+ # Start up the Selenium Remote Control server
+ selenium_server = SeleniumRemoteControl(verbose,
+ java_path,
+ selenium_server,
+ server_timeout)
+ selenium_server.setDaemon(True)
+ selenium_server.start()
+
+ # Wait till the Selenium RC Server is up
+ selenium_server.selenium_alive.wait()
+
+ print("Selenium RC server started on port %d"
+ % selenium_server.selenium_port)
+
+ return selenium_server
+
+
+class SeleniumSession(object):
+ """A selenium browser session, with support servers.
+
+ The support servers include a Selenium Remote Control server, and
+ a local HTTP server to serve static test files.
+
+ Members:
+ session: a selenium() instance
+ selenium_server: a SeleniumRemoteControl() instance
+ http_server: a LocalFileHTTPServer() instance
+ runner: a TestRunner() instance
+ """
+
+ def __init__(self, verbose, java_path, selenium_server, server_timeout):
+ """Initializes a Selenium Session.
+
+ Args:
+ verbose: boolean verbose flag
+ java_path: path to java used to run selenium.
+ selenium_server: path to jar containing selenium server.
+ server_timeout: server timeout value, in seconds
+ """
+ # Start up a static file server, to serve the test pages.
+ # Serve from the o3d directory
+ self.http_server = LocalFileHTTPServer.StartServer(
+ TESTING_ROOT + "/../")
+
+ # Start up the Selenium Remote Control Server
+ self.selenium_server = SeleniumRemoteControl.StartServer(verbose,
+ java_path,
+ selenium_server,
+ server_timeout)
+
+ # Set up a testing runner
+ self.runner = pulse_testrunner.PulseTestRunner()
+
+ # Set up a phantom selenium session so we can call shutdown if needed.
+ self.session = selenium.selenium(
+ "localhost", self.selenium_server.selenium_port, "*firefox",
+ "http://" + socket.gethostname() + ":" +
+ str(self.http_server.http_port))
+
+ def StartSession(self, browser):
+ """Starts the Selenium Session and connects to the HTTP server.
+
+ Args:
+ browser: selenium browser name
+ """
+
+ if browser == "*googlechrome":
+ # TODO: Replace socket.gethostname() with "localhost"
+ # once Chrome local proxy fix is in.
+ server_url = "http://" + socket.gethostname() + ":"
+ else:
+ server_url = "http://localhost:"
+ server_url += str(self.http_server.http_port)
+ browser_path_with_space = FLAGS.browserpath
+
+ # TODO: This is a hack to figure out if we're on 64-bit
+ # Windows. If we are, then we have to run the 32-bit Internet
+ # Explorer so that our plugin will work (indeed, even Microsoft
+ # has even made it impossible to use 64-bit Internet Explorer as
+ # your default browser). We need to find a better way to
+ # determine if we're on 64-bit Windows, so that it will work on
+ # foreign machines (which don't use the strings below for "Program
+ # Files" and "Internet Explorer").
+ if (not browser_path_with_space and
+ browser == "*iexplore"):
+ program_files_x86 = "C:\\Program Files (x86)"
+ if os.path.isdir(program_files_x86):
+ browser_path_with_space = os.path.join(program_files_x86,
+ "Internet Explorer",
+ "iexplore.exe")
+
+ if browser_path_with_space:
+ browser_path_with_space = " " + browser_path_with_space
+ self.session = selenium.selenium("localhost",
+ self.selenium_server.selenium_port,
+ browser + browser_path_with_space,
+ server_url)
+ self.session.start()
+
+ def CloseSession(self):
+ """Closes the selenium sesssion."""
+ self.session.stop()
+
+ def TearDown(self):
+ """Stops the selenium server."""
+ self.session.do_command("shutDown", [])
+ # Sync with selenium_server thread
+ self.selenium_server.join()
+
+ def TestBrowser(self, browser, test_list, test_prefix, test_suffixes,
+ server_timeout):
+ """Runs Selenium tests for a specific browser.
+
+ Args:
+ browser: selenium browser name (eg. *iexplore, *firefox).
+ test_list: list to add tests to.
+ test_prefix: prefix of tests to run.
+ test_suffixes: comma separated suffixes of tests to run.
+ server_timeout: server timeout value, in milliseconds
+
+ Returns:
+ result: result of test runner.
+ """
+ print "Testing %s..." % browser
+ self.StartSession(browser)
+ self.session.set_timeout(server_timeout)
+ self.runner.setBrowser(browser)
+
+ try:
+ result = self.runner.run(
+ SeleniumSuite(self.session, browser, test_list,
+ test_prefix, test_suffixes))
+ finally:
+ self.CloseSession()
+
+ return result
+
+
+class LocalTestSuite(unittest.TestSuite):
+ """Wrapper for unittest.TestSuite so we can collect the tests."""
+
+ def __init__(self):
+ unittest.TestSuite.__init__(self)
+ self.test_list = []
+
+ def addTest(self, name, test):
+ """Adds a test to the TestSuite and records its name and test_path.
+
+ Args:
+ name: name of test.
+ test: test to pass to unittest.TestSuite.
+ """
+ unittest.TestSuite.addTest(self, test)
+ try:
+ self.test_list.append((name, test.options))
+ except AttributeError:
+ self.test_list.append((name, []))
+
+
+def MatchesSuffix(name, suffixes):
+ """Checks if a name ends in one of the suffixes.
+
+ Args:
+ name: Name to test.
+ suffixes: list of suffixes to test for.
+ Returns:
+ True if name ends in one of the suffixes or if suffixes is empty.
+ """
+ if suffixes:
+ name_lower = name.lower()
+ for suffix in suffixes:
+ if name_lower.endswith(suffix):
+ return True
+ return False
+ else:
+ return True
+
+
+def AddTests(test_suite, session, browser, module, filename, prefix,
+ test_prefix_filter, test_suffixes):
+ """Add tests defined in filename.
+
+ Assumes module has a method "GenericTest" that uses self.args to run.
+
+ Args:
+ test_suite: A Selenium test_suite to add tests to.
+ session: a Selenium instance.
+ browser: browser name.
+ module: module which will have method GenericTest() called to run each test.
+ filename: filename of file with list of tests.
+ prefix: prefix to add to the beginning of each test.
+ test_prefix_filter: Only adds a test if it starts with this.
+ test_suffixes: list of suffixes to filter by. An empty list = pass all.
+ """
+ # See comments in that file for the expected format.
+ # skip lines that are blank or have "#" or ";" as their first non whitespace
+ # character.
+ test_list_file = open(filename, "r")
+ samples = test_list_file.readlines()
+ test_list_file.close()
+
+ for sample in samples:
+ sample = sample.strip()
+ if not sample or sample[0] == ";" or sample[0] == "#":
+ continue
+
+ arguments = sample.split()
+ test_type = arguments[0].lower()
+ test_path = arguments[1]
+ options = arguments[2:]
+
+ # TODO: Add filter based on test_type
+
+ name = ("Test" + prefix + re.sub("\W", "_", test_path) +
+ test_type.capitalize())
+
+ # Only execute this test if the current browser is not in the list
+ # of skipped browsers.
+ test_skipped = False
+ for option in options:
+ if option.startswith("except"):
+ skipped_platforms = selenium_utilities.GetArgument(option)
+ if not skipped_platforms is None:
+ skipped_platforms = skipped_platforms.split(",")
+ if browser in skipped_platforms:
+ test_skipped = True
+
+ if not test_skipped:
+ # Check if there is already a test function by this name in the module.
+ if (test_path.startswith(test_prefix_filter) and
+ hasattr(module, test_path) and callable(getattr(module, test_path))):
+ test_suite.addTest(test_path, module(test_path, session, browser,
+ options=options))
+ elif (name.startswith(test_prefix_filter) and
+ MatchesSuffix(name, test_suffixes)):
+ # no, so add a method that will run a test generically.
+ setattr(module, name, module.GenericTest)
+ test_suite.addTest(name, module(name, session, browser,
+ test_type, test_path, options))
+
+
+def SeleniumSuite(session, browser, test_list, test_prefix, test_suffixes):
+ """Creates a test suite to run the unit tests.
+
+ Args:
+ session: a selenium() instance
+ browser: browser name
+ test_list: list to add tests to.
+ test_prefix: prefix of tests to run.
+ test_suffixes: A comma separated string of suffixes to filter by.
+ Returns:
+ A selenium test suite.
+ """
+
+ test_suite = LocalTestSuite()
+
+ suffixes = test_suffixes.split(",")
+
+ # add sample tests.
+ filename = os.path.join(os.getcwd(), "tests", "selenium", "sample_list.txt")
+ AddTests(test_suite,
+ session,
+ browser,
+ samples_tests.SampleTests,
+ filename,
+ "Sample",
+ test_prefix,
+ suffixes)
+
+ # add javascript tests.
+ filename = os.path.join(os.getcwd(), "tests", "selenium",
+ "javascript_unit_test_list.txt")
+ AddTests(test_suite,
+ session,
+ browser,
+ javascript_unit_tests.JavaScriptUnitTests,
+ filename,
+ "UnitTest",
+ test_prefix,
+ suffixes)
+
+ test_list += test_suite.test_list
+
+ return test_suite
+
+
+def CompareScreenshots(browser, test_list, screencompare, screenshotsdir,
+ screencompare_tool, verbose):
+ """Performs the image validation for test-case frame captures.
+
+ Args:
+ browser: selenium browser name
+ test_list: list of tests that ran.
+ screencompare: True to actually run tests.
+ screenshotsdir: path of directory containing images to compare.
+ screencompare_tool: path to image diff tool.
+ verbose: If True then outputs verbose info.
+
+ Returns:
+ A Results object.
+ """
+ print "Validating captured frames against reference data..."
+
+ class Results(object):
+ """An object to return results of screenshot compares.
+
+ Similar to unittest.TestResults.
+ """
+
+ def __init__(self):
+ object.__init__(self)
+ self.tests_run = 0
+ self.current_test = None
+ self.errors = []
+ self.failures = []
+ self.start_time = 0
+
+ def StartTest(self, test):
+ """Adds a test.
+
+ Args:
+ test: name of test.
+ """
+ self.start_time = time.time()
+ self.tests_run += 1
+ self.current_test = test
+
+ def TimeTaken(self):
+ """Returns the time since the last call to StartTest."""
+ return time.time() - self.start_time
+
+ def AddFailure(self, test, browser, message):
+ """Adds a failure.
+
+ Args:
+ test: name of the test.
+ browser: name of the browser.
+ message: error message to print
+ """
+ self.failures.append(test)
+ print "ERROR: ", message
+ print("SELENIUMRESULT %s <%s> [%.3fs]: FAIL"
+ % (test, browser, self.TimeTaken()))
+
+ def AddSuccess(self, test):
+ """Adds a success.
+
+ Args:
+ test: name of the test.
+ """
+ print("SELENIUMRESULT %s <%s> [%.3fs]: PASS"
+ % (test, browser, self.TimeTaken()))
+
+ def WasSuccessful(self):
+ """Returns true if all tests were successful."""
+ return not self.errors and not self.failures
+
+ results = Results()
+
+ if not screencompare:
+ return results
+
+ base_path = os.getcwd()
+
+ reference_files = os.listdir(os.path.join(
+ base_path,
+ selenium_constants.REFERENCE_SCREENSHOT_PATH))
+
+ generated_files = os.listdir(os.path.join(base_path, screenshotsdir))
+
+ # Prep the test list for matching
+ temp = []
+ for (test, options) in test_list:
+ test = selenium_utilities.StripTestTypeSuffix(test)
+ temp.append((test.lower(), options))
+ test_list = temp
+
+ # Create regex object for filename
+ # file is in format "FILENAME_reference.png"
+ reference_file_name_regex = re.compile(r"^(.*)_reference\.png")
+ generated_file_name_regex = re.compile(r"^(.*)\.png")
+
+ # check that there is a reference file for each generated file.
+ for file_name in generated_files:
+ match = generated_file_name_regex.search(file_name)
+
+ if match is None:
+ # no matches
+ continue
+
+ # Generated file name without png extension
+ actual_name = match.group(1)
+
+ # Get full paths to reference and generated files
+ reference_file = os.path.join(
+ base_path,
+ selenium_constants.REFERENCE_SCREENSHOT_PATH,
+ actual_name + "_reference.png")
+ generated_file = os.path.join(
+ base_path,
+ screenshotsdir,
+ actual_name + ".png")
+
+ test_name = "TestReferenceScreenshotExists_" + actual_name
+ results.StartTest(test_name)
+ if not os.path.exists(reference_file):
+ # reference file does not exist
+ results.AddFailure(
+ test_name, browser,
+ "Missing reference file %s for generated file %s." %
+ (reference_file, generated_file))
+ else:
+ results.AddSuccess(test_name)
+
+ # Assuming both the result and reference image sets are the same size,
+ # verify that corresponding images are similar within tolerance.
+ for file_name in reference_files:
+ match = reference_file_name_regex.search(file_name)
+
+ if match is None:
+ # no matches
+ continue
+
+ # Generated file name without png extension
+ actual_name = match.group(1)
+ # Get full paths to reference and generated files
+ reference_file = os.path.join(
+ base_path,
+ selenium_constants.REFERENCE_SCREENSHOT_PATH,
+ file_name)
+ platform_specific_reference_file = os.path.join(
+ base_path,
+ selenium_constants.PLATFORM_SPECIFIC_REFERENCE_SCREENSHOT_PATH,
+ actual_name + "_reference.png")
+ generated_file = os.path.join(
+ base_path,
+ screenshotsdir,
+ actual_name + ".png")
+
+ # Generate a test case name
+ test_name = "TestScreenCompare_" + actual_name
+
+ # skip the reference file if the test is not in the test list.
+ basename = os.path.splitext(os.path.basename(file_name))[0]
+ basename = re.sub("\d+_reference", "", basename).lower()
+ basename = re.sub("\W", "_", basename)
+ test_was_run = False
+ test_options = []
+ for (test, options) in test_list:
+ if test.endswith(basename):
+ test_was_run = True
+ test_options = options or []
+ break
+
+ if test_was_run:
+ results.StartTest(test_name)
+ else:
+ # test was not planned to run for this reference image.
+ if os.path.exists(generated_file):
+ # a generated file exists? The test name does not match the screenshot.
+ results.StartTest(test_name)
+ results.AddFailure(test_name, browser,
+ "Test name and screenshot name do not match.")
+ continue
+
+ # Check if there is a platform specific version of the reference image
+ if os.path.exists(platform_specific_reference_file):
+ reference_file = platform_specific_reference_file
+
+ # Check if perceptual diff exists
+ pdiff_path = os.path.join(base_path, screencompare_tool)
+ if not os.path.exists(pdiff_path):
+ # Perceptualdiff.exe does not exist, fail.
+ results.AddFailure(
+ test_name, browser,
+ "Perceptual diff %s does not exist." % pdiff_path)
+ continue
+
+ pixel_threshold = "10"
+ use_colorfactor = False
+ use_downsample = False
+
+ # Find out if the test specified any options relating to perceptual diff
+ # that will override the defaults.
+ for opt in test_options:
+ if opt.startswith("pdiff_threshold"):
+ pixel_threshold = selenium_utilities.GetArgument(opt)
+ elif (opt.startswith("pdiff_threshold_mac") and
+ platform.system() == "Darwin"):
+ pixel_threshold = selenium_utilities.GetArgument(opt)
+ elif (opt.startswith("pdiff_threshold_win") and
+ platform.system() == "Microsoft"):
+ pixel_threshold = selenium_utilities.GetArgument(opt)
+ elif (opt.startswith("pdiff_threshold_linux") and
+ platform.system() == "Linux"):
+ pixel_threshold = selenium_utilities.GetArgument(opt)
+ elif (opt.startswith("colorfactor")):
+ colorfactor = selenium_utilities.GetArgument(opt)
+ use_colorfactor = True
+ elif (opt.startswith("downsample")):
+ downsample_factor = selenium_utilities.GetArgument(opt)
+ use_downsample = True
+
+ # Check if file exists
+ if os.path.exists(generated_file):
+ diff_file = os.path.join(base_path, screenshotsdir,
+ "compare_%s.png" % actual_name)
+
+ # Run perceptual diff
+ arguments = [pdiff_path,
+ reference_file,
+ generated_file,
+ "-output", diff_file,
+ "-fov", "45",
+ # Turn on verbose output for the percetual diff so we
+ # can see how far off we are on the threshold.
+ "-verbose",
+ # Set the threshold to zero so we can get a count
+ # of the different pixels. This causes the program
+ # to return failure for most images, but we can compare
+ # the values ourselves below.
+ "-threshold", "0"]
+ if use_colorfactor:
+ arguments += ["-colorfactor", colorfactor]
+ if use_downsample:
+ arguments += ["-downsample", downsample_factor]
+
+ # Print the perceptual diff command line so we can debug easier.
+ if verbose:
+ print " ".join(arguments)
+
+ # diff tool should return 0 on success
+ expected_result = 0
+
+ pdiff_pipe = subprocess.Popen(arguments,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ (pdiff_stdout, pdiff_stderr) = pdiff_pipe.communicate()
+ result = pdiff_pipe.returncode
+
+ # Find out how many pixels were different by looking at the output.
+ pixel_re = re.compile("(\d+) pixels are different", re.DOTALL)
+ pixel_match = pixel_re.search(pdiff_stdout)
+ different_pixels = "0"
+ if pixel_match:
+ different_pixels = pixel_match.group(1)
+
+ if (result == expected_result or (pixel_match and
+ int(different_pixels) <= int(pixel_threshold))):
+ # The perceptual diff passed.
+ pass_re = re.compile("PASS: (.*?)\n", re.DOTALL)
+ pass_match = pass_re.search(pdiff_stdout)
+ reason = "Images are not perceptually different."
+ if pass_match:
+ reason = pass_match.group(1)
+ print ("%s PASSED with %s different pixels "
+ "(threshold %s) because: %s" % (test_name,
+ different_pixels,
+ pixel_threshold,
+ reason))
+ results.AddSuccess(test_name)
+ else:
+ # The perceptual diff failed.
+ if pixel_match and int(different_pixels) > int(pixel_threshold):
+ results.AddFailure(
+ test_name, browser,
+ ("Reference framebuffer (%s) does not match generated "
+ "file (%s): it differed by %s pixels with a threshold of %s." %
+ (reference_file, generated_file, different_pixels,
+ pixel_threshold)))
+ else:
+ # The perceptual diff failed for some reason other than
+ # pixel differencing.
+ fail_re = re.compile("FAIL: (.*?)\n", re.DOTALL)
+ fail_match = fail_re.search(pdiff_stdout)
+ reason = "Unknown failure"
+ if fail_match:
+ reason = fail_match.group(1)
+ results.AddFailure(
+ test_name, browser,
+ ("Perceptual diff of reference (%s) and generated (%s) files "
+ "failed because: %s" %
+ (reference_file, generated_file, reason)))
+ else:
+ # Generated file does not exist
+ results.AddFailure(test_name, browser,
+ "File %s does not exist." % generated_file)
+
+ return results
+
+
+def main(unused_argv):
+ # Boolean to record if all tests passed.
+ all_tests_passed = True
+
+ selenium_constants.REFERENCE_SCREENSHOT_PATH = os.path.join(
+ FLAGS.referencedir,
+ "reference",
+ "")
+ selenium_constants.PLATFORM_SPECIFIC_REFERENCE_SCREENSHOT_PATH = os.path.join(
+ FLAGS.referencedir,
+ selenium_constants.PLATFORM_SCREENSHOT_DIR,
+ "")
+
+ # Open a new session to Selenium Remote Control
+ selenium_session = SeleniumSession(FLAGS.verbose, FLAGS.java,
+ FLAGS.selenium_server,
+ FLAGS.servertimeout)
+ for browser in FLAGS.browser:
+ if browser in set(selenium_constants.SELENIUM_BROWSER_SET):
+ test_list = []
+ result = selenium_session.TestBrowser(browser, test_list,
+ FLAGS.testprefix,
+ FLAGS.testsuffixes,
+ int(FLAGS.servertimeout) * 1000)
+
+ # Compare screenshots
+ compare_result = CompareScreenshots(browser,
+ test_list,
+ FLAGS.screenshots,
+ FLAGS.screenshotsdir,
+ FLAGS.screencompare,
+ FLAGS.verbose)
+ if not result.wasSuccessful() or not compare_result.WasSuccessful():
+ all_tests_passed = False
+ # Log results
+ print "Results for %s:" % browser
+ print " %d tests run." % (result.testsRun + compare_result.tests_run)
+ print " %d errors." % (len(result.errors) + len(compare_result.errors))
+ print " %d failures.\n" % (len(result.failures) +
+ len(compare_result.failures))
+
+ else:
+ print "ERROR: Browser %s is invalid." % browser
+ print "Run with --help to view list of supported browsers.\n"
+ all_tests_passed = False
+
+ # Wrap up session
+ selenium_session.TearDown()
+
+ if all_tests_passed:
+ # All tests successful.
+ return 0
+ else:
+ # Return error code 1.
+ return 1
+
+if __name__ == "__main__":
+ remaining_argv = FLAGS(sys.argv)
+ main(remaining_argv)
diff --git a/o3d/tests/selenium/pulse_testrunner.py b/o3d/tests/selenium/pulse_testrunner.py
new file mode 100644
index 0000000..71f7bb3
--- /dev/null
+++ b/o3d/tests/selenium/pulse_testrunner.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python2.4
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+"""Test runner that displays customized results for pulse.
+
+Instead of the usual '.', 'E', and 'F' symbols, more detailed
+results are printed after each test.
+
+ie. 'TESTRESULT $TESTNAME: {PASS/FAIL}'
+"""
+
+
+
+import sys
+import time
+import unittest
+
+
+# Disable lint errors about functions having to start with upper case.
+# This is done to standardize the case of all functions in this class.
+# pylint: disable-msg=C6409
+class _PulseTestResult(unittest.TestResult):
+ """A specialized class that prints formatted text results to a stream.
+
+ Test results are formatted to be recognized in pulse.
+ Used by PulseTestRunner.
+ """
+ separator1 = "=" * 70
+ separator2 = "-" * 70
+
+ def __init__(self, stream, descriptions, verbosity, browser):
+ unittest.TestResult.__init__(self)
+ self.stream = stream
+ self.show_all = verbosity > 1
+ self.dots = verbosity == 1
+ self.descriptions = descriptions
+ # Dictionary of start times
+ self.start_times = {}
+ # Dictionary of results
+ self.results = {}
+ self.browser = browser
+
+ def getDescription(self, test):
+ """Gets description of test."""
+ if self.descriptions:
+ return test.shortDescription() or str(test)
+ else:
+ return str(test)
+
+ def startTest(self, test):
+ """Starts test."""
+ # Records the start time
+ self.start_times[test] = time.time()
+ # Default testresult if success not called
+ self.results[test] = "FAIL"
+ unittest.TestResult.startTest(self, test)
+ if self.show_all:
+ self.stream.write(self.getDescription(test))
+ self.stream.write(" ... ")
+
+ def stopTest(self, test):
+ """Called when test is ended."""
+ time_taken = time.time() - self.start_times[test]
+ result = self.results[test]
+ self.stream.writeln("SELENIUMRESULT %s <%s> [%.3fs]: %s"
+ % (test, self.browser, time_taken, result))
+
+ def addSuccess(self, test):
+ """Adds success result to TestResult."""
+ unittest.TestResult.addSuccess(self, test)
+ self.results[test] = "PASS"
+
+ def addError(self, test, err):
+ """Adds error result to TestResult."""
+ unittest.TestResult.addError(self, test, err)
+ self.results[test] = "FAIL"
+
+ def addFailure(self, test, err):
+ """Adds failure result to TestResult."""
+ unittest.TestResult.addFailure(self, test, err)
+ self.results[test] = "FAIL"
+
+ def printErrors(self):
+ """Prints all errors and failures."""
+ if self.dots or self.show_all:
+ self.stream.writeln()
+ self.printErrorList("ERROR", self.errors)
+ self.printErrorList("FAIL", self.failures)
+
+ def printErrorList(self, flavour, errors):
+ """Prints a given list of errors."""
+ for test, err in errors:
+ self.stream.writeln(self.separator1)
+ self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
+ self.stream.writeln(self.separator2)
+ self.stream.writeln("%s" % err)
+
+
+class PulseTestRunner(unittest.TextTestRunner):
+ """A specialized test runner class that displays results in textual form.
+
+ Test results are formatted to be recognized in pulse.
+ """
+
+ def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1):
+ self.browser = "default_browser"
+ unittest.TextTestRunner.__init__(self, stream, descriptions, verbosity)
+
+ def setBrowser(self, browser):
+ """Sets the browser name."""
+ self.browser = browser
+
+ def _makeResult(self):
+ """Returns a formatted test result for pulse."""
+ return _PulseTestResult(self.stream,
+ self.descriptions,
+ self.verbosity,
+ self.browser)
+
+if __name__ == "__main__":
+ pass
diff --git a/o3d/tests/selenium/run.bat b/o3d/tests/selenium/run.bat
new file mode 100644
index 0000000..3305ef8
--- /dev/null
+++ b/o3d/tests/selenium/run.bat
@@ -0,0 +1,34 @@
+@echo off
+REM Copyright 2009, Google Inc.
+REM All rights reserved.
+REM
+REM Redistribution and use in source and binary forms, with or without
+REM modification, are permitted provided that the following conditions are
+REM met:
+REM
+REM * Redistributions of source code must retain the above copyright
+REM notice, this list of conditions and the following disclaimer.
+REM * Redistributions in binary form must reproduce the above
+REM copyright notice, this list of conditions and the following disclaimer
+REM in the documentation and/or other materials provided with the
+REM distribution.
+REM * Neither the name of Google Inc. nor the names of its
+REM contributors may be used to endorse or promote products derived from
+REM this software without specific prior written permission.
+REM
+REM THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+REM "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+REM LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+REM A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+REM OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+REM SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+REM LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+REM DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+REM THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+REM (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+REM OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+echo Setting up python environment..
+call %~dp0..\..\..\third_party\python_24\setup_env.bat
+echo Starting selenium tests..
+%~dp0..\..\..\third_party\python_24\python.exe main.py %*
diff --git a/o3d/tests/selenium/sample_list.txt b/o3d/tests/selenium/sample_list.txt
new file mode 100644
index 0000000..699a888
--- /dev/null
+++ b/o3d/tests/selenium/sample_list.txt
@@ -0,0 +1,118 @@
+#
+# Test Requirements:
+#
+# Each sample is expected to have a global variable called g_finished
+# that is set to true when the sample has finish initalizing and is ready
+# for a screenshot.
+#
+# Line Format:
+#
+# TestType test-name options
+#
+# Valid TestTypes are:
+#
+# small
+# medium
+# large
+#
+# options are separated by spaces.
+# screenshot : take a screenshot. You can specify a time with
+# screenshot(seconds) as in screenshot(4), take it at the 4 second mark.
+# You can also specify more than 1 screenshot by specifying more than
+# one screenshot option as in "screenshot(4), screenshot(6.5). If no time
+# is specified 27.5 is used.
+#
+# To take a screenshot the test must have a global variable "g_client"
+# that is the client plugin object from which to take a screenshot.
+#
+# If the sample is animated, it is expected to have a global variable
+# called g_timeMult that can be set to 0 to stop the animation. All of
+# its animation must be based on a global variable called g_clock, such
+# that setting g_clock to the same value will always produce the same
+# image.
+#
+# timeout(milliseconds) : Set the timeout to wait for readiness.
+# Default 10000.
+#
+# client(client_variable_name) : Name of client variable in javascript.
+# Default = "g_client"
+#
+# pdiff_threshold_mac(number_of_pixels_allowed)
+# pdiff_threshold_win(number_of_pixels_allowed)
+# pdiff_threshold_linux(number_of_pixels_allowed)
+# pdiff_threshold(number_of_pixels_allowed) : Number of pixels
+# allowed to be perceptually different before the test fails.
+# "pdiff_threshold" specifies the threshold for all
+# platforms. Platform specific versions override the
+# threshold for that platform. The default threshold is 10 pixels.
+#
+# except(*firefox, *iexplore, *googlechrome) : Name of the browser
+# environment(s) where the test should be skipped.
+# Default = ""
+#
+# NOTE! ----------------------------------------------------------------------
+#
+# Read the sample guidelines
+# http://wiki.corp.google.com/twiki/bin/view/Main/ClientThreeDSampleGuidelines
+#
+#
+medium 2d screenshot pdiff_threshold(41200)
+medium animation
+large animated-scene screenshot timeout(45000) pdiff_threshold(200)
+large beachdemo/beachdemo screenshot timeout(120000) pdiff_threshold(15500) except(*iexplore)
+medium canvas screenshot pdiff_threshold(4700) pdiff_threshold_mac(14600)
+medium canvas-fonts screenshot pdiff_threshold(1100) pdiff_threshold_mac(21900)
+medium canvas-texturedraw
+medium checkers screenshot pdiff_threshold(1100)
+medium convolution screenshot pdiff_threshold(200)
+medium culling screenshot pdiff_threshold(3400)
+medium debugging screenshot pdiff_threshold(2000) pdiff_threshold_mac(3000)
+medium displayfps
+small generate-texture screenshot pdiff_threshold(30000) except(*iexplore)
+medium hellocube screenshot pdiff_threshold(1000)
+medium hellocube-colors screenshot pdiff_threshold(1000)
+medium helloworld screenshot pdiff_threshold(900)
+medium hud-2d-overlay screenshot pdiff_threshold(11300) pdiff_threshold_win(39800)
+medium instance-override screenshot pdiff_threshold(10500)
+medium instancing screenshot pdiff_threshold(14300)
+medium juggler screenshot
+# NOTE: temporarily disabled while we investigate failures
+# medium julia screenshot
+small multiple-views screenshot pdiff_threshold(1900)
+medium particles screenshot pdiff_threshold(35500)
+medium primitives screenshot pdiff_threshold(17200) pdiff_threshold_mac(20000)
+medium procedural-texture screenshot pdiff_threshold(1300)
+medium render-targets screenshot pdiff_threshold(1400)
+medium scatter-chart screenshot pdiff_threshold(10100) pdiff_threshold_mac(10600)
+medium simple screenshot client(g_simple.client)
+medium simpletexture screenshot pdiff_threshold(5100)
+medium skinning screenshot pdiff_threshold(500)
+medium sobel screenshot pdiff_threshold(1400)
+medium stencil_example screenshot(0) screenshot(100) screenshot(7777) pdiff_threshold(4800) pdiff_threshold_win(20800)
+small texturesamplers screenshot pdiff_threshold(32200) pdiff_threshold_win(37100)
+medium tutorial-primitive screenshot pdiff_threshold(1200) pdiff_threshold_mac(10400)
+large vertex-shader screenshot timeout(45000) pdiff_threshold(1400) except(*iexplore)
+medium vertex-shader-animation screenshot pdiff_threshold(5100)
+medium zsorting screenshot pdiff_threshold(39500)
+# box2d-3d works in browsers but for some reason times out on some configs
+#large box2d-3d/box2d-3d timeout(45000) except(*googlechrome)
+large simpleviewer/simpleviewer screenshot pdiff_threshold(100)
+large trends/trends timeout(30000)
+
+# -- tests below this line are tests for which there is a python
+# function to custom run the test. As such, only the 'except' and
+# pdiff_threshold options have any meaning
+
+small TestSampleErrorTextureSmall pdiff_threshold(2200)
+small TestSampleHelloCube_TexturesSmall pdiff_threshold(5900)
+small TestSampleRefreshPageLoad_Small
+medium TestSampleCustomCamera pdiff_threshold(900) pdiff_threshold_win(5700)
+medium TestSamplePicking
+medium TestSampleRenderMode
+medium TestSampleRotateModel pdiff_threshold(900)
+medium TestSampleShader_Test pdiff_threshold(5800) pdiff_threshold_win(26000)
+large TestSampleMultipleClientsLarge
+large TestSamplePingPongLarge
+# This test currently fails on IE as it considers localhost: to be a trusted
+# domain.
+small TestLoadTextureFromFileSmall except(*iexplore)
diff --git a/o3d/tests/selenium/samples_tests.py b/o3d/tests/selenium/samples_tests.py
new file mode 100644
index 0000000..7d2591e
--- /dev/null
+++ b/o3d/tests/selenium/samples_tests.py
@@ -0,0 +1,528 @@
+#!/usr/bin/python2.4
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+"""Selenium tests on the o3d samples.
+
+Checks for javascript errors and compares screenshots
+taken with reference images.
+
+NOTE: If you manually write a test in python (vs using the generic test)
+The name of the screenshots must match the name of the test not including
+the suffix. In otherwords, if your test is called TestSampleCustomCameraMedium
+then your screenshots need to be named customcamera1, customcamera2 etc.
+
+This is so when it comes time to compare screenshots we can figure out which
+reference images require a corresponding screenshot. In other words if
+TestSampleCustomCameraMedium is run then we know that any reference file
+named customcamera1_reference.png requires that there be a corresponding
+screenshot.
+"""
+
+
+import os
+import time
+import selenium_utilities
+
+
+class SampleTests(selenium_utilities.SeleniumTestCase):
+ """Tests a couple of samples in the o3d samples directory."""
+
+ # TODO: Change to correct object class when NPAPI class is exposed.
+ SELENIUM_OBJ_TYPE = "[object HTMLObjectElement]"
+
+ def __init__(self, name, session, browser, test_type=None, sample_path=None,
+ options=None):
+ selenium_utilities.SeleniumTestCase.__init__(
+ self, name, session, browser, test_type, sample_path, options)
+
+ def GenericTest(self):
+ """Generically test a sample.
+
+ Each sample is expected to have a global variable called g_finished
+ which is set to true when it is finished initializing and is in a state
+ ready for a screenshot.
+ """
+ self.RunGenericTest(
+ self.session.browserURL + "/samples/",
+ "(typeof(window.g_finished) != 'undefined') && "
+ "window.g_finished == true;",
+ None)
+
+ def AssertEqualMatrix4(self, matrix1, matrix2):
+ """Compares two 4-by-4 matrices.
+
+ It asserts that their entries are each very close.
+ Args:
+ matrix1: Full DOM path to first matrix in javascript.
+ matrix2: Full DOM path to second matrix in javascript.
+ """
+ # Alias for the selenium session
+ s = self.session
+ for i in range(0, 4):
+ for j in range(0, 4):
+ string1 = "%s[%d][%d]" % (matrix1, i, j)
+ string2 = "%s[%d][%d]" % (matrix2, i, j)
+ a = float(s.get_eval(string1))
+ b = float(s.get_eval(string2))
+ self.assertTrue(abs(a - b) < 0.001)
+
+ def TestSampleRotateModel(self):
+ """Tests rotatemodel.html."""
+
+ # Alias for the selenium session
+ s = self.session
+ s.open(s.browserURL + "/samples/rotatemodel.html")
+
+ # wait for sample to be ready.
+ s.wait_for_condition("(typeof(window.g_finished) != 'undefined') && "
+ "window.g_finished == true;", 50000)
+
+ # Capture screenshot
+ self.assertTrue(selenium_utilities.TakeScreenShot(s, self.browser,
+ "g_client",
+ "rotatemodel1"))
+
+ # Check that local matrix is the identity
+ self.AssertEqualMatrix4("window.g_math.matrix4.identity()",
+ "window.g_sceneRoot.localMatrix")
+
+ # TODO: s.key_press is not functioning on Chrome, so we revert
+ # to button-based testing of the rotation functions on this platform.
+ # Remove this test when chrome keyboard input works in selenium.
+ browser_is_chrome = self.browser == "*googlechrome"
+
+ # Rotate slightly
+ if browser_is_chrome:
+ s.click("A")
+ else:
+ s.key_press("document.getElementById('o3d')", "a")
+
+ # Check that rotation matrix is correct
+ self.AssertEqualMatrix4(
+ "window.g_math.matrix4.rotationZYX([0, -0.05, 0])",
+ "window.g_sceneRoot.localMatrix")
+
+ # Rotate model
+ for unused_i in range(1, 10):
+ if browser_is_chrome:
+ s.click("W")
+ else:
+ s.key_press("document.getElementById('o3d')", "w")
+
+ for unused_i in range(1, 5):
+ if browser_is_chrome:
+ s.click("S")
+ else:
+ s.key_press("document.getElementById('o3d')", "s")
+
+ for unused_i in range(1, 5):
+ if browser_is_chrome:
+ s.click("A")
+ else:
+ s.key_press("document.getElementById('o3d')", "a")
+
+ for unused_i in range(1, 10):
+ if browser_is_chrome:
+ s.click("D")
+ else:
+ s.key_press("document.getElementById('o3d')", "d")
+
+ # Capture screenshot
+ self.assertTrue(selenium_utilities.TakeScreenShot(s, self.browser,
+ "g_client",
+ "rotatemodel2"))
+
+ # Reset view
+ s.click("//input[@value='Reset view']")
+
+ def TestSampleCustomCamera(self):
+ """Tests customcamera.html."""
+
+ # Alias for the selenium session
+ s = self.session
+ s.open(s.browserURL + "/samples/customcamera.html")
+
+ # Allow a limited time for the plugin to initialize.
+ s.wait_for_condition("typeof(window.g_client) != 'undefined';", 10000)
+
+ # Sanity checks.
+ self.assertEqual("Tutorial B4: Cameras and events", s.get_title())
+ self.assertEqual("null", s.get_eval("window.undefined_symbol_xxxyyy"))
+
+ # Try different views of the camera
+ time.sleep(1)
+ s.type("eyeX", "5")
+ time.sleep(1)
+ s.type("eyeY", "5")
+ time.sleep(1)
+ s.type("eyeZ", "5")
+ time.sleep(1)
+ s.click("btnSet")
+
+ time.sleep(1)
+ s.type("eyeX", "2")
+ time.sleep(1)
+ s.type("eyeY", "3")
+ time.sleep(1)
+ s.type("eyeZ", "2")
+ time.sleep(1)
+ s.type("upX", "1")
+ time.sleep(1)
+ s.type("upY", "0")
+ time.sleep(1)
+ s.click("btnSet")
+ time.sleep(2)
+
+ # Capture screenshot
+ self.assertTrue(selenium_utilities.TakeScreenShot(s, self.browser,
+ "g_client",
+ "customcamera1"))
+
+ # Reset view
+ s.click("btnDefault")
+
+ def TestSampleRenderMode(self):
+ """Tests render-mode.html."""
+
+ # Alias for the selenium session
+ s = self.session
+ s.open(s.browserURL + "/samples/render-mode.html")
+
+ # Allow a limited time for the plugin to initialize.
+ s.wait_for_condition("typeof(window.g_client) != 'undefined';", 10000)
+
+ # Sanity checks.
+ self.assertEqual("Render Mode Example.", s.get_title())
+ self.assertEqual("null", s.get_eval("window.undefined_symbol_xxxyyy"))
+ s.wait_for_condition("window.g_client != null", 5000)
+
+ # Wait for it to render a few frames.
+ s.wait_for_condition(
+ "window.g_framesRendered > 10",
+ 5000)
+
+ # stop rendering
+ s.click("//input[@value='ondemand']")
+
+ # reset frame count to 0
+ s.run_script("window.g_framesRendered = 0")
+
+ # wait 1 second
+ time.sleep(1)
+
+ # check that we didn't render much. Note: This is a kind of hacky
+ # check because if something else (the window being covered/unconvered)
+ # causes the window to be refreshed this number is unpredictable. Also,
+ # if the window is covered we don't render so that can effect this test
+ # as well. The hope is since we waiting 1 second it should render 30-60
+ # frames if render mode is not working.
+ self.assertTrue(
+ s.get_eval("window.g_framesRendered < 10"))
+
+ def TestSamplePicking(self):
+ """Tests picking.html."""
+
+ # Alias for the selenium session
+ s = self.session
+ s.open(s.browserURL + "/samples/picking.html")
+
+ # wait for sample to be ready.
+ s.wait_for_condition("(typeof(window.g_finished) != 'undefined') && "
+ "window.g_finished == true;", 50000)
+
+ # Sanity checks.
+ self.assertEqual("O3D Picking Example.", s.get_title())
+
+ pick_info = [{"x": 189, "y": 174, "shape": "pConeShape1"},
+ {"x": 191, "y": 388, "shape": "pTorusShape1"},
+ {"x": 459, "y": 365, "shape": "pPipeShape1"},
+ {"x": 466, "y": 406, "shape": "pCubeShape1"}]
+
+ # if it's not working these will timeout.
+ for record in pick_info:
+ # Selenium can't really click the mouse; it can only tell JavaScript that
+ # the mouse has been clicked, which isn't enough to get an OS-level event
+ # into the plugin. So we go around the plugin and just call the event
+ # handler that it would have called. This means we're testing most of the
+ # code in the sample, but not the actual event path.
+ s.get_eval("window.pick({x:%d,y:%d});" % (record["x"], record["y"]))
+ s.wait_for_condition(
+ "window.g_pickInfoElem.innerHTML == '" + record["shape"] + "'",
+ 10000)
+
+ def TestSampleShader_Test(self):
+ """Tests shader-test.html."""
+
+ # Alias for the selenium session
+ s = self.session
+ s.open(s.browserURL + "/samples/shader-test.html")
+
+ # Allow a limited time for the plugin to initialize.
+ s.wait_for_condition("typeof(window.g_client) != 'undefined';", 20000)
+
+ # Sanity checks.
+ self.assertEqual("Shader Test", s.get_title())
+ self.assertEqual("null", s.get_eval("window.undefined_symbol_xxxyyy"))
+
+ # wait for it to initialize.
+ s.wait_for_condition("(typeof(window.g_finished) != 'undefined') && "
+ "window.g_finished == true;",
+ 20000)
+
+ # if they are animated we need to stop the animation and set the clock
+ # to some time so we get a known state.
+ s.run_script("g_timeMult = 0")
+ s.run_script("g_clock = 27.5")
+
+ # Figure out how many options there are.
+ num_shaders = s.get_eval(
+ "window.document.getElementById('shaderSelect').length")
+
+ # try each shader
+ for shader in range(0, int(num_shaders)):
+ # select shader
+ s.select("//select[@id='shaderSelect']", ("index=%d" % shader))
+ # Take screenshot
+ self.assertTrue(selenium_utilities.TakeScreenShot(
+ s, self.browser, "g_client", ("shader-test%d" % shader)))
+
+ def TestSampleErrorTextureSmall(self):
+ """Tests error-texture.html."""
+
+ # Alias for the selenium session
+ s = self.session
+ s.open(s.browserURL + "/samples/error-texture.html")
+
+ # Allow a limited time for the plugin to initialize.
+ s.wait_for_condition("typeof(window.g_client) != 'undefined';", 30000)
+
+ # Sanity checks.
+ self.assertEqual("Error Texture", s.get_title())
+ self.assertEqual("null", s.get_eval("window.undefined_symbol_xxxyyy"))
+
+ # Take screenshots
+ time.sleep(2) # helps with Vista FF screencapture
+ self.assertTrue(selenium_utilities.TakeScreenShot(s, self.browser,
+ "g_client",
+ "errortexture1"))
+ s.click("//input[@value='User Texture']")
+ s.wait_for_condition("(window.g_errorMsgElement.innerHTML=='-');",
+ 1000)
+ time.sleep(2) # helps with Vista FF screencapture
+ self.assertTrue(selenium_utilities.TakeScreenShot(s, self.browser,
+ "g_client",
+ "errortexture2"))
+ s.click("//input[@value='No Texture']")
+ s.wait_for_condition("(window.g_errorMsgElement.innerHTML=="
+ "'Missing texture for sampler s2d');",
+ 1000)
+ self.assertTrue(selenium_utilities.TakeScreenShot(s, self.browser,
+ "g_client",
+ "errortexture3"))
+ s.click("//input[@value='hide 0']")
+ s.wait_for_condition("(window.g_errorMsgElement.innerHTML=="
+ "'Missing Sampler for ParamSampler texSampler0');",
+ 1000)
+ self.assertTrue(selenium_utilities.TakeScreenShot(s, self.browser,
+ "g_client",
+ "errortexture4"))
+ s.click("//input[@value='hide 1']")
+ s.wait_for_condition("(window.g_errorMsgElement.innerHTML=="
+ "'Missing ParamSampler');",
+ 1000)
+ self.assertTrue(selenium_utilities.TakeScreenShot(s, self.browser,
+ "g_client",
+ "errortexture5"))
+
+ def TestSampleMultipleClientsLarge(self):
+ """Tries to draw a simple example in a large number of clients."""
+
+ # Alias for the selenium session
+ s = self.session
+ s.open(s.browserURL + "/samples/multiple-clients.html")
+
+ # Allow a limited time for the plugin to initialize. We spot-check the
+ # first and last here to make sure the page has basically loaded. Before we
+ # access any plugin data, we'll check each individual client.
+ s.wait_for_condition("typeof(window.g_clients[49]) != 'undefined';", 40000)
+
+ # Sanity checks.
+ self.assertEqual("Multiple Clients", s.get_title())
+ self.assertEqual("null", s.get_eval("window.undefined_symbol_xxxyyy"))
+
+ # Wait until the entire setup is finished.
+ s.wait_for_condition("window.g_setupDone == true;", 5000)
+
+ # Spot-check assets for base o3d setup:
+ for index in range(0, 50, 12):
+ client_string = (
+ "window.document.getElementById('o3d%d').client" % index)
+
+ # Make sure this client is ready for us.
+ s.wait_for_condition(client_string + " != null;", 1000)
+ s.wait_for_condition(client_string + ".root != null;", 1000)
+
+ # There are 1 draw elements:
+ self.assertEqual(
+ "1",
+ s.get_eval(client_string +
+ ".getObjectsByClassName('o3d.DrawElement').length"))
+
+ # There are 8 nodes in the render graph.
+ # 1 root render node
+ # 1 viewport
+ # 1 clear buffer
+ # 1 tree traversal
+ # 2 draw passes
+ # 2 StateSets
+ self.assertEqual(
+ "8",
+ s.get_eval(client_string + ".renderGraphRoot."
+ "getRenderNodesInTree().length"))
+
+ def TestSamplePingPongLarge(self):
+ """Validates the start-up logic of the ping-pong sample."""
+
+ # Alias for the selenium session
+ s = self.session
+ s.open(s.browserURL + "/samples/pingpong/o3dPingPong.html")
+
+ # Sanity checks.
+ self.assertEqual("o3dPingPong", s.get_title())
+ self.assertEqual("null", s.get_eval("window.undefined_symbol_xxxyyy"))
+
+ # Start the game
+ s.click("clientBanner")
+
+ # Allow a limited time for the plugin to initialize.
+ s.wait_for_condition("(typeof(window.g_finished) != 'undefined') && "
+ "window.g_finished == true;", 10000)
+
+ def TestSampleHelloCube_TexturesSmall(self):
+ """Validates loading of files from an external source."""
+
+ # Alias for the selenium session
+ s = self.session
+ s.open(s.browserURL + "/samples/hellocube-textures.html")
+
+ # Sanity checks.
+ self.assertEqual(
+ "Hello Square Textures: Getting started with O3D, take 3.",
+ s.get_title())
+ self.assertEqual("null", s.get_eval("window.undefined_symbol_xxxyyy"))
+
+ # Allow a limited time for the plugin to initialize.
+ s.wait_for_condition("(typeof(window.g_finished) != 'undefined') && "
+ "window.g_finished == true;", 10000)
+
+ # if they are animated we need to stop the animation and set the clock
+ # to some time so we get a known state.
+ s.run_script("g_timeMult = 0")
+ s.run_script("g_clock = 27.5")
+
+ # Take screenshot
+ self.assertTrue(selenium_utilities.TakeScreenShot(
+ s,
+ self.browser,
+ "g_client",
+ "hellocube-textures1"))
+
+ def TestLoadTextureFromFileSmall(self):
+ """Checks for improper access to local files."""
+
+ # Alias for the selenium session.
+ s = self.session
+ s.open(s.browserURL + "/samples/hellocube-textures.html")
+
+ # Sanity checks.
+ self.assertEqual(
+ "Hello Square Textures: Getting started with O3D, take 3.",
+ s.get_title())
+ self.assertEqual("null", s.get_eval("window.undefined_symbol_xxxyyy"))
+
+ # Allow a limited time for the plugin to initialize.
+ s.wait_for_condition("(typeof(window.g_finished) != 'undefined') && "
+ "window.g_finished == true;", 10000)
+
+ # Check that loading from a local file fails, when the page is served over
+ # http.
+ existing_image = os.path.abspath(
+ os.path.join(os.path.dirname(__file__),
+ "..",
+ "..",
+ "samples",
+ "assets",
+ "google-square.png"))
+
+ self.assertTrue(os.path.exists(existing_image))
+ s.type("url", "file://%s" % existing_image)
+ s.click("updateButton")
+
+ s.wait_for_condition(
+ "(typeof(window.g_textureLoadDenied) != 'undefined') && "
+ "window.g_textureLoadDenied == true;", 10000)
+
+ def TestSampleRefreshPageLoad_Small(self):
+ """Tests the behaviour of the plug-in and browser when the page is
+ refreshed before all of the O3D streams have loaded.
+ """
+
+ # Alias for the selenium session
+ s = self.session
+ s.open(s.browserURL + "/samples/archive-textures.html")
+
+ # Allow a limited time for the plugin to initialize.
+ s.wait_for_condition("typeof(window.g_client) != 'undefined';", 10000)
+
+ # Instruct the page to continually download content.
+ s.run_script("window.g_repeatDownload = true")
+ s.click("startLoad")
+
+ # Wait for the browser to load the page.
+ s.wait_for_condition(
+ "(typeof(window.g_streamingStarted) != 'undefined')", 10000)
+
+ # Refresh the page, before waiting for all of the textures to be loaded.
+ # This tests that the browser won't hang while processing the
+ # no-longer-needed streams.
+ s.open(s.browserURL + "/samples/archive-textures.html")
+
+ # Allow a limited time for the plugin to initialize.
+ s.wait_for_condition("typeof(window.g_client) != 'undefined';", 10000)
+
+ # Instruct the page to download the tgz file once.
+ s.click("startLoad")
+
+ s.wait_for_condition("(typeof(window.g_finished) != 'undefined') && "
+ "window.g_finished == true;", 20000)
+
+if __name__ == "__main__":
+ pass
diff --git a/o3d/tests/selenium/selenium_constants.py b/o3d/tests/selenium/selenium_constants.py
new file mode 100644
index 0000000..a2aad74
--- /dev/null
+++ b/o3d/tests/selenium/selenium_constants.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python2.4
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+"""Constants for the selenium scripts.
+
+Variables that are shared across all the selenium scripts.
+"""
+
+
+import os
+import platform
+
+# Default path where screenshots will be stored.
+DEFAULT_SCREENSHOT_PATH = os.path.join("..",
+ "tests",
+ "selenium",
+ "screenshots")
+
+# Path where reference screenshots will be stored.
+# Unfortunately we need separate reference images for certain platforms
+# for certain tests.
+if platform.system() == "Darwin":
+ PLATFORM_SCREENSHOT_DIR = "reference-mac"
+elif platform.system() == "Linux":
+ PLATFORM_SCREENSHOT_DIR = "reference-linux"
+elif platform.system() == "Microsoft" or platform.system() == "Windows":
+ PLATFORM_SCREENSHOT_DIR = "reference-win"
+else:
+ raise Exception, 'Platform %s not supported' % platform.system()
+
+
+
+SELENIUM_BROWSER_SET = ["*iexplore", "*firefox", "*googlechrome"]
+
+# Dimensions to resize window to
+# on the Mac, the window has to be big enough to hold the entire o3d div
+# otherwise the OpenGL context will be clipped to the size of the window
+RESIZE_WIDTH = 1400
+RESIZE_HEIGHT = 1200
diff --git a/o3d/tests/selenium/selenium_utilities.py b/o3d/tests/selenium/selenium_utilities.py
new file mode 100644
index 0000000..84444e1
--- /dev/null
+++ b/o3d/tests/selenium/selenium_utilities.py
@@ -0,0 +1,287 @@
+#!/usr/bin/python2.4
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+"""Utility scripts for selenium.
+
+A collection of utility scripts for selenium test cases to use.
+"""
+
+
+import os
+import re
+import time
+import unittest
+import gflags
+import selenium_constants
+
+
+FLAGS = gflags.FLAGS
+SUFFIXES = ["small", "medium", "large"]
+
+
+def IsValidTestType(test_type):
+ """Returns True if test_type is a "small", "medium" or "large"."""
+ return test_type.lower() in SUFFIXES
+
+
+def IsValidSuffix(name):
+ """Returns True if name ends in a valid test type."""
+ name = name.lower()
+ for suffix in SUFFIXES:
+ if name.endswith(suffix):
+ return True
+ return False
+
+
+def StripTestTypeSuffix(name):
+ """Removes the suffix from name if it is a valid test type."""
+ name_lower = name.lower()
+ for suffix in SUFFIXES:
+ if name_lower.endswith(suffix):
+ return name[:-len(suffix)]
+ return name
+
+
+def GetArgument(string):
+ """Returns the value inside the first set of parentheses in a string.
+
+ Args:
+ string: String in the format "identifier(args)"
+
+ Returns:
+ args from string passed in. None if there were no parentheses.
+ """
+ match = re.match("\w+\(([^)]+)\)", string)
+ if match:
+ return match.group(1)
+ return None
+
+
+def TakeScreenShot(session, browser, client, filename):
+ """Takes a screenshot of the o3d display buffer.
+
+ This function is the preferred way to capture an image of the plugin.
+
+ Uses gflags:
+ If gflags.FLAGS.screenshots is False then screen shots will not be taken.
+ gflags.FLAGS.screenshotsdir must be set to the path to save screenshots in.
+
+ Args:
+ session: Selenium session.
+ browser: Name of the browser running the test.
+ client: String that in javascript will return the o3d client.
+ filename: Name of screenshot.
+ Returns:
+ success: True on success, False on failure.
+ """
+ # If screenshots enabled
+ if gflags.FLAGS.screenshots:
+ full_path = os.path.join(os.getcwd(),
+ FLAGS.screenshotsdir,
+ filename)
+ return TakeScreenShotAtPath(session,
+ browser,
+ client,
+ full_path)
+ else:
+ # Screenshots not enabled, return true (success).
+ return True
+
+
+def TakeScreenShotAtPath(session,
+ browser,
+ client,
+ filename):
+ """Takes a screenshot of the o3d display buffer.
+
+ This should be used by tests that need to specify exactly where to save the
+ image or don't want to use gflags.
+
+ Args:
+ session: Selenium session.
+ browser: Name of the browser running the test.
+ client: String that in javascript will return the o3d client.
+ filename: Full path to screenshot to be saved.
+
+ Returns:
+ success: True on success, False on failure.
+ """
+ session.window_focus()
+
+ # Resize window, and client area if needed.
+ resize_script = ["window.resizeTo(%d, %d)" %
+ (selenium_constants.RESIZE_WIDTH,
+ selenium_constants.RESIZE_HEIGHT)]
+ width_specification = session.get_eval(
+ "window.document.getElementById('o3d').style.width")
+ need_client_resize = width_specification.find("%") >= 0
+ if need_client_resize:
+ resize_script += [
+ "window.document.getElementById('o3d').style.width = '800px';",
+ "window.document.getElementById('o3d').style.height = '600px';"]
+ session.run_script("\n".join(resize_script))
+ if need_client_resize:
+ session.wait_for_condition(
+ "window.%s.width == 800 && window.%s.height == 600" % (client, client),
+ 20000)
+
+ # Execute screenshot capture code
+
+ # Replace all backslashes with forward slashes so it is parsed correctly
+ # by Javascript
+ full_path = filename.replace("\\", "/")
+
+ # Attempt to take a screenshot of the display buffer
+ eval_string = ("%s.saveScreen('%s')" % (client, full_path))
+
+ # Set Post render call back to take screenshot
+ script = ["window.g_selenium_post_render = false;",
+ "window.g_selenium_save_screen_result = false;",
+ "var frameCount = 0;",
+ "%s.setPostRenderCallback(function() {" % client,
+ " ++frameCount;",
+ " if (frameCount >= 3) {",
+ " %s.clearPostRenderCallback();" % client,
+ " window.g_selenium_save_screen_result = %s;" % eval_string,
+ " window.g_selenium_post_render = true;",
+ " } else {",
+ " %s.render()" % client,
+ " }",
+ "})",
+ "%s.render()" % client]
+ session.run_script("\n".join(script))
+ # Wait for screenshot to be taken.
+ session.wait_for_condition("window.g_selenium_post_render", 20000)
+
+ # Get result
+ success = session.get_eval("window.g_selenium_save_screen_result")
+
+ if success == u"true":
+ print "Saved screenshot %s." % full_path
+ return True
+
+ return False
+
+
+class SeleniumTestCase(unittest.TestCase):
+ """Wrapper for TestCase for selenium."""
+
+ def __init__(self, name, session, browser, test_type=None, sample_path=None,
+ options=None):
+ """Constructor for SampleTests.
+
+ Args:
+ name: Name of unit test.
+ session: Selenium session.
+ browser: Name of browser.
+ test_type: Type of test ("small", "medium", "large")
+ sample_path: Path to test.
+ options: list of option strings.
+ """
+
+ unittest.TestCase.__init__(self, name)
+ self.session = session
+ self.browser = browser
+ self.test_type = test_type
+ self.sample_path = sample_path
+ self.options = options
+
+ def shortDescription(self):
+ """override unittest.TestCase shortDescription for our own descriptions."""
+ if self.sample_path:
+ return "Testing: " + self.sample_path + ".html"
+ else:
+ return unittest.TestCase.shortDescription(self)
+
+ def RunGenericTest(self, base_path, ready_condition, assertion):
+ """Runs a generic test.
+
+ Args:
+ base_path: path for sample.
+ ready_condition: condition to check in javascript to know sample is ready.
+ assertion: javascript to check equals "true"
+
+ Assumes self.sample_path is a path to the html page to load and that
+ samples.options is an array of option strings.
+
+ If the sample is animated, it is expected to have a global variable
+ called g_timeMult that can be set to 0 to stop the animation. All of its
+ animation must be based on a global variable called g_clock, such that
+ setting g_clock to the same value will always produce the same image.
+
+ Finally, each sample is expected to have a global variable called
+ g_client which is the o3d client object for that sample. This is
+ used to take a screenshot.
+ """
+ screenshots = []
+ timeout = 10000
+ client = "g_client"
+
+ self.assertTrue(self.test_type in ["small", "medium", "large"])
+
+ # parse options
+ for option in self.options:
+ if option.startswith("screenshot"):
+ clock = GetArgument(option)
+ if clock is None:
+ clock = "27.5"
+ screenshots.append(clock)
+ elif option.startswith("timeout"):
+ timeout = GetArgument(option)
+ self.assertTrue(not timeout is None)
+ elif option.startswith("client"):
+ client = GetArgument(option)
+ self.assertTrue(not client is None)
+
+ url = base_path + self.sample_path + ".html"
+
+ # load the sample.
+ self.session.open(url)
+
+ # wait for it to initialize.
+ self.session.wait_for_condition(ready_condition, timeout)
+
+ if assertion:
+ self.assertEqual("true", self.session.get_eval(assertion))
+
+ # take a screenshot.
+ screenshot_id = 1
+ for clock in screenshots:
+ # if they are animated we need to stop the animation and set the clock
+ # to some time so we get a known state.
+ self.session.run_script("g_timeMult = 0")
+ self.session.run_script("g_clock = " + clock)
+
+ # take a screenshot.
+ screenshot = self.sample_path.replace("/", "_") + str(screenshot_id)
+ self.assertTrue(TakeScreenShot(self.session, self.browser,
+ client, screenshot))
+ screenshot_id += 1
diff --git a/o3d/tests/selenium/tests/assets/archive.o3dtgz b/o3d/tests/selenium/tests/assets/archive.o3dtgz
new file mode 100644
index 0000000..c482299
--- /dev/null
+++ b/o3d/tests/selenium/tests/assets/archive.o3dtgz
Binary files differ
diff --git a/o3d/tests/selenium/tests/base-test.html b/o3d/tests/selenium/tests/base-test.html
new file mode 100644
index 0000000..1683983
--- /dev/null
+++ b/o3d/tests/selenium/tests/base-test.html
@@ -0,0 +1,81 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Base Test
+</title>
+</head>
+<body>
+<h1>Base Test</h1>
+This tests base.js.
+<br/>
+
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 32px; height: 32px;"></div>
+<!-- End of O3D plugin -->
+
+<script type="text/javascript"
+ src="../../../samples/o3djs/base.js"></script>
+<script type="text/javascript">
+
+o3djs.require('o3djs.test');
+o3djs.require('o3djs.util');
+
+g_suite = {};
+
+g_suite.testIsArrayWorksInBrowserForV8Arrays = function() {
+ g_test.assertTrue(o3djs.base.isArray(g_plugin.eval('[1, 2, 3]')));
+};
+
+g_suite.testIsArrayWorksForBrowser = function() {
+ g_test.assertFalse(o3djs.base.isArray(undefined));
+ g_test.assertFalse(o3djs.base.isArray(7));
+ g_test.assertTrue(o3djs.base.isArray([1, 2, 3]));
+};
+
+function initStep2(clientElements) {
+ g_test = o3djs.test;
+ g_plugin = clientElements[0];
+ g_testResult = g_test.runTests(g_suite);
+};
+
+window.onload = function() {
+ o3djs.util.makeClients(initStep2);
+};
+
+</script>
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/culling-zsort-test.html b/o3d/tests/selenium/tests/culling-zsort-test.html
new file mode 100644
index 0000000..097b30d
--- /dev/null
+++ b/o3d/tests/selenium/tests/culling-zsort-test.html
@@ -0,0 +1,459 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Culling and ZSorting Test.
+
+Make sure things off screen get culled.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Culling and ZSorting Test.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="../../../samples/o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload= unload;
+
+// global variables
+var g_timeMult = 1.0;
+var g_framesRendered = 0;
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfos = [];
+var g_pack;
+var g_o3d_width; // width of our client area
+var g_o3d_height; // height of our client area
+var g_clock = 0;
+var g_totalTransformsElement;
+var g_transformsProcessedElement;
+var g_transformsCulledElement;
+var g_totalDrawElementsElement;
+var g_drawElementsProcessedElement;
+var g_drawElementsCulledElement;
+var g_drawElementsRenderedElement;
+var g_primitivesRenderedElement;
+var GROUPS_ACROSS = 4;
+var UNITS_ACROSS_GROUP = 3;
+var TOTAL_ACROSS = GROUPS_ACROSS * UNITS_ACROSS_GROUP;
+var HALF_WIDTH = TOTAL_ACROSS * 0.5;
+var UNIT_SPACING = 100;
+
+/**
+ * Returns the path of where the file is located
+ * with the trailing slash
+ */
+function getCurrentPath() {
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+ return path.substring(0, index + 1);
+}
+
+function createInstances(pack, shape) {
+ // now make a grid of transforms and put a teapot instance on each one
+ for (var g = 0; g < GROUPS_ACROSS; g++) {
+ for (var h = 0; h < GROUPS_ACROSS; h++) {
+ for (var i = 0; i < GROUPS_ACROSS; i++) {
+ var groupTransform = pack.createObject('Transform');
+ groupTransform.parent = g_client.root;
+ groupTransform.cull = true;
+ var boundingBox = g_o3d.BoundingBox([0, 0, 0],
+ [0, 0, 0]);
+ groupTransform.localMatrix = g_math.matrix4.translation([
+ (g * UNITS_ACROSS_GROUP - HALF_WIDTH) * UNIT_SPACING,
+ (h * UNITS_ACROSS_GROUP - HALF_WIDTH) * UNIT_SPACING,
+ (i * UNITS_ACROSS_GROUP - HALF_WIDTH) * UNIT_SPACING]);
+ for (var x = 0; x < UNITS_ACROSS_GROUP; x++) {
+ for (var y = 0; y < UNITS_ACROSS_GROUP; y++) {
+ for (var z = 0; z < UNITS_ACROSS_GROUP; z++) {
+ var transform = pack.createObject('Transform');
+ transform.parent = groupTransform;
+ transform.cull = true;
+ transform.addShape(shape);
+ var elements = shape.elements;
+ var box = elements[0].boundingBox;
+ for (var ee = 1; ee < elements.length; ee++) {
+ box = box.add(elements[ee].boundingBox);
+ }
+ transform.boundingBox = box;
+ transform.localMatrix = g_math.matrix4.translation([
+ (x - UNITS_ACROSS_GROUP * 0.5) * UNIT_SPACING,
+ (y - UNITS_ACROSS_GROUP * 0.5) * UNIT_SPACING,
+ (z - UNITS_ACROSS_GROUP * 0.5) * UNIT_SPACING]);
+ transform.createParam('colorMult', 'ParamFloat4').value = [
+ (g * UNITS_ACROSS_GROUP + x) * (1 / TOTAL_ACROSS),
+ (h * UNITS_ACROSS_GROUP + y) * (1 / TOTAL_ACROSS),
+ (i * UNITS_ACROSS_GROUP + z) * (1 / TOTAL_ACROSS),
+ 1];
+ var box = transform.boundingBox.mul(transform.localMatrix);
+ boundingBox = boundingBox.add(box);
+ }
+ }
+ }
+ groupTransform.boundingBox = boundingBox;
+ }
+ }
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and creates one shape.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3d = clientElements[0];
+ g_o3d = o3d.o3d;
+ g_math = o3djs.math;
+ g_client = o3d.client;
+
+ g_totalTransformsElement =
+ document.getElementById('totalTransforms');
+ g_transformsProcessedElement =
+ document.getElementById('transformsProcessed');
+ g_transformsCulledElement =
+ document.getElementById('transformsCulled');
+ g_totalDrawElementsElement =
+ document.getElementById('totalDrawElements');
+ g_drawElementsProcessedElement =
+ document.getElementById('drawElementsProcessed');
+ g_drawElementsCulledElement =
+ document.getElementById('drawElementsCulled');
+ g_drawElementsRenderedElement =
+ document.getElementById('drawElementsRendered');
+ g_primitivesRenderedElement =
+ document.getElementById('primitivesRendered');
+
+ // Get the width and height of our client area. We will need this to create
+ // a projection matrix.
+ g_o3d_width = o3d.clientWidth;
+ g_o3d_height = o3d.clientHeight;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ for (var yy = 0; yy < 2; yy++) {
+ for (var xx = 0; xx < 2; xx++) {
+ var viewInfo;
+ var ii = yy * 2 + xx;
+ if (xx == 0 && yy == 0) {
+ viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot,
+ [0.5, 0.5, 0.5, 1],
+ 0,
+ [0, 0, 0.5, 0.5]);
+
+ } else {
+ viewInfo = o3djs.rendergraph.createExtraView(
+ g_viewInfos[0],
+ [xx * 0.5, yy * 0.5, 0.5, 0.5], // top-right
+ [0.5, 0.2 + xx * 0.5, 0.7 - yy * 0.5, 1.0]);
+ }
+ g_viewInfos[ii] = viewInfo;
+
+ // Create our projection matrix, with a vertical field of view of 45
+ // degrees a near clipping plane of 0.1 and far clipping plane of 100.
+ viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ 45 * 3.14159 / 180,
+ g_o3d_width / g_o3d_height,
+ 0.1,
+ 10000);
+ }
+ }
+
+ /* Load the effect for our sphere from our file.
+ Effects, stored in a hidden textarea for simplicity, contain the
+ functions that define the vertex and pixel shaders used by our shape.
+
+ Here, we calculate phong illumination in our vertex shader and pass the
+ resultant color to our pixel shader, which does nothing except output its
+ given (input) color.
+ */
+ var defaultEffect = g_pack.createObject('Effect');
+ defaultEffect.loadFromFXString(document.getElementById('shader').value);
+
+ // Create a Material for the effect.
+ var material = g_pack.createObject('Material');
+
+ // Apply our effect to this material.
+ material.effect = defaultEffect;
+
+ // Set the material's drawList
+ material.drawList = g_viewInfos[0].zOrderedDrawList;
+
+ // create params the effect needs on the material.
+ defaultEffect.createUniformParameters(material);
+
+ // Light position
+ var light_pos_param = material.getParam('light_pos');
+ light_pos_param.value = [1000, 1000, 0];
+
+ // Phong components of the light source
+ var light_ambient_param = material.getParam('light_ambient');
+ var light_diffuse_param = material.getParam('light_diffuse');
+ var light_specular_param = material.getParam('light_specular');
+
+ // White ambient light
+ light_ambient_param.value = [0.1, 0.1, 0.1, 1];
+ // Reddish diffuse light
+ light_diffuse_param.value = [1, 1, 1, 1];
+ // White specular light
+ light_specular_param.value = [0.5, 0.5, 0.5, 1];
+
+ // Shininess of the material (for specular lighting)
+ var shininess_param = material.getParam('shininess');
+ shininess_param.value = 5.0;
+
+ // Position of the camera.
+ // (should be the same as the 'eye' position given below)
+ var camera_pos_param = material.getParam('camera_pos');
+ // Camera is at (0, 0, 3).
+ camera_pos_param.value = [0, 0, 3];
+
+ // Create 2 spheres.
+ var shape1 = o3djs.primitives.createSphere(
+ g_pack,
+ material,
+ 20,
+ 10,
+ 12,
+ g_math.matrix4.translation([-25, 0, 0]));
+
+ var shape2 = o3djs.primitives.createSphere(
+ g_pack,
+ material,
+ 20,
+ 10,
+ 12,
+ g_math.matrix4.translation([25, 0, 0]));
+
+ // Create a shape and move the 2 sphere primitives to the same shape.
+ var shape = g_pack.createObject('Shape');
+ shape1.elements[0].owner = shape;
+ shape2.elements[0].owner = shape;
+ g_pack.removeObject(shape1);
+ g_pack.removeObject(shape2);
+ var elements = shape.elements;
+ elements[0].cull = true;
+ elements[1].cull = true;
+
+ createInstances(g_pack, shape);
+
+ g_totalDrawElementsElement.innerHTML = g_client.getObjectsByClassName(
+ 'o3d.DrawElement').length;
+ g_totalTransformsElement.innerHTML = g_client.getObjectsByClassName(
+ 'o3d.Transform').length;
+
+ // Setup an onrender callback for animation.
+ g_client.setRenderCallback(onrender);
+}
+
+// spin the camera.
+function onrender(renderEvent) {
+ g_framesRendered++;
+ // Get the number of seconds since the last render.
+ var elapsedTime = renderEvent.elapsedTime;
+ g_clock += elapsedTime * g_timeMult;
+
+ for (var vv = 0; vv < 4; vv++) {
+ var clock = g_clock * (vv * 0.1 + 1);
+ var x = Math.sin(clock * 0.1) * 400;
+ var z = Math.cos(clock * 0.1) * 400;
+ var y = Math.sin(clock * 0.2) * 400;
+
+ g_viewInfos[vv].drawContext.view = g_math.matrix4.lookAt(
+ [x, y, z],
+ [0, 0, 0],
+ [0, 1, 0]);
+ }
+
+ g_transformsProcessedElement.innerHTML = renderEvent.transformsProcessed;
+ g_transformsCulledElement.innerHTML = renderEvent.transformsCulled;
+ g_drawElementsProcessedElement.innerHTML = renderEvent.drawElementsProcessed;
+ g_drawElementsCulledElement.innerHTML = renderEvent.drawElementsCulled;
+ g_drawElementsRenderedElement.innerHTML = renderEvent.drawElementsRendered;
+ g_primitivesRenderedElement.innerHTML = renderEvent.primitivesRendered;
+}
+
+/**
+ * Remove any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body>
+<h1>Culling and ZSort Test</h1>
+Objects off screen should get culled and nothing in the front should zbuffer out
+stuff in the back.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+<table>
+<tr><td>Total Transforms:</td><td><span id="totalTransforms">-</span></td></tr>
+<tr><td>Transforms Processed:</td><td><span id="transformsProcessed">-</span></td></tr>
+<tr><td>Transforms Culled:</td><td><span id="transformsCulled">-</span></td></tr>
+<tr><td>Total DrawElements:</td><td><span id="totalDrawElements">-</span></td></tr>
+<tr><td>DrawElements Processed:</td><td><span id="drawElementsProcessed">-</span></td></tr>
+<tr><td>DrawElements Culled:</td><td><span id="drawElementsCulled">-</span></td></tr>
+<tr><td>DrawElements Rendered:</td><td><span id="drawElementsRendered">-</span></td></tr>
+<tr><td>Primitives Rendered:</td><td><span id="primitivesRendered">-</span></td></tr>
+</table>
+<!-- Don't render the textarea -->
+<div style="display:none">
+<textarea id="shader" name="fx" cols="80" rows="20">
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WorldViewProjection;
+float4x4 worldInverseTranspose : WorldInverseTranspose;
+float4x4 world : World;
+
+// positions of the light and camera
+float3 light_pos;
+float3 camera_pos;
+
+// lighting components of the light source
+float4 light_ambient;
+float4 light_diffuse;
+float4 light_specular;
+
+// shininess of the material. (for specular lighting)
+float shininess;
+
+float4 colorMult;
+
+// input parameters for our vertex shader
+struct a2v {
+ float4 pos : POSITION;
+ float3 normal : NORMAL;
+ float4 col : COLOR;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct v2f {
+ float4 pos : POSITION;
+ float4 pos2 : TEXCOORD0;
+ float3 norm : TEXCOORD1;
+ float3 light : TEXCOORD2;
+ float4 col : COLOR;
+};
+
+/**
+ * vsMain - our vertex shader
+ *
+ * @param IN.pos Position vector of vertex
+ * @param IN.normal Normal of vertex
+ * @param IN.col Color of vertex
+ */
+v2f vsMain(a2v IN) {
+ /**
+ * We use the standard phong illumination equation here.
+ * We restrict (clamp) the dot products so that we
+ * don't get any negative values.
+ * All vectors are normalized for proper calculations.
+ *
+ * The output color is the summation of the
+ * ambient, diffuse, and specular contributions.
+ *
+ * Note that we have to transform each vertex and normal
+ * by the view projection matrix first.
+ */
+ v2f OUT;
+
+ OUT.pos = mul(IN.pos, worldViewProjection);
+ OUT.pos2 = OUT.pos;
+ OUT.norm = mul(float4(IN.normal, 0), worldInverseTranspose).xyz;
+ OUT.light = light_pos - mul(IN.pos, world).xyz;
+ OUT.col = IN.col;
+ return OUT;
+}
+/**
+ * psMain - pixel shader
+ *
+ * @param IN.pos Position vector of vertex
+ * @param IN.col Color of vertex
+ */
+float4 psMain(v2f IN): COLOR {
+ float3 light = normalize(IN.light);
+ float3 normal = normalize(IN.norm);
+ float3 litR = normalize(2 * dot(light, normal) * normal - light);
+ float3 v = normalize(mul(float4(camera_pos, 1),
+ worldViewProjection).xyz - IN.pos2.xyz);
+
+ // use lit function to calculate phong shading
+ float4 phong_coeff = lit(dot(normal, light), dot(litR, v), shininess);
+ float4 ambient = light_ambient * phong_coeff.x * IN.col;
+ float4 diffuse = light_diffuse * phong_coeff.y * IN.col;
+ float4 specular = light_specular * phong_coeff.z * IN.col;
+
+ return float4(((ambient + diffuse) * colorMult + specular).xyz, 0.5);
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vsMain
+// #o3d PixelShaderEntryPoint psMain
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+</div>
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/drawshapes.html b/o3d/tests/selenium/tests/drawshapes.html
new file mode 100644
index 0000000..c25901b
--- /dev/null
+++ b/o3d/tests/selenium/tests/drawshapes.html
@@ -0,0 +1,395 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Shape Stress Test
+
+This test generates a number of shapes that display on an empty o3d canvas.
+We assign each shape a pixel shader that colors the shape via a parameter.
+The color is set randomly.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Drawshape stress test for O3D
+</title>
+<script type="text/javascript" src="../../../samples/o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+
+// Events
+// Run the init() function once the page has finished loading.
+// and unload() when the page is unloaded.
+window.onload = init;
+window.onunload= unload;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_packEffect;
+var g_packShapes;
+var g_material;
+var g_parentTransform;
+
+// Variables for computing FPS
+var FPS_FRAMES_FOR_SAMPLE = 10; // number of frames to average for FPS.
+var g_totalTime = 0.0; // total time spent for last N frames.
+var g_timeTable = []; // elapsed time for last N frames.
+var g_timeTableCursor = 0;
+
+/**
+ * Returns a random double between -1 and 1.
+ */
+function getRandom() {
+ return (Math.random() - 0.5) * 2;
+}
+
+/**
+ * Creates a shape with the given effect
+ * @param {Material} material material which our shape will use.
+ * @param {Shape::PrimitiveType} primitiveType the shape's primitive type
+ */
+function createShape(material, primitiveType) {
+ // First, create a 'shape' and 'primitive' to store the our geometry.
+ var myShape = g_packShapes.createObject('Shape');
+ var myPrimitive = g_packShapes.createObject('Primitive');
+ var myBank = g_packShapes.createObject('StreamBank');
+ myPrimitive.owner = myShape;
+ myPrimitive.streamBank = myBank;
+
+ // Apply our material to this primitive.
+ myPrimitive.material = material;
+
+ myPrimitive.primitiveType = primitiveType;
+ myPrimitive.numberPrimitives = 1; // 1 primitive.
+
+ if (primitiveType == g_o3d.Primitive.TRIANGLELIST) {
+ // Triangle list
+ myPrimitive.numberVertices = 3; // 3 vertices in total
+
+ // Create a javascript array that gives the coordinates of each of the
+ // 3 corners (vertices) of the triangle.
+
+ var vertsArray = [
+ // Triangle
+ getRandom(), getRandom(), 0,
+ getRandom(), getRandom(), 0,
+ getRandom(), getRandom(), 0
+ ];
+
+ // Check if vertices are oriented clockwise.
+ // If not, we re-orient them.
+ // This is done by checking the determinant.
+ var determinant =
+ vertsArray[0] * vertsArray[4] +
+ vertsArray[3] * vertsArray[7] +
+ vertsArray[6] * vertsArray[1] -
+ vertsArray[3] * vertsArray[1] -
+ vertsArray[6] * vertsArray[4] -
+ vertsArray[0] * vertsArray[7];
+
+ // vertices oriented wrongly.
+ if (determinant < 0) {
+ // swap vertices to reorient.
+ var tmpVertX = vertsArray[0];
+ var tmpVertY = vertsArray[1];
+ vertsArray[0] = vertsArray[3];
+ vertsArray[1] = vertsArray[4];
+ vertsArray[3] = tmpVertX;
+ vertsArray[4] = tmpVertY;
+ }
+
+ // The index array defines the order of the vertices we want to draw.
+ // Here we tell the renderer that
+ // Triangle 1 is created from vertices 0, 1 and 2.
+ // Triangle 2 is created from vertices 3, 4 and 5.
+ var indicesArray = [
+ 0, 1, 2
+ ];
+ } else if (primitiveType == g_o3d.Primitive.LINELIST) {
+ // Line list
+ myPrimitive.numberVertices = 2; // 2 vertices in total
+
+ var vertsArray = [
+ // Line
+ getRandom(), getRandom(), 0,
+ getRandom(), getRandom(), 0
+ ];
+
+ var indicesArray = [
+ 0, 1
+ ];
+ }
+
+ // These next few lines take our javascript arrays and load them into some
+ // 'buffers' where the 3D hardware can find them. We have to do this because
+ // the 3D hardware can't 'see' javascript data until it is in a buffer.
+ var vertexBuffer = g_packShapes.createObject('VertexBuffer');
+ var field = vertexBuffer.createField('FloatField', 3);
+ vertexBuffer.set(vertsArray);
+
+ var indexBuffer = g_packShapes.createObject('IndexBuffer');
+ indexBuffer.set(indicesArray);
+
+ // Now we associate these 'buffers' of data with our primitive so that it can
+ // access the data.
+ myBank.setVertexStream(g_o3d.Stream.POSITION,
+ 0,
+ field,
+ 0);
+ myPrimitive.indexBuffer = indexBuffer;
+
+ // Create a shapeColor parameter directly on the primitive so we don't have
+ // to create multiple materials.
+ var shapeColorParam = myPrimitive.createParam('shapeColor', 'ParamFloat4');
+ shapeColorParam.value = [Math.random(), Math.random(), Math.random(), 1];
+
+ // We now attach our shape to the transform.
+ g_parentTransform.addShape(myShape);
+
+ // Lastly we generate the draw elements.
+ g_parentTransform.createDrawElements(g_packShapes, null);
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the effect.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ var o3d = clientElements[0];
+ g_o3d = o3d.o3d;
+ g_math = o3djs.math;
+ g_client = o3d.client;
+
+ var pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Create packs to manage our resources/assets
+ g_packEffect = g_client.createPack();
+ g_packShapes = g_client.createPack();
+
+ // Load the effect for the shapes from the textarea and associate it
+ // with the pack.
+ var effect = g_packEffect.createObject('Effect');
+ var fxString = document.getElementById('fx').value;
+ effect.loadFromFXString(fxString);
+
+ // Create a material.
+ g_material = g_packEffect.createObject('Material');
+
+ // Set the material's drawList
+ g_material.drawList = g_viewInfo.performanceDrawList;
+
+ // Apply our effect to this material.
+ g_material.effect = effect;
+ effect.createUniformParameters(g_material);
+
+ // Create a parent transform
+ g_parentTransform =
+ g_packShapes.createObject('Transform');
+ g_parentTransform.parent = g_client.root;
+
+ // Initialize the FPS elapsed time history table.
+ for (var tt = 0; tt < FPS_FRAMES_FOR_SAMPLE; tt++ ) {
+ g_timeTable[tt] = 0.0;
+ }
+
+ // Set our render callback to display stats.
+ g_client.setRenderCallback(onrender);
+}
+
+/**
+ * Creates several shapes in the scene and renders them.
+ */
+function createShapes(numShapes, primitiveType) {
+ // Creates the shapes using the default effect.
+ for (var i = 0; i < numShapes; i++)
+ createShape(g_material, primitiveType);
+}
+
+/**
+ * Destroys all the shapes that we have created.
+ */
+function destroyShapes() {
+ // Remove references
+ g_parentTransform.parent = null;
+ g_packShapes.destroy();
+
+ // Re-create parent transform and pack
+ g_packShapes = g_client.createPack();
+ g_parentTransform =
+ g_packShapes.createObject('Transform');
+ g_parentTransform.parent = g_client.root;
+}
+
+/**
+ * Frees unreferenced objects by destroying the packs on page unload.
+ */
+function unload() {
+ g_packEffect.destroy();
+ g_packShapes.destroy();
+}
+
+// Updates statistics.
+// This function executes on each frame.
+function onrender(renderEvent) {
+ if (window.g_client) {
+ document.getElementById('clientObjectCount').innerHTML =
+ g_client.getObjectsByClassName('o3d.ObjectBase').length;
+ // Update FPS
+ // Get the number of seconds since the last render.
+ var elapsedTime = renderEvent.elapsedTime;
+
+ // Keep the total time for the last N frames.
+ g_totalTime += elapsedTime - g_timeTable[g_timeTableCursor];
+
+ // Save off the elapsed time for this frame so we can subtract it later.
+ g_timeTable[g_timeTableCursor] = elapsedTime;
+
+ // Wrap the place to store the next time sample.
+ g_timeTableCursor++;
+ if (g_timeTableCursor == FPS_FRAMES_FOR_SAMPLE) {
+ g_timeTableCursor = 0;
+ }
+
+ // Print the average frame rate for the last N frames as well as the
+ // instantaneous frame rate.
+ document.getElementById('fps').innerHTML = "" +
+ Math.floor((1.0 / (g_totalTime / FPS_FRAMES_FOR_SAMPLE)) + 0.5) +
+ " : " + Math.floor(1.0 / elapsedTime + 0.5);
+
+
+ }
+}
+
+</script>
+</head>
+<body>
+<h1>Draw shapes stress test</h1>
+This is a shape drawing stress test for o3d.
+<br/>
+Enter in the number of shapes to create and click the
+appropriate shape type button to render the shapes.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+<br/>
+<form action="#" method="get" name="defaultForm">
+ Render <input type="text" name="numShapes" value="3">
+ <input type="button" name="btnTri" value="Triangles"
+ onClick="createShapes(
+ document.defaultForm.numShapes.value,
+ g_o3d.Primitive.TRIANGLELIST)">
+ <input type="button" name="btnLines" value="Lines"
+ onClick="createShapes(
+ document.defaultForm.numShapes.value,
+ g_o3d.Primitive.LINELIST)">
+ <br/>
+ <input type="button" name="btnClear" value="Clear"
+ onClick="destroyShapes()">
+</form>
+<table border="0" summary="O3D statistics">
+ <tr>
+ <td style="font-weight:bold">Client Object Count:</td>
+ <td id="clientObjectCount">0</td>
+ </tr>
+ <tr>
+ <td style="font-weight:bold; color:red">FPS:</td>
+ <td id="fps">0</td>
+ </tr>
+</table>
+<!-- Don't render the textarea -->
+<div style="display:none">
+<!-- Start of effect -->
+<textarea id="fx" name="fx" cols="80" rows="20">
+ // This is the color of the triangle.
+ // We bind this to a javascript variable shapeColorParam
+ // to modify its value dynamically
+ float4 shapeColor : FLOAT4;
+
+ // input parameters for our vertex shader
+ struct a2v {
+ float4 pos : POSITION;
+ };
+
+ // input parameters for our pixel shader
+ struct v2f {
+ float4 pos : POSITION;
+ };
+
+ /**
+ * Our vertex shader does nothing but returns the position of the vertex.
+ * (which is unused eventually)
+ */
+ v2f vsMain(a2v IN) {
+ v2f OUT;
+ OUT.pos = IN.pos;
+ return OUT;
+ }
+
+ /* Our pixel shader returns the color which is assigned dynamically.
+ */
+ float4 psMain(v2f IN): COLOR {
+ return shapeColor;
+ }
+
+ // Here we tell our effect file *which* functions are
+ // our vertex and pixel shaders.
+
+ // #o3d VertexShaderEntryPoint vsMain
+ // #o3d PixelShaderEntryPoint psMain
+ // #o3d MatrixLoadOrder RowMajor
+</textarea>
+<!-- End of effect -->
+</div>
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/effect-import-test.html b/o3d/tests/selenium/tests/effect-import-test.html
new file mode 100644
index 0000000..6a2515f
--- /dev/null
+++ b/o3d/tests/selenium/tests/effect-import-test.html
@@ -0,0 +1,154 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Test that when importing a file Effects get shared.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Effect Import Test
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="../../../samples/o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.scene');
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_o3d;
+var g_client;
+var g_viewInfo;
+var g_testResult;
+var g_pack;
+
+/**
+ * Loads a model into the transform graph and generates its corresponding entry
+ * in the render graph when it is done loading.
+ * @param {!o3d.Pack} pack Pack to load model into.
+ * @param {string} fileName filename of the model.
+ * @param {!o3d.Transform} parent parent node in the transform graph to
+ * which to load the model into.
+ */
+function loadModel(pack, fileName, parent) {
+ // Get our full path to the model
+ var modelPath = o3djs.util.getCurrentURI() + fileName;
+
+ // Load the file given the full path, and call the callback function
+ // when its done loading.
+ o3djs.scene.loadScene(g_client, pack, parent, modelPath, callback);
+
+ /**
+ * Our callback is called once the model has been loaded into memory
+ * from the web or locally.
+ * @param {!o3d.Pack} pack The pack that was passed in above.
+ * @param {!o3d.Transform} parent The parent that was passed in above.
+ * @param {*} exception null if loading succeeded.
+ */
+ function callback(pack, parent, exception) {
+ if (exception) {
+ alert('Could not load: ' + fileName + '\n' + exception);
+ return;
+ }
+ // Get a cameraInfo (an object with a view and projection matrix)
+ // using our javascript library function
+ var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(
+ parent,
+ g_client.width,
+ g_client.height);
+
+ // Copy the camera info from the file the draw context.
+ g_viewInfo.drawContext.view = cameraInfo.view;
+ g_viewInfo.drawContext.projection = cameraInfo.projection;
+
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, g_viewInfo);
+
+ var effects = g_client.getObjectsByClassName('o3d.Effect');
+ g_testResult = effects.length == 1;
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the model into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3d = clientElements[0];
+ g_o3d = o3d.o3d;
+ g_client = o3d.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Load the model into the transform graph.
+ loadModel(g_pack,
+ '../../../samples/assets/seven_shapes.o3dtgz',
+ g_client.root);
+}
+</script>
+</head>
+<body>
+<h1>Effect Import Test</h1>
+<br/>
+Test that when importing a file Effects get shared.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/event-test.html b/o3d/tests/selenium/tests/event-test.html
new file mode 100644
index 0000000..57c160c
--- /dev/null
+++ b/o3d/tests/selenium/tests/event-test.html
@@ -0,0 +1,284 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Event Test
+</title>
+</head>
+<body>
+<h1>Event Test</h1>
+This tests the event-handler functions in the event.js library.
+<br/>
+<script type="text/javascript"
+ src="../../../samples/o3djs/base.js"></script>
+<script type="text/javascript">
+
+o3djs.require('o3djs.event');
+o3djs.require('o3djs.test');
+
+var g_event = {};
+
+function FakeClient() {
+};
+
+FakeClient.prototype.getCallbacks = function() {
+ this.callbacks = this.callbacks || [];
+ return this.callbacks;
+};
+
+FakeClient.prototype.setEventCallback = function(type, callback) {
+ this.getCallbacks()[type] = callback;
+};
+
+FakeClient.prototype.clearEventCallback = function(type) {
+ this.getCallbacks()[type] = null;
+};
+
+FakeClient.prototype.handleEvent = function(type, event) {
+ var callback = this.getCallbacks()[type];
+ if (callback) {
+ g_test.assertTrue(typeof(callback) == 'function');
+ callback(event);
+ }
+};
+
+function FakePlugin() {
+ this.client = new FakeClient();
+}
+
+FakePlugin.prototype.handleEvent = function(type, event) {
+ this.client.handleEvent(type, event);
+};
+
+var g_a;
+function incA(event) {
+ g_test.assertTrue(event != null);
+ g_test.assertTrue(event == g_event);
+ ++g_a;
+}
+
+var incAAsEventListener = {
+ handleEvent: incA
+};
+
+var g_b;
+function incB(event) {
+ g_test.assertTrue(event != null);
+ g_test.assertTrue(event == g_event);
+ ++g_b;
+}
+
+var g_suite = {};
+
+g_suite.testSimpleHandler = function() {
+ var fakePlugin = new FakePlugin;
+ g_a = 0;
+ g_b = 0;
+ o3djs.event.addEventListener(fakePlugin, 'click', incA);
+ g_test.assertEquals(0, g_a);
+ g_test.assertEquals(0, g_b);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(1, g_a);
+ g_test.assertEquals(0, g_b);
+}
+
+g_suite.testMultipleHandlers = function() {
+ var fakePlugin = new FakePlugin;
+ g_a = 0;
+ g_b = 0;
+ o3djs.event.addEventListener(fakePlugin, 'click', incA);
+ o3djs.event.addEventListener(fakePlugin, 'click', incB);
+ g_test.assertEquals(0, g_a);
+ g_test.assertEquals(0, g_b);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(1, g_a);
+ g_test.assertEquals(1, g_b);
+}
+
+g_suite.testAddingHandlerIsIdempotent = function() {
+ var fakePlugin = new FakePlugin;
+ g_a = 0;
+ g_b = 0;
+ o3djs.event.addEventListener(fakePlugin, 'click', incA);
+ o3djs.event.addEventListener(fakePlugin, 'click', incA);
+ g_test.assertEquals(0, g_a);
+ g_test.assertEquals(0, g_b);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(1, g_a);
+ g_test.assertEquals(0, g_b);
+}
+
+g_suite.testAddingAndRemovingHandlers = function() {
+ var fakePlugin = new FakePlugin;
+ g_a = 0;
+ g_b = 0;
+ o3djs.event.addEventListener(fakePlugin, 'click', incA);
+ o3djs.event.addEventListener(fakePlugin, 'click', incB);
+ g_test.assertEquals(0, g_a);
+ g_test.assertEquals(0, g_b);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(1, g_a);
+ g_test.assertEquals(1, g_b);
+ // Check that it's repeatable.
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(2, g_a);
+ g_test.assertEquals(2, g_b);
+ o3djs.event.removeEventListener(fakePlugin, 'click', incA);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(2, g_a);
+ g_test.assertEquals(3, g_b);
+ o3djs.event.removeEventListener(fakePlugin, 'click', incB);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(2, g_a);
+ g_test.assertEquals(3, g_b);
+ // Check that removing in the opposite order works too.
+ var fakePlugin = new FakePlugin;
+ g_a = 0;
+ g_b = 0;
+ o3djs.event.addEventListener(fakePlugin, 'click', incA);
+ o3djs.event.addEventListener(fakePlugin, 'click', incB);
+ g_test.assertEquals(0, g_a);
+ g_test.assertEquals(0, g_b);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(1, g_a);
+ g_test.assertEquals(1, g_b);
+ // Check that it's repeatable.
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(2, g_a);
+ g_test.assertEquals(2, g_b);
+ o3djs.event.removeEventListener(fakePlugin, 'click', incB);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(3, g_a);
+ g_test.assertEquals(2, g_b);
+ o3djs.event.removeEventListener(fakePlugin, 'click', incA);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(3, g_a);
+ g_test.assertEquals(2, g_b);
+}
+
+g_suite.testRemovingHandlerIsIdempotent = function() {
+ var fakePlugin = new FakePlugin;
+ g_a = 0;
+ g_b = 0;
+ o3djs.event.addEventListener(fakePlugin, 'click', incA);
+ o3djs.event.addEventListener(fakePlugin, 'click', incA);
+ g_test.assertEquals(0, g_a);
+ g_test.assertEquals(0, g_b);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(1, g_a);
+ g_test.assertEquals(0, g_b);
+ o3djs.event.removeEventListener(fakePlugin, 'click', incA);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(1, g_a);
+ g_test.assertEquals(0, g_b);
+ o3djs.event.removeEventListener(fakePlugin, 'click', incA);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(1, g_a);
+ g_test.assertEquals(0, g_b);
+}
+
+g_suite.testEventListenerIsLikeFunction = function() {
+ var fakePlugin = new FakePlugin;
+ g_a = 0;
+ g_b = 0;
+ o3djs.event.addEventListener(fakePlugin, 'click', incA);
+ o3djs.event.addEventListener(fakePlugin, 'click', incAAsEventListener);
+ g_test.assertEquals(0, g_a);
+ g_test.assertEquals(0, g_b);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(2, g_a);
+ g_test.assertEquals(0, g_b);
+ o3djs.event.removeEventListener(fakePlugin, 'click', incA);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(3, g_a);
+ g_test.assertEquals(0, g_b);
+ o3djs.event.removeEventListener(fakePlugin, 'click', incA);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(4, g_a);
+ g_test.assertEquals(0, g_b);
+ o3djs.event.removeEventListener(fakePlugin, 'click',
+ incAAsEventListener);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(4, g_a);
+ g_test.assertEquals(0, g_b);
+}
+
+g_suite.testMultipleDifferentEvents = function() {
+ var fakePlugin = new FakePlugin;
+ g_a = 0;
+ g_b = 0;
+ o3djs.event.addEventListener(fakePlugin, 'click', incA);
+ o3djs.event.addEventListener(fakePlugin, 'mousedown', incB);
+ g_test.assertEquals(0, g_a);
+ g_test.assertEquals(0, g_b);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(1, g_a);
+ g_test.assertEquals(0, g_b);
+ fakePlugin.handleEvent('mousedown', g_event);
+ g_test.assertEquals(1, g_a);
+ g_test.assertEquals(1, g_b);
+ o3djs.event.removeEventListener(fakePlugin, 'mousedown', incA);
+ o3djs.event.removeEventListener(fakePlugin, 'click', incB);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(2, g_a);
+ g_test.assertEquals(1, g_b);
+ fakePlugin.handleEvent('mousedown', g_event);
+ g_test.assertEquals(2, g_a);
+ g_test.assertEquals(2, g_b);
+ o3djs.event.removeEventListener(fakePlugin, 'click', incA);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(2, g_a);
+ g_test.assertEquals(2, g_b);
+ fakePlugin.handleEvent('mousedown', g_event);
+ g_test.assertEquals(2, g_a);
+ g_test.assertEquals(3, g_b);
+ o3djs.event.removeEventListener(fakePlugin, 'mousedown', incB);
+ fakePlugin.handleEvent('click', g_event);
+ g_test.assertEquals(2, g_a);
+ g_test.assertEquals(3, g_b);
+ fakePlugin.handleEvent('mousedown', g_event);
+ g_test.assertEquals(2, g_a);
+ g_test.assertEquals(3, g_b);
+}
+
+window.onload = function() {
+ window.g_test = o3djs.test;
+ window.g_testResult = g_test.runTests(g_suite);
+};
+
+</script>
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/features-test.html b/o3d/tests/selenium/tests/features-test.html
new file mode 100644
index 0000000..22cc131
--- /dev/null
+++ b/o3d/tests/selenium/tests/features-test.html
@@ -0,0 +1,157 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Features Test
+
+Make sure when features are not requested you can not use them and when
+they are requested you can use them.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Features Test.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="../../../samples/o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.rendergraph');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_client;
+var g_pack;
+var g_testResult;
+
+/**
+ * Checks the client.lastError is empty or not after calling a function.
+ * @param {!o3d.Client} client The client to use.
+ * @param {boolean} expectError True if we expect an error.
+ * @param {!function(): void} func Function to call.
+ * @return {boolean} True if error matched expectations.
+ */
+function checkForError(client, expectError, func) {
+ client.clearLastError();
+ func();
+ var haveError = client.lastError.length > 0;
+ return haveError == expectError;
+}
+
+/**
+ * Creates a buffer with the requested number of elements.
+ * @param {!o3d.Pack} pack Pack to create buffer in.
+ * @param {number} numElements Number of elements to create.
+ */
+function createBuffer(pack, numElements) {
+ var buffer = pack.createObject('VertexBuffer');
+ var field = buffer.createField('FloatField', 1);
+ buffer.allocateElements(numElements);
+}
+
+/**
+ * Checks that features error or are available based on if they were requested.
+ *
+ * @param {!Element} o3dElement The o3dElement for the O3D object.
+ * @param {boolean} expectError True if we expect errors when using the
+ * features.
+ * @return {boolean} True if checks succeeded.
+ */
+function checkFeatures(o3dElement, expectError) {
+ var o3d = o3dElement.o3d;
+ var client = o3dElement.client;
+ client.clearErrorCallback();
+ var pack = client.createPack();
+ // A list of functions that each create something that require a feature.
+ var functions = [
+ function() { pack.createTexture2D(32, 32, o3d.Texture.R32F, 1, false); },
+ function() { pack.createTexture2D(32, 32, o3d.Texture.ABGR16F, 1, false); },
+ function() { pack.createTexture2D(32, 32, o3d.Texture.ABGR32F, 1, false); },
+ function() { createBuffer(pack, 65535); },
+ ];
+
+ for (var ii = 0; ii < functions.length; ++ii) {
+ if (!checkForError(client, expectError, functions[ii])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Creates the client areas.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2); // no features requested.
+}
+
+/**
+ * Initializes O3D and loads the model into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ g_testResult = checkFeatures(clientElements[0], true) &&
+ checkFeatures(clientElements[1], false);
+
+ document.getElementById('result').innerHTML = g_testResult ?
+ '<font color="green">success</font>' :
+ '<font color="red">failure</font>';
+}
+</script>
+</head>
+<body>
+<h1>Features Test</h1>
+Make sure when features are not requested you can not use them and when
+they are requested you can use them.
+<br/>
+<br/>
+<div>Result: <span id="result"></span></div>
+<!-- Start of O3D plugin -->
+<div id="o3d1" style="width: 100px; height: 100px;"></div>
+<br/>
+<div id="o3d2" o3d_features="FloatingPointTextures,LargeGeometry" style="width: 100px; height: 100px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/init-status-test.html b/o3d/tests/selenium/tests/init-status-test.html
new file mode 100644
index 0000000..08b9eb2
--- /dev/null
+++ b/o3d/tests/selenium/tests/init-status-test.html
@@ -0,0 +1,117 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Init Status Test
+
+Check we get the expected result if the plugin does not start.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Init Status Test.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="../../../samples/o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.rendergraph');
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_client;
+var g_pack;
+var g_testResult;
+
+/**
+ * Creates the client areas.
+ */
+function init() {
+ setTimeout(failed, 2000);
+ o3djs.util.makeClients(initStep2, 'InitStatus=3');
+}
+
+/**
+ * Initializes O3D.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ g_testResult = false;
+}
+
+/**
+ * Called if makeClients failed.
+ */
+function failed () {
+ var tag = 'div';
+ var id = '^o3d';
+ var msg =
+ 'We are terribly sorry but it appears your graphics card is not ' +
+ 'able to run o3d. We are working on a solution.';
+ var elements = document.getElementsByTagName(tag);
+ for (var ee = 0; ee < elements.length; ++ee) {
+ var element = elements[ee];
+ if (element.id && element.id.match(id)) {
+ var innerHTML = element.innerHTML;
+ g_testResult = (innerHTML.indexOf(msg) >= 0);
+ document.getElementById('result').innerHTML = g_testResult ?
+ '<font color="green">success</font>' :
+ '<font color="red">failure</font>';
+ return;
+ }
+ }
+}
+
+</script>
+</head>
+<body>
+<h1>Init Status Test</h1>
+
+Check we get the expected result if the plugin does not start.
+<br/>
+<br/>
+<div>Result: <span id="result"></span></div>
+<br/>
+Note: You should see "We are terribly sorry..." below.
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<br/>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/math-test.html b/o3d/tests/selenium/tests/math-test.html
new file mode 100644
index 0000000..981c673
--- /dev/null
+++ b/o3d/tests/selenium/tests/math-test.html
@@ -0,0 +1,861 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Math Test
+</title>
+</head>
+<body>
+<h1>Math Test</h1>
+This tests the math utility library.
+<br/>
+<script type="text/javascript"
+ src="../../../samples/o3djs/base.js"></script>
+<script type="text/javascript">
+
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.test');
+
+var isChrome = navigator.userAgent.indexOf('Chrome') != -1;
+var isSafari = !isChrome && navigator.userAgent.indexOf('Safari') != -1;
+var isFirefox = navigator.userAgent.indexOf('Firefox') != -1;
+var isInternetExplorer = navigator.userAgent.indexOf('Internet Explorer' != -1);
+
+var g_suite = {};
+
+g_suite.testDegToRad = function() {
+ g_test.assertEquals(Math.PI, g_math.degToRad(180));
+};
+
+g_suite.testRadToDeg = function() {
+ g_test.assertEquals(180, g_math.radToDeg(Math.PI));
+};
+
+g_suite.testScalarLerp = function() {
+ g_test.assertEquals(3, g_math.lerpScalar(2, 6, .25));
+};
+
+g_suite.testModClamp = function() {
+ g_test.assertEquals(0.5, g_math.modClamp(2.5, 2.0));
+ g_test.assertEquals(0.5, g_math.modClamp(6.5, 2.0));
+ g_test.assertEquals(3.0, g_math.modClamp(3.0, 2.0, 2.5));
+ g_test.assertEquals(1.5, g_math.modClamp(-0.5, 2.0));
+ g_test.assertEquals(0.5, g_math.modClamp(-3.5, 2.0));
+}
+
+g_suite.testLerpCircular = function() {
+ g_test.assertEquals(30, g_math.lerpCircular(0, 100, 0.3, 300));
+ g_test.assertEquals(270, g_math.lerpCircular(0, 200, 0.3, 300));
+ g_test.assertEquals(0, g_math.lerpCircular(0, 300, 1, 300));
+}
+
+g_suite.testLerpRadian = function() {
+ g_test.assertEquals(0.3, g_math.lerpRadian(0, 1, 0.3));
+ g_test.assertEquals(Math.PI * 2 - 0.3,
+ g_math.lerpRadian(0, Math.PI * 2 - 1, 0.3));
+ g_test.assertEquals(0, g_math.lerpRadian(0, Math.PI * 2, 1));
+}
+
+g_suite.testVectorNegative = function() {
+ g_test.assertArrayEquals([0, 6, 7], g_math.negativeVector([0, -6, -7]));
+};
+
+g_suite.testVectorCopy = function() {
+ var v = [0, 6, 7];
+ var u = g_math.copyVector(v);
+ g_test.assertArrayEquals(u, v);
+ g_test.assertFalse(u == v);
+};
+
+g_suite.testVectorAdd = function() {
+ g_test.assertArrayEquals([0, 6, 7], g_math.addVector([1, 2, 3], [-1, 4, 4]));
+ g_test.assertArrayEquals([1, -1, -1], g_math.addVector([0, 0, 0],
+ [1, -1, -1]));
+};
+
+g_suite.testVectorSub = function() {
+ g_test.assertArrayEquals([2, -2, -1, 4],
+ g_math.subVector([1, 2, 3, 4], [-1, 4, 4, 0]));
+};
+
+g_suite.testVectorLerp = function() {
+ g_test.assertArrayEquals([0.5, 2.5, 3.25, 3],
+ g_math.lerpVector([1, 2, 3, 4], [-1, 4, 4, 0], 0.25));
+}
+
+g_suite.testVectorMul = function() {
+ g_test.assertArrayEquals([2, 4, 6], g_math.mulScalarVector(2, [1, 2, 3]));
+ g_test.assertArrayEquals([2, 4, 6], g_math.mulVectorScalar([1, 2, 3], 2));
+}
+
+g_suite.testVectorDiv = function() {
+ g_test.assertArrayEquals([0.5, 1, 1.5, 2],
+ g_math.divVectorScalar([1, 2, 3, 4], 2));
+};
+
+g_suite.testDotProduct = function() {
+ g_test.assertEquals(19, g_math.dot([-1, 4, 4], [1, 2, 3]));
+};
+
+g_suite.testLength = function() {
+ g_test.assertEquals(13, g_math.length([5, 12]));
+};
+
+g_suite.testLengthSquared = function() {
+ g_test.assertEquals(169, g_math.lengthSquared([5, 12]));
+};
+
+g_suite.testDistance = function() {
+ g_test.assertEquals(13, g_math.distance([1, 1], [6, 13]));
+};
+
+g_suite.testDistanceSquared = function() {
+ g_test.assertEquals(169, g_math.distanceSquared([1, 1], [6, 13]));
+};
+
+g_suite.testNormalize = function() {
+ g_test.assertEquals(1, g_math.length(g_math.normalize([1, 2, 3])));
+ g_test.assertEquals(0,
+ g_math.length(g_math.cross(g_math.normalize([1, 2, 3]), [1, 2, 3])));
+};
+
+g_suite.testCrossProduct = function() {
+ var u = [-1, 4, 4];
+ var v = [1, 2, 3];
+ g_test.assertArrayEquals([4, 7, -6], g_math.cross(u, v));
+ g_test.assertEquals(0, g_math.dot(g_math.cross(u, v), u));
+ g_test.assertEquals(0, g_math.dot(g_math.cross(u, v), v));
+};
+
+
+var A = [[1, 2, 3], [2, -5, 1], [-1, 5, 3], [1, 4, 2]];
+var B = [[0, 1, -1], [1, -1, 3], [2, 1, 2], [0, -1, 4]];
+
+g_suite.testMatrixNegative = function() {
+ g_test.assertArrayEquals(
+ [[-1, -2, -3], [-2, 5, -1], [1, -5, -3], [-1, -4, -2]],
+ g_math.negativeMatrix(A));
+};
+
+g_suite.testMatrixCopy = function() {
+ var M = g_math.copyMatrix(A);
+ g_test.assertArrayEquals(A, M);
+ g_test.assertFalse(A == M);
+};
+
+g_suite.testMatrixAdd = function() {
+ g_test.assertArrayEquals([[1, 3, 2], [3, -6, 4], [1, 6, 5], [1, 3, 6]],
+ g_math.addMatrix(A, B));
+};
+
+g_suite.testMatrixSub = function() {
+ g_test.assertArrayEquals([[1, 1, 4], [1, -4, -2], [-3, 4, 1], [1, 5, -2]],
+ g_math.subMatrix(A, B));
+};
+
+g_suite.testMatrixLerp = function() {
+ g_test.assertArrayEquals(
+ [[0.75, 1.75, 2],
+ [1.75, -4, 1.5],
+ [-0.25, 4, 2.75],
+ [0.75, 2.75, 2.5]], g_math.lerpMatrix(A, B, .25));
+};
+
+g_suite.testMulScalarMatrix = function() {
+ g_test.assertArrayEquals([[2, 4, 6], [4, -10, 2], [-2, 10, 6], [2, 8, 4]],
+ g_math.mulMatrixScalar(A, 2));
+ g_test.assertArrayEquals([[2, 4, 6], [4, -10, 2], [-2, 10, 6], [2, 8, 4]],
+ g_math.mulScalarMatrix(2, A));
+};
+
+g_suite.testMatrixDiv = function() {
+ g_test.assertArrayEquals(
+ [[0.5, 1, 1.5], [1, -2.5, 0.5], [-0.5, 2.5, 1.5], [0.5, 2, 1]],
+ g_math.divMatrixScalar(A, 2));
+};
+
+// The square root of the sum of squares of the entries of a matrix.
+function frobeniusNorm(a) {
+ return Math.sqrt(g_math.trace(g_math.mulMatrixMatrix(
+ g_math.transpose(a), a)));
+};
+
+// The sum of squares of entries of the difference between two matrices.
+function matrixDiff(a,b) {
+ return frobeniusNorm(g_math.subMatrix(a,b));
+};
+
+// Multiplies a matrix by its inverse and checks that it is not
+// too different from the identity. (With floating-point error, exact
+// equality is too much to ask).
+function inverseTest(a) {
+ g_test.assertTrue(matrixDiff(g_math.mulMatrixMatrix(a, g_math.inverse(a)),
+ g_math.identity(a.length)) < 0.001);
+ g_test.assertTrue(matrixDiff(g_math.mulMatrixMatrix(g_math.inverse(a), a),
+ g_math.identity(a.length)) < 0.001);
+};
+
+g_suite.testTranspose = function() {
+ g_test.assertArrayEquals([[1, 2, -1, 1], [2, -5, 5, 4], [3, 1, 3, 2]],
+ g_math.transpose([[1, 2, 3], [2, -5, 1], [-1, 5, 3], [1, 4, 2]]));
+};
+
+g_suite.testTrace = function() {
+ g_test.assertEquals(6, g_math.trace([
+ [1, 2, 3, 4],
+ [1, -2, 1, 1],
+ [1, 1, 0, -1],
+ [1,2,-1,7]]));
+};
+
+g_suite.testIdentity = function() {
+ g_test.assertArrayEquals([[1, 0], [0, 1]], g_math.identity(2));
+ g_test.assertArrayEquals([[1, 0, 0], [0, 1, 0], [0, 0, 1]],
+ g_math.identity(3));
+};
+
+g_suite.testDeterminant2 = function() {
+ g_test.assertEquals(-2, g_math.det([[1, 2], [3, 4]]));
+};
+
+g_suite.testDeterminant3 = function() {
+ g_test.assertEquals(10, g_math.det([[1, 2, 3], [1, -2, 1], [1, 1, 0]]));
+};
+
+g_suite.testDeterminant4 = function() {
+ g_test.assertEquals(28, g_math.det([
+ [1, 2, 3, 4],
+ [1, -2, 1, 1],
+ [1, 1, 0, -1],
+ [1, 2, -1, 0]]));
+};
+
+g_suite.testDeterminant5 = function() {
+ g_test.assertEquals(-120.00,
+ g_math.det([
+ [2, 5,-1, 2, 1],
+ [1, 2,-1, 3, 2],
+ [1, 3, 4, 7, -3],
+ [1, -1, 1, 2, -4],
+ [0, 1, 0, 1, 0]]));
+};
+
+g_suite.testMatrixInversion2 = function() {
+ inverseTest([[1, 2], [3, 4]]);
+};
+
+g_suite.testMatrixInversion3 = function() {
+ inverseTest([[1, 2, 3], [1, -2, 1], [1, 1, 0]]);
+};
+
+g_suite.testMatrixInversion4 = function() {
+ inverseTest([[1, 2, 3, 4], [1, -2, 1, 1],
+ [1, 1, 0, -1], [1, 2, -1, 0]]);
+};
+
+g_suite.testMatrixInversion5 = function() {
+ inverseTest([
+ [2, 5, -1, 2, 1],
+ [1, 2, -1, 3, 2],
+ [1, 3, 4, 7, -3],
+ [1, -1, 1, 2, -4],
+ [0, 1, 0, 1, 0]]);
+};
+
+var S = [[1, 2, 3, 0], [2, -5, 1, 1]];
+var T = [[1, 2, 3], [2, -5, 1], [-1, 5, 3], [1, 4, 2]];
+var U = [[2, 5, -1, 2], [1, 2, -1, 3], [1, 3, 4, 7]];
+
+g_suite.testRowMajorMulMatrixMatrix = function() {
+ g_test.assertArrayEquals(
+ [[7, 18, 9, 29], [0, 3, 7, -4], [6, 14, 8, 34], [8, 19, 3, 28]],
+ g_math.rowMajor.mulMatrixMatrix(T, U));
+ g_test.assertArrayEquals([[2, 7, 14], [-8, 38, 6]],
+ g_math.rowMajor.mulMatrixMatrix(S, T));
+};
+
+g_suite.testColumnMajorMulMatrixMatrix = function() {
+ g_test.assertArrayEquals([[7, 18, 9, 29],
+ [0, 3, 7, -4],
+ [6, 14, 8, 34],
+ [8, 19, 3, 28]],
+ g_math.columnMajor.mulMatrixMatrix(U, T));
+ g_test.assertArrayEquals([[2, 7, 14], [-8, 38, 6]],
+ g_math.columnMajor.mulMatrixMatrix(T, S));
+};
+
+g_suite.testRowMajorMulMatrixMatrix2 = function() {
+ g_test.assertArrayEquals([[5, -2], [9, -2]],
+ g_math.rowMajor.mulMatrixMatrix2([[1, 2], [3, 4]], [[-1, 2], [3,-2]]));
+};
+
+g_suite.testColumnMajorMulMatrixMatrix2 = function() {
+ g_test.assertArrayEquals([[5, 6], [-3, -2]],
+ g_math.columnMajor.mulMatrixMatrix2([[1, 2], [3, 4]], [[-1, 2], [3,-2]]));
+};
+
+g_suite.testRowMajorMulMatrixMatrix3 = function() {
+ g_test.assertArrayEquals([[6, 0, 10], [14, 8, 42], [-4, 12, 20]],
+ g_math.rowMajor.mulMatrixMatrix3(
+ [[1, 2, 1], [3, 4, 5], [-1, -3, 4]],
+ [[-1, 2, 4], [3, -2, 0], [1, 2, 6]]));
+};
+
+g_suite.testColumnMajorMulMatrixMatrix3 = function() {
+ g_test.assertArrayEquals([[1, -6, 25], [-3, -2, -7], [1, -8, 35]],
+ g_math.columnMajor.mulMatrixMatrix3(
+ [[1, 2, 1], [3, 4, 5], [-1, -3, 4]],
+ [[-1, 2, 4], [3, -2, 0], [1, 2, 6]]));
+};
+
+g_suite.testRowMajorMatrixMatrix4 = function() {
+ g_test.assertArrayEquals([
+ [8, 5, 9, 7],
+ [12, 3, 43, 33],
+ [0, 22, 18, 1],
+ [14, 10, 14, 9]],
+ g_math.rowMajor.mulMatrixMatrix4(
+ [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]],
+ [[-1, 2, 4, 4], [3, -2, 0, 1], [1, 2, 6, 3], [2, 5, -1, -2]]));
+};
+
+g_suite.testColumnMajorMatrixMatrix4 = function() {
+ g_test.assertArrayEquals([
+ [5, 6, 33, 13],
+ [-2, 1, -5, 7],
+ [4, 1, 41, 17],
+ [16, 21, 19, -9]],
+ g_math.columnMajor.mulMatrixMatrix4(
+ [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]],
+ [[-1, 2, 4, 4], [3, -2, 0, 1], [1, 2, 6, 3], [2, 5, -1, -2]]));
+};
+
+g_suite.testRowMajorMulMatrixVector = function() {
+ g_test.assertArrayEquals([5, 9, 0, 1],
+ g_math.rowMajor.mulMatrixVector(T, [1, -1, 2]));
+};
+
+g_suite.testColumnMajorMulMatrixVector = function() {
+ g_test.assertArrayEquals([-2, 21, 10],
+ g_math.columnMajor.mulMatrixVector(T, [1, -1, 2, 1]));
+};
+
+g_suite.testRowMajorMulVectorMatrix = function() {
+ g_test.assertArrayEquals([-2, 21, 10],
+ g_math.rowMajor.mulVectorMatrix([1, -1, 2, 1], T));
+};
+
+g_suite.testColumnMajorMulVectorMatrix = function() {
+ g_test.assertArrayEquals([5, 9, 0, 1],
+ g_math.columnMajor.mulVectorMatrix([1, -1, 2], T));
+};
+
+g_suite.testMulVectorVector = function() {
+ g_test.assertArrayEquals([-1, 0, 6, -8],
+ g_math.mulVectorVector([1, 2, 3, 4],
+ [-1, 0, 2, -2]));
+};
+
+g_suite.testOrthonormalize = function() {
+ var W = g_math.orthonormalize([[1, 2, -1], [1, -2, 0], [2, 4, 1]]);
+ g_test.assertTrue(matrixDiff(g_math.mulMatrixMatrix(W, g_math.transpose(W)),
+ g_math.identity(3)) < 0.001);
+ g_test.assertTrue(matrixDiff(g_math.mulMatrixMatrix(g_math.transpose(W), W),
+ g_math.identity(3)) < 0.001);
+ g_test.assertTrue(matrixDiff(
+ g_math.orthonormalize([[1.01, -.01, 0], [0, 1.01, .01], [0, -0.1, 1.01]]),
+ g_math.identity(3)) < 0.1);
+};
+
+function dehomogenize(v) {
+ return [v[0] / v[3], v[1] / v[3], v[2] / v[3]];
+};
+
+function homogenize(v) {
+ return [v[0], v[1], v[2], 1];
+};
+
+function push0(v) {
+ return [v[0], v[1], v[2], 0];
+};
+
+function pop0(v) {
+ return [v[0], v[1], v[2]];
+};
+
+g_suite.testPerspective = function() {
+ var angle = 1;
+ var aspect = 16 / 9;
+ var zNear = 3;
+ var zFar = 10;
+
+ var P = g_math.matrix4.perspective(angle, aspect, zNear, zFar);
+
+ // Points on the near clipping plane should get sent to points on the plane
+ // z = 0, and points on the far clipping plane should get sent to points on
+ // the plane z = 1.
+ sendTest(P, [0, 0, -zNear], [0, 0, 0]);
+ sendTest(P, [0, 0, -zFar], [0, 0, 1]);
+
+ // The right edge of the near rectangular face of the frustum should get sent
+ // to the point [1, 0, 0]
+ sendTest(P, [zNear * aspect * Math.tan(angle / 2), 0, -zNear], [1, 0, 0]);
+
+ // The top edge of the near rectangular face of the frustum should get sent
+ // to the point [0, 1, 0]
+ sendTest(P, [0, zNear * Math.tan(angle / 2), -zNear], [0, 1, 0]);
+};
+
+g_suite.testOrthographic = function() {
+ var left = 3;
+ var right = 5;
+ var bottom = 4;
+ var top = 6;
+ var near = 2;
+ var far = 100;
+
+ var P = g_math.matrix4.orthographic(left, right, bottom, top, near, far);
+
+ // Points on the near face of the viewing box should get sent to the plane
+ // z = 0.
+ sendTest(P, [left, bottom, -near], [-1, -1, 0]);
+ sendTest(P, [right, bottom, -near], [1, -1, 0]);
+ sendTest(P, [left, top, -near], [-1, 1, 0]);
+
+ // Points on the far face of the viewing box should get sent to the plane
+ // z = 1.
+ sendTest(P, [left, bottom, -far], [-1, -1, 1]);
+};
+
+g_suite.testLookAt = function() {
+ var eye = [1, 2, 4];
+ var target = [1, -1, 3];
+ var up = [0, 1, 1];
+ var v = [];
+
+ var M = g_math.matrix4.lookAt(eye, target, up);
+
+ var U = g_math.matrix4.getUpper3x3(M);
+ g_test.assertTrue(
+ Math.abs(g_math.det(g_math.matrix4.getUpper3x3(M)) - 1) < 0.0001);
+
+ // M rotation component should be orthogonal.
+ g_test.assertTrue(matrixDiff(g_math.mulMatrixMatrix(U, g_math.transpose(U)),
+ g_math.identity(3)) < 0.001);
+
+ // M should send the eye to the origin.
+ sendTest(M, eye, [0, 0, 0]);
+
+ // M should send (target-eye) to point in the -z direction.
+ v = g_math.mulVectorMatrix(push0(g_math.subVector(target, eye)), M);
+ g_test.assertTrue(Math.abs(v[0]) < 0.0001);
+ g_test.assertTrue(Math.abs(v[1]) < 0.0001);
+ g_test.assertTrue(v[2] < 0);
+
+ // M should send up into the zy plane with positive y.
+ v = g_math.mulMatrixVector(M, push0(up));
+ g_test.assertTrue(Math.abs(v[0]) < 0.0001);
+ g_test.assertTrue(v[1] > 0);
+
+ // Arguments with four entries should be accepted, so all the following should
+ // be equal.
+ var lookats = [
+ g_math.matrix4.lookAt(homogenize(eye), homogenize(target), push0(up)),
+ g_math.matrix4.lookAt(eye, homogenize(target), push0(up)),
+ g_math.matrix4.lookAt(homogenize(eye), target, push0(up)),
+ g_math.matrix4.lookAt(eye, target, push0(up)),
+ g_math.matrix4.lookAt(homogenize(eye), homogenize(target), up),
+ g_math.matrix4.lookAt(eye, homogenize(target), up),
+ g_math.matrix4.lookAt(homogenize(eye), target, up),
+ g_math.matrix4.lookAt(eye, target, up)
+ ];
+
+ for (var i = 0; i < 7; ++i)
+ g_test.assertArrayEquals(lookats[i], lookats[i + 1]);
+};
+
+g_suite.testGetAndSetUpper3x3 = function() {
+ var a = [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]];
+ var b = [[10, 11, 12], [13, 14, 15], [16, 17, 18]];
+ var r = g_math.matrix4.setUpper3x3(a, b);
+
+ g_test.assertTrue(r == a);
+ g_test.assertArrayEquals([[10, 11, 12, 1],
+ [13, 14, 15, -1],
+ [16, 17, 18, 2],
+ [1, 3, 2, 2]], a);
+
+ g_test.assertArrayEquals(g_math.matrix4.getUpper3x3(a), b);
+};
+
+g_suite.testGetAndSetTranslation = function() {
+ var a = [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]];
+ var t = [-4, -5, -6];
+ var r = g_math.matrix4.setTranslation(a, t);
+
+ g_test.assertTrue(r == a);
+ g_test.assertArrayEquals([[1, 2, 1, 1],
+ [3, 4, 5, -1],
+ [-1, -3, 4, 2],
+ [-4, -5, -6, 1]], a);
+ g_test.assertArrayEquals(g_math.matrix4.getTranslation(a), t);
+};
+
+g_suite.testTransformPoint = function() {
+ var m = [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]];
+ var v3 = [1, 2, 3];
+
+ sendTest(m, v3, g_math.matrix4.transformPoint(m, v3));
+};
+
+g_suite.testTransformVector4 = function() {
+ var m = [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]];
+ var v4 = [1, 2, 3, 4];
+ var v3 = dehomogenize(v4);
+
+ sendTest(m, v3, dehomogenize(g_math.matrix4.transformVector4(m, v4)));
+};
+
+g_suite.testTransformDirection = function() {
+ // It is important that the last coordinate of the first three vectors of m be
+ // 0, so that m is parallel-preserving.
+ var m = [[1, 2, 1, 0], [3, 4, 5, 0], [-1, -3, 4, 0], [1, 3, 2, 2]];
+ var v3 = [1, 2, 3];
+
+ directionSendTest(m, v3, g_math.matrix4.transformDirection(m, v3));
+};
+
+g_suite.testTransformNormal = function() {
+ // It is important that the last coordinate of the first three vectors of m be
+ // 0, so that m is parallel-preserving.
+ var m = [[1, 2, 1, 0], [3, 4, 5, 0], [-1, -3, 4, 0], [1, 3, 2, 2]];
+ var v3 = [1, 2, 3];
+
+ // Transform the direction by the inverse transpose and make sure we get the
+ // same answer.
+ var a = g_math.matrix4.transformDirection(
+ g_math.inverse(g_math.transpose(m)), v3);
+ var b = g_math.matrix4.transformNormal(m, v3);
+
+ g_test.assertTrue(g_math.distance(a, b) < 0.001);
+};
+
+g_suite.testMatrix4Identity = function() {
+ g_test.assertArrayEquals([[1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [0, 0, 0, 1]], g_math.matrix4.identity());
+};
+
+g_suite.testRowAndColumn = function() {
+ var a = [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2]];
+
+ g_test.assertArrayEquals([-1, -3, 4, 2], g_math.rowMajor.row(a, 2));
+ g_test.assertArrayEquals([2, 4, -3], g_math.rowMajor.column(a, 1));
+
+ g_test.assertArrayEquals([2, 4, -3], g_math.columnMajor.row(a, 1));
+ g_test.assertArrayEquals([-1, -3, 4, 2], g_math.columnMajor.column(a, 2));
+};
+
+g_suite.testComposition = function() {
+ var b = [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]];
+ var a = g_math.matrix4.identity();
+ var u = [1, 2, 3];
+ var v = g_math.matrix4.transformPoint(a, g_math.matrix4.transformPoint(b, u));
+
+ var r = g_math.matrix4.compose(a, b);
+
+ g_test.assertTrue(r == a);
+ sendTest(a, u, v);
+};
+
+g_suite.testCompose = function() {
+ var m = [[5, 2, -4, 1], [1, 0, 0, -2], [5, 6, 3, 2], [-1, -1, 2, 3]];
+ var b = [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]];
+ var a = g_math.matrix4.composition(b, m);
+ var c = g_math.copyMatrix(b);
+ g_math.matrix4.compose(c, m);
+
+ g_test.assertTrue(matrixDiff(a, c) < 0.001);
+};
+
+g_suite.testTranslation = function() {
+ var v = [1, 2, 3];
+ var a = g_math.matrix4.identity();
+ var r = g_math.matrix4.translate(a, v);
+
+ g_test.assertTrue(r == a);
+ g_test.assertArrayEquals(a, g_math.matrix4.translation(v));
+ g_test.assertArrayEquals(g_math.identity(3), g_math.matrix4.getUpper3x3(a));
+
+ sendTest(a, [0, 0, 0], v);
+};
+
+g_suite.testTranslate = function() {
+ var v = [1, 2, 3];
+ var b = [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]];
+ var a = g_math.matrix4.translation(v);
+ var c = g_math.copyMatrix(b);
+ g_math.matrix4.translate(c, v);
+
+ composeTest(a, b, c);
+};
+
+g_suite.testScaling = function() {
+ var v = [2, 3, 4];
+ var a = g_math.matrix4.identity();
+ var r = g_math.matrix4.scale(a, v);
+
+ g_test.assertTrue(r == a);
+ g_test.assertTrue(matrixDiff(a, g_math.matrix4.scaling(v)) < 0.001);
+
+ sendTest(a, [1, 0, 0], [v[0], 0, 0]);
+ sendTest(a, [0, 1, 0], [0, v[1], 0]);
+ sendTest(a, [0, 0, 1], [0, 0, v[2]]);
+};
+
+g_suite.testScale = function() {
+ var v = [2, 3, 4];
+ var b = [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]];
+ var a = g_math.matrix4.scaling(v);
+ var c = g_math.copyMatrix(b);
+ g_math.matrix4.scale(c, v);
+
+ composeTest(a, b, c);
+};
+
+g_suite.testRotationX = function() {
+ var a = g_math.matrix4.identity();
+ var r = g_math.matrix4.rotateX(a, Math.PI / 2);
+
+ g_test.assertTrue(r == a);
+ g_test.assertTrue(
+ matrixDiff(a, g_math.matrix4.rotationX(Math.PI / 2)) < 0.001);
+
+ sendTest(a, [1, 0, 0], [1, 0, 0]);
+ sendTest(a, [0, 1, 0], [0, 0, 1]);
+ sendTest(a, [0, 0, 1], [0, -1, 0]);
+};
+
+g_suite.testRotateX = function() {
+ var angle = 1;
+ var b = [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]];
+ var a = g_math.matrix4.rotationX(angle);
+ var c = g_math.copyMatrix(b);
+ g_math.matrix4.rotateX(c, angle);
+
+ composeTest(a, b, c);
+};
+
+g_suite.testRotationY = function() {
+ var a = g_math.matrix4.identity();
+ var r = g_math.matrix4.rotateY(a, Math.PI / 2);
+
+ g_test.assertTrue(r == a);
+ g_test.assertTrue(
+ matrixDiff(a, g_math.matrix4.rotationY(Math.PI / 2)) < 0.001);
+
+ sendTest(a, [1, 0, 0], [0, 0, -1]);
+ sendTest(a, [0, 1, 0], [0, 1, 0]);
+ sendTest(a, [0, 0, 1], [1, 0, 0]);
+};
+
+g_suite.testRotateY = function() {
+ var angle = 1;
+ var b = [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]];
+ var a = g_math.matrix4.rotationY(angle);
+ var c = g_math.copyMatrix(b);
+ g_math.matrix4.rotateY(c, angle);
+
+ composeTest(a, b, c);
+};
+
+g_suite.testRotationZ = function() {
+ var a = g_math.matrix4.identity();
+ var r = g_math.matrix4.rotateZ(a, Math.PI / 2);
+
+ g_test.assertTrue(r == a);
+ g_test.assertTrue(
+ matrixDiff(a, g_math.matrix4.rotationZ(Math.PI / 2)) < 0.001);
+
+ sendTest(a, [1, 0, 0], [0, 1, 0]);
+ sendTest(a, [0, 1, 0], [-1, 0, 0]);
+ sendTest(a, [0, 0, 1], [0, 0, 1]);
+};
+
+g_suite.testRotateZ = function() {
+ var angle = 1;
+ var b = [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]];
+ var a = g_math.matrix4.rotationZ(angle);
+ var c = g_math.copyMatrix(b);
+ g_math.matrix4.rotateZ(c, angle);
+
+ composeTest(a, b, c);
+};
+
+g_suite.testRotationZYX = function() {
+ var v = [1, 2, 3];
+ var a = g_math.matrix4.identity();
+ var r = g_math.matrix4.rotateZYX(a, v);
+
+ g_test.assertTrue(r == a);
+ g_test.assertTrue(matrixDiff(a, g_math.matrix4.rotationZYX(v)) < 0.001);
+
+ var b = g_math.matrix4.identity();
+ g_math.matrix4.rotateZ(b, v[2]);
+ g_math.matrix4.rotateY(b, v[1]);
+ g_math.matrix4.rotateX(b, v[0]);
+
+ g_test.assertTrue(matrixDiff(a, b) < 0.001);
+};
+
+g_suite.testRotateZYX = function() {
+ var v = [1, 2, 3];
+ var b = [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]];
+ var a = g_math.matrix4.rotationZYX(v);
+ var c = g_math.copyMatrix(b);
+ g_math.matrix4.rotateZYX(c, v);
+
+ composeTest(a, b, c);
+};
+
+g_suite.testAxisRotation = function() {
+ var angle = 1;
+
+ g_test.assertTrue(matrixDiff(g_math.matrix4.axisRotation([1, 0, 0], angle),
+ g_math.matrix4.rotationX(angle)) < 0.001);
+ g_test.assertTrue(matrixDiff(g_math.matrix4.axisRotation([0, 1, 0], angle),
+ g_math.matrix4.rotationY(angle)) < 0.001);
+ g_test.assertTrue(matrixDiff(g_math.matrix4.axisRotation([0, 0, 1], angle),
+ g_math.matrix4.rotationZ(angle)) < 0.001);
+};
+
+g_suite.testAxisRotation111 = function() {
+ var v = [1, 1, 1];
+ var a = g_math.matrix4.identity();
+ var r = g_math.matrix4.axisRotate(a, v, 2 * Math.PI / 3);
+
+ g_test.assertTrue(r == a);
+ g_test.assertTrue(
+ matrixDiff(a, g_math.matrix4.axisRotation(v, 2 * Math.PI / 3)) < 0.001);
+
+ sendTest(a, [1, 0, 0], [0, 1, 0]);
+ sendTest(a, [0, 1, 0], [0, 0, 1]);
+ sendTest(a, [0, 0, 1], [1, 0, 0]);
+};
+
+g_suite.testAxisRotate = function() {
+ var v = [1, 2, 3];
+ var angle = 1;
+ var b = [[1, 2, 1, 1], [3, 4, 5, -1], [-1, -3, 4, 2], [1, 3, 2, 2]];
+ var a = g_math.matrix4.axisRotation(v, angle);
+ var c = g_math.copyMatrix(b);
+ g_math.matrix4.axisRotate(c, v, angle);
+
+ composeTest(a, b, c);
+};
+
+// Tests that the 4-by-4 homogenous matrix M sends the 3-vector v to the
+// 3-vector vM.
+function sendTestVM(M, v, vM) {
+ g_test.assertTrue(matrixDiff([dehomogenize(
+ g_math.mulVectorMatrix(homogenize(v), M))],
+ [vM]) < 0.001);
+}
+
+// Tests that the 4-by-4 homogenous matrix M sends the 3-vector v to the
+// 3-vector Mv.
+function sendTestMV(M, v, Mv) {
+ g_test.assertTrue(matrixDiff([dehomogenize(
+ g_math.mulMatrixVector(M, homogenize(v)))], [Mv]) < 0.001);
+}
+
+// Tests that the 4-by-4 homogenous matrix M sends the 3-vector v to the
+// 3-vector Mv when v is interpreted as a direction.
+function directionSendTestVM(M, v, vM) {
+ g_test.assertTrue(matrixDiff([pop0(
+ g_math.mulVectorMatrix(push0(v), M))], [vM]) < 0.001);
+}
+
+// Tests that the 4-by-4 homogenous matrix M sends the 3-vector v to the
+// 3-vector Mv when v is interpreted as a direction.
+function directionSendTestMV(M, v, Mv) {
+ g_test.assertTrue(matrixDiff([pop0(
+ g_math.mulMatrixVector(M, push0(v)))], [Mv]) < 0.001);
+}
+
+// Tests whether if a is pre-composed with b, the result is c; assumes A * B
+// precomposes a with b, as in row major.
+function composeTestAB(a, b, c) {
+ g_test.assertTrue(matrixDiff(g_math.mulMatrixMatrix(a, b), c) < 0.001);
+}
+
+// Tests whether if a is pre-composed with b, the result is c; assumes B * A
+// precomposes a with b, as in column major.
+function composeTestBA(a, b, c) {
+ g_test.assertTrue(matrixDiff(g_math.mulMatrixMatrix(b, a), c) < 0.001);
+}
+
+window.onload = function() {
+ window.g_test = o3djs.test;
+ window.g_math = o3djs.math;
+
+ // Whether or not a matrix transforms a vector by multiplying on
+ // the right or on the left depends on the choice of row major or column
+ // major. Tests which ask whether a matrix sends a point (or direction) to a
+ // particular other point (or direction) do so by appealing to the function
+ // sendTest (or directionSendTest). In row major mode, sendTest gets aliased
+ // to sendTestVM, in column major mode, sendTest gets aliased to sendTestMV,
+ // so in either mode, all the same tests should pass.
+
+ window.sendTest = sendTestMV;
+ window.directionSendTest = directionSendTestMV;
+ window.composeTest = composeTestBA;
+ o3djs.math.installColumnMajorFunctions();
+ var columnMajorTestResult = g_test.runTests(g_suite);
+
+ window.sendTest = sendTestVM;
+ window.directionSendTest = directionSendTestVM;
+ window.composeTest = composeTestAB;
+ o3djs.math.installRowMajorFunctions();
+ var rowMajorTestResult = g_test.runTests(g_suite);
+
+ window.g_testResult = columnMajorTestResult && rowMajorTestResult;
+};
+
+</script>
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/no-rendergraph.html b/o3d/tests/selenium/tests/no-rendergraph.html
new file mode 100644
index 0000000..e35e648
--- /dev/null
+++ b/o3d/tests/selenium/tests/no-rendergraph.html
@@ -0,0 +1,80 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+For checking what happens with no rendergraph.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+No Rendergraph.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="../../../samples/o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and creates one shape.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ window.g_client = clientElements[0].client;
+ window.g_testResult = true;
+}
+</script>
+</head>
+<body>
+<h1>No Rendergraph</h1>
+Area below should be a solid color.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/non-cachable-params.html b/o3d/tests/selenium/tests/non-cachable-params.html
new file mode 100644
index 0000000..f506304
--- /dev/null
+++ b/o3d/tests/selenium/tests/non-cachable-params.html
@@ -0,0 +1,250 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Example of multiple views into the same scene.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Non Cachable Params
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="../../../samples/o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.scene');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfos = [];
+var g_pack;
+var g_o3d_width; // width of our client area
+var g_o3d_height; // height of our client area
+
+/**
+ * Returns the path of where the file is located
+ * with the trailing slash
+ */
+function getCurrentPath() {
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+ return path.substring(0, index + 1);
+}
+
+/**
+ * Loads a model into the transform graph and generates its corresponding entry
+ * in the render graph when it is done loading.
+ * @param {Pack} packfile_name filename of the collada model.
+ * @param {string} file_name filename of the collada model.
+ * @param {Transform} parent parent node in the transform graph to which to load
+ * the model into
+ */
+function loadModel(pack, file_name, parent) {
+ // Get our full path to the model
+ var model_path = getCurrentPath() + file_name;
+
+ // Load the file given the full path, and call the callback function
+ // when its done loading.
+ o3djs.scene.loadScene(g_client, pack, parent, model_path, callback);
+
+ /**
+ * Our callback is called once the model has been loaded into memory
+ * from the web or locally.
+ * @param {!o3d.Pack} pack The pack that was passed in above.
+ * @param {!o3d.Transform} parent The parent that was passed in above.
+ * @param {*} exception null if loading succeeded.
+ */
+ function callback(pack, parent, exception) {
+ if (exception) {
+ alert('Could not load: ' + file_name + '\n' + exception);
+ } else {
+ // Get a cameraInfo (an object with a view and projection matrix)
+ // using our javascript library function
+ var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(
+ parent,
+ g_o3d_width / 2, // because this context only uses 1/2 the area
+ g_o3d_height);
+
+ // Copy the view and projection to the draw context of the first view.
+ g_viewInfos[0].drawContext.view = cameraInfo.view;
+ g_viewInfos[0].drawContext.projection = cameraInfo.projection;
+
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, g_viewInfos[0]);
+
+ // for each effect, get a list of it's params and setup a param chain
+ // for the SAS params
+ var effects = pack.getObjectsByClassName('o3d.Effect');
+ for (var ee = 0; ee < effects.length; ++ee) {
+ var effect = effects[ee];
+ var paramInfos = effect.getParameterInfo();
+ for (var pp = 0; pp < paramInfos.length; ++pp) {
+ var paramInfo = paramInfos[pp];
+ if (paramInfo.sasClassName != "") {
+ var param = effect.createParam(paramInfo.name, 'ParamMatrix4');
+ var sas_param = effect.createParam(paramInfo.name + "_sas",
+ paramInfo.sasClassName);
+ param.bind(sas_param);
+ }
+ }
+ }
+ window.g_testResult = true;
+ }
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the model into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3d = clientElements[0];
+ g_o3d = o3d.o3d;
+ g_math = o3djs.math;
+ g_client = o3d.client;
+
+ // Initialize the sample javascript library.
+ o3djs.base.init(o3d);
+
+ // Get the width and height of our client area. We will need this to create
+ // a projection matrix.
+ g_o3d_width = o3d.clientWidth;
+ g_o3d_height = o3d.clientHeight;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view for the left half of screen.
+ g_viewInfos[0] = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot,
+ [0.7, 0.2, 0.2, 1],
+ 0,
+ [0, 0, 0.5, 1]); // left half of screen.
+
+ // Setup 3 areas. Each area needs a viewport, a tree traveral, 2 drawpasses
+ // and although we could clear the screen once let's do it per viewport just
+ // as an example. The original area is already setup except it needs a
+ // viewport setting.
+
+ // make the 2 right viewports
+ for (var yy = 0; yy < 2; yy++) {
+ var viewInfo = o3djs.rendergraph.createExtraView(
+ g_viewInfos[0],
+ [0.5, yy * 0.5, 0.5, 0.5], // right half top or bottom.
+ [0.5, 0.2 + 0.5 * yy, 0.7 - 0.5 * yy, 1.0], // bg color.
+ yy + 1); // after the other views.
+
+ // save if off for later?
+ g_viewInfos[g_viewInfos.length] = viewInfo;
+
+ // Get the drawcontext for this area and set the view and
+ // projection matrices.
+ var drawContext = viewInfo.drawContext;
+
+ if (yy == 0) {
+ drawContext.projection = g_math.matrix4.perspective(
+ 45 * 3.14159 / 180,
+ (g_o3d_width * 0.5) / (g_o3d_height * 0.5),
+ 0.1,
+ 100);
+ drawContext.view = g_math.matrix4.lookAt(
+ [-10, 3, -15], // eye
+ [0, 2, 0], // target
+ [0, 1, 0]); // up
+ } else {
+ // lets make this one orthographic
+ var aspect = g_o3d_width / g_o3d_height;
+ drawContext.projection = g_math.matrix4.orthographic(
+ -10,
+ 10,
+ -10 / aspect,
+ 10 / aspect,
+ 0,
+ 100);
+ // look directly from the front
+ drawContext.view = g_math.matrix4.lookAt(
+ [0, 5, 30], // eye
+ [0, 5, 0], // target
+ [0, 1, 0]); // up
+ }
+ }
+
+ // Creates a transform to put our data on.
+ var my_data_root = g_pack.createObject('Transform');
+
+ // Connects our root to the client so that world matrices will
+ // get calculated.
+ my_data_root.parent = g_client.root;
+
+ // Load the model into the transform graph as a child my_data_root
+ loadModel(g_pack, '../../../samples/assets/yard.o3dtgz', my_data_root);
+}
+</script>
+</head>
+<body>
+<h1>Non Cachable Params</h1>
+<br/>
+Checks that non cachable param chains work by replacing all the standard (SAS)
+params with chains to those params. If they are not working the 3 views will
+show roughly the same image just with different aspect ratios.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/offscreen-test.html b/o3d/tests/selenium/tests/offscreen-test.html
new file mode 100644
index 0000000..033e0fa
--- /dev/null
+++ b/o3d/tests/selenium/tests/offscreen-test.html
@@ -0,0 +1,158 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Offscreen Test
+
+Make sure client areas that are not visible are not rendered.
+But that tick callbacks are called.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Offscreen Test.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="../../../samples/o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.rendergraph');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = uninit;
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_clients = [];
+var g_packs = [];
+var g_viewInfos = [];
+var g_renderCounts = [0, 0];
+var g_tickCounts = [0, 0];
+var g_testResult;
+
+function onRender(index) {
+ g_viewInfos[index].clearBuffer.clearColor = [Math.random(),
+ Math.random(),
+ Math.random(),
+ 1];
+ g_renderCounts[index] += 1;
+
+ // After 30 frames check that the offscreen client didn't render.
+ if (g_renderCounts[0] > 30) {
+ g_testResult = g_renderCounts[1] < 5 &&
+ g_tickCounts[1] > 25 &&
+ g_tickCounts[0] > 25;
+ }
+}
+
+function onTick(index) {
+ g_tickCounts[index] += 1;
+}
+
+/**
+ * Creates the client areas.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the model into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ for (var ii = 0; ii < clientElements.length; ++ii) {
+ var o3dElement = clientElements[ii];
+
+ g_clients[ii] = o3dElement.client;
+ g_packs[ii] = g_clients[ii].createPack();
+
+ g_viewInfos[ii] = o3djs.rendergraph.createBasicView(
+ g_packs[ii],
+ g_clients[ii].root,
+ g_clients[ii].renderGraphRoot);
+
+ setRenderCallback(ii);
+ setTickCallback(ii);
+ }
+}
+
+/**
+ * Sets the render callback such that it has access to the client.
+ * @param {!o3d.client} client The client to set the render callback on.
+ */
+function setRenderCallback(index) {
+ g_clients[index].setRenderCallback(function() { onRender(index); });
+}
+
+/**
+ * Sets the tick callback such that it has access to the client.
+ * @param {!o3d.client} client The client to set the tick callback on.
+ */
+function setTickCallback(index) {
+ g_clients[index].setTickCallback(function() { onTick(index); });
+}
+
+/**
+ * Cleans up.
+ */
+function uninit() {
+ for (var ii = 0; ii < g_clients.length; ++ii) {
+ g_clients[ii].clearRenderCallback();
+ g_clients[ii].clearTickCallback();
+ }
+}
+
+</script>
+</head>
+<body>
+<h1>Offscreen Test</h1>
+This test verifies that client areas offscreen do not render but that tick
+callbacs get called.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d-on-screen" style="width: 100px; height: 100px;"></div>
+<div style="width: 100px; height: 4000px; border: red 1px solid;"></div>
+<div id="o3d-off-screen" style="width: 100px; height: 100px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/ownership-test.html b/o3d/tests/selenium/tests/ownership-test.html
new file mode 100644
index 0000000..fea74f0
--- /dev/null
+++ b/o3d/tests/selenium/tests/ownership-test.html
@@ -0,0 +1,210 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+A Selenium test to make that streams lasting past the client don't crash.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Ownership Test
+</title>
+<!-- Our javascript code -->
+<script type="text/javascript" src="../../../samples/o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+
+window.onload = init;
+
+// global variables
+var g_testResult;
+var g_streamArrays = [];
+var g_effectInfo;
+
+/**
+ * Creates a o3d object.
+ */
+function init() {
+ // Create a client.
+ var clientSpan = document.getElementById('client');
+ var objElem = o3djs.util.createClient(clientSpan);
+ objElem.style.width = '60px';
+ objElem.style.height = '60px';
+ objElem.id = 'o3d';
+ objElem.name ='o3d';
+
+ // wait for the browsers to settle down.
+ var clearId = window.setInterval(function() {
+ if (document.o3d.o3d) {
+ window.clearInterval(clearId);
+ test();
+ }
+
+ }, 10);
+}
+
+function makeBufferAndFreeClient() {
+ // Initialize global variables and libraries.
+ var o3d = document.o3d.o3d;
+ var client = document.o3d.client;
+
+ // Create a g_pack to manage our resources/assets
+ var pack = client.createPack();
+
+ var effect = pack.createObject('Effect');
+ effect.loadFromFXString('' +
+ 'float var1;\n' +
+ 'float2 var2;\n' +
+ 'float3 var3;\n' +
+ 'float4 var4;\n' +
+ 'struct a2v {\n' +
+ ' float4 pos : POSITION;\n' +
+ '};\n'+
+ 'struct v2f {\n' +
+ ' float4 pos : POSITION;\n' +
+ '};\n' +
+ 'v2f vsMain(a2v IN) {\n' +
+ ' v2f OUT;\n' +
+ ' OUT.pos = IN.pos;\n' +
+ ' return OUT;\n' +
+ '}\n' +
+ 'float4 psMain(v2f IN): COLOR {\n' +
+ ' return float4(var1, var2.y, var3.z, var4.w);\n' +
+ '}\n' +
+ '// #o3d VertexShaderEntryPoint vsMain\n' +
+ '// #o3d PixelShaderEntryPoint psMain\n' +
+ '// #o3d MatrixLoadOrder RowMajor\n');
+ g_effectInfo = effect.getParameterInfo();
+
+
+ // Let's create a lot of buffers just to try to make it crash.
+ for (var ii = 0; ii < 100; ii++) {
+ var bank = pack.createObject('StreamBank');
+ var buffer = pack.createObject('VertexBuffer');
+ var field = buffer.createField('FloatField', 3);
+
+ var vertsArray = [
+ // Triangle 1
+ -0.5, 0.5, 0,
+ -0.5, -0.5, 0,
+ 0.5, -0.5, 0,
+
+ // Triangle 2
+ 0.5, 0.5, 0,
+ -0.5, 0.5, 0,
+ 0.5, -0.5, 0
+ ];
+ buffer.set(vertsArray);
+ bank.setVertexStream(o3d.Stream.POSITION,
+ 0,
+ field,
+ 0);
+
+ // copy the streams out so they last past the client.
+ g_streamArrays[ii] = bank.vertexStreams;
+ }
+
+ pack.destroy();
+
+ var clientSpan = document.getElementById('client');
+ var clientNode = clientSpan.childNodes[0];
+ clientSpan.removeChild(clientNode);
+}
+
+function isDefined(variable) {
+ return (typeof(variable) == "undefined") ? false : true;
+}
+
+function test() {
+ makeBufferAndFreeClient();
+
+ // check that we can still access the stream objects.
+ var caught = false;
+ try {
+ // Chrome does not always throw exceptions when methods/properties on NPAPI
+ // objects fail, so we explicitly throw here.
+ var stream = g_streamArrays[1][0];
+ if (!isDefined(stream))
+ throw 'error';
+
+ // The stream object may exist as an empty container, so probe its normally
+ // expected properties - they should no longer exist.
+ var semantic = stream.semantic;
+ var field = stream.field;
+
+ // The object was deleted, so both of its properties should not exist.
+ if (!isDefined(semantic) && !isDefined(field))
+ throw 'error'
+ } catch (e) {
+ // We should always get here unless there is a bug.
+ caught = true;
+ }
+
+ var caught2 = false;
+ try {
+ var effectInfo = g_effectInfo[0];
+ var name = effect.name;
+ if (!isDefined(name)) {
+ throw 'error';
+ }
+ } catch(e) {
+ // We should always get here unless there is a bug.
+ caught2 = true;
+ }
+
+ // This should free the Stream objects.
+ g_streamArrays = null;
+
+ // attempt to allocate lots of stuff to hopefully force the browser
+ // to garbage collect. If there is a bug we should crash here.
+ var array = {};
+ for (var ii = 0; ii < 100000; ii++) {
+ array['abced' + ii] = 'this is a waste of memory';
+ }
+
+ if (caught && caught2)
+ document.getElementById('testStatus').innerHTML = "Finished - Passed";
+ else
+ document.getElementById('testStatus').innerHTML = "Finished - Failed";
+ g_testResult = caught;
+}
+
+</script>
+</head>
+<body>
+<h1>Ownership Test</h1>
+<div id="client" style="width: 60px; height 60px;"></div>
+<div>Status: <span id="testStatus">--running--</span></div>
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/pixel-perfection.html b/o3d/tests/selenium/tests/pixel-perfection.html
new file mode 100644
index 0000000..60e7b1c
--- /dev/null
+++ b/o3d/tests/selenium/tests/pixel-perfection.html
@@ -0,0 +1,337 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Pixel Perfection Test
+
+This test generates some shapes that should be able to be rendered
+identically on all the platforms that we support. It tests fill
+coverage, zbuffer coverage, blending and color and brightness fidelity.
+
+It purposly does not test things like anti-aliasing and texture
+sampling, because those vary too much from vendor to vendor, and
+because they are tested adequately in other tests.
+
+TODO: Anti-aliasing should be turned off for this test, when
+we have a way to do that.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Pixel Perfection
+</title>
+<script type="text/javascript" src="../../../samples/o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.material');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.util');
+
+// global variables
+var g_o3dElement;
+var g_client;
+var g_o3d;
+var g_math;
+var g_pack;
+var g_viewInfo;
+
+var g_vertexShaderMaterial;
+var g_lightPosition = [0, 0, 12];
+var g_eyePosition = [0, 0, 12];
+
+/**
+ * Creates the client area.
+ */
+function initClient() {
+ window.g_finished = false; // for selenium testing.
+
+ // Runs the sample in V8. Comment out this line to run it in the browser
+ // JavaScript engine, for example if you want to debug it.
+ o3djs.util.setMainEngine(o3djs.util.Engine.V8);
+
+ o3djs.util.makeClients(main, 'NotAntiAliased');
+}
+
+/**
+ * Initializes global variables, positions camera, draws shapes.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function main(clientElements) {
+ // Init global variables.
+ initGlobals(clientElements);
+
+ // Set up the view and projection transformations.
+ initContext();
+
+ // Add the shapes to the transform heirarchy.
+ createShapes();
+
+ window.g_testResult = true; // for selenium testing.
+}
+
+/**
+ * Initializes global variables and libraries.
+ */
+function initGlobals(clientElements) {
+ g_o3dElement = clientElements[0];
+ window.g_client = g_client = g_o3dElement.client;
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+
+ // Create a pack to manage the objects created.
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Set the background color to white for more contrast.
+ g_viewInfo.clearBuffer.clearColor = [1, 1, 1, 1];
+
+ // Create and load the effect.
+ var effect = g_pack.createObject('Effect');
+ var vertexShaderString = o3djs.util.getElementContentById('vertexShader');
+ effect.loadFromFXString(vertexShaderString);
+ // Create a material for it.
+ g_vertexShaderMaterial = g_pack.createObject('Material');
+
+ // Set the material's drawList
+ g_vertexShaderMaterial.drawList = g_viewInfo.performanceDrawList;
+
+ // Apply our effect to this material. The effect tells the 3D hardware
+ // which shader to use.
+ g_vertexShaderMaterial.effect = effect;
+
+ // Create the parameters the effect needs on the material.
+ effect.createUniformParameters(g_vertexShaderMaterial);
+}
+
+/**
+ * Sets up reasonable view and projection matrices.
+ */
+function initContext() {
+ // Set up a perspective transformation for the projection.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(30), // 30 degree frustum.
+ g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio.
+ 1, // Near plane.
+ 5000); // Far plane.
+
+ // Set up our view transformation to look towards the world origin where the
+ // primitives are located.
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ g_eyePosition, // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+}
+
+/**
+ * Creates a phong material based on the given single color.
+ * @param {Array} baseColor An array with 4 entries, the R,G,B, and A components
+ * of a color.
+ * @return {Material} A phong material whose overall pigment is baseColor.
+ */
+function createMaterial(baseColor) {
+ // Create a material.
+ var material = o3djs.material.createBasicMaterial(
+ g_pack,
+ g_viewInfo,
+ baseColor,
+ baseColor[3] != 1.0);
+
+ return material;
+}
+
+/**
+ * Creates shapes using the primitives utility library, and adds them to the
+ * transform graph at the root node.
+ */
+function createShapes() {
+ var cube = o3djs.primitives.createRainbowCube(
+ g_pack,
+ g_vertexShaderMaterial,
+ Math.sqrt(2));
+
+ var sphere = o3djs.primitives.createSphere(
+ g_pack,
+ createMaterial([1, 0, 0, 1]),
+ 1.0, // Radius of the sphere.
+ 30, // Number of meridians.
+ 20); // Number of parallels.
+
+ var plane1 = o3djs.primitives.createPlane(
+ g_pack,
+ createMaterial([0, 1, 1, 1]),
+ 2.5, // Width.
+ 2.5, // Depth.
+ 3, // Horizontal subdivisions.
+ 3); // Vertical subdivisions.
+
+ var plane2 = o3djs.primitives.createPlane(
+ g_pack,
+ createMaterial([0, .5, 0, 1]),
+ 2.5, // Width.
+ 2.5, // Depth.
+ 3, // Horizontal subdivisions.
+ 3); // Vertical subdivisions.
+
+ var transPlane1 = o3djs.primitives.createPlane(
+ g_pack,
+ createMaterial([0, 1, 1, .1]),
+ 2.5, // Width.
+ 2.5, // Depth.
+ 3, // Horizontal subdivisions.
+ 3); // Vertical subdivisions.
+
+ var transPlane2 = o3djs.primitives.createPlane(
+ g_pack,
+ createMaterial([0, .5, 0, .1]),
+ 2.5, // Width.
+ 2.5, // Depth.
+ 3, // Horizontal subdivisions.
+ 3); // Vertical subdivisions.
+
+ var disc1 = o3djs.primitives.createDisc(
+ g_pack,
+ createMaterial([0, 0, 0, 0.25]),
+ 1, // Radius.
+ 50, // Divisions.
+ 2, // Stacks.
+ 1, // Start Stack.
+ 1); // Stack Power.
+
+ var disc2 = o3djs.primitives.createDisc(
+ g_pack,
+ createMaterial([0, 0, 0, 1]),
+ 1, // Radius.
+ 50, // Divisions.
+ 2, // Stacks.
+ 1, // Start Stack.
+ 1); // Stack Power.
+
+ // Add the shapes to the transforms.
+ var transformTable = [
+ {shape: cube, translation: [-2, 1, 0],
+ rotation: [Math.PI / 4, Math.PI / 4, 0]},
+ {shape: sphere, translation: [0, 1, 0], rotation: [0, 0, 0]},
+ {shape: plane1, translation: [2, 1, 0],
+ rotation: [0.99 * Math.PI / 2, 0, 0]},
+ {shape: plane2, translation: [2, 1, 0], rotation: [Math.PI / 2, 0, 0]},
+ {shape: transPlane1, translation: [-2, -1, -0.1],
+ rotation: [0.99 * Math.PI / 2, 0, 0]},
+ {shape: transPlane2, translation: [-2, -1, -0.1],
+ rotation: [Math.PI / 2, 0, 0]},
+ {shape: disc1, translation: [0, -1, 0], rotation: [Math.PI / 2, 0, 0]},
+ {shape: disc2, translation: [2, -1, 0], rotation: [Math.PI / 2, 0, 0]}
+ ];
+
+ for (var tt = 0; tt < transformTable.length; ++tt) {
+ var transform = g_pack.createObject('Transform');
+ transform.addShape(transformTable[tt].shape);
+ transform.translate(transformTable[tt].translation);
+ transform.rotateX(transformTable[tt].rotation[0]);
+ transform.rotateY(transformTable[tt].rotation[1]);
+ transform.rotateZ(transformTable[tt].rotation[2]);
+ transform.parent = g_client.root;
+ }
+}
+
+
+</script>
+</head>
+<body onload="initClient()">
+<h1>Pixel Perfection</h1>
+<p>This test generates some shapes that should be able to be rendered
+identically on all the platforms that we support. It tests things
+like fill coverage, zbuffer coverage, blending and color/brightness
+fidelity.</p>
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+<!-- Start of effect -->
+<textarea id="vertexShader" name="vertexShader" cols="80" rows="20"
+ style="display: none;">
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+
+// input parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float4 color : COLOR;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float4 color: COLOR;
+};
+
+/**
+ * Vertex Shader - our vertex shader
+ */
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ /**
+ * Our vertex shader projects the vertices onto the screen.
+ * We return its color unchanged.
+ */
+ PixelShaderInput output;
+
+ output.position = mul(input.position, worldViewProjection);
+ output.color = input.color;
+ return output;
+}
+
+/**
+ * pixel shader does nothing but return whatever color it was given.
+ */
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ return input.color;
+}
+
+// Here we tell our effect file the functions
+// which specify our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+<!-- End of effect -->
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/quaternion-test.html b/o3d/tests/selenium/tests/quaternion-test.html
new file mode 100644
index 0000000..1d37182
--- /dev/null
+++ b/o3d/tests/selenium/tests/quaternion-test.html
@@ -0,0 +1,335 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Quaternion Test
+</title>
+</head>
+<body>
+<h1>Quaternion Test</h1>
+This tests the quaternion utility library.
+<br/>
+<script type="text/javascript"
+ src="../../../samples/o3djs/base.js"></script>
+<script type="text/javascript">
+
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.quaternions');
+o3djs.require('o3djs.test');
+
+var g_suite = {};
+
+g_suite.testType = function() {
+ g_test.assertEquals(g_quat.mathType(1), "Scalar");
+ g_test.assertEquals(g_quat.mathType([1, 2, 3, 4]), "Quaternion");
+};
+
+g_suite.testNegative = function() {
+ g_test.assertArrayEquals([0, 6, 7, -1], g_quat.negative([0, -6, -7, 1]));
+};
+
+g_suite.testCopy = function() {
+ var a = [0, 6, 7, 1];
+ var b = g_quat.copy(a);
+ g_test.assertArrayEquals(a, b);
+ g_test.assertFalse(a == b);
+};
+
+g_suite.testAddQuaternions = function() {
+ g_test.assertArrayEquals([0, 6, 7, 1],
+ g_quat.add([1, 2, 3, 2], [-1, 4, 4, -1]));
+ g_test.assertArrayEquals([1, -1, -1, 2],
+ g_quat.add([0, 0, 0, 0], [1, -1, -1, 2]));
+};
+
+g_suite.testAddQuaternionsAndScalars = function() {
+ g_test.assertArrayEquals([0, 6, 7, 2], g_quat.add([0, 6, 7, 1], 1));
+ g_test.assertArrayEquals([0, 6, 7, 2], g_quat.add(1, [0, 6, 7, 1]));
+};
+
+g_suite.testSubQuaternions = function() {
+ g_test.assertArrayEquals([2, -2, -1, 4],
+ g_quat.sub([1, 2, 3, 4], [-1, 4, 4, 0]));
+};
+
+g_suite.testSubQuaternionsAndScalars = function() {
+ g_test.assertArrayEquals([0, 6, 7, 0], g_quat.sub([0, 6, 7, 1], 1));
+ g_test.assertArrayEquals([0, -6, -7, 0], g_quat.sub(1, [0, 6, 7, 1]));
+};
+
+g_suite.testMulQuaternions = function() {
+ // 2*3 = 6
+ g_test.assertArrayEquals([0, 0, 0, 6],
+ g_quat.mul([0, 0, 0, 2], [0, 0, 0, 3]));
+
+ // i^2 = -1
+ g_test.assertArrayEquals([0, 0, 0, -1],
+ g_quat.mul([1, 0, 0, 0], [1, 0, 0, 0]));
+
+ // j^2 = -1
+ g_test.assertArrayEquals([0, 0, 0, -1],
+ g_quat.mul([0, 1, 0, 0], [0, 1, 0, 0]));
+
+ // k^2 = -1
+ g_test.assertArrayEquals([0, 0, 0, -1],
+ g_quat.mul([0, 0, 1, 0], [0, 0, 1, 0]));
+
+ // i*j = k
+ g_test.assertArrayEquals([0, 0, 1, 0],
+ g_quat.mul([1, 0, 0, 0], [0, 1, 0, 0]));
+};
+
+g_suite.testMulQuaternionsAndScalars = function() {
+ g_test.assertArrayEquals([2, 4, 6, 8], g_quat.mul([1, 2, 3, 4], 2));
+ g_test.assertArrayEquals([2, 4, 6, 8], g_quat.mul(2, [1, 2, 3, 4]));
+};
+
+g_suite.testDivQuaternions = function() {
+ var q = [-1, 5, -4, 2];
+ var r = [4, 4, 2, -2];
+ assertQuaternionsClose(r, g_quat.mul(g_quat.div(r, q), q));
+};
+
+g_suite.testDivQuaternionScalar = function() {
+ g_test.assertArrayEquals([0.5, 1, 1.5, 2], g_quat.div([1, 2, 3, 4], 2));
+};
+
+g_suite.testDivScalarQuaternion = function() {
+ var q = [4, 2, -1, 7];
+ var k = 3;
+ assertQuaternionsClose([0, 0, 0, k], g_quat.mul(q, g_quat.div(k, q)));
+ assertQuaternionsClose([0, 0, 0, k], g_quat.mul(g_quat.div(k, q), q));
+};
+
+g_suite.testDivDirection = function() {
+ var q = [4, 2, -1, 7];
+ var r = [4, 4, 2, -2];
+ assertQuaternionsClose(g_quat.div(q, r), g_quat.mul(q, g_quat.div(1, r)));
+};
+
+g_suite.testLength = function() {
+ g_test.assertEquals(13, g_quat.length([5, 12, 0, 0]));
+ g_test.assertEquals(13, g_quat.length([0, 0, 5, 12]));
+ g_test.assertClose(Math.sqrt(30), g_quat.length([1, 2, 3, 4]));
+};
+
+g_suite.testLengthSquared = function() {
+ g_test.assertEquals(30, g_quat.lengthSquared([1, 2, 3, 4]));
+};
+
+g_suite.testNormalize = function() {
+ g_test.assertTrue(compareWithTolerance(1,
+ g_quat.length(g_quat.normalize([1, 2, 3, 4])), 1e-4));
+};
+
+g_suite.testConjugate = function() {
+ g_test.assertArrayEquals([-1, -2, -3, 4], g_quat.conjugate([1, 2, 3, 4]));
+};
+
+g_suite.testRotationX = function() {
+ var q = g_quat.rotationX(Math.PI / 2);
+ var v = [0, 1, 0, 0]; // Y rotated around the X axis should be Z.
+ assertVectorsClose(g_quat.mul(q, g_quat.div(v, q)), [0, 0, 1, 0]);
+};
+
+g_suite.testRotationY = function() {
+ var q = g_quat.rotationY(Math.PI / 2);
+ var v = [0, 0, 1, 0]; // Z rotated around the Y axis should be X.
+ assertVectorsClose(g_quat.mul(q, g_quat.div(v, q)), [1, 0, 0, 0]);
+};
+
+g_suite.testRotationZ = function() {
+ var q = g_quat.rotationZ(Math.PI / 2);
+ var v = [1, 0, 0, 0]; // X rotated around the Z axis should be Y.
+ assertVectorsClose(g_quat.mul(q, g_quat.div(v, q)), [0, 1, 0, 0]);
+};
+
+g_suite.testRotationVarious = function() {
+ var v = g_quat.rotationX(0);
+ assertMatricesClose(g_math.matrix4.rotationX(1),
+ g_quat.quaternionToRotation(g_quat.rotationX(1)));
+ assertMatricesClose(g_math.matrix4.rotationY(1),
+ g_quat.quaternionToRotation(g_quat.rotationY(1)));
+ assertMatricesClose(g_math.matrix4.rotationZ(1),
+ g_quat.quaternionToRotation(g_quat.rotationZ(1)));
+ assertMatricesClose(g_math.matrix4.identity(),
+ g_quat.quaternionToRotation([0, 0, 0, 1]));
+
+ var axis = [1, -4, 2];
+ var angle = 3;
+ assertMatricesClose(
+ g_math.matrix4.axisRotation(axis, angle),
+ g_quat.quaternionToRotation(g_quat.axisRotation(axis, angle)));
+};
+
+g_suite.testAxisRotation = function() {
+ var q = g_quat.axisRotation([1, 1, 1], 2 * Math.PI / 3);
+ assertVectorsClose(g_quat.mul(q, g_quat.div([1, 0, 0, 0], q)), [0, 1, 0, 0]);
+ assertVectorsClose(g_quat.mul(q, g_quat.div([0, 1, 0, 0], q)), [0, 0, 1, 0]);
+ assertVectorsClose(g_quat.mul(q, g_quat.div([0, 0, 1, 0], q)), [1, 0, 0, 0]);
+};
+
+g_suite.testQuaternionToRotation = function() {
+ // The quaternion 1, should give the identity transformation.
+ assertMatricesClose(g_math.matrix4.identity(),
+ g_quat.quaternionToRotation([0, 0, 0, 1]));
+
+ // Each of i, j, and k should rotate 180 degrees around the x, y and z axes
+ // (respectively).
+ assertMatricesClose(g_math.matrix4.rotationX(Math.PI),
+ g_quat.quaternionToRotation([1, 0, 0, 0]));
+ assertMatricesClose(g_math.matrix4.rotationY(Math.PI),
+ g_quat.quaternionToRotation([0, 1, 0, 0]));
+ assertMatricesClose(g_math.matrix4.rotationZ(Math.PI),
+ g_quat.quaternionToRotation([0, 0, 1, 0]));
+
+ // Pick an arbitrary point v and quaternion q.
+ var v = [1, 2, 3, 1];
+ var q = [2, -7, 4, -3];
+
+ // Convert the vector to a quatnerion, apply q, and convert back.
+ var t1 = toPoint(g_quat.div(g_quat.mul(q, toQuat(v)), q));
+ // Also apply the matrix obtained from quaternionToRotation.
+ var t2 = g_math.mulVectorMatrix(v, g_quat.quaternionToRotation(q));
+
+ // Results should be the same.
+ assertVectorsClose(t1, t2);
+};
+
+g_suite.testRotationToQuaternion = function() {
+ // Start with a bunch of haphazard rotation matrices.
+ var matrices = [g_math.matrix4.axisRotation([1, -2, 0], 1),
+ g_math.matrix4.axisRotation([2, -4, -1], -.1),
+ g_math.matrix4.axisRotation([-1, -4, -1], 1),
+ g_math.matrix4.axisRotation([-1, -3, 2], .1),
+ g_math.matrix4.axisRotation([2, -7, -1], -5),
+ g_math.matrix4.axisRotation([1, -7, 6], .5),
+ g_math.matrix4.axisRotation([-2, 7, -6], 3),
+ g_math.matrix4.axisRotation([-2, -2, -6], -3),
+ g_math.matrix4.axisRotation([-4, -2, 3], .4),
+ g_math.matrix4.axisRotation([-1, -2, -3], -1)
+ ];
+
+ // Test each one.
+ for (var i = 0; i < matrices.length; ++i){
+ toQuaternionAndBack(matrices[i]);
+ }
+};
+
+function toQuaternionAndBack(m1)
+{
+ // Convert the matrix to a quaternion.
+ var q = g_quat.rotationToQuaternion(m1);
+
+ // Then convert back.
+ var m2 = g_quat.quaternionToRotation(q);
+
+ // Assert that you get the same matrix you started with.
+ assertMatricesClose(m1, m2);
+};
+
+g_suite.testInverse = function() {
+ var q = [1, 2, 3, 4];
+
+ // The inverse quaternion should give you the inverse matrix.
+ var m1 = g_quat.quaternionToRotation(g_quat.inverse(q));
+ var m2 = g_math.inverse(g_quat.quaternionToRotation(q));
+
+ assertMatricesClose(m1, m2);
+};
+
+// Asks if two numbers are within a tolerance of eachother.
+function compareWithTolerance(a, b, tol) {
+ return Math.abs(a-b) < tol;
+};
+
+// Compares two quaternions by subtracting them, taking the length of the
+// difference, and comparing that length to a tolerance.
+function compareQuaternionsWithTolerance(a, b, tol) {
+ return g_quat.length(g_quat.sub(a,b)) < tol;
+};
+
+function assertQuaternionsClose(a, b) {
+ g_test.assertTrue(compareQuaternionsWithTolerance(a, b, 1e-4));
+};
+
+// Compares two vectors by subtracting them, taking the length of the
+// difference and comparing that length to a tolerance.
+function compareVectorsWithTolerance(a, b, tol) {
+ return g_math.length(g_math.subVector(a,b)) < tol;
+};
+
+function assertVectorsClose(a, b) {
+ g_test.assertTrue(compareVectorsWithTolerance(a, b, 1e-4));
+};
+
+// The square root of the sum of squares of the entries of a matrix.
+function frobeniusNorm(a) {
+ return Math.sqrt(g_math.trace(
+ g_math.mulMatrixMatrix(g_math.transpose(a), a)));
+};
+
+// The sum of squares of entries of the difference between two matrices.
+function matrixDiff(a, b) {
+ return frobeniusNorm(g_math.subMatrix(a, b));
+};
+
+function assertMatricesClose(a, b) {
+ g_test.assertTrue(matrixDiff(a, b) < 1e-4);
+};
+
+// Creates a quaternion from a vector; assumes that either the vector has three
+// entries or it is in homogenous coordinates with a w component of 1.
+function toQuat(v) {
+ return [v[0], v[1], v[2], 0];
+};
+
+// Reinterprets a quaternion as a point in homogenous coordinates with a w
+// component of 1.
+function toPoint(q) {
+ return [q[0], q[1], q[2], 1];
+};
+
+window.onload = function() {
+ window.g_quat = o3djs.quaternions;
+ window.g_math = o3djs.math;
+ window.g_test = o3djs.test;
+ window.g_testResult = g_test.runTests(g_suite);
+};
+
+</script>
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/render-test.html b/o3d/tests/selenium/tests/render-test.html
new file mode 100644
index 0000000..3b4e26a
--- /dev/null
+++ b/o3d/tests/selenium/tests/render-test.html
@@ -0,0 +1,136 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Render Test
+
+Make sure that if client.render() is called multiple times it is only rendered
+once. client.render() should only flag that the client needs to be rendered and
+the system (browser + plugin) should render it the next time it's time to
+render. It should NOT render immediately therefore multiple calls to
+client.render() without returning to the browser should only make it render
+once.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Render Test.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="../../../samples/o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.rendergraph');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = uninit;
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_client;
+var g_renderCount = 0;
+
+function onRender() {
+ ++g_renderCount;
+}
+
+/**
+ * Creates the client areas.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ var o3dElement = clientElements[0];
+
+ var o3d = o3dElement.o3d;
+ g_client = o3dElement.client;
+ g_client.renderMode = o3d.Client.RENDERMODE_ON_DEMAND;
+
+ g_client.setRenderCallback(onRender);
+
+ for (var ii = 0; ii < 60; ++ii) {
+ g_client.render();
+ }
+
+ setTimeout(checkResults, 1000);
+}
+
+function checkResults() {
+ // We give it some leeway in case the browser is settling down.
+ window.g_testResult = g_renderCount < 5;
+
+ document.getElementById('success').innerHTML =
+ g_testResult ? 'pass' : '<font color="red">fail</font>';
+}
+
+/**
+ * Cleans up.
+ */
+function uninit() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+
+</script>
+</head>
+<body>
+<h1>Render Test</h1>
+Make sure that if client.render() is called multiple times it is only rendered
+once. client.render() should only flag that the client needs to be rendered and
+the system (browser + plugin) should render it the next time it's time to
+render. It should NOT render immediately therefore multiple calls to
+client.render() without returning to the browser should only make it render
+once.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 100px; height: 100px;"></div>
+<!-- End of O3D plugin -->
+<div>Test Results: <span id="success"></span></div>
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/serialization-test.html b/o3d/tests/selenium/tests/serialization-test.html
new file mode 100644
index 0000000..dcb5ddc
--- /dev/null
+++ b/o3d/tests/selenium/tests/serialization-test.html
@@ -0,0 +1,1376 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Deserialization Test
+</title>
+</head>
+<body>
+<h1>Deserialization Test</h1>
+This tests deserialization of packs from JSON.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 32px; height: 32px;"></div>
+<!-- End of O3D plugin -->
+
+<script type="text/javascript"
+ src="../../../samples/o3djs/base.js"></script>
+<script type="text/javascript">
+
+o3djs.require('o3djs.io');
+o3djs.require('o3djs.serialization');
+o3djs.require('o3djs.test');
+o3djs.require('o3djs.util');
+
+var g_suite = {};
+
+g_suite.testDeserializesEmptyPack = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+ g_test.assertEquals(0, pack.objects.length);
+};
+
+g_suite.testExceptionIfVersionIsMissing = function() {
+ var json = {
+ objects: {
+ }
+ };
+
+ var pack = g_client.createPack();
+ var exception = null;
+ try {
+ o3djs.serialization.deserialize(pack, json);
+ } catch(e) {
+ exception = e;
+ }
+
+ g_test.assertEquals('Version in JSON file was missing.', exception);
+ g_test.assertEquals(0, pack.objects.length);
+};
+
+g_suite.testExceptionIfVersionIsUnsupported = function() {
+ var json = {
+ version: 0,
+ objects: {
+ }
+ };
+
+ var pack = g_client.createPack();
+ var exception = null;
+ try {
+ o3djs.serialization.deserialize(pack, json);
+ } catch(e) {
+ exception = e;
+ }
+
+ g_test.assertEquals(
+ 'Version in JSON file was 0 but expected at least version ' +
+ o3djs.serialization.supportedVersion + '.', exception);
+ g_test.assertEquals(0, pack.objects.length);
+};
+
+g_suite.testExceptionIfObjectsArrayIsMissing = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion
+ };
+
+ var pack = g_client.createPack();
+ var exception = null;
+ try {
+ o3djs.serialization.deserialize(pack, json);
+ } catch(e) {
+ exception = e;
+ }
+
+ g_test.assertEquals('Objects array in JSON file was missing.', exception);
+ g_test.assertEquals(0, pack.objects.length);
+};
+
+g_suite.testDeserializesObject = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Transform': [
+ {
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(1, pack.objects.length);
+ g_test.assertEquals('o3d.Transform', pack.objects[0].className);
+};
+
+g_suite.testDeserializesIntoExistingObjectIfItIsAlreadyAdded = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Transform': [
+ {
+ id: 7,
+ properties: {
+ name: 'Foo'
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var transform = pack.createObject('o3d.Transform');
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.addObject(7, transform);
+ deserializer.run();
+
+ g_test.assertEquals(1, pack.objects.length);
+ g_test.assertEquals('o3d.Transform', pack.objects[0].className);
+ g_test.assertEquals(transform.clientId, pack.objects[0].clientId);
+ g_test.assertEquals('Foo', pack.objects[0].name);
+};
+
+g_suite.testCreatesSpecialClassByInvokingCallback = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Transform': [
+ {
+ id: 7
+ }
+ ],
+ 'myname.SpecialClass': [
+ {
+ parent: 7
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+
+ deserializer.createCallbacks['myname.SpecialClass'] = function(
+ deserializer, json) {
+ var transform = deserializer.pack.createObject('o3d.Transform');
+ transform.parent = deserializer.getObjectById(json.parent);
+ return transform;
+ };
+
+ deserializer.run();
+
+ g_test.assertEquals(2, pack.objects.length);
+ g_test.assertEquals(pack.objects[0].children[0].clientId,
+ pack.objects[1].clientId);
+};
+
+g_suite.testDeserializesBoolProperty = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Transform': [
+ {
+ properties: {
+ cull: true
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(1, pack.objects.length);
+ g_test.assertEquals(true, pack.objects[0].cull);
+};
+
+g_suite.testDeserializesMatrixProperty = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Transform': [
+ {
+ properties: {
+ localMatrix: [[1,2,3,4],[5,6,7,8],[8,7,6,5],[4,3,2,1]]
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(1, pack.objects.length);
+ g_test.assertEquals(4, pack.objects[0].localMatrix[3][0]);
+};
+
+g_suite.testDeserializesStringProperty = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Transform': [
+ {
+ properties: {
+ name: 'myTransform'
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(1, pack.objects.length);
+ g_test.assertEquals('myTransform', pack.objects[0].name);
+};
+
+g_suite.testShouldDeserializeNullKeywordToNullValue = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.FunctionEval': [
+ {
+ properties: {
+ 'functionObject': null
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(1, pack.objects.length);
+ g_test.assertNull(pack.objects[0].functionObject);
+};
+
+g_suite.testDeserializesReferenceProperty = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Curve': [
+ {
+ id: 7
+ }
+ ],
+ 'o3d.FunctionEval': [
+ {
+ properties: {
+ 'functionObject': { ref: 7 }
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(2, pack.objects.length);
+ g_test.assertEquals(pack.objects[0].clientId, pack.objects[1].functionObject.clientId);
+};
+
+g_suite.testDeserializesAndFixesUpReferenceProperty = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.FunctionEval': [
+ {
+ properties: {
+ 'functionObject': { ref: 7 }
+ }
+ }
+ ],
+ 'o3d.Curve': [
+ {
+ id: 7
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(2, pack.objects.length);
+ g_test.assertEquals(pack.objects[1].clientId, pack.objects[0].functionObject.clientId);
+};
+
+g_suite.testExceptionIfReferenceCannotBeResolved = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.FunctionEval': [
+ {
+ properties: {
+ 'functionObject': { ref: 7 }
+ }
+ }
+ ]
+ }
+ };
+
+ var exception = null;
+ var pack = g_client.createPack();
+ try {
+ o3djs.serialization.deserialize(pack, json);
+ } catch (e) {
+ exception = e;
+ }
+
+ g_test.assertEquals('Could not find object with id 7.', exception);
+};
+
+g_suite.testDeserializesArrayOfReferenceProperty = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Primitive': [
+ {
+ id: 7
+ }
+ ],
+ 'o3d.Shape': [
+ {
+ properties: {
+ 'elements': [{ ref: 7 }]
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(2, pack.objects.length);
+ g_test.assertEquals(1, pack.objects[1]['elements'].length);
+ g_test.assertEquals(pack.objects[0].clientId, pack.objects[1]['elements'][0].clientId);
+};
+
+g_suite.testIgnoresMissingProperty = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Transform': [
+ {
+ properties: {
+ missing: true
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(1, pack.objects.length);
+ g_test.assertFalse('missing' in pack.objects[0]);
+};
+
+g_suite.testShouldCallInitializationCallbackIfPresent = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'myname.SpecialClass': [
+ {
+ indexBuffer: {ref: 7}
+ }
+ ],
+ 'o3d.IndexBuffer': [
+ {
+ id: 7
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+
+ deserializer.createCallbacks['myname.SpecialClass'] = function(
+ deserializer, json) {
+ return deserializer.pack.createObject('o3d.Primitive');
+ };
+
+ deserializer.initCallbacks['myname.SpecialClass'] = function(
+ deserializer, object, json) {
+ object.indexBuffer = deserializer.deserializeValue(json.indexBuffer);
+ };
+
+ deserializer.run();
+
+ g_test.assertEquals(2, pack.objects.length);
+ g_test.assertEquals(pack.objects[1].clientId, pack.objects[0].indexBuffer.clientId);
+};
+
+g_suite.testShouldIgnoreCallIfMethodIsMissing = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Primitive': [
+ {
+ calls: [
+ ['missingMethod', 1, 2, 3]
+ ]
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(1, pack.objects.length);
+};
+
+g_suite.testDeserializesExistingParam = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Transform': [
+ {
+ params: {
+ localMatrix: {value: [[1,2,3,4],[5,6,7,8],[8,7,6,5],[4,3,2,1]]}
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(1, pack.objects.length);
+ g_test.assertEquals(4, pack.objects[0].localMatrix[3][0]);
+};
+
+g_suite.testDeserializesAddedParam = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Transform': [
+ {
+ params: {
+ newParam: {'class': 'o3d.ParamFloat', value: 7}
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(1, pack.objects.length);
+ g_test.assertEquals(7, pack.objects[0].getParam('newParam').value);
+};
+
+g_suite.testBindsParam = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Matrix4Translation': [
+ {
+ params: {
+ 'o3d.outputMatrix': {id: 2}
+ }
+ },
+ {
+ params: {
+ 'o3d.inputMatrix': {bind: 2}
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(2, pack.objects.length);
+ g_test.assertEquals(pack.objects[0].getParam('o3d.outputMatrix').clientId,
+ pack.objects[1].getParam('o3d.inputMatrix').inputConnection.clientId);
+};
+
+g_suite.testIgnoresMissingOutputs = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Matrix4Translation': [
+ {
+ params: {
+ missingParam: {id: 2}
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(1, pack.objects.length);
+ g_test.assertNull(pack.objects[0].getParam('misingParam'));
+};
+
+g_suite.testExceptionIfCannotBindParam = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Matrix4Translation': [
+ {
+ params: {
+ 'o3d.inputMatrix': {bind: 2}
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var exception = null;
+ try {
+ o3djs.serialization.deserialize(pack, json);
+ } catch(e) {
+ exception = e;
+ }
+
+ g_test.assertEquals('Could not find output param with id 2.', exception);
+};
+
+g_suite.testCannotBindToMissingOutput = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Matrix4Translation': [
+ {
+ params: {
+ missingOutput: {id: 2}
+ }
+ },
+ {
+ params: {
+ 'o3d.inputMatrix': {bind: 2}
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var exception = null;
+ try {
+ o3djs.serialization.deserialize(pack, json);
+ } catch(e) {
+ exception = e;
+ }
+
+ g_test.assertEquals('Could not find output param with id 2.', exception);
+};
+
+g_suite.testDeserializesParamArray = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.ParamArray': [
+ {
+ params: [
+ {'class': 'o3d.ParamFloat', value: 7},
+ {'class': 'o3d.ParamBoolean', value: true}
+ ]
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(1, pack.objects.length);
+ var array = pack.objects[0];
+ g_test.assertEquals(2, array.length);
+ g_test.assertEquals(7, array.getParam(0).value);
+ g_test.assertEquals(true, array.getParam(1).value);
+};
+
+g_suite.testDeserializesParamArrayWithBindings = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.ParamArray': [
+ {
+ params: [
+ {'class': 'o3d.ParamFloat', id: 1, value: 7},
+ {'class': 'o3d.ParamBoolean', id: 2, value: true},
+ {'class': 'o3d.ParamFloat', id: 3, bind: 1}
+ ]
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(1, pack.objects.length);
+ var array = pack.objects[0];
+ g_test.assertEquals(3, array.length);
+ g_test.assertEquals(array.getParam(0).clientId,
+ array.getParam(2).inputConnection.clientId);
+};
+
+g_suite.testDeserializerObjectShouldDeserializeIncrementallyWhenRunRepeatedly = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ o3d_rootObject_root: {ref: 7},
+ objects: {
+ 'o3d.Transform': [
+ {
+ id: 7,
+ properties: {
+ name: 'transform1'
+ }
+ },
+ {
+ properties: {
+ name: 'transform2'
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ g_test.assertEquals(0, pack.objects.length);
+ var running;
+ running = deserializer.run(1);
+ g_test.assertEquals(true, running);
+ g_test.assertEquals(1, pack.objects.length);
+ running = deserializer.run(1);
+ g_test.assertEquals(true, running);
+ g_test.assertEquals(2, pack.objects.length);
+ running = deserializer.run(1);
+ g_test.assertEquals(true, running);
+ g_test.assertEquals(2, pack.objects.length);
+ running = deserializer.run(1);
+ g_test.assertEquals(false, running);
+ g_test.assertEquals(2, pack.objects.length);
+
+ g_test.assertEquals('o3d.Transform', pack.objects[0].className);
+ g_test.assertEquals('transform1', pack.objects[0].name);
+ g_test.assertEquals('o3d.Transform', pack.objects[1].className);
+ g_test.assertEquals('transform2', pack.objects[1].name);
+
+ running = deserializer.run(1);
+ g_test.assertEquals(false, running);
+ g_test.assertEquals(2, pack.objects.length);
+};
+
+g_suite.testDeserializesTexture2DByCallingSpecialCreateFunction = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Texture2D': [
+ {
+ params: {
+ myParam: {'class':'o3d.ParamFloat', value: 7}
+ },
+ custom: {
+ width: 256,
+ height: 256,
+ format: 1,
+ levels: 2,
+ renderSurfacesEnabled: true
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(1, pack.objects.length);
+ g_test.assertEquals('o3d.Texture2D', pack.objects[0].className);
+ g_test.assertEquals(256, pack.objects[0].width);
+ g_test.assertEquals(256, pack.objects[0].height);
+ g_test.assertEquals(1, pack.objects[0].format);
+
+ var myParam = pack.objects[0].getParam("myParam");
+ g_test.assertEquals(7, myParam.value);
+};
+
+g_suite.testFindsTextureFromArchive = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Texture2D': [
+ {
+ params: {
+ 'o3d.uri': {
+ 'class': 'o3d.ParamString',
+ value: 'dir/image1.png'
+ }
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ deserializer.run();
+
+ g_test.assertEquals(1, pack.objects.length);
+ g_test.assertEquals('o3d.Texture2D', pack.objects[0].className);
+ g_test.assertEquals(16, pack.objects[0].width);
+ g_test.assertEquals(16, pack.objects[0].height);
+ g_test.assertEquals(5, pack.objects[0].levels);
+};
+
+g_suite.testExceptionIfCannotFindTexture = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Texture2D': [
+ {
+ params: {
+ 'o3d.uri': {
+ 'class': 'o3d.ParamString',
+ value: 'dir/missing.png'
+ }
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ var error = '';
+ try {
+ deserializer.run();
+ } catch (e) {
+ error = e;
+ }
+
+ g_test.assertEquals('Could not find texture dir/missing.png in the archive',
+ error);
+};
+
+g_suite.testDeserializesTextureCUBEByCallingSpecialCreateFunction = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.TextureCUBE': [
+ {
+ params: {
+ myParam: {'class':'o3d.ParamFloat', value: 7}
+ },
+ custom: {
+ edgeLength: 256,
+ format: 1,
+ levels: 2,
+ renderSurfacesEnabled: true
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ o3djs.serialization.deserialize(pack, json);
+
+ g_test.assertEquals(1, pack.objects.length);
+ g_test.assertEquals('o3d.TextureCUBE', pack.objects[0].className);
+ g_test.assertEquals(256, pack.objects[0].edgeLength);
+ g_test.assertEquals(1, pack.objects[0].format);
+
+ var myParam = pack.objects[0].getParam("myParam");
+ g_test.assertEquals(7, myParam.value);
+};
+
+
+g_suite.testFindsCUBETextureFromArchive = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.TextureCUBE': [
+ {
+ params: {
+ 'o3d.uri': {
+ 'class': 'o3d.ParamString',
+ value: 'dir/image1.png'
+ }
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ deserializer.run();
+
+ g_test.assertEquals(1, pack.objects.length);
+ g_test.assertEquals('o3d.Texture2D', pack.objects[0].className);
+ g_test.assertEquals(16, pack.objects[0].width);
+ g_test.assertEquals(16, pack.objects[0].height);
+ g_test.assertEquals(5, pack.objects[0].levels);
+};
+
+g_suite.testExceptionIfCannotFindCUBETexture = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.TextureCUBE': [
+ {
+ params: {
+ 'o3d.uri': {
+ 'class': 'o3d.ParamString',
+ value: 'dir/missing.png'
+ }
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ var error = '';
+ try {
+ deserializer.run();
+ } catch (e) {
+ error = e;
+ }
+
+ g_test.assertEquals('Could not find texture dir/missing.png in the archive',
+ error);
+};
+
+g_suite.testShouldSetVertexBufferData = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.VertexBuffer': [
+ {
+ custom: {
+ fields: [7,8,9],
+ binaryRange: [5310,10620]
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ deserializer.run();
+
+ g_test.assertEquals(1, pack.objects.length);
+ var buffer = pack.objects[0];
+ g_test.assertEquals(1, buffer.fields.length);
+ var field = buffer.fields[0];
+ g_test.assertEquals('o3d.FloatField', field.className);
+ g_test.assertEquals(3, field.numComponents);
+ g_test.assertEquals(12, field.size);
+};
+
+g_suite.testShouldSetSourceBufferData = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.SourceBuffer': [
+ {
+ custom: {
+ fields: [7,8,9],
+ binaryRange: [5310,10620]
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ deserializer.run();
+
+ g_test.assertEquals(1, pack.objects.length);
+ var buffer = pack.objects[0];
+ g_test.assertEquals(1, buffer.fields.length);
+ var field = buffer.fields[0];
+ g_test.assertEquals('o3d.FloatField', field.className);
+ g_test.assertEquals(3, field.numComponents);
+ g_test.assertEquals(12, field.size);
+};
+
+g_suite.testShouldSetIndexBufferData = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.IndexBuffer': [
+ {
+ custom: {
+ fields:[7],
+ binaryRange:[9618,10092]
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ deserializer.run();
+
+ g_test.assertEquals(1, pack.objects.length);
+ var buffer = pack.objects[0];
+ g_test.assertEquals(1, buffer.fields.length);
+ var field = buffer.fields[0];
+ g_test.assertEquals('o3d.UInt32Field', field.className);
+ g_test.assertEquals(1, field.numComponents);
+ g_test.assertEquals(4, field.size);
+ g_test.assertArrayEquals([0, 1, 2, 0, 2], field.getAt(0, 5));
+};
+
+g_suite.testShouldSetCurveWithKeys = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Curve': [
+ {
+ custom: {
+ binaryRange: [358,716]
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ deserializer.run();
+
+ g_test.assertEquals(1, pack.objects.length);
+ var keys = pack.objects[0].keys;
+ g_test.assertEquals(14, keys.length);
+};
+
+g_suite.testShouldSetSkinEvalStreams = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.SkinEval': [
+ {
+ custom: {
+ vertexStreams: [
+ {
+ stream: {
+ field: 8,
+ startIndex: 4,
+ semantic: 1,
+ semanticIndex: 2
+ }
+ }
+ ]
+ }
+ }
+ ],
+ 'o3d.SourceBuffer': [
+ {
+ id: 7,
+ custom: {
+ fields: [8,9,10],
+ binaryRange: [5310,10620]
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ deserializer.run();
+
+ g_test.assertEquals(2, pack.objects.length);
+ var stream = pack.objects[0].vertexStreams[0];
+ var field = pack.objects[1].fields[0];
+ g_test.assertEquals(field.clientId, stream.field.clientId);
+ g_test.assertEquals(4, stream.startIndex);
+ g_test.assertEquals(1, stream.semantic);
+ g_test.assertEquals(2, stream.semanticIndex);
+};
+
+g_suite.testShouldSetStreamBankStreams = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.StreamBank': [
+ {
+ custom: {
+ vertexStreams: [
+ {
+ stream: {
+ field: 8,
+ startIndex: 4,
+ semantic: 1,
+ semanticIndex: 2
+ }
+ }
+ ]
+ }
+ }
+ ],
+ 'o3d.VertexBuffer': [
+ {
+ id: 7,
+ custom: {
+ fields: [8],
+ binaryRange: [5310,10620]
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ deserializer.run();
+
+ g_test.assertEquals(2, pack.objects.length);
+ var stream = pack.objects[0].vertexStreams[0];
+ var field = pack.objects[1].fields[0];
+ g_test.assertEquals(field.clientId, stream.field.clientId);
+ g_test.assertEquals(4, stream.startIndex);
+ g_test.assertEquals(1, stream.semantic);
+ g_test.assertEquals(2, stream.semanticIndex);
+};
+
+g_suite.testShouldSetAndBindStreamBankStreams = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.SkinEval': [
+ {
+ id: 7,
+ custom: {
+ vertexStreams: [
+ {
+ stream: {
+ field: 9,
+ startIndex: 4,
+ semantic: 1,
+ semanticIndex: 2
+ }
+ }
+ ]
+ }
+ }
+ ],
+ 'o3d.SourceBuffer': [
+ {
+ id: 8,
+ custom: {
+ fields: [9],
+ binaryRange: [5310,10620]
+ }
+ }
+ ],
+ 'o3d.StreamBank': [
+ {
+ custom: {
+ vertexStreams: [
+ {
+ stream: {
+ field: 11,
+ startIndex: 4,
+ semantic: 1,
+ semanticIndex: 2
+ },
+ bind: 7
+ }
+ ]
+ }
+ }
+ ],
+ 'o3d.VertexBuffer': [
+ {
+ id: 10,
+ custom: {
+ fields: [11],
+ binaryRange: [5310,10620]
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ deserializer.run();
+
+ g_test.assertEquals(4, pack.objects.length);
+ g_test.assertTrue(pack.objects[2].unbindStream(1, 2));
+};
+
+g_suite.testShouldSetAndBindSkinEvalStreams = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.SkinEval': [
+ {
+ id: 7,
+ custom: {
+ vertexStreams: [
+ {
+ stream: {
+ field: 9,
+ startIndex: 4,
+ semantic: 1,
+ semanticIndex: 2
+ }
+ }
+ ]
+ }
+ },
+ {
+ id: 14,
+ custom: {
+ vertexStreams: [
+ {
+ stream: {
+ field: 11,
+ startIndex: 4,
+ semantic: 1,
+ semanticIndex: 2
+ },
+ bind: 7
+ }
+ ]
+ }
+ }
+ ],
+ 'o3d.SourceBuffer': [
+ {
+ id: 8,
+ custom: {
+ fields: [9],
+ binaryRange: [5310,10620]
+ }
+ },
+ {
+ id: 10,
+ custom: {
+ fields: [11],
+ binaryRange: [5310,10620]
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ deserializer.run();
+
+ g_test.assertEquals(4, pack.objects.length);
+ g_test.assertTrue(pack.objects[1].unbindStream(1, 2));
+};
+
+g_suite.testShouldLoadEffectShaderFromArchive = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Effect': [
+ {
+ params: {
+ 'o3d.uri': {
+ 'class': 'o3d.ParamString',
+ value: 'dir/solid-color.shader'
+ }
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ deserializer.run();
+
+ g_test.assertEquals(1, pack.objects.length);
+ var effect = pack.objects[0];
+ g_test.assertEquals('o3d.Effect', effect.className);
+ g_test.assertEquals('//', effect.source.substring(0, 2));
+};
+
+g_suite.testExceptionIfCannotLoadShaderFromArchive = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Effect': [
+ {
+ params: {
+ 'o3d.uri': {
+ 'class': 'o3d.ParamString',
+ value: 'dir/image1.png'
+ }
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ var error = '';
+ try {
+ deserializer.run();
+ } catch (e) {
+ error = e;
+ }
+
+ g_test.assertEquals('Cannot load shader dir/image1.png in archive.', error);
+};
+
+g_suite.testShouldSetSkinData = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Skin': [
+ {
+ custom: {
+ binaryRange: [32724,34584]
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ deserializer.run();
+
+ g_test.assertEquals(1, pack.objects.length);
+ var skin = pack.objects[0];
+ g_test.assertEquals(79, skin.influences.length);
+ g_test.assertEquals(2, skin.influences[0].length);
+ g_test.assertArrayEquals([2, 1], skin.influences[0]);
+};
+
+g_suite.testExceptionIfEffectShaderCannotBeFound = function() {
+ var json = {
+ version: o3djs.serialization.supportedVersion,
+ objects: {
+ 'o3d.Effect': [
+ {
+ params: {
+ 'o3d.uri': {
+ 'class': 'o3d.ParamString',
+ value: 'dir/missing.shader'
+ }
+ }
+ }
+ ]
+ }
+ };
+
+ var pack = g_client.createPack();
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.archiveInfo = g_archiveInfo;
+ var error = '';
+ try {
+ deserializer.run();
+ } catch (e) {
+ error = e;
+ }
+
+ g_test.assertEquals(
+ 'Cannot find shader dir/missing.shader in archive.',
+ error);
+};
+
+function initStep3(archiveInfo, exception) {
+ window.g_archiveInfo = archiveInfo;
+ window.g_testResult = g_test.runTests(g_suite);
+}
+
+function initStep2(clientElements) {
+ window.g_test = o3djs.test;
+ window.g_plugin = clientElements[0];
+ window.g_client = clientElements[0].client;
+ window.g_pack = g_client.createPack();
+ window.g_client.clearErrorCallback();
+ o3djs.io.loadArchive(g_pack, 'assets/archive.o3dtgz', initStep3);
+};
+
+window.onload = function() {
+ o3djs.util.makeClients(initStep2);
+};
+
+</script>
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/test-test.html b/o3d/tests/selenium/tests/test-test.html
new file mode 100644
index 0000000..06d6a0d
--- /dev/null
+++ b/o3d/tests/selenium/tests/test-test.html
@@ -0,0 +1,368 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Unit Testing library tests
+</title>
+</head>
+<body>
+<h1>Unit Testing library tests</h1>
+This tests the operation of the unit testing library.
+<br/>
+<script type="text/javascript"
+ src="../../../samples/o3djs/base.js"></script>
+<script type="text/javascript">
+
+o3djs.require('o3djs.test');
+
+function MockReporter() {
+ this.reportPass = function(testName) {
+ this.passName = testName;
+ }
+ this.reportFail = function(testName, message) {
+ this.failName = testName;
+ this.failMessage = message;
+ }
+ this.reportSummary = function(passCount, failCount) {
+ this.passCount = passCount;
+ this.failCount = failCount;
+ }
+};
+
+var g_suite = {};
+
+g_suite.testRunsFunctionsWithTestPrefix = function() {
+ var ran = false;
+ var tests = {
+ testFoo: function() {
+ ran = true;
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertTrue(ran);
+ g_test.assertEquals('testFoo', reporter.passName);
+ g_test.assertEquals('undefined', typeof(reporter.failName));
+};
+
+g_suite.testDoesNotRunFunctionsWithoutTestPrefix = function() {
+ var ran = false;
+ var tests = {
+ notATest: function() {
+ ran = true;
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertFalse(ran);
+ g_test.assertEquals('undefined', typeof(reporter.passName));
+ g_test.assertEquals('undefined', typeof(reporter.failName));
+};
+
+g_suite.testDoesNotRunValuesThatAreNotFunctions = function() {
+ var tests = {
+ testFoo: 7
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertEquals('undefined', typeof(reporter.passName));
+ g_test.assertEquals('undefined', typeof(reporter.failName));
+};
+
+g_suite.testReportsNumberOfPassingTests = function() {
+ var tests = {
+ testFoo: function() {
+ },
+ testBar: function() {
+ },
+ testBaz: function() {
+ assertTrue(false);
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertEquals(2, reporter.passCount);
+ g_test.assertEquals(1, reporter.failCount);
+};
+
+g_suite.testReturnsTrueIfAllTestsPass = function() {
+ var tests = {
+ testFoo: function() {
+ },
+ testBar: function() {
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.assertTrue(g_test.runTests(tests, reporter));
+};
+
+g_suite.testReturnsFalseIfAnyTestFails = function() {
+ var tests = {
+ testFoo: function() {
+ },
+ testBar: function() {
+ g_test.assertTrue(false);
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.assertFalse(g_test.runTests(tests, reporter));
+};
+
+g_suite.testAssertTrueDoesNotAssertForTrueValue = function() {
+ g_test.assertTrue(true);
+};
+
+g_suite.testAssertTrueAssertsForFalseValue = function() {
+ var tests = {
+ testFoo: function() {
+ g_test.assertTrue(false);
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertEquals('undefined', typeof(reporter.passName));
+ g_test.assertEquals('testFoo', reporter.failName);
+ g_test.assertEquals(
+ 'assertTrue failed for false',
+ reporter.failMessage);
+};
+
+g_suite.testAssertFalseDoesNotAssertForFalseValue = function() {
+ g_test.assertFalse(false);
+};
+
+g_suite.testAssertFalseAssertsForTrueValue = function() {
+ var tests = {
+ testFoo: function() {
+ g_test.assertFalse(true);
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertEquals('undefined', typeof(reporter.passName));
+ g_test.assertEquals('testFoo', reporter.failName);
+ g_test.assertEquals(
+ 'assertFalse failed for true',
+ reporter.failMessage);
+};
+
+g_suite.testAssertNullDoesNotAssertForNullValue = function() {
+ g_test.assertNull(null);
+};
+
+g_suite.testAssertNullAssertsForNonNullValue = function() {
+ var tests = {
+ testFoo: function() {
+ g_test.assertNull(undefined);
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertEquals('undefined', typeof(reporter.passName));
+ g_test.assertEquals('testFoo', reporter.failName);
+ g_test.assertEquals(
+ 'assertNull failed for undefined',
+ reporter.failMessage);
+};
+
+g_suite.testAssertEqualsDoesNotAssertForEqualValue = function() {
+ g_test.assertEquals(7, 7);
+};
+
+g_suite.testAssertEqualsAssertsForUnequalValue = function() {
+ var tests = {
+ testFoo: function() {
+ g_test.assertEquals(6, 7);
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertEquals('undefined', typeof(reporter.passName));
+ g_test.assertEquals('testFoo', reporter.failName);
+ g_test.assertEquals(
+ 'assertEquals failed: expected 6 but got 7',
+ reporter.failMessage);
+};
+
+g_suite.testStringValuesAreReportedInQuotes = function() {
+ var tests = {
+ testFoo: function() {
+ g_test.assertEquals('a', 'b');
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertEquals('undefined', typeof(reporter.passName));
+ g_test.assertEquals('testFoo', reporter.failName);
+ g_test.assertEquals(
+ 'assertEquals failed: expected "a" but got "b"',
+ reporter.failMessage);
+};
+
+g_suite.testObjectsWithDifferentIdentityAreNotEqual = function() {
+ var tests = {
+ testFoo: function() {
+ g_test.assertEquals({a: 7, b: 8, f: function() {}},
+ {a: 7, b: 8, f: function() {}});
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertEquals('undefined', typeof(reporter.passName));
+ g_test.assertEquals('testFoo', reporter.failName);
+ g_test.assertEquals(
+ 'assertEquals failed: expected {a: 7, b: 8} but got {a: 7, b: 8}',
+ reporter.failMessage);
+};
+
+g_suite.testAssertCloseDoesNotAssertForEqualValue = function() {
+ g_test.assertClose(7, 7);
+};
+
+g_suite.testAssertCloseDoesNotAssertForSlightlySmallerActual = function() {
+ g_test.assertClose(7, 6.9991);
+};
+
+g_suite.testAssertCloseDoesNotAssertForSlightlyLargerActual = function() {
+ g_test.assertClose(7, 7.0009);
+};
+
+g_suite.testAssertCloseAssertsForValueOutsideThreshold= function() {
+ var tests = {
+ testFoo: function() {
+ g_test.assertClose(7, 7.0015);
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertEquals('undefined', typeof(reporter.passName));
+ g_test.assertEquals('testFoo', reporter.failName);
+ g_test.assertEquals(
+ 'assertClose failed: expected 7 but got 7.0015',
+ reporter.failMessage);
+};
+g_suite.testAssertArrayEqualsDoesNotAssertForEqualArrays = function() {
+ g_test.assertArrayEquals([1, 2, 3], [1, 2, 3]);
+};
+
+g_suite.testAssertArrayEqualsAssertsForNonArrayExpectedValue = function() {
+ var tests = {
+ testFoo: function() {
+ g_test.assertArrayEquals(7, [1, 2, 3]);
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertEquals('undefined', typeof(reporter.passName));
+ g_test.assertEquals('testFoo', reporter.failName);
+ g_test.assertEquals(
+ 'assertArrayEquals failed: expected value 7 is not an array',
+ reporter.failMessage);
+}
+
+g_suite.testAssertArrayEqualsAssertsForNonArrayActualValue = function() {
+ var tests = {
+ testFoo: function() {
+ g_test.assertArrayEquals([1, 2], 7);
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertEquals('undefined', typeof(reporter.passName));
+ g_test.assertEquals('testFoo', reporter.failName);
+ g_test.assertEquals(
+ 'assertArrayEquals failed: actual value 7 is not an array',
+ reporter.failMessage);
+};
+
+g_suite.testAssertArrayEqualsAssertsForArraysOfDifferingLength = function() {
+ var tests = {
+ testFoo: function() {
+ g_test.assertArrayEquals([1, 2], [1, 2, 3]);
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertEquals('undefined', typeof(reporter.passName));
+ g_test.assertEquals('testFoo', reporter.failName);
+ g_test.assertEquals(
+ 'assertArrayEquals failed: expected [1, 2] but got [1, 2, 3]',
+ reporter.failMessage);
+};
+
+g_suite.testAssertArrayEqualsAssertsForArraysWithDifferingElement = function() {
+ var tests = {
+ testFoo: function() {
+ g_test.assertArrayEquals([3, 2, 1], [1, 2, 3]);
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertEquals('undefined', typeof(reporter.passName));
+ g_test.assertEquals('testFoo', reporter.failName);
+ g_test.assertEquals(
+ 'assertArrayEquals failed: expected [3, 2, 1] but got [1, 2, 3]',
+ reporter.failMessage);
+};
+
+g_suite.testAssertArrayEqualsDoesNotAssertForEqual2DArrays = function() {
+ g_test.assertArrayEquals([[1, 2, 3]], [[1, 2, 3]]);
+};
+
+g_suite.testAssertArrayEqualsAssertsFor2DArraysWithDifferingElement =
+ function() {
+ var tests = {
+ testFoo: function() {
+ g_test.assertArrayEquals([[3, 2, 1]], [[1, 2, 3]]);
+ }
+ };
+ var reporter = new MockReporter();
+ g_test.runTests(tests, reporter);
+ g_test.assertEquals('undefined', typeof(reporter.passName));
+ g_test.assertEquals('testFoo', reporter.failName);
+ g_test.assertEquals(
+ 'assertArrayEquals failed: expected [[3, 2, 1]] but got [[1, 2, 3]]',
+ reporter.failMessage);
+};
+
+window.onload = function() {
+ window.g_test = o3djs.test;
+ window.g_testResult = g_test.runTests(g_suite);
+};
+
+</script>
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/type-test.html b/o3d/tests/selenium/tests/type-test.html
new file mode 100644
index 0000000..fe88c72
--- /dev/null
+++ b/o3d/tests/selenium/tests/type-test.html
@@ -0,0 +1,166 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+A Selenium test to make sure various math types convert.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Type Test
+</title>
+<!-- Our javascript code -->
+<script type="text/javascript" src="../../../samples/o3djs/base.js"></script>
+
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+
+window.onload = init;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_pack;
+var g_testResult;
+
+function compare3(object, x, y, z) {
+ return object[0] == x &&
+ object[1] == y &&
+ object[2] == z;
+}
+
+function compare4(object, x, y, z, w) {
+ return object[0] == x &&
+ object[1] == y &&
+ object[2] == z &&
+ object[3] == w;
+}
+
+function compareArrays(array1, array2) {
+ if (array1.length != array2.length) {
+ return false;
+ }
+ for (var ii = 0; ii < array1.length; ++ii) {
+ if (array1[ii] !== array2[ii]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes g_o3d, loads the effect, and draws the sphere.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ var o3d = clientElements[0];
+ g_o3d = o3d.o3d;
+ g_math = o3djs.math;
+ g_client = o3d.client;
+
+ // Create a g_pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ var good = true;
+ // we shouldn't get an error while we're testing any of these.
+ g_client.setErrorCallback(function(msg) { good = false; });
+
+ var transform = g_pack.createObject('Transform');
+ var paramFloat3 = transform.createParam('f3', 'ParamFloat3');
+ var paramFloat4 = transform.createParam('f4', 'ParamFloat4');
+ var float3;
+ var float4;
+ var test = 0;
+
+ // --- float3 ---
+
+ // Check that we can set a ParamFloat3 to an array
+ if (good) {
+ test++;
+ paramFloat3.value = [1, 2, 3];
+ good = good && compare3(paramFloat3.value, 1, 2, 3);
+ }
+
+ // Check that we can get an array from a ParamFloat3
+ if (good) {
+ test++;
+ float3 = paramFloat3.value;
+ good = good && compare3(float3, 1, 2, 3);
+ }
+
+ // --- float4 ---
+
+ // Check that we can set a ParamFloat4 to an array
+ if (good) {
+ test++;
+ paramFloat4.value = [1, 2, 3, 4];
+ good = good && compare4(paramFloat4.value, 1, 2, 3, 4);
+ }
+
+ // Check that we can get an array from a ParamFloat4
+ if (good) {
+ test++;
+ float4 = paramFloat4.value;
+ good = good && compare4(float4, 1, 2, 3, 4);
+ }
+
+ document.getElementById('testStatus').innerHTML = "Finished";
+ document.getElementById('testResults').innerHTML =
+ good ? "Passed" : ("Failed: at test# " + test);
+ g_testResult = good;
+}
+
+</script>
+</head>
+<body>
+<h1>Type Test</h1>
+<table>
+<tr><td>Tests:</td><td><span id="testStatus">Running</span></td></td>
+<tr><td>Results:</td><td><span id="testResults">---</span></td></td>
+</table>
+<!-- Start of g_o3d plugin -->
+<div id="o3d" style="width: 100px; height: 100px;"></div>
+<!-- End of g_o3d plugin -->
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/util-test.html b/o3d/tests/selenium/tests/util-test.html
new file mode 100644
index 0000000..9785ed3
--- /dev/null
+++ b/o3d/tests/selenium/tests/util-test.html
@@ -0,0 +1,111 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Util Test
+</title>
+</head>
+<body>
+<h1>Util Test</h1>
+This tests util.js.
+<br/>
+
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 32px; height: 32px;"></div>
+<!-- End of O3D plugin -->
+
+<script type="text/javascript"
+ src="../../../samples/o3djs/base.js"></script>
+<script type="text/javascript">
+
+o3djs.require('o3djs.test');
+o3djs.require('o3djs.util');
+
+g_suite = {};
+
+function add(a, b) {
+ return a + b;
+}
+
+g_suite.testCanCurryAFunctionFixingNoArguments = function() {
+ var newAdd = o3djs.util.curry(add);
+ g_test.assertEquals(3, newAdd(1, 2));
+}
+
+g_suite.testCanCurryAFunctionFixingSomeArguments = function() {
+ var increment = o3djs.util.curry(add, 1);
+ g_test.assertEquals(3, increment(2));
+}
+
+g_suite.testCanCurryAFunctionFixingAllArguments = function() {
+ var three = o3djs.util.curry(add, 1, 2);
+ g_test.assertEquals(3, three());
+}
+
+g_suite.testLeavesAbsoluteURIUnchanged = function() {
+ g_test.assertEquals(
+ 'http://www.domain.org/foo/bar.html',
+ o3djs.util.toAbsoluteUri('http://www.domain.org/foo/bar.html'));
+};
+
+g_suite.testAddsBaseURIToRelativeURI = function() {
+ g_test.assertEquals(
+ g_baseUri + '/foo/bar.html',
+ o3djs.util.toAbsoluteUri('foo/bar.html'));
+};
+
+g_suite.testStripsDotDotFromAbsoluteURI = function() {
+ g_test.assertEquals(
+ g_baseUri + '/bing/foo/baz.html',
+ o3djs.util.toAbsoluteUri('bing/foo/bar/../../foo/baz.html'));
+};
+
+function initStep2(clientElements) {
+ g_test = o3djs.test;
+ g_plugin = clientElements[0];
+ g_baseUri = document.location.toString();
+ var lastSlash = g_baseUri.lastIndexOf('/');
+ g_baseUri = g_baseUri.substring(0, lastSlash);
+ g_testResult = g_test.runTests(g_suite);
+};
+
+window.onload = function() {
+ o3djs.util.makeClients(initStep2);
+};
+
+</script>
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/v8-test.html b/o3d/tests/selenium/tests/v8-test.html
new file mode 100644
index 0000000..fa8173f
--- /dev/null
+++ b/o3d/tests/selenium/tests/v8-test.html
@@ -0,0 +1,772 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+V8 Test
+</title>
+</head>
+<body>
+<h1>V8 / ActiveX Bridge Tests</h1>
+This tests V8/NPAPI interoperation and the ActiveX bridge.
+<br/>
+
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 32px; height: 32px;"></div>
+<!-- End of O3D plugin -->
+
+<script type="text/javascript"
+ src="../../../samples/o3djs/base.js"></script>
+<script type="text/javascript">
+
+o3djs.require('o3djs.test');
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+
+var isChrome = navigator.userAgent.indexOf('Chrome') != -1;
+var isSafari = !isChrome && navigator.userAgent.indexOf('Safari') != -1;
+var isFirefox2 = navigator.userAgent.indexOf('Firefox/2.') != -1;
+var isFirefox = navigator.userAgent.indexOf('Firefox') != -1;
+var isInternetExplorer = navigator.userAgent.indexOf('Internet Explorer' != -1);
+
+var g_suite = {};
+
+g_suite.testEvaluatesUndefinedExpression = function() {
+ g_test.assertEquals(undefined, window.g_plugin.eval('undefined'));
+};
+
+g_suite.testEvaluatesNullExpression = function() {
+ g_test.assertEquals(null, window.g_plugin.eval('null'));
+};
+
+g_suite.testEvaluatesDoubleExpression = function() {
+ g_test.assertEquals(4.5, window.g_plugin.eval('2.25+2.25'));
+};
+
+g_suite.testEvaluatesInt32Expression = function() {
+ g_test.assertEquals(4, window.g_plugin.eval('2+2'));
+};
+
+g_suite.testEvaluatesBooleanExpression = function() {
+ g_test.assertTrue(window.g_plugin.eval('true || true'));
+};
+
+g_suite.testEvaluatesStringExpression = function() {
+ g_test.assertEquals('ab', window.g_plugin.eval('"a" + "b"'));
+};
+
+g_suite.testWrapsAndUnwrapsV8ObjectsAndPreservesIdentity = function() {
+ var a = window.g_plugin.eval('var a = {}; a');
+ var v8f = window.g_plugin.eval('function(x) { return a === x; }');
+ g_test.assertTrue(v8f(a));
+};
+
+g_suite.testBrowserConsidersTheSamePluginToBeTheSame = function() {
+ g_test.assertEquals(g_plugin, g_plugin);
+};
+
+g_suite.testBrowserConsidersTheSameNPObjectReturnedMultipleTimesToBeTheSame =
+ function() {
+ // http://code.google.com/p/chromium/issues/detail?id=5751
+ if (!isChrome && !isSafari) {
+ g_test.assertEquals(g_plugin.client, g_plugin.client);
+ }
+};
+
+g_suite.testBrowserConsidersTheSameV8ObjectReturnedMultipleTimesToBeTheSame =
+ function() {
+ // http://code.google.com/p/chromium/issues/detail?id=5751
+ if (!isChrome && !isSafari) {
+ window.g_plugin.eval('var a = {};');
+ var obj1 = window.g_plugin.eval('a');
+ var obj2 = window.g_plugin.eval('a');
+ g_test.assertEquals(obj1, obj2);
+ }
+};
+
+g_suite.testTypeofV8ObjectInBrowserIsObject = function() {
+ var a = window.g_plugin.eval('({})');
+ if (!isSafari) {
+ g_test.assertEquals('object', typeof(a));
+ } else {
+ // Safari thinks its a function!
+ g_test.assertEquals('function', typeof(a));
+ }
+};
+
+g_suite.testTypeofV8FunctionInBrowserIsFunction = function() {
+ var v8f = window.g_plugin.eval('function() {}');
+ g_test.assertEquals('function', typeof(v8f));
+};
+
+g_suite.testV8FunctionIsInstanceOfFunctionInBrowser = function() {
+ var v8f = window.g_plugin.eval('function(x) { return typeof(x); }');
+ g_test.assertTrue(v8f instanceof Function);
+};
+
+g_suite.testV8FunctionKeepsIdentityWhenPassedBackIntoV8 = function() {
+ var v8f = window.g_plugin.eval(
+ 'g_originalFunction = function(x) { return typeof(x); }');
+ var comparer = window.g_plugin.eval(
+ 'function(func) { return func === g_originalFunction; }');
+ g_test.assertTrue(comparer(v8f));
+};
+
+g_suite.testBrowserCanInvokeV8Functions = function() {
+ var v8f = window.g_plugin.eval('function(x) { return x; }');
+ g_test.assertEquals(2, v8f(2));
+};
+
+g_suite.testBrowserCanInvokeV8Constructors = function() {
+ // http://code.google.com/p/chromium/issues/detail?id=3285
+ if (!isChrome && !isSafari && !isFirefox2) {
+ var v8f = window.g_plugin.eval('function(x) { this.p = x; }');
+ var obj = new v8f(2);
+ g_test.assertEquals(2, obj.p);
+ }
+};
+
+g_suite.testBrowserCalledV8ConstructorsReturnDistinctObjects = function() {
+ // http://code.google.com/p/chromium/issues/detail?id=3285
+ if (!isChrome && !isSafari && !isFirefox2) {
+ var v8f = window.g_plugin.eval('function(x) { this.p = x; }');
+ var obj1 = new v8f(2);
+ var obj2 = new v8f(3);
+ g_test.assertFalse(obj1 === obj2);
+ }
+};
+
+g_suite.testBrowserCanGetV8Properties = function() {
+ var a = window.g_plugin.eval('({ p : 123 })');
+ g_test.assertEquals(123, a.p);
+};
+
+g_suite.testBrowserCanSetV8Properties = function() {
+ var a = window.g_plugin.eval('({ p : 123 })');
+ a.p = 321;
+ g_test.assertEquals(321, a.p);
+};
+
+g_suite.testBrowserCanGetV8ArrayElements = function() {
+ var a = window.g_plugin.eval('([1, 2, 3])');
+ g_test.assertEquals(2, a[1]);
+};
+
+g_suite.testBrowserCanGetV8ArrayLength = function() {
+ var a = window.g_plugin.eval('([1, 2, 3])');
+ g_test.assertEquals(3, a.length);
+};
+
+g_suite.testBrowserCanSetV8ArrayElements = function() {
+ var a = window.g_plugin.eval('([1, 2, 3])');
+ a[1] = 7;
+ g_test.assertEquals(7, a[1]);
+};
+
+g_suite.testBrowserCanSetV8ArrayLength = function() {
+ var a = window.g_plugin.eval('([1, 2, 3])');
+ a.length = 10;
+ g_test.assertEquals(10, a.length);
+};
+
+g_suite.testBrowserCanIterateOverV8Array = function() {
+ // Chrome bug: http://code.google.com/p/chromium/issues/detail?id=5743
+ if (!isChrome && !isFirefox2) {
+ var a = window.g_plugin.eval('([1, 2, 3])');
+ var total = 0;
+ for (var i in a) {
+ total += a[i];
+ }
+ g_test.assertEquals(6, total);
+ }
+};
+
+g_suite.testBrowserCanQueryV8Properties = function() {
+ var a = window.g_plugin.eval('({ p : 123 })');
+ g_test.assertTrue('p' in a);
+ g_test.assertFalse('q' in a);
+ g_test.assertTrue(a.hasOwnProperty('p'));
+ g_test.assertFalse(a.hasOwnProperty('q'));
+};
+
+g_suite.testBrowserCanQueryV8PropertiesThatAreUndefined = function() {
+ var a = window.g_plugin.eval('({ p : undefined })');
+ g_test.assertTrue('p' in a);
+};
+
+g_suite.testBrowserCanRemoveV8Properties = function() {
+ // Firefox does not forward property deletions from the browser
+ // through the NPAPI. Bug:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=470291
+ // Neither does Chrome:
+ // http://code.google.com/p/chromium/issues/detail?id=5746
+ if (!isChrome && !isSafari && !isFirefox) {
+ var a = window.g_plugin.eval('({ p : 123 })');
+ g_test.assertTrue(delete a.p);
+ g_test.assertFalse('p' in a);
+ }
+};
+
+g_suite.testBrowserCanInvokeV8Methods = function() {
+ var obj = window.g_plugin.eval(
+ 'function C() { this.f = 7; this.m = function() { return this.f; } } new C()');
+ g_test.assertEquals(7, obj.m());
+};
+
+g_suite.testBrowserCanEnumerateV8Properties = function() {
+ // Chrome bug: http://code.google.com/p/chromium/issues/detail?id=5743
+ if (!isChrome && !isFirefox2) {
+ var a = window.g_plugin.eval('var a = { p : 123 }; a[1] = 321; a');
+ all = "";
+ for (m in a) {
+ all += m;
+ }
+
+ // The EcmaScript spec says the order property names are returned is
+ // implementation dependent.
+ g_test.assertTrue('1p' === all || 'p1' === all);
+ }
+};
+
+g_suite.testStringValueOfV8ObjectInBrowserIsDefault = function() {
+ var a = window.g_plugin.eval('({})');
+ g_test.assertEquals('[object Object]', a.toString());
+};
+
+g_suite.testStringValueOfV8ObjectInBrowserCanBeOverriden = function() {
+ var a = window.g_plugin.eval('({ toString: function() { return "foo"; } })');
+ g_test.assertEquals('foo', a.toString());
+};
+
+g_suite.testV8SeesSameBrowserObjectAsEqualToItself = function() {
+ if (!isSafari && !isChrome) {
+ var v8f = window.g_plugin.eval('function(a, b) { return a === b; }');
+ var obj = {};
+ g_test.assertTrue(v8f(obj, obj));
+ }
+};
+
+g_suite.testTypeofBrowserObjectInV8IsObject = function() {
+ var v8f = window.g_plugin.eval('function(x) { return typeof(x); }');
+ g_test.assertEquals('object', v8f({}));
+};
+
+// I put in this test because the V8 bridge code uses the absence of
+// a call property as an early out test when determining whether a
+// browser object is a function or not.
+g_suite.testTypeofBrowserObjectInV8IsObjectEvenIfItHasACallProperty =
+ function() {
+ var v8f = window.g_plugin.eval('function(x) { return typeof(x); }');
+ g_test.assertEquals('object', v8f({ call: {} }));
+};
+
+g_suite.testTypeofBrowserFunctionInV8IsFunction = function() {
+ var v8f = window.g_plugin.eval('function(x) { return typeof(x); }');
+ g_test.assertEquals('function', v8f(function() {}));
+};
+
+g_suite.testV8CanInvokeBrowserFunctions = function() {
+ var v8f = window.g_plugin.eval('function(f) { return f(); }');
+ var f = function() { return 12; };
+ g_test.assertEquals(12, v8f(f));
+};
+
+g_suite.testV8ReceivesExceptionOnInvokingUndefinedFunction = function() {
+ var v8f = window.g_plugin.eval(
+ 'function(f) { try { f(); } catch(e) { return e; } }');
+ g_test.assertEquals("TypeError: undefined is not a function",
+ v8f(undefined).toString());
+};
+
+g_suite.testV8CanInvokeBrowserConstructor = function() {
+ window.Constructor = function(x) { this.p = x; };
+ var obj = window.g_plugin.eval('new Constructor(7)');
+ g_test.assertEquals(7, obj.p);
+};
+
+g_suite.testV8ReceivesExceptionOnInvokingUndefinedConstructor = function() {
+ var v8f = window.g_plugin.eval(
+ 'function(f) { try { new f(); } catch(e) { return e; } }');
+ g_test.assertEquals("TypeError: undefined is not a constructor",
+ v8f(undefined).toString());
+};
+
+g_suite.testV8CanGetConstructorFromBrowserObject = function() {
+ window.Constructor = function(x) { this.p = x; };
+ var obj = window.g_plugin.eval('new Constructor(7)');
+ g_test.assertTrue(obj.constructor === window.Constructor);
+};
+
+g_suite.testV8CanGetBrowserProperties = function() {
+ var a = { p: 123 };
+ var v8f = window.g_plugin.eval('function(x) { return x.p; }');
+ g_test.assertEquals(123, v8f(a));
+};
+
+g_suite.testV8ReceivesExceptionOnGettingPropertyFromUndefined = function() {
+ var v8f = window.g_plugin.eval(
+ 'function(x) { try { return x.p; } catch(e) { return e; } }');
+ g_test.assertEquals("TypeError: Cannot read property 'p' of undefined",
+ v8f(undefined).toString());
+};
+
+g_suite.testV8CodeReceivesUndefinedIfGettingBrowserPropertyFails = function() {
+ var a = window.g_plugin.eval('plugin.nonExistentProperty');
+ g_test.assertEquals(undefined, a);
+};
+
+g_suite.testV8CanSetBrowserProperties = function() {
+ var a = { p: 123 };
+ var v8f = window.g_plugin.eval('function(x) { x.p = 321; }');
+ v8f(a);
+ g_test.assertEquals(321, a.p);
+};
+
+g_suite.testV8CanGetBrowserArrayElements = function() {
+ var a = [1, 2, 3];
+ var v8f = window.g_plugin.eval('function(x) { return x[1]; }');
+ g_test.assertEquals(2, v8f(a));
+};
+
+g_suite.testV8CodeReceivesUndefinedIfGettingBrowserNonExistentArrayElement =
+ function() {
+ var a = window.g_plugin.eval('plugin[17]');
+ g_test.assertEquals(undefined, a);
+};
+
+g_suite.testV8CanGetBrowserArrayLength = function() {
+ var a = [1, 2, 3];
+ var v8f = window.g_plugin.eval('function(x) { return x.length; }');
+ g_test.assertEquals(3, v8f(a));
+};
+
+g_suite.testV8CanSetBrowserArrayElements = function() {
+ var a = [1, 2, 3];
+ var v8f = window.g_plugin.eval('function(x) { x[1] = 7; }');
+ v8f(a);
+ g_test.assertEquals(7, a[1]);
+};
+
+g_suite.testV8CanSetBrowserArrayLength = function() {
+ var a = [1, 2, 3];
+ var v8f = window.g_plugin.eval('function(x) { x.length = 10; }');
+ v8f(a);
+ g_test.assertEquals(10, a.length);
+};
+
+g_suite.testV8CanIterateOverBrowserArray = function() {
+ var a = [1, 2, 3];
+ var v8f = window.g_plugin.eval(
+ 'function(x) { var total = 0; for (var i in x) { total += x[i]; } return total; }');
+ g_test.assertEquals(6, v8f(a));
+};
+
+g_suite.testIsArrayWorksInV8ForBrowserArrays = function() {
+ var v8f = g_plugin.eval('function(x) { return o3djs.base.isArray(x); }');
+ g_test.assertTrue(v8f([1, 2, 3]));
+};
+
+g_suite.testIsArrayWorksInV8ForV8Arrays = function() {
+ g_test.assertFalse(g_plugin.eval('o3djs.base.isArray(undefined)'));
+ g_test.assertFalse(g_plugin.eval('o3djs.base.isArray(7)'));
+ g_test.assertTrue(g_plugin.eval('o3djs.base.isArray([1, 2, 3])'));
+};
+
+g_suite.testV8CanQueryBrowserProperties = function() {
+ var a = { p : 123 }; a[1] = 321;
+ var v8f = window.g_plugin.eval(
+ 'function(x) { var all = ""; for (m in x) { all += m; } return all; }');
+
+ // The EcmaScript spec says the order property names are returned is
+ // implementation dependent.
+ g_test.assertTrue('1p' === v8f(a) || 'p1' === v8f(a));
+};
+
+g_suite.testV8CanRemoveBrowserProperties = function() {
+ if (!isSafari) {
+ var a = { p : 123 };
+ var v8f = window.g_plugin.eval('function(x) { return delete x.p; }');
+ g_test.assertTrue(v8f(a));
+ // check property does not exist
+ g_test.assertEquals('undefined', typeof(a.p));
+ // delete should still succeed if the property does not exist
+ g_test.assertTrue(v8f(a));
+ }
+};
+
+g_suite.testV8DeleteOperatorReturnsFalseIfPropertyCannotBeRemoved = function() {
+ g_test.assertFalse(window.g_plugin.eval('delete plugin.client'));
+};
+
+g_suite.testV8CanInvokeBrowserMethods = function() {
+ function C() { this.f = 7; this.m = function() { return this.f; } }
+ c = new C();
+ var v8f = window.g_plugin.eval('function(x) { return x.m(); }');
+ g_test.assertEquals(7, v8f(c));
+};
+
+g_suite.testV8CodeReceivesAnExceptionIfBrowserMethodCallFails = function() {
+ var v8f = window.g_plugin.eval(
+ 'function() { try { plugin.nonExistentFunction(); } catch(e) { return e; } }');
+ g_test.assertEquals(
+ "TypeError: Object [object Object] has no method 'nonExistentFunction'",
+ v8f().toString());
+};
+
+// This won't work until will can support intercepting on V8 objects built
+// from FunctionTemplates.
+//g_suite.testV8CanInvokeBrowserFunctionsWithExplicitThisThroughCallAndApply =
+// function() {
+// var this_copy, arg_copy;
+// function f(arg) { this_copy = this; arg_copy = arg; return arg; }
+// var v8f = window.g_plugin.eval('function(x) { return x(7); }');
+// var obj = {};
+// g_test.assertEquals(7, v8f.call(obj, 7));
+// g_test.assertEquals(obj, this_copy);
+// g_test.assertEquals(7, arg_copy);
+// obj = {};
+// g_test.assertEquals(8, v8f.apply(obj, [7]));
+// g_test.assertEquals(obj, this_copy);
+// g_test.assertEquals(8, arg_copy);
+//}
+
+g_suite.testStringValueOfBrowserObjectInV8IsDefault = function() {
+ var v8f = window.g_plugin.eval('function(x) { return x.toString(); }');
+ g_test.assertEquals('[object Object]', v8f({}));
+};
+
+g_suite.testStringValueOfBrowserObjectInV8CanBeOverriden = function() {
+ var v8f = window.g_plugin.eval('function(x) { return x.toString(); }');
+ g_test.assertEquals('foo', v8f({ toString: function() { return 'foo' } }));
+};
+
+g_suite.testV8CanInvokeNativeMethod = function() {
+ g_test.assertEquals('My pack', window.g_plugin.eval(
+ 'var pack = plugin.client.createPack(); pack.name = \"My pack\"; pack.name'));
+};
+
+g_suite.testV8CanInvokeNativeObject = function() {
+ g_test.assertArrayEquals([-1, -1, -2], window.g_plugin.eval(
+ 'var b = plugin.o3d.BoundingBox(' +
+ '[-1, -1, -2], [1, 2, 4]); b.minExtent'));
+};
+
+g_suite.testV8ExceptionsInPluginEvalResultInErrorCallback = function() {
+ var error = 'no error';
+ var exceptionThrown = false;
+ window.g_plugin.client.setErrorCallback(function(e) {
+ error = e; });
+ try {
+ window.g_plugin.eval('throw "error";');
+ } catch(e) {
+ exceptionThrown = true;
+ }
+ // Chrome bug: http://code.google.com/p/chromium/issues/detail?id=5748
+ if (!isChrome && !isSafari) {
+ g_test.assertTrue(exceptionThrown);
+ }
+ g_test.assertEquals('Uncaught error in throw "error";', error);
+};
+
+g_suite.testV8ExceptionsInFunctionCallResultInErrorCallback = function() {
+ var v8f = window.g_plugin.eval('function() { throw "error"; }');
+ var error = 'no error';
+ var exceptionThrown = false;
+ window.g_plugin.client.setErrorCallback(function(e) {
+ error = e; });
+ try {
+ v8f();
+ } catch(e) {
+ exceptionThrown = true;
+ }
+ // Chrome bug: http://code.google.com/p/chromium/issues/detail?id=5748
+ if (!isChrome && !isSafari) {
+ g_test.assertTrue(exceptionThrown);
+ }
+ g_test.assertEquals('Uncaught error in function() { throw "error"; }', error);
+};
+
+g_suite.testV8ExceptionsInMethodCallResultInErrorCallback = function() {
+ var obj = window.g_plugin.eval('({ f: function() { throw "error"; } })');
+ var error = 'no error';
+ var exceptionThrown = false;
+ window.g_plugin.client.setErrorCallback(function(e) {
+ error = e; });
+ try {
+ obj.f();
+ } catch(e) {
+ exceptionThrown = true;
+ }
+ // Chrome bug: http://code.google.com/p/chromium/issues/detail?id=5748
+ if (!isChrome && !isSafari) {
+ g_test.assertTrue(exceptionThrown);
+ }
+ g_test.assertEquals(
+ 'Uncaught error in ({ f: function() { throw "error"; } })',
+ error);
+};
+
+g_suite.testV8ExceptionsInConstructorCallResultInErrorCallback = function() {
+ // Chrome bug: http://code.google.com/p/chromium/issues/detail?id=3285
+ if (!isChrome && !isSafari && !isFirefox2) {
+ var obj = window.g_plugin.eval('function() { throw "error"; }');
+ var error = 'no error';
+ var exceptionThrown = false;
+ window.g_plugin.client.setErrorCallback(function(e) {
+ error = e; });
+ try {
+ new obj();
+ } catch(e) {
+ exceptionThrown = true;
+ }
+ // Chrome bug: http://code.google.com/p/chromium/issues/detail?id=5748
+ if (!isChrome && !isSafari) {
+ g_test.assertTrue(exceptionThrown);
+ }
+ g_test.assertEquals(
+ 'Uncaught error in function() { throw "error"; }', error);
+ }
+};
+
+g_suite.testV8CanAccessBrowserGlobals = function() {
+ window.g_global = 7;
+ g_test.assertEquals(7, window.g_plugin.eval('g_global'));
+};
+
+g_suite.testV8ModificationsToBrowserGlobalsAreOnlyLocallyVisible = function() {
+ window.g_global = 7;
+ g_test.assertEquals(8, window.g_plugin.eval('g_global = 8; g_global'));
+ g_test.assertEquals(7, window.g_global);
+};
+
+g_suite.testV8CanAccessPluginThroughPluginGlobal = function() {
+ // http://code.google.com/p/chromium/issues/detail?id=5751
+ if (!isChrome && !isSafari) {
+ g_test.assertEquals(g_plugin.client, window.g_plugin.eval('plugin.client'));
+ }
+};
+
+g_suite.testEvalFailsWithBadArguments = function() {
+ // Chrome bug: http://code.google.com/p/chromium/issues/detail?id=5748
+ if (!isChrome && !isSafari) {
+ var numExceptions = 0;
+ try {
+ window.g_plugin.eval();
+ } catch (e) {
+ ++numExceptions;
+ }
+ try {
+ window.g_plugin.eval('a', 'b');
+ } catch (e) {
+ ++numExceptions;
+ }
+ try {
+ window.g_plugin.eval(7);
+ } catch (e) {
+ ++numExceptions;
+ }
+ g_test.assertEquals(3, numExceptions);
+ }
+};
+
+g_suite.testExceptionThrownIfV8CodeIsInvalid = function() {
+ var error = 'no error';
+ var exceptionThrown = false;
+ window.g_plugin.client.setErrorCallback(function(e) {
+ error = e; });
+ try {
+ window.g_plugin.eval('+1)');
+ } catch (e) {
+ exceptionThrown = true;
+ }
+ g_test.assertEquals('Uncaught SyntaxError: Unexpected token ) in +1)', error);
+ if (!isSafari && !isChrome) {
+ g_test.assertTrue(exceptionThrown);
+ }
+};
+
+g_suite.testV8CanAccessDOM = function() {
+ if (!isInternetExplorer) {
+ g_test.assertEquals('P', window.g_plugin.eval(
+ "document.createElement('P').tagName"));
+ }
+};
+
+g_suite.testV8CanRunUtilityCodeNatively = function() {
+ var result = g_plugin.eval("o3djs.math.addVector([1, 2, 3], [3, 2, 1])");
+ g_test.assertArrayEquals([4, 4, 4], result);
+};
+
+o3djs.provide('myNamespace');
+
+myNamespace.anUndefinedConstant = undefined;
+
+myNamespace.aNumberConstant = 7;
+
+myNamespace.aBooleanConstant = true;
+
+myNamespace.anEscapedStringConstant = 'foo\r\n\tbar';
+
+myNamespace.anUnescapedStringConstant = 'foo bar';
+
+myNamespace.aNullConstant = null;
+
+myNamespace.anObjectConstant = {a: 'foo'};
+
+myNamespace.anArrayConstant = [1, 2, 3];
+
+myNamespace.doSomething = function() {
+ return 7;
+};
+
+myNamespace.AConstructor = function(a) {
+ this.a = a;
+};
+
+myNamespace.AConstructor.prototype.getA = function() {
+ return this.a;
+};
+
+myNamespace.getPlugin = function() {
+ return plugin;
+};
+
+myNamespace.doSomethingInBrowser = function() {
+ o3djs.BROWSER_ONLY;
+ myNamespace.didSomethingInBrowser = true;
+ return 7;
+};
+
+myNamespace.aRegExp = /Foo\n\r/gim;
+
+g_suite.testProvidedCodeRunsInV8 = function() {
+ g_test.assertTrue('client' in g_plugin.eval('myNamespace.getPlugin()'));
+};
+
+g_suite.testV8CanAccessUndefinedInAProvidedNamespace = function() {
+ myNamespace.anUndefinedConstant = 7;
+ g_test.assertEquals(undefined, g_plugin.eval(
+ 'myNamespace.anUndefinedConstant'));
+};
+
+g_suite.testV8CanAccessNumbersInAProvidedNamespace = function() {
+ myNamespace.aNumberConstant = undefined;
+ g_test.assertEquals(7, g_plugin.eval('myNamespace.aNumberConstant'));
+};
+
+g_suite.testV8CanAccessBooleansInAProvidedNamespace = function() {
+ myNamespace.aBooleanConstant = undefined;
+ g_test.assertTrue(g_plugin.eval('myNamespace.aBooleanConstant'));
+};
+
+g_suite.testV8CanAccessEscapedStringsInAProvidedNamespace = function() {
+ myNamespace.anEscapedStringConstant = undefined;
+ g_test.assertEquals('foo\r\n\tbar', g_plugin.eval(
+ 'myNamespace.anEscapedStringConstant'));
+};
+
+g_suite.testV8CanAccessUneapedStringsInAProvidedNamespace = function() {
+ myNamespace.anUnescapedStringConstant = undefined;
+ g_test.assertEquals('foo bar', g_plugin.eval(
+ 'myNamespace.anUnescapedStringConstant'));
+};
+
+g_suite.testV8CanAccessNullInAProvidedNamespace = function() {
+ myNamespace.aNullConstant = undefined;
+ g_test.assertEquals(null, g_plugin.eval('myNamespace.aNullConstant'));
+};
+
+g_suite.testV8CanAccessObjectsInAProvidedNamespace = function() {
+ myNamespace.anObjectConstant = undefined;
+ g_test.assertEquals('foo', g_plugin.eval('myNamespace.anObjectConstant.a'));
+};
+
+g_suite.testV8CanAccessArraysInAProvidedNamespace = function() {
+ myNamespace.anArrayConstant = undefined;
+ g_test.assertArrayEquals([1, 2, 3], g_plugin.eval(
+ 'myNamespace.anArrayConstant'));
+};
+
+g_suite.testV8CanInvokeFunctionsInAProvidedNamespace = function() {
+ myNamespace.doSomething = undefined;
+ g_test.assertEquals(7, g_plugin.eval('myNamespace.doSomething()'));
+};
+
+g_suite.testV8CanInvokeConstructorsAndPrototypeMethodsInAProvidedNamespace =
+ function() {
+ myNamespace.AConstructor = undefined;
+ g_test.assertEquals(7, g_plugin.eval(
+ 'new myNamespace.AConstructor(7).getA()'));
+};
+
+g_suite.testV8CanInvokeFunctionsAnnotatedAsRunningInTheBrowser = function() {
+ g_test.assertEquals(7, g_plugin.eval('myNamespace.doSomethingInBrowser()'));
+ g_test.assertTrue(myNamespace.didSomethingInBrowser);
+};
+
+g_suite.textV8CanAccessRegExpInAProvidedNamespace = function() {
+ myNamespace.aRegExp = undefined;
+ g_test.assertEquals('Foo\n\r', g_plugin.eval('myNamespace.aRegExp.source'));
+ g_test.assertTrue(g_plugin.eval('myNamespace.aRegExp.global'));
+ g_test.assertTrue(g_plugin.eval('myNamespace.aRegExp.multiline'));
+ g_test.assertTrue(g_plugin.eval('myNamespace.aRegExp.ignoreCase'));
+}
+
+g_suite.testV8CanAccessBrowserO3djs = function() {
+ g_test.assertEquals(o3djs, g_plugin.eval('o3djs.browser'));
+};
+
+g_suite.testV8CanAccessGlobalsThroughO3djs = function() {
+ g_test.assertEquals(navigator.userAgent,
+ g_plugin.eval('o3djs.global.navigator.userAgent'));
+};
+
+g_suite.testV8CanAccessO3dNamespaceThroughO3djs = function() {
+ g_test.assertTrue(g_plugin.eval('g_plugin.o3d === o3djs.base.o3d'));
+};
+
+function initStep2(clientElements) {
+ g_test = o3djs.test;
+ g_plugin = clientElements[0];
+ g_testResult = g_test.runTests(g_suite);
+};
+
+window.onload = function() {
+ o3djs.util.makeClients(initStep2);
+};
+
+</script>
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/version-check-test.html b/o3d/tests/selenium/tests/version-check-test.html
new file mode 100644
index 0000000..b16d878
--- /dev/null
+++ b/o3d/tests/selenium/tests/version-check-test.html
@@ -0,0 +1,90 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Test we can check for the version.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Version Check
+</title>
+<!-- Include sample javascript library functions-->
+<script
+ type="text/javascript"
+ src="../../../samples/o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_testResult;
+
+/**
+ * Request version 999999 which should always fail.
+ */
+function init() {
+ o3djs.util.makeClients(haveVersion, '', '999999', dontHaveVersion);
+}
+
+function dontHaveVersion() {
+ document.getElementById('result').innerHTML =
+ 'success: version 999999 not available';
+ g_testResult = true;
+}
+
+/**
+ *
+ */
+function haveVersion() {
+ document.getElementById('result').innerHTML =
+ 'failure: version 999999 is supposedly available!';
+ g_testResult = false;
+}
+</script>
+</head>
+<body>
+<h1>Version Check</h1>
+Check that asking for a non-existent version fails<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 200px; height: 200px;"></div>
+<!-- End of O3D plugin -->
+<div>Result: <span id="result"></span><div>
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/window-overlap-test.html b/o3d/tests/selenium/tests/window-overlap-test.html
new file mode 100644
index 0000000..8ce4e16
--- /dev/null
+++ b/o3d/tests/selenium/tests/window-overlap-test.html
@@ -0,0 +1,119 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Window Overlap Test
+
+Make sure client areas that are not visible because of overlapping windows do
+not render.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Window Overlap Test
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="../../../samples/o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.rendergraph');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = uninit;
+
+// global variables
+var g_client;
+var g_pack;
+var g_viewInfo;
+var g_renderCount = 0;
+var g_finished = false;
+
+function onRender() {
+ g_viewInfo.clearBuffer.clearColor = [Math.random(),
+ Math.random(),
+ Math.random(),
+ 1];
+ g_renderCount += 1;
+}
+
+/**
+ * Creates the client areas.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the model into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ var o3dElement = clientElements[0];
+
+ g_client = o3dElement.client;
+ g_pack = g_client.createPack();
+
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ g_client.setRenderCallback(onRender);
+
+ g_finished = true;
+}
+
+/**
+ * Cleans up.
+ */
+function uninit() {
+ g_client.clearRenderCallback();
+}
+</script>
+</head>
+<body>
+<h1>Window Overlap Test</h1>
+This test verifies that client areas that are not visible because of overlapping
+windows do not render.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 100px; height: 100px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/tests/selenium/tests/window-overlap-top.html b/o3d/tests/selenium/tests/window-overlap-top.html
new file mode 100644
index 0000000..250addc
--- /dev/null
+++ b/o3d/tests/selenium/tests/window-overlap-top.html
@@ -0,0 +1,63 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Window Overlap Test Top
+
+See window-overlap-test.html
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Window Overlap Test Top Window
+</title>
+<script type="text/javascript">
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+
+var g_finished = false;
+
+function init() {
+ g_finished = true;
+}
+</script>
+</head>
+<body>
+<h1>Window Overlap Test Top Window</h1>
+This test verifies that client areas that are not visible because of overlapping
+windows do not render.
+<br/>
+</body>
+</html>
diff --git a/o3d/tests/test_driver.bat b/o3d/tests/test_driver.bat
new file mode 100644
index 0000000..c9e0e4a
--- /dev/null
+++ b/o3d/tests/test_driver.bat
@@ -0,0 +1,33 @@
+@echo off
+REM Copyright 2009, Google Inc.
+REM All rights reserved.
+REM
+REM Redistribution and use in source and binary forms, with or without
+REM modification, are permitted provided that the following conditions are
+REM met:
+REM
+REM * Redistributions of source code must retain the above copyright
+REM notice, this list of conditions and the following disclaimer.
+REM * Redistributions in binary form must reproduce the above
+REM copyright notice, this list of conditions and the following disclaimer
+REM in the documentation and/or other materials provided with the
+REM distribution.
+REM * Neither the name of Google Inc. nor the names of its
+REM contributors may be used to endorse or promote products derived from
+REM this software without specific prior written permission.
+REM
+REM THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+REM "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+REM LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+REM A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+REM OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+REM SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+REM LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+REM DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+REM THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+REM (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+REM OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set PYTHONDIR=%~dp0..\..\..\..\third_party\python_24
+call %PYTHONDIR%\setup_env.bat
+%PYTHONDIR%\python.exe %~dp0\test_driver.py %*
diff --git a/o3d/tests/test_driver.py b/o3d/tests/test_driver.py
new file mode 100644
index 0000000..891ecec
--- /dev/null
+++ b/o3d/tests/test_driver.py
@@ -0,0 +1,433 @@
+#!/usr/bin/python2.4
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+"""Test harness script that invokes O3D system-tests.
+
+This script constructs the necessary testing environment required for the
+DirectX-based O3D system-tests. It invokes each test within the PIX
+debugging tool to capture the graphics command stream and framebuffer
+contents.
+
+See (http://wiki.corp.google.com/twiki/bin/view/Main/ClientThreeDTestingPlan)
+for the o3d testing plan.
+
+Usage:
+ test_driver.py [-capture_only] [-hardware]
+
+The script will scan all of the directories, and invoke system-tests for all
+<name>_test directories present. Each of the system-tests in system_tests.exe
+will be invoked one at a time through PIXWin.exe. PIX has been configured
+to interact with the testing framework to capture graphics command streams
+and framebuffer contents.
+
+After the test has executed, the script will parse the persisted log files
+and compare the generated results.
+
+The framework assumes that each test-case is named with a camel-caps
+translation of the directory name. So in the case of the directory
+tests\import_test, the system invokes the gUnit test ImportTest.
+
+Command Arguments:
+ -capture_only: The script will only capture results. All verification with
+ reference-data will be bypassed. This flag should be used when updating the
+ reference-data.
+
+ - hardware: The the system tests will execute using the graphics hardware, as
+ opposed to the software reference rasterizer. Use this mode to look for
+ problems related to a specific hardware platform.
+"""
+
+
+import os
+import re
+from stat import ST_MODE
+from stat import S_ISDIR
+import sys
+
+_PIX_EXECUTABLE = '.\PIXWin.exe'
+
+# Template for the command line required to invoke a system-test within PIX.
+_PIX_EXPERIMENT_INVOKE = ('%s %s -start -runfile %s -targetstartfolder %s '
+ '-targetargs %s')
+
+# Template for the command line required to invoke PIX when constructing a
+# .csv file from a .PIXRun file.
+_PIX_RUNFILE_INVOKE = '%s %s -exporttocsv %s'
+
+# Template command line argument for the gunit system that selectively
+# executes individual groups of tests.
+_GUNIT_INVOKE = '--gunit_filter=%s.*'
+_SYSTEM_TEST = '.\system_test.exe'
+_EXPERIMENT_FILE_REFERENCE = 'testing_framework_reference.PIXExp'
+_EXPERIMENT_FILE_HARDWARE = 'testing_framework_hardware.PIXExp'
+
+# Command line required to invoke the perceptual diff utility.
+_PDIFF = ('perceptualdiff.exe %s %s -verbose -fov 45 -threshold 40000')
+
+# Regular expression used to search for hexadecimal pointer values in
+# generated log files
+_HEX_EXPRESSION = re.compile('0x[0-9ABCDEF]+')
+
+# Constant command argument string to enable capture-only mode.
+_CAPTURE_FLAG = '-capture_only'
+
+# Command flags to specify usage of the hardware rasterizer for the tests.
+_HARDWARE_RASTERIZER = '-hardware'
+
+# Boolen set to True if test results should be captured and not validated.
+_GENERATE_ONLY = False
+
+# Boolean set to True if hardware rasterizer is to be used instead of the DX
+# reference rasterizer.
+_HARDWARE_DEVICE = False
+
+
+def ConstructTestName(filesystem_name):
+ """Returns a camel-caps translation of the input string.
+
+ Args:
+ filesystem_name: The name of the test, as found on the file-system.
+ Typically this name is_of_this_form.
+
+ Returns:
+ A camel-caps version of the input string. _ delimiters are removed, and
+ the first letters of words are capitalized.
+ """
+ names = filesystem_name.split('_')
+ test_name = ''
+ for word in names:
+ test_name += word.upper()[0] + word[1:]
+ return test_name
+
+
+def AssertCapturedFrames(test_name):
+ """Performs the image validation for test-case frame captures.
+
+ Args:
+ test_name: The name of the test, as found on the file-system.
+
+ Returns:
+ True if all images match within tolerance, false otherwise.
+ """
+ print 'Status: Validating captured frames against reference data.'
+
+ reference_files = os.listdir('./' + test_name + '/reference_frames')
+ generated_files = os.listdir('./' + test_name)
+ generated_files = [(file_name) for file_name in generated_files
+ if '.png' in file_name]
+
+ if set(reference_files) != set(generated_files):
+ print 'Error: Reference file set does not match generated file set.'
+ print 'Reference files: ' + ', '.join(reference_files)
+ print 'Generated files: ' + ', '.join(generated_files)
+ return False
+
+ # Assuming both the result and reference image sets are the same size,
+ # verify that corresponding images are similar within tolerance.
+ for file in reference_files:
+ reference_file = test_name + '/reference_frames/' + file
+ generated_file = test_name + '/' + file
+ if os.system(_PDIFF % (reference_file, generated_file)):
+ error = ('Error: Reference framebuffer (%s) does not match generated '
+ 'file (%s).')
+ print error % (reference_file, generated_file)
+ return False
+
+ return True
+
+
+def GenerateCSVStream(test_name):
+ """Invokes PIX to convert a PIXRun file to a .csv file.
+
+ Args:
+ test_name: The name of the test for which the .csv file is
+ to be generated.
+ """
+
+ runfile_invoke = _PIX_RUNFILE_INVOKE % (
+ _PIX_EXECUTABLE,
+ test_name + '/' + test_name + '.PIXRun',
+ test_name + '/' + test_name + '.csv')
+
+ os.system(runfile_invoke)
+
+
+def RemovePointerReferences(call_log):
+ """Returns the input argument with all pointer values removed.
+
+ Args:
+ call_log: String containing an dx call-log entry.
+
+ Returns:
+ The input string with all substrings matching '0x[0-9A-F]+', which is the
+ pointer syntax used for the log files, removed.
+ """
+
+ return _HEX_EXPRESSION.sub('', call_log)
+
+
+def PartitionCSVLine(line):
+ """Returns a list of all column values in a comma-separated-value line.
+
+ Args:
+ line: The input line from a comma-separated-value source.
+
+ Returns:
+ CSV files may include quoted text segments containing commas. This
+ routine will return an array of all of the columns present in the line,
+ respecting quoted regions.
+ """
+
+ # This routine is more complicated than expected due to the presence of
+ # quoted lists within colums. The algorithm proceeds by first splitting
+ # the line by quoted sub-strings. Sub-strings not matching a quoted
+ # expression are then split on ','.
+ quoted_column = re.compile('(\"[^\"]*\")')
+ block_partitions = quoted_column.split(line)
+
+ output_columns = []
+
+ for chunk in block_partitions:
+ # If this chunk is a quoted string, append it to the output verbatim.
+ if quoted_column.match(chunk):
+ output_columns += [chunk]
+ else:
+ output_columns += chunk.split(',')
+
+ # Return the output set of columns with all empty entries removed.
+ # Empty columns will be present due to the interaction of the split by
+ # quoted string, and split by ','.
+ # For example: here, is, "a (tuple, example)", to, try
+ # After the first split: ['here, is, ', 'a (tuple, example)', 'to, try']
+ # After the second split:
+ # ['here', 'is', '', 'a (tuple, example)', 'to', 'try']
+ # Since these empty columns are not present in the original list, we remove
+ # them here.
+ return [(column) for column in output_columns if column != ""]
+
+
+def AssertCapturedStreams(test_name):
+ """Performes graphics command stream verification for test with name
+ test_name.
+
+ Args:
+ test_name: The name of the test, as found on the file-system.
+
+ Returns:
+ Returns true if the generated testing streams match the reference streams.
+ """
+
+ print 'Status: Validating generated command streams.'
+
+ generated_stream = open(test_name + '/' + test_name + '.csv')
+ reference_stream = open(test_name + '/reference_stream.csv')
+
+ reference_lines = reference_stream.readlines()
+ generated_lines = generated_stream.readlines()
+
+ if len(reference_lines) != len(generated_lines):
+ print 'Error: Reference and generated logs differ in length.'
+ return False
+
+ # Compare each of the log lines from both files.
+ for index in range(0, len(reference_lines)):
+ generated_line = generated_lines[index]
+ reference_line = reference_lines[index]
+
+ # Partition each csv line correctly wrt quoted blocks
+ reference_columns = PartitionCSVLine(reference_line)
+ generated_columns = PartitionCSVLine(generated_line)
+
+ # Only perform deep-validation on 'Call' commands.
+ if reference_columns[0] != 'Call':
+ continue
+
+ generated_log = RemovePointerReferences(generated_columns[2])
+ reference_log = RemovePointerReferences(reference_columns[2])
+
+ if (generated_log != reference_log or
+ reference_columns[0] != generated_columns[0] or
+ reference_columns[1] != generated_columns[1]):
+ print 'Error: Log file mis-match. Line: %i' % index
+ print 'Reference = %s' % reference_line
+ print 'Generated = %s' % generated_line
+ return False
+
+ return True
+
+
+def InvokeTest(test_name):
+ """Invoke the system-test with name test_name.
+
+ Args:
+ test_name: The name of the test, as found on the file-system.
+
+ Returns:
+ True if the test succeeds, false otherwise.
+ """
+
+ global _GENERATE_ONLY
+
+ if _HARDWARE_DEVICE:
+ pix_experiment_file = _EXPERIMENT_FILE_HARDWARE
+ else:
+ pix_experiment_file = _EXPERIMENT_FILE_REFERENCE
+
+ print 'Status: Executing test : %s\n' % test_name
+ gunit_invoke = _GUNIT_INVOKE % ConstructTestName(test_name)
+ pix_invoke = _PIX_EXPERIMENT_INVOKE % (_PIX_EXECUTABLE,
+ pix_experiment_file,
+ test_name + '.PIXRun',
+ test_name,
+ gunit_invoke)
+ os.system(pix_invoke)
+
+ # Invoke PIX to translate the just created .PIXRun file to a .csv suitable
+ # for parsing.
+ GenerateCSVStream(test_name)
+
+ # If invoked for capture-only, then exit here before validation.
+ if _GENERATE_ONLY:
+ return True
+
+ if not(AssertCapturedFrames(test_name)):
+ return False
+
+ if not(AssertCapturedStreams(test_name)):
+ return False
+
+ return True
+
+
+def BuildTestList():
+ """Returns a list of all available system tests.
+
+ Returns:
+ A list of test_names, constructed from the set of directory names
+ in the current directory. All directories containing the substring
+ 'test' are included in the returned set.
+ """
+
+ testing_directory = os.listdir('.')
+ test_list = []
+
+ for test in testing_directory:
+ mode = os.stat(test)[ST_MODE]
+ if ('test' in test and test != 'unittest_data' and
+ test != 'bitmap_test' and
+ test != 'conditioner_test_data' and S_ISDIR(mode)):
+ test_list += [test]
+ return test_list
+
+
+def ValidateArgs(argv):
+ """Validates the script arguments, and displays a help message,
+ if necessary.
+
+ Args:
+ argv: Array of script arguments, in argv format.
+
+ Returns:
+ True if the arguments are valid. See the usage description for
+ valid arguments.
+ """
+
+ global _GENERATE_ONLY
+ global _HARDWARE_DEVICE
+
+ # TODO : Make use of the gflags library to ease parsing of command line
+ # arguments.
+ argument_set = [_CAPTURE_FLAG, _HARDWARE_RASTERIZER]
+
+ for arg in argv[1:]:
+ if arg not in argument_set:
+ print 'O3D System-Test Harness - Usage:'
+ print ('test_driver.py [' + _CAPTURE_FLAG + '] [' +
+ _HARDWARE_RASTERIZER + ']')
+ print 'Arguments:'
+ print _CAPTURE_FLAG + ' : Force generation of reference data.'
+ print (_HARDWARE_RASTERIZER +
+ ' : Force usage of hardware for test data generation')
+ return False
+
+ if _CAPTURE_FLAG in argv[1:]:
+ _GENERATE_ONLY = True
+
+ if _HARDWARE_RASTERIZER in argv[1:]:
+ print 'Rendering with local graphics hardware.'
+ _HARDWARE_DEVICE = True
+ else:
+ print 'Rendering with DX9 reference rasterizer.'
+
+ return True
+
+
+def main(argv):
+ """Main entry point of the script.
+
+ Args:
+ argv: The c-like arguments to the script.
+
+ Returns:
+ True if all tests pass, false othwerwise.
+ """
+
+ print 'Running O3D system tests.'
+
+ if not ValidateArgs(argv):
+ return False
+
+ os.chdir(os.path.dirname(argv[0]))
+
+ test_set = BuildTestList()
+
+ # Invoke each test, tracking failures.
+ test_failures = []
+ for test in test_set:
+ if not InvokeTest(test):
+ test_failures += [test]
+
+ if len(test_failures) == 0:
+ print 'Success: All tests passed.'
+ return True
+ else:
+ print 'Error Summary: The following tests failed:'
+ print test_failures
+ return False
+
+
+if __name__ == '__main__':
+ if main(sys.argv):
+ # Return with a 0 for success (per unix convention).
+ sys.exit(0)
+ else:
+ # Return with a 1 for failure (per unix convention).
+ sys.exit(1)
diff --git a/o3d/tests/testing_framework_hardware.PIXExp b/o3d/tests/testing_framework_hardware.PIXExp
new file mode 100644
index 0000000..0495ae03
--- /dev/null
+++ b/o3d/tests/testing_framework_hardware.PIXExp
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+<PIXExperiment><Version><ExpFileVersion>202</ExpFileVersion></Version><TargetApp><TargetPath>system_tests.exe</TargetPath><StartFolder></StartFolder><Args></Args><SkipProcesses>0</SkipProcesses><RecordDiagnosticLog>0</RecordDiagnosticLog><IncludeDebugSpew>0</IncludeDebugSpew><DisableD3DXAnalysis>0</DisableD3DXAnalysis></TargetApp><Columns><Column><Name>EDID</Name><ColumnID>1</ColumnID><Flags>0</Flags><Type>3</Type><Format>%d</Format></Column><Column><Name>Event Type</Name><ColumnID>2</ColumnID><Flags>0</Flags><Type>3</Type><Format>%d</Format></Column><Column><Name>EID</Name><ColumnID>3</ColumnID><Flags>0</Flags><Type>3</Type><Format>%d</Format></Column><Column><Name>Parent EID</Name><ColumnID>4</ColumnID><Flags>0</Flags><Type>3</Type><Format>%d</Format></Column><Column><Name>Children</Name><ColumnID>11</ColumnID><Flags>0</Flags><Type>6</Type><Format>%d</Format></Column><Column><Name>Parent</Name><ColumnID>10</ColumnID><Flags>0</Flags><Type>2</Type><Format>%d</Format></Column><Column><Name>Flags</Name><ColumnID>5</ColumnID><Flags>0</Flags><Type>2</Type><Format>0x%08X</Format></Column><Column><Name>Event</Name><ColumnID>6</ColumnID><Flags>0</Flags><Type>4</Type><Format>%s</Format></Column><Column><Name>StartTime</Name><ColumnID>7</ColumnID><Flags>0</Flags><Type>5</Type><Format>%I64i</Format></Column><Column><Name>Path</Name><ColumnID>13</ColumnID><Flags>0</Flags><Type>1</Type><Format>%s</Format></Column><Column><Name>Path2</Name><ColumnID>14</ColumnID><Flags>0</Flags><Type>1</Type><Format>%s</Format></Column><Column><Name>Version</Name><ColumnID>15</ColumnID><Flags>0</Flags><Type>1</Type><Format>%s</Format></Column><Column><Name>TimeLastModified</Name><ColumnID>16</ColumnID><Flags>0</Flags><Type>1</Type><Format>%s</Format></Column><Column><Name>ProcessID</Name><ColumnID>23</ColumnID><Flags>0</Flags><Type>3</Type><Format>%d</Format></Column><Column><Name>SessionStartTimeStamp</Name><ColumnID>24</ColumnID><Flags>0</Flags><Type>5</Type><Format>%I64i</Format></Column><Column><Name>Frame</Name><ColumnID>9</ColumnID><Flags>0</Flags><Type>3</Type><Format>%d</Format></Column><Column><Name>Duration</Name><ColumnID>8</ColumnID><Flags>0</Flags><Type>5</Type><Format>%I64i</Format></Column><Column><Name>FPS</Name><ColumnID>12</ColumnID><Flags>0</Flags><Type>0</Type><Format>%0.01f</Format></Column><Column><Name>ThisEventPos</Name><ColumnID>21</ColumnID><Flags>0</Flags><Type>5</Type><Format>%I64i</Format></Column><Column><Name>NextSiblingPos</Name><ColumnID>22</ColumnID><Flags>0</Flags><Type>5</Type><Format>%I64i</Format></Column><Column><Name>User Event Name</Name><ColumnID>17</ColumnID><Flags>0</Flags><Type>1</Type><Format>%s</Format></Column><Column><Name>PackedCallPackage</Name><ColumnID>19</ColumnID><Flags>0</Flags><Type>7</Type><Format></Format></Column><Column><Name>Object Pointer</Name><ColumnID>20</ColumnID><Flags>0</Flags><Type>2</Type><Format>0x%08X</Format></Column></Columns><EventDescs><EventDesc><Name>Session Start</Name><EventType>1</EventType><EDID>1</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,1</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>CalcOnLoad,Const,-1</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>8</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,0,Start Session</String></EventDescColumn><EventDescColumn><ColumnID>13</ColumnID><String>(expfilepath)</String></EventDescColumn><EventDescColumn><ColumnID>14</ColumnID><String>(runfilepath)</String></EventDescColumn><EventDescColumn><ColumnID>24</ColumnID><String>(sessionstarttimestamp)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>Session End</Name><EventType>2</EventType><EDID>2</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,2</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>CalcOnLoad,Const,-1</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>8</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,0,End Session</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>Process Start</Name><EventType>3</EventType><EDID>3</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,3</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>CalcOnLoad,Const,-1</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>8</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,0,Start Process</String></EventDescColumn><EventDescColumn><ColumnID>13</ColumnID><String>(processpath)</String></EventDescColumn><EventDescColumn><ColumnID>15</ColumnID><String>(processversion)</String></EventDescColumn><EventDescColumn><ColumnID>16</ColumnID><String>(processtimelastmodified)</String></EventDescColumn><EventDescColumn><ColumnID>23</ColumnID><String>(processid)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>Process End</Name><EventType>4</EventType><EDID>4</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,4</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>CalcOnLoad,Const,-1</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>8</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,0,End Process</String></EventDescColumn><EventDescColumn><ColumnID>13</ColumnID><String>(processpath)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>Frame Begin</Name><EventType>5</EventType><EDID>5</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,5</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>(parenteid)</String></EventDescColumn><EventDescColumn><ColumnID>11</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>(rowflags)</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>9</ColumnID><String>(frame)</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,1,Frame %d,3,MemberOf,ThisRow,Frame</String></EventDescColumn><EventDescColumn><ColumnID>8</ColumnID><String>Async,(duration)</String></EventDescColumn><EventDescColumn><ColumnID>12</ColumnID><String>CalcOnLoad,Divide,Const,1000000000.0,MemberOf,ThisRow,Duration</String></EventDescColumn><EventDescColumn><ColumnID>21</ColumnID><String>(frameeventfilepos)</String></EventDescColumn><EventDescColumn><ColumnID>22</ColumnID><String>(lastframeeventfilepos)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>User Event Begin</Name><EventType>7</EventType><EDID>6</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,7</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>(parenteid)</String></EventDescColumn><EventDescColumn><ColumnID>10</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>11</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>17</ColumnID><String>(usereventname)</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,1,User Event: %s,1,MemberOf,ThisRow,User Event Name</String></EventDescColumn><EventDescColumn><ColumnID>8</ColumnID><String>Async,(duration)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>User Marker</Name><EventType>9</EventType><EDID>7</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,9</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>(parenteid)</String></EventDescColumn><EventDescColumn><ColumnID>10</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>11</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>17</ColumnID><String>(usereventname)</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,1,User Marker: %s,1,MemberOf,ThisRow,User Event Name</String></EventDescColumn><EventDescColumn><ColumnID>8</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>D3D Call</Name><EventType>10</EventType><EDID>8</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,10</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>(parenteid)</String></EventDescColumn><EventDescColumn><ColumnID>11</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>10</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,CallPlusParams,MemberOf,ThisRow,PackedCallPackage</String></EventDescColumn><EventDescColumn><ColumnID>19</ColumnID><String>Async,(packedcallpkg)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>Object Creation</Name><EventType>11</EventType><EDID>9</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,11</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>(parenteid)</String></EventDescColumn><EventDescColumn><ColumnID>11</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>10</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,0,Object Creation</String></EventDescColumn><EventDescColumn><ColumnID>20</ColumnID><String>(objpointer)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>Object Population</Name><EventType>12</EventType><EDID>10</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,12</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>(parenteid)</String></EventDescColumn><EventDescColumn><ColumnID>11</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>10</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,0,Object Population</String></EventDescColumn><EventDescColumn><ColumnID>20</ColumnID><String>(objpointer)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>D3D Call (Sync)</Name><EventType>13</EventType><EDID>11</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,13</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>(parenteid)</String></EventDescColumn><EventDescColumn><ColumnID>11</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>10</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,CallPlusParams,MemberOf,ThisRow,PackedCallPackage</String></EventDescColumn><EventDescColumn><ColumnID>19</ColumnID><String>(packedcallpkg)</String></EventDescColumn></EventDescColumns></EventDesc></EventDescs><Counters/><Triggers><Trigger><Type>1</Type><Recurrence>0</Recurrence><Actions><Action><Type>1</Type><Int1>1</Int1><Str1>import_test</Str1></Action></Actions></Trigger><Trigger><Type>4</Type><Str1>BeginCommandStreamCapture</Str1><Recurrence>1</Recurrence><Actions><Action><Type>3</Type><Int1>1</Int1><Int2>1</Int2></Action></Actions></Trigger><Trigger><Type>4</Type><Str1>EndCommandStreamCapture</Str1><Recurrence>1</Recurrence><Actions><Action><Type>3</Type><Int1>0</Int1><Int2>0</Int2></Action></Actions></Trigger><Trigger><Type>4</Type><Str1>CaptureScreenContents</Str1><Recurrence>1</Recurrence><Actions><Action><Type>4</Type><Int1>19</Int1><Str1>frame_capture</Str1><Str2>.png</Str2></Action></Actions></Trigger></Triggers></PIXExperiment>
diff --git a/o3d/tests/testing_framework_reference.PIXExp b/o3d/tests/testing_framework_reference.PIXExp
new file mode 100644
index 0000000..5a86d5d
--- /dev/null
+++ b/o3d/tests/testing_framework_reference.PIXExp
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+<PIXExperiment><Version><ExpFileVersion>202</ExpFileVersion></Version><TargetApp><TargetPath>system_tests.exe</TargetPath><StartFolder></StartFolder><Args></Args><SkipProcesses>0</SkipProcesses><RecordDiagnosticLog>0</RecordDiagnosticLog><IncludeDebugSpew>0</IncludeDebugSpew><DisableD3DXAnalysis>0</DisableD3DXAnalysis></TargetApp><Columns><Column><Name>EDID</Name><ColumnID>1</ColumnID><Flags>0</Flags><Type>3</Type><Format>%d</Format></Column><Column><Name>Event Type</Name><ColumnID>2</ColumnID><Flags>0</Flags><Type>3</Type><Format>%d</Format></Column><Column><Name>EID</Name><ColumnID>3</ColumnID><Flags>0</Flags><Type>3</Type><Format>%d</Format></Column><Column><Name>Parent EID</Name><ColumnID>4</ColumnID><Flags>0</Flags><Type>3</Type><Format>%d</Format></Column><Column><Name>Children</Name><ColumnID>11</ColumnID><Flags>0</Flags><Type>6</Type><Format>%d</Format></Column><Column><Name>Parent</Name><ColumnID>10</ColumnID><Flags>0</Flags><Type>2</Type><Format>%d</Format></Column><Column><Name>Flags</Name><ColumnID>5</ColumnID><Flags>0</Flags><Type>2</Type><Format>0x%08X</Format></Column><Column><Name>Event</Name><ColumnID>6</ColumnID><Flags>0</Flags><Type>4</Type><Format>%s</Format></Column><Column><Name>StartTime</Name><ColumnID>7</ColumnID><Flags>0</Flags><Type>5</Type><Format>%I64i</Format></Column><Column><Name>Path</Name><ColumnID>13</ColumnID><Flags>0</Flags><Type>1</Type><Format>%s</Format></Column><Column><Name>Path2</Name><ColumnID>14</ColumnID><Flags>0</Flags><Type>1</Type><Format>%s</Format></Column><Column><Name>Version</Name><ColumnID>15</ColumnID><Flags>0</Flags><Type>1</Type><Format>%s</Format></Column><Column><Name>TimeLastModified</Name><ColumnID>16</ColumnID><Flags>0</Flags><Type>1</Type><Format>%s</Format></Column><Column><Name>ProcessID</Name><ColumnID>23</ColumnID><Flags>0</Flags><Type>3</Type><Format>%d</Format></Column><Column><Name>SessionStartTimeStamp</Name><ColumnID>24</ColumnID><Flags>0</Flags><Type>5</Type><Format>%I64i</Format></Column><Column><Name>Frame</Name><ColumnID>9</ColumnID><Flags>0</Flags><Type>3</Type><Format>%d</Format></Column><Column><Name>Duration</Name><ColumnID>8</ColumnID><Flags>0</Flags><Type>5</Type><Format>%I64i</Format></Column><Column><Name>FPS</Name><ColumnID>12</ColumnID><Flags>0</Flags><Type>0</Type><Format>%0.01f</Format></Column><Column><Name>ThisEventPos</Name><ColumnID>21</ColumnID><Flags>0</Flags><Type>5</Type><Format>%I64i</Format></Column><Column><Name>NextSiblingPos</Name><ColumnID>22</ColumnID><Flags>0</Flags><Type>5</Type><Format>%I64i</Format></Column><Column><Name>User Event Name</Name><ColumnID>17</ColumnID><Flags>0</Flags><Type>1</Type><Format>%s</Format></Column><Column><Name>PackedCallPackage</Name><ColumnID>19</ColumnID><Flags>0</Flags><Type>7</Type><Format></Format></Column><Column><Name>Object Pointer</Name><ColumnID>20</ColumnID><Flags>0</Flags><Type>2</Type><Format>0x%08X</Format></Column></Columns><EventDescs><EventDesc><Name>Session Start</Name><EventType>1</EventType><EDID>1</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,1</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>CalcOnLoad,Const,-1</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>8</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,0,Start Session</String></EventDescColumn><EventDescColumn><ColumnID>13</ColumnID><String>(expfilepath)</String></EventDescColumn><EventDescColumn><ColumnID>14</ColumnID><String>(runfilepath)</String></EventDescColumn><EventDescColumn><ColumnID>24</ColumnID><String>(sessionstarttimestamp)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>Session End</Name><EventType>2</EventType><EDID>2</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,2</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>CalcOnLoad,Const,-1</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>8</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,0,End Session</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>Process Start</Name><EventType>3</EventType><EDID>3</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,3</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>CalcOnLoad,Const,-1</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>8</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,0,Start Process</String></EventDescColumn><EventDescColumn><ColumnID>13</ColumnID><String>(processpath)</String></EventDescColumn><EventDescColumn><ColumnID>15</ColumnID><String>(processversion)</String></EventDescColumn><EventDescColumn><ColumnID>16</ColumnID><String>(processtimelastmodified)</String></EventDescColumn><EventDescColumn><ColumnID>23</ColumnID><String>(processid)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>Process End</Name><EventType>4</EventType><EDID>4</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,4</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>CalcOnLoad,Const,-1</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>8</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,0,End Process</String></EventDescColumn><EventDescColumn><ColumnID>13</ColumnID><String>(processpath)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>Frame Begin</Name><EventType>5</EventType><EDID>5</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,5</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>(parenteid)</String></EventDescColumn><EventDescColumn><ColumnID>11</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>(rowflags)</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>9</ColumnID><String>(frame)</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,1,Frame %d,3,MemberOf,ThisRow,Frame</String></EventDescColumn><EventDescColumn><ColumnID>8</ColumnID><String>Async,(duration)</String></EventDescColumn><EventDescColumn><ColumnID>12</ColumnID><String>CalcOnLoad,Divide,Const,1000000000.0,MemberOf,ThisRow,Duration</String></EventDescColumn><EventDescColumn><ColumnID>21</ColumnID><String>(frameeventfilepos)</String></EventDescColumn><EventDescColumn><ColumnID>22</ColumnID><String>(lastframeeventfilepos)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>User Event Begin</Name><EventType>7</EventType><EDID>6</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,7</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>(parenteid)</String></EventDescColumn><EventDescColumn><ColumnID>10</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>11</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>17</ColumnID><String>(usereventname)</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,1,User Event: %s,1,MemberOf,ThisRow,User Event Name</String></EventDescColumn><EventDescColumn><ColumnID>8</ColumnID><String>Async,(duration)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>User Marker</Name><EventType>9</EventType><EDID>7</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,9</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>(parenteid)</String></EventDescColumn><EventDescColumn><ColumnID>10</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>11</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>17</ColumnID><String>(usereventname)</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,1,User Marker: %s,1,MemberOf,ThisRow,User Event Name</String></EventDescColumn><EventDescColumn><ColumnID>8</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>D3D Call</Name><EventType>10</EventType><EDID>8</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,10</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>(parenteid)</String></EventDescColumn><EventDescColumn><ColumnID>11</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>10</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,CallPlusParams,MemberOf,ThisRow,PackedCallPackage</String></EventDescColumn><EventDescColumn><ColumnID>19</ColumnID><String>Async,(packedcallpkg)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>Object Creation</Name><EventType>11</EventType><EDID>9</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,11</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>(parenteid)</String></EventDescColumn><EventDescColumn><ColumnID>11</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>10</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,0,Object Creation</String></EventDescColumn><EventDescColumn><ColumnID>20</ColumnID><String>(objpointer)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>Object Population</Name><EventType>12</EventType><EDID>10</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,12</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>(parenteid)</String></EventDescColumn><EventDescColumn><ColumnID>11</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>10</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,FormatText,0,Object Population</String></EventDescColumn><EventDescColumn><ColumnID>20</ColumnID><String>(objpointer)</String></EventDescColumn></EventDescColumns></EventDesc><EventDesc><Name>D3D Call (Sync)</Name><EventType>13</EventType><EDID>11</EDID><EventDescColumns><EventDescColumn><ColumnID>1</ColumnID><String>(edid)</String></EventDescColumn><EventDescColumn><ColumnID>2</ColumnID><String>CalcOnLoad,Const,13</String></EventDescColumn><EventDescColumn><ColumnID>3</ColumnID><String>(eid)</String></EventDescColumn><EventDescColumn><ColumnID>4</ColumnID><String>(parenteid)</String></EventDescColumn><EventDescColumn><ColumnID>11</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>10</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>5</ColumnID><String>CalcOnLoad,Const,0</String></EventDescColumn><EventDescColumn><ColumnID>7</ColumnID><String>(time)</String></EventDescColumn><EventDescColumn><ColumnID>6</ColumnID><String>CalcOnLoad,CallPlusParams,MemberOf,ThisRow,PackedCallPackage</String></EventDescColumn><EventDescColumn><ColumnID>19</ColumnID><String>(packedcallpkg)</String></EventDescColumn></EventDescColumns></EventDesc></EventDescs><Counters/><Triggers><Trigger><Type>1</Type><Recurrence>0</Recurrence><Actions><Action><Type>1</Type><Int1>1</Int1><Str1>import_test</Str1></Action><Action><Type>5</Type><Int1>1</Int1></Action></Actions></Trigger><Trigger><Type>4</Type><Str1>BeginCommandStreamCapture</Str1><Recurrence>1</Recurrence><Actions><Action><Type>3</Type><Int1>1</Int1><Int2>1</Int2></Action></Actions></Trigger><Trigger><Type>4</Type><Str1>EndCommandStreamCapture</Str1><Recurrence>1</Recurrence><Actions><Action><Type>3</Type><Int1>0</Int1><Int2>0</Int2></Action></Actions></Trigger><Trigger><Type>4</Type><Str1>CaptureScreenContents</Str1><Recurrence>1</Recurrence><Actions><Action><Type>4</Type><Int1>19</Int1><Str1>frame_capture</Str1><Str2>.png</Str2></Action></Actions></Trigger></Triggers></PIXExperiment>