1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL (ES) Module
3  * -----------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
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
21  * \brief Common object lifetime tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "glsLifetimeTests.hpp"
25 
26 #include "deString.h"
27 #include "deRandom.hpp"
28 #include "deSTLUtil.hpp"
29 #include "deStringUtil.hpp"
30 #include "tcuRGBA.hpp"
31 #include "tcuImageCompare.hpp"
32 #include "tcuRenderTarget.hpp"
33 #include "tcuStringTemplate.hpp"
34 #include "tcuTestLog.hpp"
35 #include "gluDrawUtil.hpp"
36 #include "gluObjectWrapper.hpp"
37 #include "gluPixelTransfer.hpp"
38 #include "gluShaderProgram.hpp"
39 #include "gluDefs.hpp"
40 #include "glwFunctions.hpp"
41 
42 #include <vector>
43 #include <map>
44 #include <algorithm>
45 #include <sstream>
46 
47 namespace deqp
48 {
49 namespace gls
50 {
51 namespace LifetimeTests
52 {
53 namespace details
54 {
55 
56 using std::map;
57 using std::string;
58 using std::ostringstream;
59 using de::Random;
60 using tcu::RenderTarget;
61 using tcu::RGBA;
62 using tcu::StringTemplate;
63 using tcu::TestCase;
64 typedef TestCase::IterateResult IterateResult;
65 using tcu::TestLog;
66 using tcu::ScopedLogSection;
67 using glu::Program;
68 using glu::Shader;
69 using glu::Framebuffer;
70 using glu::SHADERTYPE_VERTEX;
71 using glu::SHADERTYPE_FRAGMENT;
72 using namespace glw;
73 
74 enum { VIEWPORT_SIZE = 128, FRAMEBUFFER_SIZE = 128 };
75 
getInteger(ContextWrapper & gl,GLenum queryParam)76 GLint getInteger (ContextWrapper& gl, GLenum queryParam)
77 {
78 	GLint ret = 0;
79 	GLU_CHECK_CALL_ERROR(
80 		gl.glGetIntegerv(queryParam, &ret),
81 		gl.glGetError());
82 	gl.log() << TestLog::Message << "// Single integer output: " << ret << TestLog::EndMessage;
83 	return ret;
84 }
85 
86 #define GLSL100_SRC(BODY) ("#version 100\n" #BODY "\n")
87 
88 static const char* const s_vertexShaderSrc = GLSL100_SRC(
89 	attribute vec2 pos;
90 	void main()
91 	{
92 		gl_Position = vec4(pos.xy, 0.0, 1.0);
93 	}
94 	);
95 
96 static const char* const s_fragmentShaderSrc = GLSL100_SRC(
97 	void main()
98 	{
99 		gl_FragColor = vec4(1.0);
100 	}
101 	);
102 
103 class CheckedShader : public Shader
104 {
105 public:
CheckedShader(const RenderContext & renderCtx,glu::ShaderType type,const string & src)106 	CheckedShader (const RenderContext& renderCtx, glu::ShaderType type, const string& src)
107 		: Shader (renderCtx, type)
108 	{
109 		const char* const srcStr = src.c_str();
110 		setSources(1, &srcStr, DE_NULL);
111 		compile();
112 		TCU_CHECK(getCompileStatus());
113 	}
114 };
115 
116 class CheckedProgram : public Program
117 {
118 public:
CheckedProgram(const RenderContext & renderCtx,GLuint vtxShader,GLuint fragShader)119 	CheckedProgram	(const RenderContext& renderCtx, GLuint vtxShader, GLuint fragShader)
120 		: Program	(renderCtx)
121 	{
122 		attachShader(vtxShader);
123 		attachShader(fragShader);
124 		link();
125 		TCU_CHECK(getLinkStatus());
126 	}
127 };
128 
ContextWrapper(const Context & ctx)129 ContextWrapper::ContextWrapper (const Context& ctx)
130 	: CallLogWrapper	(ctx.gl(), ctx.log())
131 	, m_ctx				(ctx)
132 {
133 	enableLogging(true);
134 }
135 
bind(GLuint name)136 void SimpleBinder::bind (GLuint name)
137 {
138 	(this->*m_bindFunc)(m_bindTarget, name);
139 }
140 
getBinding(void)141 GLuint SimpleBinder::getBinding (void)
142 {
143 	return getInteger(*this, m_bindingParam);
144 }
145 
gen(void)146 GLuint SimpleType::gen (void)
147 {
148 	GLuint ret;
149 	(this->*m_genFunc)(1, &ret);
150 	return ret;
151 }
152 
153 class VertexArrayBinder : public SimpleBinder
154 {
155 public:
VertexArrayBinder(Context & ctx)156 						VertexArrayBinder	(Context& ctx)
157 							: SimpleBinder	(ctx, 0, GL_NONE, GL_VERTEX_ARRAY_BINDING, true) {}
bind(GLuint name)158 	void				bind				(GLuint name) { glBindVertexArray(name); }
159 };
160 
161 class QueryBinder : public Binder
162 {
163 public:
QueryBinder(Context & ctx)164 						QueryBinder		(Context& ctx) : Binder(ctx) {}
bind(GLuint name)165 	void				bind			(GLuint name)
166 	{
167 		if (name != 0)
168 			glBeginQuery(GL_ANY_SAMPLES_PASSED, name);
169 		else
170 			glEndQuery(GL_ANY_SAMPLES_PASSED);
171 	}
getBinding(void)172 	GLuint				getBinding		(void) { return 0; }
173 };
174 
isDeleteFlagged(GLuint name)175 bool ProgramType::isDeleteFlagged (GLuint name)
176 {
177 	GLint deleteFlagged = 0;
178 	glGetProgramiv(name, GL_DELETE_STATUS, &deleteFlagged);
179 	return deleteFlagged != 0;
180 }
181 
isDeleteFlagged(GLuint name)182 bool ShaderType::isDeleteFlagged (GLuint name)
183 {
184 	GLint deleteFlagged = 0;
185 	glGetShaderiv(name, GL_DELETE_STATUS, &deleteFlagged);
186 	return deleteFlagged != 0;
187 }
188 
setupFbo(const Context & ctx,GLuint seed,GLuint fbo)189 void setupFbo (const Context& ctx, GLuint seed, GLuint fbo)
190 {
191 	const Functions& gl = ctx.getRenderContext().getFunctions();
192 
193 	GLU_CHECK_CALL_ERROR(gl.bindFramebuffer(GL_FRAMEBUFFER, fbo),
194 						 gl.getError());
195 
196 	if (seed == 0)
197 	{
198 		gl.clearColor(0.0, 0.0, 0.0, 1.0);
199 		GLU_CHECK_CALL_ERROR(gl.clear(GL_COLOR_BUFFER_BIT), gl.getError());
200 	}
201 	else
202 	{
203 		Random			rnd		(seed);
204 		const GLsizei	width	= rnd.getInt(0, FRAMEBUFFER_SIZE);
205 		const GLsizei	height	= rnd.getInt(0, FRAMEBUFFER_SIZE);
206 		const GLint		x		= rnd.getInt(0, FRAMEBUFFER_SIZE - width);
207 		const GLint		y		= rnd.getInt(0, FRAMEBUFFER_SIZE - height);
208 		const GLfloat	r1		= rnd.getFloat();
209 		const GLfloat	g1		= rnd.getFloat();
210 		const GLfloat	b1		= rnd.getFloat();
211 		const GLfloat	a1		= rnd.getFloat();
212 		const GLfloat	r2		= rnd.getFloat();
213 		const GLfloat	g2		= rnd.getFloat();
214 		const GLfloat	b2		= rnd.getFloat();
215 		const GLfloat	a2		= rnd.getFloat();
216 
217 		GLU_CHECK_CALL_ERROR(gl.clearColor(r1, g1, b1, a1), gl.getError());
218 		GLU_CHECK_CALL_ERROR(gl.clear(GL_COLOR_BUFFER_BIT), gl.getError());
219 		gl.scissor(x, y, width, height);
220 		gl.enable(GL_SCISSOR_TEST);
221 		gl.clearColor(r2, g2, b2, a2);
222 		gl.clear(GL_COLOR_BUFFER_BIT);
223 		gl.disable(GL_SCISSOR_TEST);
224 	}
225 
226 	gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
227 	GLU_CHECK_ERROR(gl.getError());
228 }
229 
drawFbo(const Context & ctx,GLuint fbo,Surface & dst)230 void drawFbo (const Context& ctx, GLuint fbo, Surface& dst)
231 {
232 	const RenderContext& renderCtx = ctx.getRenderContext();
233 	const Functions& gl = renderCtx.getFunctions();
234 
235 	GLU_CHECK_CALL_ERROR(
236 		gl.bindFramebuffer(GL_FRAMEBUFFER, fbo),
237 		gl.getError());
238 
239 	dst.setSize(FRAMEBUFFER_SIZE, FRAMEBUFFER_SIZE);
240 	glu::readPixels(renderCtx, 0, 0, dst.getAccess());
241 	GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels from framebuffer");
242 
243 	GLU_CHECK_CALL_ERROR(
244 		gl.bindFramebuffer(GL_FRAMEBUFFER, 0),
245 		gl.getError());
246 }
247 
getFboAttachment(const Functions & gl,GLuint fbo,GLenum requiredType)248 GLuint getFboAttachment (const Functions& gl, GLuint fbo, GLenum requiredType)
249 {
250 	GLint type = 0, name = 0;
251 	gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
252 	GLU_CHECK_CALL_ERROR(
253 		gl.getFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
254 											   GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
255 											   &type),
256 		gl.getError());
257 
258 	if (GLenum(type) != requiredType || GLenum(type) == GL_NONE)
259 		return 0;
260 
261 	GLU_CHECK_CALL_ERROR(
262 		gl.getFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
263 											   GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
264 											   &name),
265 		gl.getError());
266 	gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
267 	GLU_CHECK_ERROR(gl.getError());
268 
269 	return name;
270 }
271 
initAttachment(GLuint seed,GLuint element)272 void FboAttacher::initAttachment (GLuint seed, GLuint element)
273 {
274 	Binder& binder = *getElementType().binder();
275 	Framebuffer fbo(getRenderContext());
276 
277 	enableLogging(false);
278 
279 	binder.enableLogging(false);
280 	binder.bind(element);
281 	initStorage();
282 	binder.bind(0);
283 	binder.enableLogging(true);
284 
285 	attach(element, *fbo);
286 	setupFbo(getContext(), seed, *fbo);
287 	detach(element, *fbo);
288 
289 	enableLogging(true);
290 
291 	log() << TestLog::Message
292 		  << "// Drew to " << getElementType().getName() << " " << element
293 		  << " with seed " << seed << "."
294 		  << TestLog::EndMessage;
295 }
296 
drawContainer(GLuint fbo,Surface & dst)297 void FboInputAttacher::drawContainer (GLuint fbo, Surface& dst)
298 {
299 	drawFbo(getContext(), fbo, dst);
300 	log() << TestLog::Message
301 		  << "// Read pixels from framebuffer " << fbo << " to output image."
302 		  << TestLog::EndMessage;
303 }
304 
setupContainer(GLuint seed,GLuint fbo)305 void FboOutputAttacher::setupContainer (GLuint seed, GLuint fbo)
306 {
307 	setupFbo(getContext(), seed, fbo);
308 	log() << TestLog::Message
309 		  << "// Drew to framebuffer " << fbo << " with seed " << seed << "."
310 		  << TestLog::EndMessage;
311 }
312 
drawAttachment(GLuint element,Surface & dst)313 void FboOutputAttacher::drawAttachment (GLuint element, Surface& dst)
314 {
315 	Framebuffer fbo(getRenderContext());
316 	m_attacher.enableLogging(false);
317 	m_attacher.attach(element, *fbo);
318 	drawFbo(getContext(), *fbo, dst);
319 	m_attacher.detach(element, *fbo);
320 	m_attacher.enableLogging(true);
321 	log() << TestLog::Message
322 		  << "// Read pixels from " << m_attacher.getElementType().getName() << " " << element
323 		  << " to output image."
324 		  << TestLog::EndMessage;
325 	GLU_CHECK_ERROR(gl().getError());
326 }
327 
attach(GLuint texture,GLuint fbo)328 void TextureFboAttacher::attach (GLuint texture, GLuint fbo)
329 {
330 	GLU_CHECK_CALL_ERROR(
331 		glBindFramebuffer(GL_FRAMEBUFFER, fbo),
332 		gl().getError());
333 	GLU_CHECK_CALL_ERROR(
334 		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
335 								  GL_TEXTURE_2D, texture, 0),
336 		gl().getError());
337 	GLU_CHECK_CALL_ERROR(
338 		glBindFramebuffer(GL_FRAMEBUFFER, 0),
339 		gl().getError());
340 }
341 
detach(GLuint texture,GLuint fbo)342 void TextureFboAttacher::detach (GLuint texture, GLuint fbo)
343 {
344 	DE_UNREF(texture);
345 	GLU_CHECK_CALL_ERROR(
346 		glBindFramebuffer(GL_FRAMEBUFFER, fbo),
347 		gl().getError());
348 	GLU_CHECK_CALL_ERROR(
349 		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0),
350 		gl().getError());
351 	GLU_CHECK_CALL_ERROR(
352 		glBindFramebuffer(GL_FRAMEBUFFER, 0),
353 		gl().getError());
354 }
355 
getAttachment(GLuint fbo)356 GLuint TextureFboAttacher::getAttachment (GLuint fbo)
357 {
358 	return getFboAttachment(gl(), fbo, GL_TEXTURE);
359 }
360 
initStorage(void)361 void TextureFboAttacher::initStorage (void)
362 {
363 	GLU_CHECK_CALL_ERROR(
364 		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, FRAMEBUFFER_SIZE, FRAMEBUFFER_SIZE, 0,
365 					 GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, DE_NULL),
366 		gl().getError());
367 }
368 
initStorage(void)369 void RboFboAttacher::initStorage (void)
370 {
371 	GLU_CHECK_CALL_ERROR(
372 		glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, FRAMEBUFFER_SIZE, FRAMEBUFFER_SIZE),
373 		gl().getError());
374 }
375 
attach(GLuint rbo,GLuint fbo)376 void RboFboAttacher::attach (GLuint rbo, GLuint fbo)
377 {
378 	GLU_CHECK_CALL_ERROR(
379 		glBindFramebuffer(GL_FRAMEBUFFER, fbo),
380 		gl().getError());
381 	GLU_CHECK_CALL_ERROR(
382 		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo),
383 		gl().getError());
384 	GLU_CHECK_CALL_ERROR(
385 		glBindFramebuffer(GL_FRAMEBUFFER, 0),
386 		gl().getError());
387 }
388 
detach(GLuint rbo,GLuint fbo)389 void RboFboAttacher::detach (GLuint rbo, GLuint fbo)
390 {
391 	DE_UNREF(rbo);
392 	GLU_CHECK_CALL_ERROR(
393 		glBindFramebuffer(GL_FRAMEBUFFER, fbo),
394 		gl().getError());
395 	GLU_CHECK_CALL_ERROR(
396 		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0),
397 		gl().getError());
398 	GLU_CHECK_CALL_ERROR(
399 		glBindFramebuffer(GL_FRAMEBUFFER, 0),
400 		gl().getError());
401 }
402 
getAttachment(GLuint fbo)403 GLuint RboFboAttacher::getAttachment (GLuint fbo)
404 {
405 	return getFboAttachment(gl(), fbo, GL_RENDERBUFFER);
406 }
407 
408 static const char* const s_fragmentShaderTemplate = GLSL100_SRC(
409 	void main()
410 	{
411 		gl_FragColor = vec4(${RED}, ${GREEN}, ${BLUE}, 1.0);
412 	}
413 	);
414 
initAttachment(GLuint seed,GLuint shader)415 void ShaderProgramAttacher::initAttachment (GLuint seed, GLuint shader)
416 {
417 	using					de::insert;
418 	using					de::floatToString;
419 
420 	Random					rnd(seed);
421 	map<string, string>		params;
422 	const StringTemplate	sourceTmpl	(s_fragmentShaderTemplate);
423 
424 	insert(params, "RED",	floatToString(rnd.getFloat(), 4));
425 	insert(params, "GREEN",	floatToString(rnd.getFloat(), 4));
426 	insert(params, "BLUE",	floatToString(rnd.getFloat(), 4));
427 
428 	{
429 		const string			source		= sourceTmpl.specialize(params);
430 		const char* const		sourceStr	= source.c_str();
431 
432 		GLU_CHECK_CALL_ERROR(glShaderSource(shader, 1, &sourceStr, DE_NULL), gl().getError());
433 		GLU_CHECK_CALL_ERROR(glCompileShader(shader), gl().getError());
434 
435 		{
436 			GLint compileStatus = 0;
437 			gl().getShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
438 			TCU_CHECK_MSG(compileStatus != 0, sourceStr);
439 		}
440 	}
441 }
442 
attach(GLuint shader,GLuint program)443 void ShaderProgramAttacher::attach (GLuint shader, GLuint program)
444 {
445 	GLU_CHECK_CALL_ERROR(
446 		glAttachShader(program, shader),
447 		gl().getError());
448 }
449 
detach(GLuint shader,GLuint program)450 void ShaderProgramAttacher::detach (GLuint shader, GLuint program)
451 {
452 	GLU_CHECK_CALL_ERROR(
453 		glDetachShader(program, shader),
454 		gl().getError());
455 }
456 
getAttachment(GLuint program)457 GLuint ShaderProgramAttacher::getAttachment (GLuint program)
458 {
459 	GLuint			shaders[2]	= { 0, 0 };
460 	const GLsizei	shadersLen	= DE_LENGTH_OF_ARRAY(shaders);
461 	GLsizei			numShaders	= 0;
462 	GLuint			ret			= 0;
463 
464 	gl().getAttachedShaders(program, shadersLen, &numShaders, shaders);
465 
466 	// There should ever be at most one attached shader in normal use, but if
467 	// something is wrong, the temporary vertex shader might not have been
468 	// detached properly, so let's find the fragment shader explicitly.
469 	for (int ndx = 0; ndx < de::min<GLsizei>(shadersLen, numShaders); ++ndx)
470 	{
471 		GLint shaderType = GL_NONE;
472 		gl().getShaderiv(shaders[ndx], GL_SHADER_TYPE, &shaderType);
473 
474 		if (shaderType == GL_FRAGMENT_SHADER)
475 		{
476 			ret = shaders[ndx];
477 			break;
478 		}
479 	}
480 
481 	return ret;
482 }
483 
setViewport(const RenderContext & renderCtx,const Rectangle & rect)484 void setViewport (const RenderContext& renderCtx, const Rectangle& rect)
485 {
486 	renderCtx.getFunctions().viewport(rect.x, rect.y, rect.width, rect.height);
487 }
488 
readRectangle(const RenderContext & renderCtx,const Rectangle & rect,Surface & dst)489 void readRectangle (const RenderContext& renderCtx, const Rectangle& rect, Surface& dst)
490 {
491 	dst.setSize(rect.width, rect.height);
492 	glu::readPixels(renderCtx, rect.x, rect.y, dst.getAccess());
493 }
494 
randomViewport(const RenderContext & ctx,GLint maxWidth,GLint maxHeight,Random & rnd)495 Rectangle randomViewport (const RenderContext& ctx, GLint maxWidth, GLint maxHeight,
496 						  Random& rnd)
497 {
498 	const RenderTarget&	target	= ctx.getRenderTarget();
499 	const GLint			width	= de::min(target.getWidth(), maxWidth);
500 	const GLint			xOff	= rnd.getInt(0, target.getWidth() - width);
501 	const GLint			height	= de::min(target.getHeight(), maxHeight);
502 	const GLint			yOff	= rnd.getInt(0, target.getHeight() - height);
503 
504 	return Rectangle(xOff, yOff, width, height);
505 }
506 
drawContainer(GLuint program,Surface & dst)507 void ShaderProgramInputAttacher::drawContainer (GLuint program, Surface& dst)
508 {
509 	static const float	s_vertices[6]	= { -1.0, 0.0, 1.0, 1.0, 0.0, -1.0 };
510 	Random				rnd				(program);
511 	CheckedShader		vtxShader		(getRenderContext(),
512 										 SHADERTYPE_VERTEX, s_vertexShaderSrc);
513 	const Rectangle		viewport		= randomViewport(getRenderContext(),
514 														 VIEWPORT_SIZE, VIEWPORT_SIZE, rnd);
515 
516 	gl().attachShader(program, vtxShader.getShader());
517 	gl().linkProgram(program);
518 
519 	{
520 		GLint linkStatus = 0;
521 		gl().getProgramiv(program, GL_LINK_STATUS, &linkStatus);
522 		TCU_CHECK(linkStatus != 0);
523 	}
524 
525 	log() << TestLog::Message
526 		  << "// Attached a temporary vertex shader and linked program " << program
527 		  << TestLog::EndMessage;
528 
529 	setViewport(getRenderContext(), viewport);
530 	log() << TestLog::Message << "// Positioned viewport randomly" << TestLog::EndMessage;
531 
532 	glUseProgram(program);
533 	{
534 		GLint posLoc = gl().getAttribLocation(program, "pos");
535 		TCU_CHECK(posLoc >= 0);
536 
537 		gl().enableVertexAttribArray(posLoc);
538 
539 		gl().clearColor(0, 0, 0, 1);
540 		gl().clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
541 		gl().vertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, 0, s_vertices);
542 		gl().drawArrays(GL_TRIANGLES, 0, 3);
543 
544 		gl().disableVertexAttribArray(posLoc);
545 		log () << TestLog::Message << "// Drew a fixed triangle" << TestLog::EndMessage;
546 	}
547 	glUseProgram(0);
548 
549 	readRectangle(getRenderContext(), viewport, dst);
550 	log() << TestLog::Message << "// Copied viewport to output image" << TestLog::EndMessage;
551 
552 	gl().detachShader(program, vtxShader.getShader());
553 	log() << TestLog::Message << "// Removed temporary vertex shader" << TestLog::EndMessage;
554 }
555 
ES2Types(const Context & ctx)556 ES2Types::ES2Types (const Context& ctx)
557 	: Types			(ctx)
558 	, m_bufferBind	(ctx, &CallLogWrapper::glBindBuffer,
559 					 GL_ARRAY_BUFFER, GL_ARRAY_BUFFER_BINDING)
560 	, m_bufferType	(ctx, "buffer", &CallLogWrapper::glGenBuffers,
561 					 &CallLogWrapper::glDeleteBuffers,
562 					 &CallLogWrapper::glIsBuffer, &m_bufferBind)
563 	, m_textureBind	(ctx, &CallLogWrapper::glBindTexture, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D)
564 	, m_textureType	(ctx, "texture", &CallLogWrapper::glGenTextures,
565 					 &CallLogWrapper::glDeleteTextures,
566 					 &CallLogWrapper::glIsTexture, &m_textureBind)
567 	, m_rboBind		(ctx, &CallLogWrapper::glBindRenderbuffer,
568 					 GL_RENDERBUFFER, GL_RENDERBUFFER_BINDING)
569 	, m_rboType		(ctx, "renderbuffer",
570 					 &CallLogWrapper::glGenRenderbuffers,
571 					 &CallLogWrapper::glDeleteRenderbuffers,
572 					 &CallLogWrapper::glIsRenderbuffer, &m_rboBind)
573 	, m_fboBind		(ctx, &CallLogWrapper::glBindFramebuffer,
574 					 GL_FRAMEBUFFER, GL_FRAMEBUFFER_BINDING)
575 	, m_fboType		(ctx, "framebuffer",
576 					 &CallLogWrapper::glGenFramebuffers,
577 					 &CallLogWrapper::glDeleteFramebuffers,
578 					 &CallLogWrapper::glIsFramebuffer, &m_fboBind)
579 	, m_shaderType	(ctx)
580 	, m_programType	(ctx)
581 	, m_texFboAtt	(ctx, m_textureType, m_fboType)
582 	, m_texFboInAtt	(m_texFboAtt)
583 	, m_texFboOutAtt(m_texFboAtt)
584 	, m_rboFboAtt	(ctx, m_rboType, m_fboType)
585 	, m_rboFboInAtt	(m_rboFboAtt)
586 	, m_rboFboOutAtt(m_rboFboAtt)
587 	, m_shaderAtt	(ctx, m_shaderType, m_programType)
588 	, m_shaderInAtt	(m_shaderAtt)
589 {
590 	Type* const types[] =
591 	{
592 		&m_bufferType, &m_textureType, &m_rboType, &m_fboType, &m_shaderType, &m_programType
593 	};
594 	m_types.insert(m_types.end(), DE_ARRAY_BEGIN(types), DE_ARRAY_END(types));
595 
596 	m_attachers.push_back(&m_texFboAtt);
597 	m_attachers.push_back(&m_rboFboAtt);
598 	m_attachers.push_back(&m_shaderAtt);
599 
600 	m_inAttachers.push_back(&m_texFboInAtt);
601 	m_inAttachers.push_back(&m_rboFboInAtt);
602 	m_inAttachers.push_back(&m_shaderInAtt);
603 
604 	m_outAttachers.push_back(&m_texFboOutAtt);
605 	m_outAttachers.push_back(&m_rboFboOutAtt);
606 }
607 
608 class Name
609 {
610 public:
Name(Type & type)611 				Name		(Type& type) : m_type(type), m_name(type.gen()) {}
Name(Type & type,GLuint name)612 				Name		(Type& type, GLuint name) : m_type(type), m_name(name) {}
~Name(void)613 				~Name		(void) { m_type.release(m_name); }
operator *(void) const614 	GLuint		operator*	(void) const { return m_name; }
615 
616 private:
617 	Type&			m_type;
618 	const GLuint	m_name;
619 };
620 
621 class ResultCollector
622 {
623 public:
624 					ResultCollector		(TestContext& testCtx);
625 	bool			check				(bool cond, const char* msg);
626 	void			fail				(const char* msg);
627 	void			warn				(const char* msg);
628 					~ResultCollector	(void);
629 
630 private:
631 	void			addResult			(qpTestResult result, const char* msg);
632 
633 	TestContext&	m_testCtx;
634 	TestLog&		m_log;
635 	qpTestResult	m_result;
636 	const char*		m_message;
637 };
638 
ResultCollector(TestContext & testCtx)639 ResultCollector::ResultCollector (TestContext& testCtx)
640 	: m_testCtx		(testCtx)
641 	, m_log			(testCtx.getLog())
642 	, m_result		(QP_TEST_RESULT_PASS)
643 	, m_message		("Pass")
644 {
645 }
646 
check(bool cond,const char * msg)647 bool ResultCollector::check (bool cond, const char* msg)
648 {
649 	if (!cond)
650 		fail(msg);
651 	return cond;
652 }
653 
addResult(qpTestResult result,const char * msg)654 void ResultCollector::addResult (qpTestResult result, const char* msg)
655 {
656 	m_log << TestLog::Message << "// Fail: " << msg << TestLog::EndMessage;
657 	if (m_result == QP_TEST_RESULT_PASS)
658 	{
659 		m_result = result;
660 		m_message = msg;
661 	}
662 	else
663 	{
664 		if (result == QP_TEST_RESULT_FAIL)
665 			m_result = result;
666 		m_message = "Multiple problems, see log for details";
667 	}
668 }
669 
fail(const char * msg)670 void ResultCollector::fail (const char* msg)
671 {
672 	addResult(QP_TEST_RESULT_FAIL, msg);
673 }
674 
warn(const char * msg)675 void ResultCollector::warn (const char* msg)
676 {
677 	addResult(QP_TEST_RESULT_QUALITY_WARNING, msg);
678 }
679 
~ResultCollector(void)680 ResultCollector::~ResultCollector (void)
681 {
682 	m_testCtx.setTestResult(m_result, m_message);
683 }
684 
685 class TestBase : public TestCase, protected CallLogWrapper
686 {
687 protected:
688 							TestBase			(const char*	name,
689 												 const char*	description,
690 												 const Context&	ctx);
691 
692 	// Copy ContextWrapper since MI (except for CallLogWrapper) is a no-no.
getContext(void) const693 	const Context&			getContext			(void) const { return m_ctx; }
getRenderContext(void) const694 	const RenderContext&	getRenderContext	(void) const { return m_ctx.getRenderContext(); }
gl(void) const695 	const Functions&		gl					(void) const { return m_ctx.gl(); }
log(void) const696 	TestLog&				log					(void) const { return m_ctx.log(); }
697 	void					init				(void);
698 
699 	Context					m_ctx;
700 	Random					m_rnd;
701 };
702 
TestBase(const char * name,const char * description,const Context & ctx)703 TestBase::TestBase (const char* name, const char* description, const Context& ctx)
704 	: TestCase			(ctx.getTestContext(), name, description)
705 	, CallLogWrapper	(ctx.gl(), ctx.log())
706 	, m_ctx				(ctx)
707 	, m_rnd				(deStringHash(name))
708 {
709 	enableLogging(true);
710 }
711 
init(void)712 void TestBase::init (void)
713 {
714 	m_rnd = Random(deStringHash(getName()));
715 }
716 
717 class LifeTest : public TestBase
718 {
719 public:
720 	typedef void			(LifeTest::*TestFunction)	(void);
721 
LifeTest(const char * name,const char * description,Type & type,TestFunction test)722 							LifeTest					(const char*	name,
723 														 const char*	description,
724 														 Type&			type,
725 														 TestFunction 	test)
726 								: TestBase		(name, description, type.getContext())
727 								, m_type		(type)
728 								, m_test		(test) {}
729 
730 	IterateResult			iterate						(void);
731 
732 	void					testGen						(void);
733 	void					testDelete					(void);
734 	void					testBind					(void);
735 	void					testDeleteBound				(void);
736 	void					testBindNoGen				(void);
737 	void					testDeleteUsed				(void);
738 
739 private:
binder(void)740 	Binder&					binder						(void) { return *m_type.binder(); }
741 
742 	Type&					m_type;
743 	TestFunction			m_test;
744 };
745 
iterate(void)746 IterateResult LifeTest::iterate (void)
747 {
748 	(this->*m_test)();
749 	return STOP;
750 }
751 
testGen(void)752 void LifeTest::testGen (void)
753 {
754 	ResultCollector	errors	(getTestContext());
755 	Name			name	(m_type);
756 
757 	if (m_type.genCreates())
758 		errors.check(m_type.exists(*name), "Gen* should have created an object, but didn't");
759 	else
760 		errors.check(!m_type.exists(*name), "Gen* should not have created an object, but did");
761 }
762 
testDelete(void)763 void LifeTest::testDelete (void)
764 {
765 	ResultCollector	errors	(getTestContext());
766 	GLuint			name	= m_type.gen();
767 
768 	m_type.release(name);
769 	errors.check(!m_type.exists(name), "Object still exists after deletion");
770 }
771 
testBind(void)772 void LifeTest::testBind (void)
773 {
774 	ResultCollector	errors	(getTestContext());
775 	Name			name	(m_type);
776 
777 	binder().bind(*name);
778 	GLU_EXPECT_NO_ERROR(gl().getError(), "Bind failed");
779 	errors.check(m_type.exists(*name), "Object does not exist after binding");
780 	binder().bind(0);
781 }
782 
testDeleteBound(void)783 void LifeTest::testDeleteBound (void)
784 {
785 	const GLuint	id		= m_type.gen();
786 	ResultCollector	errors	(getTestContext());
787 
788 	binder().bind(id);
789 	m_type.release(id);
790 
791 	if (m_type.nameLingers())
792 	{
793 		errors.check(gl().getError() == GL_NO_ERROR, "Deleting bound object failed");
794 		errors.check(binder().getBinding() == id,
795 					 "Deleting bound object did not retain binding");
796 		errors.check(m_type.exists(id),
797 					 "Deleting bound object made its name invalid");
798 		errors.check(m_type.isDeleteFlagged(id),
799 					 "Deleting bound object did not flag the object for deletion");
800 		binder().bind(0);
801 	}
802 	else
803 	{
804 		errors.check(gl().getError() == GL_NO_ERROR, "Deleting bound object failed");
805 		errors.check(binder().getBinding() == 0,
806 					 "Deleting bound object did not remove binding");
807 		errors.check(!m_type.exists(id),
808 					 "Deleting bound object did not make its name invalid");
809 		binder().bind(0);
810 	}
811 
812 	errors.check(binder().getBinding() == 0, "Unbinding didn't remove binding");
813 	errors.check(!m_type.exists(id), "Name is still valid after deleting and unbinding");
814 }
815 
testBindNoGen(void)816 void LifeTest::testBindNoGen (void)
817 {
818 	ResultCollector	errors	(getTestContext());
819 	const GLuint	id		= m_rnd.getUint32();
820 
821 	if (!errors.check(!m_type.exists(id), "Randomly chosen identifier already exists"))
822 		return;
823 
824 	Name			name	(m_type, id);
825 	binder().bind(*name);
826 
827 	if (binder().genRequired())
828 	{
829 		errors.check(glGetError() == GL_INVALID_OPERATION,
830 					 "Did not fail when binding a name not generated by Gen* call");
831 		errors.check(!m_type.exists(*name),
832 					 "Bind* created an object for a name not generated by a Gen* call");
833 	}
834 	else
835 	{
836 		errors.check(glGetError() == GL_NO_ERROR,
837 					 "Failed when binding a name not generated by Gen* call");
838 		errors.check(m_type.exists(*name),
839 					 "Object was not created by the Bind* call");
840 	}
841 }
842 
testDeleteUsed(void)843 void LifeTest::testDeleteUsed (void)
844 {
845 	ResultCollector	errors(getTestContext());
846 	GLuint			programId = 0;
847 
848 	{
849 		CheckedShader	vtxShader	(getRenderContext(),
850 									 SHADERTYPE_VERTEX, s_vertexShaderSrc);
851 		CheckedShader	fragShader	(getRenderContext(),
852 									 SHADERTYPE_FRAGMENT, s_fragmentShaderSrc);
853 		CheckedProgram	program		(getRenderContext(),
854 									 vtxShader.getShader(), fragShader.getShader());
855 
856 		programId = program.getProgram();
857 
858 		log() << TestLog::Message << "// Created and linked program " << programId
859 			  << TestLog::EndMessage;
860 		GLU_CHECK_CALL_ERROR(glUseProgram(programId), gl().getError());
861 
862 		log() << TestLog::Message << "// Deleted program " << programId
863 			  << TestLog::EndMessage;
864 	}
865 	TCU_CHECK(glIsProgram(programId));
866 	{
867 		GLint deleteFlagged = 0;
868 		glGetProgramiv(programId, GL_DELETE_STATUS, &deleteFlagged);
869 		errors.check(deleteFlagged != 0, "Program object was not flagged as deleted");
870 	}
871 	GLU_CHECK_CALL_ERROR(glUseProgram(0), gl().getError());
872 	errors.check(!gl().isProgram(programId),
873 				 "Deleted program name still valid after being made non-current");
874 }
875 
876 class AttachmentTest : public TestBase
877 {
878 public:
879 	typedef void			(AttachmentTest::*TestFunction)	(void);
AttachmentTest(const char * name,const char * description,Attacher & attacher,TestFunction test)880 							AttachmentTest					(const char*	name,
881 															 const char*	description,
882 															 Attacher&		attacher,
883 															 TestFunction	test)
884 								: TestBase		(name, description, attacher.getContext())
885 								, m_attacher	(attacher)
886 								, m_test		(test) {}
887 	IterateResult			iterate							(void);
888 
889 	void					testDeletedNames				(void);
890 	void					testDeletedBinding				(void);
891 	void					testDeletedReattach				(void);
892 
893 private:
894 	Attacher&				m_attacher;
895 	const TestFunction		m_test;
896 };
897 
iterate(void)898 IterateResult AttachmentTest::iterate (void)
899 {
900 	(this->*m_test)();
901 	return STOP;
902 }
903 
getAttachment(Attacher & attacher,GLuint container)904 GLuint getAttachment (Attacher& attacher, GLuint container)
905 {
906 	const GLuint queriedAttachment = attacher.getAttachment(container);
907 	attacher.log() << TestLog::Message
908 				   << "// Result of query for " << attacher.getElementType().getName()
909 				   << " attached to " << attacher.getContainerType().getName() << " "
910 				   << container << ": " << queriedAttachment << "."
911 				   << TestLog::EndMessage;
912 	return queriedAttachment;
913 }
914 
testDeletedNames(void)915 void AttachmentTest::testDeletedNames (void)
916 {
917 	Type&			elemType		= m_attacher.getElementType();
918 	Type&			containerType	= m_attacher.getContainerType();
919 	Name			container		(containerType);
920 	ResultCollector	errors			(getTestContext());
921 	GLuint			elementId		= 0;
922 
923 	{
924 		Name element(elemType);
925 		elementId = *element;
926 		m_attacher.initAttachment(0, *element);
927 		m_attacher.attach(*element, *container);
928 		errors.check(getAttachment(m_attacher, *container) == elementId,
929 					 "Attachment name not returned by query even before deletion.");
930 	}
931 
932 	// "Such a container or other context may continue using the object, and
933 	// may still contain state identifying its name as being currently bound"
934 	//
935 	// We here interpret "may" to mean that whenever the container has a
936 	// deleted object attached to it, a query will return that object's former
937 	// name.
938 	errors.check(getAttachment(m_attacher, *container) == elementId,
939 				 "Attachment name not returned by query after attachment was deleted.");
940 
941 	if (elemType.nameLingers())
942 		errors.check(elemType.exists(elementId),
943 					 "Attached object name no longer valid after deletion.");
944 	else
945 		errors.check(!elemType.exists(elementId),
946 					 "Attached object name still valid after deletion.");
947 
948 	m_attacher.detach(elementId, *container);
949 	errors.check(getAttachment(m_attacher, *container) == 0,
950 				 "Attachment name returned by query even after detachment.");
951 	errors.check(!elemType.exists(elementId),
952 				 "Deleted attached object name still usable after detachment.");
953 };
954 
955 class InputAttachmentTest : public TestBase
956 {
957 public:
InputAttachmentTest(const char * name,const char * description,InputAttacher & inputAttacher)958 					InputAttachmentTest	(const char*	name,
959 										 const char*	description,
960 										 InputAttacher&	inputAttacher)
961 						: TestBase			(name, description, inputAttacher.getContext())
962 						, m_inputAttacher	(inputAttacher) {}
963 
964 	IterateResult	iterate				(void);
965 
966 private:
967 	InputAttacher&	m_inputAttacher;
968 };
969 
replaceName(Type & type,GLuint oldName,TestLog & log)970 GLuint replaceName (Type& type, GLuint oldName, TestLog& log)
971 {
972 	const Binder* const	binder		= type.binder();
973 	const bool			genRequired	= binder == DE_NULL || binder->genRequired();
974 
975 	if (genRequired)
976 		return type.gen();
977 
978 	log << TestLog::Message
979 		<< "// Type does not require Gen* for binding, reusing old id " << oldName << "."
980 		<< TestLog::EndMessage;
981 
982 	return oldName;
983 }
984 
iterate(void)985 IterateResult InputAttachmentTest::iterate (void)
986 {
987 	Attacher&		attacher		= m_inputAttacher.getAttacher();
988 	Type&			containerType	= attacher.getContainerType();
989 	Type&			elementType		= attacher.getElementType();
990 	Name			container		(containerType);
991 	GLuint			elementId		= 0;
992 	const GLuint	refSeed			= m_rnd.getUint32();
993 	const GLuint	newSeed			= m_rnd.getUint32();
994 	ResultCollector	errors			(getTestContext());
995 
996 	Surface			refSurface;		// Surface from drawing with refSeed-seeded attachment
997 	Surface			delSurface;		// Surface from drawing with deleted refSeed attachment
998 	Surface			newSurface;		// Surface from drawing with newSeed-seeded attachment
999 
1000 	log() << TestLog::Message
1001 		  << "Testing if writing to a newly created object modifies a deleted attachment"
1002 		  << TestLog::EndMessage;
1003 
1004 	{
1005 		ScopedLogSection	section	(log(),
1006 									 "Write to original", "Writing to an original attachment");
1007 		const Name			element	(elementType);
1008 
1009 		elementId = *element;
1010 		attacher.initAttachment(refSeed, elementId);
1011 		attacher.attach(elementId, *container);
1012 		m_inputAttacher.drawContainer(*container, refSurface);
1013 		// element gets deleted here
1014 		log() << TestLog::Message << "// Deleting attachment";
1015 	}
1016 	{
1017 		ScopedLogSection section	(log(), "Write to new",
1018 									 "Writing to a new attachment after deleting the original");
1019 		const GLuint	newId		= replaceName(elementType, elementId, log());
1020 		const Name		newElement	(elementType, newId);
1021 
1022 		attacher.initAttachment(newSeed, newId);
1023 
1024 		m_inputAttacher.drawContainer(*container, delSurface);
1025 		attacher.detach(elementId, *container);
1026 
1027 		attacher.attach(newId, *container);
1028 		m_inputAttacher.drawContainer(*container, newSurface);
1029 		attacher.detach(newId, *container);
1030 	}
1031 	{
1032 		const bool surfacesMatch = tcu::pixelThresholdCompare(
1033 			log(), "Reading from deleted",
1034 			"Comparison result from reading from a container with a deleted attachment "
1035 			"before and after writing to a fresh object.",
1036 			refSurface, delSurface, RGBA(0, 0, 0, 0), tcu::COMPARE_LOG_RESULT);
1037 
1038 		errors.check(
1039 			surfacesMatch,
1040 			"Writing to a fresh object modified the container with a deleted attachment.");
1041 
1042 		if (!surfacesMatch)
1043 			log() << TestLog::Image("New attachment",
1044 									"Container state after attached to the fresh object",
1045 									newSurface);
1046 	}
1047 
1048 	return STOP;
1049 }
1050 
1051 class OutputAttachmentTest : public TestBase
1052 {
1053 public:
OutputAttachmentTest(const char * name,const char * description,OutputAttacher & outputAttacher)1054 						OutputAttachmentTest			(const char*		name,
1055 														 const char*		description,
1056 														 OutputAttacher&	outputAttacher)
1057 							: TestBase			(name, description,
1058 												 outputAttacher.getContext())
1059 							, m_outputAttacher	(outputAttacher) {}
1060 	IterateResult		iterate							(void);
1061 
1062 private:
1063 	OutputAttacher&		m_outputAttacher;
1064 };
1065 
iterate(void)1066 IterateResult OutputAttachmentTest::iterate (void)
1067 {
1068 	Attacher&		attacher		= m_outputAttacher.getAttacher();
1069 	Type&			containerType	= attacher.getContainerType();
1070 	Type&			elementType		= attacher.getElementType();
1071 	Name			container		(containerType);
1072 	GLuint			elementId		= 0;
1073 	const GLuint	refSeed			= m_rnd.getUint32();
1074 	const GLuint	newSeed			= m_rnd.getUint32();
1075 	ResultCollector	errors			(getTestContext());
1076 	Surface			refSurface;		// Surface drawn from attachment to refSeed container
1077 	Surface			newSurface;		// Surface drawn from attachment to newSeed container
1078 	Surface			delSurface;		// Like newSurface, after writing to a deleted attachment
1079 
1080 	log() << TestLog::Message
1081 		  << "Testing if writing to a container with a deleted attachment "
1082 		  << "modifies a newly created object"
1083 		  << TestLog::EndMessage;
1084 
1085 	{
1086 		ScopedLogSection	section	(log(), "Write to existing",
1087 									 "Writing to a container with an existing attachment");
1088 		const Name			element	(elementType);
1089 
1090 		elementId = *element;
1091 		attacher.initAttachment(0, elementId);
1092 		attacher.attach(elementId, *container);
1093 
1094 		// For reference purposes, make note of what refSeed looks like.
1095 		m_outputAttacher.setupContainer(refSeed, *container);
1096 		m_outputAttacher.drawAttachment(elementId, refSurface);
1097 	}
1098 	{
1099 		ScopedLogSection	section		(log(), "Write to deleted",
1100 										 "Writing to a container after deletion of attachment");
1101 		const GLuint		newId		= replaceName(elementType, elementId, log());
1102 		const Name			newElement	(elementType, newId);
1103 
1104 		log() << TestLog::Message
1105 			  << "Creating a new object " << newId
1106 			  << TestLog::EndMessage;
1107 
1108 		log() << TestLog::Message
1109 			  << "Recording state of new object before writing to container"
1110 			  << TestLog::EndMessage;
1111 		attacher.initAttachment(newSeed, newId);
1112 		m_outputAttacher.drawAttachment(newId, newSurface);
1113 
1114 		log() << TestLog::Message
1115 			  << "Writing to container"
1116 			  << TestLog::EndMessage;
1117 
1118 		// Now re-write refSeed to the container.
1119 		m_outputAttacher.setupContainer(refSeed, *container);
1120 		// Does it affect the newly created attachment object?
1121 		m_outputAttacher.drawAttachment(newId, delSurface);
1122 	}
1123 	attacher.detach(elementId, *container);
1124 
1125 	const bool surfacesMatch = tcu::pixelThresholdCompare(
1126 		log(), "Writing to deleted",
1127 		"Comparison result from reading from a fresh object before and after "
1128 		"writing to a container with a deleted attachment",
1129 		newSurface, delSurface, RGBA(0, 0, 0, 0), tcu::COMPARE_LOG_RESULT);
1130 
1131 	errors.check(surfacesMatch,
1132 				 "Writing to container with deleted attachment modified a new object.");
1133 
1134 	if (!surfacesMatch)
1135 		log() << TestLog::Image(
1136 			"Original attachment",
1137 			"Result of container modification on original attachment before deletion.",
1138 			refSurface);
1139 	return STOP;
1140 };
1141 
1142 struct LifeTestSpec
1143 {
1144 	const char*				name;
1145 	LifeTest::TestFunction	func;
1146 	bool					needBind;
1147 };
1148 
createLifeTestGroup(TestContext & testCtx,const LifeTestSpec & spec,const vector<Type * > & types)1149 MovePtr<TestCaseGroup> createLifeTestGroup (TestContext& testCtx,
1150 											const LifeTestSpec& spec,
1151 											const vector<Type*>& types)
1152 {
1153 	MovePtr<TestCaseGroup> group(new TestCaseGroup(testCtx, spec.name, spec.name));
1154 
1155 	for (vector<Type*>::const_iterator it = types.begin(); it != types.end(); ++it)
1156 	{
1157 		Type& type = **it;
1158 		const char* name = type.getName();
1159 		if (!spec.needBind || type.binder() != DE_NULL)
1160 			group->addChild(new LifeTest(name, name, type, spec.func));
1161 	}
1162 
1163 	return group;
1164 }
1165 
1166 static const LifeTestSpec s_lifeTests[] =
1167 {
1168 	{ "gen",			&LifeTest::testGen,			false	},
1169 	{ "delete",			&LifeTest::testDelete,		false	},
1170 	{ "bind",			&LifeTest::testBind,		true	},
1171 	{ "delete_bound",	&LifeTest::testDeleteBound,	true	},
1172 	{ "bind_no_gen",	&LifeTest::testBindNoGen,	true	},
1173 };
1174 
attacherName(Attacher & attacher)1175 string attacherName (Attacher& attacher)
1176 {
1177 	ostringstream os;
1178 	os << attacher.getElementType().getName() << "_" <<  attacher.getContainerType().getName();
1179 	return os.str();
1180 }
1181 
addTestCases(TestCaseGroup & group,Types & types)1182 void addTestCases (TestCaseGroup& group, Types& types)
1183 {
1184 	TestContext& testCtx = types.getTestContext();
1185 
1186 	for (const LifeTestSpec* it = DE_ARRAY_BEGIN(s_lifeTests);
1187 		 it != DE_ARRAY_END(s_lifeTests); ++it)
1188 		group.addChild(createLifeTestGroup(testCtx, *it, types.getTypes()).release());
1189 
1190 	{
1191 		TestCaseGroup* const delUsedGroup =
1192 			new TestCaseGroup(testCtx, "delete_used", "Delete current program");
1193 		group.addChild(delUsedGroup);
1194 
1195 		delUsedGroup->addChild(
1196 			new LifeTest("program", "program", types.getProgramType(),
1197 						 &LifeTest::testDeleteUsed));
1198 	}
1199 
1200 	{
1201 		TestCaseGroup* const	attGroup	= new TestCaseGroup(
1202 			testCtx, "attach", "Attachment tests");
1203 		group.addChild(attGroup);
1204 
1205 		{
1206 			TestCaseGroup* const	nameGroup	= new TestCaseGroup(
1207 				testCtx, "deleted_name", "Name of deleted attachment");
1208 			attGroup->addChild(nameGroup);
1209 
1210 			const vector<Attacher*>& atts = types.getAttachers();
1211 			for (vector<Attacher*>::const_iterator it = atts.begin(); it != atts.end(); ++it)
1212 			{
1213 				const string name = attacherName(**it);
1214 				nameGroup->addChild(new AttachmentTest(name.c_str(), name.c_str(), **it,
1215 													   &AttachmentTest::testDeletedNames));
1216 			}
1217 		}
1218 		{
1219 			TestCaseGroup* inputGroup = new TestCaseGroup(
1220 				testCtx, "deleted_input", "Input from deleted attachment");
1221 			attGroup->addChild(inputGroup);
1222 
1223 			const vector<InputAttacher*>& inAtts = types.getInputAttachers();
1224 			for (vector<InputAttacher*>::const_iterator it = inAtts.begin();
1225 				 it != inAtts.end(); ++it)
1226 			{
1227 				const string name = attacherName((*it)->getAttacher());
1228 				inputGroup->addChild(new InputAttachmentTest(name.c_str(), name.c_str(), **it));
1229 			}
1230 		}
1231 		{
1232 			TestCaseGroup* outputGroup = new TestCaseGroup(
1233 				testCtx, "deleted_output", "Output to deleted attachment");
1234 			attGroup->addChild(outputGroup);
1235 
1236 			const vector<OutputAttacher*>& outAtts = types.getOutputAttachers();
1237 			for (vector<OutputAttacher*>::const_iterator it = outAtts.begin();
1238 				 it != outAtts.end(); ++it)
1239 			{
1240 				string name = attacherName((*it)->getAttacher());
1241 				outputGroup->addChild(new OutputAttachmentTest(name.c_str(), name.c_str(),
1242 															   **it));
1243 			}
1244 		}
1245 	}
1246 }
1247 
1248 } // details
1249 } // LifetimeTests
1250 } // gls
1251 } // deqp
1252