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
=== modified file 'Makefile'
--- Makefile 2012-01-17 15:04:53 +0000
+++ Makefile 2012-01-17 19:10:32 +0000
@@ -11,7 +11,7 @@
11dev-db:11dev-db:
12 bin/maasdb start ./db/ disposable12 bin/maasdb start ./db/ disposable
1313
14test: bin/test dev-db14test: bin/test
15 bin/test15 bin/test
1616
17lint:17lint:
1818
=== modified file 'README.txt'
--- README.txt 2012-01-17 09:58:26 +0000
+++ README.txt 2012-01-17 19:10:32 +0000
@@ -8,9 +8,9 @@
88
9Access to the database is configured in src/maas/development.py.9Access to the database is configured in src/maas/development.py.
1010
11The Makefile sets up a development database cluster inside your branch. It11The Makefile or the test suite sets up a development database cluster inside
12lives in the "db" directory, which gets created on demand. You'll want to12your branch. It lives in the "db" directory, which gets created on demand.
13shut it down before deleting a branch; see below.13You'll want to shut it down before deleting a branch; see below.
1414
15First, set up the project. This fetches all the required dependencies, and15First, set up the project. This fetches all the required dependencies, and
16creates a local database cluster and development database:16creates a local database cluster and development database:
1717
=== modified file 'bin/maasdb'
--- bin/maasdb 2012-01-17 10:21:54 +0000
+++ bin/maasdb 2012-01-17 19:10:32 +0000
@@ -1,6 +1,6 @@
1#! /bin/bash -e1#! /bin/bash -e
2#2#
3# MaaS database control script3# MaaS database control script. See main() at the bottom for usage.
4#4#
5# Most functions take as their first argument a database cluster's data5# Most functions take as their first argument a database cluster's data
6# directory. This is where the database's socket, pidfile, log, and data will6# directory. This is where the database's socket, pidfile, log, and data will
@@ -44,7 +44,7 @@
44 if ! test -d "$DATADIR/base"44 if ! test -d "$DATADIR/base"
45 then45 then
46 mkdir -p -- "$DATADIR"46 mkdir -p -- "$DATADIR"
47 $PGCTL init -D "$DATADIR" -o '-E utf8'47 $PGCTL init -D "$DATADIR" -o '-E utf8 -A trust'
48 fi48 fi
49}49}
5050
5151
=== modified file 'src/maas/development.py'
--- src/maas/development.py 2012-01-16 17:02:09 +0000
+++ src/maas/development.py 2012-01-17 19:10:32 +0000
@@ -5,6 +5,10 @@
55
6import os6import os
77
8# Use our custom test runner, which makes sure that a local database
9# cluster is running in the branch.
10TEST_RUNNER='maas.testing.runner.CustomTestRunner'
11
8DEBUG = True12DEBUG = True
9TEMPLATE_DEBUG = DEBUG13TEMPLATE_DEBUG = DEBUG
10YUI_DEBUG = DEBUG14YUI_DEBUG = DEBUG
1115
=== added directory 'src/maas/testing'
=== added file 'src/maas/testing/__init__.py'
=== added file 'src/maas/testing/runner.py'
--- src/maas/testing/runner.py 1970-01-01 00:00:00 +0000
+++ src/maas/testing/runner.py 2012-01-17 19:10:32 +0000
@@ -0,0 +1,12 @@
1from subprocess import check_call, PIPE
2from django.test.simple import DjangoTestSuiteRunner
3
4
5class CustomTestRunner(DjangoTestSuiteRunner):
6 """Custom test runner; ensures that the test database cluster is up."""
7
8 def setup_databases(self, *args, **kwargs):
9 """Fire up the db cluster, then punt to original implementation."""
10 check_call(
11 ['bin/maasdb', 'start', './db/', 'disposable'], stdout=PIPE)
12 return super(CustomTestRunner, self).setup_databases(*args, **kwargs)
013
=== removed file 'src/maasserver/tests/simple_tests.py'
--- src/maasserver/tests/simple_tests.py 2012-01-17 15:35:41 +0000
+++ src/maasserver/tests/simple_tests.py 1970-01-01 00:00:00 +0000
@@ -1,16 +0,0 @@
1"""
2This file demonstrates writing tests using the unittest module. These will pass
3when you run "manage.py test".
4
5Replace this with more appropriate tests for your application.
6"""
7
8from django.test import TestCase
9
10
11class SimpleTest(TestCase):
12 def test_basic_addition(self):
13 """
14 Tests that 1 + 1 always equals 2.
15 """
16 self.assertEqual(1 + 1, 2)