run.sh 21 KB

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