| 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)
|
|
|
|
|