blob: d730316d4df405d7085b3b2df1d49e3d4eece4d8 [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
Dale Curtisc9aaf3a2011-08-09 15:47:40 -07009import json
Chris Sosa8dd80092012-12-10 13:39:11 -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 Sosa8dd80092012-12-10 13:39:11 -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 Sosa8dd80092012-12-10 13:39:11 -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 Sosa8dd80092012-12-10 13:39:11 -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 Sosa8dd80092012-12-10 13:39:11 -080052STATIC_URL = 'http://127.0.0.1:8080/static/archive/'
Girtsdba6ab22010-10-11 15:53:52 -070053
Dale Curtisc9aaf3a2011-08-09 15:47:40 -070054API_HOST_INFO_BAD_URL = 'http://127.0.0.1:8080/api/hostinfo/'
55API_HOST_INFO_URL = API_HOST_INFO_BAD_URL + '127.0.0.1'
56
57API_SET_UPDATE_BAD_URL = 'http://127.0.0.1:8080/api/setnextupdate/'
58API_SET_UPDATE_URL = API_SET_UPDATE_BAD_URL + '127.0.0.1'
59
60API_SET_UPDATE_REQUEST = 'new_update-test/the-new-update'
Chris Sosa8dd80092012-12-10 13:39:11 -080061DEVSERVER_STARTUP_DELAY = 1
Dale Curtisc9aaf3a2011-08-09 15:47:40 -070062
Girtsdba6ab22010-10-11 15:53:52 -070063
64class DevserverTest(unittest.TestCase):
65 """Regressions tests for devserver."""
66
67 def setUp(self):
68 """Copies in testing files."""
Girtsdba6ab22010-10-11 15:53:52 -070069
70 # Copy in developer-test.gz, as "static/" directory is hardcoded, and it
71 # would be very hard to change it (static file serving is handled deep
72 # inside webpy).
Chris Sosa8dd80092012-12-10 13:39:11 -080073 self.test_data_path = tempfile.mkdtemp()
74 self.src_dir = os.path.dirname(__file__)
75 self.image_src = os.path.join(self.src_dir, TEST_IMAGE)
76 self.image = os.path.join(self.test_data_path, TEST_IMAGE_NAME)
Zdenek Behan5d21a2a2011-02-12 02:06:01 +010077 shutil.copy(self.image_src, self.image)
Girtsdba6ab22010-10-11 15:53:52 -070078
Girtsdba6ab22010-10-11 15:53:52 -070079 def tearDown(self):
80 """Removes testing files."""
Chris Sosa8dd80092012-12-10 13:39:11 -080081 shutil.rmtree(self.test_data_path)
Girtsdba6ab22010-10-11 15:53:52 -070082
Jay Srinivasanac69d262012-10-30 19:05:53 -070083 # Helper methods begin here.
Girtsdba6ab22010-10-11 15:53:52 -070084
Chris Sosa8dd80092012-12-10 13:39:11 -080085 def _StartServer(self):
Girtsdba6ab22010-10-11 15:53:52 -070086 """Starts devserver, returns process."""
87 cmd = [
88 'python',
Chris Sosa8dd80092012-12-10 13:39:11 -080089 os.path.join(self.src_dir, 'devserver.py'),
Girtsdba6ab22010-10-11 15:53:52 -070090 'devserver.py',
Chris Sosa8dd80092012-12-10 13:39:11 -080091 '--archive_dir',
92 self.test_data_path,
93 ]
94
Girtsdba6ab22010-10-11 15:53:52 -070095 process = subprocess.Popen(cmd)
Chris Sosa8dd80092012-12-10 13:39:11 -080096 # Wait for the server to start up.
97 time.sleep(DEVSERVER_STARTUP_DELAY)
Girtsdba6ab22010-10-11 15:53:52 -070098 return process.pid
99
Chris Sosa8dd80092012-12-10 13:39:11 -0800100 def VerifyHandleUpdate(self, protocol):
101 """Tests running the server and getting an update for the given protocol."""
102 pid = self._StartServer()
Girtsdba6ab22010-10-11 15:53:52 -0700103 try:
Jay Srinivasanac69d262012-10-30 19:05:53 -0700104 request = urllib2.Request(UPDATE_URL, UPDATE_REQUEST[protocol])
Girtsdba6ab22010-10-11 15:53:52 -0700105 connection = urllib2.urlopen(request)
106 response = connection.read()
Zdenek Behan5d21a2a2011-02-12 02:06:01 +0100107 connection.close()
Girtsdba6ab22010-10-11 15:53:52 -0700108 self.assertNotEqual('', response)
109
110 # Parse the response and check if it contains the right result.
111 dom = minidom.parseString(response)
112 update = dom.getElementsByTagName('updatecheck')[0]
Jay Srinivasanac69d262012-10-30 19:05:53 -0700113 if protocol == '2.0':
114 url = self.VerifyV2Response(update)
115 else:
116 url = self.VerifyV3Response(update)
Girtsdba6ab22010-10-11 15:53:52 -0700117
118 # Try to fetch the image.
Jay Srinivasanac69d262012-10-30 19:05:53 -0700119 connection = urllib2.urlopen(url)
Girtsdba6ab22010-10-11 15:53:52 -0700120 contents = connection.read()
Zdenek Behan5d21a2a2011-02-12 02:06:01 +0100121 connection.close()
Girtsdba6ab22010-10-11 15:53:52 -0700122 self.assertEqual('Developers, developers, developers!\n', contents)
123 finally:
124 os.kill(pid, signal.SIGKILL)
125
Jay Srinivasanac69d262012-10-30 19:05:53 -0700126 def VerifyV2Response(self, update):
127 """Verifies the update DOM from a v2 response and returns the url."""
128 codebase = update.getAttribute('codebase')
Chris Sosa8dd80092012-12-10 13:39:11 -0800129 self.assertEqual(STATIC_URL + TEST_IMAGE_NAME, codebase)
Jay Srinivasanac69d262012-10-30 19:05:53 -0700130
131 hash_value = update.getAttribute('hash')
132 self.assertEqual(EXPECTED_HASH, hash_value)
Jay Srinivasanac69d262012-10-30 19:05:53 -0700133 return codebase
134
135 def VerifyV3Response(self, update):
136 """Verifies the update DOM from a v3 response and returns the url."""
137 # Parse the response and check if it contains the right result.
138 urls = update.getElementsByTagName('urls')[0]
139 url = urls.getElementsByTagName('url')[0]
140
141 codebase = url.getAttribute('codebase')
142 self.assertEqual(STATIC_URL, codebase)
143
144 manifest = update.getElementsByTagName('manifest')[0]
145 packages = manifest.getElementsByTagName('packages')[0]
146 package = packages.getElementsByTagName('package')[0]
147 filename = package.getAttribute('name')
148 self.assertEqual(TEST_IMAGE_NAME, filename)
149
150 hash_value = package.getAttribute('hash')
151 self.assertEqual(EXPECTED_HASH, hash_value)
152
153 url = os.path.join(codebase, filename)
154 return url
155
Jay Srinivasanac69d262012-10-30 19:05:53 -0700156 # Tests begin here.
Jay Srinivasanac69d262012-10-30 19:05:53 -0700157 def testHandleUpdateV2(self):
Chris Sosa8dd80092012-12-10 13:39:11 -0800158 self.VerifyHandleUpdate('2.0')
Zdenek Behan5d21a2a2011-02-12 02:06:01 +0100159
Jay Srinivasanac69d262012-10-30 19:05:53 -0700160 def testHandleUpdateV3(self):
Chris Sosa8dd80092012-12-10 13:39:11 -0800161 self.VerifyHandleUpdate('3.0')
Zdenek Behan5d21a2a2011-02-12 02:06:01 +0100162
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700163 def testApiBadSetNextUpdateRequest(self):
164 """Tests sending a bad setnextupdate request."""
165 pid = self._StartServer()
166 try:
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700167 # Send bad request and ensure it fails...
168 try:
169 request = urllib2.Request(API_SET_UPDATE_URL, '')
170 connection = urllib2.urlopen(request)
171 connection.read()
172 connection.close()
173 self.fail('Invalid setnextupdate request did not fail!')
174 except urllib2.URLError:
175 pass
176 finally:
177 os.kill(pid, signal.SIGKILL)
178
179 def testApiBadSetNextUpdateURL(self):
180 """Tests contacting a bad setnextupdate url."""
181 pid = self._StartServer()
182 try:
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700183 # Send bad request and ensure it fails...
184 try:
185 connection = urllib2.urlopen(API_SET_UPDATE_BAD_URL)
186 connection.read()
187 connection.close()
188 self.fail('Invalid setnextupdate url did not fail!')
189 except urllib2.URLError:
190 pass
191 finally:
192 os.kill(pid, signal.SIGKILL)
193
194 def testApiBadHostInfoURL(self):
195 """Tests contacting a bad hostinfo url."""
196 pid = self._StartServer()
197 try:
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700198 # Send bad request and ensure it fails...
199 try:
200 connection = urllib2.urlopen(API_HOST_INFO_BAD_URL)
201 connection.read()
202 connection.close()
203 self.fail('Invalid hostinfo url did not fail!')
204 except urllib2.URLError:
205 pass
206 finally:
207 os.kill(pid, signal.SIGKILL)
208
209 def testApiHostInfoAndSetNextUpdate(self):
210 """Tests using the setnextupdate and hostinfo api commands."""
211 pid = self._StartServer()
212 try:
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700213 # Send setnextupdate command.
214 request = urllib2.Request(API_SET_UPDATE_URL, API_SET_UPDATE_REQUEST)
215 connection = urllib2.urlopen(request)
216 response = connection.read()
217 connection.close()
218
219 # Send hostinfo command and verify the setnextupdate worked.
220 connection = urllib2.urlopen(API_HOST_INFO_URL)
221 response = connection.read()
222 connection.close()
223
224 self.assertEqual(
225 json.loads(response)['forced_update_label'], API_SET_UPDATE_REQUEST)
226 finally:
227 os.kill(pid, signal.SIGKILL)
228
Chris Sosa8dd80092012-12-10 13:39:11 -0800229
Girtsdba6ab22010-10-11 15:53:52 -0700230if __name__ == '__main__':
231 unittest.main()