<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Pervasive Code &#187; coverage</title>
	<atom:link href="http://www.pervasivecode.com/blog/category/coverage/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.pervasivecode.com/blog</link>
	<description>Jamie Flournoy's Software Development Blog</description>
	<lastBuildDate>Mon, 26 Jul 2010 05:29:53 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Techniques for Exhaustively Testing a Rails App</title>
		<link>http://www.pervasivecode.com/blog/2008/08/23/techniques-for-exhaustively-testing-a-rails-app/</link>
		<comments>http://www.pervasivecode.com/blog/2008/08/23/techniques-for-exhaustively-testing-a-rails-app/#comments</comments>
		<pubDate>Sat, 23 Aug 2008 22:55:12 +0000</pubDate>
		<dc:creator>Jamie Flournoy</dc:creator>
				<category><![CDATA[coverage]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[tools]]></category>

		<guid isPermaLink="false">http://www.pervasivecode.com/blog/2008/08/23/techniques-for-exhaustively-testing-a-rails-app/</guid>
		<description><![CDATA[I subscribe to dozens of tech blogs (including but not limited to Ruby and Rails), and although I&#8217;ve seen quite a lot of commentary about TDD and BDD, I&#8217;m not sold on either of them yet. TDD is interesting, but BDD seems like a waste of time. But I am completely sold on automated testing [...]]]></description>
			<content:encoded><![CDATA[<p>I subscribe to dozens of tech blogs (including but not limited to Ruby and Rails), and although I&#8217;ve seen quite a lot of commentary about TDD and BDD, I&#8217;m not sold on either of them yet. TDD is interesting, but BDD seems like a waste of time. But I <em>am</em> completely sold on automated testing in general. I write lots and lots of tests and make sure using rcov that my tests cover all of the code. </p>
<p>Getting 100% coverage isn&#8217;t easy. In general it means you definitely can&#8217;t just write a single test case per method and declare victory when it passes. When the number of possible combinations of inputs (method arguments and/or mock objects) and expected outputs (return values, exceptions, and side effects) becomes large, then the potential for copy-and-paste errors in your test code becomes large, and legibility becomes an issue. This is the point at which I find the recent fascination with writing tests in a near-natural-language DSL to be a distraction. It&#8217;s orthogonal to the problem I&#8217;m dealing with, which is how to comprehensively test the code. In other words, the problem is not making a small number of tests more readable, but concisely expressing a large number of input/output combinations and a large number of different tests, and making it all readable.</p>
<p>I assume that there are others working on this problem too, so I&#8217;ll describe some of the things I&#8217;ve come up with, and hopefully if you have any good ideas you&#8217;ll post them in the comments section.</p>
<p><span id="more-78"></span></p>
<p><strong>Test Data Iterators</strong></p>
<p>If you express the set of inputs and outputs as a data structure, such as a Hash of input => expected ouput pairs, or an Array of [input, expected output] Arrays, you can easily do <code>test_data.each do |input,output| ... end</code> and drive tests that way.</p>
<p>(I considered pulling the data itself out of the test code and loading it from a YAML or CSV file or even including a data-only Ruby script, but that seemed like pointless added complexity. If I were working with a professional tester who didn&#8217;t want to touch Ruby code, I might change that to make it easier to dump from an Excel spreadsheet into a text file that the tests would use.)</p>
<p>One problem with this approach is that you may have a <code>test_frobnicate</code> method that invokes <code>frobnicate</code> dozens of times. This means that this one test can take a while to run if the tested method takes any amount of time, which is problematic if you just want to re-test that one input/output combo until it works. Your edit/test/debug loop should be ultra fast, and re-testing successful pairs gets in the way of testing the pair you&#8217;re not sure about.</p>
<p>More importantly, trying all those combinations in one test method means that if an assertion fails, the failure masks the outcome of the remaining combinations. If there are 25 combinations and combination #2 fails, then a <code>Test::Unit::AssertionFailedError</code> is thrown, and the other 23 of them go unchecked. That&#8217;s not ideal, since it can be useful in debugging if you can see that 24/25 failed vs. 1/25 failed.</p>
<p>To solve this, I changed the code so that it iterates over the test data inside the class definition, generating a new test method for each input/output pair:</p>
<pre>
class_eval do
  test_data_hash.each do |input, output|
    define_method("test_frobnicator_#{input.hash}") do
      check_frobnicator(input, output)
    end
  end
end
</pre>
<p>That gives you a bunch of test methods that each run the test once with different input and expected output values. Autotest integrates well with this technique, since it will not re-run the test methods which previously passed until after you have fixed all of the ones which failed.</p>
<p><strong>Test Setup Blocks</strong></p>
<p>I also use a <code>with_x_y_z do...end</code> idiom for mock setup when possible. So a test might contain:</p>
<pre>
def check_index(inputs, expected_outputs)
  if inputs[:logged_in]
    with_fake_login{|user| get :index, inputs[:params] }
  else
    get :index, inputs[:params]
  end

  # ... test outputs against expected output
end
</pre>
<p>Since this idiom uses blocks, you can nest them to create complicated setup scenarios, pass arguments to the <code>with_blah</code> method to control the behavior of the mock objects, and so on. (You could do this with regular <code>set_up_blah</code> methods but I think this is easier to read.)</p>
<p><strong>Calculated Outputs</strong></p>
<p>Despite their shortcomings, I still use Fixtures (with the PreloadFixtures plugin) and I write unit tests for my models. That means that I can then assume that the models&#8217; behavior is correct in other tests (provided that those unit tests pass). So I try not to put any data that&#8217;s already expressed in fixture files into my test method inputs or expected outputs. Instead, I ask for the fixture by its label (<code>widgets(:doodad)</code>) and then use its properties as needed in assertions within the current test. This reduces the size of the input/output data, which makes it easier to read and reduces the amount of effort required to add more combinations later.</p>
<p><strong>Testing Views, Model Associations, and Model Validation</strong></p>
<p>As Bruce Eckel says, <a href="http://www.mindview.net/WebLog/log-0025">If it&#8217;s not tested, it&#8217;s broken.</a> Or as Peter Drucker said, &#8220;What gets measured gets managed.&#8221;</p>
<p>I test all of these. I&#8217;ve read the arguments of those who say that none of these are necessary, and I disagree. The arugment that testing associations and validations is tantamount to testing Rails itself is just incorrect. Rails&#8217; own unit tests cannot possibly test the validations and associations expressed in every Rails application&#8217;s model classes, so Rails application developers still have to do this.</p>
<p><strong>Associations</strong></p>
<p>I can&#8217;t tell you how many times I&#8217;ve gotten has_one and belongs_to backwards and only found out when ActiveRecord told me that the column didn&#8217;t exist, because I told it to look at the table on the wrong side of the association. I feel stupid when I see that I made that mistake, but writing that test takes seconds and once I see the failure it&#8217;s easy to fix. Better to catch it and fix it now rather than have a user find it, right?</p>
<p><strong>Validations</strong></p>
<p>The same goes for model validations. Yes, I know that <code>validates_format_of</code> has been tested, but my regexes for username format and email addresses need testing too.</p>
<p><strong>Views</strong></p>
<p>Views should be tested <em>only if you care what your users see</em>.</p>
<p>Somewhere there is probably an organization with no users, where inputs matter but outputs can be anything. Maybe the goal of the project is to burn cash rather than to ship something good. They don&#8217;t need to test their views.</p>
<p>I agree that views should be dumbed down as much as possible using helpers, but that&#8217;s not enough to assure that they work. If you&#8217;re not testing views with automated tests, then how do you test them? With a browser? Every time you change anything, you&#8217;re going to exhaustively check every combination of inputs and outputs to make sure that all the dynamic goodies are in the right place? I doubt it. Obviously you do have to check views in a browser to make sure that purely visual aspects are correct, but there are things you can check automatically. You can do quite a lot with assert_select, and for client-side tests, <a href="http://selenium.openqa.org/">Selenium</a> can do pretty much anything. (It&#8217;s probably not worth the effort to have Selenium use the DOM to see that the browser applied CSS in exactly the way that you wanted it to, but if you wanted to, you could.)</p>
<p>I don&#8217;t know how to do something similar to coverage testing for views, but a good start is to just check for the effects of everything that the controller stored in instance variables. Maybe those should be displayed as-is in the resulting document, or maybe they should be truncated, or processed by a helper into a different representation.</p>
<p>In any case, you don&#8217;t have to test with the same set of inputs as the functional test for the controller; just do white box testing for the minimal set of combinations that should exercise all of the permutations of the view code (which should be a very small number). Testing the helpers separately can cut down on the number of permutations of inputs and outputs also. So if you have a helper that creates a navigation breadcrumb visual element from an array of strings, you could just test that in isolation with 0, 1, 2, and 3 elements, and then check for one version of breadcrumbs when looking for it in a view.</p>
<p>I&#8217;m particularly inspired by the claim that Sebastian Delmont of <a href="http://www.streeteasy.com/">StreetEasy</a> made in the <a href="http://podcast.rubyonrails.org/programs/1/episodes/sebastian_delmont">May 31, 2006 episode of the Ruby on Rails Podcast</a> about having no human approval step between a successful test suite run and deployment to production. That&#8217;s pretty darn bold, but if you stop and think about it, it is feasible with today&#8217;s tools to automate any &#8220;white glove test&#8221; that a real person would do before approving the release for deployment to production. I&#8217;m not that brave yet, but I like the idea of a condition that checks if the changed code has anything to do with a view (ERB, JavaScript, CSS, etc.), and if not, would just rubberstamp it and deploy it. Clearly a change that could require a manual test in multiple browsers wouldn&#8217;t be a wise thing to autodeploy, but something like fixing a bug deep in the code shouldn&#8217;t require a human to approve it, especially if the autodeploy condition depends on that chunk of code having 100% test coverage following the bug fix.</p>
<p><strong>Your Tips?</strong></p>
<p>I&#8217;m always looking for ways to test code with less effort on my part. Please let me know if you have ideas or suggestions.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pervasivecode.com/blog/2008/08/23/techniques-for-exhaustively-testing-a-rails-app/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Making Rcov measure your whole Rails app, even if tests miss entire source files</title>
		<link>http://www.pervasivecode.com/blog/2008/05/16/making-rcov-measure-your-whole-rails-app-even-if-tests-miss-entire-source-files/</link>
		<comments>http://www.pervasivecode.com/blog/2008/05/16/making-rcov-measure-your-whole-rails-app-even-if-tests-miss-entire-source-files/#comments</comments>
		<pubDate>Fri, 16 May 2008 22:42:42 +0000</pubDate>
		<dc:creator>Jamie Flournoy</dc:creator>
				<category><![CDATA[coverage]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[tools]]></category>

		<guid isPermaLink="false">http://www.pervasivecode.com/blog/2008/05/16/making-rcov-measure-your-whole-rails-app-even-if-tests-miss-entire-source-files/</guid>
		<description><![CDATA[I&#8217;ve seen a few Rake tasks for Rcov that work OK, but which fail in an interesting way (if you care about coverage): they give your coverage metrics an unexpected boost if you have 0% coverage in one or more source files.
Huh? Exactly. If you have 500 source files, and your test suite only requires [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve seen a few Rake tasks for Rcov that work OK, but which fail in an interesting way (if you care about coverage): they give your coverage metrics an unexpected boost if you have 0% coverage in one or more source files.</p>
<p>Huh? Exactly. If you have 500 source files, and your test suite only <code>require</code>s one of them, then you get a free ride on those 499 files that have 0% coverage. Theoretically you could get 100% coverage in your report even though 499 source files are not touched at all. D&#8217;oh!<br />
<span id="more-72"></span><br />
The reason for this is that rcov isn&#8217;t responsible for finding all of your source files. It just measures what portion of the files which you loaded were executed. The Rake tasks that people have written just kick off the test code, which also have no need to load all of your application&#8217;s source files. They just load whatever they need, and so that&#8217;s what rcov is aware of. But that&#8217;s not answering the question you thought you were asking, which is &#8220;how much of my application is being tested?&#8221;</p>
<p>You have to explicitly say that you want to see coverage numbers for all the files your tests need, plus all of the files that your tests did not touch. Put this in your test/test_helper.rb:</p>
<pre>
# require the entire app if we're running under coverage testing,
# so we measure 0% covered files in the report
coverage_testing_active = defined?(Rcov)
if coverage_testing_active
    all_app_files = Dir.glob('{app,lib}/**/*.rb').grep(
        /^(?!lib\\/(scheduled_tasks|template_optimizer)\\/)/)
    all_app_files.unshift('app/controllers/application.rb')
    all_app_files.each{|rb| require rb}
end</pre>
<p>You&#8217;ll probably want to customize that regexp to weed out any .rb files that you don&#8217;t want to load during the test suite (or which aren&#8217;t your own code).</p>
<p>Because of all this <code>require</code>-ing, you might want to apply the snippet in my <a href="http://www.pervasivecode.com/blog/2008/05/16/rails-snippet-require-app-files-only-once/">prior post</a>, which eliminates duplicate required source files if they&#8217;re under your RAILS_ROOT. Otherwise you may see evidence of repeated loading of the same source files.</p>
<p>With this snippet in place, you&#8217;ll still never see 0% coverage in a file. If there are zero lines of executable code in the file, it will not be listed. But you&#8217;ll see really low figures (4% etc.) for files that are loaded but not executed. Hopefully that will help you see where you&#8217;ve completely overlooked some code in your tests that users might still be able to get to.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pervasivecode.com/blog/2008/05/16/making-rcov-measure-your-whole-rails-app-even-if-tests-miss-entire-source-files/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>RCOV C0 line coverage more generous than EMMA&#8217;s C1 line coverage</title>
		<link>http://www.pervasivecode.com/blog/2007/07/11/rcov-c0-line-coverage-more-generous-than-emmas-c1-bytecode-coverage/</link>
		<comments>http://www.pervasivecode.com/blog/2007/07/11/rcov-c0-line-coverage-more-generous-than-emmas-c1-bytecode-coverage/#comments</comments>
		<pubDate>Wed, 11 Jul 2007 19:26:01 +0000</pubDate>
		<dc:creator>Jamie Flournoy</dc:creator>
				<category><![CDATA[coverage]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[tools]]></category>

		<guid isPermaLink="false">http://www.pervasivecode.com/blog/2007/07/11/rcov-c0-line-coverage-more-generous-than-emmas-c1-bytecode-coverage/</guid>
		<description><![CDATA[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.

For Java code coverage, I like EMMA. Clover looks really nice and all, but seriously, $250 a seat? Yeesh. Talk about poor product pricing &#8211; I will never [...]]]></description>
			<content:encoded><![CDATA[<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 />
<span id="more-34"></span><br />
For 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>
<p>Anyway, EMMA is very conservative about coverage estimates. Consider this single line of Java code:</p>
<p><code>int x = 1; if (x &gt; 1){throw new RuntimeException();}</code></p>
<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>
<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>
<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>
<p>Okay, now the Ruby version:</p>
<p><code>x = 1; raise RuntimeError if x &gt; 1</code></p>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<p>Good testing!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pervasivecode.com/blog/2007/07/11/rcov-c0-line-coverage-more-generous-than-emmas-c1-bytecode-coverage/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Ruby First Impressions: Backup Scripting</title>
		<link>http://www.pervasivecode.com/blog/2007/03/04/ruby-first-impressions-backup-scripting/</link>
		<comments>http://www.pervasivecode.com/blog/2007/03/04/ruby-first-impressions-backup-scripting/#comments</comments>
		<pubDate>Sun, 04 Mar 2007 06:09:06 +0000</pubDate>
		<dc:creator>Jamie Flournoy</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[coverage]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://www.pervasivecode.com/blog/2007/03/04/ruby-first-impressions-backup-scripting/</guid>
		<description><![CDATA[I started programming in Ruby this week, and so far I like it a lot. From my initial use of Ruby as a backup automation scripting language, here are my thoughts.
You might be wondering, why am I working on backup scripting now? Don&#8217;t I have some big project I&#8217;m supposed to be working on 24/7? [...]]]></description>
			<content:encoded><![CDATA[<p>I started programming in Ruby this week, and so far I like it a lot. From my initial use of Ruby as a backup automation scripting language, here are my thoughts.</p>
<p>You might be wondering, why am I working on backup scripting now? Don&#8217;t I have some big project I&#8217;m supposed to be working on 24/7? Yes, and actually this work is in the critical path of that project.</p>
<p>My super fast laptop is still away being repaired for a video problem, so I&#8217;ve taken a major hit in terms of the resources of my main computer: 90% less MHz, 36% less display area, 50% less memory. In the meantime, I&#8217;ve been avoiding tasks that need a lot of CPU or graphics performance and instead working on things that are easier on my old desktop computer.</p>
<p>This week, I decided that I would pause working on the design and implementation of my startup project, until I had really sorted out my server backup and monitoring situation.<br />
<span id="more-17"></span><br />
I have a few servers at home and hosted in a data center, and there are nightly backups in place, but this is done using a hodgepodge of different programs. I&#8217;ve got some Bash scripts that invoke <a href="http://samba.org/rsync/">rsync</a> every night, some Perl scripts that use cp -rp every night and once a week for different sets of data, and a backup program I found recently that is clever but not quite what I wanted, called <a href="http://www.rsnapshot.org/">rsnapshot</a>. I had previously evaluated <a href="http://faubackup.sourceforge.net/">faubackup</a>, but right after I started using it, I experienced filesystem corruption on the backup drive, which was pretty scary. (Also, faubackup doesn&#8217;t do remote backups, so I would have used rsync anyway.) Both rsnapshot and faubackup are designed to use hard links to save space, so in theory they&#8217;re pretty similar. Finally, my Macs use <a href="http://www.bombich.com/software/ccc.html">Carbon Copy Cloner</a>, which is a GUI for <a href="http://www.dan.co.jp/cases/macosx/psync.html">psync</a> that adds some extra steps to make the target volume bootable. Windows virtual machines in VMWare are handled as one giant VM disk file, which is wasteful but better than nothing.</p>
<p>Ideally, I&#8217;d like to duplicate much of what rsnapshot does, which really means using rsync + cp -al to do the heavy lifting, and using a high level wrapper script to manage scheduling and the backup archive. Also, I want something that I haven&#8217;t seen done well yet, which is detailed status notification: how much is being backed up, how much is changing daily, how much is being ignored on each remote system by the backup process, etc. And I want a daily summary email telling me that things are being backed up successfully.</p>
<p>So, I&#8217;m writing a bunch of Ruby classes, which represent data output from du, df, and find, and which do the text processing and math to provide totals and percentages in a convenient form for high level code. That high level code will interpret configuration data and remote status information, initiate backups, manage the archived backup data, and send summary emails.</p>
<p>On to Ruby impressions. As far as documentation, <a href="http://www.oreilly.com/catalog/ruby/">O&#8217;Reilly&#8217;s Ruby in a Nutshell</a> is unacceptable because of poor proofreading; the examples don&#8217;t work, and the explanations are obviously wrong. I got <a href="http://www.pragmaticprogrammer.com/titles/ruby/index.html">Programming Ruby</a> a.k.a. the Pickaxe book yesterday at the wonderful <a href="http://www.staceys.com/">Stacey&#8217;s Bookstore</a>, and I like it much better. I&#8217;ve also found a whole lot of good Ruby sample code online, not limited to just Rails examples as I had expected. It seems like there are a whole lot of people learning and falling in love with this language, and blogging and creating helpful web sites about it.</p>
<p>Regarding the language itself, it feels like Perl, except object oriented for real, and legible. I think I&#8217;m probably pretty typical of new Ruby programmers in that I love the way blocks, iterators, and specifically collect and inject work. Things like figuring out the total amount of free disk space on a remote server, not counting a predefined set of filesystem types (procfs, tmpfs, usbfs, etc.), take a half dozen lines of (admittedly fancy, but readable) code. I shudder to imagine how much Java code this would require.</p>
<p>Speaking of which, I&#8217;ve been a Java programmer (overlapping with shorter periods of Cold Fusion and PHP work) for 10 years, and a Perl scripter for 11. One thing I loved about Java was the QA first pass you get from static types and compilation: dumb mistakes are caught immediately, before you even try to run the code. Perl always felt expressive but risky, and Ruby feels even more expressive, but still risky in the same way.</p>
<p>The <a href="http://www.xprogramming.com/xpmag/whatisxp.htm">Extreme Programming</a> folks figured out years ago that the only way to write working code in a modern dynamic scripting language (defined for the purposes of this sentence as one that uses dynamic types and is not compiled) is to write a ton of automated tests. Basically you get to omit the code that does casting and type declarations and local variable initialization from your code, but you are forced to write a bunch of explicit test code in return. This would seem like a pointless trade-off, except for the fact that you have to write all that test code for statically typed, compiled languages anyway. So really the question is whether you got to working, debugged code faster with compilation and static type checking, or without it. Although I do like the super fast feedback loop of incremental compilation that you get from IntelliJ or Eclipse with Java, I&#8217;m pretty sure the answer is that &#8220;without it&#8221; is still faster. It just feels really scary, until you&#8217;ve written tests.</p>
<p>To eliminate the scary feeling, I started using Test::Unit. It&#8217;s is pretty nice, but it is kind of disconcerting that when you define a TestCase subclass and require it in your test script, it seems to just run the tests on its own without being explicitly told to do so. Aside from that potentially irrelevant oddity, it&#8217;s working well for me so far.</p>
<p>A related issue is code coverage. Code coverage tools measure which lines of source code are executed during one or more runs of your program, and typically they also generate reports with nice features like bar graphs and highlighted source code displays that show you red areas for code you didn&#8217;t execute and green for code you did. That information tells you (with reasonable, but not perfect accuracy) how complete your automated test suite is.</p>
<p>So, as you code, you put TODO comments all through it for things to be written, and you write tests for code that you just wrote to make sure it works, and then you run the test suite with a coverage tool enabled and look at the coverage report to see what you forgot to test. Then when you&#8217;re super close to, or at 100% coverage, you attack those TODOs.</p>
<p>In Java, I used JUnit and <a href="http://emma.sourceforge.net/">EMMA</a> as my unit test framework and coverage tool. Those were installed by hand, by me googling for them and then downloading the .zip files and expanding them by hand. I then had to write a bunch of custom Ant build.xml code to enable EMMA&#8217;s coverage instrumentation to be added to the compiled Java code. Then I had to write some more Ant build.xml code to generate the coverage report, and I had to do a whole lot of super tricky Ant nonsense to separate the build sequences that led to coverage reports from the ones that led to deployable production code, since the actual compiled code was different. That effort, in total, probably took me somewhere between 1 and 2 days, including all the wrong turns and reading and futzing I had to do to get it to work elegantly, which I eventually did.</p>
<p>Part of the reason for the hassle is that Java is not open source, nor is a usable open source implementation available. The open source community doesn&#8217;t really fully embrace Java by packaging it up nicely and helping users to distribute it, because they cannot legally do so.</p>
<p>Sun&#8217;s death grip on Java means extra work for Java developers. Getting a hold of all the code and getting it to work together is tedious and requires that you click through license agreements on Sun&#8217;s web site; no Linux distribution includes all of this stuff, and <a href="http://maven.apache.org/">Maven</a>, which is cool but (the last time I looked) very poorly documented, is the only thing that seems to even try and pull all of these things together, but it requires that you manually install it on top of Java which itself must be manually installed. Setting up Maven, and configuring it to manage the dependencies of each of your Java projects is no picnic, either. Perl has CPAN and Ruby has Gems and most Linux distributions have great open source package management systems now (and even Mac OS X has MacPorts), but Java has bupkis. You want to assemble an application, you either go out on a limb and try to get your organization to adopt Maven, or you do it all by hand, with lots of trial and error.</p>
<p>So, bear that in mind when I tell you this: for Ruby on Ubuntu Linux, here&#8217;s what was required, after Googling for &#8220;ruby coverage&#8221; and seeing that there was a thing called <a href="http://eigenclass.org/hiki.rb?rcov">rcov</a>:</p>
<p><code>sudo apt-get install rcov<br />
rcov test.tb</code></p>
<p>Seriously.</p>
<p>I didn&#8217;t even look and see if &#8216;rcov&#8217; was the package name Ubuntu had adopted. I just took a guess, and it worked, and then I read that one web page about usage to find that it&#8217;s just &#8220;rcov [your Ruby script here]&#8220;. Rcov runs the program you give it as a command line argument, and automatically creates a coverage report, quite quickly. It might have taken me one whole minute from &#8220;what&#8217;s out there&#8221; to &#8220;hey nice coverage report&#8221;.</p>
<p>That is an example of my experience with Ruby thus far. It&#8217;s just too easy to be true; I keep waiting for the gigantic gotcha, like performance (fast so far, but so far my code is trivial) or some hideous feature that&#8217;s missing.</p>
<p>In the meantime (until I find something not to like), Ruby seems really excellent, and I might not be able to go back to happily programming in Perl or Java or PHP again if this keeps up. :)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pervasivecode.com/blog/2007/03/04/ruby-first-impressions-backup-scripting/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
