<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>
<channel>
	<title>Comments on: Templated test code?</title>
	<atom:link href="http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/</link>
	<description>Four in the morning, still writing Free Software</description>
	<pubDate>Thu, 21 Aug 2008 10:40:12 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
		<item>
		<title>By: Andy Balaam</title>
		<link>http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/#comment-1191</link>
		<dc:creator>Andy Balaam</dc:creator>
		<pubDate>Wed, 26 Mar 2008 15:07:15 +0000</pubDate>
		<guid isPermaLink="false">http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/#comment-1191</guid>
		<description>Stringificizationalism seems the most reliable way to get the class name here.</description>
		<content:encoded><![CDATA[<p>Stringificizationalism seems the most reliable way to get the class name here.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Andy Balaam</title>
		<link>http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/#comment-1190</link>
		<dc:creator>Andy Balaam</dc:creator>
		<pubDate>Wed, 26 Mar 2008 15:06:30 +0000</pubDate>
		<guid isPermaLink="false">http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/#comment-1190</guid>
		<description>Where TEST_ASSERT_CLS does the obvious thing.</description>
		<content:encoded><![CDATA[<p>Where TEST_ASSERT_CLS does the obvious thing.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Andy Balaam</title>
		<link>http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/#comment-1189</link>
		<dc:creator>Andy Balaam</dc:creator>
		<pubDate>Wed, 26 Mar 2008 15:04:57 +0000</pubDate>
		<guid isPermaLink="false">http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/#comment-1189</guid>
		<description>What about just plain old this?:

&lt;pre&gt;

#define TEST_ONE_THINGY(CLS,OUTPUT) test_One_Thingy&#60;CLS&#62;( #CLS, OUTPUT );

void test_all_thingies()
{
    TEST_ONE_THINGY( Thingy1, "Thingy1 expected output" );
    TEST_ONE_THINGY( Thingy2, "Thingy2 expected output" );
    TEST_ONE_THINGY( Thingy3, "Thingy3 expected output" );
    TEST_ONE_THINGY( Thingy4, "Thingy4 expected output" );
}

template&#60; class T &#62;
void test_One_Thingy( std::string class_name, std::string expected_output )
{
    T thingy;
    thingy.doSomething();
    TEST_ASSERT_CLS( class_name, thingy.getOutput() == expected_output );
}

&lt;/pre&gt;</description>
		<content:encoded><![CDATA[<p>What about just plain old this?:</p>
<pre>

#define TEST_ONE_THINGY(CLS,OUTPUT) test_One_Thingy&lt;CLS&gt;( #CLS, OUTPUT );

void test_all_thingies()
{
    TEST_ONE_THINGY( Thingy1, "Thingy1 expected output" );
    TEST_ONE_THINGY( Thingy2, "Thingy2 expected output" );
    TEST_ONE_THINGY( Thingy3, "Thingy3 expected output" );
    TEST_ONE_THINGY( Thingy4, "Thingy4 expected output" );
}

template&lt; class T &gt;
void test_One_Thingy( std::string class_name, std::string expected_output )
{
    T thingy;
    thingy.doSomething();
    TEST_ASSERT_CLS( class_name, thingy.getOutput() == expected_output );
}
</pre>
]]></content:encoded>
	</item>
	<item>
		<title>By: Andy Balaam</title>
		<link>http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/#comment-1187</link>
		<dc:creator>Andy Balaam</dc:creator>
		<pubDate>Wed, 26 Mar 2008 14:51:18 +0000</pubDate>
		<guid isPermaLink="false">http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/#comment-1187</guid>
		<description>Charles emailed the ungarbled version to me:

&lt;pre&gt;
#include "hshgtest.h"
#include &#60;iostream&#62;
#include &#60;sstream&#62;

struct ClsA
{
	const char* str() const { return "My name is A"; }
};

struct ClsB
{
	const char* str() const { return "My name is B"; }
};

struct ClsC
{
	const char* str() const { return "My Name is C"; }
};

struct ClsD
{
	const char* str() const { return "My name is D"; }
};

namespace
{

template&#60; class T &#62;
inline std::string SuffixType( const std::string&#038; in )
{
	std::ostringstream s;
	s &#60;&#60; in &#60;&#60; " : " &#60;&#60; typeid( T ).name();
	return s.str();
}

#define TEST_ASSERT_TEMPL( t, x ) HSHGTest::TestAssert( x, SuffixType&#60; t &#62;( #x ).c_str(), __FILE__, __LINE__)

template&#60; class T &#62;
void ClassTest( const std::string&#038; expect )
{
	T test;
	TEST_ASSERT_TEMPL( T, expect == test.str() );
}

void testall()
{
	ClassTest&#60; ClsA &#62;( "My name is A" );
	ClassTest&#60; ClsB &#62;( "My name is B" );
	ClassTest&#60; ClsC &#62;( "My name is C" );
	ClassTest&#60; ClsD &#62;( "My name is D" );
}

}

HSHG_BEGIN_TESTS
HSHG_TEST_ENTRY( testall )
HSHG_END_TESTS

HSHG_TEST_MAIN
&lt;/pre&gt;</description>
		<content:encoded><![CDATA[<p>Charles emailed the ungarbled version to me:</p>
<pre>
#include "hshgtest.h"
#include &lt;iostream&gt;
#include &lt;sstream&gt;

struct ClsA
{
	const char* str() const { return "My name is A"; }
};

struct ClsB
{
	const char* str() const { return "My name is B"; }
};

struct ClsC
{
	const char* str() const { return "My Name is C"; }
};

struct ClsD
{
	const char* str() const { return "My name is D"; }
};

namespace
{

template&lt; class T &gt;
inline std::string SuffixType( const std::string&#038; in )
{
	std::ostringstream s;
	s &lt;&lt; in &lt;&lt; " : " &lt;&lt; typeid( T ).name();
	return s.str();
}

#define TEST_ASSERT_TEMPL( t, x ) HSHGTest::TestAssert( x, SuffixType&lt; t &gt;( #x ).c_str(), __FILE__, __LINE__)

template&lt; class T &gt;
void ClassTest( const std::string&#038; expect )
{
	T test;
	TEST_ASSERT_TEMPL( T, expect == test.str() );
}

void testall()
{
	ClassTest&lt; ClsA &gt;( "My name is A" );
	ClassTest&lt; ClsB &gt;( "My name is B" );
	ClassTest&lt; ClsC &gt;( "My name is C" );
	ClassTest&lt; ClsD &gt;( "My name is D" );
}

}

HSHG_BEGIN_TESTS
HSHG_TEST_ENTRY( testall )
HSHG_END_TESTS

HSHG_TEST_MAIN
</pre>
]]></content:encoded>
	</item>
	<item>
		<title>By: Andy Balaam</title>
		<link>http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/#comment-1186</link>
		<dc:creator>Andy Balaam</dc:creator>
		<pubDate>Wed, 26 Mar 2008 11:36:34 +0000</pubDate>
		<guid isPermaLink="false">http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/#comment-1186</guid>
		<description>I think I get the idea, and it certainly helps us tell which test fails, but it still feels a little complicated for test code, which I should have added as a further reason against.</description>
		<content:encoded><![CDATA[<p>I think I get the idea, and it certainly helps us tell which test fails, but it still feels a little complicated for test code, which I should have added as a further reason against.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Bailey</title>
		<link>http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/#comment-1129</link>
		<dc:creator>Charles Bailey</dc:creator>
		<pubDate>Thu, 20 Mar 2008 00:52:13 +0000</pubDate>
		<guid isPermaLink="false">http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/#comment-1129</guid>
		<description>Grrrr, WordPress has completely garabled all of the angle brackets from my comments.</description>
		<content:encoded><![CDATA[<p>Grrrr, WordPress has completely garabled all of the angle brackets from my comments.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Bailey</title>
		<link>http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/#comment-1128</link>
		<dc:creator>Charles Bailey</dc:creator>
		<pubDate>Thu, 20 Mar 2008 00:49:46 +0000</pubDate>
		<guid isPermaLink="false">http://www.artificialworlds.net/blog/2008/03/19/templated-test-code/#comment-1128</guid>
		<description>I presume a clerical error has left out the template parameter specification on the test_One_Thingy function calls.

'Reasons against' can be rebutted fairly fast. The thing with templates is that they are 'real code', so you can't use any preprocessor code in the template function to distinguish different instantiations.

Assuming that TEST_ASSERT is a macro that makes use of __FILE__ and __LINE__ for its helpful diagnostic, the obvious way is to make a more configurable macro which does essentially the same thing, but allows you to specify the file and line. For example, make TEST_ASSERT_FL macro so that these two lines are equivalent.
TEST_ASSERT_FL( x, __FILE__, __LINE__ )
TEST_ASSERT( x )

Then, change the signature of test_One_Thingy as follows.
template void test_One_Thingy( const std::string&#38; ex, const char* file, unsigned int line )

And have it use the new macro.
TEST_ASSERT_FL( thingy.getOutput() == expected_output, file, line );

Then create a new macro.
#defined TEST_ONE_THINGY( cls, expect ) test_One_Thingy( expect, __FILE__, __LINE__ )

void test_all_thingies()
{
    TEST_ONE_THINGY( ClassA, "Thingy1 expected output" );
    TEST_ONE_THINGY( ClassB, "Thingy2 expected output" );
    TEST_ONE_THINGY( ClassC, "Thingy3 expected output" );
    TEST_ONE_THINGY( ClassD, "Thingy4 expected output" );
}

File and line now give you which class caused the failure, at the expense of not telling you the file and line of the template code where the actual test is. (This may not matter much if the template code only has a single assert macro.)

Now the funky way :) .

Assuming you have a TestAssert underlying function on which the TEST_ASSERT macro is built...

template
inline std::string SuffixType( const std::string&#38; in )
{
    std::ostringstream s;
    s ( #x ).c_str(), __FILE__, __LINE__)

template
void test_One_Thingy( const std::string&#38; expected_output )
{
    T thingy;
    thingy.doSomething();
    TEST_ASSERT_TEMPL( T, thingy.getOutput() == expected_output );
}

Warning! The type suffix is not guaranteed to be human readable.</description>
		<content:encoded><![CDATA[<p>I presume a clerical error has left out the template parameter specification on the test_One_Thingy function calls.</p>
<p>&#8216;Reasons against&#8217; can be rebutted fairly fast. The thing with templates is that they are &#8216;real code&#8217;, so you can&#8217;t use any preprocessor code in the template function to distinguish different instantiations.</p>
<p>Assuming that TEST_ASSERT is a macro that makes use of __FILE__ and __LINE__ for its helpful diagnostic, the obvious way is to make a more configurable macro which does essentially the same thing, but allows you to specify the file and line. For example, make TEST_ASSERT_FL macro so that these two lines are equivalent.<br />
TEST_ASSERT_FL( x, __FILE__, __LINE__ )<br />
TEST_ASSERT( x )</p>
<p>Then, change the signature of test_One_Thingy as follows.<br />
template void test_One_Thingy( const std::string&amp; ex, const char* file, unsigned int line )</p>
<p>And have it use the new macro.<br />
TEST_ASSERT_FL( thingy.getOutput() == expected_output, file, line );</p>
<p>Then create a new macro.<br />
#defined TEST_ONE_THINGY( cls, expect ) test_One_Thingy( expect, __FILE__, __LINE__ )</p>
<p>void test_all_thingies()<br />
{<br />
    TEST_ONE_THINGY( ClassA, &#8220;Thingy1 expected output&#8221; );<br />
    TEST_ONE_THINGY( ClassB, &#8220;Thingy2 expected output&#8221; );<br />
    TEST_ONE_THINGY( ClassC, &#8220;Thingy3 expected output&#8221; );<br />
    TEST_ONE_THINGY( ClassD, &#8220;Thingy4 expected output&#8221; );<br />
}</p>
<p>File and line now give you which class caused the failure, at the expense of not telling you the file and line of the template code where the actual test is. (This may not matter much if the template code only has a single assert macro.)</p>
<p>Now the funky way :) .</p>
<p>Assuming you have a TestAssert underlying function on which the TEST_ASSERT macro is built&#8230;</p>
<p>template<br />
inline std::string SuffixType( const std::string&amp; in )<br />
{<br />
    std::ostringstream s;<br />
    s ( #x ).c_str(), __FILE__, __LINE__)</p>
<p>template<br />
void test_One_Thingy( const std::string&amp; expected_output )<br />
{<br />
    T thingy;<br />
    thingy.doSomething();<br />
    TEST_ASSERT_TEMPL( T, thingy.getOutput() == expected_output );<br />
}</p>
<p>Warning! The type suffix is not guaranteed to be human readable.</p>
]]></content:encoded>
	</item>
</channel>
</rss>
