Home | History | Annotate | Download | only in glshared
      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 
     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:
    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:
    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 
    129 ContextWrapper::ContextWrapper (const Context& ctx)
    130 	: CallLogWrapper	(ctx.gl(), ctx.log())
    131 	, m_ctx				(ctx)
    132 {
    133 	enableLogging(true);
    134 }
    135 
    136 void SimpleBinder::bind (GLuint name)
    137 {
    138 	(this->*m_bindFunc)(m_bindTarget, name);
    139 }
    140 
    141 GLuint SimpleBinder::getBinding (void)
    142 {
    143 	return getInteger(*this, m_bindingParam);
    144 }
    145 
    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:
    156 						VertexArrayBinder	(Context& ctx)
    157 							: SimpleBinder	(ctx, 0, GL_NONE, GL_VERTEX_ARRAY_BINDING, true) {}
    158 	void				bind				(GLuint name) { glBindVertexArray(name); }
    159 };
    160 
    161 class QueryBinder : public Binder
    162 {
    163 public:
    164 						QueryBinder		(Context& ctx) : Binder(ctx) {}
    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 	}
    172 	GLuint				getBinding		(void) { return 0; }
    173 };
    174 
    175 bool ProgramType::isDeleteFlagged (GLuint name)
    176 {
    177 	GLint deleteFlagged = 0;
    178 	glGetProgramiv(name, GL_DELETE_STATUS, &deleteFlagged);
    179 	return deleteFlagged != 0;
    180 }
    181 
    182 bool ShaderType::isDeleteFlagged (GLuint name)
    183 {
    184 	GLint deleteFlagged = 0;
    185 	glGetShaderiv(name, GL_DELETE_STATUS, &deleteFlagged);
    186 	return deleteFlagged != 0;
    187 }
    188 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    356 GLuint TextureFboAttacher::getAttachment (GLuint fbo)
    357 {
    358 	return getFboAttachment(gl(), fbo, GL_TEXTURE);
    359 }
    360 
    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 
    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 
    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 
    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 
    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 
    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 
    443 void ShaderProgramAttacher::attach (GLuint shader, GLuint program)
    444 {
    445 	GLU_CHECK_CALL_ERROR(
    446 		glAttachShader(program, shader),
    447 		gl().getError());
    448 }
    449 
    450 void ShaderProgramAttacher::detach (GLuint shader, GLuint program)
    451 {
    452 	GLU_CHECK_CALL_ERROR(
    453 		glDetachShader(program, shader),
    454 		gl().getError());
    455 }
    456 
    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 
    484 void setViewport (const RenderContext& renderCtx, const Rectangle& rect)
    485 {
    486 	renderCtx.getFunctions().viewport(rect.x, rect.y, rect.width, rect.height);
    487 }
    488 
    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 
    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 
    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 
    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:
    611 				Name		(Type& type) : m_type(type), m_name(type.gen()) {}
    612 				Name		(Type& type, GLuint name) : m_type(type), m_name(name) {}
    613 				~Name		(void) { m_type.release(m_name); }
    614 	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 
    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 
    647 bool ResultCollector::check (bool cond, const char* msg)
    648 {
    649 	if (!cond)
    650 		fail(msg);
    651 	return cond;
    652 }
    653 
    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 
    670 void ResultCollector::fail (const char* msg)
    671 {
    672 	addResult(QP_TEST_RESULT_FAIL, msg);
    673 }
    674 
    675 void ResultCollector::warn (const char* msg)
    676 {
    677 	addResult(QP_TEST_RESULT_QUALITY_WARNING, msg);
    678 }
    679 
    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.
    693 	const Context&			getContext			(void) const { return m_ctx; }
    694 	const RenderContext&	getRenderContext	(void) const { return m_ctx.getRenderContext(); }
    695 	const Functions&		gl					(void) const { return m_ctx.gl(); }
    696 	TestLog&				log					(void) const { return m_ctx.log(); }
    697 	void					init				(void);
    698 
    699 	Context					m_ctx;
    700 	Random					m_rnd;
    701 };
    702 
    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 
    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 
    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:
    740 	Binder&					binder						(void) { return *m_type.binder(); }
    741 
    742 	Type&					m_type;
    743 	TestFunction			m_test;
    744 };
    745 
    746 IterateResult LifeTest::iterate (void)
    747 {
    748 	(this->*m_test)();
    749 	return STOP;
    750 }
    751 
    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 
    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 
    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 
    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 
    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 
    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);
    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 
    898 IterateResult AttachmentTest::iterate (void)
    899 {
    900 	(this->*m_test)();
    901 	return STOP;
    902 }
    903 
    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 
    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:
    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 
    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 
    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:
   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 
   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 
   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 
   1175 string attacherName (Attacher& attacher)
   1176 {
   1177 	ostringstream os;
   1178 	os << attacher.getElementType().getName() << "_" <<  attacher.getContainerType().getName();
   1179 	return os.str();
   1180 }
   1181 
   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