/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.0 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 Boolean State Query tests.
 *//*--------------------------------------------------------------------*/

#include "es3fBooleanStateQueryTests.hpp"
#include "glsStateQueryUtil.hpp"
#include "es3fApiCase.hpp"
#include "gluRenderContext.hpp"
#include "tcuRenderTarget.hpp"
#include "glwEnums.hpp"

using namespace glw; // GLint and other GL types
using deqp::gls::StateQueryUtil::StateQueryMemoryWriteGuard;

namespace deqp
{
namespace gles3
{
namespace Functional
{
namespace BooleanStateQueryVerifiers
{

// StateVerifier

class StateVerifier : protected glu::CallLogWrapper
{
public:
    StateVerifier(const glw::Functions &gl, tcu::TestLog &log, const char *testNamePostfix);
    virtual ~StateVerifier(); // make GCC happy

    const char *getTestNamePostfix(void) const;

    virtual void verifyBoolean(tcu::TestContext &testCtx, GLenum name, bool reference) = DE_NULL;
    virtual void verifyBoolean4(tcu::TestContext &testCtx, GLenum name, bool reference0, bool reference1,
                                bool reference2, bool reference3)                      = DE_NULL;

private:
    const char *const m_testNamePostfix;
};

StateVerifier::StateVerifier(const glw::Functions &gl, tcu::TestLog &log, const char *testNamePostfix)
    : glu::CallLogWrapper(gl, log)
    , m_testNamePostfix(testNamePostfix)
{
    enableLogging(true);
}

StateVerifier::~StateVerifier()
{
}

const char *StateVerifier::getTestNamePostfix(void) const
{
    return m_testNamePostfix;
}

// IsEnabledVerifier

class IsEnabledVerifier : public StateVerifier
{
public:
    IsEnabledVerifier(const glw::Functions &gl, tcu::TestLog &log);
    void verifyBoolean(tcu::TestContext &testCtx, GLenum name, bool reference);
    void verifyBoolean4(tcu::TestContext &testCtx, GLenum name, bool reference0, bool reference1, bool reference2,
                        bool reference3);
};

IsEnabledVerifier::IsEnabledVerifier(const glw::Functions &gl, tcu::TestLog &log) : StateVerifier(gl, log, "_isenabled")
{
}

void IsEnabledVerifier::verifyBoolean(tcu::TestContext &testCtx, GLenum name, bool reference)
{
    using tcu::TestLog;

    const GLboolean state           = glIsEnabled(name);
    const GLboolean expectedGLState = reference ? (GLboolean)GL_TRUE : (GLboolean)GL_FALSE;

    if (state != expectedGLState)
    {
        testCtx.getLog() << TestLog::Message << "// ERROR: expected " << (reference ? "GL_TRUE" : "GL_FALSE")
                         << TestLog::EndMessage;
        if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
            testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid boolean value");
    }
}

void IsEnabledVerifier::verifyBoolean4(tcu::TestContext &testCtx, GLenum name, bool reference0, bool reference1,
                                       bool reference2, bool reference3)
{
    DE_UNREF(testCtx);
    DE_UNREF(name);
    DE_UNREF(reference0);
    DE_UNREF(reference1);
    DE_UNREF(reference2);
    DE_UNREF(reference3);
    DE_ASSERT(false && "not supported");
}

// GetBooleanVerifier

class GetBooleanVerifier : public StateVerifier
{
public:
    GetBooleanVerifier(const glw::Functions &gl, tcu::TestLog &log);
    void verifyBoolean(tcu::TestContext &testCtx, GLenum name, bool reference);
    void verifyBoolean4(tcu::TestContext &testCtx, GLenum name, bool reference0, bool reference1, bool reference2,
                        bool reference3);
};

GetBooleanVerifier::GetBooleanVerifier(const glw::Functions &gl, tcu::TestLog &log)
    : StateVerifier(gl, log, "_getboolean")
{
}

void GetBooleanVerifier::verifyBoolean(tcu::TestContext &testCtx, GLenum name, bool reference)
{
    using tcu::TestLog;

    StateQueryMemoryWriteGuard<GLboolean> state;
    glGetBooleanv(name, &state);

    if (!state.verifyValidity(testCtx))
        return;

    const GLboolean expectedGLState = reference ? GL_TRUE : GL_FALSE;

    if (state != expectedGLState)
    {
        testCtx.getLog() << TestLog::Message << "// ERROR: expected " << (reference ? "GL_TRUE" : "GL_FALSE")
                         << TestLog::EndMessage;
        if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
            testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid boolean value");
    }
}

void GetBooleanVerifier::verifyBoolean4(tcu::TestContext &testCtx, GLenum name, bool reference0, bool reference1,
                                        bool reference2, bool reference3)
{
    using tcu::TestLog;

    StateQueryMemoryWriteGuard<GLboolean[4]> boolVector4;
    glGetBooleanv(name, boolVector4);

    if (!boolVector4.verifyValidity(testCtx))
        return;

    const GLboolean referenceAsGLBoolean[] = {
        reference0 ? GLboolean(GL_TRUE) : GLboolean(GL_FALSE),
        reference1 ? GLboolean(GL_TRUE) : GLboolean(GL_FALSE),
        reference2 ? GLboolean(GL_TRUE) : GLboolean(GL_FALSE),
        reference3 ? GLboolean(GL_TRUE) : GLboolean(GL_FALSE),
    };

    if (boolVector4[0] != referenceAsGLBoolean[0] || boolVector4[1] != referenceAsGLBoolean[1] ||
        boolVector4[2] != referenceAsGLBoolean[2] || boolVector4[3] != referenceAsGLBoolean[3])
    {
        testCtx.getLog() << TestLog::Message << "// ERROR: expected "
                         << (referenceAsGLBoolean[0] ? "GL_TRUE" : "GL_FALSE") << " "
                         << (referenceAsGLBoolean[1] ? "GL_TRUE" : "GL_FALSE") << " "
                         << (referenceAsGLBoolean[2] ? "GL_TRUE" : "GL_FALSE") << " "
                         << (referenceAsGLBoolean[3] ? "GL_TRUE" : "GL_FALSE") << TestLog::EndMessage;

        if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
            testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid boolean value");
    }
}

//GetIntegerVerifier

class GetIntegerVerifier : public StateVerifier
{
public:
    GetIntegerVerifier(const glw::Functions &gl, tcu::TestLog &log);
    void verifyBoolean(tcu::TestContext &testCtx, GLenum name, bool reference);
    void verifyBoolean4(tcu::TestContext &testCtx, GLenum name, bool reference0, bool reference1, bool reference2,
                        bool reference3);
};

GetIntegerVerifier::GetIntegerVerifier(const glw::Functions &gl, tcu::TestLog &log)
    : StateVerifier(gl, log, "_getinteger")
{
}

void GetIntegerVerifier::verifyBoolean(tcu::TestContext &testCtx, GLenum name, bool reference)
{
    using tcu::TestLog;

    StateQueryMemoryWriteGuard<GLint> state;
    glGetIntegerv(name, &state);

    if (!state.verifyValidity(testCtx))
        return;

    const GLint expectedGLState = reference ? 1 : 0;

    if (state != expectedGLState)
    {
        testCtx.getLog() << TestLog::Message << "// ERROR: expected " << expectedGLState << TestLog::EndMessage;
        if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
            testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer value");
    }
}

void GetIntegerVerifier::verifyBoolean4(tcu::TestContext &testCtx, GLenum name, bool reference0, bool reference1,
                                        bool reference2, bool reference3)
{
    using tcu::TestLog;

    StateQueryMemoryWriteGuard<GLint[4]> boolVector4;
    glGetIntegerv(name, boolVector4);

    if (!boolVector4.verifyValidity(testCtx))
        return;

    const GLint referenceAsGLint[] = {
        reference0 ? 1 : 0,
        reference1 ? 1 : 0,
        reference2 ? 1 : 0,
        reference3 ? 1 : 0,
    };

    if (boolVector4[0] != referenceAsGLint[0] || boolVector4[1] != referenceAsGLint[1] ||
        boolVector4[2] != referenceAsGLint[2] || boolVector4[3] != referenceAsGLint[3])
    {
        testCtx.getLog() << TestLog::Message << "// ERROR: expected " << referenceAsGLint[0] << " "
                         << referenceAsGLint[1] << " " << referenceAsGLint[2] << " " << referenceAsGLint[3] << " "
                         << TestLog::EndMessage;

        if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
            testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer value");
    }
}

//GetInteger64Verifier

class GetInteger64Verifier : public StateVerifier
{
public:
    GetInteger64Verifier(const glw::Functions &gl, tcu::TestLog &log);
    void verifyBoolean(tcu::TestContext &testCtx, GLenum name, bool reference);
    void verifyBoolean4(tcu::TestContext &testCtx, GLenum name, bool reference0, bool reference1, bool reference2,
                        bool reference3);
};

GetInteger64Verifier::GetInteger64Verifier(const glw::Functions &gl, tcu::TestLog &log)
    : StateVerifier(gl, log, "_getinteger64")
{
}

void GetInteger64Verifier::verifyBoolean(tcu::TestContext &testCtx, GLenum name, bool reference)
{
    using tcu::TestLog;

    StateQueryMemoryWriteGuard<GLint64> state;
    glGetInteger64v(name, &state);

    if (!state.verifyValidity(testCtx))
        return;

    const GLint64 expectedGLState = reference ? 1 : 0;

    if (state != expectedGLState)
    {
        testCtx.getLog() << TestLog::Message << "// ERROR: expected " << expectedGLState << TestLog::EndMessage;
        if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
            testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer value");
    }
}

void GetInteger64Verifier::verifyBoolean4(tcu::TestContext &testCtx, GLenum name, bool reference0, bool reference1,
                                          bool reference2, bool reference3)
{
    using tcu::TestLog;

    StateQueryMemoryWriteGuard<GLint64[4]> boolVector4;
    glGetInteger64v(name, boolVector4);

    if (!boolVector4.verifyValidity(testCtx))
        return;

    const GLint64 referenceAsGLint64[] = {
        reference0 ? 1 : 0,
        reference1 ? 1 : 0,
        reference2 ? 1 : 0,
        reference3 ? 1 : 0,
    };

    if (boolVector4[0] != referenceAsGLint64[0] || boolVector4[1] != referenceAsGLint64[1] ||
        boolVector4[2] != referenceAsGLint64[2] || boolVector4[3] != referenceAsGLint64[3])
    {
        testCtx.getLog() << TestLog::Message << "// ERROR: expected " << referenceAsGLint64[0] << " "
                         << referenceAsGLint64[1] << " " << referenceAsGLint64[2] << " " << referenceAsGLint64[3] << " "
                         << TestLog::EndMessage;

        if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
            testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid boolean value");
    }
}

//GetFloatVerifier

class GetFloatVerifier : public StateVerifier
{
public:
    GetFloatVerifier(const glw::Functions &gl, tcu::TestLog &log);
    void verifyBoolean(tcu::TestContext &testCtx, GLenum name, bool reference);
    void verifyBoolean4(tcu::TestContext &testCtx, GLenum name, bool reference0, bool reference1, bool reference2,
                        bool reference3);
};

GetFloatVerifier::GetFloatVerifier(const glw::Functions &gl, tcu::TestLog &log) : StateVerifier(gl, log, "_getfloat")
{
}

void GetFloatVerifier::verifyBoolean(tcu::TestContext &testCtx, GLenum name, bool reference)
{
    using tcu::TestLog;

    StateQueryMemoryWriteGuard<GLfloat> state;
    glGetFloatv(name, &state);

    if (!state.verifyValidity(testCtx))
        return;

    const GLfloat expectedGLState = reference ? 1.0f : 0.0f;

    if (state != expectedGLState)
    {
        testCtx.getLog() << TestLog::Message << "// ERROR: expected " << expectedGLState << "; got " << state
                         << TestLog::EndMessage;
        if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
            testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid float value");
    }
}

void GetFloatVerifier::verifyBoolean4(tcu::TestContext &testCtx, GLenum name, bool reference0, bool reference1,
                                      bool reference2, bool reference3)
{
    using tcu::TestLog;

    StateQueryMemoryWriteGuard<GLfloat[4]> boolVector4;
    glGetFloatv(name, boolVector4);

    if (!boolVector4.verifyValidity(testCtx))
        return;

    const GLfloat referenceAsGLfloat[] = {
        reference0 ? 1.0f : 0.0f,
        reference1 ? 1.0f : 0.0f,
        reference2 ? 1.0f : 0.0f,
        reference3 ? 1.0f : 0.0f,
    };

    if (boolVector4[0] != referenceAsGLfloat[0] || boolVector4[1] != referenceAsGLfloat[1] ||
        boolVector4[2] != referenceAsGLfloat[2] || boolVector4[3] != referenceAsGLfloat[3])
    {
        testCtx.getLog() << TestLog::Message << "// ERROR: expected " << referenceAsGLfloat[0] << " "
                         << referenceAsGLfloat[1] << " " << referenceAsGLfloat[2] << " " << referenceAsGLfloat[3] << " "
                         << TestLog::EndMessage;

        if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
            testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid float value");
    }
}

} // namespace BooleanStateQueryVerifiers

namespace
{

using namespace BooleanStateQueryVerifiers;

static const char *transformFeedbackTestVertSource = "#version 300 es\n"
                                                     "void main (void)\n"
                                                     "{\n"
                                                     "    gl_Position = vec4(0.0);\n"
                                                     "}\n\0";
static const char *transformFeedbackTestFragSource = "#version 300 es\n"
                                                     "layout(location = 0) out mediump vec4 fragColor;"
                                                     "void main (void)\n"
                                                     "{\n"
                                                     "    fragColor = vec4(0.0);\n"
                                                     "}\n\0";

class IsEnabledStateTestCase : public ApiCase
{
public:
    IsEnabledStateTestCase(Context &context, StateVerifier *verifier, const char *name, const char *description,
                           GLenum targetName, bool initial)
        : ApiCase(context, name, description)
        , m_targetName(targetName)
        , m_initial(initial)
        , m_verifier(verifier)
    {
    }

    void test(void)
    {
        // check inital value
        m_verifier->verifyBoolean(m_testCtx, m_targetName, m_initial);
        expectError(GL_NO_ERROR);

        // check toggle

        glEnable(m_targetName);
        expectError(GL_NO_ERROR);

        m_verifier->verifyBoolean(m_testCtx, m_targetName, true);
        expectError(GL_NO_ERROR);

        glDisable(m_targetName);
        expectError(GL_NO_ERROR);

        m_verifier->verifyBoolean(m_testCtx, m_targetName, false);
        expectError(GL_NO_ERROR);
    }

private:
    GLenum m_targetName;
    bool m_initial;
    StateVerifier *m_verifier;
};

class DepthWriteMaskTestCase : public ApiCase
{
public:
    DepthWriteMaskTestCase(Context &context, StateVerifier *verifier, const char *name, const char *description)
        : ApiCase(context, name, description)
        , m_verifier(verifier)
    {
    }

    void test(void)
    {
        m_verifier->verifyBoolean(m_testCtx, GL_DEPTH_WRITEMASK, true);
        expectError(GL_NO_ERROR);

        glDepthMask(GL_FALSE);
        m_verifier->verifyBoolean(m_testCtx, GL_DEPTH_WRITEMASK, false);
        expectError(GL_NO_ERROR);

        glDepthMask(GL_TRUE);
        m_verifier->verifyBoolean(m_testCtx, GL_DEPTH_WRITEMASK, true);
        expectError(GL_NO_ERROR);
    }

private:
    StateVerifier *m_verifier;
};

class SampleCoverageInvertTestCase : public ApiCase
{
public:
    SampleCoverageInvertTestCase(Context &context, StateVerifier *verifier, const char *name, const char *description)
        : ApiCase(context, name, description)
        , m_verifier(verifier)
    {
    }

    void test(void)
    {
        m_verifier->verifyBoolean(m_testCtx, GL_SAMPLE_COVERAGE_INVERT, false);
        expectError(GL_NO_ERROR);

        glSampleCoverage(1.0f, GL_TRUE);
        m_verifier->verifyBoolean(m_testCtx, GL_SAMPLE_COVERAGE_INVERT, true);
        expectError(GL_NO_ERROR);

        glSampleCoverage(1.0f, GL_FALSE);
        m_verifier->verifyBoolean(m_testCtx, GL_SAMPLE_COVERAGE_INVERT, false);
        expectError(GL_NO_ERROR);
    }

private:
    StateVerifier *m_verifier;
};

class InitialBooleanTestCase : public ApiCase
{
public:
    InitialBooleanTestCase(Context &context, StateVerifier *verifier, const char *name, const char *description,
                           GLenum target, bool reference)
        : ApiCase(context, name, description)
        , m_target(target)
        , m_reference(reference)
        , m_verifier(verifier)
    {
    }

    void test(void)
    {
        m_verifier->verifyBoolean(m_testCtx, m_target, m_reference);
        expectError(GL_NO_ERROR);
    }

private:
    GLenum m_target;
    bool m_reference;
    StateVerifier *m_verifier;
};

class ColorMaskTestCase : public ApiCase
{
public:
    ColorMaskTestCase(Context &context, StateVerifier *verifier, const char *name, const char *description)
        : ApiCase(context, name, description)
        , m_verifier(verifier)
    {
    }
    void test(void)
    {
        m_verifier->verifyBoolean4(m_testCtx, GL_COLOR_WRITEMASK, true, true, true, true);
        expectError(GL_NO_ERROR);

        const struct ColorMask
        {
            GLboolean r, g, b, a;
        } testMasks[] = {
            {GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE},    {GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE},
            {GL_TRUE, GL_TRUE, GL_FALSE, GL_TRUE},   {GL_TRUE, GL_TRUE, GL_FALSE, GL_FALSE},
            {GL_TRUE, GL_FALSE, GL_TRUE, GL_TRUE},   {GL_TRUE, GL_FALSE, GL_TRUE, GL_FALSE},
            {GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE},  {GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE},
            {GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE},   {GL_FALSE, GL_TRUE, GL_TRUE, GL_FALSE},
            {GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE},  {GL_FALSE, GL_TRUE, GL_FALSE, GL_FALSE},
            {GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE},  {GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE},
            {GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE}, {GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE},
        };

        for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(testMasks); ndx++)
        {
            glColorMask(testMasks[ndx].r, testMasks[ndx].g, testMasks[ndx].b, testMasks[ndx].a);
            m_verifier->verifyBoolean4(m_testCtx, GL_COLOR_WRITEMASK, testMasks[ndx].r == GL_TRUE,
                                       testMasks[ndx].g == GL_TRUE, testMasks[ndx].b == GL_TRUE,
                                       testMasks[ndx].a == GL_TRUE);
            expectError(GL_NO_ERROR);
        }
    }

private:
    StateVerifier *m_verifier;
};

class TransformFeedbackTestCase : public ApiCase
{
public:
    TransformFeedbackTestCase(Context &context, StateVerifier *verifier, const char *name, const char *description)
        : ApiCase(context, name, description)
        , m_verifier(verifier)
        , m_transformfeedback(0)
    {
    }

    void test(void)
    {
        glGenTransformFeedbacks(1, &m_transformfeedback);
        expectError(GL_NO_ERROR);

        GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(shaderVert, 1, &transformFeedbackTestVertSource, DE_NULL);
        glCompileShader(shaderVert);
        expectError(GL_NO_ERROR);
        GLint compileStatus;
        glGetShaderiv(shaderVert, GL_COMPILE_STATUS, &compileStatus);
        checkBooleans(compileStatus, GL_TRUE);

        GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(shaderFrag, 1, &transformFeedbackTestFragSource, DE_NULL);
        glCompileShader(shaderFrag);
        expectError(GL_NO_ERROR);
        glGetShaderiv(shaderFrag, GL_COMPILE_STATUS, &compileStatus);
        checkBooleans(compileStatus, GL_TRUE);

        GLuint shaderProg = glCreateProgram();
        glAttachShader(shaderProg, shaderVert);
        glAttachShader(shaderProg, shaderFrag);
        const char *transform_feedback_outputs = "gl_Position";
        glTransformFeedbackVaryings(shaderProg, 1, &transform_feedback_outputs, GL_INTERLEAVED_ATTRIBS);
        glLinkProgram(shaderProg);
        expectError(GL_NO_ERROR);
        GLint linkStatus;
        glGetProgramiv(shaderProg, GL_LINK_STATUS, &linkStatus);
        checkBooleans(linkStatus, GL_TRUE);

        glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_transformfeedback);
        expectError(GL_NO_ERROR);

        GLuint feedbackBufferId;
        glGenBuffers(1, &feedbackBufferId);
        glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, feedbackBufferId);
        glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 16, NULL, GL_DYNAMIC_READ);
        glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, feedbackBufferId);
        expectError(GL_NO_ERROR);

        glUseProgram(shaderProg);

        testTransformFeedback();

        glUseProgram(0);
        glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
        glDeleteTransformFeedbacks(1, &m_transformfeedback);
        glDeleteBuffers(1, &feedbackBufferId);
        glDeleteShader(shaderVert);
        glDeleteShader(shaderFrag);
        glDeleteProgram(shaderProg);
        expectError(GL_NO_ERROR);
    }

    virtual void testTransformFeedback(void) = DE_NULL;

protected:
    StateVerifier *m_verifier;
    GLuint m_transformfeedback;
};

class TransformFeedbackBasicTestCase : public TransformFeedbackTestCase
{
public:
    TransformFeedbackBasicTestCase(Context &context, StateVerifier *verifier, const char *name)
        : TransformFeedbackTestCase(context, verifier, name,
                                    "Test TRANSFORM_FEEDBACK_ACTIVE and TRANSFORM_FEEDBACK_PAUSED")
    {
    }

    void testTransformFeedback(void)
    {
        glBeginTransformFeedback(GL_POINTS);
        expectError(GL_NO_ERROR);

        m_verifier->verifyBoolean(m_testCtx, GL_TRANSFORM_FEEDBACK_ACTIVE, true);
        m_verifier->verifyBoolean(m_testCtx, GL_TRANSFORM_FEEDBACK_PAUSED, false);
        expectError(GL_NO_ERROR);

        glPauseTransformFeedback();
        expectError(GL_NO_ERROR);

        m_verifier->verifyBoolean(m_testCtx, GL_TRANSFORM_FEEDBACK_ACTIVE, true);
        m_verifier->verifyBoolean(m_testCtx, GL_TRANSFORM_FEEDBACK_PAUSED, true);
        expectError(GL_NO_ERROR);

        glResumeTransformFeedback();
        expectError(GL_NO_ERROR);

        m_verifier->verifyBoolean(m_testCtx, GL_TRANSFORM_FEEDBACK_ACTIVE, true);
        m_verifier->verifyBoolean(m_testCtx, GL_TRANSFORM_FEEDBACK_PAUSED, false);
        expectError(GL_NO_ERROR);

        glEndTransformFeedback();
        expectError(GL_NO_ERROR);

        m_verifier->verifyBoolean(m_testCtx, GL_TRANSFORM_FEEDBACK_ACTIVE, false);
        m_verifier->verifyBoolean(m_testCtx, GL_TRANSFORM_FEEDBACK_PAUSED, false);
        expectError(GL_NO_ERROR);
    }
};

class TransformFeedbackImplicitResumeTestCase : public TransformFeedbackTestCase
{
public:
    TransformFeedbackImplicitResumeTestCase(Context &context, StateVerifier *verifier, const char *name)
        : TransformFeedbackTestCase(context, verifier, name,
                                    "EndTransformFeedback performs an implicit ResumeTransformFeedback.")
    {
    }

    void testTransformFeedback(void)
    {
        glBeginTransformFeedback(GL_POINTS);
        expectError(GL_NO_ERROR);

        m_verifier->verifyBoolean(m_testCtx, GL_TRANSFORM_FEEDBACK_ACTIVE, true);
        m_verifier->verifyBoolean(m_testCtx, GL_TRANSFORM_FEEDBACK_PAUSED, false);
        expectError(GL_NO_ERROR);

        glPauseTransformFeedback();
        expectError(GL_NO_ERROR);

        m_verifier->verifyBoolean(m_testCtx, GL_TRANSFORM_FEEDBACK_ACTIVE, true);
        m_verifier->verifyBoolean(m_testCtx, GL_TRANSFORM_FEEDBACK_PAUSED, true);
        expectError(GL_NO_ERROR);

        glEndTransformFeedback();
        expectError(GL_NO_ERROR);

        m_verifier->verifyBoolean(m_testCtx, GL_TRANSFORM_FEEDBACK_ACTIVE, false);
        m_verifier->verifyBoolean(m_testCtx, GL_TRANSFORM_FEEDBACK_PAUSED, false);
        expectError(GL_NO_ERROR);
    }
};

#define FOR_EACH_VERIFIER(VERIFIERS, CODE_BLOCK)                                                 \
    do                                                                                           \
    {                                                                                            \
        for (int _verifierNdx = 0; _verifierNdx < DE_LENGTH_OF_ARRAY(VERIFIERS); _verifierNdx++) \
        {                                                                                        \
            StateVerifier *verifier = (VERIFIERS)[_verifierNdx];                                 \
            CODE_BLOCK;                                                                          \
        }                                                                                        \
    } while (0)

} // namespace

BooleanStateQueryTests::BooleanStateQueryTests(Context &context)
    : TestCaseGroup(context, "boolean", "Boolean State Query tests")
    , m_verifierIsEnabled(DE_NULL)
    , m_verifierBoolean(DE_NULL)
    , m_verifierInteger(DE_NULL)
    , m_verifierInteger64(DE_NULL)
    , m_verifierFloat(DE_NULL)
{
}

BooleanStateQueryTests::~BooleanStateQueryTests(void)
{
    deinit();
}

void BooleanStateQueryTests::init(void)
{
    DE_ASSERT(m_verifierIsEnabled == DE_NULL);
    DE_ASSERT(m_verifierBoolean == DE_NULL);
    DE_ASSERT(m_verifierInteger == DE_NULL);
    DE_ASSERT(m_verifierInteger64 == DE_NULL);
    DE_ASSERT(m_verifierFloat == DE_NULL);

    m_verifierIsEnabled =
        new IsEnabledVerifier(m_context.getRenderContext().getFunctions(), m_context.getTestContext().getLog());
    m_verifierBoolean =
        new GetBooleanVerifier(m_context.getRenderContext().getFunctions(), m_context.getTestContext().getLog());
    m_verifierInteger =
        new GetIntegerVerifier(m_context.getRenderContext().getFunctions(), m_context.getTestContext().getLog());
    m_verifierInteger64 =
        new GetInteger64Verifier(m_context.getRenderContext().getFunctions(), m_context.getTestContext().getLog());
    m_verifierFloat =
        new GetFloatVerifier(m_context.getRenderContext().getFunctions(), m_context.getTestContext().getLog());

    StateVerifier *isEnabledVerifiers[] = {m_verifierIsEnabled, m_verifierBoolean, m_verifierInteger,
                                           m_verifierInteger64, m_verifierFloat};
    StateVerifier *normalVerifiers[]    = {m_verifierBoolean, m_verifierInteger, m_verifierInteger64, m_verifierFloat};

    struct StateBoolean
    {
        const char *name;
        const char *description;
        GLenum targetName;
        bool value;
    };
    const StateBoolean isEnableds[] = {
        {"primitive_restart_fixed_index", "PRIMITIVE_RESTART_FIXED_INDEX", GL_PRIMITIVE_RESTART_FIXED_INDEX, false},
        {"rasterizer_discard", "RASTERIZER_DISCARD", GL_RASTERIZER_DISCARD, false},
        {"cull_face", "CULL_FACE", GL_CULL_FACE, false},
        {"polygon_offset_fill", "POLYGON_OFFSET_FILL", GL_POLYGON_OFFSET_FILL, false},
        {"sample_alpha_to_coverage", "SAMPLE_ALPHA_TO_COVERAGE", GL_SAMPLE_ALPHA_TO_COVERAGE, false},
        {"sample_coverage", "SAMPLE_COVERAGE", GL_SAMPLE_COVERAGE, false},
        {"scissor_test", "SCISSOR_TEST", GL_SCISSOR_TEST, false},
        {"stencil_test", "STENCIL_TEST", GL_STENCIL_TEST, false},
        {"depth_test", "DEPTH_TEST", GL_DEPTH_TEST, false},
        {"blend", "BLEND", GL_BLEND, false},
        {"dither", "DITHER", GL_DITHER, true},
    };
    for (int testNdx = 0; testNdx < DE_LENGTH_OF_ARRAY(isEnableds); testNdx++)
    {
        FOR_EACH_VERIFIER(
            isEnabledVerifiers,
            addChild(new IsEnabledStateTestCase(
                m_context, verifier, (std::string(isEnableds[testNdx].name) + verifier->getTestNamePostfix()).c_str(),
                isEnableds[testNdx].description, isEnableds[testNdx].targetName, isEnableds[testNdx].value)));
    }

    FOR_EACH_VERIFIER(normalVerifiers, addChild(new ColorMaskTestCase(
                                           m_context, verifier,
                                           (std::string("color_writemask") + verifier->getTestNamePostfix()).c_str(),
                                           "COLOR_WRITEMASK")));
    FOR_EACH_VERIFIER(normalVerifiers, addChild(new DepthWriteMaskTestCase(
                                           m_context, verifier,
                                           (std::string("depth_writemask") + verifier->getTestNamePostfix()).c_str(),
                                           "DEPTH_WRITEMASK")));
    FOR_EACH_VERIFIER(
        normalVerifiers,
        addChild(new SampleCoverageInvertTestCase(
            m_context, verifier, (std::string("sample_coverage_invert") + verifier->getTestNamePostfix()).c_str(),
            "SAMPLE_COVERAGE_INVERT")));
    FOR_EACH_VERIFIER(normalVerifiers, addChild(new InitialBooleanTestCase(
                                           m_context, verifier,
                                           (std::string("shader_compiler") + verifier->getTestNamePostfix()).c_str(),
                                           "SHADER_COMPILER", GL_SHADER_COMPILER, true)));
    FOR_EACH_VERIFIER(normalVerifiers,
                      addChild(new InitialBooleanTestCase(
                          m_context, verifier,
                          (std::string("transform_feedback_active_initial") + verifier->getTestNamePostfix()).c_str(),
                          "initial TRANSFORM_FEEDBACK_ACTIVE", GL_TRANSFORM_FEEDBACK_ACTIVE, false)));
    FOR_EACH_VERIFIER(normalVerifiers,
                      addChild(new InitialBooleanTestCase(
                          m_context, verifier,
                          (std::string("transform_feedback_paused_initial") + verifier->getTestNamePostfix()).c_str(),
                          "initial TRANSFORM_FEEDBACK_PAUSED", GL_TRANSFORM_FEEDBACK_PAUSED, false)));
    FOR_EACH_VERIFIER(
        normalVerifiers,
        addChild(new TransformFeedbackBasicTestCase(
            m_context, verifier, (std::string("transform_feedback") + verifier->getTestNamePostfix()).c_str())));
    FOR_EACH_VERIFIER(
        normalVerifiers,
        addChild(new TransformFeedbackImplicitResumeTestCase(
            m_context, verifier,
            (std::string("transform_feedback_implicit_resume") + verifier->getTestNamePostfix()).c_str())));
}

void BooleanStateQueryTests::deinit(void)
{
    if (m_verifierIsEnabled)
    {
        delete m_verifierIsEnabled;
        m_verifierIsEnabled = DE_NULL;
    }
    if (m_verifierBoolean)
    {
        delete m_verifierBoolean;
        m_verifierBoolean = DE_NULL;
    }
    if (m_verifierInteger)
    {
        delete m_verifierInteger;
        m_verifierInteger = DE_NULL;
    }
    if (m_verifierInteger64)
    {
        delete m_verifierInteger64;
        m_verifierInteger64 = DE_NULL;
    }
    if (m_verifierFloat)
    {
        delete m_verifierFloat;
        m_verifierFloat = DE_NULL;
    }

    this->TestCaseGroup::deinit();
}

} // namespace Functional
} // namespace gles3
} // namespace deqp
