Advertise with Googlier.compages tagged tddd
https://blog.liw.fi/tag/tddd/
blog.liw.fiikiwikiWed, 29 Jun 2011 12:45:43 +0000A tiny step towards TDDD and FreedomBox-NAShttps://blog.liw.fi/posts/tddd-fbxnas-step1/
https://blog.liw.fi/posts/tddd-fbxnas-step1/
freedomboxtdddWed, 29 Jun 2011 12:45:43 +00002011-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 && virsh start fbxnas && sleep 30 &&
~/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 developmenthttps://blog.liw.fi/posts/debian-tdd/
https://blog.liw.fi/posts/debian-tdd/
debiantdddWed, 08 Jun 2011 08:06:34 +00002011-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 concepthttps://blog.liw.fi/posts/tddd-systest/
https://blog.liw.fi/posts/tddd-systest/
debiansystesttdddTue, 07 Jun 2011 14:16:39 +00002011-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 thoughtshttps://blog.liw.fi/posts/debian-tdd-2/
https://blog.liw.fi/posts/debian-tdd-2/
debiantdddMon, 16 May 2011 16:26:57 +00002011-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>