Merge lp:~joetalbott/utah/add_testcase_docs_to_result into lp:utah

Proposed by Joe Talbott
Status: Merged
Merged at revision: 697
Proposed branch: lp:~joetalbott/utah/add_testcase_docs_to_result
Merge into: lp:utah
Diff against target: 215 lines (+105/-5)
4 files modified
utah/client/exceptions.py (+7/-0)
utah/client/result.py (+5/-1)
utah/client/testcase.py (+42/-4)
utah/client/tests/test_testcase.py (+51/-0)
To merge this branch: bzr merge lp:~joetalbott/utah/add_testcase_docs_to_result
Reviewer Review Type Date Requested Status
Javier Collado (community) Approve
Joe Talbott (community) Needs Resubmitting
Review via email: mp+127521@code.launchpad.net

Description of the change

This adds the testcase documentation to the result packet.

The following fields are now required in tc_control
- dependencies
- action
- expected_results

To post a comment you must log in.
Revision history for this message
Javier Collado (javier.collado) wrote :

@Joe

After looking at the changes, I've got a couple of comments

- extra_info

The extra_info dictionary is created using the fields from self.{description,dependencies,action,expected_results}, but I don't see any of those fields having a default value of being explicitly assigned. I guess, that they're set in the following line:

self.__dict__.update(control_data)

If control_data is not None, those fields are going to be available because of the schema validation. However, when control_data is None that line isn't executed and I'm not sure about what happens then. Is it possible to call TestCase.run in such a case? If that might happen, then an exception will be raised when those variables aren't found.

By the way, self.__dict__ is an implementation detail and updating it directly isn't considered a good practice. I think that I'd prefer to use internally self.control_data[<field_name>] rather than self.<field_name> to avoid the confusion I had (and others might have as well in the future) about were all those instance variables are assigned. If you like to keep using the object dictionary directly, isn't a big deal, but a comment about that could be added to the code.

An alternative code to avoid using self.__dict__ direclty would be as follows:
for key, value in control_data.iteritems():
    setattr(self, key, value)

-static_analysis

Since you said you are interested in running static analysis in your code, I can tell you that there are two errors in testcase.py
- Line 210: W293 blank line contains whitespace
- Line 212: E303 too many blank lines (2)

review: Needs Information
690. By Joe Talbott

Ensure that the testcase documentation is provided * add testcases

Revision history for this message
Joe Talbott (joetalbott) wrote :

@Javier

I've updated the merge proposal to address your issues.

review: Needs Resubmitting
691. By Joe Talbott

debian/changelog - Fix conflicts for merge.

Revision history for this message
Javier Collado (javier.collado) wrote :

Looks good to me know. Thanks for the update.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'utah/client/exceptions.py'
2--- utah/client/exceptions.py 2012-08-28 11:22:41 +0000
3+++ utah/client/exceptions.py 2012-10-04 15:47:24 +0000
4@@ -40,3 +40,10 @@
5 """
6 Used to provide additional information when schema validation fails
7 """
8+
9+
10+class MissingData(UTAHClientError):
11+ """
12+ Raised when there is missing data that is required for an object
13+ to proceed.
14+ """
15
16=== modified file 'utah/client/result.py'
17--- utah/client/result.py 2012-09-06 15:44:36 +0000
18+++ utah/client/result.py 2012-10-04 15:47:24 +0000
19@@ -60,7 +60,7 @@
20 self.failures = 0
21 self.passes = 0
22
23- def add_result(self, result):
24+ def add_result(self, result, extra_info={}):
25 """
26 Add a result to the object.
27
28@@ -78,6 +78,10 @@
29 if result is None:
30 return
31
32+ # Add items from extra_info into the result
33+ if len(extra_info) > 0:
34+ result['extra_info'] = extra_info
35+
36 if self.testsuite is not None:
37 result['testsuite'] = self.testsuite
38
39
40=== modified file 'utah/client/testcase.py'
41--- utah/client/testcase.py 2012-09-27 13:55:41 +0000
42+++ utah/client/testcase.py 2012-10-04 15:47:24 +0000
43@@ -6,7 +6,7 @@
44 from common import run_cmd, parse_control_file
45 from common import do_nothing
46 from common import CMD_TC_BUILD, CMD_TC_SETUP, CMD_TC_TEST, CMD_TC_CLEANUP
47-from exceptions import MissingFile, ValidationError
48+from exceptions import MissingFile, ValidationError, MissingData
49
50
51 class TestCase(object):
52@@ -32,6 +32,18 @@
53 'type': 'string',
54 'required': True,
55 },
56+ 'dependencies': {
57+ 'type': 'string',
58+ 'required': True,
59+ },
60+ 'action': {
61+ 'type': 'string',
62+ 'required': True,
63+ },
64+ 'expected_results': {
65+ 'type': 'string',
66+ 'required': True,
67+ },
68 'type': {
69 'type': 'string',
70 'enum': ['userland']
71@@ -70,6 +82,13 @@
72 self.reboot_callback = _reboot_callback
73 self.run_as = None
74
75+ # The following are testcase documentation and should all be provided
76+ # in a tc_control file or via the state file.
77+ self.description = None
78+ self.dependencies = None
79+ self.action = None
80+ self.expected_results = None
81+
82 if _save_state_callback is not None:
83 self.save_state_callback = _save_state_callback
84
85@@ -94,7 +113,8 @@
86 if self.timeout is not None:
87 control_data['timeout'] = self.timeout
88
89- self.__dict__.update(control_data)
90+ for key, value in control_data.iteritems():
91+ setattr(self, key, value)
92
93 # Set any values passed into __init__() as final values
94 if timeout is None and self.timeout is None:
95@@ -150,6 +170,13 @@
96 self.run_status = 'PASS'
97 return self.run_status
98
99+ # Do not proceed if we are missing data
100+ if (self.description is None
101+ or self.dependencies is None
102+ or self.action is None
103+ or self.expected_results is None):
104+ raise MissingData
105+
106 # Return value to indicate whether processing of a TestSuite should
107 # continue. This is to avoid a shutdown race on reboot cases.
108 keep_going = True
109@@ -182,12 +209,19 @@
110 timeout = self.timeout or 0
111 self.status = "RUN"
112 self.save_state_callback()
113+ extra_info = {
114+ 'description': self.description,
115+ 'dependencies': self.dependencies,
116+ 'action': self.action,
117+ 'expected_results': self.expected_results,
118+ }
119 result.add_result(run_cmd(self.command,
120 timeout=timeout,
121 cwd=self.working_dir,
122 cmd_type=CMD_TC_TEST,
123 run_as=self.run_as,
124- ))
125+ ),
126+ extra_info=extra_info)
127
128 # Clean up whether 'command' failed or not.
129 self.cleanup(result)
130@@ -224,7 +258,8 @@
131
132 Requires that 'state' has the same fieldnames as the TestCase class.
133 """
134- self.__dict__.update(state)
135+ for key, value in state.iteritems():
136+ setattr(self, key, value)
137
138 def save_state(self):
139 """
140@@ -241,6 +276,9 @@
141 'tc_cleanup': self.tc_cleanup,
142 'type': self.type,
143 'description': self.description,
144+ 'dependencies': self.dependencies,
145+ 'action': self.action,
146+ 'expected_results': self.expected_results,
147 }
148
149 return state
150
151=== modified file 'utah/client/tests/test_testcase.py'
152--- utah/client/tests/test_testcase.py 2012-08-08 16:17:12 +0000
153+++ utah/client/tests/test_testcase.py 2012-10-04 15:47:24 +0000
154@@ -1,6 +1,7 @@
155 import os
156 import unittest
157
158+from utah.client.exceptions import MissingData
159 from utah.client.result import ResultYAML
160 from utah.client.common import (
161 UTAH_DIR,
162@@ -90,3 +91,53 @@
163 )
164
165 self.assertEqual(timeout, case.timeout)
166+
167+ def test_control_data(self):
168+ """
169+ Test that a correctly formed control_data that's passed in
170+ doesn't raise an exception.
171+ """
172+ control_data = {
173+ 'description': 'a test case',
174+ 'command': '/bin/true',
175+ 'dependencies': 'n/a',
176+ 'action': 'n/a',
177+ 'expected_results': 'n/a',
178+ }
179+
180+ result = ResultYAML()
181+
182+ case = testcase.TestCase(
183+ name=self.name,
184+ path=self.path,
185+ result=result,
186+ _control_data=control_data,
187+ )
188+
189+ case.run()
190+ print result
191+ result.result()
192+
193+ self.assertTrue(case.is_done())
194+
195+ def test_bad_control_data(self):
196+ """
197+ Test that an incorrectly formed control_data that's passed in
198+ does raise an exception.
199+ """
200+ control_data = {
201+ 'description': 'a test case',
202+ 'command': '/bin/true',
203+ }
204+
205+ result = ResultYAML()
206+
207+ case = testcase.TestCase(
208+ name=self.name,
209+ path=self.path,
210+ result=result,
211+ _control_data=control_data,
212+ )
213+
214+ with self.assertRaises(MissingData):
215+ case.run()

Subscribers

People subscribed via source and target branches