run.sh 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974
  1. #!/bin/sh
  2. # basedir is the root of the test directory in the package
  3. basedir="$(dirname "$0")"
  4. [ "$(echo "$basedir" | cut -c1)" = '/' ] || basedir="$PWD/$basedir"
  5. # rootdir is the root of the package
  6. rootdir="$basedir/.."
  7. failfile_write()
  8. {
  9. (
  10. flock -x 9 || die "Failed to take lock"
  11. echo "$*" >> "$test_fail_file"
  12. ) 9< "$test_fail_file"
  13. }
  14. infomsg()
  15. {
  16. [ -z "$AWLSIM_TEST_QUIET" ] && echo "$@"
  17. }
  18. warnmsg()
  19. {
  20. echo "WARNING: $@" >&2
  21. }
  22. errormsg()
  23. {
  24. echo "$@" >&2
  25. }
  26. die()
  27. {
  28. if [ -n "$*" ]; then
  29. errormsg "$*"
  30. # We might be in a sub-job. So write to fail-file.
  31. failfile_write "$*"
  32. fi
  33. exit 1
  34. }
  35. # Create a temporary file. $1=name, $2=subdir
  36. maketemp()
  37. {
  38. local prefix="$1"
  39. local subdir="$2"
  40. if [ -z "$subdir" ]; then
  41. local subdir="."
  42. else
  43. mkdir -p "$tmp_dir/$subdir"
  44. fi
  45. mktemp --tmpdir="$tmp_dir" "${subdir}/awlsim-test-${prefix}.XXXXXX"
  46. }
  47. # $1=message
  48. test_failed()
  49. {
  50. errormsg "=== TEST FAILED ==="
  51. die "$@"
  52. }
  53. cleanup()
  54. {
  55. wait
  56. rm -rf "$tmp_dir" >/dev/null 2>&1
  57. }
  58. cleanup_and_exit()
  59. {
  60. cleanup
  61. exit 1
  62. }
  63. # Get a configuration option.
  64. # $1=configured_file
  65. # $2=option_name
  66. # ($3=default_value)
  67. get_conf()
  68. {
  69. local configured_file="$1"
  70. local option_name="$2"
  71. local default_value="$3"
  72. local conf="${configured_file}.conf"
  73. local val="$default_value"
  74. if [ -r "$conf" ]; then
  75. local regex="^${option_name}="
  76. if grep -qEe "$regex" "$conf"; then
  77. local val="$(grep -Ee "$regex" "$conf" | cut -d'=' -f2-)"
  78. fi
  79. fi
  80. printf '%s' "$val"
  81. }
  82. # Allocate a new port number.
  83. get_port()
  84. {
  85. (
  86. flock -x 8 || die "Failed to take port lock"
  87. local port="$(cat "$port_alloc_file")"
  88. local next="$(expr "$port" + 1)"
  89. echo "$next" > "$port_alloc_file" ||\
  90. die "Failed to update port allocation file"
  91. echo -n "$port"
  92. ) 8> "${port_alloc_file}.lock"
  93. }
  94. # Returns true (0), if there are more than 1 jobs.
  95. is_parallel_run()
  96. {
  97. [ $opt_jobs -gt 1 ]
  98. }
  99. # Wait until there is at least one free job slot.
  100. wait_for_free_job_slot()
  101. {
  102. while true; do
  103. jobs -l > "$jobs_tmp_file" # can't use pipe on dash
  104. [ "$(cat "$jobs_tmp_file" | wc -l)" -lt $opt_jobs ] && break
  105. # Too many jobs. Waiting...
  106. sleep 0.1
  107. done
  108. }
  109. # $1 is the PID of the job to wait for.
  110. wait_for_job_pid()
  111. {
  112. local jobpid="$1"
  113. while true; do
  114. jobs -l > "$jobs_tmp_file" # can't use pipe on dash
  115. cat "$jobs_tmp_file" | tr -d '+-' |\
  116. sed -e 's/[[:blank:]]\+/\t/g' | cut -f2 |\
  117. grep -qe '^'"$jobpid"'$' || break
  118. # Job is still running...
  119. sleep 0.1
  120. done
  121. }
  122. # Returns true (0), if at least one background job failed.
  123. check_job_failure()
  124. {
  125. is_parallel_run &&\
  126. [ -e "$test_fail_file" ] &&\
  127. [ "0" != "$(du -s "$test_fail_file" | cut -f1)" ]
  128. }
  129. wait_for_all_background_jobs()
  130. {
  131. is_parallel_run || return
  132. infomsg "Waiting for background jobs..."
  133. wait
  134. # Print the fail information.
  135. if check_job_failure; then
  136. errormsg
  137. errormsg "===== FAILURES in parallel run: ====="
  138. cat "$test_fail_file" >&2
  139. errormsg "====================================="
  140. global_retval=1
  141. fi
  142. }
  143. # $1=interpreter
  144. # Returns version on stdout as: MAJOR MINOR PATCHLEVEL
  145. get_interpreter_version()
  146. {
  147. local interpreter="$1"
  148. [ "$interpreter" = "cython3" ] && local interpreter=python3
  149. "$interpreter" -c 'import sys; print("%d %d %d" % sys.version_info[0:3]);' 2>/dev/null
  150. }
  151. # Check if an interpreter is able to run GUI code.
  152. # $1=interpreter
  153. interpreter_is_gui_compat()
  154. {
  155. local interpreter="$1"
  156. [ $opt_nogui -eq 0 ] &&\
  157. [ "$interpreter" = "python" -o \
  158. "$interpreter" = "python2" -o \
  159. "$interpreter" = "python3" -o \
  160. "$interpreter" = "cython3" ]
  161. }
  162. # $1=program_name
  163. have_prog()
  164. {
  165. local program="$1"
  166. which "$program" >/dev/null 2>&1
  167. }
  168. # $1=executable_name
  169. find_executable()
  170. {
  171. local executable_name="$1"
  172. local executable_path="$(which "$executable_name")"
  173. [ -n "$executable_path" ] ||\
  174. die "$executable_name executable not found."\
  175. "Please install $executable_name."
  176. RET="$executable_path"
  177. }
  178. # Check DOS file encoding.
  179. # $1=file
  180. check_dos_text_encoding()
  181. {
  182. local file="$1"
  183. if [ x"$(du -b "$file" | cut -f1)" != x"0" ]; then
  184. # Check CR/LF
  185. file -L "$file" | grep -qe 'CRLF line terminators' || {
  186. die "ERROR: '$file' is not in DOS format."
  187. }
  188. # Check file encoding
  189. file -L "$file" | grep -qEe '(ISO-8859 text)|(ASCII text)' || {
  190. die "ERROR: '$file' invalid file encoding."
  191. }
  192. fi
  193. }
  194. # $1=interpreter $2=tested_file [$3=test_name]
  195. setup_test_environment()
  196. {
  197. local interpreter="$1"
  198. local tested_file="$2"
  199. local test_name="$3"
  200. [ -z "$test_name" ] && local test_name="$tested_file"
  201. local test_name="$(realpath -m --no-symlinks --relative-base="$rootdir" "$test_name" | tr '/\\' _)"
  202. # Check if we want to run on Cython3 and set up the environment.
  203. local use_cython=0
  204. if [ "$interpreter" = "cython3" ] ||\
  205. [ "$interpreter" = "python3" -a "$AWLSIM_CYTHON" != "" ]; then
  206. # We want to run the test using Cython3
  207. local use_cython=3
  208. for i in "$rootdir"/build/lib.linux-*-3.*; do
  209. export PYTHONPATH="$i"
  210. break
  211. done
  212. # Enforce cython module usage
  213. export AWLSIM_CYTHON=2
  214. # The actual interpreter is Python
  215. local interpreter=python3
  216. else
  217. # Not Cython
  218. export PYTHONPATH=
  219. export AWLSIM_CYTHON=
  220. fi
  221. # Extra environment variables
  222. RAW_EXTRA_ENV="$(get_conf "$tested_file" env)"
  223. for env in $(printf '%s' "$RAW_EXTRA_ENV" | tr ':' ' '); do
  224. eval export "$env"
  225. done
  226. # Get extra PYTHONPATH from test case config file.
  227. local conf_pythonpath=
  228. if [ -n "$tested_file" ]; then
  229. local raw_conf_pythonpath="$(get_conf "$tested_file" PYTHONPATH)"
  230. local onepath=
  231. for onepath in $(printf '%s' "$raw_conf_pythonpath" | tr ':' ' '); do
  232. if [ -n "$conf_pythonpath" ]; then
  233. local conf_pythonpath="$conf_pythonpath:"
  234. fi
  235. local conf_pythonpath="${conf_pythonpath}$(realpath -m --no-symlinks "$rootdir/$onepath")"
  236. done
  237. fi
  238. # Export PYTHONPATHs
  239. export PYTHONPATH="$PYTHONPATH:$EXTRA_PYTHONPATH:$conf_pythonpath"
  240. export JYTHONPATH="$JYTHONPATH:$EXTRA_PYTHONPATH:$conf_pythonpath"
  241. export IRONPYTHONPATH="$IRONPYTHONPATH:$EXTRA_PYTHONPATH:$conf_pythonpath"
  242. export MICROPYPATH="$MICROPYPATH:$EXTRA_PYTHONPATH:$conf_pythonpath"
  243. # Disable Python optimization so that assert statements are enabled.
  244. # Enable warnings
  245. # Enable hash seed randomization.
  246. unset PYTHONSTARTUP
  247. unset PYTHONY2K
  248. unset PYTHONOPTIMIZE
  249. unset PYTHONDEBUG
  250. unset PYTHONDONTWRITEBYTECODE
  251. unset PYTHONINSPECT
  252. unset PYTHONIOENCODING
  253. unset PYTHONNOUSERSITE
  254. unset PYTHONUNBUFFERED
  255. unset PYTHONVERBOSE
  256. if [ $use_cython -eq 0 ]; then
  257. export PYTHONWARNINGS=once
  258. else
  259. export PYTHONWARNINGS=once,ignore::ImportWarning
  260. fi
  261. export PYTHONHASHSEED=random
  262. # Disable CPU affinity
  263. unset AWLSIM_AFFINITY
  264. # Setup coverage tracing
  265. if [ $coverage_enabled -eq 0 ]; then
  266. unset AWLSIM_COVERAGE
  267. else
  268. local coverage_data_file="$(maketemp "coverage_${test_name}" "$coverage_data_subdir")"
  269. rm "$coverage_data_file"
  270. export AWLSIM_COVERAGE="$coverage_data_file"
  271. fi
  272. RET="$interpreter"
  273. }
  274. cleanup_test_environment()
  275. {
  276. export AWLSIM_CYTHON=
  277. export PYTHONPATH=
  278. export JYTHONPATH=
  279. export IRONPYTHONPATH=
  280. export MICROPYPATH=
  281. export EXTRA_PYTHONPATH=
  282. # Unexport all extra envs
  283. for env in $(printf '%s' "$RAW_EXTRA_ENV" | tr ':' ' '); do
  284. eval export "$(printf '%s' "$env" | cut -d'=' -f1)"=
  285. done
  286. }
  287. # $1=interpreter $2=awl_file ($3ff additional options to awlsim-test)
  288. run_awl_test()
  289. {
  290. local interpreter="$1"
  291. local awl="$2"
  292. shift; shift
  293. # By default run once with all optimizers enabled.
  294. local optimizer_runs="$(get_conf "$awl" optimizer_runs all)"
  295. local first_opti=1
  296. for optimizers in $optimizer_runs; do
  297. [ $first_opti -eq 0 ] && infomsg -n " / "
  298. local first_opti=0
  299. local tries="$(get_conf "$awl" tries 1)"
  300. [ $tries -lt 1 ] && local tries=1
  301. local first_try=1
  302. local ok=0
  303. local exit_code=-1
  304. local expected_exit_code=-2
  305. while [ $tries -gt 0 -a $ok -eq 0 ]; do
  306. local tries="$(expr "$tries" - 1)"
  307. (
  308. [ $first_try -ne 0 ] && adjust_niceness "$($SHELL -c 'echo $PPID')"
  309. setup_test_environment "$interpreter" "$awl"
  310. local actual_interpreter="$RET"
  311. local loglevel="$(get_conf "$awl" loglevel "$opt_loglevel")"
  312. local expected_exit_code="$(get_conf "$awl" exit_code 0)"
  313. [ $expected_exit_code -eq 0 ] || local loglevel=0
  314. local cycle_limit="$(get_conf "$awl" cycle_limit 60)"
  315. local max_runtime="$(get_conf "$awl" max_runtime -1)"
  316. local accus="$(get_conf "$awl" accus)"
  317. if [ "$accus" = "2" ]; then
  318. local accus=--twoaccu
  319. elif [ "$accus" = "4" ]; then
  320. local accus=--fouraccu
  321. elif [ -n "$accus" ]; then
  322. cleanup_test_environment
  323. die "Invalid 'accus' value in .conf"
  324. fi
  325. local dump_opt=
  326. [ $loglevel -ge 3 ] && local dump_opt="--no-cpu-dump"
  327. "$actual_interpreter" "$rootdir/awlsim-test" \
  328. --loglevel $loglevel \
  329. --extended-insns \
  330. --hardware debug:inputAddressBase=7:outputAddressBase=8:dummyParam=True \
  331. --cycle-limit "$cycle_limit" \
  332. --max-runtime "$max_runtime" \
  333. --optimizers "$optimizers" \
  334. $accus \
  335. $dump_opt \
  336. "$@" \
  337. "$awl"
  338. local exit_code=$?
  339. if [ $exit_code -ne $expected_exit_code ]; then
  340. # Test failed
  341. cleanup_test_environment
  342. if [ $tries -gt 0 ]; then
  343. infomsg "Test '$(basename "$awl")' FAILED, but retrying ($tries tries left)..."
  344. sleep 1
  345. die # Next try
  346. else
  347. test_failed "\nTest '$(basename "$awl")' FAILED" \
  348. "\nInterpreter = $interpreter" \
  349. "\nOptimizers = $optimizers" \
  350. "\nActual exit code = $exit_code" \
  351. "\nExpected exit code = $expected_exit_code"
  352. fi
  353. fi
  354. cleanup_test_environment
  355. ) && local ok=1
  356. local first_try=0
  357. done
  358. if [ $ok -eq 0 ]; then
  359. die # Test failed
  360. fi
  361. if is_parallel_run; then
  362. infomsg "$(basename "$awl"): O=$optimizers -> OK"
  363. else
  364. infomsg -n "O=$optimizers -> OK"
  365. fi
  366. done
  367. is_parallel_run || infomsg
  368. }
  369. # $1=interpreter $2=sh_file
  370. run_sh_test()
  371. {
  372. local interpreter="$1"
  373. local sh_file="$2"
  374. shift; shift
  375. [ -x "$sh_file" ] && die "SH-file '$sh_file' must NOT be executable"
  376. [ "$(echo "$sh_file" | cut -c1)" = '/' ] || local sh_file="$(pwd)/$sh_file"
  377. # Run the test
  378. (
  379. # Source the test file
  380. . "$basedir/sh-test.defaults"
  381. . "$sh_file"
  382. adjust_niceness "$($SHELL -c 'echo $PPID')"
  383. setup_test_environment "$interpreter" "$sh_file"
  384. local interpreter="$RET"
  385. local test_dir="$(dirname "$sh_file")"
  386. local test_name="$(basename "$sh_file" .sh)"
  387. sh_test "$interpreter" "$test_dir" "$test_name"
  388. cleanup_test_environment
  389. )
  390. local result=$?
  391. [ $result -eq 0 ] || die "Test failed with error code $result"
  392. if is_parallel_run; then
  393. infomsg "$(basename "$sh_file"): OK"
  394. else
  395. infomsg "OK"
  396. fi
  397. }
  398. # $1=interpreter $2=test_file
  399. run_pyunit_test()
  400. {
  401. local interpreter="$1"
  402. local test_case="$2"
  403. shift; shift
  404. [ -z "$test_case" ] &&\
  405. die "Python unittest test case is missing"
  406. [ -d "$test_case" ] &&\
  407. die "Python unittest test case '$test_case' must not be a directory"
  408. [ -x "$test_case" ] &&\
  409. die "Python unittest test case '$test_case' must NOT be executable"
  410. # Resolve relative path
  411. [ "$(echo "$test_case" | cut -c1)" = '/' ] ||\
  412. local test_case="$(pwd)/$test_case"
  413. (
  414. # Add awlsim_tstlib.py to PYTHONPATH
  415. EXTRA_PYTHONPATH="$rootdir:$rootdir/tests:$EXTRA_PYTHONPATH"
  416. # Setup python environment
  417. adjust_niceness "$($SHELL -c 'echo $PPID')"
  418. local orig_interpreter="$interpreter"
  419. setup_test_environment "$interpreter" "$test_case"
  420. local interpreter="$RET"
  421. export PYTHONDONTWRITEBYTECODE=1
  422. if [ "$orig_interpreter" = "cython3" ] && ! [ -e "$(dirname "$test_case")/no_cython" ]; then
  423. # Get the relative test case path starting in 'tests' directory.
  424. local relpath="$(realpath -m --no-symlinks --relative-base="$rootdir/tests" "$test_case")"
  425. # Patch the module name to Cython name (append _cython).
  426. local patch_re='s/(tc[0-9][0-9][0-9]_[0-9a-zA-Z]*)/\1_cython/'
  427. local relpath_cython="$(printf "%s" "$relpath" | sed -Ee "$patch_re")"
  428. # Get the relative directory of the test case.
  429. local reldir_cython="$(dirname "$relpath_cython")"
  430. # Go to the unittest subdir to run the Cython unittest.
  431. cd "$rootdir/tests/build/"lib.*-3.*"/$reldir_cython" || die "Failed to cd to test directory."
  432. else
  433. # Go to the unittest subdir to run the Python unittest.
  434. cd "$(dirname "$test_case")" || die "Failed to cd to test directory."
  435. fi
  436. # Convert test name to module name (for python2)
  437. local test_case="$(basename "$test_case" .py)"
  438. # Run it.
  439. if [ -n "$AWLSIM_TEST_QUIET" ]; then
  440. "$interpreter" -m unittest "$test_case" >/dev/null 2>&1 ||\
  441. die "Python unittest test case '$(basename "$test_case")' failed."
  442. else
  443. "$interpreter" -m unittest "$test_case" ||\
  444. die "Python unittest test case '$(basename "$test_case")' failed."
  445. fi
  446. infomsg "$(basename "$test_case"): OK"
  447. cleanup_test_environment
  448. ) || die "'$(basename "$test_case")' FAILED"
  449. }
  450. # $1=interpreter $2=testfile(.awl/.sh) ($3ff additional options to awlsim-test or testfile)
  451. run_test()
  452. {
  453. local interpreter="$1"
  454. local testfile="$2"
  455. shift; shift
  456. # Don't run ourself
  457. [ "$(basename "$testfile")" = "run.sh" ] && return
  458. # Don't run artifacts that aren't actual test cases.
  459. [ "$(basename "$testfile")" = "awlsim_tstlib.py" ] && return
  460. [ "$(basename "$testfile")" = "setup-cython-tests.py" ] && return
  461. [ "$(basename "$testfile")" = "__init__.py" ] && return
  462. local disabled="$(get_conf "$testfile" disabled)"
  463. if [ -z "$disabled" ]; then
  464. # Print test headline
  465. local nl="-n"
  466. is_parallel_run && local nl=
  467. infomsg $nl "$(basename "$testfile") @ $(basename "$interpreter"): "
  468. local prev_dir="$(pwd)"
  469. cd "$rootdir" || die "cd to $rootdir failed"
  470. # Check the file type and run the tester
  471. if [ "$(echo -n "$testfile" | tail -c4)" = ".awl" ]; then
  472. check_dos_text_encoding "$testfile"
  473. run_awl_test "$interpreter" "$testfile" "$@"
  474. elif [ "$(echo -n "$testfile" | tail -c7)" = ".awlpro" ]; then
  475. run_awl_test "$interpreter" "$testfile" "$@"
  476. elif [ "$(echo -n "$testfile" | tail -c3)" = ".sh" ]; then
  477. run_sh_test "$interpreter" "$testfile" "$@"
  478. elif [ "$(echo -n "$testfile" | tail -c3)" = ".py" ]; then
  479. run_pyunit_test "$interpreter" "$testfile" "$@"
  480. else
  481. die "Test file type of '$testfile' not recognized"
  482. fi
  483. cd "$prev_dir" || die "cd to $prev_dir failed"
  484. else
  485. warnmsg "Skipping '$testfile' as it is disabled."
  486. fi
  487. }
  488. run_test_parallel()
  489. {
  490. if is_parallel_run; then
  491. # Run tests in parallel.
  492. wait_for_free_job_slot
  493. run_test "$@" &
  494. else
  495. # Run tests one-by-one.
  496. run_test "$@"
  497. fi
  498. }
  499. # $1=interpreter, $2=directory
  500. run_test_directory()
  501. {
  502. local interpreter="$1"
  503. local directory="$2"
  504. [ "$(basename "$directory")" = "build" ] && return
  505. local prettydir="$(realpath -m --no-symlinks --relative-base="$rootdir" "$directory")/"
  506. infomsg ">>> entering $prettydir"
  507. # run .awlpro tests
  508. for entry in "$directory"/*; do
  509. [ -d "$entry" ] && continue
  510. [ "$(echo -n "$entry" | tail -c7)" = ".awlpro" ] || continue
  511. [ -e "$(dirname "$entry")/$(basename "$entry" .awlpro).sh" ] && continue
  512. run_test_parallel "$interpreter" "$entry"
  513. check_job_failure && return
  514. done
  515. # run .awl tests
  516. for entry in "$directory"/*; do
  517. [ -d "$entry" ] && continue
  518. [ "$(echo -n "$entry" | tail -c4)" = ".awl" ] || continue
  519. [ -e "$(dirname "$entry")/$(basename "$entry" .awl).awlpro" ] && continue
  520. [ -e "$(dirname "$entry")/$(basename "$entry" .awl).sh" ] && continue
  521. run_test_parallel "$interpreter" "$entry"
  522. check_job_failure && return
  523. done
  524. # run .sh tests
  525. for entry in "$directory"/*; do
  526. [ -d "$entry" ] && continue
  527. [ "$(echo -n "$entry" | tail -c3)" = ".sh" ] || continue
  528. run_test_parallel "$interpreter" "$entry"
  529. check_job_failure && return
  530. done
  531. # run .py unittest tests
  532. for entry in "$directory"/*; do
  533. [ -d "$entry" ] && continue
  534. [ "$(echo -n "$entry" | tail -c3)" = ".py" ] || continue
  535. [ "$entry" = "__init__.py" ] && continue
  536. run_test_parallel "$interpreter" "$entry"
  537. check_job_failure && return
  538. done
  539. # Recurse into subdirectories
  540. for entry in "$directory"/*; do
  541. [ -d "$entry" ] || continue
  542. run_test_directory "$interpreter" "$entry"
  543. done
  544. infomsg "<<< leaving $prettydir"
  545. }
  546. # $1=interpreter
  547. warn_skipped()
  548. {
  549. local interpreter="$1"
  550. warnmsg "=== WARNING: '$interpreter' interpreter not found. Test skipped."
  551. warnmsg
  552. }
  553. __build_cython()
  554. {
  555. local cython="$1"
  556. local python="$2"
  557. have_prog "$cython" && have_prog "$python" || {
  558. warnmsg "=== WARNING: Cannot build $cython modules"
  559. return 1
  560. }
  561. (
  562. infomsg "=== Building awlsim $cython modules with $python"
  563. cd "$rootdir" || die "cd to $rootdir failed"
  564. CFLAGS="-O0" CPPFLAGS= CXXFLAGS="-O0" LDFLAGS= \
  565. AWLSIM_CYTHON_BUILD=1 \
  566. AWLSIM_CYTHON_PARALLEL=1 \
  567. nice -n 5 \
  568. "$python" ./setup.py build >/dev/null ||\
  569. die "'$python ./setup.py build' failed"
  570. ) || die
  571. (
  572. infomsg "=== Building awlsim $cython test cases with $python"
  573. cd "$rootdir/tests" || die "cd to $rootdir/tests failed"
  574. rm -rf build || die "Failed to clean test cases build"
  575. nice -n 5 \
  576. "$python" ./setup-cython-tests.py build >/dev/null ||\
  577. die "'$python ./setup-cython-tests.py build' failed"
  578. ) || die
  579. return 0
  580. }
  581. build_cython3()
  582. {
  583. __build_cython cython3 python3
  584. }
  585. # $@=testfiles
  586. do_tests()
  587. {
  588. cleanup_test_environment
  589. if [ $opt_quick -eq 0 ]; then
  590. local all_interp="python3 python2 pypy3 pypy cython3"
  591. if [ $opt_extended -ne 0 ]; then
  592. local all_interp="$all_interp jython"
  593. fi
  594. else
  595. local all_interp="python3 python2"
  596. if [ $opt_extended -ne 0 ]; then
  597. die "The options --quick and --extended are mutually exclusive."
  598. fi
  599. fi
  600. for interpreter in "$opt_interpreter" $all_interp; do
  601. [ -z "$interpreter" ] && continue
  602. cleanup_test_environment
  603. # Create an interpreter name suitable as path component
  604. local interpreter_name="$(printf '%s' "$interpreter" | tr '/\\' _)"
  605. # Check if we should enable coverage tracing
  606. coverage_enabled=$opt_coverage
  607. if [ $coverage_enabled -ne 0 ] &&\
  608. [ "$interpreter" = "pypy" -o "$interpreter" = "pypy3" ]; then
  609. # Performance impact of coverage on PyPy is too big.
  610. # Disable coverage to avoid test failures.
  611. warnmsg "Disabling code coverage tracing (-c|--coverage) on PyPy due to bad performace."
  612. coverage_enabled=0
  613. fi
  614. # Prepare code coverage directory
  615. coverage_data_subdir="coverage-$interpreter_name"
  616. mkdir -p "$tmp_dir/$coverage_data_subdir" || die "Failed to create coverage data dir"
  617. # Basic interpreter setup. Build Cython modules.
  618. if [ "$interpreter" = "cython3" ]; then
  619. have_prog cython3 && have_prog python3 || {
  620. warn_skipped "$interpreter"
  621. [ -n "$opt_interpreter" ] && break || continue
  622. }
  623. wait_for_all_background_jobs
  624. build_cython3 || die "Cython3 build failed."
  625. else
  626. have_prog "$interpreter" || {
  627. warn_skipped "$interpreter"
  628. [ -n "$opt_interpreter" ] && break || continue
  629. }
  630. fi
  631. local interp_ver="$(get_interpreter_version "$interpreter")"
  632. local interp_ver_dot="$(echo "$interp_ver" | tr ' ' '.')"
  633. local interp_major="$(echo "$interp_ver" | cut -d' ' -f 1)"
  634. local interp_minor="$(echo "$interp_ver" | cut -d' ' -f 2)"
  635. [ -z "$interp_ver" ] &&\
  636. die "Failed to get '$interpreter' version."
  637. [ "$interp_major" -eq 2 -a "$interp_minor" -lt 7 ] &&\
  638. die "'$interpreter' interpreter version '$interp_ver_dot' too old."
  639. infomsg "=== Running tests with '$interpreter'"
  640. if [ $# -eq 0 ]; then
  641. run_test_directory "$interpreter" "$basedir"
  642. else
  643. for opt in "$@"; do
  644. local opt="$(realpath -m --no-symlinks "$opt")"
  645. if [ -d "$opt" ]; then
  646. run_test_directory "$interpreter" "$opt"
  647. else
  648. run_test_parallel "$interpreter" "$opt"
  649. fi
  650. check_job_failure && break
  651. done
  652. fi
  653. infomsg
  654. check_job_failure && break
  655. # Generate code coverage report
  656. if [ $coverage_enabled -ne 0 ]; then
  657. # Wait for background jobs to finish
  658. wait_for_all_background_jobs
  659. if [ $global_retval -eq 0 ]; then
  660. infomsg "\nGenerating code coverage report..."
  661. local reportbase="$rootdir/code-coverage-report"
  662. local reportdir="$reportbase/awlsim-coverage-$interpreter_name"
  663. rm -rf "$reportdir"
  664. "$rootdir/awlsim-covreport" \
  665. "$reportdir" \
  666. "$tmp_dir/$coverage_data_subdir/" ||\
  667. die "Failed to generate code coverage report."
  668. fi
  669. fi
  670. [ -n "$opt_interpreter" ] && break
  671. done
  672. # Wait for background jobs to finish
  673. wait_for_all_background_jobs
  674. # Print summary
  675. if [ $global_retval -eq 0 ]; then
  676. infomsg
  677. infomsg -n "All tests succeeded"
  678. else
  679. errormsg
  680. errormsg -n "Some tests FAILED"
  681. fi
  682. if [ -n "$opt_interpreter" ]; then
  683. infomsg " (with interpreter '$opt_interpreter')"
  684. else
  685. if [ $opt_quick -eq 0 ]; then
  686. if [ $opt_extended -eq 0 ]; then
  687. infomsg " (full run)"
  688. else
  689. infomsg " (extended run)"
  690. fi
  691. else
  692. infomsg " (quick run)"
  693. fi
  694. fi
  695. }
  696. show_help()
  697. {
  698. infomsg "awlsim unit test script"
  699. infomsg
  700. infomsg "Usage: run.sh [OPTIONS] [testdirectory/testscript.awl/.awlpro/.sh/.py]"
  701. infomsg
  702. infomsg "Options:"
  703. infomsg " -i|--interpreter INTER Use INTER as interpreter for the tests"
  704. infomsg " -j|--jobs NR Set the number of jobs to run in parallel."
  705. infomsg " 0 means number-of-CPUs"
  706. infomsg " Default: 1"
  707. infomsg " -q|--quick Only run python2 and python3 tests"
  708. infomsg " -qq Shortcut for: -q -j 0"
  709. infomsg " -g|--no-gui Avoid tests that need GUI libraries"
  710. infomsg " -x|--extended Run tests on additional interpreters"
  711. infomsg " -n|--renice NICENESS Renice by NICENESS. Defaults to 10."
  712. infomsg " -Q|--quiet Less messages"
  713. infomsg " -L|--loglevel Default log level."
  714. infomsg " -l|--loop COUNT Number of test loops to execute."
  715. infomsg " Default: 1"
  716. infomsg " Set to 0 for infinite looping."
  717. infomsg " -c|--coverage Enable code coverage tracing."
  718. }
  719. tmp_dir="/tmp/awlsim-test-$$"
  720. rm -rf "$tmp_dir" >/dev/null 2>&1
  721. mkdir -p "$tmp_dir" || die "Failed to create temp dir '$tmp_dir'"
  722. trap cleanup_and_exit INT TERM
  723. trap cleanup EXIT
  724. test_fail_file="$(maketemp fail)"
  725. port_alloc_file="$(maketemp port)"
  726. jobs_tmp_file="$(maketemp jobs)"
  727. touch "${port_alloc_file}.lock"
  728. echo 4096 > "$port_alloc_file" || die "Failed to initialize port file"
  729. opt_interpreter=
  730. opt_quick=0
  731. opt_nogui=0
  732. opt_extended=0
  733. opt_renice=
  734. opt_jobs=1
  735. opt_loglevel=2
  736. opt_loop=1
  737. opt_coverage=0
  738. while [ $# -ge 1 ]; do
  739. [ "$(printf '%s' "$1" | cut -c1)" != "-" ] && break
  740. case "$1" in
  741. -h|--help)
  742. show_help
  743. exit 0
  744. ;;
  745. -i|--interpreter)
  746. shift
  747. opt_interpreter="$1"
  748. have_prog "$opt_interpreter" ||\
  749. die "Interpreter '${opt_interpreter}' not found"
  750. ;;
  751. -j|--jobs)
  752. shift
  753. opt_jobs="$1"
  754. ;;
  755. -q|--quick)
  756. opt_quick=1
  757. ;;
  758. -qq)
  759. opt_quick=1
  760. opt_jobs=0
  761. ;;
  762. -g|--no-gui)
  763. opt_nogui=1
  764. ;;
  765. -x|--extended)
  766. opt_extended=1
  767. ;;
  768. -n|--renice)
  769. shift
  770. opt_renice="$1"
  771. ;;
  772. -Q|--quiet)
  773. export AWLSIM_TEST_QUIET=1
  774. ;;
  775. -L|--loglevel)
  776. shift
  777. opt_loglevel="$1"
  778. ;;
  779. -l|--loop)
  780. shift
  781. opt_loop="$1"
  782. ;;
  783. -c|--coverage)
  784. opt_coverage=1
  785. ;;
  786. *)
  787. errormsg "Unknown option: $1"
  788. exit 1
  789. ;;
  790. esac
  791. shift
  792. done
  793. [ -z "$opt_jobs" -o -n "$(printf '%s' "$opt_jobs" | tr -d '[0-9]')" ] &&\
  794. die "--jobs: '$opt_jobs' is not a positive integer number."
  795. if [ $opt_jobs -eq 0 ]; then
  796. opt_jobs="$(getconf _NPROCESSORS_ONLN)"
  797. opt_jobs="$(expr $opt_jobs + 2)"
  798. fi
  799. [ -z "$opt_jobs" ] &&\
  800. die "Could not detect number of CPUs."
  801. if [ -z "$opt_loop" -o -n "$(printf '%s' "$opt_loop" | tr -d '[0-9]')" ] || [ $opt_loop -le 0 ]; then
  802. opt_loop=infinite
  803. fi
  804. do_renice()
  805. {
  806. local niceness="$1"
  807. local pid="$2"
  808. renice "$niceness" "$pid" >/dev/null
  809. }
  810. adjust_niceness()
  811. {
  812. local pid="$1"
  813. if [ -n "$opt_renice" ]; then
  814. do_renice "$opt_renice" "$pid" || die "Failed to renice"
  815. else
  816. # Try to renice. Ignore failure.
  817. do_renice 10 "$pid"
  818. fi
  819. }
  820. # Run the tests
  821. global_retval=0
  822. loop_iteration=0
  823. while [ "$opt_loop" = "infinite" ] || [ $opt_loop -gt 0 ]; do
  824. infomsg "Running test loop iteration $(expr "$loop_iteration" + 1)"
  825. do_tests "$@"
  826. if [ $global_retval -ne 0 ]; then
  827. break
  828. fi
  829. if [ "$opt_loop" != "infinite" ]; then
  830. opt_loop="$(expr "$opt_loop" - 1)"
  831. fi
  832. loop_iteration="$(expr "$loop_iteration" + 1)"
  833. done
  834. exit $global_retval