OSDN Git Service

GLES2Dbg: initial tests
authorDavid Li <davidxli@google.com>
Thu, 14 Apr 2011 18:20:22 +0000 (11:20 -0700)
committerDavid Li <davidxli@google.com>
Tue, 19 Apr 2011 21:42:14 +0000 (14:42 -0700)
Change-Id: Ibf07eff68d39267fd5c9bec2870f59a5ae2f51e3

opengl/libs/GLES2_dbg/Android.mk
opengl/libs/GLES2_dbg/src/dbgcontext.cpp
opengl/libs/GLES2_dbg/src/header.h
opengl/libs/GLES2_dbg/src/server.cpp
opengl/libs/GLES2_dbg/src/vertex.cpp
opengl/libs/GLES2_dbg/test/Android.mk [new file with mode: 0644]
opengl/libs/GLES2_dbg/test/test_main.cpp [new file with mode: 0644]
opengl/libs/GLES2_dbg/test/test_server.cpp [new file with mode: 0644]
opengl/libs/GLES2_dbg/test/test_socket.cpp [new file with mode: 0644]

index 853cce6..9f6e68c 100644 (file)
@@ -45,3 +45,5 @@ LOCAL_MODULE:= libGLESv2_dbg
 LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_SHARED_LIBRARY)
+
+include $(LOCAL_PATH)/test/Android.mk
index fe93874..71892d3 100644 (file)
@@ -25,11 +25,11 @@ extern "C"
 namespace android
 {
 
-static pthread_key_t sEGLThreadLocalStorageKey = -1;
+pthread_key_t dbgEGLThreadLocalStorageKey = -1;
 
 DbgContext * getDbgContextThreadSpecific()
 {
-    tls_t* tls = (tls_t*)pthread_getspecific(sEGLThreadLocalStorageKey);
+    tls_t* tls = (tls_t*)pthread_getspecific(dbgEGLThreadLocalStorageKey);
     return tls->dbg;
 }
 
@@ -63,7 +63,7 @@ DbgContext::~DbgContext()
 DbgContext * CreateDbgContext(const pthread_key_t EGLThreadLocalStorageKey,
                               const unsigned version, const gl_hooks_t * const hooks)
 {
-    sEGLThreadLocalStorageKey = EGLThreadLocalStorageKey;
+    dbgEGLThreadLocalStorageKey = EGLThreadLocalStorageKey;
     assert(version < 2);
     assert(GL_NO_ERROR == hooks->gl.glGetError());
     GLint MAX_VERTEX_ATTRIBS = 0;
@@ -149,6 +149,37 @@ void DbgContext::Compress(const void * in_data, unsigned int in_len,
     }
 }
 
+unsigned char * DbgContext::Decompress(const void * in, const unsigned int inLen,
+                                       unsigned int * const outLen)
+{
+    assert(inLen > 4 * 3);
+    if (inLen < 4 * 3)
+        return NULL;
+    *outLen = *(uint32_t *)in;
+    unsigned char * const out = (unsigned char *)malloc(*outLen);
+    unsigned int outPos = 0;
+    const unsigned char * const end = (const unsigned char *)in + inLen;
+    for (const unsigned char * inData = (const unsigned char *)in + 4; inData < end; ) {
+        const uint32_t chunkOut = *(uint32_t *)inData;
+        inData += 4;
+        const uint32_t chunkIn = *(uint32_t *)inData;
+        inData += 4;
+        if (chunkIn > 0) {
+            assert(inData + chunkIn <= end);
+            assert(outPos + chunkOut <= *outLen);
+            outPos += lzf_decompress(inData, chunkIn, out + outPos, chunkOut);
+            inData += chunkIn;
+        } else {
+            assert(inData + chunkOut <= end);
+            assert(outPos + chunkOut <= *outLen);
+            memcpy(out + outPos, inData, chunkOut);
+            inData += chunkOut;
+            outPos += chunkOut;
+        }
+    }
+    return out;
+}
+
 void * DbgContext::GetReadPixelsBuffer(const unsigned size)
 {
     if (lzf_refBufSize < size + 8) {
index c9e6c41..f2b1fa6 100644 (file)
@@ -73,8 +73,9 @@ struct GLFunctionBitfield {
 };
 
 struct DbgContext {
-private:
     static const unsigned int LZF_CHUNK_SIZE = 256 * 1024;
+
+private:
     char * lzf_buf; // malloc / free; for lzf chunk compression and other uses
 
     // used as buffer and reference frame for ReadPixels; malloc/free
@@ -129,6 +130,8 @@ public:
 
     void Fetch(const unsigned index, std::string * const data) const;
     void Compress(const void * in_data, unsigned in_len, std::string * const outStr);
+    static unsigned char * Decompress(const void * in, const unsigned int inLen,
+                                      unsigned int * const outLen); // malloc/free
     void * GetReadPixelsBuffer(const unsigned size);
     bool IsReadPixelBuffer(const void * const ptr)  {
         return ptr == lzf_ref[lzf_readIndex];
index f13d6cc..ba4960d 100644 (file)
@@ -186,7 +186,7 @@ float Send(const glesv2debugger::Message & msg, glesv2debugger::Message & cmd)
         Die("Failed to send message length");
     }
     nsecs_t c0 = systemTime(timeMode);
-    sent = send(clientSock, str.c_str(), str.length(), 0);
+    sent = send(clientSock, str.data(), str.length(), 0);
     float t = (float)ns2ms(systemTime(timeMode) - c0);
     if (sent != str.length()) {
         LOGD("actual sent=%d expected=%d clientSock=%d", sent, str.length(), clientSock);
@@ -246,8 +246,9 @@ int * MessageLoop(FunctionCall & functionCall, glesv2debugger::Message & msg,
     msg.set_function(function);
 
     // when not exectResponse, set cmd to CONTINUE then SKIP
+    // cmd will be overwritten by received command
     cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
-    cmd.set_expect_response(false);
+    cmd.set_expect_response(expectResponse);
     glesv2debugger::Message_Function oldCmd = cmd.function();
     Send(msg, cmd);
     expectResponse = cmd.expect_response();
index 7edc050..029ee3b 100644 (file)
@@ -43,10 +43,8 @@ void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count)
 
     void * pixels = NULL;
     int viewport[4] = {};
-    if (!expectResponse) {
-        cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
-        cmd.set_expect_response(false);
-    }
+    cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+    cmd.set_expect_response(expectResponse);
     glesv2debugger::Message_Function oldCmd = cmd.function();
     Send(msg, cmd);
     expectResponse = cmd.expect_response();
@@ -61,8 +59,6 @@ void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count)
             msg.set_function(glesv2debugger::Message_Function_glDrawArrays);
             msg.set_type(glesv2debugger::Message_Type_AfterCall);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
-                cmd.set_function(glesv2debugger::Message_Function_SKIP);
             if (!expectResponse) {
                 cmd.set_function(glesv2debugger::Message_Function_SKIP);
                 cmd.set_expect_response(false);
@@ -154,10 +150,8 @@ void Debug_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid*
 
     void * pixels = NULL;
     int viewport[4] = {};
-    if (!expectResponse) {
-        cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
-        cmd.set_expect_response(false);
-    }
+    cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+    cmd.set_expect_response(expectResponse);
     glesv2debugger::Message_Function oldCmd = cmd.function();
     Send(msg, cmd);
     expectResponse = cmd.expect_response();
@@ -172,8 +166,6 @@ void Debug_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid*
             msg.set_function(glesv2debugger::Message_Function_glDrawElements);
             msg.set_type(glesv2debugger::Message_Type_AfterCall);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
-                cmd.set_function(glesv2debugger::Message_Function_SKIP);
             if (!expectResponse) {
                 cmd.set_function(glesv2debugger::Message_Function_SKIP);
                 cmd.set_expect_response(false);
diff --git a/opengl/libs/GLES2_dbg/test/Android.mk b/opengl/libs/GLES2_dbg/test/Android.mk
new file mode 100644 (file)
index 0000000..14a84b4
--- /dev/null
@@ -0,0 +1,39 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH) \
+    $(LOCAL_PATH)/../src \
+    $(LOCAL_PATH)/../../ \
+    external/gtest/include \
+    external/stlport/stlport \
+    external/protobuf/src \
+    bionic \
+    external \
+#
+
+LOCAL_SRC_FILES:= \
+    test_main.cpp \
+    test_server.cpp \
+    test_socket.cpp \
+#
+
+LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2_dbg libstlport
+LOCAL_STATIC_LIBRARIES := libgtest libprotobuf-cpp-2.3.0-lite liblzf
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE:= libGLESv2_dbg_test
+
+ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true)
+    LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
+endif
+ifneq ($(TARGET_SIMULATOR),true)
+    LOCAL_C_INCLUDES += bionic/libc/private
+endif
+
+LOCAL_CFLAGS += -DLOG_TAG=\"libEGL\"
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+LOCAL_CFLAGS += -fvisibility=hidden
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/opengl/libs/GLES2_dbg/test/test_main.cpp b/opengl/libs/GLES2_dbg/test/test_main.cpp
new file mode 100644 (file)
index 0000000..058bea4
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include "header.h"
+#include "gtest/gtest.h"
+#include "hooks.h"
+
+namespace
+{
+
+// The fixture for testing class Foo.
+class DbgContextTest : public ::testing::Test
+{
+protected:
+    android::DbgContext dbg;
+    gl_hooks_t hooks;
+
+    DbgContextTest()
+            : dbg(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE) {
+        // You can do set-up work for each test here.
+        hooks.gl.glGetError = GetError;
+    }
+
+    static GLenum GetError() {
+        return GL_NO_ERROR;
+    }
+
+    virtual ~DbgContextTest() {
+        // You can do clean-up work that doesn't throw exceptions here.
+    }
+
+    // If the constructor and destructor are not enough for setting up
+    // and cleaning up each test, you can define the following methods:
+
+    virtual void SetUp() {
+        // Code here will be called immediately after the constructor (right
+        // before each test).
+    }
+
+    virtual void TearDown() {
+        // Code here will be called immediately after each test (right
+        // before the destructor).
+    }
+};
+
+TEST_F(DbgContextTest, GetReadPixelBuffer)
+{
+    const unsigned int bufferSize = 512;
+    // test that it's allocating two buffers and swapping them
+    void * const buffer0 = dbg.GetReadPixelsBuffer(bufferSize);
+    ASSERT_NE((void *)NULL, buffer0);
+    for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++) {
+        EXPECT_EQ(0, ((unsigned int *)buffer0)[i])
+        << "GetReadPixelsBuffer should allocate and zero";
+        ((unsigned int *)buffer0)[i] = i * 13;
+    }
+
+    void * const buffer1 = dbg.GetReadPixelsBuffer(bufferSize);
+    ASSERT_NE((void *)NULL, buffer1);
+    EXPECT_NE(buffer0, buffer1);
+    for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++) {
+        EXPECT_EQ(0, ((unsigned int *)buffer1)[i])
+        << "GetReadPixelsBuffer should allocate and zero";
+        ((unsigned int *)buffer1)[i] = i * 17;
+    }
+
+    void * const buffer2 = dbg.GetReadPixelsBuffer(bufferSize);
+    EXPECT_EQ(buffer2, buffer0);
+    for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++)
+        EXPECT_EQ(i * 13, ((unsigned int *)buffer2)[i])
+        << "GetReadPixelsBuffer should swap buffers";
+
+    void * const buffer3 = dbg.GetReadPixelsBuffer(bufferSize);
+    EXPECT_EQ(buffer3, buffer1);
+    for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++)
+        EXPECT_EQ(i * 17, ((unsigned int *)buffer3)[i])
+        << "GetReadPixelsBuffer should swap buffers";
+
+    void * const buffer4 = dbg.GetReadPixelsBuffer(bufferSize);
+    EXPECT_NE(buffer3, buffer4);
+    EXPECT_EQ(buffer0, buffer2);
+    EXPECT_EQ(buffer1, buffer3);
+    EXPECT_EQ(buffer2, buffer4);
+
+    // it reallocs as necessary; 0 size may result in NULL
+    for (unsigned int i = 0; i < 42; i++) {
+        void * const buffer = dbg.GetReadPixelsBuffer(((i & 7)) << 20);
+        EXPECT_NE((void *)NULL, buffer)
+        << "should be able to get a variety of reasonable sizes";
+        EXPECT_TRUE(dbg.IsReadPixelBuffer(buffer));
+    }
+}
+
+TEST_F(DbgContextTest, CompressReadPixelBuffer)
+{
+    const unsigned int bufferSize = dbg.LZF_CHUNK_SIZE * 4 + 33;
+    std::string out;
+    unsigned char * buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize);
+    for (unsigned int i = 0; i < bufferSize; i++)
+        buffer[i] = i * 13;
+    dbg.CompressReadPixelBuffer(&out);
+    uint32_t decompSize = 0;
+    ASSERT_LT(12, out.length()); // at least written chunk header
+    ASSERT_EQ(bufferSize, *(uint32_t *)out.data())
+    << "total decompressed size should be as requested in GetReadPixelsBuffer";
+    for (unsigned int i = 4; i < out.length();) {
+        const uint32_t outSize = *(uint32_t *)(out.data() + i);
+        i += 4;
+        const uint32_t inSize = *(uint32_t *)(out.data() + i);
+        i += 4;
+        if (inSize == 0)
+            i += outSize; // chunk not compressed
+        else
+            i += inSize; // skip the actual compressed chunk
+        decompSize += outSize;
+    }
+    ASSERT_EQ(bufferSize, decompSize);
+    decompSize = 0;
+
+    unsigned char * decomp = dbg.Decompress(out.data(), out.length(), &decompSize);
+    ASSERT_EQ(decompSize, bufferSize);
+    for (unsigned int i = 0; i < bufferSize; i++)
+        EXPECT_EQ((unsigned char)(i * 13), decomp[i]) << "xor with 0 ref is identity";
+    free(decomp);
+
+    buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize);
+    for (unsigned int i = 0; i < bufferSize; i++)
+        buffer[i] = i * 13;
+    out.clear();
+    dbg.CompressReadPixelBuffer(&out);
+    decompSize = 0;
+    decomp = dbg.Decompress(out.data(), out.length(), &decompSize);
+    ASSERT_EQ(decompSize, bufferSize);
+    for (unsigned int i = 0; i < bufferSize; i++)
+        EXPECT_EQ(0, decomp[i]) << "xor with same ref is 0";
+    free(decomp);
+
+    buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize);
+    for (unsigned int i = 0; i < bufferSize; i++)
+        buffer[i] = i * 19;
+    out.clear();
+    dbg.CompressReadPixelBuffer(&out);
+    decompSize = 0;
+    decomp = dbg.Decompress(out.data(), out.length(), &decompSize);
+    ASSERT_EQ(decompSize, bufferSize);
+    for (unsigned int i = 0; i < bufferSize; i++)
+        EXPECT_EQ((unsigned char)(i * 13) ^ (unsigned char)(i * 19), decomp[i])
+        << "xor ref";
+    free(decomp);
+}
+
+TEST_F(DbgContextTest, UseProgram)
+{
+    static const GLuint _program = 74568;
+    static const struct Attribute {
+        const char * name;
+        GLint location;
+        GLint size;
+        GLenum type;
+    } _attributes [] = {
+        {"aaa", 2, 2, GL_FLOAT_VEC2},
+        {"bb", 6, 2, GL_FLOAT_MAT2},
+        {"c", 1, 1, GL_FLOAT},
+    };
+    static const unsigned int _attributeCount = sizeof(_attributes) / sizeof(*_attributes);
+    struct GL {
+        static void GetProgramiv(GLuint program, GLenum pname, GLint* params) {
+            EXPECT_EQ(_program, program);
+            ASSERT_NE((GLint *)NULL, params);
+            switch (pname) {
+            case GL_ACTIVE_ATTRIBUTES:
+                *params = _attributeCount;
+                return;
+            case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH:
+                *params = 4; // includes NULL terminator
+                return;
+            default:
+                ADD_FAILURE() << "not handled pname: " << pname;
+            }
+        }
+
+        static GLint GetAttribLocation(GLuint program, const GLchar* name) {
+            EXPECT_EQ(_program, program);
+            for (unsigned int i = 0; i < _attributeCount; i++)
+                if (!strcmp(name, _attributes[i].name))
+                    return _attributes[i].location;
+            ADD_FAILURE() << "unknown attribute name: " << name;
+            return -1;
+        }
+
+        static void GetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize,
+                                    GLsizei* length, GLint* size, GLenum* type, GLchar* name) {
+            EXPECT_EQ(_program, program);
+            ASSERT_LT(index, _attributeCount);
+            const Attribute & att = _attributes[index];
+            ASSERT_GE(bufsize, strlen(att.name) + 1);
+            ASSERT_NE((GLint *)NULL, size);
+            ASSERT_NE((GLenum *)NULL, type);
+            ASSERT_NE((GLchar *)NULL, name);
+            strcpy(name, att.name);
+            if (length)
+                *length = strlen(name) + 1;
+            *size = att.size;
+            *type = att.type;
+        }
+    };
+    hooks.gl.glGetProgramiv = GL::GetProgramiv;
+    hooks.gl.glGetAttribLocation = GL::GetAttribLocation;
+    hooks.gl.glGetActiveAttrib = GL::GetActiveAttrib;
+    dbg.glUseProgram(_program);
+    EXPECT_EQ(10, dbg.maxAttrib);
+    dbg.glUseProgram(0);
+    EXPECT_EQ(0, dbg.maxAttrib);
+}
+}  // namespace
+
+int main(int argc, char **argv)
+{
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/opengl/libs/GLES2_dbg/test/test_server.cpp b/opengl/libs/GLES2_dbg/test/test_server.cpp
new file mode 100644 (file)
index 0000000..7fb87ea
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include "header.h"
+#include "gtest/gtest.h"
+#include "egl_tls.h"
+#include "hooks.h"
+
+namespace android
+{
+extern FILE * file;
+extern unsigned int MAX_FILE_SIZE;
+extern pthread_key_t dbgEGLThreadLocalStorageKey;
+};
+
+// tmpfile fails, so need to manually make a writable file first
+static const char * filePath = "/data/local/tmp/dump.gles2dbg";
+
+class ServerFileTest : public ::testing::Test
+{
+protected:
+    ServerFileTest() { }
+
+    virtual ~ServerFileTest() { }
+
+    virtual void SetUp() {
+        MAX_FILE_SIZE = 8 << 20;
+        ASSERT_EQ((FILE *)NULL, file);
+        file = fopen("/data/local/tmp/dump.gles2dbg", "wb+");
+        ASSERT_NE((FILE *)NULL, file) << "make sure file is writable: "
+        << filePath;
+    }
+
+    virtual void TearDown() {
+        ASSERT_NE((FILE *)NULL, file);
+        fclose(file);
+        file = NULL;
+    }
+
+    void Read(glesv2debugger::Message & msg) const {
+        msg.Clear();
+        uint32_t len = 0;
+        ASSERT_EQ(sizeof(len), fread(&len, 1, sizeof(len), file));
+        ASSERT_GT(len, 0u);
+        char * buffer = new char [len];
+        ASSERT_EQ(len, fread(buffer, 1, len, file));
+        msg.ParseFromArray(buffer, len);
+        delete buffer;
+    }
+};
+
+
+
+TEST_F(ServerFileTest, Send)
+{
+    glesv2debugger::Message msg, cmd, read;
+    msg.set_context_id(1);
+    msg.set_function(msg.glFinish);
+    msg.set_expect_response(false);
+    msg.set_type(msg.BeforeCall);
+    rewind(file);
+    android::Send(msg, cmd);
+    rewind(file);
+    Read(read);
+    EXPECT_EQ(msg.context_id(), read.context_id());
+    EXPECT_EQ(msg.function(), read.function());
+    EXPECT_EQ(msg.expect_response(), read.expect_response());
+    EXPECT_EQ(msg.type(), read.type());
+}
+
+void * glNoop()
+{
+    return 0;
+}
+
+class ServerFileContextTest : public ServerFileTest
+{
+protected:
+    tls_t tls;
+    gl_hooks_t hooks;
+
+    ServerFileContextTest() { }
+
+    virtual ~ServerFileContextTest() { }
+
+    virtual void SetUp() {
+        ServerFileTest::SetUp();
+
+        if (dbgEGLThreadLocalStorageKey == -1)
+            pthread_key_create(&dbgEGLThreadLocalStorageKey, NULL);
+        ASSERT_NE(-1, dbgEGLThreadLocalStorageKey);
+        tls.dbg = new DbgContext(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE);
+        ASSERT_NE((void *)NULL, tls.dbg);
+        pthread_setspecific(dbgEGLThreadLocalStorageKey, &tls);
+        for (unsigned int i = 0; i < sizeof(hooks) / sizeof(void *); i++)
+            ((void **)&hooks)[i] = reinterpret_cast<void *>(glNoop);
+    }
+
+    virtual void TearDown() {
+        ServerFileTest::TearDown();
+    }
+};
+
+TEST_F(ServerFileContextTest, MessageLoop)
+{
+    static const int arg0 = 45;
+    static const float arg7 = -87.2331f;
+    static const int arg8 = -3;
+    static const int * ret = reinterpret_cast<int *>(870);
+
+    struct Caller : public FunctionCall {
+        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+            msg.set_arg0(arg0);
+            msg.set_arg7((int &)arg7);
+            msg.set_arg8(arg8);
+            return ret;
+        }
+    } caller;
+    const int contextId = reinterpret_cast<int>(tls.dbg);
+    glesv2debugger::Message msg, read;
+
+    EXPECT_EQ(ret, MessageLoop(caller, msg, msg.glFinish));
+
+    rewind(file);
+    Read(read);
+    EXPECT_EQ(contextId, read.context_id());
+    EXPECT_EQ(read.glFinish, read.function());
+    EXPECT_EQ(false, read.expect_response());
+    EXPECT_EQ(read.BeforeCall, read.type());
+
+    Read(read);
+    EXPECT_EQ(contextId, read.context_id());
+    EXPECT_EQ(read.glFinish, read.function());
+    EXPECT_EQ(false, read.expect_response());
+    EXPECT_EQ(read.AfterCall, read.type());
+    EXPECT_TRUE(read.has_time());
+    EXPECT_EQ(arg0, read.arg0());
+    const int readArg7 = read.arg7();
+    EXPECT_EQ(arg7, (float &)readArg7);
+    EXPECT_EQ(arg8, read.arg8());
+
+    const long pos = ftell(file);
+    fseek(file, 0, SEEK_END);
+    EXPECT_EQ(pos, ftell(file))
+    << "should only write the BeforeCall and AfterCall messages";
+}
+
+TEST_F(ServerFileContextTest, DisableEnableVertexAttribArray)
+{
+    Debug_glEnableVertexAttribArray(tls.dbg->MAX_VERTEX_ATTRIBS + 2); // should just ignore invalid index
+
+    glesv2debugger::Message read;
+    rewind(file);
+    Read(read);
+    EXPECT_EQ(read.glEnableVertexAttribArray, read.function());
+    EXPECT_EQ(tls.dbg->MAX_VERTEX_ATTRIBS + 2, read.arg0());
+    Read(read);
+
+    rewind(file);
+    Debug_glDisableVertexAttribArray(tls.dbg->MAX_VERTEX_ATTRIBS + 4); // should just ignore invalid index
+    rewind(file);
+    Read(read);
+    Read(read);
+
+    for (unsigned int i = 0; i < tls.dbg->MAX_VERTEX_ATTRIBS; i += 5) {
+        rewind(file);
+        Debug_glEnableVertexAttribArray(i);
+        EXPECT_TRUE(tls.dbg->vertexAttribs[i].enabled);
+        rewind(file);
+        Read(read);
+        EXPECT_EQ(read.glEnableVertexAttribArray, read.function());
+        EXPECT_EQ(i, read.arg0());
+        Read(read);
+
+        rewind(file);
+        Debug_glDisableVertexAttribArray(i);
+        EXPECT_FALSE(tls.dbg->vertexAttribs[i].enabled);
+        rewind(file);
+        Read(read);
+        EXPECT_EQ(read.glDisableVertexAttribArray, read.function());
+        EXPECT_EQ(i, read.arg0());
+        Read(read);
+    }
+}
diff --git a/opengl/libs/GLES2_dbg/test/test_socket.cpp b/opengl/libs/GLES2_dbg/test/test_socket.cpp
new file mode 100644 (file)
index 0000000..1c31a94
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include "header.h"
+#include "gtest/gtest.h"
+#include "egl_tls.h"
+#include "hooks.h"
+
+namespace android
+{
+extern int serverSock, clientSock;
+extern pthread_key_t dbgEGLThreadLocalStorageKey;
+};
+
+void * glNoop();
+
+class SocketContextTest : public ::testing::Test
+{
+protected:
+    tls_t tls;
+    gl_hooks_t hooks;
+    int sock;
+    char * buffer;
+    unsigned int bufferSize;
+
+    SocketContextTest() : sock(-1) {
+    }
+
+    virtual ~SocketContextTest() {
+    }
+
+    virtual void SetUp() {
+        if (dbgEGLThreadLocalStorageKey == -1)
+            pthread_key_create(&dbgEGLThreadLocalStorageKey, NULL);
+        ASSERT_NE(-1, dbgEGLThreadLocalStorageKey);
+        tls.dbg = new DbgContext(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE);
+        ASSERT_NE((void *)NULL, tls.dbg);
+        pthread_setspecific(dbgEGLThreadLocalStorageKey, &tls);
+        for (unsigned int i = 0; i < sizeof(hooks) / sizeof(void *); i++)
+            ((void **)&hooks)[i] = (void *)glNoop;
+
+        int socks[2] = {-1, -1};
+        ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, socks));
+        clientSock = socks[0];
+        sock = socks[1];
+
+        bufferSize = 128;
+        buffer = new char [128];
+        ASSERT_NE((char *)NULL, buffer);
+    }
+
+    virtual void TearDown() {
+        close(sock);
+        close(clientSock);
+        clientSock = -1;
+        delete buffer;
+    }
+
+    void Write(glesv2debugger::Message & msg) const {
+        msg.set_context_id((int)tls.dbg);
+        msg.set_type(msg.Response);
+        ASSERT_TRUE(msg.has_context_id());
+        ASSERT_TRUE(msg.has_function());
+        ASSERT_TRUE(msg.has_type());
+        ASSERT_TRUE(msg.has_expect_response());
+        static std::string str;
+        msg.SerializeToString(&str);
+        const uint32_t len = str.length();
+        ASSERT_EQ(sizeof(len), send(sock, &len, sizeof(len), 0));
+        ASSERT_EQ(str.length(), send(sock, str.data(), str.length(), 0));
+    }
+
+    void Read(glesv2debugger::Message & msg) {
+        int available = 0;
+        ASSERT_EQ(0, ioctl(sock, FIONREAD, &available));
+        ASSERT_GT(available, 0);
+        uint32_t len = 0;
+        ASSERT_EQ(sizeof(len), recv(sock, &len, sizeof(len), 0));
+        if (len > bufferSize) {
+            bufferSize = len;
+            buffer = new char[bufferSize];
+            ASSERT_NE((char *)NULL, buffer);
+        }
+        ASSERT_EQ(len, recv(sock, buffer, len, 0));
+        msg.Clear();
+        msg.ParseFromArray(buffer, len);
+        ASSERT_TRUE(msg.has_context_id());
+        ASSERT_TRUE(msg.has_function());
+        ASSERT_TRUE(msg.has_type());
+        ASSERT_TRUE(msg.has_expect_response());
+    }
+
+    void CheckNoAvailable() {
+        int available = 0;
+        ASSERT_EQ(0, ioctl(sock, FIONREAD, &available));
+        ASSERT_EQ(available, 0);
+    }
+};
+
+TEST_F(SocketContextTest, MessageLoopSkip)
+{
+    static const int arg0 = 45;
+    static const float arg7 = -87.2331f;
+    static const int arg8 = -3;
+    static const int * ret = (int *)870;
+
+    struct Caller : public FunctionCall {
+        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+            msg.set_arg0(arg0);
+            msg.set_arg7((int &)arg7);
+            msg.set_arg8(arg8);
+            return ret;
+        }
+    } caller;
+    glesv2debugger::Message msg, read, cmd;
+    tls.dbg->expectResponse.Bit(msg.glFinish, true);
+
+    cmd.set_function(cmd.SKIP);
+    cmd.set_expect_response(false);
+    Write(cmd);
+
+    EXPECT_NE(ret, MessageLoop(caller, msg, msg.glFinish));
+
+    Read(read);
+    EXPECT_EQ(read.glFinish, read.function());
+    EXPECT_EQ(read.BeforeCall, read.type());
+    EXPECT_NE(arg0, read.arg0());
+    EXPECT_NE((int &)arg7, read.arg7());
+    EXPECT_NE(arg8, read.arg8());
+
+    CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, MessageLoopContinue)
+{
+    static const int arg0 = GL_FRAGMENT_SHADER;
+    static const int ret = -342;
+    struct Caller : public FunctionCall {
+        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+            msg.set_ret(ret);
+            return (int *)ret;
+        }
+    } caller;
+    glesv2debugger::Message msg, read, cmd;
+    tls.dbg->expectResponse.Bit(msg.glCreateShader, true);
+
+    cmd.set_function(cmd.CONTINUE);
+    cmd.set_expect_response(false); // MessageLoop should automatically skip after continue
+    Write(cmd);
+
+    msg.set_arg0(arg0);
+    EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateShader));
+
+    Read(read);
+    EXPECT_EQ(read.glCreateShader, read.function());
+    EXPECT_EQ(read.BeforeCall, read.type());
+    EXPECT_EQ(arg0, read.arg0());
+
+    Read(read);
+    EXPECT_EQ(read.glCreateShader, read.function());
+    EXPECT_EQ(read.AfterCall, read.type());
+    EXPECT_EQ(ret, read.ret());
+
+    CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, MessageLoopGenerateCall)
+{
+    static const int ret = -342;
+    static unsigned int createShader, createProgram;
+    createShader = 0;
+    createProgram = 0;
+    struct Caller : public FunctionCall {
+        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+            const int r = (int)_c->glCreateProgram();
+            msg.set_ret(r);
+            return (int *)r;
+        }
+        static GLuint CreateShader(const GLenum type) {
+            createShader++;
+            return type;
+        }
+        static GLuint CreateProgram() {
+            createProgram++;
+            return ret;
+        }
+    } caller;
+    glesv2debugger::Message msg, read, cmd;
+    hooks.gl.glCreateShader = caller.CreateShader;
+    hooks.gl.glCreateProgram = caller.CreateProgram;
+    tls.dbg->expectResponse.Bit(msg.glCreateProgram, true);
+
+    cmd.set_function(cmd.glCreateShader);
+    cmd.set_arg0(GL_FRAGMENT_SHADER);
+    cmd.set_expect_response(true);
+    Write(cmd);
+
+    cmd.Clear();
+    cmd.set_function(cmd.CONTINUE);
+    cmd.set_expect_response(true);
+    Write(cmd);
+
+    cmd.set_function(cmd.glCreateShader);
+    cmd.set_arg0(GL_VERTEX_SHADER);
+    cmd.set_expect_response(false); // MessageLoop should automatically skip afterwards
+    Write(cmd);
+
+    EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateProgram));
+
+    Read(read);
+    EXPECT_EQ(read.glCreateProgram, read.function());
+    EXPECT_EQ(read.BeforeCall, read.type());
+
+    Read(read);
+    EXPECT_EQ(read.glCreateShader, read.function());
+    EXPECT_EQ(read.AfterGeneratedCall, read.type());
+    EXPECT_EQ(GL_FRAGMENT_SHADER, read.ret());
+
+    Read(read);
+    EXPECT_EQ(read.glCreateProgram, read.function());
+    EXPECT_EQ(read.AfterCall, read.type());
+    EXPECT_EQ(ret, read.ret());
+
+    Read(read);
+    EXPECT_EQ(read.glCreateShader, read.function());
+    EXPECT_EQ(read.AfterGeneratedCall, read.type());
+    EXPECT_EQ(GL_VERTEX_SHADER, read.ret());
+
+    EXPECT_EQ(2, createShader);
+    EXPECT_EQ(1, createProgram);
+
+    CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, MessageLoopSetProp)
+{
+    static const int ret = -342;
+    static unsigned int createShader, createProgram;
+    createShader = 0;
+    createProgram = 0;
+    struct Caller : public FunctionCall {
+        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+            const int r = (int)_c->glCreateProgram();
+            msg.set_ret(r);
+            return (int *)r;
+        }
+        static GLuint CreateShader(const GLenum type) {
+            createShader++;
+            return type;
+        }
+        static GLuint CreateProgram() {
+            createProgram++;
+            return ret;
+        }
+    } caller;
+    glesv2debugger::Message msg, read, cmd;
+    hooks.gl.glCreateShader = caller.CreateShader;
+    hooks.gl.glCreateProgram = caller.CreateProgram;
+    tls.dbg->expectResponse.Bit(msg.glCreateProgram, false);
+
+    cmd.set_function(cmd.SETPROP);
+    cmd.set_prop(cmd.ExpectResponse);
+    cmd.set_arg0(cmd.glCreateProgram);
+    cmd.set_arg1(true);
+    cmd.set_expect_response(true);
+    Write(cmd);
+
+    cmd.Clear();
+    cmd.set_function(cmd.glCreateShader);
+    cmd.set_arg0(GL_FRAGMENT_SHADER);
+    cmd.set_expect_response(true);
+    Write(cmd);
+
+    cmd.set_function(cmd.SETPROP);
+    cmd.set_prop(cmd.CaptureDraw);
+    cmd.set_arg0(819);
+    cmd.set_expect_response(true);
+    Write(cmd);
+
+    cmd.Clear();
+    cmd.set_function(cmd.CONTINUE);
+    cmd.set_expect_response(true);
+    Write(cmd);
+
+    cmd.set_function(cmd.glCreateShader);
+    cmd.set_arg0(GL_VERTEX_SHADER);
+    cmd.set_expect_response(false); // MessageLoop should automatically skip afterwards
+    Write(cmd);
+
+    EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateProgram));
+
+    EXPECT_TRUE(tls.dbg->expectResponse.Bit(msg.glCreateProgram));
+    EXPECT_EQ(819, tls.dbg->captureDraw);
+
+    Read(read);
+    EXPECT_EQ(read.glCreateProgram, read.function());
+    EXPECT_EQ(read.BeforeCall, read.type());
+
+    Read(read);
+    EXPECT_EQ(read.glCreateShader, read.function());
+    EXPECT_EQ(read.AfterGeneratedCall, read.type());
+    EXPECT_EQ(GL_FRAGMENT_SHADER, read.ret());
+
+    Read(read);
+    EXPECT_EQ(read.glCreateProgram, read.function());
+    EXPECT_EQ(read.AfterCall, read.type());
+    EXPECT_EQ(ret, read.ret());
+
+    Read(read);
+    EXPECT_EQ(read.glCreateShader, read.function());
+    EXPECT_EQ(read.AfterGeneratedCall, read.type());
+    EXPECT_EQ(GL_VERTEX_SHADER, read.ret());
+
+    EXPECT_EQ(2, createShader);
+    EXPECT_EQ(1, createProgram);
+
+    CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, TexImage2D)
+{
+    static const GLenum _target = GL_TEXTURE_2D;
+    static const GLint _level = 1, _internalformat = GL_RGBA;
+    static const GLsizei _width = 2, _height = 2;
+    static const GLint _border = 333;
+    static const GLenum _format = GL_RGB, _type = GL_UNSIGNED_SHORT_5_6_5;
+    static const short _pixels [_width * _height] = {11, 22, 33, 44};
+    static unsigned int texImage2D;
+    texImage2D = 0;
+
+    struct Caller {
+        static void TexImage2D(GLenum target, GLint level, GLint internalformat,
+                               GLsizei width, GLsizei height, GLint border,
+                               GLenum format, GLenum type, const GLvoid* pixels) {
+            EXPECT_EQ(_target, target);
+            EXPECT_EQ(_level, level);
+            EXPECT_EQ(_internalformat, internalformat);
+            EXPECT_EQ(_width, width);
+            EXPECT_EQ(_height, height);
+            EXPECT_EQ(_border, border);
+            EXPECT_EQ(_format, format);
+            EXPECT_EQ(_type, type);
+            EXPECT_EQ(0, memcmp(_pixels, pixels, sizeof(_pixels)));
+            texImage2D++;
+        }
+    } caller;
+    glesv2debugger::Message msg, read, cmd;
+    hooks.gl.glTexImage2D = caller.TexImage2D;
+    tls.dbg->expectResponse.Bit(msg.glTexImage2D, false);
+
+    Debug_glTexImage2D(_target, _level, _internalformat, _width, _height, _border,
+                       _format, _type, _pixels);
+    EXPECT_EQ(1, texImage2D);
+
+    Read(read);
+    EXPECT_EQ(read.glTexImage2D, read.function());
+    EXPECT_EQ(read.BeforeCall, read.type());
+    EXPECT_EQ(_target, read.arg0());
+    EXPECT_EQ(_level, read.arg1());
+    EXPECT_EQ(_internalformat, read.arg2());
+    EXPECT_EQ(_width, read.arg3());
+    EXPECT_EQ(_height, read.arg4());
+    EXPECT_EQ(_border, read.arg5());
+    EXPECT_EQ(_format, read.arg6());
+    EXPECT_EQ(_type, read.arg7());
+
+    EXPECT_TRUE(read.has_data());
+    uint32_t dataLen = 0;
+    const unsigned char * data = tls.dbg->Decompress(read.data().data(),
+                                 read.data().length(), &dataLen);
+    EXPECT_EQ(sizeof(_pixels), dataLen);
+    if (sizeof(_pixels) == dataLen)
+        EXPECT_EQ(0, memcmp(_pixels, data, sizeof(_pixels)));
+
+    Read(read);
+    EXPECT_EQ(read.glTexImage2D, read.function());
+    EXPECT_EQ(read.AfterCall, read.type());
+
+    CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, CopyTexImage2D)
+{
+    static const GLenum _target = GL_TEXTURE_2D;
+    static const GLint _level = 1, _internalformat = GL_RGBA;
+    static const GLint _x = 9, _y = 99;
+    static const GLsizei _width = 2, _height = 3;
+    static const GLint _border = 333;
+    static const int _pixels [_width * _height] = {11, 22, 33, 44, 55, 66};
+    static unsigned int copyTexImage2D, readPixels;
+    copyTexImage2D = 0, readPixels = 0;
+
+    struct Caller {
+        static void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat,
+                                   GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {
+            EXPECT_EQ(_target, target);
+            EXPECT_EQ(_level, level);
+            EXPECT_EQ(_internalformat, internalformat);
+            EXPECT_EQ(_x, x);
+            EXPECT_EQ(_y, y);
+            EXPECT_EQ(_width, width);
+            EXPECT_EQ(_height, height);
+            EXPECT_EQ(_border, border);
+            copyTexImage2D++;
+        }
+        static void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
+                               GLenum format, GLenum type, GLvoid* pixels) {
+            EXPECT_EQ(_x, x);
+            EXPECT_EQ(_y, y);
+            EXPECT_EQ(_width, width);
+            EXPECT_EQ(_height, height);
+            EXPECT_EQ(GL_RGBA, format);
+            EXPECT_EQ(GL_UNSIGNED_BYTE, type);
+            ASSERT_NE((void *)NULL, pixels);
+            memcpy(pixels, _pixels, sizeof(_pixels));
+            readPixels++;
+        }
+    } caller;
+    glesv2debugger::Message msg, read, cmd;
+    hooks.gl.glCopyTexImage2D = caller.CopyTexImage2D;
+    hooks.gl.glReadPixels = caller.ReadPixels;
+    tls.dbg->expectResponse.Bit(msg.glCopyTexImage2D, false);
+
+    Debug_glCopyTexImage2D(_target, _level, _internalformat, _x, _y, _width, _height,
+                           _border);
+    ASSERT_EQ(1, copyTexImage2D);
+    ASSERT_EQ(1, readPixels);
+
+    Read(read);
+    EXPECT_EQ(read.glCopyTexImage2D, read.function());
+    EXPECT_EQ(read.BeforeCall, read.type());
+    EXPECT_EQ(_target, read.arg0());
+    EXPECT_EQ(_level, read.arg1());
+    EXPECT_EQ(_internalformat, read.arg2());
+    EXPECT_EQ(_x, read.arg3());
+    EXPECT_EQ(_y, read.arg4());
+    EXPECT_EQ(_width, read.arg5());
+    EXPECT_EQ(_height, read.arg6());
+    EXPECT_EQ(_border, read.arg7());
+
+    EXPECT_TRUE(read.has_data());
+    EXPECT_EQ(read.ReferencedImage, read.data_type());
+    EXPECT_EQ(GL_RGBA, read.pixel_format());
+    EXPECT_EQ(GL_UNSIGNED_BYTE, read.pixel_type());
+    uint32_t dataLen = 0;
+    unsigned char * const data = tls.dbg->Decompress(read.data().data(),
+                                 read.data().length(), &dataLen);
+    ASSERT_EQ(sizeof(_pixels), dataLen);
+    for (unsigned i = 0; i < sizeof(_pixels) / sizeof(*_pixels); i++)
+        EXPECT_EQ(_pixels[i], ((const int *)data)[i]) << "xor with 0 ref is identity";
+    free(data);
+
+    Read(read);
+    EXPECT_EQ(read.glCopyTexImage2D, read.function());
+    EXPECT_EQ(read.AfterCall, read.type());
+
+    CheckNoAvailable();
+}