blob: 256e958ee460d5748d780a9af3e86b31c0414a3f [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
8import datetime
9import logging
10import os
11import shutil
J. Richard Barnette2e443ef2014-05-20 12:31:35 -070012import signal
Laurence Goodbyca7726d2017-02-14 17:09:07 -080013import stat
J. Richard Barnetteea785362014-03-17 16:00:53 -070014import sys
15import 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 time_utils
25from autotest_lib.client.common_lib import utils
Michael Tange8bc9592017-07-06 10:59:32 -070026#For unittest without cloud_client.proto compiled.
27try:
28 from autotest_lib.site_utils import cloud_console_client
29except ImportError:
30 cloud_console_client = None
Prathmesh Prabhubeb9e012017-01-30 16:18:39 -080031from autotest_lib.site_utils import gs_offloader
32from autotest_lib.site_utils import job_directories
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
41# When constructing sample time values for testing expiration,
42# allow this many seconds between the expiration time and the
43# current time.
44_MARGIN_SECS = 10.0
45
46
47def _get_options(argv):
48 """Helper function to exercise command line parsing.
49
50 @param argv Value of sys.argv to be parsed.
51
52 """
53 sys.argv = ['bogus.py'] + argv
54 return gs_offloader.parse_options()
55
56
Laurence Goodbyca7726d2017-02-14 17:09:07 -080057def is_fifo(path):
Allen Li93585382017-05-05 14:24:53 -070058 """Determines whether a path is a fifo.
59
60 @param path: fifo path string.
61 """
Laurence Goodbyca7726d2017-02-14 17:09:07 -080062 return stat.S_ISFIFO(os.lstat(path).st_mode)
63
64
Simran Basidd129972014-09-11 14:34:49 -070065class OffloaderOptionsTests(mox.MoxTestBase):
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -070066 """Tests for the `Offloader` constructor.
67
68 Tests that offloader instance fields are set as expected
69 for given command line options.
70
71 """
72
73 _REGULAR_ONLY = set([job_directories.RegularJobDirectory])
74 _SPECIAL_ONLY = set([job_directories.SpecialJobDirectory])
75 _BOTH = _REGULAR_ONLY | _SPECIAL_ONLY
J. Richard Barnetteea785362014-03-17 16:00:53 -070076
Jakob Juelich24f22c22014-09-26 11:46:11 -070077
Simran Basidd129972014-09-11 14:34:49 -070078 def setUp(self):
79 super(OffloaderOptionsTests, self).setUp()
80 self.mox.StubOutWithMock(utils, 'get_offload_gsuri')
Simran Basif3e305f2014-10-03 14:43:53 -070081 gs_offloader.GS_OFFLOADING_ENABLED = True
Michael Tang0df2eb42016-05-13 19:06:54 -070082 gs_offloader.GS_OFFLOADER_MULTIPROCESSING = False
Simran Basidd129972014-09-11 14:34:49 -070083
Jakob Juelich24f22c22014-09-26 11:46:11 -070084
Allen Lib41527d2017-06-22 17:28:00 -070085 def _mock_get_sub_offloader(self, is_moblab, multiprocessing=False,
Michael Tang0f553bd2017-06-16 17:38:45 -070086 console_client=None, delete_age=0):
Simran Basidd129972014-09-11 14:34:49 -070087 """Mock the process of getting the offload_dir function."""
88 if is_moblab:
89 expected_gsuri = '%sresults/%s/%s/' % (
90 global_config.global_config.get_config_value(
91 'CROS', 'image_storage_server'),
92 'Fa:ke:ma:c0:12:34', 'rand0m-uu1d')
93 else:
94 expected_gsuri = utils.DEFAULT_OFFLOAD_GSURI
95 utils.get_offload_gsuri().AndReturn(expected_gsuri)
Allen Lib41527d2017-06-22 17:28:00 -070096 sub_offloader = gs_offloader.GSOffloader(expected_gsuri,
Michael Tang0f553bd2017-06-16 17:38:45 -070097 multiprocessing, delete_age, console_client)
Allen Lib41527d2017-06-22 17:28:00 -070098 self.mox.StubOutWithMock(gs_offloader, 'GSOffloader')
Michael Tange8bc9592017-07-06 10:59:32 -070099 if cloud_console_client:
100 self.mox.StubOutWithMock(cloud_console_client,
101 'is_cloud_notification_enabled')
Michael Tang0f553bd2017-06-16 17:38:45 -0700102 if console_client:
103 cloud_console_client.is_cloud_notification_enabled().AndReturn(True)
104 gs_offloader.GSOffloader(
105 expected_gsuri, multiprocessing, delete_age,
106 mox.IsA(cloud_console_client.PubSubBasedClient)).AndReturn(
107 sub_offloader)
108 else:
Michael Tange8bc9592017-07-06 10:59:32 -0700109 if cloud_console_client:
110 cloud_console_client.is_cloud_notification_enabled().AndReturn(
111 False)
Michael Tang0f553bd2017-06-16 17:38:45 -0700112 gs_offloader.GSOffloader(
113 expected_gsuri, multiprocessing, delete_age, None).AndReturn(
114 sub_offloader)
Simran Basidd129972014-09-11 14:34:49 -0700115 self.mox.ReplayAll()
Allen Lib41527d2017-06-22 17:28:00 -0700116 return sub_offloader
Simran Basidd129972014-09-11 14:34:49 -0700117
Jakob Juelich24f22c22014-09-26 11:46:11 -0700118
J. Richard Barnetteea785362014-03-17 16:00:53 -0700119 def test_process_no_options(self):
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700120 """Test default offloader options."""
Allen Lib41527d2017-06-22 17:28:00 -0700121 sub_offloader = self._mock_get_sub_offloader(False)
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700122 offloader = gs_offloader.Offloader(_get_options([]))
123 self.assertEqual(set(offloader._jobdir_classes),
124 self._REGULAR_ONLY)
125 self.assertEqual(offloader._processes, 1)
Allen Lib41527d2017-06-22 17:28:00 -0700126 self.assertEqual(offloader._gs_offloader,
127 sub_offloader)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800128 self.assertEqual(offloader._upload_age_limit, 0)
129 self.assertEqual(offloader._delete_age_limit, 0)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700130
Jakob Juelich24f22c22014-09-26 11:46:11 -0700131
J. Richard Barnetteea785362014-03-17 16:00:53 -0700132 def test_process_all_option(self):
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700133 """Test offloader handling for the --all option."""
Allen Lib41527d2017-06-22 17:28:00 -0700134 sub_offloader = self._mock_get_sub_offloader(False)
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700135 offloader = gs_offloader.Offloader(_get_options(['--all']))
136 self.assertEqual(set(offloader._jobdir_classes), self._BOTH)
137 self.assertEqual(offloader._processes, 1)
Allen Lib41527d2017-06-22 17:28:00 -0700138 self.assertEqual(offloader._gs_offloader,
139 sub_offloader)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800140 self.assertEqual(offloader._upload_age_limit, 0)
141 self.assertEqual(offloader._delete_age_limit, 0)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700142
Jakob Juelich24f22c22014-09-26 11:46:11 -0700143
J. Richard Barnetteea785362014-03-17 16:00:53 -0700144 def test_process_hosts_option(self):
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700145 """Test offloader handling for the --hosts option."""
Allen Lib41527d2017-06-22 17:28:00 -0700146 sub_offloader = self._mock_get_sub_offloader(False)
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700147 offloader = gs_offloader.Offloader(
148 _get_options(['--hosts']))
149 self.assertEqual(set(offloader._jobdir_classes),
150 self._SPECIAL_ONLY)
151 self.assertEqual(offloader._processes, 1)
Allen Lib41527d2017-06-22 17:28:00 -0700152 self.assertEqual(offloader._gs_offloader,
153 sub_offloader)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800154 self.assertEqual(offloader._upload_age_limit, 0)
155 self.assertEqual(offloader._delete_age_limit, 0)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700156
Jakob Juelich24f22c22014-09-26 11:46:11 -0700157
J. Richard Barnetteea785362014-03-17 16:00:53 -0700158 def test_parallelism_option(self):
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700159 """Test offloader handling for the --parallelism option."""
Allen Lib41527d2017-06-22 17:28:00 -0700160 sub_offloader = self._mock_get_sub_offloader(False)
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700161 offloader = gs_offloader.Offloader(
162 _get_options(['--parallelism', '2']))
163 self.assertEqual(set(offloader._jobdir_classes),
164 self._REGULAR_ONLY)
165 self.assertEqual(offloader._processes, 2)
Allen Lib41527d2017-06-22 17:28:00 -0700166 self.assertEqual(offloader._gs_offloader,
167 sub_offloader)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800168 self.assertEqual(offloader._upload_age_limit, 0)
169 self.assertEqual(offloader._delete_age_limit, 0)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700170
Jakob Juelich24f22c22014-09-26 11:46:11 -0700171
J. Richard Barnetteea785362014-03-17 16:00:53 -0700172 def test_delete_only_option(self):
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700173 """Test offloader handling for the --delete_only option."""
174 offloader = gs_offloader.Offloader(
175 _get_options(['--delete_only']))
176 self.assertEqual(set(offloader._jobdir_classes),
177 self._REGULAR_ONLY)
178 self.assertEqual(offloader._processes, 1)
Allen Lib41527d2017-06-22 17:28:00 -0700179 self.assertIsInstance(offloader._gs_offloader,
180 gs_offloader.FakeGSOffloader)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800181 self.assertEqual(offloader._upload_age_limit, 0)
182 self.assertEqual(offloader._delete_age_limit, 0)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700183
Jakob Juelich24f22c22014-09-26 11:46:11 -0700184
Simran Basidf4751e2014-10-10 14:19:22 -0700185 def test_days_old_option(self):
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700186 """Test offloader handling for the --days_old option."""
Allen Lib41527d2017-06-22 17:28:00 -0700187 sub_offloader = self._mock_get_sub_offloader(False, delete_age=7)
J. Richard Barnetteef4b47d2014-05-19 07:52:08 -0700188 offloader = gs_offloader.Offloader(
189 _get_options(['--days_old', '7']))
190 self.assertEqual(set(offloader._jobdir_classes),
191 self._REGULAR_ONLY)
192 self.assertEqual(offloader._processes, 1)
Allen Lib41527d2017-06-22 17:28:00 -0700193 self.assertEqual(offloader._gs_offloader,
194 sub_offloader)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800195 self.assertEqual(offloader._upload_age_limit, 7)
196 self.assertEqual(offloader._delete_age_limit, 7)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700197
Jakob Juelich24f22c22014-09-26 11:46:11 -0700198
Simran Basidd129972014-09-11 14:34:49 -0700199 def test_moblab_gsuri_generation(self):
200 """Test offloader construction for Moblab."""
Allen Lib41527d2017-06-22 17:28:00 -0700201 sub_offloader = self._mock_get_sub_offloader(True)
Simran Basidd129972014-09-11 14:34:49 -0700202 offloader = gs_offloader.Offloader(_get_options([]))
203 self.assertEqual(set(offloader._jobdir_classes),
204 self._REGULAR_ONLY)
205 self.assertEqual(offloader._processes, 1)
Allen Lib41527d2017-06-22 17:28:00 -0700206 self.assertEqual(offloader._gs_offloader,
207 sub_offloader)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800208 self.assertEqual(offloader._upload_age_limit, 0)
209 self.assertEqual(offloader._delete_age_limit, 0)
Simran Basidd129972014-09-11 14:34:49 -0700210
J. Richard Barnetteea785362014-03-17 16:00:53 -0700211
Simran Basif3e305f2014-10-03 14:43:53 -0700212 def test_globalconfig_offloading_flag(self):
213 """Test enabling of --delete_only via global_config."""
214 gs_offloader.GS_OFFLOADING_ENABLED = False
215 offloader = gs_offloader.Offloader(
216 _get_options([]))
Allen Lib41527d2017-06-22 17:28:00 -0700217 self.assertIsInstance(offloader._gs_offloader,
218 gs_offloader.FakeGSOffloader)
Simran Basif3e305f2014-10-03 14:43:53 -0700219
Michael Tang0df2eb42016-05-13 19:06:54 -0700220 def test_offloader_multiprocessing_flag_set(self):
221 """Test multiprocessing is set."""
Allen Lib41527d2017-06-22 17:28:00 -0700222 sub_offloader = self._mock_get_sub_offloader(True, True)
Michael Tang0df2eb42016-05-13 19:06:54 -0700223 offloader = gs_offloader.Offloader(_get_options(['-m']))
Allen Lib41527d2017-06-22 17:28:00 -0700224 self.assertEqual(offloader._gs_offloader,
225 sub_offloader)
Michael Tang0df2eb42016-05-13 19:06:54 -0700226 self.mox.VerifyAll()
227
228 def test_offloader_multiprocessing_flag_not_set_default_false(self):
229 """Test multiprocessing is set."""
230 gs_offloader.GS_OFFLOADER_MULTIPROCESSING = False
Allen Lib41527d2017-06-22 17:28:00 -0700231 sub_offloader = self._mock_get_sub_offloader(True, False)
Michael Tang0df2eb42016-05-13 19:06:54 -0700232 offloader = gs_offloader.Offloader(_get_options([]))
Allen Lib41527d2017-06-22 17:28:00 -0700233 self.assertEqual(offloader._gs_offloader,
234 sub_offloader)
Michael Tang0df2eb42016-05-13 19:06:54 -0700235 self.mox.VerifyAll()
236
237 def test_offloader_multiprocessing_flag_not_set_default_true(self):
238 """Test multiprocessing is set."""
239 gs_offloader.GS_OFFLOADER_MULTIPROCESSING = True
Allen Lib41527d2017-06-22 17:28:00 -0700240 sub_offloader = self._mock_get_sub_offloader(True, True)
Michael Tang0df2eb42016-05-13 19:06:54 -0700241 offloader = gs_offloader.Offloader(_get_options([]))
Allen Lib41527d2017-06-22 17:28:00 -0700242 self.assertEqual(offloader._gs_offloader,
243 sub_offloader)
Michael Tang0df2eb42016-05-13 19:06:54 -0700244 self.mox.VerifyAll()
245
Michael Tang97d188c2016-06-25 11:18:42 -0700246
Michael Tang0f553bd2017-06-16 17:38:45 -0700247 def test_offloader_pubsub_enabled(self):
Michael Tang97d188c2016-06-25 11:18:42 -0700248 """Test multiprocessing is set."""
Michael Tange8bc9592017-07-06 10:59:32 -0700249 if not cloud_console_client:
250 return
Michael Tang0f553bd2017-06-16 17:38:45 -0700251 self.mox.StubOutWithMock(pubsub_utils, "PubSubClient")
252 sub_offloader = self._mock_get_sub_offloader(True, False,
253 cloud_console_client.PubSubBasedClient())
254 offloader = gs_offloader.Offloader(_get_options([]))
Allen Lib41527d2017-06-22 17:28:00 -0700255 self.assertEqual(offloader._gs_offloader,
256 sub_offloader)
Michael Tang97d188c2016-06-25 11:18:42 -0700257 self.mox.VerifyAll()
258
Simran Basif3e305f2014-10-03 14:43:53 -0700259
J. Richard Barnetteea785362014-03-17 16:00:53 -0700260def _make_timestamp(age_limit, is_expired):
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800261 """Create a timestamp for use by `job_directories.is_job_expired()`.
J. Richard Barnetteea785362014-03-17 16:00:53 -0700262
263 The timestamp will meet the syntactic requirements for
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800264 timestamps used as input to `is_job_expired()`. If
J. Richard Barnetteea785362014-03-17 16:00:53 -0700265 `is_expired` is true, the timestamp will be older than
266 `age_limit` days before the current time; otherwise, the
267 date will be younger.
268
269 @param age_limit The number of days before expiration of the
270 target timestamp.
271 @param is_expired Whether the timestamp should be expired
272 relative to `age_limit`.
273
274 """
275 seconds = -_MARGIN_SECS
276 if is_expired:
277 seconds = -seconds
278 delta = datetime.timedelta(days=age_limit, seconds=seconds)
279 reference_time = datetime.datetime.now() - delta
Dan Shidfea3682014-08-10 23:38:40 -0700280 return reference_time.strftime(time_utils.TIME_FMT)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700281
282
283class JobExpirationTests(unittest.TestCase):
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800284 """Tests to exercise `job_directories.is_job_expired()`."""
J. Richard Barnetteea785362014-03-17 16:00:53 -0700285
286 def test_expired(self):
287 """Test detection of an expired job."""
288 timestamp = _make_timestamp(_TEST_EXPIRATION_AGE, True)
289 self.assertTrue(
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800290 job_directories.is_job_expired(
J. Richard Barnetteea785362014-03-17 16:00:53 -0700291 _TEST_EXPIRATION_AGE, timestamp))
292
293
294 def test_alive(self):
295 """Test detection of a job that's not expired."""
296 # N.B. This test may fail if its run time exceeds more than
297 # about _MARGIN_SECS seconds.
298 timestamp = _make_timestamp(_TEST_EXPIRATION_AGE, False)
299 self.assertFalse(
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800300 job_directories.is_job_expired(
J. Richard Barnetteea785362014-03-17 16:00:53 -0700301 _TEST_EXPIRATION_AGE, timestamp))
302
303
304class _MockJobDirectory(job_directories._JobDirectory):
305 """Subclass of `_JobDirectory` used as a helper for tests."""
306
307 GLOB_PATTERN = '[0-9]*-*'
308
Jakob Juelich24f22c22014-09-26 11:46:11 -0700309
J. Richard Barnetteea785362014-03-17 16:00:53 -0700310 def __init__(self, resultsdir):
311 """Create new job in initial state."""
312 super(_MockJobDirectory, self).__init__(resultsdir)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700313 self._timestamp = None
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800314 self.queue_args = [resultsdir, os.path.dirname(resultsdir), self._timestamp]
J. Richard Barnetteea785362014-03-17 16:00:53 -0700315
Jakob Juelich24f22c22014-09-26 11:46:11 -0700316
J. Richard Barnetteea785362014-03-17 16:00:53 -0700317 def get_timestamp_if_finished(self):
318 return self._timestamp
319
Jakob Juelich24f22c22014-09-26 11:46:11 -0700320
J. Richard Barnetteea785362014-03-17 16:00:53 -0700321 def set_finished(self, days_old):
322 """Make this job appear to be finished.
323
324 After calling this function, calls to `enqueue_offload()`
325 will find this job as finished, but not expired and ready
326 for offload. Note that when `days_old` is 0,
327 `enqueue_offload()` will treat a finished job as eligible
328 for offload.
329
330 @param days_old The value of the `days_old` parameter that
331 will be passed to `enqueue_offload()` for
332 testing.
333
334 """
335 self._timestamp = _make_timestamp(days_old, False)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800336 self.queue_args[2] = self._timestamp
J. Richard Barnetteea785362014-03-17 16:00:53 -0700337
Jakob Juelich24f22c22014-09-26 11:46:11 -0700338
J. Richard Barnetteea785362014-03-17 16:00:53 -0700339 def set_expired(self, days_old):
340 """Make this job eligible to be offloaded.
341
342 After calling this function, calls to `offload` will attempt
343 to offload this job.
344
345 @param days_old The value of the `days_old` parameter that
346 will be passed to `enqueue_offload()` for
347 testing.
348
349 """
350 self._timestamp = _make_timestamp(days_old, True)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800351 self.queue_args[2] = self._timestamp
J. Richard Barnetteea785362014-03-17 16:00:53 -0700352
Jakob Juelich24f22c22014-09-26 11:46:11 -0700353
J. Richard Barnetteea785362014-03-17 16:00:53 -0700354 def set_incomplete(self):
355 """Make this job appear to have failed offload just once."""
Allen Lib41527d2017-06-22 17:28:00 -0700356 self.offload_count += 1
357 self.first_offload_start = time.time()
358 if not os.path.isdir(self.dirname):
359 os.mkdir(self.dirname)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700360
Jakob Juelich24f22c22014-09-26 11:46:11 -0700361
J. Richard Barnetteea785362014-03-17 16:00:53 -0700362 def set_reportable(self):
363 """Make this job be reportable."""
J. Richard Barnetteea785362014-03-17 16:00:53 -0700364 self.set_incomplete()
Allen Lib41527d2017-06-22 17:28:00 -0700365 self.offload_count += 1
J. Richard Barnetteea785362014-03-17 16:00:53 -0700366
Jakob Juelich24f22c22014-09-26 11:46:11 -0700367
J. Richard Barnetteea785362014-03-17 16:00:53 -0700368 def set_complete(self):
369 """Make this job be completed."""
Allen Lib41527d2017-06-22 17:28:00 -0700370 self.offload_count += 1
371 if os.path.isdir(self.dirname):
372 os.rmdir(self.dirname)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700373
374
Simran Basi1e10e922015-04-16 15:09:56 -0700375 def process_gs_instructions(self):
376 """Always still offload the job directory."""
377 return True
378
379
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700380class CommandListTests(unittest.TestCase):
Allen Lib41527d2017-06-22 17:28:00 -0700381 """Tests for `_get_cmd_list()`."""
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700382
MK Ryue93c8572015-08-11 11:53:00 -0700383 def _command_list_assertions(self, job, use_rsync=True, multi=False):
Allen Lib41527d2017-06-22 17:28:00 -0700384 """Call `_get_cmd_list()` and check the return value.
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700385
386 Check the following assertions:
387 * The command name (argv[0]) is 'gsutil'.
MK Ryue93c8572015-08-11 11:53:00 -0700388 * '-m' option (argv[1]) is on when the argument, multi, is True.
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700389 * The arguments contain the 'cp' subcommand.
390 * The next-to-last argument (the source directory) is the
391 job's `queue_args[0]`.
Simran Basidd129972014-09-11 14:34:49 -0700392 * The last argument (the destination URL) is the job's
393 'queue_args[1]'.
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700394
395 @param job A job with properly calculated arguments to
Allen Lib41527d2017-06-22 17:28:00 -0700396 `_get_cmd_list()`
MK Ryue93c8572015-08-11 11:53:00 -0700397 @param use_rsync True when using 'rsync'. False when using 'cp'.
398 @param multi True when using '-m' option for gsutil.
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700399
400 """
Jakob Juelich24f22c22014-09-26 11:46:11 -0700401 test_bucket_uri = 'gs://a-test-bucket'
402
403 gs_offloader.USE_RSYNC_ENABLED = use_rsync
404
Allen Lib41527d2017-06-22 17:28:00 -0700405 command = gs_offloader._get_cmd_list(
MK Ryue93c8572015-08-11 11:53:00 -0700406 multi, job.queue_args[0],
407 os.path.join(test_bucket_uri, job.queue_args[1]))
Jakob Juelich24f22c22014-09-26 11:46:11 -0700408
Dan Shi365049f2017-05-28 08:00:02 +0000409 self.assertEqual(command[0], 'gsutil')
MK Ryue93c8572015-08-11 11:53:00 -0700410 if multi:
411 self.assertEqual(command[1], '-m')
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700412 self.assertEqual(command[-2], job.queue_args[0])
Jakob Juelich24f22c22014-09-26 11:46:11 -0700413
414 if use_rsync:
415 self.assertTrue('rsync' in command)
416 self.assertEqual(command[-1],
417 os.path.join(test_bucket_uri, job.queue_args[0]))
418 else:
419 self.assertTrue('cp' in command)
420 self.assertEqual(command[-1],
421 os.path.join(test_bucket_uri, job.queue_args[1]))
422
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700423
Allen Lib41527d2017-06-22 17:28:00 -0700424 def test__get_cmd_list_regular(self):
425 """Test `_get_cmd_list()` as for a regular job."""
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700426 job = _MockJobDirectory('118-debug')
427 self._command_list_assertions(job)
428
Jakob Juelich24f22c22014-09-26 11:46:11 -0700429
Allen Lib41527d2017-06-22 17:28:00 -0700430 def test__get_cmd_list_special(self):
431 """Test `_get_cmd_list()` as for a special job."""
J. Richard Barnette9f4be0d2014-05-20 12:28:31 -0700432 job = _MockJobDirectory('hosts/host1/118-reset')
433 self._command_list_assertions(job)
434
435
Jakob Juelich24f22c22014-09-26 11:46:11 -0700436 def test_get_cmd_list_regular_no_rsync(self):
Allen Lib41527d2017-06-22 17:28:00 -0700437 """Test `_get_cmd_list()` as for a regular job."""
Jakob Juelich24f22c22014-09-26 11:46:11 -0700438 job = _MockJobDirectory('118-debug')
439 self._command_list_assertions(job, use_rsync=False)
440
441
442 def test_get_cmd_list_special_no_rsync(self):
Allen Lib41527d2017-06-22 17:28:00 -0700443 """Test `_get_cmd_list()` as for a special job."""
Jakob Juelich24f22c22014-09-26 11:46:11 -0700444 job = _MockJobDirectory('hosts/host1/118-reset')
445 self._command_list_assertions(job, use_rsync=False)
446
447
MK Ryue93c8572015-08-11 11:53:00 -0700448 def test_get_cmd_list_regular_multi(self):
Allen Lib41527d2017-06-22 17:28:00 -0700449 """Test `_get_cmd_list()` as for a regular job with True multi."""
MK Ryue93c8572015-08-11 11:53:00 -0700450 job = _MockJobDirectory('118-debug')
451 self._command_list_assertions(job, multi=True)
452
453
Allen Lib41527d2017-06-22 17:28:00 -0700454 def test__get_cmd_list_special_multi(self):
455 """Test `_get_cmd_list()` as for a special job with True multi."""
MK Ryue93c8572015-08-11 11:53:00 -0700456 job = _MockJobDirectory('hosts/host1/118-reset')
457 self._command_list_assertions(job, multi=True)
458
459
J. Richard Barnettedd0227d2015-04-10 15:18:48 -0700460class _MockJob(object):
461 """Class to mock the return value of `AFE.get_jobs()`."""
462 def __init__(self, created):
463 self.created_on = created
464
465
466class _MockHostQueueEntry(object):
467 """Class to mock the return value of `AFE.get_host_queue_entries()`."""
468 def __init__(self, finished):
469 self.finished_on = finished
470
471
472class _MockSpecialTask(object):
473 """Class to mock the return value of `AFE.get_special_tasks()`."""
474 def __init__(self, finished):
475 self.time_finished = finished
476
477
J. Richard Barnette2c72ddd2014-05-20 12:17:37 -0700478class JobDirectorySubclassTests(mox.MoxTestBase):
479 """Test specific to RegularJobDirectory and SpecialJobDirectory.
J. Richard Barnette3e3ed6a2014-05-19 07:59:00 -0700480
481 This provides coverage for the implementation in both
482 RegularJobDirectory and SpecialJobDirectory.
483
484 """
485
486 def setUp(self):
J. Richard Barnette2c72ddd2014-05-20 12:17:37 -0700487 super(JobDirectorySubclassTests, self).setUp()
J. Richard Barnettedd0227d2015-04-10 15:18:48 -0700488 self.mox.StubOutWithMock(job_directories._AFE, 'get_jobs')
489 self.mox.StubOutWithMock(job_directories._AFE,
490 'get_host_queue_entries')
491 self.mox.StubOutWithMock(job_directories._AFE,
492 'get_special_tasks')
J. Richard Barnette3e3ed6a2014-05-19 07:59:00 -0700493
Jakob Juelich24f22c22014-09-26 11:46:11 -0700494
J. Richard Barnette2c72ddd2014-05-20 12:17:37 -0700495 def test_regular_job_fields(self):
496 """Test the constructor for `RegularJobDirectory`.
497
Allen Lib41527d2017-06-22 17:28:00 -0700498 Construct a regular job, and assert that the `dirname`
J. Richard Barnette2c72ddd2014-05-20 12:17:37 -0700499 and `_id` attributes are set as expected.
500
501 """
502 resultsdir = '118-fubar'
503 job = job_directories.RegularJobDirectory(resultsdir)
Allen Lib41527d2017-06-22 17:28:00 -0700504 self.assertEqual(job.dirname, resultsdir)
Dan Shicf4d2032015-03-12 15:04:21 -0700505 self.assertEqual(job._id, 118)
J. Richard Barnette2c72ddd2014-05-20 12:17:37 -0700506
Jakob Juelich24f22c22014-09-26 11:46:11 -0700507
J. Richard Barnette2c72ddd2014-05-20 12:17:37 -0700508 def test_special_job_fields(self):
509 """Test the constructor for `SpecialJobDirectory`.
510
Allen Lib41527d2017-06-22 17:28:00 -0700511 Construct a special job, and assert that the `dirname`
J. Richard Barnette2c72ddd2014-05-20 12:17:37 -0700512 and `_id` attributes are set as expected.
513
514 """
515 destdir = 'hosts/host1'
516 resultsdir = destdir + '/118-reset'
517 job = job_directories.SpecialJobDirectory(resultsdir)
Allen Lib41527d2017-06-22 17:28:00 -0700518 self.assertEqual(job.dirname, resultsdir)
Dan Shicf4d2032015-03-12 15:04:21 -0700519 self.assertEqual(job._id, 118)
J. Richard Barnette2c72ddd2014-05-20 12:17:37 -0700520
Jakob Juelich24f22c22014-09-26 11:46:11 -0700521
J. Richard Barnettedd0227d2015-04-10 15:18:48 -0700522 def _check_finished_job(self, jobtime, hqetimes, expected):
523 """Mock and test behavior of a finished job.
524
525 Initialize the mocks for a call to
526 `get_timestamp_if_finished()`, then simulate one call.
527 Assert that the returned timestamp matches the passed
528 in expected value.
529
530 @param jobtime Time used to construct a _MockJob object.
531 @param hqetimes List of times used to construct
532 _MockHostQueueEntry objects.
533 @param expected Expected time to be returned by
534 get_timestamp_if_finished
535
536 """
537 job = job_directories.RegularJobDirectory('118-fubar')
538 job_directories._AFE.get_jobs(
539 id=job._id, finished=True).AndReturn(
540 [_MockJob(jobtime)])
541 job_directories._AFE.get_host_queue_entries(
542 finished_on__isnull=False,
543 job_id=job._id).AndReturn(
544 [_MockHostQueueEntry(t) for t in hqetimes])
545 self.mox.ReplayAll()
546 self.assertEqual(expected, job.get_timestamp_if_finished())
547 self.mox.VerifyAll()
548
549
J. Richard Barnette3e3ed6a2014-05-19 07:59:00 -0700550 def test_finished_regular_job(self):
551 """Test getting the timestamp for a finished regular job.
552
553 Tests the return value for
554 `RegularJobDirectory.get_timestamp_if_finished()` when
555 the AFE indicates the job is finished.
556
557 """
J. Richard Barnettedd0227d2015-04-10 15:18:48 -0700558 created_timestamp = _make_timestamp(1, True)
559 hqe_timestamp = _make_timestamp(0, True)
560 self._check_finished_job(created_timestamp,
561 [hqe_timestamp],
562 hqe_timestamp)
Simran Basifb98e462014-08-18 12:35:44 -0700563
Jakob Juelich24f22c22014-09-26 11:46:11 -0700564
Simran Basifb98e462014-08-18 12:35:44 -0700565 def test_finished_regular_job_multiple_hqes(self):
566 """Test getting the timestamp for a regular job with multiple hqes.
567
568 Tests the return value for
569 `RegularJobDirectory.get_timestamp_if_finished()` when
570 the AFE indicates the job is finished and the job has multiple host
571 queue entries.
572
J. Richard Barnettedd0227d2015-04-10 15:18:48 -0700573 Tests that the returned timestamp is the latest timestamp in
574 the list of HQEs, regardless of the returned order.
575
Simran Basifb98e462014-08-18 12:35:44 -0700576 """
Simran Basifb98e462014-08-18 12:35:44 -0700577 created_timestamp = _make_timestamp(2, True)
578 older_hqe_timestamp = _make_timestamp(1, True)
579 newer_hqe_timestamp = _make_timestamp(0, True)
J. Richard Barnettedd0227d2015-04-10 15:18:48 -0700580 hqe_list = [older_hqe_timestamp,
581 newer_hqe_timestamp]
582 self._check_finished_job(created_timestamp,
583 hqe_list,
584 newer_hqe_timestamp)
585 self.mox.ResetAll()
586 hqe_list.reverse()
587 self._check_finished_job(created_timestamp,
588 hqe_list,
589 newer_hqe_timestamp)
Simran Basifb98e462014-08-18 12:35:44 -0700590
Jakob Juelich24f22c22014-09-26 11:46:11 -0700591
Simran Basifb98e462014-08-18 12:35:44 -0700592 def test_finished_regular_job_null_finished_times(self):
593 """Test getting the timestamp for an aborted regular job.
594
595 Tests the return value for
596 `RegularJobDirectory.get_timestamp_if_finished()` when
597 the AFE indicates the job is finished and the job has aborted host
598 queue entries.
599
600 """
Simran Basifb98e462014-08-18 12:35:44 -0700601 timestamp = _make_timestamp(0, True)
J. Richard Barnettedd0227d2015-04-10 15:18:48 -0700602 self._check_finished_job(timestamp, [], timestamp)
J. Richard Barnette3e3ed6a2014-05-19 07:59:00 -0700603
Jakob Juelich24f22c22014-09-26 11:46:11 -0700604
J. Richard Barnette3e3ed6a2014-05-19 07:59:00 -0700605 def test_unfinished_regular_job(self):
606 """Test getting the timestamp for an unfinished regular job.
607
608 Tests the return value for
609 `RegularJobDirectory.get_timestamp_if_finished()` when
610 the AFE indicates the job is not finished.
611
612 """
613 job = job_directories.RegularJobDirectory('118-fubar')
J. Richard Barnettedd0227d2015-04-10 15:18:48 -0700614 job_directories._AFE.get_jobs(
615 id=job._id, finished=True).AndReturn([])
J. Richard Barnette3e3ed6a2014-05-19 07:59:00 -0700616 self.mox.ReplayAll()
617 self.assertIsNone(job.get_timestamp_if_finished())
J. Richard Barnettedd0227d2015-04-10 15:18:48 -0700618 self.mox.VerifyAll()
J. Richard Barnette3e3ed6a2014-05-19 07:59:00 -0700619
Jakob Juelich24f22c22014-09-26 11:46:11 -0700620
J. Richard Barnette3e3ed6a2014-05-19 07:59:00 -0700621 def test_finished_special_job(self):
622 """Test getting the timestamp for a finished special job.
623
624 Tests the return value for
625 `SpecialJobDirectory.get_timestamp_if_finished()` when
626 the AFE indicates the job is finished.
627
628 """
J. Richard Barnette2c72ddd2014-05-20 12:17:37 -0700629 job = job_directories.SpecialJobDirectory(
630 'hosts/host1/118-reset')
J. Richard Barnette3e3ed6a2014-05-19 07:59:00 -0700631 timestamp = _make_timestamp(0, True)
J. Richard Barnettedd0227d2015-04-10 15:18:48 -0700632 job_directories._AFE.get_special_tasks(
633 id=job._id, is_complete=True).AndReturn(
634 [_MockSpecialTask(timestamp)])
J. Richard Barnette3e3ed6a2014-05-19 07:59:00 -0700635 self.mox.ReplayAll()
636 self.assertEqual(timestamp,
637 job.get_timestamp_if_finished())
J. Richard Barnettedd0227d2015-04-10 15:18:48 -0700638 self.mox.VerifyAll()
J. Richard Barnette3e3ed6a2014-05-19 07:59:00 -0700639
Jakob Juelich24f22c22014-09-26 11:46:11 -0700640
J. Richard Barnette3e3ed6a2014-05-19 07:59:00 -0700641 def test_unfinished_special_job(self):
642 """Test getting the timestamp for an unfinished special job.
643
644 Tests the return value for
645 `SpecialJobDirectory.get_timestamp_if_finished()` when
646 the AFE indicates the job is not finished.
647
648 """
J. Richard Barnette2c72ddd2014-05-20 12:17:37 -0700649 job = job_directories.SpecialJobDirectory(
650 'hosts/host1/118-reset')
J. Richard Barnettedd0227d2015-04-10 15:18:48 -0700651 job_directories._AFE.get_special_tasks(
652 id=job._id, is_complete=True).AndReturn([])
J. Richard Barnette3e3ed6a2014-05-19 07:59:00 -0700653 self.mox.ReplayAll()
654 self.assertIsNone(job.get_timestamp_if_finished())
J. Richard Barnettedd0227d2015-04-10 15:18:48 -0700655 self.mox.VerifyAll()
J. Richard Barnette3e3ed6a2014-05-19 07:59:00 -0700656
657
Allen Lib41527d2017-06-22 17:28:00 -0700658class _TempResultsDirTestCase(unittest.TestCase):
659 """Mixin class for tests using a temporary results directory."""
J. Richard Barnetteea785362014-03-17 16:00:53 -0700660
J. Richard Barnette08800322014-05-16 14:49:46 -0700661 REGULAR_JOBLIST = [
662 '111-fubar', '112-fubar', '113-fubar', '114-snafu']
663 HOST_LIST = ['host1', 'host2', 'host3']
664 SPECIAL_JOBLIST = [
665 'hosts/host1/333-reset', 'hosts/host1/334-reset',
666 'hosts/host2/444-reset', 'hosts/host3/555-reset']
667
Jakob Juelich24f22c22014-09-26 11:46:11 -0700668
J. Richard Barnetteea785362014-03-17 16:00:53 -0700669 def setUp(self):
Allen Lib41527d2017-06-22 17:28:00 -0700670 super(_TempResultsDirTestCase, self).setUp()
J. Richard Barnette08800322014-05-16 14:49:46 -0700671 self._resultsroot = tempfile.mkdtemp()
672 self._cwd = os.getcwd()
673 os.chdir(self._resultsroot)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700674
Jakob Juelich24f22c22014-09-26 11:46:11 -0700675
J. Richard Barnetteea785362014-03-17 16:00:53 -0700676 def tearDown(self):
J. Richard Barnette08800322014-05-16 14:49:46 -0700677 os.chdir(self._cwd)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700678 shutil.rmtree(self._resultsroot)
Allen Lib41527d2017-06-22 17:28:00 -0700679 super(_TempResultsDirTestCase, self).tearDown()
J. Richard Barnetteea785362014-03-17 16:00:53 -0700680
Jakob Juelich24f22c22014-09-26 11:46:11 -0700681
J. Richard Barnetteea785362014-03-17 16:00:53 -0700682 def make_job(self, jobdir):
683 """Create a job with results in `self._resultsroot`.
684
685 @param jobdir Name of the subdirectory to be created in
686 `self._resultsroot`.
687
688 """
J. Richard Barnette08800322014-05-16 14:49:46 -0700689 os.mkdir(jobdir)
690 return _MockJobDirectory(jobdir)
691
Jakob Juelich24f22c22014-09-26 11:46:11 -0700692
J. Richard Barnette08800322014-05-16 14:49:46 -0700693 def make_job_hierarchy(self):
694 """Create a sample hierarchy of job directories.
695
696 `self.REGULAR_JOBLIST` is a list of directories for regular
697 jobs to be created; `self.SPECIAL_JOBLIST` is a list of
698 directories for special jobs to be created.
699
700 """
701 for d in self.REGULAR_JOBLIST:
702 os.mkdir(d)
703 hostsdir = 'hosts'
704 os.mkdir(hostsdir)
705 for host in self.HOST_LIST:
706 os.mkdir(os.path.join(hostsdir, host))
707 for d in self.SPECIAL_JOBLIST:
708 os.mkdir(d)
J. Richard Barnetteea785362014-03-17 16:00:53 -0700709
710
Allen Lib41527d2017-06-22 17:28:00 -0700711class _TempResultsDirTestBase(_TempResultsDirTestCase, mox.MoxTestBase):
712 """Base Mox test class for tests using a temporary results directory."""
713
714
Prathmesh Prabhu80dfb1e2017-01-30 18:01:29 -0800715class FailedOffloadsLogTest(_TempResultsDirTestBase):
716 """Test the formatting of failed offloads log file."""
717 # Below is partial sample of a failed offload log file. This text is
718 # deliberately hard-coded and then parsed to create the test data; the idea
719 # is to make sure the actual text format will be reviewed by a human being.
720 #
721 # first offload count directory
722 # --+----1----+---- ----+ ----+----1----+----2----+----3
723 _SAMPLE_DIRECTORIES_REPORT = '''\
724 =================== ====== ==============================
725 2014-03-14 15:09:26 1 118-fubar
726 2014-03-14 15:19:23 2 117-fubar
727 2014-03-14 15:29:20 6 116-fubar
728 2014-03-14 15:39:17 24 115-fubar
729 2014-03-14 15:49:14 120 114-fubar
730 2014-03-14 15:59:11 720 113-fubar
731 2014-03-14 16:09:08 5040 112-fubar
732 2014-03-14 16:19:05 40320 111-fubar
733 '''
734
735 def setUp(self):
736 super(FailedOffloadsLogTest, self).setUp()
737 self._offloader = gs_offloader.Offloader(_get_options([]))
738 self._joblist = []
739 for line in self._SAMPLE_DIRECTORIES_REPORT.split('\n')[1 : -1]:
740 date_, time_, count, dir_ = line.split()
741 job = _MockJobDirectory(dir_)
Allen Lib41527d2017-06-22 17:28:00 -0700742 job.offload_count = int(count)
Prathmesh Prabhu80dfb1e2017-01-30 18:01:29 -0800743 timestruct = time.strptime("%s %s" % (date_, time_),
744 gs_offloader.FAILED_OFFLOADS_TIME_FORMAT)
Allen Lib41527d2017-06-22 17:28:00 -0700745 job.first_offload_start = time.mktime(timestruct)
Prathmesh Prabhu80dfb1e2017-01-30 18:01:29 -0800746 # enter the jobs in reverse order, to make sure we
747 # test that the output will be sorted.
748 self._joblist.insert(0, job)
749
750
751 def assert_report_well_formatted(self, report_file):
Allen Li93585382017-05-05 14:24:53 -0700752 """Assert that report file is well formatted.
753
754 @param report_file: Path to report file
755 """
Prathmesh Prabhu80dfb1e2017-01-30 18:01:29 -0800756 with open(report_file, 'r') as f:
757 report_lines = f.read().split()
758
759 for end_of_header_index in range(len(report_lines)):
760 if report_lines[end_of_header_index].startswith('=='):
761 break
762 self.assertLess(end_of_header_index, len(report_lines),
763 'Failed to find end-of-header marker in the report')
764
765 relevant_lines = report_lines[end_of_header_index:]
766 expected_lines = self._SAMPLE_DIRECTORIES_REPORT.split()
767 self.assertListEqual(relevant_lines, expected_lines)
768
769
770 def test_failed_offload_log_format(self):
771 """Trigger an e-mail report and check its contents."""
772 log_file = os.path.join(self._resultsroot, 'failed_log')
773 report = self._offloader._log_failed_jobs_locally(self._joblist,
774 log_file=log_file)
775 self.assert_report_well_formatted(log_file)
776
777
778 def test_failed_offload_file_overwrite(self):
779 """Verify that we can saefly overwrite the log file."""
780 log_file = os.path.join(self._resultsroot, 'failed_log')
781 with open(log_file, 'w') as f:
782 f.write('boohoohoo')
783 report = self._offloader._log_failed_jobs_locally(self._joblist,
784 log_file=log_file)
785 self.assert_report_well_formatted(log_file)
786
787
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700788class OffloadDirectoryTests(_TempResultsDirTestBase):
789 """Tests for `offload_dir()`."""
790
791 def setUp(self):
792 super(OffloadDirectoryTests, self).setUp()
793 # offload_dir() logs messages; silence them.
794 self._saved_loglevel = logging.getLogger().getEffectiveLevel()
795 logging.getLogger().setLevel(logging.CRITICAL+1)
796 self._job = self.make_job(self.REGULAR_JOBLIST[0])
Allen Lib41527d2017-06-22 17:28:00 -0700797 self.mox.StubOutWithMock(gs_offloader, '_get_cmd_list')
798 alarm = mock.patch('signal.alarm', return_value=0)
799 alarm.start()
800 self.addCleanup(alarm.stop)
Ningning Xia2d981ee2016-07-06 17:59:54 -0700801 self.mox.StubOutWithMock(models.test, 'parse_job_keyval')
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700802
Jakob Juelich24f22c22014-09-26 11:46:11 -0700803
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700804 def tearDown(self):
805 logging.getLogger().setLevel(self._saved_loglevel)
806 super(OffloadDirectoryTests, self).tearDown()
807
Allen Lib41527d2017-06-22 17:28:00 -0700808 def _mock__upload_cts_testresult(self):
809 self.mox.StubOutWithMock(gs_offloader, '_upload_cts_testresult')
810 gs_offloader._upload_cts_testresult(
Ningning Xia42111242016-06-15 14:35:58 -0700811 mox.IgnoreArg(),mox.IgnoreArg()).AndReturn(None)
Jakob Juelich24f22c22014-09-26 11:46:11 -0700812
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800813 def _mock_create_marker_file(self):
814 self.mox.StubOutWithMock(__builtin__, 'open')
Allen Li9579b382017-05-05 17:07:43 -0700815 open(mox.IgnoreArg(), 'a').AndReturn(mock.MagicMock())
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800816
817
818 def _mock_offload_dir_calls(self, command, queue_args,
Allen Lib41527d2017-06-22 17:28:00 -0700819 marker_initially_exists=False):
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700820 """Mock out the calls needed by `offload_dir()`.
821
822 This covers only the calls made when there is no timeout.
823
824 @param command Command list to be returned by the mocked
Allen Lib41527d2017-06-22 17:28:00 -0700825 call to `_get_cmd_list()`.
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700826
827 """
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800828 self.mox.StubOutWithMock(os.path, 'isfile')
829 os.path.isfile(mox.IgnoreArg()).AndReturn(marker_initially_exists)
Simran Basidd129972014-09-11 14:34:49 -0700830 command.append(queue_args[0])
Allen Lib41527d2017-06-22 17:28:00 -0700831 gs_offloader._get_cmd_list(
MK Ryue93c8572015-08-11 11:53:00 -0700832 False, queue_args[0],
833 '%s%s' % (utils.DEFAULT_OFFLOAD_GSURI,
834 queue_args[1])).AndReturn(command)
Allen Lib41527d2017-06-22 17:28:00 -0700835 self._mock__upload_cts_testresult()
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700836
Jakob Juelich24f22c22014-09-26 11:46:11 -0700837
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800838 def _run_offload_dir(self, should_succeed, delete_age):
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700839 """Make one call to `offload_dir()`.
840
841 The caller ensures all mocks are set up already.
842
843 @param should_succeed True iff the call to `offload_dir()`
844 is expected to succeed and remove the
845 offloaded job directory.
846
847 """
848 self.mox.ReplayAll()
Allen Lib41527d2017-06-22 17:28:00 -0700849 gs_offloader.GSOffloader(
850 utils.DEFAULT_OFFLOAD_GSURI, False, delete_age).offload(
MK Ryue93c8572015-08-11 11:53:00 -0700851 self._job.queue_args[0],
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800852 self._job.queue_args[1],
853 self._job.queue_args[2])
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700854 self.mox.VerifyAll()
855 self.assertEqual(not should_succeed,
856 os.path.isdir(self._job.queue_args[0]))
857
Jakob Juelich24f22c22014-09-26 11:46:11 -0700858
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700859 def test_offload_success(self):
860 """Test that `offload_dir()` can succeed correctly."""
Simran Basidd129972014-09-11 14:34:49 -0700861 self._mock_offload_dir_calls(['test', '-d'],
862 self._job.queue_args)
Allen Lib41527d2017-06-22 17:28:00 -0700863 os.path.isfile(mox.IgnoreArg()).AndReturn(True)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800864 self._mock_create_marker_file()
865 self._run_offload_dir(True, 0)
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700866
Jakob Juelich24f22c22014-09-26 11:46:11 -0700867
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700868 def test_offload_failure(self):
869 """Test that `offload_dir()` can fail correctly."""
Simran Basidd129972014-09-11 14:34:49 -0700870 self._mock_offload_dir_calls(['test', '!', '-d'],
Allen Lib41527d2017-06-22 17:28:00 -0700871 self._job.queue_args)
Keith Haddow5ba5fb82016-11-09 11:39:36 -0800872 self._run_offload_dir(False, 0)
J. Richard Barnette2e443ef2014-05-20 12:31:35 -0700873
874
Dan Shiaffb9222015-04-15 17:05:47 -0700875 def test_sanitize_dir(self):
876 """Test that folder/file name with invalid character can be corrected.
877 """
878 results_folder = tempfile.mkdtemp()
Allen Lib41527d2017-06-22 17:28:00 -0700879 invalid_chars = '_'.join(['[', ']', '*', '?', '#'])
Dan Shiaffb9222015-04-15 17:05:47 -0700880 invalid_files = []
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800881 invalid_folder_name = 'invalid_name_folder_%s' % invalid_chars
Dan Shiaffb9222015-04-15 17:05:47 -0700882 invalid_folder = os.path.join(
883 results_folder,
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800884 invalid_folder_name)
Dan Shiaffb9222015-04-15 17:05:47 -0700885 invalid_files.append(os.path.join(
886 invalid_folder,
887 'invalid_name_file_%s' % invalid_chars))
Dan Shiaffb9222015-04-15 17:05:47 -0700888 good_folder = os.path.join(results_folder, 'valid_name_folder')
889 good_file = os.path.join(good_folder, 'valid_name_file')
890 for folder in [invalid_folder, good_folder]:
891 os.makedirs(folder)
892 for f in invalid_files + [good_file]:
893 with open(f, 'w'):
894 pass
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800895 # check that broken symlinks don't break sanitization
896 symlink = os.path.join(invalid_folder, 'broken-link')
897 os.symlink(os.path.join(results_folder, 'no-such-file'),
898 symlink)
899 fifo1 = os.path.join(results_folder, 'test_fifo1')
900 fifo2 = os.path.join(good_folder, 'test_fifo2')
901 fifo3 = os.path.join(invalid_folder, 'test_fifo3')
902 invalid_fifo4_name = 'test_fifo4_%s' % invalid_chars
903 fifo4 = os.path.join(invalid_folder, invalid_fifo4_name)
904 os.mkfifo(fifo1)
905 os.mkfifo(fifo2)
906 os.mkfifo(fifo3)
907 os.mkfifo(fifo4)
Dan Shiaffb9222015-04-15 17:05:47 -0700908 gs_offloader.sanitize_dir(results_folder)
909 for _, dirs, files in os.walk(results_folder):
910 for name in dirs + files:
Allen Lib41527d2017-06-22 17:28:00 -0700911 self.assertEqual(name, gslib.escape(name))
Dan Shiaffb9222015-04-15 17:05:47 -0700912 for c in name:
Allen Lib41527d2017-06-22 17:28:00 -0700913 self.assertFalse(c in ['[', ']', '*', '?', '#'])
Dan Shiaffb9222015-04-15 17:05:47 -0700914 self.assertTrue(os.path.exists(good_file))
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800915
916 self.assertTrue(os.path.exists(fifo1))
917 self.assertFalse(is_fifo(fifo1))
918 self.assertTrue(os.path.exists(fifo2))
919 self.assertFalse(is_fifo(fifo2))
920 corrected_folder = os.path.join(
Allen Lib41527d2017-06-22 17:28:00 -0700921 results_folder, gslib.escape(invalid_folder_name))
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800922 corrected_fifo3 = os.path.join(
923 corrected_folder,
924 'test_fifo3')
925 self.assertFalse(os.path.exists(fifo3))
926 self.assertTrue(os.path.exists(corrected_fifo3))
927 self.assertFalse(is_fifo(corrected_fifo3))
928 corrected_fifo4 = os.path.join(
Allen Lib41527d2017-06-22 17:28:00 -0700929 corrected_folder, gslib.escape(invalid_fifo4_name))
Laurence Goodbyca7726d2017-02-14 17:09:07 -0800930 self.assertFalse(os.path.exists(fifo4))
931 self.assertTrue(os.path.exists(corrected_fifo4))
932 self.assertFalse(is_fifo(corrected_fifo4))
933
934 corrected_symlink = os.path.join(
935 corrected_folder,
936 'broken-link')
937 self.assertFalse(os.path.lexists(symlink))
938 self.assertTrue(os.path.exists(corrected_symlink))
939 self.assertFalse(os.path.islink(corrected_symlink))
Dan Shiaffb9222015-04-15 17:05:47 -0700940 shutil.rmtree(results_folder)
941
942
Dan Shi1b4c7c32015-10-05 10:38:57 -0700943 def check_limit_file_count(self, is_test_job=True):
944 """Test that folder with too many files can be compressed.
945
946 @param is_test_job: True to check the method with test job result
947 folder. Set to False for special task folder.
948 """
949 results_folder = tempfile.mkdtemp()
950 host_folder = os.path.join(
951 results_folder,
952 'lab1-host1' if is_test_job else 'hosts/lab1-host1/1-repair')
953 debug_folder = os.path.join(host_folder, 'debug')
954 sysinfo_folder = os.path.join(host_folder, 'sysinfo')
955 for folder in [debug_folder, sysinfo_folder]:
956 os.makedirs(folder)
957 for i in range(10):
958 with open(os.path.join(folder, str(i)), 'w') as f:
959 f.write('test')
960
Allen Lib41527d2017-06-22 17:28:00 -0700961 gs_offloader._MAX_FILE_COUNT = 100
Dan Shi1b4c7c32015-10-05 10:38:57 -0700962 gs_offloader.limit_file_count(
963 results_folder if is_test_job else host_folder)
964 self.assertTrue(os.path.exists(sysinfo_folder))
965
Allen Lib41527d2017-06-22 17:28:00 -0700966 gs_offloader._MAX_FILE_COUNT = 10
Dan Shi1b4c7c32015-10-05 10:38:57 -0700967 gs_offloader.limit_file_count(
968 results_folder if is_test_job else host_folder)
969 self.assertFalse(os.path.exists(sysinfo_folder))
970 self.assertTrue(os.path.exists(sysinfo_folder + '.tgz'))
971 self.assertTrue(os.path.exists(debug_folder))
972
973 shutil.rmtree(results_folder)
974
975
976 def test_limit_file_count(self):
977 """Test that folder with too many files can be compressed.
978 """
979 self.check_limit_file_count(is_test_job=True)
980 self.check_limit_file_count(is_test_job=False)
981
Ningning Xia2d88eec2016-07-25 23:18:46 -0700982
Ningning Xia8db632f2016-08-19 11:01:35 -0700983 def test_is_valid_result(self):
984 """Test _is_valid_result."""
Ningning Xia21922c82016-07-29 11:03:15 -0700985 release_build = 'veyron_minnie-cheets-release/R52-8248.0.0'
986 pfq_build = 'cyan-cheets-android-pfq/R54-8623.0.0-rc1'
987 trybot_build = 'trybot-samus-release/R54-8640.0.0-b5092'
988 trybot_2_build = 'trybot-samus-pfq/R54-8640.0.0-b5092'
989 release_2_build = 'test-trybot-release/R54-8640.0.0-b5092'
Ningning Xia8db632f2016-08-19 11:01:35 -0700990 self.assertTrue(gs_offloader._is_valid_result(
991 release_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
Rohit Makasana6384c102016-10-21 17:09:47 -0700992 self.assertTrue(gs_offloader._is_valid_result(
993 release_build, gs_offloader.CTS_RESULT_PATTERN, 'test_that_wrapper'))
Ningning Xia8db632f2016-08-19 11:01:35 -0700994 self.assertFalse(gs_offloader._is_valid_result(
Richard Barnette33e12892017-05-26 09:25:34 -0700995 release_build, gs_offloader.CTS_RESULT_PATTERN, 'bvt-arc'))
Ningning Xia8db632f2016-08-19 11:01:35 -0700996 self.assertTrue(gs_offloader._is_valid_result(
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -0800997 release_build, gs_offloader.CTS_V2_RESULT_PATTERN, 'arc-gts'))
Ningning Xia8db632f2016-08-19 11:01:35 -0700998 self.assertFalse(gs_offloader._is_valid_result(
999 None, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
1000 self.assertFalse(gs_offloader._is_valid_result(
1001 release_build, gs_offloader.CTS_RESULT_PATTERN, None))
1002 self.assertFalse(gs_offloader._is_valid_result(
1003 pfq_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
1004 self.assertFalse(gs_offloader._is_valid_result(
1005 trybot_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
1006 self.assertFalse(gs_offloader._is_valid_result(
1007 trybot_2_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
1008 self.assertTrue(gs_offloader._is_valid_result(
1009 release_2_build, gs_offloader.CTS_RESULT_PATTERN, 'arc-cts'))
Ningning Xia21922c82016-07-29 11:03:15 -07001010
1011
Ningning Xia0c27d9b2016-08-04 14:02:39 -07001012 def create_results_folder(self):
1013 """Create CTS/GTS results folders."""
Ningning Xia42111242016-06-15 14:35:58 -07001014 results_folder = tempfile.mkdtemp()
1015 host_folder = os.path.join(results_folder, 'chromeos4-row9-rack11-host22')
1016 debug_folder = os.path.join(host_folder, 'debug')
1017 sysinfo_folder = os.path.join(host_folder, 'sysinfo')
1018 cts_result_folder = os.path.join(
1019 host_folder, 'cheets_CTS.android.dpi', 'results', 'cts-results')
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -08001020 cts_v2_result_folder = os.path.join(host_folder,
1021 'cheets_CTS_N.CtsGraphicsTestCases', 'results', 'android-cts')
Ningning Xia2d88eec2016-07-25 23:18:46 -07001022 gts_result_folder = os.path.join(
Ilja H. Friedelbfa63142017-01-26 00:56:29 -08001023 host_folder, 'cheets_GTS.google.admin', 'results', 'android-gts')
Ningning Xia42111242016-06-15 14:35:58 -07001024 timestamp_str = '2016.04.28_01.41.44'
Ningning Xia2d88eec2016-07-25 23:18:46 -07001025 timestamp_cts_folder = os.path.join(cts_result_folder, timestamp_str)
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -08001026 timestamp_cts_v2_folder = os.path.join(cts_v2_result_folder, timestamp_str)
Ningning Xia2d88eec2016-07-25 23:18:46 -07001027 timestamp_gts_folder = os.path.join(gts_result_folder, timestamp_str)
1028
Ningning Xia0c27d9b2016-08-04 14:02:39 -07001029 # Test results in cts_result_folder with a different time-stamp.
1030 timestamp_str_2 = '2016.04.28_10.41.44'
1031 timestamp_cts_folder_2 = os.path.join(cts_result_folder, timestamp_str_2)
1032
Ningning Xia2d88eec2016-07-25 23:18:46 -07001033 for folder in [debug_folder, sysinfo_folder, cts_result_folder,
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -08001034 timestamp_cts_folder, timestamp_cts_folder_2,
1035 timestamp_cts_v2_folder, timestamp_gts_folder]:
Ningning Xia42111242016-06-15 14:35:58 -07001036 os.makedirs(folder)
Ningning Xia2d88eec2016-07-25 23:18:46 -07001037
Ningning Xia0c27d9b2016-08-04 14:02:39 -07001038 path_pattern_pair = [(timestamp_cts_folder, gs_offloader.CTS_RESULT_PATTERN),
1039 (timestamp_cts_folder_2, gs_offloader.CTS_RESULT_PATTERN),
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -08001040 (timestamp_cts_v2_folder, gs_offloader.CTS_V2_RESULT_PATTERN),
1041 (timestamp_gts_folder, gs_offloader.CTS_V2_RESULT_PATTERN)]
Ningning Xia0c27d9b2016-08-04 14:02:39 -07001042
1043 # Create timestamp.zip file_path.
Ningning Xia2d88eec2016-07-25 23:18:46 -07001044 cts_zip_file = os.path.join(cts_result_folder, timestamp_str + '.zip')
Ningning Xia0c27d9b2016-08-04 14:02:39 -07001045 cts_zip_file_2 = os.path.join(cts_result_folder, timestamp_str_2 + '.zip')
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -08001046 cts_v2_zip_file = os.path.join(cts_v2_result_folder, timestamp_str + '.zip')
Ningning Xia2d88eec2016-07-25 23:18:46 -07001047 gts_zip_file = os.path.join(gts_result_folder, timestamp_str + '.zip')
Ningning Xia2d88eec2016-07-25 23:18:46 -07001048
Ningning Xia0c27d9b2016-08-04 14:02:39 -07001049 # Create xml file_path.
1050 cts_result_file = os.path.join(timestamp_cts_folder, 'testResult.xml')
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -08001051 cts_result_file_2 = os.path.join(timestamp_cts_folder_2,
1052 'testResult.xml')
Ilja H. Friedelbfa63142017-01-26 00:56:29 -08001053 gts_result_file = os.path.join(timestamp_gts_folder, 'test_result.xml')
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -08001054 cts_v2_result_file = os.path.join(timestamp_cts_v2_folder,
1055 'test_result.xml')
Ningning Xia2d88eec2016-07-25 23:18:46 -07001056
Ilja H. Friedel73cf6cd2017-03-01 12:23:00 -08001057 for file_path in [cts_zip_file, cts_zip_file_2, cts_v2_zip_file,
1058 gts_zip_file, cts_result_file, cts_result_file_2,
1059 gts_result_file, cts_v2_result_file]:
Ningning Xia0c27d9b2016-08-04 14:02:39 -07001060 with open(file_path, 'w') as f:
1061 f.write('test')
Ningning Xia42111242016-06-15 14:35:58 -07001062
Ningning Xia0c27d9b2016-08-04 14:02:39 -07001063 return (results_folder, host_folder, path_pattern_pair)
Ningning Xia2d88eec2016-07-25 23:18:46 -07001064
Ningning Xia0c27d9b2016-08-04 14:02:39 -07001065
Allen Lib41527d2017-06-22 17:28:00 -07001066 def test__upload_cts_testresult(self):
1067 """Test _upload_cts_testresult."""
Ningning Xia0c27d9b2016-08-04 14:02:39 -07001068 results_folder, host_folder, path_pattern_pair = self.create_results_folder()
1069
1070 self.mox.StubOutWithMock(gs_offloader, '_upload_files')
1071 gs_offloader._upload_files(
1072 mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg(), False).AndReturn(
1073 ['test', '-d', host_folder])
1074 gs_offloader._upload_files(
1075 mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg(), False).AndReturn(
1076 ['test', '-d', host_folder])
1077 gs_offloader._upload_files(
1078 mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg(), False).AndReturn(
1079 ['test', '-d', host_folder])
Ningning Xia2d88eec2016-07-25 23:18:46 -07001080
Ningning Xia42111242016-06-15 14:35:58 -07001081 self.mox.ReplayAll()
Allen Lib41527d2017-06-22 17:28:00 -07001082 gs_offloader._upload_cts_testresult(results_folder, False)
Ningning Xia42111242016-06-15 14:35:58 -07001083 self.mox.VerifyAll()
Ningning Xia0c27d9b2016-08-04 14:02:39 -07001084 shutil.rmtree(results_folder)
1085
1086
1087 def test_upload_files(self):
1088 """Test upload_files"""
1089 results_folder, host_folder, path_pattern_pair = self.create_results_folder()
1090
1091 for path, pattern in path_pattern_pair:
1092 models.test.parse_job_keyval(mox.IgnoreArg()).AndReturn({
1093 'build': 'veyron_minnie-cheets-release/R52-8248.0.0',
Ningning Xia8db632f2016-08-19 11:01:35 -07001094 'parent_job_id': 'p_id',
1095 'suite': 'arc-cts'
Ningning Xia0c27d9b2016-08-04 14:02:39 -07001096 })
1097
Allen Lib41527d2017-06-22 17:28:00 -07001098 gs_offloader._get_cmd_list(
Ningning Xia0c27d9b2016-08-04 14:02:39 -07001099 False, mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
1100 ['test', '-d', path])
Allen Lib41527d2017-06-22 17:28:00 -07001101 gs_offloader._get_cmd_list(
Ningning Xia0c27d9b2016-08-04 14:02:39 -07001102 False, mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
1103 ['test', '-d', path])
1104
1105 self.mox.ReplayAll()
1106 gs_offloader._upload_files(host_folder, path, pattern, False)
1107 self.mox.VerifyAll()
1108 self.mox.ResetAll()
Ningning Xia42111242016-06-15 14:35:58 -07001109
1110 shutil.rmtree(results_folder)
Dan Shi1b4c7c32015-10-05 10:38:57 -07001111
Michael Tang97d188c2016-06-25 11:18:42 -07001112
Dan Shi02dd0662017-05-23 11:24:32 -07001113 def test_get_metrics_fields(self):
1114 """Test method _get_metrics_fields."""
1115 results_folder, host_folder, _ = self.create_results_folder()
1116 models.test.parse_job_keyval(mox.IgnoreArg()).AndReturn({
1117 'build': 'veyron_minnie-cheets-release/R52-8248.0.0',
1118 'parent_job_id': 'p_id',
1119 'suite': 'arc-cts'
1120 })
1121 try:
1122 self.mox.ReplayAll()
1123 self.assertEqual({'board': 'veyron_minnie-cheets',
1124 'milestone': 'R52'},
1125 gs_offloader._get_metrics_fields(host_folder))
1126 self.mox.VerifyAll()
1127 finally:
1128 shutil.rmtree(results_folder)
1129
1130
J. Richard Barnetteea785362014-03-17 16:00:53 -07001131class JobDirectoryOffloadTests(_TempResultsDirTestBase):
1132 """Tests for `_JobDirectory.enqueue_offload()`.
1133
1134 When testing with a `days_old` parameter of 0, we use
1135 `set_finished()` instead of `set_expired()`. This causes the
1136 job's timestamp to be set in the future. This is done so as
1137 to test that when `days_old` is 0, the job is always treated
1138 as eligible for offload, regardless of the timestamp's value.
1139
1140 Testing covers the following assertions:
1141 A. Each time `enqueue_offload()` is called, a message that
1142 includes the job's directory name will be logged using
1143 `logging.debug()`, regardless of whether the job was
1144 enqueued. Nothing else is allowed to be logged.
1145 B. If the job is not eligible to be offloaded,
Allen Lib41527d2017-06-22 17:28:00 -07001146 `first_offload_start` and `offload_count` are 0.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001147 C. If the job is not eligible for offload, nothing is
1148 enqueued in `queue`.
Allen Lib41527d2017-06-22 17:28:00 -07001149 D. When the job is offloaded, `offload_count` increments
J. Richard Barnetteea785362014-03-17 16:00:53 -07001150 each time.
1151 E. When the job is offloaded, the appropriate parameters are
1152 enqueued exactly once.
Allen Lib41527d2017-06-22 17:28:00 -07001153 F. The first time a job is offloaded, `first_offload_start` is
J. Richard Barnetteea785362014-03-17 16:00:53 -07001154 set to the current time.
Allen Lib41527d2017-06-22 17:28:00 -07001155 G. `first_offload_start` only changes the first time that the
J. Richard Barnetteea785362014-03-17 16:00:53 -07001156 job is offloaded.
1157
1158 The test cases below are designed to exercise all of the
1159 meaningful state transitions at least once.
1160
1161 """
1162
1163 def setUp(self):
1164 super(JobDirectoryOffloadTests, self).setUp()
J. Richard Barnette08800322014-05-16 14:49:46 -07001165 self._job = self.make_job(self.REGULAR_JOBLIST[0])
J. Richard Barnetteea785362014-03-17 16:00:53 -07001166 self._queue = Queue.Queue()
J. Richard Barnetteea785362014-03-17 16:00:53 -07001167
Jakob Juelich24f22c22014-09-26 11:46:11 -07001168
J. Richard Barnetteea785362014-03-17 16:00:53 -07001169 def _offload_unexpired_job(self, days_old):
1170 """Make calls to `enqueue_offload()` for an unexpired job.
1171
1172 This method tests assertions B and C that calling
1173 `enqueue_offload()` has no effect.
1174
1175 """
Allen Lib41527d2017-06-22 17:28:00 -07001176 self.assertEqual(self._job.offload_count, 0)
1177 self.assertEqual(self._job.first_offload_start, 0)
1178 gs_offloader._enqueue_offload(self._job, self._queue, days_old)
1179 gs_offloader._enqueue_offload(self._job, self._queue, days_old)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001180 self.assertTrue(self._queue.empty())
Allen Lib41527d2017-06-22 17:28:00 -07001181 self.assertEqual(self._job.offload_count, 0)
1182 self.assertEqual(self._job.first_offload_start, 0)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001183
Jakob Juelich24f22c22014-09-26 11:46:11 -07001184
J. Richard Barnetteea785362014-03-17 16:00:53 -07001185 def _offload_expired_once(self, days_old, count):
1186 """Make one call to `enqueue_offload()` for an expired job.
1187
1188 This method tests assertions D and E regarding side-effects
1189 expected when a job is offloaded.
1190
1191 """
Allen Lib41527d2017-06-22 17:28:00 -07001192 gs_offloader._enqueue_offload(self._job, self._queue, days_old)
1193 self.assertEqual(self._job.offload_count, count)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001194 self.assertFalse(self._queue.empty())
1195 v = self._queue.get_nowait()
1196 self.assertTrue(self._queue.empty())
J. Richard Barnette2c72ddd2014-05-20 12:17:37 -07001197 self.assertEqual(v, self._job.queue_args)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001198
Jakob Juelich24f22c22014-09-26 11:46:11 -07001199
J. Richard Barnetteea785362014-03-17 16:00:53 -07001200 def _offload_expired_job(self, days_old):
1201 """Make calls to `enqueue_offload()` for a just-expired job.
1202
1203 This method directly tests assertions F and G regarding
Allen Lib41527d2017-06-22 17:28:00 -07001204 side-effects on `first_offload_start`.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001205
1206 """
1207 t0 = time.time()
1208 self._offload_expired_once(days_old, 1)
Allen Lib41527d2017-06-22 17:28:00 -07001209 t1 = self._job.first_offload_start
J. Richard Barnetteea785362014-03-17 16:00:53 -07001210 self.assertLessEqual(t1, time.time())
1211 self.assertGreaterEqual(t1, t0)
1212 self._offload_expired_once(days_old, 2)
Allen Lib41527d2017-06-22 17:28:00 -07001213 self.assertEqual(self._job.first_offload_start, t1)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001214 self._offload_expired_once(days_old, 3)
Allen Lib41527d2017-06-22 17:28:00 -07001215 self.assertEqual(self._job.first_offload_start, t1)
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_case_1_no_expiration(self):
1219 """Test a series of `enqueue_offload()` calls with `days_old` of 0.
1220
1221 This tests that offload works as expected if calls are
1222 made both before and after the job becomes expired.
1223
1224 """
1225 self._offload_unexpired_job(0)
1226 self._job.set_finished(0)
1227 self._offload_expired_job(0)
1228
Jakob Juelich24f22c22014-09-26 11:46:11 -07001229
J. Richard Barnetteea785362014-03-17 16:00:53 -07001230 def test_case_2_no_expiration(self):
1231 """Test a series of `enqueue_offload()` calls with `days_old` of 0.
1232
1233 This tests that offload works as expected if calls are made
1234 only after the job becomes expired.
1235
1236 """
1237 self._job.set_finished(0)
1238 self._offload_expired_job(0)
1239
Jakob Juelich24f22c22014-09-26 11:46:11 -07001240
J. Richard Barnetteea785362014-03-17 16:00:53 -07001241 def test_case_1_with_expiration(self):
1242 """Test a series of `enqueue_offload()` calls with `days_old` non-zero.
1243
1244 This tests that offload works as expected if calls are made
1245 before the job finishes, before the job expires, and after
1246 the job expires.
1247
1248 """
1249 self._offload_unexpired_job(_TEST_EXPIRATION_AGE)
1250 self._job.set_finished(_TEST_EXPIRATION_AGE)
1251 self._offload_unexpired_job(_TEST_EXPIRATION_AGE)
1252 self._job.set_expired(_TEST_EXPIRATION_AGE)
1253 self._offload_expired_job(_TEST_EXPIRATION_AGE)
1254
Jakob Juelich24f22c22014-09-26 11:46:11 -07001255
J. Richard Barnetteea785362014-03-17 16:00:53 -07001256 def test_case_2_with_expiration(self):
1257 """Test a series of `enqueue_offload()` calls with `days_old` non-zero.
1258
1259 This tests that offload works as expected if calls are made
1260 between finishing and expiration, and after the job expires.
1261
1262 """
1263 self._job.set_finished(_TEST_EXPIRATION_AGE)
1264 self._offload_unexpired_job(_TEST_EXPIRATION_AGE)
1265 self._job.set_expired(_TEST_EXPIRATION_AGE)
1266 self._offload_expired_job(_TEST_EXPIRATION_AGE)
1267
Jakob Juelich24f22c22014-09-26 11:46:11 -07001268
J. Richard Barnetteea785362014-03-17 16:00:53 -07001269 def test_case_3_with_expiration(self):
1270 """Test a series of `enqueue_offload()` calls with `days_old` non-zero.
1271
1272 This tests that offload works as expected if calls are made
1273 only before finishing and after expiration.
1274
1275 """
1276 self._offload_unexpired_job(_TEST_EXPIRATION_AGE)
1277 self._job.set_expired(_TEST_EXPIRATION_AGE)
1278 self._offload_expired_job(_TEST_EXPIRATION_AGE)
1279
Jakob Juelich24f22c22014-09-26 11:46:11 -07001280
J. Richard Barnetteea785362014-03-17 16:00:53 -07001281 def test_case_4_with_expiration(self):
1282 """Test a series of `enqueue_offload()` calls with `days_old` non-zero.
1283
1284 This tests that offload works as expected if calls are made
1285 only after expiration.
1286
1287 """
1288 self._job.set_expired(_TEST_EXPIRATION_AGE)
1289 self._offload_expired_job(_TEST_EXPIRATION_AGE)
1290
1291
1292class GetJobDirectoriesTests(_TempResultsDirTestBase):
1293 """Tests for `_JobDirectory.get_job_directories()`."""
1294
J. Richard Barnetteea785362014-03-17 16:00:53 -07001295 def setUp(self):
1296 super(GetJobDirectoriesTests, self).setUp()
J. Richard Barnette08800322014-05-16 14:49:46 -07001297 self.make_job_hierarchy()
1298 os.mkdir('not-a-job')
1299 open('not-a-dir', 'w').close()
J. Richard Barnetteea785362014-03-17 16:00:53 -07001300
Jakob Juelich24f22c22014-09-26 11:46:11 -07001301
J. Richard Barnetteea785362014-03-17 16:00:53 -07001302 def _run_get_directories(self, cls, expected_list):
1303 """Test `get_job_directories()` for the given class.
1304
1305 Calls the method, and asserts that the returned list of
1306 directories matches the expected return value.
1307
1308 @param expected_list Expected return value from the call.
1309 """
J. Richard Barnetteea785362014-03-17 16:00:53 -07001310 dirlist = cls.get_job_directories()
1311 self.assertEqual(set(dirlist), set(expected_list))
J. Richard Barnetteea785362014-03-17 16:00:53 -07001312
Jakob Juelich24f22c22014-09-26 11:46:11 -07001313
J. Richard Barnetteea785362014-03-17 16:00:53 -07001314 def test_get_regular_jobs(self):
1315 """Test `RegularJobDirectory.get_job_directories()`."""
1316 self._run_get_directories(job_directories.RegularJobDirectory,
J. Richard Barnette08800322014-05-16 14:49:46 -07001317 self.REGULAR_JOBLIST)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001318
Jakob Juelich24f22c22014-09-26 11:46:11 -07001319
J. Richard Barnetteea785362014-03-17 16:00:53 -07001320 def test_get_special_jobs(self):
1321 """Test `SpecialJobDirectory.get_job_directories()`."""
1322 self._run_get_directories(job_directories.SpecialJobDirectory,
J. Richard Barnette08800322014-05-16 14:49:46 -07001323 self.SPECIAL_JOBLIST)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001324
1325
1326class AddJobsTests(_TempResultsDirTestBase):
1327 """Tests for `Offloader._add_new_jobs()`."""
1328
J. Richard Barnette08800322014-05-16 14:49:46 -07001329 MOREJOBS = ['115-fubar', '116-fubar', '117-fubar', '118-snafu']
J. Richard Barnetteea785362014-03-17 16:00:53 -07001330
1331 def setUp(self):
1332 super(AddJobsTests, self).setUp()
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001333 self._initial_job_names = (
1334 set(self.REGULAR_JOBLIST) | set(self.SPECIAL_JOBLIST))
J. Richard Barnette08800322014-05-16 14:49:46 -07001335 self.make_job_hierarchy()
1336 self._offloader = gs_offloader.Offloader(_get_options(['-a']))
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001337 self.mox.StubOutWithMock(logging, 'debug')
J. Richard Barnetteea785362014-03-17 16:00:53 -07001338
Jakob Juelich24f22c22014-09-26 11:46:11 -07001339
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001340 def _run_add_new_jobs(self, expected_key_set):
J. Richard Barnetteea785362014-03-17 16:00:53 -07001341 """Basic test assertions for `_add_new_jobs()`.
1342
1343 Asserts the following:
1344 * The keys in the offloader's `_open_jobs` dictionary
1345 matches the expected set of keys.
1346 * For every job in `_open_jobs`, the job has the expected
1347 directory name.
1348
1349 """
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001350 count = len(expected_key_set) - len(self._offloader._open_jobs)
1351 logging.debug(mox.IgnoreArg(), count)
1352 self.mox.ReplayAll()
1353 self._offloader._add_new_jobs()
J. Richard Barnetteea785362014-03-17 16:00:53 -07001354 self.assertEqual(expected_key_set,
1355 set(self._offloader._open_jobs.keys()))
1356 for jobkey, job in self._offloader._open_jobs.items():
Allen Lib41527d2017-06-22 17:28:00 -07001357 self.assertEqual(jobkey, job.dirname)
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001358 self.mox.VerifyAll()
1359 self.mox.ResetAll()
J. Richard Barnetteea785362014-03-17 16:00:53 -07001360
Jakob Juelich24f22c22014-09-26 11:46:11 -07001361
J. Richard Barnetteea785362014-03-17 16:00:53 -07001362 def test_add_jobs_empty(self):
1363 """Test adding jobs to an empty dictionary.
1364
1365 Calls the offloader's `_add_new_jobs()`, then perform
1366 the assertions of `self._check_open_jobs()`.
1367
1368 """
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001369 self._run_add_new_jobs(self._initial_job_names)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001370
Jakob Juelich24f22c22014-09-26 11:46:11 -07001371
J. Richard Barnetteea785362014-03-17 16:00:53 -07001372 def test_add_jobs_non_empty(self):
1373 """Test adding jobs to a non-empty dictionary.
1374
1375 Calls the offloader's `_add_new_jobs()` twice; once from
1376 initial conditions, and then again after adding more
1377 directories. After the second call, perform the assertions
1378 of `self._check_open_jobs()`. Additionally, assert that
1379 keys added by the first call still map to their original
1380 job object after the second call.
1381
1382 """
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001383 self._run_add_new_jobs(self._initial_job_names)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001384 jobs_copy = self._offloader._open_jobs.copy()
J. Richard Barnette08800322014-05-16 14:49:46 -07001385 for d in self.MOREJOBS:
1386 os.mkdir(d)
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001387 self._run_add_new_jobs(self._initial_job_names |
1388 set(self.MOREJOBS))
J. Richard Barnetteea785362014-03-17 16:00:53 -07001389 for key in jobs_copy.keys():
1390 self.assertIs(jobs_copy[key],
1391 self._offloader._open_jobs[key])
1392
1393
J. Richard Barnetteea785362014-03-17 16:00:53 -07001394class ReportingTests(_TempResultsDirTestBase):
Allen Lib41527d2017-06-22 17:28:00 -07001395 """Tests for `Offloader._report_failed_jobs()`."""
J. Richard Barnetteea785362014-03-17 16:00:53 -07001396
J. Richard Barnetteea785362014-03-17 16:00:53 -07001397 def setUp(self):
1398 super(ReportingTests, self).setUp()
1399 self._offloader = gs_offloader.Offloader(_get_options([]))
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001400 self.mox.StubOutWithMock(self._offloader, '_log_failed_jobs_locally')
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001401 self.mox.StubOutWithMock(logging, 'debug')
J. Richard Barnetteea785362014-03-17 16:00:53 -07001402
Jakob Juelich24f22c22014-09-26 11:46:11 -07001403
J. Richard Barnetteea785362014-03-17 16:00:53 -07001404 def _add_job(self, jobdir):
1405 """Add a job to the dictionary of unfinished jobs."""
1406 j = self.make_job(jobdir)
Allen Lib41527d2017-06-22 17:28:00 -07001407 self._offloader._open_jobs[j.dirname] = j
J. Richard Barnetteea785362014-03-17 16:00:53 -07001408 return j
1409
Jakob Juelich24f22c22014-09-26 11:46:11 -07001410
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001411 def _expect_log_message(self, new_open_jobs, with_failures):
1412 """Mock expected logging calls.
1413
Allen Lib41527d2017-06-22 17:28:00 -07001414 `_report_failed_jobs()` logs one message with the number
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001415 of jobs removed from the open job set and the number of jobs
1416 still remaining. Additionally, if there are reportable
1417 jobs, then it logs the number of jobs that haven't yet
1418 offloaded.
1419
1420 This sets up the logging calls using `new_open_jobs` to
1421 figure the job counts. If `with_failures` is true, then
1422 the log message is set up assuming that all jobs in
1423 `new_open_jobs` have offload failures.
1424
1425 @param new_open_jobs New job set for calculating counts
1426 in the messages.
1427 @param with_failures Whether the log message with a
1428 failure count is expected.
1429
1430 """
1431 count = len(self._offloader._open_jobs) - len(new_open_jobs)
1432 logging.debug(mox.IgnoreArg(), count, len(new_open_jobs))
1433 if with_failures:
1434 logging.debug(mox.IgnoreArg(), len(new_open_jobs))
1435
Jakob Juelich24f22c22014-09-26 11:46:11 -07001436
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001437 def _run_update(self, new_open_jobs):
Allen Lib41527d2017-06-22 17:28:00 -07001438 """Call `_report_failed_jobs()`.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001439
1440 Initial conditions are set up by the caller. This calls
Allen Lib41527d2017-06-22 17:28:00 -07001441 `_report_failed_jobs()` once, and then checks these
J. Richard Barnetteea785362014-03-17 16:00:53 -07001442 assertions:
J. Richard Barnetteea785362014-03-17 16:00:53 -07001443 * The offloader's new `_open_jobs` field contains only
1444 the entries in `new_open_jobs`.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001445
1446 @param new_open_jobs A dictionary representing the expected
1447 new value of the offloader's
1448 `_open_jobs` field.
1449 """
1450 self.mox.ReplayAll()
Allen Lib41527d2017-06-22 17:28:00 -07001451 self._offloader._report_failed_jobs()
1452 self._offloader._remove_offloaded_jobs()
J. Richard Barnetteea785362014-03-17 16:00:53 -07001453 self.assertEqual(self._offloader._open_jobs, new_open_jobs)
1454 self.mox.VerifyAll()
1455 self.mox.ResetAll()
1456
Jakob Juelich24f22c22014-09-26 11:46:11 -07001457
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001458 def _expect_failed_jobs(self, failed_jobs):
1459 """Mock expected call to log the failed jobs on local disk.
1460
1461 TODO(crbug.com/686904): The fact that we have to mock an internal
1462 function for this test is evidence that we need to pull out the local
1463 file formatter in its own object in a future CL.
1464
1465 @param failed_jobs: The list of jobs being logged as failed.
1466 """
1467 self._offloader._log_failed_jobs_locally(failed_jobs)
1468
1469
J. Richard Barnetteea785362014-03-17 16:00:53 -07001470 def test_no_jobs(self):
Allen Lib41527d2017-06-22 17:28:00 -07001471 """Test `_report_failed_jobs()` with no open jobs.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001472
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001473 Initial conditions are an empty `_open_jobs` list.
1474 Expected result is an empty `_open_jobs` list.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001475
1476 """
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001477 self._expect_log_message({}, False)
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001478 self._expect_failed_jobs([])
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001479 self._run_update({})
J. Richard Barnetteea785362014-03-17 16:00:53 -07001480
Jakob Juelich24f22c22014-09-26 11:46:11 -07001481
J. Richard Barnetteea785362014-03-17 16:00:53 -07001482 def test_all_completed(self):
Allen Lib41527d2017-06-22 17:28:00 -07001483 """Test `_report_failed_jobs()` with only complete jobs.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001484
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001485 Initial conditions are an `_open_jobs` list consisting of only completed
1486 jobs.
1487 Expected result is an empty `_open_jobs` list.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001488
1489 """
J. Richard Barnette08800322014-05-16 14:49:46 -07001490 for d in self.REGULAR_JOBLIST:
J. Richard Barnetteea785362014-03-17 16:00:53 -07001491 self._add_job(d).set_complete()
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001492 self._expect_log_message({}, False)
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001493 self._expect_failed_jobs([])
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001494 self._run_update({})
J. Richard Barnetteea785362014-03-17 16:00:53 -07001495
Jakob Juelich24f22c22014-09-26 11:46:11 -07001496
J. Richard Barnetteea785362014-03-17 16:00:53 -07001497 def test_none_finished(self):
Allen Lib41527d2017-06-22 17:28:00 -07001498 """Test `_report_failed_jobs()` with only unfinished jobs.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001499
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001500 Initial conditions are an `_open_jobs` list consisting of only
1501 unfinished jobs.
1502 Expected result is no change to the `_open_jobs` list.
J. Richard Barnetteea785362014-03-17 16:00:53 -07001503
1504 """
J. Richard Barnette08800322014-05-16 14:49:46 -07001505 for d in self.REGULAR_JOBLIST:
J. Richard Barnetteea785362014-03-17 16:00:53 -07001506 self._add_job(d)
J. Richard Barnette22dd7482014-06-23 12:25:02 -07001507 new_jobs = self._offloader._open_jobs.copy()
1508 self._expect_log_message(new_jobs, False)
Prathmesh Prabhu16f9e5c2017-01-30 17:54:40 -08001509 self._expect_failed_jobs([])
Prathmesh Prabhu33e3e1a2017-01-30 18:03:34 -08001510 self._run_update(new_jobs)
J. Richard Barnetteea785362014-03-17 16:00:53 -07001511
1512
Allen Lib41527d2017-06-22 17:28:00 -07001513class GsOffloaderMockTests(_TempResultsDirTestCase):
1514 """Tests using mock instead of mox."""
1515
1516 def setUp(self):
1517 super(GsOffloaderMockTests, self).setUp()
1518 alarm = mock.patch('signal.alarm', return_value=0)
1519 alarm.start()
1520 self.addCleanup(alarm.stop)
1521
1522 self._saved_loglevel = logging.getLogger().getEffectiveLevel()
1523 logging.getLogger().setLevel(logging.CRITICAL + 1)
1524
1525 self._job = self.make_job(self.REGULAR_JOBLIST[0])
1526
1527
1528 def test_offload_timeout_early(self):
1529 """Test that `offload_dir()` times out correctly.
1530
1531 This test triggers timeout at the earliest possible moment,
1532 at the first call to set the timeout alarm.
1533
1534 """
1535 signal.alarm.side_effect = [0, timeout_util.TimeoutError('fubar')]
1536 with mock.patch.object(gs_offloader, '_upload_cts_testresult',
1537 autospec=True) as upload:
1538 upload.return_value = None
1539 gs_offloader.GSOffloader(
1540 utils.DEFAULT_OFFLOAD_GSURI, False, 0).offload(
1541 self._job.queue_args[0],
1542 self._job.queue_args[1],
1543 self._job.queue_args[2])
1544 self.assertTrue(os.path.isdir(self._job.queue_args[0]))
1545
1546
1547 # TODO(ayatane): This tests passes when run locally, but it fails
1548 # when run on trybot. I have no idea why, but the assert isdir
1549 # fails.
1550 #
1551 # This test is also kind of redundant since we are using the timeout
1552 # from chromite which has its own tests.
1553 @unittest.skip('This fails on trybot')
1554 def test_offload_timeout_late(self):
1555 """Test that `offload_dir()` times out correctly.
1556
1557 This test triggers timeout at the latest possible moment, at
1558 the call to clear the timeout alarm.
1559
1560 """
1561 signal.alarm.side_effect = [0, 0, timeout_util.TimeoutError('fubar')]
1562 with mock.patch.object(gs_offloader, '_upload_cts_testresult',
1563 autospec=True) as upload, \
1564 mock.patch.object(gs_offloader, '_get_cmd_list',
1565 autospec=True) as get_cmd_list:
1566 upload.return_value = None
1567 get_cmd_list.return_value = ['test', '-d', self._job.queue_args[0]]
1568 gs_offloader.GSOffloader(
1569 utils.DEFAULT_OFFLOAD_GSURI, False, 0).offload(
1570 self._job.queue_args[0],
1571 self._job.queue_args[1],
1572 self._job.queue_args[2])
1573 self.assertTrue(os.path.isdir(self._job.queue_args[0]))
1574
1575
1576
J. Richard Barnetteea785362014-03-17 16:00:53 -07001577if __name__ == '__main__':
1578 unittest.main()