run.sh 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935
  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. 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. # Extra environment variables
  221. RAW_EXTRA_ENV="$(get_conf "$tested_file" env)"
  222. for env in $(printf '%s' "$RAW_EXTRA_ENV" | tr ':' ' '); do
  223. eval export "$env"
  224. done
  225. # Get extra PYTHONPATH from test case config file.
  226. local conf_pythonpath=
  227. if [ -n "$tested_file" ]; then
  228. local raw_conf_pythonpath="$(get_conf "$tested_file" PYTHONPATH)"
  229. local onepath=
  230. for onepath in $(printf '%s' "$raw_conf_pythonpath" | tr ':' ' '); do
  231. if [ -n "$conf_pythonpath" ]; then
  232. local conf_pythonpath="$conf_pythonpath:"
  233. fi
  234. local conf_pythonpath="${conf_pythonpath}$(realpath -m --no-symlinks "$rootdir/$onepath")"
  235. done
  236. fi
  237. # Export PYTHONPATHs
  238. export PYTHONPATH="$PYTHONPATH:$EXTRA_PYTHONPATH:$conf_pythonpath"
  239. export JYTHONPATH="$JYTHONPATH:$EXTRA_PYTHONPATH:$conf_pythonpath"
  240. export IRONPYTHONPATH="$IRONPYTHONPATH:$EXTRA_PYTHONPATH:$conf_pythonpath"
  241. export MICROPYPATH="$MICROPYPATH:$EXTRA_PYTHONPATH:$conf_pythonpath"
  242. # Disable Python optimization so that assert statements are enabled.
  243. # Enable warnings
  244. # Enable hash seed randomization.
  245. unset PYTHONSTARTUP
  246. unset PYTHONY2K
  247. unset PYTHONOPTIMIZE
  248. unset PYTHONDEBUG
  249. unset PYTHONDONTWRITEBYTECODE
  250. unset PYTHONINSPECT
  251. unset PYTHONIOENCODING
  252. unset PYTHONNOUSERSITE
  253. unset PYTHONUNBUFFERED
  254. unset PYTHONVERBOSE
  255. if [ $use_cython -eq 0 ]; then
  256. export PYTHONWARNINGS=once
  257. else
  258. export PYTHONWARNINGS=once,ignore::ImportWarning
  259. fi
  260. export PYTHONHASHSEED=random
  261. # Disable CPU affinity
  262. unset AWLSIM_AFFINITY
  263. RET="$interpreter"
  264. }
  265. cleanup_test_environment()
  266. {
  267. export AWLSIM_CYTHON=
  268. export PYTHONPATH=
  269. export JYTHONPATH=
  270. export IRONPYTHONPATH=
  271. export MICROPYPATH=
  272. export EXTRA_PYTHONPATH=
  273. # Unexport all extra envs
  274. for env in $(printf '%s' "$RAW_EXTRA_ENV" | tr ':' ' '); do
  275. eval export "$(printf '%s' "$env" | cut -d'=' -f1)"=
  276. done
  277. }
  278. # $1=interpreter $2=awl_file ($3ff additional options to awlsim-test)
  279. run_awl_test()
  280. {
  281. local interpreter="$1"
  282. local awl="$2"
  283. shift; shift
  284. setup_test_environment "$interpreter" "$awl"
  285. local actual_interpreter="$RET"
  286. # By default run once with all optimizers enabled.
  287. local optimizer_runs="$(get_conf "$awl" optimizer_runs all)"
  288. local first=1
  289. for optimizers in $optimizer_runs; do
  290. [ $first -eq 0 ] && infomsg -n " / "
  291. local first=0
  292. local test_time_file="$(maketemp time)"
  293. local tries="$(get_conf "$awl" tries 1)"
  294. [ $tries -lt 1 ] && local tries=1
  295. local ok=0
  296. local exit_code=-1
  297. local expected_exit_code=-2
  298. while [ $tries -gt 0 -a $ok -eq 0 ]; do
  299. local ok=1
  300. local tries="$(expr "$tries" - 1)"
  301. local loglevel="$(get_conf "$awl" loglevel "$opt_loglevel")"
  302. local expected_exit_code="$(get_conf "$awl" exit_code 0)"
  303. [ $expected_exit_code -eq 0 ] || local loglevel=0
  304. local cycle_limit="$(get_conf "$awl" cycle_limit 60)"
  305. local max_runtime="$(get_conf "$awl" max_runtime -1)"
  306. local dump_opt=
  307. [ $loglevel -ge 3 ] && local dump_opt="--no-cpu-dump"
  308. command time -o "$test_time_file" -f '%E' --quiet \
  309. "$actual_interpreter" "$rootdir/awlsim-test" \
  310. --loglevel $loglevel \
  311. --extended-insns \
  312. --hardware debug:inputAddressBase=7:outputAddressBase=8:dummyParam=True \
  313. --cycle-limit "$cycle_limit" \
  314. --max-runtime "$max_runtime" \
  315. --optimizers "$optimizers" \
  316. $dump_opt \
  317. "$@" \
  318. "$awl"
  319. local exit_code=$?
  320. if [ $exit_code -ne $expected_exit_code ]; then
  321. local ok=0
  322. if [ $tries -gt 0 ]; then
  323. infomsg "Test '$(basename "$awl")' FAILED, but retrying ($tries)..."
  324. sleep 1
  325. fi
  326. fi
  327. done
  328. if [ $ok -eq 0 ]; then
  329. test_failed "\nTest '$(basename "$awl")' FAILED" \
  330. "\nInterpreter = $interpreter" \
  331. "\nOptimizers = $optimizers" \
  332. "\nActual exit code = $exit_code" \
  333. "\nExpected exit code = $expected_exit_code"
  334. fi
  335. if is_parallel_run; then
  336. [ $ok -ne 0 ] && infomsg "$(basename "$awl"): O=$optimizers t=$(cat "$test_time_file") -> OK"
  337. else
  338. [ $ok -ne 0 ] && infomsg -n "O=$optimizers t=$(cat "$test_time_file") -> OK"
  339. fi
  340. rm "$test_time_file"
  341. done
  342. is_parallel_run || infomsg
  343. cleanup_test_environment
  344. }
  345. # $1=interpreter $2=sh_file
  346. run_sh_test()
  347. {
  348. local interpreter="$1"
  349. local sh_file="$2"
  350. shift; shift
  351. [ -x "$sh_file" ] && die "SH-file '$sh_file' must NOT be executable"
  352. [ "$(echo "$sh_file" | cut -c1)" = '/' ] || local sh_file="$(pwd)/$sh_file"
  353. # Source the test file
  354. . "$basedir/sh-test.defaults"
  355. . "$sh_file"
  356. # Run the test
  357. (
  358. setup_test_environment "$interpreter" "$sh_file"
  359. local interpreter="$RET"
  360. local test_dir="$(dirname "$sh_file")"
  361. local test_name="$(basename "$sh_file" .sh)"
  362. sh_test "$interpreter" "$test_dir" "$test_name"
  363. cleanup_test_environment
  364. )
  365. local result=$?
  366. [ $result -eq 0 ] || die "Test failed with error code $result"
  367. if is_parallel_run; then
  368. infomsg "$(basename "$sh_file"): OK"
  369. else
  370. infomsg "OK"
  371. fi
  372. }
  373. # $1=interpreter $2=test_file
  374. run_nose_test()
  375. {
  376. local interpreter="$1"
  377. local test_case="$2"
  378. shift; shift
  379. [ -z "$test_case" ] &&\
  380. die "Nose test case is missing"
  381. [ -d "$test_case" ] &&\
  382. die "Nose test case '$test_case' must not be a directory"
  383. [ -x "$test_case" ] &&\
  384. die "Nose test case '$test_case' must NOT be executable"
  385. # Select nosetests or nosetests3
  386. local interp_ver="$(get_interpreter_version "$interpreter")"
  387. local interp_major="$(echo "$interp_ver" | cut -d' ' -f 1)"
  388. if [ "$interp_major" = "2" ]; then
  389. local nose="$nosetests"
  390. else
  391. local nose="$nosetests3"
  392. fi
  393. # Resolve relative path
  394. [ "$(echo "$test_case" | cut -c1)" = '/' ] ||\
  395. local test_case="$(pwd)/$test_case"
  396. # Add nose libraries to pypy environment
  397. if [ "$(basename "$interpreter")" = "pypy" ]; then
  398. local p='import sys; print(":".join(p for p in sys.path if p.startswith("/usr/")))'
  399. EXTRA_PYTHONPATH="$("$interpreter" -c "$p"):$(python2 -c "$p"):$EXTRA_PYTHONPATH"
  400. fi
  401. # Add awlsim_tstlib.py to PYTHONPATH
  402. EXTRA_PYTHONPATH="$rootdir/tests:$EXTRA_PYTHONPATH"
  403. # Setup python environment
  404. setup_test_environment "$interpreter" "$test_case"
  405. local interpreter="$RET"
  406. # Run the nose test case
  407. cd "$rootdir" || die "Failed to cd to rootdir."
  408. local opts="--no-byte-compile --verbosity=2"
  409. if [ -n "$AWLSIM_TEST_QUIET" ]; then
  410. "$interpreter" "$nose" $opts "$test_case" >/dev/null 2>&1 ||\
  411. die "Nose test case '$(basename "$test_case")' failed."
  412. else
  413. "$interpreter" "$nose" $opts "$test_case" ||\
  414. die "Nose test case '$(basename "$test_case")' failed."
  415. fi
  416. infomsg "$(basename "$test_case"): OK"
  417. cleanup_test_environment
  418. }
  419. # $1=interpreter $2=testfile(.awl/.sh) ($3ff additional options to awlsim-test or testfile)
  420. __run_test()
  421. {
  422. local interpreter="$1"
  423. local testfile="$2"
  424. shift; shift
  425. # Don't run ourself
  426. [ "$(basename "$testfile")" = "run.sh" ] && return
  427. # Don't run the test helper library
  428. [ "$(basename "$testfile")" = "awlsim_tstlib.py" ] && return
  429. local disabled="$(get_conf "$testfile" disabled)"
  430. if [ -z "$disabled" ]; then
  431. # Print test headline
  432. local nl="-n"
  433. is_parallel_run && local nl=
  434. infomsg $nl "$(basename "$testfile") @ $(basename "$interpreter"): "
  435. local prev_dir="$(pwd)"
  436. cd "$rootdir" || die "cd to $rootdir failed"
  437. # Check the file type and run the tester
  438. if [ "$(echo -n "$testfile" | tail -c4)" = ".awl" ]; then
  439. check_dos_text_encoding "$testfile"
  440. run_awl_test "$interpreter" "$testfile" "$@"
  441. elif [ "$(echo -n "$testfile" | tail -c7)" = ".awlpro" ]; then
  442. run_awl_test "$interpreter" "$testfile" "$@"
  443. elif [ "$(echo -n "$testfile" | tail -c3)" = ".sh" ]; then
  444. run_sh_test "$interpreter" "$testfile" "$@"
  445. elif [ "$(echo -n "$testfile" | tail -c3)" = ".py" ]; then
  446. run_nose_test "$interpreter" "$testfile" "$@"
  447. else
  448. die "Test file type of '$testfile' not recognized"
  449. fi
  450. cd "$prev_dir" || die "cd to $prev_dir failed"
  451. else
  452. warnmsg "Skipping '$testfile' as it is disabled."
  453. fi
  454. }
  455. run_test()
  456. {
  457. if is_parallel_run; then
  458. # Run tests in parallel.
  459. wait_for_free_job_slot
  460. __run_test "$@" &
  461. else
  462. # Run tests one-by-one.
  463. __run_test "$@"
  464. fi
  465. }
  466. # $1=interpreter, $2=directory
  467. run_test_directory()
  468. {
  469. local interpreter="$1"
  470. local directory="$2"
  471. local prettydir="$(realpath -m --no-symlinks --relative-base="$rootdir" "$directory")/"
  472. infomsg ">>> entering $prettydir"
  473. # run .awlpro tests
  474. for entry in "$directory"/*; do
  475. [ -d "$entry" ] && continue
  476. [ "$(echo -n "$entry" | tail -c7)" = ".awlpro" ] || continue
  477. [ -e "$(dirname "$entry")/$(basename "$entry" .awlpro).sh" ] && continue
  478. run_test "$interpreter" "$entry"
  479. check_job_failure && return
  480. done
  481. # run .awl tests
  482. for entry in "$directory"/*; do
  483. [ -d "$entry" ] && continue
  484. [ "$(echo -n "$entry" | tail -c4)" = ".awl" ] || continue
  485. [ -e "$(dirname "$entry")/$(basename "$entry" .awl).awlpro" ] && continue
  486. [ -e "$(dirname "$entry")/$(basename "$entry" .awl).sh" ] && continue
  487. run_test "$interpreter" "$entry"
  488. check_job_failure && return
  489. done
  490. # run .sh tests
  491. for entry in "$directory"/*; do
  492. [ -d "$entry" ] && continue
  493. [ "$(echo -n "$entry" | tail -c3)" = ".sh" ] || continue
  494. run_test "$interpreter" "$entry"
  495. check_job_failure && return
  496. done
  497. # run nose tests
  498. for entry in "$directory"/*; do
  499. [ -d "$entry" ] && continue
  500. [ "$(echo -n "$entry" | tail -c3)" = ".py" ] || continue
  501. run_test "$interpreter" "$entry"
  502. check_job_failure && return
  503. done
  504. # Recurse into subdirectories
  505. for entry in "$directory"/*; do
  506. [ -d "$entry" ] || continue
  507. run_test_directory "$interpreter" "$entry"
  508. done
  509. infomsg "<<< leaving $prettydir"
  510. }
  511. # $1=interpreter
  512. warn_skipped()
  513. {
  514. local interpreter="$1"
  515. warnmsg "=== WARNING: '$interpreter' interpreter not found. Test skipped."
  516. warnmsg
  517. }
  518. __build_cython()
  519. {
  520. local cython="$1"
  521. local python="$2"
  522. have_prog "$cython" && have_prog "$python" || {
  523. warnmsg "=== WARNING: Cannot build $cython modules"
  524. return 1
  525. }
  526. cd "$rootdir" || die "cd to $rootdir failed"
  527. infomsg "=== Building awlsim $cython modules with $python"
  528. CFLAGS="-O0" CPPFLAGS= CXXFLAGS="-O0" LDFLAGS= \
  529. AWLSIM_CYTHON_BUILD=1 \
  530. AWLSIM_CYTHON_PARALLEL=1 \
  531. nice -n 5 \
  532. "$python" ./setup.py build >/dev/null ||\
  533. die "'$python ./setup.py build' failed"
  534. return 0
  535. }
  536. build_cython2()
  537. {
  538. __build_cython cython python2
  539. }
  540. build_cython3()
  541. {
  542. __build_cython cython3 python3
  543. }
  544. # $@=testfiles
  545. do_tests()
  546. {
  547. have_prog time ||\
  548. die "ERROR: 'time' executable not available."\
  549. "Please install the 'time' package."
  550. cleanup_test_environment
  551. if [ $opt_quick -eq 0 ]; then
  552. local all_interp="python3 python2 pypy3 pypy cython3"
  553. if [ $opt_extended -ne 0 ]; then
  554. local all_interp="$all_interp jython"
  555. fi
  556. else
  557. local all_interp="python3 python2"
  558. if [ $opt_extended -ne 0 ]; then
  559. die "The options --quick and --extended are mutually exclusive."
  560. fi
  561. fi
  562. find_executable nosetests
  563. local nosetests="$RET"
  564. find_executable nosetests3
  565. local nosetests3="$RET"
  566. local cython2_build_pid=
  567. local cython3_build_pid=
  568. if is_parallel_run; then
  569. # Trigger the build jobs, if required.
  570. local inter="$opt_interpreter $all_interp"
  571. if printf '%s' "$inter" | grep -Eqwe 'cython|cython2'; then
  572. wait_for_free_job_slot
  573. build_cython2 &
  574. local cython2_build_pid=$!
  575. fi
  576. if printf '%s' "$inter" | grep -qwe 'cython3'; then
  577. wait_for_free_job_slot
  578. build_cython3 &
  579. local cython3_build_pid=$!
  580. fi
  581. fi
  582. for interpreter in "$opt_interpreter" $all_interp; do
  583. [ -z "$interpreter" ] && continue
  584. cleanup_test_environment
  585. if [ "$interpreter" = "cython" -o "$interpreter" = "cython2" ]; then
  586. have_prog cython && have_prog python2 || {
  587. warn_skipped "$interpreter"
  588. [ -n "$opt_interpreter" ] && break || continue
  589. }
  590. if is_parallel_run; then
  591. wait_for_job_pid $cython2_build_pid
  592. else
  593. build_cython2 || die "Cython2 build failed."
  594. fi
  595. elif [ "$interpreter" = "cython3" ]; then
  596. have_prog cython3 && have_prog python3 || {
  597. warn_skipped "$interpreter"
  598. [ -n "$opt_interpreter" ] && break || continue
  599. }
  600. if is_parallel_run; then
  601. wait_for_job_pid $cython3_build_pid
  602. else
  603. build_cython3 || die "Cython3 build failed."
  604. fi
  605. else
  606. have_prog "$interpreter" || {
  607. warn_skipped "$interpreter"
  608. [ -n "$opt_interpreter" ] && break || continue
  609. }
  610. fi
  611. local interp_ver="$(get_interpreter_version "$interpreter")"
  612. local interp_ver_dot="$(echo "$interp_ver" | tr ' ' '.')"
  613. local interp_major="$(echo "$interp_ver" | cut -d' ' -f 1)"
  614. local interp_minor="$(echo "$interp_ver" | cut -d' ' -f 2)"
  615. [ -z "$interp_ver" ] &&\
  616. die "Failed to get '$interpreter' version."
  617. [ "$interp_major" -eq 2 -a "$interp_minor" -lt 7 ] &&\
  618. die "'$interpreter' interpreter version '$interp_ver_dot' too old."
  619. infomsg "=== Running tests with '$interpreter'"
  620. if [ $# -eq 0 ]; then
  621. run_test_directory "$interpreter" "$basedir"
  622. else
  623. for opt in "$@"; do
  624. local opt="$(realpath -m --no-symlinks "$opt")"
  625. if [ -d "$opt" ]; then
  626. run_test_directory "$interpreter" "$opt"
  627. else
  628. run_test "$interpreter" "$opt"
  629. fi
  630. check_job_failure && break
  631. done
  632. fi
  633. infomsg
  634. check_job_failure && break
  635. [ -n "$opt_interpreter" ] && break
  636. done
  637. if is_parallel_run; then
  638. # This is a parallel run. Wait for all jobs.
  639. infomsg "Waiting for background jobs..."
  640. wait
  641. # Print the fail information.
  642. if check_job_failure; then
  643. errormsg
  644. errormsg "===== FAILURES in parallel run: ====="
  645. cat "$test_fail_file" >&2
  646. errormsg "====================================="
  647. global_retval=1
  648. fi
  649. fi
  650. # Print summary
  651. if [ $global_retval -eq 0 ]; then
  652. infomsg
  653. infomsg -n "All tests succeeded"
  654. else
  655. errormsg
  656. errormsg -n "Some tests FAILED"
  657. fi
  658. if [ -n "$opt_interpreter" ]; then
  659. infomsg " (with interpreter '$opt_interpreter')"
  660. else
  661. if [ $opt_quick -eq 0 ]; then
  662. if [ $opt_extended -eq 0 ]; then
  663. infomsg " (full run)"
  664. else
  665. infomsg " (extended run)"
  666. fi
  667. else
  668. infomsg " (quick run)"
  669. fi
  670. fi
  671. }
  672. show_help()
  673. {
  674. infomsg "awlsim unit test script"
  675. infomsg
  676. infomsg "Usage: run.sh [OPTIONS] [testdirectory/testscript.awl/.awlpro/.sh/.py]"
  677. infomsg
  678. infomsg "Options:"
  679. infomsg " -i|--interpreter INTER Use INTER as interpreter for the tests"
  680. infomsg " -s|--softfail Do not abort on single test failures"
  681. infomsg " -j|--jobs NR Set the number of jobs to run in parallel."
  682. infomsg " 0 means number-of-CPUs"
  683. infomsg " Default: 1"
  684. infomsg " -q|--quick Only run python2 and python3 tests"
  685. infomsg " -qq Shortcut for: -q -j 0"
  686. infomsg " -g|--no-gui Avoid tests that need GUI libraries"
  687. infomsg " -x|--extended Run tests on additional interpreters"
  688. infomsg " -n|--renice NICENESS Renice by NICENESS. Defaults to 10."
  689. infomsg " -Q|--quiet Less messages"
  690. infomsg " -L|--loglevel Default log level."
  691. infomsg " -l|--loop COUNT Number of test loops to execute."
  692. infomsg " Default: 1"
  693. infomsg " Set to 0 for infinite looping."
  694. }
  695. tmp_dir="/tmp/awlsim-test-$$"
  696. rm -rf "$tmp_dir" >/dev/null 2>&1
  697. mkdir -p "$tmp_dir" || die "Failed to create temp dir '$tmp_dir'"
  698. trap cleanup_and_exit INT TERM
  699. trap cleanup EXIT
  700. test_fail_file="$(maketemp fail)"
  701. port_alloc_file="$(maketemp port)"
  702. jobs_tmp_file="$(maketemp jobs)"
  703. touch "${port_alloc_file}.lock"
  704. echo 4096 > "$port_alloc_file" || die "Failed to initialize port file"
  705. opt_interpreter=
  706. opt_softfail=0
  707. opt_quick=0
  708. opt_nogui=0
  709. opt_extended=0
  710. opt_renice=
  711. opt_jobs=1
  712. opt_loglevel=2
  713. opt_loop=1
  714. while [ $# -ge 1 ]; do
  715. [ "$(printf '%s' "$1" | cut -c1)" != "-" ] && break
  716. case "$1" in
  717. -h|--help)
  718. show_help
  719. exit 0
  720. ;;
  721. -i|--interpreter)
  722. shift
  723. opt_interpreter="$1"
  724. have_prog "$opt_interpreter" ||\
  725. die "Interpreter '${opt_interpreter}' not found"
  726. ;;
  727. -s|--softfail)
  728. opt_softfail=1
  729. ;;
  730. -j|--jobs)
  731. shift
  732. opt_jobs="$1"
  733. ;;
  734. -q|--quick)
  735. opt_quick=1
  736. ;;
  737. -qq)
  738. opt_quick=1
  739. opt_jobs=0
  740. ;;
  741. -g|--no-gui)
  742. opt_nogui=1
  743. ;;
  744. -x|--extended)
  745. opt_extended=1
  746. ;;
  747. -n|--renice)
  748. shift
  749. opt_renice="$1"
  750. ;;
  751. -Q|--quiet)
  752. export AWLSIM_TEST_QUIET=1
  753. ;;
  754. -L|--loglevel)
  755. shift
  756. opt_loglevel="$1"
  757. ;;
  758. -l|--loop)
  759. shift
  760. opt_loop="$1"
  761. ;;
  762. *)
  763. errormsg "Unknown option: $1"
  764. exit 1
  765. ;;
  766. esac
  767. shift
  768. done
  769. [ -z "$opt_jobs" -o -n "$(printf '%s' "$opt_jobs" | tr -d '[0-9]')" ] &&\
  770. die "--jobs: '$opt_jobs' is not a positive integer number."
  771. if [ $opt_jobs -eq 0 ]; then
  772. opt_jobs="$(getconf _NPROCESSORS_ONLN)"
  773. opt_jobs="$(expr $opt_jobs + 2)"
  774. fi
  775. [ -z "$opt_jobs" ] &&\
  776. die "Could not detect number of CPUs."
  777. if [ -z "$opt_loop" -o -n "$(printf '%s' "$opt_loop" | tr -d '[0-9]')" ] || [ $opt_loop -le 0 ]; then
  778. opt_loop=infinite
  779. fi
  780. do_renice()
  781. {
  782. renice "$1" "$$" >/dev/null
  783. }
  784. if [ -n "$opt_renice" ]; then
  785. do_renice "$opt_renice" || die "Failed to renice"
  786. else
  787. # Try to renice. Ignore failure.
  788. do_renice 10
  789. fi
  790. # Run the tests
  791. global_retval=0
  792. loop_iteration=0
  793. while [ "$opt_loop" = "infinite" ] || [ $opt_loop -gt 0 ]; do
  794. infomsg "Running test loop iteration $(expr "$loop_iteration" + 1)"
  795. do_tests "$@"
  796. if [ $global_retval -ne 0 ]; then
  797. break
  798. fi
  799. if [ "$opt_loop" != "infinite" ]; then
  800. opt_loop="$(expr "$opt_loop" - 1)"
  801. fi
  802. loop_iteration="$(expr "$loop_iteration" + 1)"
  803. done
  804. exit $global_retval