| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 | 2 |
| 3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 import optparse | 7 import optparse |
| 8 import os | 8 import os |
| 9 import sys | 9 import sys |
| 10 import unittest | 10 import unittest |
| 11 | 11 |
| 12 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) | 12 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) |
| 13 from cros_build_lib import RunCommand, Info, Warning, ReinterpretPathForChroot | 13 from cros_build_lib import RunCommand, Info, Warning, ReinterpretPathForChroot |
| 14 | 14 |
| 15 _KVM_PID_FILE = '/tmp/harness_pid' | 15 _KVM_PID_FILE = '/tmp/harness_pid' |
| 16 _SCRIPTS_DIR = os.path.join(os.path.dirname(__file__), '..') | |
| 17 _FULL_VDISK_SIZE = 6072 | 16 _FULL_VDISK_SIZE = 6072 |
| 18 _FULL_STATEFULFS_SIZE = 2048 | 17 _FULL_STATEFULFS_SIZE = 2048 |
| 19 | 18 |
| 19 # Globals to communicate options to unit tests. |
| 20 global base_image_path | 20 global base_image_path |
| 21 global board | 21 global board |
| 22 global remote |
| 22 global target_image_path | 23 global target_image_path |
| 23 | 24 |
| 24 _VERIFY_SUITE = 'suite_Smoke' | 25 _VERIFY_SUITE = 'suite_Smoke' |
| 25 | 26 |
| 26 class AUTest(object): | 27 class AUTest(object): |
| 27 """Abstract interface that defines an Auto Update test.""" | 28 """Abstract interface that defines an Auto Update test.""" |
| 28 | 29 |
| 30 def setUp(self): |
| 31 unittest.TestCase.setUp(self) |
| 32 # Set these up as they are used often. |
| 33 self.crosutils = os.path.join(os.path.dirname(__file__), '..') |
| 34 self.crosutilsbin = os.path.join(os.path.dirname(__file__)) |
| 35 |
| 36 def GetStatefulChangeFlag(self, stateful_change): |
| 37 """Returns the flag to pass to image_to_vm for the stateful change.""" |
| 38 stateful_change_flag = '' |
| 39 if stateful_change: |
| 40 stateful_change_flag = '--stateful_update_flag=%s' % stateful_change |
| 41 |
| 42 return stateful_change_flag |
| 43 |
| 29 def PrepareBase(self): | 44 def PrepareBase(self): |
| 30 """Prepares target with base_image_path.""" | 45 """Prepares target with base_image_path.""" |
| 31 pass | 46 pass |
| 32 | 47 |
| 33 def UpdateImage(self, image_path, stateful_change='old'): | 48 def UpdateImage(self, image_path, stateful_change='old'): |
| 34 """Updates target with the image given by the image_path. | 49 """Updates target with the image given by the image_path. |
| 35 | 50 |
| 36 Args: | 51 Args: |
| 37 image_path: Path to the image to update with. This image must be a test | 52 image_path: Path to the image to update with. This image must be a test |
| 38 image. | 53 image. |
| 39 stateful_change: How to modify the stateful partition. Values are: | 54 stateful_change: How to modify the stateful partition. Values are: |
| 40 'old': Don't modify stateful partition. Just update normally. | 55 'old': Don't modify stateful partition. Just update normally. |
| 41 'clean': Uses clobber-state to wipe the stateful partition with the | 56 'clean': Uses clobber-state to wipe the stateful partition with the |
| 42 exception of code needed for ssh. | 57 exception of code needed for ssh. |
| 43 """ | 58 """ |
| 44 pass | 59 pass |
| 45 | 60 |
| 46 def VerifyImage(self): | 61 def VerifyImage(self): |
| 47 """Verifies the image is correct.""" | 62 """Verifies the image is correct.""" |
| 48 pass | 63 pass |
| 49 | 64 |
| 50 def testFullUpdateKeepStateful(self): | 65 def testFullUpdateKeepStateful(self): |
| 66 """Tests if we can update normally. |
| 67 |
| 68 This test checks that we can update by updating the stateful partition |
| 69 rather than wiping it. |
| 70 """ |
| 51 # Prepare and verify the base image has been prepared correctly. | 71 # Prepare and verify the base image has been prepared correctly. |
| 52 self.PrepareBase() | 72 self.PrepareBase() |
| 53 self.VerifyImage() | 73 self.VerifyImage() |
| 54 | 74 |
| 55 # Update to. | 75 # Update to. |
| 56 Info('Updating from base image on vm to target image.') | 76 Info('Updating from base image on vm to target image.') |
| 57 self.UpdateImage(target_image_path) | 77 self.UpdateImage(target_image_path) |
| 58 self.VerifyImage() | 78 self.VerifyImage() |
| 59 | 79 |
| 60 # Update from. | 80 # Update from. |
| 61 Info('Updating from updated image on vm back to base image.') | 81 Info('Updating from updated image on vm back to base image.') |
| 62 self.UpdateImage(base_image_path) | 82 self.UpdateImage(base_image_path) |
| 63 self.VerifyImage() | 83 self.VerifyImage() |
| 64 | 84 |
| 65 def testFullUpdateWipeStateful(self): | 85 # TODO(sosa): Re-enable once we have a good way of checking for version |
| 86 # compatability. |
| 87 def NotestFullUpdateWipeStateful(self): |
| 88 """Tests if we can update after cleaning the stateful partition. |
| 89 |
| 90 This test checks that we can update successfully after wiping the |
| 91 stateful partition. |
| 92 """ |
| 66 # Prepare and verify the base image has been prepared correctly. | 93 # Prepare and verify the base image has been prepared correctly. |
| 67 self.PrepareBase() | 94 self.PrepareBase() |
| 68 self.VerifyImage() | 95 self.VerifyImage() |
| 69 | 96 |
| 70 # Update to. | 97 # Update to. |
| 71 Info('Updating from base image on vm to target image and wiping stateful.') | 98 Info('Updating from base image on vm to target image and wiping stateful.') |
| 72 self.UpdateImage(target_image_path, 'clean') | 99 self.UpdateImage(target_image_path, 'clean') |
| 73 self.VerifyImage() | 100 self.VerifyImage() |
| 74 | 101 |
| 75 # Update from. | 102 # Update from. |
| 76 Info('Updating from updated image back to base image and wiping stateful.') | 103 Info('Updating from updated image back to base image and wiping stateful.') |
| 77 self.UpdateImage(base_image_path, 'clean') | 104 self.UpdateImage(base_image_path, 'clean') |
| 78 self.VerifyImage() | 105 self.VerifyImage() |
| 79 | 106 |
| 80 | 107 |
| 108 class RealAUTest(unittest.TestCase, AUTest): |
| 109 """Test harness for updating real images.""" |
| 110 |
| 111 def setUp(self): |
| 112 AUTest.setUp(self) |
| 113 |
| 114 def UpdateImage(self, image_path, stateful_change='old'): |
| 115 """Updates a remote image using image_to_live.sh.""" |
| 116 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) |
| 117 |
| 118 RunCommand([ |
| 119 '%s/image_to_live.sh' % self.crosutils, |
| 120 '--image=%s' % image_path, |
| 121 '--remote=%s' % remote, |
| 122 stateful_change_flag, |
| 123 '--verify', |
| 124 ], enter_chroot=False) |
| 125 |
| 126 |
| 127 def NotVerifyImage(self): |
| 128 """Verifies an image using run_remote_tests.sh with verification suite.""" |
| 129 RunCommand([ |
| 130 '%s/run_remote_tests.sh' % self.crosutils, |
| 131 '--remote=%s' % remote, |
| 132 _VERIFY_SUITE, |
| 133 ], error_ok=False, enter_chroot=False) |
| 134 |
| 135 |
| 81 class VirtualAUTest(unittest.TestCase, AUTest): | 136 class VirtualAUTest(unittest.TestCase, AUTest): |
| 82 """Test harness for updating virtual machines.""" | 137 """Test harness for updating virtual machines.""" |
| 83 vm_image_path = None | 138 vm_image_path = None |
| 84 | 139 |
| 85 def _KillExistingVM(self, pid_file): | 140 def _KillExistingVM(self, pid_file): |
| 86 if os.path.exists(pid_file): | 141 if os.path.exists(pid_file): |
| 87 Warning('Existing %s found. Deleting and killing process' % | 142 Warning('Existing %s found. Deleting and killing process' % |
| 88 pid_file) | 143 pid_file) |
| 89 pid = RunCommand(['sudo', 'cat', pid_file], redirect_stdout=True, | 144 pid = RunCommand(['sudo', 'cat', pid_file], redirect_stdout=True, |
| 90 enter_chroot=False) | 145 enter_chroot=False) |
| 91 if pid: | 146 if pid: |
| 92 RunCommand(['sudo', 'kill', pid.strip()], error_ok=True, | 147 RunCommand(['sudo', 'kill', pid.strip()], error_ok=True, |
| 93 enter_chroot=False) | 148 enter_chroot=False) |
| 94 RunCommand(['sudo', 'rm', pid_file], enter_chroot=False) | 149 RunCommand(['sudo', 'rm', pid_file], enter_chroot=False) |
| 95 | 150 |
| 96 def setUp(self): | 151 def setUp(self): |
| 97 """Unit test overriden method. Is called before every test.""" | 152 """Unit test overriden method. Is called before every test.""" |
| 98 | 153 AUTest.setUp(self) |
| 99 self._KillExistingVM(_KVM_PID_FILE) | 154 self._KillExistingVM(_KVM_PID_FILE) |
| 100 | 155 |
| 101 def PrepareBase(self): | 156 def PrepareBase(self): |
| 102 """Creates an update-able VM based on base image.""" | 157 """Creates an update-able VM based on base image.""" |
| 103 | 158 |
| 104 self.vm_image_path = ('%s/chromiumos_qemu_image.bin' % os.path.dirname( | 159 self.vm_image_path = ('%s/chromiumos_qemu_image.bin' % os.path.dirname( |
| 105 base_image_path)) | 160 base_image_path)) |
| 106 if not os.path.exists(self.vm_image_path): | 161 if not os.path.exists(self.vm_image_path): |
| 107 Info('Qemu image not found, creating one.') | 162 Info('Qemu image not found, creating one.') |
| 108 RunCommand(['%s/image_to_vm.sh' % _SCRIPTS_DIR, | 163 RunCommand(['%s/image_to_vm.sh' % self.crosutils, |
| 109 '--full', | 164 '--full', |
| 110 '--from %s' % ReinterpretPathForChroot( | 165 '--from %s' % ReinterpretPathForChroot( |
| 111 os.path.dirname(base_image_path)), | 166 os.path.dirname(base_image_path)), |
| 112 '--vdisk_size %s' % _FULL_VDISK_SIZE, | 167 '--vdisk_size %s' % _FULL_VDISK_SIZE, |
| 113 '--statefulfs_size %s' % _FULL_STATEFULFS_SIZE, | 168 '--statefulfs_size %s' % _FULL_STATEFULFS_SIZE, |
| 114 '--board %s' % board, | 169 '--board %s' % board, |
| 115 '--test_image'], enter_chroot=True) | 170 '--test_image'], enter_chroot=True) |
| 116 else: | 171 else: |
| 117 Info('Using existing VM image') | 172 Info('Using existing VM image') |
| 118 | 173 |
| 119 self.assertTrue(os.path.exists(self.vm_image_path)) | 174 self.assertTrue(os.path.exists(self.vm_image_path)) |
| 120 | 175 |
| 121 def UpdateImage(self, image_path, stateful_change='old'): | 176 def UpdateImage(self, image_path, stateful_change='old'): |
| 122 """Updates VM image with image_path.""" | 177 """Updates VM image with image_path.""" |
| 178 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) |
| 123 | 179 |
| 124 stateful_change_flag = '' | 180 RunCommand(['%s/cros_run_vm_update' % self.crosutilsbin, |
| 125 if stateful_change: | |
| 126 stateful_change_flag = '--stateful_flags=%s' % stateful_change | |
| 127 | |
| 128 RunCommand(['%s/cros_run_vm_update' % os.path.dirname(__file__), | |
| 129 '--update_image_path=%s' % image_path, | 181 '--update_image_path=%s' % image_path, |
| 130 '--vm_image_path=%s' % self.vm_image_path, | 182 '--vm_image_path=%s' % self.vm_image_path, |
| 131 '--snapshot', | 183 '--snapshot', |
| 132 '--persist', | 184 '--persist', |
| 133 '--kvm_pid=%s' % _KVM_PID_FILE, | 185 '--kvm_pid=%s' % _KVM_PID_FILE, |
| 134 stateful_change_flag, | 186 stateful_change_flag, |
| 135 ], enter_chroot=False) | 187 ], enter_chroot=False) |
| 136 | 188 |
| 137 def VerifyImage(self): | 189 def VerifyImage(self): |
| 138 """Runs vm smoke suite to verify image.""" | 190 """Runs vm smoke suite to verify image.""" |
| 139 | |
| 140 # image_to_live already verifies lsb-release matching. This is just | 191 # image_to_live already verifies lsb-release matching. This is just |
| 141 # for additional steps. | 192 # for additional steps. |
| 142 | 193 RunCommand(['%s/cros_run_vm_test' % self.crosutilsbin, |
| 143 # TODO(sosa): Compare output with results of base image. | |
| 144 RunCommand(['%s/cros_run_vm_test' % os.path.dirname(__file__), | |
| 145 '--image_path=%s' % self.vm_image_path, | 194 '--image_path=%s' % self.vm_image_path, |
| 146 '--snapshot', | 195 '--snapshot', |
| 147 '--persist', | 196 '--persist', |
| 148 '--kvm_pid=%s' % _KVM_PID_FILE, | 197 '--kvm_pid=%s' % _KVM_PID_FILE, |
| 149 '--test_case=%s' % _VERIFY_SUITE, | 198 '--test_case=%s' % _VERIFY_SUITE, |
| 150 ], error_ok=True, enter_chroot=False) | 199 ], error_ok=False, enter_chroot=False) |
| 151 | 200 |
| 152 | 201 |
| 153 if __name__ == '__main__': | 202 if __name__ == '__main__': |
| 154 parser = optparse.OptionParser() | 203 parser = optparse.OptionParser() |
| 155 parser.add_option('-b', '--base_image', | 204 parser.add_option('-b', '--base_image', |
| 156 help='path to the base image.') | 205 help='path to the base image.') |
| 157 parser.add_option('-t', '--target_image', | 206 parser.add_option('-t', '--target_image', |
| 158 help='path to the target image') | 207 help='path to the target image.') |
| 159 parser.add_option('-r', '--board', | 208 parser.add_option('-r', '--board', |
| 160 help='board for the images') | 209 help='board for the images.') |
| 210 parser.add_option('-p', '--type', default='vm', |
| 211 help='type of test to run: [vm, real]. Default: vm.') |
| 212 parser.add_option('-m', '--remote', |
| 213 help='Remote address for real test.') |
| 161 # Set the usage to include flags. | 214 # Set the usage to include flags. |
| 162 parser.set_usage(parser.format_help()) | 215 parser.set_usage(parser.format_help()) |
| 163 # Parse existing sys.argv so we can pass rest to unittest.main. | 216 # Parse existing sys.argv so we can pass rest to unittest.main. |
| 164 (options, sys.argv) = parser.parse_args(sys.argv) | 217 (options, sys.argv) = parser.parse_args(sys.argv) |
| 165 | 218 |
| 166 base_image_path = options.base_image | 219 base_image_path = options.base_image |
| 167 target_image_path = options.target_image | 220 target_image_path = options.target_image |
| 168 board = options.board | 221 board = options.board |
| 169 | 222 |
| 170 if not base_image_path: | 223 if not base_image_path: |
| 171 parser.error('Need path to base image for vm.') | 224 parser.error('Need path to base image for vm.') |
| 172 | 225 |
| 173 if not target_image_path: | 226 if not target_image_path: |
| 174 parser.error('Need path to target image to update with.') | 227 parser.error('Need path to target image to update with.') |
| 175 | 228 |
| 176 if not board: | 229 if not board: |
| 177 parser.error('Need board to convert base image to vm.') | 230 parser.error('Need board to convert base image to vm.') |
| 178 | 231 |
| 179 unittest.main() | 232 # Only run the test harness we care about. |
| 233 if options.type == 'vm': |
| 234 suite = unittest.TestLoader().loadTestsFromTestCase(VirtualAUTest) |
| 235 unittest.TextTestRunner(verbosity=2).run(suite) |
| 236 elif options.type == 'real': |
| 237 if not options.remote: |
| 238 parser.error('Real tests require a remote test machine.') |
| 239 else: |
| 240 remote = options.remote |
| 241 |
| 242 suite = unittest.TestLoader().loadTestsFromTestCase(RealAUTest) |
| 243 unittest.TextTestRunner(verbosity=2).run(suite) |
| 244 else: |
| 245 parser.error('Could not parse harness type %s.' % options.type) |
| OLD | NEW |