{"id":776,"date":"2015-08-28T02:37:46","date_gmt":"2015-08-28T06:37:46","guid":{"rendered":"http:\/\/www.rfdm.com\/blog\/?p=776"},"modified":"2015-08-28T02:37:46","modified_gmt":"2015-08-28T06:37:46","slug":"llvmclang-libclangtooling-making-progress-on-unit-test-generator","status":"publish","type":"post","link":"https:\/\/www.rfdm.com\/blog\/?p=776","title":{"rendered":"llvm\/clang libclangTooling: making progress on unit test generator"},"content":{"rendered":"<p>I&#8217;ve spent a decent amount of time for my day job creating a tool to generate unit\/component test skeletons from existing source code.  I&#8217;ve made reasonable progress in the last couple of weeks.<\/p>\n<p>Background&#8230; this isn&#8217;t the first time I&#8217;ve run into code without unit tests.  Any of us that&#8217;ve had to deal with legacy code have found small to large chunks of code with no unit tests.  I think of this as flying blind from a development perspective.  It&#8217;s generally impossible for integration testing and software validation to find all problems.  This is of course true of unit tests as well, but unit tests can find issues that other testing cannot, and ideally before code is committed to a repository.<\/p>\n<p>At any rate, the point of my tool is to speed up the creation of unit tests for legacy code that doesn&#8217;t have any unit tests written.<\/p>\n<p>The first pass was relatively easy after finding my way around the libclangTooling APIs.  Generating skeletons from a blank slate (no existing tests) is easy.<\/p>\n<p>I did get hung up for a bit with an existing pattern from a tool we already had.  That pattern combined the code to be tested with the unit tests in a single translation unit.  It turns out that this complicates things significantly if you really want to produce test code that compiles without intervention in most cases.  I&#8217;ve since decided that it&#8217;s smarter to produce a translation unit that #includes the code to be tested.  This accomplishes the main purpose of the previous pattern (bring module-static variables into scope) without touching the code under test.  This separates the concerns of unit tests versus original source code, so there are no questions about whether or not the original source code matches what is being tested: by #including it, it is unquestionably unmodified.  Yes, it&#8217;s unconventional to #include a .c or<\/p>\n<p>The hard part came when I decided that I needed to be able to automatically update the unit test code.  This is basically a sanity check sort of thing&#8230; if someone adds a new function to a translation unit and doesn&#8217;t write a test for the new function, at a minimum I want my tool to warn me.  Better, it should generate a new unit test that fails until someone populates it, and should call it from main() in the test driver so that no one will miss the fact that there&#8217;s a failing unit test.<\/p>\n<p>Conversely, the tool should warn about tests for functions that no longer exist and hence have unit test functions that will not compile and link.  Or better, delete the unit test for the function that no longer exists.<\/p>\n<p>I&#8217;m now doing both of these things.  I add new unit test skeletons that don&#8217;t exist, while leaving the existing ones alone.  I remove unit tests for functions that have disappeared.<\/p>\n<p>I wound up doing this with 2 separate passes of ClangTool::run(), each using a different class derived from ASTFrontendAction and processing different files.  The first pass processes the code under test, the second pass processes the unit test code.<\/p>\n<p>So far so good with respect to functions under test and unit test functions.  I now need to to the same for the extern global variables and stub functions that I generate as part of the first pass.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve spent a decent amount of time for my day job creating a tool to generate unit\/component test skeletons from existing source code. I&#8217;ve made reasonable progress in the last couple of weeks. Background&#8230; this isn&#8217;t the first time I&#8217;ve run into code without unit tests. Any of us that&#8217;ve had to deal with legacy &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.rfdm.com\/blog\/?p=776\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;llvm\/clang libclangTooling: making progress on unit test generator&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[],"class_list":["post-776","post","type-post","status-publish","format-standard","hentry","category-software-development"],"_links":{"self":[{"href":"https:\/\/www.rfdm.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/776","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rfdm.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.rfdm.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.rfdm.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rfdm.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=776"}],"version-history":[{"count":1,"href":"https:\/\/www.rfdm.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/776\/revisions"}],"predecessor-version":[{"id":777,"href":"https:\/\/www.rfdm.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/776\/revisions\/777"}],"wp:attachment":[{"href":"https:\/\/www.rfdm.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=776"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rfdm.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=776"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rfdm.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=776"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}