blob: 8a439af521783377e4bafc15063551a9299ff61f [file] [log] [blame]
Alex Kleina2e42c42019-04-17 16:13:19 -06001# -*- coding: utf-8 -*-
2# Copyright 2019 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""The test controller tests."""
7
8from __future__ import print_function
9
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -060010import contextlib
Alex Kleinfa6ebdc2019-05-10 10:57:31 -060011import mock
12
Alex Klein8cb365a2019-05-15 16:24:53 -060013from chromite.api import controller
Alex Kleina2e42c42019-04-17 16:13:19 -060014from chromite.api.controller import test as test_controller
Evan Hernandez4e388a52019-05-01 12:16:33 -060015from chromite.api.gen.chromiumos import common_pb2
Alex Kleina2e42c42019-04-17 16:13:19 -060016from chromite.api.gen.chromite.api import test_pb2
17from chromite.cbuildbot import commands
Alex Kleinfa6ebdc2019-05-10 10:57:31 -060018from chromite.lib import constants
Alex Kleina2e42c42019-04-17 16:13:19 -060019from chromite.lib import cros_build_lib
20from chromite.lib import cros_test_lib
21from chromite.lib import failures_lib
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -060022from chromite.lib import image_lib
Alex Kleina2e42c42019-04-17 16:13:19 -060023from chromite.lib import osutils
24from chromite.lib import portage_util
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -060025from chromite.scripts import cros_set_lsb_release
26from chromite.service import test as test_service
Alex Kleina2e42c42019-04-17 16:13:19 -060027
28
Evan Hernandez4e388a52019-05-01 12:16:33 -060029class BuildTargetUnitTestTest(cros_test_lib.MockTempDirTestCase):
Alex Kleina2e42c42019-04-17 16:13:19 -060030 """Tests for the UnitTest function."""
31
32 def _GetInput(self, board=None, result_path=None, chroot_path=None,
Alex Kleinf2674462019-05-16 16:47:24 -060033 cache_dir=None, empty_sysroot=None, blacklist=None):
Alex Kleina2e42c42019-04-17 16:13:19 -060034 """Helper to build an input message instance."""
Alex Kleinf2674462019-05-16 16:47:24 -060035 formatted_blacklist = []
36 for pkg in blacklist or []:
37 formatted_blacklist.append({'category': pkg.category,
38 'package_name': pkg.package})
39
Alex Kleina2e42c42019-04-17 16:13:19 -060040 return test_pb2.BuildTargetUnitTestRequest(
41 build_target={'name': board}, result_path=result_path,
Alex Kleinfa6ebdc2019-05-10 10:57:31 -060042 chroot={'path': chroot_path, 'cache_dir': cache_dir},
Alex Kleinf2674462019-05-16 16:47:24 -060043 flags={'empty_sysroot': empty_sysroot},
44 package_blacklist=formatted_blacklist,
Alex Kleina2e42c42019-04-17 16:13:19 -060045 )
46
47 def _GetOutput(self):
48 """Helper to get an empty output message instance."""
49 return test_pb2.BuildTargetUnitTestResponse()
50
51 def testNoArgumentFails(self):
52 """Test no arguments fails."""
53 input_msg = self._GetInput()
54 output_msg = self._GetOutput()
55 with self.assertRaises(cros_build_lib.DieSystemExit):
56 test_controller.BuildTargetUnitTest(input_msg, output_msg)
57
58 def testNoBuildTargetFails(self):
59 """Test missing build target name fails."""
60 input_msg = self._GetInput(result_path=self.tempdir)
61 output_msg = self._GetOutput()
62 with self.assertRaises(cros_build_lib.DieSystemExit):
63 test_controller.BuildTargetUnitTest(input_msg, output_msg)
64
65 def testNoResultPathFails(self):
66 """Test missing result path fails."""
67 # Missing result_path.
68 input_msg = self._GetInput(board='board')
69 output_msg = self._GetOutput()
70 with self.assertRaises(cros_build_lib.DieSystemExit):
71 test_controller.BuildTargetUnitTest(input_msg, output_msg)
72
73 def testPackageBuildFailure(self):
74 """Test handling of raised BuildPackageFailure."""
75 tempdir = osutils.TempDir(base_dir=self.tempdir)
76 self.PatchObject(osutils, 'TempDir', return_value=tempdir)
77
78 pkgs = ['cat/pkg', 'foo/bar']
79 expected = [('cat', 'pkg'), ('foo', 'bar')]
80 rce = cros_build_lib.RunCommandError('error',
81 cros_build_lib.CommandResult())
82 error = failures_lib.PackageBuildFailure(rce, 'shortname', pkgs)
83 self.PatchObject(commands, 'RunUnitTests', side_effect=error)
84
85 input_msg = self._GetInput(board='board', result_path=self.tempdir)
86 output_msg = self._GetOutput()
87
88 rc = test_controller.BuildTargetUnitTest(input_msg, output_msg)
89
Alex Klein8cb365a2019-05-15 16:24:53 -060090 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
Alex Kleina2e42c42019-04-17 16:13:19 -060091 self.assertTrue(output_msg.failed_packages)
92 failed = []
93 for pi in output_msg.failed_packages:
94 failed.append((pi.category, pi.package_name))
95 self.assertItemsEqual(expected, failed)
96
97 def testPopulatedEmergeFile(self):
98 """Test build script failure due to using outside emerge status file."""
99 tempdir = osutils.TempDir(base_dir=self.tempdir)
100 self.PatchObject(osutils, 'TempDir', return_value=tempdir)
101
102 pkgs = ['cat/pkg', 'foo/bar']
103 cpvs = [portage_util.SplitCPV(pkg, strict=False) for pkg in pkgs]
104 expected = [('cat', 'pkg'), ('foo', 'bar')]
105 rce = cros_build_lib.RunCommandError('error',
106 cros_build_lib.CommandResult())
107 error = failures_lib.BuildScriptFailure(rce, 'shortname')
108 self.PatchObject(commands, 'RunUnitTests', side_effect=error)
109 self.PatchObject(portage_util, 'ParseParallelEmergeStatusFile',
110 return_value=cpvs)
111
112 input_msg = self._GetInput(board='board', result_path=self.tempdir)
113 output_msg = self._GetOutput()
114
115 rc = test_controller.BuildTargetUnitTest(input_msg, output_msg)
116
Alex Klein8cb365a2019-05-15 16:24:53 -0600117 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
Alex Kleina2e42c42019-04-17 16:13:19 -0600118 self.assertTrue(output_msg.failed_packages)
119 failed = []
120 for pi in output_msg.failed_packages:
121 failed.append((pi.category, pi.package_name))
122 self.assertItemsEqual(expected, failed)
123
124 def testOtherBuildScriptFailure(self):
125 """Test build script failure due to non-package emerge error."""
126 tempdir = osutils.TempDir(base_dir=self.tempdir)
127 self.PatchObject(osutils, 'TempDir', return_value=tempdir)
128
129 rce = cros_build_lib.RunCommandError('error',
130 cros_build_lib.CommandResult())
131 error = failures_lib.BuildScriptFailure(rce, 'shortname')
Alex Kleinfa6ebdc2019-05-10 10:57:31 -0600132 patch = self.PatchObject(commands, 'RunUnitTests', side_effect=error)
Alex Kleina2e42c42019-04-17 16:13:19 -0600133 self.PatchObject(portage_util, 'ParseParallelEmergeStatusFile',
134 return_value=[])
135
Alex Kleinf2674462019-05-16 16:47:24 -0600136 pkgs = ['foo/bar', 'cat/pkg']
137 blacklist = [portage_util.SplitCPV(p, strict=False) for p in pkgs]
Alex Kleinfa6ebdc2019-05-10 10:57:31 -0600138 input_msg = self._GetInput(board='board', result_path=self.tempdir,
Alex Kleinf2674462019-05-16 16:47:24 -0600139 empty_sysroot=True, blacklist=blacklist)
Alex Kleina2e42c42019-04-17 16:13:19 -0600140 output_msg = self._GetOutput()
141
142 rc = test_controller.BuildTargetUnitTest(input_msg, output_msg)
143
Alex Klein8cb365a2019-05-15 16:24:53 -0600144 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Alex Kleina2e42c42019-04-17 16:13:19 -0600145 self.assertFalse(output_msg.failed_packages)
Alex Kleinfa6ebdc2019-05-10 10:57:31 -0600146 patch.assert_called_with(constants.SOURCE_ROOT, 'board', extra_env=mock.ANY,
Alex Kleinf2674462019-05-16 16:47:24 -0600147 chroot_args=mock.ANY, build_stage=False,
148 blacklist=pkgs)
Evan Hernandez4e388a52019-05-01 12:16:33 -0600149
150
151class VmTestTest(cros_test_lib.MockTestCase):
152 """Test the VmTest endpoint."""
153
154 def _GetInput(self, **kwargs):
155 values = dict(
156 build_target=common_pb2.BuildTarget(name='target'),
Alex Klein311b8022019-06-05 16:00:07 -0600157 vm_path=common_pb2.Path(path='/path/to/image.bin',
158 location=common_pb2.Path.INSIDE),
Evan Hernandez4e388a52019-05-01 12:16:33 -0600159 test_harness=test_pb2.VmTestRequest.TAST,
160 vm_tests=[test_pb2.VmTestRequest.VmTest(pattern='suite')],
161 ssh_options=test_pb2.VmTestRequest.SshOptions(
Alex Kleinaa705412019-06-04 15:00:30 -0600162 port=1234, private_key_path={'path':'/path/to/id_rsa',
163 'location': common_pb2.Path.INSIDE}),
Evan Hernandez4e388a52019-05-01 12:16:33 -0600164 )
165 values.update(kwargs)
166 return test_pb2.VmTestRequest(**values)
167
168 def setUp(self):
169 self.rc_mock = cros_test_lib.RunCommandMock()
170 self.rc_mock.SetDefaultCmdResult()
171 self.StartPatcher(self.rc_mock)
172
173 def testTastAllOptions(self):
174 """Test VmTest for Tast with all options set."""
175 test_controller.VmTest(self._GetInput(), None)
176 self.rc_mock.assertCommandContains([
Achuith Bhandarkara9e9c3d2019-05-22 13:56:11 -0700177 'cros_run_test', '--debug', '--no-display', '--copy-on-write',
Evan Hernandez4e388a52019-05-01 12:16:33 -0600178 '--board', 'target',
179 '--image-path', '/path/to/image.bin',
180 '--tast', 'suite',
181 '--ssh-port', '1234',
182 '--private-key', '/path/to/id_rsa',
183 ])
184
185 def testAutotestAllOptions(self):
186 """Test VmTest for Autotest with all options set."""
187 input_proto = self._GetInput(test_harness=test_pb2.VmTestRequest.AUTOTEST)
188 test_controller.VmTest(input_proto, None)
189 self.rc_mock.assertCommandContains([
Achuith Bhandarkara9e9c3d2019-05-22 13:56:11 -0700190 'cros_run_test', '--debug', '--no-display', '--copy-on-write',
Evan Hernandez4e388a52019-05-01 12:16:33 -0600191 '--board', 'target',
192 '--image-path', '/path/to/image.bin',
193 '--autotest', 'suite',
194 '--ssh-port', '1234',
195 '--private-key', '/path/to/id_rsa',
196 '--test_that-args=--whitelist-chrome-crashes',
197 ])
198
199 def testMissingBuildTarget(self):
200 """Test VmTest dies when build_target not set."""
201 input_proto = self._GetInput(build_target=None)
202 with self.assertRaises(cros_build_lib.DieSystemExit):
203 test_controller.VmTest(input_proto, None)
204
205 def testMissingVmImage(self):
206 """Test VmTest dies when vm_image not set."""
Alex Klein311b8022019-06-05 16:00:07 -0600207 input_proto = self._GetInput(vm_path=None)
Evan Hernandez4e388a52019-05-01 12:16:33 -0600208 with self.assertRaises(cros_build_lib.DieSystemExit):
209 test_controller.VmTest(input_proto, None)
210
211 def testMissingTestHarness(self):
212 """Test VmTest dies when test_harness not specified."""
213 input_proto = self._GetInput(
214 test_harness=test_pb2.VmTestRequest.UNSPECIFIED)
215 with self.assertRaises(cros_build_lib.DieSystemExit):
216 test_controller.VmTest(input_proto, None)
217
218 def testMissingVmTests(self):
219 """Test VmTest dies when vm_tests not set."""
220 input_proto = self._GetInput(vm_tests=[])
221 with self.assertRaises(cros_build_lib.DieSystemExit):
222 test_controller.VmTest(input_proto, None)
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600223
224
225class MoblabVmTestTest(cros_test_lib.MockTestCase):
226 """Test the MoblabVmTest endpoint."""
227
228 @staticmethod
229 def _Payload(path):
230 return test_pb2.MoblabVmTestRequest.Payload(
231 path=common_pb2.Path(path=path))
232
233 @staticmethod
234 def _Output():
235 return test_pb2.MoblabVmTestResponse()
236
237 def _Input(self):
238 return test_pb2.MoblabVmTestRequest(
239 image_payload=self._Payload(self.image_payload_dir),
240 cache_payloads=[self._Payload(self.autotest_payload_dir)])
241
242 def setUp(self):
243 self.image_payload_dir = '/payloads/image'
244 self.autotest_payload_dir = '/payloads/autotest'
245 self.builder = 'moblab-generic-vm/R12-3.4.5-67.890'
246 self.image_cache_dir = '/mnt/moblab/cache'
247 self.image_mount_dir = '/mnt/image'
248
249 self.mock_create_moblab_vms = self.PatchObject(
250 test_service, 'CreateMoblabVm')
251 self.mock_prepare_moblab_vm_image_cache = self.PatchObject(
252 test_service, 'PrepareMoblabVmImageCache',
253 return_value=self.image_cache_dir)
254 self.mock_run_moblab_vm_tests = self.PatchObject(
255 test_service, 'RunMoblabVmTest')
256 self.mock_validate_moblab_vm_tests = self.PatchObject(
257 test_service, 'ValidateMoblabVmTest')
258
259 @contextlib.contextmanager
260 def MockLoopbackPartitions(*args, **kwargs):
261 mount = mock.MagicMock()
262 mount.destination = self.image_mount_dir
263 yield mount
264 self.PatchObject(image_lib, 'LoopbackPartitions', MockLoopbackPartitions)
265
266 def testImageContainsBuilder(self):
267 """MoblabVmTest calls service with correct args."""
268 request = self._Input()
269 response = self._Output()
270
271 self.PatchObject(
272 cros_build_lib, 'LoadKeyValueFile',
273 return_value={cros_set_lsb_release.LSB_KEY_BUILDER_PATH: self.builder})
274
275 test_controller.MoblabVmTest(request, response)
276
277 self.assertEqual(
278 self.mock_create_moblab_vms.call_args_list,
279 [mock.call(mock.ANY, self.image_payload_dir)])
280 self.assertEqual(
281 self.mock_prepare_moblab_vm_image_cache.call_args_list,
282 [mock.call(mock.ANY, self.builder, [self.autotest_payload_dir])])
283 self.assertEqual(
284 self.mock_run_moblab_vm_tests.call_args_list,
285 [mock.call(mock.ANY, self.builder, self.image_cache_dir, mock.ANY)])
286 self.assertEqual(
287 self.mock_validate_moblab_vm_tests.call_args_list,
288 [mock.call(mock.ANY)])
289
290 def testImageMissingBuilder(self):
291 """MoblabVmTest dies when builder path not found in lsb-release."""
292 request = self._Input()
293 response = self._Output()
294
295 self.PatchObject(cros_build_lib, 'LoadKeyValueFile', return_value={})
296
297 with self.assertRaises(cros_build_lib.DieSystemExit):
298 test_controller.MoblabVmTest(request, response)