Skip to content

Commit 819669f

Browse files
committed
test: issue2551366. Probe for open port in test_liveserver.py
Add a method to probe for an open port to wsgi_liveserver.py. Start the roundup server under wsgi on the open port. If a port can't be found, it skips all tests. Also changed all hardcoded URL references to use the dynamicly determined tracker url/port value. I fed my patch to wsgi_liveserver.py upstream at: jerrykan/wsgi-liveserver#3
1 parent 7299214 commit 819669f

File tree

2 files changed

+72
-43
lines changed

2 files changed

+72
-43
lines changed

test/test_liveserver.py

Lines changed: 54 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,19 @@ class WsgiSetup(LiveServerTestCase):
7979
# have chicken and egg issue here. Need to encode the base_url
8080
# in the config file but we don't know it until after
8181
# the server is started and has read the config.ini.
82-
# so only allow one port number
83-
port_range = (9001, 9001) # default is (8080, 8090)
82+
# Probe for an unused port and set the port range to
83+
# include only that port.
84+
tracker_port = LiveServerTestCase.probe_ports(8080, 8100)
85+
if tracker_port is None:
86+
pytest.skip("Unable to find available port for server: 8080-8100",
87+
allow_module_level=True)
88+
port_range = (tracker_port, tracker_port)
89+
90+
# set a couple of properties to use for URL generation in
91+
# expected output or use to set TRACKER_WEB in config.ini.
92+
tracker_web = "http://localhost:%d/" % tracker_port
93+
# tracker_web_base should be the same as self.base_url()
94+
tracker_web_base = "http://localhost:%d" % tracker_port
8495

8596
dirname = '_test_instance'
8697
backend = 'anydbm'
@@ -106,7 +117,7 @@ def setup_class(cls):
106117
password=password.Password('sekrit'), address='[email protected]')
107118

108119
# set the url the test instance will run at.
109-
cls.db.config['TRACKER_WEB'] = "http://localhost:9001/"
120+
cls.db.config['TRACKER_WEB'] = cls.tracker_web
110121
# set up mailhost so errors get reported to debuging capture file
111122
cls.db.config.MAILHOST = "localhost"
112123
cls.db.config.MAIL_HOST = "localhost"
@@ -190,8 +201,9 @@ class ClientSetup():
190201
def create_login_session(self, username="admin", password="sekrit",
191202
return_response=True, expect_login_ok=True):
192203
# Set up session to manage cookies <insert blue monster here>
204+
193205
session = requests.Session()
194-
session.headers.update({'Origin': 'http://localhost:9001'})
206+
session.headers.update({'Origin': self.tracker_web_base})
195207

196208
# login using form to get cookie
197209
login = {"__login_name": username, '__login_password': password,
@@ -747,13 +759,13 @@ def test_rest_endpoint_root_options(self):
747759
f = requests.options(self.url_base() + '/rest',
748760
auth=('admin', 'sekrit'),
749761
headers = {'content-type': "",
750-
'Origin': "http://localhost:9001",
762+
'Origin': self.tracker_web_base,
751763
})
752764
print(f.status_code)
753765
print(f.headers)
754766

755767
self.assertEqual(f.status_code, 204)
756-
expected = { 'Access-Control-Allow-Origin': 'http://localhost:9001',
768+
expected = { 'Access-Control-Allow-Origin': self.tracker_web_base,
757769
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Requested-With, X-HTTP-Method-Override',
758770
'Allow': 'OPTIONS, GET',
759771
'Access-Control-Allow-Credentials': 'true',
@@ -770,13 +782,13 @@ def test_rest_endpoint_data_options(self):
770782
f = requests.options(self.url_base() + '/rest/data',
771783
auth=('admin', 'sekrit'),
772784
headers = {'content-type': "",
773-
'Origin': "http://localhost:9001",
785+
'Origin': self.tracker_web_base,
774786
})
775787
print(f.status_code)
776788
print(f.headers)
777789

778790
self.assertEqual(f.status_code, 204)
779-
expected = { 'Access-Control-Allow-Origin': 'http://localhost:9001',
791+
expected = { 'Access-Control-Allow-Origin': self.tracker_web_base,
780792
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Requested-With, X-HTTP-Method-Override',
781793
'Allow': 'OPTIONS, GET',
782794
'Access-Control-Allow-Methods': 'OPTIONS, GET',
@@ -792,13 +804,13 @@ def test_rest_endpoint_collection_options(self):
792804
f = requests.options(self.url_base() + '/rest/data/user',
793805
auth=('admin', 'sekrit'),
794806
headers = {'content-type': "",
795-
'Origin': "http://localhost:9001",
807+
'Origin': self.tracker_web_base,
796808
})
797809
print(f.status_code)
798810
print(f.headers)
799811

800812
self.assertEqual(f.status_code, 204)
801-
expected = { 'Access-Control-Allow-Origin': 'http://localhost:9001',
813+
expected = { 'Access-Control-Allow-Origin': self.tracker_web_base,
802814
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Requested-With, X-HTTP-Method-Override',
803815
'Allow': 'OPTIONS, GET, POST',
804816
'Access-Control-Allow-Methods': 'OPTIONS, GET, POST',
@@ -815,13 +827,13 @@ def test_rest_endpoint_item_options(self):
815827
f = requests.options(self.url_base() + '/rest/data/user/1',
816828
auth=('admin', 'sekrit'),
817829
headers = {'content-type': "",
818-
'Origin': "http://localhost:9001",
830+
'Origin': self.tracker_web_base,
819831
})
820832
print(f.status_code)
821833
print(f.headers)
822834

823835
self.assertEqual(f.status_code, 204)
824-
expected = { 'Access-Control-Allow-Origin': 'http://localhost:9001',
836+
expected = { 'Access-Control-Allow-Origin': self.tracker_web_base,
825837
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Requested-With, X-HTTP-Method-Override',
826838
'Allow': 'OPTIONS, GET, PUT, DELETE, PATCH',
827839
'Access-Control-Allow-Methods': 'OPTIONS, GET, PUT, DELETE, PATCH',
@@ -837,13 +849,13 @@ def test_rest_endpoint_attribute_options(self):
837849
f = requests.options(self.url_base() + '/rest/data/user/1/username',
838850
auth=('admin', 'sekrit'),
839851
headers = {'content-type': "",
840-
'Origin': "http://localhost:9001",
852+
'Origin': self.tracker_web_base,
841853
})
842854
print(f.status_code)
843855
print(f.headers)
844856

845857
self.assertEqual(f.status_code, 204)
846-
expected = { 'Access-Control-Allow-Origin': 'http://localhost:9001',
858+
expected = { 'Access-Control-Allow-Origin': self.tracker_web_base,
847859
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Requested-With, X-HTTP-Method-Override',
848860
'Allow': 'OPTIONS, GET, PUT, DELETE, PATCH',
849861
'Access-Control-Allow-Methods': 'OPTIONS, GET, PUT, DELETE, PATCH',
@@ -859,7 +871,7 @@ def test_rest_endpoint_attribute_options(self):
859871
f = requests.options(self.url_base() + '/rest/data/user/1/creator',
860872
auth=('admin', 'sekrit'),
861873
headers = {'content-type': "",
862-
'Origin': "http://localhost:9001",
874+
'Origin': self.tracker_web_base,
863875
})
864876
print(f.status_code)
865877
print(f.headers)
@@ -877,7 +889,7 @@ def test_rest_endpoint_attribute_options(self):
877889
f = requests.options(self.url_base() + '/rest/data/user/1/zot',
878890
auth=('admin', 'sekrit'),
879891
headers = {'content-type': "",
880-
'Origin': "http://localhost:9001",})
892+
'Origin': self.tracker_web_base,})
881893
print(f.status_code)
882894
print(f.headers)
883895

@@ -888,7 +900,7 @@ def test_rest_endpoint_user_roles(self):
888900
f = requests.get(self.url_base() + '/rest/data/user/roles',
889901
auth=('admin', 'sekrit'),
890902
headers = {'content-type': "",
891-
'Origin': "http://localhost:9001",
903+
'Origin': self.tracker_web_base,
892904
})
893905
print(f.status_code)
894906
print(f.headers)
@@ -1190,10 +1202,10 @@ def test_compression_none_etag(self):
11901202

11911203
content_str = '''{ "data": {
11921204
"id": "1",
1193-
"link": "http://localhost:9001/rest/data/user/1/username",
1205+
"link": "%s/rest/data/user/1/username",
11941206
"data": "admin"
11951207
}
1196-
}'''
1208+
}''' % self.tracker_web_base
11971209
content = json.loads(content_str)
11981210

11991211

@@ -1248,10 +1260,10 @@ def test_compression_gzip(self, method='gzip'):
12481260

12491261
content_str = '''{ "data": {
12501262
"id": "1",
1251-
"link": "http://localhost:9001/rest/data/user/1/username",
1263+
"link": "%s/rest/data/user/1/username",
12521264
"data": "admin"
12531265
}
1254-
}'''
1266+
}''' % self.tracker_web_base
12551267
content = json.loads(content_str)
12561268

12571269
print(f.content)
@@ -1496,7 +1508,7 @@ def test_new_issue_with_file_upload(self):
14961508
# Escape % signs in string by doubling them. This verifies the
14971509
# search is working correctly.
14981510
# use groupdict for python2.
1499-
self.assertEqual('http://localhost:9001/issue%(issue)s?@ok_message=file%%20%(file)s%%20created%%0Aissue%%20%(issue)s%%20created&@template=item'%m.groupdict(), f.url)
1511+
self.assertEqual( self.tracker_web_base + '/issue%(issue)s?@ok_message=file%%20%(file)s%%20created%%0Aissue%%20%(issue)s%%20created&@template=item'%m.groupdict(), f.url)
15001512

15011513
# we have an issue display, verify filename is listed there
15021514
# seach for unique filename given to it.
@@ -1520,21 +1532,21 @@ def test_new_file_via_rest(self):
15201532
c = dict (content = r'xyzzy')
15211533
r = session.post(url + 'file', files = c, data = d,
15221534
headers = {'x-requested-with': "rest",
1523-
'Origin': "http://localhost:9001"}
1535+
'Origin': self.tracker_web_base}
15241536
)
15251537

15261538
# was a 500 before fix for issue2551178
15271539
self.assertEqual(r.status_code, 201)
15281540
# just compare the path leave off the number
1529-
self.assertIn('http://localhost:9001/rest/data/file/',
1541+
self.assertIn(self.tracker_web_base + '/rest/data/file/',
15301542
r.headers["location"])
15311543
json_dict = json.loads(r.text)
15321544
self.assertEqual(json_dict["data"]["link"], r.headers["location"])
15331545

15341546
# download file and verify content
15351547
r = session.get(r.headers["location"] +'/content',
15361548
headers = {'x-requested-with': "rest",
1537-
'Origin': "http://localhost:9001"}
1549+
'Origin': self.tracker_web_base}
15381550
)
15391551
json_dict = json.loads(r.text)
15401552
self.assertEqual(json_dict['data']['data'], c["content"])
@@ -1544,7 +1556,7 @@ def test_new_file_via_rest(self):
15441556
session.auth = None
15451557
r = session.post(url + 'file', files = c, data = d,
15461558
headers = {'x-requested-with': "rest",
1547-
'Origin': "http://localhost:9001"}
1559+
'Origin': self.tracker_web_base}
15481560
)
15491561
self.assertEqual(r.status_code, 403)
15501562

@@ -1556,7 +1568,7 @@ def test_new_file_via_rest(self):
15561568

15571569
r = session.post(url + 'file', files = c, data = d,
15581570
headers = {'x-requested-with': "rest",
1559-
'Origin': "http://localhost:9001"}
1571+
'Origin': self.tracker_web_base}
15601572
)
15611573
self.assertEqual(r.status_code, 201)
15621574
print(r.status_code)
@@ -1611,7 +1623,7 @@ def setup_class(cls):
16111623
password=password.Password('sekrit'), address='[email protected]')
16121624

16131625
# set the url the test instance will run at.
1614-
cls.db.config['TRACKER_WEB'] = "http://localhost:9001/"
1626+
cls.db.config['TRACKER_WEB'] = cls.tracker_web
16151627
# set up mailhost so errors get reported to debuging capture file
16161628
cls.db.config.MAILHOST = "localhost"
16171629
cls.db.config.MAIL_HOST = "localhost"
@@ -1699,7 +1711,7 @@ def setup_class(cls):
16991711
password=password.Password('sekrit'), address='[email protected]')
17001712

17011713
# set the url the test instance will run at.
1702-
cls.db.config['TRACKER_WEB'] = "http://localhost:9001/"
1714+
cls.db.config['TRACKER_WEB'] = cls.tracker_web
17031715
# set up mailhost so errors get reported to debuging capture file
17041716
cls.db.config.MAILHOST = "localhost"
17051717
cls.db.config.MAIL_HOST = "localhost"
@@ -1761,7 +1773,7 @@ def test_rest_login_RateLimit(self):
17611773
# use basic auth for rest endpoint
17621774

17631775
request_headers = {'content-type': "",
1764-
'Origin': "http://localhost:9001",}
1776+
'Origin': self.tracker_web_base,}
17651777
f = requests.options(url_base_numeric + '/rest/data',
17661778
auth=('admin', 'sekrit'),
17671779
headers=request_headers
@@ -1798,7 +1810,7 @@ def test_rest_login_RateLimit(self):
17981810
f = requests.options(url_base_numeric + '/rest/data',
17991811
auth=('admin', 'ekrit'),
18001812
headers = {'content-type': "",
1801-
'Origin': "http://localhost:9001",}
1813+
'Origin': self.tracker_web_base,}
18021814
)
18031815

18041816
if (i < 4): # assuming limit is 4.
@@ -1835,7 +1847,7 @@ def test_rest_login_RateLimit(self):
18351847
f = requests.options(url_base_numeric + '/rest/data',
18361848
auth=('admin', 'sekrit'),
18371849
headers = {'content-type': "",
1838-
'Origin': "http://localhost:9001",}
1850+
'Origin': self.tracker_web_base,}
18391851
)
18401852
self.assertEqual(f.status_code, 429)
18411853

@@ -1850,7 +1862,7 @@ def test_rest_login_RateLimit(self):
18501862
f = requests.get(url_base_numeric + '/rest/data',
18511863
auth=('admin', 'sekrit'),
18521864
headers = {'content-type': "",
1853-
'Origin': "http://localhost:9001",}
1865+
'Origin': self.tracker_web_base,}
18541866
)
18551867
self.assertEqual(f.status_code, 200)
18561868
print(i, f.status_code)
@@ -1868,7 +1880,7 @@ def test_rest_login_RateLimit(self):
18681880
'Retry-After, '
18691881
'Sunset, '
18701882
'Allow'),
1871-
'Access-Control-Allow-Origin': 'http://localhost:9001',
1883+
'Access-Control-Allow-Origin': self.tracker_web_base,
18721884
'Access-Control-Allow-Credentials': 'true',
18731885
'Allow': 'OPTIONS, GET, POST, PUT, DELETE, PATCH'
18741886
}
@@ -1879,28 +1891,28 @@ def test_rest_login_RateLimit(self):
18791891

18801892
expected_data = {
18811893
"status": {
1882-
"link": "http://localhost:9001/rest/data/status"
1894+
"link": self.tracker_web_base + "/rest/data/status"
18831895
},
18841896
"keyword": {
1885-
"link": "http://localhost:9001/rest/data/keyword"
1897+
"link": self.tracker_web_base + "/rest/data/keyword"
18861898
},
18871899
"priority": {
1888-
"link": "http://localhost:9001/rest/data/priority"
1900+
"link": self.tracker_web_base + "/rest/data/priority"
18891901
},
18901902
"user": {
1891-
"link": "http://localhost:9001/rest/data/user"
1903+
"link": self.tracker_web_base + "/rest/data/user"
18921904
},
18931905
"file": {
1894-
"link": "http://localhost:9001/rest/data/file"
1906+
"link": self.tracker_web_base + "/rest/data/file"
18951907
},
18961908
"msg": {
1897-
"link": "http://localhost:9001/rest/data/msg"
1909+
"link": self.tracker_web_base + "/rest/data/msg"
18981910
},
18991911
"query": {
1900-
"link": "http://localhost:9001/rest/data/query"
1912+
"link": self.tracker_web_base + "/rest/data/query"
19011913
},
19021914
"issue": {
1903-
"link": "http://localhost:9001/rest/data/issue"
1915+
"link": self.tracker_web_base + "/rest/data/issue"
19041916
}
19051917
}
19061918

test/wsgi_liveserver.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
1010
Copyright (c) 2013 John Kristensen (unless explicitly stated otherwise).
1111
"""
12-
import threading
12+
import errno
1313
import socket
14+
import threading
1415
import unittest
1516
from wsgiref.simple_server import make_server, WSGIRequestHandler
1617

@@ -82,3 +83,19 @@ def _post_teardown(self):
8283
self._server.server_close()
8384
self._thread.join()
8485
del self._server
86+
87+
def probe_ports(start=port_range[0], end=port_range[1]):
88+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
89+
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
90+
port = start
91+
try:
92+
s.connect(('127.0.0.1', port))
93+
except socket.error as e:
94+
if not hasattr(e, 'args') or e.args[0] != errno.ECONNREFUSED:
95+
raise
96+
return port
97+
else:
98+
s.close()
99+
port += 1
100+
if port > end:
101+
return None

0 commit comments

Comments
 (0)