blob: b971a8851d0d708a827abb69258f4fa22719e12a [file] [log] [blame]
Girtsdba6ab22010-10-11 15:53:52 -07001#!/usr/bin/python
2
3# Copyright (c) 2010 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"""Regression tests for devserver."""
8
Chris Sosa65d339b2013-01-21 18:59:21 -08009import json
Chris Sosa6a3697f2013-01-29 16:44:43 -080010from xml.dom import minidom
Girtsdba6ab22010-10-11 15:53:52 -070011import os
Girtsdba6ab22010-10-11 15:53:52 -070012import shutil
Gilad Arnoldabb352e2012-09-23 01:24:27 -070013import signal
Girtsdba6ab22010-10-11 15:53:52 -070014import subprocess
Chris Sosa6a3697f2013-01-29 16:44:43 -080015import tempfile
Girtsdba6ab22010-10-11 15:53:52 -070016import time
17import unittest
18import urllib2
Gilad Arnoldabb352e2012-09-23 01:24:27 -070019
Girtsdba6ab22010-10-11 15:53:52 -070020
21# Paths are relative to this script's base directory.
joychen7c2054a2013-07-25 11:14:07 -070022LABEL = 'devserver'
Zdenek Behan5d21a2a2011-02-12 02:06:01 +010023TEST_IMAGE_PATH = 'testdata/devserver'
Chris Sosa6a3697f2013-01-29 16:44:43 -080024TEST_IMAGE_NAME = 'update.gz'
Zdenek Behan5d21a2a2011-02-12 02:06:01 +010025TEST_IMAGE = TEST_IMAGE_PATH + '/' + TEST_IMAGE_NAME
Jay Srinivasanac69d262012-10-30 19:05:53 -070026EXPECTED_HASH = 'kGcOinJ0vA8vdYX53FN0F5BdwfY='
Girtsdba6ab22010-10-11 15:53:52 -070027
Jay Srinivasanac69d262012-10-30 19:05:53 -070028# Update request based on Omaha v2 protocol format.
29UPDATE_REQUEST = {}
30UPDATE_REQUEST['2.0'] = """<?xml version="1.0" encoding="UTF-8"?>
Greg Spencerc8b59b22011-03-15 14:15:23 -070031<o:gupdate xmlns:o="http://www.google.com/update2/request" version="ChromeOSUpdateEngine-0.1.0.0" updaterversion="ChromeOSUpdateEngine-0.1.0.0" protocol="2.0" ismachine="1">
32 <o:os version="Indy" platform="Chrome OS" sp="0.11.254.2011_03_09_1814_i686"></o:os>
33 <o:app appid="{DEV-BUILD}" version="0.11.254.2011_03_09_1814" lang="en-US" track="developer-build" board="x86-generic" hardware_class="BETA DVT" delta_okay="true">
34 <o:updatecheck></o:updatecheck>
35 <o:event eventtype="3" eventresult="2" previousversion="0.11.216.2011_03_02_1358"></o:event>
36 </o:app>
Girtsdba6ab22010-10-11 15:53:52 -070037</o:gupdate>
38"""
Jay Srinivasanac69d262012-10-30 19:05:53 -070039
40# Update request based on Omaha v3 protocol format.
41UPDATE_REQUEST['3.0'] = """<?xml version="1.0" encoding="UTF-8"?>
42<request version="ChromeOSUpdateEngine-0.1.0.0" updaterversion="ChromeOSUpdateEngine-0.1.0.0" protocol="3.0" ismachine="1">
43 <os version="Indy" platform="Chrome OS" sp="0.11.254.2011_03_09_1814_i686"></os>
44 <app appid="{DEV-BUILD}" version="0.11.254.2011_03_09_1814" lang="en-US" track="developer-build" board="x86-generic" hardware_class="BETA DVT" delta_okay="true">
45 <updatecheck></updatecheck>
46 <event eventtype="3" eventresult="2" previousversion="0.11.216.2011_03_02_1358"></event>
47 </app>
48</request>
49"""
Chris Sosa6a3697f2013-01-29 16:44:43 -080050
Girtsdba6ab22010-10-11 15:53:52 -070051# TODO(girts): use a random available port.
joychen7c2054a2013-07-25 11:14:07 -070052UPDATE_URL = 'http://127.0.0.1:8080/update/devserver'
joychen3b6bac62013-07-12 11:42:49 -070053CHECK_HEALTH_URL = 'http://127.0.0.1:8080/check_health'
joychened64b222013-06-21 16:39:34 -070054STATIC_URL = 'http://127.0.0.1:8080/static/'
joychen7c2054a2013-07-25 11:14:07 -070055SERVE_URL = STATIC_URL + LABEL + '/'
Girtsdba6ab22010-10-11 15:53:52 -070056
Dale Curtisc9aaf3a2011-08-09 15:47:40 -070057API_HOST_INFO_BAD_URL = 'http://127.0.0.1:8080/api/hostinfo/'
58API_HOST_INFO_URL = API_HOST_INFO_BAD_URL + '127.0.0.1'
59
60API_SET_UPDATE_BAD_URL = 'http://127.0.0.1:8080/api/setnextupdate/'
61API_SET_UPDATE_URL = API_SET_UPDATE_BAD_URL + '127.0.0.1'
62
63API_SET_UPDATE_REQUEST = 'new_update-test/the-new-update'
64
joychen3b6bac62013-07-12 11:42:49 -070065DEVSERVER_START_TIMEOUT = 15
Girtsdba6ab22010-10-11 15:53:52 -070066
67class DevserverTest(unittest.TestCase):
68 """Regressions tests for devserver."""
69
70 def setUp(self):
71 """Copies in testing files."""
Girtsdba6ab22010-10-11 15:53:52 -070072
73 # Copy in developer-test.gz, as "static/" directory is hardcoded, and it
74 # would be very hard to change it (static file serving is handled deep
75 # inside webpy).
Chris Sosa6a3697f2013-01-29 16:44:43 -080076 self.test_data_path = tempfile.mkdtemp()
77 self.src_dir = os.path.dirname(__file__)
joychen7c2054a2013-07-25 11:14:07 -070078
79 # Copy the payload to the root of static_dir.
Chris Sosa6a3697f2013-01-29 16:44:43 -080080 self.image_src = os.path.join(self.src_dir, TEST_IMAGE)
81 self.image = os.path.join(self.test_data_path, TEST_IMAGE_NAME)
Zdenek Behan5d21a2a2011-02-12 02:06:01 +010082 shutil.copy(self.image_src, self.image)
joychen7c2054a2013-07-25 11:14:07 -070083
84 # Copy the payload to the location of the update label "devserver."
85 os.makedirs(os.path.join(self.test_data_path, LABEL))
86 shutil.copy(self.image_src, os.path.join(self.test_data_path, LABEL,
87 TEST_IMAGE_NAME))
88
89 # Copy the payload to the location of forced label.
90 os.makedirs(os.path.join(self.test_data_path, API_SET_UPDATE_REQUEST))
91 shutil.copy(self.image_src, os.path.join(self.test_data_path,
92 API_SET_UPDATE_REQUEST,
93 TEST_IMAGE_NAME))
94
joychen3b6bac62013-07-12 11:42:49 -070095 self.devserver_process = self._StartServer()
Girtsdba6ab22010-10-11 15:53:52 -070096
Girtsdba6ab22010-10-11 15:53:52 -070097 def tearDown(self):
98 """Removes testing files."""
Chris Sosa6a3697f2013-01-29 16:44:43 -080099 shutil.rmtree(self.test_data_path)
joychen3b6bac62013-07-12 11:42:49 -0700100 os.kill(self.devserver_process.pid, signal.SIGKILL)
Girtsdba6ab22010-10-11 15:53:52 -0700101
Jay Srinivasanac69d262012-10-30 19:05:53 -0700102 # Helper methods begin here.
Girtsdba6ab22010-10-11 15:53:52 -0700103
Chris Sosa6a3697f2013-01-29 16:44:43 -0800104 def _StartServer(self):
Girtsdba6ab22010-10-11 15:53:52 -0700105 """Starts devserver, returns process."""
106 cmd = [
107 'python',
Chris Sosa6a3697f2013-01-29 16:44:43 -0800108 os.path.join(self.src_dir, 'devserver.py'),
Girtsdba6ab22010-10-11 15:53:52 -0700109 'devserver.py',
joychen7c2054a2013-07-25 11:14:07 -0700110 '--static_dir',
Chris Sosa6a3697f2013-01-29 16:44:43 -0800111 self.test_data_path,
112 ]
113
joychen3b6bac62013-07-12 11:42:49 -0700114 process = subprocess.Popen(cmd,
115 stderr=subprocess.PIPE)
116
117 # wait for devserver to start
118 current_time = time.time()
119 deadline = current_time + DEVSERVER_START_TIMEOUT
120 while current_time < deadline:
121 current_time = time.time()
122 try:
123 urllib2.urlopen(CHECK_HEALTH_URL, timeout=0.05)
124 break
125 except Exception:
126 continue
127 else:
128 self.fail('Devserver failed to start within timeout.')
129
130 return process
Girtsdba6ab22010-10-11 15:53:52 -0700131
Chris Sosa6a3697f2013-01-29 16:44:43 -0800132 def VerifyHandleUpdate(self, protocol):
133 """Tests running the server and getting an update for the given protocol."""
joychen3b6bac62013-07-12 11:42:49 -0700134 request = urllib2.Request(UPDATE_URL, UPDATE_REQUEST[protocol])
135 connection = urllib2.urlopen(request)
136 response = connection.read()
137 connection.close()
138 self.assertNotEqual('', response)
Girtsdba6ab22010-10-11 15:53:52 -0700139
joychen3b6bac62013-07-12 11:42:49 -0700140 # Parse the response and check if it contains the right result.
141 dom = minidom.parseString(response)
142 update = dom.getElementsByTagName('updatecheck')[0]
143 if protocol == '2.0':
144 url = self.VerifyV2Response(update)
145 else:
146 url = self.VerifyV3Response(update)
Girtsdba6ab22010-10-11 15:53:52 -0700147
joychen3b6bac62013-07-12 11:42:49 -0700148 # Try to fetch the image.
149 connection = urllib2.urlopen(url)
150 contents = connection.read()
151 connection.close()
152 self.assertEqual('Developers, developers, developers!\n', contents)
Girtsdba6ab22010-10-11 15:53:52 -0700153
Jay Srinivasanac69d262012-10-30 19:05:53 -0700154 def VerifyV2Response(self, update):
155 """Verifies the update DOM from a v2 response and returns the url."""
156 codebase = update.getAttribute('codebase')
joychen7c2054a2013-07-25 11:14:07 -0700157 self.assertEqual(SERVE_URL + TEST_IMAGE_NAME, codebase)
Jay Srinivasanac69d262012-10-30 19:05:53 -0700158
159 hash_value = update.getAttribute('hash')
160 self.assertEqual(EXPECTED_HASH, hash_value)
Jay Srinivasanac69d262012-10-30 19:05:53 -0700161 return codebase
162
163 def VerifyV3Response(self, update):
164 """Verifies the update DOM from a v3 response and returns the url."""
165 # Parse the response and check if it contains the right result.
166 urls = update.getElementsByTagName('urls')[0]
167 url = urls.getElementsByTagName('url')[0]
168
169 codebase = url.getAttribute('codebase')
joychen7c2054a2013-07-25 11:14:07 -0700170 self.assertEqual(SERVE_URL, codebase)
Jay Srinivasanac69d262012-10-30 19:05:53 -0700171
172 manifest = update.getElementsByTagName('manifest')[0]
173 packages = manifest.getElementsByTagName('packages')[0]
174 package = packages.getElementsByTagName('package')[0]
175 filename = package.getAttribute('name')
176 self.assertEqual(TEST_IMAGE_NAME, filename)
177
178 hash_value = package.getAttribute('hash')
179 self.assertEqual(EXPECTED_HASH, hash_value)
180
181 url = os.path.join(codebase, filename)
182 return url
183
Jay Srinivasanac69d262012-10-30 19:05:53 -0700184 # Tests begin here.
Jay Srinivasanac69d262012-10-30 19:05:53 -0700185 def testHandleUpdateV2(self):
Chris Sosa6a3697f2013-01-29 16:44:43 -0800186 self.VerifyHandleUpdate('2.0')
Zdenek Behan5d21a2a2011-02-12 02:06:01 +0100187
Jay Srinivasanac69d262012-10-30 19:05:53 -0700188 def testHandleUpdateV3(self):
Chris Sosa6a3697f2013-01-29 16:44:43 -0800189 self.VerifyHandleUpdate('3.0')
Zdenek Behan5d21a2a2011-02-12 02:06:01 +0100190
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700191 def testApiBadSetNextUpdateRequest(self):
192 """Tests sending a bad setnextupdate request."""
joychen3b6bac62013-07-12 11:42:49 -0700193 # Send bad request and ensure it fails...
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700194 try:
joychen3b6bac62013-07-12 11:42:49 -0700195 request = urllib2.Request(API_SET_UPDATE_URL, '')
196 connection = urllib2.urlopen(request)
197 connection.read()
198 connection.close()
199 self.fail('Invalid setnextupdate request did not fail!')
200 except urllib2.URLError:
201 pass
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700202
203 def testApiBadSetNextUpdateURL(self):
204 """Tests contacting a bad setnextupdate url."""
joychen3b6bac62013-07-12 11:42:49 -0700205 # Send bad request and ensure it fails...
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700206 try:
joychen3b6bac62013-07-12 11:42:49 -0700207 connection = urllib2.urlopen(API_SET_UPDATE_BAD_URL)
208 connection.read()
209 connection.close()
210 self.fail('Invalid setnextupdate url did not fail!')
211 except urllib2.URLError:
212 pass
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700213
214 def testApiBadHostInfoURL(self):
215 """Tests contacting a bad hostinfo url."""
joychen3b6bac62013-07-12 11:42:49 -0700216 # Send bad request and ensure it fails...
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700217 try:
joychen3b6bac62013-07-12 11:42:49 -0700218 connection = urllib2.urlopen(API_HOST_INFO_BAD_URL)
219 connection.read()
220 connection.close()
221 self.fail('Invalid hostinfo url did not fail!')
222 except urllib2.URLError:
223 pass
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700224
225 def testApiHostInfoAndSetNextUpdate(self):
226 """Tests using the setnextupdate and hostinfo api commands."""
joychen3b6bac62013-07-12 11:42:49 -0700227 # Send setnextupdate command.
228 request = urllib2.Request(API_SET_UPDATE_URL, API_SET_UPDATE_REQUEST)
229 connection = urllib2.urlopen(request)
230 response = connection.read()
231 connection.close()
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700232
joychen3b6bac62013-07-12 11:42:49 -0700233 # Send hostinfo command and verify the setnextupdate worked.
234 connection = urllib2.urlopen(API_HOST_INFO_URL)
235 response = connection.read()
236 connection.close()
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700237
joychen3b6bac62013-07-12 11:42:49 -0700238 self.assertEqual(
239 json.loads(response)['forced_update_label'], API_SET_UPDATE_REQUEST)
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700240
Chris Sosa6a3697f2013-01-29 16:44:43 -0800241
Girtsdba6ab22010-10-11 15:53:52 -0700242if __name__ == '__main__':
243 unittest.main()