Merge lp:~marcoceppi/charm-tools/test-cfg into lp:charm-tools/1.2

Proposed by Marco Ceppi
Status: Rejected
Rejected by: Marco Ceppi
Proposed branch: lp:~marcoceppi/charm-tools/test-cfg
Merge into: lp:charm-tools/1.2
Diff against target: 269 lines (+50/-21)
5 files modified
charmtools/bundles.py (+0/-1)
charmtools/generate.py (+0/-1)
charmtools/test.py (+36/-10)
charmtools/update.py (+2/-0)
tests/test_juju_test.py (+12/-9)
To merge this branch: bzr merge lp:~marcoceppi/charm-tools/test-cfg
Reviewer Review Type Date Requested Status
charmers Pending
Review via email: mp+200921@code.launchpad.net

Description of the change

Added support for substrate parsing in TestCfg

https://codereview.appspot.com/49160046/

To post a comment you must log in.
lp:~marcoceppi/charm-tools/test-cfg updated
287. By Marco Ceppi

PEP8 Fixes

Revision history for this message
Charm Tester (charmtester) wrote :
Download full text (9.8 KiB)

Reviewers: mp+200921_code.launchpad.net,

Message:
Please take a look.

Description:

Added support for substrate parsing in TestCfg

https://code.launchpad.net/~marcoceppi/charm-tools/test-cfg/+merge/200921

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/49160046/

Affected files (+52, -21 lines):
   A [revision details]
   M charmtools/bundles.py
   M charmtools/generate.py
   M charmtools/test.py
   M charmtools/update.py
   M tests/test_juju_test.py

Index: [revision details]
=== added file '[revision details]'
--- [revision details] 2012-01-01 00:00:00 +0000
+++ [revision details] 2012-01-01 00:00:00 +0000
@@ -0,0 +1,2 @@
+Old revision: <email address hidden>
+New revision: <email address hidden>

Index: charmtools/bundles.py
=== modified file 'charmtools/bundles.py'
--- charmtools/bundles.py 2013-12-20 14:06:43 +0000
+++ charmtools/bundles.py 2014-01-08 22:43:34 +0000
@@ -2,7 +2,6 @@
  import yaml
  import glob
  import json
-import requests

  from linter import Linter
  from charmworldlib import bundle as cw_bundle

Index: charmtools/generate.py
=== modified file 'charmtools/generate.py'
--- charmtools/generate.py 2013-12-12 17:20:39 +0000
+++ charmtools/generate.py 2014-01-08 22:43:34 +0000
@@ -16,7 +16,6 @@
  # along with this program. If not, see <http://www.gnu.org/licenses/>.

  import os
-import sys
  import shutil
  import argparse

Index: charmtools/test.py
=== modified file 'charmtools/test.py'
--- charmtools/test.py 2013-12-21 12:59:08 +0000
+++ charmtools/test.py 2014-01-08 22:43:34 +0000
@@ -3,7 +3,6 @@

  import os
  import sys
-import uuid
  import yaml
  import time
  import glob
@@ -12,7 +11,6 @@
  import argparse
  import subprocess

-from tempfile import mkdtemp
  from datetime import timedelta
  from contextlib import contextmanager

@@ -44,6 +42,10 @@
      pass

+class SubstrateMismatch(Exception):
+ pass
+
+
  class TimeoutError(Exception):
      def __init__(self, value="Timed Out"):
          self.value = value
@@ -114,7 +116,7 @@
                  self.destroy(self.juju_env)
              except DestroyUnreliable:
                  self.log.warn('Unable to destroy bootstrap, trying again')
- sleep(2)
+ time.sleep(2)
                  try:
                      self.destroy(self.juju_env)
                  except:
@@ -258,6 +260,7 @@
          try:
              with timeout(self.conductor.args.timeout):
                  output = subprocess.check_output(self.test, env=self.env)
+ self.log.debug(output)
          except TimeoutError, e:
              self.log.debug('Killed by timeout after %s seconds',
                             self.conductor.args.timeout)
@@ -279,7 +282,7 @@
              try:
                  self.archive_logs()
              except OrchestraError, e:
- juju.log.error(e)
+ self.log.error(e)

          if error:
              raise error
@@ -293,7 +296,7 @@
              raise OrchestraError('Unable to query juju status')

          services = status['services']
- machines = status['machines']
+...

Read more...

Revision history for this message
Charm Tester (charmtester) wrote :
Revision history for this message
Benjamin Saller (bcsaller) wrote :

I like what you started here and put a few comments inline.

I took a stab at extending your work in another branch.

https://codereview.appspot.com/49670043/

Look at this when you have time. I think its more testable than what you
have there (and more tested infact). If you like the model we can look
at cleaning it up a little more and merging. Let me know what you think.

https://codereview.appspot.com/49160046/diff/20001/charmtools/test.py
File charmtools/test.py (right):

https://codereview.appspot.com/49160046/diff/20001/charmtools/test.py#newcode411
charmtools/test.py:411: setattr(self, key, val)
While this is a common pattern, its generally better to whitelist vars
that can be set in this way. Its possible now for the options to break
the tests by setting a name they sholdn't have on this class.

https://codereview.appspot.com/49160046/diff/20001/charmtools/test.py#newcode413
charmtools/test.py:413: self.substrates = {}
I think while we could validate the substrates its important to note
that what we produce from this parse is a set of rules, not a list of
substrates.

As we add substrates we want to automatically include those unless the
charm explicitly said it only tests on one substrate (which could happen
for EC2 specific charms for example) or that it explicitly doesn't run
on one (or more) substrates

https://codereview.appspot.com/49160046/

Revision history for this message
Charm Tester (charmtester) wrote :

I'll take a look at your proposal too, thanks for the review!

https://codereview.appspot.com/49160046/diff/20001/charmtools/test.py
File charmtools/test.py (right):

https://codereview.appspot.com/49160046/diff/20001/charmtools/test.py#newcode413
charmtools/test.py:413: self.substrates = {}
I agree, think of self.substrates as more self.substrate_rules where it
as if it's inclusive or not and the values for inclusive or not. It
never has a comprehensive list of _all_ substrates at any one time, it's
more a parsing of the substrate option provided in the test_config.yaml

On 2014/01/09 07:52:40, benjamin.saller wrote:
> I think while we could validate the substrates its important to note
that what
> we produce from this parse is a set of rules, not a list of
substrates.

> As we add substrates we want to automatically include those unless the
charm
> explicitly said it only tests on one substrate (which could happen for
EC2
> specific charms for example) or that it explicitly doesn't run on one
(or more)
> substrates

https://codereview.appspot.com/49160046/

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmtools/bundles.py'
2--- charmtools/bundles.py 2013-12-20 14:06:43 +0000
3+++ charmtools/bundles.py 2014-01-08 22:43:54 +0000
4@@ -2,7 +2,6 @@
5 import yaml
6 import glob
7 import json
8-import requests
9
10 from linter import Linter
11 from charmworldlib import bundle as cw_bundle
12
13=== modified file 'charmtools/generate.py'
14--- charmtools/generate.py 2013-12-12 17:20:39 +0000
15+++ charmtools/generate.py 2014-01-08 22:43:54 +0000
16@@ -16,7 +16,6 @@
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 import os
20-import sys
21 import shutil
22 import argparse
23
24
25=== modified file 'charmtools/test.py'
26--- charmtools/test.py 2013-12-21 12:59:08 +0000
27+++ charmtools/test.py 2014-01-08 22:43:54 +0000
28@@ -3,7 +3,6 @@
29
30 import os
31 import sys
32-import uuid
33 import yaml
34 import time
35 import glob
36@@ -12,7 +11,6 @@
37 import argparse
38 import subprocess
39
40-from tempfile import mkdtemp
41 from datetime import timedelta
42 from contextlib import contextmanager
43
44@@ -44,6 +42,10 @@
45 pass
46
47
48+class SubstrateMismatch(Exception):
49+ pass
50+
51+
52 class TimeoutError(Exception):
53 def __init__(self, value="Timed Out"):
54 self.value = value
55@@ -114,7 +116,7 @@
56 self.destroy(self.juju_env)
57 except DestroyUnreliable:
58 self.log.warn('Unable to destroy bootstrap, trying again')
59- sleep(2)
60+ time.sleep(2)
61 try:
62 self.destroy(self.juju_env)
63 except:
64@@ -258,6 +260,7 @@
65 try:
66 with timeout(self.conductor.args.timeout):
67 output = subprocess.check_output(self.test, env=self.env)
68+ self.log.debug(output)
69 except TimeoutError, e:
70 self.log.debug('Killed by timeout after %s seconds',
71 self.conductor.args.timeout)
72@@ -279,7 +282,7 @@
73 try:
74 self.archive_logs()
75 except OrchestraError, e:
76- juju.log.error(e)
77+ self.log.error(e)
78
79 if error:
80 raise error
81@@ -293,7 +296,7 @@
82 raise OrchestraError('Unable to query juju status')
83
84 services = status['services']
85- machines = status['machines']
86+ # machines = status['machines']
87
88 if self.conductor.juju_version.major == 0:
89 logs.append('/var/lib/juju/units/./*/charm.log')
90@@ -402,9 +405,18 @@
91 with open(config_file) as f:
92 cfg = yaml.safe_load(f.read())
93
94- for key, val in cfg['options'].iteritems():
95- if key in self._keys:
96- setattr(self, key, val)
97+ if 'options' in cfg:
98+ for key, val in cfg['options'].iteritems():
99+ if key in self._keys:
100+ setattr(self, key, val)
101+ if 'substrates' in cfg:
102+ self.substrates = {}
103+ if 'allow' in cfg:
104+ self.substrates['inclusive'] = True
105+ self.substrates['values'] = cfg['substrates']['allow']
106+ elif 'skip' in cfg:
107+ self.substrates['inclusive'] = False
108+ self.substrates['values'] = cfg['substrates']['skip']
109
110
111 def get_juju_version():
112@@ -564,8 +576,9 @@
113 logger.info('Starting test run on %s using Juju %s'
114 % (args.juju_env, get_juju_version()))
115 logger.debug('Loading configuration options from testplan YAML')
116- test_plans = glob.glob(os.path.join(os.getcwd(), 'tests', 'testplan.y*ml'))
117- test_plan = test_plan[0] if test_plans else None
118+ test_plans = glob.glob(os.path.join(os.getcwd(), 'tests',
119+ 'test_config.y*ml'))
120+ test_plan = test_plans[0] if test_plans else None
121 if test_plan:
122 cfg = TestCfg(test_plan)
123 for key, val in args.iteritems():
124@@ -577,10 +590,23 @@
125 logger.debug('Creating a new Conductor')
126 try:
127 tester = Conductor(args)
128+ env_yaml = tester.get_environment(cfg.juju_env)
129+ if 'substrates' in cfg:
130+ # We have substrate configuration data.
131+ substrate_match = env_yaml['type'] in cfg.substrates['values']
132+ if cfg.substrates['inclusive'] and not substrate_match:
133+ pass
134+ if not cfg.substrates['inclusive'] and substrate_match:
135+ raise Exception('%s is not in allowed substrates: %s' %
136+ (env_yaml['type'],
137+ cfg.substrates['values'].join(', ')))
138 errors, failures, passes = tester.run()
139 except NoTests:
140 logger.critical('No tests were found')
141 sys.exit(1)
142+ except Exception as e:
143+ logger.critical(str(e))
144+ sys.exit(1)
145 except:
146 raise
147
148
149=== modified file 'charmtools/update.py'
150--- charmtools/update.py 2013-12-16 16:12:29 +0000
151+++ charmtools/update.py 2014-01-08 22:43:54 +0000
152@@ -34,6 +34,7 @@
153
154 return parser
155
156+
157 def update(charm_dir, fix=False):
158 mr = Mr(charm_dir)
159 for charm in charms.remote():
160@@ -48,6 +49,7 @@
161 except Exception as e:
162 raise Exception(".mrconfig not saved: %s" % e.strerror)
163
164+
165 def main():
166 parser = setup_parser()
167 args = parser.parse_args()
168
169=== modified file 'tests/test_juju_test.py'
170--- tests/test_juju_test.py 2013-12-20 12:33:21 +0000
171+++ tests/test_juju_test.py 2014-01-08 22:43:54 +0000
172@@ -30,13 +30,16 @@
173
174 PARSED_ENVIRONMENTS_YAML = yaml.safe_load(RAW_ENVIRONMENTS_YAML)
175
176+
177 class Arguments(object):
178 def __init__(self, **kwargs):
179 for key, value in kwargs.items():
180 setattr(self, key, value)
181+
182 def __getattr__(self, name):
183 return None
184
185+
186 class JujuTestPluginTest(unittest.TestCase):
187 @patch('subprocess.check_output')
188 def test_get_gojuju_version(self, mock_check_output):
189@@ -77,7 +80,7 @@
190 self.assertEqual(100, juju_test.convert_to_timedelta('100s'))
191 self.assertEqual(100, juju_test.convert_to_timedelta(100))
192 self.assertEqual(60, juju_test.convert_to_timedelta('1m'))
193- self.assertEqual(60*60, juju_test.convert_to_timedelta('1h'))
194+ self.assertEqual(60 * 60, juju_test.convert_to_timedelta('1h'))
195
196 @patch('glob.glob')
197 @patch('os.path.isfile')
198@@ -89,7 +92,7 @@
199 mock_isfile.side_effect = files_exist
200 mock_glob.return_value = tests_directory
201
202- args = Arguments(tests = 'dummy')
203+ args = Arguments(tests='dummy')
204 c = juju_test.Conductor(args)
205 results = c.find_tests()
206
207@@ -100,7 +103,7 @@
208 def test_conductor_find_tests_fails(self, mock_glob):
209 mock_glob.return_value = []
210
211- args = Arguments(tests = 'dummy')
212+ args = Arguments(tests='dummy')
213 c = juju_test.Conductor(args)
214 results = c.find_tests()
215
216@@ -126,7 +129,7 @@
217
218 mcheck_output.side_effect = [yml_output, Exception('not bootstrapped')]
219 juju_env = 'test'
220- args = Arguments(tests = 'dummy')
221+ args = Arguments(tests='dummy')
222 c = juju_test.Conductor(args)
223 results = c.status(juju_env)
224
225@@ -193,7 +196,7 @@
226
227 @patch('subprocess.check_call')
228 def test_conductor_destroy(self, mock_check_call):
229- args = Arguments(tests = 'dummy')
230+ args = Arguments(tests='dummy')
231 c = juju_test.Conductor(args)
232 c.juju_version = juju_test.JujuVersion(major=1, minor=1, patch=1)
233 good_env = 'valid'
234@@ -216,7 +219,7 @@
235
236 bad_env = 'invalid'
237
238- args = Arguments(tests = 'dummy')
239+ args = Arguments(tests='dummy')
240 c = juju_test.Conductor(args)
241 c.juju_version = juju_test.JujuVersion(major=1, minor=8, patch=0)
242 self.assertRaises(juju_test.DestroyUnreliable, c.destroy, bad_env)
243@@ -448,7 +451,7 @@
244 o.log.status.assert_called_with('%s (%s)' % (juju_test.TEST_FAIL,
245 juju_test.TEST_TIMEOUT))
246
247- o.conductor.args.on_timeout='skip'
248+ o.conductor.args.on_timeout = 'skip'
249 o.log.status.reset_mock()
250 o.print_status(124)
251 o.log.status.assert_called_with('%s (%s)' % (juju_test.TEST_FAIL,
252@@ -583,7 +586,7 @@
253 goyml_parsed = yaml.safe_load(goyml_output)
254 mstatus.return_value = goyml_parsed
255
256- juju_env='testing'
257+ juju_env = 'testing'
258 args = Arguments(tests='dummy', juju_env=juju_env, logdir='/tmp')
259 c = juju_test.Conductor(args)
260 c.juju_version = juju_test.JujuVersion(major=1, minor=10, patch=0)
261@@ -623,7 +626,7 @@
262 pyyml_parsed = yaml.safe_load(pyyml_output)
263 mstatus.return_value = pyyml_parsed
264
265- juju_env='testing'
266+ juju_env = 'testing'
267 args = Arguments(tests='dummy', juju_env=juju_env, logdir='/tmp')
268 c = juju_test.Conductor(args)
269 c.juju_version = juju_test.JujuVersion(major=0, minor=7, patch=0)

Subscribers

People subscribed via source and target branches

to all changes: