Add ability to pass a base test root and create results dirs relative.

Sets up dir paths as follows:

test_root/test_name i.e. SimpleUpdate/<num>_stage

For example:

test_root/testNormalUpdateKeepStateful/2_verify/...test_results...
corresponds to the second invocation of verify image.  The test
results for this stage are stored here.

This is a predecessor for storing arbitrary logs e.g. update into
this test results root.

Change-Id: I7183420b5dcb0d6971aa508a338c048c3557e359

BUG=chromium-os:12211
TEST=With simple a full vm test suite w/ w/out explicit test root
set.

Review URL: http://codereview.chromium.org/6614029
This commit is contained in:
Chris Sosa 2011-03-04 15:06:01 -08:00
parent 47974d4216
commit b54301bb9a
7 changed files with 62 additions and 11 deletions

View File

@ -5,6 +5,7 @@
"""Module containing a test suite that is run to test auto updates.""" """Module containing a test suite that is run to test auto updates."""
import os import os
import tempfile
import time import time
import unittest import unittest
@ -23,6 +24,8 @@ class AUTest(unittest.TestCase):
be created to perform and validates updates on both virtual and real devices. be created to perform and validates updates on both virtual and real devices.
See documentation for au_worker for more information. See documentation for au_worker for more information.
""" """
test_results_root = None
@classmethod @classmethod
def ProcessOptions(cls, options, use_dummy_worker): def ProcessOptions(cls, options, use_dummy_worker):
"""Processes options for the test suite and sets up the worker class. """Processes options for the test suite and sets up the worker class.
@ -55,6 +58,15 @@ class AUTest(unittest.TestCase):
elif not os.path.exists(cls.target_image_path): elif not os.path.exists(cls.target_image_path):
cros_lib.Die('%s does not exist' % cls.target_image_path) cros_lib.Die('%s does not exist' % cls.target_image_path)
# Initialize test root.
if not cls.test_results_root:
if options.test_results_root:
cls.test_results_root = options.test_results_root
else:
cls.test_results_root = tempfile.mkdtemp(prefix='au_test_harness')
cros_lib.Info('Using %s as the test results root' % cls.test_results_root)
# Cache away options to instantiate workers later. # Cache away options to instantiate workers later.
cls.options = options cls.options = options
@ -98,7 +110,7 @@ class AUTest(unittest.TestCase):
Sets instance specific variables and initializes worker. Sets instance specific variables and initializes worker.
""" """
unittest.TestCase.setUp(self) unittest.TestCase.setUp(self)
self.worker = self.worker_class(self.options) self.worker = self.worker_class(self.options, AUTest.test_results_root)
self.crosutils = os.path.join(os.path.dirname(__file__), '..', '..') self.crosutils = os.path.join(os.path.dirname(__file__), '..', '..')
self.download_folder = os.path.join(self.crosutils, 'latest_download') self.download_folder = os.path.join(self.crosutils, 'latest_download')
if not os.path.exists(self.download_folder): if not os.path.exists(self.download_folder):
@ -114,6 +126,7 @@ class AUTest(unittest.TestCase):
This test checks that we can update by updating the stateful partition This test checks that we can update by updating the stateful partition
rather than wiping it. rather than wiping it.
""" """
self.worker.InitializeResultsDirectory()
# Just make sure some tests pass on original image. Some old images # Just make sure some tests pass on original image. Some old images
# don't pass many tests. # don't pass many tests.
self.worker.PrepareBase(self.base_image_path) self.worker.PrepareBase(self.base_image_path)
@ -135,6 +148,7 @@ class AUTest(unittest.TestCase):
This test checks that we can update successfully after wiping the This test checks that we can update successfully after wiping the
stateful partition. stateful partition.
""" """
self.worker.InitializeResultsDirectory()
# Just make sure some tests pass on original image. Some old images # Just make sure some tests pass on original image. Some old images
# don't pass many tests. # don't pass many tests.
self.worker.PrepareBase(self.base_image_path) self.worker.PrepareBase(self.base_image_path)
@ -182,6 +196,7 @@ class AUTest(unittest.TestCase):
self.data_size += len(data) self.data_size += len(data)
return data return data
self.worker.InitializeResultsDirectory()
self.AttemptUpdateWithFilter(InterruptionFilter(), proxy_port=8082) self.AttemptUpdateWithFilter(InterruptionFilter(), proxy_port=8082)
def testDelayedUpdate(self): def testDelayedUpdate(self):
@ -212,6 +227,7 @@ class AUTest(unittest.TestCase):
self.data_size += len(data) self.data_size += len(data)
return data return data
self.worker.InitializeResultsDirectory()
self.AttemptUpdateWithFilter(DelayedFilter(), proxy_port=8083) self.AttemptUpdateWithFilter(DelayedFilter(), proxy_port=8083)
def SimpleTest(self): def SimpleTest(self):
@ -220,8 +236,9 @@ class AUTest(unittest.TestCase):
We explicitly don't use test prefix so that isn't run by default. Can be We explicitly don't use test prefix so that isn't run by default. Can be
run using test_prefix option. run using test_prefix option.
""" """
self.worker.InitializeResultsDirectory()
self.worker.PrepareBase(self.base_image_path) self.worker.PrepareBase(self.base_image_path)
self.worker.PerformUpdate(self.target_image_path, self.base_image_path) #self.worker.PerformUpdate(self.target_image_path, self.base_image_path)
self.worker.VerifyImage(self) self.worker.VerifyImage(self)
# --- DISABLED TESTS --- # --- DISABLED TESTS ---
@ -229,6 +246,7 @@ class AUTest(unittest.TestCase):
# TODO(sosa): Get test to work with verbose. # TODO(sosa): Get test to work with verbose.
def NotestPartialUpdate(self): def NotestPartialUpdate(self):
"""Tests what happens if we attempt to update with a truncated payload.""" """Tests what happens if we attempt to update with a truncated payload."""
self.worker.InitializeResultsDirectory()
# Preload with the version we are trying to test. # Preload with the version we are trying to test.
self.worker.PrepareBase(self.target_image_path) self.worker.PrepareBase(self.target_image_path)
@ -247,6 +265,7 @@ class AUTest(unittest.TestCase):
# TODO(sosa): Get test to work with verbose. # TODO(sosa): Get test to work with verbose.
def NotestCorruptedUpdate(self): def NotestCorruptedUpdate(self):
"""Tests what happens if we attempt to update with a corrupted payload.""" """Tests what happens if we attempt to update with a corrupted payload."""
self.worker.InitializeResultsDirectory()
# Preload with the version we are trying to test. # Preload with the version we are trying to test.
self.worker.PrepareBase(self.target_image_path) self.worker.PrepareBase(self.target_image_path)

View File

@ -9,6 +9,8 @@ and validating updates on a target. This should be subclassed to handle
various types of target. Types of targets include VM's, real devices, etc. various types of target. Types of targets include VM's, real devices, etc.
""" """
import inspect
import threading
import os import os
import sys import sys
@ -20,15 +22,16 @@ import update_exception
class AUWorker(object): class AUWorker(object):
"""Interface for a worker that updates and verifies images.""" """Interface for a worker that updates and verifies images."""
# Mapping between cached payloads to directory locations.
update_cache = None update_cache = None
# --- INTERFACE --- # --- INTERFACE ---
def __init__(self, options): def __init__(self, options, test_results_root):
"""Processes options for the specific-type of worker.""" """Processes options for the specific-type of worker."""
self.board = options.board self.board = options.board
self.private_key = options.private_key self.private_key = options.private_key
self.test_results_root = test_results_root
self.use_delta_updates = options.delta self.use_delta_updates = options.delta
self.verbose = options.verbose self.verbose = options.verbose
self.vm_image_path = None self.vm_image_path = None
@ -225,6 +228,23 @@ class AUWorker(object):
unittest.assertTrue(percent_passed >= percent_required_to_pass) unittest.assertTrue(percent_passed >= percent_required_to_pass)
return percent_passed return percent_passed
def InitializeResultsDirectory(self):
"""Called by a test to initialize a results directory for this worker."""
# Use the name of the test.
test_name = inspect.stack()[1][3]
self.results_directory = os.path.join(self.test_results_root, test_name)
self.results_count = 0
def GetNextResultsPath(self, label):
"""Returns a new results path based for this label.
Prefixes directory returned for worker with time called i.e. 1_label,
2_label, etc.
"""
self.results_count += 1
return os.path.join(self.results_directory, '%s_%s' % (self.results_count,
label))
# --- PRIVATE HELPER FUNCTIONS --- # --- PRIVATE HELPER FUNCTIONS ---
def _ParseGenerateTestReportOutput(self, output): def _ParseGenerateTestReportOutput(self, output):

View File

@ -237,6 +237,9 @@ def main():
help='Remote address for real test.') help='Remote address for real test.')
parser.add_option('-t', '--target_image', parser.add_option('-t', '--target_image',
help='path to the target image.') help='path to the target image.')
parser.add_option('--test_results_root', default=None,
help='Root directory to store test results. Should '
'be defined relative to chroot root.')
parser.add_option('--test_prefix', default='test', parser.add_option('--test_prefix', default='test',
help='Only runs tests with specific prefix i.e. ' help='Only runs tests with specific prefix i.e. '
'testFullUpdateWipeStateful.') 'testFullUpdateWipeStateful.')

View File

@ -18,8 +18,8 @@ class DummyAUWorker(au_worker.AUWorker):
# Class variable that stores the list of payloads that would be needed. # Class variable that stores the list of payloads that would be needed.
delta_list = {} delta_list = {}
def __init__(self, options): def __init__(self, options, test_results_root):
au_worker.AUWorker.__init__(self, options) au_worker.AUWorker.__init__(self, options, test_results_root)
self.au_type = options.type self.au_type = options.type
def PrepareBase(self, image_path): def PrepareBase(self, image_path):

View File

@ -13,9 +13,9 @@ import au_worker
class RealAUWorker(au_worker.AUWorker): class RealAUWorker(au_worker.AUWorker):
"""Test harness for updating real images.""" """Test harness for updating real images."""
def __init__(self, options): def __init__(self, options, test_results_root):
"""Processes non-vm-specific options.""" """Processes non-vm-specific options."""
au_worker.AUWorker.__init__(self, options) au_worker.AUWorker.__init__(self, options, test_results_root)
self.remote = options.remote self.remote = options.remote
if not self.remote: cros_lib.Die('We require a remote address for tests.') if not self.remote: cros_lib.Die('We require a remote address for tests.')
@ -51,9 +51,11 @@ class RealAUWorker(au_worker.AUWorker):
def VerifyImage(self, unittest, percent_required_to_pass=100): def VerifyImage(self, unittest, percent_required_to_pass=100):
"""Verifies an image using run_remote_tests.sh with verification suite.""" """Verifies an image using run_remote_tests.sh with verification suite."""
test_directory = self.GetNextResultsPath('verify')
output = cros_lib.RunCommand( output = cros_lib.RunCommand(
['%s/run_remote_tests.sh' % self.crosutils, ['%s/run_remote_tests.sh' % self.crosutils,
'--remote=%s' % self.remote, '--remote=%s' % self.remote,
'--results_dir_root=%s' % test_directory,
self.verify_suite, self.verify_suite,
], error_ok=True, enter_chroot=False, redirect_stdout=True) ], error_ok=True, enter_chroot=False, redirect_stdout=True)
return self.AssertEnoughTestsPassed(unittest, output, return self.AssertEnoughTestsPassed(unittest, output,

View File

@ -20,9 +20,9 @@ class VMAUWorker(au_worker.AUWorker):
_vm_lock = threading.Lock() _vm_lock = threading.Lock()
_next_port = 9222 _next_port = 9222
def __init__(self, options): def __init__(self, options, test_results_root):
"""Processes vm-specific options.""" """Processes vm-specific options."""
au_worker.AUWorker.__init__(self, options) au_worker.AUWorker.__init__(self, options, test_results_root)
self.graphics_flag = '' self.graphics_flag = ''
if options.no_graphics: self.graphics_flag = '--no_graphics' if options.no_graphics: self.graphics_flag = '--no_graphics'
if not self.board: cros_lib.Die('Need board to convert base image to vm.') if not self.board: cros_lib.Die('Need board to convert base image to vm.')
@ -95,6 +95,7 @@ class VMAUWorker(au_worker.AUWorker):
def VerifyImage(self, unittest, percent_required_to_pass=100): def VerifyImage(self, unittest, percent_required_to_pass=100):
"""Runs vm smoke suite to verify image.""" """Runs vm smoke suite to verify image."""
test_directory = self.GetNextResultsPath('verify')
# image_to_live already verifies lsb-release matching. This is just # image_to_live already verifies lsb-release matching. This is just
# for additional steps. # for additional steps.
commandWithArgs = ['%s/cros_run_vm_test' % self.crosutilsbin, commandWithArgs = ['%s/cros_run_vm_test' % self.crosutilsbin,
@ -103,6 +104,7 @@ class VMAUWorker(au_worker.AUWorker):
'--persist', '--persist',
'--kvm_pid=%s' % self._kvm_pid_file, '--kvm_pid=%s' % self._kvm_pid_file,
'--ssh_port=%s' % self._ssh_port, '--ssh_port=%s' % self._ssh_port,
'--results_dir_root=%s' % test_directory,
self.verify_suite, self.verify_suite,
] ]
if self.graphics_flag: commandWithArgs.append(self.graphics_flag) if self.graphics_flag: commandWithArgs.append(self.graphics_flag)

View File

@ -226,7 +226,7 @@ def GrabZipAndExtractImage(zip_url, download_folder, image_name) :
def RunAUTestHarness(board, channel, latest_url_base, zip_server_base, def RunAUTestHarness(board, channel, latest_url_base, zip_server_base,
no_graphics, type, remote, clean): no_graphics, type, remote, clean, test_results_root):
"""Runs the auto update test harness. """Runs the auto update test harness.
The auto update test harness encapsulates testing the auto-update mechanism The auto update test harness encapsulates testing the auto-update mechanism
@ -243,6 +243,7 @@ def RunAUTestHarness(board, channel, latest_url_base, zip_server_base,
type: which test harness to run. Possible values: real, vm. type: which test harness to run. Possible values: real, vm.
remote: ip address for real test harness run. remote: ip address for real test harness run.
clean: Clean the state of test harness before running. clean: Clean the state of test harness before running.
test_results_root: Root directory to store au_test_harness results.
""" """
crosutils_root = os.path.join(os.path.dirname(__file__), '..') crosutils_root = os.path.join(os.path.dirname(__file__), '..')
download_folder = os.path.abspath('latest_download') download_folder = os.path.abspath('latest_download')
@ -270,6 +271,7 @@ def RunAUTestHarness(board, channel, latest_url_base, zip_server_base,
'--public_key=%s' % os.path.join(update_engine_path, '--public_key=%s' % os.path.join(update_engine_path,
'unittest_key.pub.pem'), 'unittest_key.pub.pem'),
] ]
if test_results_root: cmd.append('--test_results_root=%s' % test_results_root)
if no_graphics: cmd.append('--no_graphics') if no_graphics: cmd.append('--no_graphics')
if clean: cmd.append('--clean') if clean: cmd.append('--clean')
@ -290,6 +292,9 @@ def main():
help='Base url for hosted images.') help='Base url for hosted images.')
parser.add_option('--no_graphics', action='store_true', default=False, parser.add_option('--no_graphics', action='store_true', default=False,
help='Disable graphics for the vm test.') help='Disable graphics for the vm test.')
parser.add_option('--test_results_root', default=None,
help='Root directory to store test results. Should '
'be defined relative to chroot root.')
parser.add_option('--type', default='vm', parser.add_option('--type', default='vm',
help='type of test to run: [vm, real]. Default: vm.') help='type of test to run: [vm, real]. Default: vm.')
parser.add_option('--remote', default='0.0.0.0', parser.add_option('--remote', default='0.0.0.0',