/*
 * Copyright © 2015 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

/**
 * \file getprograminterfaceiv.c
 *
 * Tests the error cases of the GetProgramInterfaceiv interface.
 *
 * From the GL_ARB_program_interface_query spec:
 *      "The command
 *
 *      void GetProgramInterfaceiv(uint program, enum programInterface,
 *                                 enum pname, int *params);
 *
 *      queries a property of the interface <programInterface> in program
 *      <program>, returning its value in <params>.  The property to return is
 *      specified by <pname>.
 *
 *      If <pname> is ACTIVE_RESOURCES, the value returned is the number of
 *      resources in the active resource list for <programInterface>. If the
 *      list of active resources for <programInterface> is empty, zero is
 *      returned.
 *
 *      If <pname> is MAX_NAME_LENGTH, the value returned is the length of the
 *      longest active name string for an active resource in <programInterface>.
 *      This length includes an extra character for the null terminator. If
 *      the list of active resources for <programInterface> is empty, zero is
 *      returned.  The error INVALID_OPERATION is generated if
 *      <programInterface> is ATOMIC_COUNTER_BUFFER, since active atomic counter
 *      buffer resources are not assigned name strings.
 *
 *      If <pname> is MAX_NUM_ACTIVE_VARIABLES, the value returned is the number
 *      of active variables belonging to the interface block or atomic counter
 *      buffer resource in <programInterface> with the most active variables.
 *      If the list of active resources for <programInterface> is empty, zero is
 *      returned.  The error INVALID_OPERATION is generated if
 *      <programInterface> is not UNIFORM_BLOCK, ATOMIC_COUNTER_BUFFER, or
 *      SHADER_STORAGE_BLOCK.
 *
 *      If <pname> is MAX_NUM_COMPATIBLE_SUBROUTINES, the value returned is the
 *      number of compatible subroutines belonging to the active subroutine
 *      uniform in <programInterface> with the most compatible subroutines. If
 *      the list of active resources for <programInterface> is empty, zero is
 *      returned.  The error INVALID_OPERATION is generated unless
 *      <programInterface> is VERTEX_SUBROUTINE_UNIFORM,
 *      TESS_CONTROL_SUBROUTINE_UNIFORM, TESS_EVALUATION_SUBROUTINE_UNIFORM,
 *      GEOMETRY_SUBROUTINE_UNIFORM, FRAGMENT_SUBROUTINE_UNIFORM, or
 *      COMPUTE_SUBROUTINE_UNIFORM.
 *
 *      [...]
 *
 *      An INVALID_VALUE error is generated by GetProgramInterfaceiv,
 *      GetProgramResourceIndex, GetProgramResourceName, GetProgramResourceiv,
 *      GetProgramResourceLocation, and GetProgramResourceLocationIndex if
 *      <program> is not the name of either a shader or program object.
 *
 *      An INVALID_OPERATION error is generated by GetProgramInterfaceiv,
 *      GetProgramResourceIndex, GetProgramResourceName, GetProgramResourceiv,
 *      GetProgramResourceLocation, and GetProgramResourceLocationIndex if
 *      <program> is the name of a shader object.
 *
 *      INVALID_OPERATION is generated by GetProgramInterfaceiv if the parameter
 *     <pname> is not supported for the interface <programInterface>."
 */

#include "piglit-util-gl.h"
#include "common.h"

PIGLIT_GL_TEST_CONFIG_BEGIN

	config.supports_gl_core_version = 32;
	config.khr_no_error_support = PIGLIT_HAS_ERRORS;

PIGLIT_GL_TEST_CONFIG_END

struct subtest_t {
	GLenum programInterface;
	GLenum pname;
	GLenum expected_error;
};

static const struct subtest_t programInterface_subtests[] = {
	{ GL_TRUE, GL_MAX_NAME_LENGTH, GL_INVALID_OPERATION },
	{ GL_UNIFORM, GL_TRUE, GL_INVALID_OPERATION },
	{ GL_ATOMIC_COUNTER_BUFFER, GL_MAX_NAME_LENGTH,
		GL_INVALID_OPERATION },
	{ GL_UNIFORM, GL_MAX_NUM_ACTIVE_VARIABLES, GL_INVALID_OPERATION },
	{ GL_PROGRAM_OUTPUT, GL_MAX_NUM_COMPATIBLE_SUBROUTINES,
		GL_INVALID_OPERATION },
};

static bool
check_extensions(const struct subtest_t st)
{
	if (st.programInterface == GL_ATOMIC_COUNTER_BUFFER &&
	    !piglit_is_extension_supported("GL_ARB_shader_atomic_counters")) {
		return false;
	}

	return true;
}

static void
run_subtest(const struct subtest_t st, GLuint prog, bool *pass)
{
	enum piglit_result result;
	bool local_pass = true;
	int value;
	const char *programInterface_str =
		piglit_get_gl_enum_name(st.programInterface);
	const char *pname_str = piglit_get_gl_enum_name(st.pname);

	if (!check_extensions(st)) {
		result = PIGLIT_SKIP;
		goto report_result;
	}

	glGetProgramInterfaceiv(prog, st.programInterface, st.pname, &value);
	if (!piglit_check_gl_error(st.expected_error)) {
		printf("	Call was glGetProgramInterfaceiv(prog, %s, "
		       "%s, ...)\n", programInterface_str, pname_str);
		local_pass = false;
	}

	*pass = *pass && local_pass;
	result = local_pass ? PIGLIT_PASS : PIGLIT_FAIL;

report_result:
	piglit_report_subtest_result(result, "%s on %s", pname_str,
				     programInterface_str);
}

void
piglit_init(int argc, char **argv)
{
	piglit_require_extension("GL_ARB_program_interface_query");
}

enum piglit_result
piglit_display(void)
{
	GLuint prog, shader, test_cnt;
	bool pass = true, prg_tst;
	int i;

	/* test using an unexisting program ID */
	glGetProgramInterfaceiv(1337, GL_UNIFORM, GL_MAX_NUM_ACTIVE_VARIABLES,
				&i);
	prg_tst = piglit_check_gl_error(GL_INVALID_VALUE);
	pass = pass && prg_tst;
	piglit_report_subtest_result(prg_tst ? PIGLIT_PASS : PIGLIT_FAIL,
				     "Invalid program (undefined ID)");

	/* test using a shader ID */
	shader = piglit_compile_shader_text(GL_VERTEX_SHADER, vs_empty);
	glGetProgramInterfaceiv(shader, GL_UNIFORM, GL_MAX_NUM_ACTIVE_VARIABLES,
				&i);
	prg_tst = piglit_check_gl_error(GL_INVALID_OPERATION);
	pass = pass && prg_tst;
	piglit_report_subtest_result(prg_tst ? PIGLIT_PASS : PIGLIT_FAIL,
				     "Invalid program (call on shader)");

	/* build a valid program that will be used to run the other tests */
	prog = piglit_build_simple_program(vs_empty, fs_empty);
	if (!piglit_link_check_status(prog)) {
		glDeleteProgram(prog);
		return PIGLIT_FAIL;
	}

	/* run all the getprograminterfaceiv tests */
	test_cnt = sizeof(programInterface_subtests) / sizeof(struct subtest_t);
	for (i = 0; i < test_cnt; i++) {
		run_subtest(programInterface_subtests[i], prog, &pass);
	}

	glDeleteProgram(prog);

	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
}
