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