blob: eaf014af48e6f62ec333a7351954910e9b129a1b [file] [log] [blame]
Yiming Chen3c0103a2015-03-31 11:32:35 -07001# Copyright 2015 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 for integration VM tests for CLI commands.
6
7This module contains the basic functionalities for setting up a VM and testing
8the CLI commands.
9"""
10
11from __future__ import print_function
12
13from chromite.lib import cros_build_lib
14from chromite.lib import cros_logging as logging
15from chromite.lib import remote_access
16from chromite.lib import vm
17
18
19class Error(Exception):
20 """Base exception for CLI command VM tests."""
21
22
23class CommandError(Error):
24 """Raised when running a command test."""
25
26
27def _PrintCommandLog(command, content):
28 """Print out the log |content| for |command|."""
29 if content:
30 logging.info('\n----------- Start of %s log -----------\n%s\n'
31 '----------- End of %s log -----------',
32 command, content.rstrip(), command)
33
34
35def TestCommandDecorator(command_name):
36 """Decorator that runs the command test function."""
37
38 def Decorator(test_function):
39 """Inner decorator that actually wraps the function."""
40
41 def Wrapper(command_test):
42 """Wrapper for the test function."""
43 command = cros_build_lib.CmdToStr(command_test.BuildCommand(command_name))
44 logging.info('Running test for %s.', command)
45 try:
46 test_function(command_test)
47 logging.info('Test for %s passed.', command)
48 except CommandError as e:
49 _PrintCommandLog(command, str(e))
50 raise Error('Test for %s failed.' % command)
51
52 return Wrapper
53
54 return Decorator
55
56
57class CommandVMTest(object):
58 """Base class for CLI command VM tests.
59
60 This class provides the abstract interface for testing CLI commands on a VM.
61 The sub-class must define the BuildCommand method in order to be usable. And
62 the test functions must use the TestCommandDecorator decorator.
63 """
64
65 def __init__(self, board, image_path):
66 """Initializes CommandVMTest.
67
68 Args:
69 board: Board for the VM to run tests.
70 image_path: Path to the image for the VM to run tests.
71 """
72 self.board = board
73 self.image_path = image_path
74 self.working_image_path = None
75 self.vm = None
76
77 def BuildCommand(self, command, device=None, pos_args=None, opt_args=None):
78 """Builds a CLI command.
79
80 Args:
81 command: The sub-command to build on (e.g. 'flash', 'deploy').
82 device: The device's address for the command.
83 pos_args: A list of positional arguments for the command.
84 opt_args: A list of optional arguments for the command.
85 """
86 raise NotImplementedError()
87
88 def SetUp(self):
89 """Creates and starts the VM instance for testing."""
90 try:
91 logging.info('Setting up the VM for testing.')
92 self.working_image_path = vm.CreateVMImage(
93 image=self.image_path, board=self.board, updatable=True)
94 self.vm = vm.VMInstance(self.working_image_path)
95 self.vm.Start()
96 logging.info('The VM has been successfully set up. Ready to run tests.')
97 except vm.VMError as e:
98 raise Error('Failed to set up the VM for testing: %s' % e)
99
100 def TearDown(self):
101 """Stops the VM instance after testing."""
102 try:
103 logging.info('Stopping the VM.')
104 if self.vm:
105 self.vm.Stop()
106 logging.info('The VM has been stopped.')
107 except vm.VMStopError as e:
108 logging.warning('Failed to stop the VM: %s', e)
109
110 @TestCommandDecorator('flash')
111 def TestFlash(self):
112 """Tests the flash command."""
113 # We explicitly disable reboot after the update because VMs sometimes do
114 # not come back after reboot. The flash command does not need to verify
115 # the integrity of the updated image. We have AU tests for that.
116 cmd = self.BuildCommand('flash', device=self.vm.device_addr,
117 pos_args=['latest'],
118 opt_args=['--no-wipe', '--no-reboot'])
119
120 logging.info('Test to flash the VM device with the latest image.')
121 result = cros_build_lib.RunCommand(cmd, capture_output=True,
122 error_code_ok=True)
123 if result.returncode:
124 logging.error('Failed to flash the VM device.')
125 raise CommandError(result.error)
126
127 @TestCommandDecorator('deploy')
128 def TestDeploy(self):
129 """Tests the deploy command."""
130 packages = ['dev-python/cherrypy', 'app-portage/portage-utils']
131 # Set the installation root to /usr/local so that the command does not
132 # attempt to remount rootfs (which leads to VM reboot).
133 cmd = self.BuildCommand('deploy', device=self.vm.device_addr,
134 pos_args=packages, opt_args=['--root=/usr/local'])
135
136 logging.info('Test to uninstall packages on the VM device.')
137 result = cros_build_lib.RunCommand(cmd + ['--unmerge'],
138 capture_output=True,
139 error_code_ok=True)
140 if result.returncode:
141 logging.error('Failed to uninstall packages on the VM device.')
142 raise CommandError(result.error)
143
144 logging.info('Test to install packages on the VM device.')
145 result = cros_build_lib.RunCommand(cmd, capture_output=True,
146 error_code_ok=True)
147 if result.returncode:
148 logging.error('Failed to install packages on the VM device.')
149 raise CommandError(result.error)
150
151 # Verify that the packages are installed.
152 with remote_access.ChromiumOSDeviceHandler(
153 remote_access.LOCALHOST, port=self.vm.port) as device:
154 try:
155 device.RunCommand(['python', '-c', '"import cherrypy"'])
156 device.RunCommand(['qmerge', '-h'])
157 except remote_access.SSHConnectionError as e:
158 logging.error('Unable to connect to the VM to verify packages: %s', e)
159 raise CommandError()
160 except cros_build_lib.RunCommandError as e:
161 logging.error('Unable to verify packages installed on VM: %s', e)
162 raise CommandError()
163
164 def RunTests(self):
165 """Calls the test functions."""
166 self.TestFlash()
167 self.TestDeploy()
168
169 def Run(self):
170 """Runs the tests."""
171 try:
172 self.SetUp()
173 self.RunTests()
174 logging.info('All tests completed successfully.')
175 except Exception as e:
176 logging.error(e)
177 finally:
178 self.TearDown()