blob: b46046d7f674ee71e4ea4baac787044600965048 [file] [log] [blame]
Evan Hernandezf388cbf2019-04-01 11:15:23 -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"""Unittests for Artifacts operations."""
7
8from __future__ import print_function
9
10import mock
11import os
12
13from chromite.api.controller import artifacts
14from chromite.api.gen.chromite.api import artifacts_pb2
15from chromite.cbuildbot import commands
Alex Klein6504eca2019-04-18 15:37:56 -060016from chromite.cbuildbot.stages import vm_test_stages
Evan Hernandezf388cbf2019-04-01 11:15:23 -060017from chromite.lib import constants
18from chromite.lib import cros_build_lib
19from chromite.lib import cros_test_lib
20from chromite.lib import osutils
Alex Klein2275d692019-04-23 16:04:12 -060021from chromite.service import artifacts as artifacts_svc
Evan Hernandezf388cbf2019-04-01 11:15:23 -060022
23
24class BundleTestCase(cros_test_lib.MockTestCase):
25 """Basic setup for all artifacts unittests."""
26
27 def setUp(self):
28 self.input_proto = artifacts_pb2.BundleRequest()
29 self.input_proto.build_target.name = 'target'
30 self.input_proto.output_dir = '/tmp/artifacts'
31 self.output_proto = artifacts_pb2.BundleResponse()
32
33 self.PatchObject(constants, 'SOURCE_ROOT', new='/cros')
34
35
Evan Hernandez9f125ac2019-04-08 17:18:47 -060036class BundleImageZipTest(BundleTestCase):
37 """Unittests for BundleImageZip."""
38
39 def testBundleImageZip(self):
40 """BundleImageZip calls cbuildbot/commands with correct args."""
41 build_image_zip = self.PatchObject(
42 commands, 'BuildImageZip', return_value='image.zip')
43 self.PatchObject(os.path, 'exists', return_value=True)
44 artifacts.BundleImageZip(self.input_proto, self.output_proto)
45 self.assertEqual(
46 [artifact.path for artifact in self.output_proto.artifacts],
47 ['/tmp/artifacts/image.zip'])
48 self.assertEqual(
49 build_image_zip.call_args_list,
50 [mock.call('/tmp/artifacts', '/cros/src/build/images/target/latest')])
51
52 def testBundleImageZipNoImageDir(self):
53 """BundleImageZip dies when image dir does not exist."""
54 self.PatchObject(os.path, 'exists', return_value=False)
55 with self.assertRaises(cros_build_lib.DieSystemExit):
56 artifacts.BundleImageZip(self.input_proto, self.output_proto)
57
58
Evan Hernandezf388cbf2019-04-01 11:15:23 -060059class BundleAutotestFilesTest(BundleTestCase):
60 """Unittests for BundleAutotestFiles."""
61
62 def testBundleAutotestFiles(self):
63 """BundleAutotestFiles calls cbuildbot/commands with correct args."""
64 build_autotest_tarballs = self.PatchObject(
65 commands,
66 'BuildAutotestTarballsForHWTest',
67 return_value=[
68 '/tmp/artifacts/autotest-a.tar.gz',
69 '/tmp/artifacts/autotest-b.tar.gz',
70 ])
71 artifacts.BundleAutotestFiles(self.input_proto, self.output_proto)
72 self.assertItemsEqual([
73 artifact.path for artifact in self.output_proto.artifacts
74 ], ['/tmp/artifacts/autotest-a.tar.gz', '/tmp/artifacts/autotest-b.tar.gz'])
75 self.assertEqual(build_autotest_tarballs.call_args_list, [
Evan Hernandez36589c62019-04-05 18:14:42 -060076 mock.call('/cros', '/cros/chroot/build/target/usr/local/build',
77 '/tmp/artifacts')
Evan Hernandezf388cbf2019-04-01 11:15:23 -060078 ])
79
80
81class BundleTastFilesTest(BundleTestCase):
82 """Unittests for BundleTastFiles."""
83
84 def testBundleTastFiles(self):
85 """BundleTastFiles calls cbuildbot/commands with correct args."""
86 build_tast_bundle_tarball = self.PatchObject(
87 commands,
88 'BuildTastBundleTarball',
89 return_value='/tmp/artifacts/tast.tar.gz')
90 artifacts.BundleTastFiles(self.input_proto, self.output_proto)
91 self.assertEqual(
92 [artifact.path for artifact in self.output_proto.artifacts],
93 ['/tmp/artifacts/tast.tar.gz'])
94 self.assertEqual(build_tast_bundle_tarball.call_args_list, [
95 mock.call('/cros', '/cros/chroot/build/target/build', '/tmp/artifacts')
96 ])
97
Evan Hernandez9a5d3122019-04-09 10:51:23 -060098 def testBundleTastFilesNoLogs(self):
99 """BundleTasteFiles dies when no tast files found."""
100 self.PatchObject(commands, 'BuildTastBundleTarball',
101 return_value=None)
102 with self.assertRaises(cros_build_lib.DieSystemExit):
103 artifacts.BundleTastFiles(self.input_proto, self.output_proto)
104
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600105
106class BundlePinnedGuestImagesTest(BundleTestCase):
107 """Unittests for BundlePinnedGuestImages."""
108
109 def testBundlePinnedGuestImages(self):
110 """BundlePinnedGuestImages calls cbuildbot/commands with correct args."""
111 build_pinned_guest_images_tarball = self.PatchObject(
112 commands,
113 'BuildPinnedGuestImagesTarball',
114 return_value='pinned-guest-images.tar.gz')
115 artifacts.BundlePinnedGuestImages(self.input_proto, self.output_proto)
116 self.assertEqual(
117 [artifact.path for artifact in self.output_proto.artifacts],
118 ['/tmp/artifacts/pinned-guest-images.tar.gz'])
119 self.assertEqual(build_pinned_guest_images_tarball.call_args_list,
120 [mock.call('/cros', 'target', '/tmp/artifacts')])
121
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600122 def testBundlePinnedGuestImagesNoLogs(self):
Evan Hernandezde445982019-04-22 13:42:34 -0600123 """BundlePinnedGuestImages does not die when no pinned images found."""
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600124 self.PatchObject(commands, 'BuildPinnedGuestImagesTarball',
125 return_value=None)
Evan Hernandezde445982019-04-22 13:42:34 -0600126 artifacts.BundlePinnedGuestImages(self.input_proto, self.output_proto)
127 self.assertFalse(self.output_proto.artifacts)
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600128
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600129
130class BundleFirmwareTest(BundleTestCase):
131 """Unittests for BundleFirmware."""
132
133 def testBundleFirmware(self):
134 """BundleFirmware calls cbuildbot/commands with correct args."""
135 build_firmware_archive = self.PatchObject(
136 commands, 'BuildFirmwareArchive', return_value='firmware.tar.gz')
137 artifacts.BundleFirmware(self.input_proto, self.output_proto)
138 self.assertEqual(
139 [artifact.path for artifact in self.output_proto.artifacts],
140 ['/tmp/artifacts/firmware.tar.gz'])
141 self.assertEqual(build_firmware_archive.call_args_list,
142 [mock.call('/cros', 'target', '/tmp/artifacts')])
143
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600144 def testBundleFirmwareNoLogs(self):
145 """BundleFirmware dies when no firmware found."""
146 self.PatchObject(commands, 'BuildFirmwareArchive', return_value=None)
147 with self.assertRaises(cros_build_lib.DieSystemExit):
148 artifacts.BundleFirmware(self.input_proto, self.output_proto)
149
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600150
151class BundleEbuildLogsTest(BundleTestCase):
152 """Unittests for BundleEbuildLogs."""
153
154 def testBundleEbuildLogs(self):
155 """BundleEbuildLogs calls cbuildbot/commands with correct args."""
156 build_ebuild_logs_tarball = self.PatchObject(
157 commands, 'BuildEbuildLogsTarball', return_value='ebuild-logs.tar.gz')
158 artifacts.BundleEbuildLogs(self.input_proto, self.output_proto)
159 self.assertEqual(
160 [artifact.path for artifact in self.output_proto.artifacts],
161 ['/tmp/artifacts/ebuild-logs.tar.gz'])
Evan Hernandeza478d802019-04-08 15:08:24 -0600162 self.assertEqual(
163 build_ebuild_logs_tarball.call_args_list,
164 [mock.call('/cros/chroot/build', 'target', '/tmp/artifacts')])
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600165
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600166 def testBundleEbuildLogsNoLogs(self):
167 """BundleEbuildLogs dies when no logs found."""
168 self.PatchObject(commands, 'BuildEbuildLogsTarball', return_value=None)
169 with self.assertRaises(cros_build_lib.DieSystemExit):
170 artifacts.BundleEbuildLogs(self.input_proto, self.output_proto)
171
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600172
173class BundleTestUpdatePayloadsTest(cros_test_lib.MockTempDirTestCase):
174 """Unittests for BundleTestUpdatePayloads."""
175
176 def setUp(self):
177 self.source_root = os.path.join(self.tempdir, 'cros')
178 osutils.SafeMakedirs(self.source_root)
179
180 self.archive_root = os.path.join(self.tempdir, 'output')
181 osutils.SafeMakedirs(self.archive_root)
182
183 self.target = 'target'
Evan Hernandez59690b72019-04-08 16:24:45 -0600184 self.image_root = os.path.join(self.source_root,
185 'src/build/images/target/latest')
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600186
187 self.input_proto = artifacts_pb2.BundleRequest()
188 self.input_proto.build_target.name = self.target
189 self.input_proto.output_dir = self.archive_root
190 self.output_proto = artifacts_pb2.BundleResponse()
191
192 self.PatchObject(constants, 'SOURCE_ROOT', new=self.source_root)
193
194 def MockGeneratePayloads(image_path, archive_dir, **kwargs):
195 assert kwargs
196 osutils.WriteFile(os.path.join(archive_dir, 'payload.bin'), image_path)
197
198 self.generate_payloads = self.PatchObject(
199 commands, 'GeneratePayloads', side_effect=MockGeneratePayloads)
200
201 def MockGenerateQuickProvisionPayloads(image_path, archive_dir):
202 osutils.WriteFile(os.path.join(archive_dir, 'payload-qp.bin'), image_path)
203
204 self.generate_quick_provision_payloads = self.PatchObject(
205 commands,
206 'GenerateQuickProvisionPayloads',
207 side_effect=MockGenerateQuickProvisionPayloads)
208
209 def testBundleTestUpdatePayloads(self):
210 """BundleTestUpdatePayloads calls cbuildbot/commands with correct args."""
211 image_path = os.path.join(self.image_root, constants.BASE_IMAGE_BIN)
212 osutils.WriteFile(image_path, 'image!', makedirs=True)
213
214 artifacts.BundleTestUpdatePayloads(self.input_proto, self.output_proto)
215
216 actual = [
217 os.path.relpath(artifact.path, self.archive_root)
218 for artifact in self.output_proto.artifacts
219 ]
220 expected = ['payload.bin', 'payload-qp.bin']
221 self.assertItemsEqual(actual, expected)
222
223 actual = [
224 os.path.relpath(path, self.archive_root)
225 for path in osutils.DirectoryIterator(self.archive_root)
226 ]
227 self.assertItemsEqual(actual, expected)
228
229 self.assertEqual(self.generate_payloads.call_args_list, [
230 mock.call(image_path, mock.ANY, full=True, stateful=True, delta=True),
231 ])
232
233 self.assertEqual(self.generate_quick_provision_payloads.call_args_list,
234 [mock.call(image_path, mock.ANY)])
235
Evan Hernandez9f125ac2019-04-08 17:18:47 -0600236 def testBundleTestUpdatePayloadsNoImageDir(self):
237 """BundleTestUpdatePayloads dies if no image dir is found."""
238 # Intentionally do not write image directory.
239 with self.assertRaises(cros_build_lib.DieSystemExit):
240 artifacts.BundleTestUpdatePayloads(self.input_proto, self.output_proto)
241
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600242 def testBundleTestUpdatePayloadsNoImage(self):
243 """BundleTestUpdatePayloads dies if no usable image is found for target."""
Evan Hernandez9f125ac2019-04-08 17:18:47 -0600244 # Intentionally do not write image, but create the directory.
245 osutils.SafeMakedirs(self.image_root)
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600246 with self.assertRaises(cros_build_lib.DieSystemExit):
247 artifacts.BundleTestUpdatePayloads(self.input_proto, self.output_proto)
Alex Klein6504eca2019-04-18 15:37:56 -0600248
249
Alex Klein2275d692019-04-23 16:04:12 -0600250class BundleSimpleChromeArtifactsTest(cros_test_lib.MockTempDirTestCase):
251 """BundleSimpleChromeArtifacts tests."""
252
253 def setUp(self):
254 self.chroot_dir = os.path.join(self.tempdir, 'chroot_dir')
255 self.sysroot_path = '/sysroot'
256 self.sysroot_dir = os.path.join(self.chroot_dir, 'sysroot')
257 osutils.SafeMakedirs(self.sysroot_dir)
258 self.output_dir = os.path.join(self.tempdir, 'output_dir')
259 osutils.SafeMakedirs(self.output_dir)
260
261 self.does_not_exist = os.path.join(self.tempdir, 'does_not_exist')
262
263 def _GetRequest(self, chroot=None, sysroot=None, build_target=None,
264 output_dir=None):
265 """Helper to create a request message instance.
266
267 Args:
268 chroot (str): The chroot path.
269 sysroot (str): The sysroot path.
270 build_target (str): The build target name.
271 output_dir (str): The output directory.
272 """
273 return artifacts_pb2.BundleRequest(
274 sysroot={'path': sysroot, 'build_target': {'name': build_target}},
275 chroot={'path': chroot}, output_dir=output_dir)
276
277 def _GetResponse(self):
278 return artifacts_pb2.BundleResponse()
279
280 def testNoBuildTarget(self):
281 """Test no build target fails."""
282 request = self._GetRequest(chroot=self.chroot_dir,
283 sysroot=self.sysroot_path,
284 output_dir=self.output_dir)
285 response = self._GetResponse()
286 with self.assertRaises(cros_build_lib.DieSystemExit):
287 artifacts.BundleSimpleChromeArtifacts(request, response)
288
289 def testNoSysroot(self):
290 """Test no sysroot fails."""
291 request = self._GetRequest(build_target='board', output_dir=self.output_dir)
292 response = self._GetResponse()
293 with self.assertRaises(cros_build_lib.DieSystemExit):
294 artifacts.BundleSimpleChromeArtifacts(request, response)
295
296 def testSysrootDoesNotExist(self):
297 """Test no sysroot fails."""
298 request = self._GetRequest(build_target='board', output_dir=self.output_dir,
299 sysroot=self.does_not_exist)
300 response = self._GetResponse()
301 with self.assertRaises(cros_build_lib.DieSystemExit):
302 artifacts.BundleSimpleChromeArtifacts(request, response)
303
304 def testNoOutputDir(self):
305 """Test no output dir fails."""
306 request = self._GetRequest(chroot=self.chroot_dir,
307 sysroot=self.sysroot_path,
308 build_target='board')
309 response = self._GetResponse()
310 with self.assertRaises(cros_build_lib.DieSystemExit):
311 artifacts.BundleSimpleChromeArtifacts(request, response)
312
313 def testOutputDirDoesNotExist(self):
314 """Test no output dir fails."""
315 request = self._GetRequest(chroot=self.chroot_dir,
316 sysroot=self.sysroot_path,
317 build_target='board',
318 output_dir=self.does_not_exist)
319 response = self._GetResponse()
320 with self.assertRaises(cros_build_lib.DieSystemExit):
321 artifacts.BundleSimpleChromeArtifacts(request, response)
322
323 def testOutputHandling(self):
324 """Test response output."""
325 files = ['file1', 'file2', 'file3']
326 expected_files = [os.path.join(self.output_dir, f) for f in files]
327 self.PatchObject(artifacts_svc, 'BundleSimpleChromeArtifacts',
328 return_value=expected_files)
329 request = self._GetRequest(chroot=self.chroot_dir,
330 sysroot=self.sysroot_path,
331 build_target='board', output_dir=self.output_dir)
332 response = self._GetResponse()
333
334 artifacts.BundleSimpleChromeArtifacts(request, response)
335
336 self.assertTrue(response.artifacts)
337 self.assertItemsEqual(expected_files, [a.path for a in response.artifacts])
338
339
Alex Klein6504eca2019-04-18 15:37:56 -0600340class BundleVmFilesTest(cros_test_lib.MockTestCase):
341 """BuildVmFiles tests."""
342
343 def _GetInput(self, chroot=None, sysroot=None, test_results_dir=None,
344 output_dir=None):
345 """Helper to build out an input message instance.
346
347 Args:
348 chroot (str|None): The chroot path.
349 sysroot (str|None): The sysroot path relative to the chroot.
350 test_results_dir (str|None): The test results directory relative to the
351 sysroot.
352 output_dir (str|None): The directory where the results tarball should be
353 saved.
354 """
355 return artifacts_pb2.BundleVmFilesRequest(
356 chroot={'path': chroot}, sysroot={'path': sysroot},
357 test_results_dir=test_results_dir, output_dir=output_dir,
358 )
359
360 def _GetOutput(self):
361 """Helper to get an empty output message instance."""
362 return artifacts_pb2.BundleResponse()
363
364 def testChrootMissing(self):
365 """Test error handling for missing chroot."""
366 in_proto = self._GetInput(sysroot='/build/board',
367 test_results_dir='/test/results',
368 output_dir='/tmp/output')
369 out_proto = self._GetOutput()
370
371 with self.assertRaises(cros_build_lib.DieSystemExit):
372 artifacts.BundleVmFiles(in_proto, out_proto)
373
374 def testSysrootMissing(self):
375 """Test error handling for missing sysroot."""
376 in_proto = self._GetInput(chroot='/chroot/dir',
377 test_results_dir='/test/results',
378 output_dir='/tmp/output')
379 out_proto = self._GetOutput()
380
381 with self.assertRaises(cros_build_lib.DieSystemExit):
382 artifacts.BundleVmFiles(in_proto, out_proto)
383
384
385 def testTestResultsDirMissing(self):
386 """Test error handling for missing test results directory."""
387 in_proto = self._GetInput(chroot='/chroot/dir', sysroot='/build/board',
388 output_dir='/tmp/output')
389 out_proto = self._GetOutput()
390
391 with self.assertRaises(cros_build_lib.DieSystemExit):
392 artifacts.BundleVmFiles(in_proto, out_proto)
393
394 def testOutputDirMissing(self):
395 """Test error handling for missing output directory."""
396 in_proto = self._GetInput(chroot='/chroot/dir', sysroot='/build/board',
397 test_results_dir='/test/results')
398 out_proto = self._GetOutput()
399
400 with self.assertRaises(cros_build_lib.DieSystemExit):
401 artifacts.BundleVmFiles(in_proto, out_proto)
402
403 def testValidCall(self):
404 """Test image dir building."""
405 in_proto = self._GetInput(chroot='/chroot/dir', sysroot='/build/board',
406 test_results_dir='/test/results',
407 output_dir='/tmp/output')
408 out_proto = self._GetOutput()
409 expected_files = ['/tmp/output/f1.tar', '/tmp/output/f2.tar']
410 patch = self.PatchObject(vm_test_stages, 'ArchiveVMFilesFromImageDir',
411 return_value=expected_files)
412
413 artifacts.BundleVmFiles(in_proto, out_proto)
414
415 patch.assert_called_with('/chroot/dir/build/board/test/results',
416 '/tmp/output')
417
418 # Make sure we have artifacts, and that every artifact is an expected file.
419 self.assertTrue(out_proto.artifacts)
420 for artifact in out_proto.artifacts:
421 self.assertIn(artifact.path, expected_files)
422 expected_files.remove(artifact.path)
423
424 # Make sure we've seen all of the expected files.
425 self.assertFalse(expected_files)