/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.1 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 Shader API tests.
 *//*--------------------------------------------------------------------*/

#include "es31fNegativeShaderApiTests.hpp"

#include "deUniquePtr.hpp"

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

#include "gluShaderProgram.hpp"
#include "gluCallLogWrapper.hpp"

#include "gluContextInfo.hpp"
#include "gluRenderContext.hpp"

namespace deqp
{
namespace gles31
{
namespace Functional
{
namespace NegativeTestShared
{
using glu::CallLogWrapper;
using tcu::TestLog;
using namespace glw;

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";

static const char *uniformTestVertSource = "#version 300 es\n"
                                           "uniform mediump vec4 vec4_v;\n"
                                           "uniform mediump mat4 mat4_v;\n"
                                           "void main (void)\n"
                                           "{\n"
                                           "    gl_Position = mat4_v * vec4_v;\n"
                                           "}\n\0";

static const char *uniformTestFragSource = "#version 300 es\n"
                                           "uniform mediump ivec4 ivec4_f;\n"
                                           "uniform mediump uvec4 uvec4_f;\n"
                                           "uniform sampler2D sampler_f;\n"
                                           "layout(location = 0) out mediump vec4 fragColor;"
                                           "void main (void)\n"
                                           "{\n"
                                           "    fragColor.xy = (vec4(uvec4_f) + vec4(ivec4_f)).xy;\n"
                                           "    fragColor.zw = texture(sampler_f, vec2(0.0, 0.0)).zw;\n"
                                           "}\n\0";

static const char *uniformBlockVertSource = "#version 300 es\n"
                                            "layout(shared) uniform Block { lowp float var; };\n"
                                            "void main (void)\n"
                                            "{\n"
                                            "    gl_Position = vec4(var);\n"
                                            "}\n\0";

static bool supportsES32orGL45(NegativeTestContext &ctx)
{
    return contextSupports(ctx.getRenderContext().getType(), glu::ApiType::es(3, 2)) ||
           contextSupports(ctx.getRenderContext().getType(), glu::ApiType::core(4, 5));
}

// Shader control commands
void create_shader(NegativeTestContext &ctx)
{
    ctx.beginSection("GL_INVALID_ENUM is generated if shaderType is not an accepted value.");
    ctx.glCreateShader(-1);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();
}

void shader_source(NegativeTestContext &ctx)
{
    // make notAShader not a shader id
    const GLuint notAShader = ctx.glCreateShader(GL_VERTEX_SHADER);
    ctx.glDeleteShader(notAShader);

    ctx.beginSection("GL_INVALID_VALUE is generated if shader is not a value generated by OpenGL.");
    ctx.glShaderSource(notAShader, 0, 0, 0);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_VALUE is generated if count is less than 0.");
    GLuint shader = ctx.glCreateShader(GL_VERTEX_SHADER);
    ctx.glShaderSource(shader, -1, 0, 0);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if shader is not a shader object.");
    GLuint program = ctx.glCreateProgram();
    ctx.glShaderSource(program, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glDeleteProgram(program);
    ctx.glDeleteShader(shader);
}

void compile_shader(NegativeTestContext &ctx)
{
    const GLuint notAShader = ctx.glCreateShader(GL_VERTEX_SHADER);
    ctx.glDeleteShader(notAShader);

    ctx.beginSection("GL_INVALID_VALUE is generated if shader is not a value generated by OpenGL.");
    ctx.glCompileShader(notAShader);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if shader is not a shader object.");
    GLuint program = ctx.glCreateProgram();
    ctx.glCompileShader(program);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glDeleteProgram(program);
}

void delete_shader(NegativeTestContext &ctx)
{
    const GLuint notAShader = ctx.glCreateShader(GL_VERTEX_SHADER);
    ctx.glDeleteShader(notAShader);

    ctx.beginSection("GL_INVALID_VALUE is generated if shader is not a value generated by OpenGL.");
    ctx.glDeleteShader(notAShader);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();
}

void shader_binary(NegativeTestContext &ctx)
{
    std::vector<int32_t> binaryFormats;
    bool shaderBinarySupported = !binaryFormats.empty();
    GLuint shaders[2];
    GLuint shaderPair[2];
    GLuint nonProgram[2];
    GLuint shaderProgram[2];

    {
        int32_t numFormats = 0x1234;
        ctx.glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &numFormats);

        if (numFormats == 0)
            ctx.getLog() << TestLog::Message << "// No supported extensions available." << TestLog::EndMessage;
        else
        {
            binaryFormats.resize(numFormats);
            ctx.glGetIntegerv(GL_SHADER_BINARY_FORMATS, &binaryFormats[0]);
        }
    }

    if (!shaderBinarySupported)
        ctx.getLog() << TestLog::Message << "// Shader binaries not supported." << TestLog::EndMessage;
    else
        ctx.getLog() << TestLog::Message << "// Shader binaries supported" << TestLog::EndMessage;

    shaders[0]       = ctx.glCreateShader(GL_VERTEX_SHADER);
    shaders[1]       = ctx.glCreateShader(GL_VERTEX_SHADER);
    shaderPair[0]    = ctx.glCreateShader(GL_VERTEX_SHADER);
    shaderPair[1]    = ctx.glCreateShader(GL_FRAGMENT_SHADER);
    nonProgram[0]    = -1;
    nonProgram[1]    = -1;
    shaderProgram[0] = ctx.glCreateShader(GL_VERTEX_SHADER);
    shaderProgram[1] = ctx.glCreateProgram();

    ctx.beginSection("GL_INVALID_ENUM is generated if binaryFormat is not an accepted value.");
    ctx.glShaderBinary(1, &shaders[0], -1, 0, 0);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    if (shaderBinarySupported)
    {
        ctx.beginSection("GL_INVALID_VALUE is generated if the data pointed to by binary does not match the format "
                         "specified by binaryFormat.");
        const GLbyte data = 0x005F;
        ctx.glShaderBinary(1, &shaders[0], binaryFormats[0], &data, 1);
        ctx.expectError(GL_INVALID_VALUE);
        ctx.endSection();

        ctx.beginSection("GL_INVALID_OPERATION is generated if more than one of the handles in shaders refers to the "
                         "same type of shader, or GL_INVALID_VALUE due to invalid data pointer.");
        ctx.glShaderBinary(2, &shaders[0], binaryFormats[0], 0, 0);
        ctx.expectError(GL_INVALID_OPERATION, GL_INVALID_VALUE);
        ctx.endSection();

        ctx.beginSection("GL_INVALID_VALUE is generated if count or length is negative.");
        ctx.glShaderBinary(2, &shaderPair[0], binaryFormats[0], 0, -1);
        ctx.expectError(GL_INVALID_VALUE);
        ctx.glShaderBinary(-1, &shaderPair[0], binaryFormats[0], 0, 0);
        ctx.expectError(GL_INVALID_VALUE);
        ctx.endSection();

        ctx.beginSection(
            "GL_INVALID_VALUE is generated if shaders contains anything other than shader or program objects.");
        ctx.glShaderBinary(2, &nonProgram[0], binaryFormats[0], 0, 0);
        ctx.expectError(GL_INVALID_VALUE);
        ctx.endSection();

        ctx.beginSection("GL_INVALID_OPERATION is generated if shaders refers to a program object.");
        ctx.glShaderBinary(2, &shaderProgram[0], binaryFormats[0], 0, 0);
        ctx.expectError(GL_INVALID_OPERATION);
        ctx.endSection();
    }

    ctx.glDeleteShader(shaders[0]);
    ctx.glDeleteShader(shaders[1]);
}

void attach_shader(NegativeTestContext &ctx)
{
    GLuint shader1 = ctx.glCreateShader(GL_VERTEX_SHADER);
    GLuint shader2 = ctx.glCreateShader(GL_VERTEX_SHADER);
    GLuint program = ctx.glCreateProgram();

    const GLuint notAShader  = ctx.glCreateShader(GL_VERTEX_SHADER);
    const GLuint notAProgram = ctx.glCreateProgram();

    ctx.glDeleteShader(notAShader);
    ctx.glDeleteProgram(notAProgram);

    ctx.beginSection("GL_INVALID_OPERATION is generated if program is not a program object.");
    ctx.glAttachShader(shader1, shader1);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if shader is not a shader object.");
    ctx.glAttachShader(program, program);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glAttachShader(shader1, program);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_VALUE is generated if either program or shader is not a value generated by OpenGL.");
    ctx.glAttachShader(program, notAShader);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.glAttachShader(notAProgram, shader1);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.glAttachShader(notAProgram, notAShader);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if shader is already attached to program.");
    ctx.glAttachShader(program, shader1);
    ctx.expectError(GL_NO_ERROR);
    ctx.glAttachShader(program, shader1);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    if (glu::isContextTypeES(ctx.getRenderContext().getType()))
    {
        ctx.beginSection(
            "GL_INVALID_OPERATION is generated if a shader of the same type as shader is already attached to program.");
        ctx.glAttachShader(program, shader2);
        ctx.expectError(GL_INVALID_OPERATION);
        ctx.endSection();
    }

    ctx.glDeleteProgram(program);
    ctx.glDeleteShader(shader1);
    ctx.glDeleteShader(shader2);
}

void detach_shader(NegativeTestContext &ctx)
{
    GLuint shader  = ctx.glCreateShader(GL_VERTEX_SHADER);
    GLuint program = ctx.glCreateProgram();

    const GLuint notAShader  = ctx.glCreateShader(GL_VERTEX_SHADER);
    const GLuint notAProgram = ctx.glCreateProgram();

    ctx.glDeleteShader(notAShader);
    ctx.glDeleteProgram(notAProgram);

    ctx.beginSection("GL_INVALID_VALUE is generated if either program or shader is not a value generated by OpenGL.");
    ctx.glDetachShader(notAProgram, shader);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.glDetachShader(program, notAShader);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.glDetachShader(notAProgram, notAShader);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if program is not a program object.");
    ctx.glDetachShader(shader, shader);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if shader is not a shader object.");
    ctx.glDetachShader(program, program);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glDetachShader(shader, program);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if shader is not attached to program.");
    ctx.glDetachShader(program, shader);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glDeleteProgram(program);
    ctx.glDeleteShader(shader);
}

void link_program(NegativeTestContext &ctx)
{
    GLuint shader = ctx.glCreateShader(GL_VERTEX_SHADER);

    const GLuint notAProgram = ctx.glCreateProgram();
    ctx.glDeleteProgram(notAProgram);

    ctx.beginSection("GL_INVALID_VALUE is generated if program is not a value generated by OpenGL.");
    ctx.glLinkProgram(notAProgram);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if program is not a program object.");
    ctx.glLinkProgram(shader);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glDeleteShader(shader);

    ctx.beginSection("GL_INVALID_OPERATION is generated if program is the currently active program object and "
                     "transform feedback mode is active.");
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
    uint32_t buf          = 0x1234;
    uint32_t tfID         = 0x1234;
    const char *tfVarying = "gl_Position";

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

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

    ctx.glLinkProgram(program.getProgram());
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glEndTransformFeedback();
    ctx.glDeleteTransformFeedbacks(1, &tfID);
    ctx.glDeleteBuffers(1, &buf);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();
}

void use_program(NegativeTestContext &ctx)
{
    GLuint shader = ctx.glCreateShader(GL_VERTEX_SHADER);

    const GLuint notAProgram = ctx.glCreateProgram();
    ctx.glDeleteProgram(notAProgram);

    ctx.beginSection("GL_INVALID_VALUE is generated if program is neither 0 nor a value generated by OpenGL.");
    ctx.glUseProgram(notAProgram);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if program is not a program object.");
    ctx.glUseProgram(shader);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if transform feedback mode is active and not paused.");
    glu::ShaderProgram program1(ctx.getRenderContext(),
                                glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
    glu::ShaderProgram program2(ctx.getRenderContext(),
                                glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
    uint32_t buf          = 0x1234;
    uint32_t tfID         = 0x1234;
    const char *tfVarying = "gl_Position";

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

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

    ctx.glUseProgram(program2.getProgram());
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glPauseTransformFeedback();
    ctx.glUseProgram(program2.getProgram());
    ctx.expectError(GL_NO_ERROR);

    ctx.glEndTransformFeedback();
    ctx.glDeleteTransformFeedbacks(1, &tfID);
    ctx.glDeleteBuffers(1, &buf);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.glUseProgram(0);
    ctx.glDeleteShader(shader);
}

void delete_program(NegativeTestContext &ctx)
{
    GLuint shader = ctx.glCreateShader(GL_VERTEX_SHADER);

    const GLuint notAProgram = ctx.glCreateProgram();
    ctx.glDeleteProgram(notAProgram);

    ctx.beginSection("GL_INVALID_VALUE is generated if program is not a value generated by OpenGL.");
    ctx.glDeleteProgram(notAProgram);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if program is not zero and is the name of a shader object.");
    ctx.glDeleteProgram(shader);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glDeleteShader(shader);
}

void validate_program(NegativeTestContext &ctx)
{
    GLuint shader = ctx.glCreateShader(GL_VERTEX_SHADER);

    const GLuint notAProgram = ctx.glCreateProgram();
    ctx.glDeleteProgram(notAProgram);

    ctx.beginSection("GL_INVALID_VALUE is generated if program is not a value generated by OpenGL.");
    ctx.glValidateProgram(notAProgram);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if program is not a program object.");
    ctx.glValidateProgram(shader);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glDeleteShader(shader);
}

void get_program_binary(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
    glu::ShaderProgram programInvalid(ctx.getRenderContext(), glu::makeVtxFragSources(vertexShaderSource, ""));
    GLenum binaryFormat  = -1;
    GLsizei binaryLength = -1;
    GLint binaryPtr      = -1;
    GLint bufSize        = -1;
    GLint linkStatus     = -1;

    ctx.beginSection(
        "GL_INVALID_OPERATION is generated if bufSize is less than the size of GL_PROGRAM_BINARY_LENGTH for program.");
    ctx.glGetProgramiv(program.getProgram(), GL_PROGRAM_BINARY_LENGTH, &bufSize);
    ctx.expectError(GL_NO_ERROR);
    ctx.glGetProgramiv(program.getProgram(), GL_LINK_STATUS, &linkStatus);
    ctx.getLog() << TestLog::Message << "// GL_PROGRAM_BINARY_LENGTH = " << bufSize << TestLog::EndMessage;
    ctx.getLog() << TestLog::Message << "// GL_LINK_STATUS = " << linkStatus << TestLog::EndMessage;
    ctx.expectError(GL_NO_ERROR);

    ctx.glGetProgramBinary(program.getProgram(), 0, &binaryLength, &binaryFormat, &binaryPtr);
    ctx.expectError(GL_INVALID_OPERATION);
    if (bufSize > 0)
    {
        ctx.glGetProgramBinary(program.getProgram(), bufSize - 1, &binaryLength, &binaryFormat, &binaryPtr);
        ctx.expectError(GL_INVALID_OPERATION);
    }
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if GL_LINK_STATUS for the program object is false.");
    ctx.glGetProgramiv(programInvalid.getProgram(), GL_PROGRAM_BINARY_LENGTH, &bufSize);
    ctx.expectError(GL_NO_ERROR);
    ctx.glGetProgramiv(programInvalid.getProgram(), GL_LINK_STATUS, &linkStatus);
    ctx.getLog() << TestLog::Message << "// GL_PROGRAM_BINARY_LENGTH = " << bufSize << TestLog::EndMessage;
    ctx.getLog() << TestLog::Message << "// GL_LINK_STATUS = " << linkStatus << TestLog::EndMessage;
    ctx.expectError(GL_NO_ERROR);

    if (!linkStatus)
    {
        ctx.glGetProgramBinary(programInvalid.getProgram(), bufSize, &binaryLength, &binaryFormat, &binaryPtr);
        ctx.expectError(GL_INVALID_OPERATION);
        ctx.endSection();
    }
    else
    {
        if (isContextTypeES(ctx.getRenderContext().getType()))
            ctx.fail("Program should not have linked");
    }
}

void program_binary(NegativeTestContext &ctx)
{
    glu::ShaderProgram srcProgram(ctx.getRenderContext(),
                                  glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
    GLuint dstProgram    = ctx.glCreateProgram();
    GLuint unusedShader  = ctx.glCreateShader(GL_VERTEX_SHADER);
    GLenum binaryFormat  = -1;
    GLsizei binaryLength = -1;
    std::vector<uint8_t> binaryBuf;
    GLint bufSize    = -1;
    GLint linkStatus = -1;

    ctx.glGetProgramiv(srcProgram.getProgram(), GL_PROGRAM_BINARY_LENGTH, &bufSize);
    ctx.glGetProgramiv(srcProgram.getProgram(), GL_LINK_STATUS, &linkStatus);
    ctx.getLog() << TestLog::Message << "// GL_PROGRAM_BINARY_LENGTH = " << bufSize << TestLog::EndMessage;
    ctx.getLog() << TestLog::Message << "// GL_LINK_STATUS = " << linkStatus << TestLog::EndMessage;

    TCU_CHECK(bufSize >= 0);
    if (bufSize > 0)
    {
        binaryBuf.resize(bufSize);
        ctx.glGetProgramBinary(srcProgram.getProgram(), bufSize, &binaryLength, &binaryFormat, &binaryBuf[0]);
        ctx.expectError(GL_NO_ERROR);

        ctx.beginSection("GL_INVALID_OPERATION is generated if program is not the name of an existing program object.");
        ctx.glProgramBinary(unusedShader, binaryFormat, &binaryBuf[0], binaryLength);
        ctx.expectError(GL_INVALID_OPERATION);
        ctx.endSection();

        ctx.beginSection(
            "GL_INVALID_ENUM is generated if binaryFormat is not a value recognized by the implementation.");
        ctx.glProgramBinary(dstProgram, -1, &binaryBuf[0], binaryLength);
        ctx.expectError(GL_INVALID_ENUM);
        ctx.endSection();
    }

    ctx.glDeleteShader(unusedShader);
    ctx.glDeleteProgram(dstProgram);
}

void program_parameteri(NegativeTestContext &ctx)
{
    GLuint program = ctx.glCreateProgram();
    GLuint shader  = ctx.glCreateShader(GL_VERTEX_SHADER);

    const GLuint notAProgram = ctx.glCreateProgram();
    ctx.glDeleteProgram(notAProgram);

    ctx.beginSection("GL_INVALID_VALUE is generated if program is not the name of an existing program object.");
    ctx.glProgramParameteri(notAProgram, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if program is the name of a shader object.");
    ctx.glProgramParameteri(shader, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection(
        "GL_INVALID_ENUM is generated if pname is not GL_PROGRAM_BINARY_RETRIEVABLE_HINT or PROGRAM_SEPARABLE.");
    ctx.glProgramParameteri(program, -1, GL_TRUE);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_VALUE is generated if value is not GL_FALSE or GL_TRUE.");
    ctx.glProgramParameteri(program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, 2);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.glDeleteProgram(program);
    ctx.glDeleteShader(shader);
}

void gen_samplers(NegativeTestContext &ctx)
{
    ctx.beginSection("GL_INVALID_VALUE is generated if n is negative.");
    GLuint sampler = 0;
    ctx.glGenSamplers(-1, &sampler);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();
}

void bind_sampler(NegativeTestContext &ctx)
{
    int maxTexImageUnits = 0x1234;
    GLuint sampler       = 0;
    ctx.glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTexImageUnits);
    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_VALUE is generated if unit is greater than or equal to the value of "
                     "GL_MAX_COMBIED_TEXTURE_IMAGE_UNITS.");
    ctx.glBindSampler(maxTexImageUnits, sampler);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if sampler is not zero or a name previously returned from a "
                     "call to ctx.glGenSamplers.");
    ctx.glBindSampler(1, -1);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection(
        "GL_INVALID_OPERATION is generated if sampler has been deleted by a call to ctx.glDeleteSamplers.");
    ctx.glDeleteSamplers(1, &sampler);
    ctx.glBindSampler(1, sampler);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();
}

void delete_samplers(NegativeTestContext &ctx)
{
    ctx.beginSection("GL_INVALID_VALUE is generated if n is negative.");
    ctx.glDeleteSamplers(-1, 0);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();
}

void get_sampler_parameteriv(NegativeTestContext &ctx)
{
    int params     = 0x1234;
    GLuint sampler = 0;
    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_OPERATION is generated if sampler is not the name of a sampler object returned from a "
                     "previous call to ctx.glGenSamplers.");
    ctx.glGetSamplerParameteriv(-1, GL_TEXTURE_MAG_FILTER, &params);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_ENUM is generated if pname is not an accepted value.");
    ctx.glGetSamplerParameteriv(sampler, -1, &params);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.glDeleteSamplers(1, &sampler);
}

void get_sampler_parameterfv(NegativeTestContext &ctx)
{
    float params   = 0.0f;
    GLuint sampler = 0;
    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_OPERATION is generated if sampler is not the name of a sampler object returned from a "
                     "previous call to ctx.glGenSamplers.");
    ctx.glGetSamplerParameterfv(-1, GL_TEXTURE_MAG_FILTER, &params);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_ENUM is generated if pname is not an accepted value.");
    ctx.glGetSamplerParameterfv(sampler, -1, &params);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.glDeleteSamplers(1, &sampler);
}

void get_sampler_parameterIiv(NegativeTestContext &ctx)
{
    if (!supportsES32orGL45(ctx))
        throw tcu::NotSupportedError("glGetSamplerParameterIiv is not supported.", DE_NULL, __FILE__, __LINE__);

    GLuint sampler      = 0x1234;
    GLint borderColor[] = {0x1234, 0x4123, 0x3412, 0x2341};

    ctx.beginSection("GL_INVALID_OPERATION is generated if sampler is not the name of a sampler object returned from a "
                     "previous call to ctx.glGenSamplers.");
    ctx.glGetSamplerParameterIiv(sampler, GL_TEXTURE_BORDER_COLOR, &borderColor[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_ENUM is generated if pname is not an accepted value.");
    ctx.glGetSamplerParameterIiv(sampler, -1, &borderColor[0]);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.glDeleteSamplers(1, &sampler);
}

void get_sampler_parameterIuiv(NegativeTestContext &ctx)
{
    if (!supportsES32orGL45(ctx))
        throw tcu::NotSupportedError("glGetSamplerParameterIuiv is not supported.", DE_NULL, __FILE__, __LINE__);

    GLuint sampler       = 0x1234;
    GLuint borderColor[] = {0x1234, 0x4123, 0x3412, 0x2341};

    ctx.beginSection("GL_INVALID_OPERATION is generated if sampler is not the name of a sampler object returned from a "
                     "previous call to ctx.glGenSamplers.");
    ctx.glGetSamplerParameterIuiv(sampler, GL_TEXTURE_BORDER_COLOR, &borderColor[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_ENUM is generated if pname is not an accepted value.");
    ctx.glGetSamplerParameterIuiv(sampler, -1, &borderColor[0]);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.glDeleteSamplers(1, &sampler);
}

void sampler_parameteri(NegativeTestContext &ctx)
{
    GLuint sampler = 0;

    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_OPERATION is generated if sampler is not the name of a sampler object previously "
                     "returned from a call to ctx.glGenSamplers.");
    ctx.glSamplerParameteri(-1, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_ENUM is generated if params should have a defined constant value (based on the value "
                     "of pname) and does not.");
    ctx.glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, -1);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    if (supportsES32orGL45(ctx))
    {
        ctx.beginSection("GL_INVALID_ENUM is generated if glSamplerParameteri is called for a non-scalar parameter.");
        ctx.glSamplerParameteri(sampler, GL_TEXTURE_BORDER_COLOR, 0);
        ctx.expectError(GL_INVALID_ENUM);
        ctx.endSection();
    }

    ctx.glDeleteSamplers(1, &sampler);
}

void sampler_parameteriv(NegativeTestContext &ctx)
{
    int params     = 0x1234;
    GLuint sampler = 0;
    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_OPERATION is generated if sampler is not the name of a sampler object previously "
                     "returned from a call to ctx.glGenSamplers.");
    params = GL_CLAMP_TO_EDGE;
    ctx.glSamplerParameteriv(-1, GL_TEXTURE_WRAP_S, &params);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_ENUM is generated if params should have a defined constant value (based on the value "
                     "of pname) and does not.");
    params = -1;
    ctx.glSamplerParameteriv(sampler, GL_TEXTURE_WRAP_S, &params);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.glDeleteSamplers(1, &sampler);
}

void sampler_parameterf(NegativeTestContext &ctx)
{
    GLuint sampler = 0;

    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_OPERATION is generated if sampler is not the name of a sampler object previously "
                     "returned from a call to ctx.glGenSamplers.");
    ctx.glSamplerParameterf(-1, GL_TEXTURE_MIN_LOD, -1000.0f);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_ENUM is generated if params should have a defined constant value (based on the value "
                     "of pname) and does not.");
    ctx.glSamplerParameterf(sampler, GL_TEXTURE_WRAP_S, -1.0f);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    if (supportsES32orGL45(ctx))
    {
        ctx.beginSection("GL_INVALID_ENUM is generated if glSamplerParameterf is called for a non-scalar parameter.");
        ctx.glSamplerParameteri(sampler, GL_TEXTURE_BORDER_COLOR, 0);
        ctx.expectError(GL_INVALID_ENUM);
        ctx.endSection();
    }

    ctx.glDeleteSamplers(1, &sampler);
}

void sampler_parameterfv(NegativeTestContext &ctx)
{
    float params;
    GLuint sampler = 0;
    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_OPERATION is generated if sampler is not the name of a sampler object previously "
                     "returned from a call to ctx.glGenSamplers.");
    params = -1000.0f;
    ctx.glSamplerParameterfv(-1, GL_TEXTURE_WRAP_S, &params);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_ENUM is generated if params should have a defined constant value (based on the value "
                     "of pname) and does not.");
    params = -1.0f;
    ctx.glSamplerParameterfv(sampler, GL_TEXTURE_WRAP_S, &params);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.glDeleteSamplers(1, &sampler);
}

void sampler_parameterIiv(NegativeTestContext &ctx)
{
    if (!supportsES32orGL45(ctx))
        throw tcu::NotSupportedError("glSamplerParameterIiv is not supported.", DE_NULL, __FILE__, __LINE__);

    GLuint sampler;
    GLint color[] = {0, 0, 0, 0};

    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_OPERATION is generated if sampler is not the name of a sampler object previously "
                     "returned from a call to ctx.glGenSamplers.");
    ctx.glSamplerParameterIiv(-1, GL_TEXTURE_BORDER_COLOR, color);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_ENUM is generated if pname is not an accepted sampler state name.");
    ctx.glSamplerParameterIiv(sampler, -1, color);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();
}

void sampler_parameterIuiv(NegativeTestContext &ctx)
{
    if (!supportsES32orGL45(ctx))
        throw tcu::NotSupportedError("glSamplerParameterIuiv is not supported.", DE_NULL, __FILE__, __LINE__);

    GLuint sampler;
    GLuint color[] = {0, 0, 0, 0};

    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_OPERATION is generated if sampler is not the name of a sampler object previously "
                     "returned from a call to ctx.glGenSamplers.");
    ctx.glSamplerParameterIuiv(-1, GL_TEXTURE_BORDER_COLOR, color);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_ENUM is generated if pname is not an accepted sampler state name.");
    ctx.glSamplerParameterIuiv(sampler, -1, color);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();
}

// Shader data commands

void get_attrib_location(NegativeTestContext &ctx)
{
    GLuint programEmpty = ctx.glCreateProgram();
    GLuint shader       = ctx.glCreateShader(GL_VERTEX_SHADER);

    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));

    const GLuint notAProgram = ctx.glCreateProgram();
    ctx.glDeleteProgram(notAProgram);

    ctx.beginSection("GL_INVALID_OPERATION is generated if program has not been successfully linked.");
    ctx.glBindAttribLocation(programEmpty, 0, "test");
    ctx.glGetAttribLocation(programEmpty, "test");
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_VALUE is generated if program is not a program or shader object.");
    ctx.glUseProgram(program.getProgram());
    ctx.glBindAttribLocation(program.getProgram(), 0, "test");
    ctx.expectError(GL_NO_ERROR);
    ctx.glGetAttribLocation(program.getProgram(), "test");
    ctx.expectError(GL_NO_ERROR);
    ctx.glGetAttribLocation(notAProgram, "test");
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if program is not a program object.");
    ctx.glGetAttribLocation(shader, "test");
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glUseProgram(0);
    ctx.glDeleteShader(shader);
    ctx.glDeleteProgram(programEmpty);
}

void get_uniform_location(NegativeTestContext &ctx)
{
    GLuint programEmpty = ctx.glCreateProgram();
    GLuint shader       = ctx.glCreateShader(GL_VERTEX_SHADER);

    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));

    const GLuint notAProgram = ctx.glCreateProgram();
    ctx.glDeleteProgram(notAProgram);

    ctx.beginSection("GL_INVALID_OPERATION is generated if program has not been successfully linked.");
    ctx.glGetUniformLocation(programEmpty, "test");
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_VALUE is generated if program is not a value generated by OpenGL.");
    ctx.glUseProgram(program.getProgram());
    ctx.glGetUniformLocation(notAProgram, "test");
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if program is not a program object.");
    ctx.glGetAttribLocation(shader, "test");
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glUseProgram(0);
    ctx.glDeleteProgram(programEmpty);
    ctx.glDeleteShader(shader);
}

void bind_attrib_location(NegativeTestContext &ctx)
{
    GLuint program  = ctx.glCreateProgram();
    GLuint maxIndex = ctx.getInteger(GL_MAX_VERTEX_ATTRIBS);
    GLuint shader   = ctx.glCreateShader(GL_VERTEX_SHADER);

    ctx.beginSection("GL_INVALID_VALUE is generated if index is greater than or equal to GL_MAX_VERTEX_ATTRIBS.");
    ctx.glBindAttribLocation(program, maxIndex, "test");
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if name starts with the reserved prefix \"gl_\".");
    ctx.glBindAttribLocation(program, maxIndex - 1, "gl_test");
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_VALUE is generated if program is not a value generated by OpenGL.");
    ctx.glBindAttribLocation(-1, maxIndex - 1, "test");
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if program is not a program object.");
    ctx.glBindAttribLocation(shader, maxIndex - 1, "test");
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glDeleteProgram(program);
    ctx.glDeleteShader(shader);
}

void uniform_block_binding(NegativeTestContext &ctx)
{
    GLint maxUniformBufferBindings = -1;
    GLint numActiveUniforms        = -1;
    GLint numActiveBlocks          = -1;
    GLuint shader                  = -1;
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformBlockVertSource, uniformTestFragSource));

    shader = ctx.glCreateShader(GL_VERTEX_SHADER);
    ctx.glUseProgram(program.getProgram());

    ctx.glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings);
    ctx.glGetProgramiv(program.getProgram(), GL_ACTIVE_UNIFORMS, &numActiveUniforms);
    ctx.glGetProgramiv(program.getProgram(), GL_ACTIVE_UNIFORM_BLOCKS, &numActiveBlocks);
    ctx.getLog() << TestLog::Message << "// GL_MAX_UNIFORM_BUFFER_BINDINGS = " << maxUniformBufferBindings
                 << TestLog::EndMessage;
    ctx.getLog() << TestLog::Message << "// GL_ACTIVE_UNIFORMS = " << numActiveUniforms << TestLog::EndMessage;
    ctx.getLog() << TestLog::Message << "// GL_ACTIVE_UNIFORM_BLOCKS = " << numActiveBlocks << TestLog::EndMessage;
    ctx.expectError(GL_NO_ERROR);

    ctx.beginSection(
        "GL_INVALID_VALUE is generated if uniformBlockIndex is not an active uniform block index of program.");
    ctx.glUniformBlockBinding(program.getProgram(), -1, 0);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.glUniformBlockBinding(program.getProgram(), 5, 0);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_VALUE is generated if uniformBlockBinding is greater than or equal to the value of "
                     "GL_MAX_UNIFORM_BUFFER_BINDINGS.");
    ctx.glUniformBlockBinding(program.getProgram(), maxUniformBufferBindings, 0);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection(
        "GL_INVALID_VALUE is generated if program is not the name of a program object generated by the GL.");
    ctx.glUniformBlockBinding(-1, 0, 0);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if program is the name of a shader object.");
    ctx.glUniformBlockBinding(shader, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glDeleteShader(shader);
}

// ctx.glUniform*f

void uniformf_invalid_program(NegativeTestContext &ctx)
{
    ctx.beginSection("GL_INVALID_OPERATION is generated if there is no current program object.");
    ctx.glUseProgram(0);
    ctx.glUniform1f(-1, 0.0f);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2f(-1, 0.0f, 0.0f);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3f(-1, 0.0f, 0.0f, 0.0f);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4f(-1, 0.0f, 0.0f, 0.0f, 0.0f);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();
}

void uniformf_incompatible_type(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    GLint vec4_v    = ctx.glGetUniformLocation(program.getProgram(), "vec4_v");    // vec4
    GLint ivec4_f   = ctx.glGetUniformLocation(program.getProgram(), "ivec4_f");   // ivec4
    GLint uvec4_f   = ctx.glGetUniformLocation(program.getProgram(), "uvec4_f");   // uvec4
    GLint sampler_f = ctx.glGetUniformLocation(program.getProgram(), "sampler_f"); // sampler2D
    ctx.expectError(GL_NO_ERROR);

    if (vec4_v == -1 || ivec4_f == -1 || uvec4_f == -1 || sampler_f == -1)
    {
        ctx.getLog() << TestLog::Message << "// ERROR: Failed to retrieve uniform location" << TestLog::EndMessage;
        ctx.fail("Failed to retrieve uniform location");
    }

    ctx.beginSection("GL_INVALID_OPERATION is generated if the size of the uniform variable declared in the shader "
                     "does not match the size indicated by the ctx.glUniform command.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1f(vec4_v, 0.0f);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2f(vec4_v, 0.0f, 0.0f);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3f(vec4_v, 0.0f, 0.0f, 0.0f);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4f(vec4_v, 0.0f, 0.0f, 0.0f, 0.0f);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if ctx.glUniform{1234}f is used to load a uniform variable of "
                     "type int, ivec2, ivec3, ivec4, unsigned int, uvec2, uvec3, uvec4.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform4f(ivec4_f, 0.0f, 0.0f, 0.0f, 0.0f);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4f(uvec4_f, 0.0f, 0.0f, 0.0f, 0.0f);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if a sampler is loaded using a command other than "
                     "ctx.glUniform1i and ctx.glUniform1iv.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1f(sampler_f, 0.0f);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glUseProgram(0);
}

void uniformf_invalid_location(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    ctx.expectError(GL_NO_ERROR);

    ctx.beginSection("GL_INVALID_OPERATION is generated if location is an invalid uniform location for the current "
                     "program object and location is not equal to -1.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1f(-2, 0.0f);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2f(-2, 0.0f, 0.0f);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3f(-2, 0.0f, 0.0f, 0.0f);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4f(-2, 0.0f, 0.0f, 0.0f, 0.0f);
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1f(-1, 0.0f);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform2f(-1, 0.0f, 0.0f);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform3f(-1, 0.0f, 0.0f, 0.0f);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform4f(-1, 0.0f, 0.0f, 0.0f, 0.0f);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.glUseProgram(0);
}

// ctx.glUniform*fv

void uniformfv_invalid_program(NegativeTestContext &ctx)
{
    std::vector<GLfloat> data(4);

    ctx.beginSection("GL_INVALID_OPERATION is generated if there is no current program object.");
    ctx.glUseProgram(0);
    ctx.glUniform1fv(-1, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2fv(-1, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3fv(-1, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4fv(-1, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();
}

void uniformfv_incompatible_type(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    GLint vec4_v    = ctx.glGetUniformLocation(program.getProgram(), "vec4_v");    // vec4
    GLint ivec4_f   = ctx.glGetUniformLocation(program.getProgram(), "ivec4_f");   // ivec4
    GLint uvec4_f   = ctx.glGetUniformLocation(program.getProgram(), "uvec4_f");   // uvec4
    GLint sampler_f = ctx.glGetUniformLocation(program.getProgram(), "sampler_f"); // sampler2D
    ctx.expectError(GL_NO_ERROR);

    if (vec4_v == -1 || ivec4_f == -1 || uvec4_f == -1 || sampler_f == -1)
    {
        ctx.getLog() << TestLog::Message << "// ERROR: Failed to retrieve uniform location" << TestLog::EndMessage;
        ctx.fail("Failed to retrieve uniform location");
    }

    std::vector<GLfloat> data(4);

    ctx.beginSection("GL_INVALID_OPERATION is generated if the size of the uniform variable declared in the shader "
                     "does not match the size indicated by the ctx.glUniform command.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1fv(vec4_v, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2fv(vec4_v, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3fv(vec4_v, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4fv(vec4_v, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if ctx.glUniform{1234}fv is used to load a uniform variable of "
                     "type int, ivec2, ivec3, ivec4, unsigned int, uvec2, uvec3, uvec4.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform4fv(ivec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4fv(uvec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if a sampler is loaded using a command other than "
                     "ctx.glUniform1i and ctx.glUniform1iv.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1fv(sampler_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glUseProgram(0);
}

void uniformfv_invalid_location(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    ctx.expectError(GL_NO_ERROR);

    std::vector<GLfloat> data(4);

    ctx.beginSection("GL_INVALID_OPERATION is generated if location is an invalid uniform location for the current "
                     "program object and location is not equal to -1.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1fv(-2, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2fv(-2, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3fv(-2, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4fv(-2, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1fv(-1, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform2fv(-1, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform3fv(-1, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform4fv(-1, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.glUseProgram(0);
}

void uniformfv_invalid_count(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    GLint vec4_v = ctx.glGetUniformLocation(program.getProgram(), "vec4_v"); // vec4
    ctx.expectError(GL_NO_ERROR);

    if (vec4_v == -1)
    {
        ctx.getLog() << TestLog::Message << "// ERROR: Failed to retrieve uniform location" << TestLog::EndMessage;
        ctx.fail("Failed to retrieve uniform location");
    }

    std::vector<GLfloat> data(8);

    ctx.beginSection("GL_INVALID_OPERATION is generated if count is greater than 1 and the indicated uniform variable "
                     "is not an array variable.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1fv(vec4_v, 2, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2fv(vec4_v, 2, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3fv(vec4_v, 2, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4fv(vec4_v, 2, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glUseProgram(0);
}

// ctx.glUniform*i

void uniformi_invalid_program(NegativeTestContext &ctx)
{
    ctx.beginSection("GL_INVALID_OPERATION is generated if there is no current program object.");
    ctx.glUseProgram(0);
    ctx.glUniform1i(-1, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2i(-1, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3i(-1, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4i(-1, 0, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();
}

void uniformi_incompatible_type(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    GLint vec4_v    = ctx.glGetUniformLocation(program.getProgram(), "vec4_v");    // vec4
    GLint ivec4_f   = ctx.glGetUniformLocation(program.getProgram(), "ivec4_f");   // ivec4
    GLint uvec4_f   = ctx.glGetUniformLocation(program.getProgram(), "uvec4_f");   // uvec4
    GLint sampler_f = ctx.glGetUniformLocation(program.getProgram(), "sampler_f"); // sampler2D
    ctx.expectError(GL_NO_ERROR);

    if (vec4_v == -1 || ivec4_f == -1 || uvec4_f == -1 || sampler_f == -1)
    {
        ctx.getLog() << TestLog::Message << "// ERROR: Failed to retrieve uniform location" << TestLog::EndMessage;
        ctx.fail("Failed to retrieve uniform location");
    }

    ctx.beginSection("GL_INVALID_OPERATION is generated if the size of the uniform variable declared in the shader "
                     "does not match the size indicated by the ctx.glUniform command.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1i(ivec4_f, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2i(ivec4_f, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3i(ivec4_f, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4i(ivec4_f, 0, 0, 0, 0);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if ctx.glUniform{1234}i is used to load a uniform variable of "
                     "type unsigned int, uvec2, uvec3, uvec4, or an array of these.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1i(uvec4_f, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2i(uvec4_f, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3i(uvec4_f, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4i(uvec4_f, 0, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if ctx.glUniform{1234}i is used to load a uniform variable of "
                     "type float, vec2, vec3, or vec4.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1i(vec4_v, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2i(vec4_v, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3i(vec4_v, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4i(vec4_v, 0, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glUseProgram(0);
}

void uniformi_invalid_location(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    ctx.expectError(GL_NO_ERROR);

    ctx.beginSection("GL_INVALID_OPERATION is generated if location is an invalid uniform location for the current "
                     "program object and location is not equal to -1.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1i(-2, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2i(-2, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3i(-2, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4i(-2, 0, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1i(-1, 0);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform2i(-1, 0, 0);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform3i(-1, 0, 0, 0);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform4i(-1, 0, 0, 0, 0);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.glUseProgram(0);
}

// ctx.glUniform*iv

void uniformiv_invalid_program(NegativeTestContext &ctx)
{
    std::vector<GLint> data(4);

    ctx.beginSection("GL_INVALID_OPERATION is generated if there is no current program object.");
    ctx.glUseProgram(0);
    ctx.glUniform1iv(-1, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2iv(-1, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3iv(-1, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4iv(-1, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();
}

void uniformiv_incompatible_type(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    GLint vec4_v    = ctx.glGetUniformLocation(program.getProgram(), "vec4_v");    // vec4
    GLint ivec4_f   = ctx.glGetUniformLocation(program.getProgram(), "ivec4_f");   // ivec4
    GLint uvec4_f   = ctx.glGetUniformLocation(program.getProgram(), "uvec4_f");   // uvec4
    GLint sampler_f = ctx.glGetUniformLocation(program.getProgram(), "sampler_f"); // sampler2D
    ctx.expectError(GL_NO_ERROR);

    if (vec4_v == -1 || ivec4_f == -1 || uvec4_f == -1 || sampler_f == -1)
    {
        ctx.getLog() << TestLog::Message << "// ERROR: Failed to retrieve uniform location" << TestLog::EndMessage;
        ctx.fail("Failed to retrieve uniform location");
    }

    std::vector<GLint> data(4);

    ctx.beginSection("GL_INVALID_OPERATION is generated if the size of the uniform variable declared in the shader "
                     "does not match the size indicated by the ctx.glUniform command.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1iv(ivec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2iv(ivec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3iv(ivec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4iv(ivec4_f, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if ctx.glUniform{1234}iv is used to load a uniform variable of "
                     "type float, vec2, vec3, or vec4.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1iv(vec4_v, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2iv(vec4_v, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3iv(vec4_v, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4iv(vec4_v, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if ctx.glUniform{1234}iv is used to load a uniform variable of "
                     "type unsigned int, uvec2, uvec3 or uvec4.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1iv(uvec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2iv(uvec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3iv(uvec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4iv(uvec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glUseProgram(0);
}

void uniformiv_invalid_location(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    ctx.expectError(GL_NO_ERROR);

    std::vector<GLint> data(4);

    ctx.beginSection("GL_INVALID_OPERATION is generated if location is an invalid uniform location for the current "
                     "program object and location is not equal to -1.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1iv(-2, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2iv(-2, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3iv(-2, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4iv(-2, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1iv(-1, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform2iv(-1, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform3iv(-1, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform4iv(-1, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.glUseProgram(0);
}

void uniformiv_invalid_count(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    GLint ivec4_f = ctx.glGetUniformLocation(program.getProgram(), "ivec4_f"); // ivec4
    ctx.expectError(GL_NO_ERROR);

    if (ivec4_f == -1)
    {
        ctx.getLog() << TestLog::Message << "// ERROR: Failed to retrieve uniform location" << TestLog::EndMessage;
        ctx.fail("Failed to retrieve uniform location");
    }

    std::vector<GLint> data(8);

    ctx.beginSection("GL_INVALID_OPERATION is generated if count is greater than 1 and the indicated uniform variable "
                     "is not an array variable.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1iv(ivec4_f, 2, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2iv(ivec4_f, 2, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3iv(ivec4_f, 2, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4iv(ivec4_f, 2, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glUseProgram(0);
}

// ctx.glUniform{1234}ui

void uniformui_invalid_program(NegativeTestContext &ctx)
{
    ctx.beginSection("GL_INVALID_OPERATION is generated if there is no current program object.");
    ctx.glUseProgram(0);
    ctx.glUniform1ui(-1, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2ui(-1, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3ui(-1, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4ui(-1, 0, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();
}

void uniformui_incompatible_type(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    GLint vec4_v    = ctx.glGetUniformLocation(program.getProgram(), "vec4_v");    // vec4
    GLint ivec4_f   = ctx.glGetUniformLocation(program.getProgram(), "ivec4_f");   // ivec4
    GLint uvec4_f   = ctx.glGetUniformLocation(program.getProgram(), "uvec4_f");   // uvec4
    GLint sampler_f = ctx.glGetUniformLocation(program.getProgram(), "sampler_f"); // sampler2D
    ctx.expectError(GL_NO_ERROR);

    if (vec4_v == -1 || ivec4_f == -1 || uvec4_f == -1 || sampler_f == -1)
    {
        ctx.getLog() << TestLog::Message << "// ERROR: Failed to retrieve uniform location" << TestLog::EndMessage;
        ctx.fail("Failed to retrieve uniform location");
    }

    ctx.beginSection("GL_INVALID_OPERATION is generated if the size of the uniform variable declared in the shader "
                     "does not match the size indicated by the ctx.glUniform command.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1ui(uvec4_f, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2ui(uvec4_f, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3ui(uvec4_f, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4ui(uvec4_f, 0, 0, 0, 0);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if ctx.glUniform{1234}i is used to load a uniform variable of "
                     "type int, ivec2, ivec3, ivec4, or an array of these.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1ui(ivec4_f, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2ui(ivec4_f, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3ui(ivec4_f, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4ui(ivec4_f, 0, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if ctx.glUniform{1234}i is used to load a uniform variable of "
                     "type float, vec2, vec3, or vec4.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1ui(vec4_v, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2ui(vec4_v, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3ui(vec4_v, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4ui(vec4_v, 0, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if a sampler is loaded using a command other than "
                     "ctx.glUniform1i and ctx.glUniform1iv.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1ui(sampler_f, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glUseProgram(0);
}

void uniformui_invalid_location(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    ctx.expectError(GL_NO_ERROR);

    ctx.beginSection("GL_INVALID_OPERATION is generated if location is an invalid uniform location for the current "
                     "program object and location is not equal to -1.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1i(-2, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2i(-2, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3i(-2, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4i(-2, 0, 0, 0, 0);
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1i(-1, 0);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform2i(-1, 0, 0);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform3i(-1, 0, 0, 0);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform4i(-1, 0, 0, 0, 0);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.glUseProgram(0);
}

// ctx.glUniform{1234}uiv

void uniformuiv_invalid_program(NegativeTestContext &ctx)
{
    std::vector<GLuint> data(4);

    ctx.beginSection("GL_INVALID_OPERATION is generated if there is no current program object.");
    ctx.glUseProgram(0);
    ctx.glUniform1uiv(-1, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2uiv(-1, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3uiv(-1, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4uiv(-1, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();
}

void uniformuiv_incompatible_type(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    GLint vec4_v    = ctx.glGetUniformLocation(program.getProgram(), "vec4_v");    // vec4
    GLint ivec4_f   = ctx.glGetUniformLocation(program.getProgram(), "ivec4_f");   // ivec4
    GLint uvec4_f   = ctx.glGetUniformLocation(program.getProgram(), "uvec4_f");   // uvec4
    GLint sampler_f = ctx.glGetUniformLocation(program.getProgram(), "sampler_f"); // sampler2D
    ctx.expectError(GL_NO_ERROR);

    if (vec4_v == -1 || ivec4_f == -1 || uvec4_f == -1 || sampler_f == -1)
    {
        ctx.getLog() << TestLog::Message << "// ERROR: Failed to retrieve uniform location" << TestLog::EndMessage;
        ctx.fail("Failed to retrieve uniform location");
    }

    std::vector<GLuint> data(4);

    ctx.beginSection("GL_INVALID_OPERATION is generated if the size of the uniform variable declared in the shader "
                     "does not match the size indicated by the ctx.glUniform command.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1uiv(uvec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2uiv(uvec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3uiv(uvec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4uiv(uvec4_f, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if ctx.glUniform{1234}uiv is used to load a uniform variable "
                     "of type float, vec2, vec3, or vec4.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1uiv(vec4_v, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2uiv(vec4_v, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3uiv(vec4_v, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4uiv(vec4_v, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if ctx.glUniform{1234}uiv is used to load a uniform variable "
                     "of type int, ivec2, ivec3 or ivec4.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1uiv(ivec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2uiv(ivec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3uiv(ivec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4uiv(ivec4_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if a sampler is loaded using a command other than "
                     "ctx.glUniform1i and ctx.glUniform1iv.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1uiv(sampler_f, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glUseProgram(0);
}

void uniformuiv_invalid_location(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    ctx.expectError(GL_NO_ERROR);

    std::vector<GLuint> data(4);

    ctx.beginSection("GL_INVALID_OPERATION is generated if location is an invalid uniform location for the current "
                     "program object and location is not equal to -1.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1uiv(-2, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2uiv(-2, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3uiv(-2, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4uiv(-2, 1, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1uiv(-1, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform2uiv(-1, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform3uiv(-1, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniform4uiv(-1, 1, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.glUseProgram(0);
}

void uniformuiv_invalid_count(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    int uvec4_f = ctx.glGetUniformLocation(program.getProgram(), "uvec4_f"); // uvec4
    ctx.expectError(GL_NO_ERROR);

    if (uvec4_f == -1)
    {
        ctx.getLog() << TestLog::Message << "// ERROR: Failed to retrieve uniform location" << TestLog::EndMessage;
        ctx.fail("Failed to retrieve uniform location");
    }

    std::vector<GLuint> data(8);

    ctx.beginSection("GL_INVALID_OPERATION is generated if count is greater than 1 and the indicated uniform variable "
                     "is not an array variable.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniform1uiv(uvec4_f, 2, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform2uiv(uvec4_f, 2, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform3uiv(uvec4_f, 2, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniform4uiv(uvec4_f, 2, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glUseProgram(0);
}

// ctx.glUniformMatrix*fv

void uniform_matrixfv_invalid_program(NegativeTestContext &ctx)
{
    std::vector<GLfloat> data(16);

    ctx.beginSection("GL_INVALID_OPERATION is generated if there is no current program object.");
    ctx.glUseProgram(0);
    ctx.glUniformMatrix2fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glUniformMatrix2x3fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3x2fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix2x4fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4x2fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3x4fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4x3fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();
}

void uniform_matrixfv_incompatible_type(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    GLint mat4_v    = ctx.glGetUniformLocation(program.getProgram(), "mat4_v");    // mat4
    GLint sampler_f = ctx.glGetUniformLocation(program.getProgram(), "sampler_f"); // sampler2D
    ctx.expectError(GL_NO_ERROR);

    if (mat4_v == -1 || sampler_f == -1)
    {
        ctx.getLog() << TestLog::Message << "// ERROR: Failed to retrieve uniform location" << TestLog::EndMessage;
        ctx.fail("Failed to retrieve uniform location");
    }

    std::vector<GLfloat> data(16);

    ctx.beginSection("GL_INVALID_OPERATION is generated if the size of the uniform variable declared in the shader "
                     "does not match the size indicated by the ctx.glUniform command.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniformMatrix2fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_NO_ERROR);

    ctx.glUniformMatrix2x3fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3x2fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix2x4fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4x2fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3x4fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4x3fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if a sampler is loaded using a command other than "
                     "ctx.glUniform1i and ctx.glUniform1iv.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniformMatrix2fv(sampler_f, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3fv(sampler_f, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4fv(sampler_f, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glUniformMatrix2x3fv(sampler_f, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3x2fv(sampler_f, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix2x4fv(sampler_f, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4x2fv(sampler_f, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3x4fv(sampler_f, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4x3fv(sampler_f, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glUseProgram(0);
}

void uniform_matrixfv_invalid_location(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    ctx.expectError(GL_NO_ERROR);

    std::vector<GLfloat> data(16);

    ctx.beginSection("GL_INVALID_OPERATION is generated if location is an invalid uniform location for the current "
                     "program object and location is not equal to -1.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniformMatrix2fv(-2, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3fv(-2, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4fv(-2, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glUniformMatrix2x3fv(-2, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3x2fv(-2, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix2x4fv(-2, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4x2fv(-2, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3x4fv(-2, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4x3fv(-2, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glUseProgram(program.getProgram());
    ctx.glUniformMatrix2fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniformMatrix3fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniformMatrix4fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_NO_ERROR);

    ctx.glUniformMatrix2x3fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniformMatrix3x2fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniformMatrix2x4fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniformMatrix4x2fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniformMatrix3x4fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.glUniformMatrix4x3fv(-1, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.glUseProgram(0);
}

void uniform_matrixfv_invalid_count(NegativeTestContext &ctx)
{
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(uniformTestVertSource, uniformTestFragSource));

    ctx.glUseProgram(program.getProgram());
    GLint mat4_v = ctx.glGetUniformLocation(program.getProgram(), "mat4_v"); // mat4
    ctx.expectError(GL_NO_ERROR);

    if (mat4_v == -1)
    {
        ctx.getLog() << TestLog::Message << "// ERROR: Failed to retrieve uniform location" << TestLog::EndMessage;
        ctx.fail("Failed to retrieve uniform location");
    }

    std::vector<GLfloat> data(32);

    ctx.beginSection("GL_INVALID_OPERATION is generated if count is greater than 1 and the indicated uniform variable "
                     "is not an array variable.");
    ctx.glUseProgram(program.getProgram());
    ctx.glUniformMatrix2fv(mat4_v, 2, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3fv(mat4_v, 2, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4fv(mat4_v, 2, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glUniformMatrix2x3fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3x2fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix2x4fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4x2fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix3x4fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUniformMatrix4x3fv(mat4_v, 1, GL_FALSE, &data[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glUseProgram(0);
}

// Transform feedback
void gen_transform_feedbacks(NegativeTestContext &ctx)
{
    ctx.beginSection("GL_INVALID_VALUE is generated if n is negative.");
    GLuint id = 0;
    ctx.glGenTransformFeedbacks(-1, &id);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();
}

void bind_transform_feedback(NegativeTestContext &ctx)
{
    GLuint tfID[2];
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
    uint32_t buf          = 0x1234;
    const char *tfVarying = "gl_Position";

    ctx.glGenBuffers(1, &buf);
    ctx.glGenTransformFeedbacks(2, tfID);

    ctx.beginSection("GL_INVALID_ENUM is generated if target is not GL_TRANSFORM_FEEDBACK.");
    ctx.glBindTransformFeedback(-1, tfID[0]);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if the transform feedback operation is active on the currently "
                     "bound transform feedback object, and is not paused.");
    ctx.glUseProgram(program.getProgram());
    ctx.glTransformFeedbackVaryings(program.getProgram(), 1, &tfVarying, GL_INTERLEAVED_ATTRIBS);
    ctx.glLinkProgram(program.getProgram());
    ctx.glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfID[0]);
    ctx.glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buf);
    ctx.glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 32, DE_NULL, GL_DYNAMIC_DRAW);
    ctx.glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf);
    ctx.glBeginTransformFeedback(GL_TRIANGLES);
    ctx.expectError(GL_NO_ERROR);

    ctx.glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfID[1]);
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glEndTransformFeedback();
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.glUseProgram(0);
    ctx.glDeleteBuffers(1, &buf);
    ctx.glDeleteTransformFeedbacks(2, tfID);
    ctx.expectError(GL_NO_ERROR);

    ctx.beginSection("GL_INVALID_OPERATION is generated if id has been deleted with glDeleteTransformFeedback().");
    ctx.glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfID[0]);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection(
        "GL_INVALID_OPERATION is generated if id is not 0 or a value returned from glGenTransformFeedbacks().");
    ctx.glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, -1);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();
}

void delete_transform_feedbacks(NegativeTestContext &ctx)
{
    GLuint id = 0;
    GLuint tfID[2];
    uint32_t buf          = 0x1234;
    const char *tfVarying = "gl_Position";
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));

    ctx.glGenBuffers(1, &buf);
    ctx.glGenTransformFeedbacks(1, &id);
    ctx.glGenTransformFeedbacks(2, tfID);

    ctx.beginSection("GL_INVALID_VALUE is generated if n is negative.");
    ctx.glDeleteTransformFeedbacks(-1, &id);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if the transform feedback operation for any object named by "
                     "ids is currently active.");
    ctx.glUseProgram(program.getProgram());
    ctx.glTransformFeedbackVaryings(program.getProgram(), 1, &tfVarying, GL_INTERLEAVED_ATTRIBS);
    ctx.glLinkProgram(program.getProgram());
    ctx.glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfID[0]);
    ctx.glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buf);
    ctx.glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 32, DE_NULL, GL_DYNAMIC_DRAW);
    ctx.glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf);
    ctx.glBeginTransformFeedback(GL_TRIANGLES);
    ctx.expectError(GL_NO_ERROR);

    ctx.glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfID[1]);
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glDeleteTransformFeedbacks(2, tfID);
    ctx.expectError(GL_INVALID_OPERATION);

    ctx.glEndTransformFeedback();
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.glDeleteTransformFeedbacks(1, &id);
    ctx.glDeleteTransformFeedbacks(2, tfID);
    ctx.glDeleteBuffers(1, &buf);
}

void begin_transform_feedback(NegativeTestContext &ctx)
{
    GLuint tfID[2];
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
    uint32_t buf          = 0x1234;
    const char *tfVarying = "gl_Position";

    ctx.glGenBuffers(1, &buf);
    ctx.glGenTransformFeedbacks(2, tfID);

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

    ctx.beginSection(
        "GL_INVALID_ENUM is generated if primitiveMode is not one of GL_POINTS, GL_LINES, or GL_TRIANGLES.");
    ctx.glBeginTransformFeedback(-1);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if transform feedback is already active.");
    ctx.glBeginTransformFeedback(GL_TRIANGLES);
    ctx.expectError(GL_NO_ERROR);
    ctx.glBeginTransformFeedback(GL_POINTS);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if any binding point used in transform feedback mode does not "
                     "have a buffer object bound.");
    ctx.glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
    ctx.glBeginTransformFeedback(GL_TRIANGLES);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf);
    ctx.endSection();

    ctx.beginSection(
        "GL_INVALID_OPERATION is generated if no binding points would be used because no program object is active.");
    ctx.glUseProgram(0);
    ctx.glBeginTransformFeedback(GL_TRIANGLES);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glUseProgram(program.getProgram());
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if no binding points would be used because the active program "
                     "object has specified no varying variables to record.");
    ctx.glTransformFeedbackVaryings(program.getProgram(), 0, 0, GL_INTERLEAVED_ATTRIBS);
    ctx.glBeginTransformFeedback(GL_TRIANGLES);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glEndTransformFeedback();
    ctx.glDeleteBuffers(1, &buf);
    ctx.glDeleteTransformFeedbacks(2, tfID);
    ctx.expectError(GL_NO_ERROR);
}

void pause_transform_feedback(NegativeTestContext &ctx)
{
    GLuint tfID[2];
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
    uint32_t buf          = 0x1234;
    const char *tfVarying = "gl_Position";

    ctx.glGenBuffers(1, &buf);
    ctx.glGenTransformFeedbacks(2, tfID);

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

    ctx.beginSection("GL_INVALID_OPERATION is generated if the currently bound transform feedback object is not active "
                     "or is paused.");
    ctx.glPauseTransformFeedback();
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glBeginTransformFeedback(GL_TRIANGLES);
    ctx.glPauseTransformFeedback();
    ctx.expectError(GL_NO_ERROR);
    ctx.glPauseTransformFeedback();
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.endSection();

    ctx.glEndTransformFeedback();
    ctx.glDeleteBuffers(1, &buf);
    ctx.glDeleteTransformFeedbacks(2, tfID);
    ctx.expectError(GL_NO_ERROR);
}

void resume_transform_feedback(NegativeTestContext &ctx)
{
    GLuint tfID[2];
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
    uint32_t buf          = 0x1234;
    const char *tfVarying = "gl_Position";

    ctx.glGenBuffers(1, &buf);
    ctx.glGenTransformFeedbacks(2, tfID);

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

    ctx.beginSection("GL_INVALID_OPERATION is generated if the currently bound transform feedback object is not active "
                     "or is not paused.");
    ctx.glResumeTransformFeedback();
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glBeginTransformFeedback(GL_TRIANGLES);
    ctx.glResumeTransformFeedback();
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glPauseTransformFeedback();
    ctx.glResumeTransformFeedback();
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.glEndTransformFeedback();
    ctx.glDeleteBuffers(1, &buf);
    ctx.glDeleteTransformFeedbacks(2, tfID);
    ctx.expectError(GL_NO_ERROR);
}

void end_transform_feedback(NegativeTestContext &ctx)
{
    GLuint tfID = 0;
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
    uint32_t buf          = 0x1234;
    const char *tfVarying = "gl_Position";

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

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

    ctx.beginSection("GL_INVALID_OPERATION is generated if transform feedback is not active.");
    ctx.glEndTransformFeedback();
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glBeginTransformFeedback(GL_TRIANGLES);
    ctx.glEndTransformFeedback();
    ctx.expectError(GL_NO_ERROR);
    ctx.endSection();

    ctx.glDeleteBuffers(1, &buf);
    ctx.glDeleteTransformFeedbacks(1, &tfID);
    ctx.expectError(GL_NO_ERROR);
}

void get_transform_feedback_varying(NegativeTestContext &ctx)
{
    GLuint tfID = 0;
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
    glu::ShaderProgram programInvalid(ctx.getRenderContext(), glu::makeVtxFragSources(vertexShaderSource, ""));
    const char *tfVarying            = "gl_Position";
    int maxTransformFeedbackVaryings = 0;

    GLsizei length;
    GLsizei size;
    GLenum type;
    char name[32];

    const GLuint notAProgram = ctx.glCreateProgram();
    ctx.glDeleteProgram(notAProgram);

    ctx.glGenTransformFeedbacks(1, &tfID);

    ctx.glTransformFeedbackVaryings(program.getProgram(), 1, &tfVarying, GL_INTERLEAVED_ATTRIBS);
    ctx.expectError(GL_NO_ERROR);
    ctx.glLinkProgram(program.getProgram());
    ctx.expectError(GL_NO_ERROR);

    ctx.glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfID);
    ctx.expectError(GL_NO_ERROR);

    ctx.beginSection("GL_INVALID_VALUE is generated if program is not the name of a program object.");
    ctx.glGetTransformFeedbackVarying(notAProgram, 0, 32, &length, &size, &type, &name[0]);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection(
        "GL_INVALID_VALUE is generated if index is greater or equal to the value of GL_TRANSFORM_FEEDBACK_VARYINGS.");
    ctx.glGetProgramiv(program.getProgram(), GL_TRANSFORM_FEEDBACK_VARYINGS, &maxTransformFeedbackVaryings);
    ctx.glGetTransformFeedbackVarying(program.getProgram(), maxTransformFeedbackVaryings, 32, &length, &size, &type,
                                      &name[0]);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION or GL_INVALID_VALUE is generated program has not been linked.");
    ctx.glGetTransformFeedbackVarying(programInvalid.getProgram(), 0, 32, &length, &size, &type, &name[0]);
    ctx.expectError(GL_INVALID_OPERATION, GL_INVALID_VALUE);
    ctx.endSection();

    ctx.glDeleteTransformFeedbacks(1, &tfID);
    ctx.expectError(GL_NO_ERROR);
}

void transform_feedback_varyings(NegativeTestContext &ctx)
{
    GLuint tfID   = 0;
    GLuint shader = ctx.glCreateShader(GL_VERTEX_SHADER);
    glu::ShaderProgram program(ctx.getRenderContext(),
                               glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
    const char *tfVarying                     = "gl_Position";
    GLint maxTransformFeedbackSeparateAttribs = 0;

    const GLuint notAProgram = ctx.glCreateProgram();
    ctx.glDeleteProgram(notAProgram);

    ctx.glGenTransformFeedbacks(1, &tfID);
    ctx.expectError(GL_NO_ERROR);

    ctx.beginSection("GL_INVALID_VALUE is generated if program is not the name of a program object.");
    ctx.glTransformFeedbackVaryings(notAProgram, 1, &tfVarying, GL_INTERLEAVED_ATTRIBS);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_OPERATION is generated if program is the name of a shader object.");
    ctx.glTransformFeedbackVaryings(shader, 1, &tfVarying, GL_INTERLEAVED_ATTRIBS);
    ctx.expectError(GL_INVALID_OPERATION);
    ctx.glDeleteShader(shader);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_VALUE is generated if count is negative.");
    ctx.glTransformFeedbackVaryings(program.getProgram(), -1, &tfVarying, GL_INTERLEAVED_ATTRIBS);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_ENUM is generated if bufferMode is not SEPARATE_ATTRIBS or INTERLEAVED_ATTRIBS.");
    ctx.glTransformFeedbackVaryings(program.getProgram(), 1, &tfVarying, 0);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.beginSection("GL_INVALID_VALUE is generated if bufferMode is GL_SEPARATE_ATTRIBS and count is greater than "
                     "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS.");
    ctx.glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &maxTransformFeedbackSeparateAttribs);
    ctx.glTransformFeedbackVaryings(program.getProgram(), maxTransformFeedbackSeparateAttribs + 1, &tfVarying,
                                    GL_SEPARATE_ATTRIBS);
    ctx.expectError(GL_INVALID_VALUE);
    ctx.endSection();

    ctx.glDeleteTransformFeedbacks(1, &tfID);
    ctx.expectError(GL_NO_ERROR);
}

void link_compute_shader(NegativeTestContext &ctx)
{
    const char *computeShaderSource = "#version 320 es\n"
                                      "void main (void)\n"
                                      "{\n"
                                      "}\n\0";
    {
        const GLenum shaderTypes[] = {GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, GL_TESS_CONTROL_SHADER,
                                      GL_TESS_EVALUATION_SHADER};

        ctx.beginSection("Compute Shader linked with shader of other kind.");
        for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(shaderTypes); ndx++)
        {
            GLint linkStatus              = -1;
            GLuint program                = ctx.glCreateProgram();
            GLuint computeShader          = ctx.glCreateShader(GL_COMPUTE_SHADER);
            GLuint otherShader            = ctx.glCreateShader(shaderTypes[ndx]);
            const char *otherShaderSource = (shaderTypes[ndx] != GL_GEOMETRY_SHADER) ? computeShaderSource :
                                                                                       "#version 320 es\n"
                                                                                       "layout(max_vertices = 3) out;\n"
                                                                                       "void main(void){}\n\0";

            ctx.glShaderSource(computeShader, 1, &computeShaderSource, DE_NULL);
            ctx.glShaderSource(otherShader, 1, &otherShaderSource, DE_NULL);
            ctx.glCompileShader(computeShader);
            ctx.glCompileShader(otherShader);
            ctx.glAttachShader(program, computeShader);
            ctx.glAttachShader(program, otherShader);
            ctx.glLinkProgram(program);
            ctx.glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
            ctx.glDeleteShader(otherShader);
            ctx.glDeleteShader(computeShader);
            ctx.glDeleteProgram(program);
            if (linkStatus != GL_FALSE)
                ctx.fail("Program should not have linked");
        }
        ctx.endSection();
    }
    {
        const char *computeShaderSource310 = "#version 310 es\n"
                                             "void main (void)\n"
                                             "{\n"
                                             "}\n\0";
        GLint linkStatus                   = -1;
        GLuint program                     = ctx.glCreateProgram();
        GLuint computeShader               = ctx.glCreateShader(GL_COMPUTE_SHADER);
        GLuint computeShader310            = ctx.glCreateShader(GL_FRAGMENT_SHADER);

        ctx.glShaderSource(computeShader, 1, &computeShaderSource, DE_NULL);
        ctx.glShaderSource(computeShader310, 1, &computeShaderSource310, DE_NULL);
        ctx.beginSection("Compute Shader should not be linked with shaders of different version.");
        ctx.glCompileShader(computeShader);
        ctx.glCompileShader(computeShader310);
        ctx.glAttachShader(program, computeShader);
        ctx.glAttachShader(program, computeShader310);
        ctx.glLinkProgram(program);
        ctx.glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
        ctx.glDeleteShader(computeShader310);
        ctx.glDeleteShader(computeShader);
        ctx.glDeleteProgram(program);
        if (linkStatus != GL_FALSE)
            ctx.fail("Program should not have linked");
        ctx.endSection();
    }
}

void compile_compute_shader_helper(NegativeTestContext &ctx, const char *const *computeShaderSource,
                                   GLint *compileStatus)
{
    GLuint shader = ctx.glCreateShader(GL_COMPUTE_SHADER);

    *compileStatus = -1;
    ctx.glShaderSource(shader, 1, computeShaderSource, DE_NULL);
    ctx.glCompileShader(shader);
    ctx.glGetShaderiv(shader, GL_COMPILE_STATUS, compileStatus);
    ctx.glDeleteShader(shader);
}

void compile_compute_shader(NegativeTestContext &ctx)
{
    GLint compileStatus;
    ctx.beginSection("Compile Computer Shader");

    {
        const char *const computeShaderSource = "#version 300 es\n"
                                                "void main (void)\n"
                                                "{\n"
                                                "}\n";

        compile_compute_shader_helper(ctx, &computeShaderSource, &compileStatus);
        if (compileStatus != GL_FALSE)
            ctx.fail("Compute Shader should not have compiled with #version 300 es.");
    }
    {
        const char *const computeShaderSource = "#version 310 es\n"
                                                "buffer SSBO { vec4 data }"
                                                "void main (void)\n"
                                                "{\n"
                                                "}\n";

        compile_compute_shader_helper(ctx, &computeShaderSource, &compileStatus);
        if (compileStatus != GL_FALSE)
            ctx.fail("Compute Shader should not have compiled: incorrect SSBO syntax.");
    }
    {
        const char *const computeShaderSource = "#version 310 es\n"
                                                "buffer SSBO { vec4 data;};"
                                                "uniform mat4 data;"
                                                "void main (void)\n"
                                                "{\n"
                                                "}\n";

        compile_compute_shader_helper(ctx, &computeShaderSource, &compileStatus);
        if (compileStatus != GL_FALSE)
            ctx.fail("Compute Shader should not have compiled: buffer variable redefinition.");
    }
    {
        const char *const computeShaderSource = "#version 310 es\n"
                                                "buffer SSBO { vec4 data[]; vec4 moreData;};"
                                                "void main (void)\n"
                                                "{\n"
                                                "}\n";

        compile_compute_shader_helper(ctx, &computeShaderSource, &compileStatus);
        if (compileStatus != GL_FALSE)
            ctx.fail("Compute Shader should not have compiled: unspecified length buffer member not at the end.");
    }
    {
        const char *const computeShaderSource = "#version 310 es\n"
                                                "in vec4 data;"
                                                "void main (void)\n"
                                                "{\n"
                                                "}\n";

        compile_compute_shader_helper(ctx, &computeShaderSource, &compileStatus);
        if (compileStatus != GL_FALSE)
            ctx.fail("Compute Shader should not have compiled: input qualifier used.");
    }
    {
        const char *const computeShaderSource = "#version 310 es\n"
                                                "shared uint data = 0;";

        compile_compute_shader_helper(ctx, &computeShaderSource, &compileStatus);
        if (compileStatus != GL_FALSE)
            ctx.fail("Compute Shader should not have compiled: shared-qualified variable initialized.");
    }
    {
        const char *const computeShaderSource = "#version 310 es\n"
                                                "buffer SSBO { vec4 data; vec4 moreData[];} ssbo;"
                                                "void test (vec4 data[10]) {}"
                                                "void main (void)\n"
                                                "{\n"
                                                "    test(ssbo.moreData);"
                                                "}\n";

        compile_compute_shader_helper(ctx, &computeShaderSource, &compileStatus);
        if (compileStatus != GL_FALSE)
            ctx.fail("Compute Shader should not have compiled: unspecified length buffer member passed as argument to "
                     "function.");
    }
    {
        const char *const computeShaderSource = "#version 310 es\n"
                                                "buffer SSBO { vec4 data; vec4 moreData[];} ssbo;"
                                                "void main (void)\n"
                                                "{\n"
                                                "    vec4 var = ssbo.moreData[-1];"
                                                "}\n";

        compile_compute_shader_helper(ctx, &computeShaderSource, &compileStatus);
        if (compileStatus != GL_FALSE)
            ctx.fail("Compute Shader should not have compiled: unspecified length buffer member indexed with negative "
                     "constant expression.");
    }
    {
        const char *const computeShaderSource = "#version 310 es\n"
                                                "layout(binding=-1) buffer SSBO { vec4 data;};"
                                                "void main (void)\n"
                                                "{\n"
                                                "}\n";

        compile_compute_shader_helper(ctx, &computeShaderSource, &compileStatus);
        if (compileStatus != GL_FALSE)
            ctx.fail("Compute Shader should not have compiled: binding point less than zero.");
    }
    {
        const char *const computeShaderSource = "#version 310 es\n"
                                                "layout(binding=1) buffer;"
                                                "layout(binding=2) buffer SSBO { vec4 data;};"
                                                "void main (void)\n"
                                                "{\n"
                                                "}\n";

        compile_compute_shader_helper(ctx, &computeShaderSource, &compileStatus);
        if (compileStatus != GL_FALSE)
            ctx.fail("Compute Shader should not have compiled: binding point specified for global scope.");
    }
    {
        const char *const computeShaderSource = "#version 310 es\n"
                                                "buffer SSBO {"
                                                "    layout(binding=1) vec4 data;"
                                                "    layout(binding=2) vec4 moreData[];"
                                                "} ssbo;"
                                                "void main (void)\n"
                                                "{\n"
                                                "}\n";

        compile_compute_shader_helper(ctx, &computeShaderSource, &compileStatus);
        if (compileStatus != GL_FALSE)
            ctx.fail("Compute Shader should not have compiled: binding point specified for block member declarations.");
    }
    {
        const char *const computeShaderSource = "#version 310 es\n"
                                                "readonly buffer SSBO {vec4 data;} ssbo;"
                                                "void main (void)\n"
                                                "{\n"
                                                "ssbo.data = vec4(1, 1, 1, 1);"
                                                "}\n";

        compile_compute_shader_helper(ctx, &computeShaderSource, &compileStatus);
        if (compileStatus != GL_FALSE)
            ctx.fail("Compute Shader should not have compiled: writing to buffer block qualified with readonly.");
    }
    {
        const char *const computeShaderSource = "#version 310 es\n"
                                                "writeonly buffer SSBO {vec4 data;} ssbo;"
                                                "void main (void)\n"
                                                "{\n"
                                                "vec4 var = ssbo.data;"
                                                "}\n";

        compile_compute_shader_helper(ctx, &computeShaderSource, &compileStatus);
        if (compileStatus != GL_FALSE)
            ctx.fail("Compute Shader should not have compiled: reading from buffer block qualified with writeonly.");
    }

    ctx.endSection();
}

void srgb_decode_samplerparameteri(NegativeTestContext &ctx)
{
    if (!ctx.isExtensionSupported("GL_EXT_texture_sRGB_decode"))
        TCU_THROW(NotSupportedError, "GL_EXT_texture_sRGB_decode is not supported.");

    GLuint sampler    = 0x1234;
    GLint samplerMode = -1;

    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_ENUM is generated if pname is GL_TEXTURE_SRGB_DECODE_EXT and the value of param(s) is "
                     "not a valid value (one of DECODE_EXT, or SKIP_DECODE_EXT).");
    ctx.glSamplerParameteri(sampler, GL_TEXTURE_SRGB_DECODE_EXT, samplerMode);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.glDeleteSamplers(1, &sampler);
}

void srgb_decode_samplerparameterf(NegativeTestContext &ctx)
{
    if (!ctx.isExtensionSupported("GL_EXT_texture_sRGB_decode"))
        TCU_THROW(NotSupportedError, "GL_EXT_texture_sRGB_decode is not supported.");

    GLuint sampler      = 0x1234;
    GLfloat samplerMode = -1.0f;

    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_ENUM is generated if pname is GL_TEXTURE_SRGB_DECODE_EXT and the value of param(s) is "
                     "not a valid value (one of DECODE_EXT, or SKIP_DECODE_EXT).");
    ctx.glSamplerParameterf(sampler, GL_TEXTURE_SRGB_DECODE_EXT, samplerMode);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.glDeleteSamplers(1, &sampler);
}

void srgb_decode_samplerparameteriv(NegativeTestContext &ctx)
{
    if (!ctx.isExtensionSupported("GL_EXT_texture_sRGB_decode"))
        TCU_THROW(NotSupportedError, "GL_EXT_texture_sRGB_decode is not supported.");

    int params[1]  = {GL_LINEAR};
    GLuint sampler = 0x1234;

    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_ENUM is generated if pname is GL_TEXTURE_SRGB_DECODE_EXT and the value of param(s) is "
                     "not a valid value (one of DECODE_EXT, or SKIP_DECODE_EXT).");
    params[0] = -1;
    ctx.glSamplerParameteriv(sampler, GL_TEXTURE_SRGB_DECODE_EXT, &params[0]);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.glDeleteSamplers(1, &sampler);
}

void srgb_decode_samplerparameterfv(NegativeTestContext &ctx)
{
    if (!ctx.isExtensionSupported("GL_EXT_texture_sRGB_decode"))
        TCU_THROW(NotSupportedError, "GL_EXT_texture_sRGB_decode is not supported.");

    float params[1] = {GL_LINEAR};
    GLuint sampler  = 0x1234;

    ctx.glGenSamplers(1, &sampler);

    params[0] = -1.0f;
    ctx.beginSection("GL_INVALID_ENUM is generated if pname is GL_TEXTURE_SRGB_DECODE_EXT and the value of param(s) is "
                     "not a valid value (one of DECODE_EXT, or SKIP_DECODE_EXT).");
    ctx.glSamplerParameterfv(sampler, GL_TEXTURE_SRGB_DECODE_EXT, &params[0]);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.glDeleteSamplers(1, &sampler);
}

void srgb_decode_samplerparameterIiv(NegativeTestContext &ctx)
{
    if (!supportsES32orGL45(ctx))
        TCU_THROW(NotSupportedError, "glSamplerParameterIiv is not supported.");

    if (!ctx.isExtensionSupported("GL_EXT_texture_sRGB_decode"))
        TCU_THROW(NotSupportedError, "GL_EXT_texture_sRGB_decode is not supported.");

    GLint samplerMode[] = {GL_DEPTH_COMPONENT, GL_STENCIL_INDEX};
    GLuint sampler      = 0x1234;

    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_ENUM is generated if pname is GL_TEXTURE_SRGB_DECODE_EXT and the value of param(s) is "
                     "not a valid value (one of DECODE_EXT, or SKIP_DECODE_EXT).");
    samplerMode[0] = -1;
    samplerMode[1] = -1;
    ctx.glSamplerParameterIiv(sampler, GL_TEXTURE_SRGB_DECODE_EXT, samplerMode);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.glDeleteSamplers(1, &sampler);
}

void srgb_decode_samplerparameterIuiv(NegativeTestContext &ctx)
{
    if (!supportsES32orGL45(ctx))
        TCU_THROW(NotSupportedError, "glSamplerParameterIuiv is not supported.");

    if (!ctx.isExtensionSupported("GL_EXT_texture_sRGB_decode"))
        TCU_THROW(NotSupportedError, "GL_EXT_texture_sRGB_decode is not supported.");

    GLuint samplerMode[] = {GL_DEPTH_COMPONENT, GL_STENCIL_INDEX};
    GLuint sampler       = 0x1234;

    ctx.glGenSamplers(1, &sampler);

    ctx.beginSection("GL_INVALID_ENUM is generated if pname is GL_TEXTURE_SRGB_DECODE_EXT and the value of param(s) is "
                     "not a valid value (one of DECODE_EXT, or SKIP_DECODE_EXT).");
    samplerMode[0] = GL_DONT_CARE;
    samplerMode[1] = GL_DONT_CARE;
    ctx.glSamplerParameterIuiv(sampler, GL_TEXTURE_SRGB_DECODE_EXT, samplerMode);
    ctx.expectError(GL_INVALID_ENUM);
    ctx.endSection();

    ctx.glDeleteSamplers(1, &sampler);
}

std::vector<FunctionContainer> getNegativeShaderApiTestFunctions()
{
    FunctionContainer funcs[] = {
        {create_shader, "create_shader", "Invalid glCreateShader() usage"},
        {shader_source, "shader_source", "Invalid glShaderSource() usage"},
        {compile_shader, "compile_shader", "Invalid glCompileShader() usage"},
        {delete_shader, "delete_shader", "Invalid glDeleteShader() usage"},
        {shader_binary, "shader_binary", "Invalid glShaderBinary() usage"},
        {attach_shader, "attach_shader", "Invalid glAttachShader() usage"},
        {detach_shader, "detach_shader", "Invalid glDetachShader() usage"},
        {link_program, "link_program", "Invalid glLinkProgram() usage"},
        {use_program, "use_program", "Invalid glUseProgram() usage"},
        {delete_program, "delete_program", "Invalid glDeleteProgram() usage"},
        {validate_program, "validate_program", "Invalid glValidateProgram() usage"},
        {get_program_binary, "get_program_binary", "Invalid glGetProgramBinary() usage"},
        {program_binary, "program_binary", "Invalid glProgramBinary() usage"},
        {program_parameteri, "program_parameteri", "Invalid glProgramParameteri() usage"},
        {gen_samplers, "gen_samplers", "Invalid glGenSamplers() usage"},
        {bind_sampler, "bind_sampler", "Invalid glBindSampler() usage"},
        {delete_samplers, "delete_samplers", "Invalid glDeleteSamplers() usage"},
        {get_sampler_parameteriv, "get_sampler_parameteriv", "Invalid glGetSamplerParameteriv() usage"},
        {get_sampler_parameterfv, "get_sampler_parameterfv", "Invalid glGetSamplerParameterfv() usage"},
        {get_sampler_parameterIiv, "get_sampler_parameterIiv", "Invalid glGetSamplerParameterIiv() usage"},
        {get_sampler_parameterIuiv, "get_sampler_parameterIuiv", "Invalid glGetSamplerParameterIuiv() usage"},
        {sampler_parameteri, "sampler_parameteri", "Invalid glSamplerParameteri() usage"},
        {sampler_parameteriv, "sampler_parameteriv", "Invalid glSamplerParameteriv() usage"},
        {sampler_parameterf, "sampler_parameterf", "Invalid glSamplerParameterf() usage"},
        {sampler_parameterfv, "sampler_parameterfv", "Invalid glSamplerParameterfv() usage"},
        {sampler_parameterIiv, "sampler_parameterIiv", "Invalid glSamplerParameterIiv() usage"},
        {sampler_parameterIuiv, "sampler_parameterIuiv", "Invalid glSamplerParameterIuiv() usage"},
        {get_attrib_location, "get_attrib_location", "Invalid glGetAttribLocation() usage"},
        {get_uniform_location, "get_uniform_location", "Invalid glGetUniformLocation() usage"},
        {bind_attrib_location, "bind_attrib_location", "Invalid glBindAttribLocation() usage"},
        {uniform_block_binding, "uniform_block_binding", "Invalid glUniformBlockBinding() usage"},
        {uniformf_invalid_program, "uniformf_invalid_program", "Invalid glUniform{1234}f() usage"},
        {uniformf_incompatible_type, "uniformf_incompatible_type", "Invalid glUniform{1234}f() usage"},
        {uniformf_invalid_location, "uniformf_invalid_location", "Invalid glUniform{1234}f() usage"},
        {uniformfv_invalid_program, "uniformfv_invalid_program", "Invalid glUniform{1234}fv() usage"},
        {uniformfv_incompatible_type, "uniformfv_incompatible_type", "Invalid glUniform{1234}fv() usage"},
        {uniformfv_invalid_location, "uniformfv_invalid_location", "Invalid glUniform{1234}fv() usage"},
        {uniformfv_invalid_count, "uniformfv_invalid_count", "Invalid glUniform{1234}fv() usage"},
        {uniformi_invalid_program, "uniformi_invalid_program", "Invalid glUniform{1234}i() usage"},
        {uniformi_incompatible_type, "uniformi_incompatible_type", "Invalid glUniform{1234}i() usage"},
        {uniformi_invalid_location, "uniformi_invalid_location", "Invalid glUniform{1234}i() usage"},
        {uniformiv_invalid_program, "uniformiv_invalid_program", "Invalid glUniform{1234}iv() usage"},
        {uniformiv_incompatible_type, "uniformiv_incompatible_type", "Invalid glUniform{1234}iv() usage"},
        {uniformiv_invalid_location, "uniformiv_invalid_location", "Invalid glUniform{1234}iv() usage"},
        {uniformiv_invalid_count, "uniformiv_invalid_count", "Invalid glUniform{1234}iv() usage"},
        {uniformui_invalid_program, "uniformui_invalid_program", "Invalid glUniform{234}ui() usage"},
        {uniformui_incompatible_type, "uniformui_incompatible_type", "Invalid glUniform{1234}ui() usage"},
        {uniformui_invalid_location, "uniformui_invalid_location", "Invalid glUniform{1234}ui() usage"},
        {uniformuiv_invalid_program, "uniformuiv_invalid_program", "Invalid glUniform{234}uiv() usage"},
        {uniformuiv_incompatible_type, "uniformuiv_incompatible_type", "Invalid glUniform{1234}uiv() usage"},
        {uniformuiv_invalid_location, "uniformuiv_invalid_location", "Invalid glUniform{1234}uiv() usage"},
        {uniformuiv_invalid_count, "uniformuiv_invalid_count", "Invalid glUniform{1234}uiv() usage"},
        {uniform_matrixfv_invalid_program, "uniform_matrixfv_invalid_program",
         "Invalid glUniformMatrix{234}fv() usage"},
        {uniform_matrixfv_incompatible_type, "uniform_matrixfv_incompatible_type",
         "Invalid glUniformMatrix{234}fv() usage"},
        {uniform_matrixfv_invalid_location, "uniform_matrixfv_invalid_location",
         "Invalid glUniformMatrix{234}fv() usage"},
        {uniform_matrixfv_invalid_count, "uniform_matrixfv_invalid_count", "Invalid glUniformMatrix{234}fv() usage"},
        {gen_transform_feedbacks, "gen_transform_feedbacks", "Invalid glGenTransformFeedbacks() usage"},
        {bind_transform_feedback, "bind_transform_feedback", "Invalid glBindTransformFeedback() usage"},
        {delete_transform_feedbacks, "delete_transform_feedbacks", "Invalid glDeleteTransformFeedbacks() usage"},
        {begin_transform_feedback, "begin_transform_feedback", "Invalid glBeginTransformFeedback() usage"},
        {pause_transform_feedback, "pause_transform_feedback", "Invalid glPauseTransformFeedback() usage"},
        {resume_transform_feedback, "resume_transform_feedback", "Invalid glResumeTransformFeedback() usage"},
        {end_transform_feedback, "end_transform_feedback", "Invalid glEndTransformFeedback() usage"},
        {get_transform_feedback_varying, "get_transform_feedback_varying",
         "Invalid glGetTransformFeedbackVarying() usage"},
        {transform_feedback_varyings, "transform_feedback_varyings", "Invalid glTransformFeedbackVaryings() usage"},
        {compile_compute_shader, "compile_compute_shader", "Invalid Compute Shader compilation"},
        {link_compute_shader, "link_compute_shader", "Invalid Compute Shader linkage"},
        {srgb_decode_samplerparameteri, "srgb_decode_samplerparameteri", "Invalid glSamplerParameteri() usage srgb"},
        {srgb_decode_samplerparameterf, "srgb_decode_samplerparameterf", "Invalid glSamplerParameterf() usage srgb"},
        {srgb_decode_samplerparameteriv, "srgb_decode_samplerparameteriv", "Invalid glSamplerParameteriv() usage srgb"},
        {srgb_decode_samplerparameterfv, "srgb_decode_samplerparameterfv", "Invalid glSamplerParameterfv() usage srgb"},
        {srgb_decode_samplerparameterIiv, "srgb_decode_samplerparameterIiv",
         "Invalid glSamplerParameterIiv() usage srgb"},
        {srgb_decode_samplerparameterIuiv, "srgb_decode_samplerparameterIuiv",
         "Invalid glSamplerParameterIiuv() usage srgb"},
    };

    return std::vector<FunctionContainer>(DE_ARRAY_BEGIN(funcs), DE_ARRAY_END(funcs));
}

} // namespace NegativeTestShared
} // namespace Functional
} // namespace gles31
} // namespace deqp
