2019-02-12 09:59:40 +01:00
#!/usr/bin/env python3
2013-03-27 13:40:10 +01:00
#
2014-02-21 19:25:25 +01:00
# Test case executor
2019-03-17 14:26:34 +01:00
# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
2013-03-27 13:40:10 +01:00
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
import os
import re
tests: Work around reentrant logging issues due to __del__ misuse
Unfortunately, some objects (WlantestCapture, WpaSupplicant
and wpaspy.Ctrl) use __del__ and actually have some logic
there. This is more or less wrong, and we should be using
context managers for it. However, cleaning that up is a
pretty large task.
Unfortunately, __del__ can cause reentrant logging which is
wrong too, because it might be invoked while in the middle
of a logging call, and the __del__ of these objects closes
connections and logs while doing that.
Since we're (likely) using cpython, we can work around this
by explicitly calling gc.collect() in a context where the
logging and close is fine, not only ensuring that all the
connections are closed properly before the next test, but
also fixing the issue with reentrant logging.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2022-05-22 10:46:09 +02:00
import gc
2013-03-27 13:40:10 +01:00
import sys
import time
2013-09-28 17:20:32 +02:00
from datetime import datetime
2013-10-30 18:28:22 +01:00
import argparse
2013-10-30 21:05:24 +01:00
import subprocess
2014-12-24 10:53:14 +01:00
import termios
2013-03-27 13:40:10 +01:00
import logging
2013-10-31 11:46:42 +01:00
logger = logging . getLogger ( )
2013-03-27 13:40:10 +01:00
2015-09-07 15:53:23 +02:00
try :
import sqlite3
sqlite3_imported = True
except ImportError :
sqlite3_imported = False
2015-03-03 23:08:41 +01:00
scriptsdir = os . path . dirname ( os . path . realpath ( sys . modules [ __name__ ] . __file__ ) )
sys . path . append ( os . path . join ( scriptsdir , ' .. ' , ' .. ' , ' wpaspy ' ) )
2013-10-27 17:28:28 +01:00
2013-03-27 13:40:10 +01:00
from wpasupplicant import WpaSupplicant
2013-03-29 17:37:03 +01:00
from hostapd import HostapdGlobal
2013-11-04 10:00:36 +01:00
from check_kernel import check_kernel
2013-11-17 20:25:17 +01:00
from wlantest import Wlantest
2015-01-07 12:41:31 +01:00
from utils import HwsimSkip
2013-03-29 17:37:03 +01:00
2014-12-24 10:53:14 +01:00
def set_term_echo ( fd , enabled ) :
[ iflag , oflag , cflag , lflag , ispeed , ospeed , cc ] = termios . tcgetattr ( fd )
if enabled :
lflag | = termios . ECHO
else :
lflag & = ~ termios . ECHO
termios . tcsetattr ( fd , termios . TCSANOW ,
[ iflag , oflag , cflag , lflag , ispeed , ospeed , cc ] )
2013-03-31 15:16:37 +02:00
def reset_devs ( dev , apdev ) :
2013-11-05 12:21:58 +01:00
ok = True
2013-03-31 19:32:33 +02:00
for d in dev :
2013-08-24 20:16:04 +02:00
try :
d . reset ( )
2019-01-24 08:45:41 +01:00
except Exception as e :
2013-08-24 20:16:04 +02:00
logger . info ( " Failed to reset device " + d . ifname )
2019-01-24 08:45:42 +01:00
print ( str ( e ) )
2013-11-05 12:21:58 +01:00
ok = False
2013-12-30 22:08:25 +01:00
2015-01-18 16:13:55 +01:00
wpas = None
2013-12-30 22:08:25 +01:00
try :
2016-12-14 12:56:15 +01:00
wpas = WpaSupplicant ( global_iface = ' /tmp/wpas-wlan5 ' , monitor = False )
2014-10-20 11:38:43 +02:00
ifaces = wpas . global_request ( " INTERFACES " ) . splitlines ( )
for iface in ifaces :
if iface . startswith ( " wlan " ) :
wpas . interface_remove ( iface )
2019-01-24 08:45:41 +01:00
except Exception as e :
2013-12-30 22:08:25 +01:00
pass
2015-01-18 16:13:55 +01:00
if wpas :
wpas . close_ctrl ( )
2019-03-17 14:26:34 +01:00
del wpas
2013-12-30 22:08:25 +01:00
2013-11-05 12:21:58 +01:00
try :
hapd = HostapdGlobal ( )
2013-11-28 14:50:47 +01:00
hapd . flush ( )
2023-02-16 00:09:03 +01:00
ifaces = hapd . request ( " INTERFACES " ) . splitlines ( )
for iface in ifaces :
if iface . startswith ( " wlan " ) :
hapd . remove ( iface )
2019-05-25 18:07:11 +02:00
hapd . remove ( ' as-erp ' )
2019-01-24 08:45:41 +01:00
except Exception as e :
2013-11-05 12:21:58 +01:00
logger . info ( " Failed to remove hostapd interface " )
2019-01-24 08:45:42 +01:00
print ( str ( e ) )
2013-11-05 12:21:58 +01:00
ok = False
return ok
2013-03-27 13:40:10 +01:00
2014-01-04 15:36:10 +01:00
def add_log_file ( conn , test , run , type , path ) :
if not os . path . exists ( path ) :
return
contents = None
2019-02-02 12:08:08 +01:00
with open ( path , ' rb ' ) as f :
2014-01-04 15:36:10 +01:00
contents = f . read ( )
if contents is None :
return
sql = " INSERT INTO logs(test,run,type,contents) VALUES(?, ?, ?, ?) "
2015-09-07 15:53:23 +02:00
params = ( test , run , type , sqlite3 . Binary ( contents ) )
2014-01-04 15:36:10 +01:00
try :
conn . execute ( sql , params )
conn . commit ( )
2019-01-24 08:45:41 +01:00
except Exception as e :
2019-01-24 08:45:42 +01:00
print ( " sqlite: " + str ( e ) )
print ( " sql: %r " % ( params , ) )
2014-01-04 15:36:10 +01:00
2014-11-27 19:08:15 +01:00
def report ( conn , prefill , build , commit , run , test , result , duration , logdir ,
sql_commit = True ) :
2013-10-27 00:05:45 +02:00
if conn :
if not build :
build = ' '
if not commit :
commit = ' '
2013-10-31 16:05:11 +01:00
if prefill :
conn . execute ( ' DELETE FROM results WHERE test=? AND run=? AND result=? ' , ( test , run , ' NOTRUN ' ) )
2013-10-30 18:19:07 +01:00
sql = " INSERT INTO results(test,result,run,time,duration,build,commitid) VALUES(?, ?, ?, ?, ?, ?, ?) "
2013-10-31 16:05:11 +01:00
params = ( test , result , run , time . time ( ) , duration , build , commit )
2013-10-27 00:05:45 +02:00
try :
2013-10-30 18:19:07 +01:00
conn . execute ( sql , params )
2014-11-27 19:08:15 +01:00
if sql_commit :
conn . commit ( )
2019-01-24 08:45:41 +01:00
except Exception as e :
2019-01-24 08:45:42 +01:00
print ( " sqlite: " + str ( e ) )
print ( " sql: %r " % ( params , ) )
2013-10-27 00:05:45 +02:00
2014-01-04 15:36:10 +01:00
if result == " FAIL " :
2019-03-15 11:10:37 +01:00
for log in [ " log " , " log0 " , " log1 " , " log2 " , " log3 " , " log5 " ,
" hostapd " , " dmesg " , " hwsim0 " , " hwsim0.pcapng " ] :
2014-01-04 15:36:10 +01:00
add_log_file ( conn , test , run , log ,
logdir + " / " + test + " . " + log )
2013-10-30 22:50:56 +01:00
class DataCollector ( object ) :
2023-12-25 11:21:09 +01:00
def __init__ ( self , logdir , testname , kmemleak , args ) :
2013-10-30 22:50:56 +01:00
self . _logdir = logdir
2013-10-30 21:05:24 +01:00
self . _testname = testname
2018-02-12 20:26:10 +01:00
self . _tracing = args . tracing
self . _dmesg = args . dmesg
2023-12-25 11:21:09 +01:00
self . _kmemleak = kmemleak
2018-02-12 20:26:10 +01:00
self . _dbus = args . dbus
2013-10-30 21:05:24 +01:00
def __enter__ ( self ) :
2013-10-30 22:50:56 +01:00
if self . _tracing :
2014-03-11 16:04:33 +01:00
output = os . path . abspath ( os . path . join ( self . _logdir , ' %s .dat ' % ( self . _testname , ) ) )
2023-09-25 09:20:49 +02:00
self . _trace_cmd = subprocess . Popen ( [ ' trace-cmd ' , ' record ' , ' -o ' , output , ' -T ' , ' -e ' , ' skb ' , ' -e ' , ' mac80211 ' , ' -e ' , ' cfg80211 ' , ' -e ' , ' printk ' , ' sh ' , ' -c ' , ' echo STARTED ; read l ' ] ,
2013-10-30 22:50:56 +01:00
stdin = subprocess . PIPE ,
stdout = subprocess . PIPE ,
stderr = open ( ' /dev/null ' , ' w ' ) ,
cwd = self . _logdir )
l = self . _trace_cmd . stdout . read ( 7 )
2019-04-12 13:20:19 +02:00
while self . _trace_cmd . poll ( ) is None and b ' STARTED ' not in l :
2013-10-30 22:50:56 +01:00
l + = self . _trace_cmd . stdout . read ( 1 )
2014-02-25 22:27:03 +01:00
res = self . _trace_cmd . returncode
if res :
2019-01-24 08:45:42 +01:00
print ( " Failed calling trace-cmd: returned exit status %d " % res )
2014-02-25 22:27:03 +01:00
sys . exit ( 1 )
2018-02-12 20:26:10 +01:00
if self . _dbus :
output = os . path . abspath ( os . path . join ( self . _logdir , ' %s .dbus ' % ( self . _testname , ) ) )
self . _dbus_cmd = subprocess . Popen ( [ ' dbus-monitor ' , ' --system ' ] ,
stdout = open ( output , ' w ' ) ,
stderr = open ( ' /dev/null ' , ' w ' ) ,
cwd = self . _logdir )
res = self . _dbus_cmd . returncode
if res :
2019-01-24 08:45:42 +01:00
print ( " Failed calling dbus-monitor: returned exit status %d " % res )
2018-02-12 20:26:10 +01:00
sys . exit ( 1 )
2013-10-30 21:05:24 +01:00
def __exit__ ( self , type , value , traceback ) :
2013-10-30 22:50:56 +01:00
if self . _tracing :
2019-04-12 13:20:19 +02:00
self . _trace_cmd . stdin . write ( b ' DONE \n ' )
self . _trace_cmd . stdin . flush ( )
2013-10-30 22:50:56 +01:00
self . _trace_cmd . wait ( )
2023-12-25 11:21:09 +01:00
if self . _kmemleak :
output = os . path . join ( self . _logdir , ' %s .kmemleak ' % ( self . _testname , ) )
num = 0
while os . path . exists ( output ) :
output = os . path . join ( self . _logdir , ' %s .kmemleak- %d ' % ( self . _testname , num ) )
num + = 1
# Trigger kmemleak
with open ( ' /sys/kernel/debug/kmemleak ' , ' w+ ' ) as kmemleak :
kmemleak . write ( ' scan ' )
kmemleak . seek ( 0 )
# Minimum reporting age
time . sleep ( 5 )
kmemleak . write ( ' scan ' )
kmemleak . seek ( 0 )
leaks = [ ]
while l := kmemleak . read ( ) :
leaks . append ( l )
leaks = ' ' . join ( leaks )
if leaks :
with open ( output , ' w ' ) as out :
out . write ( leaks )
kmemleak . seek ( 0 )
kmemleak . write ( ' clear ' )
2013-10-30 22:50:56 +01:00
if self . _dmesg :
output = os . path . join ( self . _logdir , ' %s .dmesg ' % ( self . _testname , ) )
2015-02-21 15:06:57 +01:00
num = 0
while os . path . exists ( output ) :
output = os . path . join ( self . _logdir , ' %s .dmesg- %d ' % ( self . _testname , num ) )
num + = 1
2015-02-06 23:22:32 +01:00
subprocess . call ( [ ' dmesg ' , ' -c ' ] , stdout = open ( output , ' w ' ) )
2013-10-30 21:05:24 +01:00
2013-11-02 12:04:06 +01:00
def rename_log ( logdir , basename , testname , dev ) :
try :
import getpass
srcname = os . path . join ( logdir , basename )
dstname = os . path . join ( logdir , testname + ' . ' + basename )
num = 0
while os . path . exists ( dstname ) :
dstname = os . path . join ( logdir ,
testname + ' . ' + basename + ' - ' + str ( num ) )
num = num + 1
os . rename ( srcname , dstname )
2013-11-05 12:21:58 +01:00
if dev :
dev . relog ( )
2015-02-06 23:22:32 +01:00
subprocess . call ( [ ' chown ' , ' -f ' , getpass . getuser ( ) , srcname ] )
2019-01-24 08:45:41 +01:00
except Exception as e :
2013-11-02 12:04:06 +01:00
logger . info ( " Failed to rename log files " )
logger . info ( e )
2020-04-18 10:00:49 +02:00
def is_long_duration_test ( t ) :
return hasattr ( t , " long_duration_test " ) and t . long_duration_test
def get_test_description ( t ) :
if t . __doc__ is None :
desc = " MISSING DESCRIPTION "
else :
desc = t . __doc__
if is_long_duration_test ( t ) :
desc + = " [long] "
return desc
2013-03-27 13:40:10 +01:00
def main ( ) :
2013-10-30 18:28:22 +01:00
tests = [ ]
test_modules = [ ]
2024-03-17 14:20:26 +01:00
names = set ( )
2015-03-03 23:08:41 +01:00
files = os . listdir ( scriptsdir )
2014-11-18 23:41:45 +01:00
for t in files :
2013-10-30 18:28:22 +01:00
m = re . match ( r ' (test_.*) \ .py$ ' , t )
if m :
logger . debug ( " Import test cases from " + t )
mod = __import__ ( m . group ( 1 ) )
2013-10-31 10:47:43 +01:00
test_modules . append ( mod . __name__ . replace ( ' test_ ' , ' ' , 1 ) )
2019-03-15 11:10:37 +01:00
for key , val in mod . __dict__ . items ( ) :
2014-12-22 19:08:11 +01:00
if key . startswith ( " test_ " ) :
2024-03-17 14:20:26 +01:00
if val . __doc__ is None :
print ( f " Test case { val . __name__ } misses __doc__ " )
2014-12-22 19:08:11 +01:00
tests . append ( val )
2024-03-17 14:20:26 +01:00
name = val . __name__ . replace ( ' test_ ' , ' ' , 1 )
if name in names :
print ( f " Test case { name } defined multiple times " )
names . add ( name )
test_names = list ( names )
2013-10-30 18:28:22 +01:00
2013-10-27 00:05:45 +02:00
run = None
2013-10-30 18:28:22 +01:00
parser = argparse . ArgumentParser ( description = ' hwsim test runner ' )
2013-10-31 13:27:57 +01:00
parser . add_argument ( ' --logdir ' , metavar = ' <directory> ' ,
2013-10-30 22:37:15 +01:00
help = ' log output directory for all other options, ' +
' must be given if other log options are used ' )
2013-10-30 18:28:22 +01:00
group = parser . add_mutually_exclusive_group ( )
group . add_argument ( ' -d ' , const = logging . DEBUG , action = ' store_const ' ,
dest = ' loglevel ' , default = logging . INFO ,
help = " verbose debug output " )
group . add_argument ( ' -q ' , const = logging . WARNING , action = ' store_const ' ,
dest = ' loglevel ' , help = " be quiet " )
parser . add_argument ( ' -S ' , metavar = ' <sqlite3 db> ' , dest = ' database ' ,
help = ' database to write results to ' )
2013-10-31 16:05:11 +01:00
parser . add_argument ( ' --prefill-tests ' , action = ' store_true ' , dest = ' prefill ' ,
help = ' prefill test database with NOTRUN before all tests ' )
2013-10-30 20:01:11 +01:00
parser . add_argument ( ' --commit ' , metavar = ' <commit id> ' ,
help = ' commit ID, only for database ' )
2013-10-30 18:28:22 +01:00
parser . add_argument ( ' -b ' , metavar = ' <build> ' , dest = ' build ' , help = ' build ID ' )
parser . add_argument ( ' -L ' , action = ' store_true ' , dest = ' update_tests_db ' ,
help = ' List tests (and update descriptions in DB) ' )
2013-10-30 22:37:15 +01:00
parser . add_argument ( ' -T ' , action = ' store_true ' , dest = ' tracing ' ,
help = ' collect tracing per test case (in log directory) ' )
2013-10-30 22:50:56 +01:00
parser . add_argument ( ' -D ' , action = ' store_true ' , dest = ' dmesg ' ,
help = ' collect dmesg per test case (in log directory) ' )
2018-02-12 20:26:10 +01:00
parser . add_argument ( ' --dbus ' , action = ' store_true ' , dest = ' dbus ' ,
help = ' collect dbus per test case (in log directory) ' )
2013-11-02 09:30:37 +01:00
parser . add_argument ( ' --shuffle-tests ' , action = ' store_true ' ,
dest = ' shuffle_tests ' ,
help = ' Shuffle test cases to randomize order ' )
2014-03-24 00:35:58 +01:00
parser . add_argument ( ' --split ' , help = ' split tests for parallel execution (<server number>/<total servers>) ' )
2013-11-02 19:38:40 +01:00
parser . add_argument ( ' --no-reset ' , action = ' store_true ' , dest = ' no_reset ' ,
help = ' Do not reset devices at the end of the test ' )
2014-04-06 12:11:32 +02:00
parser . add_argument ( ' --long ' , action = ' store_true ' ,
help = ' Include test cases that take long time ' )
2013-10-30 18:49:12 +01:00
parser . add_argument ( ' -f ' , dest = ' testmodules ' , metavar = ' <test module> ' ,
help = ' execute only tests from these test modules ' ,
type = str , choices = [ [ ] ] + test_modules , nargs = ' + ' )
2014-10-26 08:06:33 +01:00
parser . add_argument ( ' -l ' , metavar = ' <modules file> ' , dest = ' mfile ' ,
help = ' test modules file name ' )
2014-11-19 01:01:27 +01:00
parser . add_argument ( ' -i ' , action = ' store_true ' , dest = ' stdin_ctrl ' ,
help = ' stdin-controlled test case execution ' )
2013-10-30 18:28:22 +01:00
parser . add_argument ( ' tests ' , metavar = ' <test> ' , nargs = ' * ' , type = str ,
2019-02-02 23:16:04 +01:00
help = ' tests to run (only valid without -f) ' )
2013-10-30 18:28:22 +01:00
args = parser . parse_args ( )
2014-10-26 08:06:33 +01:00
if ( args . tests and args . testmodules ) or ( args . tests and args . mfile ) or ( args . testmodules and args . mfile ) :
2019-01-24 08:45:42 +01:00
print ( ' Invalid arguments - only one of (test, test modules, modules file) can be given. ' )
2013-10-30 18:28:22 +01:00
sys . exit ( 2 )
2019-02-02 23:16:04 +01:00
if args . tests :
fail = False
for t in args . tests :
2019-07-09 15:09:04 +02:00
if t . endswith ( ' * ' ) :
prefix = t . rstrip ( ' * ' )
found = False
for tn in test_names :
if tn . startswith ( prefix ) :
found = True
break
if not found :
print ( ' Invalid arguments - test " %s " wildcard did not match ' % t )
fail = True
elif t not in test_names :
2019-02-02 23:16:04 +01:00
print ( ' Invalid arguments - test " %s " not known ' % t )
fail = True
if fail :
sys . exit ( 2 )
2013-10-30 18:28:22 +01:00
if args . database :
2015-09-07 15:53:23 +02:00
if not sqlite3_imported :
2019-01-24 08:45:42 +01:00
print ( " No sqlite3 module found " )
2015-09-07 15:53:23 +02:00
sys . exit ( 2 )
2013-10-30 18:28:22 +01:00
conn = sqlite3 . connect ( args . database )
2013-10-31 15:36:44 +01:00
conn . execute ( ' CREATE TABLE IF NOT EXISTS results (test,result,run,time,duration,build,commitid) ' )
conn . execute ( ' CREATE TABLE IF NOT EXISTS tests (test,description) ' )
2014-01-04 15:36:10 +01:00
conn . execute ( ' CREATE TABLE IF NOT EXISTS logs (test,run,type,contents) ' )
2013-10-30 18:28:22 +01:00
else :
conn = None
2013-03-31 15:29:16 +02:00
2013-10-27 00:05:45 +02:00
if conn :
2013-10-31 09:43:02 +01:00
run = int ( time . time ( ) )
2013-10-27 00:05:45 +02:00
2014-11-18 23:34:36 +01:00
# read the modules from the modules file
if args . mfile :
2016-04-24 23:19:40 +02:00
args . testmodules = [ ]
with open ( args . mfile ) as f :
for line in f . readlines ( ) :
line = line . strip ( )
if not line or line . startswith ( ' # ' ) :
continue
2019-03-15 10:18:20 +01:00
args . testmodules . append ( line )
2014-11-18 23:34:36 +01:00
tests_to_run = [ ]
2014-11-27 18:48:41 +01:00
if args . tests :
for selected in args . tests :
for t in tests :
name = t . __name__ . replace ( ' test_ ' , ' ' , 1 )
2019-07-09 15:09:04 +02:00
if selected . endswith ( ' * ' ) :
prefix = selected . rstrip ( ' * ' )
if name . startswith ( prefix ) :
tests_to_run . append ( t )
elif name == selected :
2014-11-27 18:48:41 +01:00
tests_to_run . append ( t )
else :
for t in tests :
name = t . __name__ . replace ( ' test_ ' , ' ' , 1 )
if args . testmodules :
2019-03-15 10:34:32 +01:00
if t . __module__ . replace ( ' test_ ' , ' ' , 1 ) not in args . testmodules :
2014-11-27 18:48:41 +01:00
continue
tests_to_run . append ( t )
2014-11-18 23:34:36 +01:00
2013-10-30 18:28:22 +01:00
if args . update_tests_db :
2014-11-18 23:34:36 +01:00
for t in tests_to_run :
2013-10-31 10:43:45 +01:00
name = t . __name__ . replace ( ' test_ ' , ' ' , 1 )
2020-04-18 10:00:49 +02:00
print ( name + " - " + get_test_description ( t ) )
2013-10-27 11:16:36 +01:00
if conn :
2013-10-30 18:19:07 +01:00
sql = ' INSERT OR REPLACE INTO tests(test,description) VALUES (?, ?) '
2020-04-18 10:00:49 +02:00
params = ( name , get_test_description ( t ) )
2013-10-27 11:16:36 +01:00
try :
2013-10-30 18:19:07 +01:00
conn . execute ( sql , params )
2019-01-24 08:45:41 +01:00
except Exception as e :
2019-01-24 08:45:42 +01:00
print ( " sqlite: " + str ( e ) )
print ( " sql: %r " % ( params , ) )
2013-10-27 11:16:36 +01:00
if conn :
conn . commit ( )
conn . close ( )
2013-09-29 15:11:48 +02:00
sys . exit ( 0 )
2014-11-18 23:41:45 +01:00
if not args . logdir :
if os . path . exists ( ' logs/current ' ) :
args . logdir = ' logs/current '
else :
args . logdir = ' logs '
# Write debug level log to a file and configurable verbosity to stdout
logger . setLevel ( logging . DEBUG )
stdout_handler = logging . StreamHandler ( )
stdout_handler . setLevel ( args . loglevel )
logger . addHandler ( stdout_handler )
file_name = os . path . join ( args . logdir , ' run-tests.log ' )
2019-02-18 17:19:06 +01:00
log_handler = logging . FileHandler ( file_name , encoding = ' utf-8 ' )
2014-11-18 23:41:45 +01:00
log_handler . setLevel ( logging . DEBUG )
fmt = " %(asctime)s %(levelname)s %(message)s "
log_formatter = logging . Formatter ( fmt )
log_handler . setFormatter ( log_formatter )
logger . addHandler ( log_handler )
2013-03-27 13:40:10 +01:00
2013-06-30 23:13:11 +02:00
dev0 = WpaSupplicant ( ' wlan0 ' , ' /tmp/wpas-wlan0 ' )
dev1 = WpaSupplicant ( ' wlan1 ' , ' /tmp/wpas-wlan1 ' )
dev2 = WpaSupplicant ( ' wlan2 ' , ' /tmp/wpas-wlan2 ' )
2019-03-15 11:10:37 +01:00
dev = [ dev0 , dev1 , dev2 ]
apdev = [ ]
2013-03-31 15:16:37 +02:00
apdev . append ( { " ifname " : ' wlan3 ' , " bssid " : " 02:00:00:00:03:00 " } )
2013-03-31 15:19:12 +02:00
apdev . append ( { " ifname " : ' wlan4 ' , " bssid " : " 02:00:00:00:04:00 " } )
2013-03-27 13:40:10 +01:00
for d in dev :
if not d . ping ( ) :
2013-08-24 18:48:04 +02:00
logger . info ( d . ifname + " : No response from wpa_supplicant " )
2013-03-27 13:40:10 +01:00
return
2013-08-24 18:48:04 +02:00
logger . info ( " DEV: " + d . ifname + " : " + d . p2p_dev_addr ( ) )
2013-03-31 15:16:37 +02:00
for ap in apdev :
2013-08-24 18:48:04 +02:00
logger . info ( " APDEV: " + ap [ ' ifname ' ] )
2013-03-27 13:40:10 +01:00
passed = [ ]
2013-09-29 15:21:42 +02:00
skipped = [ ]
2013-03-27 13:40:10 +01:00
failed = [ ]
2013-10-30 22:53:29 +01:00
# make sure nothing is left over from previous runs
# (if there were any other manual runs or we crashed)
2013-11-05 12:21:58 +01:00
if not reset_devs ( dev , apdev ) :
if conn :
conn . close ( )
conn = None
sys . exit ( 1 )
2013-10-30 22:53:29 +01:00
2013-10-30 22:50:56 +01:00
if args . dmesg :
2015-02-06 23:22:32 +01:00
subprocess . call ( [ ' dmesg ' , ' -c ' ] , stdout = open ( ' /dev/null ' , ' w ' ) )
2013-10-30 22:50:56 +01:00
2023-12-25 11:21:09 +01:00
try :
# try to clear out any leaks that happened earlier
with open ( ' /sys/kernel/debug/kmemleak ' , ' w ' ) as kmemleak :
kmemleak . write ( ' scan ' )
kmemleak . seek ( 0 )
time . sleep ( 5 )
kmemleak . write ( ' scan ' )
kmemleak . seek ( 0 )
kmemleak . write ( ' clear ' )
have_kmemleak = True
except OSError :
have_kmemleak = False
2013-10-31 16:05:11 +01:00
if conn and args . prefill :
for t in tests_to_run :
name = t . __name__ . replace ( ' test_ ' , ' ' , 1 )
2014-01-04 15:36:10 +01:00
report ( conn , False , args . build , args . commit , run , name , ' NOTRUN ' , 0 ,
2014-11-27 19:08:15 +01:00
args . logdir , sql_commit = False )
conn . commit ( )
2013-10-31 16:05:11 +01:00
2014-03-24 00:35:58 +01:00
if args . split :
vals = args . split . split ( ' / ' )
split_server = int ( vals [ 0 ] )
split_total = int ( vals [ 1 ] )
logger . info ( " Parallel execution - %d / %d " % ( split_server , split_total ) )
split_server - = 1
tests_to_run . sort ( key = lambda t : t . __name__ )
2019-03-15 11:10:37 +01:00
tests_to_run = [ x for i , x in enumerate ( tests_to_run ) if i % split_total == split_server ]
2014-03-24 00:35:58 +01:00
2013-11-02 09:30:37 +01:00
if args . shuffle_tests :
from random import shuffle
shuffle ( tests_to_run )
2013-12-29 15:45:47 +01:00
count = 0
2014-11-19 01:01:27 +01:00
if args . stdin_ctrl :
2019-01-24 08:45:42 +01:00
print ( " READY " )
2014-11-19 01:01:27 +01:00
sys . stdout . flush ( )
2014-11-19 21:02:08 +01:00
num_tests = 0
2014-11-19 01:01:27 +01:00
else :
2014-11-19 21:02:08 +01:00
num_tests = len ( tests_to_run )
2014-12-24 10:53:14 +01:00
if args . stdin_ctrl :
set_term_echo ( sys . stdin . fileno ( ) , False )
2018-12-31 16:05:03 +01:00
check_country_00 = True
for d in dev :
if d . get_driver_status_field ( " country " ) != " 00 " :
check_country_00 = False
2014-11-19 01:01:27 +01:00
while True :
if args . stdin_ctrl :
test = sys . stdin . readline ( )
if not test :
break
test = test . splitlines ( ) [ 0 ]
if test == ' ' :
break
t = None
for tt in tests :
name = tt . __name__ . replace ( ' test_ ' , ' ' , 1 )
if name == test :
t = tt
break
if not t :
2019-01-24 08:45:42 +01:00
print ( " NOT-FOUND " )
2014-11-19 01:01:27 +01:00
sys . stdout . flush ( )
continue
else :
2014-11-19 21:02:08 +01:00
if len ( tests_to_run ) == 0 :
2014-11-19 01:01:27 +01:00
break
2014-11-19 21:02:08 +01:00
t = tests_to_run . pop ( 0 )
2014-11-19 01:01:27 +01:00
2018-12-31 16:05:03 +01:00
if dev [ 0 ] . get_driver_status_field ( " country " ) == " 98 " :
# Work around cfg80211 regulatory issues in clearing intersected
# country code 98. Need to make station disconnect without any
# other wiphy being active in the system.
logger . info ( " country=98 workaround - try to clear state " )
id = dev [ 1 ] . add_network ( )
dev [ 1 ] . set_network ( id , " mode " , " 2 " )
dev [ 1 ] . set_network_quoted ( id , " ssid " , " country98 " )
dev [ 1 ] . set_network ( id , " key_mgmt " , " NONE " )
dev [ 1 ] . set_network ( id , " frequency " , " 2412 " )
dev [ 1 ] . set_network ( id , " scan_freq " , " 2412 " )
dev [ 1 ] . select_network ( id )
ev = dev [ 1 ] . wait_event ( [ " CTRL-EVENT-CONNECTED " ] )
if ev :
dev [ 0 ] . connect ( " country98 " , key_mgmt = " NONE " , scan_freq = " 2412 " )
dev [ 1 ] . request ( " DISCONNECT " )
dev [ 0 ] . wait_disconnected ( )
2019-03-17 16:55:43 +01:00
dev [ 0 ] . disconnect_and_stop_scan ( )
2018-12-31 16:05:03 +01:00
dev [ 0 ] . reset ( )
dev [ 1 ] . reset ( )
dev [ 0 ] . dump_monitor ( )
dev [ 1 ] . dump_monitor ( )
2013-10-31 16:05:11 +01:00
name = t . __name__ . replace ( ' test_ ' , ' ' , 1 )
2015-11-24 13:27:57 +01:00
open ( ' /dev/kmsg ' , ' w ' ) . write ( ' running hwsim test case %s \n ' % name )
2013-10-31 11:46:42 +01:00
if log_handler :
log_handler . stream . close ( )
logger . removeHandler ( log_handler )
file_name = os . path . join ( args . logdir , name + ' .log ' )
2019-02-18 17:19:06 +01:00
log_handler = logging . FileHandler ( file_name , encoding = ' utf-8 ' )
2013-11-02 11:20:59 +01:00
log_handler . setLevel ( logging . DEBUG )
2013-10-31 11:46:42 +01:00
log_handler . setFormatter ( log_formatter )
logger . addHandler ( log_handler )
2023-12-12 09:07:15 +01:00
try :
with open ( ' /sys/kernel/debug/clear_warn_once ' , ' w ' ) as f :
f . write ( ' 1 \n ' )
except FileNotFoundError :
pass
2013-11-05 12:21:58 +01:00
reset_ok = True
2023-12-25 11:21:09 +01:00
with DataCollector ( args . logdir , name , have_kmemleak , args ) :
2013-12-29 15:45:47 +01:00
count = count + 1
2014-11-19 21:02:08 +01:00
msg = " START {} {} / {} " . format ( name , count , num_tests )
2013-12-29 15:45:47 +01:00
logger . info ( msg )
2013-11-02 11:20:59 +01:00
if args . loglevel == logging . WARNING :
2019-01-24 08:45:42 +01:00
print ( msg )
2013-09-29 19:42:37 +02:00
sys . stdout . flush ( )
2013-10-30 21:05:24 +01:00
if t . __doc__ :
logger . info ( " Test: " + t . __doc__ )
start = datetime . now ( )
2017-05-27 10:08:16 +02:00
open ( ' /dev/kmsg ' , ' w ' ) . write ( ' TEST-START %s @ %.6f \n ' % ( name , time . time ( ) ) )
2013-10-30 21:05:24 +01:00
for d in dev :
try :
2014-04-29 13:46:09 +02:00
d . dump_monitor ( )
if not d . ping ( ) :
raise Exception ( " PING failed for {} " . format ( d . ifname ) )
if not d . global_ping ( ) :
raise Exception ( " Global PING failed for {} " . format ( d . ifname ) )
2013-10-31 10:43:45 +01:00
d . request ( " NOTE TEST-START " + name )
2019-01-24 08:45:41 +01:00
except Exception as e :
2013-10-31 10:43:45 +01:00
logger . info ( " Failed to issue TEST-START before " + name + " for " + d . ifname )
2013-10-30 21:05:24 +01:00
logger . info ( e )
2019-01-24 08:45:42 +01:00
print ( " FAIL " + name + " - could not start test " )
2013-10-30 21:05:24 +01:00
if conn :
conn . close ( )
conn = None
2014-12-24 10:53:14 +01:00
if args . stdin_ctrl :
set_term_echo ( sys . stdin . fileno ( ) , True )
2013-10-30 21:05:24 +01:00
sys . exit ( 1 )
2019-12-27 09:46:13 +01:00
skip_reason = None
2013-08-24 18:41:08 +02:00
try :
2020-04-18 10:00:49 +02:00
if is_long_duration_test ( t ) and not args . long :
raise HwsimSkip ( " Skip test case with long duration due to --long not specified " )
2019-01-24 08:45:45 +01:00
if t . __code__ . co_argcount > 2 :
2014-03-23 10:59:43 +01:00
params = { }
params [ ' logdir ' ] = args . logdir
2020-03-16 15:22:32 +01:00
params [ ' name ' ] = name
params [ ' prefix ' ] = os . path . join ( args . logdir , name )
2015-01-07 13:19:30 +01:00
t ( dev , apdev , params )
2019-01-24 08:45:45 +01:00
elif t . __code__ . co_argcount > 1 :
2015-01-07 13:19:30 +01:00
t ( dev , apdev )
2013-10-30 21:05:24 +01:00
else :
2015-01-07 13:19:30 +01:00
t ( dev )
result = " PASS "
2018-12-31 16:05:03 +01:00
if check_country_00 :
for d in dev :
country = d . get_driver_status_field ( " country " )
2020-05-17 12:04:26 +02:00
if country is None :
logger . info ( d . ifname + " : Could not fetch country code after the test case run " )
elif country != " 00 " :
2018-12-31 16:05:03 +01:00
d . dump_monitor ( )
2019-04-13 11:06:09 +02:00
logger . info ( d . ifname + " : Country code not reset back to 00: is " + country )
print ( d . ifname + " : Country code not reset back to 00: is " + country )
2018-12-31 16:05:03 +01:00
result = " FAIL "
# Try to wait for cfg80211 regulatory state to
# clear.
d . cmd_execute ( [ ' iw ' , ' reg ' , ' set ' , ' 00 ' ] )
for i in range ( 5 ) :
time . sleep ( 1 )
country = d . get_driver_status_field ( " country " )
if country == " 00 " :
break
if country == " 00 " :
2019-04-13 11:06:09 +02:00
print ( d . ifname + " : Country code cleared back to 00 " )
logger . info ( d . ifname + " : Country code cleared back to 00 " )
2018-12-31 16:05:03 +01:00
else :
2019-01-24 08:45:42 +01:00
print ( " Country code remains set - expect following test cases to fail " )
2018-12-31 16:05:03 +01:00
logger . info ( " Country code remains set - expect following test cases to fail " )
break
2019-01-24 08:45:41 +01:00
except HwsimSkip as e :
2015-01-07 12:41:31 +01:00
logger . info ( " Skip test case: %s " % e )
2019-12-27 09:46:13 +01:00
skip_reason = e
2015-01-07 12:41:31 +01:00
result = " SKIP "
2019-01-24 08:45:41 +01:00
except NameError as e :
2015-11-27 19:54:58 +01:00
import traceback
logger . info ( e )
traceback . print_exc ( )
result = " FAIL "
2019-01-24 08:45:41 +01:00
except Exception as e :
2016-04-07 07:38:10 +02:00
import traceback
2015-01-07 12:41:31 +01:00
logger . info ( e )
2016-04-07 07:38:10 +02:00
traceback . print_exc ( )
2015-01-07 12:41:31 +01:00
if args . loglevel == logging . WARNING :
2019-01-24 08:45:42 +01:00
print ( " Exception: " + str ( e ) )
2015-01-07 12:41:31 +01:00
result = " FAIL "
tests: Work around reentrant logging issues due to __del__ misuse
Unfortunately, some objects (WlantestCapture, WpaSupplicant
and wpaspy.Ctrl) use __del__ and actually have some logic
there. This is more or less wrong, and we should be using
context managers for it. However, cleaning that up is a
pretty large task.
Unfortunately, __del__ can cause reentrant logging which is
wrong too, because it might be invoked while in the middle
of a logging call, and the __del__ of these objects closes
connections and logs while doing that.
Since we're (likely) using cpython, we can work around this
by explicitly calling gc.collect() in a context where the
logging and close is fine, not only ensuring that all the
connections are closed properly before the next test, but
also fixing the issue with reentrant logging.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2022-05-22 10:46:09 +02:00
# Work around some objects having __del__, we really should
# use context managers, but that's complex. Doing this here
# will (on cpython at least) at make sure those objects that
# are no longer reachable will be collected now, invoking
# __del__() on them. This then ensures that __del__() isn't
# invoked at a bad time, e.g. causing recursion in locking.
gc . collect ( )
2017-05-27 10:08:16 +02:00
open ( ' /dev/kmsg ' , ' w ' ) . write ( ' TEST-STOP %s @ %.6f \n ' % ( name , time . time ( ) ) )
2013-10-30 21:05:24 +01:00
for d in dev :
try :
2014-04-29 13:46:09 +02:00
d . dump_monitor ( )
2013-10-31 10:43:45 +01:00
d . request ( " NOTE TEST-STOP " + name )
2019-01-24 08:45:41 +01:00
except Exception as e :
2013-11-02 16:07:13 +01:00
logger . info ( " Failed to issue TEST-STOP after {} for {} " . format ( name , d . ifname ) )
2013-10-30 21:05:24 +01:00
logger . info ( e )
2013-11-05 12:21:58 +01:00
result = " FAIL "
2016-09-23 15:01:36 +02:00
if args . no_reset :
2019-01-24 08:45:42 +01:00
print ( " Leaving devices in current state " )
2016-09-23 15:01:36 +02:00
else :
reset_ok = reset_devs ( dev , apdev )
2015-01-18 16:13:55 +01:00
wpas = None
2013-12-30 22:08:25 +01:00
try :
2019-03-17 14:26:34 +01:00
wpas = WpaSupplicant ( global_iface = " /tmp/wpas-wlan5 " ,
monitor = False )
2013-12-30 22:08:25 +01:00
rename_log ( args . logdir , ' log5 ' , name , wpas )
if not args . no_reset :
wpas . remove_ifname ( )
2019-01-24 08:45:41 +01:00
except Exception as e :
2013-12-30 22:08:25 +01:00
pass
2015-01-18 16:13:55 +01:00
if wpas :
wpas . close_ctrl ( )
2019-03-17 14:26:34 +01:00
del wpas
2013-03-27 13:40:10 +01:00
2013-10-31 12:09:14 +01:00
for i in range ( 0 , 3 ) :
2013-11-02 12:04:06 +01:00
rename_log ( args . logdir , ' log ' + str ( i ) , name , dev [ i ] )
2013-11-05 12:21:58 +01:00
try :
hapd = HostapdGlobal ( )
2019-01-24 08:45:41 +01:00
except Exception as e :
2019-01-24 08:45:42 +01:00
print ( " Failed to connect to hostapd interface " )
print ( str ( e ) )
2013-11-05 12:21:58 +01:00
reset_ok = False
result = " FAIL "
hapd = None
2013-11-02 12:04:06 +01:00
rename_log ( args . logdir , ' hostapd ' , name , hapd )
2015-07-26 12:34:54 +02:00
if hapd :
del hapd
hapd = None
2013-11-02 10:53:38 +01:00
2016-05-19 15:06:49 +02:00
# Use None here since this instance of Wlantest() will never be
# used for remote host hwsim tests on real hardware.
Wlantest . setup ( None )
2013-11-17 20:25:17 +01:00
wt = Wlantest ( )
rename_log ( args . logdir , ' hwsim0.pcapng ' , name , wt )
rename_log ( args . logdir , ' hwsim0 ' , name , wt )
2014-08-05 17:25:59 +02:00
if os . path . exists ( os . path . join ( args . logdir , ' fst-wpa_supplicant ' ) ) :
rename_log ( args . logdir , ' fst-wpa_supplicant ' , name , None )
if os . path . exists ( os . path . join ( args . logdir , ' fst-hostapd ' ) ) :
rename_log ( args . logdir , ' fst-hostapd ' , name , None )
2017-02-28 01:50:51 +01:00
if os . path . exists ( os . path . join ( args . logdir , ' wmediumd.log ' ) ) :
rename_log ( args . logdir , ' wmediumd.log ' , name , None )
2013-11-17 20:25:17 +01:00
2013-10-31 15:02:50 +01:00
end = datetime . now ( )
diff = end - start
2013-11-04 10:00:36 +01:00
if result == ' PASS ' and args . dmesg :
if not check_kernel ( os . path . join ( args . logdir , name + ' .dmesg ' ) ) :
2013-12-27 10:04:38 +01:00
logger . info ( " Kernel issue found in dmesg - mark test failed " )
2013-11-04 10:00:36 +01:00
result = ' FAIL '
2023-12-25 11:21:09 +01:00
if result == ' PASS ' and have_kmemleak :
# The file is only created if a leak was found
if os . path . exists ( os . path . join ( args . logdir , name + ' .kmemleak ' ) ) :
logger . info ( " Kernel memory leak found - mark test failed " )
result = ' FAIL '
2013-11-04 10:00:36 +01:00
if result == ' PASS ' :
passed . append ( name )
elif result == ' SKIP ' :
skipped . append ( name )
else :
failed . append ( name )
2014-01-04 15:36:10 +01:00
report ( conn , args . prefill , args . build , args . commit , run , name , result ,
diff . total_seconds ( ) , args . logdir )
2013-11-02 16:07:13 +01:00
result = " {} {} {} {} " . format ( result , name , diff . total_seconds ( ) , end )
2013-10-31 15:02:50 +01:00
logger . info ( result )
2013-11-02 11:20:59 +01:00
if args . loglevel == logging . WARNING :
2019-01-24 08:45:42 +01:00
print ( result )
2019-12-27 09:46:13 +01:00
if skip_reason :
print ( " REASON " , skip_reason )
2013-10-31 15:02:50 +01:00
sys . stdout . flush ( )
2013-11-05 12:21:58 +01:00
if not reset_ok :
2019-01-24 08:45:42 +01:00
print ( " Terminating early due to device reset failure " )
2013-11-05 12:21:58 +01:00
break
2024-01-31 11:16:36 +01:00
for d in dev :
d . close_ctrl ( )
2014-12-24 10:53:14 +01:00
if args . stdin_ctrl :
set_term_echo ( sys . stdin . fileno ( ) , True )
2013-11-05 12:21:58 +01:00
2013-10-31 11:46:42 +01:00
if log_handler :
log_handler . stream . close ( )
logger . removeHandler ( log_handler )
file_name = os . path . join ( args . logdir , ' run-tests.log ' )
2019-02-18 17:19:06 +01:00
log_handler = logging . FileHandler ( file_name , encoding = ' utf-8 ' )
2013-11-02 11:20:59 +01:00
log_handler . setLevel ( logging . DEBUG )
2013-10-31 11:46:42 +01:00
log_handler . setFormatter ( log_formatter )
logger . addHandler ( log_handler )
2013-10-27 00:05:45 +02:00
if conn :
conn . close ( )
2013-03-27 13:40:10 +01:00
if len ( failed ) :
2013-11-02 16:07:13 +01:00
logger . info ( " passed {} test case(s) " . format ( len ( passed ) ) )
logger . info ( " skipped {} test case(s) " . format ( len ( skipped ) ) )
2013-11-11 17:19:33 +01:00
logger . info ( " failed tests: " + ' ' . join ( failed ) )
2013-11-02 11:20:59 +01:00
if args . loglevel == logging . WARNING :
2019-01-24 08:45:42 +01:00
print ( " failed tests: " + ' ' . join ( failed ) )
2013-03-27 13:40:10 +01:00
sys . exit ( 1 )
2013-11-02 16:07:13 +01:00
logger . info ( " passed all {} test case(s) " . format ( len ( passed ) ) )
2013-09-29 15:21:42 +02:00
if len ( skipped ) :
2013-11-02 16:07:13 +01:00
logger . info ( " skipped {} test case(s) " . format ( len ( skipped ) ) )
2013-11-02 11:20:59 +01:00
if args . loglevel == logging . WARNING :
2019-01-24 08:45:42 +01:00
print ( " passed all {} test case(s) " . format ( len ( passed ) ) )
2013-09-29 15:21:42 +02:00
if len ( skipped ) :
2019-01-24 08:45:42 +01:00
print ( " skipped {} test case(s) " . format ( len ( skipped ) ) )
2013-03-27 13:40:10 +01:00
if __name__ == " __main__ " :
main ( )