{"id":34,"date":"2007-07-11T13:26:01","date_gmt":"2007-07-11T19:26:01","guid":{"rendered":"http:\/\/www.pervasivecode.com\/blog\/2007\/07\/11\/rcov-c0-line-coverage-more-generous-than-emmas-c1-bytecode-coverage\/"},"modified":"2016-01-03T20:39:12","modified_gmt":"2016-01-04T04:39:12","slug":"rcov-c0-line-coverage-more-generous-than-emmas-c1-bytecode-coverage","status":"publish","type":"post","link":"http:\/\/www.pervasivecode.com\/blog\/2007\/07\/11\/rcov-c0-line-coverage-more-generous-than-emmas-c1-bytecode-coverage\/","title":{"rendered":"RCOV C0 line coverage more generous than EMMA&#8217;s C1 line coverage"},"content":{"rendered":"<p>Coverage tests in Ruby (with <a href=\"http:\/\/eigenclass.org\/hiki.rb?rcov\">rcov<\/a>) are less strict than in Java (with <a href=\"http:\/\/emma.sourceforge.net\/\">EMMA<\/a>), so watch out &#8211; 100% coverage is easy to attain but not as meaningful.<br \/>\n<!--more--><br \/>\nFor Java code coverage, I like EMMA. <a href=\"http:\/\/www.cenqua.com\/clover\/\">Clover<\/a> looks really nice and all, but seriously, $250 a seat? Yeesh. Talk about poor product pricing &#8211; I will <i>never<\/i> buy that product, nor seriously consider buying it, because of the outrageous price. I mean, a single license of <a href=\"http:\/\/www.jetbrains.com\/idea\/\">IntelliJ<\/a> is $1 cheaper. That&#8217;s like charging $300 for a better Ant. C&#8217;mon.<\/p>\n<p>Anyway, EMMA is very conservative about coverage estimates. Consider this single line of Java code:<\/p>\n<pre>int x = 1; if (x &gt; 1){throw new RuntimeException();}<\/pre>\n<p>When you run it, the assignment will execute, as will the comparison, but the block containing <code>throw<\/code> will never be reached. So, the line is not fully covered. EMMA looks at bytecodes and maps those back to line numbers, and will mark this line as partially covered.<\/p>\n<p>It&#8217;s maddening sometimes, but it&#8217;s correct; it&#8217;s your problem to figure out how to force all those darned IOExceptions that you know can&#8217;t ever happen, or to give up and let some apocalyptic error handling code not be covered. I used to shoot for 85-90% coverage, and just live with some uncovered wacky code when I couldn&#8217;t find a reasonable way to trigger hideous errors.<\/p>\n<p>(There&#8217;s a little voice in my head that says, &#8220;Come on, you slacker, you should make it possible to dynamically break the database configuration and remount the root filesystem as read-only during the test suite so that the error handlers can all be 100% covered!&#8221; But that little voice never ships anything, he just sits around and writes more and more paranoid code &#8212; no <a href=\"http:\/\/archive.eiffel.com\/doc\/manuals\/technology\/contract\/ariane\/page.html\">Ariane 5 disaster<\/a> on my watch! &#8212; and gets asymptotically closer to 100% coverage.)<\/p>\n<p>Okay, now the Ruby version:<\/p>\n<pre>x = 1; raise RuntimeError if x &gt; 1<\/pre>\n<p>Same story, prettier code (cuz it&#8217;s Ruby; duh, of course it&#8217;s prettier): assignment gets executed, condition is executed, no exception is raised. Rcov says it&#8217;s 100% covered.<\/p>\n<p>The difference is <a href=\"http:\/\/eigenclass.org\/hiki.rb?rcov#l15\">C0 coverage<\/a> (Rcov) vs. <a href=\"http:\/\/emma.sourceforge.net\/faq.html#q.fractional\">C1 coverage<\/a> (EMMA). EMMA only counts a line as 100% covered if all of the bytecodes compiled from it were executed. Rcov counts a line as 100% covered if execution visited that line at all.<\/p>\n<p>(EMMA uses &#8220;basic blocks&#8221; instead of bytecodes, so in obscure failure cases the reported line coverage figure is even lower, but that doesn&#8217;t affect the conditions for what 100% line coverage requires.)<\/p>\n<p>I&#8217;m not sure if it&#8217;s feasible to get C1 coverage on the regular Ruby VM, but it would be nice to have. In the meantime, just be aware that 100% C0 coverage is easy to attain with rcov, but doesn&#8217;t mean you&#8217;re done testing everything.<\/p>\n<p>Of course, 100% C1 coverage doesn&#8217;t mean you&#8217;re done testing everything either, but it&#8217;s closer to meaning that than 100% C0 coverage is.<\/p>\n<p>In fact, it&#8217;s good to over-test code that&#8217;s already covered once. On any given project in the real world, there is code that will succeed with some values and fail with others. (<code>print x\/y<\/code>, for example.)  Just testing with one set of values isn&#8217;t enough. I like to create an array of inputs and expected outputs and use Array.each to iterate over them and pass them into a block that calls the code under test and then compares the actual outputs to the expected outputs with assert_equal. It means that I&#8217;m following DRY (rather than cutting and pasting several lines of test code and then editing a teeny bit of each pasted chunk&#8230; eww) but still testing many different ways.<\/p>\n<p>But, even with a C0 coverage tool, at least you know what <em>hasn&#8217;t<\/em> been touched at all, and you can use your big smart programmer brain to think of a few ways to torture that code. Just remember that you probably should be aiming for an unmeasurable way-over-100% imaginary coverage target, beating the heck out of your fanciest code with many, many different inputs and then moving on to the next chunk of uncovered code only when the last chunk is thoroughly proven to not suck.<\/p>\n<p>Good testing!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Coverage tests in Ruby (with rcov) are less strict than in Java (with EMMA), so watch out &#8211; 100% coverage is easy to attain but not as meaningful.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[29,27,26,30,9],"tags":[],"class_list":["post-34","post","type-post","status-publish","format-standard","hentry","category-coverage","category-java","category-ruby","category-testing","category-tools"],"_links":{"self":[{"href":"http:\/\/www.pervasivecode.com\/blog\/wp-json\/wp\/v2\/posts\/34"}],"collection":[{"href":"http:\/\/www.pervasivecode.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.pervasivecode.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.pervasivecode.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.pervasivecode.com\/blog\/wp-json\/wp\/v2\/comments?post=34"}],"version-history":[{"count":1,"href":"http:\/\/www.pervasivecode.com\/blog\/wp-json\/wp\/v2\/posts\/34\/revisions"}],"predecessor-version":[{"id":347,"href":"http:\/\/www.pervasivecode.com\/blog\/wp-json\/wp\/v2\/posts\/34\/revisions\/347"}],"wp:attachment":[{"href":"http:\/\/www.pervasivecode.com\/blog\/wp-json\/wp\/v2\/media?parent=34"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.pervasivecode.com\/blog\/wp-json\/wp\/v2\/categories?post=34"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.pervasivecode.com\/blog\/wp-json\/wp\/v2\/tags?post=34"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}