Merge lp:~marcoceppi/charm-tools/fix-add-tests into lp:charm-tools/1.4

Proposed by Marco Ceppi
Status: Merged
Merge reported by: Tim Van Steenburgh
Merged at revision: not available
Proposed branch: lp:~marcoceppi/charm-tools/fix-add-tests
Merge into: lp:charm-tools/1.4
Diff against target: 214 lines (+92/-40)
5 files modified
charmtools/generate.py (+23/-12)
charmtools/templates/tests/body.tpl (+51/-25)
charmtools/templates/tests/deploy.tpl (+1/-1)
charmtools/templates/tests/relate.tpl (+1/-1)
tests/test_charm_generate.py (+16/-1)
To merge this branch: bzr merge lp:~marcoceppi/charm-tools/fix-add-tests
Reviewer Review Type Date Requested Status
Tim Van Steenburgh (community) Approve
Review via email: mp+240760@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Tim Van Steenburgh (tvansteenburgh) wrote :

This looks good, I just have a few comments, using this snip from the test I created as an example:

```
#!/usr/bin/env python3

import amulet
import requests
import unittest

class TestDeployment(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.deployment = amulet.Deployment(series='trusty')

        cls.deployment.add('thinkup')
        cls.deployment.add('cs:precise/apache2-25')
        cls.deployment.add('cs:precise/mysql-50')

        cls.deployment.relate('thinkup:website', 'apache2')
        cls.deployment.relate('thinkup:db', 'mysql')
```

1. We should install python3-requests in 00-setup since it's imported by 99-autogen.

2. cls.deployment.add('cs:precise/apache2-25') - I think we should just add 'apache2' here, and let amulet get the latest revision in the correct series.

3. cls.deployment.relate('thinkup:website', 'apache2') - Amulet will choke on this b/c the 2nd arg does not explicitly name the relation. Can this be corrected?

4. I agree with you about the templating approach, it's not ideal. Better would be to make body.tpl a Jinja template and do the looping directly in the template. Not a big deal though, we could clean that up later if you don't feel like doing it now.

review: Needs Fixing
345. By Marco Ceppi

Feedback from Tim

346. By Marco Ceppi

python3

Revision history for this message
Tim Van Steenburgh (tvansteenburgh) wrote :

LGTM.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmtools/generate.py'
2--- charmtools/generate.py 2014-07-01 15:35:22 +0000
3+++ charmtools/generate.py 2014-11-05 18:11:03 +0000
4@@ -51,18 +51,18 @@
5 shutil.copy(os.path.join(CHARM_TPL, tpl_file), charm_dir)
6
7
8-def tests(charm_dir, is_bundle=False, debug=False):
9+def tests(charm_dir, is_bundle=False, debug=False, series='trusty'):
10 c = Charm(charm_dir)
11
12+ if not c.is_charm():
13+ raise Exception('Not a Charm')
14+
15+ mdata = c.metadata()
16+
17 interfaces = {}
18- deploy = []
19+ deploy = [mdata['name']]
20 relations = []
21
22- if not c.is_charm():
23- raise Exception('Not a Charm')
24-
25- mdata = c.metadata()
26-
27 for rel_type in ['provides', 'requires']:
28 if rel_type in mdata:
29 interfaces[rel_type] = {}
30@@ -81,12 +81,13 @@
31 d = Template(file=ATPL['deploy'], searchList=[{'services': deploy}])
32 s = Template(file=ATPL['relate'], searchList=[{'relations': relations}])
33
34- t = Template(file=ATPL['body'], searchList=[{'deploy': d, 'relate': s}])
35+ t = Template(file=ATPL['body'], searchList=[{'deploy': d, 'relate': s,
36+ 'series': series}])
37
38 if not os.path.exists(os.path.join(charm_dir, 'tests')):
39 os.mkdir(os.path.join(charm_dir, 'tests'))
40
41- with open(os.path.join(charm_dir, 'tests', '00-autogen'), 'w') as f:
42+ with open(os.path.join(charm_dir, 'tests', '99-autogen'), 'w') as f:
43 f.write(str(t))
44
45 if not os.path.exists(os.path.join(charm_dir, 'tests', '00-setup')):
46@@ -98,7 +99,7 @@
47 sudo apt-get install amulet -y
48 """)
49
50- os.chmod(os.path.join(charm_dir, 'tests', '00-autogen'), 0755)
51+ os.chmod(os.path.join(charm_dir, 'tests', '99-autogen'), 0755)
52 os.chmod(os.path.join(charm_dir, 'tests', '00-setup'), 0755)
53
54
55@@ -108,13 +109,23 @@
56 parser.add_argument('subcommand', choices=['tests', 'readme', 'icon'],
57 help='Which type of generator to run')
58 parser = parser_defaults(parser)
59+ return parser.parse_known_args(args)
60+
61+
62+def tests_parser(args):
63+ # This bites, need an argparser experter
64+ parser = argparse.ArgumentParser(description="Add tests to a charm")
65+ parser.add_argument('--series', '-s', default='trusty',
66+ help='Series for the generated test')
67 return parser.parse_args(args)
68
69
70 def main():
71- a = parser()
72+ a, extra = parser()
73 if a.subcommand == 'tests':
74- tests(os.getcwd(), is_bundle=a.bundle, debug=a.debug)
75+ opts = tests_parser(extra)
76+ tests(os.getcwd(), is_bundle=a.bundle, debug=a.debug,
77+ series=opts.series)
78 elif a.subcommand == 'readme':
79 copy_file('README.ex', os.getcwd(), is_bundle=a.bundle, debug=a.debug)
80 elif a.subcommand == 'icon':
81
82=== modified file 'charmtools/templates/tests/body.tpl'
83--- charmtools/templates/tests/body.tpl 2013-12-10 17:37:20 +0000
84+++ charmtools/templates/tests/body.tpl 2014-11-05 18:11:03 +0000
85@@ -1,30 +1,56 @@
86-#!/usr/bin/python3
87+#!/usr/bin/env python3
88
89 import amulet
90-
91-d = amulet.Deployment()
92+import requests
93+import unittest
94+
95+
96+class TestDeployment(unittest.TestCase):
97+ @classmethod
98+ def setUpClass(cls):
99+ cls.deployment = amulet.Deployment(series='$series')
100
101 $deploy
102 $relate
103-# Don't forget to expose using d.expose(service)
104-
105-try:
106- d.setup(timeout=900)
107- d.sentry.wait()
108-except amulet.helpers.TimeoutError:
109- amulet.raise_status(amulet.SKIP, msg="Environment wasn't stood up in time")
110-except:
111- raise
112-
113-# Now you can use d.sentry.unit[UNIT] to address each of the units and perform
114-# more in-depth steps. There are three test statuses: amulet.PASS, amulet.FAIL,
115-# and amulet.SKIP - these can be triggered with amulet.raise_status(). Each
116-# d.sentry.unit[] has the following methods:
117-# - .info - An array of the information of that unit from Juju
118-# - .file(PATH) - Get the details of a file on that unit
119-# - .file_contents(PATH) - Get plain text output of PATH file from that unit
120-# - .directory(PATH) - Get details of directory
121-# - .directory_contents(PATH) - List files and folders in PATH on that unit
122-# - .relation(relation, service:rel) - Get relation data from return service
123-
124-# Make sure to rename this file from something other than 00-autogen
125+
126+ try:
127+ cls.deployment.setup(timeout=900)
128+ cls.deployment.sentry.wait()
129+ except amulet.helpers.TimeoutError:
130+ amulet.raise_status(amulet.SKIP, msg="Environment wasn't stood up in time")
131+ except:
132+ raise
133+
134+ def test_case(self):
135+ # Now you can use self.deployment.sentry.unit[UNIT] to address each of
136+ # the units and perform more in-depth steps. You can also reference
137+ # the first unit as self.unit.
138+ #
139+ # There are three test statuses that can be triggered with
140+ # amulet.raise_status():
141+ # - amulet.PASS
142+ # - amulet.FAIL
143+ # - amulet.SKIP
144+ #
145+ # Each unit has the following methods:
146+ # - .info - An array of the information of that unit from Juju
147+ # - .file(PATH) - Get the details of a file on that unit
148+ # - .file_contents(PATH) - Get plain text output of PATH file from that unit
149+ # - .directory(PATH) - Get details of directory
150+ # - .directory_contents(PATH) - List files and folders in PATH on that unit
151+ # - .relation(relation, service:rel) - Get relation data from return service
152+ # add tests here to confirm service is up and working properly
153+ #
154+ # For example, to confirm that it has a functioning HTTP server:
155+ #
156+ # page = requests.get('http://{}'.format(self.unit.info['public-address']))
157+ # page.raise_for_status()
158+ #
159+ # More information on writing Amulet tests can be found at:
160+ #
161+ # https://juju.ubuntu.com/docs/tools-amulet.html
162+ pass
163+
164+
165+if __name__ == '__main__':
166+ unittest.main()
167
168=== modified file 'charmtools/templates/tests/deploy.tpl'
169--- charmtools/templates/tests/deploy.tpl 2013-12-10 17:37:20 +0000
170+++ charmtools/templates/tests/deploy.tpl 2014-11-05 18:11:03 +0000
171@@ -1,3 +1,3 @@
172 #for $s in $services
173-d.add('$s')
174+ cls.deployment.add('$s')
175 #end for
176
177=== modified file 'charmtools/templates/tests/relate.tpl'
178--- charmtools/templates/tests/relate.tpl 2013-12-10 21:03:14 +0000
179+++ charmtools/templates/tests/relate.tpl 2014-11-05 18:11:03 +0000
180@@ -1,3 +1,3 @@
181 #for $r in $relations
182-d.relate('$r[0]', '$r[1]')
183+ cls.deployment.relate('$r[0]', '$r[1]')
184 #end for
185
186=== modified file 'tests/test_charm_generate.py'
187--- tests/test_charm_generate.py 2014-02-10 17:21:40 +0000
188+++ tests/test_charm_generate.py 2014-11-05 18:11:03 +0000
189@@ -1,5 +1,8 @@
190 import unittest
191-from charmtools.generate import graph
192+from charmtools.generate import (
193+ graph,
194+ copy_file,
195+)
196 from mock import patch
197
198
199@@ -19,3 +22,15 @@
200 self.assertEqual(graph('nointerface', 'requires'), None)
201 m.search.assert_called_with({'series': 'precise',
202 'provides': 'nointerface'})
203+
204+ @patch('charmtools.generate.Charm')
205+ @patch('charmtools.generate.shutil')
206+ def test_copy_file(self, msh, mcharm):
207+ m = mcharm.return_value.is_charm.return_value = True
208+ copy_file('1.ex', '/tmp')
209+ msh.copy.assert_called()
210+
211+ @patch('charmtools.generate.Charm')
212+ def test_not_charm(self, mcharm):
213+ mcharm.return_value.is_charm.return_value = False
214+ self.assertRaises(Exception, copy_file, '1.ex', '/no-charm')

Subscribers

People subscribed via source and target branches

to all changes: