Merge lp:~dobey/ubuntuone-dev-tools/updates-2992 into lp:ubuntuone-dev-tools/stable-3-0

Proposed by dobey
Status: Merged
Approved by: dobey
Approved revision: 53
Merged at revision: 53
Proposed branch: lp:~dobey/ubuntuone-dev-tools/updates-2992
Merge into: lp:ubuntuone-dev-tools/stable-3-0
Diff against target: 1307 lines (+1145/-30)
14 files modified
bin/u1lint.bat (+17/-0)
bin/u1trial.bat (+17/-0)
data/squid.conf.in (+120/-0)
run-tests (+1/-1)
setup.py (+12/-4)
ubuntuone/devtools/services/__init__.py (+53/-0)
ubuntuone/devtools/services/dbus.py (+8/-23)
ubuntuone/devtools/services/squid.py (+245/-0)
ubuntuone/devtools/services/tests/test_dbus.py (+1/-1)
ubuntuone/devtools/services/tests/test_squid.py (+334/-0)
ubuntuone/devtools/testcases/dbus.py (+1/-1)
ubuntuone/devtools/testcases/squid.py (+58/-0)
ubuntuone/devtools/testcases/tests/__init__.py (+16/-0)
ubuntuone/devtools/testcases/tests/test_squid_testcase.py (+262/-0)
To merge this branch: bzr merge lp:~dobey/ubuntuone-dev-tools/updates-2992
Reviewer Review Type Date Requested Status
dobey (community) Abstain
Brian Curtin (community) Approve
Diego Sarmentero (community) Approve
Review via email: mp+88946@code.launchpad.net

Commit message

[Manuel de la Peña]

    Provide batch scripts for u1trial and u1lint on Windows.
    Provide a new SquidTestCase for running tests with a private squid instance.

[Rodney Dawes]

    Add missing squid.conf.in to setup.py

To post a comment you must log in.
Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

+1

review: Approve
Revision history for this message
Brian Curtin (brian.curtin) wrote :

Minor issue: In your uses of subprocess.Popen, you open stdout and stderr but don't close them.

Otherwise: +1

review: Approve
Revision history for this message
dobey (dobey) :
review: Abstain

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'bin/u1lint.bat'
2--- bin/u1lint.bat 1970-01-01 00:00:00 +0000
3+++ bin/u1lint.bat 2012-01-17 21:43:26 +0000
4@@ -0,0 +1,17 @@
5+::
6+:: Copyright 2012 Canonical Ltd.
7+::
8+:: This program is free software: you can redistribute it and/or modify it
9+:: under the terms of the GNU General Public License version 3, as published
10+:: by the Free Software Foundation.
11+::
12+:: This program is distributed in the hope that it will be useful, but
13+:: WITHOUT ANY WARRANTY; without even the implied warranties of
14+:: MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15+:: PURPOSE. See the GNU General Public License for more details.
16+::
17+:: You should have received a copy of the GNU General Public License along
18+:: with this program. If not, see <http://www.gnu.org/licenses/>.
19+
20+:: Use python to execute the script having the same name as this batch
21+python "%~dpn0" %*
22
23=== added file 'bin/u1trial.bat'
24--- bin/u1trial.bat 1970-01-01 00:00:00 +0000
25+++ bin/u1trial.bat 2012-01-17 21:43:26 +0000
26@@ -0,0 +1,17 @@
27+::
28+:: Copyright 2012 Canonical Ltd.
29+::
30+:: This program is free software: you can redistribute it and/or modify it
31+:: under the terms of the GNU General Public License version 3, as published
32+:: by the Free Software Foundation.
33+::
34+:: This program is distributed in the hope that it will be useful, but
35+:: WITHOUT ANY WARRANTY; without even the implied warranties of
36+:: MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
37+:: PURPOSE. See the GNU General Public License for more details.
38+::
39+:: You should have received a copy of the GNU General Public License along
40+:: with this program. If not, see <http://www.gnu.org/licenses/>.
41+
42+:: Use python to execute the script having the same name as this batch
43+python "%~dpn0" %*
44
45=== added file 'data/squid.conf.in'
46--- data/squid.conf.in 1970-01-01 00:00:00 +0000
47+++ data/squid.conf.in 2012-01-17 21:43:26 +0000
48@@ -0,0 +1,120 @@
49+auth_param basic casesensitive on
50+# Use a default auth using ncsa and the passed generated file.
51+auth_param basic program ${auth_process} ${auth_file}
52+#Recommended minimum configuration:
53+acl manager proto cache_object
54+acl localhost src 127.0.0.1/32
55+acl to_localhost dst 127.0.0.0/32
56+#
57+# Example rule allowing access from your local networks.
58+# Adapt to list your (internal) IP networks from where browsing
59+# should be allowed
60+acl all src all
61+acl localnet src 10.0.0.0/8 # RFC1918 possible internal network
62+acl localnet src 172.16.0.0/12 # RFC1918 possible internal network
63+acl localnet src 192.168.0.0/16 # RFC1918 possible internal network
64+#
65+acl SSL_ports port 443 # https
66+acl SSL_ports port 563 # snews
67+acl SSL_ports port 873 # rsync
68+acl Safe_ports port 80 # http
69+acl Safe_ports port 21 # ftp
70+acl Safe_ports port 443 # https
71+acl Safe_ports port 70 # gopher
72+acl Safe_ports port 210 # wais
73+acl Safe_ports port 1025-65535 # unregistered ports
74+acl Safe_ports port 280 # http-mgmt
75+acl Safe_ports port 488 # gss-http
76+acl Safe_ports port 591 # filemaker
77+acl Safe_ports port 777 # multiling http
78+acl Safe_ports port 631 # cups
79+acl Safe_ports port 873 # rsync
80+acl Safe_ports port 901 # SWAT
81+acl purge method PURGE
82+acl CONNECT method CONNECT
83+
84+# make an acl for users that have auth
85+acl password proxy_auth REQUIRED myportname ${auth_port_number}
86+acl auth_port_connected myportname ${auth_port_number}
87+acl nonauth_port_connected myportname ${noauth_port_number}
88+
89+# Settings used for the tests:
90+# Allow users connected to the nonauth port
91+# Allow users authenticated AND connected to the auth port
92+http_access allow nonauth_port_connected
93+http_access allow password
94+
95+#Recommended minimum configuration:
96+#
97+# Only allow cachemgr access from localhost
98+http_access allow manager localhost
99+http_access deny manager
100+# Only allow purge requests from localhost
101+http_access allow purge localhost
102+http_access deny purge
103+# Deny requests to unknown ports
104+http_access deny !Safe_ports
105+# Deny CONNECT to other than SSL ports
106+http_access deny CONNECT !SSL_ports
107+# Example rule allowing access from your local networks.
108+# Adapt localnet in the ACL section to list your (internal) IP networks
109+# from where browsing should be allowed
110+#http_access allow localnet
111+http_access allow localhost
112+
113+# And finally deny all other access to this proxy
114+http_access deny all
115+
116+icp_access allow localnet
117+icp_access deny all
118+
119+# Squid normally listens to port 3128 but we are going to listento two
120+# different ports, one for auth one for nonauth.
121+http_port ${noauth_port_number}
122+http_port ${auth_port_number}
123+
124+#We recommend you to use at least the following line.
125+hierarchy_stoplist cgi-bin ?
126+
127+# Default cache settings.
128+cache_dir ufs ${spool_temp} 1000 16 256
129+
130+# access log settings
131+access_log ${squid_temp}/access.log squid
132+
133+# cache log settings
134+cache_log ${squid_temp}/cache.log
135+
136+# cache store log settings
137+cache_store_log ${squid_temp}/store.log
138+
139+# mime table conf
140+# mime_table /usr/share/squid/mime.conf
141+
142+#Default pid file name
143+pid_filename ${squid_temp}/squid.pid
144+
145+# debug options (Full debugging)
146+debug_options ALL,1
147+
148+#Default netdb_filename
149+
150+#Suggested default:
151+refresh_pattern ^ftp: 1440 20% 10080
152+refresh_pattern ^gopher: 1440 0% 1440
153+refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
154+refresh_pattern (Release|Packages(.gz)*)$ 0 20% 2880
155+# example line deb packages
156+refresh_pattern . 0 20% 4320
157+
158+# Don't upgrade ShoutCast responses to HTTP
159+acl shoutcast rep_header X-HTTP09-First-Line ^ICY.[0-9]
160+
161+# Apache mod_gzip and mod_deflate known to be broken so don't trust
162+# Apache to signal ETag correctly on such responses
163+acl apache rep_header Server ^Apache
164+
165+hosts_file /etc/hosts
166+
167+# Leave coredumps in the first cache dir
168+coredump_dir ${spool_temp}/squid
169
170=== modified file 'run-tests'
171--- run-tests 2011-11-21 19:58:40 +0000
172+++ run-tests 2012-01-17 21:43:26 +0000
173@@ -20,6 +20,6 @@
174 bin/u1trial --reactor=twisted ubuntuone
175 echo "Running style checks..."
176 bin/u1lint
177-pep8 --repeat . bin/*
178+pep8 --repeat . bin/* --exclude=*.bat
179 rm -rf _trial_temp
180 rm -rf .coverage
181
182=== modified file 'setup.py'
183--- setup.py 2012-01-03 20:29:17 +0000
184+++ setup.py 2012-01-17 21:43:26 +0000
185@@ -44,6 +44,15 @@
186 if retcode != 0:
187 sys.exit(retcode)
188
189+# pylint: disable=C0103
190+scripts = ['bin/u1lint',
191+ 'bin/u1trial']
192+# pylint: enable=C0103
193+
194+if sys.platform == 'win32':
195+ # lets add the .bat so that windows users are happy
196+ scripts.extend(['bin/u1lint.bat', 'bin/u1trial.bat'])
197+
198 setup(name=PACKAGE,
199 version=VERSION,
200 description='Ubuntu One development tools and utilities',
201@@ -55,12 +64,11 @@
202 'ubuntuone.devtools.testing',
203 'ubuntuone.devtools.testcases'],
204 extra_path='ubuntuone-dev-tools',
205- scripts=['bin/u1lint',
206- 'bin/u1trial',
207- ],
208+ scripts=scripts,
209 data_files=[('share/%s' % PACKAGE,
210 ['pylintrc',
211- 'data/dbus-session.conf.in']),
212+ 'data/dbus-session.conf.in',
213+ 'data/squid.conf.in']),
214 ('share/man/man1',
215 ['man/u1lint.1',
216 'man/u1trial.1']),
217
218=== modified file 'ubuntuone/devtools/services/__init__.py'
219--- ubuntuone/devtools/services/__init__.py 2010-11-03 21:04:06 +0000
220+++ ubuntuone/devtools/services/__init__.py 2012-01-17 21:43:26 +0000
221@@ -1,1 +1,54 @@
222+#
223+# Copyright 2011 Canonical Ltd.
224+#
225+# This program is free software: you can redistribute it and/or modify it
226+# under the terms of the GNU General Public License version 3, as published
227+# by the Free Software Foundation.
228+#
229+# This program is distributed in the hope that it will be useful, but
230+# WITHOUT ANY WARRANTY; without even the implied warranties of
231+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
232+# PURPOSE. See the GNU General Public License for more details.
233+#
234+# You should have received a copy of the GNU General Public License along
235+# with this program. If not, see <http://www.gnu.org/licenses/>.
236 """Service runners for testing."""
237+
238+import os
239+import socket
240+
241+from dirspec.basedir import load_data_paths
242+
243+
244+def find_config_file(in_config_file):
245+ """Find the first appropriate conf to use."""
246+ # In case we're running from within the source tree
247+ path = os.path.abspath(os.path.join(os.path.dirname(__file__),
248+ os.path.pardir, os.path.pardir,
249+ os.path.pardir,
250+ "data", in_config_file))
251+ if not os.path.exists(path):
252+ # Use the installed file in $pkgdatadir as source
253+ for path in load_data_paths("ubuntuone-dev-tools",
254+ in_config_file):
255+ if os.path.exists(path):
256+ break
257+
258+ # Check to make sure we didn't just fall out of the loop
259+ if not os.path.exists(path):
260+ raise IOError('Could not locate suitable %s' % in_config_file)
261+ return path
262+
263+
264+def get_arbitrary_port():
265+ """
266+ Find an unused port, and return it.
267+
268+ There might be a small race condition here, but we aren't
269+ worried about it.
270+ """
271+ sock = socket.socket()
272+ sock.bind(('localhost', 0))
273+ _, port = sock.getsockname()
274+ sock.close()
275+ return port
276
277=== modified file 'ubuntuone/devtools/services/dbus.py'
278--- ubuntuone/devtools/services/dbus.py 2011-12-19 22:37:31 +0000
279+++ ubuntuone/devtools/services/dbus.py 2012-01-17 21:43:26 +0000
280@@ -20,10 +20,12 @@
281 import signal
282 import subprocess
283
284-from dirspec.basedir import load_data_paths
285 from distutils.spawn import find_executable
286 from urllib import quote
287
288+from ubuntuone.devtools.services import find_config_file
289+DBUS_CONFIG_FILE = 'dbus-session.conf.in'
290+
291
292 class DBusLaunchError(Exception):
293 """Error while launching dbus-daemon"""
294@@ -44,42 +46,25 @@
295 self.running = False
296 self.config_file = None
297
298- def _find_config_file(self, tempdir=None):
299+ def _generate_config_file(self, tempdir=None):
300 """Find the first appropriate dbus-session.conf to use."""
301- # In case we're running from within the source tree
302- path = os.path.abspath(os.path.join(os.path.dirname(__file__),
303- os.path.pardir, os.path.pardir,
304- os.path.pardir,
305- "data", "dbus-session.conf.in"))
306- if not os.path.exists(path):
307- # Use the installed file in $pkgdatadir as source
308- for path in load_data_paths("ubuntuone-dev-tools",
309- "dbus-session.conf.in"):
310- if os.path.exists(path):
311- break
312-
313- # Check to make sure we didn't just fall out of the loop
314- if not os.path.exists(path):
315- raise IOError('Could not locate suitable dbus-session.conf.in')
316-
317+ # load the config file
318+ path = find_config_file(DBUS_CONFIG_FILE)
319+ # replace config settings
320 self.config_file = os.path.join(tempdir, 'dbus-session.conf')
321 dbus_address = 'unix:tmpdir=%s' % quote(tempdir)
322 with open(path) as in_file:
323 content = in_file.read()
324 with open(self.config_file, 'w') as out_file:
325 out_file.write(content.replace('@ADDRESS@', dbus_address))
326- out_file.close()
327- in_file.close()
328
329 def start_service(self, tempdir=None):
330 """Start our own session bus daemon for testing."""
331- if not tempdir:
332- tempdir = os.path.join(os.getcwd(), '_trial_temp')
333 dbus = find_executable("dbus-daemon")
334 if not dbus:
335 raise NotFoundError("dbus-daemon was not found.")
336
337- self._find_config_file(tempdir)
338+ self._generate_config_file(tempdir)
339
340 dbus_args = ["--fork",
341 "--config-file=" + self.config_file,
342
343=== added file 'ubuntuone/devtools/services/squid.py'
344--- ubuntuone/devtools/services/squid.py 1970-01-01 00:00:00 +0000
345+++ ubuntuone/devtools/services/squid.py 2012-01-17 21:43:26 +0000
346@@ -0,0 +1,245 @@
347+#
348+# Copyright 2011 Canonical Ltd.
349+#
350+# This program is free software: you can redistribute it and/or modify it
351+# under the terms of the GNU General Public License version 3, as published
352+# by the Free Software Foundation.
353+#
354+# This program is distributed in the hope that it will be useful, but
355+# WITHOUT ANY WARRANTY; without even the implied warranties of
356+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
357+# PURPOSE. See the GNU General Public License for more details.
358+#
359+# You should have received a copy of the GNU General Public License along
360+# with this program. If not, see <http://www.gnu.org/licenses/>.
361+"""Utilities for finding and running a squid proxy for testing."""
362+
363+import random
364+import signal
365+# pylint:disable=W0402
366+import string
367+# pylint:enable=W0402
368+import subprocess
369+import time
370+
371+from json import dumps, loads
372+from os import environ, makedirs, kill, unlink
373+from os.path import abspath, exists, join
374+
375+from distutils.spawn import find_executable
376+
377+from ubuntuone.devtools.services import (
378+ find_config_file,
379+ get_arbitrary_port,
380+)
381+
382+SQUID_CONFIG_FILE = 'squid.conf.in'
383+SQUID_DIR = 'squid'
384+AUTH_PROCESS_PATH = '/usr/lib/%s/ncsa_auth'
385+SPOOL_DIR = 'spool'
386+AUTH_FILE = 'htpasswd'
387+PROXY_ENV_VAR = 'SQUID_PROXY_SETTINGS'
388+
389+
390+def get_squid_executable():
391+ """Return the squid executable of the system."""
392+ # try with squid and if not present try with squid3 for newer systems
393+ # (Ubuntu P). We also return the path to the auth process so that we can
394+ # point to the correct one.
395+ squid = find_executable('squid3')
396+ auth_process = AUTH_PROCESS_PATH % 'squid3'
397+ if squid is None:
398+ squid = find_executable('squid')
399+ auth_process = AUTH_PROCESS_PATH % 'squid'
400+ return squid, auth_process
401+
402+
403+def get_htpasswd_executable():
404+ """Return the htpasswd executable."""
405+ return find_executable('htpasswd')
406+
407+
408+def _make_random_string(count):
409+ """Make a random string of the given length."""
410+ entropy = random.SystemRandom()
411+ return ''.join([entropy.choice(string.letters) for _ in
412+ range(count)])
413+
414+
415+def _get_basedir(tempdir):
416+ """Return the base squid config."""
417+ basedir = join(tempdir, SQUID_DIR)
418+ basedir = abspath(basedir)
419+ if not exists(basedir):
420+ makedirs(basedir)
421+ return basedir
422+
423+
424+def _get_spool_temp_path(tempdir=''):
425+ """Return the temp dir to be used for spool."""
426+ basedir = _get_basedir(tempdir)
427+ path = join(basedir, SPOOL_DIR)
428+ path = abspath(path)
429+ if not exists(path):
430+ makedirs(path)
431+ return path
432+
433+
434+def _get_squid_temp_path(tempdir=''):
435+ """Return the temp dir to be used by squid."""
436+ basedir = _get_basedir(tempdir)
437+ path = join(basedir, SQUID_DIR)
438+ path = abspath(path)
439+ if not exists(path):
440+ makedirs(path)
441+ return path
442+
443+
444+def _get_auth_temp_path(tempdir=''):
445+ """Return the path for the auth file."""
446+ basedir = _get_basedir(tempdir)
447+ auth_file = join(basedir, AUTH_FILE)
448+ if not exists(basedir):
449+ makedirs(basedir)
450+ return auth_file
451+
452+
453+def store_proxy_settings(settings):
454+ """Store the proxy setting in an env var."""
455+ environ[PROXY_ENV_VAR] = dumps(settings)
456+
457+
458+def retrieve_proxy_settings():
459+ """Return the proxy settings of the env."""
460+ if PROXY_ENV_VAR in environ:
461+ return loads(environ[PROXY_ENV_VAR])
462+ return None
463+
464+
465+def delete_proxy_settings():
466+ """Delete the proxy env settings."""
467+ if PROXY_ENV_VAR in environ:
468+ del environ[PROXY_ENV_VAR]
469+
470+
471+class SquidLaunchError(Exception):
472+ """Error while launching squid."""
473+
474+
475+class SquidRunner(object):
476+ """Class for running a squid proxy with the local config."""
477+
478+ def __init__(self):
479+ """Create a new instance."""
480+ self.squid, self.auth_process = get_squid_executable()
481+ if self.squid is None:
482+ raise SquidLaunchError('Could not locate "squid".')
483+
484+ self.htpasswd = get_htpasswd_executable()
485+ if self.htpasswd is None:
486+ raise SquidLaunchError('Could not locate "htpasswd".')
487+
488+ self.settings = dict(noauth_port=None, auth_port=None,
489+ username=None, password=None)
490+ self.squid_pid = None
491+ self.running = False
492+ self.config_file = None
493+ self.auth_file = None
494+
495+ def _generate_config_file(self, tempdir=''):
496+ """Find the first appropiate squid.conf to use."""
497+ # load the config file
498+ path = find_config_file(SQUID_CONFIG_FILE)
499+ # replace config settings
500+ basedir = join(tempdir, 'squid')
501+ basedir = abspath(basedir)
502+ if not exists(basedir):
503+ makedirs(basedir)
504+ self.config_file = join(basedir, 'squid.conf')
505+ with open(path) as in_file:
506+ template = string.Template(in_file.read())
507+
508+ self.settings['noauth_port'] = get_arbitrary_port()
509+ self.settings['auth_port'] = get_arbitrary_port()
510+ spool_path = _get_spool_temp_path(tempdir)
511+ squid_path = _get_squid_temp_path(tempdir)
512+ with open(self.config_file, 'w') as out_file:
513+ out_file.write(template.safe_substitute(
514+ auth_file=self.auth_file,
515+ auth_process=self.auth_process,
516+ noauth_port_number=self.settings['noauth_port'],
517+ auth_port_number=self.settings['auth_port'],
518+ spool_temp=spool_path,
519+ squid_temp=squid_path))
520+
521+ def _generate_swap(self, config_file):
522+ """Generate the squid swap files."""
523+ squid_args = ['-z', '-f', config_file]
524+ sp = subprocess.Popen([self.squid] + squid_args,
525+ stdout=subprocess.PIPE,
526+ stderr=subprocess.PIPE)
527+ sp.wait()
528+
529+ def _generate_auth_file(self, tempdir=''):
530+ """Generates a auth file using htpasswd."""
531+ if self.settings['username'] is None:
532+ self.settings['username'] = _make_random_string(10)
533+ if self.settings['password'] is None:
534+ self.settings['password'] = _make_random_string(10)
535+
536+ self.auth_file = _get_auth_temp_path(tempdir)
537+ # remove possible old auth file
538+ if exists(self.auth_file):
539+ unlink(self.auth_file)
540+ # create a new htpasswrd
541+ htpasswd_args = ['-bc',
542+ self.auth_file,
543+ self.settings['username'],
544+ self.settings['password']]
545+ sp = subprocess.Popen([self.htpasswd] + htpasswd_args,
546+ stdout=subprocess.PIPE,
547+ stderr=subprocess.PIPE)
548+ sp.wait()
549+
550+ def _is_squid_running(self):
551+ """Return if squid is running."""
552+ squid_args = ['-k', 'check', '-f', self.config_file]
553+ print 'Starting squid version...'
554+ message = 'Waiting for squid to start...'
555+ for timeout in (0.4, 0.1, 0.1, 0.2, 0.5, 1, 3, 5):
556+ try:
557+ # Do not use stdout=PIPE or stderr=PIPE with this function.
558+ subprocess.check_call([self.squid] + squid_args,
559+ stdout=subprocess.PIPE,
560+ stderr=subprocess.PIPE)
561+ return True
562+ except subprocess.CalledProcessError:
563+ message += '.'
564+ print message
565+ time.sleep(timeout)
566+ return False
567+
568+ def start_service(self, tempdir=None):
569+ """Start our own proxy."""
570+ # generate auth, config and swap dirs
571+ self._generate_auth_file(tempdir)
572+ self._generate_config_file(tempdir)
573+ self._generate_swap(self.config_file)
574+ squid_args = ['-N', '-X', '-f', self.config_file]
575+ sp = subprocess.Popen([self.squid] + squid_args,
576+ stdout=subprocess.PIPE,
577+ stderr=subprocess.PIPE)
578+ store_proxy_settings(self.settings)
579+ if not self._is_squid_running():
580+ raise SquidLaunchError('Could not start squid.')
581+ self.squid_pid = sp.pid
582+ self.running = True
583+
584+ def stop_service(self):
585+ """Stop our proxy,"""
586+ kill(self.squid_pid, signal.SIGKILL)
587+ delete_proxy_settings()
588+ self.running = False
589+ unlink(self.config_file)
590+ unlink(self.auth_file)
591+ self.config_file = None
592
593=== modified file 'ubuntuone/devtools/services/tests/test_dbus.py'
594--- ubuntuone/devtools/services/tests/test_dbus.py 2011-11-23 11:31:08 +0000
595+++ ubuntuone/devtools/services/tests/test_dbus.py 2012-01-17 21:43:26 +0000
596@@ -25,6 +25,6 @@
597 runner = DBusRunner()
598 # pylint: disable=W0212
599 os.makedirs(self.tmpdir)
600- runner._find_config_file(tempdir=self.tmpdir)
601+ runner._generate_config_file(tempdir=self.tmpdir)
602 shutil.rmtree(self.tmpdir)
603 self.assertEqual(expected, runner.config_file)
604
605=== added file 'ubuntuone/devtools/services/tests/test_squid.py'
606--- ubuntuone/devtools/services/tests/test_squid.py 1970-01-01 00:00:00 +0000
607+++ ubuntuone/devtools/services/tests/test_squid.py 2012-01-17 21:43:26 +0000
608@@ -0,0 +1,334 @@
609+#
610+# Copyright 2011 Canonical Ltd.
611+#
612+# This program is free software: you can redistribute it and/or modify it
613+# under the terms of the GNU General Public License version 3, as published
614+# by the Free Software Foundation.
615+#
616+# This program is distributed in the hope that it will be useful, but
617+# WITHOUT ANY WARRANTY; without even the implied warranties of
618+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
619+# PURPOSE. See the GNU General Public License for more details.
620+#
621+# You should have received a copy of the GNU General Public License along
622+# with this program. If not, see <http://www.gnu.org/licenses/>.
623+"""Test the squid service."""
624+
625+import json
626+import os
627+
628+from twisted.internet import defer
629+
630+from ubuntuone.devtools.testcases import BaseTestCase
631+from ubuntuone.devtools.services import squid
632+
633+# yes, we know names with _ are private
634+#pylint: disable=W0212
635+
636+
637+class PathsTestCase(BaseTestCase):
638+ """Test the different path functions."""
639+
640+ @defer.inlineCallbacks
641+ def setUp(self):
642+ """Set the different tests."""
643+ yield super(PathsTestCase, self).setUp()
644+ self.basedir_fn = squid._get_basedir
645+ self.basedir = self.mktemp('paths')
646+ self.path_exists = False
647+ self.created_paths = []
648+ self.called = []
649+
650+ def fake_basedir_fn(tempdir):
651+ """Retun the base dir."""
652+ self.called.append(('fake_basedir_fn', tempdir))
653+ return self.basedir
654+
655+ def fake_makedirs(path):
656+ """Fake the makedirs function."""
657+ self.called.append(('fake_makedirs', path))
658+ self.created_paths.append(path)
659+
660+ def fake_exists(path):
661+ """Fake the exists method."""
662+ self.called.append(('fake_exists', path))
663+ return path in self.created_paths or self.path_exists
664+
665+ self.patch(squid, '_get_basedir', fake_basedir_fn)
666+ self.patch(squid, 'makedirs', fake_makedirs)
667+ self.patch(squid, 'exists', fake_exists)
668+
669+ def test_get_basedir_missing(self):
670+ """Test the base dir creation."""
671+ basedir = self.basedir_fn(self.basedir)
672+ expected_path = os.path.join(self.basedir, squid.SQUID_DIR)
673+ self.assertEqual(expected_path, basedir)
674+ self.assertTrue(('fake_makedirs', expected_path) in self.called)
675+ self.assertTrue(expected_path in self.created_paths)
676+ self.assertTrue(('fake_exists', expected_path) in self.called)
677+
678+ def test_get_basedir_present(self):
679+ """Test the base dir creation."""
680+ self.path_exists = True
681+ basedir = self.basedir_fn(self.basedir)
682+ expected_path = os.path.join(self.basedir, squid.SQUID_DIR)
683+ self.assertEqual(expected_path, basedir)
684+ expected_path = os.path.join(self.basedir, squid.SQUID_DIR)
685+ self.assertTrue(('fake_makedirs', expected_path) not in self.called)
686+ self.assertTrue(expected_path not in self.created_paths)
687+ self.assertTrue(('fake_exists', expected_path) in self.called)
688+
689+ def test_get_spool_temp_path_missing(self):
690+ """Test the spool path creation."""
691+ expected_path = os.path.join(self.basedir, squid.SPOOL_DIR)
692+ result = squid._get_spool_temp_path()
693+ self.assertEqual(expected_path, result)
694+ self.assertTrue(('fake_basedir_fn', '') in self.called)
695+ self.assertTrue(('fake_makedirs', expected_path) in self.called)
696+ self.assertTrue(expected_path in self.created_paths)
697+ self.assertTrue(('fake_exists', expected_path) in self.called)
698+
699+ def test_get_spool_temp_path_present(self):
700+ """Test the spool path creation."""
701+ self.path_exists = True
702+ expected_path = os.path.join(self.basedir, squid.SPOOL_DIR)
703+ result = squid._get_spool_temp_path()
704+ self.assertEqual(expected_path, result)
705+ self.assertTrue(('fake_basedir_fn', '') in self.called)
706+ self.assertTrue(('fake_makedirs', expected_path) not in self.called)
707+ self.assertTrue(expected_path not in self.created_paths)
708+ self.assertTrue(('fake_exists', expected_path) in self.called)
709+
710+ def test_get_squid_temp_path_missing(self):
711+ """Test the squid path creation."""
712+ expected_path = os.path.join(self.basedir, squid.SQUID_DIR)
713+ result = squid._get_squid_temp_path()
714+ self.assertEqual(expected_path, result)
715+ self.assertTrue(('fake_basedir_fn', '') in self.called)
716+ self.assertTrue(('fake_makedirs', expected_path) in self.called)
717+ self.assertTrue(expected_path in self.created_paths)
718+ self.assertTrue(('fake_exists', expected_path) in self.called)
719+
720+ def test_get_squid_temp_path_present(self):
721+ """Test the squid path creation."""
722+ self.path_exists = True
723+ expected_path = os.path.join(self.basedir, squid.SQUID_DIR)
724+ result = squid._get_squid_temp_path()
725+ self.assertEqual(expected_path, result)
726+ self.assertTrue(('fake_basedir_fn', '') in self.called)
727+ self.assertTrue(('fake_makedirs', expected_path) not in self.called)
728+ self.assertTrue(expected_path not in self.created_paths)
729+ self.assertTrue(('fake_exists', expected_path) in self.called)
730+
731+ def test_get_auth_temp_path(self):
732+ """Test the creation of the auth path."""
733+ self.path_exists = False
734+ expected_path = os.path.join(self.basedir, squid.AUTH_FILE)
735+ result = squid._get_auth_temp_path()
736+ self.assertEqual(expected_path, result)
737+ self.assertTrue(('fake_basedir_fn', '') in self.called)
738+ self.assertTrue(('fake_makedirs', self.basedir) in self.called)
739+ self.assertTrue(self.basedir in self.created_paths)
740+ self.assertTrue(('fake_exists', self.basedir) in self.called)
741+
742+
743+class EnvironTestCase(BaseTestCase):
744+ """Test the different environ functions."""
745+
746+ @defer.inlineCallbacks
747+ def setUp(self):
748+ """Set the tests."""
749+ yield super(EnvironTestCase, self).setUp()
750+ self.called = []
751+ self.settings = dict(noauth_port=3434, auth_port=232323,
752+ username='u1', password='test')
753+
754+ def fake_dumps(data):
755+ """Fake dumps."""
756+ self.called.append(('dumps', data))
757+ return json.dumps(data)
758+
759+ def fake_loads(data):
760+ """Fake loads."""
761+ self.called.append(('loads', data))
762+ return json.loads(data)
763+
764+ self.patch(squid, 'dumps', fake_dumps)
765+ self.patch(squid, 'loads', fake_loads)
766+ self.env = {}
767+ self.old_env = os.environ
768+ squid.environ = self.env
769+ self.addCleanup(self.set_back_environ)
770+
771+ def set_back_environ(self):
772+ """Set back the env."""
773+ squid.environ = self.old_env
774+
775+ def test_store_settings(self):
776+ """Test the storage of the settings."""
777+ squid.store_proxy_settings(self.settings)
778+ self.assertTrue(('dumps', self.settings) in self.called)
779+ self.assertEqual(self.env[squid.PROXY_ENV_VAR],
780+ json.dumps(self.settings))
781+
782+ def test_retrieve_proxy_settings(self):
783+ """Test reading the settings."""
784+ self.env[squid.PROXY_ENV_VAR] = json.dumps(self.settings)
785+ self.assertTrue(('loads', self.env[squid.PROXY_ENV_VAR]))
786+ self.assertEqual(squid.retrieve_proxy_settings(), self.settings)
787+
788+ def test_delete_proxy_settings_present(self):
789+ """Delete the proxy settings."""
790+ self.env[squid.PROXY_ENV_VAR] = json.dumps(self.settings)
791+ squid.delete_proxy_settings()
792+ self.assertFalse(squid.PROXY_ENV_VAR in self.env)
793+
794+
795+class SquidRunnerInitTestCase(BaseTestCase):
796+ """Test the creation of the runner."""
797+
798+ @defer.inlineCallbacks
799+ def setUp(self):
800+ """Set the different tests."""
801+ yield super(SquidRunnerInitTestCase, self).setUp()
802+ self.executables = {}
803+ self.called = []
804+
805+ def fake_find_executable(executable):
806+ """Fake the find executable."""
807+ self.called.append(('fake_find_executable', executable))
808+ return self.executables.get(executable, None)
809+
810+ self.patch(squid, 'find_executable', fake_find_executable)
811+
812+ def _assert_missing_binary(self, binary):
813+ """Perform the assertion when a bin is missing."""
814+ self.assertRaises(squid.SquidLaunchError, squid.SquidRunner)
815+ self.assertTrue(('fake_find_executable', binary) in self.called,
816+ self.called)
817+
818+ def test_squid_missing(self):
819+ """Test when squid is missing."""
820+ self.executables['htpasswd'] = 'htpasswd'
821+ self._assert_missing_binary('squid')
822+
823+ def test_htpasswd_missing(self):
824+ """Test when htpasswd is missing."""
825+ self.executables['squid'] = 'squid'
826+ self.executables['squid3'] = 'squid'
827+ self._assert_missing_binary('htpasswd')
828+
829+
830+class FakeSubprocess(object):
831+ """Fake the subprocess module."""
832+
833+ # pylint: disable=C0103
834+ def __init__(self):
835+ """Create a new instance."""
836+ self.called = []
837+ self.PIPE = 'PIPE'
838+
839+ def Popen(self, args, stdout=None, stderr=None):
840+ """Fake Popen."""
841+ self.called.append(('Popen', args, stdout, stderr))
842+ return self
843+ # pylint: enable=C0103
844+
845+ def wait(self):
846+ """Fake wait from a Popen object."""
847+ self.called.append(('wait',))
848+
849+
850+class FakeTemplate(object):
851+ """Fake the string.Template."""
852+
853+ def __init__(self):
854+ """Create a new instance."""
855+ self.data = None
856+ self.called = []
857+
858+ def __call__(self, data):
859+ """Fake constructor."""
860+ self.data = data
861+ return self
862+
863+ def safe_substitute(self, *args, **kwargs):
864+ """Fake the safe_substitute."""
865+ self.called.append(('safe_substitute', args, kwargs))
866+ return self.data
867+
868+
869+class SquidRunnerTestCase(BaseTestCase):
870+ """Test the default test case."""
871+
872+ @defer.inlineCallbacks
873+ def setUp(self):
874+ """Set the different tests."""
875+ yield super(SquidRunnerTestCase, self).setUp()
876+ self.subprocess = FakeSubprocess()
877+ self.patch(squid, 'subprocess', self.subprocess)
878+
879+ self.called = []
880+ self.executables = dict(squid='squid', htpasswd='htpasswd')
881+
882+ def fake_find_executable(executable):
883+ """Fake the find executable."""
884+ self.called.append(('fake_find_executable', executable))
885+ return self.executables.get(executable, None)
886+
887+ self.patch(squid, 'find_executable', fake_find_executable)
888+
889+ self.auth_temp = 'path/to/auth'
890+
891+ def fake_get_auth_temp_path(tempdir):
892+ """Return the path for the auth file."""
893+ self.called.append(('fake_get_auth_temp_path', tempdir))
894+ return self.auth_temp
895+
896+ self.patch(squid, '_get_auth_temp_path', fake_get_auth_temp_path)
897+
898+ self.port = 2324
899+
900+ def fake_get_port():
901+ """Fake the methos that returns the ports."""
902+ self.called.append(('fake_get_port',))
903+ return self.port
904+
905+ self.patch(squid, 'get_arbitrary_port', fake_get_port)
906+ self.template = FakeTemplate()
907+ self.patch(squid.string, 'Template', self.template)
908+ self.runner = squid.SquidRunner()
909+
910+ def test_generate_swap(self):
911+ """Test the generation of the squid swap."""
912+ config_file = 'path/to/config'
913+ expected_args = ['squid', '-z', '-f', config_file]
914+ self.runner._generate_swap(config_file)
915+ self.assertEqual(expected_args, self.subprocess.called[0][1])
916+ self.assertTrue('wait' in self.subprocess.called[1])
917+
918+ def test_generate_auth_file(self):
919+ """Test the generation of the auth file."""
920+ username = self.runner.settings['username'] = 'mandel'
921+ password = self.runner.settings['password'] = 'test'
922+ expected_args = ['htpasswd', '-bc', self.auth_temp,
923+ username, password]
924+ self.patch(squid, 'exists', lambda f: False)
925+ self.runner._generate_auth_file()
926+ self.assertEqual(expected_args, self.subprocess.called[0][1])
927+ self.assertTrue('wait' in self.subprocess.called[1])
928+
929+ def test_generate_config_file(self):
930+ """Test the generation of the config file."""
931+ self.runner.auth_file = self.auth_temp
932+ self.runner._generate_config_file(self.tmpdir)
933+ # remove the generated file
934+ self.addCleanup(os.unlink, self.runner.config_file)
935+ expected_parameters = ('safe_substitute', (),
936+ dict(auth_file=self.runner.auth_file,
937+ noauth_port_number=self.port,
938+ auth_port_number=self.port,
939+ spool_temp=squid._get_spool_temp_path(self.tmpdir),
940+ squid_temp=squid._get_squid_temp_path(self.tmpdir)))
941+ self.assertTrue(expected_parameters, self.template.called[0])
942+ self.assertEqual(2, self.called.count(('fake_get_port',)))
943
944=== modified file 'ubuntuone/devtools/testcases/dbus.py'
945--- ubuntuone/devtools/testcases/dbus.py 2011-11-23 11:14:00 +0000
946+++ ubuntuone/devtools/testcases/dbus.py 2012-01-17 21:43:26 +0000
947@@ -14,7 +14,7 @@
948 # You should have received a copy of the GNU General Public License along
949 # with this program. If not, see <http://www.gnu.org/licenses/>.
950
951-"""Base tests cases and test utilities."""
952+"""Base dbus tests cases and test utilities."""
953
954 from __future__ import absolute_import, with_statement
955
956
957=== added file 'ubuntuone/devtools/testcases/squid.py'
958--- ubuntuone/devtools/testcases/squid.py 1970-01-01 00:00:00 +0000
959+++ ubuntuone/devtools/testcases/squid.py 2012-01-17 21:43:26 +0000
960@@ -0,0 +1,58 @@
961+# -*- coding: utf-8 -*-
962+#
963+# Copyright 2011 Canonical Ltd.
964+#
965+# This program is free software: you can redistribute it and/or modify it
966+# under the terms of the GNU General Public License version 3, as published
967+# by the Free Software Foundation.
968+#
969+# This program is distributed in the hope that it will be useful, but
970+# WITHOUT ANY WARRANTY; without even the implied warranties of
971+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
972+# PURPOSE. See the GNU General Public License for more details.
973+#
974+# You should have received a copy of the GNU General Public License along
975+# with this program. If not, see <http://www.gnu.org/licenses/>.
976+
977+"""Base squid tests cases and test utilities."""
978+
979+from ubuntuone.devtools.testcase import BaseTestCase, skipIf
980+from ubuntuone.devtools.services.squid import (
981+ SquidRunner,
982+ SquidLaunchError,
983+ get_squid_executable,
984+ get_htpasswd_executable,
985+ retrieve_proxy_settings)
986+
987+# pylint: disable=C0103
988+squid, _ = get_squid_executable()
989+htpasswd = get_htpasswd_executable()
990+# pylint: enable=C0103
991+
992+
993+@skipIf(squid is None or htpasswd is None,
994+ 'The test requires squid and htpasswd.')
995+class SquidTestCase(BaseTestCase):
996+ """Test that uses a proxy."""
997+
998+ def required_services(self):
999+ """Return the list of required services for DBusTestCase."""
1000+ services = super(SquidTestCase, self).required_services()
1001+ services.extend([SquidRunner])
1002+ return services
1003+
1004+ def get_nonauth_proxy_settings(self):
1005+ """Return the settings of the noneauth proxy."""
1006+ settings = retrieve_proxy_settings()
1007+ if settings is None:
1008+ raise SquidLaunchError('Proxy is not running.')
1009+ return dict(host='localhost', port=settings['noauth_port'])
1010+
1011+ def get_auth_proxy_settings(self):
1012+ """Return the settings of the auth proxy."""
1013+ settings = retrieve_proxy_settings()
1014+ if settings is None:
1015+ raise SquidLaunchError('Proxy is not running.')
1016+ return dict(host='localhost', port=settings['auth_port'],
1017+ username=settings['username'],
1018+ password=settings['password'])
1019
1020=== added directory 'ubuntuone/devtools/testcases/tests'
1021=== added file 'ubuntuone/devtools/testcases/tests/__init__.py'
1022--- ubuntuone/devtools/testcases/tests/__init__.py 1970-01-01 00:00:00 +0000
1023+++ ubuntuone/devtools/testcases/tests/__init__.py 2012-01-17 21:43:26 +0000
1024@@ -0,0 +1,16 @@
1025+# -*- coding: utf-8 -*-
1026+#
1027+# Copyright 2011 Canonical Ltd.
1028+#
1029+# This program is free software: you can redistribute it and/or modify it
1030+# under the terms of the GNU General Public License version 3, as published
1031+# by the Free Software Foundation.
1032+#
1033+# This program is distributed in the hope that it will be useful, but
1034+# WITHOUT ANY WARRANTY; without even the implied warranties of
1035+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1036+# PURPOSE. See the GNU General Public License for more details.
1037+#
1038+# You should have received a copy of the GNU General Public License along
1039+# with this program. If not, see <http://www.gnu.org/licenses/>.
1040+"""Tests for the test cases."""
1041
1042=== added file 'ubuntuone/devtools/testcases/tests/test_squid_testcase.py'
1043--- ubuntuone/devtools/testcases/tests/test_squid_testcase.py 1970-01-01 00:00:00 +0000
1044+++ ubuntuone/devtools/testcases/tests/test_squid_testcase.py 2012-01-17 21:43:26 +0000
1045@@ -0,0 +1,262 @@
1046+# -*- coding: utf-8 -*-
1047+#
1048+# Copyright 2011 Canonical Ltd.
1049+#
1050+# This program is free software: you can redistribute it and/or modify it
1051+# under the terms of the GNU General Public License version 3, as published
1052+# by the Free Software Foundation.
1053+#
1054+# This program is distributed in the hope that it will be useful, but
1055+# WITHOUT ANY WARRANTY; without even the implied warranties of
1056+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1057+# PURPOSE. See the GNU General Public License for more details.
1058+#
1059+# You should have received a copy of the GNU General Public License along
1060+# with this program. If not, see <http://www.gnu.org/licenses/>.
1061+"""Test the squid test case."""
1062+import base64
1063+
1064+from twisted.application import internet, service
1065+from twisted.internet import defer, reactor
1066+from twisted.web import client, error, http, resource, server
1067+
1068+from ubuntuone.devtools.testcases.squid import SquidTestCase
1069+
1070+
1071+SAMPLE_RESOURCE = "<p>Hello World!</p>"
1072+SIMPLERESOURCE = "simpleresource"
1073+THROWERROR = "throwerror"
1074+UNAUTHORIZED = "unauthorized"
1075+
1076+# ignore common twisted lint errors
1077+# pylint: disable=C0103, W0212
1078+
1079+
1080+class ProxyClientFactory(client.HTTPClientFactory):
1081+ """Factory that supports proxy."""
1082+
1083+ def __init__(self, proxy_url, proxy_port, url, headers=None):
1084+ # we set the proxy details before the init because the parent __init__
1085+ # calls setURL
1086+ self.proxy_url = proxy_url
1087+ self.proxy_port = proxy_port
1088+ self.disconnected_d = defer.Deferred()
1089+ client.HTTPClientFactory.__init__(self, url, headers=headers)
1090+
1091+ def setURL(self, url):
1092+ self.host = self.proxy_url
1093+ self.port = self.proxy_port
1094+ self.url = url
1095+ self.path = url
1096+
1097+ def clientConnectionLost(self, connector, reason, reconnecting=0):
1098+ """Connection lost."""
1099+ self.disconnected_d.callback(self)
1100+
1101+
1102+class ProxyWebClient(object):
1103+ """Provide useful web methods with proxy."""
1104+
1105+ def __init__(self, proxy_url=None, proxy_port=None, username=None,
1106+ password=None):
1107+ """Create a new instance with the proxy settings."""
1108+ self.proxy_url = proxy_url
1109+ self.proxy_port = proxy_port
1110+ self.username = username
1111+ self.password = password
1112+ self.factory = None
1113+ self.connectors = []
1114+
1115+ def _connect(self, url, contextFactory):
1116+ """Perform the connection."""
1117+ scheme, _, _, _ = client._parse(url)
1118+ # pylint: disable=E1101
1119+ if scheme == 'https':
1120+ from twisted.internet import ssl
1121+ if contextFactory is None:
1122+ contextFactory = ssl.ClientContextFactory()
1123+ self.connectors.append(reactor.connectSSL(self.proxy_url,
1124+ self.proxy_port,
1125+ self.factory,
1126+ contextFactory))
1127+ else:
1128+ self.connectors.append(reactor.connectTCP(self.proxy_url,
1129+ self.proxy_port,
1130+ self.factory))
1131+ # pylint: enable=E1101
1132+
1133+ def _process_auth_error(self, failure, url, contextFactory):
1134+ """Process an auth failure."""
1135+ failure.trap(error.Error)
1136+ if failure.value.status == str(http.PROXY_AUTH_REQUIRED):
1137+ # we try to get the page using the basic auth
1138+ auth = base64.b64encode('%s:%s' % (self.username, self.password))
1139+ auth_header = 'Basic ' + auth.strip()
1140+ self.factory = ProxyClientFactory(self.proxy_url, self.proxy_port,
1141+ url, headers={'Proxy-Authorization': auth_header})
1142+ self._connect(url, contextFactory)
1143+ return self.factory.deferred
1144+ else:
1145+ return failure
1146+
1147+ def get_page(self, url, contextFactory=None, *args, **kwargs):
1148+ """Download a webpage as a string.
1149+
1150+ This method relies on the twisted.web.client.getPage but adds and extra
1151+ step. If there is an auth error the method will perform a second try
1152+ so that the username and password are used.
1153+ """
1154+ self.factory = ProxyClientFactory(self.proxy_url, self.proxy_port, url,
1155+ headers={'Connection': 'close'})
1156+ self._connect(url, contextFactory)
1157+ self.factory.deferred.addErrback(self._process_auth_error, url,
1158+ contextFactory)
1159+ return self.factory.deferred
1160+
1161+ @defer.inlineCallbacks
1162+ def shutdown(self):
1163+ """Clean all connectors."""
1164+ for connector in self.connectors:
1165+ yield connector.disconnect()
1166+ defer.returnValue(True)
1167+
1168+
1169+class SimpleResource(resource.Resource):
1170+ """A simple web resource."""
1171+
1172+ def render_GET(self, request):
1173+ """Make a bit of html out of these resource's
1174+ content."""
1175+ return SAMPLE_RESOURCE
1176+
1177+
1178+class SaveHTTPChannel(http.HTTPChannel):
1179+ """A save protocol to be used in tests."""
1180+
1181+ protocolInstance = None
1182+
1183+ def connectionMade(self):
1184+ """Keep track of the given protocol."""
1185+ SaveHTTPChannel.protocolInstance = self
1186+ http.HTTPChannel.connectionMade(self)
1187+
1188+
1189+class SaveSite(server.Site):
1190+ """A site that let us know when it closed."""
1191+
1192+ protocol = SaveHTTPChannel
1193+
1194+ def __init__(self, *args, **kwargs):
1195+ """Create a new instance."""
1196+ server.Site.__init__(self, *args, **kwargs)
1197+ # we disable the timeout in the tests, we will deal with it manually.
1198+ self.timeOut = None
1199+
1200+
1201+class MockWebServer(object):
1202+ """A mock webserver for testing"""
1203+
1204+ def __init__(self):
1205+ """Start up this instance."""
1206+ root = resource.Resource()
1207+ root.putChild(SIMPLERESOURCE, SimpleResource())
1208+
1209+ root.putChild(THROWERROR, resource.NoResource())
1210+
1211+ unauthorized_resource = resource.ErrorPage(resource.http.UNAUTHORIZED,
1212+ "Unauthorized", "Unauthorized")
1213+ root.putChild(UNAUTHORIZED, unauthorized_resource)
1214+
1215+ self.site = SaveSite(root)
1216+ application = service.Application('web')
1217+ self.service_collection = service.IServiceCollection(application)
1218+ #pylint: disable=E1101
1219+ self.tcpserver = internet.TCPServer(0, self.site)
1220+ self.tcpserver.setServiceParent(self.service_collection)
1221+ self.service_collection.startService()
1222+
1223+ def get_url(self):
1224+ """Build the url for this mock server."""
1225+ #pylint: disable=W0212
1226+ port_num = self.tcpserver._port.getHost().port
1227+ return "http://localhost:%d/" % port_num
1228+
1229+ @defer.inlineCallbacks
1230+ def stop(self):
1231+ """Shut it down."""
1232+ #pylint: disable=E1101
1233+ # make the connection time out so that is works with squid3 when
1234+ # the connection is kept alive.
1235+ if self.site.protocol.protocolInstance:
1236+ self.site.protocol.protocolInstance.timeoutConnection()
1237+ yield self.service_collection.stopService()
1238+
1239+
1240+class ProxyTestCase(SquidTestCase):
1241+ """A squid test with no auth proxy."""
1242+
1243+ @defer.inlineCallbacks
1244+ def setUp(self):
1245+ """Set the tests."""
1246+ yield super(ProxyTestCase, self).setUp()
1247+ self.ws = MockWebServer()
1248+ self.proxy_client = None
1249+ self.addCleanup(self.teardown_client_server)
1250+ self.url = self.ws.get_url() + SIMPLERESOURCE
1251+
1252+ def teardown_client_server(self):
1253+ """Clean resources."""
1254+ if self.proxy_client is not None:
1255+ self.proxy_client.shutdown()
1256+ return defer.gatherResults([self.ws.stop(),
1257+ self.proxy_client.shutdown(),
1258+ self.proxy_client.factory.disconnected_d])
1259+ else:
1260+ return self.ws.stop()
1261+
1262+ def access_noauth_url(self, address, port):
1263+ """Access a url throught the proxy."""
1264+ self.proxy_client = ProxyWebClient(proxy_url=address, proxy_port=port)
1265+ return self.proxy_client.get_page(self.url)
1266+
1267+ def access_auth_url(self, address, port, username, password):
1268+ """Access a url throught the proxy."""
1269+ self.proxy_client = ProxyWebClient(proxy_url=address, proxy_port=port,
1270+ username=username, password=password)
1271+ return self.proxy_client.get_page(self.url)
1272+
1273+ @defer.inlineCallbacks
1274+ def test_noauth_url_access(self):
1275+ """Test accessing to the url."""
1276+ settings = self.get_nonauth_proxy_settings()
1277+ # if there is an exception we fail.
1278+ data = yield self.access_noauth_url(settings['host'],
1279+ settings['port'])
1280+ self.assertEqual(SAMPLE_RESOURCE, data)
1281+
1282+ @defer.inlineCallbacks
1283+ def test_auth_url_access(self):
1284+ """Test accessing to the url."""
1285+ settings = self.get_auth_proxy_settings()
1286+ # if there is an exception we fail.
1287+ data = yield self.access_auth_url(settings['host'],
1288+ settings['port'],
1289+ settings['username'],
1290+ settings['password'])
1291+ self.assertEqual(SAMPLE_RESOURCE, data)
1292+
1293+ def test_auth_url_401(self):
1294+ """Test failing accessing the url."""
1295+ settings = self.get_auth_proxy_settings()
1296+ # swap password for username to fail
1297+ d = self.failUnlessFailure(self.access_auth_url(settings['host'],
1298+ settings['port'], settings['password'],
1299+ settings['username']), error.Error)
1300+ return d
1301+
1302+ def test_auth_url_407(self):
1303+ """Test failing accessing the url."""
1304+ settings = self.get_auth_proxy_settings()
1305+ d = self.failUnlessFailure(self.access_noauth_url(settings['host'],
1306+ settings['port']), error.Error)
1307+ return d

Subscribers

People subscribed via source and target branches

to all changes: