new tests for SurfaceTexture synchronous mode

Change-Id: Icfdaa625238246f8d0224efe28fdf2c1c24203f8
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 5c37288..e9c3411 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -17,23 +17,78 @@
 #include <EGL/egl.h>
 #include <gtest/gtest.h>
 #include <gui/SurfaceTextureClient.h>
+#include <utils/threads.h>
 
 namespace android {
 
 class SurfaceTextureClientTest : public ::testing::Test {
 protected:
+    SurfaceTextureClientTest():
+            mEglDisplay(EGL_NO_DISPLAY),
+            mEglSurface(EGL_NO_SURFACE),
+            mEglContext(EGL_NO_CONTEXT) {
+    }
+
     virtual void SetUp() {
         mST = new SurfaceTexture(123);
         mSTC = new SurfaceTextureClient(mST);
+
+        // We need a valid GL context so we can test updateTexImage()
+        // This initializes EGL and create a dummy GL context with a
+        // pbuffer render target.
+        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
+
+        EGLint majorVersion, minorVersion;
+        EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        EGLConfig myConfig;
+        EGLint numConfigs = 0;
+        EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(),
+                &myConfig, 1, &numConfigs));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        EGLint pbufferAttribs[] = {
+            EGL_WIDTH, 16,
+            EGL_HEIGHT, 16,
+            EGL_NONE };
+        mEglSurface = eglCreatePbufferSurface(mEglDisplay, myConfig, pbufferAttribs);
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
+
+        mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, 0);
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
+
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
     }
 
     virtual void TearDown() {
         mST.clear();
         mSTC.clear();
+        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+        eglDestroyContext(mEglDisplay, mEglContext);
+        eglDestroySurface(mEglDisplay, mEglSurface);
+        eglTerminate(mEglDisplay);
+    }
+
+    virtual EGLint const* getConfigAttribs() {
+        static EGLint sDefaultConfigAttribs[] = {
+            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+            EGL_NONE
+        };
+
+        return sDefaultConfigAttribs;
     }
 
     sp<SurfaceTexture> mST;
     sp<SurfaceTextureClient> mSTC;
+    EGLDisplay mEglDisplay;
+    EGLSurface mEglSurface;
+    EGLContext mEglContext;
 };
 
 TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) {
@@ -247,4 +302,212 @@
     ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
 }
 
+TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, st->setSynchronousMode(false));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(OK, st->updateTexImage());
+
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 2));
+
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(OK, st->updateTexImage());
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeSlowRetire) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+    EXPECT_NE(buf[0], buf[1]);
+    EXPECT_NE(buf[1], buf[2]);
+    EXPECT_NE(buf[2], buf[0]);
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]);
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeFastRetire) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+    EXPECT_NE(buf[0], buf[1]);
+    EXPECT_NE(buf[1], buf[2]);
+    EXPECT_NE(buf[2], buf[0]);
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]);
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDQQR) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]);
+
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    EXPECT_NE(buf[0], buf[1]);
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+    EXPECT_NE(buf[1], buf[2]);
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDequeueCurrent) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    android_native_buffer_t* firstBuf;
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &firstBuf));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), firstBuf));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), firstBuf);
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+    EXPECT_NE(buf[0], buf[1]);
+    EXPECT_NE(buf[1], buf[2]);
+    EXPECT_NE(buf[2], buf[0]);
+    EXPECT_EQ(firstBuf, buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeTwoBuffers) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    EXPECT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+    EXPECT_EQ(OK, native_window_set_buffer_count(anw.get(), 2));
+    EXPECT_NE(OK, native_window_set_buffer_count(anw.get(), 1));
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeMinUndequeued) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+    EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    EXPECT_EQ(-EBUSY, anw->dequeueBuffer(anw.get(), &buf[2]));
+
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+
+    EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[2]));
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeWaitRetire) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+
+    class MyThread : public Thread {
+        sp<SurfaceTexture> st;
+        EGLContext ctx;
+        EGLSurface sur;
+        EGLDisplay dpy;
+        bool mBufferRetired;
+        Mutex mLock;
+        virtual bool threadLoop() {
+            eglMakeCurrent(dpy, sur, sur, ctx);
+            usleep(20000);
+            Mutex::Autolock _l(mLock);
+            st->updateTexImage();
+            mBufferRetired = true;
+            eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+            return false;
+        }
+    public:
+        MyThread(const sp<SurfaceTexture>& st)
+            : st(st), mBufferRetired(false) {
+            ctx = eglGetCurrentContext();
+            sur = eglGetCurrentSurface(EGL_DRAW);
+            dpy = eglGetCurrentDisplay();
+            eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+        }
+        ~MyThread() {
+            eglMakeCurrent(dpy, sur, sur, ctx);
+        }
+        void bufferDequeued() {
+            Mutex::Autolock _l(mLock);
+            EXPECT_EQ(true, mBufferRetired);
+        }
+    };
+
+    android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 2));
+    // dequeue/queue/update so we have a current buffer
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    st->updateTexImage();
+
+    MyThread* thread = new MyThread(st);
+    sp<Thread> threadBase(thread);
+
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+    thread->run();
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+    thread->bufferDequeued();
+    thread->requestExitAndWait();
+}
+
 }