blob: 598fb80abd5e09fb251ac8197356d8507f6192db [file] [log] [blame]
Mike Frysingerd03e6b52019-08-03 12:49:01 -04001#!/usr/bin/python2
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 """
Xinan Line1dc6962019-09-20 15:43:52 -0700444 os.makedirs(jobdir)
J. Richard Barnette08800322014-05-16 14:49:46 -0700445 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')
Xinan Line1dc6962019-09-20 15:43:52 -0700557 self.should_remove_sarming_req_dir = False
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700558
Jakob Juelich24f22c22014-09-26 11:46:11 -0700559
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700560 def tearDown(self):
561 logging.getLogger().setLevel(self._saved_loglevel)
562 super(OffloadDirectoryTests, self).tearDown()
563
Allen Lib41527d2017-06-22 17:28:00 -0700564 def _mock__upload_cts_testresult(self):
565 self.mox.StubOutWithMock(gs_offloader, '_upload_cts_testresult')
566 gs_offloader._upload_cts_testresult(
Ningning Xia42111242016-06-15 14:35:58 -0700567 mox.IgnoreArg(),mox.IgnoreArg()).AndReturn(None)
Jakob Juelich24f22c22014-09-26 11:46:11 -0700568
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800569 def _mock_create_marker_file(self):
570 self.mox.StubOutWithMock(__builtin__, 'open')
Allen Li9579b382017-05-05 17:07:43 -0700571 open(mox.IgnoreArg(), 'a').AndReturn(mock.MagicMock())
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800572
573
574 def _mock_offload_dir_calls(self, command, queue_args,
Allen Lib41527d2017-06-22 17:28:00 -0700575 marker_initially_exists=False):
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700576 """Mock out the calls needed by `offload_dir()`.
577
578 This covers only the calls made when there is no timeout.
579
580 @param command Command list to be returned by the mocked
Allen Lib41527d2017-06-22 17:28:00 -0700581 call to `_get_cmd_list()`.
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700582
583 """
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800584 self.mox.StubOutWithMock(os.path, 'isfile')
585 os.path.isfile(mox.IgnoreArg()).AndReturn(marker_initially_exists)
Simran Basidd129972014-09-11 14:34:49 -0700586 command.append(queue_args[0])
Allen Lib41527d2017-06-22 17:28:00 -0700587 gs_offloader._get_cmd_list(
MK Ryue93c8572015-08-11 11:53:00 -0700588 False, queue_args[0],
589 '%s%s' % (utils.DEFAULT_OFFLOAD_GSURI,
590 queue_args[1])).AndReturn(command)
Allen Lib41527d2017-06-22 17:28:00 -0700591 self._mock__upload_cts_testresult()
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700592
Jakob Juelich24f22c22014-09-26 11:46:11 -0700593
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800594 def _run_offload_dir(self, should_succeed, delete_age):
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700595 """Make one call to `offload_dir()`.
596
597 The caller ensures all mocks are set up already.
598
599 @param should_succeed True iff the call to `offload_dir()`
600 is expected to succeed and remove the
601 offloaded job directory.
602
603 """
604 self.mox.ReplayAll()
Allen Lib41527d2017-06-22 17:28:00 -0700605 gs_offloader.GSOffloader(
606 utils.DEFAULT_OFFLOAD_GSURI, False, delete_age).offload(
MK Ryue93c8572015-08-11 11:53:00 -0700607 self._job.queue_args[0],
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800608 self._job.queue_args[1],
609 self._job.queue_args[2])
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700610 self.mox.VerifyAll()
611 self.assertEqual(not should_succeed,
612 os.path.isdir(self._job.queue_args[0]))
Xinan Line1dc6962019-09-20 15:43:52 -0700613 swarming_req_dir = gs_offloader._get_swarming_req_dir(
614 self._job.queue_args[0])
615 if swarming_req_dir:
616 self.assertEqual(not self.should_remove_sarming_req_dir,
617 os.path.exists(swarming_req_dir))
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700618
Jakob Juelich24f22c22014-09-26 11:46:11 -0700619
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700620 def test_offload_success(self):
621 """Test that `offload_dir()` can succeed correctly."""
Simran Basidd129972014-09-11 14:34:49 -0700622 self._mock_offload_dir_calls(['test', '-d'],
623 self._job.queue_args)
Allen Lib41527d2017-06-22 17:28:00 -0700624 os.path.isfile(mox.IgnoreArg()).AndReturn(True)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800625 self._mock_create_marker_file()
626 self._run_offload_dir(True, 0)
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700627
Jakob Juelich24f22c22014-09-26 11:46:11 -0700628
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700629 def test_offload_failure(self):
630 """Test that `offload_dir()` can fail correctly."""
Simran Basidd129972014-09-11 14:34:49 -0700631 self._mock_offload_dir_calls(['test', '!', '-d'],
Allen Lib41527d2017-06-22 17:28:00 -0700632 self._job.queue_args)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800633 self._run_offload_dir(False, 0)
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700634
635
Xinan Line1dc6962019-09-20 15:43:52 -0700636 def test_offload_swarming_req_dir_remove(self):
637 """Test that `offload_dir()` can prune the empty swarming task dir."""
638 should_remove = os.path.join('results', 'swarming-123abc0')
639 self._job = self.make_job(os.path.join(should_remove, '1'))
640 self._mock_offload_dir_calls(['test', '-d'],
641 self._job.queue_args)
642
643 os.path.isfile(mox.IgnoreArg()).AndReturn(True)
644 self.should_remove_sarming_req_dir = True
645 self._mock_create_marker_file()
646 self._run_offload_dir(True, 0)
647
648
649 def test_offload_swarming_req_dir_exist(self):
650 """Test that `offload_dir()` keeps the non-empty swarming task dir."""
651 should_not_remove = os.path.join('results', 'swarming-456edf0')
652 self._job = self.make_job(os.path.join(should_not_remove, '1'))
653 self.make_job(os.path.join(should_not_remove, '2'))
654 self._mock_offload_dir_calls(['test', '-d'],
655 self._job.queue_args)
656
657 os.path.isfile(mox.IgnoreArg()).AndReturn(True)
658 self.should_remove_sarming_req_dir = False
659 self._mock_create_marker_file()
660 self._run_offload_dir(True, 0)
661
662
Dan Shiaffb9222015-04-15 17:05:47 -0700663 def test_sanitize_dir(self):
664 """Test that folder/file name with invalid character can be corrected.
665 """
666 results_folder = tempfile.mkdtemp()
Allen Lib41527d2017-06-22 17:28:00 -0700667 invalid_chars = '_'.join(['[', ']', '*', '?', '#'])
Dan Shiaffb9222015-04-15 17:05:47 -0700668 invalid_files = []
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800669 invalid_folder_name = 'invalid_name_folder_%s' % invalid_chars
Dan Shiaffb9222015-04-15 17:05:47 -0700670 invalid_folder = os.path.join(
671 results_folder,
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800672 invalid_folder_name)
Dan Shiaffb9222015-04-15 17:05:47 -0700673 invalid_files.append(os.path.join(
674 invalid_folder,
675 'invalid_name_file_%s' % invalid_chars))
Dan Shiaffb9222015-04-15 17:05:47 -0700676 good_folder = os.path.join(results_folder, 'valid_name_folder')
677 good_file = os.path.join(good_folder, 'valid_name_file')
678 for folder in [invalid_folder, good_folder]:
679 os.makedirs(folder)
680 for f in invalid_files + [good_file]:
681 with open(f, 'w'):
682 pass
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800683 # check that broken symlinks don't break sanitization
684 symlink = os.path.join(invalid_folder, 'broken-link')
685 os.symlink(os.path.join(results_folder, 'no-such-file'),
686 symlink)
687 fifo1 = os.path.join(results_folder, 'test_fifo1')
688 fifo2 = os.path.join(good_folder, 'test_fifo2')
689 fifo3 = os.path.join(invalid_folder, 'test_fifo3')
690 invalid_fifo4_name = 'test_fifo4_%s' % invalid_chars
691 fifo4 = os.path.join(invalid_folder, invalid_fifo4_name)
692 os.mkfifo(fifo1)
693 os.mkfifo(fifo2)
694 os.mkfifo(fifo3)
695 os.mkfifo(fifo4)
Dan Shiaffb9222015-04-15 17:05:47 -0700696 gs_offloader.sanitize_dir(results_folder)
697 for _, dirs, files in os.walk(results_folder):
698 for name in dirs + files:
Allen Lib41527d2017-06-22 17:28:00 -0700699 self.assertEqual(name, gslib.escape(name))
Dan Shiaffb9222015-04-15 17:05:47 -0700700 for c in name:
Allen Lib41527d2017-06-22 17:28:00 -0700701 self.assertFalse(c in ['[', ']', '*', '?', '#'])
Dan Shiaffb9222015-04-15 17:05:47 -0700702 self.assertTrue(os.path.exists(good_file))
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800703
704 self.assertTrue(os.path.exists(fifo1))
705 self.assertFalse(is_fifo(fifo1))
706 self.assertTrue(os.path.exists(fifo2))
707 self.assertFalse(is_fifo(fifo2))
708 corrected_folder = os.path.join(
Allen Lib41527d2017-06-22 17:28:00 -0700709 results_folder, gslib.escape(invalid_folder_name))
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800710 corrected_fifo3 = os.path.join(
711 corrected_folder,
712 'test_fifo3')
713 self.assertFalse(os.path.exists(fifo3))
714 self.assertTrue(os.path.exists(corrected_fifo3))
715 self.assertFalse(is_fifo(corrected_fifo3))
716 corrected_fifo4 = os.path.join(
Allen Lib41527d2017-06-22 17:28:00 -0700717 corrected_folder, gslib.escape(invalid_fifo4_name))
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800718 self.assertFalse(os.path.exists(fifo4))
719 self.assertTrue(os.path.exists(corrected_fifo4))
720 self.assertFalse(is_fifo(corrected_fifo4))
721
722 corrected_symlink = os.path.join(
723 corrected_folder,
724 'broken-link')
725 self.assertFalse(os.path.lexists(symlink))
726 self.assertTrue(os.path.exists(corrected_symlink))
727 self.assertFalse(os.path.islink(corrected_symlink))
Dan Shiaffb9222015-04-15 17:05:47 -0700728 shutil.rmtree(results_folder)
729
730
Dan Shi1b4c7c32015-10-05 10:38:57 -0700731 def check_limit_file_count(self, is_test_job=True):
732 """Test that folder with too many files can be compressed.
733
734 @param is_test_job: True to check the method with test job result
735 folder. Set to False for special task folder.
736 """
737 results_folder = tempfile.mkdtemp()
738 host_folder = os.path.join(
739 results_folder,
740 'lab1-host1' if is_test_job else 'hosts/lab1-host1/1-repair')
741 debug_folder = os.path.join(host_folder, 'debug')
742 sysinfo_folder = os.path.join(host_folder, 'sysinfo')
743 for folder in [debug_folder, sysinfo_folder]:
744 os.makedirs(folder)
745 for i in range(10):
746 with open(os.path.join(folder, str(i)), 'w') as f:
747 f.write('test')
748
Allen Lib41527d2017-06-22 17:28:00 -0700749 gs_offloader._MAX_FILE_COUNT = 100
Dan Shi1b4c7c32015-10-05 10:38:57 -0700750 gs_offloader.limit_file_count(
751 results_folder if is_test_job else host_folder)
752 self.assertTrue(os.path.exists(sysinfo_folder))
753
Allen Lib41527d2017-06-22 17:28:00 -0700754 gs_offloader._MAX_FILE_COUNT = 10
Dan Shi1b4c7c32015-10-05 10:38:57 -0700755 gs_offloader.limit_file_count(
756 results_folder if is_test_job else host_folder)
757 self.assertFalse(os.path.exists(sysinfo_folder))
758 self.assertTrue(os.path.exists(sysinfo_folder + '.tgz'))
759 self.assertTrue(os.path.exists(debug_folder))
760
761 shutil.rmtree(results_folder)
762
763
764 def test_limit_file_count(self):
765 """Test that folder with too many files can be compressed.
766 """
767 self.check_limit_file_count(is_test_job=True)
768 self.check_limit_file_count(is_test_job=False)
769
Ningning Xia2d88eec2016-07-25 23:18:46 -0700770
Ningning Xia8db632f2016-08-19 11:01:35 -0700771 def test_is_valid_result(self):
772 """Test _is_valid_result."""
Ningning Xia21922c82016-07-29 11:03:15 -0700773 release_build = 'veyron_minnie-cheets-release/R52-8248.0.0'
774 pfq_build = 'cyan-cheets-android-pfq/R54-8623.0.0-rc1'
775 trybot_build = 'trybot-samus-release/R54-8640.0.0-b5092'
776 trybot_2_build = 'trybot-samus-pfq/R54-8640.0.0-b5092'
777 release_2_build = 'test-trybot-release/R54-8640.0.0-b5092'
Ningning Xia8db632f2016-08-19 11:01:35 -0700778 self.assertTrue(gs_offloader._is_valid_result(
779 release_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
Rohit Makasana6384c102016-10-21 17:09:47 -0700780 self.assertTrue(gs_offloader._is_valid_result(
781 release_build, gs_offloader.CTS_RESULT_PATTERN, 'test_that_wrapper'))
Rohit Makasana8d868c92018-06-08 11:29:50 -0700782 self.assertTrue(gs_offloader._is_valid_result(
Rohit Makasana54c78f72019-09-17 17:20:03 -0700783 release_build, gs_offloader.CTS_RESULT_PATTERN, 'cros_test_platform'))
784 self.assertTrue(gs_offloader._is_valid_result(
Richard Barnette33e12892017-05-26 09:25:34 -0700785 release_build, gs_offloader.CTS_RESULT_PATTERN, 'bvt-arc'))
Rohit Makasana8d868c92018-06-08 11:29:50 -0700786 self.assertFalse(gs_offloader._is_valid_result(
787 release_build, gs_offloader.CTS_RESULT_PATTERN, 'bvt-cq'))
Ningning Xia8db632f2016-08-19 11:01:35 -0700788 self.assertTrue(gs_offloader._is_valid_result(
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800789 release_build, gs_offloader.CTS_V2_RESULT_PATTERN, 'arc-gts'))
Ningning Xia8db632f2016-08-19 11:01:35 -0700790 self.assertFalse(gs_offloader._is_valid_result(
791 None, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
792 self.assertFalse(gs_offloader._is_valid_result(
793 release_build, gs_offloader.CTS_RESULT_PATTERN, None))
794 self.assertFalse(gs_offloader._is_valid_result(
795 pfq_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
796 self.assertFalse(gs_offloader._is_valid_result(
797 trybot_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
798 self.assertFalse(gs_offloader._is_valid_result(
799 trybot_2_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
800 self.assertTrue(gs_offloader._is_valid_result(
801 release_2_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
Ningning Xia21922c82016-07-29 11:03:15 -0700802
803
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700804 def create_results_folder(self):
805 """Create CTS/GTS results folders."""
Ningning Xia42111242016-06-15 14:35:58 -0700806 results_folder = tempfile.mkdtemp()
807 host_folder = os.path.join(results_folder, 'chromeos4-row9-rack11-host22')
808 debug_folder = os.path.join(host_folder, 'debug')
809 sysinfo_folder = os.path.join(host_folder, 'sysinfo')
810 cts_result_folder = os.path.join(
811 host_folder, 'cheets_CTS.android.dpi', 'results', 'cts-results')
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800812 cts_v2_result_folder = os.path.join(host_folder,
813 'cheets_CTS_N.CtsGraphicsTestCases', 'results', 'android-cts')
Ningning Xia2d88eec2016-07-25 23:18:46 -0700814 gts_result_folder = os.path.join(
Ilja H. Friedelbfa63142017-01-26 00:56:29 -0800815 host_folder, 'cheets_GTS.google.admin', 'results', 'android-gts')
Ningning Xia42111242016-06-15 14:35:58 -0700816 timestamp_str = '2016.04.28_01.41.44'
Ningning Xia2d88eec2016-07-25 23:18:46 -0700817 timestamp_cts_folder = os.path.join(cts_result_folder, timestamp_str)
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800818 timestamp_cts_v2_folder = os.path.join(cts_v2_result_folder, timestamp_str)
Ningning Xia2d88eec2016-07-25 23:18:46 -0700819 timestamp_gts_folder = os.path.join(gts_result_folder, timestamp_str)
820
Rohit Makasana2030dde2019-04-05 04:18:52 -0700821 # Build host keyvals set to parse model info.
822 host_info_path = os.path.join(host_folder, 'host_keyvals')
823 dir_to_create = '/'
824 for tdir in host_info_path.split('/'):
825 dir_to_create = os.path.join(dir_to_create, tdir)
826 if not os.path.exists(dir_to_create):
827 os.mkdir(dir_to_create)
828 with open(os.path.join(host_info_path, 'chromeos4-row9-rack11-host22'), 'w') as store_file:
829 store_file.write('labels=board%3Acoral,hw_video_acc_vp9,cros,'+
830 'hw_jpeg_acc_dec,bluetooth,model%3Arobo360,'+
831 'accel%3Acros-ec,'+
832 'sku%3Arobo360_IntelR_CeleronR_CPU_N3450_1_10GHz_4Gb')
833
834 # .autoserv_execute file is needed for the test results package to look
835 # legit.
836 autoserve_path = os.path.join(host_folder, '.autoserv_execute')
837 with open(autoserve_path, 'w') as temp_file:
838 temp_file.write(' ')
839
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700840 # Test results in cts_result_folder with a different time-stamp.
841 timestamp_str_2 = '2016.04.28_10.41.44'
842 timestamp_cts_folder_2 = os.path.join(cts_result_folder, timestamp_str_2)
843
Ningning Xia2d88eec2016-07-25 23:18:46 -0700844 for folder in [debug_folder, sysinfo_folder, cts_result_folder,
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800845 timestamp_cts_folder, timestamp_cts_folder_2,
846 timestamp_cts_v2_folder, timestamp_gts_folder]:
Ningning Xia42111242016-06-15 14:35:58 -0700847 os.makedirs(folder)
Ningning Xia2d88eec2016-07-25 23:18:46 -0700848
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700849 path_pattern_pair = [(timestamp_cts_folder, gs_offloader.CTS_RESULT_PATTERN),
850 (timestamp_cts_folder_2, gs_offloader.CTS_RESULT_PATTERN),
Rohit Makasanac1c7e782019-04-02 18:06:00 -0700851 (timestamp_cts_folder_2, gs_offloader.CTS_COMPRESSED_RESULT_PATTERN),
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800852 (timestamp_cts_v2_folder, gs_offloader.CTS_V2_RESULT_PATTERN),
Rohit Makasanac1c7e782019-04-02 18:06:00 -0700853 (timestamp_cts_v2_folder, gs_offloader.CTS_V2_COMPRESSED_RESULT_PATTERN),
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800854 (timestamp_gts_folder, gs_offloader.CTS_V2_RESULT_PATTERN)]
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700855
856 # Create timestamp.zip file_path.
Ningning Xia2d88eec2016-07-25 23:18:46 -0700857 cts_zip_file = os.path.join(cts_result_folder, timestamp_str + '.zip')
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700858 cts_zip_file_2 = os.path.join(cts_result_folder, timestamp_str_2 + '.zip')
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800859 cts_v2_zip_file = os.path.join(cts_v2_result_folder, timestamp_str + '.zip')
Ningning Xia2d88eec2016-07-25 23:18:46 -0700860 gts_zip_file = os.path.join(gts_result_folder, timestamp_str + '.zip')
Ningning Xia2d88eec2016-07-25 23:18:46 -0700861
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700862 # Create xml file_path.
863 cts_result_file = os.path.join(timestamp_cts_folder, 'testResult.xml')
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800864 cts_result_file_2 = os.path.join(timestamp_cts_folder_2,
865 'testResult.xml')
Rohit Makasanac1c7e782019-04-02 18:06:00 -0700866 cts_result_compressed_file_2 = os.path.join(timestamp_cts_folder_2,
867 'testResult.xml.tgz')
Ilja H. Friedelbfa63142017-01-26 00:56:29 -0800868 gts_result_file = os.path.join(timestamp_gts_folder, 'test_result.xml')
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800869 cts_v2_result_file = os.path.join(timestamp_cts_v2_folder,
870 'test_result.xml')
Rohit Makasanac1c7e782019-04-02 18:06:00 -0700871 cts_v2_result_compressed_file = os.path.join(timestamp_cts_v2_folder,
872 'test_result.xml.tgz')
Ningning Xia2d88eec2016-07-25 23:18:46 -0700873
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800874 for file_path in [cts_zip_file, cts_zip_file_2, cts_v2_zip_file,
875 gts_zip_file, cts_result_file, cts_result_file_2,
Rohit Makasanac1c7e782019-04-02 18:06:00 -0700876 cts_result_compressed_file_2, gts_result_file,
877 cts_v2_result_file, cts_v2_result_compressed_file]:
878 if file_path.endswith('tgz'):
879 test_result_file = gs_offloader.CTS_COMPRESSED_RESULT_TYPES[
880 os.path.basename(file_path)]
881 with open(test_result_file, 'w') as f:
882 f.write('test')
883 with tarfile.open(file_path, 'w:gz') as tar_file:
884 tar_file.add(test_result_file)
885 os.remove(test_result_file)
886 else:
887 with open(file_path, 'w') as f:
888 f.write('test')
Ningning Xia42111242016-06-15 14:35:58 -0700889
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700890 return (results_folder, host_folder, path_pattern_pair)
Ningning Xia2d88eec2016-07-25 23:18:46 -0700891
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700892
Allen Lib41527d2017-06-22 17:28:00 -0700893 def test__upload_cts_testresult(self):
894 """Test _upload_cts_testresult."""
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700895 results_folder, host_folder, path_pattern_pair = self.create_results_folder()
896
897 self.mox.StubOutWithMock(gs_offloader, '_upload_files')
898 gs_offloader._upload_files(
Rohit Makasanaea337c52018-04-11 18:03:41 -0700899 mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg(), False,
900 mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700901 ['test', '-d', host_folder])
902 gs_offloader._upload_files(
Rohit Makasanaea337c52018-04-11 18:03:41 -0700903 mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg(), False,
904 mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700905 ['test', '-d', host_folder])
906 gs_offloader._upload_files(
Rohit Makasanaea337c52018-04-11 18:03:41 -0700907 mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg(), False,
908 mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700909 ['test', '-d', host_folder])
Ningning Xia2d88eec2016-07-25 23:18:46 -0700910
Ningning Xia42111242016-06-15 14:35:58 -0700911 self.mox.ReplayAll()
Allen Lib41527d2017-06-22 17:28:00 -0700912 gs_offloader._upload_cts_testresult(results_folder, False)
Ningning Xia42111242016-06-15 14:35:58 -0700913 self.mox.VerifyAll()
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700914 shutil.rmtree(results_folder)
915
916
Keith Haddow295a54e2019-07-10 15:27:17 -0700917 def test_parse_cts_job_results_file_path(self):
918 # A autotest path
919 path = ('/317739475-chromeos-test/chromeos4-row9-rack11-host22/'
920 'cheets_CTS.android.dpi/results/cts-results/'
921 '2016.04.28_01.41.44')
922 job_id, package, timestamp = \
923 gs_offloader._parse_cts_job_results_file_path(path)
924 self.assertEqual('317739475-chromeos-test', job_id)
925 self.assertEqual('cheets_CTS.android.dpi', package)
926 self.assertEqual('2016.04.28_01.41.44', timestamp)
927
928
929 # A skylab path
930 path = ('/swarming-458e3a3a7fc6f210/1/autoserv_test/'
931 'cheets_CTS.android.dpi/results/cts-results/'
932 '2016.04.28_01.41.44')
933 job_id, package, timestamp = \
934 gs_offloader._parse_cts_job_results_file_path(path)
935 self.assertEqual('swarming-458e3a3a7fc6f210-1', job_id)
936 self.assertEqual('cheets_CTS.android.dpi', package)
937 self.assertEqual('2016.04.28_01.41.44', timestamp)
938
939
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700940 def test_upload_files(self):
941 """Test upload_files"""
942 results_folder, host_folder, path_pattern_pair = self.create_results_folder()
943
944 for path, pattern in path_pattern_pair:
945 models.test.parse_job_keyval(mox.IgnoreArg()).AndReturn({
946 'build': 'veyron_minnie-cheets-release/R52-8248.0.0',
Rohit Makasana2030dde2019-04-05 04:18:52 -0700947 'hostname': 'chromeos4-row9-rack11-host22',
Ningning Xia8db632f2016-08-19 11:01:35 -0700948 'parent_job_id': 'p_id',
949 'suite': 'arc-cts'
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700950 })
951
Allen Lib41527d2017-06-22 17:28:00 -0700952 gs_offloader._get_cmd_list(
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700953 False, mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
954 ['test', '-d', path])
Allen Lib41527d2017-06-22 17:28:00 -0700955 gs_offloader._get_cmd_list(
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700956 False, mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
957 ['test', '-d', path])
958
959 self.mox.ReplayAll()
Rohit Makasanaea337c52018-04-11 18:03:41 -0700960 gs_offloader._upload_files(host_folder, path, pattern, False,
961 'gs://a-test-bucket/',
962 'gs://a-test-apfe-bucket/')
Ningning Xia0c27d9b2016-08-04 14:02:39 -0700963 self.mox.VerifyAll()
964 self.mox.ResetAll()
Ningning Xia42111242016-06-15 14:35:58 -0700965
966 shutil.rmtree(results_folder)
Dan Shi1b4c7c32015-10-05 10:38:57 -0700967
Michael Tang97d188c2016-06-25 11:18:42 -0700968
Dan Shi02dd0662017-05-23 11:24:32 -0700969 def test_get_metrics_fields(self):
970 """Test method _get_metrics_fields."""
971 results_folder, host_folder, _ = self.create_results_folder()
972 models.test.parse_job_keyval(mox.IgnoreArg()).AndReturn({
973 'build': 'veyron_minnie-cheets-release/R52-8248.0.0',
974 'parent_job_id': 'p_id',
975 'suite': 'arc-cts'
976 })
977 try:
978 self.mox.ReplayAll()
979 self.assertEqual({'board': 'veyron_minnie-cheets',
980 'milestone': 'R52'},
981 gs_offloader._get_metrics_fields(host_folder))
982 self.mox.VerifyAll()
983 finally:
984 shutil.rmtree(results_folder)
985
986
J. Richard Barnetteea785362014-03-17 16:00:53 -0700987class JobDirectoryOffloadTests(_TempResultsDirTestBase):
988 """Tests for `_JobDirectory.enqueue_offload()`.
989
990 When testing with a `days_old` parameter of 0, we use
991 `set_finished()` instead of `set_expired()`. This causes the
992 job's timestamp to be set in the future. This is done so as
993 to test that when `days_old` is 0, the job is always treated
994 as eligible for offload, regardless of the timestamp's value.
995
996 Testing covers the following assertions:
997 A. Each time `enqueue_offload()` is called, a message that
998 includes the job's directory name will be logged using
999 `logging.debug()`, regardless of whether the job was
1000 enqueued. Nothing else is allowed to be logged.
1001 B. If the job is not eligible to be offloaded,
Allen Lib41527d2017-06-22 17:28:00 -07001002 `first_offload_start` and `offload_count` are 0.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001003 C. If the job is not eligible for offload, nothing is
1004 enqueued in `queue`.
Allen Lib41527d2017-06-22 17:28:00 -07001005 D. When the job is offloaded, `offload_count` increments
J. Richard Barnetteea785362014-03-17 16:00:53 -07001006 each time.
1007 E. When the job is offloaded, the appropriate parameters are
1008 enqueued exactly once.
Allen Lib41527d2017-06-22 17:28:00 -07001009 F. The first time a job is offloaded, `first_offload_start` is
J. Richard Barnetteea785362014-03-17 16:00:53 -07001010 set to the current time.
Allen Lib41527d2017-06-22 17:28:00 -07001011 G. `first_offload_start` only changes the first time that the
J. Richard Barnetteea785362014-03-17 16:00:53 -07001012 job is offloaded.
1013
1014 The test cases below are designed to exercise all of the
1015 meaningful state transitions at least once.
1016
1017 """
1018
1019 def setUp(self):
1020 super(JobDirectoryOffloadTests, self).setUp()
J. Richard Barnette08800322014-05-16 14:49:46 -07001021 self._job = self.make_job(self.REGULAR_JOBLIST[0])
J. Richard Barnetteea785362014-03-17 16:00:53 -07001022 self._queue = Queue.Queue()
J. Richard Barnetteea785362014-03-17 16:00:53 -07001023
Jakob Juelich24f22c22014-09-26 11:46:11 -07001024
J. Richard Barnetteea785362014-03-17 16:00:53 -07001025 def _offload_unexpired_job(self, days_old):
1026 """Make calls to `enqueue_offload()` for an unexpired job.
1027
1028 This method tests assertions B and C that calling
1029 `enqueue_offload()` has no effect.
1030
1031 """
Allen Lib41527d2017-06-22 17:28:00 -07001032 self.assertEqual(self._job.offload_count, 0)
1033 self.assertEqual(self._job.first_offload_start, 0)
1034 gs_offloader._enqueue_offload(self._job, self._queue, days_old)
1035 gs_offloader._enqueue_offload(self._job, self._queue, days_old)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001036 self.assertTrue(self._queue.empty())
Allen Lib41527d2017-06-22 17:28:00 -07001037 self.assertEqual(self._job.offload_count, 0)
1038 self.assertEqual(self._job.first_offload_start, 0)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001039
Jakob Juelich24f22c22014-09-26 11:46:11 -07001040
J. Richard Barnetteea785362014-03-17 16:00:53 -07001041 def _offload_expired_once(self, days_old, count):
1042 """Make one call to `enqueue_offload()` for an expired job.
1043
1044 This method tests assertions D and E regarding side-effects
1045 expected when a job is offloaded.
1046
1047 """
Allen Lib41527d2017-06-22 17:28:00 -07001048 gs_offloader._enqueue_offload(self._job, self._queue, days_old)
1049 self.assertEqual(self._job.offload_count, count)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001050 self.assertFalse(self._queue.empty())
1051 v = self._queue.get_nowait()
1052 self.assertTrue(self._queue.empty())
J. Richard Barnette2c72ddd2014-05-20 12:17:37 -07001053 self.assertEqual(v, self._job.queue_args)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001054
Jakob Juelich24f22c22014-09-26 11:46:11 -07001055
J. Richard Barnetteea785362014-03-17 16:00:53 -07001056 def _offload_expired_job(self, days_old):
1057 """Make calls to `enqueue_offload()` for a just-expired job.
1058
1059 This method directly tests assertions F and G regarding
Allen Lib41527d2017-06-22 17:28:00 -07001060 side-effects on `first_offload_start`.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001061
1062 """
1063 t0 = time.time()
1064 self._offload_expired_once(days_old, 1)
Allen Lib41527d2017-06-22 17:28:00 -07001065 t1 = self._job.first_offload_start
J. Richard Barnetteea785362014-03-17 16:00:53 -07001066 self.assertLessEqual(t1, time.time())
1067 self.assertGreaterEqual(t1, t0)
1068 self._offload_expired_once(days_old, 2)
Allen Lib41527d2017-06-22 17:28:00 -07001069 self.assertEqual(self._job.first_offload_start, t1)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001070 self._offload_expired_once(days_old, 3)
Allen Lib41527d2017-06-22 17:28:00 -07001071 self.assertEqual(self._job.first_offload_start, t1)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001072
Jakob Juelich24f22c22014-09-26 11:46:11 -07001073
J. Richard Barnetteea785362014-03-17 16:00:53 -07001074 def test_case_1_no_expiration(self):
1075 """Test a series of `enqueue_offload()` calls with `days_old` of 0.
1076
1077 This tests that offload works as expected if calls are
1078 made both before and after the job becomes expired.
1079
1080 """
1081 self._offload_unexpired_job(0)
1082 self._job.set_finished(0)
1083 self._offload_expired_job(0)
1084
Jakob Juelich24f22c22014-09-26 11:46:11 -07001085
J. Richard Barnetteea785362014-03-17 16:00:53 -07001086 def test_case_2_no_expiration(self):
1087 """Test a series of `enqueue_offload()` calls with `days_old` of 0.
1088
1089 This tests that offload works as expected if calls are made
1090 only after the job becomes expired.
1091
1092 """
1093 self._job.set_finished(0)
1094 self._offload_expired_job(0)
1095
Jakob Juelich24f22c22014-09-26 11:46:11 -07001096
J. Richard Barnetteea785362014-03-17 16:00:53 -07001097 def test_case_1_with_expiration(self):
1098 """Test a series of `enqueue_offload()` calls with `days_old` non-zero.
1099
1100 This tests that offload works as expected if calls are made
1101 before the job finishes, before the job expires, and after
1102 the job expires.
1103
1104 """
1105 self._offload_unexpired_job(_TEST_EXPIRATION_AGE)
1106 self._job.set_finished(_TEST_EXPIRATION_AGE)
1107 self._offload_unexpired_job(_TEST_EXPIRATION_AGE)
1108 self._job.set_expired(_TEST_EXPIRATION_AGE)
1109 self._offload_expired_job(_TEST_EXPIRATION_AGE)
1110
Jakob Juelich24f22c22014-09-26 11:46:11 -07001111
J. Richard Barnetteea785362014-03-17 16:00:53 -07001112 def test_case_2_with_expiration(self):
1113 """Test a series of `enqueue_offload()` calls with `days_old` non-zero.
1114
1115 This tests that offload works as expected if calls are made
1116 between finishing and expiration, and after the job expires.
1117
1118 """
1119 self._job.set_finished(_TEST_EXPIRATION_AGE)
1120 self._offload_unexpired_job(_TEST_EXPIRATION_AGE)
1121 self._job.set_expired(_TEST_EXPIRATION_AGE)
1122 self._offload_expired_job(_TEST_EXPIRATION_AGE)
1123
Jakob Juelich24f22c22014-09-26 11:46:11 -07001124
J. Richard Barnetteea785362014-03-17 16:00:53 -07001125 def test_case_3_with_expiration(self):
1126 """Test a series of `enqueue_offload()` calls with `days_old` non-zero.
1127
1128 This tests that offload works as expected if calls are made
1129 only before finishing and after expiration.
1130
1131 """
1132 self._offload_unexpired_job(_TEST_EXPIRATION_AGE)
1133 self._job.set_expired(_TEST_EXPIRATION_AGE)
1134 self._offload_expired_job(_TEST_EXPIRATION_AGE)
1135
Jakob Juelich24f22c22014-09-26 11:46:11 -07001136
J. Richard Barnetteea785362014-03-17 16:00:53 -07001137 def test_case_4_with_expiration(self):
1138 """Test a series of `enqueue_offload()` calls with `days_old` non-zero.
1139
1140 This tests that offload works as expected if calls are made
1141 only after expiration.
1142
1143 """
1144 self._job.set_expired(_TEST_EXPIRATION_AGE)
1145 self._offload_expired_job(_TEST_EXPIRATION_AGE)
1146
1147
1148class GetJobDirectoriesTests(_TempResultsDirTestBase):
1149 """Tests for `_JobDirectory.get_job_directories()`."""
1150
J. Richard Barnetteea785362014-03-17 16:00:53 -07001151 def setUp(self):
1152 super(GetJobDirectoriesTests, self).setUp()
J. Richard Barnette08800322014-05-16 14:49:46 -07001153 self.make_job_hierarchy()
1154 os.mkdir('not-a-job')
1155 open('not-a-dir', 'w').close()
J. Richard Barnetteea785362014-03-17 16:00:53 -07001156
Jakob Juelich24f22c22014-09-26 11:46:11 -07001157
J. Richard Barnetteea785362014-03-17 16:00:53 -07001158 def _run_get_directories(self, cls, expected_list):
1159 """Test `get_job_directories()` for the given class.
1160
1161 Calls the method, and asserts that the returned list of
1162 directories matches the expected return value.
1163
1164 @param expected_list Expected return value from the call.
1165 """
J. Richard Barnetteea785362014-03-17 16:00:53 -07001166 dirlist = cls.get_job_directories()
1167 self.assertEqual(set(dirlist), set(expected_list))
J. Richard Barnetteea785362014-03-17 16:00:53 -07001168
Jakob Juelich24f22c22014-09-26 11:46:11 -07001169
J. Richard Barnetteea785362014-03-17 16:00:53 -07001170 def test_get_regular_jobs(self):
1171 """Test `RegularJobDirectory.get_job_directories()`."""
1172 self._run_get_directories(job_directories.RegularJobDirectory,
J. Richard Barnette08800322014-05-16 14:49:46 -07001173 self.REGULAR_JOBLIST)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001174
Jakob Juelich24f22c22014-09-26 11:46:11 -07001175
J. Richard Barnetteea785362014-03-17 16:00:53 -07001176 def test_get_special_jobs(self):
1177 """Test `SpecialJobDirectory.get_job_directories()`."""
1178 self._run_get_directories(job_directories.SpecialJobDirectory,
J. Richard Barnette08800322014-05-16 14:49:46 -07001179 self.SPECIAL_JOBLIST)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001180
1181
1182class AddJobsTests(_TempResultsDirTestBase):
1183 """Tests for `Offloader._add_new_jobs()`."""
1184
J. Richard Barnette08800322014-05-16 14:49:46 -07001185 MOREJOBS = ['115-fubar', '116-fubar', '117-fubar', '118-snafu']
J. Richard Barnetteea785362014-03-17 16:00:53 -07001186
1187 def setUp(self):
1188 super(AddJobsTests, self).setUp()
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001189 self._initial_job_names = (
1190 set(self.REGULAR_JOBLIST) | set(self.SPECIAL_JOBLIST))
J. Richard Barnette08800322014-05-16 14:49:46 -07001191 self.make_job_hierarchy()
1192 self._offloader = gs_offloader.Offloader(_get_options(['-a']))
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001193 self.mox.StubOutWithMock(logging, 'debug')
J. Richard Barnetteea785362014-03-17 16:00:53 -07001194
Jakob Juelich24f22c22014-09-26 11:46:11 -07001195
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001196 def _run_add_new_jobs(self, expected_key_set):
J. Richard Barnetteea785362014-03-17 16:00:53 -07001197 """Basic test assertions for `_add_new_jobs()`.
1198
1199 Asserts the following:
1200 * The keys in the offloader's `_open_jobs` dictionary
1201 matches the expected set of keys.
1202 * For every job in `_open_jobs`, the job has the expected
1203 directory name.
1204
1205 """
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001206 count = len(expected_key_set) - len(self._offloader._open_jobs)
1207 logging.debug(mox.IgnoreArg(), count)
1208 self.mox.ReplayAll()
1209 self._offloader._add_new_jobs()
J. Richard Barnetteea785362014-03-17 16:00:53 -07001210 self.assertEqual(expected_key_set,
1211 set(self._offloader._open_jobs.keys()))
1212 for jobkey, job in self._offloader._open_jobs.items():
Allen Lib41527d2017-06-22 17:28:00 -07001213 self.assertEqual(jobkey, job.dirname)
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001214 self.mox.VerifyAll()
1215 self.mox.ResetAll()
J. Richard Barnetteea785362014-03-17 16:00:53 -07001216
Jakob Juelich24f22c22014-09-26 11:46:11 -07001217
J. Richard Barnetteea785362014-03-17 16:00:53 -07001218 def test_add_jobs_empty(self):
1219 """Test adding jobs to an empty dictionary.
1220
1221 Calls the offloader's `_add_new_jobs()`, then perform
1222 the assertions of `self._check_open_jobs()`.
1223
1224 """
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001225 self._run_add_new_jobs(self._initial_job_names)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001226
Jakob Juelich24f22c22014-09-26 11:46:11 -07001227
J. Richard Barnetteea785362014-03-17 16:00:53 -07001228 def test_add_jobs_non_empty(self):
1229 """Test adding jobs to a non-empty dictionary.
1230
1231 Calls the offloader's `_add_new_jobs()` twice; once from
1232 initial conditions, and then again after adding more
1233 directories. After the second call, perform the assertions
1234 of `self._check_open_jobs()`. Additionally, assert that
1235 keys added by the first call still map to their original
1236 job object after the second call.
1237
1238 """
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001239 self._run_add_new_jobs(self._initial_job_names)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001240 jobs_copy = self._offloader._open_jobs.copy()
J. Richard Barnette08800322014-05-16 14:49:46 -07001241 for d in self.MOREJOBS:
1242 os.mkdir(d)
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001243 self._run_add_new_jobs(self._initial_job_names |
1244 set(self.MOREJOBS))
J. Richard Barnetteea785362014-03-17 16:00:53 -07001245 for key in jobs_copy.keys():
1246 self.assertIs(jobs_copy[key],
1247 self._offloader._open_jobs[key])
1248
1249
J. Richard Barnetteea785362014-03-17 16:00:53 -07001250class ReportingTests(_TempResultsDirTestBase):
Allen Lib41527d2017-06-22 17:28:00 -07001251 """Tests for `Offloader._report_failed_jobs()`."""
J. Richard Barnetteea785362014-03-17 16:00:53 -07001252
J. Richard Barnetteea785362014-03-17 16:00:53 -07001253 def setUp(self):
1254 super(ReportingTests, self).setUp()
1255 self._offloader = gs_offloader.Offloader(_get_options([]))
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001256 self.mox.StubOutWithMock(self._offloader, '_log_failed_jobs_locally')
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001257 self.mox.StubOutWithMock(logging, 'debug')
J. Richard Barnetteea785362014-03-17 16:00:53 -07001258
Jakob Juelich24f22c22014-09-26 11:46:11 -07001259
J. Richard Barnetteea785362014-03-17 16:00:53 -07001260 def _add_job(self, jobdir):
1261 """Add a job to the dictionary of unfinished jobs."""
1262 j = self.make_job(jobdir)
Allen Lib41527d2017-06-22 17:28:00 -07001263 self._offloader._open_jobs[j.dirname] = j
J. Richard Barnetteea785362014-03-17 16:00:53 -07001264 return j
1265
Jakob Juelich24f22c22014-09-26 11:46:11 -07001266
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001267 def _expect_log_message(self, new_open_jobs, with_failures):
1268 """Mock expected logging calls.
1269
Allen Lib41527d2017-06-22 17:28:00 -07001270 `_report_failed_jobs()` logs one message with the number
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001271 of jobs removed from the open job set and the number of jobs
1272 still remaining. Additionally, if there are reportable
1273 jobs, then it logs the number of jobs that haven't yet
1274 offloaded.
1275
1276 This sets up the logging calls using `new_open_jobs` to
1277 figure the job counts. If `with_failures` is true, then
1278 the log message is set up assuming that all jobs in
1279 `new_open_jobs` have offload failures.
1280
1281 @param new_open_jobs New job set for calculating counts
1282 in the messages.
1283 @param with_failures Whether the log message with a
1284 failure count is expected.
1285
1286 """
1287 count = len(self._offloader._open_jobs) - len(new_open_jobs)
1288 logging.debug(mox.IgnoreArg(), count, len(new_open_jobs))
1289 if with_failures:
1290 logging.debug(mox.IgnoreArg(), len(new_open_jobs))
1291
Jakob Juelich24f22c22014-09-26 11:46:11 -07001292
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001293 def _run_update(self, new_open_jobs):
Allen Lib41527d2017-06-22 17:28:00 -07001294 """Call `_report_failed_jobs()`.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001295
1296 Initial conditions are set up by the caller. This calls
Allen Lib41527d2017-06-22 17:28:00 -07001297 `_report_failed_jobs()` once, and then checks these
J. Richard Barnetteea785362014-03-17 16:00:53 -07001298 assertions:
J. Richard Barnetteea785362014-03-17 16:00:53 -07001299 * The offloader's new `_open_jobs` field contains only
1300 the entries in `new_open_jobs`.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001301
1302 @param new_open_jobs A dictionary representing the expected
1303 new value of the offloader's
1304 `_open_jobs` field.
1305 """
1306 self.mox.ReplayAll()
Allen Lib41527d2017-06-22 17:28:00 -07001307 self._offloader._report_failed_jobs()
1308 self._offloader._remove_offloaded_jobs()
J. Richard Barnetteea785362014-03-17 16:00:53 -07001309 self.assertEqual(self._offloader._open_jobs, new_open_jobs)
1310 self.mox.VerifyAll()
1311 self.mox.ResetAll()
1312
Jakob Juelich24f22c22014-09-26 11:46:11 -07001313
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001314 def _expect_failed_jobs(self, failed_jobs):
1315 """Mock expected call to log the failed jobs on local disk.
1316
1317 TODO(crbug.com/686904): The fact that we have to mock an internal
1318 function for this test is evidence that we need to pull out the local
1319 file formatter in its own object in a future CL.
1320
1321 @param failed_jobs: The list of jobs being logged as failed.
1322 """
1323 self._offloader._log_failed_jobs_locally(failed_jobs)
1324
1325
J. Richard Barnetteea785362014-03-17 16:00:53 -07001326 def test_no_jobs(self):
Allen Lib41527d2017-06-22 17:28:00 -07001327 """Test `_report_failed_jobs()` with no open jobs.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001328
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001329 Initial conditions are an empty `_open_jobs` list.
1330 Expected result is an empty `_open_jobs` list.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001331
1332 """
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001333 self._expect_log_message({}, False)
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001334 self._expect_failed_jobs([])
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001335 self._run_update({})
J. Richard Barnetteea785362014-03-17 16:00:53 -07001336
Jakob Juelich24f22c22014-09-26 11:46:11 -07001337
J. Richard Barnetteea785362014-03-17 16:00:53 -07001338 def test_all_completed(self):
Allen Lib41527d2017-06-22 17:28:00 -07001339 """Test `_report_failed_jobs()` with only complete jobs.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001340
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001341 Initial conditions are an `_open_jobs` list consisting of only completed
1342 jobs.
1343 Expected result is an empty `_open_jobs` list.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001344
1345 """
J. Richard Barnette08800322014-05-16 14:49:46 -07001346 for d in self.REGULAR_JOBLIST:
J. Richard Barnetteea785362014-03-17 16:00:53 -07001347 self._add_job(d).set_complete()
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001348 self._expect_log_message({}, False)
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001349 self._expect_failed_jobs([])
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001350 self._run_update({})
J. Richard Barnetteea785362014-03-17 16:00:53 -07001351
Jakob Juelich24f22c22014-09-26 11:46:11 -07001352
J. Richard Barnetteea785362014-03-17 16:00:53 -07001353 def test_none_finished(self):
Allen Lib41527d2017-06-22 17:28:00 -07001354 """Test `_report_failed_jobs()` with only unfinished jobs.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001355
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001356 Initial conditions are an `_open_jobs` list consisting of only
1357 unfinished jobs.
1358 Expected result is no change to the `_open_jobs` list.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001359
1360 """
J. Richard Barnette08800322014-05-16 14:49:46 -07001361 for d in self.REGULAR_JOBLIST:
J. Richard Barnetteea785362014-03-17 16:00:53 -07001362 self._add_job(d)
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001363 new_jobs = self._offloader._open_jobs.copy()
1364 self._expect_log_message(new_jobs, False)
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001365 self._expect_failed_jobs([])
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001366 self._run_update(new_jobs)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001367
1368
Allen Lib41527d2017-06-22 17:28:00 -07001369class GsOffloaderMockTests(_TempResultsDirTestCase):
1370 """Tests using mock instead of mox."""
1371
1372 def setUp(self):
1373 super(GsOffloaderMockTests, self).setUp()
1374 alarm = mock.patch('signal.alarm', return_value=0)
1375 alarm.start()
1376 self.addCleanup(alarm.stop)
1377
1378 self._saved_loglevel = logging.getLogger().getEffectiveLevel()
1379 logging.getLogger().setLevel(logging.CRITICAL + 1)
1380
1381 self._job = self.make_job(self.REGULAR_JOBLIST[0])
1382
1383
1384 def test_offload_timeout_early(self):
1385 """Test that `offload_dir()` times out correctly.
1386
1387 This test triggers timeout at the earliest possible moment,
1388 at the first call to set the timeout alarm.
1389
1390 """
1391 signal.alarm.side_effect = [0, timeout_util.TimeoutError('fubar')]
1392 with mock.patch.object(gs_offloader, '_upload_cts_testresult',
1393 autospec=True) as upload:
1394 upload.return_value = None
1395 gs_offloader.GSOffloader(
1396 utils.DEFAULT_OFFLOAD_GSURI, False, 0).offload(
1397 self._job.queue_args[0],
1398 self._job.queue_args[1],
1399 self._job.queue_args[2])
1400 self.assertTrue(os.path.isdir(self._job.queue_args[0]))
1401
1402
1403 # TODO(ayatane): This tests passes when run locally, but it fails
1404 # when run on trybot. I have no idea why, but the assert isdir
1405 # fails.
1406 #
1407 # This test is also kind of redundant since we are using the timeout
1408 # from chromite which has its own tests.
1409 @unittest.skip('This fails on trybot')
1410 def test_offload_timeout_late(self):
1411 """Test that `offload_dir()` times out correctly.
1412
1413 This test triggers timeout at the latest possible moment, at
1414 the call to clear the timeout alarm.
1415
1416 """
1417 signal.alarm.side_effect = [0, 0, timeout_util.TimeoutError('fubar')]
1418 with mock.patch.object(gs_offloader, '_upload_cts_testresult',
1419 autospec=True) as upload, \
1420 mock.patch.object(gs_offloader, '_get_cmd_list',
1421 autospec=True) as get_cmd_list:
1422 upload.return_value = None
1423 get_cmd_list.return_value = ['test', '-d', self._job.queue_args[0]]
1424 gs_offloader.GSOffloader(
1425 utils.DEFAULT_OFFLOAD_GSURI, False, 0).offload(
1426 self._job.queue_args[0],
1427 self._job.queue_args[1],
1428 self._job.queue_args[2])
1429 self.assertTrue(os.path.isdir(self._job.queue_args[0]))
1430
1431
1432
J. Richard Barnetteea785362014-03-17 16:00:53 -07001433if __name__ == '__main__':
1434 unittest.main()