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 |