blob: 302d521acf132ce380de35386150c5721bf07c2a [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.
Zdenek Behan5d21a2a2011-02-12 02:06:01 +010022TEST_IMAGE_PATH = 'testdata/devserver'
Chris Sosa6a3697f2013-01-29 16:44:43 -080023TEST_IMAGE_NAME = 'update.gz'
Zdenek Behan5d21a2a2011-02-12 02:06:01 +010024TEST_IMAGE = TEST_IMAGE_PATH + '/' + TEST_IMAGE_NAME
Jay Srinivasanac69d262012-10-30 19:05:53 -070025EXPECTED_HASH = 'kGcOinJ0vA8vdYX53FN0F5BdwfY='
Girtsdba6ab22010-10-11 15:53:52 -070026
Jay Srinivasanac69d262012-10-30 19:05:53 -070027# Update request based on Omaha v2 protocol format.
28UPDATE_REQUEST = {}
29UPDATE_REQUEST['2.0'] = """<?xml version="1.0" encoding="UTF-8"?>
Greg Spencerc8b59b22011-03-15 14:15:23 -070030<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">
31 <o:os version="Indy" platform="Chrome OS" sp="0.11.254.2011_03_09_1814_i686"></o:os>
32 <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">
33 <o:updatecheck></o:updatecheck>
34 <o:event eventtype="3" eventresult="2" previousversion="0.11.216.2011_03_02_1358"></o:event>
35 </o:app>
Girtsdba6ab22010-10-11 15:53:52 -070036</o:gupdate>
37"""
Jay Srinivasanac69d262012-10-30 19:05:53 -070038
39# Update request based on Omaha v3 protocol format.
40UPDATE_REQUEST['3.0'] = """<?xml version="1.0" encoding="UTF-8"?>
41<request version="ChromeOSUpdateEngine-0.1.0.0" updaterversion="ChromeOSUpdateEngine-0.1.0.0" protocol="3.0" ismachine="1">
42 <os version="Indy" platform="Chrome OS" sp="0.11.254.2011_03_09_1814_i686"></os>
43 <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">
44 <updatecheck></updatecheck>
45 <event eventtype="3" eventresult="2" previousversion="0.11.216.2011_03_02_1358"></event>
46 </app>
47</request>
48"""
Chris Sosa6a3697f2013-01-29 16:44:43 -080049
Girtsdba6ab22010-10-11 15:53:52 -070050# TODO(girts): use a random available port.
51UPDATE_URL = 'http://127.0.0.1:8080/update'
Chris Sosa6a3697f2013-01-29 16:44:43 -080052STATIC_URL = 'http://127.0.0.1:8080/static/archive/'
joychen3b6bac62013-07-12 11:42:49 -070053CHECK_HEALTH_URL = 'http://127.0.0.1:8080/check_health'
Girtsdba6ab22010-10-11 15:53:52 -070054
Dale Curtisc9aaf3a2011-08-09 15:47:40 -070055API_HOST_INFO_BAD_URL = 'http://127.0.0.1:8080/api/hostinfo/'
56API_HOST_INFO_URL = API_HOST_INFO_BAD_URL + '127.0.0.1'
57
58API_SET_UPDATE_BAD_URL = 'http://127.0.0.1:8080/api/setnextupdate/'
59API_SET_UPDATE_URL = API_SET_UPDATE_BAD_URL + '127.0.0.1'
60
61API_SET_UPDATE_REQUEST = 'new_update-test/the-new-update'
62
joychen3b6bac62013-07-12 11:42:49 -070063DEVSERVER_START_TIMEOUT = 15
Girtsdba6ab22010-10-11 15:53:52 -070064
65class DevserverTest(unittest.TestCase):
66 """Regressions tests for devserver."""
67
68 def setUp(self):
69 """Copies in testing files."""
Girtsdba6ab22010-10-11 15:53:52 -070070
71 # Copy in developer-test.gz, as "static/" directory is hardcoded, and it
72 # would be very hard to change it (static file serving is handled deep
73 # inside webpy).
Chris Sosa6a3697f2013-01-29 16:44:43 -080074 self.test_data_path = tempfile.mkdtemp()
75 self.src_dir = os.path.dirname(__file__)
76 self.image_src = os.path.join(self.src_dir, TEST_IMAGE)
77 self.image = os.path.join(self.test_data_path, TEST_IMAGE_NAME)
Zdenek Behan5d21a2a2011-02-12 02:06:01 +010078 shutil.copy(self.image_src, self.image)
joychen3b6bac62013-07-12 11:42:49 -070079 self.devserver_process = self._StartServer()
Girtsdba6ab22010-10-11 15:53:52 -070080
Girtsdba6ab22010-10-11 15:53:52 -070081 def tearDown(self):
82 """Removes testing files."""
Chris Sosa6a3697f2013-01-29 16:44:43 -080083 shutil.rmtree(self.test_data_path)
joychen3b6bac62013-07-12 11:42:49 -070084 os.kill(self.devserver_process.pid, signal.SIGKILL)
Girtsdba6ab22010-10-11 15:53:52 -070085
Jay Srinivasanac69d262012-10-30 19:05:53 -070086 # Helper methods begin here.
Girtsdba6ab22010-10-11 15:53:52 -070087
Chris Sosa6a3697f2013-01-29 16:44:43 -080088 def _StartServer(self):
Girtsdba6ab22010-10-11 15:53:52 -070089 """Starts devserver, returns process."""
90 cmd = [
91 'python',
Chris Sosa6a3697f2013-01-29 16:44:43 -080092 os.path.join(self.src_dir, 'devserver.py'),
Girtsdba6ab22010-10-11 15:53:52 -070093 'devserver.py',
Chris Sosa6a3697f2013-01-29 16:44:43 -080094 '--archive_dir',
95 self.test_data_path,
96 ]
97
joychen3b6bac62013-07-12 11:42:49 -070098 process = subprocess.Popen(cmd,
99 stderr=subprocess.PIPE)
100
101 # wait for devserver to start
102 current_time = time.time()
103 deadline = current_time + DEVSERVER_START_TIMEOUT
104 while current_time < deadline:
105 current_time = time.time()
106 try:
107 urllib2.urlopen(CHECK_HEALTH_URL, timeout=0.05)
108 break
109 except Exception:
110 continue
111 else:
112 self.fail('Devserver failed to start within timeout.')
113
114 return process
Girtsdba6ab22010-10-11 15:53:52 -0700115
Chris Sosa6a3697f2013-01-29 16:44:43 -0800116 def VerifyHandleUpdate(self, protocol):
117 """Tests running the server and getting an update for the given protocol."""
joychen3b6bac62013-07-12 11:42:49 -0700118 request = urllib2.Request(UPDATE_URL, UPDATE_REQUEST[protocol])
119 connection = urllib2.urlopen(request)
120 response = connection.read()
121 connection.close()
122 self.assertNotEqual('', response)
Girtsdba6ab22010-10-11 15:53:52 -0700123
joychen3b6bac62013-07-12 11:42:49 -0700124 # Parse the response and check if it contains the right result.
125 dom = minidom.parseString(response)
126 update = dom.getElementsByTagName('updatecheck')[0]
127 if protocol == '2.0':
128 url = self.VerifyV2Response(update)
129 else:
130 url = self.VerifyV3Response(update)
Girtsdba6ab22010-10-11 15:53:52 -0700131
joychen3b6bac62013-07-12 11:42:49 -0700132 # Try to fetch the image.
133 connection = urllib2.urlopen(url)
134 contents = connection.read()
135 connection.close()
136 self.assertEqual('Developers, developers, developers!\n', contents)
Girtsdba6ab22010-10-11 15:53:52 -0700137
Jay Srinivasanac69d262012-10-30 19:05:53 -0700138 def VerifyV2Response(self, update):
139 """Verifies the update DOM from a v2 response and returns the url."""
140 codebase = update.getAttribute('codebase')
Chris Sosa6a3697f2013-01-29 16:44:43 -0800141 self.assertEqual(STATIC_URL + TEST_IMAGE_NAME, codebase)
Jay Srinivasanac69d262012-10-30 19:05:53 -0700142
143 hash_value = update.getAttribute('hash')
144 self.assertEqual(EXPECTED_HASH, hash_value)
Jay Srinivasanac69d262012-10-30 19:05:53 -0700145 return codebase
146
147 def VerifyV3Response(self, update):
148 """Verifies the update DOM from a v3 response and returns the url."""
149 # Parse the response and check if it contains the right result.
150 urls = update.getElementsByTagName('urls')[0]
151 url = urls.getElementsByTagName('url')[0]
152
153 codebase = url.getAttribute('codebase')
154 self.assertEqual(STATIC_URL, codebase)
155
156 manifest = update.getElementsByTagName('manifest')[0]
157 packages = manifest.getElementsByTagName('packages')[0]
158 package = packages.getElementsByTagName('package')[0]
159 filename = package.getAttribute('name')
160 self.assertEqual(TEST_IMAGE_NAME, filename)
161
162 hash_value = package.getAttribute('hash')
163 self.assertEqual(EXPECTED_HASH, hash_value)
164
165 url = os.path.join(codebase, filename)
166 return url
167
Jay Srinivasanac69d262012-10-30 19:05:53 -0700168 # Tests begin here.
Jay Srinivasanac69d262012-10-30 19:05:53 -0700169 def testHandleUpdateV2(self):
Chris Sosa6a3697f2013-01-29 16:44:43 -0800170 self.VerifyHandleUpdate('2.0')
Zdenek Behan5d21a2a2011-02-12 02:06:01 +0100171
Jay Srinivasanac69d262012-10-30 19:05:53 -0700172 def testHandleUpdateV3(self):
Chris Sosa6a3697f2013-01-29 16:44:43 -0800173 self.VerifyHandleUpdate('3.0')
Zdenek Behan5d21a2a2011-02-12 02:06:01 +0100174
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700175 def testApiBadSetNextUpdateRequest(self):
176 """Tests sending a bad setnextupdate request."""
joychen3b6bac62013-07-12 11:42:49 -0700177 # Send bad request and ensure it fails...
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700178 try:
joychen3b6bac62013-07-12 11:42:49 -0700179 request = urllib2.Request(API_SET_UPDATE_URL, '')
180 connection = urllib2.urlopen(request)
181 connection.read()
182 connection.close()
183 self.fail('Invalid setnextupdate request did not fail!')
184 except urllib2.URLError:
185 pass
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700186
187 def testApiBadSetNextUpdateURL(self):
188 """Tests contacting a bad setnextupdate url."""
joychen3b6bac62013-07-12 11:42:49 -0700189 # Send bad request and ensure it fails...
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700190 try:
joychen3b6bac62013-07-12 11:42:49 -0700191 connection = urllib2.urlopen(API_SET_UPDATE_BAD_URL)
192 connection.read()
193 connection.close()
194 self.fail('Invalid setnextupdate url did not fail!')
195 except urllib2.URLError:
196 pass
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700197
198 def testApiBadHostInfoURL(self):
199 """Tests contacting a bad hostinfo url."""
joychen3b6bac62013-07-12 11:42:49 -0700200 # Send bad request and ensure it fails...
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700201 try:
joychen3b6bac62013-07-12 11:42:49 -0700202 connection = urllib2.urlopen(API_HOST_INFO_BAD_URL)
203 connection.read()
204 connection.close()
205 self.fail('Invalid hostinfo url did not fail!')
206 except urllib2.URLError:
207 pass
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700208
209 def testApiHostInfoAndSetNextUpdate(self):
210 """Tests using the setnextupdate and hostinfo api commands."""
joychen3b6bac62013-07-12 11:42:49 -0700211 # Send setnextupdate command.
212 request = urllib2.Request(API_SET_UPDATE_URL, API_SET_UPDATE_REQUEST)
213 connection = urllib2.urlopen(request)
214 response = connection.read()
215 connection.close()
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700216
joychen3b6bac62013-07-12 11:42:49 -0700217 # Send hostinfo command and verify the setnextupdate worked.
218 connection = urllib2.urlopen(API_HOST_INFO_URL)
219 response = connection.read()
220 connection.close()
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700221
joychen3b6bac62013-07-12 11:42:49 -0700222 self.assertEqual(
223 json.loads(response)['forced_update_label'], API_SET_UPDATE_REQUEST)
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700224
Chris Sosa6a3697f2013-01-29 16:44:43 -0800225
Girtsdba6ab22010-10-11 15:53:52 -0700226if __name__ == '__main__':
227 unittest.main()