test: finalize suite_capture conversion to fixtures, drop config.py
[metze/wireshark/wip.git] / test / fixtures_ws.py
1 #
2 # -*- coding: utf-8 -*-
3 # Wireshark tests
4 #
5 # Copyright (c) 2018 Peter Wu <peter@lekensteyn.nl>
6 #
7 # SPDX-License-Identifier: GPL-2.0-or-later
8 #
9 '''Fixtures that are specific to Wireshark.'''
10
11 import logging
12 import os
13 import re
14 import subprocess
15 import sys
16 import tempfile
17 import types
18
19 import fixtures
20 import subprocesstest
21
22
23 @fixtures.fixture(scope='session')
24 def capture_interface(request, cmd_dumpcap):
25     '''
26     Name of capture interface. Tests will be skipped if dumpcap is not
27     available or if the capture interface is unknown.
28     '''
29     iface = request.config.getoption('--capture-interface', default=None)
30     disabled = request.config.getoption('--disable-capture', default=False)
31     if disabled:
32         fixtures.skip('Capture tests are disabled via --disable-capture')
33     if iface:
34         # If a non-empty interface is given, assume capturing is possible.
35         return iface
36     else:
37         if sys.platform == 'win32':
38             patterns = '.*(Ethernet|Network Connection|VMware|Intel)'
39         else:
40             patterns = None
41         if patterns:
42             try:
43                 output = subprocess.check_output((cmd_dumpcap, '-D'),
44                                                  stderr=subprocess.DEVNULL,
45                                                  universal_newlines=True)
46                 m = re.search(r'^(\d+)\. %s' % patterns, output, re.MULTILINE)
47                 if m:
48                     return m.group(1)
49             except subprocess.CalledProcessError:
50                 pass
51     fixtures.skip('Test requires capture privileges and an interface.')
52
53
54 @fixtures.fixture(scope='session')
55 def program_path(request):
56     '''
57     Path to the Wireshark binaries as set by the --program-path option, the
58     WS_BIN_PATH environment variable or (curdir)/run.
59     '''
60     paths = (
61         request.config.getoption('--program-path', default=None),
62         os.environ.get('WS_BIN_PATH'),
63         os.path.join(os.curdir, 'run'),
64     )
65     for path in paths:
66         if type(path) == str and os.path.isdir(path):
67             return path
68     raise AssertionError('Missing directory with Wireshark binaries')
69
70
71 @fixtures.fixture(scope='session')
72 def program(program_path):
73     def resolver(name):
74         dotexe = ''
75         if sys.platform.startswith('win32'):
76             dotexe = '.exe'
77         path = os.path.normpath(os.path.join(program_path, name + dotexe))
78         if not os.access(path, os.X_OK):
79             fixtures.skip('Program %s is not available' % (name,))
80         return path
81     return resolver
82
83
84 @fixtures.fixture(scope='session')
85 def cmd_capinfos(program):
86     return program('capinfos')
87
88
89 @fixtures.fixture(scope='session')
90 def cmd_dumpcap(program):
91     return program('dumpcap')
92
93
94 @fixtures.fixture(scope='session')
95 def cmd_mergecap(program):
96     return program('mergecap')
97
98
99 @fixtures.fixture(scope='session')
100 def cmd_rawshark(program):
101     return program('rawshark')
102
103
104 @fixtures.fixture(scope='session')
105 def cmd_tshark(program):
106     return program('tshark')
107
108
109 @fixtures.fixture(scope='session')
110 def cmd_text2pcap(program):
111     return program('text2pcap')
112
113
114 @fixtures.fixture(scope='session')
115 def cmd_wireshark(program):
116     return program('wireshark')
117
118
119 @fixtures.fixture(scope='session')
120 def wireshark_command(cmd_wireshark):
121     if sys.platform not in ('win32', 'darwin'):
122         # TODO check DISPLAY for X11 on Linux or BSD?
123         fixtures.skip('Wireshark GUI tests requires DISPLAY')
124     return (cmd_wireshark, '-ogui.update.enabled:FALSE')
125
126
127 @fixtures.fixture(scope='session')
128 def features(cmd_tshark, make_env):
129     '''Returns an object describing available features in tshark.'''
130     try:
131         tshark_v = subprocess.check_output(
132             (cmd_tshark, '--version'),
133             stderr=subprocess.PIPE,
134             universal_newlines=True,
135             env=make_env()
136         )
137         tshark_v = re.sub(r'\s+', ' ', tshark_v)
138     except subprocess.CalledProcessError as ex:
139         logging.warning('Failed to detect tshark features: %s', ex)
140         tshark_v = ''
141     gcry_m = re.search(r'with +Gcrypt +([0-9]+\.[0-9]+)', tshark_v)
142     return types.SimpleNamespace(
143         have_lua='with Lua' in tshark_v,
144         have_nghttp2='with nghttp2' in tshark_v,
145         have_kerberos='with MIT Kerberos' in tshark_v or 'with Heimdal Kerberos' in tshark_v,
146         have_libgcrypt16=gcry_m and float(gcry_m.group(1)) >= 1.6,
147         have_libgcrypt17=gcry_m and float(gcry_m.group(1)) >= 1.7,
148     )
149
150
151 @fixtures.fixture(scope='session')
152 def dirs():
153     '''Returns fixed directories containing test input.'''
154     this_dir = os.path.dirname(__file__)
155     return types.SimpleNamespace(
156         baseline_dir=os.path.join(this_dir, 'baseline'),
157         capture_dir=os.path.join(this_dir, 'captures'),
158         config_dir=os.path.join(this_dir, 'config'),
159         key_dir=os.path.join(this_dir, 'keys'),
160         lua_dir=os.path.join(this_dir, 'lua'),
161         tools_dir=os.path.join(this_dir, '..', 'tools'),
162     )
163
164
165 @fixtures.fixture(scope='session')
166 def capture_file(dirs):
167     '''Returns the path to a capture file.'''
168     def resolver(filename):
169         return os.path.join(dirs.capture_dir, filename)
170     return resolver
171
172
173 @fixtures.fixture
174 def home_path():
175     '''Per-test home directory, removed when finished.'''
176     with tempfile.TemporaryDirectory(prefix='wireshark-tests-home-') as dirname:
177         yield dirname
178
179
180 @fixtures.fixture
181 def conf_path(home_path):
182     '''Path to the Wireshark configuration directory.'''
183     if sys.platform.startswith('win32'):
184         conf_path = os.path.join(home_path, 'Wireshark')
185     else:
186         conf_path = os.path.join(home_path, '.config', 'wireshark')
187     os.makedirs(conf_path)
188     return conf_path
189
190
191 @fixtures.fixture(scope='session')
192 def make_env():
193     """A factory for a modified environment to ensure reproducible tests."""
194     def make_env_real(home=None):
195         env = os.environ.copy()
196         env['TZ'] = 'UTC'
197         home_env = 'APPDATA' if sys.platform.startswith('win32') else 'HOME'
198         if home:
199             env[home_env] = home
200         else:
201             # This directory is supposed not to be written and is used by
202             # "readonly" tests that do not read any other preferences.
203             env[home_env] = "/wireshark-tests-unused"
204         return env
205     return make_env_real
206
207
208 @fixtures.fixture
209 def base_env(home_path, make_env, request):
210     """A modified environment to ensure reproducible tests. Tests can modify
211     this environment as they see fit."""
212     env = make_env(home=home_path)
213
214     # Remove this if test instances no longer inherit from SubprocessTestCase?
215     if isinstance(request.instance, subprocesstest.SubprocessTestCase):
216         # Inject the test environment as default if it was not overridden.
217         request.instance.injected_test_env = env
218     return env
219
220
221 @fixtures.fixture
222 def test_env(base_env, conf_path, request, dirs):
223     '''A process environment with a populated configuration directory.'''
224     # Populate our UAT files
225     uat_files = [
226         '80211_keys',
227         'dtlsdecrypttablefile',
228         'esp_sa',
229         'ssl_keys',
230         'c1222_decryption_table',
231         'ikev1_decryption_table',
232         'ikev2_decryption_table',
233     ]
234     # uat.c replaces backslashes...
235     key_dir_path = os.path.join(dirs.key_dir, '').replace('\\', '\\x5c')
236     for uat in uat_files:
237         template_file = os.path.join(dirs.config_dir, uat + '.tmpl')
238         out_file = os.path.join(conf_path, uat)
239         with open(template_file, 'r') as f:
240             template_contents = f.read()
241         cf_contents = template_contents.replace('TEST_KEYS_DIR', key_dir_path)
242         with open(out_file, 'w') as f:
243             f.write(cf_contents)
244
245     env = base_env
246     env['WIRESHARK_RUN_FROM_BUILD_DIRECTORY'] = '1'
247     env['WIRESHARK_QUIT_AFTER_CAPTURE'] = '1'
248
249     # Remove this if test instances no longer inherit from SubprocessTestCase?
250     if isinstance(request.instance, subprocesstest.SubprocessTestCase):
251         # Inject the test environment as default if it was not overridden.
252         request.instance.injected_test_env = env
253     return env