Advertise with Googlier.com pages tagged tddd https://blog.liw.fi/tag/tddd/ blog.liw.fi ikiwiki Wed, 29 Jun 2011 12:45:43 +0000 A tiny step towards TDDD and FreedomBox-NAS https://blog.liw.fi/posts/tddd-fbxnas-step1/ https://blog.liw.fi/posts/tddd-fbxnas-step1/ freedombox tddd Wed, 29 Jun 2011 12:45:43 +0000 2011-06-29T12:45:43Z <p>I've taken a tiny step towards getting together a prototype toolset for developing an initial development iteration of the NAS functionality of the FreedomBox.</p> <p>Here's what I am trying to do:</p> <ul> <li>create a disk image for a system that provides simplistic NAS functionality, with no configuration, including no authentication: just serve whatever disks are available over SMB</li> <li>use a TDD approach to developing it</li> </ul> <p>In essence, this means I want to specify the functionality of the system I am building using automated tests. As soon as those tests pass, I will write more tests, and make them pass. When I can't think of new relevant tests, I know I'm done.</p> <p>I'm building a simple tool, <code>systest</code>, that will execute the tests. It is built on top of Python's <code>unittest</code> library, because that makes my implementation simple.</p> <p>I've now developed <code>systest</code> far enough that I can almost automatically do this:</p> <ul> <li>build a new image, using <code>vmdebootstrap</code>, plus a customization script</li> <li>start the image with KVM controlled by libvirt</li> <li>run the tests using <code>systest</code></li> </ul> <p>(The command line would be something like this: <code>./build-fbxnas &amp;&amp; virsh start fbxnas &amp;&amp; sleep 30 &amp;&amp; ~/systest/systest --target=192.168.122.89 --user=tomjon ~/systest/tests-debian-base-install.py --user-ssh-private-key=tomjon.secret --log=foo.log --user-password=password1</code> where <code>build-fbxnas</code> is a wrapper script around <code>vmdebootstrap</code>. But I didn't actually run that command, and it's lacking the <code>virt-install</code> command to actually set up the virtual machine the first time.)</p> <p><code>systest</code> contains very little code, but to get that far, I had to shave some yaks, and that made writing it rather an unpleasant experience.</p> <p>The tests so far really simple, and don't test NAS stuff at all, just that the basic Debian install works: only the ssh port is open, and basic networking and such things work. However, now that I've gotten this much to work, I can start writing NAS tests. (<code>smbclient</code> may be my next yak. This would be a lot easier if I had ever used Samba for anything real...)</p> <p><a href="https://gitorious.org/systest">systest</a> and <a href="https://gitorious.org/vmdebootstrap/vmdebootstrap">vmdebootstrap</a> are on Gitorious. No .debs yet, this stuff is much too raw, but you can run them from the checkouts, no install necessary.</p> <p>PS. If anyone knows how to query KVM or libvirt for the IP address assigned by their internal DHCP server to the virtual machine guest, automatically, without asking the actual virtual machine, please tell.</p> Test driven distribution development https://blog.liw.fi/posts/debian-tdd/ https://blog.liw.fi/posts/debian-tdd/ debian tddd Wed, 08 Jun 2011 08:06:34 +0000 2011-06-08T08:06:34Z <blockquote><p>Disclaimer: There is a huge discussion going on right now in the <a href="http://lists.debian.org/debian-devel/2011/05/threads.html">debian-devel</a> mailing list, about different ways to rearrange how Debian development happens, and maybe to provide a Debian users with an additional release. This blog entry is not a comment on that discussion, which I am ignoring. The discussion did, however, prompt a <a href="http://identi.ca/notice/72416032">dent</a> about "test driven development". That prompted a short exchange of thoughts with Tom Marble, and this blog post is a cleaned up version of my part in that discussion.</p></blockquote> <p>Currently Debian development happens roughly like this: all packages are uploaded to a part of the Debian archive called <strong>unstable</strong>. Once they've been in use for a while without serious problems, they get automatically copied into another part called <strong>testing</strong>. Every year or two, testing is frozen and any remaining problems are fixed, after which all of testing gets copied into yet another part of the archive called <strong>stable</strong>, and that's the actual release.</p> <p>There is a little bit of automatic testing, but only a little. Almost all testing is done by people, by them using unstable or testing on their usual computers. If they have problems, they report them.</p> <p>Contrast this with development using Test Driven Development, or <a href="http://en.wikipedia.org/wiki/Test-driven_development">TDD</a>, and other modern development methodologies. Here's a rough summary of what I do when I write (much) of my software.</p> <ul> <li>first write one or more automatic tests (unit or functional ones)</li> <li>write the actual code, enough to make all tests pass</li> <li>add more tests</li> <li>write more code, or change existing code</li> <li>repeat this until tests describe all the behavior desired of the code</li> </ul> <p>In addition, I measure <strong>coverage</strong> to make sure all parts of the code gets tested. I usually aim for 100% coverage, except for those parts that very hard or quite pointless to test. (That's easier to achieve than you'd think, but that's the topic of another blog post.)</p> <p>This all sounds like a lot of bureaucratic nonsense, but what I get out of this is this: once all tests pass, I have a strong confidence that the software works. As soon as I've added all the features I want to have for a release, I can push a button to push it out. (Well, actually there's a <a href="http://liw.fi/release/">little bit</a> more to it.)</p> <p>This does mean I never have bugs in my software. Of course I do. However, there's a lot fewer of them. In fact, there's so few of them, after tests pass, that I would almost be happy to make an automatic release after every successful run of "make check".</p> <p>Another aspect of the way I do development is distributed version control. The relevant feature is powerful branching and merging. Most things I develop happen in short-lived single-purpose branches: whenever I start work on a feature or bugfix, I create a branch for it. (Unless I'm feeling particularly lazy, which happens more often than you'd think.)</p> <p>When I've finished the feature, or fixed the bug, I merge the branch back into the <strong>trunk</strong> branch.</p> <p>This way, the trunk is always in releaseable state. It might not have all the features I want for the next release, but the features that are there, do work. The branch probably has bugs, but if I've written tests that are good enough, I know the software works well enough.</p> <p>Or that's the idea. Sometimes things go wrong. Then I write more tests and the next time goes better. (It doesn't have to be perfect, as long as it gets better every time.)</p> <p>See the contrast between automatic tests with good coverage, and the Debian style of relying on user feedback? There's no need to wait for user feedback with automatic tests. This speeds up development, makes releasing easier, but most importantly takes away any reason to fear making changes, even big changes.</p> <p>Automatic tests and good test coverage are easy achieve in small projects. For a system as huge as Debian, good test coverage is quite hard to achieve.</p> <p>The thing about automatic tests, though, is that even a little bit of it is helpful, and after you have a few tests, it gets easier to add more. The first test is the hardest to get done, since you need to set up all the infrastructure for it.</p> <p>Debian does do a bit of automatic testing already. (See lintian, piuparts, autopkgtest, edos, etc.) I don't want to belittle that, but I think we could do better.</p> <p>Here's what I would love to see:</p> <ul> <li>we have a part of the archive that corresponds to a trunk branch, i.e., it is always in a releasable state (the "testing" area was originally meant to be that; we could make it so now)</li> <li>releaseable state is determined by two things: <ul> <li>an automatic test suite</li> <li>user feedback, particularly in the form of bug reports (as now)</li> </ul> </li> <li>whenever changes are made, they happen in "branches" <ul> <li>the branch is not affected by changes elsewhere in the archive, except by manual synching ("merge from trunk")</li> <li>individual package uploads, as well as groups of packages such as for transitions, are each in their own branch</li> <li>this is sort of similar to a PPA, but more powerful</li> </ul> </li> <li>when a branch is to be merged into trunk, the automatic tests must first pass (or at least no new failures in them can be introduced) <ul> <li>tests can also be run for any other branch, of course, so that those developing the branch know if they're ready to push their changes into trunk</li> </ul> </li> <li>there's a culture of writing tests for bugs (whenever that is feasible), and for new features <ul> <li>particularly release goals should be expressed as automatic test suites</li> </ul> </li> <li>there's a culture of sharing tests with upstreams, and with other distributions</li> </ul> <p>Since full test coverage is going to be impossible for Debian, some subset should be targeted. Perhaps something like this would suffice to start with:</p> <ul> <li>a version of debian-installer is generated from a branch</li> <li>test installation of a new system from scratch <ul> <li>with a large set of tasks selected</li> </ul> </li> <li>test upgrade from previous stable release to the branch <ul> <li>with a large set of packages installed (as many as possible, actually)</li> </ul> </li> <li>test upgrade from trunk branch to branch to be merged <ul> <li>ditto large set of packages</li> </ul> </li> <li>test with lintian, reject on specific tests failing</li> <li>test with piuparts</li> <li>test the whole system for specific functionality <ul> <li>ssh access from outside</li> <li>sudo access for a logged in user</li> <li>sending mail with SMTP</li> <li>web server</li> <li>possibly test specific web applications</li> <li>possibly test Samba and NFS services</li> <li>possibly test printing services (CUPS)</li> <li>possibly test essential desktop functionality (automatic login, at least)</li> </ul> </li> <li>if package comes with package specific tests, run those too</li> </ul> <p>These tests would not guarantee that a set of changes would not break Debian, but they would give a high confidence that at least basic stuff would still work after the changes.</p> <p>Now, obviously implementing all of this I'm dreaming of (it is just past midnight, after all) is going to be impossible. There's way too much work to do, there's not enough tools for writing tests, and it would require too much computing power to run, and so on and so forth. But it's late, and I've had a bad day, and I might as well dream of something nice.</p> <p>Anyway, anyone interested in these things should perhaps help drive <a href="http://dep.debian.net/deps/dep8/">DEP8</a>.</p> TDDD systest tool proof of concept https://blog.liw.fi/posts/tddd-systest/ https://blog.liw.fi/posts/tddd-systest/ debian systest tddd Tue, 07 Jun 2011 14:16:39 +0000 2011-06-07T14:16:39Z <p>Continuing my earlier musings about test driven distro development, and the tools that would require.</p> <p>I imagine something like this:</p> <ul> <li>create a virtual machine with a particular configuration of stable</li> <li>run a test suite verifying the VM works</li> <li>upgrade the VM to testing and reboot</li> <li>re-run test suite, verifying things work in testing</li> </ul> <p>We might also want this scenario:</p> <ul> <li>install testing directly in a VM (instead of upgrading from stable)</li> <li>run test suite</li> </ul> <p>There might be other scenarios that would be useful to test as well. Even from these two, it's clear there's a need for at least three separate tools:</p> <ul> <li>create a machine image with a particular configuration <ul> <li>might be based on stable or testing or unstable</li> <li>might also be using some other package sources</li> <li>might want particular packages installed</li> <li>might want particular configuration settings</li> </ul> </li> <li>run a suite of tests against the running image <ul> <li>which tests to run will depend on the scenario</li> </ul> </li> <li>run various scenarios using the above two tools <ul> <li>needs reasonably easy ways to specify scenarios</li> <li>needs to support tests specific to scenarios</li> </ul> </li> </ul> <p>I've written a rudimentary version of the first tool: vmdebootstrap. I've since learned there's a bunch of others that might also work. There's room for more: someone should write (or find) a tool to make a snapshot of a real system and create a VM image that mimicks it, for example. Anyway, for now, I'll assume that one of the existing tools is good enough to get started.</p> <p>For the second tool, I wrote a quick-and-dirty proof-of-concept thing, see <a href="https://blog.liw.fi/posts/tddd-systest/systest.py">systest.py</a>. Here's a sample of how it might be used:</p> <pre><code>liw@havelock$ ./systest -v --target 192.168.122.139 --user tomjon test 1/6: cat test 2/6: only-ssh-port ERROR: Assertion failed: ['22/tcp', '139/tcp', '445/tcp'] != ['22/tcp'] [status 1] liw@havelock$ ./systest -v --target 192.168.122.139 --user tomjon \ cat ping6-localhost ping-localhost simple-dns-lookup ssh-login test 1/5: cat test 2/5: ping6-localhost test 3/5: ping-localhost test 4/5: simple-dns-lookup test 5/5: ssh-login liw@havelock$ </code></pre> <p>The first run failed, because the VM I'm testing against has some extra ports open. Some of the tests will require logging into the machine, via ssh, and for that one needs to specify the user to use.</p> <p><code>systool</code> may overlap heavily on system monitoring tools, and possibly the production implementation should be based on those.</p> <p>I think it's best to design such a tool for the more general purpose of testing whether a system currently works rather than as an integrated part of a more specific larger tool. This lets the tool be useful for other things than just testing specific things about Debian. (The production implementation would then need to not have all the tests hardcoded, of course. SMOP.)</p> <p>The third tool I have not spent a lot of time on yet. One thing at a time.</p> <p>Given these tools, one would then need to decide how to use them. The easiest way would be to use them like lintian and piuparts: run them frequently on whatever packages happen to be in testing or unstable or experimental, and put links to the test reports to the PTS, and hope that people fix things.</p> <p>That is the easiest way to start things.</p> <p>Once there's a nice set of test cases and scenarios, it may be interesting to think about more aggressive ways: for example, preventing packages from migrating to testing unless the test suite passes with them. If the tests do not pass, one of four things is broken:</p> <ul> <li>the package or packages in question</li> <li>other packages already in testing</li> <li>the tests themselves</li> <li>the test environment</li> </ul> <p>If things are set up properly, the last one should be rare. The other three always require manual inspection: it is not possible to automatically know whether the test itself, or the code it tests, is at fault. it is, however, enough to know that something is wrong. If the tests are written well, they should be robust enough to not be the culprits very often.</p> <p>(Someone wanting to make a rolling distrbution, or even better, making a monthly mini-release, might benefit from this sort of automated testing.)</p> TDDD, more thoughts https://blog.liw.fi/posts/debian-tdd-2/ https://blog.liw.fi/posts/debian-tdd-2/ debian tddd Mon, 16 May 2011 16:26:57 +0000 2011-05-16T16:26:57Z <p>My <a href="https://blog.liw.fi/posts/debian-tdd/">previous blog entry</a> about Test Driven Distro Development (TDDD) was rather rambling. Here is the condensed version, plus some further thoughts.</p> <p><strong>I would like to automatically determine whether "testing" is minimally ready to be released, at any given point in time.</strong></p> <p>To implement this, I would like to see Debian get tools that would allow us to define specific minimal functionality that should work in a Debian release, and automatically verify that it does. I would like us to use these tools to determine whether a release is ready for release, with manual override from the release team. Ideally, we would use the tools to prevent breakages from moving into "testing", so that it gets closer to always being releasable.</p> <p>Getting test coverage sufficiently high that we can replace the release team's judgement abour releasablity will never happen: Debian is rather too large and and complex for that. However, we can reap much benefit from even relatively small coverage.</p> <p>Here is my initial suggestion for the minimal criteria that should be satisfied, at all times, in "testing". They are aimed at making sure that even if something else breaks, at least the basic stuff works well enough that one can log in and fix things.</p> <ul> <li>there are no open ports in a base install</li> <li>logging in via getty works</li> <li>logging in via ssh works, if openssh-server is installed</li> <li>su, sudo, ls, cp, mv, rm, and vi work at least in some typical use cases</li> <li>web pages can be accessed from outside the system, given suitably configured apache2</li> <li>after automatic login into a graphical desktop environment, all the crucial per-session programs start up, as well as web browsers and other GUI programs that almost everyone expects to be part of a desktop environment <ul> <li>if there are good GUI testing tools, we may want to test in more detail, but that may have to wait for later</li> </ul> </li> </ul> <p>These are <strong>minimal</strong> criteria. They're not meant to verify that everything in Debian works. After these work, adding more coverage is not just possible, but reasonably easy in many cases. Want to make sure DNS gets configured right? Write a test that logs in and does a lookup inside the test system. After the tools have evolved, writing a simple test like that should be only a line or two.</p> <p>The tests I am talking here are system tests, or acceptance tests for the whole system, not just for individual packages. Unit tests would test individual functions or methods or classes; integration tests would test groups of them, or possibly entire programs; system tests test groups of programs/packages.</p> <p>The point is not to verify that every package works in every possible case, but that the system as a whole works. Most package-specific functional tests should be part of the package's own test suite. We should also run those, but that's somewhat orthogonal to the vision I'm trying to articulate here.</p> <p>At least the following scenarios should be tested (the above tests should be run after each scenario):</p> <ul> <li>install "stable" and upgrade to "testing"</li> <li>install "testing" using latest d-i release, and upgrade to current "testing"</li> <li>use an install from the previous successful test run, and upgrade to the current "testing" <ul> <li>this answers the question "if testing worked yesterday, does it still work if we add these new packages"</li> </ul> </li> </ul> <p>It may be useful to run the tests against "unstable" as well as "testing". However, rejecting an upload to "unstable" may be less wise than preventing packages from being copied from "unstable" to "testing". Exactly how this the development processes should adopt TDDD is an interesting question, but a reasonable toolset should be adaptable to anything we want to do.</p> <p>(Indeed, it would be possible to structure the toolset so that it isn't specific to developing a distro: a "system-test" tool that a sysadmin could use to verify that a server works would probably be useful to some people. Perhaps such a tool exists and could be adapted for TDDD?)</p> <p>Running these tests for security updates to "stable" would obviously also be a possibility that should perhaps be considered.</p> <p>Running these tests would happen something like this:</p> <ul> <li>generate a suitable virtual machine disk image, configured in the proper way for the test</li> <li>run the image under virtualization (or on real hardware if that can be remote-controlled suitably)</li> <li>run the tests against the image</li> <li>if tests fail, archive the image (or sufficient information to reproduce the start image) <ul> <li>also, automatically file bugs against all packages involved in the test, and e-mail debian-devel-announce with the names of the people who uploaded the new versions of packages compared to the previous successful test run, Cc'ing their spouses and parents for further shaming, and also Cc'ing the Debconf organization team so they can stood up before an audience armed with rotten tomatoes and eggs (this bullet point is here only to see if anyone actually reads my blog posts)</li> </ul> </li> </ul> <p>I would further like to see Debian make it possible to have isolated branches for development. This would help with, for example, large transitions that take a long time. The transition could be prepared in a branch of its own, and when it's ready to be pushed into unstable, the automatic tests outlined above could be used to verify the transition doesn't break anything too critical. (If the tests fail, the push fails; if necessary, that can be overridden manually, but that should be rare.)</p> <p>In my view, branches would greatly benefit Debian development, but they're not necessary for starting to implement TDDD.</p> <p>My condensed versions are still too long.</p>