1#!/usr/bin/env python3
2
3import os
4import sys
5import subprocess
6import socket
7import atexit
8import signal
9import time
10import urllib
11
12import pytest
13
14RUNNER = "charteps"
15# MODULE = 'charteps'
16
17FIRST_PORT = 39000
18LAST_PORT = 39999
19
20LOCALHOST = "127.0.0.1"
21
22# Number of seconds to wait for the internal web server process to start up
23MAX_SERVER_START_TIME = 15
24
25
26def pytest_runtest_setup():
27 """Hackish way to ensure the project and core can be imported.
28
29 Otherwise you have to manually set PYTHONPATH before running pytest."""
30 # always add current project location to interpreter path
31 proj_root = os.path.dirname(os.path.dirname(__file__))
32 # print('pre adding {p} to sys path'.format(p=proj_root))
33 sys.path.insert(0, proj_root)
34
35 # the user might have pointed PYTHONPATH to a core install so look for it
36 # we cannot, for caching reasons, look for it using "import chart"
37 found_core = False
38 for dirname in sys.path:
39 if os.path.exists(os.path.join(dirname, "chart", "__init__.py")):
40 found_core = True
41 break
42
43 if not found_core:
44 core_root = os.path.join(proj_root, "chart")
45 # print('adding {p} to sys path'.format(p=core_root))
46 sys.path.insert(0, core_root)
47
48 # from chart.common.shell import spawn
49
50
51# there is a neat way of naming and placing a function so pytest always calls it first
52# but I can't find a proper way so we rely on the fact this module at least gets imported
53pytest_runtest_setup()
54
55
56def pytest_addoption(parser):
57 group = parser.getgroup("server")
58 group.addoption(
59 "--local",
60 action="store_true",
61 default=True,
62 help="Run local webserver (default)",
63 )
64 group.addoption("--url", help="Test against remote server")
65 group.addoption("--env", help="Test against OPE/VAL/TST/EPP/EPPVAL system")
66
67
68@pytest.fixture(scope="session")
69def webserver(request):
70 """Spawn a webserver, finding a free socket to run on. Return (how?) the socket number.
71
72 Automatic shutdown. Test fails on any server error code or empty ."""
73 config_local = request.config.getoption("local")
74 config_url = request.config.getoption("url")
75 config_env = request.config.getoption("env")
76 if config_url:
77 print("webserver remote url", config_url)
78 url = config_url
79
80 elif config_env:
81 print("webserver env", config_env)
82 url = {
83 "OPE": "http://chart/eps/",
84 "VAL": "http://chart/val/eps/",
85 "TST": "http://chart/tst/eps/",
86 }[config_env.upper()]
87 else:
88 print("webserver spawn")
89 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
90 # sock.settimeout(1)
91 port = None
92 for p in range(FIRST_PORT, LAST_PORT):
93 # res = sock.connect_ex((LOCALHOST, p))
94 try:
95 sock.bind((LOCALHOST, p))
96 sock.listen(1)
97 sock.close()
98 port = p
99 break
100
101 except socket.error as e:
102 pass
103 # print('sock port',p,'res',res)
104 # if res == 0:
105 # break
106
107 if not port:
108 raise ValueError("Cannot find free port to run local server")
109
110 url = "http://{host}:{port}/test/".format(host=LOCALHOST, port=p)
111 from chart.common.shell import spawn
112
113 cmd = "charteps serve -p {port} --prefix test/".format(port=p)
114 # print('spawning')
115 os.setpgrp()
116
117 def ending():
118 os.killpg(0, signal.SIGKILL)
119
120 atexit.register(ending)
121 spawn(cmd.split(" "), linger=True)
122 # print('donespawn')
123 tries = 0
124 while True:
125 # print('try',url)
126 try:
127 res = urllib.request.urlopen(url)
128 break
129 except IOError as e:
130 print(e)
131 pass
132
133 if tries > MAX_SERVER_START_TIME:
134 raise ValueError("Cannot start internal web server")
135
136 tries += 1
137 time.sleep(1)
138
139 return {"base_url": url}
140
141
142# webserver.port = None
143
144
145@pytest.fixture
146def temp_dir():
147 pass
148
149
150@pytest.fixture
151def test_database():
152 # read from settings system including environment variables and command line parameters
153 pass
154
155
156@pytest.fixture
157def operational_database():
158 # read from settings system including environment variables and command line parameters
159 pass
160
161
162@pytest.fixture
163def product():
164 # parmeterised with product filename
165 pass
166
167
168@pytest.fixture
169def ts():
170 # parameterised with 1+ table(s), sid and time range
171 pass
172
173
174@pytest.fixture
175def tool():
176 def run(cmd, expect_fail=False):
177 command = [RUNNER] + cmd.split(" \t") # + ' ' + cmd # [str(i) for i in cmd]
178 # command = ['charteps', 'info']
179 child = subprocess.Popen(command)
180 # stdout=stdout,
181 # stderr=stderr)
182 res = child.communicate()
183 retcode = child.returncode
184 assert retcode == 0
185
186 return run