blob: 51a40586d48001f354269d78e93900e54669f003 [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.
joychen121fc9b2013-08-02 14:30:30 -070029UPDATE_REQUEST = """<?xml version="1.0" encoding="UTF-8"?>
Jay Srinivasanac69d262012-10-30 19:05:53 -070030<request version="ChromeOSUpdateEngine-0.1.0.0" updaterversion="ChromeOSUpdateEngine-0.1.0.0" protocol="3.0" ismachine="1">
31 <os version="Indy" platform="Chrome OS" sp="0.11.254.2011_03_09_1814_i686"></os>
joychen121fc9b2013-08-02 14:30:30 -070032 <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">
Jay Srinivasanac69d262012-10-30 19:05:53 -070033 <updatecheck></updatecheck>
34 <event eventtype="3" eventresult="2" previousversion="0.11.216.2011_03_02_1358"></event>
35 </app>
36</request>
37"""
Chris Sosa6a3697f2013-01-29 16:44:43 -080038
Girtsdba6ab22010-10-11 15:53:52 -070039# TODO(girts): use a random available port.
joychen7c2054a2013-07-25 11:14:07 -070040UPDATE_URL = 'http://127.0.0.1:8080/update/devserver'
joychen3b6bac62013-07-12 11:42:49 -070041CHECK_HEALTH_URL = 'http://127.0.0.1:8080/check_health'
joychened64b222013-06-21 16:39:34 -070042STATIC_URL = 'http://127.0.0.1:8080/static/'
joychen7c2054a2013-07-25 11:14:07 -070043SERVE_URL = STATIC_URL + LABEL + '/'
Girtsdba6ab22010-10-11 15:53:52 -070044
Dale Curtisc9aaf3a2011-08-09 15:47:40 -070045API_HOST_INFO_BAD_URL = 'http://127.0.0.1:8080/api/hostinfo/'
46API_HOST_INFO_URL = API_HOST_INFO_BAD_URL + '127.0.0.1'
47
48API_SET_UPDATE_BAD_URL = 'http://127.0.0.1:8080/api/setnextupdate/'
49API_SET_UPDATE_URL = API_SET_UPDATE_BAD_URL + '127.0.0.1'
50
51API_SET_UPDATE_REQUEST = 'new_update-test/the-new-update'
52
joychen3b6bac62013-07-12 11:42:49 -070053DEVSERVER_START_TIMEOUT = 15
Girtsdba6ab22010-10-11 15:53:52 -070054
55class DevserverTest(unittest.TestCase):
56 """Regressions tests for devserver."""
57
58 def setUp(self):
59 """Copies in testing files."""
Girtsdba6ab22010-10-11 15:53:52 -070060
61 # Copy in developer-test.gz, as "static/" directory is hardcoded, and it
62 # would be very hard to change it (static file serving is handled deep
63 # inside webpy).
Chris Sosa6a3697f2013-01-29 16:44:43 -080064 self.test_data_path = tempfile.mkdtemp()
65 self.src_dir = os.path.dirname(__file__)
joychen7c2054a2013-07-25 11:14:07 -070066
67 # Copy the payload to the root of static_dir.
Chris Sosa6a3697f2013-01-29 16:44:43 -080068 self.image_src = os.path.join(self.src_dir, TEST_IMAGE)
69 self.image = os.path.join(self.test_data_path, TEST_IMAGE_NAME)
Zdenek Behan5d21a2a2011-02-12 02:06:01 +010070 shutil.copy(self.image_src, self.image)
joychen7c2054a2013-07-25 11:14:07 -070071
72 # Copy the payload to the location of the update label "devserver."
73 os.makedirs(os.path.join(self.test_data_path, LABEL))
74 shutil.copy(self.image_src, os.path.join(self.test_data_path, LABEL,
75 TEST_IMAGE_NAME))
76
77 # Copy the payload to the location of forced label.
78 os.makedirs(os.path.join(self.test_data_path, API_SET_UPDATE_REQUEST))
79 shutil.copy(self.image_src, os.path.join(self.test_data_path,
80 API_SET_UPDATE_REQUEST,
81 TEST_IMAGE_NAME))
82
joychen3b6bac62013-07-12 11:42:49 -070083 self.devserver_process = self._StartServer()
Girtsdba6ab22010-10-11 15:53:52 -070084
Girtsdba6ab22010-10-11 15:53:52 -070085 def tearDown(self):
86 """Removes testing files."""
Chris Sosa6a3697f2013-01-29 16:44:43 -080087 shutil.rmtree(self.test_data_path)
joychen3b6bac62013-07-12 11:42:49 -070088 os.kill(self.devserver_process.pid, signal.SIGKILL)
Girtsdba6ab22010-10-11 15:53:52 -070089
Jay Srinivasanac69d262012-10-30 19:05:53 -070090 # Helper methods begin here.
Girtsdba6ab22010-10-11 15:53:52 -070091
Chris Sosa6a3697f2013-01-29 16:44:43 -080092 def _StartServer(self):
Girtsdba6ab22010-10-11 15:53:52 -070093 """Starts devserver, returns process."""
94 cmd = [
95 'python',
Chris Sosa6a3697f2013-01-29 16:44:43 -080096 os.path.join(self.src_dir, 'devserver.py'),
Girtsdba6ab22010-10-11 15:53:52 -070097 'devserver.py',
joychen7c2054a2013-07-25 11:14:07 -070098 '--static_dir',
Chris Sosa6a3697f2013-01-29 16:44:43 -080099 self.test_data_path,
100 ]
101
joychen3b6bac62013-07-12 11:42:49 -0700102 process = subprocess.Popen(cmd,
103 stderr=subprocess.PIPE)
104
105 # wait for devserver to start
106 current_time = time.time()
107 deadline = current_time + DEVSERVER_START_TIMEOUT
108 while current_time < deadline:
109 current_time = time.time()
110 try:
111 urllib2.urlopen(CHECK_HEALTH_URL, timeout=0.05)
112 break
113 except Exception:
114 continue
115 else:
116 self.fail('Devserver failed to start within timeout.')
117
118 return process
Girtsdba6ab22010-10-11 15:53:52 -0700119
joychen121fc9b2013-08-02 14:30:30 -0700120 def VerifyHandleUpdate(self):
Chris Sosa6a3697f2013-01-29 16:44:43 -0800121 """Tests running the server and getting an update for the given protocol."""
joychen121fc9b2013-08-02 14:30:30 -0700122 request = urllib2.Request(UPDATE_URL, UPDATE_REQUEST)
joychen3b6bac62013-07-12 11:42:49 -0700123 connection = urllib2.urlopen(request)
124 response = connection.read()
125 connection.close()
126 self.assertNotEqual('', response)
Girtsdba6ab22010-10-11 15:53:52 -0700127
joychen3b6bac62013-07-12 11:42:49 -0700128 # Parse the response and check if it contains the right result.
129 dom = minidom.parseString(response)
130 update = dom.getElementsByTagName('updatecheck')[0]
joychen121fc9b2013-08-02 14:30:30 -0700131 url = self.VerifyV3Response(update)
Girtsdba6ab22010-10-11 15:53:52 -0700132
joychen3b6bac62013-07-12 11:42:49 -0700133 # Try to fetch the image.
134 connection = urllib2.urlopen(url)
135 contents = connection.read()
136 connection.close()
137 self.assertEqual('Developers, developers, developers!\n', contents)
Girtsdba6ab22010-10-11 15:53:52 -0700138
Jay Srinivasanac69d262012-10-30 19:05:53 -0700139 def VerifyV3Response(self, update):
140 """Verifies the update DOM from a v3 response and returns the url."""
141 # Parse the response and check if it contains the right result.
142 urls = update.getElementsByTagName('urls')[0]
143 url = urls.getElementsByTagName('url')[0]
144
145 codebase = url.getAttribute('codebase')
joychen7c2054a2013-07-25 11:14:07 -0700146 self.assertEqual(SERVE_URL, codebase)
Jay Srinivasanac69d262012-10-30 19:05:53 -0700147
148 manifest = update.getElementsByTagName('manifest')[0]
149 packages = manifest.getElementsByTagName('packages')[0]
150 package = packages.getElementsByTagName('package')[0]
151 filename = package.getAttribute('name')
152 self.assertEqual(TEST_IMAGE_NAME, filename)
153
154 hash_value = package.getAttribute('hash')
155 self.assertEqual(EXPECTED_HASH, hash_value)
156
157 url = os.path.join(codebase, filename)
158 return url
159
Jay Srinivasanac69d262012-10-30 19:05:53 -0700160 # Tests begin here.
Jay Srinivasanac69d262012-10-30 19:05:53 -0700161 def testHandleUpdateV3(self):
joychen121fc9b2013-08-02 14:30:30 -0700162 self.VerifyHandleUpdate()
Zdenek Behan5d21a2a2011-02-12 02:06:01 +0100163
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700164 def testApiBadSetNextUpdateRequest(self):
165 """Tests sending a bad setnextupdate request."""
joychen3b6bac62013-07-12 11:42:49 -0700166 # Send bad request and ensure it fails...
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700167 try:
joychen3b6bac62013-07-12 11:42:49 -0700168 request = urllib2.Request(API_SET_UPDATE_URL, '')
169 connection = urllib2.urlopen(request)
170 connection.read()
171 connection.close()
172 self.fail('Invalid setnextupdate request did not fail!')
173 except urllib2.URLError:
174 pass
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700175
176 def testApiBadSetNextUpdateURL(self):
177 """Tests contacting a bad setnextupdate url."""
joychen3b6bac62013-07-12 11:42:49 -0700178 # Send bad request and ensure it fails...
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700179 try:
joychen3b6bac62013-07-12 11:42:49 -0700180 connection = urllib2.urlopen(API_SET_UPDATE_BAD_URL)
181 connection.read()
182 connection.close()
183 self.fail('Invalid setnextupdate url did not fail!')
184 except urllib2.URLError:
185 pass
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700186
187 def testApiBadHostInfoURL(self):
188 """Tests contacting a bad hostinfo 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_HOST_INFO_BAD_URL)
192 connection.read()
193 connection.close()
194 self.fail('Invalid hostinfo url did not fail!')
195 except urllib2.URLError:
196 pass
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700197
198 def testApiHostInfoAndSetNextUpdate(self):
199 """Tests using the setnextupdate and hostinfo api commands."""
joychen3b6bac62013-07-12 11:42:49 -0700200 # Send setnextupdate command.
201 request = urllib2.Request(API_SET_UPDATE_URL, API_SET_UPDATE_REQUEST)
202 connection = urllib2.urlopen(request)
203 response = connection.read()
204 connection.close()
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700205
joychen3b6bac62013-07-12 11:42:49 -0700206 # Send hostinfo command and verify the setnextupdate worked.
207 connection = urllib2.urlopen(API_HOST_INFO_URL)
208 response = connection.read()
209 connection.close()
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700210
joychen3b6bac62013-07-12 11:42:49 -0700211 self.assertEqual(
212 json.loads(response)['forced_update_label'], API_SET_UPDATE_REQUEST)
Dale Curtisc9aaf3a2011-08-09 15:47:40 -0700213
Chris Sosa6a3697f2013-01-29 16:44:43 -0800214
Girtsdba6ab22010-10-11 15:53:52 -0700215if __name__ == '__main__':
216 unittest.main()