Merge lp:~jtv/maas/db-fixture into lp:~maas-committers/maas/trunk

Proposed by Jeroen T. Vermeulen
Status: Merged
Merged at revision: 16
Proposed branch: lp:~jtv/maas/db-fixture
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 105 lines (+22/-22)
6 files modified
Makefile (+1/-1)
README.txt (+3/-3)
bin/maasdb (+2/-2)
src/maas/development.py (+4/-0)
src/maas/testing/runner.py (+12/-0)
src/maasserver/tests/simple_tests.py (+0/-16)
To merge this branch: bzr merge lp:~jtv/maas/db-fixture
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+88898@code.launchpad.net

Commit message

Start test database cluster from test runner.

Description of the change

Set up the testing database cluster as part of the test runner.

It took me a while to find a hook into the test runner that I could use. The one I found was: use a custom test runner based on the django one, but with a call to maasdb added to it. I couldn't define the custom runner in development.py or maas/__init__.py etc. because it'd just lead to circular imports and a mysterious failure.

To try this out, create a new branch (so there's certainly no database cluster running in it), "make," then check "ps -ef | grep postgres" to verify that there still is no postgres running in the branch, then "./bin/test" to see that the tests can access the database. I added some only-slightly-more-meaningful tests to make sure of that.

You may notice that database setup before a test takes a while. That doesn't go away when I remove the custom test runner though; I guess it's something we just can't get rid of.

Something a bit nasty I did was to hard-code "./db/" as the database cluster location. Ideally I'd have some way of specifying "main branch directory/db."

The maasdb script does nothing when asked to start a cluster that's already running. Doesn't take very long to find out, either.

Jeroen

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

This is a neat solution :)

[1]

+ def setup_databases(self, *args, **kwargs):
+ """Fire up the db cluster, then punt to original implementation."""
+ process = Popen(['bin/maasdb', 'start', './db/'])
+ retval = process.wait()
+ if retval != 0:
+ raise RuntimeError("Failed to start database cluster.")

Consider subprocess.check_call() here; it'll raise CalledProcessError
if the command does not exit with 0. Four lines into one:

        check_call(['bin/maasdb', 'start', './db/'])

It would also be nice to make this quiet unless there's an
error. Django doesn't seem to know about subunit output yet, but it
would be nice not to muddy things in advance. Witness how long it has
taken to make the Launchpad test suite STFU.

[2]

 class SimpleTest(TestCase):
- def test_basic_addition(self):
- """
- Tests that 1 + 1 always equals 2.
- """
- self.assertEqual(1 + 1, 2)
+
+ def test_can_create_nodes(self):
+ self.assertEqual([], list(Node.objects.all()))
+ n = Node(name='foo', status='NEW')
+ n.save()
+ self.assertEqual([n], list(Node.objects.all()))
+
+ def test_no_nodes_exist_initially(self):
+ self.assertEqual([], list(Node.objects.all()))
+

Raphael's recent changes provide enough to exercise your change, so
consider removing this.

review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :

Fwiw, I'd still prefer to use van.pg, but this works and that's plenty for now :)

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

> + def setup_databases(self, *args, **kwargs):
> + """Fire up the db cluster, then punt to original implementation."""
> + process = Popen(['bin/maasdb', 'start', './db/'])
> + retval = process.wait()
> + if retval != 0:
> + raise RuntimeError("Failed to start database cluster.")
>
> Consider subprocess.check_call() here; it'll raise CalledProcessError
> if the command does not exit with 0. Four lines into one:
>
> check_call(['bin/maasdb', 'start', './db/'])

Nice, thanks.

> It would also be nice to make this quiet unless there's an
> error. Django doesn't seem to know about subunit output yet, but it
> would be nice not to muddy things in advance. Witness how long it has
> taken to make the Launchpad test suite STFU.

Done. I changed 2 things:

1. Pass stdout=PIPE to check_call, which eats the routine output when running implicitly from bin/test.

2. Initialize the cluster with "-A trust" to suppress the warning about defaulting to trust authentication.

Together, those completely silence successful cluster initialization from bin/test.

> class SimpleTest(TestCase):
> - def test_basic_addition(self):
> - """
> - Tests that 1 + 1 always equals 2.
> - """
> - self.assertEqual(1 + 1, 2)
> +
> + def test_can_create_nodes(self):
> + self.assertEqual([], list(Node.objects.all()))
> + n = Node(name='foo', status='NEW')
> + n.save()
> + self.assertEqual([n], list(Node.objects.all()))
> +
> + def test_no_nodes_exist_initially(self):
> + self.assertEqual([], list(Node.objects.all()))
> +
>
> Raphael's recent changes provide enough to exercise your change, so
> consider removing this.

Thanks for pointing that out. I'll remove this whole test file then.

Jeroen

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

As for van.pg, it's attractive to have this job done by code from others but several things made me decide against it:

 * As far as I can tell, it's not packaged for Ubuntu. Dealing with that could be more trouble than rolling our own.

 * It depends on testresources, which is in universe.

 * The documentation didn't tell me what I needed to know, so I got lazy and just wrote my own.

 * Both django and van.pg try to create the test databases themselves, so there's a bit of overlap.

 * We have more flexibility to tweak our own, purpose-built code. For instance, our code can create a non-disposable database if necessary.

Revision history for this message
Gavin Panella (allenap) wrote :

> 1. Pass stdout=PIPE to check_call, which eats the routine output when
> running implicitly from bin/test.

This can deadlock when the pipe's buffer fills up. Instead stdout
could be redirected to a temporary file, or just to /dev/null:

  with open(os.devnull, "wb") as devnull:
      check_call([...], stdout=devnull)

> 2. Initialize the cluster with "-A trust" to suppress the warning
> about defaulting to trust authentication.

Cool :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2012-01-17 15:04:53 +0000
3+++ Makefile 2012-01-17 19:10:32 +0000
4@@ -11,7 +11,7 @@
5 dev-db:
6 bin/maasdb start ./db/ disposable
7
8-test: bin/test dev-db
9+test: bin/test
10 bin/test
11
12 lint:
13
14=== modified file 'README.txt'
15--- README.txt 2012-01-17 09:58:26 +0000
16+++ README.txt 2012-01-17 19:10:32 +0000
17@@ -8,9 +8,9 @@
18
19 Access to the database is configured in src/maas/development.py.
20
21-The Makefile sets up a development database cluster inside your branch. It
22-lives in the "db" directory, which gets created on demand. You'll want to
23-shut it down before deleting a branch; see below.
24+The Makefile or the test suite sets up a development database cluster inside
25+your branch. It lives in the "db" directory, which gets created on demand.
26+You'll want to shut it down before deleting a branch; see below.
27
28 First, set up the project. This fetches all the required dependencies, and
29 creates a local database cluster and development database:
30
31=== modified file 'bin/maasdb'
32--- bin/maasdb 2012-01-17 10:21:54 +0000
33+++ bin/maasdb 2012-01-17 19:10:32 +0000
34@@ -1,6 +1,6 @@
35 #! /bin/bash -e
36 #
37-# MaaS database control script
38+# MaaS database control script. See main() at the bottom for usage.
39 #
40 # Most functions take as their first argument a database cluster's data
41 # directory. This is where the database's socket, pidfile, log, and data will
42@@ -44,7 +44,7 @@
43 if ! test -d "$DATADIR/base"
44 then
45 mkdir -p -- "$DATADIR"
46- $PGCTL init -D "$DATADIR" -o '-E utf8'
47+ $PGCTL init -D "$DATADIR" -o '-E utf8 -A trust'
48 fi
49 }
50
51
52=== modified file 'src/maas/development.py'
53--- src/maas/development.py 2012-01-16 17:02:09 +0000
54+++ src/maas/development.py 2012-01-17 19:10:32 +0000
55@@ -5,6 +5,10 @@
56
57 import os
58
59+# Use our custom test runner, which makes sure that a local database
60+# cluster is running in the branch.
61+TEST_RUNNER='maas.testing.runner.CustomTestRunner'
62+
63 DEBUG = True
64 TEMPLATE_DEBUG = DEBUG
65 YUI_DEBUG = DEBUG
66
67=== added directory 'src/maas/testing'
68=== added file 'src/maas/testing/__init__.py'
69=== added file 'src/maas/testing/runner.py'
70--- src/maas/testing/runner.py 1970-01-01 00:00:00 +0000
71+++ src/maas/testing/runner.py 2012-01-17 19:10:32 +0000
72@@ -0,0 +1,12 @@
73+from subprocess import check_call, PIPE
74+from django.test.simple import DjangoTestSuiteRunner
75+
76+
77+class CustomTestRunner(DjangoTestSuiteRunner):
78+ """Custom test runner; ensures that the test database cluster is up."""
79+
80+ def setup_databases(self, *args, **kwargs):
81+ """Fire up the db cluster, then punt to original implementation."""
82+ check_call(
83+ ['bin/maasdb', 'start', './db/', 'disposable'], stdout=PIPE)
84+ return super(CustomTestRunner, self).setup_databases(*args, **kwargs)
85
86=== removed file 'src/maasserver/tests/simple_tests.py'
87--- src/maasserver/tests/simple_tests.py 2012-01-17 15:35:41 +0000
88+++ src/maasserver/tests/simple_tests.py 1970-01-01 00:00:00 +0000
89@@ -1,16 +0,0 @@
90-"""
91-This file demonstrates writing tests using the unittest module. These will pass
92-when you run "manage.py test".
93-
94-Replace this with more appropriate tests for your application.
95-"""
96-
97-from django.test import TestCase
98-
99-
100-class SimpleTest(TestCase):
101- def test_basic_addition(self):
102- """
103- Tests that 1 + 1 always equals 2.
104- """
105- self.assertEqual(1 + 1, 2)