blob: 0ee49cf7f994791d47396d0c651227e5ba4d0e0a [file] [log] [blame]
Allen Li4ad0c3b2017-05-05 13:12:11 -07001#!/usr/bin/python
Michael Tang97d188c2016-06-25 11:18:42 -07002# Copyright 2016 The Chromium OS Authors. All rights reserved.
J. Richard Barnetteea785362014-03-17 16:00:53 -07003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Keith Haddow5ba5fb82016-11-09 11:39:36 -08006import __builtin__
J. Richard Barnetteea785362014-03-17 16:00:53 -07007import Queue
J. Richard Barnetteea785362014-03-17 16:00:53 -07008import logging
9import os
10import shutil
J. Richard Barnette2e443ef2014-05-20 12:31:35 -070011import signal
Laurence Goodbyca7726d2017-02-14 17:09:07 -080012import stat
J. Richard Barnetteea785362014-03-17 16:00:53 -070013import sys
Rohit Makasanac1c7e782019-04-02 18:06:00 -070014import tarfile
J. Richard Barnetteea785362014-03-17 16:00:53 -070015import tempfile
16import time
17import unittest
18
Allen Li9579b382017-05-05 17:07:43 -070019import mock
J. Richard Barnetteea785362014-03-17 16:00:53 -070020import mox
21
22import common
Allen Li5ed7e632017-02-03 16:31:33 -080023from autotest_lib.client.common_lib import global_config
Dan Shi1b4c7c32015-10-05 10:38:57 -070024from autotest_lib.client.common_lib import utils
Michael Tange8bc9592017-07-06 10:59:32 -070025#For unittest without cloud_client.proto compiled.
26try:
27 from autotest_lib.site_utils import cloud_console_client
28except ImportError:
29 cloud_console_client = None
Prathmesh Prabhubeb9e012017-01-30 16:18:39 -080030from autotest_lib.site_utils import gs_offloader
31from autotest_lib.site_utils import job_directories
Prathmesh Prabhud47e9d32019-03-13 13:04:05 -070032from autotest_lib.site_utils import job_directories_unittest as jd_test
Ningning Xia2d981ee2016-07-06 17:59:54 -070033from autotest_lib.tko import models
Allen Lib41527d2017-06-22 17:28:00 -070034from autotest_lib.utils import gslib
Michael Tang0f553bd2017-06-16 17:38:45 -070035from autotest_lib.site_utils import pubsub_utils
Allen Lib41527d2017-06-22 17:28:00 -070036from chromite.lib import timeout_util
Jakob Juelich24f22c22014-09-26 11:46:11 -070037
J. Richard Barnetteea785362014-03-17 16:00:53 -070038# Test value to use for `days_old`, if nothing else is required.
39_TEST_EXPIRATION_AGE = 7
40
J. Richard Barnetteea785362014-03-17 16:00:53 -070041
42def _get_options(argv):
43 """Helper function to exercise command line parsing.
44
45 @param argv Value of sys.argv to be parsed.
46
47 """
48 sys.argv = ['bogus.py'] + argv
49 return gs_offloader.parse_options()
50
51
Laurence Goodbyca7726d2017-02-14 17:09:07 -080052def is_fifo(path):
Allen Li93585382017-05-05 14:24:53 -070053 """Determines whether a path is a fifo.
54
55 @param path: fifo path string.
56 """
Laurence Goodbyca7726d2017-02-14 17:09:07 -080057 return stat.S_ISFIFO(os.lstat(path).st_mode)
58
59
Simran Basidd129972014-09-11 14:34:49 -070060class OffloaderOptionsTests(mox.MoxTestBase):
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -070061 """Tests for the `Offloader` constructor.
62
63 Tests that offloader instance fields are set as expected
64 for given command line options.
65
66 """
67
Allen Li7402f092018-06-26 15:42:21 -070068 _REGULAR_ONLY = {job_directories.SwarmingJobDirectory,
69 job_directories.RegularJobDirectory}
70 _SPECIAL_ONLY = {job_directories.SwarmingJobDirectory,
71 job_directories.SpecialJobDirectory}
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -070072 _BOTH = _REGULAR_ONLY | _SPECIAL_ONLY
J. Richard Barnetteea785362014-03-17 16:00:53 -070073
Jakob Juelich24f22c22014-09-26 11:46:11 -070074
Simran Basidd129972014-09-11 14:34:49 -070075 def setUp(self):
76 super(OffloaderOptionsTests, self).setUp()
77 self.mox.StubOutWithMock(utils, 'get_offload_gsuri')
Simran Basif3e305f2014-10-03 14:43:53 -070078 gs_offloader.GS_OFFLOADING_ENABLED = True
Michael Tang0df2eb42016-05-13 19:06:54 -070079 gs_offloader.GS_OFFLOADER_MULTIPROCESSING = False
Simran Basidd129972014-09-11 14:34:49 -070080
Jakob Juelich24f22c22014-09-26 11:46:11 -070081
Allen Lib41527d2017-06-22 17:28:00 -070082 def _mock_get_sub_offloader(self, is_moblab, multiprocessing=False,
Michael Tang0f553bd2017-06-16 17:38:45 -070083 console_client=None, delete_age=0):
Simran Basidd129972014-09-11 14:34:49 -070084 """Mock the process of getting the offload_dir function."""
85 if is_moblab:
86 expected_gsuri = '%sresults/%s/%s/' % (
87 global_config.global_config.get_config_value(
88 'CROS', 'image_storage_server'),
89 'Fa:ke:ma:c0:12:34', 'rand0m-uu1d')
90 else:
91 expected_gsuri = utils.DEFAULT_OFFLOAD_GSURI
92 utils.get_offload_gsuri().AndReturn(expected_gsuri)
Allen Lib41527d2017-06-22 17:28:00 -070093 sub_offloader = gs_offloader.GSOffloader(expected_gsuri,
Michael Tang0f553bd2017-06-16 17:38:45 -070094 multiprocessing, delete_age, console_client)
Allen Lib41527d2017-06-22 17:28:00 -070095 self.mox.StubOutWithMock(gs_offloader, 'GSOffloader')
Michael Tange8bc9592017-07-06 10:59:32 -070096 if cloud_console_client:
97 self.mox.StubOutWithMock(cloud_console_client,
98 'is_cloud_notification_enabled')
Michael Tang0f553bd2017-06-16 17:38:45 -070099 if console_client:
100 cloud_console_client.is_cloud_notification_enabled().AndReturn(True)
101 gs_offloader.GSOffloader(
102 expected_gsuri, multiprocessing, delete_age,
103 mox.IsA(cloud_console_client.PubSubBasedClient)).AndReturn(
104 sub_offloader)
105 else:
Michael Tange8bc9592017-07-06 10:59:32 -0700106 if cloud_console_client:
107 cloud_console_client.is_cloud_notification_enabled().AndReturn(
108 False)
Michael Tang0f553bd2017-06-16 17:38:45 -0700109 gs_offloader.GSOffloader(
110 expected_gsuri, multiprocessing, delete_age, None).AndReturn(
111 sub_offloader)
Simran Basidd129972014-09-11 14:34:49 -0700112 self.mox.ReplayAll()
Allen Lib41527d2017-06-22 17:28:00 -0700113 return sub_offloader
Simran Basidd129972014-09-11 14:34:49 -0700114
Jakob Juelich24f22c22014-09-26 11:46:11 -0700115
J. Richard Barnetteea785362014-03-17 16:00:53 -0700116 def test_process_no_options(self):
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700117 """Test default offloader options."""
Allen Lib41527d2017-06-22 17:28:00 -0700118 sub_offloader = self._mock_get_sub_offloader(False)
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700119 offloader = gs_offloader.Offloader(_get_options([]))
120 self.assertEqual(set(offloader._jobdir_classes),
121 self._REGULAR_ONLY)
122 self.assertEqual(offloader._processes, 1)
Allen Lib41527d2017-06-22 17:28:00 -0700123 self.assertEqual(offloader._gs_offloader,
124 sub_offloader)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800125 self.assertEqual(offloader._upload_age_limit, 0)
126 self.assertEqual(offloader._delete_age_limit, 0)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700127
Jakob Juelich24f22c22014-09-26 11:46:11 -0700128
J. Richard Barnetteea785362014-03-17 16:00:53 -0700129 def test_process_all_option(self):
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700130 """Test offloader handling for the --all option."""
Allen Lib41527d2017-06-22 17:28:00 -0700131 sub_offloader = self._mock_get_sub_offloader(False)
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700132 offloader = gs_offloader.Offloader(_get_options(['--all']))
133 self.assertEqual(set(offloader._jobdir_classes), self._BOTH)
134 self.assertEqual(offloader._processes, 1)
Allen Lib41527d2017-06-22 17:28:00 -0700135 self.assertEqual(offloader._gs_offloader,
136 sub_offloader)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800137 self.assertEqual(offloader._upload_age_limit, 0)
138 self.assertEqual(offloader._delete_age_limit, 0)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700139
Jakob Juelich24f22c22014-09-26 11:46:11 -0700140
J. Richard Barnetteea785362014-03-17 16:00:53 -0700141 def test_process_hosts_option(self):
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700142 """Test offloader handling for the --hosts option."""
Allen Lib41527d2017-06-22 17:28:00 -0700143 sub_offloader = self._mock_get_sub_offloader(False)
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700144 offloader = gs_offloader.Offloader(
145 _get_options(['--hosts']))
146 self.assertEqual(set(offloader._jobdir_classes),
147 self._SPECIAL_ONLY)
148 self.assertEqual(offloader._processes, 1)
Allen Lib41527d2017-06-22 17:28:00 -0700149 self.assertEqual(offloader._gs_offloader,
150 sub_offloader)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800151 self.assertEqual(offloader._upload_age_limit, 0)
152 self.assertEqual(offloader._delete_age_limit, 0)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700153
Jakob Juelich24f22c22014-09-26 11:46:11 -0700154
J. Richard Barnetteea785362014-03-17 16:00:53 -0700155 def test_parallelism_option(self):
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700156 """Test offloader handling for the --parallelism option."""
Allen Lib41527d2017-06-22 17:28:00 -0700157 sub_offloader = self._mock_get_sub_offloader(False)
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700158 offloader = gs_offloader.Offloader(
159 _get_options(['--parallelism', '2']))
160 self.assertEqual(set(offloader._jobdir_classes),
161 self._REGULAR_ONLY)
162 self.assertEqual(offloader._processes, 2)
Allen Lib41527d2017-06-22 17:28:00 -0700163 self.assertEqual(offloader._gs_offloader,
164 sub_offloader)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800165 self.assertEqual(offloader._upload_age_limit, 0)
166 self.assertEqual(offloader._delete_age_limit, 0)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700167
Jakob Juelich24f22c22014-09-26 11:46:11 -0700168
J. Richard Barnetteea785362014-03-17 16:00:53 -0700169 def test_delete_only_option(self):
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700170 """Test offloader handling for the --delete_only option."""
171 offloader = gs_offloader.Offloader(
172 _get_options(['--delete_only']))
173 self.assertEqual(set(offloader._jobdir_classes),
174 self._REGULAR_ONLY)
175 self.assertEqual(offloader._processes, 1)
Allen Lib41527d2017-06-22 17:28:00 -0700176 self.assertIsInstance(offloader._gs_offloader,
177 gs_offloader.FakeGSOffloader)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800178 self.assertEqual(offloader._upload_age_limit, 0)
179 self.assertEqual(offloader._delete_age_limit, 0)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700180
Jakob Juelich24f22c22014-09-26 11:46:11 -0700181
Simran Basidf4751e2014-10-10 14:19:22 -0700182 def test_days_old_option(self):
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700183 """Test offloader handling for the --days_old option."""
Allen Lib41527d2017-06-22 17:28:00 -0700184 sub_offloader = self._mock_get_sub_offloader(False, delete_age=7)
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700185 offloader = gs_offloader.Offloader(
186 _get_options(['--days_old', '7']))
187 self.assertEqual(set(offloader._jobdir_classes),
188 self._REGULAR_ONLY)
189 self.assertEqual(offloader._processes, 1)
Allen Lib41527d2017-06-22 17:28:00 -0700190 self.assertEqual(offloader._gs_offloader,
191 sub_offloader)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800192 self.assertEqual(offloader._upload_age_limit, 7)
193 self.assertEqual(offloader._delete_age_limit, 7)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700194
Jakob Juelich24f22c22014-09-26 11:46:11 -0700195
Simran Basidd129972014-09-11 14:34:49 -0700196 def test_moblab_gsuri_generation(self):
197 """Test offloader construction for Moblab."""
Allen Lib41527d2017-06-22 17:28:00 -0700198 sub_offloader = self._mock_get_sub_offloader(True)
Simran Basidd129972014-09-11 14:34:49 -0700199 offloader = gs_offloader.Offloader(_get_options([]))
200 self.assertEqual(set(offloader._jobdir_classes),
201 self._REGULAR_ONLY)
202 self.assertEqual(offloader._processes, 1)
Allen Lib41527d2017-06-22 17:28:00 -0700203 self.assertEqual(offloader._gs_offloader,
204 sub_offloader)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800205 self.assertEqual(offloader._upload_age_limit, 0)
206 self.assertEqual(offloader._delete_age_limit, 0)
Simran Basidd129972014-09-11 14:34:49 -0700207
J. Richard Barnetteea785362014-03-17 16:00:53 -0700208
Simran Basif3e305f2014-10-03 14:43:53 -0700209 def test_globalconfig_offloading_flag(self):
210 """Test enabling of --delete_only via global_config."""
211 gs_offloader.GS_OFFLOADING_ENABLED = False
212 offloader = gs_offloader.Offloader(
213 _get_options([]))
Allen Lib41527d2017-06-22 17:28:00 -0700214 self.assertIsInstance(offloader._gs_offloader,
215 gs_offloader.FakeGSOffloader)
Simran Basif3e305f2014-10-03 14:43:53 -0700216
Michael Tang0df2eb42016-05-13 19:06:54 -0700217 def test_offloader_multiprocessing_flag_set(self):
218 """Test multiprocessing is set."""
Allen Lib41527d2017-06-22 17:28:00 -0700219 sub_offloader = self._mock_get_sub_offloader(True, True)
Michael Tang0df2eb42016-05-13 19:06:54 -0700220 offloader = gs_offloader.Offloader(_get_options(['-m']))
Allen Lib41527d2017-06-22 17:28:00 -0700221 self.assertEqual(offloader._gs_offloader,
222 sub_offloader)
Michael Tang0df2eb42016-05-13 19:06:54 -0700223 self.mox.VerifyAll()
224
225 def test_offloader_multiprocessing_flag_not_set_default_false(self):
226 """Test multiprocessing is set."""
227 gs_offloader.GS_OFFLOADER_MULTIPROCESSING = False
Allen Lib41527d2017-06-22 17:28:00 -0700228 sub_offloader = self._mock_get_sub_offloader(True, False)
Michael Tang0df2eb42016-05-13 19:06:54 -0700229 offloader = gs_offloader.Offloader(_get_options([]))
Allen Lib41527d2017-06-22 17:28:00 -0700230 self.assertEqual(offloader._gs_offloader,
231 sub_offloader)
Michael Tang0df2eb42016-05-13 19:06:54 -0700232 self.mox.VerifyAll()
233
234 def test_offloader_multiprocessing_flag_not_set_default_true(self):
235 """Test multiprocessing is set."""
236 gs_offloader.GS_OFFLOADER_MULTIPROCESSING = True
Allen Lib41527d2017-06-22 17:28:00 -0700237 sub_offloader = self._mock_get_sub_offloader(True, True)
Michael Tang0df2eb42016-05-13 19:06:54 -0700238 offloader = gs_offloader.Offloader(_get_options([]))
Allen Lib41527d2017-06-22 17:28:00 -0700239 self.assertEqual(offloader._gs_offloader,
240 sub_offloader)
Michael Tang0df2eb42016-05-13 19:06:54 -0700241 self.mox.VerifyAll()
242
Michael Tang97d188c2016-06-25 11:18:42 -0700243
Michael Tang0f553bd2017-06-16 17:38:45 -0700244 def test_offloader_pubsub_enabled(self):
Michael Tang97d188c2016-06-25 11:18:42 -0700245 """Test multiprocessing is set."""
Michael Tange8bc9592017-07-06 10:59:32 -0700246 if not cloud_console_client:
247 return
Michael Tang0f553bd2017-06-16 17:38:45 -0700248 self.mox.StubOutWithMock(pubsub_utils, "PubSubClient")
249 sub_offloader = self._mock_get_sub_offloader(True, False,
250 cloud_console_client.PubSubBasedClient())
251 offloader = gs_offloader.Offloader(_get_options([]))
Allen Lib41527d2017-06-22 17:28:00 -0700252 self.assertEqual(offloader._gs_offloader,
253 sub_offloader)
Michael Tang97d188c2016-06-25 11:18:42 -0700254 self.mox.VerifyAll()
255
Simran Basif3e305f2014-10-03 14:43:53 -0700256
J. Richard Barnetteea785362014-03-17 16:00:53 -0700257class _MockJobDirectory(job_directories._JobDirectory):
258 """Subclass of `_JobDirectory` used as a helper for tests."""
259
260 GLOB_PATTERN = '[0-9]*-*'
261
Jakob Juelich24f22c22014-09-26 11:46:11 -0700262
J. Richard Barnetteea785362014-03-17 16:00:53 -0700263 def __init__(self, resultsdir):
264 """Create new job in initial state."""
265 super(_MockJobDirectory, self).__init__(resultsdir)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700266 self._timestamp = None
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800267 self.queue_args = [resultsdir, os.path.dirname(resultsdir), self._timestamp]
J. Richard Barnetteea785362014-03-17 16:00:53 -0700268
Jakob Juelich24f22c22014-09-26 11:46:11 -0700269
J. Richard Barnetteea785362014-03-17 16:00:53 -0700270 def get_timestamp_if_finished(self):
271 return self._timestamp
272
Jakob Juelich24f22c22014-09-26 11:46:11 -0700273
J. Richard Barnetteea785362014-03-17 16:00:53 -0700274 def set_finished(self, days_old):
275 """Make this job appear to be finished.
276
277 After calling this function, calls to `enqueue_offload()`
278 will find this job as finished, but not expired and ready
279 for offload. Note that when `days_old` is 0,
280 `enqueue_offload()` will treat a finished job as eligible
281 for offload.
282
283 @param days_old The value of the `days_old` parameter that
284 will be passed to `enqueue_offload()` for
285 testing.
286
287 """
Prathmesh Prabhud47e9d32019-03-13 13:04:05 -0700288 self._timestamp = jd_test.make_timestamp(days_old, False)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800289 self.queue_args[2] = self._timestamp
J. Richard Barnetteea785362014-03-17 16:00:53 -0700290
Jakob Juelich24f22c22014-09-26 11:46:11 -0700291
J. Richard Barnetteea785362014-03-17 16:00:53 -0700292 def set_expired(self, days_old):
293 """Make this job eligible to be offloaded.
294
295 After calling this function, calls to `offload` will attempt
296 to offload this job.
297
298 @param days_old The value of the `days_old` parameter that
299 will be passed to `enqueue_offload()` for
300 testing.
301
302 """
Prathmesh Prabhud47e9d32019-03-13 13:04:05 -0700303 self._timestamp = jd_test.make_timestamp(days_old, True)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800304 self.queue_args[2] = self._timestamp
J. Richard Barnetteea785362014-03-17 16:00:53 -0700305
Jakob Juelich24f22c22014-09-26 11:46:11 -0700306
J. Richard Barnetteea785362014-03-17 16:00:53 -0700307 def set_incomplete(self):
308 """Make this job appear to have failed offload just once."""
Allen Lib41527d2017-06-22 17:28:00 -0700309 self.offload_count += 1
310 self.first_offload_start = time.time()
311 if not os.path.isdir(self.dirname):
312 os.mkdir(self.dirname)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700313
Jakob Juelich24f22c22014-09-26 11:46:11 -0700314
J. Richard Barnetteea785362014-03-17 16:00:53 -0700315 def set_reportable(self):
316 """Make this job be reportable."""
J. Richard Barnetteea785362014-03-17 16:00:53 -0700317 self.set_incomplete()
Allen Lib41527d2017-06-22 17:28:00 -0700318 self.offload_count += 1
J. Richard Barnetteea785362014-03-17 16:00:53 -0700319
Jakob Juelich24f22c22014-09-26 11:46:11 -0700320
J. Richard Barnetteea785362014-03-17 16:00:53 -0700321 def set_complete(self):
322 """Make this job be completed."""
Allen Lib41527d2017-06-22 17:28:00 -0700323 self.offload_count += 1
324 if os.path.isdir(self.dirname):
325 os.rmdir(self.dirname)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700326
327
Simran Basi1e10e922015-04-16 15:09:56 -0700328 def process_gs_instructions(self):
329 """Always still offload the job directory."""
330 return True
331
332
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700333class CommandListTests(unittest.TestCase):
Allen Lib41527d2017-06-22 17:28:00 -0700334 """Tests for `_get_cmd_list()`."""
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700335
MK Ryue93c8572015-08-11 11:53:00 -0700336 def _command_list_assertions(self, job, use_rsync=True, multi=False):
Allen Lib41527d2017-06-22 17:28:00 -0700337 """Call `_get_cmd_list()` and check the return value.
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700338
339 Check the following assertions:
340 * The command name (argv[0]) is 'gsutil'.
MK Ryue93c8572015-08-11 11:53:00 -0700341 * '-m' option (argv[1]) is on when the argument, multi, is True.
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700342 * The arguments contain the 'cp' subcommand.
343 * The next-to-last argument (the source directory) is the
344 job's `queue_args[0]`.
Simran Basidd129972014-09-11 14:34:49 -0700345 * The last argument (the destination URL) is the job's
346 'queue_args[1]'.
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700347
348 @param job A job with properly calculated arguments to
Allen Lib41527d2017-06-22 17:28:00 -0700349 `_get_cmd_list()`
MK Ryue93c8572015-08-11 11:53:00 -0700350 @param use_rsync True when using 'rsync'. False when using 'cp'.
351 @param multi True when using '-m' option for gsutil.
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700352
353 """
Jakob Juelich24f22c22014-09-26 11:46:11 -0700354 test_bucket_uri = 'gs://a-test-bucket'
355
356 gs_offloader.USE_RSYNC_ENABLED = use_rsync
357
Allen Lib41527d2017-06-22 17:28:00 -0700358 command = gs_offloader._get_cmd_list(
MK Ryue93c8572015-08-11 11:53:00 -0700359 multi, job.queue_args[0],
360 os.path.join(test_bucket_uri, job.queue_args[1]))
Jakob Juelich24f22c22014-09-26 11:46:11 -0700361
Dan Shi365049f2017-05-28 08:00:02 +0000362 self.assertEqual(command[0], 'gsutil')
MK Ryue93c8572015-08-11 11:53:00 -0700363 if multi:
364 self.assertEqual(command[1], '-m')
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700365 self.assertEqual(command[-2], job.queue_args[0])
Jakob Juelich24f22c22014-09-26 11:46:11 -0700366
367 if use_rsync:
368 self.assertTrue('rsync' in command)
369 self.assertEqual(command[-1],
370 os.path.join(test_bucket_uri, job.queue_args[0]))
371 else:
372 self.assertTrue('cp' in command)
373 self.assertEqual(command[-1],
374 os.path.join(test_bucket_uri, job.queue_args[1]))
375
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700376
Allen Lib41527d2017-06-22 17:28:00 -0700377 def test__get_cmd_list_regular(self):
378 """Test `_get_cmd_list()` as for a regular job."""
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700379 job = _MockJobDirectory('118-debug')
380 self._command_list_assertions(job)
381
Jakob Juelich24f22c22014-09-26 11:46:11 -0700382
Allen Lib41527d2017-06-22 17:28:00 -0700383 def test__get_cmd_list_special(self):
384 """Test `_get_cmd_list()` as for a special job."""
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700385 job = _MockJobDirectory('hosts/host1/118-reset')
386 self._command_list_assertions(job)
387
388
Jakob Juelich24f22c22014-09-26 11:46:11 -0700389 def test_get_cmd_list_regular_no_rsync(self):
Allen Lib41527d2017-06-22 17:28:00 -0700390 """Test `_get_cmd_list()` as for a regular job."""
Jakob Juelich24f22c22014-09-26 11:46:11 -0700391 job = _MockJobDirectory('118-debug')
392 self._command_list_assertions(job, use_rsync=False)
393
394
395 def test_get_cmd_list_special_no_rsync(self):
Allen Lib41527d2017-06-22 17:28:00 -0700396 """Test `_get_cmd_list()` as for a special job."""
Jakob Juelich24f22c22014-09-26 11:46:11 -0700397 job = _MockJobDirectory('hosts/host1/118-reset')
398 self._command_list_assertions(job, use_rsync=False)
399
400
MK Ryue93c8572015-08-11 11:53:00 -0700401 def test_get_cmd_list_regular_multi(self):
Allen Lib41527d2017-06-22 17:28:00 -0700402 """Test `_get_cmd_list()` as for a regular job with True multi."""
MK Ryue93c8572015-08-11 11:53:00 -0700403 job = _MockJobDirectory('118-debug')
404 self._command_list_assertions(job, multi=True)
405
406
Allen Lib41527d2017-06-22 17:28:00 -0700407 def test__get_cmd_list_special_multi(self):
408 """Test `_get_cmd_list()` as for a special job with True multi."""
MK Ryue93c8572015-08-11 11:53:00 -0700409 job = _MockJobDirectory('hosts/host1/118-reset')
410 self._command_list_assertions(job, multi=True)
411
412
Allen Lib41527d2017-06-22 17:28:00 -0700413class _TempResultsDirTestCase(unittest.TestCase):
414 """Mixin class for tests using a temporary results directory."""
J. Richard Barnetteea785362014-03-17 16:00:53 -0700415
J. Richard Barnette08800322014-05-16 14:49:46 -0700416 REGULAR_JOBLIST = [
417 '111-fubar', '112-fubar', '113-fubar', '114-snafu']
418 HOST_LIST = ['host1', 'host2', 'host3']
419 SPECIAL_JOBLIST = [
420 'hosts/host1/333-reset', 'hosts/host1/334-reset',
421 'hosts/host2/444-reset', 'hosts/host3/555-reset']
422
Jakob Juelich24f22c22014-09-26 11:46:11 -0700423
J. Richard Barnetteea785362014-03-17 16:00:53 -0700424 def setUp(self):
Allen Lib41527d2017-06-22 17:28:00 -0700425 super(_TempResultsDirTestCase, self).setUp()
J. Richard Barnette08800322014-05-16 14:49:46 -0700426 self._resultsroot = tempfile.mkdtemp()
427 self._cwd = os.getcwd()
428 os.chdir(self._resultsroot)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700429
Jakob Juelich24f22c22014-09-26 11:46:11 -0700430
J. Richard Barnetteea785362014-03-17 16:00:53 -0700431 def tearDown(self):
J. Richard Barnette08800322014-05-16 14:49:46 -0700432 os.chdir(self._cwd)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700433 shutil.rmtree(self._resultsroot)
Allen Lib41527d2017-06-22 17:28:00 -0700434 super(_TempResultsDirTestCase, self).tearDown()
J. Richard Barnetteea785362014-03-17 16:00:53 -0700435
Jakob Juelich24f22c22014-09-26 11:46:11 -0700436
J. Richard Barnetteea785362014-03-17 16:00:53 -0700437 def make_job(self, jobdir):
438 """Create a job with results in `self._resultsroot`.
439
440 @param jobdir Name of the subdirectory to be created in
441 `self._resultsroot`.
442
443 """
J. Richard Barnette08800322014-05-16 14:49:46 -0700444 os.mkdir(jobdir)
445 return _MockJobDirectory(jobdir)
446
Jakob Juelich24f22c22014-09-26 11:46:11 -0700447
J. Richard Barnette08800322014-05-16 14:49:46 -0700448 def make_job_hierarchy(self):
449 """Create a sample hierarchy of job directories.
450
451 `self.REGULAR_JOBLIST` is a list of directories for regular
452 jobs to be created; `self.SPECIAL_JOBLIST` is a list of
453 directories for special jobs to be created.
454
455 """
456 for d in self.REGULAR_JOBLIST:
457 os.mkdir(d)
458 hostsdir = 'hosts'
459 os.mkdir(hostsdir)
460 for host in self.HOST_LIST:
461 os.mkdir(os.path.join(hostsdir, host))
462 for d in self.SPECIAL_JOBLIST:
463 os.mkdir(d)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700464
465
Allen Lib41527d2017-06-22 17:28:00 -0700466class _TempResultsDirTestBase(_TempResultsDirTestCase, mox.MoxTestBase):
467 """Base Mox test class for tests using a temporary results directory."""
468
469
Prathmesh Prabhu80dfb1e2017-01-30 18:01:29 -0800470class FailedOffloadsLogTest(_TempResultsDirTestBase):
471 """Test the formatting of failed offloads log file."""
472 # Below is partial sample of a failed offload log file. This text is
473 # deliberately hard-coded and then parsed to create the test data; the idea
474 # is to make sure the actual text format will be reviewed by a human being.
475 #
476 # first offload count directory
477 # --+----1----+---- ----+ ----+----1----+----2----+----3
478 _SAMPLE_DIRECTORIES_REPORT = '''\
479 =================== ====== ==============================
480 2014-03-14 15:09:26 1 118-fubar
481 2014-03-14 15:19:23 2 117-fubar
482 2014-03-14 15:29:20 6 116-fubar
483 2014-03-14 15:39:17 24 115-fubar
484 2014-03-14 15:49:14 120 114-fubar
485 2014-03-14 15:59:11 720 113-fubar
486 2014-03-14 16:09:08 5040 112-fubar
487 2014-03-14 16:19:05 40320 111-fubar
488 '''
489
490 def setUp(self):
491 super(FailedOffloadsLogTest, self).setUp()
492 self._offloader = gs_offloader.Offloader(_get_options([]))
493 self._joblist = []
494 for line in self._SAMPLE_DIRECTORIES_REPORT.split('\n')[1 : -1]:
495 date_, time_, count, dir_ = line.split()
496 job = _MockJobDirectory(dir_)
Allen Lib41527d2017-06-22 17:28:00 -0700497 job.offload_count = int(count)
Prathmesh Prabhu80dfb1e2017-01-30 18:01:29 -0800498 timestruct = time.strptime("%s %s" % (date_, time_),
499 gs_offloader.FAILED_OFFLOADS_TIME_FORMAT)
Allen Lib41527d2017-06-22 17:28:00 -0700500 job.first_offload_start = time.mktime(timestruct)
Prathmesh Prabhu80dfb1e2017-01-30 18:01:29 -0800501 # enter the jobs in reverse order, to make sure we
502 # test that the output will be sorted.
503 self._joblist.insert(0, job)
504
505
506 def assert_report_well_formatted(self, report_file):
Allen Li93585382017-05-05 14:24:53 -0700507 """Assert that report file is well formatted.
508
509 @param report_file: Path to report file
510 """
Prathmesh Prabhu80dfb1e2017-01-30 18:01:29 -0800511 with open(report_file, 'r') as f:
512 report_lines = f.read().split()
513
514 for end_of_header_index in range(len(report_lines)):
515 if report_lines[end_of_header_index].startswith('=='):
516 break
517 self.assertLess(end_of_header_index, len(report_lines),
518 'Failed to find end-of-header marker in the report')
519
520 relevant_lines = report_lines[end_of_header_index:]
521 expected_lines = self._SAMPLE_DIRECTORIES_REPORT.split()
522 self.assertListEqual(relevant_lines, expected_lines)
523
524
525 def test_failed_offload_log_format(self):
526 """Trigger an e-mail report and check its contents."""
527 log_file = os.path.join(self._resultsroot, 'failed_log')
528 report = self._offloader._log_failed_jobs_locally(self._joblist,
529 log_file=log_file)
530 self.assert_report_well_formatted(log_file)
531
532
533 def test_failed_offload_file_overwrite(self):
534 """Verify that we can saefly overwrite the log file."""
535 log_file = os.path.join(self._resultsroot, 'failed_log')
536 with open(log_file, 'w') as f:
537 f.write('boohoohoo')
538 report = self._offloader._log_failed_jobs_locally(self._joblist,
539 log_file=log_file)
540 self.assert_report_well_formatted(log_file)
541
542
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700543class OffloadDirectoryTests(_TempResultsDirTestBase):
544 """Tests for `offload_dir()`."""
545
546 def setUp(self):
547 super(OffloadDirectoryTests, self).setUp()
548 # offload_dir() logs messages; silence them.
549 self._saved_loglevel = logging.getLogger().getEffectiveLevel()
550 logging.getLogger().setLevel(logging.CRITICAL+1)
551 self._job = self.make_job(self.REGULAR_JOBLIST[0])
Allen Lib41527d2017-06-22 17:28:00 -0700552 self.mox.StubOutWithMock(gs_offloader, '_get_cmd_list')
553 alarm = mock.patch('signal.alarm', return_value=0)
554 alarm.start()
555 self.addCleanup(alarm.stop)
Ningning Xia2d981ee2016-07-06 17:59:54 -0700556 self.mox.StubOutWithMock(models.test, 'parse_job_keyval')
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700557
Jakob Juelich24f22c22014-09-26 11:46:11 -0700558
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700559 def tearDown(self):
560 logging.getLogger().setLevel(self._saved_loglevel)
561 super(OffloadDirectoryTests, self).tearDown()
562
Allen Lib41527d2017-06-22 17:28:00 -0700563 def _mock__upload_cts_testresult(self):
564 self.mox.StubOutWithMock(gs_offloader, '_upload_cts_testresult')
565 gs_offloader._upload_cts_testresult(
Ningning Xia42111242016-06-15 14:35:58 -0700566 mox.IgnoreArg(),mox.IgnoreArg()).AndReturn(None)
Jakob Juelich24f22c22014-09-26 11:46:11 -0700567
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800568 def _mock_create_marker_file(self):
569 self.mox.StubOutWithMock(__builtin__, 'open')
Allen Li9579b382017-05-05 17:07:43 -0700570 open(mox.IgnoreArg(), 'a').AndReturn(mock.MagicMock())
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800571
572
573 def _mock_offload_dir_calls(self, command, queue_args,
Allen Lib41527d2017-06-22 17:28:00 -0700574 marker_initially_exists=False):
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700575 """Mock out the calls needed by `offload_dir()`.
576
577 This covers only the calls made when there is no timeout.
578
579 @param command Command list to be returned by the mocked
Allen Lib41527d2017-06-22 17:28:00 -0700580 call to `_get_cmd_list()`.
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700581
582 """
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800583 self.mox.StubOutWithMock(os.path, 'isfile')
584 os.path.isfile(mox.IgnoreArg()).AndReturn(marker_initially_exists)
Simran Basidd129972014-09-11 14:34:49 -0700585 command.append(queue_args[0])
Allen Lib41527d2017-06-22 17:28:00 -0700586 gs_offloader._get_cmd_list(
MK Ryue93c8572015-08-11 11:53:00 -0700587 False, queue_args[0],
588 '%s%s' % (utils.DEFAULT_OFFLOAD_GSURI,
589 queue_args[1])).AndReturn(command)
Allen Lib41527d2017-06-22 17:28:00 -0700590 self._mock__upload_cts_testresult()
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700591
Jakob Juelich24f22c22014-09-26 11:46:11 -0700592
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800593 def _run_offload_dir(self, should_succeed, delete_age):
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700594 """Make one call to `offload_dir()`.
595
596 The caller ensures all mocks are set up already.
597
598 @param should_succeed True iff the call to `offload_dir()`
599 is expected to succeed and remove the
600 offloaded job directory.
601
602 """
603 self.mox.ReplayAll()
Allen Lib41527d2017-06-22 17:28:00 -0700604 gs_offloader.GSOffloader(
605 utils.DEFAULT_OFFLOAD_GSURI, False, delete_age).offload(
MK Ryue93c8572015-08-11 11:53:00 -0700606 self._job.queue_args[0],
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800607 self._job.queue_args[1],
608 self._job.queue_args[2])
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700609 self.mox.VerifyAll()
610 self.assertEqual(not should_succeed,
611 os.path.isdir(self._job.queue_args[0]))
612
Jakob Juelich24f22c22014-09-26 11:46:11 -0700613
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700614 def test_offload_success(self):
615 """Test that `offload_dir()` can succeed correctly."""
Simran Basidd129972014-09-11 14:34:49 -0700616 self._mock_offload_dir_calls(['test', '-d'],
617 self._job.queue_args)
Allen Lib41527d2017-06-22 17:28:00 -0700618 os.path.isfile(mox.IgnoreArg()).AndReturn(True)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800619 self._mock_create_marker_file()
620 self._run_offload_dir(True, 0)
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700621
Jakob Juelich24f22c22014-09-26 11:46:11 -0700622
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700623 def test_offload_failure(self):
624 """Test that `offload_dir()` can fail correctly."""
Simran Basidd129972014-09-11 14:34:49 -0700625 self._mock_offload_dir_calls(['test', '!', '-d'],
Allen Lib41527d2017-06-22 17:28:00 -0700626 self._job.queue_args)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800627 self._run_offload_dir(False, 0)
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700628
629
Dan Shiaffb9222015-04-15 17:05:47 -0700630 def test_sanitize_dir(self):
631 """Test that folder/file name with invalid character can be corrected.
632 """
633 results_folder = tempfile.mkdtemp()
Allen Lib41527d2017-06-22 17:28:00 -0700634 invalid_chars = '_'.join(['[', ']', '*', '?', '#'])
Dan Shiaffb9222015-04-15 17:05:47 -0700635 invalid_files = []
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800636 invalid_folder_name = 'invalid_name_folder_%s' % invalid_chars
Dan Shiaffb9222015-04-15 17:05:47 -0700637 invalid_folder = os.path.join(
638 results_folder,
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800639 invalid_folder_name)
Dan Shiaffb9222015-04-15 17:05:47 -0700640 invalid_files.append(os.path.join(
641 invalid_folder,
642 'invalid_name_file_%s' % invalid_chars))
Dan Shiaffb9222015-04-15 17:05:47 -0700643 good_folder = os.path.join(results_folder, 'valid_name_folder')
644 good_file = os.path.join(good_folder, 'valid_name_file')
645 for folder in [invalid_folder, good_folder]:
646 os.makedirs(folder)
647 for f in invalid_files + [good_file]:
648 with open(f, 'w'):
649 pass
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800650 # check that broken symlinks don't break sanitization
651 symlink = os.path.join(invalid_folder, 'broken-link')
652 os.symlink(os.path.join(results_folder, 'no-such-file'),
653 symlink)
654 fifo1 = os.path.join(results_folder, 'test_fifo1')
655 fifo2 = os.path.join(good_folder, 'test_fifo2')
656 fifo3 = os.path.join(invalid_folder, 'test_fifo3')
657 invalid_fifo4_name = 'test_fifo4_%s' % invalid_chars
658 fifo4 = os.path.join(invalid_folder, invalid_fifo4_name)
659 os.mkfifo(fifo1)
660 os.mkfifo(fifo2)
661 os.mkfifo(fifo3)
662 os.mkfifo(fifo4)
Dan Shiaffb9222015-04-15 17:05:47 -0700663 gs_offloader.sanitize_dir(results_folder)
664 for _, dirs, files in os.walk(results_folder):
665 for name in dirs + files:
Allen Lib41527d2017-06-22 17:28:00 -0700666 self.assertEqual(name, gslib.escape(name))
Dan Shiaffb9222015-04-15 17:05:47 -0700667 for c in name:
Allen Lib41527d2017-06-22 17:28:00 -0700668 self.assertFalse(c in ['[', ']', '*', '?', '#'])
Dan Shiaffb9222015-04-15 17:05:47 -0700669 self.assertTrue(os.path.exists(good_file))
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800670
671 self.assertTrue(os.path.exists(fifo1))
672 self.assertFalse(is_fifo(fifo1))
673 self.assertTrue(os.path.exists(fifo2))
674 self.assertFalse(is_fifo(fifo2))
675 corrected_folder = os.path.join(
Allen Lib41527d2017-06-22 17:28:00 -0700676 results_folder, gslib.escape(invalid_folder_name))
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800677 corrected_fifo3 = os.path.join(
678 corrected_folder,
679 'test_fifo3')
680 self.assertFalse(os.path.exists(fifo3))
681 self.assertTrue(os.path.exists(corrected_fifo3))
682 self.assertFalse(is_fifo(corrected_fifo3))
683 corrected_fifo4 = os.path.join(
Allen Lib41527d2017-06-22 17:28:00 -0700684 corrected_folder, gslib.escape(invalid_fifo4_name))
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800685 self.assertFalse(os.path.exists(fifo4))
686 self.assertTrue(os.path.exists(corrected_fifo4))
687 self.assertFalse(is_fifo(corrected_fifo4))
688
689 corrected_symlink = os.path.join(
690 corrected_folder,
691 'broken-link')
692 self.assertFalse(os.path.lexists(symlink))
693 self.assertTrue(os.path.exists(corrected_symlink))
694 self.assertFalse(os.path.islink(corrected_symlink))
Dan Shiaffb9222015-04-15 17:05:47 -0700695 shutil.rmtree(results_folder)
696
697
Dan Shi1b4c7c32015-10-05 10:38:57 -0700698 def check_limit_file_count(self, is_test_job=True):
699 """Test that folder with too many files can be compressed.
700
701 @param is_test_job: True to check the method with test job result
702 folder. Set to False for special task folder.
703 """
704 results_folder = tempfile.mkdtemp()
705 host_folder = os.path.join(
706 results_folder,
707 'lab1-host1' if is_test_job else 'hosts/lab1-host1/1-repair')
708 debug_folder = os.path.join(host_folder, 'debug')
709 sysinfo_folder = os.path.join(host_folder, 'sysinfo')
710 for folder in [debug_folder, sysinfo_folder]:
711 os.makedirs(folder)
712 for i in range(10):
713 with open(os.path.join(folder, str(i)), 'w') as f:
714 f.write('test')
715
Allen Lib41527d2017-06-22 17:28:00 -0700716 gs_offloader._MAX_FILE_COUNT = 100
Dan Shi1b4c7c32015-10-05 10:38:57 -0700717 gs_offloader.limit_file_count(
718 results_folder if is_test_job else host_folder)
719 self.assertTrue(os.path.exists(sysinfo_folder))
720
Allen Lib41527d2017-06-22 17:28:00 -0700721 gs_offloader._MAX_FILE_COUNT = 10
Dan Shi1b4c7c32015-10-05 10:38:57 -0700722 gs_offloader.limit_file_count(
723 results_folder if is_test_job else host_folder)
724 self.assertFalse(os.path.exists(sysinfo_folder))
725 self.assertTrue(os.path.exists(sysinfo_folder + '.tgz'))
726 self.assertTrue(os.path.exists(debug_folder))
727
728 shutil.rmtree(results_folder)
729
730
731 def test_limit_file_count(self):
732 """Test that folder with too many files can be compressed.
733 """
734 self.check_limit_file_count(is_test_job=True)
735 self.check_limit_file_count(is_test_job=False)
736
Ningning Xia2d88eec2016-07-25 23:18:46 -0700737
Ningning Xia8db632f2016-08-19 11:01:35 -0700738 def test_is_valid_result(self):
739 """Test _is_valid_result."""
Ningning Xia21922c82016-07-29 11:03:15 -0700740 release_build = 'veyron_minnie-cheets-release/R52-8248.0.0'
741 pfq_build = 'cyan-cheets-android-pfq/R54-8623.0.0-rc1'
742 trybot_build = 'trybot-samus-release/R54-8640.0.0-b5092'
743 trybot_2_build = 'trybot-samus-pfq/R54-8640.0.0-b5092'
744 release_2_build = 'test-trybot-release/R54-8640.0.0-b5092'
Ningning Xia8db632f2016-08-19 11:01:35 -0700745 self.assertTrue(gs_offloader._is_valid_result(
746 release_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
Rohit Makasana6384c102016-10-21 17:09:47 -0700747 self.assertTrue(gs_offloader._is_valid_result(
748 release_build, gs_offloader.CTS_RESULT_PATTERN, 'test_that_wrapper'))
Rohit Makasana8d868c92018-06-08 11:29:50 -0700749 self.assertTrue(gs_offloader._is_valid_result(
Richard Barnette33e12892017-05-26 09:25:34 -0700750 release_build, gs_offloader.CTS_RESULT_PATTERN, 'bvt-arc'))
Rohit Makasana8d868c92018-06-08 11:29:50 -0700751 self.assertFalse(gs_offloader._is_valid_result(
752 release_build, gs_offloader.CTS_RESULT_PATTERN, 'bvt-cq'))
Ningning Xia8db632f2016-08-19 11:01:35 -0700753 self.assertTrue(gs_offloader._is_valid_result(
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800754 release_build, gs_offloader.CTS_V2_RESULT_PATTERN, 'arc-gts'))
Ningning Xia8db632f2016-08-19 11:01:35 -0700755 self.assertFalse(gs_offloader._is_valid_result(
756 None, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
757 self.assertFalse(gs_offloader._is_valid_result(
758 release_build, gs_offloader.CTS_RESULT_PATTERN, None))
759 self.assertFalse(gs_offloader._is_valid_result(
760 pfq_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
761 self.assertFalse(gs_offloader._is_valid_result(
762 trybot_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
763 self.assertFalse(gs_offloader._is_valid_result(
764 trybot_2_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
765 self.assertTrue(gs_offloader._is_valid_result(
766 release_2_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
Ningning Xia21922c82016-07-29 11:03:15 -0700767
768
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700769 def create_results_folder(self):
770 """Create CTS/GTS results folders."""
Ningning Xia42111242016-06-15 14:35:58 -0700771 results_folder = tempfile.mkdtemp()
772 host_folder = os.path.join(results_folder, 'chromeos4-row9-rack11-host22')
773 debug_folder = os.path.join(host_folder, 'debug')
774 sysinfo_folder = os.path.join(host_folder, 'sysinfo')
775 cts_result_folder = os.path.join(
776 host_folder, 'cheets_CTS.android.dpi', 'results', 'cts-results')
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800777 cts_v2_result_folder = os.path.join(host_folder,
778 'cheets_CTS_N.CtsGraphicsTestCases', 'results', 'android-cts')
Ningning Xia2d88eec2016-07-25 23:18:46 -0700779 gts_result_folder = os.path.join(
Ilja H. Friedelbfa63142017-01-26 00:56:29 -0800780 host_folder, 'cheets_GTS.google.admin', 'results', 'android-gts')
Ningning Xia42111242016-06-15 14:35:58 -0700781 timestamp_str = '2016.04.28_01.41.44'
Ningning Xia2d88eec2016-07-25 23:18:46 -0700782 timestamp_cts_folder = os.path.join(cts_result_folder, timestamp_str)
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800783 timestamp_cts_v2_folder = os.path.join(cts_v2_result_folder, timestamp_str)
Ningning Xia2d88eec2016-07-25 23:18:46 -0700784 timestamp_gts_folder = os.path.join(gts_result_folder, timestamp_str)
785
Rohit Makasana2030dde2019-04-05 04:18:52 -0700786 # Build host keyvals set to parse model info.
787 host_info_path = os.path.join(host_folder, 'host_keyvals')
788 dir_to_create = '/'
789 for tdir in host_info_path.split('/'):
790 dir_to_create = os.path.join(dir_to_create, tdir)
791 if not os.path.exists(dir_to_create):
792 os.mkdir(dir_to_create)
793 with open(os.path.join(host_info_path, 'chromeos4-row9-rack11-host22'), 'w') as store_file:
794 store_file.write('labels=board%3Acoral,hw_video_acc_vp9,cros,'+
795 'hw_jpeg_acc_dec,bluetooth,model%3Arobo360,'+
796 'accel%3Acros-ec,'+
797 'sku%3Arobo360_IntelR_CeleronR_CPU_N3450_1_10GHz_4Gb')
798
799 # .autoserv_execute file is needed for the test results package to look
800 # legit.
801 autoserve_path = os.path.join(host_folder, '.autoserv_execute')
802 with open(autoserve_path, 'w') as temp_file:
803 temp_file.write(' ')
804
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700805 # Test results in cts_result_folder with a different time-stamp.
806 timestamp_str_2 = '2016.04.28_10.41.44'
807 timestamp_cts_folder_2 = os.path.join(cts_result_folder, timestamp_str_2)
808
Ningning Xia2d88eec2016-07-25 23:18:46 -0700809 for folder in [debug_folder, sysinfo_folder, cts_result_folder,
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800810 timestamp_cts_folder, timestamp_cts_folder_2,
811 timestamp_cts_v2_folder, timestamp_gts_folder]:
Ningning Xia42111242016-06-15 14:35:58 -0700812 os.makedirs(folder)
Ningning Xia2d88eec2016-07-25 23:18:46 -0700813
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700814 path_pattern_pair = [(timestamp_cts_folder, gs_offloader.CTS_RESULT_PATTERN),
815 (timestamp_cts_folder_2, gs_offloader.CTS_RESULT_PATTERN),
Rohit Makasanac1c7e782019-04-02 18:06:00 -0700816 (timestamp_cts_folder_2, gs_offloader.CTS_COMPRESSED_RESULT_PATTERN),
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800817 (timestamp_cts_v2_folder, gs_offloader.CTS_V2_RESULT_PATTERN),
Rohit Makasanac1c7e782019-04-02 18:06:00 -0700818 (timestamp_cts_v2_folder, gs_offloader.CTS_V2_COMPRESSED_RESULT_PATTERN),
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800819 (timestamp_gts_folder, gs_offloader.CTS_V2_RESULT_PATTERN)]
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700820
821 # Create timestamp.zip file_path.
Ningning Xia2d88eec2016-07-25 23:18:46 -0700822 cts_zip_file = os.path.join(cts_result_folder, timestamp_str + '.zip')
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700823 cts_zip_file_2 = os.path.join(cts_result_folder, timestamp_str_2 + '.zip')
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800824 cts_v2_zip_file = os.path.join(cts_v2_result_folder, timestamp_str + '.zip')
Ningning Xia2d88eec2016-07-25 23:18:46 -0700825 gts_zip_file = os.path.join(gts_result_folder, timestamp_str + '.zip')
Ningning Xia2d88eec2016-07-25 23:18:46 -0700826
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700827 # Create xml file_path.
828 cts_result_file = os.path.join(timestamp_cts_folder, 'testResult.xml')
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800829 cts_result_file_2 = os.path.join(timestamp_cts_folder_2,
830 'testResult.xml')
Rohit Makasanac1c7e782019-04-02 18:06:00 -0700831 cts_result_compressed_file_2 = os.path.join(timestamp_cts_folder_2,
832 'testResult.xml.tgz')
Ilja H. Friedelbfa63142017-01-26 00:56:29 -0800833 gts_result_file = os.path.join(timestamp_gts_folder, 'test_result.xml')
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800834 cts_v2_result_file = os.path.join(timestamp_cts_v2_folder,
835 'test_result.xml')
Rohit Makasanac1c7e782019-04-02 18:06:00 -0700836 cts_v2_result_compressed_file = os.path.join(timestamp_cts_v2_folder,
837 'test_result.xml.tgz')
Ningning Xia2d88eec2016-07-25 23:18:46 -0700838
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800839 for file_path in [cts_zip_file, cts_zip_file_2, cts_v2_zip_file,
840 gts_zip_file, cts_result_file, cts_result_file_2,
Rohit Makasanac1c7e782019-04-02 18:06:00 -0700841 cts_result_compressed_file_2, gts_result_file,
842 cts_v2_result_file, cts_v2_result_compressed_file]:
843 if file_path.endswith('tgz'):
844 test_result_file = gs_offloader.CTS_COMPRESSED_RESULT_TYPES[
845 os.path.basename(file_path)]
846 with open(test_result_file, 'w') as f:
847 f.write('test')
848 with tarfile.open(file_path, 'w:gz') as tar_file:
849 tar_file.add(test_result_file)
850 os.remove(test_result_file)
851 else:
852 with open(file_path, 'w') as f:
853 f.write('test')
Ningning Xia42111242016-06-15 14:35:58 -0700854
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700855 return (results_folder, host_folder, path_pattern_pair)
Ningning Xia2d88eec2016-07-25 23:18:46 -0700856
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700857
Allen Lib41527d2017-06-22 17:28:00 -0700858 def test__upload_cts_testresult(self):
859 """Test _upload_cts_testresult."""
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700860 results_folder, host_folder, path_pattern_pair = self.create_results_folder()
861
862 self.mox.StubOutWithMock(gs_offloader, '_upload_files')
863 gs_offloader._upload_files(
Rohit Makasanaea337c52018-04-11 18:03:41 -0700864 mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg(), False,
865 mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700866 ['test', '-d', host_folder])
867 gs_offloader._upload_files(
Rohit Makasanaea337c52018-04-11 18:03:41 -0700868 mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg(), False,
869 mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700870 ['test', '-d', host_folder])
871 gs_offloader._upload_files(
Rohit Makasanaea337c52018-04-11 18:03:41 -0700872 mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg(), False,
873 mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700874 ['test', '-d', host_folder])
Ningning Xia2d88eec2016-07-25 23:18:46 -0700875
Ningning Xia42111242016-06-15 14:35:58 -0700876 self.mox.ReplayAll()
Allen Lib41527d2017-06-22 17:28:00 -0700877 gs_offloader._upload_cts_testresult(results_folder, False)
Ningning Xia42111242016-06-15 14:35:58 -0700878 self.mox.VerifyAll()
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700879 shutil.rmtree(results_folder)
880
881
Keith Haddow295a54e2019-07-10 15:27:17 -0700882 def test_parse_cts_job_results_file_path(self):
883 # A autotest path
884 path = ('/317739475-chromeos-test/chromeos4-row9-rack11-host22/'
885 'cheets_CTS.android.dpi/results/cts-results/'
886 '2016.04.28_01.41.44')
887 job_id, package, timestamp = \
888 gs_offloader._parse_cts_job_results_file_path(path)
889 self.assertEqual('317739475-chromeos-test', job_id)
890 self.assertEqual('cheets_CTS.android.dpi', package)
891 self.assertEqual('2016.04.28_01.41.44', timestamp)
892
893
894 # A skylab path
895 path = ('/swarming-458e3a3a7fc6f210/1/autoserv_test/'
896 'cheets_CTS.android.dpi/results/cts-results/'
897 '2016.04.28_01.41.44')
898 job_id, package, timestamp = \
899 gs_offloader._parse_cts_job_results_file_path(path)
900 self.assertEqual('swarming-458e3a3a7fc6f210-1', job_id)
901 self.assertEqual('cheets_CTS.android.dpi', package)
902 self.assertEqual('2016.04.28_01.41.44', timestamp)
903
904
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700905 def test_upload_files(self):
906 """Test upload_files"""
907 results_folder, host_folder, path_pattern_pair = self.create_results_folder()
908
909 for path, pattern in path_pattern_pair:
910 models.test.parse_job_keyval(mox.IgnoreArg()).AndReturn({
911 'build': 'veyron_minnie-cheets-release/R52-8248.0.0',
Rohit Makasana2030dde2019-04-05 04:18:52 -0700912 'hostname': 'chromeos4-row9-rack11-host22',
Ningning Xia8db632f2016-08-19 11:01:35 -0700913 'parent_job_id': 'p_id',
914 'suite': 'arc-cts'
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700915 })
916
Allen Lib41527d2017-06-22 17:28:00 -0700917 gs_offloader._get_cmd_list(
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700918 False, mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
919 ['test', '-d', path])
Allen Lib41527d2017-06-22 17:28:00 -0700920 gs_offloader._get_cmd_list(
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700921 False, mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
922 ['test', '-d', path])
923
924 self.mox.ReplayAll()
Rohit Makasanaea337c52018-04-11 18:03:41 -0700925 gs_offloader._upload_files(host_folder, path, pattern, False,
926 'gs://a-test-bucket/',
927 'gs://a-test-apfe-bucket/')
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700928 self.mox.VerifyAll()
929 self.mox.ResetAll()
Ningning Xia42111242016-06-15 14:35:58 -0700930
931 shutil.rmtree(results_folder)
Dan Shi1b4c7c32015-10-05 10:38:57 -0700932
Michael Tang97d188c2016-06-25 11:18:42 -0700933
Dan Shi02dd0662017-05-23 11:24:32 -0700934 def test_get_metrics_fields(self):
935 """Test method _get_metrics_fields."""
936 results_folder, host_folder, _ = self.create_results_folder()
937 models.test.parse_job_keyval(mox.IgnoreArg()).AndReturn({
938 'build': 'veyron_minnie-cheets-release/R52-8248.0.0',
939 'parent_job_id': 'p_id',
940 'suite': 'arc-cts'
941 })
942 try:
943 self.mox.ReplayAll()
944 self.assertEqual({'board': 'veyron_minnie-cheets',
945 'milestone': 'R52'},
946 gs_offloader._get_metrics_fields(host_folder))
947 self.mox.VerifyAll()
948 finally:
949 shutil.rmtree(results_folder)
950
951
J. Richard Barnetteea785362014-03-17 16:00:53 -0700952class JobDirectoryOffloadTests(_TempResultsDirTestBase):
953 """Tests for `_JobDirectory.enqueue_offload()`.
954
955 When testing with a `days_old` parameter of 0, we use
956 `set_finished()` instead of `set_expired()`. This causes the
957 job's timestamp to be set in the future. This is done so as
958 to test that when `days_old` is 0, the job is always treated
959 as eligible for offload, regardless of the timestamp's value.
960
961 Testing covers the following assertions:
962 A. Each time `enqueue_offload()` is called, a message that
963 includes the job's directory name will be logged using
964 `logging.debug()`, regardless of whether the job was
965 enqueued. Nothing else is allowed to be logged.
966 B. If the job is not eligible to be offloaded,
Allen Lib41527d2017-06-22 17:28:00 -0700967 `first_offload_start` and `offload_count` are 0.
J. Richard Barnetteea785362014-03-17 16:00:53 -0700968 C. If the job is not eligible for offload, nothing is
969 enqueued in `queue`.
Allen Lib41527d2017-06-22 17:28:00 -0700970 D. When the job is offloaded, `offload_count` increments
J. Richard Barnetteea785362014-03-17 16:00:53 -0700971 each time.
972 E. When the job is offloaded, the appropriate parameters are
973 enqueued exactly once.
Allen Lib41527d2017-06-22 17:28:00 -0700974 F. The first time a job is offloaded, `first_offload_start` is
J. Richard Barnetteea785362014-03-17 16:00:53 -0700975 set to the current time.
Allen Lib41527d2017-06-22 17:28:00 -0700976 G. `first_offload_start` only changes the first time that the
J. Richard Barnetteea785362014-03-17 16:00:53 -0700977 job is offloaded.
978
979 The test cases below are designed to exercise all of the
980 meaningful state transitions at least once.
981
982 """
983
984 def setUp(self):
985 super(JobDirectoryOffloadTests, self).setUp()
J. Richard Barnette08800322014-05-16 14:49:46 -0700986 self._job = self.make_job(self.REGULAR_JOBLIST[0])
J. Richard Barnetteea785362014-03-17 16:00:53 -0700987 self._queue = Queue.Queue()
J. Richard Barnetteea785362014-03-17 16:00:53 -0700988
Jakob Juelich24f22c22014-09-26 11:46:11 -0700989
J. Richard Barnetteea785362014-03-17 16:00:53 -0700990 def _offload_unexpired_job(self, days_old):
991 """Make calls to `enqueue_offload()` for an unexpired job.
992
993 This method tests assertions B and C that calling
994 `enqueue_offload()` has no effect.
995
996 """
Allen Lib41527d2017-06-22 17:28:00 -0700997 self.assertEqual(self._job.offload_count, 0)
998 self.assertEqual(self._job.first_offload_start, 0)
999 gs_offloader._enqueue_offload(self._job, self._queue, days_old)
1000 gs_offloader._enqueue_offload(self._job, self._queue, days_old)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001001 self.assertTrue(self._queue.empty())
Allen Lib41527d2017-06-22 17:28:00 -07001002 self.assertEqual(self._job.offload_count, 0)
1003 self.assertEqual(self._job.first_offload_start, 0)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001004
Jakob Juelich24f22c22014-09-26 11:46:11 -07001005
J. Richard Barnetteea785362014-03-17 16:00:53 -07001006 def _offload_expired_once(self, days_old, count):
1007 """Make one call to `enqueue_offload()` for an expired job.
1008
1009 This method tests assertions D and E regarding side-effects
1010 expected when a job is offloaded.
1011
1012 """
Allen Lib41527d2017-06-22 17:28:00 -07001013 gs_offloader._enqueue_offload(self._job, self._queue, days_old)
1014 self.assertEqual(self._job.offload_count, count)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001015 self.assertFalse(self._queue.empty())
1016 v = self._queue.get_nowait()
1017 self.assertTrue(self._queue.empty())
J. Richard Barnette2c72ddd2014-05-20 12:17:37 -07001018 self.assertEqual(v, self._job.queue_args)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001019
Jakob Juelich24f22c22014-09-26 11:46:11 -07001020
J. Richard Barnetteea785362014-03-17 16:00:53 -07001021 def _offload_expired_job(self, days_old):
1022 """Make calls to `enqueue_offload()` for a just-expired job.
1023
1024 This method directly tests assertions F and G regarding
Allen Lib41527d2017-06-22 17:28:00 -07001025 side-effects on `first_offload_start`.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001026
1027 """
1028 t0 = time.time()
1029 self._offload_expired_once(days_old, 1)
Allen Lib41527d2017-06-22 17:28:00 -07001030 t1 = self._job.first_offload_start
J. Richard Barnetteea785362014-03-17 16:00:53 -07001031 self.assertLessEqual(t1, time.time())
1032 self.assertGreaterEqual(t1, t0)
1033 self._offload_expired_once(days_old, 2)
Allen Lib41527d2017-06-22 17:28:00 -07001034 self.assertEqual(self._job.first_offload_start, t1)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001035 self._offload_expired_once(days_old, 3)
Allen Lib41527d2017-06-22 17:28:00 -07001036 self.assertEqual(self._job.first_offload_start, t1)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001037
Jakob Juelich24f22c22014-09-26 11:46:11 -07001038
J. Richard Barnetteea785362014-03-17 16:00:53 -07001039 def test_case_1_no_expiration(self):
1040 """Test a series of `enqueue_offload()` calls with `days_old` of 0.
1041
1042 This tests that offload works as expected if calls are
1043 made both before and after the job becomes expired.
1044
1045 """
1046 self._offload_unexpired_job(0)
1047 self._job.set_finished(0)
1048 self._offload_expired_job(0)
1049
Jakob Juelich24f22c22014-09-26 11:46:11 -07001050
J. Richard Barnetteea785362014-03-17 16:00:53 -07001051 def test_case_2_no_expiration(self):
1052 """Test a series of `enqueue_offload()` calls with `days_old` of 0.
1053
1054 This tests that offload works as expected if calls are made
1055 only after the job becomes expired.
1056
1057 """
1058 self._job.set_finished(0)
1059 self._offload_expired_job(0)
1060
Jakob Juelich24f22c22014-09-26 11:46:11 -07001061
J. Richard Barnetteea785362014-03-17 16:00:53 -07001062 def test_case_1_with_expiration(self):
1063 """Test a series of `enqueue_offload()` calls with `days_old` non-zero.
1064
1065 This tests that offload works as expected if calls are made
1066 before the job finishes, before the job expires, and after
1067 the job expires.
1068
1069 """
1070 self._offload_unexpired_job(_TEST_EXPIRATION_AGE)
1071 self._job.set_finished(_TEST_EXPIRATION_AGE)
1072 self._offload_unexpired_job(_TEST_EXPIRATION_AGE)
1073 self._job.set_expired(_TEST_EXPIRATION_AGE)
1074 self._offload_expired_job(_TEST_EXPIRATION_AGE)
1075
Jakob Juelich24f22c22014-09-26 11:46:11 -07001076
J. Richard Barnetteea785362014-03-17 16:00:53 -07001077 def test_case_2_with_expiration(self):
1078 """Test a series of `enqueue_offload()` calls with `days_old` non-zero.
1079
1080 This tests that offload works as expected if calls are made
1081 between finishing and expiration, and after the job expires.
1082
1083 """
1084 self._job.set_finished(_TEST_EXPIRATION_AGE)
1085 self._offload_unexpired_job(_TEST_EXPIRATION_AGE)
1086 self._job.set_expired(_TEST_EXPIRATION_AGE)
1087 self._offload_expired_job(_TEST_EXPIRATION_AGE)
1088
Jakob Juelich24f22c22014-09-26 11:46:11 -07001089
J. Richard Barnetteea785362014-03-17 16:00:53 -07001090 def test_case_3_with_expiration(self):
1091 """Test a series of `enqueue_offload()` calls with `days_old` non-zero.
1092
1093 This tests that offload works as expected if calls are made
1094 only before finishing and after expiration.
1095
1096 """
1097 self._offload_unexpired_job(_TEST_EXPIRATION_AGE)
1098 self._job.set_expired(_TEST_EXPIRATION_AGE)
1099 self._offload_expired_job(_TEST_EXPIRATION_AGE)
1100
Jakob Juelich24f22c22014-09-26 11:46:11 -07001101
J. Richard Barnetteea785362014-03-17 16:00:53 -07001102 def test_case_4_with_expiration(self):
1103 """Test a series of `enqueue_offload()` calls with `days_old` non-zero.
1104
1105 This tests that offload works as expected if calls are made
1106 only after expiration.
1107
1108 """
1109 self._job.set_expired(_TEST_EXPIRATION_AGE)
1110 self._offload_expired_job(_TEST_EXPIRATION_AGE)
1111
1112
1113class GetJobDirectoriesTests(_TempResultsDirTestBase):
1114 """Tests for `_JobDirectory.get_job_directories()`."""
1115
J. Richard Barnetteea785362014-03-17 16:00:53 -07001116 def setUp(self):
1117 super(GetJobDirectoriesTests, self).setUp()
J. Richard Barnette08800322014-05-16 14:49:46 -07001118 self.make_job_hierarchy()
1119 os.mkdir('not-a-job')
1120 open('not-a-dir', 'w').close()
J. Richard Barnetteea785362014-03-17 16:00:53 -07001121
Jakob Juelich24f22c22014-09-26 11:46:11 -07001122
J. Richard Barnetteea785362014-03-17 16:00:53 -07001123 def _run_get_directories(self, cls, expected_list):
1124 """Test `get_job_directories()` for the given class.
1125
1126 Calls the method, and asserts that the returned list of
1127 directories matches the expected return value.
1128
1129 @param expected_list Expected return value from the call.
1130 """
J. Richard Barnetteea785362014-03-17 16:00:53 -07001131 dirlist = cls.get_job_directories()
1132 self.assertEqual(set(dirlist), set(expected_list))
J. Richard Barnetteea785362014-03-17 16:00:53 -07001133
Jakob Juelich24f22c22014-09-26 11:46:11 -07001134
J. Richard Barnetteea785362014-03-17 16:00:53 -07001135 def test_get_regular_jobs(self):
1136 """Test `RegularJobDirectory.get_job_directories()`."""
1137 self._run_get_directories(job_directories.RegularJobDirectory,
J. Richard Barnette08800322014-05-16 14:49:46 -07001138 self.REGULAR_JOBLIST)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001139
Jakob Juelich24f22c22014-09-26 11:46:11 -07001140
J. Richard Barnetteea785362014-03-17 16:00:53 -07001141 def test_get_special_jobs(self):
1142 """Test `SpecialJobDirectory.get_job_directories()`."""
1143 self._run_get_directories(job_directories.SpecialJobDirectory,
J. Richard Barnette08800322014-05-16 14:49:46 -07001144 self.SPECIAL_JOBLIST)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001145
1146
1147class AddJobsTests(_TempResultsDirTestBase):
1148 """Tests for `Offloader._add_new_jobs()`."""
1149
J. Richard Barnette08800322014-05-16 14:49:46 -07001150 MOREJOBS = ['115-fubar', '116-fubar', '117-fubar', '118-snafu']
J. Richard Barnetteea785362014-03-17 16:00:53 -07001151
1152 def setUp(self):
1153 super(AddJobsTests, self).setUp()
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001154 self._initial_job_names = (
1155 set(self.REGULAR_JOBLIST) | set(self.SPECIAL_JOBLIST))
J. Richard Barnette08800322014-05-16 14:49:46 -07001156 self.make_job_hierarchy()
1157 self._offloader = gs_offloader.Offloader(_get_options(['-a']))
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001158 self.mox.StubOutWithMock(logging, 'debug')
J. Richard Barnetteea785362014-03-17 16:00:53 -07001159
Jakob Juelich24f22c22014-09-26 11:46:11 -07001160
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001161 def _run_add_new_jobs(self, expected_key_set):
J. Richard Barnetteea785362014-03-17 16:00:53 -07001162 """Basic test assertions for `_add_new_jobs()`.
1163
1164 Asserts the following:
1165 * The keys in the offloader's `_open_jobs` dictionary
1166 matches the expected set of keys.
1167 * For every job in `_open_jobs`, the job has the expected
1168 directory name.
1169
1170 """
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001171 count = len(expected_key_set) - len(self._offloader._open_jobs)
1172 logging.debug(mox.IgnoreArg(), count)
1173 self.mox.ReplayAll()
1174 self._offloader._add_new_jobs()
J. Richard Barnetteea785362014-03-17 16:00:53 -07001175 self.assertEqual(expected_key_set,
1176 set(self._offloader._open_jobs.keys()))
1177 for jobkey, job in self._offloader._open_jobs.items():
Allen Lib41527d2017-06-22 17:28:00 -07001178 self.assertEqual(jobkey, job.dirname)
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001179 self.mox.VerifyAll()
1180 self.mox.ResetAll()
J. Richard Barnetteea785362014-03-17 16:00:53 -07001181
Jakob Juelich24f22c22014-09-26 11:46:11 -07001182
J. Richard Barnetteea785362014-03-17 16:00:53 -07001183 def test_add_jobs_empty(self):
1184 """Test adding jobs to an empty dictionary.
1185
1186 Calls the offloader's `_add_new_jobs()`, then perform
1187 the assertions of `self._check_open_jobs()`.
1188
1189 """
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001190 self._run_add_new_jobs(self._initial_job_names)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001191
Jakob Juelich24f22c22014-09-26 11:46:11 -07001192
J. Richard Barnetteea785362014-03-17 16:00:53 -07001193 def test_add_jobs_non_empty(self):
1194 """Test adding jobs to a non-empty dictionary.
1195
1196 Calls the offloader's `_add_new_jobs()` twice; once from
1197 initial conditions, and then again after adding more
1198 directories. After the second call, perform the assertions
1199 of `self._check_open_jobs()`. Additionally, assert that
1200 keys added by the first call still map to their original
1201 job object after the second call.
1202
1203 """
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001204 self._run_add_new_jobs(self._initial_job_names)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001205 jobs_copy = self._offloader._open_jobs.copy()
J. Richard Barnette08800322014-05-16 14:49:46 -07001206 for d in self.MOREJOBS:
1207 os.mkdir(d)
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001208 self._run_add_new_jobs(self._initial_job_names |
1209 set(self.MOREJOBS))
J. Richard Barnetteea785362014-03-17 16:00:53 -07001210 for key in jobs_copy.keys():
1211 self.assertIs(jobs_copy[key],
1212 self._offloader._open_jobs[key])
1213
1214
J. Richard Barnetteea785362014-03-17 16:00:53 -07001215class ReportingTests(_TempResultsDirTestBase):
Allen Lib41527d2017-06-22 17:28:00 -07001216 """Tests for `Offloader._report_failed_jobs()`."""
J. Richard Barnetteea785362014-03-17 16:00:53 -07001217
J. Richard Barnetteea785362014-03-17 16:00:53 -07001218 def setUp(self):
1219 super(ReportingTests, self).setUp()
1220 self._offloader = gs_offloader.Offloader(_get_options([]))
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001221 self.mox.StubOutWithMock(self._offloader, '_log_failed_jobs_locally')
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001222 self.mox.StubOutWithMock(logging, 'debug')
J. Richard Barnetteea785362014-03-17 16:00:53 -07001223
Jakob Juelich24f22c22014-09-26 11:46:11 -07001224
J. Richard Barnetteea785362014-03-17 16:00:53 -07001225 def _add_job(self, jobdir):
1226 """Add a job to the dictionary of unfinished jobs."""
1227 j = self.make_job(jobdir)
Allen Lib41527d2017-06-22 17:28:00 -07001228 self._offloader._open_jobs[j.dirname] = j
J. Richard Barnetteea785362014-03-17 16:00:53 -07001229 return j
1230
Jakob Juelich24f22c22014-09-26 11:46:11 -07001231
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001232 def _expect_log_message(self, new_open_jobs, with_failures):
1233 """Mock expected logging calls.
1234
Allen Lib41527d2017-06-22 17:28:00 -07001235 `_report_failed_jobs()` logs one message with the number
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001236 of jobs removed from the open job set and the number of jobs
1237 still remaining. Additionally, if there are reportable
1238 jobs, then it logs the number of jobs that haven't yet
1239 offloaded.
1240
1241 This sets up the logging calls using `new_open_jobs` to
1242 figure the job counts. If `with_failures` is true, then
1243 the log message is set up assuming that all jobs in
1244 `new_open_jobs` have offload failures.
1245
1246 @param new_open_jobs New job set for calculating counts
1247 in the messages.
1248 @param with_failures Whether the log message with a
1249 failure count is expected.
1250
1251 """
1252 count = len(self._offloader._open_jobs) - len(new_open_jobs)
1253 logging.debug(mox.IgnoreArg(), count, len(new_open_jobs))
1254 if with_failures:
1255 logging.debug(mox.IgnoreArg(), len(new_open_jobs))
1256
Jakob Juelich24f22c22014-09-26 11:46:11 -07001257
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001258 def _run_update(self, new_open_jobs):
Allen Lib41527d2017-06-22 17:28:00 -07001259 """Call `_report_failed_jobs()`.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001260
1261 Initial conditions are set up by the caller. This calls
Allen Lib41527d2017-06-22 17:28:00 -07001262 `_report_failed_jobs()` once, and then checks these
J. Richard Barnetteea785362014-03-17 16:00:53 -07001263 assertions:
J. Richard Barnetteea785362014-03-17 16:00:53 -07001264 * The offloader's new `_open_jobs` field contains only
1265 the entries in `new_open_jobs`.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001266
1267 @param new_open_jobs A dictionary representing the expected
1268 new value of the offloader's
1269 `_open_jobs` field.
1270 """
1271 self.mox.ReplayAll()
Allen Lib41527d2017-06-22 17:28:00 -07001272 self._offloader._report_failed_jobs()
1273 self._offloader._remove_offloaded_jobs()
J. Richard Barnetteea785362014-03-17 16:00:53 -07001274 self.assertEqual(self._offloader._open_jobs, new_open_jobs)
1275 self.mox.VerifyAll()
1276 self.mox.ResetAll()
1277
Jakob Juelich24f22c22014-09-26 11:46:11 -07001278
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001279 def _expect_failed_jobs(self, failed_jobs):
1280 """Mock expected call to log the failed jobs on local disk.
1281
1282 TODO(crbug.com/686904): The fact that we have to mock an internal
1283 function for this test is evidence that we need to pull out the local
1284 file formatter in its own object in a future CL.
1285
1286 @param failed_jobs: The list of jobs being logged as failed.
1287 """
1288 self._offloader._log_failed_jobs_locally(failed_jobs)
1289
1290
J. Richard Barnetteea785362014-03-17 16:00:53 -07001291 def test_no_jobs(self):
Allen Lib41527d2017-06-22 17:28:00 -07001292 """Test `_report_failed_jobs()` with no open jobs.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001293
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001294 Initial conditions are an empty `_open_jobs` list.
1295 Expected result is an empty `_open_jobs` list.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001296
1297 """
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001298 self._expect_log_message({}, False)
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001299 self._expect_failed_jobs([])
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001300 self._run_update({})
J. Richard Barnetteea785362014-03-17 16:00:53 -07001301
Jakob Juelich24f22c22014-09-26 11:46:11 -07001302
J. Richard Barnetteea785362014-03-17 16:00:53 -07001303 def test_all_completed(self):
Allen Lib41527d2017-06-22 17:28:00 -07001304 """Test `_report_failed_jobs()` with only complete jobs.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001305
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001306 Initial conditions are an `_open_jobs` list consisting of only completed
1307 jobs.
1308 Expected result is an empty `_open_jobs` list.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001309
1310 """
J. Richard Barnette08800322014-05-16 14:49:46 -07001311 for d in self.REGULAR_JOBLIST:
J. Richard Barnetteea785362014-03-17 16:00:53 -07001312 self._add_job(d).set_complete()
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001313 self._expect_log_message({}, False)
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001314 self._expect_failed_jobs([])
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001315 self._run_update({})
J. Richard Barnetteea785362014-03-17 16:00:53 -07001316
Jakob Juelich24f22c22014-09-26 11:46:11 -07001317
J. Richard Barnetteea785362014-03-17 16:00:53 -07001318 def test_none_finished(self):
Allen Lib41527d2017-06-22 17:28:00 -07001319 """Test `_report_failed_jobs()` with only unfinished jobs.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001320
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001321 Initial conditions are an `_open_jobs` list consisting of only
1322 unfinished jobs.
1323 Expected result is no change to the `_open_jobs` list.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001324
1325 """
J. Richard Barnette08800322014-05-16 14:49:46 -07001326 for d in self.REGULAR_JOBLIST:
J. Richard Barnetteea785362014-03-17 16:00:53 -07001327 self._add_job(d)
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001328 new_jobs = self._offloader._open_jobs.copy()
1329 self._expect_log_message(new_jobs, False)
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001330 self._expect_failed_jobs([])
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001331 self._run_update(new_jobs)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001332
1333
Allen Lib41527d2017-06-22 17:28:00 -07001334class GsOffloaderMockTests(_TempResultsDirTestCase):
1335 """Tests using mock instead of mox."""
1336
1337 def setUp(self):
1338 super(GsOffloaderMockTests, self).setUp()
1339 alarm = mock.patch('signal.alarm', return_value=0)
1340 alarm.start()
1341 self.addCleanup(alarm.stop)
1342
1343 self._saved_loglevel = logging.getLogger().getEffectiveLevel()
1344 logging.getLogger().setLevel(logging.CRITICAL + 1)
1345
1346 self._job = self.make_job(self.REGULAR_JOBLIST[0])
1347
1348
1349 def test_offload_timeout_early(self):
1350 """Test that `offload_dir()` times out correctly.
1351
1352 This test triggers timeout at the earliest possible moment,
1353 at the first call to set the timeout alarm.
1354
1355 """
1356 signal.alarm.side_effect = [0, timeout_util.TimeoutError('fubar')]
1357 with mock.patch.object(gs_offloader, '_upload_cts_testresult',
1358 autospec=True) as upload:
1359 upload.return_value = None
1360 gs_offloader.GSOffloader(
1361 utils.DEFAULT_OFFLOAD_GSURI, False, 0).offload(
1362 self._job.queue_args[0],
1363 self._job.queue_args[1],
1364 self._job.queue_args[2])
1365 self.assertTrue(os.path.isdir(self._job.queue_args[0]))
1366
1367
1368 # TODO(ayatane): This tests passes when run locally, but it fails
1369 # when run on trybot. I have no idea why, but the assert isdir
1370 # fails.
1371 #
1372 # This test is also kind of redundant since we are using the timeout
1373 # from chromite which has its own tests.
1374 @unittest.skip('This fails on trybot')
1375 def test_offload_timeout_late(self):
1376 """Test that `offload_dir()` times out correctly.
1377
1378 This test triggers timeout at the latest possible moment, at
1379 the call to clear the timeout alarm.
1380
1381 """
1382 signal.alarm.side_effect = [0, 0, timeout_util.TimeoutError('fubar')]
1383 with mock.patch.object(gs_offloader, '_upload_cts_testresult',
1384 autospec=True) as upload, \
1385 mock.patch.object(gs_offloader, '_get_cmd_list',
1386 autospec=True) as get_cmd_list:
1387 upload.return_value = None
1388 get_cmd_list.return_value = ['test', '-d', self._job.queue_args[0]]
1389 gs_offloader.GSOffloader(
1390 utils.DEFAULT_OFFLOAD_GSURI, False, 0).offload(
1391 self._job.queue_args[0],
1392 self._job.queue_args[1],
1393 self._job.queue_args[2])
1394 self.assertTrue(os.path.isdir(self._job.queue_args[0]))
1395
1396
1397
J. Richard Barnetteea785362014-03-17 16:00:53 -07001398if __name__ == '__main__':
1399 unittest.main()