libgui: Fix a couple of minor bugs, extract GLTest

Fix a couple of minor bugs in existing tests, and extract the GLTest class into
its own files.

Change-Id: I7d9570a0d3dbc9c5d7f9b13f2017607af42744b3
diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp
new file mode 100644
index 0000000..d7acb4d
--- /dev/null
+++ b/libs/gui/tests/GLTest.cpp
@@ -0,0 +1,324 @@
+#include "GLTest.h"
+
+#include <gui/Surface.h>
+
+#include <GLES2/gl2.h>
+
+namespace android {
+
+static int abs(int value) {
+    return value > 0 ? value : -value;
+}
+
+void GLTest::SetUp() {
+    const ::testing::TestInfo* const testInfo =
+        ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name());
+
+    mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
+
+    EGLint majorVersion;
+    EGLint minorVersion;
+    EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    RecordProperty("EglVersionMajor", majorVersion);
+    RecordProperty("EglVersionMinor", minorVersion);
+
+    EGLint numConfigs = 0;
+    EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig, 1,
+            &numConfigs));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS");
+    if (displaySecsEnv != NULL) {
+        mDisplaySecs = atoi(displaySecsEnv);
+        if (mDisplaySecs < 0) {
+            mDisplaySecs = 0;
+        }
+    } else {
+        mDisplaySecs = 0;
+    }
+
+    if (mDisplaySecs > 0) {
+        mComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+        mSurfaceControl = mComposerClient->createSurface(
+                String8("Test Surface"), getSurfaceWidth(), getSurfaceHeight(),
+                PIXEL_FORMAT_RGB_888, 0);
+
+        ASSERT_TRUE(mSurfaceControl != NULL);
+        ASSERT_TRUE(mSurfaceControl->isValid());
+
+        SurfaceComposerClient::openGlobalTransaction();
+        ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF));
+        ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
+        SurfaceComposerClient::closeGlobalTransaction();
+
+        sp<ANativeWindow> window = mSurfaceControl->getSurface();
+        mEglSurface = createWindowSurface(mEglDisplay, mGlConfig, window);
+    } else {
+        EGLint pbufferAttribs[] = {
+            EGL_WIDTH, getSurfaceWidth(),
+            EGL_HEIGHT, getSurfaceHeight(),
+            EGL_NONE };
+
+        mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig,
+                pbufferAttribs);
+    }
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
+
+    mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT,
+            getContextAttribs());
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
+
+    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+            mEglContext));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    EGLint w, h;
+    EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    RecordProperty("EglSurfaceWidth", w);
+    RecordProperty("EglSurfaceHeight", h);
+
+    glViewport(0, 0, w, h);
+    ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+}
+
+void GLTest::TearDown() {
+    // Display the result
+    if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) {
+        eglSwapBuffers(mEglDisplay, mEglSurface);
+        sleep(mDisplaySecs);
+    }
+
+    if (mComposerClient != NULL) {
+        mComposerClient->dispose();
+    }
+    if (mEglContext != EGL_NO_CONTEXT) {
+        eglDestroyContext(mEglDisplay, mEglContext);
+    }
+    if (mEglSurface != EGL_NO_SURFACE) {
+        eglDestroySurface(mEglDisplay, mEglSurface);
+    }
+    if (mEglDisplay != EGL_NO_DISPLAY) {
+        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+                EGL_NO_CONTEXT);
+        eglTerminate(mEglDisplay);
+    }
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    const ::testing::TestInfo* const testInfo =
+        ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGV("End test:   %s.%s", testInfo->test_case_name(), testInfo->name());
+}
+
+EGLint const* GLTest::getConfigAttribs() {
+    static const EGLint sDefaultConfigAttribs[] = {
+        EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+        EGL_RED_SIZE, 8,
+        EGL_GREEN_SIZE, 8,
+        EGL_BLUE_SIZE, 8,
+        EGL_ALPHA_SIZE, 8,
+        EGL_DEPTH_SIZE, 16,
+        EGL_STENCIL_SIZE, 8,
+        EGL_NONE };
+
+    return sDefaultConfigAttribs;
+}
+
+EGLint const* GLTest::getContextAttribs() {
+    static const EGLint sDefaultContextAttribs[] = {
+        EGL_CONTEXT_CLIENT_VERSION, 2,
+        EGL_NONE };
+
+    return sDefaultContextAttribs;
+}
+
+EGLint GLTest::getSurfaceWidth() {
+    return 512;
+}
+
+EGLint GLTest::getSurfaceHeight() {
+    return 512;
+}
+
+EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config,
+                                       sp<ANativeWindow>& window) const {
+    ALOGI("Creating window surface (non-SRGB)");
+    return eglCreateWindowSurface(display, config, window.get(), NULL);
+}
+
+::testing::AssertionResult GLTest::checkPixel(int x, int y,
+        int r, int g, int b, int a, int tolerance) {
+    GLubyte pixel[4];
+    String8 msg;
+    glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+    GLenum err = glGetError();
+    if (err != GL_NO_ERROR) {
+        msg += String8::format("error reading pixel: %#x", err);
+        while ((err = glGetError()) != GL_NO_ERROR) {
+            msg += String8::format(", %#x", err);
+        }
+        return ::testing::AssertionFailure(::testing::Message(msg.string()));
+    }
+    if (r >= 0 && abs(r - int(pixel[0])) > tolerance) {
+        msg += String8::format("r(%d isn't %d)", pixel[0], r);
+    }
+    if (g >= 0 && abs(g - int(pixel[1])) > tolerance) {
+        if (!msg.isEmpty()) {
+            msg += " ";
+        }
+        msg += String8::format("g(%d isn't %d)", pixel[1], g);
+    }
+    if (b >= 0 && abs(b - int(pixel[2])) > tolerance) {
+        if (!msg.isEmpty()) {
+            msg += " ";
+        }
+        msg += String8::format("b(%d isn't %d)", pixel[2], b);
+    }
+    if (a >= 0 && abs(a - int(pixel[3])) > tolerance) {
+        if (!msg.isEmpty()) {
+            msg += " ";
+        }
+        msg += String8::format("a(%d isn't %d)", pixel[3], a);
+    }
+    if (!msg.isEmpty()) {
+        return ::testing::AssertionFailure(::testing::Message(msg.string()));
+    } else {
+        return ::testing::AssertionSuccess();
+    }
+}
+
+::testing::AssertionResult GLTest::assertRectEq(const Rect &r1, const Rect &r2,
+                                                int tolerance) {
+    String8 msg;
+
+    if (abs(r1.left - r2.left) > tolerance) {
+        msg += String8::format("left(%d isn't %d)", r1.left, r2.left);
+    }
+    if (abs(r1.top - r2.top) > tolerance) {
+        if (!msg.isEmpty()) {
+            msg += " ";
+        }
+        msg += String8::format("top(%d isn't %d)", r1.top, r2.top);
+    }
+    if (abs(r1.right - r2.right) > tolerance) {
+        if (!msg.isEmpty()) {
+            msg += " ";
+        }
+        msg += String8::format("right(%d isn't %d)", r1.right, r2.right);
+    }
+    if (abs(r1.bottom - r2.bottom) > tolerance) {
+        if (!msg.isEmpty()) {
+            msg += " ";
+        }
+        msg += String8::format("bottom(%d isn't %d)", r1.bottom, r2.bottom);
+    }
+    if (!msg.isEmpty()) {
+        msg += String8::format(" R1: [%d %d %d %d] R2: [%d %d %d %d]",
+                               r1.left, r1.top, r1.right, r1.bottom,
+                               r2.left, r2.top, r2.right, r2.bottom);
+        fprintf(stderr, "assertRectEq: %s\n", msg.string());
+        return ::testing::AssertionFailure(::testing::Message(msg.string()));
+    } else {
+        return ::testing::AssertionSuccess();
+    }
+}
+
+void GLTest::loadShader(GLenum shaderType, const char* pSource,
+        GLuint* outShader) {
+    GLuint shader = glCreateShader(shaderType);
+    ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+    if (shader) {
+        glShaderSource(shader, 1, &pSource, NULL);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+        glCompileShader(shader);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+        GLint compiled = 0;
+        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+        if (!compiled) {
+            GLint infoLen = 0;
+            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+            ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+            if (infoLen) {
+                char* buf = (char*) malloc(infoLen);
+                if (buf) {
+                    glGetShaderInfoLog(shader, infoLen, NULL, buf);
+                    printf("Shader compile log:\n%s\n", buf);
+                    free(buf);
+                    FAIL();
+                }
+            } else {
+                char* buf = (char*) malloc(0x1000);
+                if (buf) {
+                    glGetShaderInfoLog(shader, 0x1000, NULL, buf);
+                    printf("Shader compile log:\n%s\n", buf);
+                    free(buf);
+                    FAIL();
+                }
+            }
+            glDeleteShader(shader);
+            shader = 0;
+        }
+    }
+    ASSERT_TRUE(shader != 0);
+    *outShader = shader;
+}
+
+void GLTest::createProgram(const char* pVertexSource,
+        const char* pFragmentSource, GLuint* outPgm) {
+    GLuint vertexShader, fragmentShader;
+    {
+        SCOPED_TRACE("compiling vertex shader");
+        ASSERT_NO_FATAL_FAILURE(loadShader(GL_VERTEX_SHADER, pVertexSource,
+                &vertexShader));
+    }
+    {
+        SCOPED_TRACE("compiling fragment shader");
+        ASSERT_NO_FATAL_FAILURE(loadShader(GL_FRAGMENT_SHADER, pFragmentSource,
+                &fragmentShader));
+    }
+
+    GLuint program = glCreateProgram();
+    ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+    if (program) {
+        glAttachShader(program, vertexShader);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+        glAttachShader(program, fragmentShader);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+        glLinkProgram(program);
+        GLint linkStatus = GL_FALSE;
+        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+        if (linkStatus != GL_TRUE) {
+            GLint bufLength = 0;
+            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+            if (bufLength) {
+                char* buf = (char*) malloc(bufLength);
+                if (buf) {
+                    glGetProgramInfoLog(program, bufLength, NULL, buf);
+                    printf("Program link log:\n%s\n", buf);
+                    free(buf);
+                    FAIL();
+                }
+            }
+            glDeleteProgram(program);
+            program = 0;
+        }
+    }
+    glDeleteShader(vertexShader);
+    glDeleteShader(fragmentShader);
+    ASSERT_TRUE(program != 0);
+    *outPgm = program;
+}
+
+} // namespace android