Index: bin/cros_au_test_harness.py |
diff --git a/bin/cros_au_test_harness.py b/bin/cros_au_test_harness.py |
index a495f06f9bd21230ec05da69933b70ad716b9c9c..799a5240c878f8d314df4ffbc1f74cb9924b4adb 100755 |
--- a/bin/cros_au_test_harness.py |
+++ b/bin/cros_au_test_harness.py |
@@ -10,7 +10,12 @@ import sys |
import unittest |
sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) |
-from cros_build_lib import RunCommand, Info, Warning, ReinterpretPathForChroot |
+from cros_build_lib import Die |
+from cros_build_lib import Info |
+from cros_build_lib import ReinterpretPathForChroot |
+from cros_build_lib import RunCommand |
+from cros_build_lib import Warning |
+ |
_KVM_PID_FILE = '/tmp/harness_pid' |
_FULL_VDISK_SIZE = 6072 |
@@ -21,6 +26,8 @@ global base_image_path |
global board |
global remote |
global target_image_path |
+global vm_graphics_flag |
+ |
_VERIFY_SUITE = 'suite_Smoke' |
@@ -41,6 +48,20 @@ class AUTest(object): |
return stateful_change_flag |
+ def ParseGenerateTestReportOutput(self, output): |
+ """Returns the percentage of tests that passed based on output.""" |
+ percent_passed = 0 |
+ lines = output.split('\n') |
+ |
+ for line in lines: |
+ if line.startswith("Total PASS:"): |
+ # FORMAT: ^TOTAL PASS: num_passed/num_total (percent%)$ |
+ percent_passed = line.split()[3].strip('()%') |
+ Info('Percent of tests passed %s' % percent_passed) |
+ break |
+ |
+ return int(percent_passed) |
+ |
def PrepareBase(self): |
"""Prepares target with base_image_path.""" |
pass |
@@ -58,51 +79,90 @@ class AUTest(object): |
""" |
pass |
- def VerifyImage(self): |
- """Verifies the image is correct.""" |
+ def VerifyImage(self, percent_required_to_pass): |
+ """Verifies the image with tests. |
+ |
+ Verifies that the test images passes the percent required. |
+ |
+ Args: |
+ percent_required_to_pass: percentage required to pass. This should be |
+ fall between 0-100. |
+ |
+ Returns: |
+ Returns the percent that passed. |
+ """ |
pass |
+ def CommonVerifyImage(self, unittest, output, percent_required_to_pass): |
+ """Helper function for VerifyImage that returns percent of tests passed. |
+ |
+ Takes output from a test suite, verifies the number of tests passed is |
+ sufficient and outputs info. |
+ |
+ Args: |
+ unittest: Handle to the unittest. |
+ output: stdout from a test run. |
+ percent_required_to_pass: percentage required to pass. This should be |
+ fall between 0-100. |
+ Returns: |
+ percent that passed. |
+ """ |
+ Info('Output from VerifyImage():') |
+ print output |
+ percent_passed = self.ParseGenerateTestReportOutput(output) |
+ Info('Percent passed: %d vs. Percent required: %d' % ( |
+ percent_passed, percent_required_to_pass)) |
+ unittest.assertTrue(percent_passed >= |
+ percent_required_to_pass) |
+ return percent_passed |
+ |
def testFullUpdateKeepStateful(self): |
"""Tests if we can update normally. |
This test checks that we can update by updating the stateful partition |
rather than wiping it. |
""" |
- # Prepare and verify the base image has been prepared correctly. |
+ # Just make sure some tests pass on original image. Some old images |
+ # don't pass many tests. |
self.PrepareBase() |
- self.VerifyImage() |
+ # TODO(sosa): move to 100% once we start testing using the autotest paired |
+ # with the dev channel. |
+ percent_passed = self.VerifyImage(42) |
- # Update to. |
+ # Update to - all tests should pass on new image. |
Info('Updating from base image on vm to target image.') |
self.UpdateImage(target_image_path) |
- self.VerifyImage() |
+ self.VerifyImage(100) |
- # Update from. |
+ # Update from - same percentage should pass that originally passed. |
Info('Updating from updated image on vm back to base image.') |
self.UpdateImage(base_image_path) |
- self.VerifyImage() |
+ self.VerifyImage(percent_passed) |
# TODO(sosa): Re-enable once we have a good way of checking for version |
- # compatability. |
+ # compatibility. |
def NotestFullUpdateWipeStateful(self): |
"""Tests if we can update after cleaning the stateful partition. |
This test checks that we can update successfully after wiping the |
stateful partition. |
""" |
- # Prepare and verify the base image has been prepared correctly. |
+ # Just make sure some tests pass on original image. Some old images |
+ # don't pass many tests. |
self.PrepareBase() |
- self.VerifyImage() |
+ # TODO(sosa): move to 100% once we start testing using the autotest paired |
+ # with the dev channel. |
+ percent_passed = self.VerifyImage(42) |
- # Update to. |
+ # Update to - all tests should pass on new image. |
Info('Updating from base image on vm to target image and wiping stateful.') |
self.UpdateImage(target_image_path, 'clean') |
- self.VerifyImage() |
+ self.VerifyImage(100) |
- # Update from. |
+ # Update from - same percentage should pass that originally passed. |
Info('Updating from updated image back to base image and wiping stateful.') |
self.UpdateImage(base_image_path, 'clean') |
- self.VerifyImage() |
+ self.VerifyImage(percent_passed) |
class RealAUTest(unittest.TestCase, AUTest): |
@@ -111,6 +171,10 @@ class RealAUTest(unittest.TestCase, AUTest): |
def setUp(self): |
AUTest.setUp(self) |
+ def PrepareBase(self): |
+ """Auto-update to base image to prepare for test.""" |
+ self.UpdateImage(base_image_path) |
+ |
def UpdateImage(self, image_path, stateful_change='old'): |
"""Updates a remote image using image_to_live.sh.""" |
stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) |
@@ -124,13 +188,14 @@ class RealAUTest(unittest.TestCase, AUTest): |
], enter_chroot=False) |
- def VerifyImage(self): |
+ def VerifyImage(self, percent_required_to_pass): |
"""Verifies an image using run_remote_tests.sh with verification suite.""" |
- RunCommand([ |
+ output = RunCommand([ |
'%s/run_remote_tests.sh' % self.crosutils, |
'--remote=%s' % remote, |
_VERIFY_SUITE, |
- ], error_ok=False, enter_chroot=False) |
+ ], error_ok=True, enter_chroot=False, redirect_stdout=True) |
+ return self.CommonVerifyImage(self, output, percent_required_to_pass) |
class VirtualAUTest(unittest.TestCase, AUTest): |
@@ -181,22 +246,26 @@ class VirtualAUTest(unittest.TestCase, AUTest): |
'--update_image_path=%s' % image_path, |
'--vm_image_path=%s' % self.vm_image_path, |
'--snapshot', |
+ vm_graphics_flag, |
'--persist', |
'--kvm_pid=%s' % _KVM_PID_FILE, |
stateful_change_flag, |
], enter_chroot=False) |
- def VerifyImage(self): |
+ def VerifyImage(self, percent_required_to_pass): |
"""Runs vm smoke suite to verify image.""" |
# image_to_live already verifies lsb-release matching. This is just |
# for additional steps. |
- RunCommand(['%s/cros_run_vm_test' % self.crosutilsbin, |
- '--image_path=%s' % self.vm_image_path, |
- '--snapshot', |
- '--persist', |
- '--kvm_pid=%s' % _KVM_PID_FILE, |
- '--test_case=%s' % _VERIFY_SUITE, |
- ], error_ok=False, enter_chroot=False) |
+ output = RunCommand(['%s/cros_run_vm_test' % self.crosutilsbin, |
+ '--image_path=%s' % self.vm_image_path, |
+ '--snapshot', |
+ '--persist', |
+ vm_graphics_flag, |
+ '--kvm_pid=%s' % _KVM_PID_FILE, |
+ '--test_case=%s' % _VERIFY_SUITE, |
+ ], error_ok=True, enter_chroot=False, |
+ redirect_stdout=True) |
+ return self.CommonVerifyImage(self, output, percent_required_to_pass) |
if __name__ == '__main__': |
@@ -211,6 +280,8 @@ if __name__ == '__main__': |
help='type of test to run: [vm, real]. Default: vm.') |
parser.add_option('-m', '--remote', |
help='Remote address for real test.') |
+ parser.add_option('--no_graphics', action='store_true', |
+ help='Disable graphics for the vm test.') |
# Set the usage to include flags. |
parser.set_usage(parser.format_help()) |
# Parse existing sys.argv so we can pass rest to unittest.main. |
@@ -222,19 +293,24 @@ if __name__ == '__main__': |
if not base_image_path: |
parser.error('Need path to base image for vm.') |
+ elif not os.path.exists(base_image_path): |
+ Die('%s does not exist' % base_image_path) |
if not target_image_path: |
parser.error('Need path to target image to update with.') |
+ elif not os.path.exists(target_image_path): |
+ Die('%s does not exist' % target_image_path) |
if not board: |
parser.error('Need board to convert base image to vm.') |
- return_code = 0 |
+ vm_graphics_flag = '' |
+ if options.no_graphics: vm_graphics_flag = '--no_graphics' |
# Only run the test harness we care about. |
if options.type == 'vm': |
suite = unittest.TestLoader().loadTestsFromTestCase(VirtualAUTest) |
- return_code = unittest.TextTestRunner(verbosity=2).run(suite) |
+ test_result = unittest.TextTestRunner(verbosity=2).run(suite) |
elif options.type == 'real': |
if not options.remote: |
parser.error('Real tests require a remote test machine.') |
@@ -242,8 +318,9 @@ if __name__ == '__main__': |
remote = options.remote |
suite = unittest.TestLoader().loadTestsFromTestCase(RealAUTest) |
- return_code = unittest.TextTestRunner(verbosity=2).run(suite) |
+ test_result = unittest.TextTestRunner(verbosity=2).run(suite) |
else: |
parser.error('Could not parse harness type %s.' % options.type) |
- sys.exit(return_code) |
+ if not test_result.wasSuccessful(): |
+ Die('Test harness was not successful') |