Index: bin/cros_au_test_harness.py |
diff --git a/bin/cros_au_test_harness.py b/bin/cros_au_test_harness.py |
index 4cb6653d17a65a800c7b57008ed926071a5eab95..0e484947ab26cf6204f36cbe5cced81c32b39c52 100755 |
--- a/bin/cros_au_test_harness.py |
+++ b/bin/cros_au_test_harness.py |
@@ -8,6 +8,8 @@ import optparse |
import os |
import re |
import sys |
+import thread |
+import time |
import unittest |
import urllib |
@@ -19,6 +21,8 @@ from cros_build_lib import RunCommand |
from cros_build_lib import RunCommandCaptureOutput |
from cros_build_lib import Warning |
+import cros_test_proxy |
+ |
# VM Constants. |
_FULL_VDISK_SIZE = 6072 |
_FULL_STATEFULFS_SIZE = 3074 |
@@ -90,20 +94,21 @@ class AUTest(object): |
else: |
self._UpdateImageReportError(image) |
- def _UpdateImageReportError(self, image_path, stateful_change='old'): |
+ def _UpdateImageReportError(self, image_path, stateful_change='old', |
+ proxy_port=None): |
"""Calls UpdateImage and reports any error to the console. |
Still throws the exception. |
""" |
try: |
- self.UpdateImage(image_path, stateful_change) |
+ self.UpdateImage(image_path, stateful_change, proxy_port) |
except UpdateException as err: |
# If the update fails, print it out |
Warning(err.stdout) |
raise |
def _AttemptUpdateWithPayloadExpectedFailure(self, payload, expected_msg): |
- # This update is expected to fail... |
+ """Attempt a payload update, expect it to fail with expected log""" |
try: |
self.UpdateUsingPayload(payload) |
except UpdateException as err: |
@@ -115,11 +120,32 @@ class AUTest(object): |
Warning(err.stdout) |
self.fail('We managed to update when failure was expected') |
+ def _AttemptUpdateWithFilter(self, filter): |
+ """Update through a proxy, with a specified filter, and expect success.""" |
+ |
+ self.PrepareBase(target_image_path) |
+ |
+ # The devserver runs at port 8080 by default. We assume that here, and |
+ # start our proxy at 8081. We then tell our update tools to have the |
+ # client connect to 8081 instead of 8080. |
+ proxy_port = 8081 |
+ proxy = cros_test_proxy.CrosTestProxy(port_in=proxy_port, |
+ address_out='127.0.0.1', |
+ port_out=8080, |
+ filter=filter) |
+ proxy.serve_forever_in_thread() |
+ |
+ # This update is expected to fail... |
+ try: |
+ self._UpdateImageReportError(target_image_path, proxy_port=proxy_port) |
+ finally: |
+ proxy.shutdown() |
+ |
def PrepareBase(self, image_path): |
"""Prepares target with base_image_path.""" |
pass |
- def UpdateImage(self, image_path, stateful_change='old'): |
+ def UpdateImage(self, image_path, stateful_change='old', proxy_port=None): |
"""Updates target with the image given by the image_path. |
Args: |
@@ -129,15 +155,22 @@ class AUTest(object): |
'old': Don't modify stateful partition. Just update normally. |
'clean': Uses clobber-state to wipe the stateful partition with the |
exception of code needed for ssh. |
+ proxy_port: Port to have the client connect to. For use with |
+ CrosTestProxy. |
""" |
pass |
- def UpdateUsingPayload(self, update_path, stateful_change='old'): |
+ def UpdateUsingPayload(self, |
+ update_path, |
+ stateful_change='old', |
+ proxy_port=None): |
"""Updates target with the pre-generated update stored in update_path |
Args: |
update_path: Path to the image to update with. This directory should |
- contain both update.gz, and stateful.image.gz |
+ contain both update.gz, and stateful.image.gz |
+ proxy_port: Port to have the client connect to. For use with |
+ CrosTestProxy. |
""" |
pass |
@@ -187,7 +220,7 @@ class AUTest(object): |
""" |
# Just make sure some tests pass on original image. Some old images |
# don't pass many tests. |
- self.PrepareBase(image_path=base_image_path) |
+ self.PrepareBase(base_image_path) |
# TODO(sosa): move to 100% once we start testing using the autotest paired |
# with the dev channel. |
percent_passed = self.VerifyImage(10) |
@@ -210,7 +243,7 @@ class AUTest(object): |
""" |
# Just make sure some tests pass on original image. Some old images |
# don't pass many tests. |
- self.PrepareBase(image_path=base_image_path) |
+ self.PrepareBase(base_image_path) |
# TODO(sosa): move to 100% once we start testing using the autotest paired |
# with the dev channel. |
percent_passed = self.VerifyImage(10) |
@@ -228,7 +261,7 @@ class AUTest(object): |
def testPartialUpdate(self): |
"""Tests what happens if we attempt to update with a truncated payload.""" |
# Preload with the version we are trying to test. |
- self.PrepareBase(image_path=target_image_path) |
+ self.PrepareBase(target_image_path) |
# Image can be updated at: |
# ~chrome-eng/chromeos/localmirror/autest-images |
@@ -245,7 +278,7 @@ class AUTest(object): |
def testCorruptedUpdate(self): |
"""Tests what happens if we attempt to update with a corrupted payload.""" |
# Preload with the version we are trying to test. |
- self.PrepareBase(image_path=target_image_path) |
+ self.PrepareBase(target_image_path) |
# Image can be updated at: |
# ~chrome-eng/chromeos/localmirror/autest-images |
@@ -260,6 +293,71 @@ class AUTest(object): |
expected_msg = 'zlib inflate() error:-3' |
self._AttemptUpdateWithPayloadExpectedFailure(payload, expected_msg) |
+ def testInterruptedUpdate(self): |
+ """Tests what happens if we interrupt payload delivery 3 times.""" |
+ |
+ class InterruptionFilter(cros_test_proxy.Filter): |
+ """This filter causes the proxy to interrupt the download 3 times |
+ |
+ It does this by closing the first three connections to transfer |
+ 2M total in the outbound connection after they transfer the |
+ 2M. |
+ """ |
+ def __init__(self): |
+ """Defines variable shared across all connections""" |
+ self.close_count = 0 |
+ |
+ def setup(self): |
+ """Called once at the start of each connection.""" |
+ self.data_size = 0 |
+ |
+ def OutBound(self, data): |
+ """Called once per packet for outgoing data. |
+ |
+ The first three connections transferring more than 2M |
+ outbound will be closed. |
+ """ |
+ if self.close_count < 3: |
+ if self.data_size > (2 * 1024 * 1024): |
+ self.close_count += 1 |
+ return None |
+ |
+ self.data_size += len(data) |
+ return data |
+ |
+ self._AttemptUpdateWithFilter(InterruptionFilter()) |
+ |
+ def testDelayedUpdate(self): |
+ """Tests what happens if some data is delayed during update delivery""" |
+ |
+ class DelayedFilter(cros_test_proxy.Filter): |
+ """Causes intermittent delays in data transmission. |
+ |
+ It does this by inserting 3 20 second delays when transmitting |
+ data after 2M has been sent. |
+ """ |
+ def setup(self): |
+ """Called once at the start of each connection.""" |
+ self.data_size = 0 |
+ self.delay_count = 0 |
+ |
+ def OutBound(self, data): |
+ """Called once per packet for outgoing data. |
+ |
+ The first three packets after we reach 2M transferred |
+ are delayed by 20 seconds. |
+ """ |
+ if self.delay_count < 3: |
+ if self.data_size > (2 * 1024 * 1024): |
+ self.delay_count += 1 |
+ time.sleep(20) |
+ |
+ self.data_size += len(data) |
+ return data |
+ |
+ |
+ self._AttemptUpdateWithFilter(DelayedFilter()) |
+ |
class RealAUTest(unittest.TestCase, AUTest): |
"""Test harness for updating real images.""" |
@@ -270,7 +368,7 @@ class RealAUTest(unittest.TestCase, AUTest): |
"""Auto-update to base image to prepare for test.""" |
self._UpdateImageReportError(image_path) |
- def UpdateImage(self, image_path, stateful_change='old'): |
+ def UpdateImage(self, image_path, stateful_change='old', proxy_port=None): |
"""Updates a remote image using image_to_live.sh.""" |
stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) |
cmd = ['%s/image_to_live.sh' % self.crosutils, |
@@ -281,6 +379,9 @@ class RealAUTest(unittest.TestCase, AUTest): |
'--src_image=%s' % self.source_image |
] |
+ if proxy_port: |
+ cmd.append('--proxy_port=%s' % proxy_port) |
+ |
if self.verbose: |
try: |
RunCommand(cmd) |
@@ -291,7 +392,10 @@ class RealAUTest(unittest.TestCase, AUTest): |
if code != 0: |
raise UpdateException(code, stdout) |
- def UpdateUsingPayload(self, update_path, stateful_change='old'): |
+ def UpdateUsingPayload(self, |
+ update_path, |
+ stateful_change='old', |
+ proxy_port=None): |
"""Updates a remote image using image_to_live.sh.""" |
stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) |
cmd = ['%s/image_to_live.sh' % self.crosutils, |
@@ -301,6 +405,9 @@ class RealAUTest(unittest.TestCase, AUTest): |
'--verify', |
] |
+ if proxy_port: |
+ cmd.append('--proxy_port=%s' % proxy_port) |
+ |
if self.verbose: |
try: |
RunCommand(cmd) |
@@ -366,7 +473,7 @@ class VirtualAUTest(unittest.TestCase, AUTest): |
self.assertTrue(os.path.exists(self.vm_image_path)) |
- def UpdateImage(self, image_path, stateful_change='old'): |
+ def UpdateImage(self, image_path, stateful_change='old', proxy_port=None): |
"""Updates VM image with image_path.""" |
stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) |
if self.source_image == base_image_path: |
@@ -382,6 +489,10 @@ class VirtualAUTest(unittest.TestCase, AUTest): |
stateful_change_flag, |
'--src_image=%s' % self.source_image, |
] |
+ |
+ if proxy_port: |
+ cmd.append('--proxy_port=%s' % proxy_port) |
+ |
if self.verbose: |
try: |
RunCommand(cmd) |
@@ -392,7 +503,10 @@ class VirtualAUTest(unittest.TestCase, AUTest): |
if code != 0: |
raise UpdateException(code, stdout) |
- def UpdateUsingPayload(self, update_path, stateful_change='old'): |
+ def UpdateUsingPayload(self, |
+ update_path, |
+ stateful_change='old', |
+ proxy_port=None): |
"""Updates a remote image using image_to_live.sh.""" |
stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) |
if self.source_image == base_image_path: |
@@ -409,6 +523,9 @@ class VirtualAUTest(unittest.TestCase, AUTest): |
'--src_image=%s' % self.source_image, |
] |
+ if proxy_port: |
+ cmd.append('--proxy_port=%s' % proxy_port) |
+ |
if self.verbose: |
try: |
RunCommand(cmd) |
@@ -502,6 +619,8 @@ if __name__ == '__main__': |
elif options.type == 'real': test_class = RealAUTest |
else: parser.error('Could not parse harness type %s.' % options.type) |
+ remote = options.remote |
+ |
test_suite = test_loader.loadTestsFromTestCase(test_class) |
test_result = unittest.TextTestRunner(verbosity=2).run(test_suite) |