Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / test / fixtures_ws.py
blobc76393fd467a72404bd1e4c3eb07eed18e7882f1
2 # Wireshark tests
4 # Copyright (c) 2018 Peter Wu <peter@lekensteyn.nl>
6 # SPDX-License-Identifier: GPL-2.0-or-later
8 '''Fixtures that are specific to Wireshark.'''
10 from contextlib import contextmanager
11 import os
12 import re
13 import subprocess
14 import sys
15 import types
16 import pytest
17 import shutil
19 @pytest.fixture(scope='session')
20 def capture_interface(request, cmd_dumpcap):
21 '''
22 Name of capture interface. Tests will be skipped if dumpcap is not
23 available or no Loopback interface is available.
24 '''
25 disabled = request.config.getoption('--disable-capture', default=False)
26 if disabled:
27 pytest.skip('Capture tests are disabled via --disable-capture')
28 proc = subprocess.Popen((cmd_dumpcap, '-D'), stdout=subprocess.PIPE,
29 stderr=subprocess.PIPE, universal_newlines=True)
30 outs, errs = proc.communicate()
31 if proc.returncode != 0:
32 print('"dumpcap -D" exited with %d. stderr:\n%s' %
33 (proc.returncode, errs))
34 pytest.skip('Test requires capture privileges and an interface.')
35 # Matches: "lo (Loopback)" (Linux), "lo0 (Loopback)" (macOS) or
36 # "\Device\NPF_{...} (Npcap Loopback Adapter)" (Windows)
37 print('"dumpcap -D" output:\n%s' % (outs,))
38 m = re.search(r'^(\d+)\. .*\(.*Loopback.*\)', outs, re.MULTILINE|re.IGNORECASE)
39 if not m:
40 pytest.skip('Test requires a capture interface.')
41 iface = m.group(1)
42 # Interface found, check for capture privileges (needed for Linux).
43 try:
44 subprocess.check_output((cmd_dumpcap, '-L', '-i', iface),
45 stderr=subprocess.STDOUT,
46 universal_newlines=True)
47 return iface
48 except subprocess.CalledProcessError as e:
49 print('"dumpcap -L -i %s" exited with %d. Output:\n%s' % (iface,
50 e.returncode,
51 e.output))
52 pytest.skip('Test requires capture privileges.')
55 @pytest.fixture(scope='session')
56 def program_path(request):
57 '''
58 Path to the Wireshark binaries as set by the --program-path option, the
59 WS_BIN_PATH environment variable or (curdir)/run.
60 '''
61 curdir_run = os.path.join(os.curdir, 'run')
62 if sys.platform == 'win32':
63 build_type = request.config.getoption('--build-type')
64 curdir_run_config = os.path.join(curdir_run, build_type)
65 if os.path.exists(curdir_run_config):
66 curdir_run = curdir_run_config
67 paths = (
68 request.config.getoption('--program-path', default=None),
69 os.environ.get('WS_BIN_PATH'),
70 curdir_run,
72 for path in paths:
73 if type(path) == str and os.path.isdir(path):
74 return path
75 raise AssertionError('Missing directory with Wireshark binaries')
78 @pytest.fixture(scope='session')
79 def program(program_path, request):
80 skip_if_missing = request.config.getoption('--skip-missing-programs',
81 default='')
82 skip_if_missing = skip_if_missing.split(',') if skip_if_missing else []
83 dotexe = ''
84 if sys.platform.startswith('win32'):
85 dotexe = '.exe'
87 def resolver(name):
88 path = os.path.abspath(os.path.join(program_path, name + dotexe))
89 if not os.access(path, os.X_OK):
90 if skip_if_missing == ['all'] or name in skip_if_missing:
91 pytest.skip('Program %s is not available' % (name,))
92 raise AssertionError('Program %s is not available' % (name,))
93 return path
94 return resolver
97 @pytest.fixture(scope='session')
98 def cmd_capinfos(program):
99 return program('capinfos')
102 @pytest.fixture(scope='session')
103 def cmd_dumpcap(program):
104 return program('dumpcap')
107 @pytest.fixture(scope='session')
108 def cmd_mergecap(program):
109 return program('mergecap')
112 @pytest.fixture(scope='session')
113 def cmd_rawshark(program):
114 return program('rawshark')
117 @pytest.fixture(scope='session')
118 def cmd_tshark(program):
119 return program('tshark')
122 @pytest.fixture(scope='session')
123 def cmd_text2pcap(program):
124 return program('text2pcap')
127 @pytest.fixture(scope='session')
128 def cmd_editcap(program):
129 return program('editcap')
132 @pytest.fixture(scope='session')
133 def cmd_wireshark(program):
134 return program('wireshark')
137 @pytest.fixture(scope='session')
138 def wireshark_command(cmd_wireshark):
139 # Windows can always display the GUI and macOS can if we're in a login session.
140 # On Linux, headless mode is used, see QT_QPA_PLATFORM in the 'test_env' fixture.
141 if sys.platform == 'darwin' and 'SECURITYSESSIONID' not in os.environ:
142 pytest.skip('Wireshark GUI tests require loginwindow session')
143 if sys.platform not in ('win32', 'darwin', 'linux'):
144 if 'DISPLAY' not in os.environ:
145 pytest.skip('Wireshark GUI tests require DISPLAY')
146 return (cmd_wireshark, '-ogui.update.enabled:FALSE')
149 @pytest.fixture(scope='session')
150 def cmd_extcap(program):
151 def extcap_name(name, stratoshark_extcap=False):
152 if stratoshark_extcap:
153 if sys.platform == 'darwin':
154 return program(os.path.join('Stratoshark.app/Contents/MacOS/extcap', name))
155 else:
156 return program(os.path.join('extcap/stratoshark', name))
157 else:
158 if sys.platform == 'darwin':
159 return program(os.path.join('Wireshark.app/Contents/MacOS/extcap', name))
160 else:
161 return program(os.path.join('extcap/wireshark', name))
162 return extcap_name
165 @pytest.fixture(scope='session')
166 def features(cmd_tshark, make_env):
167 '''Returns an object describing available features in tshark.'''
168 try:
169 tshark_v = subprocess.check_output(
170 (cmd_tshark, '--version'),
171 stderr=subprocess.PIPE,
172 universal_newlines=True,
173 env=make_env()
175 tshark_v = re.sub(r'\s+', ' ', tshark_v)
176 except subprocess.CalledProcessError as ex:
177 print('Failed to detect tshark features: %s' % (ex,))
178 tshark_v = ''
179 gcry_m = re.search(r'\+Gcrypt +([0-9]+)\.([0-9]+)', tshark_v)
180 gcry_ver = (int(gcry_m.group(1)),int(gcry_m.group(2)))
181 return types.SimpleNamespace(
182 have_x64='Compiler info: 64-bit' in tshark_v,
183 have_lua='+Lua' in tshark_v,
184 have_lua_unicode='(with UfW patches)' in tshark_v,
185 have_nghttp2='+nghttp2' in tshark_v,
186 have_nghttp3='+nghttp3' in tshark_v,
187 have_kerberos='+Kerberos' in tshark_v,
188 have_gnutls='+GnuTLS' in tshark_v,
189 have_pkcs11='PKCS #11' in tshark_v,
190 have_brotli='+brotli' in tshark_v,
191 have_zstd='+Zstandard' in tshark_v,
192 have_plugins='Plugins: supported' in tshark_v,
196 @pytest.fixture(scope='session')
197 def dirs():
198 '''Returns fixed directories containing test input.'''
199 this_dir = os.path.dirname(__file__)
200 return types.SimpleNamespace(
201 baseline_dir=os.path.join(this_dir, 'baseline'),
202 capture_dir=os.path.join(this_dir, 'captures'),
203 config_dir=os.path.join(this_dir, 'config'),
204 key_dir=os.path.join(this_dir, 'keys'),
205 lua_dir=os.path.join(this_dir, 'lua'),
206 protobuf_lang_files_dir=os.path.join(this_dir, 'protobuf_lang_files'),
207 tools_dir=os.path.join(this_dir, '..', 'tools'),
208 dfilter_dir=os.path.join(this_dir, 'suite_dfilter'),
212 @pytest.fixture(scope='session')
213 def capture_file(dirs):
214 '''Returns the path to a capture file.'''
215 def resolver(filename):
216 return os.path.join(dirs.capture_dir, filename)
217 return resolver
219 @pytest.fixture
220 def result_file(tmp_path):
221 '''Returns the path to a temporary file.'''
222 def result_file_real(filename):
223 return str(tmp_path / filename)
224 return result_file_real
226 @pytest.fixture
227 def home_path(tmp_path):
228 '''Per-test home directory.'''
229 return str(tmp_path / 'test-home')
231 @pytest.fixture
232 def conf_path(home_path):
233 '''Path to the Wireshark configuration directory.'''
234 if sys.platform.startswith('win32'):
235 conf_path = os.path.join(home_path, 'Wireshark')
236 else:
237 conf_path = os.path.join(home_path, '.config', 'wireshark')
238 os.makedirs(conf_path)
239 return conf_path
242 @pytest.fixture(scope='session')
243 def make_env():
244 """A factory for a modified environment to ensure reproducible tests."""
245 def make_env_real(home=None):
246 env = os.environ.copy()
247 env['TZ'] = 'UTC'
248 home_env = 'APPDATA' if sys.platform.startswith('win32') else 'HOME'
249 if home:
250 env[home_env] = home
251 else:
252 # This directory is supposed not to be written and is used by
253 # "readonly" tests that do not read any other preferences.
254 env[home_env] = "/wireshark-tests-unused"
255 # XDG_CONFIG_HOME takes precedence over HOME, which we don't want.
256 try:
257 del env['XDG_CONFIG_HOME']
258 except KeyError:
259 pass
260 return env
261 return make_env_real
264 @pytest.fixture
265 def base_env(home_path, make_env, request):
266 """A modified environment to ensure reproducible tests. Tests can modify
267 this environment as they see fit."""
268 env = make_env(home=home_path)
270 return env
273 @pytest.fixture
274 def test_env(base_env, conf_path, request, dirs):
275 '''A process environment with a populated configuration directory.'''
276 # Populate our UAT files
277 uat_files = [
278 '80211_keys',
279 'dtlsdecrypttablefile',
280 'esp_sa',
281 'ssl_keys',
282 'c1222_decryption_table',
283 'ikev1_decryption_table',
284 'ikev2_decryption_table',
286 # uat.c replaces backslashes...
287 key_dir_path = os.path.join(dirs.key_dir, '').replace('\\', '\\x5c')
288 for uat in uat_files:
289 template_file = os.path.join(dirs.config_dir, uat + '.tmpl')
290 out_file = os.path.join(conf_path, uat)
291 with open(template_file, 'r') as f:
292 template_contents = f.read()
293 cf_contents = template_contents.replace('TEST_KEYS_DIR', key_dir_path)
294 with open(out_file, 'w') as f:
295 f.write(cf_contents)
297 env = base_env
298 env['WIRESHARK_RUN_FROM_BUILD_DIRECTORY'] = '1'
299 env['WIRESHARK_QUIT_AFTER_CAPTURE'] = '1'
301 # Allow GUI tests to be run without opening windows nor requiring a Xserver.
302 # Set envvar QT_DEBUG_BACKINGSTORE=1 to save the window contents to a file
303 # in the current directory, output0000.png, output0001.png, etc. Note that
304 # this will overwrite existing files.
305 if sys.platform == 'linux':
306 # This option was verified working on Arch Linux with Qt 5.12.0-2 and
307 # Ubuntu 16.04 with libqt5gui5 5.5.1+dfsg-16ubuntu7.5. On macOS and
308 # Windows it unfortunately crashes (Qt 5.12.0).
309 env['QT_QPA_PLATFORM'] = 'minimal'
311 return env
314 @pytest.fixture
315 def test_env_80211_user_tk(base_env, conf_path, request, dirs):
316 '''A process environment with a populated configuration directory.'''
317 # Populate our UAT files
318 uat_files = [
319 '80211_keys',
321 # uat.c replaces backslashes...
322 key_dir_path = os.path.join(dirs.key_dir, '').replace('\\', '\\x5c')
323 for uat in uat_files:
324 template_file = os.path.join(dirs.config_dir, uat + '.user_tk_tmpl')
325 out_file = os.path.join(conf_path, uat)
326 with open(template_file, 'r') as f:
327 template_contents = f.read()
328 cf_contents = template_contents.replace('TEST_KEYS_DIR', key_dir_path)
329 with open(out_file, 'w') as f:
330 f.write(cf_contents)
332 env = base_env
333 env['WIRESHARK_RUN_FROM_BUILD_DIRECTORY'] = '1'
334 env['WIRESHARK_QUIT_AFTER_CAPTURE'] = '1'
336 # Allow GUI tests to be run without opening windows nor requiring a Xserver.
337 # Set envvar QT_DEBUG_BACKINGSTORE=1 to save the window contents to a file
338 # in the current directory, output0000.png, output0001.png, etc. Note that
339 # this will overwrite existing files.
340 if sys.platform == 'linux':
341 # This option was verified working on Arch Linux with Qt 5.12.0-2 and
342 # Ubuntu 16.04 with libqt5gui5 5.5.1+dfsg-16ubuntu7.5. On macOS and
343 # Windows it unfortunately crashes (Qt 5.12.0).
344 env['QT_QPA_PLATFORM'] = 'minimal'
346 return env
348 @pytest.fixture
349 def dfilter_env(base_env, conf_path, request, dirs):
350 '''A process environment with a populated configuration directory.'''
351 src_macro_path = os.path.join(dirs.dfilter_dir, 'test_dmacros')
352 dst_macro_path = os.path.join(conf_path, 'dmacros')
353 shutil.copy(src_macro_path, dst_macro_path)
355 env = base_env
356 return env
358 @pytest.fixture
359 def unicode_env(home_path, make_env):
360 '''A Wireshark configuration directory with Unicode in its path.'''
361 home_env = 'APPDATA' if sys.platform.startswith('win32') else 'HOME'
362 uni_home = os.path.join(home_path, 'unicode-Ф-€-中-testcases')
363 env = make_env(home=uni_home)
364 if sys.platform == 'win32':
365 pluginsdir = os.path.join(uni_home, 'Wireshark', 'plugins')
366 else:
367 pluginsdir = os.path.join(uni_home, '.local/lib/wireshark/plugins')
368 os.makedirs(pluginsdir)
369 return types.SimpleNamespace(
370 path=lambda *args: os.path.join(uni_home, *args),
371 env=env,
372 pluginsdir=pluginsdir
376 @pytest.fixture(scope='session')
377 def make_screenshot():
378 '''Creates a screenshot and save it to a file. Intended for CI purposes.'''
379 def make_screenshot_real(filename):
380 try:
381 if sys.platform == 'darwin':
382 subprocess.check_call(['screencapture', filename])
383 else:
384 print("Creating a screenshot on this platform is not supported")
385 return
386 size = os.path.getsize(filename)
387 print("Created screenshot %s (%d bytes)" % (filename, size))
388 except (subprocess.CalledProcessError, OSError) as e:
389 print("Failed to take screenshot:", e)
390 return make_screenshot_real
393 @pytest.fixture
394 def make_screenshot_on_error(request, make_screenshot, result_file):
395 '''Writes a screenshot when a process times out.'''
396 @contextmanager
397 def make_screenshot_on_error_real():
398 try:
399 yield
400 except subprocess.TimeoutExpired:
401 filename = result_file('screenshot.png')
402 make_screenshot(filename)
403 raise
404 return make_screenshot_on_error_real