/*-------------------------------------------------------------------------
 * 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 Negative Vertex Array API tests.
 *//*--------------------------------------------------------------------*/

#include "es3fNegativeVertexArrayApiTests.hpp"
#include "es3fApiCase.hpp"
#include "gluShaderProgram.hpp"
#include "gluContextInfo.hpp"
#include "deString.h"

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

using namespace glw; // GL types

namespace deqp
{
namespace gles3
{
namespace Functional
{

static const char *vertexShaderSource = "#version 300 es\n"
                                        "void main (void)\n"
                                        "{\n"
                                        "    gl_Position = vec4(0.0);\n"
                                        "}\n\0";

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

using tcu::TestLog;

// Helper class that enables tests to be executed on GL4.5 context
// and removes code redundancy in each test that requires it.
class VAOHelper : protected glu::CallLogWrapper
{
public:
    VAOHelper(Context &ctx)
        : CallLogWrapper(ctx.getRenderContext().getFunctions(), ctx.getTestContext().getLog())
        , m_vao(0)
    {
        // tests need vao only for GL4.5 context
        if (glu::isContextTypeES(ctx.getRenderContext().getType()))
            return;

        glGenVertexArrays(1, &m_vao);
        glBindVertexArray(m_vao);
        glVertexAttribPointer(0, 1, GL_BYTE, GL_TRUE, 0, NULL);
        glEnableVertexAttribArray(0);
    }

    ~VAOHelper()
    {
        if (m_vao)
            glDeleteVertexArrays(1, &m_vao);
    }

private:
    GLuint m_vao;
};

NegativeVertexArrayApiTests::NegativeVertexArrayApiTests(Context &context)
    : TestCaseGroup(context, "vertex_array", "Negative Vertex Array API Cases")
{
}

NegativeVertexArrayApiTests::~NegativeVertexArrayApiTests(void)
{
}

void NegativeVertexArrayApiTests::init(void)
{
    ES3F_ADD_API_CASE(vertex_attribf, "Invalid glVertexAttrib{1234}f() usage", {
        m_log << tcu::TestLog::Section(
            "", "GL_INVALID_VALUE is generated if index is greater than or equal to GL_MAX_VERTEX_ATTRIBS.");
        int maxVertexAttribs = m_context.getContextInfo().getInt(GL_MAX_VERTEX_ATTRIBS);
        glVertexAttrib1f(maxVertexAttribs, 0.0f);
        expectError(GL_INVALID_VALUE);
        glVertexAttrib2f(maxVertexAttribs, 0.0f, 0.0f);
        expectError(GL_INVALID_VALUE);
        glVertexAttrib3f(maxVertexAttribs, 0.0f, 0.0f, 0.0f);
        expectError(GL_INVALID_VALUE);
        glVertexAttrib4f(maxVertexAttribs, 0.0f, 0.0f, 0.0f, 0.0f);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(vertex_attribfv, "Invalid glVertexAttrib{1234}fv() usage", {
        m_log << tcu::TestLog::Section(
            "", "GL_INVALID_VALUE is generated if index is greater than or equal to GL_MAX_VERTEX_ATTRIBS.");
        int maxVertexAttribs = m_context.getContextInfo().getInt(GL_MAX_VERTEX_ATTRIBS);
        float v[4]           = {0.0f};
        glVertexAttrib1fv(maxVertexAttribs, &v[0]);
        expectError(GL_INVALID_VALUE);
        glVertexAttrib2fv(maxVertexAttribs, &v[0]);
        expectError(GL_INVALID_VALUE);
        glVertexAttrib3fv(maxVertexAttribs, &v[0]);
        expectError(GL_INVALID_VALUE);
        glVertexAttrib4fv(maxVertexAttribs, &v[0]);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(vertex_attribi4, "Invalid glVertexAttribI4{i|ui}f() usage", {
        int maxVertexAttribs = m_context.getContextInfo().getInt(GL_MAX_VERTEX_ATTRIBS);
        GLint valInt         = 0;
        GLuint valUint       = 0;

        m_log << tcu::TestLog::Section(
            "", "GL_INVALID_VALUE is generated if index is greater than or equal to GL_MAX_VERTEX_ATTRIBS.");
        glVertexAttribI4i(maxVertexAttribs, valInt, valInt, valInt, valInt);
        expectError(GL_INVALID_VALUE);
        glVertexAttribI4ui(maxVertexAttribs, valUint, valUint, valUint, valUint);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(vertex_attribi4v, "Invalid glVertexAttribI4{i|ui}fv() usage", {
        int maxVertexAttribs = m_context.getContextInfo().getInt(GL_MAX_VERTEX_ATTRIBS);
        GLint valInt[4]      = {0};
        GLuint valUint[4]    = {0};

        m_log << tcu::TestLog::Section(
            "", "GL_INVALID_VALUE is generated if index is greater than or equal to GL_MAX_VERTEX_ATTRIBS.");
        glVertexAttribI4iv(maxVertexAttribs, &valInt[0]);
        expectError(GL_INVALID_VALUE);
        glVertexAttribI4uiv(maxVertexAttribs, &valUint[0]);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(vertex_attrib_pointer, "Invalid glVertexAttribPointer() usage", {
        GLuint vao = 0;
        glGenVertexArrays(1, &vao);
        if (glu::isContextTypeES(m_context.getRenderContext().getType()))
            glBindVertexArray(0);
        else
            glBindVertexArray(vao);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if type is not an accepted value.");
        glVertexAttribPointer(0, 1, 0, GL_TRUE, 0, 0);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section(
            "", "GL_INVALID_VALUE is generated if index is greater than or equal to GL_MAX_VERTEX_ATTRIBS.");
        int maxVertexAttribs = m_context.getContextInfo().getInt(GL_MAX_VERTEX_ATTRIBS);
        glVertexAttribPointer(maxVertexAttribs, 1, GL_BYTE, GL_TRUE, 0, 0);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if size is not 1, 2, 3, or 4.");
        glVertexAttribPointer(0, 0, GL_BYTE, GL_TRUE, 0, 0);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if stride is negative.");
        glVertexAttribPointer(0, 1, GL_BYTE, GL_TRUE, -1, 0);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_OPERATION is generated if type is GL_INT_2_10_10_10_REV or "
                                           "GL_UNSIGNED_INT_2_10_10_10_REV and size is not 4.");
        glVertexAttribPointer(0, 2, GL_INT_2_10_10_10_REV, GL_TRUE, 0, 0);
        expectError(GL_INVALID_OPERATION);
        glVertexAttribPointer(0, 2, GL_UNSIGNED_INT_2_10_10_10_REV, GL_TRUE, 0, 0);
        expectError(GL_INVALID_OPERATION);
        glVertexAttribPointer(0, 4, GL_INT_2_10_10_10_REV, GL_TRUE, 0, 0);
        expectError(GL_NO_ERROR);
        glVertexAttribPointer(0, 4, GL_UNSIGNED_INT_2_10_10_10_REV, GL_TRUE, 0, 0);
        expectError(GL_NO_ERROR);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section(
            "", "GL_INVALID_OPERATION is generated a non-zero vertex array object is bound, zero is bound to the "
                "GL_ARRAY_BUFFER buffer object binding point and the pointer argument is not NULL.");
        GLbyte offset = 1;
        glBindVertexArray(vao);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        expectError(GL_NO_ERROR);

        glVertexAttribPointer(0, 1, GL_BYTE, GL_TRUE, 0, &offset);
        expectError(GL_INVALID_OPERATION);

        glBindVertexArray(0);
        glDeleteVertexArrays(1, &vao);
        expectError(GL_NO_ERROR);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(vertex_attrib_i_pointer, "Invalid glVertexAttribPointer() usage", {
        GLuint vao = 0;
        glGenVertexArrays(1, &vao);
        if (glu::isContextTypeES(m_context.getRenderContext().getType()))
            glBindVertexArray(0);
        else
            glBindVertexArray(vao);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if type is not an accepted value.");
        glVertexAttribIPointer(0, 1, 0, 0, 0);
        expectError(GL_INVALID_ENUM);
        glVertexAttribIPointer(0, 4, GL_INT_2_10_10_10_REV, 0, 0);
        expectError(GL_INVALID_ENUM);
        glVertexAttribIPointer(0, 4, GL_UNSIGNED_INT_2_10_10_10_REV, 0, 0);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section(
            "", "GL_INVALID_VALUE is generated if index is greater than or equal to GL_MAX_VERTEX_ATTRIBS.");
        int maxVertexAttribs = m_context.getContextInfo().getInt(GL_MAX_VERTEX_ATTRIBS);
        glVertexAttribIPointer(maxVertexAttribs, 1, GL_BYTE, 0, 0);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if size is not 1, 2, 3, or 4.");
        glVertexAttribIPointer(0, 0, GL_BYTE, 0, 0);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if stride is negative.");
        glVertexAttribIPointer(0, 1, GL_BYTE, -1, 0);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section(
            "", "GL_INVALID_OPERATION is generated a non-zero vertex array object is bound, zero is bound to the "
                "GL_ARRAY_BUFFER buffer object binding point and the pointer argument is not NULL.");
        GLbyte offset = 1;
        glBindVertexArray(vao);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        expectError(GL_NO_ERROR);

        glVertexAttribIPointer(0, 1, GL_BYTE, 0, &offset);
        expectError(GL_INVALID_OPERATION);

        glBindVertexArray(0);
        glDeleteVertexArrays(1, &vao);
        expectError(GL_NO_ERROR);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(enable_vertex_attrib_array, "Invalid glEnableVertexAttribArray() usage", {
        m_log << tcu::TestLog::Section(
            "", "GL_INVALID_VALUE is generated if index is greater than or equal to GL_MAX_VERTEX_ATTRIBS.");
        int maxVertexAttribs = m_context.getContextInfo().getInt(GL_MAX_VERTEX_ATTRIBS);
        glEnableVertexAttribArray(maxVertexAttribs);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(disable_vertex_attrib_array, "Invalid glDisableVertexAttribArray() usage", {
        m_log << tcu::TestLog::Section(
            "", "GL_INVALID_VALUE is generated if index is greater than or equal to GL_MAX_VERTEX_ATTRIBS.");
        int maxVertexAttribs = m_context.getContextInfo().getInt(GL_MAX_VERTEX_ATTRIBS);
        glDisableVertexAttribArray(maxVertexAttribs);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(gen_vertex_arrays, "Invalid glGenVertexArrays() usage", {
        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if n is negative.");
        GLuint arrays;
        glGenVertexArrays(-1, &arrays);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(bind_vertex_array, "Invalid glBindVertexArray() usage", {
        m_log << tcu::TestLog::Section(
            "",
            "GL_INVALID_OPERATION is generated if array is not zero or the name of an existing vertex array object.");
        glBindVertexArray(-1);
        expectError(GL_INVALID_OPERATION);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(delete_vertex_arrays, "Invalid glDeleteVertexArrays() usage", {
        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if n is negative.");
        glDeleteVertexArrays(-1, 0);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(vertex_attrib_divisor, "Invalid glVertexAttribDivisor() usage", {
        m_log << tcu::TestLog::Section(
            "", "GL_INVALID_VALUE is generated if index is greater than or equal to GL_MAX_VERTEX_ATTRIBS.");
        int maxVertexAttribs = m_context.getContextInfo().getInt(GL_MAX_VERTEX_ATTRIBS);
        glVertexAttribDivisor(maxVertexAttribs, 0);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(draw_arrays, "Invalid glDrawArrays() usage", {
        glu::ShaderProgram program(m_context.getRenderContext(),
                                   glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
        glUseProgram(program.getProgram());
        GLuint fbo;
        VAOHelper vao(m_context);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawArrays(-1, 0, 1);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count is negative.");
        glDrawArrays(GL_POINTS, 0, -1);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawArrays(GL_POINTS, 0, 1);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;

        glUseProgram(0);
    });
    ES3F_ADD_API_CASE(draw_arrays_invalid_program, "Invalid glDrawArrays() usage", {
        glUseProgram(0);
        GLuint fbo;
        VAOHelper vao(m_context);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawArrays(-1, 0, 1);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count is negative.");
        glDrawArrays(GL_POINTS, 0, -1);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawArrays(GL_POINTS, 0, 1);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(draw_arrays_incomplete_primitive, "Invalid glDrawArrays() usage", {
        glu::ShaderProgram program(m_context.getRenderContext(),
                                   glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
        glUseProgram(program.getProgram());
        GLuint fbo;
        VAOHelper vao(m_context);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawArrays(-1, 0, 1);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count is negative.");
        glDrawArrays(GL_TRIANGLES, 0, -1);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawArrays(GL_TRIANGLES, 0, 1);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;

        glUseProgram(0);
    });
    ES3F_ADD_API_CASE(draw_elements, "Invalid glDrawElements() usage", {
        const bool isES = glu::isContextTypeES(m_context.getRenderContext().getType());
        glu::ShaderProgram program(m_context.getRenderContext(),
                                   glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
        glUseProgram(program.getProgram());
        GLuint fbo;
        GLuint buf;
        GLuint tfID;
        GLfloat vertices[1] = {0};
        VAOHelper vao(m_context);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawElements(-1, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if type is not one of the accepted values.");
        glDrawElements(GL_POINTS, 1, -1, vertices);
        expectError(GL_INVALID_ENUM);
        glDrawElements(GL_POINTS, 1, GL_FLOAT, vertices);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count is negative.");
        glDrawElements(GL_POINTS, -1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawElements(GL_POINTS, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;

        if (isES && !m_context.getContextInfo().isExtensionSupported(
                        "GL_EXT_geometry_shader")) // GL_EXT_geometry_shader removes error
        {
            m_log << tcu::TestLog::Section(
                "", "GL_INVALID_OPERATION is generated if transform feedback is active and not paused.");
            const char *tfVarying = "gl_Position";

            glGenBuffers(1, &buf);
            glGenTransformFeedbacks(1, &tfID);

            glUseProgram(program.getProgram());
            glTransformFeedbackVaryings(program.getProgram(), 1, &tfVarying, GL_INTERLEAVED_ATTRIBS);
            glLinkProgram(program.getProgram());
            glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfID);
            glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buf);
            glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 32, DE_NULL, GL_DYNAMIC_DRAW);
            glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf);
            glBeginTransformFeedback(GL_POINTS);
            expectError(GL_NO_ERROR);

            glDrawElements(GL_POINTS, 1, GL_UNSIGNED_BYTE, vertices);
            expectError(GL_INVALID_OPERATION);

            glPauseTransformFeedback();
            glDrawElements(GL_POINTS, 1, GL_UNSIGNED_BYTE, vertices);
            expectError(GL_NO_ERROR);

            glEndTransformFeedback();
            glDeleteBuffers(1, &buf);
            glDeleteTransformFeedbacks(1, &tfID);
            expectError(GL_NO_ERROR);
            m_log << tcu::TestLog::EndSection;
        }

        glUseProgram(0);
    });
    ES3F_ADD_API_CASE(draw_elements_invalid_program, "Invalid glDrawElements() usage", {
        glUseProgram(0);
        GLuint fbo;
        GLfloat vertices[1] = {0};
        VAOHelper vao(m_context);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawElements(-1, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if type is not one of the accepted values.");
        glDrawElements(GL_POINTS, 1, -1, vertices);
        expectError(GL_INVALID_ENUM);
        glDrawElements(GL_POINTS, 1, GL_FLOAT, vertices);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count is negative.");
        glDrawElements(GL_POINTS, -1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawElements(GL_POINTS, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(draw_elements_incomplete_primitive, "Invalid glDrawElements() usage", {
        const bool isES = glu::isContextTypeES(m_context.getRenderContext().getType());
        glu::ShaderProgram program(m_context.getRenderContext(),
                                   glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
        glUseProgram(program.getProgram());
        GLuint fbo;
        GLuint buf;
        GLuint tfID;
        GLfloat vertices[1] = {0};
        VAOHelper vao(m_context);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawElements(-1, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if type is not one of the accepted values.");
        glDrawElements(GL_TRIANGLES, 1, -1, vertices);
        expectError(GL_INVALID_ENUM);
        glDrawElements(GL_TRIANGLES, 1, GL_FLOAT, vertices);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count is negative.");
        glDrawElements(GL_TRIANGLES, -1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawElements(GL_TRIANGLES, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;

        if (isES && !m_context.getContextInfo().isExtensionSupported(
                        "GL_EXT_geometry_shader")) // GL_EXT_geometry_shader removes error
        {
            m_log << tcu::TestLog::Section(
                "", "GL_INVALID_OPERATION is generated if transform feedback is active and not paused.");
            const char *tfVarying = "gl_Position";

            glGenBuffers(1, &buf);
            glGenTransformFeedbacks(1, &tfID);

            glUseProgram(program.getProgram());
            glTransformFeedbackVaryings(program.getProgram(), 1, &tfVarying, GL_INTERLEAVED_ATTRIBS);
            glLinkProgram(program.getProgram());
            glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfID);
            glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buf);
            glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 32, DE_NULL, GL_DYNAMIC_DRAW);
            glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf);
            glBeginTransformFeedback(GL_TRIANGLES);
            expectError(GL_NO_ERROR);

            glDrawElements(GL_TRIANGLES, 1, GL_UNSIGNED_BYTE, vertices);
            expectError(GL_INVALID_OPERATION);

            glPauseTransformFeedback();
            glDrawElements(GL_TRIANGLES, 1, GL_UNSIGNED_BYTE, vertices);
            expectError(GL_NO_ERROR);

            glEndTransformFeedback();
            glDeleteBuffers(1, &buf);
            glDeleteTransformFeedbacks(1, &tfID);
            expectError(GL_NO_ERROR);
            m_log << tcu::TestLog::EndSection;
        }

        glUseProgram(0);
    });
    ES3F_ADD_API_CASE(draw_arrays_instanced, "Invalid glDrawArraysInstanced() usage", {
        glu::ShaderProgram program(m_context.getRenderContext(),
                                   glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
        glUseProgram(program.getProgram());
        GLuint fbo;
        VAOHelper vao(m_context);
        glVertexAttribDivisor(0, 1);
        expectError(GL_NO_ERROR);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawArraysInstanced(-1, 0, 1, 1);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count or primcount are negative.");
        glDrawArraysInstanced(GL_POINTS, 0, -1, 1);
        expectError(GL_INVALID_VALUE);
        glDrawArraysInstanced(GL_POINTS, 0, 1, -1);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawArraysInstanced(GL_POINTS, 0, 1, 1);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;

        glUseProgram(0);
    });
    ES3F_ADD_API_CASE(draw_arrays_instanced_invalid_program, "Invalid glDrawArraysInstanced() usage", {
        glUseProgram(0);
        GLuint fbo;
        VAOHelper vao(m_context);
        glVertexAttribDivisor(0, 1);
        expectError(GL_NO_ERROR);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawArraysInstanced(-1, 0, 1, 1);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count or primcount are negative.");
        glDrawArraysInstanced(GL_POINTS, 0, -1, 1);
        expectError(GL_INVALID_VALUE);
        glDrawArraysInstanced(GL_POINTS, 0, 1, -1);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawArraysInstanced(GL_POINTS, 0, 1, 1);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(draw_arrays_instanced_incomplete_primitive, "Invalid glDrawArraysInstanced() usage", {
        glu::ShaderProgram program(m_context.getRenderContext(),
                                   glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
        glUseProgram(program.getProgram());
        GLuint fbo;
        VAOHelper vao(m_context);
        glVertexAttribDivisor(0, 1);
        expectError(GL_NO_ERROR);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawArraysInstanced(-1, 0, 1, 1);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count or primcount are negative.");
        glDrawArraysInstanced(GL_TRIANGLES, 0, -1, 1);
        expectError(GL_INVALID_VALUE);
        glDrawArraysInstanced(GL_TRIANGLES, 0, 1, -1);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawArraysInstanced(GL_TRIANGLES, 0, 1, 1);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;

        glUseProgram(0);
    });
    ES3F_ADD_API_CASE(draw_elements_instanced, "Invalid glDrawElementsInstanced() usage", {
        const bool isES = glu::isContextTypeES(m_context.getRenderContext().getType());
        glu::ShaderProgram program(m_context.getRenderContext(),
                                   glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
        glUseProgram(program.getProgram());
        GLuint fbo;
        GLuint buf;
        GLuint tfID;
        GLfloat vertices[1] = {0};
        VAOHelper vao(m_context);

        glVertexAttribDivisor(0, 1);
        expectError(GL_NO_ERROR);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawElementsInstanced(-1, 1, GL_UNSIGNED_BYTE, vertices, 1);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if type is not one of the accepted values.");
        glDrawElementsInstanced(GL_POINTS, 1, -1, vertices, 1);
        expectError(GL_INVALID_ENUM);
        glDrawElementsInstanced(GL_POINTS, 1, GL_FLOAT, vertices, 1);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count or primcount are negative.");
        glDrawElementsInstanced(GL_POINTS, -1, GL_UNSIGNED_BYTE, vertices, 1);
        expectError(GL_INVALID_VALUE);
        glDrawElementsInstanced(GL_POINTS, 11, GL_UNSIGNED_BYTE, vertices, -1);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawElementsInstanced(GL_POINTS, 1, GL_UNSIGNED_BYTE, vertices, 1);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;

        if (isES && !m_context.getContextInfo().isExtensionSupported(
                        "GL_EXT_geometry_shader")) // GL_EXT_geometry_shader removes error
        {
            m_log << tcu::TestLog::Section(
                "", "GL_INVALID_OPERATION is generated if transform feedback is active and not paused.");
            const char *tfVarying = "gl_Position";

            glGenBuffers(1, &buf);
            glGenTransformFeedbacks(1, &tfID);

            glUseProgram(program.getProgram());
            glTransformFeedbackVaryings(program.getProgram(), 1, &tfVarying, GL_INTERLEAVED_ATTRIBS);
            glLinkProgram(program.getProgram());
            glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfID);
            glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buf);
            glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 32, DE_NULL, GL_DYNAMIC_DRAW);
            glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf);
            glBeginTransformFeedback(GL_POINTS);
            expectError(GL_NO_ERROR);

            glDrawElementsInstanced(GL_POINTS, 1, GL_UNSIGNED_BYTE, vertices, 1);
            expectError(GL_INVALID_OPERATION);

            glPauseTransformFeedback();
            glDrawElementsInstanced(GL_POINTS, 1, GL_UNSIGNED_BYTE, vertices, 1);
            expectError(GL_NO_ERROR);

            glEndTransformFeedback();
            glDeleteBuffers(1, &buf);
            glDeleteTransformFeedbacks(1, &tfID);
            expectError(GL_NO_ERROR);
            m_log << tcu::TestLog::EndSection;
        }

        glUseProgram(0);
    });
    ES3F_ADD_API_CASE(draw_elements_instanced_invalid_program, "Invalid glDrawElementsInstanced() usage", {
        glUseProgram(0);
        GLuint fbo;
        GLfloat vertices[1] = {0};
        VAOHelper vao(m_context);
        glVertexAttribDivisor(0, 1);
        expectError(GL_NO_ERROR);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawElementsInstanced(-1, 1, GL_UNSIGNED_BYTE, vertices, 1);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if type is not one of the accepted values.");
        glDrawElementsInstanced(GL_POINTS, 1, -1, vertices, 1);
        expectError(GL_INVALID_ENUM);
        glDrawElementsInstanced(GL_POINTS, 1, GL_FLOAT, vertices, 1);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count or primcount are negative.");
        glDrawElementsInstanced(GL_POINTS, -1, GL_UNSIGNED_BYTE, vertices, 1);
        expectError(GL_INVALID_VALUE);
        glDrawElementsInstanced(GL_POINTS, 11, GL_UNSIGNED_BYTE, vertices, -1);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawElementsInstanced(GL_POINTS, 1, GL_UNSIGNED_BYTE, vertices, 1);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(draw_elements_instanced_incomplete_primitive, "Invalid glDrawElementsInstanced() usage", {
        const bool isES = glu::isContextTypeES(m_context.getRenderContext().getType());
        glu::ShaderProgram program(m_context.getRenderContext(),
                                   glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
        glUseProgram(program.getProgram());
        GLuint fbo;
        GLuint buf;
        GLuint tfID;
        GLfloat vertices[1] = {0};
        VAOHelper vao(m_context);
        glVertexAttribDivisor(0, 1);
        expectError(GL_NO_ERROR);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawElementsInstanced(-1, 1, GL_UNSIGNED_BYTE, vertices, 1);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if type is not one of the accepted values.");
        glDrawElementsInstanced(GL_TRIANGLES, 1, -1, vertices, 1);
        expectError(GL_INVALID_ENUM);
        glDrawElementsInstanced(GL_TRIANGLES, 1, GL_FLOAT, vertices, 1);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count or primcount are negative.");
        glDrawElementsInstanced(GL_TRIANGLES, -1, GL_UNSIGNED_BYTE, vertices, 1);
        expectError(GL_INVALID_VALUE);
        glDrawElementsInstanced(GL_TRIANGLES, 11, GL_UNSIGNED_BYTE, vertices, -1);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawElementsInstanced(GL_TRIANGLES, 1, GL_UNSIGNED_BYTE, vertices, 1);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;

        if (isES && !m_context.getContextInfo().isExtensionSupported(
                        "GL_EXT_geometry_shader")) // GL_EXT_geometry_shader removes error
        {
            m_log << tcu::TestLog::Section(
                "", "GL_INVALID_OPERATION is generated if transform feedback is active and not paused.");
            const char *tfVarying = "gl_Position";

            glGenBuffers(1, &buf);
            glGenTransformFeedbacks(1, &tfID);

            glUseProgram(program.getProgram());
            glTransformFeedbackVaryings(program.getProgram(), 1, &tfVarying, GL_INTERLEAVED_ATTRIBS);
            glLinkProgram(program.getProgram());
            glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfID);
            glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buf);
            glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 32, DE_NULL, GL_DYNAMIC_DRAW);
            glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf);
            glBeginTransformFeedback(GL_TRIANGLES);
            expectError(GL_NO_ERROR);

            glDrawElementsInstanced(GL_TRIANGLES, 1, GL_UNSIGNED_BYTE, vertices, 1);
            expectError(GL_INVALID_OPERATION);

            glPauseTransformFeedback();
            glDrawElementsInstanced(GL_TRIANGLES, 1, GL_UNSIGNED_BYTE, vertices, 1);
            expectError(GL_NO_ERROR);

            glEndTransformFeedback();
            glDeleteBuffers(1, &buf);
            glDeleteTransformFeedbacks(1, &tfID);
            expectError(GL_NO_ERROR);
            m_log << tcu::TestLog::EndSection;
        }

        glUseProgram(0);
    });
    ES3F_ADD_API_CASE(draw_range_elements, "Invalid glDrawRangeElements() usage", {
        const bool isES = glu::isContextTypeES(m_context.getRenderContext().getType());
        glu::ShaderProgram program(m_context.getRenderContext(),
                                   glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
        glUseProgram(program.getProgram());
        GLuint fbo;
        GLuint buf;
        GLuint tfID;
        uint32_t vertices[1];
        vertices[0] = 0xffffffffu;
        VAOHelper vao(m_context);

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawRangeElements(-1, 0, 1, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if type is not one of the accepted values.");
        glDrawRangeElements(GL_POINTS, 0, 1, 1, -1, vertices);
        expectError(GL_INVALID_ENUM);
        glDrawRangeElements(GL_POINTS, 0, 1, 1, GL_FLOAT, vertices);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count is negative.");
        glDrawRangeElements(GL_POINTS, 0, 1, -1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if end < start.");
        glDrawRangeElements(GL_POINTS, 1, 0, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawRangeElements(GL_POINTS, 0, 1, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;

        if (isES && !m_context.getContextInfo().isExtensionSupported(
                        "GL_EXT_geometry_shader")) // GL_EXT_geometry_shader removes error
        {
            m_log << tcu::TestLog::Section(
                "", "GL_INVALID_OPERATION is generated if transform feedback is active and not paused.");
            const char *tfVarying = "gl_Position";
            uint32_t verticesInRange[1];
            verticesInRange[0] = 0;

            glGenBuffers(1, &buf);
            glGenTransformFeedbacks(1, &tfID);

            glUseProgram(program.getProgram());
            glTransformFeedbackVaryings(program.getProgram(), 1, &tfVarying, GL_INTERLEAVED_ATTRIBS);
            glLinkProgram(program.getProgram());
            glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfID);
            glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buf);
            glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 32, DE_NULL, GL_DYNAMIC_DRAW);
            glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf);
            glBeginTransformFeedback(GL_POINTS);
            expectError(GL_NO_ERROR);

            glDrawRangeElements(GL_POINTS, 0, 1, 1, GL_UNSIGNED_BYTE, vertices);
            expectError(GL_INVALID_OPERATION);

            glPauseTransformFeedback();
            glDrawRangeElements(GL_POINTS, 0, 1, 1, GL_UNSIGNED_BYTE, verticesInRange);
            expectError(GL_NO_ERROR);

            glEndTransformFeedback();
            glDeleteBuffers(1, &buf);
            glDeleteTransformFeedbacks(1, &tfID);
            expectError(GL_NO_ERROR);
            m_log << tcu::TestLog::EndSection;
        }

        glUseProgram(0);
    });
    ES3F_ADD_API_CASE(draw_range_elements_invalid_program, "Invalid glDrawRangeElements() usage", {
        glUseProgram(0);
        GLuint fbo;
        uint32_t vertices[1];
        VAOHelper vao(m_context);
        vertices[0] = 0xffffffffu;

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawRangeElements(-1, 0, 1, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if type is not one of the accepted values.");
        glDrawRangeElements(GL_POINTS, 0, 1, 1, -1, vertices);
        expectError(GL_INVALID_ENUM);
        glDrawRangeElements(GL_POINTS, 0, 1, 1, GL_FLOAT, vertices);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count is negative.");
        glDrawRangeElements(GL_POINTS, 0, 1, -1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if end < start.");
        glDrawRangeElements(GL_POINTS, 1, 0, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawRangeElements(GL_POINTS, 0, 1, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;
    });
    ES3F_ADD_API_CASE(draw_range_elements_incomplete_primitive, "Invalid glDrawRangeElements() usage", {
        const bool isES = glu::isContextTypeES(m_context.getRenderContext().getType());
        glu::ShaderProgram program(m_context.getRenderContext(),
                                   glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
        glUseProgram(program.getProgram());
        GLuint fbo;
        GLuint buf;
        GLuint tfID;
        uint32_t vertices[1];
        VAOHelper vao(m_context);
        vertices[0] = 0xffffffffu;

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if mode is not an accepted value.");
        glDrawRangeElements(-1, 0, 1, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_ENUM is generated if type is not one of the accepted values.");
        glDrawRangeElements(GL_TRIANGLES, 0, 1, 1, -1, vertices);
        expectError(GL_INVALID_ENUM);
        glDrawRangeElements(GL_TRIANGLES, 0, 1, 1, GL_FLOAT, vertices);
        expectError(GL_INVALID_ENUM);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if count is negative.");
        glDrawRangeElements(GL_TRIANGLES, 0, 1, -1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_VALUE is generated if end < start.");
        glDrawRangeElements(GL_TRIANGLES, 1, 0, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_VALUE);
        m_log << tcu::TestLog::EndSection;

        m_log << tcu::TestLog::Section("", "GL_INVALID_FRAMEBUFFER_OPERATION is generated if the currently bound "
                                           "framebuffer is not framebuffer complete.");
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
        glDrawRangeElements(GL_TRIANGLES, 0, 1, 1, GL_UNSIGNED_BYTE, vertices);
        expectError(GL_INVALID_FRAMEBUFFER_OPERATION);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &fbo);
        m_log << tcu::TestLog::EndSection;

        if (isES && !m_context.getContextInfo().isExtensionSupported(
                        "GL_EXT_geometry_shader")) // GL_EXT_geometry_shader removes error
        {
            m_log << tcu::TestLog::Section(
                "", "GL_INVALID_OPERATION is generated if transform feedback is active and not paused.");
            const char *tfVarying = "gl_Position";
            uint32_t verticesInRange[1];
            verticesInRange[0] = 0;

            glGenBuffers(1, &buf);
            glGenTransformFeedbacks(1, &tfID);

            glUseProgram(program.getProgram());
            glTransformFeedbackVaryings(program.getProgram(), 1, &tfVarying, GL_INTERLEAVED_ATTRIBS);
            glLinkProgram(program.getProgram());
            glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfID);
            glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buf);
            glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 32, DE_NULL, GL_DYNAMIC_DRAW);
            glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf);
            glBeginTransformFeedback(GL_TRIANGLES);
            expectError(GL_NO_ERROR);

            glDrawRangeElements(GL_TRIANGLES, 0, 1, 1, GL_UNSIGNED_BYTE, vertices);
            expectError(GL_INVALID_OPERATION);

            glPauseTransformFeedback();
            glDrawRangeElements(GL_TRIANGLES, 0, 1, 1, GL_UNSIGNED_BYTE, verticesInRange);
            expectError(GL_NO_ERROR);

            glEndTransformFeedback();
            glDeleteBuffers(1, &buf);
            glDeleteTransformFeedbacks(1, &tfID);
            expectError(GL_NO_ERROR);
            m_log << tcu::TestLog::EndSection;
        }

        glUseProgram(0);
    });
}

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