summaryrefslogtreecommitdiffstats
path: root/o3d/tests
diff options
context:
space:
mode:
authorgspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-27 23:15:42 +0000
committergspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-27 23:15:42 +0000
commit05b47f7a8c5451f858dc220df0e3a97542edace6 (patch)
treea2273d619f0625c9d44d40842845ccce2eac1045 /o3d/tests
parent5cdc8bdb4c847cefe7f4542bd10c9880c2c557a0 (diff)
downloadchromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.zip
chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.gz
chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.bz2
This is the O3D source tree's initial commit to the Chromium tree. It
is not built or referenced at all by the chrome build yet, and doesn't yet build in it's new home. We'll change that shortly. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17035 0039d316-1c4b-4281-b951-d872f2087c98
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>