run.sh 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
  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_nose_test()
  400. {
  401. local interpreter="$1"
  402. local test_case="$2"
  403. shift; shift
  404. [ -z "$test_case" ] &&\
  405. die "Nose test case is missing"
  406. [ -d "$test_case" ] &&\
  407. die "Nose test case '$test_case' must not be a directory"
  408. [ -x "$test_case" ] &&\
  409. die "Nose test case '$test_case' must NOT be executable"
  410. # Select nosetests or nosetests3
  411. local interp_ver="$(get_interpreter_version "$interpreter")"
  412. local interp_major="$(echo "$interp_ver" | cut -d' ' -f 1)"
  413. if [ "$interp_major" = "2" ]; then
  414. find_executable nosetests
  415. local nose="$RET"
  416. else
  417. find_executable nosetests3
  418. local nose="$RET"
  419. fi
  420. # Resolve relative path
  421. [ "$(echo "$test_case" | cut -c1)" = '/' ] ||\
  422. local test_case="$(pwd)/$test_case"
  423. (
  424. EXTRA_PYTHONPATH=
  425. # Add nose libraries to pypy environment
  426. if [ "$(basename "$interpreter")" = "pypy" ]; then
  427. local p='import sys; print(":".join(p for p in sys.path if p.startswith("/usr/")))'
  428. EXTRA_PYTHONPATH="$("$interpreter" -c "$p"):$(python2 -c "$p"):$EXTRA_PYTHONPATH"
  429. fi
  430. # Add awlsim_tstlib.py to PYTHONPATH
  431. EXTRA_PYTHONPATH="$rootdir/tests:$EXTRA_PYTHONPATH"
  432. # Setup python environment
  433. adjust_niceness "$($SHELL -c 'echo $PPID')"
  434. setup_test_environment "$interpreter" "$test_case"
  435. local interpreter="$RET"
  436. # Run the nose test case
  437. cd "$rootdir" || die "Failed to cd to rootdir."
  438. local opts="--no-byte-compile --verbosity=2"
  439. if [ -n "$AWLSIM_TEST_QUIET" ]; then
  440. "$interpreter" "$nose" $opts "$test_case" >/dev/null 2>&1 ||\
  441. die "Nose test case '$(basename "$test_case")' failed."
  442. else
  443. "$interpreter" "$nose" $opts "$test_case" ||\
  444. die "Nose 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 the test helper library
  459. [ "$(basename "$testfile")" = "awlsim_tstlib.py" ] && return
  460. local disabled="$(get_conf "$testfile" disabled)"
  461. if [ -z "$disabled" ]; then
  462. # Print test headline
  463. local nl="-n"
  464. is_parallel_run && local nl=
  465. infomsg $nl "$(basename "$testfile") @ $(basename "$interpreter"): "
  466. local prev_dir="$(pwd)"
  467. cd "$rootdir" || die "cd to $rootdir failed"
  468. # Check the file type and run the tester
  469. if [ "$(echo -n "$testfile" | tail -c4)" = ".awl" ]; then
  470. check_dos_text_encoding "$testfile"
  471. run_awl_test "$interpreter" "$testfile" "$@"
  472. elif [ "$(echo -n "$testfile" | tail -c7)" = ".awlpro" ]; then
  473. run_awl_test "$interpreter" "$testfile" "$@"
  474. elif [ "$(echo -n "$testfile" | tail -c3)" = ".sh" ]; then
  475. run_sh_test "$interpreter" "$testfile" "$@"
  476. elif [ "$(echo -n "$testfile" | tail -c3)" = ".py" ]; then
  477. run_nose_test "$interpreter" "$testfile" "$@"
  478. else
  479. die "Test file type of '$testfile' not recognized"
  480. fi
  481. cd "$prev_dir" || die "cd to $prev_dir failed"
  482. else
  483. warnmsg "Skipping '$testfile' as it is disabled."
  484. fi
  485. }
  486. run_test_parallel()
  487. {
  488. if is_parallel_run; then
  489. # Run tests in parallel.
  490. wait_for_free_job_slot
  491. run_test "$@" &
  492. else
  493. # Run tests one-by-one.
  494. run_test "$@"
  495. fi
  496. }
  497. # $1=interpreter, $2=directory
  498. run_test_directory()
  499. {
  500. local interpreter="$1"
  501. local directory="$2"
  502. local prettydir="$(realpath -m --no-symlinks --relative-base="$rootdir" "$directory")/"
  503. infomsg ">>> entering $prettydir"
  504. # run .awlpro tests
  505. for entry in "$directory"/*; do
  506. [ -d "$entry" ] && continue
  507. [ "$(echo -n "$entry" | tail -c7)" = ".awlpro" ] || continue
  508. [ -e "$(dirname "$entry")/$(basename "$entry" .awlpro).sh" ] && continue
  509. run_test_parallel "$interpreter" "$entry"
  510. check_job_failure && return
  511. done
  512. # run .awl tests
  513. for entry in "$directory"/*; do
  514. [ -d "$entry" ] && continue
  515. [ "$(echo -n "$entry" | tail -c4)" = ".awl" ] || continue
  516. [ -e "$(dirname "$entry")/$(basename "$entry" .awl).awlpro" ] && continue
  517. [ -e "$(dirname "$entry")/$(basename "$entry" .awl).sh" ] && continue
  518. run_test_parallel "$interpreter" "$entry"
  519. check_job_failure && return
  520. done
  521. # run .sh tests
  522. for entry in "$directory"/*; do
  523. [ -d "$entry" ] && continue
  524. [ "$(echo -n "$entry" | tail -c3)" = ".sh" ] || continue
  525. run_test_parallel "$interpreter" "$entry"
  526. check_job_failure && return
  527. done
  528. # run nose tests
  529. for entry in "$directory"/*; do
  530. [ -d "$entry" ] && continue
  531. [ "$(echo -n "$entry" | tail -c3)" = ".py" ] || continue
  532. run_test_parallel "$interpreter" "$entry"
  533. check_job_failure && return
  534. done
  535. # Recurse into subdirectories
  536. for entry in "$directory"/*; do
  537. [ -d "$entry" ] || continue
  538. run_test_directory "$interpreter" "$entry"
  539. done
  540. infomsg "<<< leaving $prettydir"
  541. }
  542. # $1=interpreter
  543. warn_skipped()
  544. {
  545. local interpreter="$1"
  546. warnmsg "=== WARNING: '$interpreter' interpreter not found. Test skipped."
  547. warnmsg
  548. }
  549. __build_cython()
  550. {
  551. local cython="$1"
  552. local python="$2"
  553. have_prog "$cython" && have_prog "$python" || {
  554. warnmsg "=== WARNING: Cannot build $cython modules"
  555. return 1
  556. }
  557. cd "$rootdir" || die "cd to $rootdir failed"
  558. infomsg "=== Building awlsim $cython modules with $python"
  559. CFLAGS="-O0" CPPFLAGS= CXXFLAGS="-O0" LDFLAGS= \
  560. AWLSIM_CYTHON_BUILD=1 \
  561. AWLSIM_CYTHON_PARALLEL=1 \
  562. nice -n 5 \
  563. "$python" ./setup.py build >/dev/null ||\
  564. die "'$python ./setup.py build' failed"
  565. return 0
  566. }
  567. build_cython3()
  568. {
  569. __build_cython cython3 python3
  570. }
  571. # $@=testfiles
  572. do_tests()
  573. {
  574. cleanup_test_environment
  575. if [ $opt_quick -eq 0 ]; then
  576. local all_interp="python3 python2 pypy3 pypy cython3"
  577. if [ $opt_extended -ne 0 ]; then
  578. local all_interp="$all_interp jython"
  579. fi
  580. else
  581. local all_interp="python3 python2"
  582. if [ $opt_extended -ne 0 ]; then
  583. die "The options --quick and --extended are mutually exclusive."
  584. fi
  585. fi
  586. local cython3_build_pid=
  587. if is_parallel_run; then
  588. # Trigger the build jobs, if required.
  589. local inter="$opt_interpreter $all_interp"
  590. if printf '%s' "$inter" | grep -qwe 'cython3'; then
  591. wait_for_free_job_slot
  592. build_cython3 &
  593. local cython3_build_pid=$!
  594. fi
  595. fi
  596. for interpreter in "$opt_interpreter" $all_interp; do
  597. [ -z "$interpreter" ] && continue
  598. cleanup_test_environment
  599. # Create an interpreter name suitable as path component
  600. local interpreter_name="$(printf '%s' "$interpreter" | tr '/\\' _)"
  601. # Check if we should enable coverage tracing
  602. coverage_enabled=$opt_coverage
  603. if [ $coverage_enabled -ne 0 ] &&\
  604. [ "$interpreter" = "pypy" -o "$interpreter" = "pypy3" ]; then
  605. # Performance impact of coverage on PyPy is too big.
  606. # Disable coverage to avoid test failures.
  607. warnmsg "Disabling code coverage tracing (-c|--coverage) on PyPy due to bad performace."
  608. coverage_enabled=0
  609. fi
  610. # Prepare code coverage directory
  611. coverage_data_subdir="coverage-$interpreter_name"
  612. mkdir -p "$tmp_dir/$coverage_data_subdir" || die "Failed to create coverage data dir"
  613. # Basic interpreter setup. Build Cython modules.
  614. if [ "$interpreter" = "cython3" ]; then
  615. have_prog cython3 && have_prog python3 || {
  616. warn_skipped "$interpreter"
  617. [ -n "$opt_interpreter" ] && break || continue
  618. }
  619. if is_parallel_run; then
  620. wait_for_job_pid $cython3_build_pid
  621. else
  622. build_cython3 || die "Cython3 build failed."
  623. fi
  624. else
  625. have_prog "$interpreter" || {
  626. warn_skipped "$interpreter"
  627. [ -n "$opt_interpreter" ] && break || continue
  628. }
  629. fi
  630. local interp_ver="$(get_interpreter_version "$interpreter")"
  631. local interp_ver_dot="$(echo "$interp_ver" | tr ' ' '.')"
  632. local interp_major="$(echo "$interp_ver" | cut -d' ' -f 1)"
  633. local interp_minor="$(echo "$interp_ver" | cut -d' ' -f 2)"
  634. [ -z "$interp_ver" ] &&\
  635. die "Failed to get '$interpreter' version."
  636. [ "$interp_major" -eq 2 -a "$interp_minor" -lt 7 ] &&\
  637. die "'$interpreter' interpreter version '$interp_ver_dot' too old."
  638. infomsg "=== Running tests with '$interpreter'"
  639. if [ $# -eq 0 ]; then
  640. run_test_directory "$interpreter" "$basedir"
  641. else
  642. for opt in "$@"; do
  643. local opt="$(realpath -m --no-symlinks "$opt")"
  644. if [ -d "$opt" ]; then
  645. run_test_directory "$interpreter" "$opt"
  646. else
  647. run_test_parallel "$interpreter" "$opt"
  648. fi
  649. check_job_failure && break
  650. done
  651. fi
  652. infomsg
  653. check_job_failure && break
  654. # Generate code coverage report
  655. if [ $coverage_enabled -ne 0 ]; then
  656. # Wait for background jobs to finish
  657. wait_for_all_background_jobs
  658. if [ $global_retval -eq 0 ]; then
  659. infomsg "\nGenerating code coverage report..."
  660. local reportbase="$rootdir/code-coverage-report"
  661. local reportdir="$reportbase/awlsim-coverage-$interpreter_name"
  662. rm -rf "$reportdir"
  663. "$rootdir/awlsim-covreport" \
  664. "$reportdir" \
  665. "$tmp_dir/$coverage_data_subdir/" ||\
  666. die "Failed to generate code coverage report."
  667. fi
  668. fi
  669. [ -n "$opt_interpreter" ] && break
  670. done
  671. # Wait for background jobs to finish
  672. wait_for_all_background_jobs
  673. # Print summary
  674. if [ $global_retval -eq 0 ]; then
  675. infomsg
  676. infomsg -n "All tests succeeded"
  677. else
  678. errormsg
  679. errormsg -n "Some tests FAILED"
  680. fi
  681. if [ -n "$opt_interpreter" ]; then
  682. infomsg " (with interpreter '$opt_interpreter')"
  683. else
  684. if [ $opt_quick -eq 0 ]; then
  685. if [ $opt_extended -eq 0 ]; then
  686. infomsg " (full run)"
  687. else
  688. infomsg " (extended run)"
  689. fi
  690. else
  691. infomsg " (quick run)"
  692. fi
  693. fi
  694. }
  695. show_help()
  696. {
  697. infomsg "awlsim unit test script"
  698. infomsg
  699. infomsg "Usage: run.sh [OPTIONS] [testdirectory/testscript.awl/.awlpro/.sh/.py]"
  700. infomsg
  701. infomsg "Options:"
  702. infomsg " -i|--interpreter INTER Use INTER as interpreter for the tests"
  703. infomsg " -j|--jobs NR Set the number of jobs to run in parallel."
  704. infomsg " 0 means number-of-CPUs"
  705. infomsg " Default: 1"
  706. infomsg " -q|--quick Only run python2 and python3 tests"
  707. infomsg " -qq Shortcut for: -q -j 0"
  708. infomsg " -g|--no-gui Avoid tests that need GUI libraries"
  709. infomsg " -x|--extended Run tests on additional interpreters"
  710. infomsg " -n|--renice NICENESS Renice by NICENESS. Defaults to 10."
  711. infomsg " -Q|--quiet Less messages"
  712. infomsg " -L|--loglevel Default log level."
  713. infomsg " -l|--loop COUNT Number of test loops to execute."
  714. infomsg " Default: 1"
  715. infomsg " Set to 0 for infinite looping."
  716. infomsg " -c|--coverage Enable code coverage tracing."
  717. }
  718. tmp_dir="/tmp/awlsim-test-$$"
  719. rm -rf "$tmp_dir" >/dev/null 2>&1
  720. mkdir -p "$tmp_dir" || die "Failed to create temp dir '$tmp_dir'"
  721. trap cleanup_and_exit INT TERM
  722. trap cleanup EXIT
  723. test_fail_file="$(maketemp fail)"
  724. port_alloc_file="$(maketemp port)"
  725. jobs_tmp_file="$(maketemp jobs)"
  726. touch "${port_alloc_file}.lock"
  727. echo 4096 > "$port_alloc_file" || die "Failed to initialize port file"
  728. opt_interpreter=
  729. opt_quick=0
  730. opt_nogui=0
  731. opt_extended=0
  732. opt_renice=
  733. opt_jobs=1
  734. opt_loglevel=2
  735. opt_loop=1
  736. opt_coverage=0
  737. while [ $# -ge 1 ]; do
  738. [ "$(printf '%s' "$1" | cut -c1)" != "-" ] && break
  739. case "$1" in
  740. -h|--help)
  741. show_help
  742. exit 0
  743. ;;
  744. -i|--interpreter)
  745. shift
  746. opt_interpreter="$1"
  747. have_prog "$opt_interpreter" ||\
  748. die "Interpreter '${opt_interpreter}' not found"
  749. ;;
  750. -j|--jobs)
  751. shift
  752. opt_jobs="$1"
  753. ;;
  754. -q|--quick)
  755. opt_quick=1
  756. ;;
  757. -qq)
  758. opt_quick=1
  759. opt_jobs=0
  760. ;;
  761. -g|--no-gui)
  762. opt_nogui=1
  763. ;;
  764. -x|--extended)
  765. opt_extended=1
  766. ;;
  767. -n|--renice)
  768. shift
  769. opt_renice="$1"
  770. ;;
  771. -Q|--quiet)
  772. export AWLSIM_TEST_QUIET=1
  773. ;;
  774. -L|--loglevel)
  775. shift
  776. opt_loglevel="$1"
  777. ;;
  778. -l|--loop)
  779. shift
  780. opt_loop="$1"
  781. ;;
  782. -c|--coverage)
  783. opt_coverage=1
  784. ;;
  785. *)
  786. errormsg "Unknown option: $1"
  787. exit 1
  788. ;;
  789. esac
  790. shift
  791. done
  792. [ -z "$opt_jobs" -o -n "$(printf '%s' "$opt_jobs" | tr -d '[0-9]')" ] &&\
  793. die "--jobs: '$opt_jobs' is not a positive integer number."
  794. if [ $opt_jobs -eq 0 ]; then
  795. opt_jobs="$(getconf _NPROCESSORS_ONLN)"
  796. opt_jobs="$(expr $opt_jobs + 2)"
  797. fi
  798. [ -z "$opt_jobs" ] &&\
  799. die "Could not detect number of CPUs."
  800. if [ -z "$opt_loop" -o -n "$(printf '%s' "$opt_loop" | tr -d '[0-9]')" ] || [ $opt_loop -le 0 ]; then
  801. opt_loop=infinite
  802. fi
  803. do_renice()
  804. {
  805. local niceness="$1"
  806. local pid="$2"
  807. renice "$niceness" "$pid" >/dev/null
  808. }
  809. adjust_niceness()
  810. {
  811. local pid="$1"
  812. if [ -n "$opt_renice" ]; then
  813. do_renice "$opt_renice" "$pid" || die "Failed to renice"
  814. else
  815. # Try to renice. Ignore failure.
  816. do_renice 10 "$pid"
  817. fi
  818. }
  819. # Run the tests
  820. global_retval=0
  821. loop_iteration=0
  822. while [ "$opt_loop" = "infinite" ] || [ $opt_loop -gt 0 ]; do
  823. infomsg "Running test loop iteration $(expr "$loop_iteration" + 1)"
  824. do_tests "$@"
  825. if [ $global_retval -ne 0 ]; then
  826. break
  827. fi
  828. if [ "$opt_loop" != "infinite" ]; then
  829. opt_loop="$(expr "$opt_loop" - 1)"
  830. fi
  831. loop_iteration="$(expr "$loop_iteration" + 1)"
  832. done
  833. exit $global_retval