blob: 14a2c2a04dd76869d095529e5db9b31d14e52d1a [file] [log] [blame]
Luis Hector Chaveza1518052018-06-14 08:19:34 -07001#!/usr/bin/env python2
2# -*- coding: utf-8 -*-
Chris Sosa7cd23202013-10-15 17:22:57 -07003# 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
Chris Sosa7cd23202013-10-15 17:22:57 -070018import os
Chris Sosa7cd23202013-10-15 17:22:57 -070019import shutil
Gilad Arnold7de05f72014-02-14 13:14:20 -080020import socket
Chris Sosa7cd23202013-10-15 17:22:57 -070021import subprocess
Amin Hassaniba847c82019-12-06 16:30:56 -080022import sys
Chris Sosa7cd23202013-10-15 17:22:57 -070023import tempfile
24import time
25import unittest
Chris Sosa7cd23202013-10-15 17:22:57 -070026
Amin Hassani0b4843b2019-09-25 16:38:56 -070027from string import Template
28
29from xml.dom import minidom
30
Amin Hassani6eec8792020-01-09 14:06:48 -080031import requests
32
Amin Hassani469f5702019-10-21 15:35:06 -070033from six.moves import urllib
34
35import psutil # pylint: disable=import-error
36
Amin Hassani469f5702019-10-21 15:35:06 -070037import setup_chromite # pylint: disable=unused-import
Luis Hector Chaveza1518052018-06-14 08:19:34 -070038from chromite.lib import cros_logging as logging
Achuith Bhandarkar662fb722019-10-31 16:12:49 -070039from chromite.lib.xbuddy import devserver_constants
Luis Hector Chaveza1518052018-06-14 08:19:34 -070040
Chris Sosa7cd23202013-10-15 17:22:57 -070041
42# Paths are relative to this script's base directory.
43LABEL = 'devserver'
44TEST_IMAGE_PATH = 'testdata/devserver'
Amin Hassani0b4843b2019-09-25 16:38:56 -070045TEST_UPDATE_PAYLOAD_NAME = 'update.gz'
46TEST_UPDATE_PAYLOAD_METADATA_NAME = 'update.gz.json'
Chris Sosa7cd23202013-10-15 17:22:57 -070047
48# Update request based on Omaha v3 protocol format.
Amin Hassani0b4843b2019-09-25 16:38:56 -070049UPDATE_REQUEST = Template("""<?xml version="1.0" encoding="UTF-8"?>
Amin Hassani495f1de2019-02-26 11:13:39 -080050<request protocol="3.0" updater="ChromeOSUpdateEngine" updaterversion="0.1.0.0" ismachine="1">
Chris Sosa7cd23202013-10-15 17:22:57 -070051 <os version="Indy" platform="Chrome OS" sp="0.11.254.2011_03_09_1814_i686"></os>
Amin Hassani0b4843b2019-09-25 16:38:56 -070052 <app appid="$appid" version="11.254.2011_03_09_1814" lang="en-US" track="developer-build" board="x86-generic" hardware_class="BETA DVT" delta_okay="true">
Chris Sosa7cd23202013-10-15 17:22:57 -070053 <updatecheck></updatecheck>
54 </app>
55</request>
Amin Hassani0b4843b2019-09-25 16:38:56 -070056""")
Chris Sosa7cd23202013-10-15 17:22:57 -070057
58# RPC constants.
59STAGE = 'stage'
60IS_STAGED = 'is_staged'
61STATIC = 'static'
62UPDATE = 'update'
63CHECK_HEALTH = 'check_health'
64CONTROL_FILES = 'controlfiles'
65XBUDDY = 'xbuddy'
Prashanth Ba06d2d22014-03-07 15:35:19 -080066LIST_IMAGE_DIR = 'list_image_dir'
Chris Sosa7cd23202013-10-15 17:22:57 -070067
68# API rpcs and constants.
Chris Sosa7cd23202013-10-15 17:22:57 -070069API_SET_UPDATE_REQUEST = 'new_update-test/the-new-update'
70API_TEST_IP_ADDR = '127.0.0.1'
71
72DEVSERVER_START_TIMEOUT = 15
Gilad Arnold08516112014-02-14 13:14:03 -080073DEVSERVER_START_SLEEP = 1
Gilad Arnold7de05f72014-02-14 13:14:20 -080074MAX_START_ATTEMPTS = 5
Chris Sosa7cd23202013-10-15 17:22:57 -070075
76
77class DevserverFailedToStart(Exception):
78 """Raised if we could not start the devserver."""
79
80
Gilad Arnold08516112014-02-14 13:14:03 -080081class DevserverTestBase(unittest.TestCase):
Chris Sosa7cd23202013-10-15 17:22:57 -070082 """Class containing common logic between devserver test classes."""
83
84 def setUp(self):
Gilad Arnold08516112014-02-14 13:14:03 -080085 """Creates and populates a test directory, temporary files."""
Chris Sosa7cd23202013-10-15 17:22:57 -070086 self.test_data_path = tempfile.mkdtemp()
87 self.src_dir = os.path.dirname(__file__)
88
Gilad Arnold08516112014-02-14 13:14:03 -080089 # Copy the payload to the location of the update label.
Amin Hassani0b4843b2019-09-25 16:38:56 -070090 self._CreateLabelAndCopyUpdatePayloadFiles(LABEL)
Chris Sosa7cd23202013-10-15 17:22:57 -070091
92 # Copy the payload to the location of forced label.
Amin Hassani0b4843b2019-09-25 16:38:56 -070093 self._CreateLabelAndCopyUpdatePayloadFiles(API_SET_UPDATE_REQUEST)
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
Amin Hassaniba847c82019-12-06 16:30:56 -0800102 self.devserver = None
Chris Sosa7cd23202013-10-15 17:22:57 -0700103
104 def tearDown(self):
Gilad Arnold08516112014-02-14 13:14:03 -0800105 """Kill the server, remove the test directory and temporary files."""
Amin Hassaniba847c82019-12-06 16:30:56 -0800106
107 self._StopServer()
Chris Sosa7cd23202013-10-15 17:22:57 -0700108
Gilad Arnold08516112014-02-14 13:14:03 -0800109 self._RemoveFile(self.pidfile)
110 self._RemoveFile(self.portfile)
Amin Hassaniba847c82019-12-06 16:30:56 -0800111 # If the unittest did not succeed, print out the devserver log.
112 if sys.exc_info() != (None, None, None):
113 with open(self.logfile, 'r') as f:
114 logging.info('--- BEGINNING OF DEVSERVER LOG ---')
115 logging.info(f.read())
116 logging.info('--- ENDING OF DEVSERVER LOG ---')
Gilad Arnold08516112014-02-14 13:14:03 -0800117 self._RemoveFile(self.logfile)
118 shutil.rmtree(self.test_data_path)
Chris Sosa7cd23202013-10-15 17:22:57 -0700119
120 # Helper methods begin here.
121
Amin Hassani0b4843b2019-09-25 16:38:56 -0700122 def _CreateLabelAndCopyUpdatePayloadFiles(self, label):
Gilad Arnold08516112014-02-14 13:14:03 -0800123 """Creates a label location and copies an image to it."""
Amin Hassani0b4843b2019-09-25 16:38:56 -0700124 update_dir = os.path.join(self.src_dir, TEST_IMAGE_PATH)
Gilad Arnold08516112014-02-14 13:14:03 -0800125 label_dir = os.path.join(self.test_data_path, label)
126 os.makedirs(label_dir)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700127 for name in (TEST_UPDATE_PAYLOAD_NAME, TEST_UPDATE_PAYLOAD_METADATA_NAME):
128 shutil.copy(os.path.join(update_dir, name), label_dir)
Gilad Arnold08516112014-02-14 13:14:03 -0800129
130 def _MakeTempFile(self, suffix):
131 """Return path of a newly created temporary file."""
132 with tempfile.NamedTemporaryFile(suffix='-devserver-%s' % suffix) as f:
133 name = f.name
134 f.close()
135
136 return name
137
138 def _RemoveFile(self, filename):
139 """Removes a file if it is present."""
140 if os.path.isfile(filename):
141 os.remove(filename)
142
143 def _ReadIntValueFromFile(self, path, desc):
144 """Reads a string from file and returns its conversion into an integer."""
145 if not os.path.isfile(path):
146 raise DevserverFailedToStart('Devserver did not drop %s (%r).' %
147 (desc, path))
148
149 with open(path) as f:
150 value_str = f.read()
151
152 try:
153 return int(value_str)
154 except ValueError:
155 raise DevserverFailedToStart('Devserver did not drop a valid value '
156 'in %s (%r).' % (desc, value_str))
157
158 def _StartServer(self, port=0):
Chris Sosa7cd23202013-10-15 17:22:57 -0700159 """Attempts to start devserver on |port|.
160
Gilad Arnold08516112014-02-14 13:14:03 -0800161 In the default case where port == 0, the server will bind to an arbitrary
Dan Shi2f136862016-02-11 15:38:38 -0800162 available port. If successful, this method will set the devserver's pid
Gilad Arnold08516112014-02-14 13:14:03 -0800163 (self.pid), actual listening port (self.port) and URL (self.devserver_url).
Chris Sosa7cd23202013-10-15 17:22:57 -0700164
165 Raises:
166 DevserverFailedToStart: If the devserver could not be started.
167 """
168 cmd = [
Chris Sosa7cd23202013-10-15 17:22:57 -0700169 os.path.join(self.src_dir, 'devserver.py'),
Chris Sosa7cd23202013-10-15 17:22:57 -0700170 '--static_dir', self.test_data_path,
171 '--pidfile', self.pidfile,
Gilad Arnold08516112014-02-14 13:14:03 -0800172 '--portfile', self.portfile,
Chris Sosa7cd23202013-10-15 17:22:57 -0700173 '--port', str(port),
Amin Hassaniba847c82019-12-06 16:30:56 -0800174 '--logfile', self.logfile,
175 ]
Chris Sosa7cd23202013-10-15 17:22:57 -0700176
177 # Pipe all output. Use logfile to get devserver log.
Amin Hassaniba847c82019-12-06 16:30:56 -0800178 self.devserver = subprocess.Popen(cmd, stderr=subprocess.PIPE,
179 stdout=subprocess.PIPE)
Chris Sosa7cd23202013-10-15 17:22:57 -0700180
Gilad Arnold08516112014-02-14 13:14:03 -0800181 # Wait for devserver to start, determining its actual serving port and URL.
Chris Sosa7cd23202013-10-15 17:22:57 -0700182 current_time = time.time()
183 deadline = current_time + DEVSERVER_START_TIMEOUT
Amin Hassaniba847c82019-12-06 16:30:56 -0800184 error = None
Chris Sosa7cd23202013-10-15 17:22:57 -0700185 while current_time < deadline:
Chris Sosa7cd23202013-10-15 17:22:57 -0700186 try:
Gilad Arnold08516112014-02-14 13:14:03 -0800187 self.port = self._ReadIntValueFromFile(self.portfile, 'portfile')
188 self.devserver_url = 'http://127.0.0.1:%d' % self.port
Gabe Black3b567202015-09-23 14:07:59 -0700189 self._MakeRPC(CHECK_HEALTH, timeout=1)
Chris Sosa7cd23202013-10-15 17:22:57 -0700190 break
Amin Hassaniba847c82019-12-06 16:30:56 -0800191 except Exception as e:
192 error = e
Gilad Arnold08516112014-02-14 13:14:03 -0800193 time.sleep(DEVSERVER_START_SLEEP)
194 current_time = time.time()
Chris Sosa7cd23202013-10-15 17:22:57 -0700195 else:
Amin Hassaniba847c82019-12-06 16:30:56 -0800196 raise DevserverFailedToStart(
197 'Devserver failed to start within timeout with error: %s' % error)
Chris Sosa7cd23202013-10-15 17:22:57 -0700198
Gilad Arnold08516112014-02-14 13:14:03 -0800199 # Retrieve PID.
200 self.pid = self._ReadIntValueFromFile(self.pidfile, 'pidfile')
Chris Sosa7cd23202013-10-15 17:22:57 -0700201
Amin Hassaniba847c82019-12-06 16:30:56 -0800202 def _StopServer(self):
203 """Stops the current running devserver."""
204 if not self.pid:
205 return
206
207 self.devserver.terminate()
208
209 # Just to flush the stdout/stderr so python3 doesn't complain about the
210 # unclosed file.
211 self.devserver.communicate()
212
213 self.devserver.wait()
214
215 self.pid = None
216 self.devserver = None
217
218
Amin Hassani0b4843b2019-09-25 16:38:56 -0700219 def VerifyHandleUpdate(self, label, use_test_payload=True,
220 appid='{DEV-BUILD}'):
Chris Sosa7cd23202013-10-15 17:22:57 -0700221 """Verifies that we can send an update request to the devserver.
222
223 This method verifies (using a fake update_request blob) that the devserver
224 can interpret the payload and give us back the right payload.
225
226 Args:
227 label: Label that update is served from e.g. <board>-release/<version>
228 use_test_payload: If set to true, expects to serve payload under
229 testdata/ and does extra checks i.e. compares hash and content of
230 payload.
Amin Hassaniba847c82019-12-06 16:30:56 -0800231 appid: The APP ID of the board.
Gilad Arnold08516112014-02-14 13:14:03 -0800232
Chris Sosa7cd23202013-10-15 17:22:57 -0700233 Returns:
234 url of the update payload if we verified the update.
235 """
236 update_label = '/'.join([UPDATE, label])
Amin Hassani0b4843b2019-09-25 16:38:56 -0700237 response = self._MakeRPC(
Amin Hassani6eec8792020-01-09 14:06:48 -0800238 update_label, data=UPDATE_REQUEST.substitute({'appid': appid}),
239 critical_update=True)
Chris Sosa7cd23202013-10-15 17:22:57 -0700240 self.assertNotEqual('', response)
Amin Hassani6eec8792020-01-09 14:06:48 -0800241 self.assertIn('deadline="now"', response)
Chris Sosa7cd23202013-10-15 17:22:57 -0700242
243 # Parse the response and check if it contains the right result.
244 dom = minidom.parseString(response)
245 update = dom.getElementsByTagName('updatecheck')[0]
246 expected_static_url = '/'.join([self.devserver_url, STATIC, label])
Amin Hassani0b4843b2019-09-25 16:38:56 -0700247 url = self.VerifyV3Response(update, expected_static_url)
Chris Sosa7cd23202013-10-15 17:22:57 -0700248
249 # Verify the image we download is correct since we already know what it is.
250 if use_test_payload:
Amin Hassani469f5702019-10-21 15:35:06 -0700251 connection = urllib.request.urlopen(url)
Amin Hassaniba847c82019-12-06 16:30:56 -0800252 contents = connection.read().decode('utf-8')
Chris Sosa7cd23202013-10-15 17:22:57 -0700253 connection.close()
254 self.assertEqual('Developers, developers, developers!\n', contents)
255
256 return url
257
Amin Hassani0b4843b2019-09-25 16:38:56 -0700258 def VerifyV3Response(self, update, expected_static_url):
Chris Sosa7cd23202013-10-15 17:22:57 -0700259 """Verifies the update DOM from a v3 response and returns the url."""
260 # Parse the response and check if it contains the right result.
261 urls = update.getElementsByTagName('urls')[0]
262 url = urls.getElementsByTagName('url')[0]
263
264 static_url = url.getAttribute('codebase')
265 # Static url's end in /.
266 self.assertEqual(expected_static_url + '/', static_url)
267
268 manifest = update.getElementsByTagName('manifest')[0]
269 packages = manifest.getElementsByTagName('packages')[0]
270 package = packages.getElementsByTagName('package')[0]
271 filename = package.getAttribute('name')
Amin Hassani0b4843b2019-09-25 16:38:56 -0700272 self.assertEqual(TEST_UPDATE_PAYLOAD_NAME, filename)
Chris Sosa7cd23202013-10-15 17:22:57 -0700273
Amin Hassaniba847c82019-12-06 16:30:56 -0800274 return os.path.join(static_url, filename)
Chris Sosa7cd23202013-10-15 17:22:57 -0700275
276 def _MakeRPC(self, rpc, data=None, timeout=None, **kwargs):
Gilad Arnold08516112014-02-14 13:14:03 -0800277 """Makes an RPC call to the devserver.
Chris Sosa7cd23202013-10-15 17:22:57 -0700278
279 Args:
Gilad Arnold08516112014-02-14 13:14:03 -0800280 rpc: The function to run on the devserver, e.g. 'stage'.
Chris Sosa7cd23202013-10-15 17:22:57 -0700281 data: Optional post data to send.
282 timeout: Optional timeout to pass to urlopen.
Gilad Arnold08516112014-02-14 13:14:03 -0800283 kwargs: Optional arguments to the function, e.g. artifact_url='foo/bar'.
Chris Sosa7cd23202013-10-15 17:22:57 -0700284
Gilad Arnold08516112014-02-14 13:14:03 -0800285 Returns:
286 The function output.
Chris Sosa7cd23202013-10-15 17:22:57 -0700287 """
288 request = '/'.join([self.devserver_url, rpc])
289 if kwargs:
290 # Join the kwargs to the URL.
Amin Hassani6eec8792020-01-09 14:06:48 -0800291 request += '?' + '&'.join('%s=%s' % (k, v) for k, v in kwargs.items())
Chris Sosa7cd23202013-10-15 17:22:57 -0700292
Amin Hassani6eec8792020-01-09 14:06:48 -0800293 response = (requests.post(request, data=data, timeout=timeout) if data
294 else requests.get(request, timeout=timeout))
295 response.raise_for_status()
296 return response.text
Chris Sosa7cd23202013-10-15 17:22:57 -0700297
298
Gilad Arnold08516112014-02-14 13:14:03 -0800299class AutoStartDevserverTestBase(DevserverTestBase):
300 """Test base class that automatically starts the devserver."""
301
302 def setUp(self):
303 """Initialize everything, then start the server."""
304 super(AutoStartDevserverTestBase, self).setUp()
305 self._StartServer()
306
307
Gilad Arnold7de05f72014-02-14 13:14:20 -0800308class DevserverStartTests(DevserverTestBase):
309 """Test that devserver starts up correctly."""
310
311 def testStartAnyPort(self):
312 """Starts the devserver, have it bind to an arbitrary available port."""
313 self._StartServer()
314
315 def testStartSpecificPort(self):
316 """Starts the devserver with a specific port."""
317 for _ in range(MAX_START_ATTEMPTS):
318 # This is a cheap hack to find an arbitrary unused port: we open a socket
319 # and bind it to port zero, then pull out the actual port number and
320 # close the socket. In all likelihood, this will leave us with an
321 # available port number that we can use for starting the devserver.
322 # However, this heuristic is susceptible to race conditions, hence the
323 # retry loop.
324 s = socket.socket()
325 s.bind(('', 0))
Gilad Arnold7de05f72014-02-14 13:14:20 -0800326 _, port = s.getsockname()
327 s.close()
328
329 self._StartServer(port=port)
Amin Hassaniba847c82019-12-06 16:30:56 -0800330 self._StopServer()
Gilad Arnold7de05f72014-02-14 13:14:20 -0800331
332
Gilad Arnold08516112014-02-14 13:14:03 -0800333class DevserverBasicTests(AutoStartDevserverTestBase):
334 """Short running tests for the devserver (no remote deps).
Chris Sosa7cd23202013-10-15 17:22:57 -0700335
336 These are technically not unittests because they depend on being able to
337 start a devserver locally which technically requires external resources so
338 they are lumped with the remote tests here.
339 """
340
341 def testHandleUpdateV3(self):
342 self.VerifyHandleUpdate(label=LABEL)
343
Chris Sosa7cd23202013-10-15 17:22:57 -0700344 def testXBuddyLocalAlias(self):
345 """Extensive local image xbuddy unittest.
346
347 This test verifies all the local xbuddy logic by creating a new local folder
348 with the necessary update items and verifies we can use all of them.
349 """
Chris Sosa7cd23202013-10-15 17:22:57 -0700350 build_id = 'x86-generic/R32-9999.0.0-a1'
351 xbuddy_path = 'x86-generic/R32-9999.0.0-a1/test'
352 build_dir = os.path.join(self.test_data_path, build_id)
353 os.makedirs(build_dir)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700354
355 # Writing dummy files.
356 image_data = 'TEST IMAGE'
Chris Sosa7cd23202013-10-15 17:22:57 -0700357 test_image_file = os.path.join(build_dir,
358 devserver_constants.TEST_IMAGE_FILE)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700359 with open(test_image_file, 'w') as f:
360 f.write(image_data)
361
362 stateful_data = 'STATEFUL STUFFS'
Chris Sosa7cd23202013-10-15 17:22:57 -0700363 stateful_file = os.path.join(build_dir, devserver_constants.STATEFUL_FILE)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700364 with open(stateful_file, 'w') as f:
365 f.write(stateful_data)
Chris Sosa7cd23202013-10-15 17:22:57 -0700366
Amin Hassani0b4843b2019-09-25 16:38:56 -0700367 update_dir = os.path.join(self.src_dir, TEST_IMAGE_PATH)
368 for name in (TEST_UPDATE_PAYLOAD_NAME, TEST_UPDATE_PAYLOAD_METADATA_NAME):
369 shutil.copy(os.path.join(update_dir, name), build_dir)
370 with open(os.path.join(build_dir, TEST_UPDATE_PAYLOAD_NAME), 'r') as f:
371 update_data = f.read()
Chris Sosa7cd23202013-10-15 17:22:57 -0700372
Amin Hassani0b4843b2019-09-25 16:38:56 -0700373 for item, data in zip(['full_payload', 'test', 'stateful'],
374 [update_data, image_data, stateful_data]):
Chris Sosa7cd23202013-10-15 17:22:57 -0700375
376 xbuddy_path = '/'.join([build_id, item])
377 logging.info('Testing xbuddy path %s', xbuddy_path)
378 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]))
379 self.assertEqual(response, data)
380
381 expected_dir = '/'.join([self.devserver_url, STATIC, build_id])
382 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), return_dir=True)
383 self.assertEqual(response, expected_dir)
384
Yu-Ju Hong51495eb2013-12-12 17:08:43 -0800385 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]),
386 relative_path=True)
387 self.assertEqual(response, build_id)
388
Chris Sosa7cd23202013-10-15 17:22:57 -0700389 xbuddy_path = '/'.join([build_id, 'test'])
390 logging.info('Testing for_update for %s', xbuddy_path)
391 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), for_update=True)
392 expected_path = '/'.join([self.devserver_url, UPDATE, build_id])
393 self.assertTrue(response, expected_path)
394
395 logging.info('Verifying the actual payload data')
396 url = self.VerifyHandleUpdate(build_id, use_test_payload=False)
397 logging.info('Verify the actual content of the update payload')
Amin Hassani469f5702019-10-21 15:35:06 -0700398 connection = urllib.request.urlopen(url)
Amin Hassaniba847c82019-12-06 16:30:56 -0800399 contents = connection.read().decode('utf-8')
Chris Sosa7cd23202013-10-15 17:22:57 -0700400 connection.close()
401 self.assertEqual(update_data, contents)
402
403 def testPidFile(self):
404 """Test that using a pidfile works correctly."""
405 with open(self.pidfile, 'r') as f:
406 pid = f.read()
Chris Sosa7cd23202013-10-15 17:22:57 -0700407 # Let's assert some process information about the devserver.
Dan Shi2f136862016-02-11 15:38:38 -0800408 self.assertTrue(pid.strip().isdigit())
Chris Sosa7cd23202013-10-15 17:22:57 -0700409 process = psutil.Process(int(pid))
410 self.assertTrue(process.is_running())
Amin Hassaniba847c82019-12-06 16:30:56 -0800411 self.assertIn('./devserver.py', process.cmdline())
Chris Sosa7cd23202013-10-15 17:22:57 -0700412
Gilad Arnold08516112014-02-14 13:14:03 -0800413class DevserverExtendedTests(AutoStartDevserverTestBase):
Chris Sosa7cd23202013-10-15 17:22:57 -0700414 """Longer running integration tests that test interaction with Google Storage.
415
416 Note: due to the interaction with Google Storage, these tests both require
417 1) runner has access to the Google Storage bucket where builders store builds.
418 2) time. These tests actually download the artifacts needed.
419 """
420
421 def testStageAndUpdate(self):
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700422 """Tests core stage/update autotest workflow where with a test payload."""
Amin Hassani0b4843b2019-09-25 16:38:56 -0700423 build_id = 'eve-release/R78-12499.0.0'
Chris Sosa7cd23202013-10-15 17:22:57 -0700424 archive_url = 'gs://chromeos-image-archive/%s' % build_id
425
426 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
427 artifacts='full_payload,stateful')
428 self.assertEqual(response, 'False')
429
430 logging.info('Staging update artifacts')
431 self._MakeRPC(STAGE, archive_url=archive_url,
432 artifacts='full_payload,stateful')
433 logging.info('Staging complete. '
434 'Verifying files exist and are staged in the staging '
435 'directory.')
436 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
437 artifacts='full_payload,stateful')
438 self.assertEqual(response, 'True')
439 staged_dir = os.path.join(self.test_data_path, build_id)
440 self.assertTrue(os.path.isdir(staged_dir))
441 self.assertTrue(os.path.exists(
442 os.path.join(staged_dir, devserver_constants.UPDATE_FILE)))
443 self.assertTrue(os.path.exists(
Amin Hassani0b4843b2019-09-25 16:38:56 -0700444 os.path.join(staged_dir, devserver_constants.UPDATE_METADATA_FILE)))
445 self.assertTrue(os.path.exists(
Chris Sosa7cd23202013-10-15 17:22:57 -0700446 os.path.join(staged_dir, devserver_constants.STATEFUL_FILE)))
447
448 logging.info('Verifying we can update using the stage update artifacts.')
Amin Hassani0b4843b2019-09-25 16:38:56 -0700449 self.VerifyHandleUpdate(build_id, use_test_payload=False,
450 appid='{01906EA2-3EB2-41F1-8F62-F0B7120EFD2E}')
Chris Sosa7cd23202013-10-15 17:22:57 -0700451
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700452 @unittest.skip('crbug.com/640063 Broken test.')
Chris Sosa7cd23202013-10-15 17:22:57 -0700453 def testStageAutotestAndGetPackages(self):
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700454 """Another stage/update autotest workflow test with a test payload."""
455 build_id = 'eve-release/R69-10782.0.0'
Chris Sosa7cd23202013-10-15 17:22:57 -0700456 archive_url = 'gs://chromeos-image-archive/%s' % build_id
457 autotest_artifacts = 'autotest,test_suites,au_suite'
458 logging.info('Staging autotest artifacts (may take a while).')
459 self._MakeRPC(STAGE, archive_url=archive_url, artifacts=autotest_artifacts)
460
461 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
462 artifacts=autotest_artifacts)
463 self.assertEqual(response, 'True')
464
465 # Verify the files exist and are staged in the staging directory.
466 logging.info('Checking directories exist after we staged the files.')
467 staged_dir = os.path.join(self.test_data_path, build_id)
468 autotest_dir = os.path.join(staged_dir, 'autotest')
469 package_dir = os.path.join(autotest_dir, 'packages')
470 self.assertTrue(os.path.isdir(staged_dir))
471 self.assertTrue(os.path.isdir(autotest_dir))
472 self.assertTrue(os.path.isdir(package_dir))
473
474 control_files = self._MakeRPC(CONTROL_FILES, build=build_id,
475 suite_name='bvt')
476 logging.info('Checking for known control file in bvt suite.')
Amin Hassaniba847c82019-12-06 16:30:56 -0800477 self.assertIn('client/site_tests/platform_FilePerms/control', control_files)
Chris Sosa7cd23202013-10-15 17:22:57 -0700478
479 def testRemoteXBuddyAlias(self):
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700480 """Another stage/update autotest workflow test with a test payload."""
481 build_id = 'eve-release/R69-10782.0.0'
482 xbuddy_path = 'remote/eve/R69-10782.0.0/full_payload'
483 xbuddy_bad_path = 'remote/eve/R32-9999.9999.9999'
Chris Sosa7cd23202013-10-15 17:22:57 -0700484 logging.info('Staging artifacts using xbuddy.')
485 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), return_dir=True)
486
487 logging.info('Verifying static url returned is valid.')
488 expected_static_url = '/'.join([self.devserver_url, STATIC, build_id])
489 self.assertEqual(response, expected_static_url)
490
491 logging.info('Checking for_update returns an update_url for what we just '
492 'staged.')
493 expected_update_url = '/'.join([self.devserver_url, UPDATE, build_id])
494 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), for_update=True)
495 self.assertEqual(response, expected_update_url)
496
497 logging.info('Now give xbuddy a bad path.')
Amin Hassani6eec8792020-01-09 14:06:48 -0800498 self.assertRaises(requests.exceptions.RequestException,
Chris Sosa7cd23202013-10-15 17:22:57 -0700499 self._MakeRPC,
500 '/'.join([XBUDDY, xbuddy_bad_path]))
501
Prashanth Ba06d2d22014-03-07 15:35:19 -0800502 def testListImageDir(self):
503 """Verifies that we can list the contents of the image directory."""
504 build_id = 'x86-mario-release/R32-4810.0.0'
505 archive_url = 'gs://chromeos-image-archive/%s' % build_id
506 build_dir = os.path.join(self.test_data_path, build_id)
507 shutil.rmtree(build_dir, ignore_errors=True)
508
509 logging.info('checking for %s on an unstaged build.', LIST_IMAGE_DIR)
510 response = self._MakeRPC(LIST_IMAGE_DIR, archive_url=archive_url)
Amin Hassaniba847c82019-12-06 16:30:56 -0800511 self.assertIn(archive_url, response)
512 self.assertIn('not been staged', response)
Prashanth Ba06d2d22014-03-07 15:35:19 -0800513
514 logging.info('Checking for %s on a staged build.', LIST_IMAGE_DIR)
515 fake_file_name = 'fake_file'
516 try:
517 os.makedirs(build_dir)
518 open(os.path.join(build_dir, fake_file_name), 'w').close()
519 except OSError:
520 logging.error('Could not create files to imitate staged content. '
521 'Build dir %s, file %s', build_dir, fake_file_name)
522 raise
523 response = self._MakeRPC(LIST_IMAGE_DIR, archive_url=archive_url)
Amin Hassaniba847c82019-12-06 16:30:56 -0800524 self.assertIn(fake_file_name, response)
Prashanth Ba06d2d22014-03-07 15:35:19 -0800525 shutil.rmtree(build_dir, ignore_errors=True)
Chris Sosa7cd23202013-10-15 17:22:57 -0700526
Amin Hassani28df4212019-10-28 10:16:50 -0700527
Chris Sosa7cd23202013-10-15 17:22:57 -0700528if __name__ == '__main__':
Chris Sosa7cd23202013-10-15 17:22:57 -0700529 unittest.main()