/*-------------------------------------------------------------------------
 * drawElements Quality Program EGL Module
 * ---------------------------------------
 *
 * Copyright 2014 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.
 *
 *//*!
 * \file
 * \brief EGL image tests.
 *//*--------------------------------------------------------------------*/

#include "teglImageTests.hpp"

#include "teglImageUtil.hpp"
#include "teglAndroidUtil.hpp"
#include "teglImageFormatTests.hpp"

#include "egluNativeDisplay.hpp"
#include "egluNativeWindow.hpp"
#include "egluNativePixmap.hpp"
#include "egluStrUtil.hpp"
#include "egluUnique.hpp"
#include "egluUtil.hpp"
#include "egluGLUtil.hpp"

#include "eglwLibrary.hpp"
#include "eglwEnums.hpp"

#include "gluDefs.hpp"
#include "gluCallLogWrapper.hpp"
#include "gluObjectWrapper.hpp"
#include "gluStrUtil.hpp"

#include "glwDefs.hpp"
#include "glwEnums.hpp"

#include "tcuTestLog.hpp"
#include "tcuCommandLine.hpp"

#include "deUniquePtr.hpp"

#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
#include <set>

using tcu::TestLog;

using std::ostringstream;
using std::set;
using std::string;
using std::vector;

using de::MovePtr;
using de::UniquePtr;
using eglu::AttribMap;
using eglu::NativePixmap;
using eglu::NativeWindow;
using eglu::ScopedCurrentContext;
using eglu::UniqueImage;
using eglu::UniqueSurface;
using glu::ApiType;
using glu::ContextType;
using glu::Texture;

using namespace glw;
using namespace eglw;

namespace deqp
{
namespace egl
{

namespace Image
{

#define CHECK_EXTENSION(DPY, EXTNAME)                                                                   \
    TCU_CHECK_AND_THROW(NotSupportedError, eglu::hasExtension(m_eglTestCtx.getLibrary(), DPY, EXTNAME), \
                        (string("Unsupported extension: ") + (EXTNAME)).c_str())

template <typename RetVal>
RetVal checkCallError(EglTestContext &eglTestCtx, const char *call, RetVal returnValue, EGLint expectError)
{
    tcu::TestContext &testCtx = eglTestCtx.getTestContext();
    TestLog &log              = testCtx.getLog();
    EGLint error;

    log << TestLog::Message << call << TestLog::EndMessage;

    error = eglTestCtx.getLibrary().getError();

    if (error != expectError)
    {
        log << TestLog::Message << "  Fail: Error code mismatch! Expected " << eglu::getErrorStr(expectError)
            << ", got " << eglu::getErrorStr(error) << TestLog::EndMessage;
        log << TestLog::Message << "  " << returnValue << " was returned" << TestLog::EndMessage;

        if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
            testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid error code");
    }

    return returnValue;
}

template <typename RetVal>
void checkCallReturn(EglTestContext &eglTestCtx, const char *call, RetVal returnValue, RetVal expectReturnValue,
                     EGLint expectError)
{
    tcu::TestContext &testCtx = eglTestCtx.getTestContext();
    TestLog &log              = testCtx.getLog();
    EGLint error;

    log << TestLog::Message << call << TestLog::EndMessage;

    error = eglTestCtx.getLibrary().getError();

    if (returnValue != expectReturnValue)
    {
        log << TestLog::Message << "  Fail: Return value mismatch! Expected " << expectReturnValue << ", got "
            << returnValue << TestLog::EndMessage;

        if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
            testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid return value");
    }

    if (error != expectError)
    {
        log << TestLog::Message << "  Fail: Error code mismatch! Expected " << eglu::getErrorStr(expectError)
            << ", got " << eglu::getErrorStr(error) << TestLog::EndMessage;

        if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
            testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid error code");
    }
}

// \note These macros expect "EglTestContext m_eglTestCtx" to be defined.
#define CHECK_EXT_CALL_RET(CALL, EXPECT_RETURN_VALUE, EXPECT_ERROR) \
    checkCallReturn(m_eglTestCtx, #CALL, CALL, (EXPECT_RETURN_VALUE), (EXPECT_ERROR))
#define CHECK_EXT_CALL_ERR(CALL, EXPECT_ERROR) checkCallError(m_eglTestCtx, #CALL, CALL, (EXPECT_ERROR))

class ImageTestCase : public TestCase, public glu::CallLogWrapper
{
public:
    ImageTestCase(EglTestContext &eglTestCtx, ApiType api, const string &name, const string &desc)
        : TestCase(eglTestCtx, name.c_str(), desc.c_str())
        , glu::CallLogWrapper(m_gl, m_testCtx.getLog())
        , m_api(api)
        , m_display(EGL_NO_DISPLAY)
    {
    }

    void init(void)
    {
        DE_ASSERT(m_display == EGL_NO_DISPLAY);
        m_display = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());

        const char *extensions[] = {"GL_OES_EGL_image"};
        m_eglTestCtx.initGLFunctions(&m_gl, m_api, DE_LENGTH_OF_ARRAY(extensions), &extensions[0]);
    }

    void deinit(void)
    {
        m_eglTestCtx.getLibrary().terminate(m_display);
        m_display = EGL_NO_DISPLAY;
    }

    bool isGLRedSupported(void)
    {
        return m_api.getMajorVersion() >= 3 || glu::hasExtension(m_gl, m_api, "GL_EXT_texture_rg");
    }

protected:
    glw::Functions m_gl;
    ApiType m_api;
    EGLDisplay m_display;
};

class InvalidCreateImage : public ImageTestCase
{
public:
    typedef EGLImage (Library::*createImage)(EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib *) const;
    typedef EGLImageKHR (Library::*createImageKHR)(EGLDisplay, EGLContext, EGLenum, EGLClientBuffer,
                                                   const EGLint *) const;

    InvalidCreateImage(EglTestContext &eglTestCtx)
        : ImageTestCase(eglTestCtx, ApiType::es(2, 0), "invalid_create_image",
                        "eglCreateImageKHR() with invalid arguments")
    {
    }

    template <typename createImageFuncType, typename imageType>
    void checkCreate(createImageFuncType createImageFunc, string createImageName, const char *msg, EGLDisplay dpy,
                     const char *dpyStr, EGLContext context, const char *ctxStr, EGLenum source, const char *srcStr,
                     EGLint expectError, imageType noImageVal);

    IterateResult iterate(void)
    {
        const Library &egl = m_eglTestCtx.getLibrary();

        if (eglu::getVersion(egl, m_display) < eglu::Version(1, 5) &&
            !eglu::hasExtension(egl, m_display, "EGL_KHR_image") &&
            !eglu::hasExtension(egl, m_display, "EGL_KHR_image_base"))
        {
            TCU_THROW(NotSupportedError, "EGLimages not supported");
        }

        if (eglu::getVersion(egl, m_display) >= eglu::Version(1, 5))
        {
#define CHECK_CREATE(MSG, DPY, CONTEXT, SOURCE, ERR)                                                               \
    checkCreate<createImage, EGLImage>(&Library::createImage, "eglCreateImage", MSG, DPY, #DPY, CONTEXT, #CONTEXT, \
                                       SOURCE, #SOURCE, ERR, EGL_NO_IMAGE)
            CHECK_CREATE("Testing bad display (-1)...", (EGLDisplay)-1, EGL_NO_CONTEXT, EGL_NONE, EGL_BAD_DISPLAY);
            CHECK_CREATE("Testing bad context (-1)...", m_display, (EGLContext)-1, EGL_NONE, EGL_BAD_CONTEXT);
            CHECK_CREATE("Testing bad source (-1)...", m_display, EGL_NO_CONTEXT, (EGLenum)-1, EGL_BAD_PARAMETER);
#undef CHECK_CREATE
        }

        if (eglu::hasExtension(egl, m_display, "EGL_KHR_image") &&
            eglu::hasExtension(egl, m_display, "EGL_KHR_image_base"))
        {
#define CHECK_CREATE_KHR(MSG, DPY, CONTEXT, SOURCE, ERR)                                                             \
    checkCreate<createImageKHR, EGLImageKHR>(&Library::createImageKHR, "eglCreateImageKHR", MSG, DPY, #DPY, CONTEXT, \
                                             #CONTEXT, SOURCE, #SOURCE, ERR, EGL_NO_IMAGE_KHR)
            CHECK_CREATE_KHR("Testing bad display (-1)...", (EGLDisplay)-1, EGL_NO_CONTEXT, EGL_NONE, EGL_BAD_DISPLAY);
            CHECK_CREATE_KHR("Testing bad context (-1)...", m_display, (EGLContext)-1, EGL_NONE, EGL_BAD_CONTEXT);
            CHECK_CREATE_KHR("Testing bad source (-1)...", m_display, EGL_NO_CONTEXT, (EGLenum)-1, EGL_BAD_PARAMETER);
#undef CHECK_CREATE_KHR
        }

        m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
        return STOP;
    }
};

template <typename createImageFuncType, typename imageType>
void InvalidCreateImage::checkCreate(createImageFuncType createImageFunc, string createImageName, const char *msg,
                                     EGLDisplay dpy, const char *dpyStr, EGLContext context, const char *ctxStr,
                                     EGLenum source, const char *srcStr, EGLint expectError, imageType noImageVal)
{
    m_testCtx.getLog() << TestLog::Message << msg << TestLog::EndMessage;
    {
        const Library &egl    = m_eglTestCtx.getLibrary();
        const imageType image = (egl.*createImageFunc)(dpy, context, source, 0, DE_NULL);
        ostringstream call;

        call << createImageName << "(" << dpyStr << ", " << ctxStr << ", " << srcStr << ", 0, DE_NULL)";
        checkCallReturn(m_eglTestCtx, call.str().c_str(), image, noImageVal, expectError);
    }
}

EGLConfig chooseConfig(const Library &egl, EGLDisplay display, ApiType apiType)
{
    AttribMap attribs;
    vector<EGLConfig> configs;
    // Prefer configs in order: pbuffer, window, pixmap
    static const EGLenum s_surfaceTypes[] = {EGL_PBUFFER_BIT, EGL_WINDOW_BIT, EGL_PIXMAP_BIT};

    attribs[EGL_RENDERABLE_TYPE] = eglu::apiRenderableType(apiType);

    for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_surfaceTypes); ++ndx)
    {
        attribs[EGL_SURFACE_TYPE] = s_surfaceTypes[ndx];
        configs                   = eglu::chooseConfigs(egl, display, attribs);

        if (!configs.empty())
            return configs.front();
    }

    TCU_THROW(NotSupportedError, "No compatible EGL configs found");
    return (EGLConfig)0;
}

class Context
{
public:
    Context(EglTestContext &eglTestCtx, EGLDisplay display, ContextType ctxType, int width, int height)
        : m_eglTestCtx(eglTestCtx)
        , m_display(display)
        , m_config(chooseConfig(eglTestCtx.getLibrary(), display, ctxType.getAPI()))
        , m_context(m_eglTestCtx.getLibrary(), m_display,
                    eglu::createGLContext(eglTestCtx.getLibrary(), m_display, m_config, ctxType))
        , m_surface(createSurface(eglTestCtx, m_display, m_config, width, height))
        , m_current(eglTestCtx.getLibrary(), m_display, m_surface->get(), m_surface->get(), *m_context)
    {
        m_eglTestCtx.initGLFunctions(&m_gl, ctxType.getAPI());
    }

    EGLConfig getConfig(void) const
    {
        return m_config;
    }
    EGLDisplay getEglDisplay(void) const
    {
        return m_display;
    }
    EGLContext getEglContext(void) const
    {
        return *m_context;
    }
    const glw::Functions &gl(void) const
    {
        return m_gl;
    }

private:
    EglTestContext &m_eglTestCtx;
    EGLDisplay m_display;
    EGLConfig m_config;
    eglu::UniqueContext m_context;
    UniquePtr<ManagedSurface> m_surface;
    ScopedCurrentContext m_current;
    glw::Functions m_gl;

    Context(const Context &);
    Context &operator=(const Context &);
};

class CreateImageGLES2 : public ImageTestCase
{
public:
    static const char *getTargetName(EGLint target)
    {
        switch (target)
        {
        case EGL_GL_TEXTURE_2D_KHR:
            return "tex2d";
        case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR:
            return "cubemap_pos_x";
        case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR:
            return "cubemap_neg_x";
        case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR:
            return "cubemap_pos_y";
        case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR:
            return "cubemap_neg_y";
        case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR:
            return "cubemap_pos_z";
        case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR:
            return "cubemap_neg_z";
        case EGL_GL_RENDERBUFFER_KHR:
            return "renderbuffer";
        case EGL_NATIVE_BUFFER_ANDROID:
            return "android_native";
        default:
            DE_ASSERT(false);
            return "";
        }
    }

    static const char *getStorageName(GLenum storage)
    {
        switch (storage)
        {
        case GL_RED:
            return "red";
        case GL_RG:
            return "rg";
        case GL_LUMINANCE:
            return "luminance";
        case GL_LUMINANCE_ALPHA:
            return "luminance_alpha";
        case GL_RGB:
            return "rgb";
        case GL_RGBA:
            return "rgba";
        case GL_RGBA16F:
            return "rgba16f";
        case GL_DEPTH_COMPONENT16:
            return "depth_component_16";
        case GL_DEPTH_COMPONENT24:
            return "depth_component_24";
        case GL_DEPTH_COMPONENT32F:
            return "depth_component_32f";
        case GL_DEPTH24_STENCIL8:
            return "depth24_stencil8";
        case GL_DEPTH32F_STENCIL8:
            return "depth32f_stencil8";
        case GL_RGB10_A2:
            return "rgb10_a2";
        case GL_RGBA4:
            return "rgba4";
        case GL_RGB5_A1:
            return "rgb5_a1";
        case GL_RGB565:
            return "rgb565";
        case GL_RGB8:
            return "rgb8";
        case GL_RGBA8:
            return "rgba8";
        case GL_STENCIL_INDEX8:
            return "stencil_index8";
        default:
            DE_ASSERT(false);
            return "";
        }
    }

    MovePtr<ImageSource> getImageSource(EGLint target, GLenum internalFormat, GLenum format, GLenum type,
                                        bool useTexLevel0)
    {
        switch (target)
        {
        case EGL_GL_TEXTURE_2D_KHR:
        case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR:
        case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR:
        case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR:
        case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR:
        case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR:
        case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR:
            DE_ASSERT(format != 0u && type != 0u);
            return createTextureImageSource(target, internalFormat, format, type, useTexLevel0);

        case EGL_GL_RENDERBUFFER_KHR:
            DE_ASSERT(format == 0u && type == 0u);
            return createRenderbufferImageSource(internalFormat);

        case EGL_NATIVE_BUFFER_ANDROID:
            DE_ASSERT(format == 0u && type == 0u);
            return createAndroidNativeImageSource(internalFormat, 1u, false);

        default:
            DE_FATAL("Impossible");
            return MovePtr<ImageSource>();
        }
    }

    CreateImageGLES2(EglTestContext &eglTestCtx, EGLint target, GLenum internalFormat, GLenum format, GLenum type,
                     bool useTexLevel0 = false)
        : ImageTestCase(eglTestCtx, ApiType::es(2, 0),
                        string("create_image_gles2_") + getTargetName(target) + "_" + getStorageName(internalFormat) +
                            (useTexLevel0 ? "_level0_only" : ""),
                        "Create EGLImage from GLES2 object")
        , m_source(getImageSource(target, internalFormat, format, type, useTexLevel0))
        , m_internalFormat(internalFormat)
    {
    }

    IterateResult iterate(void)
    {
        const Library &egl   = m_eglTestCtx.getLibrary();
        const EGLDisplay dpy = m_display;

        if (eglu::getVersion(egl, dpy) < eglu::Version(1, 5))
            CHECK_EXTENSION(dpy, m_source->getRequiredExtension());

        // Initialize result.
        m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");

        // Create GLES2 context
        TestLog &log = m_testCtx.getLog();
        const ContextType contextType(ApiType::es(2, 0));
        Context context(m_eglTestCtx, dpy, contextType, 64, 64);
        const EGLContext eglContext = context.getEglContext();

        if ((m_internalFormat == GL_RED || m_internalFormat == GL_RG) && !isGLRedSupported())
            TCU_THROW(NotSupportedError, "Unsupported extension: GL_EXT_texture_rg");

        log << TestLog::Message << "Using EGL config " << eglu::getConfigID(egl, dpy, context.getConfig())
            << TestLog::EndMessage;

        UniquePtr<ClientBuffer> clientBuffer(m_source->createBuffer(egl, context.gl()));
        const EGLImageKHR image = m_source->createImage(egl, dpy, eglContext, clientBuffer->get());

        if (image == EGL_NO_IMAGE_KHR)
        {
            log << TestLog::Message << "  Fail: Got EGL_NO_IMAGE_KHR!" << TestLog::EndMessage;

            if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
                m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got EGL_NO_IMAGE_KHR");
        }

        // Destroy image
        CHECK_EXT_CALL_RET(egl.destroyImageKHR(context.getEglDisplay(), image), (EGLBoolean)EGL_TRUE, EGL_SUCCESS);

        return STOP;
    }

private:
    const UniquePtr<ImageSource> m_source;
    const GLenum m_internalFormat;
};

class ImageTargetGLES2 : public ImageTestCase
{
public:
    static const char *getTargetName(GLenum target)
    {
        switch (target)
        {
        case GL_TEXTURE_2D:
            return "tex2d";
        case GL_RENDERBUFFER:
            return "renderbuffer";
        default:
            DE_ASSERT(false);
            return "";
        }
    }

    ImageTargetGLES2(EglTestContext &eglTestCtx, GLenum target)
        : ImageTestCase(eglTestCtx, ApiType::es(2, 0), string("image_target_gles2_") + getTargetName(target),
                        "Use EGLImage as GLES2 object")
        , m_target(target)
    {
    }

    IterateResult iterate(void)
    {
        const Library &egl = m_eglTestCtx.getLibrary();
        TestLog &log       = m_testCtx.getLog();

        // \todo [2011-07-21 pyry] Try all possible EGLImage sources
        CHECK_EXTENSION(m_display, "EGL_KHR_gl_texture_2D_image");

        // Initialize result.
        m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");

        // Create GLES2 context

        Context context(m_eglTestCtx, m_display, ContextType(ApiType::es(2, 0)), 64, 64);
        log << TestLog::Message << "Using EGL config "
            << eglu::getConfigID(m_eglTestCtx.getLibrary(), context.getEglDisplay(), context.getConfig())
            << TestLog::EndMessage;

        // Check for OES_EGL_image
        {
            const char *glExt = (const char *)glGetString(GL_EXTENSIONS);

            if (string(glExt).find("GL_OES_EGL_image") == string::npos)
                throw tcu::NotSupportedError("Extension not supported", "GL_OES_EGL_image", __FILE__, __LINE__);

            TCU_CHECK(m_gl.eglImageTargetTexture2DOES);
            TCU_CHECK(m_gl.eglImageTargetRenderbufferStorageOES);
        }

        // Create GL_TEXTURE_2D and EGLImage from it.
        log << TestLog::Message << "Creating EGLImage using GL_TEXTURE_2D with GL_RGBA storage" << TestLog::EndMessage;

        uint32_t srcTex = 1;
        GLU_CHECK_CALL(glBindTexture(GL_TEXTURE_2D, srcTex));
        GLU_CHECK_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL));
        GLU_CHECK_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));

        // Create EGL image
        EGLint attribs[] = {EGL_GL_TEXTURE_LEVEL_KHR, 0, EGL_NONE};
        EGLImageKHR image =
            CHECK_EXT_CALL_ERR(egl.createImageKHR(context.getEglDisplay(), context.getEglContext(),
                                                  EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(uintptr_t)srcTex, attribs),
                               EGL_SUCCESS);
        if (image == EGL_NO_IMAGE_KHR)
        {
            log << TestLog::Message << "  Fail: Got EGL_NO_IMAGE_KHR!" << TestLog::EndMessage;

            if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
                m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got EGL_NO_IMAGE_KHR");
        }

        // Create texture or renderbuffer
        if (m_target == GL_TEXTURE_2D)
        {
            log << TestLog::Message << "Creating GL_TEXTURE_2D from EGLimage" << TestLog::EndMessage;

            uint32_t dstTex = 2;
            GLU_CHECK_CALL(glBindTexture(GL_TEXTURE_2D, dstTex));
            GLU_CHECK_CALL(glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image));
            GLU_CHECK_CALL(glDeleteTextures(1, &dstTex));
        }
        else
        {
            DE_ASSERT(m_target == GL_RENDERBUFFER);

            log << TestLog::Message << "Creating GL_RENDERBUFFER from EGLimage" << TestLog::EndMessage;

            uint32_t dstRbo = 2;
            GLU_CHECK_CALL(glBindRenderbuffer(GL_RENDERBUFFER, dstRbo));
            GLU_CHECK_CALL(glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)image));
            GLU_CHECK_CALL(glDeleteRenderbuffers(1, &dstRbo));
        }

        // Destroy image
        CHECK_EXT_CALL_RET(egl.destroyImageKHR(context.getEglDisplay(), image), (EGLBoolean)EGL_TRUE, EGL_SUCCESS);

        // Destroy source texture object
        GLU_CHECK_CALL(glDeleteTextures(1, &srcTex));

        return STOP;
    }

private:
    GLenum m_target;
};

class ApiTests : public TestCaseGroup
{
public:
    ApiTests(EglTestContext &eglTestCtx, const string &name, const string &desc)
        : TestCaseGroup(eglTestCtx, name.c_str(), desc.c_str())
    {
    }

    void init(void)
    {
        addChild(new Image::InvalidCreateImage(m_eglTestCtx));

        addChild(
            new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_2D_KHR, GL_RED, GL_RED, GL_UNSIGNED_BYTE, false));
        addChild(
            new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_2D_KHR, GL_RG, GL_RG, GL_UNSIGNED_BYTE, false));

        addChild(new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_2D_KHR, GL_LUMINANCE, GL_LUMINANCE,
                                             GL_UNSIGNED_BYTE));
        addChild(new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_2D_KHR, GL_LUMINANCE_ALPHA,
                                             GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE));

        addChild(new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_2D_KHR, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE));
        addChild(new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_2D_KHR, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE));
        addChild(
            new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_2D_KHR, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, true));

        addChild(new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR, GL_RGB, GL_RGB,
                                             GL_UNSIGNED_BYTE));
        addChild(new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR, GL_RGBA, GL_RGBA,
                                             GL_UNSIGNED_BYTE));
        addChild(new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR, GL_RGBA, GL_RGBA,
                                             GL_UNSIGNED_BYTE, true));

        addChild(new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR, GL_RGBA, GL_RGBA,
                                             GL_UNSIGNED_BYTE));
        addChild(new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR, GL_RGBA, GL_RGBA,
                                             GL_UNSIGNED_BYTE));
        addChild(new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR, GL_RGBA, GL_RGBA,
                                             GL_UNSIGNED_BYTE));
        addChild(new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR, GL_RGBA, GL_RGBA,
                                             GL_UNSIGNED_BYTE));
        addChild(new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR, GL_RGBA, GL_RGBA,
                                             GL_UNSIGNED_BYTE));

        static const GLenum rboStorages[] = {GL_DEPTH_COMPONENT16, GL_RGBA4, GL_RGB5_A1, GL_RGB565, GL_STENCIL_INDEX8};
        for (int storageNdx = 0; storageNdx < DE_LENGTH_OF_ARRAY(rboStorages); storageNdx++)
            addChild(new Image::CreateImageGLES2(m_eglTestCtx, EGL_GL_RENDERBUFFER_KHR, rboStorages[storageNdx],
                                                 (GLenum)0, (GLenum)0));

        static const GLenum androidFormats[] = {GL_RGB565,
                                                GL_RGB8,
                                                GL_RGBA8,
                                                GL_RGBA4,
                                                GL_RGB5_A1,
                                                GL_DEPTH_COMPONENT16,
                                                GL_DEPTH_COMPONENT24,
                                                GL_DEPTH24_STENCIL8,
                                                GL_DEPTH_COMPONENT32F,
                                                GL_DEPTH32F_STENCIL8,
                                                GL_RGB10_A2,
                                                GL_RGBA16F,
                                                GL_STENCIL_INDEX8};

        for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(androidFormats); ++formatNdx)
            addChild(new Image::CreateImageGLES2(m_eglTestCtx, EGL_NATIVE_BUFFER_ANDROID, androidFormats[formatNdx],
                                                 (GLenum)0, (GLenum)0));

        addChild(new Image::ImageTargetGLES2(m_eglTestCtx, GL_TEXTURE_2D));
        addChild(new Image::ImageTargetGLES2(m_eglTestCtx, GL_RENDERBUFFER));
    }
};

} // namespace Image

ImageTests::ImageTests(EglTestContext &eglTestCtx) : TestCaseGroup(eglTestCtx, "image", "EGLImage Tests")
{
}

ImageTests::~ImageTests(void)
{
}

void ImageTests::init(void)
{
    addChild(new Image::ApiTests(m_eglTestCtx, "api", "EGLImage API tests"));
    addChild(Image::createSimpleCreationTests(m_eglTestCtx, "create", "EGLImage creation tests"));
    addChild(Image::createModifyTests(m_eglTestCtx, "modify", "EGLImage modifying tests"));
    addChild(Image::createMultiContextRenderTests(m_eglTestCtx, "render_multiple_contexts",
                                                  "EGLImage render tests on multiple contexts"));
}

} // namespace egl
} // namespace deqp
