]>
Commit | Line | Data |
---|---|---|
040dc68e | 1 | #!/usr/bin/env python2 |
05955587 | 2 | # |
8107d333 | 3 | # Test case executor |
51c5aeb4 | 4 | # Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi> |
05955587 JM |
5 | # |
6 | # This software may be distributed under the terms of the BSD license. | |
7 | # See README for more details. | |
8 | ||
9 | import os | |
10 | import re | |
11 | import sys | |
12 | import time | |
1fc114a1 | 13 | from datetime import datetime |
0b1faa8b | 14 | import argparse |
435e37df | 15 | import subprocess |
1942b681 | 16 | import termios |
05955587 JM |
17 | |
18 | import logging | |
c9aa4308 | 19 | logger = logging.getLogger() |
05955587 | 20 | |
a302ee34 JM |
21 | try: |
22 | import sqlite3 | |
23 | sqlite3_imported = True | |
24 | except ImportError: | |
25 | sqlite3_imported = False | |
26 | ||
d2002f83 JB |
27 | scriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__)) |
28 | sys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy')) | |
b3e2ce3b | 29 | |
05955587 | 30 | from wpasupplicant import WpaSupplicant |
b8cd4c54 | 31 | from hostapd import HostapdGlobal |
5061a301 | 32 | from check_kernel import check_kernel |
848bb8de | 33 | from wlantest import Wlantest |
51c5aeb4 | 34 | from utils import HwsimSkip |
b8cd4c54 | 35 | |
1942b681 JM |
36 | def set_term_echo(fd, enabled): |
37 | [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] = termios.tcgetattr(fd) | |
38 | if enabled: | |
39 | lflag |= termios.ECHO | |
40 | else: | |
41 | lflag &= ~termios.ECHO | |
42 | termios.tcsetattr(fd, termios.TCSANOW, | |
43 | [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]) | |
44 | ||
ae3ad328 | 45 | def reset_devs(dev, apdev): |
66f0bdb4 | 46 | ok = True |
62d58f7a | 47 | for d in dev: |
789959c4 JM |
48 | try: |
49 | d.reset() | |
bab493b9 | 50 | except Exception as e: |
789959c4 | 51 | logger.info("Failed to reset device " + d.ifname) |
1fc114a1 | 52 | print str(e) |
66f0bdb4 | 53 | ok = False |
9489637b | 54 | |
a66d2248 | 55 | wpas = None |
9489637b | 56 | try: |
bb5d761c | 57 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5', monitor=False) |
eb92d389 JM |
58 | ifaces = wpas.global_request("INTERFACES").splitlines() |
59 | for iface in ifaces: | |
60 | if iface.startswith("wlan"): | |
61 | wpas.interface_remove(iface) | |
bab493b9 | 62 | except Exception as e: |
9489637b | 63 | pass |
a66d2248 JM |
64 | if wpas: |
65 | wpas.close_ctrl() | |
9489637b | 66 | |
66f0bdb4 JM |
67 | try: |
68 | hapd = HostapdGlobal() | |
f8949f5f | 69 | hapd.flush() |
1edc3190 JM |
70 | hapd.remove('wlan3-6') |
71 | hapd.remove('wlan3-5') | |
72 | hapd.remove('wlan3-4') | |
66f0bdb4 JM |
73 | hapd.remove('wlan3-3') |
74 | hapd.remove('wlan3-2') | |
75 | for ap in apdev: | |
76 | hapd.remove(ap['ifname']) | |
bab493b9 | 77 | except Exception as e: |
66f0bdb4 JM |
78 | logger.info("Failed to remove hostapd interface") |
79 | print str(e) | |
80 | ok = False | |
81 | return ok | |
05955587 | 82 | |
1d646f5e JM |
83 | def add_log_file(conn, test, run, type, path): |
84 | if not os.path.exists(path): | |
85 | return | |
86 | contents = None | |
87 | with open(path, 'r') as f: | |
88 | contents = f.read() | |
89 | if contents is None: | |
90 | return | |
91 | sql = "INSERT INTO logs(test,run,type,contents) VALUES(?, ?, ?, ?)" | |
a302ee34 | 92 | params = (test, run, type, sqlite3.Binary(contents)) |
1d646f5e JM |
93 | try: |
94 | conn.execute(sql, params) | |
95 | conn.commit() | |
bab493b9 | 96 | except Exception as e: |
1d646f5e JM |
97 | print "sqlite: " + str(e) |
98 | print "sql: %r" % (params, ) | |
99 | ||
9363f5e0 JM |
100 | def report(conn, prefill, build, commit, run, test, result, duration, logdir, |
101 | sql_commit=True): | |
b74b7e87 JM |
102 | if conn: |
103 | if not build: | |
104 | build = '' | |
105 | if not commit: | |
106 | commit = '' | |
a5d7da3f JB |
107 | if prefill: |
108 | conn.execute('DELETE FROM results WHERE test=? AND run=? AND result=?', (test, run, 'NOTRUN')) | |
781b65cf | 109 | sql = "INSERT INTO results(test,result,run,time,duration,build,commitid) VALUES(?, ?, ?, ?, ?, ?, ?)" |
a5d7da3f | 110 | params = (test, result, run, time.time(), duration, build, commit) |
b74b7e87 | 111 | try: |
781b65cf | 112 | conn.execute(sql, params) |
9363f5e0 JM |
113 | if sql_commit: |
114 | conn.commit() | |
bab493b9 | 115 | except Exception as e: |
b74b7e87 | 116 | print "sqlite: " + str(e) |
781b65cf | 117 | print "sql: %r" % (params, ) |
b74b7e87 | 118 | |
1d646f5e JM |
119 | if result == "FAIL": |
120 | for log in [ "log", "log0", "log1", "log2", "log3", "log5", | |
121 | "hostapd", "dmesg", "hwsim0", "hwsim0.pcapng" ]: | |
122 | add_log_file(conn, test, run, log, | |
123 | logdir + "/" + test + "." + log) | |
124 | ||
0d5a5404 | 125 | class DataCollector(object): |
2494bcef | 126 | def __init__(self, logdir, testname, args): |
0d5a5404 | 127 | self._logdir = logdir |
435e37df | 128 | self._testname = testname |
2494bcef VV |
129 | self._tracing = args.tracing |
130 | self._dmesg = args.dmesg | |
131 | self._dbus = args.dbus | |
435e37df | 132 | def __enter__(self): |
0d5a5404 | 133 | if self._tracing: |
2ca47da4 | 134 | output = os.path.abspath(os.path.join(self._logdir, '%s.dat' % (self._testname, ))) |
c4668009 | 135 | self._trace_cmd = subprocess.Popen(['trace-cmd', 'record', '-o', output, '-e', 'mac80211', '-e', 'cfg80211', '-e', 'printk', 'sh', '-c', 'echo STARTED ; read l'], |
0d5a5404 JB |
136 | stdin=subprocess.PIPE, |
137 | stdout=subprocess.PIPE, | |
138 | stderr=open('/dev/null', 'w'), | |
139 | cwd=self._logdir) | |
140 | l = self._trace_cmd.stdout.read(7) | |
b5008c2c | 141 | while self._trace_cmd.poll() is None and not 'STARTED' in l: |
0d5a5404 | 142 | l += self._trace_cmd.stdout.read(1) |
b5008c2c EA |
143 | res = self._trace_cmd.returncode |
144 | if res: | |
145 | print "Failed calling trace-cmd: returned exit status %d" % res | |
146 | sys.exit(1) | |
2494bcef VV |
147 | if self._dbus: |
148 | output = os.path.abspath(os.path.join(self._logdir, '%s.dbus' % (self._testname, ))) | |
149 | self._dbus_cmd = subprocess.Popen(['dbus-monitor', '--system'], | |
150 | stdout=open(output, 'w'), | |
151 | stderr=open('/dev/null', 'w'), | |
152 | cwd=self._logdir) | |
153 | res = self._dbus_cmd.returncode | |
154 | if res: | |
155 | print "Failed calling dbus-monitor: returned exit status %d" % res | |
156 | sys.exit(1) | |
435e37df | 157 | def __exit__(self, type, value, traceback): |
0d5a5404 JB |
158 | if self._tracing: |
159 | self._trace_cmd.stdin.write('DONE\n') | |
160 | self._trace_cmd.wait() | |
161 | if self._dmesg: | |
162 | output = os.path.join(self._logdir, '%s.dmesg' % (self._testname, )) | |
cfb5576d JM |
163 | num = 0 |
164 | while os.path.exists(output): | |
165 | output = os.path.join(self._logdir, '%s.dmesg-%d' % (self._testname, num)) | |
166 | num += 1 | |
c4668009 | 167 | subprocess.call(['dmesg', '-c'], stdout=open(output, 'w')) |
435e37df | 168 | |
57661377 JM |
169 | def rename_log(logdir, basename, testname, dev): |
170 | try: | |
171 | import getpass | |
172 | srcname = os.path.join(logdir, basename) | |
173 | dstname = os.path.join(logdir, testname + '.' + basename) | |
174 | num = 0 | |
175 | while os.path.exists(dstname): | |
176 | dstname = os.path.join(logdir, | |
177 | testname + '.' + basename + '-' + str(num)) | |
178 | num = num + 1 | |
179 | os.rename(srcname, dstname) | |
66f0bdb4 JM |
180 | if dev: |
181 | dev.relog() | |
c4668009 | 182 | subprocess.call(['chown', '-f', getpass.getuser(), srcname]) |
bab493b9 | 183 | except Exception as e: |
57661377 JM |
184 | logger.info("Failed to rename log files") |
185 | logger.info(e) | |
186 | ||
05955587 | 187 | def main(): |
0b1faa8b JB |
188 | tests = [] |
189 | test_modules = [] | |
d2002f83 | 190 | files = os.listdir(scriptsdir) |
cb8cb33e | 191 | for t in files: |
0b1faa8b JB |
192 | m = re.match(r'(test_.*)\.py$', t) |
193 | if m: | |
194 | logger.debug("Import test cases from " + t) | |
195 | mod = __import__(m.group(1)) | |
e9552262 | 196 | test_modules.append(mod.__name__.replace('test_', '', 1)) |
6ac593ec JM |
197 | for key,val in mod.__dict__.iteritems(): |
198 | if key.startswith("test_"): | |
199 | tests.append(val) | |
4cd8343f | 200 | test_names = list(set([t.__name__.replace('test_', '', 1) for t in tests])) |
0b1faa8b | 201 | |
b74b7e87 | 202 | run = None |
0b1faa8b JB |
203 | |
204 | parser = argparse.ArgumentParser(description='hwsim test runner') | |
b72c3002 | 205 | parser.add_argument('--logdir', metavar='<directory>', |
0141fa52 JB |
206 | help='log output directory for all other options, ' + |
207 | 'must be given if other log options are used') | |
0b1faa8b JB |
208 | group = parser.add_mutually_exclusive_group() |
209 | group.add_argument('-d', const=logging.DEBUG, action='store_const', | |
210 | dest='loglevel', default=logging.INFO, | |
211 | help="verbose debug output") | |
212 | group.add_argument('-q', const=logging.WARNING, action='store_const', | |
213 | dest='loglevel', help="be quiet") | |
0b1faa8b | 214 | |
0b1faa8b JB |
215 | parser.add_argument('-S', metavar='<sqlite3 db>', dest='database', |
216 | help='database to write results to') | |
a5d7da3f JB |
217 | parser.add_argument('--prefill-tests', action='store_true', dest='prefill', |
218 | help='prefill test database with NOTRUN before all tests') | |
aee9446d JB |
219 | parser.add_argument('--commit', metavar='<commit id>', |
220 | help='commit ID, only for database') | |
0b1faa8b JB |
221 | parser.add_argument('-b', metavar='<build>', dest='build', help='build ID') |
222 | parser.add_argument('-L', action='store_true', dest='update_tests_db', | |
223 | help='List tests (and update descriptions in DB)') | |
0141fa52 JB |
224 | parser.add_argument('-T', action='store_true', dest='tracing', |
225 | help='collect tracing per test case (in log directory)') | |
0d5a5404 JB |
226 | parser.add_argument('-D', action='store_true', dest='dmesg', |
227 | help='collect dmesg per test case (in log directory)') | |
2494bcef VV |
228 | parser.add_argument('--dbus', action='store_true', dest='dbus', |
229 | help='collect dbus per test case (in log directory)') | |
de684a2b JM |
230 | parser.add_argument('--shuffle-tests', action='store_true', |
231 | dest='shuffle_tests', | |
232 | help='Shuffle test cases to randomize order') | |
09e38c2f | 233 | parser.add_argument('--split', help='split tests for parallel execution (<server number>/<total servers>)') |
b5476d94 JM |
234 | parser.add_argument('--no-reset', action='store_true', dest='no_reset', |
235 | help='Do not reset devices at the end of the test') | |
91f3cf69 JM |
236 | parser.add_argument('--long', action='store_true', |
237 | help='Include test cases that take long time') | |
f72434b4 JB |
238 | parser.add_argument('-f', dest='testmodules', metavar='<test module>', |
239 | help='execute only tests from these test modules', | |
240 | type=str, choices=[[]] + test_modules, nargs='+') | |
3bfa5f10 IP |
241 | parser.add_argument('-l', metavar='<modules file>', dest='mfile', |
242 | help='test modules file name') | |
0d7456e9 JM |
243 | parser.add_argument('-i', action='store_true', dest='stdin_ctrl', |
244 | help='stdin-controlled test case execution') | |
0b1faa8b JB |
245 | parser.add_argument('tests', metavar='<test>', nargs='*', type=str, |
246 | help='tests to run (only valid without -f)', | |
247 | choices=[[]] + test_names) | |
248 | ||
249 | args = parser.parse_args() | |
250 | ||
3bfa5f10 IP |
251 | if (args.tests and args.testmodules) or (args.tests and args.mfile) or (args.testmodules and args.mfile): |
252 | print 'Invalid arguments - only one of (test, test modules, modules file) can be given.' | |
0b1faa8b JB |
253 | sys.exit(2) |
254 | ||
0b1faa8b | 255 | if args.database: |
a302ee34 JM |
256 | if not sqlite3_imported: |
257 | print "No sqlite3 module found" | |
258 | sys.exit(2) | |
0b1faa8b | 259 | conn = sqlite3.connect(args.database) |
63f83fac JB |
260 | conn.execute('CREATE TABLE IF NOT EXISTS results (test,result,run,time,duration,build,commitid)') |
261 | conn.execute('CREATE TABLE IF NOT EXISTS tests (test,description)') | |
1d646f5e | 262 | conn.execute('CREATE TABLE IF NOT EXISTS logs (test,run,type,contents)') |
0b1faa8b JB |
263 | else: |
264 | conn = None | |
7585f349 | 265 | |
b74b7e87 | 266 | if conn: |
6a5b4b1a | 267 | run = int(time.time()) |
b74b7e87 | 268 | |
5e3c0b02 JM |
269 | # read the modules from the modules file |
270 | if args.mfile: | |
0663ae22 JM |
271 | args.testmodules = [] |
272 | with open(args.mfile) as f: | |
273 | for line in f.readlines(): | |
274 | line = line.strip() | |
275 | if not line or line.startswith('#'): | |
276 | continue | |
277 | args.testmodules.append(line) | |
5e3c0b02 JM |
278 | |
279 | tests_to_run = [] | |
499c5e9d JM |
280 | if args.tests: |
281 | for selected in args.tests: | |
282 | for t in tests: | |
283 | name = t.__name__.replace('test_', '', 1) | |
284 | if name == selected: | |
285 | tests_to_run.append(t) | |
286 | else: | |
287 | for t in tests: | |
288 | name = t.__name__.replace('test_', '', 1) | |
289 | if args.testmodules: | |
290 | if not t.__module__.replace('test_', '', 1) in args.testmodules: | |
291 | continue | |
292 | tests_to_run.append(t) | |
5e3c0b02 | 293 | |
0b1faa8b | 294 | if args.update_tests_db: |
5e3c0b02 | 295 | for t in tests_to_run: |
4cd8343f | 296 | name = t.__name__.replace('test_', '', 1) |
aa5fcc55 JM |
297 | if t.__doc__ is None: |
298 | print name + " - MISSING DESCRIPTION" | |
299 | else: | |
300 | print name + " - " + t.__doc__ | |
f3be6eea | 301 | if conn: |
781b65cf | 302 | sql = 'INSERT OR REPLACE INTO tests(test,description) VALUES (?, ?)' |
4cd8343f | 303 | params = (name, t.__doc__) |
f3be6eea | 304 | try: |
781b65cf | 305 | conn.execute(sql, params) |
bab493b9 | 306 | except Exception as e: |
f3be6eea | 307 | print "sqlite: " + str(e) |
781b65cf | 308 | print "sql: %r" % (params,) |
f3be6eea JM |
309 | if conn: |
310 | conn.commit() | |
311 | conn.close() | |
9b00f24a JM |
312 | sys.exit(0) |
313 | ||
cb8cb33e JM |
314 | if not args.logdir: |
315 | if os.path.exists('logs/current'): | |
316 | args.logdir = 'logs/current' | |
317 | else: | |
318 | args.logdir = 'logs' | |
319 | ||
320 | # Write debug level log to a file and configurable verbosity to stdout | |
321 | logger.setLevel(logging.DEBUG) | |
322 | ||
323 | stdout_handler = logging.StreamHandler() | |
324 | stdout_handler.setLevel(args.loglevel) | |
325 | logger.addHandler(stdout_handler) | |
326 | ||
327 | file_name = os.path.join(args.logdir, 'run-tests.log') | |
328 | log_handler = logging.FileHandler(file_name) | |
329 | log_handler.setLevel(logging.DEBUG) | |
330 | fmt = "%(asctime)s %(levelname)s %(message)s" | |
331 | log_formatter = logging.Formatter(fmt) | |
332 | log_handler.setFormatter(log_formatter) | |
333 | logger.addHandler(log_handler) | |
05955587 | 334 | |
0fa28afe JM |
335 | dev0 = WpaSupplicant('wlan0', '/tmp/wpas-wlan0') |
336 | dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1') | |
337 | dev2 = WpaSupplicant('wlan2', '/tmp/wpas-wlan2') | |
0b20f514 | 338 | dev = [ dev0, dev1, dev2 ] |
ae3ad328 | 339 | apdev = [ ] |
ae3ad328 | 340 | apdev.append({"ifname": 'wlan3', "bssid": "02:00:00:00:03:00"}) |
0b20f514 | 341 | apdev.append({"ifname": 'wlan4', "bssid": "02:00:00:00:04:00"}) |
05955587 JM |
342 | |
343 | for d in dev: | |
344 | if not d.ping(): | |
3ed2814a | 345 | logger.info(d.ifname + ": No response from wpa_supplicant") |
05955587 | 346 | return |
3ed2814a | 347 | logger.info("DEV: " + d.ifname + ": " + d.p2p_dev_addr()) |
ae3ad328 | 348 | for ap in apdev: |
3ed2814a | 349 | logger.info("APDEV: " + ap['ifname']) |
05955587 | 350 | |
05955587 | 351 | passed = [] |
0d84c400 | 352 | skipped = [] |
05955587 JM |
353 | failed = [] |
354 | ||
8f946511 JB |
355 | # make sure nothing is left over from previous runs |
356 | # (if there were any other manual runs or we crashed) | |
66f0bdb4 JM |
357 | if not reset_devs(dev, apdev): |
358 | if conn: | |
359 | conn.close() | |
360 | conn = None | |
361 | sys.exit(1) | |
8f946511 | 362 | |
0d5a5404 | 363 | if args.dmesg: |
c4668009 | 364 | subprocess.call(['dmesg', '-c'], stdout=open('/dev/null', 'w')) |
0d5a5404 | 365 | |
a5d7da3f JB |
366 | if conn and args.prefill: |
367 | for t in tests_to_run: | |
368 | name = t.__name__.replace('test_', '', 1) | |
1d646f5e | 369 | report(conn, False, args.build, args.commit, run, name, 'NOTRUN', 0, |
9363f5e0 JM |
370 | args.logdir, sql_commit=False) |
371 | conn.commit() | |
a5d7da3f | 372 | |
09e38c2f JM |
373 | if args.split: |
374 | vals = args.split.split('/') | |
375 | split_server = int(vals[0]) | |
376 | split_total = int(vals[1]) | |
377 | logger.info("Parallel execution - %d/%d" % (split_server, split_total)) | |
378 | split_server -= 1 | |
379 | tests_to_run.sort(key=lambda t: t.__name__) | |
380 | tests_to_run = [x for i,x in enumerate(tests_to_run) if i % split_total == split_server] | |
381 | ||
de684a2b JM |
382 | if args.shuffle_tests: |
383 | from random import shuffle | |
384 | shuffle(tests_to_run) | |
385 | ||
b1448311 | 386 | count = 0 |
0d7456e9 JM |
387 | if args.stdin_ctrl: |
388 | print "READY" | |
389 | sys.stdout.flush() | |
f6d1c5d9 | 390 | num_tests = 0 |
0d7456e9 | 391 | else: |
f6d1c5d9 | 392 | num_tests = len(tests_to_run) |
1942b681 JM |
393 | if args.stdin_ctrl: |
394 | set_term_echo(sys.stdin.fileno(), False) | |
c55799e5 JM |
395 | |
396 | check_country_00 = True | |
397 | for d in dev: | |
398 | if d.get_driver_status_field("country") != "00": | |
399 | check_country_00 = False | |
400 | ||
0d7456e9 JM |
401 | while True: |
402 | if args.stdin_ctrl: | |
403 | test = sys.stdin.readline() | |
404 | if not test: | |
405 | break | |
406 | test = test.splitlines()[0] | |
407 | if test == '': | |
408 | break | |
409 | t = None | |
410 | for tt in tests: | |
411 | name = tt.__name__.replace('test_', '', 1) | |
412 | if name == test: | |
413 | t = tt | |
414 | break | |
415 | if not t: | |
416 | print "NOT-FOUND" | |
417 | sys.stdout.flush() | |
418 | continue | |
419 | else: | |
f6d1c5d9 | 420 | if len(tests_to_run) == 0: |
0d7456e9 | 421 | break |
f6d1c5d9 | 422 | t = tests_to_run.pop(0) |
0d7456e9 | 423 | |
c55799e5 JM |
424 | if dev[0].get_driver_status_field("country") == "98": |
425 | # Work around cfg80211 regulatory issues in clearing intersected | |
426 | # country code 98. Need to make station disconnect without any | |
427 | # other wiphy being active in the system. | |
428 | logger.info("country=98 workaround - try to clear state") | |
429 | id = dev[1].add_network() | |
430 | dev[1].set_network(id, "mode", "2") | |
431 | dev[1].set_network_quoted(id, "ssid", "country98") | |
432 | dev[1].set_network(id, "key_mgmt", "NONE") | |
433 | dev[1].set_network(id, "frequency", "2412") | |
434 | dev[1].set_network(id, "scan_freq", "2412") | |
435 | dev[1].select_network(id) | |
436 | ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"]) | |
437 | if ev: | |
438 | dev[0].connect("country98", key_mgmt="NONE", scan_freq="2412") | |
439 | dev[1].request("DISCONNECT") | |
440 | dev[0].wait_disconnected() | |
441 | dev[0].request("DISCONNECT") | |
442 | dev[0].request("ABORT_SCAN") | |
443 | time.sleep(1) | |
444 | dev[0].reset() | |
445 | dev[1].reset() | |
446 | dev[0].dump_monitor() | |
447 | dev[1].dump_monitor() | |
448 | ||
a5d7da3f | 449 | name = t.__name__.replace('test_', '', 1) |
f2a438f5 | 450 | open('/dev/kmsg', 'w').write('running hwsim test case %s\n' % name) |
c9aa4308 JM |
451 | if log_handler: |
452 | log_handler.stream.close() | |
453 | logger.removeHandler(log_handler) | |
454 | file_name = os.path.join(args.logdir, name + '.log') | |
455 | log_handler = logging.FileHandler(file_name) | |
8f47f31d | 456 | log_handler.setLevel(logging.DEBUG) |
c9aa4308 JM |
457 | log_handler.setFormatter(log_formatter) |
458 | logger.addHandler(log_handler) | |
459 | ||
66f0bdb4 | 460 | reset_ok = True |
2494bcef | 461 | with DataCollector(args.logdir, name, args): |
b1448311 | 462 | count = count + 1 |
f6d1c5d9 | 463 | msg = "START {} {}/{}".format(name, count, num_tests) |
b1448311 | 464 | logger.info(msg) |
8f47f31d | 465 | if args.loglevel == logging.WARNING: |
b1448311 | 466 | print msg |
f9174c3b | 467 | sys.stdout.flush() |
435e37df JB |
468 | if t.__doc__: |
469 | logger.info("Test: " + t.__doc__) | |
470 | start = datetime.now() | |
8171d704 | 471 | open('/dev/kmsg', 'w').write('TEST-START %s @%.6f\n' % (name, time.time())) |
435e37df JB |
472 | for d in dev: |
473 | try: | |
521b7e79 JM |
474 | d.dump_monitor() |
475 | if not d.ping(): | |
476 | raise Exception("PING failed for {}".format(d.ifname)) | |
477 | if not d.global_ping(): | |
478 | raise Exception("Global PING failed for {}".format(d.ifname)) | |
4cd8343f | 479 | d.request("NOTE TEST-START " + name) |
bab493b9 | 480 | except Exception as e: |
4cd8343f | 481 | logger.info("Failed to issue TEST-START before " + name + " for " + d.ifname) |
435e37df | 482 | logger.info(e) |
4cd8343f | 483 | print "FAIL " + name + " - could not start test" |
435e37df JB |
484 | if conn: |
485 | conn.close() | |
486 | conn = None | |
1942b681 JM |
487 | if args.stdin_ctrl: |
488 | set_term_echo(sys.stdin.fileno(), True) | |
435e37df | 489 | sys.exit(1) |
4896ddae | 490 | try: |
90073916 JM |
491 | if t.func_code.co_argcount > 2: |
492 | params = {} | |
493 | params['logdir'] = args.logdir | |
91f3cf69 | 494 | params['long'] = args.long |
81e787b7 | 495 | t(dev, apdev, params) |
90073916 | 496 | elif t.func_code.co_argcount > 1: |
81e787b7 | 497 | t(dev, apdev) |
435e37df | 498 | else: |
81e787b7 JM |
499 | t(dev) |
500 | result = "PASS" | |
c55799e5 JM |
501 | if check_country_00: |
502 | for d in dev: | |
503 | country = d.get_driver_status_field("country") | |
504 | if country != "00": | |
505 | d.dump_monitor() | |
506 | logger.info("Country code not reset back to 00: is " + country) | |
507 | print "Country code not reset back to 00: is " + country | |
508 | result = "FAIL" | |
509 | ||
510 | # Try to wait for cfg80211 regulatory state to | |
511 | # clear. | |
512 | d.cmd_execute(['iw', 'reg', 'set', '00']) | |
513 | for i in range(5): | |
514 | time.sleep(1) | |
515 | country = d.get_driver_status_field("country") | |
516 | if country == "00": | |
517 | break | |
518 | if country == "00": | |
519 | print "Country code cleared back to 00" | |
520 | logger.info("Country code cleared back to 00") | |
521 | else: | |
522 | print "Country code remains set - expect following test cases to fail" | |
523 | logger.info("Country code remains set - expect following test cases to fail") | |
524 | break | |
bab493b9 | 525 | except HwsimSkip as e: |
51c5aeb4 JM |
526 | logger.info("Skip test case: %s" % e) |
527 | result = "SKIP" | |
bab493b9 | 528 | except NameError as e: |
1a4f18d8 JB |
529 | import traceback |
530 | logger.info(e) | |
531 | traceback.print_exc() | |
532 | result = "FAIL" | |
bab493b9 | 533 | except Exception as e: |
2147b3a7 | 534 | import traceback |
51c5aeb4 | 535 | logger.info(e) |
2147b3a7 | 536 | traceback.print_exc() |
51c5aeb4 JM |
537 | if args.loglevel == logging.WARNING: |
538 | print "Exception: " + str(e) | |
539 | result = "FAIL" | |
8171d704 | 540 | open('/dev/kmsg', 'w').write('TEST-STOP %s @%.6f\n' % (name, time.time())) |
435e37df JB |
541 | for d in dev: |
542 | try: | |
521b7e79 | 543 | d.dump_monitor() |
4cd8343f | 544 | d.request("NOTE TEST-STOP " + name) |
bab493b9 | 545 | except Exception as e: |
f5f59e4f | 546 | logger.info("Failed to issue TEST-STOP after {} for {}".format(name, d.ifname)) |
435e37df | 547 | logger.info(e) |
66f0bdb4 | 548 | result = "FAIL" |
78a37200 JM |
549 | if args.no_reset: |
550 | print "Leaving devices in current state" | |
551 | else: | |
552 | reset_ok = reset_devs(dev, apdev) | |
a66d2248 | 553 | wpas = None |
9489637b | 554 | try: |
a66d2248 | 555 | wpas = WpaSupplicant(global_iface="/tmp/wpas-wlan5") |
9489637b JM |
556 | rename_log(args.logdir, 'log5', name, wpas) |
557 | if not args.no_reset: | |
558 | wpas.remove_ifname() | |
bab493b9 | 559 | except Exception as e: |
9489637b | 560 | pass |
a66d2248 JM |
561 | if wpas: |
562 | wpas.close_ctrl() | |
05955587 | 563 | |
353e5067 | 564 | for i in range(0, 3): |
57661377 | 565 | rename_log(args.logdir, 'log' + str(i), name, dev[i]) |
66f0bdb4 JM |
566 | try: |
567 | hapd = HostapdGlobal() | |
bab493b9 | 568 | except Exception as e: |
66f0bdb4 JM |
569 | print "Failed to connect to hostapd interface" |
570 | print str(e) | |
571 | reset_ok = False | |
572 | result = "FAIL" | |
573 | hapd = None | |
57661377 | 574 | rename_log(args.logdir, 'hostapd', name, hapd) |
1f9427c9 JM |
575 | if hapd: |
576 | del hapd | |
577 | hapd = None | |
75428961 | 578 | |
8efc83d4 JA |
579 | # Use None here since this instance of Wlantest() will never be |
580 | # used for remote host hwsim tests on real hardware. | |
581 | Wlantest.setup(None) | |
848bb8de JM |
582 | wt = Wlantest() |
583 | rename_log(args.logdir, 'hwsim0.pcapng', name, wt) | |
584 | rename_log(args.logdir, 'hwsim0', name, wt) | |
41a256ec AN |
585 | if os.path.exists(os.path.join(args.logdir, 'fst-wpa_supplicant')): |
586 | rename_log(args.logdir, 'fst-wpa_supplicant', name, None) | |
587 | if os.path.exists(os.path.join(args.logdir, 'fst-hostapd')): | |
588 | rename_log(args.logdir, 'fst-hostapd', name, None) | |
d760db19 MH |
589 | if os.path.exists(os.path.join(args.logdir, 'wmediumd.log')): |
590 | rename_log(args.logdir, 'wmediumd.log', name, None) | |
848bb8de | 591 | |
4a1b8901 JB |
592 | end = datetime.now() |
593 | diff = end - start | |
5061a301 JB |
594 | |
595 | if result == 'PASS' and args.dmesg: | |
596 | if not check_kernel(os.path.join(args.logdir, name + '.dmesg')): | |
bcbd7415 | 597 | logger.info("Kernel issue found in dmesg - mark test failed") |
5061a301 JB |
598 | result = 'FAIL' |
599 | ||
600 | if result == 'PASS': | |
601 | passed.append(name) | |
602 | elif result == 'SKIP': | |
603 | skipped.append(name) | |
604 | else: | |
605 | failed.append(name) | |
606 | ||
1d646f5e JM |
607 | report(conn, args.prefill, args.build, args.commit, run, name, result, |
608 | diff.total_seconds(), args.logdir) | |
f5f59e4f | 609 | result = "{} {} {} {}".format(result, name, diff.total_seconds(), end) |
4a1b8901 | 610 | logger.info(result) |
8f47f31d | 611 | if args.loglevel == logging.WARNING: |
4a1b8901 JB |
612 | print result |
613 | sys.stdout.flush() | |
614 | ||
66f0bdb4 JM |
615 | if not reset_ok: |
616 | print "Terminating early due to device reset failure" | |
617 | break | |
1942b681 JM |
618 | if args.stdin_ctrl: |
619 | set_term_echo(sys.stdin.fileno(), True) | |
66f0bdb4 | 620 | |
c9aa4308 JM |
621 | if log_handler: |
622 | log_handler.stream.close() | |
623 | logger.removeHandler(log_handler) | |
624 | file_name = os.path.join(args.logdir, 'run-tests.log') | |
625 | log_handler = logging.FileHandler(file_name) | |
8f47f31d | 626 | log_handler.setLevel(logging.DEBUG) |
c9aa4308 JM |
627 | log_handler.setFormatter(log_formatter) |
628 | logger.addHandler(log_handler) | |
629 | ||
b74b7e87 JM |
630 | if conn: |
631 | conn.close() | |
632 | ||
05955587 | 633 | if len(failed): |
f5f59e4f JM |
634 | logger.info("passed {} test case(s)".format(len(passed))) |
635 | logger.info("skipped {} test case(s)".format(len(skipped))) | |
a572f0b9 | 636 | logger.info("failed tests: " + ' '.join(failed)) |
8f47f31d | 637 | if args.loglevel == logging.WARNING: |
a572f0b9 | 638 | print "failed tests: " + ' '.join(failed) |
05955587 | 639 | sys.exit(1) |
f5f59e4f | 640 | logger.info("passed all {} test case(s)".format(len(passed))) |
0d84c400 | 641 | if len(skipped): |
f5f59e4f | 642 | logger.info("skipped {} test case(s)".format(len(skipped))) |
8f47f31d | 643 | if args.loglevel == logging.WARNING: |
f5f59e4f | 644 | print "passed all {} test case(s)".format(len(passed)) |
0d84c400 | 645 | if len(skipped): |
f5f59e4f | 646 | print "skipped {} test case(s)".format(len(skipped)) |
05955587 JM |
647 | |
648 | if __name__ == "__main__": | |
649 | main() |