{"id":74,"date":"2015-05-04T22:47:34","date_gmt":"2015-05-04T22:47:34","guid":{"rendered":"http:\/\/metareal.net\/blog\/?p=74"},"modified":"2015-05-04T22:47:34","modified_gmt":"2015-05-04T22:47:34","slug":"testing-debugging-miscellanea","status":"publish","type":"post","link":"http:\/\/metareal.net\/blog\/?p=74","title":{"rendered":"Testing &#038; Debugging Miscellanea"},"content":{"rendered":"<p>Here&#8217;s a quick peek into some of the test and debug strategae I&#8217;m using&#8230;<\/p>\n<h3>Base Object<\/h3>\n<p>One of the great hazards of C++ is object leaks. To mitigate this, I have a base object that everyone descends from, called MeObjectBase. My descendant object constructors all look like<\/p>\n<pre>\nMeRenderWorld::MeRenderWorld(int width, int height) : MeObjectBase(\"MeRenderWorld\")\n{ ... }\n<\/pre>\n<p>Then we can print a tally of current objects by type name. But all that text-dictionary lookup can be expensive! So there&#8217;s a global boolean to enable or disable count-by-name. Even when disabled, the total number of objects is tracked.<\/p>\n<h3>Malloc<\/h3>\n<p>Like object leaks, memory leaks are alway looming. The main code never calls malloc or free directly. Instead, some wrappers are used which keep a tally of how much memory has been allocated and freed, and how many pointers have been allocated and freed. A few bytes at the beginning of the block hold the size. These static methods on MeObjectBase.<\/p>\n<pre>\nclass MeObjectBase\n{\npublic:\n      ...    \n    static void *malloc(size_t bytes);\n    static void reallocAt(void **ptr, size_t bytes);\n    static void freeAt(void **ptr);\n};\n<\/pre>\n<h3>Unit Testing<\/h3>\n<p>I&#8217;ve been coding since I was 12. That&#8217;s forty years now, but who&#8217;s counting. The only significant thing I&#8217;ve learned in the last twenty or so is the joy and beauty of organized unit tests. I used to do unit tests, without realizing it: I&#8217;d write some code at the top of main() to call the new function, print out the result, and quit. Then I&#8217;d delete the test and continue development. <\/p>\n<p>I&#8217;ve also often written little test apps alongside my &#8220;real app&#8221; to exercise libraries.<\/p>\n<p>Anyway, yeah, unit testing. For Metareal, I&#8217;m just running a small command line app that tallies up trues and falses and prints the result at the end.<\/p>\n<p>Why not use an existing C++ unit testing framework? No great reasons, but among them: Tends to run slower as files are scanned or preprocessed for tests, adds code I don&#8217;t know that well. On the other hand, testing frameworks typically let you run single tests if needed. And I have to explicitly add every test function to main(). Oh well, is a tradeoff.<\/p>\n<p>Here&#8217;s what some tests look like:<\/p>\n<pre>\n\/\/ A typical \"main method\" for a test file\nvoid allMatrixTests()\n{\n    castingFloatToVec4();\n    vec4ToVec3Tests();\n    matrixRowColumnExtracts();\n    matrixTests();\n    matrixTestWTranslate();\n    matrixTestLTranslate();\n    vectorCallTest();\n    basicMatrixMathFun();\n}\n\n\/\/ A typical assertion\n    float m03 = m4(0,3);\n    ASSERT_EQUALS_FLOAT(\"col\/row\", 3, m03);\n\n\/\/ Implementation of one of the assertions\n#define ASSERT_EQUALS_INT(_message,_want,_got) assertEqualsInt(1,__LINE__,_message,#_want,#_got,(long long)_want,(long long)_got)\nvoid assertEqualsInt(int sayIt,int line,cc message,cc wantS,cc gotS,long long want,long long got)\n{\n    g.assertions++;\n    if(got == want)\n    {\n        g.passes++;\n    }\n    else\n    {\n        g.fails++;\n        assertLogFail(\"%6d. FAIL %4d %s %s(%lld) != %s(%d)\\n\",g.fails,line,message,wantS,want,gotS,got);\n    }\n}\n<\/pre>\n<p>The last assertion I make is that all the memory and objects have been freed. Here&#8217;s a happy output.<\/p>\n<pre>\n\n    currentCount:   0\nconstructorCount:3801\n       copyCount:   0\n       everCount:3801\n     mallocCount:6249\n       freeCount:6249\n    unfreedCount:   0\n     mallocBytes:2363449281\n      freedBytes:2363449281\n    unfreedBytes:   0\n         BitmapThing:   0    2    3\n                Ham1:   0    1    1\n             IfThing:   0    2    6\n            IxMover2:   0    2    8\n   MeCollisionVolume:   0   10  134\n              MeGaud:   0    3   34\n          MeGeometry:   0 1001 1541\n               MeHam:   0    1    1\n          MeITexture:   0    4   81\n               MeLru:   0    2    3\n              MePart:   0 1000 1190\n       MeRenderParts:   0    3   59\n       MeRenderWorld:   0    1   19\n      MeTextureAtlas:   0    2   23\n     MeTextureFloat4:   0    3   22\n         MeThingKind:   0    1    8\n      MeThingManager:   0    1    4\n            MeVolume:   0  128  493\n       MeVolumeWorld:   0    1   25\n     MockFrameBuffer:   0    2   10\n        MockMaterial:   0    3   67\n        MockRenderer:   0    3   28\n                   a:   0    3    3\n                   b:   0    1    1\n             unknown:   0    3   37\n     0.  ok       undisposed objects 0(0) == MeObjectBase::currentCount(0)\n     0.  ok       undisposed mallocs 0(0) == MeObjectBase::mallocCount - MeObjectBase::freeCount(0)\ntest results: 7551 pass \/ 7551 assertions (0.686 seconds)\n---------------------\n aok\n---------------------\nProgram ended with exit code: 0\n<\/pre>\n<p>Thing to notice: the total runtime for these tests is about a second. It covers the math, part and triangle-list management, many, many collision and volume intersection cases. It does not cover actual, live OpenGL code.<\/p>\n<h3>Debug Logging<\/h3>\n<p>Little to say here, I have a couple of log methods, which take arguments like printf. Listeners can be added.<\/p>\n<h3>Debug Global Booleans<\/h3>\n<p>Aaah, yes, debugging realtime code can be tricky. To help, when running the realtime app I map control-0 through control-9 directly to ten globally accessible booleans. Sometimes I&#8217;ll add some code to a deep, inner function which does something special based on those booleans. Then I can trigger it at will while running. Works nicely with breakpoints.<\/p>\n<h3>Well, Ok<\/h3>\n<p>That&#8217;s just some of the goodies in play.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here&#8217;s a quick peek into some of the test and debug strategae I&#8217;m using&#8230; Base Object One of the great hazards of C++ is object leaks. To mitigate this, I have a base object that everyone descends from, called MeObjectBase. My descendant object constructors all look like MeRenderWorld::MeRenderWorld(int width, int height) : MeObjectBase(&#8220;MeRenderWorld&#8221;) { &#8230; [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-74","post","type-post","status-publish","format-standard","hentry","category-3"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/74","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=74"}],"version-history":[{"count":1,"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/74\/revisions"}],"predecessor-version":[{"id":75,"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/74\/revisions\/75"}],"wp:attachment":[{"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=74"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=74"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=74"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}