blob: 96e51c46ba4096d8d46c40b2065363e0f46da35b [file] [log] [blame]
Gabe Black3b567202015-09-23 14:07:59 -07001#!/usr/bin/python2
Chris Sosa7cd23202013-10-15 17:22:57 -07002
3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Integration tests for the devserver.
8
9This module is responsible for testing the actual devserver APIs and should be
10run whenever changes are made to the devserver.
11
xixuan52c2fba2016-05-20 17:02:48 -070012To run the integration test for devserver:
13 python ./devserver_integration_test.py
Chris Sosa7cd23202013-10-15 17:22:57 -070014"""
15
Gabe Black3b567202015-09-23 14:07:59 -070016from __future__ import print_function
17
xixuan52c2fba2016-05-20 17:02:48 -070018import cros_update_progress
Chris Sosa7cd23202013-10-15 17:22:57 -070019import devserver_constants
20import json
21import logging
22from xml.dom import minidom
23import os
24import psutil
25import shutil
26import signal
Gilad Arnold7de05f72014-02-14 13:14:20 -080027import socket
Chris Sosa7cd23202013-10-15 17:22:57 -070028import subprocess
29import tempfile
30import time
31import unittest
32import urllib2
33
34
35# Paths are relative to this script's base directory.
36LABEL = 'devserver'
37TEST_IMAGE_PATH = 'testdata/devserver'
38TEST_IMAGE_NAME = 'update.gz'
39EXPECTED_HASH = 'kGcOinJ0vA8vdYX53FN0F5BdwfY='
40
41# Update request based on Omaha v3 protocol format.
42UPDATE_REQUEST = """<?xml version="1.0" encoding="UTF-8"?>
43<request version="ChromeOSUpdateEngine-0.1.0.0" updaterversion="ChromeOSUpdateEngine-0.1.0.0" protocol="3.0" ismachine="1">
44 <os version="Indy" platform="Chrome OS" sp="0.11.254.2011_03_09_1814_i686"></os>
45 <app appid="{DEV-BUILD}" version="11.254.2011_03_09_1814" lang="en-US" track="developer-build" board="x86-generic" hardware_class="BETA DVT" delta_okay="true">
46 <updatecheck></updatecheck>
47 </app>
48</request>
49"""
50
51# RPC constants.
52STAGE = 'stage'
53IS_STAGED = 'is_staged'
54STATIC = 'static'
55UPDATE = 'update'
56CHECK_HEALTH = 'check_health'
57CONTROL_FILES = 'controlfiles'
58XBUDDY = 'xbuddy'
Prashanth Ba06d2d22014-03-07 15:35:19 -080059LIST_IMAGE_DIR = 'list_image_dir'
Chris Sosa7cd23202013-10-15 17:22:57 -070060
61# API rpcs and constants.
62API_HOST_INFO = 'api/hostinfo'
63API_SET_NEXT_UPDATE = 'api/setnextupdate'
64API_SET_UPDATE_REQUEST = 'new_update-test/the-new-update'
65API_TEST_IP_ADDR = '127.0.0.1'
66
67DEVSERVER_START_TIMEOUT = 15
Gilad Arnold08516112014-02-14 13:14:03 -080068DEVSERVER_START_SLEEP = 1
Gilad Arnold7de05f72014-02-14 13:14:20 -080069MAX_START_ATTEMPTS = 5
Chris Sosa7cd23202013-10-15 17:22:57 -070070
71
72class DevserverFailedToStart(Exception):
73 """Raised if we could not start the devserver."""
74
75
Gilad Arnold08516112014-02-14 13:14:03 -080076class DevserverTestBase(unittest.TestCase):
Chris Sosa7cd23202013-10-15 17:22:57 -070077 """Class containing common logic between devserver test classes."""
78
79 def setUp(self):
Gilad Arnold08516112014-02-14 13:14:03 -080080 """Creates and populates a test directory, temporary files."""
Chris Sosa7cd23202013-10-15 17:22:57 -070081 self.test_data_path = tempfile.mkdtemp()
82 self.src_dir = os.path.dirname(__file__)
83
84 # Current location of testdata payload.
85 image_src = os.path.join(self.src_dir, TEST_IMAGE_PATH, TEST_IMAGE_NAME)
86
Gilad Arnold08516112014-02-14 13:14:03 -080087 # Copy the payload to the location of the update label.
88 self._CreateLabelAndCopyImage(LABEL, image_src)
Chris Sosa7cd23202013-10-15 17:22:57 -070089
90 # Copy the payload to the location of forced label.
Gilad Arnold08516112014-02-14 13:14:03 -080091 self._CreateLabelAndCopyImage(API_SET_UPDATE_REQUEST, image_src)
Chris Sosa7cd23202013-10-15 17:22:57 -070092
Gilad Arnold08516112014-02-14 13:14:03 -080093 # Allocate temporary files for various devserver outputs.
94 self.pidfile = self._MakeTempFile('pid')
95 self.portfile = self._MakeTempFile('port')
96 self.logfile = self._MakeTempFile('log')
Chris Sosa7cd23202013-10-15 17:22:57 -070097
Gilad Arnold08516112014-02-14 13:14:03 -080098 # Initialize various runtime values.
99 self.devserver_url = self.port = self.pid = None
Chris Sosa7cd23202013-10-15 17:22:57 -0700100
101 def tearDown(self):
Gilad Arnold08516112014-02-14 13:14:03 -0800102 """Kill the server, remove the test directory and temporary files."""
Chris Sosa7cd23202013-10-15 17:22:57 -0700103 if self.pid:
104 os.kill(self.pid, signal.SIGKILL)
105
Gilad Arnold08516112014-02-14 13:14:03 -0800106 self._RemoveFile(self.pidfile)
107 self._RemoveFile(self.portfile)
108 self._RemoveFile(self.logfile)
109 shutil.rmtree(self.test_data_path)
Chris Sosa7cd23202013-10-15 17:22:57 -0700110
111 # Helper methods begin here.
112
Gilad Arnold08516112014-02-14 13:14:03 -0800113 def _CreateLabelAndCopyImage(self, label, image):
114 """Creates a label location and copies an image to it."""
115 label_dir = os.path.join(self.test_data_path, label)
116 os.makedirs(label_dir)
117 shutil.copy(image, os.path.join(label_dir, TEST_IMAGE_NAME))
118
119 def _MakeTempFile(self, suffix):
120 """Return path of a newly created temporary file."""
121 with tempfile.NamedTemporaryFile(suffix='-devserver-%s' % suffix) as f:
122 name = f.name
123 f.close()
124
125 return name
126
127 def _RemoveFile(self, filename):
128 """Removes a file if it is present."""
129 if os.path.isfile(filename):
130 os.remove(filename)
131
132 def _ReadIntValueFromFile(self, path, desc):
133 """Reads a string from file and returns its conversion into an integer."""
134 if not os.path.isfile(path):
135 raise DevserverFailedToStart('Devserver did not drop %s (%r).' %
136 (desc, path))
137
138 with open(path) as f:
139 value_str = f.read()
140
141 try:
142 return int(value_str)
143 except ValueError:
144 raise DevserverFailedToStart('Devserver did not drop a valid value '
145 'in %s (%r).' % (desc, value_str))
146
147 def _StartServer(self, port=0):
Chris Sosa7cd23202013-10-15 17:22:57 -0700148 """Attempts to start devserver on |port|.
149
Gilad Arnold08516112014-02-14 13:14:03 -0800150 In the default case where port == 0, the server will bind to an arbitrary
Dan Shi2f136862016-02-11 15:38:38 -0800151 available port. If successful, this method will set the devserver's pid
Gilad Arnold08516112014-02-14 13:14:03 -0800152 (self.pid), actual listening port (self.port) and URL (self.devserver_url).
Chris Sosa7cd23202013-10-15 17:22:57 -0700153
154 Raises:
155 DevserverFailedToStart: If the devserver could not be started.
156 """
157 cmd = [
158 'python',
159 os.path.join(self.src_dir, 'devserver.py'),
160 'devserver.py',
161 '--static_dir', self.test_data_path,
162 '--pidfile', self.pidfile,
Gilad Arnold08516112014-02-14 13:14:03 -0800163 '--portfile', self.portfile,
Chris Sosa7cd23202013-10-15 17:22:57 -0700164 '--port', str(port),
165 '--logfile', self.logfile]
166
167 # Pipe all output. Use logfile to get devserver log.
168 subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
169
Gilad Arnold08516112014-02-14 13:14:03 -0800170 # Wait for devserver to start, determining its actual serving port and URL.
Chris Sosa7cd23202013-10-15 17:22:57 -0700171 current_time = time.time()
172 deadline = current_time + DEVSERVER_START_TIMEOUT
173 while current_time < deadline:
Chris Sosa7cd23202013-10-15 17:22:57 -0700174 try:
Gilad Arnold08516112014-02-14 13:14:03 -0800175 self.port = self._ReadIntValueFromFile(self.portfile, 'portfile')
176 self.devserver_url = 'http://127.0.0.1:%d' % self.port
Gabe Black3b567202015-09-23 14:07:59 -0700177 self._MakeRPC(CHECK_HEALTH, timeout=1)
Chris Sosa7cd23202013-10-15 17:22:57 -0700178 break
179 except Exception:
Gilad Arnold08516112014-02-14 13:14:03 -0800180 time.sleep(DEVSERVER_START_SLEEP)
181 current_time = time.time()
Chris Sosa7cd23202013-10-15 17:22:57 -0700182 else:
183 raise DevserverFailedToStart('Devserver failed to start within timeout.')
184
Gilad Arnold08516112014-02-14 13:14:03 -0800185 # Retrieve PID.
186 self.pid = self._ReadIntValueFromFile(self.pidfile, 'pidfile')
Chris Sosa7cd23202013-10-15 17:22:57 -0700187
188 def VerifyHandleUpdate(self, label, use_test_payload=True):
189 """Verifies that we can send an update request to the devserver.
190
191 This method verifies (using a fake update_request blob) that the devserver
192 can interpret the payload and give us back the right payload.
193
194 Args:
195 label: Label that update is served from e.g. <board>-release/<version>
196 use_test_payload: If set to true, expects to serve payload under
197 testdata/ and does extra checks i.e. compares hash and content of
198 payload.
Gilad Arnold08516112014-02-14 13:14:03 -0800199
Chris Sosa7cd23202013-10-15 17:22:57 -0700200 Returns:
201 url of the update payload if we verified the update.
202 """
203 update_label = '/'.join([UPDATE, label])
204 response = self._MakeRPC(update_label, data=UPDATE_REQUEST)
205 self.assertNotEqual('', response)
206
207 # Parse the response and check if it contains the right result.
208 dom = minidom.parseString(response)
209 update = dom.getElementsByTagName('updatecheck')[0]
210 expected_static_url = '/'.join([self.devserver_url, STATIC, label])
211 expected_hash = EXPECTED_HASH if use_test_payload else None
212 url = self.VerifyV3Response(update, expected_static_url,
213 expected_hash=expected_hash)
214
215 # Verify the image we download is correct since we already know what it is.
216 if use_test_payload:
217 connection = urllib2.urlopen(url)
218 contents = connection.read()
219 connection.close()
220 self.assertEqual('Developers, developers, developers!\n', contents)
221
222 return url
223
224 def VerifyV3Response(self, update, expected_static_url, expected_hash):
225 """Verifies the update DOM from a v3 response and returns the url."""
226 # Parse the response and check if it contains the right result.
227 urls = update.getElementsByTagName('urls')[0]
228 url = urls.getElementsByTagName('url')[0]
229
230 static_url = url.getAttribute('codebase')
231 # Static url's end in /.
232 self.assertEqual(expected_static_url + '/', static_url)
233
234 manifest = update.getElementsByTagName('manifest')[0]
235 packages = manifest.getElementsByTagName('packages')[0]
236 package = packages.getElementsByTagName('package')[0]
237 filename = package.getAttribute('name')
238 self.assertEqual(TEST_IMAGE_NAME, filename)
239
240 if expected_hash:
241 hash_value = package.getAttribute('hash')
242 self.assertEqual(EXPECTED_HASH, hash_value)
243
244 url = os.path.join(static_url, filename)
245 return url
246
247 def _MakeRPC(self, rpc, data=None, timeout=None, **kwargs):
Gilad Arnold08516112014-02-14 13:14:03 -0800248 """Makes an RPC call to the devserver.
Chris Sosa7cd23202013-10-15 17:22:57 -0700249
250 Args:
Gilad Arnold08516112014-02-14 13:14:03 -0800251 rpc: The function to run on the devserver, e.g. 'stage'.
Chris Sosa7cd23202013-10-15 17:22:57 -0700252 data: Optional post data to send.
253 timeout: Optional timeout to pass to urlopen.
Gilad Arnold08516112014-02-14 13:14:03 -0800254 kwargs: Optional arguments to the function, e.g. artifact_url='foo/bar'.
Chris Sosa7cd23202013-10-15 17:22:57 -0700255
Gilad Arnold08516112014-02-14 13:14:03 -0800256 Returns:
257 The function output.
Chris Sosa7cd23202013-10-15 17:22:57 -0700258 """
259 request = '/'.join([self.devserver_url, rpc])
260 if kwargs:
261 # Join the kwargs to the URL.
262 request += '?' + '&'.join('%s=%s' % item for item in kwargs.iteritems())
263
264 output = None
265 try:
266 # Let's log output for all rpc's without timeouts because we only
267 # use timeouts to check to see if something is up and these checks tend
268 # to be small and so logging it will be extremely repetitive.
269 if not timeout:
270 logging.info('Making request using %s', request)
271
272 connection = urllib2.urlopen(request, data=data, timeout=timeout)
273 output = connection.read()
274 connection.close()
275 except urllib2.HTTPError:
276 raise
277
278 return output
279
280
Gilad Arnold08516112014-02-14 13:14:03 -0800281class AutoStartDevserverTestBase(DevserverTestBase):
282 """Test base class that automatically starts the devserver."""
283
284 def setUp(self):
285 """Initialize everything, then start the server."""
286 super(AutoStartDevserverTestBase, self).setUp()
287 self._StartServer()
288
289
Gilad Arnold7de05f72014-02-14 13:14:20 -0800290class DevserverStartTests(DevserverTestBase):
291 """Test that devserver starts up correctly."""
292
293 def testStartAnyPort(self):
294 """Starts the devserver, have it bind to an arbitrary available port."""
295 self._StartServer()
296
297 def testStartSpecificPort(self):
298 """Starts the devserver with a specific port."""
299 for _ in range(MAX_START_ATTEMPTS):
300 # This is a cheap hack to find an arbitrary unused port: we open a socket
301 # and bind it to port zero, then pull out the actual port number and
302 # close the socket. In all likelihood, this will leave us with an
303 # available port number that we can use for starting the devserver.
304 # However, this heuristic is susceptible to race conditions, hence the
305 # retry loop.
306 s = socket.socket()
307 s.bind(('', 0))
308 # s.getsockname() is definitely callable.
309 # pylint: disable=E1102
310 _, port = s.getsockname()
311 s.close()
312
313 self._StartServer(port=port)
314
315
Gilad Arnold08516112014-02-14 13:14:03 -0800316class DevserverBasicTests(AutoStartDevserverTestBase):
317 """Short running tests for the devserver (no remote deps).
Chris Sosa7cd23202013-10-15 17:22:57 -0700318
319 These are technically not unittests because they depend on being able to
320 start a devserver locally which technically requires external resources so
321 they are lumped with the remote tests here.
322 """
323
324 def testHandleUpdateV3(self):
325 self.VerifyHandleUpdate(label=LABEL)
326
327 def testApiBadSetNextUpdateRequest(self):
328 """Tests sending a bad setnextupdate request."""
329 # Send bad request and ensure it fails...
330 self.assertRaises(urllib2.URLError,
331 self._MakeRPC,
332 '/'.join([API_SET_NEXT_UPDATE, API_TEST_IP_ADDR]))
333
334 def testApiBadSetNextUpdateURL(self):
335 """Tests contacting a bad setnextupdate url."""
336 # Send bad request and ensure it fails...
337 self.assertRaises(urllib2.URLError,
338 self._MakeRPC, API_SET_NEXT_UPDATE)
339
340 def testApiBadHostInfoURL(self):
341 """Tests contacting a bad hostinfo url."""
342 # Host info should be invalid without a specified address.
343 self.assertRaises(urllib2.URLError,
344 self._MakeRPC, API_HOST_INFO)
345
346 def testApiHostInfoAndSetNextUpdate(self):
347 """Tests using the setnextupdate and hostinfo api commands."""
348 # Send setnextupdate command.
349 self._MakeRPC('/'.join([API_SET_NEXT_UPDATE, API_TEST_IP_ADDR]),
350 data=API_SET_UPDATE_REQUEST)
351
352 # Send hostinfo command and verify the setnextupdate worked.
353 response = self._MakeRPC('/'.join([API_HOST_INFO, API_TEST_IP_ADDR]))
354
355 self.assertEqual(
356 json.loads(response)['forced_update_label'], API_SET_UPDATE_REQUEST)
357
358 def testXBuddyLocalAlias(self):
359 """Extensive local image xbuddy unittest.
360
361 This test verifies all the local xbuddy logic by creating a new local folder
362 with the necessary update items and verifies we can use all of them.
363 """
364 update_data = 'TEST UPDATE'
365 image_data = 'TEST IMAGE'
366 stateful_data = 'STATEFUL STUFFS'
367 build_id = 'x86-generic/R32-9999.0.0-a1'
368 xbuddy_path = 'x86-generic/R32-9999.0.0-a1/test'
369 build_dir = os.path.join(self.test_data_path, build_id)
370 os.makedirs(build_dir)
371 test_image_file = os.path.join(build_dir,
372 devserver_constants.TEST_IMAGE_FILE)
373 update_file = os.path.join(build_dir, devserver_constants.UPDATE_FILE)
374 stateful_file = os.path.join(build_dir, devserver_constants.STATEFUL_FILE)
375
376 logging.info('Creating dummy files')
377
378 for item, filename, data in zip(
379 ['full_payload', 'test', 'stateful'],
380 [update_file, test_image_file, stateful_file],
381 [update_data, image_data, stateful_data]):
382 logging.info('Creating file %s', filename)
383 with open(filename, 'w') as fh:
384 fh.write(data)
385
386 xbuddy_path = '/'.join([build_id, item])
387 logging.info('Testing xbuddy path %s', xbuddy_path)
388 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]))
389 self.assertEqual(response, data)
390
391 expected_dir = '/'.join([self.devserver_url, STATIC, build_id])
392 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), return_dir=True)
393 self.assertEqual(response, expected_dir)
394
Yu-Ju Hong51495eb2013-12-12 17:08:43 -0800395 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]),
396 relative_path=True)
397 self.assertEqual(response, build_id)
398
Chris Sosa7cd23202013-10-15 17:22:57 -0700399 xbuddy_path = '/'.join([build_id, 'test'])
400 logging.info('Testing for_update for %s', xbuddy_path)
401 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), for_update=True)
402 expected_path = '/'.join([self.devserver_url, UPDATE, build_id])
403 self.assertTrue(response, expected_path)
404
405 logging.info('Verifying the actual payload data')
406 url = self.VerifyHandleUpdate(build_id, use_test_payload=False)
407 logging.info('Verify the actual content of the update payload')
408 connection = urllib2.urlopen(url)
409 contents = connection.read()
410 connection.close()
411 self.assertEqual(update_data, contents)
412
413 def testPidFile(self):
414 """Test that using a pidfile works correctly."""
415 with open(self.pidfile, 'r') as f:
416 pid = f.read()
Chris Sosa7cd23202013-10-15 17:22:57 -0700417 # Let's assert some process information about the devserver.
Dan Shi2f136862016-02-11 15:38:38 -0800418 self.assertTrue(pid.strip().isdigit())
Chris Sosa7cd23202013-10-15 17:22:57 -0700419 process = psutil.Process(int(pid))
420 self.assertTrue(process.is_running())
421 self.assertTrue('devserver.py' in process.cmdline)
422
423
Gilad Arnold08516112014-02-14 13:14:03 -0800424class DevserverExtendedTests(AutoStartDevserverTestBase):
Chris Sosa7cd23202013-10-15 17:22:57 -0700425 """Longer running integration tests that test interaction with Google Storage.
426
427 Note: due to the interaction with Google Storage, these tests both require
428 1) runner has access to the Google Storage bucket where builders store builds.
429 2) time. These tests actually download the artifacts needed.
430 """
431
xixuan52c2fba2016-05-20 17:02:48 -0700432 def testCrosAU(self):
433 """Tests core autotest workflow where we trigger CrOS auto-update.
434
435 It mainly tests the following API:
436 a. 'get_au_status'
437 b. 'handler_cleanup'
438 c. 'kill_au_proc'
439 """
440 host_name = '100.0.0.0'
xixuan2a0970a2016-08-10 12:12:44 -0700441 p = subprocess.Popen(['sleep 100'], shell=True, preexec_fn=os.setsid)
442 pid = os.getpgid(p.pid)
xixuan52c2fba2016-05-20 17:02:48 -0700443 status = 'updating'
444 progress_tracker = cros_update_progress.AUProgress(host_name, pid)
445 progress_tracker.WriteStatus(status)
446
447 logging.info('Retrieving auto-update status for process %d', pid)
448 response = self._MakeRPC('get_au_status', host_name=host_name, pid=pid)
449 self.assertFalse(json.loads(response)[0])
450 self.assertEqual(json.loads(response)[1], status)
451
452 progress_tracker.WriteStatus(cros_update_progress.FINISHED)
453 logging.info('Mock auto-update process is finished')
454 response = self._MakeRPC('get_au_status', host_name=host_name, pid=pid)
455 self.assertTrue(json.loads(response)[0])
456 self.assertEqual(json.loads(response)[1], cros_update_progress.FINISHED)
457
458 logging.info('Delete auto-update track status file')
459 self.assertTrue(os.path.exists(progress_tracker.track_status_file))
460 self._MakeRPC('handler_cleanup', host_name=host_name, pid=pid)
461 self.assertFalse(os.path.exists(progress_tracker.track_status_file))
462
463 logging.info('Kill the left auto-update processes for host %s', host_name)
464 progress_tracker.WriteStatus(cros_update_progress.FINISHED)
465 response = self._MakeRPC('kill_au_proc', host_name=host_name)
466 self.assertEqual(response, 'True')
467 self.assertFalse(os.path.exists(progress_tracker.track_status_file))
468 self.assertFalse(cros_update_progress.IsProcessAlive(pid))
469
470
Chris Sosa7cd23202013-10-15 17:22:57 -0700471 def testStageAndUpdate(self):
472 """Tests core autotest workflow where we stage/update with a test payload.
Gabe Black3b567202015-09-23 14:07:59 -0700473
Chris Sosa7cd23202013-10-15 17:22:57 -0700474 """
Dan Shi2f136862016-02-11 15:38:38 -0800475 build_id = 'peppy-release/R50-7870.0.0'
Chris Sosa7cd23202013-10-15 17:22:57 -0700476 archive_url = 'gs://chromeos-image-archive/%s' % build_id
477
478 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
479 artifacts='full_payload,stateful')
480 self.assertEqual(response, 'False')
481
482 logging.info('Staging update artifacts')
483 self._MakeRPC(STAGE, archive_url=archive_url,
484 artifacts='full_payload,stateful')
485 logging.info('Staging complete. '
486 'Verifying files exist and are staged in the staging '
487 'directory.')
488 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
489 artifacts='full_payload,stateful')
490 self.assertEqual(response, 'True')
491 staged_dir = os.path.join(self.test_data_path, build_id)
492 self.assertTrue(os.path.isdir(staged_dir))
493 self.assertTrue(os.path.exists(
494 os.path.join(staged_dir, devserver_constants.UPDATE_FILE)))
495 self.assertTrue(os.path.exists(
496 os.path.join(staged_dir, devserver_constants.STATEFUL_FILE)))
497
498 logging.info('Verifying we can update using the stage update artifacts.')
499 self.VerifyHandleUpdate(build_id, use_test_payload=False)
500
501 def testStageAutotestAndGetPackages(self):
502 """Another autotest workflow test where we stage/update with a test payload.
Gabe Black3b567202015-09-23 14:07:59 -0700503
Chris Sosa7cd23202013-10-15 17:22:57 -0700504 """
505 build_id = 'x86-mario-release/R32-4810.0.0'
506 archive_url = 'gs://chromeos-image-archive/%s' % build_id
507 autotest_artifacts = 'autotest,test_suites,au_suite'
508 logging.info('Staging autotest artifacts (may take a while).')
509 self._MakeRPC(STAGE, archive_url=archive_url, artifacts=autotest_artifacts)
510
511 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
512 artifacts=autotest_artifacts)
513 self.assertEqual(response, 'True')
514
515 # Verify the files exist and are staged in the staging directory.
516 logging.info('Checking directories exist after we staged the files.')
517 staged_dir = os.path.join(self.test_data_path, build_id)
518 autotest_dir = os.path.join(staged_dir, 'autotest')
519 package_dir = os.path.join(autotest_dir, 'packages')
520 self.assertTrue(os.path.isdir(staged_dir))
521 self.assertTrue(os.path.isdir(autotest_dir))
522 self.assertTrue(os.path.isdir(package_dir))
523
524 control_files = self._MakeRPC(CONTROL_FILES, build=build_id,
525 suite_name='bvt')
526 logging.info('Checking for known control file in bvt suite.')
527 self.assertTrue('client/site_tests/platform_FilePerms/'
528 'control' in control_files)
529
530 def testRemoteXBuddyAlias(self):
531 """Another autotest workflow test where we stage/update with a test payload.
Gabe Black3b567202015-09-23 14:07:59 -0700532
Chris Sosa7cd23202013-10-15 17:22:57 -0700533 """
534 build_id = 'x86-mario-release/R32-4810.0.0'
535 xbuddy_path = 'remote/x86-mario/R32-4810.0.0/full_payload'
536 xbuddy_bad_path = 'remote/x86-mario/R32-9999.9999.9999'
537 logging.info('Staging artifacts using xbuddy.')
538 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), return_dir=True)
539
540 logging.info('Verifying static url returned is valid.')
541 expected_static_url = '/'.join([self.devserver_url, STATIC, build_id])
542 self.assertEqual(response, expected_static_url)
543
544 logging.info('Checking for_update returns an update_url for what we just '
545 'staged.')
546 expected_update_url = '/'.join([self.devserver_url, UPDATE, build_id])
547 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), for_update=True)
548 self.assertEqual(response, expected_update_url)
549
550 logging.info('Now give xbuddy a bad path.')
551 self.assertRaises(urllib2.HTTPError,
552 self._MakeRPC,
553 '/'.join([XBUDDY, xbuddy_bad_path]))
554
Prashanth Ba06d2d22014-03-07 15:35:19 -0800555 def testListImageDir(self):
556 """Verifies that we can list the contents of the image directory."""
557 build_id = 'x86-mario-release/R32-4810.0.0'
558 archive_url = 'gs://chromeos-image-archive/%s' % build_id
559 build_dir = os.path.join(self.test_data_path, build_id)
560 shutil.rmtree(build_dir, ignore_errors=True)
561
562 logging.info('checking for %s on an unstaged build.', LIST_IMAGE_DIR)
563 response = self._MakeRPC(LIST_IMAGE_DIR, archive_url=archive_url)
564 self.assertTrue(archive_url in response and 'not been staged' in response)
565
566 logging.info('Checking for %s on a staged build.', LIST_IMAGE_DIR)
567 fake_file_name = 'fake_file'
568 try:
569 os.makedirs(build_dir)
570 open(os.path.join(build_dir, fake_file_name), 'w').close()
571 except OSError:
572 logging.error('Could not create files to imitate staged content. '
573 'Build dir %s, file %s', build_dir, fake_file_name)
574 raise
575 response = self._MakeRPC(LIST_IMAGE_DIR, archive_url=archive_url)
576 self.assertTrue(fake_file_name in response)
577 shutil.rmtree(build_dir, ignore_errors=True)
Chris Sosa7cd23202013-10-15 17:22:57 -0700578
579if __name__ == '__main__':
580 logging_format = '%(levelname)-8s: %(message)s'
581 logging.basicConfig(level=logging.DEBUG, format=logging_format)
582 unittest.main()