1/*-------------------------------------------------------------------------
2 * OpenGL Conformance Test Suite
3 * -----------------------------
4 *
5 * Copyright (c) 2017 The Khronos Group Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *	  http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 */ /*!
20 * \file glcRobustnessTests.cpp
21 * \brief Conformance tests for the Robustness feature functionality.
22 */ /*-------------------------------------------------------------------*/
23
24#include "glcRobustnessTests.hpp"
25#include "deSharedPtr.hpp"
26#include "glcRobustBufferAccessBehaviorTests.hpp"
27#include "gluContextInfo.hpp"
28#include "gluPlatform.hpp"
29#include "gluRenderContext.hpp"
30#include "glwEnums.hpp"
31#include "glwFunctions.hpp"
32#include "tcuCommandLine.hpp"
33#include "tcuTestLog.hpp"
34#include <cstring>
35
36using namespace glw;
37using namespace glcts::RobustBufferAccessBehavior;
38
39namespace glcts
40{
41
42namespace ResetNotificationStrategy
43{
44
45class RobustnessBase : public tcu::TestCase
46{
47public:
48	RobustnessBase(tcu::TestContext& testCtx, const char* name, const char* description, glu::ApiType apiType);
49
50	glu::RenderContext* createRobustContext(glu::ResetNotificationStrategy reset);
51
52private:
53	glu::ApiType m_ApiType;
54};
55
56RobustnessBase::RobustnessBase(tcu::TestContext& testCtx, const char* name, const char* description,
57							   glu::ApiType apiType)
58	: tcu::TestCase(testCtx, name, description), m_ApiType(apiType)
59{
60}
61
62glu::RenderContext* RobustnessBase::createRobustContext(glu::ResetNotificationStrategy reset)
63{
64	/* Create test context to verify if GL_KHR_robustness extension is available */
65	{
66		deqp::Context context(m_testCtx, glu::ContextType(m_ApiType));
67		if (!context.getContextInfo().isExtensionSupported("GL_KHR_robustness") &&
68			!contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2)))
69		{
70			m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED,
71									"GL_KHR_robustness extension not supported");
72			return NULL;
73		}
74	}
75
76	glu::RenderConfig		renderCfg(glu::ContextType(m_ApiType, glu::CONTEXT_ROBUST));
77	const tcu::CommandLine& commandLine = m_testCtx.getCommandLine();
78	glu::parseRenderConfig(&renderCfg, commandLine);
79
80	if (commandLine.getSurfaceType() == tcu::SURFACETYPE_WINDOW)
81		renderCfg.resetNotificationStrategy = reset;
82	else
83		throw tcu::NotSupportedError("Test not supported in non-windowed context");
84
85	/* Try to create core/es robusness context */
86	return createRenderContext(m_testCtx.getPlatform(), commandLine, renderCfg);
87}
88
89class NoResetNotificationCase : public RobustnessBase
90{
91	typedef glw::GLenum(GLW_APIENTRY* PFNGLGETGRAPHICSRESETSTATUS)();
92
93public:
94	NoResetNotificationCase(tcu::TestContext& testCtx, const char* name, const char* description, glu::ApiType apiType)
95		: RobustnessBase(testCtx, name, description, apiType)
96	{
97	}
98
99	virtual IterateResult iterate(void)
100	{
101		glu::ResetNotificationStrategy	strategy = glu::RESET_NOTIFICATION_STRATEGY_NO_RESET_NOTIFICATION;
102		de::SharedPtr<glu::RenderContext> robustContext(createRobustContext(strategy));
103		if (!robustContext.get())
104			return STOP;
105
106		glw::GLint reset = 0;
107
108		const glw::Functions& gl = robustContext->getFunctions();
109		gl.getIntegerv(GL_RESET_NOTIFICATION_STRATEGY, &reset);
110		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv");
111
112		if (reset != GL_NO_RESET_NOTIFICATION)
113		{
114			m_testCtx.getLog() << tcu::TestLog::Message << "Test failed! glGet returned wrong value [" << reset
115							   << ", expected " << GL_NO_RESET_NOTIFICATION << "]." << tcu::TestLog::EndMessage;
116
117			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
118			return STOP;
119		}
120
121		glw::GLint status = gl.getGraphicsResetStatus();
122		if (status != GL_NO_ERROR)
123		{
124			m_testCtx.getLog() << tcu::TestLog::Message
125							   << "Test failed! glGetGraphicsResetStatus returned wrong value [" << status
126							   << ", expected " << GL_NO_ERROR << "]." << tcu::TestLog::EndMessage;
127
128			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
129			return STOP;
130		}
131
132		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
133		return STOP;
134	}
135};
136
137class LoseContextOnResetCase : public RobustnessBase
138{
139public:
140	LoseContextOnResetCase(tcu::TestContext& testCtx, const char* name, const char* description, glu::ApiType apiType)
141		: RobustnessBase(testCtx, name, description, apiType)
142	{
143	}
144
145	virtual IterateResult iterate(void)
146	{
147		glu::ResetNotificationStrategy	strategy = glu::RESET_NOTIFICATION_STRATEGY_LOSE_CONTEXT_ON_RESET;
148		de::SharedPtr<glu::RenderContext> robustContext(createRobustContext(strategy));
149		if (!robustContext.get())
150			return STOP;
151
152		glw::GLint reset = 0;
153
154		const glw::Functions& gl = robustContext->getFunctions();
155		gl.getIntegerv(GL_RESET_NOTIFICATION_STRATEGY, &reset);
156		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv");
157
158		if (reset != GL_LOSE_CONTEXT_ON_RESET)
159		{
160			m_testCtx.getLog() << tcu::TestLog::Message << "Test failed! glGet returned wrong value [" << reset
161							   << ", expected " << GL_LOSE_CONTEXT_ON_RESET << "]." << tcu::TestLog::EndMessage;
162
163			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
164			return STOP;
165		}
166
167		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
168		return STOP;
169	}
170};
171
172} // ResetNotificationStrategy namespace
173
174namespace RobustBufferAccessBehavior
175{
176
177static deqp::Context* createContext(tcu::TestContext& testCtx, glu::ApiType apiType)
178{
179	deqp::Context* context = new deqp::Context(testCtx, glu::ContextType(apiType));
180	if (!context)
181	{
182		testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Pointer to context is NULL.");
183		return DE_NULL;
184	}
185
186	if (!(contextSupports(context->getRenderContext().getType(), glu::ApiType::es(3, 2)) ||
187		  (context->getContextInfo().isExtensionSupported("GL_KHR_robustness") &&
188		   context->getContextInfo().isExtensionSupported("GL_KHR_robust_buffer_access_behavior"))))
189	{
190		testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported");
191		delete context;
192		return DE_NULL;
193	}
194
195	return context;
196}
197
198/** Implementation of test GetnUniformTest. Description follows:
199 *
200 * This test verifies if read uniform variables to the buffer with bufSize less than expected result with GL_INVALID_OPERATION error;
201 **/
202class GetnUniformTest : public tcu::TestCase
203{
204public:
205	/* Public methods */
206	GetnUniformTest(tcu::TestContext& testCtx, glu::ApiType apiType);
207	virtual ~GetnUniformTest()
208	{
209	}
210
211	/* Public methods inherited from TestCase */
212	virtual tcu::TestNode::IterateResult iterate(void);
213
214private:
215	/* Private methods */
216	std::string getComputeShader(bool glslES320);
217
218	bool verifyResult(const void* inputData, const void* resultData, int size, const char* method);
219	bool verifyError(glw::GLint error, glw::GLint expectedError, const char* method);
220
221	glu::ApiType m_ApiType;
222};
223
224/** Constructor
225 *
226 * @param context Test context
227 **/
228GetnUniformTest::GetnUniformTest(tcu::TestContext& testCtx, glu::ApiType apiType)
229	: tcu::TestCase(testCtx, "getnuniform", "Verifies if read uniform variables to the buffer with bufSize less than "
230											"expected result with GL_INVALID_OPERATION")
231	, m_ApiType(apiType)
232{
233	/* Nothing to be done here */
234}
235
236/** Execute test
237 *
238 * @return tcu::TestNode::STOP
239 **/
240tcu::TestNode::IterateResult GetnUniformTest::iterate()
241{
242	de::SharedPtr<deqp::Context> context(createContext(m_testCtx, m_ApiType));
243	if (!context.get())
244		return STOP;
245
246	glu::RenderContext& renderContext = context->getRenderContext();
247	const Functions& gl = renderContext.getFunctions();
248
249	const GLfloat input4f[]  = { 1.0f, 5.4f, 3.14159f, 1.28f };
250	const GLint   input3i[]  = { 10, -20, -30 };
251	const GLuint  input4ui[] = { 10, 20, 30, 40 };
252
253	/* Test result indicator */
254	bool test_result = true;
255
256	/* Iterate over all cases */
257	Program program(gl);
258
259	/* Compute Shader */
260	bool			   glslES320 = contextSupports(renderContext.getType(), glu::ApiType::es(3, 2));
261	const std::string& cs		 = getComputeShader(glslES320);
262
263	/* Shaders initialization */
264	program.Init(cs /* cs */, "" /* fs */, "" /* gs */, "" /* tcs */, "" /* tes */, "" /* vs */);
265	program.Use();
266
267	/* Initialize shader storage buffer */
268	GLuint buf;
269
270	gl.genBuffers(1, &buf);
271	GLU_EXPECT_NO_ERROR(gl.getError(), "GenBuffers");
272
273	gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buf);
274	GLU_EXPECT_NO_ERROR(gl.getError(), "BindBufferBase");
275
276	gl.bufferData(GL_SHADER_STORAGE_BUFFER, 16, DE_NULL, GL_STREAM_DRAW);
277	GLU_EXPECT_NO_ERROR(gl.getError(), "BufferData");
278
279	/* passing uniform values */
280	gl.programUniform4fv(program.m_id, 11, 1, input4f);
281	GLU_EXPECT_NO_ERROR(gl.getError(), "ProgramUniform4fv");
282
283	gl.programUniform3iv(program.m_id, 12, 1, input3i);
284	GLU_EXPECT_NO_ERROR(gl.getError(), "ProgramUniform3iv");
285
286	gl.programUniform4uiv(program.m_id, 13, 1, input4ui);
287	GLU_EXPECT_NO_ERROR(gl.getError(), "ProgramUniform4uiv");
288
289	gl.dispatchCompute(1, 1, 1);
290	GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute");
291
292	/* veryfing gfetnUniform error messages */
293	GLfloat result4f[4];
294	GLint   result3i[3];
295	GLuint  result4ui[4];
296
297	gl.getnUniformfv(program.m_id, 11, sizeof(GLfloat) * 4, result4f);
298	test_result = test_result &&
299				  verifyResult((void*)input4f, (void*)result4f, sizeof(GLfloat) * 4, "getnUniformfv [false negative]");
300	test_result = test_result && verifyError(gl.getError(), GL_NO_ERROR, "getnUniformfv [false negative]");
301
302	gl.getnUniformfv(program.m_id, 11, sizeof(GLfloat) * 3, result4f);
303	test_result = test_result && verifyError(gl.getError(), GL_INVALID_OPERATION, "getnUniformfv [false positive]");
304
305	gl.getnUniformiv(program.m_id, 12, sizeof(GLint) * 3, result3i);
306	test_result = test_result &&
307				  verifyResult((void*)input3i, (void*)result3i, sizeof(GLint) * 3, "getnUniformiv [false negative]");
308	test_result = test_result && verifyError(gl.getError(), GL_NO_ERROR, "getnUniformiv [false negative]");
309
310	gl.getnUniformiv(program.m_id, 12, sizeof(GLint) * 2, result3i);
311	test_result = test_result && verifyError(gl.getError(), GL_INVALID_OPERATION, "getnUniformiv [false positive]");
312
313	gl.getnUniformuiv(program.m_id, 13, sizeof(GLuint) * 4, result4ui);
314	test_result = test_result && verifyResult((void*)input4ui, (void*)result4ui, sizeof(GLuint) * 4,
315											  "getnUniformuiv [false negative]");
316	test_result = test_result && verifyError(gl.getError(), GL_NO_ERROR, "getnUniformuiv [false negative]");
317
318	gl.getnUniformuiv(program.m_id, 13, sizeof(GLuint) * 3, result4ui);
319	test_result = test_result && verifyError(gl.getError(), GL_INVALID_OPERATION, "getnUniformuiv [false positive]");
320
321	/* Set result */
322	if (true == test_result)
323	{
324		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
325	}
326	else
327	{
328		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
329	}
330
331	gl.deleteBuffers(1, &buf);
332
333	/* Done */
334	return tcu::TestNode::STOP;
335}
336
337std::string GetnUniformTest::getComputeShader(bool glslES320)
338{
339	std::stringstream shader;
340	shader << "#version " << (glslES320 ? "320 es\n" : "450\n");
341	shader << "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
342		  "layout (location = 11) uniform vec4 inputf;\n"
343		  "layout (location = 12) uniform ivec3 inputi;\n"
344		  "layout (location = 13) uniform uvec4 inputu;\n"
345		  "layout (std140, binding = 0) buffer ssbo {\n"
346		  "   float valuef;\n"
347		  "   int valuei;\n"
348		  "   uint valueu;\n"
349		  "};\n"
350		  "void main()\n"
351		  "{\n"
352		  "   valuef = inputf.r + inputf.g + inputf.b + inputf.a;\n"
353		  "   valuei = inputi.r + inputi.g + inputi.b;\n"
354		  "   valueu = inputu.r + inputu.g + inputu.b + inputu.a;\n"
355		  "}\n";
356	return shader.str();
357}
358
359bool GetnUniformTest::verifyResult(const void* inputData, const void* resultData, int size, const char* method)
360{
361	int diff = memcmp(inputData, resultData, size);
362	if (diff != 0)
363	{
364		m_testCtx.getLog() << tcu::TestLog::Message << "Test failed! " << method << " result is not as expected."
365						   << tcu::TestLog::EndMessage;
366
367		return false;
368	}
369
370	return true;
371}
372
373bool GetnUniformTest::verifyError(GLint error, GLint expectedError, const char* method)
374{
375	if (error != expectedError)
376	{
377		m_testCtx.getLog() << tcu::TestLog::Message << "Test failed! " << method << " throws unexpected error ["
378						   << error << "]." << tcu::TestLog::EndMessage;
379
380		return false;
381	}
382
383	return true;
384}
385
386/** Implementation of test ReadnPixelsTest. Description follows:
387 *
388 * This test verifies if read pixels to the buffer with bufSize less than expected result with GL_INVALID_OPERATION error;
389 **/
390class ReadnPixelsTest : public tcu::TestCase
391{
392public:
393	/* Public methods */
394	ReadnPixelsTest(tcu::TestContext& testCtx, glu::ApiType apiType);
395	virtual ~ReadnPixelsTest()
396	{
397	}
398
399	/* Public methods inherited from TestCase */
400	virtual tcu::TestNode::IterateResult iterate(void);
401
402private:
403	/* Private methods */
404	void cleanTexture(deqp::Context& context, glw::GLuint texture_id);
405	bool verifyResults(deqp::Context& context);
406	bool verifyError(glw::GLint error, glw::GLint expectedError, const char* method);
407
408	glu::ApiType m_ApiType;
409};
410
411/** Constructor
412 *
413 * @param context Test context
414 **/
415ReadnPixelsTest::ReadnPixelsTest(tcu::TestContext& testCtx, glu::ApiType apiType)
416	: tcu::TestCase(testCtx, "readnpixels",
417					"Verifies if read pixels to the buffer with bufSize less than expected result "
418					"with GL_INVALID_OPERATION error")
419	, m_ApiType(apiType)
420{
421	/* Nothing to be done here */
422}
423
424/** Execute test
425 *
426 * @return tcu::TestNode::STOP
427 **/
428tcu::TestNode::IterateResult ReadnPixelsTest::iterate()
429{
430	de::SharedPtr<deqp::Context> context(createContext(m_testCtx, m_ApiType));
431	if (!context.get())
432		return STOP;
433
434	static const GLuint elements[] = {
435		0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 6, 0, 6, 7, 0, 7, 8, 0, 8, 1,
436	};
437
438	static const GLfloat vertices[] = {
439		0.0f,  0.0f,  0.0f, 1.0f, /* 0 */
440		-1.0f, 0.0f,  0.0f, 1.0f, /* 1 */
441		-1.0f, 1.0f,  0.0f, 1.0f, /* 2 */
442		0.0f,  1.0f,  0.0f, 1.0f, /* 3 */
443		1.0f,  1.0f,  0.0f, 1.0f, /* 4 */
444		1.0f,  0.0f,  0.0f, 1.0f, /* 5 */
445		1.0f,  -1.0f, 0.0f, 1.0f, /* 6 */
446		0.0f,  -1.0f, 0.0f, 1.0f, /* 7 */
447		-1.0f, -1.0f, 0.0f, 1.0f, /* 8 */
448	};
449
450	bool glslES320 = contextSupports(context->getRenderContext().getType(), glu::ApiType::es(3, 2));
451	std::string fs("#version ");
452	fs += (glslES320 ? "320 es\n" : "450\n");
453	fs += "layout (location = 0) out lowp uvec4 out_fs_color;\n"
454		  "\n"
455		  "void main()\n"
456		  "{\n"
457		  "	out_fs_color = uvec4(1, 0, 0, 1);\n"
458		  "}\n"
459		  "\n";
460
461	std::string vs("#version ");
462	vs += (glslES320 ? "320 es\n" : "450\n");
463	vs += "layout (location = 0) in vec4 in_vs_position;\n"
464		  "\n"
465		  "void main()\n"
466		  "{\n"
467		  "	gl_Position = in_vs_position;\n"
468		  "}\n"
469		  "\n";
470
471	static const GLuint height	 = 8;
472	static const GLuint width	  = 8;
473	static const GLuint n_vertices = 24;
474
475	/* GL entry points */
476	const Functions& gl = context->getRenderContext().getFunctions();
477
478	/* Test case objects */
479	Program		program(gl);
480	Texture		texture(gl);
481	Buffer		elements_buffer(gl);
482	Buffer		vertices_buffer(gl);
483	VertexArray vao(gl);
484
485	/* Vertex array initialization */
486	VertexArray::Generate(gl, vao.m_id);
487	VertexArray::Bind(gl, vao.m_id);
488
489	/* Texture initialization */
490	Texture::Generate(gl, texture.m_id);
491	Texture::Bind(gl, texture.m_id, GL_TEXTURE_2D);
492	Texture::Storage(gl, GL_TEXTURE_2D, 1, GL_R8UI, width, height, 0);
493	Texture::Bind(gl, 0, GL_TEXTURE_2D);
494
495	/* Framebuffer initialization */
496	GLuint fbo;
497	gl.genFramebuffers(1, &fbo);
498	GLU_EXPECT_NO_ERROR(gl.getError(), "GenFramebuffers");
499	gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
500	GLU_EXPECT_NO_ERROR(gl.getError(), "BindFramebuffer");
501	gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.m_id, 0);
502	GLU_EXPECT_NO_ERROR(gl.getError(), "FramebufferTexture2D");
503
504	/* Buffers initialization */
505	elements_buffer.InitData(GL_ELEMENT_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(elements), elements);
506	vertices_buffer.InitData(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(vertices), vertices);
507
508	/* Shaders initialization */
509	program.Init("" /* cs */, fs, "" /* gs */, "" /* tcs */, "" /* tes */, vs);
510	Program::Use(gl, program.m_id);
511
512	/* Vertex buffer initialization */
513	vertices_buffer.Bind();
514	gl.bindVertexBuffer(0 /* bindindex = location */, vertices_buffer.m_id, 0 /* offset */, 16 /* stride */);
515	gl.vertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 16, NULL);
516	gl.enableVertexAttribArray(0 /* location */);
517
518	/* Binding elements/indices buffer */
519	elements_buffer.Bind();
520
521	cleanTexture(*context, texture.m_id);
522
523	gl.drawElements(GL_TRIANGLES, n_vertices, GL_UNSIGNED_INT, 0 /* indices */);
524	GLU_EXPECT_NO_ERROR(gl.getError(), "DrawElements");
525
526	/* Set result */
527	if (verifyResults(*context))
528	{
529		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
530	}
531	else
532	{
533		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
534	}
535
536	/* Done */
537	return tcu::TestNode::STOP;
538}
539
540/** Fill texture with value 128
541 *
542 * @param texture_id Id of texture
543 **/
544void ReadnPixelsTest::cleanTexture(deqp::Context& context, glw::GLuint texture_id)
545{
546	static const GLuint height = 8;
547	static const GLuint width  = 8;
548
549	const Functions& gl = context.getRenderContext().getFunctions();
550
551	GLubyte pixels[width * height];
552	for (GLuint i = 0; i < width * height; ++i)
553	{
554		pixels[i] = 64;
555	}
556
557	Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
558
559	Texture::SubImage(gl, GL_TEXTURE_2D, 0 /* level  */, 0 /* x */, 0 /* y */, 0 /* z */, width, height, 0 /* depth */,
560					  GL_RED_INTEGER, GL_UNSIGNED_BYTE, pixels);
561
562	/* Unbind */
563	Texture::Bind(gl, 0, GL_TEXTURE_2D);
564}
565
566/** Verifies glReadnPixels results
567 *
568 * @return true when glReadnPixels , false otherwise
569 **/
570bool ReadnPixelsTest::verifyResults(deqp::Context& context)
571{
572	static const GLuint height	 = 8;
573	static const GLuint width	  = 8;
574	static const GLuint pixel_size = 4 * sizeof(GLuint);
575
576	const Functions& gl = context.getRenderContext().getFunctions();
577
578	//Valid buffer size test
579	const GLint bufSizeValid = width * height * pixel_size;
580	GLubyte		pixelsValid[bufSizeValid];
581
582	gl.readnPixels(0, 0, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, bufSizeValid, pixelsValid);
583	GLU_EXPECT_NO_ERROR(gl.getError(), "ReadnPixels");
584
585	//Verify glReadnPixels result
586	for (unsigned int i = 0; i < width * height; ++i)
587	{
588		const size_t offset = i * pixel_size;
589		const GLuint value  = *(GLuint*)(pixelsValid + offset);
590
591		if (value != 1)
592		{
593			context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid pixel value: " << value
594											  << ". Offset: " << offset << tcu::TestLog::EndMessage;
595			return false;
596		}
597	}
598
599	//Invalid buffer size test
600	const GLint bufSizeInvalid = width * height * pixel_size - 1;
601	GLubyte		pixelsInvalid[bufSizeInvalid];
602
603	gl.readnPixels(0, 0, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, bufSizeInvalid, pixelsInvalid);
604	if (!verifyError(gl.getError(), GL_INVALID_OPERATION, "ReadnPixels [false positive]"))
605		return false;
606
607	return true;
608}
609
610/** Verify operation errors
611 *
612 * @param error OpenGL ES error code
613 * @param expectedError Expected error code
614 * @param method Method name marker
615 *
616 * @return true when error is as expected, false otherwise
617 **/
618bool ReadnPixelsTest::verifyError(GLint error, GLint expectedError, const char* method)
619{
620	if (error != expectedError)
621	{
622		m_testCtx.getLog() << tcu::TestLog::Message << "Test failed! " << method << " throws unexpected error ["
623						   << error << "]." << tcu::TestLog::EndMessage;
624
625		return false;
626	}
627
628	return true;
629}
630
631} // RobustBufferAccessBehavior namespace
632
633RobustnessTests::RobustnessTests(tcu::TestContext& testCtx, glu::ApiType apiType)
634	: tcu::TestCaseGroup(testCtx, "robustness",
635						 "Verifies API coverage and functionality of GL_KHR_robustness extension.")
636	, m_ApiType(apiType)
637{
638}
639
640void RobustnessTests::init(void)
641{
642	tcu::TestCaseGroup::init();
643
644	try
645	{
646		addChild(new ResetNotificationStrategy::NoResetNotificationCase(
647			m_testCtx, "no_reset_notification", "Verifies if NO_RESET_NOTIFICATION strategy works as expected.",
648			m_ApiType));
649		addChild(new ResetNotificationStrategy::LoseContextOnResetCase(
650			m_testCtx, "lose_context_on_reset", "Verifies if LOSE_CONTEXT_ON_RESET strategy works as expected.",
651			m_ApiType));
652
653		addChild(new RobustBufferAccessBehavior::GetnUniformTest(m_testCtx, m_ApiType));
654		addChild(new RobustBufferAccessBehavior::ReadnPixelsTest(m_testCtx, m_ApiType));
655	}
656	catch (...)
657	{
658		// Destroy context.
659		tcu::TestCaseGroup::deinit();
660		throw;
661	}
662}
663
664} // glcts namespace
665