blob: 49eea74bda4154a354fb264b5cff95a36796ac2f [file] [log] [blame]
Chris Sosa7cd23202013-10-15 17:22:57 -07001#!/usr/bin/python
2
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
12Note there are two classes of tests here and they can be run separately.
13
14To just run the short-running "unittests" run:
15 ./devserver_integration_tests.py DevserverUnittests
16
17To just run the longer-running tests, run:
18 ./devserver_integration_tests.py DevserverIntegrationTests
19"""
20
21import devserver_constants
22import json
23import logging
24from xml.dom import minidom
25import os
26import psutil
27import shutil
28import signal
Gilad Arnold7de05f72014-02-14 13:14:20 -080029import socket
Chris Sosa7cd23202013-10-15 17:22:57 -070030import subprocess
31import tempfile
32import time
33import unittest
34import urllib2
35
36
37# Paths are relative to this script's base directory.
38LABEL = 'devserver'
39TEST_IMAGE_PATH = 'testdata/devserver'
40TEST_IMAGE_NAME = 'update.gz'
41EXPECTED_HASH = 'kGcOinJ0vA8vdYX53FN0F5BdwfY='
42
43# Update request based on Omaha v3 protocol format.
44UPDATE_REQUEST = """<?xml version="1.0" encoding="UTF-8"?>
45<request version="ChromeOSUpdateEngine-0.1.0.0" updaterversion="ChromeOSUpdateEngine-0.1.0.0" protocol="3.0" ismachine="1">
46 <os version="Indy" platform="Chrome OS" sp="0.11.254.2011_03_09_1814_i686"></os>
47 <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">
48 <updatecheck></updatecheck>
49 </app>
50</request>
51"""
52
53# RPC constants.
54STAGE = 'stage'
55IS_STAGED = 'is_staged'
56STATIC = 'static'
57UPDATE = 'update'
58CHECK_HEALTH = 'check_health'
59CONTROL_FILES = 'controlfiles'
60XBUDDY = 'xbuddy'
Prashanth Ba06d2d22014-03-07 15:35:19 -080061LIST_IMAGE_DIR = 'list_image_dir'
Chris Sosa7cd23202013-10-15 17:22:57 -070062
63# API rpcs and constants.
64API_HOST_INFO = 'api/hostinfo'
65API_SET_NEXT_UPDATE = 'api/setnextupdate'
66API_SET_UPDATE_REQUEST = 'new_update-test/the-new-update'
67API_TEST_IP_ADDR = '127.0.0.1'
68
69DEVSERVER_START_TIMEOUT = 15
Gilad Arnold08516112014-02-14 13:14:03 -080070DEVSERVER_START_SLEEP = 1
Gilad Arnold7de05f72014-02-14 13:14:20 -080071MAX_START_ATTEMPTS = 5
Chris Sosa7cd23202013-10-15 17:22:57 -070072
73
74class DevserverFailedToStart(Exception):
75 """Raised if we could not start the devserver."""
76
77
Gilad Arnold08516112014-02-14 13:14:03 -080078class DevserverTestBase(unittest.TestCase):
Chris Sosa7cd23202013-10-15 17:22:57 -070079 """Class containing common logic between devserver test classes."""
80
81 def setUp(self):
Gilad Arnold08516112014-02-14 13:14:03 -080082 """Creates and populates a test directory, temporary files."""
Chris Sosa7cd23202013-10-15 17:22:57 -070083 self.test_data_path = tempfile.mkdtemp()
84 self.src_dir = os.path.dirname(__file__)
85
86 # Current location of testdata payload.
87 image_src = os.path.join(self.src_dir, TEST_IMAGE_PATH, TEST_IMAGE_NAME)
88
Gilad Arnold08516112014-02-14 13:14:03 -080089 # Copy the payload to the location of the update label.
90 self._CreateLabelAndCopyImage(LABEL, image_src)
Chris Sosa7cd23202013-10-15 17:22:57 -070091
92 # Copy the payload to the location of forced label.
Gilad Arnold08516112014-02-14 13:14:03 -080093 self._CreateLabelAndCopyImage(API_SET_UPDATE_REQUEST, image_src)
Chris Sosa7cd23202013-10-15 17:22:57 -070094
Gilad Arnold08516112014-02-14 13:14:03 -080095 # Allocate temporary files for various devserver outputs.
96 self.pidfile = self._MakeTempFile('pid')
97 self.portfile = self._MakeTempFile('port')
98 self.logfile = self._MakeTempFile('log')
Chris Sosa7cd23202013-10-15 17:22:57 -070099
Gilad Arnold08516112014-02-14 13:14:03 -0800100 # Initialize various runtime values.
101 self.devserver_url = self.port = self.pid = None
Chris Sosa7cd23202013-10-15 17:22:57 -0700102
103 def tearDown(self):
Gilad Arnold08516112014-02-14 13:14:03 -0800104 """Kill the server, remove the test directory and temporary files."""
Chris Sosa7cd23202013-10-15 17:22:57 -0700105 if self.pid:
106 os.kill(self.pid, signal.SIGKILL)
107
Gilad Arnold08516112014-02-14 13:14:03 -0800108 self._RemoveFile(self.pidfile)
109 self._RemoveFile(self.portfile)
110 self._RemoveFile(self.logfile)
111 shutil.rmtree(self.test_data_path)
Chris Sosa7cd23202013-10-15 17:22:57 -0700112
113 # Helper methods begin here.
114
Gilad Arnold08516112014-02-14 13:14:03 -0800115 def _CreateLabelAndCopyImage(self, label, image):
116 """Creates a label location and copies an image to it."""
117 label_dir = os.path.join(self.test_data_path, label)
118 os.makedirs(label_dir)
119 shutil.copy(image, os.path.join(label_dir, TEST_IMAGE_NAME))
120
121 def _MakeTempFile(self, suffix):
122 """Return path of a newly created temporary file."""
123 with tempfile.NamedTemporaryFile(suffix='-devserver-%s' % suffix) as f:
124 name = f.name
125 f.close()
126
127 return name
128
129 def _RemoveFile(self, filename):
130 """Removes a file if it is present."""
131 if os.path.isfile(filename):
132 os.remove(filename)
133
134 def _ReadIntValueFromFile(self, path, desc):
135 """Reads a string from file and returns its conversion into an integer."""
136 if not os.path.isfile(path):
137 raise DevserverFailedToStart('Devserver did not drop %s (%r).' %
138 (desc, path))
139
140 with open(path) as f:
141 value_str = f.read()
142
143 try:
144 return int(value_str)
145 except ValueError:
146 raise DevserverFailedToStart('Devserver did not drop a valid value '
147 'in %s (%r).' % (desc, value_str))
148
149 def _StartServer(self, port=0):
Chris Sosa7cd23202013-10-15 17:22:57 -0700150 """Attempts to start devserver on |port|.
151
Gilad Arnold08516112014-02-14 13:14:03 -0800152 In the default case where port == 0, the server will bind to an arbitrary
153 availble port. If successful, this method will set the devserver's pid
154 (self.pid), actual listening port (self.port) and URL (self.devserver_url).
Chris Sosa7cd23202013-10-15 17:22:57 -0700155
156 Raises:
157 DevserverFailedToStart: If the devserver could not be started.
158 """
159 cmd = [
160 'python',
161 os.path.join(self.src_dir, 'devserver.py'),
162 'devserver.py',
163 '--static_dir', self.test_data_path,
164 '--pidfile', self.pidfile,
Gilad Arnold08516112014-02-14 13:14:03 -0800165 '--portfile', self.portfile,
Chris Sosa7cd23202013-10-15 17:22:57 -0700166 '--port', str(port),
167 '--logfile', self.logfile]
168
169 # Pipe all output. Use logfile to get devserver log.
170 subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
171
Gilad Arnold08516112014-02-14 13:14:03 -0800172 # Wait for devserver to start, determining its actual serving port and URL.
Chris Sosa7cd23202013-10-15 17:22:57 -0700173 current_time = time.time()
174 deadline = current_time + DEVSERVER_START_TIMEOUT
175 while current_time < deadline:
Chris Sosa7cd23202013-10-15 17:22:57 -0700176 try:
Gilad Arnold08516112014-02-14 13:14:03 -0800177 self.port = self._ReadIntValueFromFile(self.portfile, 'portfile')
178 self.devserver_url = 'http://127.0.0.1:%d' % self.port
Chris Sosa7cd23202013-10-15 17:22:57 -0700179 self._MakeRPC(CHECK_HEALTH, timeout=0.1)
180 break
181 except Exception:
Gilad Arnold08516112014-02-14 13:14:03 -0800182 time.sleep(DEVSERVER_START_SLEEP)
183 current_time = time.time()
Chris Sosa7cd23202013-10-15 17:22:57 -0700184 else:
185 raise DevserverFailedToStart('Devserver failed to start within timeout.')
186
Gilad Arnold08516112014-02-14 13:14:03 -0800187 # Retrieve PID.
188 self.pid = self._ReadIntValueFromFile(self.pidfile, 'pidfile')
Chris Sosa7cd23202013-10-15 17:22:57 -0700189
190 def VerifyHandleUpdate(self, label, use_test_payload=True):
191 """Verifies that we can send an update request to the devserver.
192
193 This method verifies (using a fake update_request blob) that the devserver
194 can interpret the payload and give us back the right payload.
195
196 Args:
197 label: Label that update is served from e.g. <board>-release/<version>
198 use_test_payload: If set to true, expects to serve payload under
199 testdata/ and does extra checks i.e. compares hash and content of
200 payload.
Gilad Arnold08516112014-02-14 13:14:03 -0800201
Chris Sosa7cd23202013-10-15 17:22:57 -0700202 Returns:
203 url of the update payload if we verified the update.
204 """
205 update_label = '/'.join([UPDATE, label])
206 response = self._MakeRPC(update_label, data=UPDATE_REQUEST)
207 self.assertNotEqual('', response)
208
209 # Parse the response and check if it contains the right result.
210 dom = minidom.parseString(response)
211 update = dom.getElementsByTagName('updatecheck')[0]
212 expected_static_url = '/'.join([self.devserver_url, STATIC, label])
213 expected_hash = EXPECTED_HASH if use_test_payload else None
214 url = self.VerifyV3Response(update, expected_static_url,
215 expected_hash=expected_hash)
216
217 # Verify the image we download is correct since we already know what it is.
218 if use_test_payload:
219 connection = urllib2.urlopen(url)
220 contents = connection.read()
221 connection.close()
222 self.assertEqual('Developers, developers, developers!\n', contents)
223
224 return url
225
226 def VerifyV3Response(self, update, expected_static_url, expected_hash):
227 """Verifies the update DOM from a v3 response and returns the url."""
228 # Parse the response and check if it contains the right result.
229 urls = update.getElementsByTagName('urls')[0]
230 url = urls.getElementsByTagName('url')[0]
231
232 static_url = url.getAttribute('codebase')
233 # Static url's end in /.
234 self.assertEqual(expected_static_url + '/', static_url)
235
236 manifest = update.getElementsByTagName('manifest')[0]
237 packages = manifest.getElementsByTagName('packages')[0]
238 package = packages.getElementsByTagName('package')[0]
239 filename = package.getAttribute('name')
240 self.assertEqual(TEST_IMAGE_NAME, filename)
241
242 if expected_hash:
243 hash_value = package.getAttribute('hash')
244 self.assertEqual(EXPECTED_HASH, hash_value)
245
246 url = os.path.join(static_url, filename)
247 return url
248
249 def _MakeRPC(self, rpc, data=None, timeout=None, **kwargs):
Gilad Arnold08516112014-02-14 13:14:03 -0800250 """Makes an RPC call to the devserver.
Chris Sosa7cd23202013-10-15 17:22:57 -0700251
252 Args:
Gilad Arnold08516112014-02-14 13:14:03 -0800253 rpc: The function to run on the devserver, e.g. 'stage'.
Chris Sosa7cd23202013-10-15 17:22:57 -0700254 data: Optional post data to send.
255 timeout: Optional timeout to pass to urlopen.
Gilad Arnold08516112014-02-14 13:14:03 -0800256 kwargs: Optional arguments to the function, e.g. artifact_url='foo/bar'.
Chris Sosa7cd23202013-10-15 17:22:57 -0700257
Gilad Arnold08516112014-02-14 13:14:03 -0800258 Returns:
259 The function output.
Chris Sosa7cd23202013-10-15 17:22:57 -0700260 """
261 request = '/'.join([self.devserver_url, rpc])
262 if kwargs:
263 # Join the kwargs to the URL.
264 request += '?' + '&'.join('%s=%s' % item for item in kwargs.iteritems())
265
266 output = None
267 try:
268 # Let's log output for all rpc's without timeouts because we only
269 # use timeouts to check to see if something is up and these checks tend
270 # to be small and so logging it will be extremely repetitive.
271 if not timeout:
272 logging.info('Making request using %s', request)
273
274 connection = urllib2.urlopen(request, data=data, timeout=timeout)
275 output = connection.read()
276 connection.close()
277 except urllib2.HTTPError:
278 raise
279
280 return output
281
282
Gilad Arnold08516112014-02-14 13:14:03 -0800283class AutoStartDevserverTestBase(DevserverTestBase):
284 """Test base class that automatically starts the devserver."""
285
286 def setUp(self):
287 """Initialize everything, then start the server."""
288 super(AutoStartDevserverTestBase, self).setUp()
289 self._StartServer()
290
291
Gilad Arnold7de05f72014-02-14 13:14:20 -0800292class DevserverStartTests(DevserverTestBase):
293 """Test that devserver starts up correctly."""
294
295 def testStartAnyPort(self):
296 """Starts the devserver, have it bind to an arbitrary available port."""
297 self._StartServer()
298
299 def testStartSpecificPort(self):
300 """Starts the devserver with a specific port."""
301 for _ in range(MAX_START_ATTEMPTS):
302 # This is a cheap hack to find an arbitrary unused port: we open a socket
303 # and bind it to port zero, then pull out the actual port number and
304 # close the socket. In all likelihood, this will leave us with an
305 # available port number that we can use for starting the devserver.
306 # However, this heuristic is susceptible to race conditions, hence the
307 # retry loop.
308 s = socket.socket()
309 s.bind(('', 0))
310 # s.getsockname() is definitely callable.
311 # pylint: disable=E1102
312 _, port = s.getsockname()
313 s.close()
314
315 self._StartServer(port=port)
316
317
Gilad Arnold08516112014-02-14 13:14:03 -0800318class DevserverBasicTests(AutoStartDevserverTestBase):
319 """Short running tests for the devserver (no remote deps).
Chris Sosa7cd23202013-10-15 17:22:57 -0700320
321 These are technically not unittests because they depend on being able to
322 start a devserver locally which technically requires external resources so
323 they are lumped with the remote tests here.
324 """
325
326 def testHandleUpdateV3(self):
327 self.VerifyHandleUpdate(label=LABEL)
328
329 def testApiBadSetNextUpdateRequest(self):
330 """Tests sending a bad setnextupdate request."""
331 # Send bad request and ensure it fails...
332 self.assertRaises(urllib2.URLError,
333 self._MakeRPC,
334 '/'.join([API_SET_NEXT_UPDATE, API_TEST_IP_ADDR]))
335
336 def testApiBadSetNextUpdateURL(self):
337 """Tests contacting a bad setnextupdate url."""
338 # Send bad request and ensure it fails...
339 self.assertRaises(urllib2.URLError,
340 self._MakeRPC, API_SET_NEXT_UPDATE)
341
342 def testApiBadHostInfoURL(self):
343 """Tests contacting a bad hostinfo url."""
344 # Host info should be invalid without a specified address.
345 self.assertRaises(urllib2.URLError,
346 self._MakeRPC, API_HOST_INFO)
347
348 def testApiHostInfoAndSetNextUpdate(self):
349 """Tests using the setnextupdate and hostinfo api commands."""
350 # Send setnextupdate command.
351 self._MakeRPC('/'.join([API_SET_NEXT_UPDATE, API_TEST_IP_ADDR]),
352 data=API_SET_UPDATE_REQUEST)
353
354 # Send hostinfo command and verify the setnextupdate worked.
355 response = self._MakeRPC('/'.join([API_HOST_INFO, API_TEST_IP_ADDR]))
356
357 self.assertEqual(
358 json.loads(response)['forced_update_label'], API_SET_UPDATE_REQUEST)
359
360 def testXBuddyLocalAlias(self):
361 """Extensive local image xbuddy unittest.
362
363 This test verifies all the local xbuddy logic by creating a new local folder
364 with the necessary update items and verifies we can use all of them.
365 """
366 update_data = 'TEST UPDATE'
367 image_data = 'TEST IMAGE'
368 stateful_data = 'STATEFUL STUFFS'
369 build_id = 'x86-generic/R32-9999.0.0-a1'
370 xbuddy_path = 'x86-generic/R32-9999.0.0-a1/test'
371 build_dir = os.path.join(self.test_data_path, build_id)
372 os.makedirs(build_dir)
373 test_image_file = os.path.join(build_dir,
374 devserver_constants.TEST_IMAGE_FILE)
375 update_file = os.path.join(build_dir, devserver_constants.UPDATE_FILE)
376 stateful_file = os.path.join(build_dir, devserver_constants.STATEFUL_FILE)
377
378 logging.info('Creating dummy files')
379
380 for item, filename, data in zip(
381 ['full_payload', 'test', 'stateful'],
382 [update_file, test_image_file, stateful_file],
383 [update_data, image_data, stateful_data]):
384 logging.info('Creating file %s', filename)
385 with open(filename, 'w') as fh:
386 fh.write(data)
387
388 xbuddy_path = '/'.join([build_id, item])
389 logging.info('Testing xbuddy path %s', xbuddy_path)
390 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]))
391 self.assertEqual(response, data)
392
393 expected_dir = '/'.join([self.devserver_url, STATIC, build_id])
394 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), return_dir=True)
395 self.assertEqual(response, expected_dir)
396
Yu-Ju Hong51495eb2013-12-12 17:08:43 -0800397 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]),
398 relative_path=True)
399 self.assertEqual(response, build_id)
400
Chris Sosa7cd23202013-10-15 17:22:57 -0700401 xbuddy_path = '/'.join([build_id, 'test'])
402 logging.info('Testing for_update for %s', xbuddy_path)
403 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), for_update=True)
404 expected_path = '/'.join([self.devserver_url, UPDATE, build_id])
405 self.assertTrue(response, expected_path)
406
407 logging.info('Verifying the actual payload data')
408 url = self.VerifyHandleUpdate(build_id, use_test_payload=False)
409 logging.info('Verify the actual content of the update payload')
410 connection = urllib2.urlopen(url)
411 contents = connection.read()
412 connection.close()
413 self.assertEqual(update_data, contents)
414
415 def testPidFile(self):
416 """Test that using a pidfile works correctly."""
417 with open(self.pidfile, 'r') as f:
418 pid = f.read()
419
420 # Let's assert some process information about the devserver.
421 self.assertTrue(pid.isdigit())
422 process = psutil.Process(int(pid))
423 self.assertTrue(process.is_running())
424 self.assertTrue('devserver.py' in process.cmdline)
425
426
Gilad Arnold08516112014-02-14 13:14:03 -0800427class DevserverExtendedTests(AutoStartDevserverTestBase):
Chris Sosa7cd23202013-10-15 17:22:57 -0700428 """Longer running integration tests that test interaction with Google Storage.
429
430 Note: due to the interaction with Google Storage, these tests both require
431 1) runner has access to the Google Storage bucket where builders store builds.
432 2) time. These tests actually download the artifacts needed.
433 """
434
435 def testStageAndUpdate(self):
436 """Tests core autotest workflow where we stage/update with a test payload.
437 """
438 build_id = 'x86-mario-release/R32-4810.0.0'
439 archive_url = 'gs://chromeos-image-archive/%s' % build_id
440
441 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
442 artifacts='full_payload,stateful')
443 self.assertEqual(response, 'False')
444
445 logging.info('Staging update artifacts')
446 self._MakeRPC(STAGE, archive_url=archive_url,
447 artifacts='full_payload,stateful')
448 logging.info('Staging complete. '
449 'Verifying files exist and are staged in the staging '
450 'directory.')
451 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
452 artifacts='full_payload,stateful')
453 self.assertEqual(response, 'True')
454 staged_dir = os.path.join(self.test_data_path, build_id)
455 self.assertTrue(os.path.isdir(staged_dir))
456 self.assertTrue(os.path.exists(
457 os.path.join(staged_dir, devserver_constants.UPDATE_FILE)))
458 self.assertTrue(os.path.exists(
459 os.path.join(staged_dir, devserver_constants.STATEFUL_FILE)))
460
461 logging.info('Verifying we can update using the stage update artifacts.')
462 self.VerifyHandleUpdate(build_id, use_test_payload=False)
463
464 def testStageAutotestAndGetPackages(self):
465 """Another autotest workflow test where we stage/update with a test payload.
466 """
467 build_id = 'x86-mario-release/R32-4810.0.0'
468 archive_url = 'gs://chromeos-image-archive/%s' % build_id
469 autotest_artifacts = 'autotest,test_suites,au_suite'
470 logging.info('Staging autotest artifacts (may take a while).')
471 self._MakeRPC(STAGE, archive_url=archive_url, artifacts=autotest_artifacts)
472
473 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
474 artifacts=autotest_artifacts)
475 self.assertEqual(response, 'True')
476
477 # Verify the files exist and are staged in the staging directory.
478 logging.info('Checking directories exist after we staged the files.')
479 staged_dir = os.path.join(self.test_data_path, build_id)
480 autotest_dir = os.path.join(staged_dir, 'autotest')
481 package_dir = os.path.join(autotest_dir, 'packages')
482 self.assertTrue(os.path.isdir(staged_dir))
483 self.assertTrue(os.path.isdir(autotest_dir))
484 self.assertTrue(os.path.isdir(package_dir))
485
486 control_files = self._MakeRPC(CONTROL_FILES, build=build_id,
487 suite_name='bvt')
488 logging.info('Checking for known control file in bvt suite.')
489 self.assertTrue('client/site_tests/platform_FilePerms/'
490 'control' in control_files)
491
492 def testRemoteXBuddyAlias(self):
493 """Another autotest workflow test where we stage/update with a test payload.
494 """
495 build_id = 'x86-mario-release/R32-4810.0.0'
496 xbuddy_path = 'remote/x86-mario/R32-4810.0.0/full_payload'
497 xbuddy_bad_path = 'remote/x86-mario/R32-9999.9999.9999'
498 logging.info('Staging artifacts using xbuddy.')
499 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), return_dir=True)
500
501 logging.info('Verifying static url returned is valid.')
502 expected_static_url = '/'.join([self.devserver_url, STATIC, build_id])
503 self.assertEqual(response, expected_static_url)
504
505 logging.info('Checking for_update returns an update_url for what we just '
506 'staged.')
507 expected_update_url = '/'.join([self.devserver_url, UPDATE, build_id])
508 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), for_update=True)
509 self.assertEqual(response, expected_update_url)
510
511 logging.info('Now give xbuddy a bad path.')
512 self.assertRaises(urllib2.HTTPError,
513 self._MakeRPC,
514 '/'.join([XBUDDY, xbuddy_bad_path]))
515
Prashanth Ba06d2d22014-03-07 15:35:19 -0800516 def testListImageDir(self):
517 """Verifies that we can list the contents of the image directory."""
518 build_id = 'x86-mario-release/R32-4810.0.0'
519 archive_url = 'gs://chromeos-image-archive/%s' % build_id
520 build_dir = os.path.join(self.test_data_path, build_id)
521 shutil.rmtree(build_dir, ignore_errors=True)
522
523 logging.info('checking for %s on an unstaged build.', LIST_IMAGE_DIR)
524 response = self._MakeRPC(LIST_IMAGE_DIR, archive_url=archive_url)
525 self.assertTrue(archive_url in response and 'not been staged' in response)
526
527 logging.info('Checking for %s on a staged build.', LIST_IMAGE_DIR)
528 fake_file_name = 'fake_file'
529 try:
530 os.makedirs(build_dir)
531 open(os.path.join(build_dir, fake_file_name), 'w').close()
532 except OSError:
533 logging.error('Could not create files to imitate staged content. '
534 'Build dir %s, file %s', build_dir, fake_file_name)
535 raise
536 response = self._MakeRPC(LIST_IMAGE_DIR, archive_url=archive_url)
537 self.assertTrue(fake_file_name in response)
538 shutil.rmtree(build_dir, ignore_errors=True)
Chris Sosa7cd23202013-10-15 17:22:57 -0700539
540if __name__ == '__main__':
541 logging_format = '%(levelname)-8s: %(message)s'
542 logging.basicConfig(level=logging.DEBUG, format=logging_format)
543 unittest.main()