/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.1 Module
 * -------------------------------------------------
 *
 * Copyright 2015 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 Program State Query tests.
 *//*--------------------------------------------------------------------*/

#include "es31fProgramStateQueryTests.hpp"
#include "es31fInfoLogQueryShared.hpp"
#include "glsStateQueryUtil.hpp"
#include "gluRenderContext.hpp"
#include "gluCallLogWrapper.hpp"
#include "gluContextInfo.hpp"
#include "gluObjectWrapper.hpp"
#include "gluShaderProgram.hpp"
#include "glwFunctions.hpp"
#include "glwEnums.hpp"
#include "tcuStringTemplate.hpp"

namespace deqp
{

using std::string;
using std::map;

namespace gles31
{
namespace Functional
{
namespace
{

using namespace gls::StateQueryUtil;

static const char* getVerifierSuffix (QueryType type)
{
	switch (type)
	{
		case QUERY_PROGRAM_INTEGER_VEC3:
		case QUERY_PROGRAM_INTEGER:
			return "get_programiv";

		default:
			DE_ASSERT(DE_FALSE);
			return DE_NULL;
	}
}

static std::string specializeShader(Context& context, const char* code)
{
	const glu::GLSLVersion				glslVersion			= glu::getContextTypeGLSLVersion(context.getRenderContext().getType());
	std::map<std::string, std::string>	specializationMap;

	specializationMap["GLSL_VERSION_DECL"] = glu::getGLSLVersionDeclaration(glslVersion);

	if (glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2)))
	{
		specializationMap["GEOMETRY_SHADER_REQUIRE"] = "";
		specializationMap["TESSELLATION_SHADER_REQUIRE"] = "";
	}
	else
	{
		specializationMap["GEOMETRY_SHADER_REQUIRE"] = "#extension GL_EXT_geometry_shader : require";
		specializationMap["TESSELLATION_SHADER_REQUIRE"] = "#extension GL_EXT_tessellation_shader : require";
	}

	return tcu::StringTemplate(code).specialize(specializationMap);
}


class GeometryShaderCase : public TestCase
{
public:
						GeometryShaderCase		(Context& context, QueryType verifier, const char* name, const char* desc);
	IterateResult		iterate					(void);

private:
	const QueryType		m_verifier;
};

GeometryShaderCase::GeometryShaderCase (Context& context, QueryType verifier, const char* name, const char* desc)
	: TestCase		(context, name, desc)
	, m_verifier	(verifier)
{
}

GeometryShaderCase::IterateResult GeometryShaderCase::iterate (void)
{
	const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));

	if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader"))
		TCU_THROW(NotSupportedError, "Geometry shader tests require GL_EXT_geometry_shader extension or an OpenGL ES 3.2 or higher context.");


	static const char* const	s_vtxFragTemplate	=	"${GLSL_VERSION_DECL}\n"
														"void main()\n"
														"{\n"
														"}\n";

	static const char* const	s_geometryTemplate1	=	"${GLSL_VERSION_DECL}\n"
														"${GEOMETRY_SHADER_REQUIRE}\n"
														"layout(triangles) in;"
														"layout(triangle_strip, max_vertices = 3) out;\n"
														"void main()\n"
														"{\n"
														"   EndPrimitive();\n"
														"}\n";

	static const char* const	s_geometryTemplate2	=	"${GLSL_VERSION_DECL}\n"
														"${GEOMETRY_SHADER_REQUIRE}\n"
														"layout(points) in;"
														"layout(line_strip, max_vertices = 5) out;\n"
														"void main()\n"
														"{\n"
														"   EndPrimitive();\n"
														"}\n";

	static const char* const	s_geometryTemplate3	=	"${GLSL_VERSION_DECL}\n"
														"${GEOMETRY_SHADER_REQUIRE}\n"
														"layout(points) in;"
														"layout(points, max_vertices = 50) out;\n"
														"void main()\n"
														"{\n"
														"   EndPrimitive();\n"
														"}\n";

	glu::CallLogWrapper			gl						(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	tcu::ResultCollector		result					(m_testCtx.getLog(), " // ERROR: ");

	gl.enableLogging(true);

	{
		const tcu::ScopedLogSection	section		(m_testCtx.getLog(), "Layout", "triangles in, triangle strip out, 3 vertices");
		glu::ShaderProgram			program		(m_context.getRenderContext(), glu::ProgramSources()
			<< glu::VertexSource(specializeShader(m_context, s_vtxFragTemplate))
			<< glu::FragmentSource(specializeShader(m_context, s_vtxFragTemplate))
			<< glu::GeometrySource(specializeShader(m_context, s_geometryTemplate1)));

		TCU_CHECK_MSG(program.isOk(), "Compile failed");

		m_testCtx.getLog() << program;

		verifyStateProgramInteger(result, gl, program.getProgram(), GL_GEOMETRY_VERTICES_OUT, 3, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_GEOMETRY_INPUT_TYPE, GL_TRIANGLES, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_GEOMETRY_OUTPUT_TYPE, GL_TRIANGLE_STRIP, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_GEOMETRY_SHADER_INVOCATIONS, 1, m_verifier);
	}

	{
		const tcu::ScopedLogSection	section		(m_testCtx.getLog(), "Layout", "points in, line strip out, 5 vertices");
		glu::ShaderProgram			program		(m_context.getRenderContext(), glu::ProgramSources()
			<< glu::VertexSource(specializeShader(m_context, s_vtxFragTemplate))
			<< glu::FragmentSource(specializeShader(m_context, s_vtxFragTemplate))
			<< glu::GeometrySource(specializeShader(m_context, s_geometryTemplate2)));

		TCU_CHECK_MSG(program.isOk(), "Compile failed");

		m_testCtx.getLog() << program;

		verifyStateProgramInteger(result, gl, program.getProgram(), GL_GEOMETRY_VERTICES_OUT, 5, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_GEOMETRY_INPUT_TYPE, GL_POINTS, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_GEOMETRY_OUTPUT_TYPE, GL_LINE_STRIP, m_verifier);
	}

	{
		const tcu::ScopedLogSection	section		(m_testCtx.getLog(), "Layout", "points in, points out, 50 vertices");
		glu::ShaderProgram			program		(m_context.getRenderContext(), glu::ProgramSources()
			<< glu::VertexSource(specializeShader(m_context, s_vtxFragTemplate))
			<< glu::FragmentSource(specializeShader(m_context, s_vtxFragTemplate))
			<< glu::GeometrySource(specializeShader(m_context, s_geometryTemplate3)));

		TCU_CHECK_MSG(program.isOk(), "Compile failed");

		m_testCtx.getLog() << program;

		verifyStateProgramInteger(result, gl, program.getProgram(), GL_GEOMETRY_VERTICES_OUT, 50, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_GEOMETRY_INPUT_TYPE, GL_POINTS, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_GEOMETRY_OUTPUT_TYPE, GL_POINTS, m_verifier);
	}

	result.setTestContextResult(m_testCtx);
	return STOP;
}

class TessellationShaderCase : public TestCase
{
public:
						TessellationShaderCase		(Context& context, QueryType verifier, const char* name, const char* desc);
	IterateResult		iterate					(void);

private:
	const QueryType		m_verifier;
};

TessellationShaderCase::TessellationShaderCase (Context& context, QueryType verifier, const char* name, const char* desc)
	: TestCase		(context, name, desc)
	, m_verifier	(verifier)
{
}

TessellationShaderCase::IterateResult TessellationShaderCase::iterate (void)
{
	const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));

	if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
		TCU_THROW(NotSupportedError, "Tessellation shader tests require GL_EXT_tessellation_shader extension or an OpenGL ES 3.2 or higher context.");


	static const char* const	s_vtxFragTemplate	=	"${GLSL_VERSION_DECL}\n"
														"void main()\n"
														"{\n"
														"}\n";

	static const char* const	s_tessCtrlTemplate1	=	"${GLSL_VERSION_DECL}\n"
														"${TESSELLATION_SHADER_REQUIRE}\n"
														"layout(vertices = 3) out;\n"
														"void main()\n"
														"{\n"
														"}\n";

	static const char* const	s_tessEvalTemplate1	=	"${GLSL_VERSION_DECL}\n"
														"${TESSELLATION_SHADER_REQUIRE}\n"
														"layout(triangles, equal_spacing, cw) in;\n"
														"void main()\n"
														"{\n"
														"}\n";

	static const char* const	s_tessCtrlTemplate2	=	"${GLSL_VERSION_DECL}\n"
														"${TESSELLATION_SHADER_REQUIRE}\n"
														"layout(vertices = 5) out;\n"
														"void main()\n"
														"{\n"
														"}\n";

	static const char* const	s_tessEvalTemplate2	=	"${GLSL_VERSION_DECL}\n"
														"${TESSELLATION_SHADER_REQUIRE}\n"
														"layout(quads, fractional_even_spacing, ccw) in;\n"
														"void main()\n"
														"{\n"
														"}\n";

	static const char* const	s_tessEvalTemplate3	=	"${GLSL_VERSION_DECL}\n"
														"${TESSELLATION_SHADER_REQUIRE}\n"
														"layout(isolines, fractional_odd_spacing, ccw, point_mode) in;\n"
														"void main()\n"
														"{\n"
														"}\n";

	glu::CallLogWrapper			gl						(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	tcu::ResultCollector		result					(m_testCtx.getLog(), " // ERROR: ");

	gl.enableLogging(true);

	{
		const tcu::ScopedLogSection	section		(m_testCtx.getLog(), "Query State", "3 vertices, triangles, equal_spacing, cw");
		glu::ShaderProgram			program		(m_context.getRenderContext(), glu::ProgramSources()
			<< glu::VertexSource(specializeShader(m_context, s_vtxFragTemplate))
			<< glu::FragmentSource(specializeShader(m_context, s_vtxFragTemplate))
			<< glu::TessellationControlSource(specializeShader(m_context, s_tessCtrlTemplate1))
			<< glu::TessellationEvaluationSource(specializeShader(m_context, s_tessEvalTemplate1)));

		TCU_CHECK_MSG(program.isOk(), "Compile failed");

		m_testCtx.getLog() << program;

		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_CONTROL_OUTPUT_VERTICES, 3, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_MODE, GL_TRIANGLES, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_SPACING, GL_EQUAL, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_VERTEX_ORDER, GL_CW, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_POINT_MODE, GL_FALSE, m_verifier);
	}

	{
		const tcu::ScopedLogSection	section		(m_testCtx.getLog(), "Query State", "5 vertices, quads, fractional_even_spacing, ccw");
		glu::ShaderProgram			program		(m_context.getRenderContext(), glu::ProgramSources()
			<< glu::VertexSource(specializeShader(m_context, s_vtxFragTemplate))
			<< glu::FragmentSource(specializeShader(m_context, s_vtxFragTemplate))
			<< glu::TessellationControlSource(specializeShader(m_context, s_tessCtrlTemplate2))
			<< glu::TessellationEvaluationSource(specializeShader(m_context, s_tessEvalTemplate2)));

		TCU_CHECK_MSG(program.isOk(), "Compile failed");

		m_testCtx.getLog() << program;

		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_CONTROL_OUTPUT_VERTICES, 5, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_MODE, GL_QUADS, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_SPACING, GL_FRACTIONAL_EVEN, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_VERTEX_ORDER, GL_CCW, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_POINT_MODE, GL_FALSE, m_verifier);
	}

	{
		const tcu::ScopedLogSection	section		(m_testCtx.getLog(), "Query State", "5 vertices, isolines, fractional_odd_spacing, ccw, point_mode");
		glu::ShaderProgram			program		(m_context.getRenderContext(), glu::ProgramSources()
			<< glu::VertexSource(specializeShader(m_context, s_vtxFragTemplate))
			<< glu::FragmentSource(specializeShader(m_context, s_vtxFragTemplate))
			<< glu::TessellationControlSource(specializeShader(m_context, s_tessCtrlTemplate2))
			<< glu::TessellationEvaluationSource(specializeShader(m_context, s_tessEvalTemplate3)));

		TCU_CHECK_MSG(program.isOk(), "Compile failed");

		m_testCtx.getLog() << program;

		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_CONTROL_OUTPUT_VERTICES, 5, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_MODE, GL_ISOLINES, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_SPACING, GL_FRACTIONAL_ODD, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_VERTEX_ORDER, GL_CCW, m_verifier);
		verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_POINT_MODE, GL_TRUE, m_verifier);
	}

	result.setTestContextResult(m_testCtx);
	return STOP;
}

class ProgramSeparableCase : public TestCase
{
public:
						ProgramSeparableCase	(Context& context, QueryType verifier, const char* name, const char* desc);
	IterateResult		iterate					(void);

private:
	const QueryType		m_verifier;
};

ProgramSeparableCase::ProgramSeparableCase (Context& context, QueryType verifier, const char* name, const char* desc)
	: TestCase		(context, name, desc)
	, m_verifier	(verifier)
{
}

ProgramSeparableCase::IterateResult ProgramSeparableCase::iterate (void)
{
	const string				vtxTemplate	=	"${GLSL_VERSION_DECL}\n"
												"out highp vec4 v_color;\n"
												"void main()\n"
												"{\n"
												"	gl_Position = vec4(float(gl_VertexID) * 0.5, float(gl_VertexID+1) * 0.5, 0.0, 1.0);\n"
												"	v_color = vec4(float(gl_VertexID), 1.0, 0.0, 1.0);\n"
												"}\n";
	const string				fragTemplate =	"${GLSL_VERSION_DECL}\n"
												"in highp vec4 v_color;\n"
												"layout(location=0) out highp vec4 o_color;\n"
												"void main()\n"
												"{\n"
												"	o_color = v_color;\n"
												"}\n";

	glu::CallLogWrapper			gl				(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	tcu::ResultCollector		result			(m_testCtx.getLog(), " // ERROR: ");
	glu::Shader					vtxShader		(m_context.getRenderContext(), glu::SHADERTYPE_VERTEX);
	glu::Shader					frgShader		(m_context.getRenderContext(), glu::SHADERTYPE_FRAGMENT);

	const std::string			vStr			= specializeShader(m_context, vtxTemplate.c_str());
	const std::string			fStr			= specializeShader(m_context, fragTemplate.c_str());
	const char* const			vtxSourcePtr	= vStr.c_str();
	const char* const			fragSourcePtr	= fStr.c_str();

	vtxShader.setSources(1, &vtxSourcePtr, DE_NULL);
	frgShader.setSources(1, &fragSourcePtr, DE_NULL);

	vtxShader.compile();
	frgShader.compile();

	{
		const tcu::ScopedLogSection section(m_testCtx.getLog(), "VtxShader", "Vertex shader");
		m_testCtx.getLog() << vtxShader;
	}

	{
		const tcu::ScopedLogSection section(m_testCtx.getLog(), "FrgShader", "Fragment shader");
		m_testCtx.getLog() << frgShader;
	}

	TCU_CHECK_MSG(vtxShader.getCompileStatus() && frgShader.getCompileStatus(), "failed to build shaders");

	gl.enableLogging(true);

	{
		const tcu::ScopedLogSection	section	(m_testCtx.getLog(), "Initial", "Initial");
		glu::Program				program	(m_context.getRenderContext());

		verifyStateProgramInteger(result, gl, program.getProgram(), GL_PROGRAM_SEPARABLE, 0, m_verifier);
	}

	{
		const tcu::ScopedLogSection section		(m_testCtx.getLog(), "SetFalse", "SetFalse");
		glu::Program				program		(m_context.getRenderContext());
		int							linkStatus	= 0;

		gl.glAttachShader(program.getProgram(), vtxShader.getShader());
		gl.glAttachShader(program.getProgram(), frgShader.getShader());
		gl.glProgramParameteri(program.getProgram(), GL_PROGRAM_SEPARABLE, GL_FALSE);
		gl.glLinkProgram(program.getProgram());
		GLU_EXPECT_NO_ERROR(gl.glGetError(), "setup program");

		gl.glGetProgramiv(program.getProgram(), GL_LINK_STATUS, &linkStatus);
		GLU_EXPECT_NO_ERROR(gl.glGetError(), "query link status");

		TCU_CHECK_MSG(linkStatus == GL_TRUE, "failed to link program");

		verifyStateProgramInteger(result, gl, program.getProgram(), GL_PROGRAM_SEPARABLE, 0, m_verifier);
	}

	{
		const tcu::ScopedLogSection section		(m_testCtx.getLog(), "SetTrue", "SetTrue");
		glu::Program				program		(m_context.getRenderContext());
		int							linkStatus	= 0;

		gl.glAttachShader(program.getProgram(), vtxShader.getShader());
		gl.glAttachShader(program.getProgram(), frgShader.getShader());
		gl.glProgramParameteri(program.getProgram(), GL_PROGRAM_SEPARABLE, GL_TRUE);
		gl.glLinkProgram(program.getProgram());
		GLU_EXPECT_NO_ERROR(gl.glGetError(), "setup program");

		gl.glGetProgramiv(program.getProgram(), GL_LINK_STATUS, &linkStatus);
		GLU_EXPECT_NO_ERROR(gl.glGetError(), "query link status");

		TCU_CHECK_MSG(linkStatus == GL_TRUE, "failed to link program");

		verifyStateProgramInteger(result, gl, program.getProgram(), GL_PROGRAM_SEPARABLE, GL_TRUE, m_verifier);
	}

	result.setTestContextResult(m_testCtx);
	return STOP;
}

class ComputeWorkGroupSizeCase : public TestCase
{
public:
						ComputeWorkGroupSizeCase	(Context& context, QueryType verifier, const char* name, const char* desc);
	IterateResult		iterate						(void);

private:
	const QueryType		m_verifier;
};

ComputeWorkGroupSizeCase::ComputeWorkGroupSizeCase (Context& context, QueryType verifier, const char* name, const char* desc)
	: TestCase		(context, name, desc)
	, m_verifier	(verifier)
{
}

ComputeWorkGroupSizeCase::IterateResult ComputeWorkGroupSizeCase::iterate (void)
{
	static const char* const	s_computeTemplate1D =	"${GLSL_VERSION_DECL}\n"
														"layout (local_size_x = 3) in;\n"
														"layout(binding = 0) buffer Output\n"
														"{\n"
														"	highp float val;\n"
														"} sb_out;\n"
														"\n"
														"void main (void)\n"
														"{\n"
														"	sb_out.val = 1.0;\n"
														"}\n";
	static const char* const	s_computeTemplate2D =	"${GLSL_VERSION_DECL}\n"
														"layout (local_size_x = 3, local_size_y = 2) in;\n"
														"layout(binding = 0) buffer Output\n"
														"{\n"
														"	highp float val;\n"
														"} sb_out;\n"
														"\n"
														"void main (void)\n"
														"{\n"
														"	sb_out.val = 1.0;\n"
														"}\n";
	static const char* const	s_computeTemplate3D =	"${GLSL_VERSION_DECL}\n"
														"layout (local_size_x = 3, local_size_y = 2, local_size_z = 4) in;\n"
														"layout(binding = 0) buffer Output\n"
														"{\n"
														"	highp float val;\n"
														"} sb_out;\n"
														"\n"
														"void main (void)\n"
														"{\n"
														"	sb_out.val = 1.0;\n"
														"}\n";

	glu::CallLogWrapper			gl						(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	tcu::ResultCollector		result					(m_testCtx.getLog(), " // ERROR: ");

	gl.enableLogging(true);

	{
		const tcu::ScopedLogSection section		(m_testCtx.getLog(), "OneDimensional", "1D");
		glu::ShaderProgram			program		(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(specializeShader(m_context, s_computeTemplate1D)));

		m_testCtx.getLog() << program;

		TCU_CHECK_MSG(program.isOk(), "failed to build program");

		verifyStateProgramIntegerVec3(result, gl, program.getProgram(), GL_COMPUTE_WORK_GROUP_SIZE, tcu::IVec3(3, 1, 1), m_verifier);
	}

	{
		const tcu::ScopedLogSection section		(m_testCtx.getLog(), "TwoDimensional", "2D");
		glu::ShaderProgram			program		(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(specializeShader(m_context, s_computeTemplate2D)));

		m_testCtx.getLog() << program;

		TCU_CHECK_MSG(program.isOk(), "failed to build program");

		verifyStateProgramIntegerVec3(result, gl, program.getProgram(), GL_COMPUTE_WORK_GROUP_SIZE, tcu::IVec3(3, 2, 1), m_verifier);
	}

	{
		const tcu::ScopedLogSection section		(m_testCtx.getLog(), "TreeDimensional", "3D");
		glu::ShaderProgram			program		(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(specializeShader(m_context, s_computeTemplate3D)));

		m_testCtx.getLog() << program;

		TCU_CHECK_MSG(program.isOk(), "failed to build program");

		verifyStateProgramIntegerVec3(result, gl, program.getProgram(), GL_COMPUTE_WORK_GROUP_SIZE, tcu::IVec3(3, 2, 4), m_verifier);
	}

	result.setTestContextResult(m_testCtx);
	return STOP;
}

class ActiveAtomicCounterBuffersCase : public TestCase
{
public:
						ActiveAtomicCounterBuffersCase	(Context& context, QueryType verifier, const char* name, const char* desc);
	IterateResult		iterate							(void);

private:
	const QueryType		m_verifier;
};

ActiveAtomicCounterBuffersCase::ActiveAtomicCounterBuffersCase (Context& context, QueryType verifier, const char* name, const char* desc)
	: TestCase		(context, name, desc)
	, m_verifier	(verifier)
{
}

ActiveAtomicCounterBuffersCase::IterateResult ActiveAtomicCounterBuffersCase::iterate (void)
{
	static const char* const	s_computeTemplate0	=	"${GLSL_VERSION_DECL}\n"
														"layout (local_size_x = 3) in;\n"
														"layout(binding = 0) buffer Output\n"
														"{\n"
														"	highp float val;\n"
														"} sb_out;\n"
														"\n"
														"void main (void)\n"
														"{\n"
														"	sb_out.val = 1.0;\n"
														"}\n";
	static const char* const	s_computeTemplate1	=	"${GLSL_VERSION_DECL}\n"
														"layout (local_size_x = 3) in;\n"
														"layout(binding = 0) uniform highp atomic_uint u_counters[2];\n"
														"layout(binding = 0) buffer Output\n"
														"{\n"
														"	highp float val;\n"
														"} sb_out;\n"
														"\n"
														"void main (void)\n"
														"{\n"
														"	sb_out.val = float(atomicCounterIncrement(u_counters[0])) + float(atomicCounterIncrement(u_counters[1]));\n"
														"}\n";

	glu::CallLogWrapper			gl						(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	tcu::ResultCollector		result					(m_testCtx.getLog(), " // ERROR: ");

	gl.enableLogging(true);

	{
		const tcu::ScopedLogSection	section		(m_testCtx.getLog(), "Initial", "Initial");
		glu::Program				program		(m_context.getRenderContext());

		verifyStateProgramInteger(result, gl, program.getProgram(), GL_ACTIVE_ATOMIC_COUNTER_BUFFERS, 0, m_verifier);
	}

	{
		const tcu::ScopedLogSection	section		(m_testCtx.getLog(), "NoBuffers", "No buffers");
		glu::ShaderProgram			program		(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(specializeShader(m_context, s_computeTemplate0)));

		m_testCtx.getLog() << program;

		TCU_CHECK_MSG(program.isOk(), "failed to build program");

		verifyStateProgramInteger(result, gl, program.getProgram(), GL_ACTIVE_ATOMIC_COUNTER_BUFFERS, 0, m_verifier);
	}

	{
		const tcu::ScopedLogSection	section		(m_testCtx.getLog(), "OneBuffer", "One buffer");
		glu::ShaderProgram			program		(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(specializeShader(m_context, s_computeTemplate1)));

		m_testCtx.getLog() << program;

		TCU_CHECK_MSG(program.isOk(), "failed to build program");

		verifyStateProgramInteger(result, gl, program.getProgram(), GL_ACTIVE_ATOMIC_COUNTER_BUFFERS, 1, m_verifier);
	}

	result.setTestContextResult(m_testCtx);
	return STOP;
}

class ProgramLogCase : public TestCase
{
public:
	enum BuildErrorType
	{
		BUILDERROR_VERTEX_FRAGMENT = 0,
		BUILDERROR_COMPUTE,
		BUILDERROR_GEOMETRY,
		BUILDERROR_TESSELLATION,
	};

							ProgramLogCase		(Context& ctx, const char* name, const char* desc, BuildErrorType errorType);

private:
	void					init				(void);
	IterateResult			iterate				(void);
	glu::ProgramSources		getProgramSources	(void) const;

	const BuildErrorType	m_buildErrorType;
};

ProgramLogCase::ProgramLogCase (Context& ctx, const char* name, const char* desc, BuildErrorType errorType)
	: TestCase			(ctx, name, desc)
	, m_buildErrorType	(errorType)
{
}

void ProgramLogCase::init (void)
{
	const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));

	switch (m_buildErrorType)
	{
		case BUILDERROR_VERTEX_FRAGMENT:
		case BUILDERROR_COMPUTE:
			break;

		case BUILDERROR_GEOMETRY:
			if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader"))
				TCU_THROW(NotSupportedError, "Test requires GL_EXT_geometry_shader extension");
			break;

		case BUILDERROR_TESSELLATION:
			if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
				TCU_THROW(NotSupportedError, "Test requires GL_EXT_tessellation_shader extension");
			break;

		default:
			DE_ASSERT(false);
			break;
	}
}

ProgramLogCase::IterateResult ProgramLogCase::iterate (void)
{
	using gls::StateQueryUtil::StateQueryMemoryWriteGuard;

	tcu::ResultCollector					result		(m_testCtx.getLog());
	glu::CallLogWrapper						gl			(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	glu::ShaderProgram						program		(m_context.getRenderContext(), getProgramSources());
	StateQueryMemoryWriteGuard<glw::GLint>	logLen;

	gl.enableLogging(true);

	m_testCtx.getLog() << tcu::TestLog::Message << "Trying to link a broken program." << tcu::TestLog::EndMessage;

	gl.glGetProgramiv(program.getProgram(), GL_INFO_LOG_LENGTH, &logLen);
	logLen.verifyValidity(result);

	if (logLen.verifyValidity(result))
		verifyInfoLogQuery(result, gl, logLen, program.getProgram(), &glu::CallLogWrapper::glGetProgramInfoLog, "glGetProgramInfoLog");

	result.setTestContextResult(m_testCtx);
	return STOP;
}

glu::ProgramSources ProgramLogCase::getProgramSources (void) const
{
	const char* const	vertexTemplate1 =	"${GLSL_VERSION_DECL}\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";
	const char* const	vertexTemplate2 =	"${GLSL_VERSION_DECL}\n"
											"in highp vec4 a_pos;\n"
											"void main()\n"
											"{\n"
											"	gl_Position = a_pos;\n"
											"}\n";
	const char* const	fragmentTemplate1 =	"${GLSL_VERSION_DECL}\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* const	fragmentTemplate2 =	"${GLSL_VERSION_DECL}\n"
											"layout(location = 0) out mediump vec4 fragColor;\n"
											"void main()\n"
											"{\n"
											"	fragColor = vec4(1.0);\n"
											"}\n";
	const char* const	computeTemplate1 =	"${GLSL_VERSION_DECL}\n"
											"layout (binding = 0) buffer IOBuffer { highp float buf_var; };\n"
											"uniform highp vec4 u_uniform;\n"
											"void main()\n"
											"{\n"
											"	buf_var = u_uniform.x;\n"
											"}\n";
	const char* const	geometryTemplate1 =	"${GLSL_VERSION_DECL}\n"
											"${GEOMETRY_SHADER_REQUIRE}\n"
											"layout(triangles) in;\n"
											"layout(max_vertices=1, points) out;\n"
											"in highp vec4 v_missingVar[];\n"
											"uniform highp int u_uniform;\n"
											"void main()\n"
											"{\n"
											"	gl_Position = gl_in[0].gl_Position + v_missingVar[2] + vec4(float(u_uniform));\n"
											"	EmitVertex();\n"
											"}\n";
	const char* const	tessCtrlTemplate1 =	"${GLSL_VERSION_DECL}\n"
											"${TESSELLATION_SHADER_REQUIRE}\n"
											"layout(vertices=2) out;"
											"patch out highp vec2 vp_var;\n"
											"void main()\n"
											"{\n"
											"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position\n"
											"	gl_TessLevelOuter[0] = 0.8;\n"
											"	gl_TessLevelOuter[1] = 0.8;\n"
											"	if (gl_InvocationID == 0)\n"
											"		vp_var = gl_in[gl_InvocationID].gl_Position.xy;\n"
											"}\n";
	const char* const	tessEvalTemplate1 =	"${GLSL_VERSION_DECL}\n"
											"${TESSELLATION_SHADER_REQUIRE}\n"
											"layout(isolines) in;"
											"in highp float vp_var[];\n"
											"void main()\n"
											"{\n"
											"	gl_Position = gl_in[gl_InvocationID].gl_Position + vec4(vp_var[1]);\n"
											"}\n";

	switch (m_buildErrorType)
	{
		case BUILDERROR_VERTEX_FRAGMENT:
			return glu::ProgramSources()
					<< glu::VertexSource(specializeShader(m_context, vertexTemplate1))
					<< glu::FragmentSource(specializeShader(m_context, fragmentTemplate1));

		case BUILDERROR_COMPUTE:
			return glu::ProgramSources()
					<< glu::ComputeSource(specializeShader(m_context, computeTemplate1));

		case BUILDERROR_GEOMETRY:
			return glu::ProgramSources()
					<< glu::VertexSource(specializeShader(m_context, vertexTemplate1))
					<< glu::GeometrySource(specializeShader(m_context, geometryTemplate1))
					<< glu::FragmentSource(specializeShader(m_context, fragmentTemplate2));

		case BUILDERROR_TESSELLATION:
			return glu::ProgramSources()
					<< glu::VertexSource(specializeShader(m_context, vertexTemplate2))
					<< glu::TessellationControlSource(specializeShader(m_context, tessCtrlTemplate1))
					<< glu::TessellationEvaluationSource(specializeShader(m_context, tessEvalTemplate1))
					<< glu::FragmentSource(specializeShader(m_context, fragmentTemplate2));

		default:
			DE_ASSERT(false);
			return glu::ProgramSources();
	}
}

} // anonymous

ProgramStateQueryTests::ProgramStateQueryTests (Context& context)
	: TestCaseGroup(context, "program", "Program State Query tests")
{
}

ProgramStateQueryTests::~ProgramStateQueryTests (void)
{
}

void ProgramStateQueryTests::init (void)
{
	static const QueryType intVerifiers[] =
	{
		QUERY_PROGRAM_INTEGER,
	};
	static const QueryType intVec3Verifiers[] =
	{
		QUERY_PROGRAM_INTEGER_VEC3,
	};

#define FOR_EACH_INT_VERIFIER(X) \
	for (int verifierNdx = 0; verifierNdx < DE_LENGTH_OF_ARRAY(intVerifiers); ++verifierNdx)	\
	{																							\
		const char* verifierSuffix = getVerifierSuffix(intVerifiers[verifierNdx]);				\
		const QueryType verifier = intVerifiers[verifierNdx];									\
		this->addChild(X);																		\
	}

#define FOR_EACH_VEC_VERIFIER(X) \
	for (int verifierNdx = 0; verifierNdx < DE_LENGTH_OF_ARRAY(intVec3Verifiers); ++verifierNdx)	\
	{																								\
		const char* verifierSuffix = getVerifierSuffix(intVec3Verifiers[verifierNdx]);				\
		const QueryType verifier = intVec3Verifiers[verifierNdx];									\
		this->addChild(X);																			\
	}

	FOR_EACH_INT_VERIFIER(new ProgramSeparableCase				(m_context, verifier, (std::string("program_separable_") + verifierSuffix).c_str(),				"Test PROGRAM_SEPARABLE"));
	FOR_EACH_VEC_VERIFIER(new ComputeWorkGroupSizeCase			(m_context, verifier, (std::string("compute_work_group_size_") + verifierSuffix).c_str(),		"Test COMPUTE_WORK_GROUP_SIZE"));
	FOR_EACH_INT_VERIFIER(new ActiveAtomicCounterBuffersCase	(m_context, verifier, (std::string("active_atomic_counter_buffers_") + verifierSuffix).c_str(),	"Test ACTIVE_ATOMIC_COUNTER_BUFFERS"));
	FOR_EACH_INT_VERIFIER(new GeometryShaderCase				(m_context, verifier, (std::string("geometry_shader_state_") + verifierSuffix).c_str(),			"Test Geometry Shader State"));
	FOR_EACH_INT_VERIFIER(new TessellationShaderCase			(m_context, verifier, (std::string("tesselation_shader_state_") + verifierSuffix).c_str(),		"Test Tesselation Shader State"));

#undef FOR_EACH_INT_VERIFIER
#undef FOR_EACH_VEC_VERIFIER

	// program info log tests
	// \note, there exists similar tests in gles3 module. However, the gles31 could use a different
	//        shader compiler with different INFO_LOG bugs.
	{
		static const struct
		{
			const char*						caseName;
			ProgramLogCase::BuildErrorType	caseType;
		} shaderTypes[] =
		{
			{ "info_log_vertex_fragment_link_fail",		ProgramLogCase::BUILDERROR_VERTEX_FRAGMENT	},
			{ "info_log_compute_link_fail",				ProgramLogCase::BUILDERROR_COMPUTE			},
			{ "info_log_geometry_link_fail",			ProgramLogCase::BUILDERROR_GEOMETRY			},
			{ "info_log_tessellation_link_fail",		ProgramLogCase::BUILDERROR_TESSELLATION		},
		};

		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(shaderTypes); ++ndx)
			addChild(new ProgramLogCase(m_context, shaderTypes[ndx].caseName, "", shaderTypes[ndx].caseType));
	}
}

} // Functional
} // gles31
} // deqp