/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.0 Module
 * -------------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Rbo state query tests.
 *//*--------------------------------------------------------------------*/

#include "es3fShaderStateQueryTests.hpp"
#include "glsStateQueryUtil.hpp"
#include "es3fApiCase.hpp"
#include "gluRenderContext.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "deRandom.hpp"
#include "deMath.h"
#include "deString.h"

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

namespace deqp
{
namespace gles3
{
namespace Functional
{
namespace
{

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

static const char* brokenShader				=	"#version 300 es\n"
												"broken, this should not compile!\n"
												"\n\0";

// rounds x.1 to x+1
template <typename T>
T roundGLfloatToNearestIntegerUp (GLfloat val)
{
	return (T)(ceil(val));
}

// rounds x.9 to x
template <typename T>
T roundGLfloatToNearestIntegerDown (GLfloat val)
{
	return (T)(floor(val));
}

bool checkIntEquals (tcu::TestContext& testCtx, GLint got, GLint expected)
{
	using tcu::TestLog;

	if (got != expected)
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: Expected " << expected << "; got " << got << TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid value");
		return false;
	}
	return true;
}

void checkPointerEquals (tcu::TestContext& testCtx, const void* got, const void* expected)
{
	using tcu::TestLog;

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

void verifyShaderParam (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint shader, GLenum pname, GLenum reference)
{
	StateQueryMemoryWriteGuard<GLint> state;
	gl.glGetShaderiv(shader, pname, &state);

	if (state.verifyValidity(testCtx))
		checkIntEquals(testCtx, state, reference);
}

bool verifyProgramParam (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLenum pname, GLenum reference)
{
	StateQueryMemoryWriteGuard<GLint> state;
	gl.glGetProgramiv(program, pname, &state);

	return state.verifyValidity(testCtx) && checkIntEquals(testCtx, state, reference);
}

void verifyActiveUniformParam  (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLuint index, GLenum pname, GLenum reference)
{
	StateQueryMemoryWriteGuard<GLint> state;
	gl.glGetActiveUniformsiv(program, 1, &index, pname, &state);

	if (state.verifyValidity(testCtx))
		checkIntEquals(testCtx, state, reference);
}

void verifyActiveUniformBlockParam  (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLuint blockIndex, GLenum pname, GLenum reference)
{
	StateQueryMemoryWriteGuard<GLint> state;
	gl.glGetActiveUniformBlockiv(program, blockIndex, pname, &state);

	if (state.verifyValidity(testCtx))
		checkIntEquals(testCtx, state, reference);
}

void verifyCurrentVertexAttribf (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLfloat[4]> attribValue;
	gl.glGetVertexAttribfv(index, GL_CURRENT_VERTEX_ATTRIB, attribValue);

	attribValue.verifyValidity(testCtx);

	if (attribValue[0] != x || attribValue[1] != y || attribValue[2] != z || attribValue[3] != w)
	{
		testCtx.getLog() << TestLog::Message
			<< "// ERROR: Expected [" << x << "," << y << "," << z << "," << w << "];"
			<< "got [" << attribValue[0] << "," << attribValue[1] << "," << attribValue[2] << "," << attribValue[3] << "]"
			<< TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid attribute value");
	}
}

void verifyCurrentVertexAttribIi (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLint index, GLint x, GLint y, GLint z, GLint w)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLint[4]> attribValue;
	gl.glGetVertexAttribIiv(index, GL_CURRENT_VERTEX_ATTRIB, attribValue);

	attribValue.verifyValidity(testCtx);

	if (attribValue[0] != x || attribValue[1] != y || attribValue[2] != z || attribValue[3] != w)
	{
		testCtx.getLog() << TestLog::Message
			<< "// ERROR: Expected [" << x << "," << y << "," << z << "," << w << "];"
			<< "got [" << attribValue[0] << "," << attribValue[1] << "," << attribValue[2] << "," << attribValue[3] << "]"
			<< TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid attribute value");
	}
}

void verifyCurrentVertexAttribIui (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLint index, GLuint x, GLuint y, GLuint z, GLuint w)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLuint[4]> attribValue;
	gl.glGetVertexAttribIuiv(index, GL_CURRENT_VERTEX_ATTRIB, attribValue);

	attribValue.verifyValidity(testCtx);

	if (attribValue[0] != x || attribValue[1] != y || attribValue[2] != z || attribValue[3] != w)
	{
		testCtx.getLog() << TestLog::Message
			<< "// ERROR: Expected [" << x << "," << y << "," << z << "," << w << "];"
			<< "got [" << attribValue[0] << "," << attribValue[1] << "," << attribValue[2] << "," << attribValue[3] << "]"
			<< TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid attribute value");
	}
}

void verifyCurrentVertexAttribConversion (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLint[4]> attribValue;
	gl.glGetVertexAttribiv(index, GL_CURRENT_VERTEX_ATTRIB, attribValue);

	attribValue.verifyValidity(testCtx);

	const GLint referenceAsGLintMin[] =
	{
		roundGLfloatToNearestIntegerDown<GLint>(x),
		roundGLfloatToNearestIntegerDown<GLint>(y),
		roundGLfloatToNearestIntegerDown<GLint>(z),
		roundGLfloatToNearestIntegerDown<GLint>(w)
	};
	const GLint referenceAsGLintMax[] =
	{
		roundGLfloatToNearestIntegerUp<GLint>(x),
		roundGLfloatToNearestIntegerUp<GLint>(y),
		roundGLfloatToNearestIntegerUp<GLint>(z),
		roundGLfloatToNearestIntegerUp<GLint>(w)
	};

	if (attribValue[0] < referenceAsGLintMin[0] || attribValue[0] > referenceAsGLintMax[0] ||
		attribValue[1] < referenceAsGLintMin[1] || attribValue[1] > referenceAsGLintMax[1] ||
		attribValue[2] < referenceAsGLintMin[2] || attribValue[2] > referenceAsGLintMax[2] ||
		attribValue[3] < referenceAsGLintMin[3] || attribValue[3] > referenceAsGLintMax[3])
	{
		testCtx.getLog() << TestLog::Message
			<< "// ERROR: expected in range "
			<< "[" << referenceAsGLintMin[0] << " " << referenceAsGLintMax[0] << "], "
			<< "[" << referenceAsGLintMin[1] << " " << referenceAsGLintMax[1] << "], "
			<< "[" << referenceAsGLintMin[2] << " " << referenceAsGLintMax[2] << "], "
			<< "[" << referenceAsGLintMin[3] << " " << referenceAsGLintMax[3] << "]"
			<< "; got "
			<< attribValue[0] << ", "
			<< attribValue[1] << ", "
			<< attribValue[2] << ", "
			<< attribValue[3] << " "
			<< "; Input="
			<< x << "; "
			<< y << "; "
			<< z << "; "
			<< w << " " << TestLog::EndMessage;

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

void verifyVertexAttrib (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLint index, GLenum pname, GLenum reference)
{
	StateQueryMemoryWriteGuard<GLint> state;
	gl.glGetVertexAttribIiv(index, pname, &state);

	if (state.verifyValidity(testCtx))
		checkIntEquals(testCtx, state, reference);
}

void verifyUniformValue1f (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, float x)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLfloat[1]> state;
	gl.glGetUniformfv(program, location, state);

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

	if (state[0] != x)
	{
		testCtx.getLog() << TestLog::Message
		<< "// ERROR: expected ["
		<< x
		<< "]; got ["
		<< state[0]
		<< "]"
		<< TestLog::EndMessage;

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

void verifyUniformValue2f (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, float x, float y)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLfloat[2]> state;
	gl.glGetUniformfv(program, location, state);

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

	if (state[0] != x ||
		state[1] != y)
	{
		testCtx.getLog() << TestLog::Message
		<< "// ERROR: expected ["
		<< x << ", "
		<< y
		<< "]; got ["
		<< state[0] << ", "
		<< state[1]
		<< "]"
		<< TestLog::EndMessage;

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

void verifyUniformValue3f (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, float x, float y, float z)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLfloat[3]> state;
	gl.glGetUniformfv(program, location, state);

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

	if (state[0] != x ||
		state[1] != y ||
		state[2] != z)
	{
		testCtx.getLog() << TestLog::Message
		<< "// ERROR: expected ["
		<< x << ", "
		<< y << ", "
		<< z
		<< "]; got ["
		<< state[0] << ", "
		<< state[1] << ", "
		<< state[2]
		<< "]"
		<< TestLog::EndMessage;

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

void verifyUniformValue4f (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, float x, float y, float z, float w)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLfloat[4]> state;
	gl.glGetUniformfv(program, location, state);

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

	if (state[0] != x ||
		state[1] != y ||
		state[2] != z ||
		state[3] != w)
	{
		testCtx.getLog() << TestLog::Message
		<< "// ERROR: expected ["
		<< x << ", "
		<< y << ", "
		<< z << ", "
		<< w
		<< "]; got ["
		<< state[0] << ", "
		<< state[1] << ", "
		<< state[2] << ", "
		<< state[3]
		<< "]"
		<< TestLog::EndMessage;

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

void verifyUniformValue1i (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, GLint x)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLint[1]> state;
	gl.glGetUniformiv(program, location, state);

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

	if (state[0] != x)
	{
		testCtx.getLog() << TestLog::Message
		<< "// ERROR: expected ["
		<< x
		<< "]; got ["
		<< state[0]
		<< "]"
		<< TestLog::EndMessage;

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

void verifyUniformValue2i (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, GLint x, GLint y)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLint[2]> state;
	gl.glGetUniformiv(program, location, state);

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

	if (state[0] != x ||
		state[1] != y)
	{
		testCtx.getLog() << TestLog::Message
		<< "// ERROR: expected ["
		<< x << ", "
		<< y
		<< "]; got ["
		<< state[0] << ", "
		<< state[1]
		<< "]"
		<< TestLog::EndMessage;

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

void verifyUniformValue3i (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, GLint x, GLint y, GLint z)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLint[3]> state;
	gl.glGetUniformiv(program, location, state);

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

	if (state[0] != x ||
		state[1] != y ||
		state[2] != z)
	{
		testCtx.getLog() << TestLog::Message
		<< "// ERROR: expected ["
		<< x << ", "
		<< y << ", "
		<< z
		<< "]; got ["
		<< state[0] << ", "
		<< state[1] << ", "
		<< state[2]
		<< "]"
		<< TestLog::EndMessage;

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

void verifyUniformValue4i (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, GLint x, GLint y, GLint z, GLint w)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLint[4]> state;
	gl.glGetUniformiv(program, location, state);

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

	if (state[0] != x ||
		state[1] != y ||
		state[2] != z ||
		state[3] != w)
	{
		testCtx.getLog() << TestLog::Message
		<< "// ERROR: expected ["
		<< x << ", "
		<< y << ", "
		<< z << ", "
		<< w
		<< "]; got ["
		<< state[0] << ", "
		<< state[1] << ", "
		<< state[2] << ", "
		<< state[3]
		<< "]"
		<< TestLog::EndMessage;

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

void verifyUniformValue1ui (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, GLuint x)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLuint[1]> state;
	gl.glGetUniformuiv(program, location, state);

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

	if (state[0] != x)
	{
		testCtx.getLog() << TestLog::Message
		<< "// ERROR: expected ["
		<< x
		<< "]; got ["
		<< state[0]
		<< "]"
		<< TestLog::EndMessage;

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

void verifyUniformValue2ui (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, GLuint x, GLuint y)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLuint[2]> state;
	gl.glGetUniformuiv(program, location, state);

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

	if (state[0] != x ||
		state[1] != y)
	{
		testCtx.getLog() << TestLog::Message
		<< "// ERROR: expected ["
		<< x << ", "
		<< y
		<< "]; got ["
		<< state[0] << ", "
		<< state[1]
		<< "]"
		<< TestLog::EndMessage;

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

void verifyUniformValue3ui (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, GLuint x, GLuint y, GLuint z)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLuint[3]> state;
	gl.glGetUniformuiv(program, location, state);

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

	if (state[0] != x ||
		state[1] != y ||
		state[2] != z)
	{
		testCtx.getLog() << TestLog::Message
		<< "// ERROR: expected ["
		<< x << ", "
		<< y << ", "
		<< z
		<< "]; got ["
		<< state[0] << ", "
		<< state[1] << ", "
		<< state[2]
		<< "]"
		<< TestLog::EndMessage;

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

void verifyUniformValue4ui (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, GLuint x, GLuint y, GLuint z, GLuint w)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLuint[4]> state;
	gl.glGetUniformuiv(program, location, state);

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

	if (state[0] != x ||
		state[1] != y ||
		state[2] != z ||
		state[3] != w)
	{
		testCtx.getLog() << TestLog::Message
		<< "// ERROR: expected ["
		<< x << ", "
		<< y << ", "
		<< z << ", "
		<< w
		<< "]; got ["
		<< state[0] << ", "
		<< state[1] << ", "
		<< state[2] << ", "
		<< state[3]
		<< "]"
		<< TestLog::EndMessage;

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

template <int Count>
void verifyUniformValues (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, const GLfloat* values)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLfloat[Count]> state;
	gl.glGetUniformfv(program, location, state);

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

	for (int ndx = 0; ndx < Count; ++ndx)
	{
		if (values[ndx] != state[ndx])
		{
			testCtx.getLog() << TestLog::Message << "// ERROR: at index " << ndx << " expected " << values[ndx] << "; got " << state[ndx] << TestLog::EndMessage;

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

template <int N>
void verifyUniformMatrixValues (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLuint program, GLint location, const GLfloat* values, bool transpose)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLfloat[N*N]> state;
	gl.glGetUniformfv(program, location, state);

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

	for (int y = 0; y < N; ++y)
		for (int x = 0; x < N; ++x)
		{
			const int refIndex = y*N + x;
			const int stateIndex = transpose ? (x*N + y) : (y*N + x);

			if (values[refIndex] != state[stateIndex])
			{
				testCtx.getLog() << TestLog::Message << "// ERROR: at index [" << y << "][" << x << "] expected " << values[refIndex] << "; got " << state[stateIndex] << TestLog::EndMessage;

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

class ShaderTypeCase : public ApiCase
{
public:
	ShaderTypeCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		const GLenum shaderTypes[] = {GL_VERTEX_SHADER, GL_FRAGMENT_SHADER};
		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(shaderTypes); ++ndx)
		{
			const GLuint shader = glCreateShader(shaderTypes[ndx]);
			verifyShaderParam(m_testCtx, *this, shader, GL_SHADER_TYPE, shaderTypes[ndx]);
			glDeleteShader(shader);
		}
	}
};

class ShaderCompileStatusCase : public ApiCase
{
public:
	ShaderCompileStatusCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		verifyShaderParam(m_testCtx, *this, shaderVert, GL_COMPILE_STATUS, GL_FALSE);
		verifyShaderParam(m_testCtx, *this, shaderFrag, GL_COMPILE_STATUS, GL_FALSE);

		glShaderSource(shaderVert, 1, &commonTestVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &commonTestFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		verifyShaderParam(m_testCtx, *this, shaderVert, GL_COMPILE_STATUS, GL_TRUE);
		verifyShaderParam(m_testCtx, *this, shaderFrag, GL_COMPILE_STATUS, GL_TRUE);

		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		expectError(GL_NO_ERROR);
	}
};

class ShaderInfoLogCase : public ApiCase
{
public:
	ShaderInfoLogCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		using tcu::TestLog;

		// INFO_LOG_LENGTH is 0 by default and it includes null-terminator
		const GLuint shader = glCreateShader(GL_VERTEX_SHADER);
		verifyShaderParam(m_testCtx, *this, shader, GL_INFO_LOG_LENGTH, 0);

		glShaderSource(shader, 1, &brokenShader, DE_NULL);
		glCompileShader(shader);
		expectError(GL_NO_ERROR);

		// check the log length
		StateQueryMemoryWriteGuard<GLint> logLength;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
		if (!logLength.verifyValidity(m_testCtx))
		{
			glDeleteShader(shader);
			return;
		}
		if (logLength == 0)
		{
			glDeleteShader(shader);
			return;
		}

		// check normal case
		{
			char buffer[2048] = {'x'}; // non-zero initialization

			GLint written = 0; // written does not include null terminator
			glGetShaderInfoLog(shader, DE_LENGTH_OF_ARRAY(buffer), &written, buffer);

			// check lengths are consistent
			if (logLength <= DE_LENGTH_OF_ARRAY(buffer))
			{
				if (written != logLength-1)
				{
					m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected length " << logLength-1 << "; got " << written << TestLog::EndMessage;
					if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
						m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid log length");
				}
			}

			// check null-terminator, either at end of buffer or at buffer[written]
			const char* terminator = &buffer[DE_LENGTH_OF_ARRAY(buffer) - 1];
			if (logLength < DE_LENGTH_OF_ARRAY(buffer))
				terminator = &buffer[written];

			if (*terminator != '\0')
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected null terminator, got " << (int)*terminator << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid log terminator");
			}
		}

		// check with too small buffer
		{
			char buffer[2048] = {'x'}; // non-zero initialization

			// check string always ends with \0, even with small buffers
			GLint written = 0;
			glGetShaderInfoLog(shader, 1, &written, buffer);
			if (written != 0)
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected length 0; got " << written << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid log length");
			}
			if (buffer[0] != '\0')
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected null terminator, got " << (int)buffer[0] << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid log terminator");
			}
		}

		glDeleteShader(shader);
		expectError(GL_NO_ERROR);
	}
};

class ShaderSourceCase : public ApiCase
{
public:
	ShaderSourceCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		using tcu::TestLog;

		// SHADER_SOURCE_LENGTH does include 0-terminator
		const GLuint shader = glCreateShader(GL_VERTEX_SHADER);
		verifyShaderParam(m_testCtx, *this, shader, GL_SHADER_SOURCE_LENGTH, 0);

		// check the SHADER_SOURCE_LENGTH
		{
			glShaderSource(shader, 1, &brokenShader, DE_NULL);
			expectError(GL_NO_ERROR);

			StateQueryMemoryWriteGuard<GLint> sourceLength;
			glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &sourceLength);

			sourceLength.verifyValidity(m_testCtx);

			const GLint referenceLength = (GLint)std::string(brokenShader).length() + 1; // including the null terminator
			if (sourceLength != referenceLength)
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected length " << referenceLength	<< "; got " << sourceLength << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid source length");
			}
		}

		// check the concat source SHADER_SOURCE_LENGTH
		{
			const char* shaders[] = {brokenShader, brokenShader};
			glShaderSource(shader, 2, shaders, DE_NULL);
			expectError(GL_NO_ERROR);

			StateQueryMemoryWriteGuard<GLint> sourceLength;
			glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &sourceLength);

			sourceLength.verifyValidity(m_testCtx);

			const GLint referenceLength = 2 * (GLint)std::string(brokenShader).length() + 1; // including the null terminator
			if (sourceLength != referenceLength)
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected length " << referenceLength << "; got " << sourceLength << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid source length");
			}
		}

		// check the string length
		{
			char buffer[2048] = {'x'};
			DE_ASSERT(DE_LENGTH_OF_ARRAY(buffer) > 2 * (int)std::string(brokenShader).length());

			GLint written = 0; // not inluding null-terminator
			glGetShaderSource(shader, DE_LENGTH_OF_ARRAY(buffer), &written, buffer);

			const GLint referenceLength = 2 * (GLint)std::string(brokenShader).length();
			if (written != referenceLength)
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected write length " << referenceLength << "; got " << written << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid source length");
			}
			// check null pointer at
			else
			{
				if (buffer[referenceLength] != '\0')
				{
					m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected null terminator at " << referenceLength << TestLog::EndMessage;
					if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
						m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "did not get a null terminator");
				}
			}
		}

		// check with small buffer
		{
			char buffer[2048] = {'x'};

			GLint written = 0;
			glGetShaderSource(shader, 1, &written, buffer);

			if (written != 0)
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected write length 0; got " << written << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid source length");
			}
			if (buffer[0] != '\0')
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected null terminator; got=" << int(buffer[0]) << ", char=" << buffer[0] << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid terminator");
			}
		}

		glDeleteShader(shader);
		expectError(GL_NO_ERROR);
	}
};

class DeleteStatusCase : public ApiCase
{
public:
	DeleteStatusCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		glShaderSource(shaderVert, 1, &commonTestVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &commonTestFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		verifyShaderParam(m_testCtx, *this, shaderVert, GL_COMPILE_STATUS, GL_TRUE);
		verifyShaderParam(m_testCtx, *this, shaderFrag, GL_COMPILE_STATUS, GL_TRUE);

		GLuint shaderProg = glCreateProgram();
		glAttachShader(shaderProg, shaderVert);
		glAttachShader(shaderProg, shaderFrag);
		glLinkProgram(shaderProg);
		expectError(GL_NO_ERROR);

		verifyProgramParam	(m_testCtx, *this, shaderProg, GL_LINK_STATUS, GL_TRUE);

		verifyShaderParam	(m_testCtx, *this, shaderVert, GL_DELETE_STATUS, GL_FALSE);
		verifyShaderParam	(m_testCtx, *this, shaderFrag, GL_DELETE_STATUS, GL_FALSE);
		verifyProgramParam	(m_testCtx, *this, shaderProg, GL_DELETE_STATUS, GL_FALSE);
		expectError(GL_NO_ERROR);

		glUseProgram(shaderProg);

		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(shaderProg);
		expectError(GL_NO_ERROR);

		verifyShaderParam	(m_testCtx, *this, shaderVert, GL_DELETE_STATUS, GL_TRUE);
		verifyShaderParam	(m_testCtx, *this, shaderFrag, GL_DELETE_STATUS, GL_TRUE);
		verifyProgramParam	(m_testCtx, *this, shaderProg, GL_DELETE_STATUS, GL_TRUE);
		expectError(GL_NO_ERROR);

		glUseProgram(0);
		expectError(GL_NO_ERROR);
	}
};

class CurrentVertexAttribInitialCase : public ApiCase
{
public:
	CurrentVertexAttribInitialCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		using tcu::TestLog;

		int attribute_count = 16;
		glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &attribute_count);

		// initial

		for (int index = 0; index < attribute_count; ++index)
		{
			StateQueryMemoryWriteGuard<GLfloat[4]> attribValue;
			glGetVertexAttribfv(index, GL_CURRENT_VERTEX_ATTRIB, attribValue);
			attribValue.verifyValidity(m_testCtx);

			if (attribValue[0] != 0.0f || attribValue[1] != 0.0f || attribValue[2] != 0.0f || attribValue[3] != 1.0f)
			{
				m_testCtx.getLog() << TestLog::Message
					<< "// ERROR: Expected [0, 0, 0, 1];"
					<< "got [" << attribValue[0] << "," << attribValue[1] << "," << attribValue[2] << "," << attribValue[3] << "]"
					<< TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid attribute value");
			}
		}
	}
};

class CurrentVertexAttribFloatCase : public ApiCase
{
public:
	CurrentVertexAttribFloatCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		using tcu::TestLog;

		de::Random rnd(0xabcdef);

		int attribute_count = 16;
		glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &attribute_count);

		// test write float/read float

		for (int index = 0; index < attribute_count; ++index)
		{
			const GLfloat x = rnd.getFloat(-64000, 64000);
			const GLfloat y = rnd.getFloat(-64000, 64000);
			const GLfloat z = rnd.getFloat(-64000, 64000);
			const GLfloat w = rnd.getFloat(-64000, 64000);

			glVertexAttrib4f(index, x, y, z, w);
			verifyCurrentVertexAttribf(m_testCtx, *this, index, x, y, z, w);
		}
		for (int index = 0; index < attribute_count; ++index)
		{
			const GLfloat x = rnd.getFloat(-64000, 64000);
			const GLfloat y = rnd.getFloat(-64000, 64000);
			const GLfloat z = rnd.getFloat(-64000, 64000);
			const GLfloat w = 1.0f;

			glVertexAttrib3f(index, x, y, z);
			verifyCurrentVertexAttribf(m_testCtx, *this, index, x, y, z, w);
		}
		for (int index = 0; index < attribute_count; ++index)
		{
			const GLfloat x = rnd.getFloat(-64000, 64000);
			const GLfloat y = rnd.getFloat(-64000, 64000);
			const GLfloat z = 0.0f;
			const GLfloat w = 1.0f;

			glVertexAttrib2f(index, x, y);
			verifyCurrentVertexAttribf(m_testCtx, *this, index, x, y, z, w);
		}
		for (int index = 0; index < attribute_count; ++index)
		{
			const GLfloat x = rnd.getFloat(-64000, 64000);
			const GLfloat y = 0.0f;
			const GLfloat z = 0.0f;
			const GLfloat w = 1.0f;

			glVertexAttrib1f(index, x);
			verifyCurrentVertexAttribf(m_testCtx, *this, index, x, y, z, w);
		}
	}
};

class CurrentVertexAttribIntCase : public ApiCase
{
public:
	CurrentVertexAttribIntCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		using tcu::TestLog;

		de::Random rnd(0xabcdef);

		int attribute_count = 16;
		glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &attribute_count);

		// test write float/read float

		for (int index = 0; index < attribute_count; ++index)
		{
			const GLint x = rnd.getInt(-64000, 64000);
			const GLint y = rnd.getInt(-64000, 64000);
			const GLint z = rnd.getInt(-64000, 64000);
			const GLint w = rnd.getInt(-64000, 64000);

			glVertexAttribI4i(index, x, y, z, w);
			verifyCurrentVertexAttribIi(m_testCtx, *this, index, x, y, z, w);
		}
	}
};

class CurrentVertexAttribUintCase : public ApiCase
{
public:
	CurrentVertexAttribUintCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		using tcu::TestLog;

		de::Random rnd(0xabcdef);

		int attribute_count = 16;
		glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &attribute_count);

		// test write float/read float

		for (int index = 0; index < attribute_count; ++index)
		{
			const GLuint x = rnd.getInt(0, 64000);
			const GLuint y = rnd.getInt(0, 64000);
			const GLuint z = rnd.getInt(0, 64000);
			const GLuint w = rnd.getInt(0, 64000);

			glVertexAttribI4ui(index, x, y, z, w);
			verifyCurrentVertexAttribIui(m_testCtx, *this, index, x, y, z, w);
		}
	}
};

class CurrentVertexAttribConversionCase : public ApiCase
{
public:
	CurrentVertexAttribConversionCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		using tcu::TestLog;

		de::Random rnd(0xabcdef);

		int attribute_count = 16;
		glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &attribute_count);

		// test write float/read float

		for (int index = 0; index < attribute_count; ++index)
		{
			const GLfloat x = rnd.getFloat(-64000, 64000);
			const GLfloat y = rnd.getFloat(-64000, 64000);
			const GLfloat z = rnd.getFloat(-64000, 64000);
			const GLfloat w = rnd.getFloat(-64000, 64000);

			glVertexAttrib4f(index, x, y, z, w);
			verifyCurrentVertexAttribConversion(m_testCtx, *this, index, x, y, z, w);
		}
		for (int index = 0; index < attribute_count; ++index)
		{
			const GLfloat x = rnd.getFloat(-64000, 64000);
			const GLfloat y = rnd.getFloat(-64000, 64000);
			const GLfloat z = rnd.getFloat(-64000, 64000);
			const GLfloat w = 1.0f;

			glVertexAttrib3f(index, x, y, z);
			verifyCurrentVertexAttribConversion(m_testCtx, *this, index, x, y, z, w);
		}
		for (int index = 0; index < attribute_count; ++index)
		{
			const GLfloat x = rnd.getFloat(-64000, 64000);
			const GLfloat y = rnd.getFloat(-64000, 64000);
			const GLfloat z = 0.0f;
			const GLfloat w = 1.0f;

			glVertexAttrib2f(index, x, y);
			verifyCurrentVertexAttribConversion(m_testCtx, *this, index, x, y, z, w);
		}
		for (int index = 0; index < attribute_count; ++index)
		{
			const GLfloat x = rnd.getFloat(-64000, 64000);
			const GLfloat y = 0.0f;
			const GLfloat z = 0.0f;
			const GLfloat w = 1.0f;

			glVertexAttrib1f(index, x);
			verifyCurrentVertexAttribConversion(m_testCtx, *this, index, x, y, z, w);
		}
	}
};

class ProgramInfoLogCase : public ApiCase
{
public:
	enum BuildErrorType
	{
		BUILDERROR_COMPILE = 0,
		BUILDERROR_LINK
	};

	ProgramInfoLogCase (Context& context, const char* name, const char* description, BuildErrorType buildErrorType)
		: ApiCase			(context, name, description)
		, m_buildErrorType	(buildErrorType)
	{
	}

	void test (void)
	{
		using tcu::TestLog;

		enum
		{
			BUF_SIZE = 2048
		};

		static const char* const linkErrorVtxSource = "#version 300 es\n"
													  "in highp vec4 a_pos;\n"
													  "uniform highp vec4 u_uniform;\n"
													  "void main ()\n"
													  "{\n"
													  "	gl_Position = a_pos + u_uniform;\n"
													  "}\n";
		static const char* const linkErrorFrgSource = "#version 300 es\n"
													  "in highp vec4 v_missingVar;\n"
													  "uniform highp int u_uniform;\n"
													  "layout(location = 0) out mediump vec4 fragColor;\n"
													  "void main ()\n"
													  "{\n"
													  "	fragColor = v_missingVar + vec4(float(u_uniform));\n"
													  "}\n";

		const char* vtxSource = (m_buildErrorType == BUILDERROR_COMPILE) ? (brokenShader) : (linkErrorVtxSource);
		const char* frgSource = (m_buildErrorType == BUILDERROR_COMPILE) ? (brokenShader) : (linkErrorFrgSource);

		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		glShaderSource(shaderVert, 1, &vtxSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &frgSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		GLuint program = glCreateProgram();
		glAttachShader(program, shaderVert);
		glAttachShader(program, shaderFrag);
		glLinkProgram(program);

		StateQueryMemoryWriteGuard<GLint> logLength;
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
		logLength.verifyValidity(m_testCtx);

		// check INFO_LOG_LENGTH == GetProgramInfoLog len
		{
			const tcu::ScopedLogSection	section				(m_testCtx.getLog(), "QueryLarge", "Query to large buffer");
			char						buffer[BUF_SIZE]	= {'x'};
			GLint						written				= 0;

			glGetProgramInfoLog(program, BUF_SIZE, &written, buffer);

			if (logLength != 0 && written+1 != logLength) // INFO_LOG_LENGTH contains 0-terminator
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected INFO_LOG_LENGTH " << written+1 << "; got " << logLength << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid log length");
			}
			else if (logLength != 0 && buffer[written] != '\0')
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected null terminator at index " << written << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "missing null terminator");
			}
		}

		// check query to just correct sized buffer
		if (BUF_SIZE > logLength)
		{
			const tcu::ScopedLogSection	section				(m_testCtx.getLog(), "QueryAll", "Query all to exactly right sized buffer");
			char						buffer[BUF_SIZE]	= {'x'};
			GLint						written				= 0;

			glGetProgramInfoLog(program, logLength, &written, buffer);

			if (logLength != 0 && written+1 != logLength) // INFO_LOG_LENGTH contains 0-terminator
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected INFO_LOG_LENGTH " << written+1 << "; got " << logLength << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid log length");
			}
			else if (logLength != 0 && buffer[written] != '\0')
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected null terminator at index " << written << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "missing null terminator");
			}
		}

		// check GetProgramInfoLog works with too small buffer
		{
			const tcu::ScopedLogSection	section				(m_testCtx.getLog(), "QueryNone", "Query none");
			char						buffer[BUF_SIZE]	= {'x'};
			GLint						written				= 0;

			glGetProgramInfoLog(program, 1, &written, buffer);

			if (written != 0)
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected write length 0; got " << written << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid log length");
			}
		}

		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(program);
		expectError(GL_NO_ERROR);
	}

	const BuildErrorType m_buildErrorType;
};

class ProgramValidateStatusCase : public ApiCase
{
public:
	ProgramValidateStatusCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		// test validate ok
		{
			GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
			GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

			glShaderSource(shaderVert, 1, &commonTestVertSource, DE_NULL);
			glShaderSource(shaderFrag, 1, &commonTestFragSource, DE_NULL);

			glCompileShader(shaderVert);
			glCompileShader(shaderFrag);
			expectError(GL_NO_ERROR);

			GLuint program = glCreateProgram();
			glAttachShader(program, shaderVert);
			glAttachShader(program, shaderFrag);
			glLinkProgram(program);
			expectError(GL_NO_ERROR);

			verifyShaderParam	(m_testCtx, *this, shaderVert,	GL_COMPILE_STATUS,	GL_TRUE);
			verifyShaderParam	(m_testCtx, *this, shaderFrag,	GL_COMPILE_STATUS,	GL_TRUE);
			verifyProgramParam	(m_testCtx, *this, program,		GL_LINK_STATUS,		GL_TRUE);

			glValidateProgram(program);
			verifyProgramParam(m_testCtx, *this, program, GL_VALIDATE_STATUS, GL_TRUE);

			glDeleteShader(shaderVert);
			glDeleteShader(shaderFrag);
			glDeleteProgram(program);
			expectError(GL_NO_ERROR);
		}

		// test with broken shader
		{
			GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
			GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

			glShaderSource(shaderVert, 1, &commonTestVertSource,	DE_NULL);
			glShaderSource(shaderFrag, 1, &brokenShader,			DE_NULL);

			glCompileShader(shaderVert);
			glCompileShader(shaderFrag);
			expectError(GL_NO_ERROR);

			GLuint program = glCreateProgram();
			glAttachShader(program, shaderVert);
			glAttachShader(program, shaderFrag);
			glLinkProgram(program);
			expectError(GL_NO_ERROR);

			verifyShaderParam	(m_testCtx, *this, shaderVert,	GL_COMPILE_STATUS,	GL_TRUE);
			verifyShaderParam	(m_testCtx, *this, shaderFrag,	GL_COMPILE_STATUS,	GL_FALSE);
			verifyProgramParam	(m_testCtx, *this, program,		GL_LINK_STATUS,		GL_FALSE);

			glValidateProgram(program);
			verifyProgramParam(m_testCtx, *this, program, GL_VALIDATE_STATUS, GL_FALSE);

			glDeleteShader(shaderVert);
			glDeleteShader(shaderFrag);
			glDeleteProgram(program);
			expectError(GL_NO_ERROR);
		}
	}
};

class ProgramAttachedShadersCase : public ApiCase
{
public:
	ProgramAttachedShadersCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		using tcu::TestLog;

		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		glShaderSource(shaderVert, 1, &commonTestVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &commonTestFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		// check ATTACHED_SHADERS

		GLuint program = glCreateProgram();
		verifyProgramParam(m_testCtx, *this, program, GL_ATTACHED_SHADERS, 0);
		expectError(GL_NO_ERROR);

		glAttachShader(program, shaderVert);
		verifyProgramParam(m_testCtx, *this, program, GL_ATTACHED_SHADERS, 1);
		expectError(GL_NO_ERROR);

		glAttachShader(program, shaderFrag);
		verifyProgramParam(m_testCtx, *this, program, GL_ATTACHED_SHADERS, 2);
		expectError(GL_NO_ERROR);

		// check GetAttachedShaders
		{
			GLuint shaders[2] = {0, 0};
			GLint count = 0;
			glGetAttachedShaders(program, DE_LENGTH_OF_ARRAY(shaders), &count, shaders);

			if (count != 2)
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected 2; got " << count << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong shader count");
			}
			// shaders are the attached shaders?
			if (!((shaders[0] == shaderVert && shaders[1] == shaderFrag) ||
				  (shaders[0] == shaderFrag && shaders[1] == shaderVert)))
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected {" << shaderVert << ", " << shaderFrag << "}; got {" << shaders[0] << ", " << shaders[1] << "}" << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong shader count");
			}
		}

		// check GetAttachedShaders with too small buffer
		{
			GLuint shaders[2] = {0, 0};
			GLint count = 0;

			glGetAttachedShaders(program, 0, &count, shaders);
			if (count != 0)
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected 0; got " << count << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong shader count");
			}

			count = 0;
			glGetAttachedShaders(program, 1, &count, shaders);
			if (count != 1)
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected 1; got " << count << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong shader count");
			}
		}

		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(program);
		expectError(GL_NO_ERROR);
	}
};

class ProgramActiveUniformNameCase : public ApiCase
{
public:
	ProgramActiveUniformNameCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		using tcu::TestLog;

		static const char* testVertSource =
			"#version 300 es\n"
			"uniform highp float uniformNameWithLength23;\n"
			"uniform highp vec2 uniformVec2;\n"
			"uniform highp mat4 uniformMat4;\n"
			"void main (void)\n"
			"{\n"
			"	gl_Position = vec4(0.0) + vec4(uniformNameWithLength23) + vec4(uniformVec2.x) + vec4(uniformMat4[2][3]);\n"
			"}\n\0";
		static const char* testFragSource =
			"#version 300 es\n"
			"layout(location = 0) out mediump vec4 fragColor;"
			"void main (void)\n"
			"{\n"
			"	fragColor = vec4(0.0);\n"
			"}\n\0";

		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		glShaderSource(shaderVert, 1, &testVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &testFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		GLuint program = glCreateProgram();
		glAttachShader(program, shaderVert);
		glAttachShader(program, shaderFrag);
		glLinkProgram(program);
		expectError(GL_NO_ERROR);

		verifyProgramParam(m_testCtx, *this, program, GL_ACTIVE_UNIFORMS, 3);
		verifyProgramParam(m_testCtx, *this, program, GL_ACTIVE_UNIFORM_MAX_LENGTH, (GLint)std::string("uniformNameWithLength23").length() + 1); // including a null terminator
		expectError(GL_NO_ERROR);

		const char* uniformNames[] =
		{
			"uniformNameWithLength23",
			"uniformVec2",
			"uniformMat4"
		};
		StateQueryMemoryWriteGuard<GLuint[DE_LENGTH_OF_ARRAY(uniformNames)]> uniformIndices;
		glGetUniformIndices(program, DE_LENGTH_OF_ARRAY(uniformNames), uniformNames, uniformIndices);
		uniformIndices.verifyValidity(m_testCtx);

		// check name lengths
		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(uniformNames); ++ndx)
		{
			const GLuint uniformIndex = uniformIndices[ndx];

			StateQueryMemoryWriteGuard<GLint> uniformNameLen;
			glGetActiveUniformsiv(program, 1, &uniformIndex, GL_UNIFORM_NAME_LENGTH, &uniformNameLen);

			uniformNameLen.verifyValidity(m_testCtx);

			const GLint referenceLength = (GLint)std::string(uniformNames[ndx]).length() + 1;
			if (referenceLength != uniformNameLen) // uniformNameLen is with null terminator
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected " << referenceLength << "got " << uniformNameLen << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong uniform name length");
			}
		}

		// check names
		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(uniformNames); ++ndx)
		{
			char buffer[2048] = {'x'};

			const GLuint uniformIndex = uniformIndices[ndx];

			GLint written = 0; // null terminator not included
			GLint size = 0;
			GLenum type = 0;
			glGetActiveUniform(program, uniformIndex, DE_LENGTH_OF_ARRAY(buffer), &written, &size, &type, buffer);

			const GLint referenceLength = (GLint)std::string(uniformNames[ndx]).length();
			if (referenceLength != written)
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected " << referenceLength << "got " << written << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong uniform name length");
			}

			// and with too small buffer
			written = 0;
			glGetActiveUniform(program, uniformIndex, 1, &written, &size, &type, buffer);

			if (written != 0)
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected 0 got " << written << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong uniform name length");
			}
		}


		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(program);
		expectError(GL_NO_ERROR);
	}
};

class ProgramUniformCase : public ApiCase
{
public:
	ProgramUniformCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		const struct UniformType
		{
			const char* declaration;
			const char* postDeclaration;
			const char* precision;
			const char* layout;
			const char* getter;
			GLenum		type;
			GLint		size;
			GLint		isRowMajor;
		} uniformTypes[] =
		{
			{ "float",					"",			"highp",	"",						"uniformValue",							GL_FLOAT,							1, GL_FALSE },
			{ "float[2]",				"",			"highp",	"",						"uniformValue[1]",						GL_FLOAT,							2, GL_FALSE },
			{ "vec2",					"",			"highp",	"",						"uniformValue.x",						GL_FLOAT_VEC2,						1, GL_FALSE },
			{ "vec3",					"",			"highp",	"",						"uniformValue.x",						GL_FLOAT_VEC3,						1, GL_FALSE },
			{ "vec4",					"",			"highp",	"",						"uniformValue.x",						GL_FLOAT_VEC4,						1, GL_FALSE },
			{ "int",					"",			"highp",	"",						"float(uniformValue)",					GL_INT,								1, GL_FALSE },
			{ "ivec2",					"",			"highp",	"",						"float(uniformValue.x)",				GL_INT_VEC2,						1, GL_FALSE },
			{ "ivec3",					"",			"highp",	"",						"float(uniformValue.x)",				GL_INT_VEC3,						1, GL_FALSE },
			{ "ivec4",					"",			"highp",	"",						"float(uniformValue.x)",				GL_INT_VEC4,						1, GL_FALSE },
			{ "uint",					"",			"highp",	"",						"float(uniformValue)",					GL_UNSIGNED_INT,					1, GL_FALSE },
			{ "uvec2",					"",			"highp",	"",						"float(uniformValue.x)",				GL_UNSIGNED_INT_VEC2,				1, GL_FALSE },
			{ "uvec3",					"",			"highp",	"",						"float(uniformValue.x)",				GL_UNSIGNED_INT_VEC3,				1, GL_FALSE },
			{ "uvec4",					"",			"highp",	"",						"float(uniformValue.x)",				GL_UNSIGNED_INT_VEC4,				1, GL_FALSE },
			{ "bool",					"",			"",			"",						"float(uniformValue)",					GL_BOOL,							1, GL_FALSE },
			{ "bvec2",					"",			"",			"",						"float(uniformValue.x)",				GL_BOOL_VEC2,						1, GL_FALSE },
			{ "bvec3",					"",			"",			"",						"float(uniformValue.x)",				GL_BOOL_VEC3,						1, GL_FALSE },
			{ "bvec4",					"",			"",			"",						"float(uniformValue.x)",				GL_BOOL_VEC4,						1, GL_FALSE },
			{ "mat2",					"",			"highp",	"",						"float(uniformValue[0][0])",			GL_FLOAT_MAT2,						1, GL_FALSE },
			{ "mat3",					"",			"highp",	"",						"float(uniformValue[0][0])",			GL_FLOAT_MAT3,						1, GL_FALSE },
			{ "mat4",					"",			"highp",	"",						"float(uniformValue[0][0])",			GL_FLOAT_MAT4,						1, GL_FALSE },
			{ "mat2x3",					"",			"highp",	"",						"float(uniformValue[0][0])",			GL_FLOAT_MAT2x3,					1, GL_FALSE },
			{ "mat2x4",					"",			"highp",	"",						"float(uniformValue[0][0])",			GL_FLOAT_MAT2x4,					1, GL_FALSE },
			{ "mat3x2",					"",			"highp",	"",						"float(uniformValue[0][0])",			GL_FLOAT_MAT3x2,					1, GL_FALSE },
			{ "mat3x4",					"",			"highp",	"",						"float(uniformValue[0][0])",			GL_FLOAT_MAT3x4,					1, GL_FALSE },
			{ "mat4x2",					"",			"highp",	"",						"float(uniformValue[0][0])",			GL_FLOAT_MAT4x2,					1, GL_FALSE },
			{ "mat4x3",					"",			"highp",	"",						"float(uniformValue[0][0])",			GL_FLOAT_MAT4x3,					1, GL_FALSE },
			{ "sampler2D",				"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_SAMPLER_2D,						1, GL_FALSE },
			{ "sampler3D",				"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_SAMPLER_3D,						1, GL_FALSE },
			{ "samplerCube",			"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_SAMPLER_CUBE,					1, GL_FALSE },
			{ "sampler2DShadow",		"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_SAMPLER_2D_SHADOW,				1, GL_FALSE },
			{ "sampler2DArray",			"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_SAMPLER_2D_ARRAY,				1, GL_FALSE },
			{ "sampler2DArrayShadow",	"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_SAMPLER_2D_ARRAY_SHADOW,			1, GL_FALSE },
			{ "samplerCubeShadow",		"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_SAMPLER_CUBE_SHADOW,				1, GL_FALSE },
			{ "isampler2D",				"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_INT_SAMPLER_2D,					1, GL_FALSE },
			{ "isampler3D",				"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_INT_SAMPLER_3D,					1, GL_FALSE },
			{ "isamplerCube",			"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_INT_SAMPLER_CUBE,				1, GL_FALSE },
			{ "isampler2DArray",		"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_INT_SAMPLER_2D_ARRAY,			1, GL_FALSE },
			{ "usampler2D",				"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_UNSIGNED_INT_SAMPLER_2D,			1, GL_FALSE },
			{ "usampler3D",				"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_UNSIGNED_INT_SAMPLER_3D,			1, GL_FALSE },
			{ "usamplerCube",			"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_UNSIGNED_INT_SAMPLER_CUBE,		1, GL_FALSE },
			{ "usampler2DArray",		"",			"highp",	"",						"float(textureSize(uniformValue,0).r)",	GL_UNSIGNED_INT_SAMPLER_2D_ARRAY,	1, GL_FALSE },
		};

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

		GLuint shaderVert	= glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag	= glCreateShader(GL_FRAGMENT_SHADER);
		GLuint program		= glCreateProgram();

		glAttachShader(program, shaderVert);
		glAttachShader(program, shaderFrag);

		glShaderSource(shaderVert, 1, &vertSource, DE_NULL);
		glCompileShader(shaderVert);
		expectError(GL_NO_ERROR);

		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(uniformTypes); ++ndx)
		{
			tcu::ScopedLogSection(m_log, uniformTypes[ndx].declaration, std::string("Verify type of ") + uniformTypes[ndx].declaration + " variable" + uniformTypes[ndx].postDeclaration );

			// gen fragment shader

			std::ostringstream frag;
			frag << "#version 300 es\n";
			frag << uniformTypes[ndx].layout << "uniform " << uniformTypes[ndx].precision << " " << uniformTypes[ndx].declaration << " uniformValue" << uniformTypes[ndx].postDeclaration << ";\n";
			frag << "layout(location = 0) out mediump vec4 fragColor;\n";
			frag << "void main (void)\n";
			frag << "{\n";
			frag << "	fragColor = vec4(" << uniformTypes[ndx].getter << ");\n";
			frag << "}\n";

			{
				std::string fragmentSource = frag.str();
				const char* fragmentSourceCStr = fragmentSource.c_str();
				glShaderSource(shaderFrag, 1, &fragmentSourceCStr, DE_NULL);
			}

			// compile & link

			glCompileShader(shaderFrag);
			glLinkProgram(program);

			// test
			if (verifyProgramParam(m_testCtx, *this, program, GL_LINK_STATUS, GL_TRUE))
			{
				const char* uniformNames[] = {"uniformValue"};
				StateQueryMemoryWriteGuard<GLuint> uniformIndex;
				glGetUniformIndices(program, 1, uniformNames, &uniformIndex);
				uniformIndex.verifyValidity(m_testCtx);

				verifyActiveUniformParam(m_testCtx, *this, program, uniformIndex, GL_UNIFORM_TYPE,			uniformTypes[ndx].type);
				verifyActiveUniformParam(m_testCtx, *this, program, uniformIndex, GL_UNIFORM_SIZE,			uniformTypes[ndx].size);
				verifyActiveUniformParam(m_testCtx, *this, program, uniformIndex, GL_UNIFORM_IS_ROW_MAJOR,	uniformTypes[ndx].isRowMajor);
			}
		}

		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(program);
		expectError(GL_NO_ERROR);
	}
};

class ProgramActiveUniformBlocksCase : public ApiCase
{
public:
	ProgramActiveUniformBlocksCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		using tcu::TestLog;

		static const char* testVertSource =
			"#version 300 es\n"
			"uniform longlongUniformBlockName {highp vec2 vector2;} longlongUniformInstanceName;\n"
			"uniform shortUniformBlockName {highp vec2 vector2;highp vec4 vector4;} shortUniformInstanceName;\n"
			"void main (void)\n"
			"{\n"
			"	gl_Position = shortUniformInstanceName.vector4 + vec4(longlongUniformInstanceName.vector2.x) + vec4(shortUniformInstanceName.vector2.x);\n"
			"}\n\0";
		static const char* testFragSource =
			"#version 300 es\n"
			"uniform longlongUniformBlockName {highp vec2 vector2;} longlongUniformInstanceName;\n"
			"layout(location = 0) out mediump vec4 fragColor;"
			"void main (void)\n"
			"{\n"
			"	fragColor = vec4(longlongUniformInstanceName.vector2.y);\n"
			"}\n\0";

		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		glShaderSource(shaderVert, 1, &testVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &testFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		GLuint program = glCreateProgram();
		glAttachShader(program, shaderVert);
		glAttachShader(program, shaderFrag);
		glLinkProgram(program);
		expectError(GL_NO_ERROR);

		verifyShaderParam	(m_testCtx, *this, shaderVert,	GL_COMPILE_STATUS,	GL_TRUE);
		verifyShaderParam	(m_testCtx, *this, shaderFrag,	GL_COMPILE_STATUS,	GL_TRUE);
		verifyProgramParam	(m_testCtx, *this, program,		GL_LINK_STATUS,		GL_TRUE);

		verifyProgramParam	(m_testCtx, *this, program,		GL_ACTIVE_UNIFORM_BLOCKS, 2);
		verifyProgramParam	(m_testCtx, *this, program,		GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, (GLint)std::string("longlongUniformBlockName").length() + 1); // including a null terminator
		expectError(GL_NO_ERROR);

		GLint longlongUniformBlockIndex	= glGetUniformBlockIndex(program, "longlongUniformBlockName");
		GLint shortUniformBlockIndex	= glGetUniformBlockIndex(program, "shortUniformBlockName");

		const char* uniformNames[] =
		{
			"longlongUniformBlockName.vector2",
			"shortUniformBlockName.vector2",
			"shortUniformBlockName.vector4"
		};

		// test UNIFORM_BLOCK_INDEX

		DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(uniformNames) == 3);

		StateQueryMemoryWriteGuard<GLuint[DE_LENGTH_OF_ARRAY(uniformNames)]> uniformIndices;
		StateQueryMemoryWriteGuard<GLint[DE_LENGTH_OF_ARRAY(uniformNames)]> uniformsBlockIndices;

		glGetUniformIndices(program, DE_LENGTH_OF_ARRAY(uniformNames), uniformNames, uniformIndices);
		uniformIndices.verifyValidity(m_testCtx);
		expectError(GL_NO_ERROR);

		glGetActiveUniformsiv(program, DE_LENGTH_OF_ARRAY(uniformNames), uniformIndices, GL_UNIFORM_BLOCK_INDEX, uniformsBlockIndices);
		uniformsBlockIndices.verifyValidity(m_testCtx);
		expectError(GL_NO_ERROR);

		if (uniformsBlockIndices[0] != longlongUniformBlockIndex ||
			uniformsBlockIndices[1] != shortUniformBlockIndex ||
			uniformsBlockIndices[2] != shortUniformBlockIndex)
		{
			m_testCtx.getLog() << TestLog::Message
				<< "// ERROR: Expected ["	<< longlongUniformBlockIndex	<< ", " << shortUniformBlockIndex	<< ", " << shortUniformBlockIndex	<< "];"
				<<	"got ["					<< uniformsBlockIndices[0]		<< ", " << uniformsBlockIndices[1]	<< ", " << uniformsBlockIndices[2]	<< "]" << TestLog::EndMessage;
			if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong uniform block index");
		}

		// test UNIFORM_BLOCK_NAME_LENGTH

		verifyActiveUniformBlockParam(m_testCtx, *this, program, longlongUniformBlockIndex,	GL_UNIFORM_BLOCK_NAME_LENGTH, (GLint)std::string("longlongUniformBlockName").length() + 1); // including null-terminator
		verifyActiveUniformBlockParam(m_testCtx, *this, program, shortUniformBlockIndex,	GL_UNIFORM_BLOCK_NAME_LENGTH, (GLint)std::string("shortUniformBlockName").length() + 1); // including null-terminator
		expectError(GL_NO_ERROR);

		// test UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER & UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER

		verifyActiveUniformBlockParam(m_testCtx, *this, program, longlongUniformBlockIndex,	GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER,	GL_TRUE);
		verifyActiveUniformBlockParam(m_testCtx, *this, program, longlongUniformBlockIndex,	GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER,	GL_TRUE);
		verifyActiveUniformBlockParam(m_testCtx, *this, program, shortUniformBlockIndex,	GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER,	GL_TRUE);
		verifyActiveUniformBlockParam(m_testCtx, *this, program, shortUniformBlockIndex,	GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER,	GL_FALSE);
		expectError(GL_NO_ERROR);

		// test UNIFORM_BLOCK_ACTIVE_UNIFORMS

		verifyActiveUniformBlockParam(m_testCtx, *this, program, longlongUniformBlockIndex,	GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS,	1);
		verifyActiveUniformBlockParam(m_testCtx, *this, program, shortUniformBlockIndex,	GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS,	2);
		expectError(GL_NO_ERROR);

		// test UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES

		{
			StateQueryMemoryWriteGuard<GLint> longlongUniformBlockUniforms;
			glGetActiveUniformBlockiv(program, longlongUniformBlockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &longlongUniformBlockUniforms);
			longlongUniformBlockUniforms.verifyValidity(m_testCtx);

			if (longlongUniformBlockUniforms == 2)
			{
				StateQueryMemoryWriteGuard<GLint[2]> longlongUniformBlockUniformIndices;
				glGetActiveUniformBlockiv(program, longlongUniformBlockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, longlongUniformBlockUniformIndices);
				longlongUniformBlockUniformIndices.verifyValidity(m_testCtx);

				if ((GLuint(longlongUniformBlockUniformIndices[0]) != uniformIndices[0] || GLuint(longlongUniformBlockUniformIndices[1]) != uniformIndices[1]) &&
					(GLuint(longlongUniformBlockUniformIndices[1]) != uniformIndices[0] || GLuint(longlongUniformBlockUniformIndices[0]) != uniformIndices[1]))
				{
					m_testCtx.getLog() << TestLog::Message
						<< "// ERROR: Expected {"	<< uniformIndices[0]						<< ", " << uniformIndices[1] << "};"
						<<	"got {"					<< longlongUniformBlockUniformIndices[0]	<< ", " << longlongUniformBlockUniformIndices[1] << "}" << TestLog::EndMessage;

					if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
						m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got wrong uniform indices");
				}

			}
		}

		// check block names

		{
			char buffer[2048] = {'x'};
			GLint written = 0;
			glGetActiveUniformBlockName(program, longlongUniformBlockIndex, DE_LENGTH_OF_ARRAY(buffer), &written, buffer);
			checkIntEquals(m_testCtx, written, (GLint)std::string("longlongUniformBlockName").length());

			written = 0;
			glGetActiveUniformBlockName(program, shortUniformBlockIndex, DE_LENGTH_OF_ARRAY(buffer), &written, buffer);
			checkIntEquals(m_testCtx, written, (GLint)std::string("shortUniformBlockName").length());

			// and one with too small buffer
			written = 0;
			glGetActiveUniformBlockName(program, longlongUniformBlockIndex, 1, &written, buffer);
			checkIntEquals(m_testCtx, written, 0);
		}

		expectError(GL_NO_ERROR);
		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(program);
		expectError(GL_NO_ERROR);
	}
};

class ProgramBinaryCase : public ApiCase
{
public:
	ProgramBinaryCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		using tcu::TestLog;

		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		glShaderSource(shaderVert, 1, &commonTestVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &commonTestFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		GLuint program = glCreateProgram();
		glAttachShader(program, shaderVert);
		glAttachShader(program, shaderFrag);
		glLinkProgram(program);
		expectError(GL_NO_ERROR);

		// test PROGRAM_BINARY_RETRIEVABLE_HINT
		verifyProgramParam(m_testCtx, *this, program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_FALSE);

		glProgramParameteri(program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
		expectError(GL_NO_ERROR);

		glLinkProgram(program);
		expectError(GL_NO_ERROR);

		verifyProgramParam(m_testCtx, *this, program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);

		// test PROGRAM_BINARY_LENGTH does something

		StateQueryMemoryWriteGuard<GLint> programLength;
		glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &programLength);
		expectError(GL_NO_ERROR);
		programLength.verifyValidity(m_testCtx);

		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(program);
		expectError(GL_NO_ERROR);
	}
};

class TransformFeedbackCase : public ApiCase
{
public:
	TransformFeedbackCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		using tcu::TestLog;

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

		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);
		GLuint shaderProg = glCreateProgram();

		verifyProgramParam(m_testCtx, *this, shaderProg, GL_TRANSFORM_FEEDBACK_BUFFER_MODE, GL_INTERLEAVED_ATTRIBS);

		glShaderSource(shaderVert, 1, &transformFeedbackTestVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &transformFeedbackTestFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);

		verifyShaderParam(m_testCtx, *this, shaderVert, GL_COMPILE_STATUS, GL_TRUE);
		verifyShaderParam(m_testCtx, *this, shaderFrag, GL_COMPILE_STATUS, GL_TRUE);

		glAttachShader(shaderProg, shaderVert);
		glAttachShader(shaderProg, shaderFrag);

		// check TRANSFORM_FEEDBACK_BUFFER_MODE

		const char* transform_feedback_outputs[] = {"gl_Position", "tfOutput2withLongName"};
		const char* longest_output = transform_feedback_outputs[1];
		const GLenum bufferModes[] = {GL_SEPARATE_ATTRIBS, GL_INTERLEAVED_ATTRIBS};

		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(bufferModes); ++ndx)
		{
			glTransformFeedbackVaryings(shaderProg, DE_LENGTH_OF_ARRAY(transform_feedback_outputs), transform_feedback_outputs, bufferModes[ndx]);
			glLinkProgram(shaderProg);
			expectError(GL_NO_ERROR);

			verifyProgramParam(m_testCtx, *this, shaderProg, GL_LINK_STATUS, GL_TRUE);
			verifyProgramParam(m_testCtx, *this, shaderProg, GL_TRANSFORM_FEEDBACK_BUFFER_MODE, bufferModes[ndx]);
		}

		// TRANSFORM_FEEDBACK_VARYINGS
		verifyProgramParam(m_testCtx, *this, shaderProg, GL_TRANSFORM_FEEDBACK_VARYINGS, 2);

		// TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH
		{
			StateQueryMemoryWriteGuard<GLint> maxOutputLen;
			glGetProgramiv(shaderProg, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &maxOutputLen);

			maxOutputLen.verifyValidity(m_testCtx);

			const GLint referenceLength = (GLint)std::string(longest_output).length() + 1;
			checkIntEquals(m_testCtx, maxOutputLen, referenceLength);
		}

		// check varyings
		{
			StateQueryMemoryWriteGuard<GLint> varyings;
			glGetProgramiv(shaderProg, GL_TRANSFORM_FEEDBACK_VARYINGS, &varyings);

			if (!varyings.isUndefined())
				for (int index = 0; index < varyings; ++index)
				{
					char buffer[2048] = {'x'};

					GLint written = 0;
					GLint size = 0;
					GLenum type = 0;
					glGetTransformFeedbackVarying(shaderProg, index, DE_LENGTH_OF_ARRAY(buffer), &written, &size, &type, buffer);

					if (written < DE_LENGTH_OF_ARRAY(buffer) && buffer[written] != '\0')
					{
						m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected null terminator" << TestLog::EndMessage;
						if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
							m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid string terminator");
					}

					// check with too small buffer
					written = 0;
					glGetTransformFeedbackVarying(shaderProg, index, 1, &written, &size, &type, buffer);
					if (written != 0)
					{
						m_testCtx.getLog() << TestLog::Message << "// ERROR: Expected 0; got " << written << TestLog::EndMessage;
						if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
							m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid write length");
					}
				}
		}


		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(shaderProg);
		expectError(GL_NO_ERROR);
	}
};

class ActiveAttributesCase : public ApiCase
{
public:
	ActiveAttributesCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		using tcu::TestLog;

		static const char* testVertSource =
			"#version 300 es\n"
			"in highp vec2 longInputAttributeName;\n"
			"in highp vec2 shortName;\n"
			"void main (void)\n"
			"{\n"
			"	gl_Position = longInputAttributeName.yxxy + shortName.xyxy;\n"
			"}\n\0";
		static const char* testFragSource =
			"#version 300 es\n"
			"layout(location = 0) out mediump vec4 fragColor;"
			"void main (void)\n"
			"{\n"
			"	fragColor = vec4(0.0);\n"
			"}\n\0";

		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		glShaderSource(shaderVert, 1, &testVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &testFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		GLuint program = glCreateProgram();
		glAttachShader(program, shaderVert);
		glAttachShader(program, shaderFrag);
		glLinkProgram(program);
		expectError(GL_NO_ERROR);

		verifyProgramParam(m_testCtx, *this, program, GL_ACTIVE_ATTRIBUTES, 2);
		verifyProgramParam(m_testCtx, *this, program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, (GLint)std::string("longInputAttributeName").length() + 1); // does include null-terminator

		// check names
		for (int attributeNdx = 0; attributeNdx < 2; ++attributeNdx)
		{
			char buffer[2048] = {'x'};

			GLint written = 0;
			GLint size = 0;
			GLenum type = 0;
			glGetActiveAttrib(program, attributeNdx, DE_LENGTH_OF_ARRAY(buffer), &written, &size, &type, buffer);
			expectError(GL_NO_ERROR);

			if (deStringBeginsWith(buffer, "longInputAttributeName"))
			{
				checkIntEquals(m_testCtx, written, (GLint)std::string("longInputAttributeName").length()); // does NOT include null-terminator
			}
			else if (deStringBeginsWith(buffer, "shortName"))
			{
				checkIntEquals(m_testCtx, written, (GLint)std::string("shortName").length()); // does NOT include null-terminator
			}
			else
			{
				m_testCtx.getLog() << TestLog::Message << "// ERROR: Got unexpected attribute name." << TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected name");
			}
		}

		// and with too short buffer
		{
			char buffer[2048] = {'x'};

			GLint written = 0;
			GLint size = 0;
			GLenum type = 0;

			glGetActiveAttrib(program, 0, 1, &written, &size, &type, buffer);
			expectError(GL_NO_ERROR);
			checkIntEquals(m_testCtx, written, 0);
		}

		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(program);
		expectError(GL_NO_ERROR);
	}
};

struct PointerData
{
	GLint		size;
	GLenum		type;
	GLint		stride;
	GLboolean	normalized;
	const void*	pointer;
};

class VertexAttributeSizeCase : public ApiCase
{
public:
	VertexAttributeSizeCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		GLfloat vertexData[4] = {0.0f}; // never accessed

		const PointerData pointers[] =
		{
			// size test
			{ 4, GL_FLOAT,		0,	GL_FALSE, vertexData },
			{ 3, GL_FLOAT,		0,	GL_FALSE, vertexData },
			{ 2, GL_FLOAT,		0,	GL_FALSE, vertexData },
			{ 1, GL_FLOAT,		0,	GL_FALSE, vertexData },
			{ 4, GL_INT,		0,	GL_FALSE, vertexData },
			{ 3, GL_INT,		0,	GL_FALSE, vertexData },
			{ 2, GL_INT,		0,	GL_FALSE, vertexData },
			{ 1, GL_INT,		0,	GL_FALSE, vertexData },
		};

		// Test with default VAO
		{
			const tcu::ScopedLogSection section(m_log, "DefaultVAO", "Test with default VAO");

			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx)
			{
				glVertexAttribPointer(0, pointers[ndx].size, pointers[ndx].type, pointers[ndx].normalized, pointers[ndx].stride, pointers[ndx].pointer);
				expectError(GL_NO_ERROR);

				verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_SIZE, pointers[ndx].size);
			}
		}

		// Test with multiple VAOs
		{
			const tcu::ScopedLogSection section(m_log, "WithVAO", "Test with VAO");

			GLuint buf		= 0;
			GLuint vaos[2]	= {0};

			glGenVertexArrays(2, vaos);
			glGenBuffers(1, &buf);
			glBindBuffer(GL_ARRAY_BUFFER, buf);
			expectError(GL_NO_ERROR);

			// initial
			glBindVertexArray(vaos[0]);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_SIZE, 4);
			expectError(GL_NO_ERROR);

			// set vao 0 to some value
			glVertexAttribPointer(0, pointers[0].size, pointers[0].type, pointers[0].normalized, pointers[0].stride, DE_NULL);
			expectError(GL_NO_ERROR);

			// set vao 1 to some other value
			glBindVertexArray(vaos[1]);
			glVertexAttribPointer(0, pointers[1].size, pointers[1].type, pointers[1].normalized, pointers[1].stride, DE_NULL);
			expectError(GL_NO_ERROR);

			// verify vao 1 state
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_SIZE, pointers[1].size);
			expectError(GL_NO_ERROR);

			// verify vao 0 state
			glBindVertexArray(vaos[0]);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_SIZE, pointers[0].size);
			expectError(GL_NO_ERROR);

			glDeleteVertexArrays(2, vaos);
			glDeleteBuffers(1, &buf);
			expectError(GL_NO_ERROR);
		}
	}
};

class VertexAttributeTypeCase : public ApiCase
{
public:
	VertexAttributeTypeCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		// Test with default VAO
		{
			const tcu::ScopedLogSection section(m_log, "DefaultVAO", "Test with default VAO");

			const GLfloat vertexData[4] = {0.0f}; // never accessed

			// test VertexAttribPointer
			{
				const PointerData pointers[] =
				{
					{ 1, GL_BYTE,								0,	GL_FALSE, vertexData	},
					{ 1, GL_SHORT,								0,	GL_FALSE, vertexData	},
					{ 1, GL_INT,								0,	GL_FALSE, vertexData	},
					{ 1, GL_FIXED,								0,	GL_FALSE, vertexData	},
					{ 1, GL_FLOAT,								0,	GL_FALSE, vertexData	},
					{ 1, GL_HALF_FLOAT,							0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_BYTE,						0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_SHORT,						0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_INT,						0,	GL_FALSE, vertexData	},
					{ 4, GL_INT_2_10_10_10_REV,					0,	GL_FALSE, vertexData	},
					{ 4, GL_UNSIGNED_INT_2_10_10_10_REV,		0,	GL_FALSE, vertexData	},
				};

				for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx)
				{
					glVertexAttribPointer(0, pointers[ndx].size, pointers[ndx].type, pointers[ndx].normalized, pointers[ndx].stride, pointers[ndx].pointer);
					expectError(GL_NO_ERROR);

					verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_TYPE, pointers[ndx].type);
				}
			}

			// test glVertexAttribIPointer
			{
				const PointerData pointers[] =
				{
					{ 1, GL_BYTE,								0,	GL_FALSE, vertexData	},
					{ 1, GL_SHORT,								0,	GL_FALSE, vertexData	},
					{ 1, GL_INT,								0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_BYTE,						0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_SHORT,						0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_INT,						0,	GL_FALSE, vertexData	},
				};

				for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx)
				{
					glVertexAttribIPointer(0, pointers[ndx].size, pointers[ndx].type, pointers[ndx].stride, pointers[ndx].pointer);
					expectError(GL_NO_ERROR);

					verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_TYPE, pointers[ndx].type);
				}
			}
		}

		// Test with multiple VAOs
		{
			const tcu::ScopedLogSection section(m_log, "WithVAO", "Test with VAO");

			GLuint buf		= 0;
			GLuint vaos[2]	= {0};

			glGenVertexArrays(2, vaos);
			glGenBuffers(1, &buf);
			glBindBuffer(GL_ARRAY_BUFFER, buf);
			expectError(GL_NO_ERROR);

			// initial
			glBindVertexArray(vaos[0]);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_TYPE, GL_FLOAT);
			expectError(GL_NO_ERROR);

			// set vao 0 to some value
			glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, DE_NULL);
			expectError(GL_NO_ERROR);

			// set vao 1 to some other value
			glBindVertexArray(vaos[1]);
			glVertexAttribPointer(0, 1, GL_SHORT, GL_FALSE, 0, DE_NULL);
			expectError(GL_NO_ERROR);

			// verify vao 1 state
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_TYPE, GL_SHORT);
			expectError(GL_NO_ERROR);

			// verify vao 0 state
			glBindVertexArray(vaos[0]);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_TYPE, GL_FLOAT);
			expectError(GL_NO_ERROR);

			glDeleteVertexArrays(2, vaos);
			glDeleteBuffers(1, &buf);
			expectError(GL_NO_ERROR);
		}
	}
};

class VertexAttributeStrideCase : public ApiCase
{
public:
	VertexAttributeStrideCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		// Test with default VAO
		{
			const tcu::ScopedLogSection section(m_log, "DefaultVAO", "Test with default VAO");

			const GLfloat vertexData[4] = {0.0f}; // never accessed

			struct StridePointerData
			{
				GLint		size;
				GLenum		type;
				GLint		stride;
				const void*	pointer;
			};

			// test VertexAttribPointer
			{
				const StridePointerData pointers[] =
				{
					{ 1, GL_FLOAT,			0,	vertexData },
					{ 1, GL_FLOAT,			1,	vertexData },
					{ 1, GL_FLOAT,			4,	vertexData },
					{ 1, GL_HALF_FLOAT,		0,	vertexData },
					{ 1, GL_HALF_FLOAT,		1,	vertexData },
					{ 1, GL_HALF_FLOAT,		4,	vertexData },
					{ 1, GL_FIXED,			0,	vertexData },
					{ 1, GL_FIXED,			1,	vertexData },
					{ 1, GL_FIXED,			4,	vertexData },
				};

				for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx)
				{
					glVertexAttribPointer(0, pointers[ndx].size, pointers[ndx].type, GL_FALSE, pointers[ndx].stride, pointers[ndx].pointer);
					expectError(GL_NO_ERROR);

					verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_STRIDE, pointers[ndx].stride);
				}
			}

			// test glVertexAttribIPointer
			{
				const StridePointerData pointers[] =
				{
					{ 1, GL_INT,				0,	vertexData },
					{ 1, GL_INT,				1,	vertexData },
					{ 1, GL_INT,				4,	vertexData },
					{ 4, GL_UNSIGNED_BYTE,		0,	vertexData },
					{ 4, GL_UNSIGNED_BYTE,		1,	vertexData },
					{ 4, GL_UNSIGNED_BYTE,		4,	vertexData },
					{ 2, GL_SHORT,				0,	vertexData },
					{ 2, GL_SHORT,				1,	vertexData },
					{ 2, GL_SHORT,				4,	vertexData },
				};

				for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx)
				{
					glVertexAttribIPointer(0, pointers[ndx].size, pointers[ndx].type, pointers[ndx].stride, pointers[ndx].pointer);
					expectError(GL_NO_ERROR);

					verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_STRIDE, pointers[ndx].stride);
				}
			}
		}

		// Test with multiple VAOs
		{
			const tcu::ScopedLogSection section(m_log, "WithVAO", "Test with VAO");

			GLuint buf		= 0;
			GLuint vaos[2]	= {0};

			glGenVertexArrays(2, vaos);
			glGenBuffers(1, &buf);
			glBindBuffer(GL_ARRAY_BUFFER, buf);
			expectError(GL_NO_ERROR);

			// initial
			glBindVertexArray(vaos[0]);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_STRIDE, 0);
			expectError(GL_NO_ERROR);

			// set vao 0 to some value
			glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 4, DE_NULL);
			expectError(GL_NO_ERROR);

			// set vao 1 to some other value
			glBindVertexArray(vaos[1]);
			glVertexAttribPointer(0, 1, GL_SHORT, GL_FALSE, 8, DE_NULL);
			expectError(GL_NO_ERROR);

			// verify vao 1 state
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_STRIDE, 8);
			expectError(GL_NO_ERROR);

			// verify vao 0 state
			glBindVertexArray(vaos[0]);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_STRIDE, 4);
			expectError(GL_NO_ERROR);

			glDeleteVertexArrays(2, vaos);
			glDeleteBuffers(1, &buf);
			expectError(GL_NO_ERROR);
		}
	}
};

class VertexAttributeNormalizedCase : public ApiCase
{
public:
	VertexAttributeNormalizedCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		// Test with default VAO
		{
			const tcu::ScopedLogSection section(m_log, "DefaultVAO", "Test with default VAO");

			const GLfloat vertexData[4] = {0.0f}; // never accessed

			// test VertexAttribPointer
			{
				const PointerData pointers[] =
				{
					{ 1, GL_BYTE,								0,	GL_FALSE,	vertexData	},
					{ 1, GL_SHORT,								0,	GL_FALSE,	vertexData	},
					{ 1, GL_INT,								0,	GL_FALSE,	vertexData	},
					{ 1, GL_UNSIGNED_BYTE,						0,	GL_FALSE,	vertexData	},
					{ 1, GL_UNSIGNED_SHORT,						0,	GL_FALSE,	vertexData	},
					{ 1, GL_UNSIGNED_INT,						0,	GL_FALSE,	vertexData	},
					{ 4, GL_INT_2_10_10_10_REV,					0,	GL_FALSE,	vertexData	},
					{ 4, GL_UNSIGNED_INT_2_10_10_10_REV,		0,	GL_FALSE,	vertexData	},
					{ 1, GL_BYTE,								0,	GL_TRUE,	vertexData	},
					{ 1, GL_SHORT,								0,	GL_TRUE,	vertexData	},
					{ 1, GL_INT,								0,	GL_TRUE,	vertexData	},
					{ 1, GL_UNSIGNED_BYTE,						0,	GL_TRUE,	vertexData	},
					{ 1, GL_UNSIGNED_SHORT,						0,	GL_TRUE,	vertexData	},
					{ 1, GL_UNSIGNED_INT,						0,	GL_TRUE,	vertexData	},
					{ 4, GL_INT_2_10_10_10_REV,					0,	GL_TRUE,	vertexData	},
					{ 4, GL_UNSIGNED_INT_2_10_10_10_REV,		0,	GL_TRUE,	vertexData	},
				};

				for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx)
				{
					glVertexAttribPointer(0, pointers[ndx].size, pointers[ndx].type, pointers[ndx].normalized, pointers[ndx].stride, pointers[ndx].pointer);
					expectError(GL_NO_ERROR);

					verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, pointers[ndx].normalized);
				}
			}

			// test glVertexAttribIPointer
			{
				const PointerData pointers[] =
				{
					{ 1, GL_BYTE,				0,	GL_FALSE, vertexData	},
					{ 1, GL_SHORT,				0,	GL_FALSE, vertexData	},
					{ 1, GL_INT,				0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_BYTE,		0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_SHORT,		0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_INT,		0,	GL_FALSE, vertexData	},
				};

				for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx)
				{
					glVertexAttribIPointer(0, pointers[ndx].size, pointers[ndx].type, pointers[ndx].stride, pointers[ndx].pointer);
					expectError(GL_NO_ERROR);

					verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, GL_FALSE);
				}
			}
		}

		// Test with multiple VAOs
		{
			const tcu::ScopedLogSection section(m_log, "WithVAO", "Test with VAO");

			GLuint buf		= 0;
			GLuint vaos[2]	= {0};

			glGenVertexArrays(2, vaos);
			glGenBuffers(1, &buf);
			glBindBuffer(GL_ARRAY_BUFFER, buf);
			expectError(GL_NO_ERROR);

			// initial
			glBindVertexArray(vaos[0]);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, GL_FALSE);
			expectError(GL_NO_ERROR);

			// set vao 0 to some value
			glVertexAttribPointer(0, 1, GL_INT, GL_TRUE, 0, DE_NULL);
			expectError(GL_NO_ERROR);

			// set vao 1 to some other value
			glBindVertexArray(vaos[1]);
			glVertexAttribPointer(0, 1, GL_INT, GL_FALSE, 0, DE_NULL);
			expectError(GL_NO_ERROR);

			// verify vao 1 state
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, GL_FALSE);
			expectError(GL_NO_ERROR);

			// verify vao 0 state
			glBindVertexArray(vaos[0]);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, GL_TRUE);
			expectError(GL_NO_ERROR);

			glDeleteVertexArrays(2, vaos);
			glDeleteBuffers(1, &buf);
			expectError(GL_NO_ERROR);
		}
	}
};

class VertexAttributeIntegerCase : public ApiCase
{
public:
	VertexAttributeIntegerCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		// Test with default VAO
		{
			const tcu::ScopedLogSection section(m_log, "DefaultVAO", "Test with default VAO");

			const GLfloat vertexData[4] = {0.0f}; // never accessed

			// test VertexAttribPointer
			{
				const PointerData pointers[] =
				{
					{ 1, GL_BYTE,								0,	GL_FALSE, vertexData	},
					{ 1, GL_SHORT,								0,	GL_FALSE, vertexData	},
					{ 1, GL_INT,								0,	GL_FALSE, vertexData	},
					{ 1, GL_FIXED,								0,	GL_FALSE, vertexData	},
					{ 1, GL_FLOAT,								0,	GL_FALSE, vertexData	},
					{ 1, GL_HALF_FLOAT,							0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_BYTE,						0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_SHORT,						0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_INT,						0,	GL_FALSE, vertexData	},
					{ 4, GL_INT_2_10_10_10_REV,					0,	GL_FALSE, vertexData	},
					{ 4, GL_UNSIGNED_INT_2_10_10_10_REV,		0,	GL_FALSE, vertexData	},
				};

				for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx)
				{
					glVertexAttribPointer(0, pointers[ndx].size, pointers[ndx].type, pointers[ndx].normalized, pointers[ndx].stride, pointers[ndx].pointer);
					expectError(GL_NO_ERROR);

					verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_INTEGER, GL_FALSE);
				}
			}

			// test glVertexAttribIPointer
			{
				const PointerData pointers[] =
				{
					{ 1, GL_BYTE,								0,	GL_FALSE, vertexData	},
					{ 1, GL_SHORT,								0,	GL_FALSE, vertexData	},
					{ 1, GL_INT,								0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_BYTE,						0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_SHORT,						0,	GL_FALSE, vertexData	},
					{ 1, GL_UNSIGNED_INT,						0,	GL_FALSE, vertexData	},
				};

				for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx)
				{
					glVertexAttribIPointer(0, pointers[ndx].size, pointers[ndx].type, pointers[ndx].stride, pointers[ndx].pointer);
					expectError(GL_NO_ERROR);

					verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_INTEGER, GL_TRUE);
				}
			}
		}

		// Test with multiple VAOs
		{
			const tcu::ScopedLogSection section(m_log, "WithVAO", "Test with VAO");

			GLuint buf		= 0;
			GLuint vaos[2]	= {0};

			glGenVertexArrays(2, vaos);
			glGenBuffers(1, &buf);
			glBindBuffer(GL_ARRAY_BUFFER, buf);
			expectError(GL_NO_ERROR);

			// initial
			glBindVertexArray(vaos[0]);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_INTEGER, GL_FALSE);
			expectError(GL_NO_ERROR);

			// set vao 0 to some value
			glVertexAttribIPointer(0, 1, GL_INT, 0, DE_NULL);
			expectError(GL_NO_ERROR);

			// set vao 1 to some other value
			glBindVertexArray(vaos[1]);
			glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, DE_NULL);
			expectError(GL_NO_ERROR);

			// verify vao 1 state
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_INTEGER, GL_FALSE);
			expectError(GL_NO_ERROR);

			// verify vao 0 state
			glBindVertexArray(vaos[0]);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_INTEGER, GL_TRUE);
			expectError(GL_NO_ERROR);

			glDeleteVertexArrays(2, vaos);
			glDeleteBuffers(1, &buf);
			expectError(GL_NO_ERROR);
		}
	}
};

class VertexAttributeEnabledCase : public ApiCase
{
public:
	VertexAttributeEnabledCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		// VERTEX_ATTRIB_ARRAY_ENABLED

		// Test with default VAO
		{
			const tcu::ScopedLogSection section(m_log, "DefaultVAO", "Test with default VAO");

			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_ENABLED, GL_FALSE);
			glEnableVertexAttribArray(0);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_ENABLED, GL_TRUE);
			glDisableVertexAttribArray(0);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_ENABLED, GL_FALSE);
		}

		// Test with multiple VAOs
		{
			const tcu::ScopedLogSection section(m_log, "WithVAO", "Test with VAO");

			GLuint vaos[2] = {0};

			glGenVertexArrays(2, vaos);
			expectError(GL_NO_ERROR);

			// set vao 0 to some value
			glBindVertexArray(vaos[0]);
			glEnableVertexAttribArray(0);
			expectError(GL_NO_ERROR);

			// set vao 1 to some other value
			glBindVertexArray(vaos[1]);
			glDisableVertexAttribArray(0);
			expectError(GL_NO_ERROR);

			// verify vao 1 state
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_ENABLED, GL_FALSE);
			expectError(GL_NO_ERROR);

			// verify vao 0 state
			glBindVertexArray(vaos[0]);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_ENABLED, GL_TRUE);
			expectError(GL_NO_ERROR);

			glDeleteVertexArrays(2, vaos);
			expectError(GL_NO_ERROR);
		}
	}
};

class VertexAttributeDivisorCase : public ApiCase
{
public:
	VertexAttributeDivisorCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		// Test with default VAO
		{
			const tcu::ScopedLogSection section(m_log, "DefaultVAO", "Test with default VAO");

			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_DIVISOR,	0);
			glVertexAttribDivisor(0, 1);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_DIVISOR,	1);
			glVertexAttribDivisor(0, 5);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_DIVISOR,	5);
		}

		// Test with multiple VAOs
		{
			const tcu::ScopedLogSection section(m_log, "WithVAO", "Test with VAO");

			GLuint vaos[2] = {0};

			glGenVertexArrays(2, vaos);
			expectError(GL_NO_ERROR);

			// set vao 0 to some value
			glBindVertexArray(vaos[0]);
			glVertexAttribDivisor(0, 1);
			expectError(GL_NO_ERROR);

			// set vao 1 to some other value
			glBindVertexArray(vaos[1]);
			glVertexAttribDivisor(0, 5);
			expectError(GL_NO_ERROR);

			// verify vao 1 state
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_DIVISOR, 5);
			expectError(GL_NO_ERROR);

			// verify vao 0 state
			glBindVertexArray(vaos[0]);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_DIVISOR, 1);
			expectError(GL_NO_ERROR);

			glDeleteVertexArrays(2, vaos);
			expectError(GL_NO_ERROR);
		}
	}
};

class VertexAttributeBufferBindingCase : public ApiCase
{
public:
	VertexAttributeBufferBindingCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		// Test with default VAO
		{
			const tcu::ScopedLogSection section(m_log, "DefaultVAO", "Test with default VAO");

			// initial
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, 0);

			GLuint bufferID;
			glGenBuffers(1, &bufferID);
			glBindBuffer(GL_ARRAY_BUFFER, bufferID);
			expectError(GL_NO_ERROR);

			glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
			expectError(GL_NO_ERROR);

			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, bufferID);

			glDeleteBuffers(1, &bufferID);
			expectError(GL_NO_ERROR);
		}

		// Test with multiple VAOs
		{
			const tcu::ScopedLogSection section(m_log, "WithVAO", "Test with VAO");

			GLuint vaos[2] = {0};
			GLuint bufs[2] = {0};

			glGenBuffers(2, bufs);
			expectError(GL_NO_ERROR);

			glGenVertexArrays(2, vaos);
			expectError(GL_NO_ERROR);

			// set vao 0 to some value
			glBindVertexArray(vaos[0]);
			glBindBuffer(GL_ARRAY_BUFFER, bufs[0]);
			glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
			expectError(GL_NO_ERROR);

			// set vao 1 to some other value
			glBindVertexArray(vaos[1]);
			glBindBuffer(GL_ARRAY_BUFFER, bufs[1]);
			glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
			expectError(GL_NO_ERROR);

			// verify vao 1 state
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, bufs[1]);
			expectError(GL_NO_ERROR);

			// verify vao 0 state
			glBindVertexArray(vaos[0]);
			verifyVertexAttrib(m_testCtx, *this, 0, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, bufs[0]);
			expectError(GL_NO_ERROR);

			glDeleteVertexArrays(2, vaos);
			glDeleteBuffers(2, bufs);
			expectError(GL_NO_ERROR);
		}
	}
};

class VertexAttributePointerCase : public ApiCase
{
public:
	VertexAttributePointerCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		// Test with default VAO
		{
			const tcu::ScopedLogSection section(m_log, "DefaultVAO", "Test with default VAO");

			StateQueryMemoryWriteGuard<GLvoid*> initialState;
			glGetVertexAttribPointerv(0, GL_VERTEX_ATTRIB_ARRAY_POINTER, &initialState);
			initialState.verifyValidity(m_testCtx);
			checkPointerEquals(m_testCtx, initialState, 0);

			const GLfloat vertexData[4] = {0.0f}; // never accessed
			const PointerData pointers[] =
			{
				{ 1, GL_BYTE,				0,	GL_FALSE, &vertexData[2] },
				{ 1, GL_SHORT,				0,	GL_FALSE, &vertexData[1] },
				{ 1, GL_INT,				0,	GL_FALSE, &vertexData[2] },
				{ 1, GL_FIXED,				0,	GL_FALSE, &vertexData[2] },
				{ 1, GL_FIXED,				0,	GL_FALSE, &vertexData[1] },
				{ 1, GL_FLOAT,				0,	GL_FALSE, &vertexData[0] },
				{ 1, GL_FLOAT,				0,	GL_FALSE, &vertexData[3] },
				{ 1, GL_FLOAT,				0,	GL_FALSE, &vertexData[2] },
				{ 1, GL_HALF_FLOAT,			0,	GL_FALSE, &vertexData[0] },
				{ 4, GL_HALF_FLOAT,			0,	GL_FALSE, &vertexData[1] },
				{ 4, GL_HALF_FLOAT,			0,	GL_FALSE, &vertexData[2] },
			};

			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pointers); ++ndx)
			{
				glVertexAttribPointer(0, pointers[ndx].size, pointers[ndx].type, pointers[ndx].normalized, pointers[ndx].stride, pointers[ndx].pointer);
				expectError(GL_NO_ERROR);

				StateQueryMemoryWriteGuard<GLvoid*> state;
				glGetVertexAttribPointerv(0, GL_VERTEX_ATTRIB_ARRAY_POINTER, &state);
				state.verifyValidity(m_testCtx);
				checkPointerEquals(m_testCtx, state, pointers[ndx].pointer);
			}
		}

		// Test with multiple VAOs
		{
			const tcu::ScopedLogSection section(m_log, "WithVAO", "Test with VAO");

			GLuint vaos[2] = {0};
			GLuint bufs[2] = {0};

			glGenBuffers(2, bufs);
			expectError(GL_NO_ERROR);

			glGenVertexArrays(2, vaos);
			expectError(GL_NO_ERROR);

			// set vao 0 to some value
			glBindVertexArray(vaos[0]);
			glBindBuffer(GL_ARRAY_BUFFER, bufs[0]);
			glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, ((deUint8*)DE_NULL) + 8);
			expectError(GL_NO_ERROR);

			// set vao 1 to some other value
			glBindVertexArray(vaos[1]);
			glBindBuffer(GL_ARRAY_BUFFER, bufs[1]);
			glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, ((deUint8*)DE_NULL) + 4);
			expectError(GL_NO_ERROR);

			// verify vao 1 state
			{
				StateQueryMemoryWriteGuard<GLvoid*> state;
				glGetVertexAttribPointerv(0, GL_VERTEX_ATTRIB_ARRAY_POINTER, &state);
				state.verifyValidity(m_testCtx);
				checkPointerEquals(m_testCtx, state, ((deUint8*)DE_NULL) + 4);
			}
			expectError(GL_NO_ERROR);

			// verify vao 0 state
			glBindVertexArray(vaos[0]);
			{
				StateQueryMemoryWriteGuard<GLvoid*> state;
				glGetVertexAttribPointerv(0, GL_VERTEX_ATTRIB_ARRAY_POINTER, &state);
				state.verifyValidity(m_testCtx);
				checkPointerEquals(m_testCtx, state, ((deUint8*)DE_NULL) + 8);
			}
			expectError(GL_NO_ERROR);

			glDeleteVertexArrays(2, vaos);
			glDeleteBuffers(2, bufs);
			expectError(GL_NO_ERROR);
		}
	}
};

class UniformValueFloatCase : public ApiCase
{
public:
	UniformValueFloatCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		static const char* testVertSource =
			"#version 300 es\n"
			"uniform highp float floatUniform;\n"
			"uniform highp vec2 float2Uniform;\n"
			"uniform highp vec3 float3Uniform;\n"
			"uniform highp vec4 float4Uniform;\n"
			"void main (void)\n"
			"{\n"
			"	gl_Position = vec4(floatUniform + float2Uniform.x + float3Uniform.x + float4Uniform.x);\n"
			"}\n";
		static const char* testFragSource =
			"#version 300 es\n"
			"layout(location = 0) out mediump vec4 fragColor;"
			"void main (void)\n"
			"{\n"
			"	fragColor = vec4(0.0);\n"
			"}\n";

		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		glShaderSource(shaderVert, 1, &testVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &testFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		GLuint program = glCreateProgram();
		glAttachShader(program, shaderVert);
		glAttachShader(program, shaderFrag);
		glLinkProgram(program);
		glUseProgram(program);
		expectError(GL_NO_ERROR);

		GLint location;

		location = glGetUniformLocation(program,"floatUniform");
		glUniform1f(location, 1.0f);
		verifyUniformValue1f(m_testCtx, *this, program, location, 1.0f);

		location = glGetUniformLocation(program,"float2Uniform");
		glUniform2f(location, 1.0f, 2.0f);
		verifyUniformValue2f(m_testCtx, *this, program, location, 1.0f, 2.0f);

		location = glGetUniformLocation(program,"float3Uniform");
		glUniform3f(location, 1.0f, 2.0f, 3.0f);
		verifyUniformValue3f(m_testCtx, *this, program, location, 1.0f, 2.0f, 3.0f);

		location = glGetUniformLocation(program,"float4Uniform");
		glUniform4f(location, 1.0f, 2.0f, 3.0f, 4.0f);
		verifyUniformValue4f(m_testCtx, *this, program, location, 1.0f, 2.0f, 3.0f, 4.0f);

		glUseProgram(0);
		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(program);
		expectError(GL_NO_ERROR);
	}
};

class UniformValueIntCase : public ApiCase
{
public:
	UniformValueIntCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		static const char* testVertSource =
			"#version 300 es\n"
			"uniform highp int intUniform;\n"
			"uniform highp ivec2 int2Uniform;\n"
			"uniform highp ivec3 int3Uniform;\n"
			"uniform highp ivec4 int4Uniform;\n"
			"void main (void)\n"
			"{\n"
			"	gl_Position = vec4(float(intUniform + int2Uniform.x + int3Uniform.x + int4Uniform.x));\n"
			"}\n";
		static const char* testFragSource =
			"#version 300 es\n"
			"layout(location = 0) out mediump vec4 fragColor;"
			"void main (void)\n"
			"{\n"
			"	fragColor = vec4(0.0);\n"
			"}\n";

		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		glShaderSource(shaderVert, 1, &testVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &testFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		GLuint program = glCreateProgram();
		glAttachShader(program, shaderVert);
		glAttachShader(program, shaderFrag);
		glLinkProgram(program);
		glUseProgram(program);
		expectError(GL_NO_ERROR);

		GLint location;

		location = glGetUniformLocation(program,"intUniform");
		glUniform1i(location, 1);
		verifyUniformValue1i(m_testCtx, *this, program, location, 1);

		location = glGetUniformLocation(program,"int2Uniform");
		glUniform2i(location, 1, 2);
		verifyUniformValue2i(m_testCtx, *this, program, location, 1, 2);

		location = glGetUniformLocation(program,"int3Uniform");
		glUniform3i(location, 1, 2, 3);
		verifyUniformValue3i(m_testCtx, *this, program, location, 1, 2, 3);

		location = glGetUniformLocation(program,"int4Uniform");
		glUniform4i(location, 1, 2, 3, 4);
		verifyUniformValue4i(m_testCtx, *this, program, location, 1, 2, 3, 4);

		glUseProgram(0);
		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(program);
		expectError(GL_NO_ERROR);
	}
};

class UniformValueUintCase : public ApiCase
{
public:
	UniformValueUintCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		static const char* testVertSource =
			"#version 300 es\n"
			"uniform highp uint uintUniform;\n"
			"uniform highp uvec2 uint2Uniform;\n"
			"uniform highp uvec3 uint3Uniform;\n"
			"uniform highp uvec4 uint4Uniform;\n"
			"void main (void)\n"
			"{\n"
			"	gl_Position = vec4(float(uintUniform + uint2Uniform.x + uint3Uniform.x + uint4Uniform.x));\n"
			"}\n";
		static const char* testFragSource =
			"#version 300 es\n"
			"layout(location = 0) out mediump vec4 fragColor;"
			"void main (void)\n"
			"{\n"
			"	fragColor = vec4(0.0);\n"
			"}\n";

		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		glShaderSource(shaderVert, 1, &testVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &testFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		GLuint program = glCreateProgram();
		glAttachShader(program, shaderVert);
		glAttachShader(program, shaderFrag);
		glLinkProgram(program);
		glUseProgram(program);
		expectError(GL_NO_ERROR);

		GLint location;

		location = glGetUniformLocation(program,"uintUniform");
		glUniform1ui(location, 1);
		verifyUniformValue1ui(m_testCtx, *this, program, location, 1);

		location = glGetUniformLocation(program,"uint2Uniform");
		glUniform2ui(location, 1, 2);
		verifyUniformValue2ui(m_testCtx, *this, program, location, 1, 2);

		location = glGetUniformLocation(program,"uint3Uniform");
		glUniform3ui(location, 1, 2, 3);
		verifyUniformValue3ui(m_testCtx, *this, program, location, 1, 2, 3);

		location = glGetUniformLocation(program,"uint4Uniform");
		glUniform4ui(location, 1, 2, 3, 4);
		verifyUniformValue4ui(m_testCtx, *this, program, location, 1, 2, 3, 4);

		glUseProgram(0);
		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(program);
		expectError(GL_NO_ERROR);
	}
};


class UniformValueBooleanCase : public ApiCase
{
public:
	UniformValueBooleanCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		static const char* testVertSource =
			"#version 300 es\n"
			"uniform bool boolUniform;\n"
			"uniform bvec2 bool2Uniform;\n"
			"uniform bvec3 bool3Uniform;\n"
			"uniform bvec4 bool4Uniform;\n"
			"void main (void)\n"
			"{\n"
			"	gl_Position = vec4(float(boolUniform) + float(bool2Uniform.x) + float(bool3Uniform.x) + float(bool4Uniform.x));\n"
			"}\n";
		static const char* testFragSource =
			"#version 300 es\n"
			"layout(location = 0) out mediump vec4 fragColor;"
			"void main (void)\n"
			"{\n"
			"	fragColor = vec4(0.0);\n"
			"}\n";

		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		glShaderSource(shaderVert, 1, &testVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &testFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		GLuint program = glCreateProgram();
		glAttachShader(program, shaderVert);
		glAttachShader(program, shaderFrag);
		glLinkProgram(program);
		glUseProgram(program);
		expectError(GL_NO_ERROR);

		GLint location;

		// int conversion

		location = glGetUniformLocation(program,"boolUniform");
		glUniform1i(location, 1);
		verifyUniformValue1i(m_testCtx, *this, program, location, 1);

		location = glGetUniformLocation(program,"bool2Uniform");
		glUniform2i(location, 1, 2);
		verifyUniformValue2i(m_testCtx, *this, program, location, 1, 1);

		location = glGetUniformLocation(program,"bool3Uniform");
		glUniform3i(location, 0, 1, 2);
		verifyUniformValue3i(m_testCtx, *this, program, location, 0, 1, 1);

		location = glGetUniformLocation(program,"bool4Uniform");
		glUniform4i(location, 1, 0, 1, -1);
		verifyUniformValue4i(m_testCtx, *this, program, location, 1, 0, 1, 1);

		// float conversion

		location = glGetUniformLocation(program,"boolUniform");
		glUniform1f(location, 1.0f);
		verifyUniformValue1i(m_testCtx, *this, program, location, 1);

		location = glGetUniformLocation(program,"bool2Uniform");
		glUniform2f(location, 1.0f, 0.1f);
		verifyUniformValue2i(m_testCtx, *this, program, location, 1, 1);

		location = glGetUniformLocation(program,"bool3Uniform");
		glUniform3f(location, 0.0f, 0.1f, -0.1f);
		verifyUniformValue3i(m_testCtx, *this, program, location, 0, 1, 1);

		location = glGetUniformLocation(program,"bool4Uniform");
		glUniform4f(location, 1.0f, 0.0f, 0.1f, -0.9f);
		verifyUniformValue4i(m_testCtx, *this, program, location, 1, 0, 1, 1);

		glUseProgram(0);
		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(program);
		expectError(GL_NO_ERROR);
	}
};

class UniformValueSamplerCase : public ApiCase
{
public:
	UniformValueSamplerCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		static const char* testVertSource =
			"#version 300 es\n"
			"void main (void)\n"
			"{\n"
			"	gl_Position = vec4(0.0);\n"
			"}\n";
		static const char* testFragSource =
			"#version 300 es\n"
			"uniform highp sampler2D uniformSampler;\n"
			"layout(location = 0) out mediump vec4 fragColor;"
			"void main (void)\n"
			"{\n"
			"	fragColor = vec4(textureSize(uniformSampler, 0).x);\n"
			"}\n";

		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		glShaderSource(shaderVert, 1, &testVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &testFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		GLuint program = glCreateProgram();
		glAttachShader(program, shaderVert);
		glAttachShader(program, shaderFrag);
		glLinkProgram(program);
		glUseProgram(program);
		expectError(GL_NO_ERROR);

		GLint location;

		location = glGetUniformLocation(program,"uniformSampler");
		glUniform1i(location, 1);
		verifyUniformValue1i(m_testCtx, *this, program, location, 1);

		glUseProgram(0);
		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(program);
		expectError(GL_NO_ERROR);
	}
};

class UniformValueArrayCase : public ApiCase
{
public:
	UniformValueArrayCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		static const char* testVertSource =
			"#version 300 es\n"
			"uniform highp float arrayUniform[5];"
			"uniform highp vec2 array2Uniform[5];"
			"uniform highp vec3 array3Uniform[5];"
			"uniform highp vec4 array4Uniform[5];"
			"void main (void)\n"
			"{\n"
			"	gl_Position = \n"
			"		+ vec4(arrayUniform[0]		+ arrayUniform[1]		+ arrayUniform[2]		+ arrayUniform[3]		+ arrayUniform[4])\n"
			"		+ vec4(array2Uniform[0].x	+ array2Uniform[1].x	+ array2Uniform[2].x	+ array2Uniform[3].x	+ array2Uniform[4].x)\n"
			"		+ vec4(array3Uniform[0].x	+ array3Uniform[1].x	+ array3Uniform[2].x	+ array3Uniform[3].x	+ array3Uniform[4].x)\n"
			"		+ vec4(array4Uniform[0].x	+ array4Uniform[1].x	+ array4Uniform[2].x	+ array4Uniform[3].x	+ array4Uniform[4].x);\n"
			"}\n";
		static const char* testFragSource =
			"#version 300 es\n"
			"layout(location = 0) out mediump vec4 fragColor;"
			"void main (void)\n"
			"{\n"
			"	fragColor = vec4(0.0);\n"
			"}\n";

		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		glShaderSource(shaderVert, 1, &testVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &testFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		GLuint program = glCreateProgram();
		glAttachShader(program, shaderVert);
		glAttachShader(program, shaderFrag);
		glLinkProgram(program);
		glUseProgram(program);
		expectError(GL_NO_ERROR);

		GLint location;

		float uniformValue[5 * 4] =
		{
			-1.0f,	0.1f,	4.0f,	800.0f,
			13.0f,	55.0f,	12.0f,	91.0f,
			-55.1f,	1.1f,	98.0f,	19.0f,
			41.0f,	65.0f,	4.0f,	12.2f,
			95.0f,	77.0f,	32.0f,	48.0f
		};

		location = glGetUniformLocation(program,"arrayUniform");
		glUniform1fv(location, 5, uniformValue);
		expectError(GL_NO_ERROR);

		verifyUniformValue1f(m_testCtx, *this, program, glGetUniformLocation(program,"arrayUniform[0]"), uniformValue[0]);
		verifyUniformValue1f(m_testCtx, *this, program, glGetUniformLocation(program,"arrayUniform[1]"), uniformValue[1]);
		verifyUniformValue1f(m_testCtx, *this, program, glGetUniformLocation(program,"arrayUniform[2]"), uniformValue[2]);
		verifyUniformValue1f(m_testCtx, *this, program, glGetUniformLocation(program,"arrayUniform[3]"), uniformValue[3]);
		verifyUniformValue1f(m_testCtx, *this, program, glGetUniformLocation(program,"arrayUniform[4]"), uniformValue[4]);
		expectError(GL_NO_ERROR);

		location = glGetUniformLocation(program,"array2Uniform");
		glUniform2fv(location, 5, uniformValue);
		expectError(GL_NO_ERROR);

		verifyUniformValue2f(m_testCtx, *this, program, glGetUniformLocation(program,"array2Uniform[0]"), uniformValue[2 * 0], uniformValue[(2 * 0) + 1]);
		verifyUniformValue2f(m_testCtx, *this, program, glGetUniformLocation(program,"array2Uniform[1]"), uniformValue[2 * 1], uniformValue[(2 * 1) + 1]);
		verifyUniformValue2f(m_testCtx, *this, program, glGetUniformLocation(program,"array2Uniform[2]"), uniformValue[2 * 2], uniformValue[(2 * 2) + 1]);
		verifyUniformValue2f(m_testCtx, *this, program, glGetUniformLocation(program,"array2Uniform[3]"), uniformValue[2 * 3], uniformValue[(2 * 3) + 1]);
		verifyUniformValue2f(m_testCtx, *this, program, glGetUniformLocation(program,"array2Uniform[4]"), uniformValue[2 * 4], uniformValue[(2 * 4) + 1]);
		expectError(GL_NO_ERROR);

		location = glGetUniformLocation(program,"array3Uniform");
		glUniform3fv(location, 5, uniformValue);
		expectError(GL_NO_ERROR);

		verifyUniformValue3f(m_testCtx, *this, program, glGetUniformLocation(program,"array3Uniform[0]"), uniformValue[3 * 0], uniformValue[(3 * 0) + 1], uniformValue[(3 * 0) + 2]);
		verifyUniformValue3f(m_testCtx, *this, program, glGetUniformLocation(program,"array3Uniform[1]"), uniformValue[3 * 1], uniformValue[(3 * 1) + 1], uniformValue[(3 * 1) + 2]);
		verifyUniformValue3f(m_testCtx, *this, program, glGetUniformLocation(program,"array3Uniform[2]"), uniformValue[3 * 2], uniformValue[(3 * 2) + 1], uniformValue[(3 * 2) + 2]);
		verifyUniformValue3f(m_testCtx, *this, program, glGetUniformLocation(program,"array3Uniform[3]"), uniformValue[3 * 3], uniformValue[(3 * 3) + 1], uniformValue[(3 * 3) + 2]);
		verifyUniformValue3f(m_testCtx, *this, program, glGetUniformLocation(program,"array3Uniform[4]"), uniformValue[3 * 4], uniformValue[(3 * 4) + 1], uniformValue[(3 * 4) + 2]);
		expectError(GL_NO_ERROR);

		location = glGetUniformLocation(program,"array4Uniform");
		glUniform4fv(location, 5, uniformValue);
		expectError(GL_NO_ERROR);

		verifyUniformValue4f(m_testCtx, *this, program, glGetUniformLocation(program,"array4Uniform[0]"), uniformValue[4 * 0], uniformValue[(4 * 0) + 1], uniformValue[(4 * 0) + 2], uniformValue[(4 * 0) + 3]);
		verifyUniformValue4f(m_testCtx, *this, program, glGetUniformLocation(program,"array4Uniform[1]"), uniformValue[4 * 1], uniformValue[(4 * 1) + 1], uniformValue[(4 * 1) + 2], uniformValue[(4 * 1) + 3]);
		verifyUniformValue4f(m_testCtx, *this, program, glGetUniformLocation(program,"array4Uniform[2]"), uniformValue[4 * 2], uniformValue[(4 * 2) + 1], uniformValue[(4 * 2) + 2], uniformValue[(4 * 2) + 3]);
		verifyUniformValue4f(m_testCtx, *this, program, glGetUniformLocation(program,"array4Uniform[3]"), uniformValue[4 * 3], uniformValue[(4 * 3) + 1], uniformValue[(4 * 3) + 2], uniformValue[(4 * 3) + 3]);
		verifyUniformValue4f(m_testCtx, *this, program, glGetUniformLocation(program,"array4Uniform[4]"), uniformValue[4 * 4], uniformValue[(4 * 4) + 1], uniformValue[(4 * 4) + 2], uniformValue[(4 * 4) + 3]);
		expectError(GL_NO_ERROR);

		glUseProgram(0);
		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(program);
		expectError(GL_NO_ERROR);
	}
};

class UniformValueMatrixCase : public ApiCase
{
public:
	UniformValueMatrixCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		static const char* testVertSource =
			"#version 300 es\n"
			"uniform highp mat2 mat2Uniform;"
			"uniform highp mat3 mat3Uniform;"
			"uniform highp mat4 mat4Uniform;"
			"void main (void)\n"
			"{\n"
			"	gl_Position = vec4(mat2Uniform[0][0] + mat3Uniform[0][0] + mat4Uniform[0][0]);\n"
			"}\n";
		static const char* testFragSource =
			"#version 300 es\n"
			"layout(location = 0) out mediump vec4 fragColor;"
			"void main (void)\n"
			"{\n"
			"	fragColor = vec4(0.0);\n"
			"}\n";

		GLuint shaderVert = glCreateShader(GL_VERTEX_SHADER);
		GLuint shaderFrag = glCreateShader(GL_FRAGMENT_SHADER);

		glShaderSource(shaderVert, 1, &testVertSource, DE_NULL);
		glShaderSource(shaderFrag, 1, &testFragSource, DE_NULL);

		glCompileShader(shaderVert);
		glCompileShader(shaderFrag);
		expectError(GL_NO_ERROR);

		GLuint program = glCreateProgram();
		glAttachShader(program, shaderVert);
		glAttachShader(program, shaderFrag);
		glLinkProgram(program);
		glUseProgram(program);
		expectError(GL_NO_ERROR);

		GLint location;

		float matrixValues[4 * 4] =
		{
			-1.0f,	0.1f,	4.0f,	800.0f,
			13.0f,	55.0f,	12.0f,	91.0f,
			-55.1f,	1.1f,	98.0f,	19.0f,
			41.0f,	65.0f,	4.0f,	12.2f,
		};

		// the values of the matrix are returned in column major order but they can be given in either order

		location = glGetUniformLocation(program,"mat2Uniform");
		glUniformMatrix2fv(location, 1, GL_FALSE, matrixValues);
		verifyUniformMatrixValues<2>(m_testCtx, *this, program, location, matrixValues, false);
		glUniformMatrix2fv(location, 1, GL_TRUE, matrixValues);
		verifyUniformMatrixValues<2>(m_testCtx, *this, program, location, matrixValues, true);

		location = glGetUniformLocation(program,"mat3Uniform");
		glUniformMatrix3fv(location, 1, GL_FALSE, matrixValues);
		verifyUniformMatrixValues<3>(m_testCtx, *this, program, location, matrixValues, false);
		glUniformMatrix3fv(location, 1, GL_TRUE, matrixValues);
		verifyUniformMatrixValues<3>(m_testCtx, *this, program, location, matrixValues, true);

		location = glGetUniformLocation(program,"mat4Uniform");
		glUniformMatrix4fv(location, 1, GL_FALSE, matrixValues);
		verifyUniformMatrixValues<4>(m_testCtx, *this, program, location, matrixValues, false);
		glUniformMatrix4fv(location, 1, GL_TRUE, matrixValues);
		verifyUniformMatrixValues<4>(m_testCtx, *this, program, location, matrixValues, true);

		glUseProgram(0);
		glDeleteShader(shaderVert);
		glDeleteShader(shaderFrag);
		glDeleteProgram(program);
		expectError(GL_NO_ERROR);
	}
};

class PrecisionFormatCase : public ApiCase
{
public:
	struct RequiredFormat
	{
		int negativeRange;
		int positiveRange;
		int precision;
	};

	PrecisionFormatCase (Context& context, const char* name, const char* description, glw::GLenum shaderType, glw::GLenum precisionType)
		: ApiCase			(context, name, description)
		, m_shaderType		(shaderType)
		, m_precisionType	(precisionType)
	{
	}

private:
	void test (void)
	{
		const RequiredFormat											expected = getRequiredFormat();
		bool															error = false;
		gls::StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLboolean>	shaderCompiler;
		gls::StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint[2]>	range;
		gls::StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint>		precision;

		// query values
		glGetShaderPrecisionFormat(m_shaderType, m_precisionType, range, &precision);
		expectError(GL_NO_ERROR);

		if (!range.verifyValidity(m_testCtx))
			return;
		if (!precision.verifyValidity(m_testCtx))
			return;

		m_log
			<< tcu::TestLog::Message
			<< "range[0] = " << range[0] << "\n"
			<< "range[1] = " << range[1] << "\n"
			<< "precision = " << precision
			<< tcu::TestLog::EndMessage;

		// verify values

		if (m_precisionType == GL_HIGH_FLOAT)
		{
			// highp float must be IEEE 754 single

			if (range[0] != expected.negativeRange ||
				range[1] != expected.positiveRange ||
				precision != expected.precision)
			{
				m_log
					<< tcu::TestLog::Message
					<< "// ERROR: Invalid precision format, expected:\n"
					<< "\trange[0] = " << expected.negativeRange << "\n"
					<< "\trange[1] = " << expected.positiveRange << "\n"
					<< "\tprecision = " << expected.precision
					<< tcu::TestLog::EndMessage;
				error = true;
			}
		}
		else
		{
			if (range[0] < expected.negativeRange)
			{
				m_log << tcu::TestLog::Message << "// ERROR: Invalid range[0], expected greater or equal to " << expected.negativeRange << tcu::TestLog::EndMessage;
				error = true;
			}

			if (range[1] < expected.positiveRange)
			{
				m_log << tcu::TestLog::Message << "// ERROR: Invalid range[1], expected greater or equal to " << expected.positiveRange << tcu::TestLog::EndMessage;
				error = true;
			}

			if (precision < expected.precision)
			{
				m_log << tcu::TestLog::Message << "// ERROR: Invalid precision, expected greater or equal to " << expected.precision << tcu::TestLog::EndMessage;
				error = true;
			}
		}

		if (error)
			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid precision/range");
	}

	RequiredFormat getRequiredFormat (void) const
	{
		// Precisions for different types.
		const RequiredFormat requirements[] =
		{
			{   0,   0,  8 }, //!< lowp float
			{  13,  13, 10 }, //!< mediump float
			{ 127, 127, 23 }, //!< highp float
			{   8,   7,  0 }, //!< lowp int
			{  15,  14,  0 }, //!< mediump int
			{  31,  30,  0 }, //!< highp int
		};
		const int ndx = (int)m_precisionType - (int)GL_LOW_FLOAT;

		DE_ASSERT(ndx >= 0);
		DE_ASSERT(ndx < DE_LENGTH_OF_ARRAY(requirements));
		return requirements[ndx];
	}

	const glw::GLenum m_shaderType;
	const glw::GLenum m_precisionType;
};

} // anonymous


ShaderStateQueryTests::ShaderStateQueryTests (Context& context)
	: TestCaseGroup(context, "shader", "Shader State Query tests")
{
}

void ShaderStateQueryTests::init (void)
{
	// shader
	addChild(new ShaderTypeCase						(m_context, "shader_type",							"SHADER_TYPE"));
	addChild(new ShaderCompileStatusCase			(m_context, "shader_compile_status",				"COMPILE_STATUS"));
	addChild(new ShaderInfoLogCase					(m_context, "shader_info_log_length",				"INFO_LOG_LENGTH"));
	addChild(new ShaderSourceCase					(m_context, "shader_source_length",					"SHADER_SOURCE_LENGTH"));

	// shader and program
	addChild(new DeleteStatusCase					(m_context, "delete_status",						"DELETE_STATUS"));

	// vertex-attrib
	addChild(new CurrentVertexAttribInitialCase		(m_context, "current_vertex_attrib_initial",		"CURRENT_VERTEX_ATTRIB"));
	addChild(new CurrentVertexAttribFloatCase		(m_context, "current_vertex_attrib_float",			"CURRENT_VERTEX_ATTRIB"));
	addChild(new CurrentVertexAttribIntCase			(m_context, "current_vertex_attrib_int",			"CURRENT_VERTEX_ATTRIB"));
	addChild(new CurrentVertexAttribUintCase		(m_context, "current_vertex_attrib_uint",			"CURRENT_VERTEX_ATTRIB"));
	addChild(new CurrentVertexAttribConversionCase	(m_context, "current_vertex_attrib_float_to_int",	"CURRENT_VERTEX_ATTRIB"));

	// program
	addChild(new ProgramInfoLogCase					(m_context, "program_info_log_length",				"INFO_LOG_LENGTH",	ProgramInfoLogCase::BUILDERROR_COMPILE));
	addChild(new ProgramInfoLogCase					(m_context, "program_info_log_length_link_error",	"INFO_LOG_LENGTH",	ProgramInfoLogCase::BUILDERROR_LINK));
	addChild(new ProgramValidateStatusCase			(m_context, "program_validate_status",				"VALIDATE_STATUS"));
	addChild(new ProgramAttachedShadersCase			(m_context, "program_attached_shaders",				"ATTACHED_SHADERS"));

	addChild(new ProgramActiveUniformNameCase		(m_context, "program_active_uniform_name",			"ACTIVE_UNIFORMS and ACTIVE_UNIFORM_MAX_LENGTH"));
	addChild(new ProgramUniformCase					(m_context, "program_active_uniform_types",			"UNIFORM_TYPE, UNIFORM_SIZE, and UNIFORM_IS_ROW_MAJOR"));
	addChild(new ProgramActiveUniformBlocksCase		(m_context, "program_active_uniform_blocks",		"ACTIVE_UNIFORM_BLOCK_x"));
	addChild(new ProgramBinaryCase					(m_context, "program_binary",						"PROGRAM_BINARY_LENGTH and PROGRAM_BINARY_RETRIEVABLE_HINT"));

	// transform feedback
	addChild(new TransformFeedbackCase				(m_context, "transform_feedback",					"TRANSFORM_FEEDBACK_BUFFER_MODE, TRANSFORM_FEEDBACK_VARYINGS, TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH"));

	// attribute related
	addChild(new ActiveAttributesCase				(m_context, "active_attributes",					"ACTIVE_ATTRIBUTES and ACTIVE_ATTRIBUTE_MAX_LENGTH"));
	addChild(new VertexAttributeSizeCase			(m_context, "vertex_attrib_size",					"VERTEX_ATTRIB_ARRAY_SIZE"));
	addChild(new VertexAttributeTypeCase			(m_context, "vertex_attrib_type",					"VERTEX_ATTRIB_ARRAY_TYPE"));
	addChild(new VertexAttributeStrideCase			(m_context, "vertex_attrib_stride",					"VERTEX_ATTRIB_ARRAY_STRIDE"));
	addChild(new VertexAttributeNormalizedCase		(m_context, "vertex_attrib_normalized",				"VERTEX_ATTRIB_ARRAY_NORMALIZED"));
	addChild(new VertexAttributeIntegerCase			(m_context, "vertex_attrib_integer",				"VERTEX_ATTRIB_ARRAY_INTEGER"));
	addChild(new VertexAttributeEnabledCase			(m_context, "vertex_attrib_array_enabled",			"VERTEX_ATTRIB_ARRAY_ENABLED"));
	addChild(new VertexAttributeDivisorCase			(m_context, "vertex_attrib_array_divisor",			"VERTEX_ATTRIB_ARRAY_DIVISOR"));
	addChild(new VertexAttributeBufferBindingCase	(m_context, "vertex_attrib_array_buffer_binding",	"VERTEX_ATTRIB_ARRAY_BUFFER_BINDING"));
	addChild(new VertexAttributePointerCase			(m_context, "vertex_attrib_pointerv",				"GetVertexAttribPointerv"));

	// uniform values
	addChild(new UniformValueFloatCase				(m_context, "uniform_value_float",					"GetUniform*"));
	addChild(new UniformValueIntCase				(m_context, "uniform_value_int",					"GetUniform*"));
	addChild(new UniformValueUintCase				(m_context, "uniform_value_uint",					"GetUniform*"));
	addChild(new UniformValueBooleanCase			(m_context, "uniform_value_boolean",				"GetUniform*"));
	addChild(new UniformValueSamplerCase			(m_context, "uniform_value_sampler",				"GetUniform*"));
	addChild(new UniformValueArrayCase				(m_context, "uniform_value_array",					"GetUniform*"));
	addChild(new UniformValueMatrixCase				(m_context, "uniform_value_matrix",					"GetUniform*"));

	// precision format query
	addChild(new PrecisionFormatCase				(m_context, "precision_vertex_lowp_float",			"GetShaderPrecisionFormat",		GL_VERTEX_SHADER,	GL_LOW_FLOAT));
	addChild(new PrecisionFormatCase				(m_context, "precision_vertex_mediump_float",		"GetShaderPrecisionFormat",		GL_VERTEX_SHADER,	GL_MEDIUM_FLOAT));
	addChild(new PrecisionFormatCase				(m_context, "precision_vertex_highp_float",			"GetShaderPrecisionFormat",		GL_VERTEX_SHADER,	GL_HIGH_FLOAT));
	addChild(new PrecisionFormatCase				(m_context, "precision_vertex_lowp_int",			"GetShaderPrecisionFormat",		GL_VERTEX_SHADER,	GL_LOW_INT));
	addChild(new PrecisionFormatCase				(m_context, "precision_vertex_mediump_int",			"GetShaderPrecisionFormat",		GL_VERTEX_SHADER,	GL_MEDIUM_INT));
	addChild(new PrecisionFormatCase				(m_context, "precision_vertex_highp_int",			"GetShaderPrecisionFormat",		GL_VERTEX_SHADER,	GL_HIGH_INT));
	addChild(new PrecisionFormatCase				(m_context, "precision_fragment_lowp_float",		"GetShaderPrecisionFormat",		GL_FRAGMENT_SHADER,	GL_LOW_FLOAT));
	addChild(new PrecisionFormatCase				(m_context, "precision_fragment_mediump_float",		"GetShaderPrecisionFormat",		GL_FRAGMENT_SHADER,	GL_MEDIUM_FLOAT));
	addChild(new PrecisionFormatCase				(m_context, "precision_fragment_highp_float",		"GetShaderPrecisionFormat",		GL_FRAGMENT_SHADER,	GL_HIGH_FLOAT));
	addChild(new PrecisionFormatCase				(m_context, "precision_fragment_lowp_int",			"GetShaderPrecisionFormat",		GL_FRAGMENT_SHADER,	GL_LOW_INT));
	addChild(new PrecisionFormatCase				(m_context, "precision_fragment_mediump_int",		"GetShaderPrecisionFormat",		GL_FRAGMENT_SHADER,	GL_MEDIUM_INT));
	addChild(new PrecisionFormatCase				(m_context, "precision_fragment_highp_int",			"GetShaderPrecisionFormat",		GL_FRAGMENT_SHADER,	GL_HIGH_INT));
}

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