Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(366)

Side by Side Diff: bin/au_test_harness/au_test.py

Issue 6597122: Refactor au_test_harness into modules and refactor to use worker design. (Closed) Base URL: http://git.chromium.org/git/crosutils.git@master
Patch Set: 80 char Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Module containing a test suite that is run to test auto updates."""
6
7 import os
8 import time
9 import unittest
10
11 import cros_build_lib as cros_lib
12
13 import cros_test_proxy
14 import dummy_au_worker
15 import real_au_worker
16 import vm_au_worker
17
18
19 class AUTest(unittest.TestCase):
20 """Test harness that uses an au_worker to perform and validate updates.
21
22 Defines a test suite that is run using an au_worker. An au_worker can
23 be created to perform and validates updates on both virtual and real devices.
24 See documentation for au_worker for more information.
25 """
26 @classmethod
dgarrett 2011/03/03 02:17:21 Why use the classmethod decorator only for this me
sosa 2011/03/03 03:11:41 Done to set class options i.e. static method since
27 def ProcessOptions(cls, options, use_dummy_worker):
28 """Processes options for the test suite and sets up the worker class.
29
30 Args:
31 options: options class to be parsed from main class.
32 use_dummy_worker: If True, use a dummy_worker_class rather than deriving
33 one from options.type.
34 """
35 cls.base_image_path = options.base_image
36 cls.target_image_path = options.target_image
37 cls.clean = options.clean
38
39 assert options.type in ['real', 'vm'], 'Failed to specify either real|vm.'
40 if use_dummy_worker:
41 cls.worker_class = dummy_au_worker.DummyAUWorker
42 elif options.type == 'vm':
43 cls.worker_class = vm_au_worker.VMAUWorker
44 else:
45 cls.worker_class = real_au_worker.RealAUWorker
46
47 # Sanity checks.
48 if not cls.base_image_path:
49 cros_lib.Die('Need path to base image for vm.')
50 elif not os.path.exists(cls.base_image_path):
51 cros_lib.Die('%s does not exist' % cls.base_image_path)
52
53 if not cls.target_image_path:
54 cros_lib.Die('Need path to target image to update with.')
55 elif not os.path.exists(cls.target_image_path):
56 cros_lib.Die('%s does not exist' % cls.target_image_path)
57
58 # Cache away options to instantiate workers later.
59 cls.options = options
60
61 def AttemptUpdateWithPayloadExpectedFailure(self, payload, expected_msg):
62 """Attempt a payload update, expect it to fail with expected log"""
63 try:
64 self.worker.UpdateUsingPayload(payload)
65 except UpdateException as err:
66 # Will raise ValueError if expected is not found.
67 if re.search(re.escape(expected_msg), err.stdout, re.MULTILINE):
68 return
69 else:
70 cros_lib.Warning("Didn't find '%s' in:" % expected_msg)
71 cros_lib.Warning(err.stdout)
72
73 self.fail('We managed to update when failure was expected')
74
75 def AttemptUpdateWithFilter(self, filter, proxy_port=8081):
76 """Update through a proxy, with a specified filter, and expect success."""
77 self.worker.PrepareBase(self.target_image_path)
78
79 # The devserver runs at port 8080 by default. We assume that here, and
80 # start our proxy at a different. We then tell our update tools to
dgarrett 2011/03/03 02:17:21 s/different/different one/ (Yeah, I know it's my
sosa 2011/03/03 03:11:41 Done.
81 # have the client connect to our proxy_port instead of 8080.
82 proxy = cros_test_proxy.CrosTestProxy(port_in=proxy_port,
83 address_out='127.0.0.1',
84 port_out=8080,
85 filter=filter)
86 proxy.serve_forever_in_thread()
87
88 # This update is expected to fail...
dgarrett 2011/03/03 02:17:21 Either this comment or the docstring is wrong. (A
sosa 2011/03/03 03:11:41 heh .. comment is wrong :) Done. On 2011/03/03 02
89 try:
90 self.worker.PerformUpdate(self.target_image_path, self.target_image_path,
91 proxy_port=proxy_port)
92 finally:
93 proxy.shutdown()
94
95 # --- UNITTEST SPECIFIC METHODS ---
96
97 def setUp(self):
98 """Overrides unittest.TestCase.setUp and called before every test.
99
100 Sets instance specific variables and initializes worker.
101 """
102 unittest.TestCase.setUp(self)
103 self.worker = self.worker_class(self.options)
104 self.crosutils = os.path.join(os.path.dirname(__file__), '..', '..')
105 self.download_folder = os.path.join(self.crosutils, 'latest_download')
106 if not os.path.exists(self.download_folder):
107 os.makedirs(self.download_folder)
108
109 def testUpdateKeepStateful(self):
110 """Tests if we can update normally.
111
112 This test checks that we can update by updating the stateful partition
113 rather than wiping it.
114 """
115 # Just make sure some tests pass on original image. Some old images
116 # don't pass many tests.
117 self.worker.PrepareBase(self.base_image_path)
118 # TODO(sosa): move to 100% once we start testing using the autotest paired
119 # with the dev channel.
120 percent_passed = self.worker.VerifyImage(self, 10)
121
122 # Update to - all tests should pass on new image.
123 self.worker.PerformUpdate(self.target_image_path, self.base_image_path)
124 percent_passed = self.worker.VerifyImage(self)
125
126 # Update from - same percentage should pass that originally passed.
127 self.worker.PerformUpdate(self.base_image_path, self.target_image_path)
128 self.worker.VerifyImage(self, percent_passed)
129
130 def testUpdateWipeStateful(self):
131 """Tests if we can update after cleaning the stateful partition.
132
133 This test checks that we can update successfully after wiping the
134 stateful partition.
135 """
136 # Just make sure some tests pass on original image. Some old images
137 # don't pass many tests.
138 self.worker.PrepareBase(self.base_image_path)
139 percent_passed = self.worker.VerifyImage(self, 10)
140
141 # Update to - all tests should pass on new image.
142 self.worker.PerformUpdate(self.target_image_path, self.base_image_path,
143 'clean')
144 self.worker.VerifyImage(self)
145
146 # Update from - same percentage should pass that originally passed.
147 self.worker.PerformUpdate(self.base_image_path, self.target_image_path,
148 'clean')
149 self.worker.VerifyImage(self, percent_passed)
150
151 def testInterruptedUpdate(self):
152 """Tests what happens if we interrupt payload delivery 3 times."""
153
154 class InterruptionFilter(cros_test_proxy.Filter):
155 """This filter causes the proxy to interrupt the download 3 times
156
157 It does this by closing the first three connections to transfer
158 2M total in the outbound connection after they transfer the
159 2M.
160 """
161 def __init__(self):
162 """Defines variable shared across all connections"""
163 self.close_count = 0
164
165 def setup(self):
166 """Called once at the start of each connection."""
167 self.data_size = 0
168
169 def OutBound(self, data):
170 """Called once per packet for outgoing data.
171
172 The first three connections transferring more than 2M
173 outbound will be closed.
174 """
175 if self.close_count < 3:
176 if self.data_size > (2 * 1024 * 1024):
177 self.close_count += 1
178 return None
179
180 self.data_size += len(data)
181 return data
182
183 self.AttemptUpdateWithFilter(InterruptionFilter(), proxy_port=8082)
184
185 def testDelayedUpdate(self):
186 """Tests what happens if some data is delayed during update delivery"""
187
188 class DelayedFilter(cros_test_proxy.Filter):
189 """Causes intermittent delays in data transmission.
190
191 It does this by inserting 3 20 second delays when transmitting
192 data after 2M has been sent.
193 """
194 def setup(self):
195 """Called once at the start of each connection."""
196 self.data_size = 0
197 self.delay_count = 0
198
199 def OutBound(self, data):
200 """Called once per packet for outgoing data.
201
202 The first three packets after we reach 2M transferred
203 are delayed by 20 seconds.
204 """
205 if self.delay_count < 3:
206 if self.data_size > (2 * 1024 * 1024):
207 self.delay_count += 1
208 time.sleep(20)
209
210 self.data_size += len(data)
211 return data
212
213 self.AttemptUpdateWithFilter(DelayedFilter(), proxy_port=8083)
214
215 def SimpleTest(self):
216 """A simple update that updates once from a base image to a target.
217
218 We explicitly don't use test prefix so that isn't run by default. Can be
219 run using test_prefix option.
220 """
221 self.worker.PrepareBase(self.base_image_path)
222 self.worker.PerformUpdate(self.target_image_path, self.base_image_path)
223 self.worker.VerifyImage(self)
224
225 # --- DISABLED TESTS ---
226
227 # TODO(sosa): Get test to work with verbose.
228 def NotestPartialUpdate(self):
229 """Tests what happens if we attempt to update with a truncated payload."""
230 # Preload with the version we are trying to test.
231 self.worker.PrepareBase(self.target_image_path)
232
233 # Image can be updated at:
234 # ~chrome-eng/chromeos/localmirror/autest-images
235 url = 'http://gsdview.appspot.com/chromeos-localmirror/' \
236 'autest-images/truncated_image.gz'
237 payload = os.path.join(self.download_folder, 'truncated_image.gz')
238
239 # Read from the URL and write to the local file
240 urllib.urlretrieve(url, payload)
241
242 expected_msg = 'download_hash_data == update_check_response_hash failed'
243 self.AttemptUpdateWithPayloadExpectedFailure(payload, expected_msg)
244
245 # TODO(sosa): Get test to work with verbose.
246 def NotestCorruptedUpdate(self):
247 """Tests what happens if we attempt to update with a corrupted payload."""
248 # Preload with the version we are trying to test.
249 self.worker.PrepareBase(self.target_image_path)
250
251 # Image can be updated at:
252 # ~chrome-eng/chromeos/localmirror/autest-images
253 url = 'http://gsdview.appspot.com/chromeos-localmirror/' \
254 'autest-images/corrupted_image.gz'
255 payload = os.path.join(self.download_folder, 'corrupted.gz')
256
257 # Read from the URL and write to the local file
258 urllib.urlretrieve(url, payload)
259
260 # This update is expected to fail...
261 expected_msg = 'zlib inflate() error:-3'
262 self.AttemptUpdateWithPayloadExpectedFailure(payload, expected_msg)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698