JAT - Just A Testing library https://pagure.io/shellfu-bash-jat

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395
  1. #!/bin/bash
  2. shellfu import pretty
  3. shellfu import isa
  4. shellfu import termcolors
  5. #
  6. # Just A[nother] Test assert and logging library
  7. #
  8. #
  9. # COMMON ARGUMENTS
  10. # ================
  11. #
  12. # Assert functions share following parameters:
  13. #
  14. # * `-b BEID` Behavior Evidence ID; is a Simple Name (see STRINGS)
  15. # associated with a particular behavior. If provided, BEID is merely
  16. # included within the log, see BEHAVIOR EVIDENCE ID section for more
  17. # details.
  18. #
  19. # * `-c CASEID` Case ID; together with $JAT__TEST_ID can be used to
  20. # uniquely identify a particular test case, with granularity up to
  21. # single assert. CASEID set here is joined with any CASEIDs set
  22. # by active phase.
  23. #
  24. # * `-h HINT` Human-readable description used to describe meaning of
  25. # the assertion to reader. Note that HINT should be worded in such
  26. # way that it makes claim about the reality that matches expectation.
  27. # That is, to reader of the log, a passing assert tells true, but
  28. # a failing assert "lies".
  29. #
  30. # Phase start functions share following parameters:
  31. #
  32. # * `-C DIR` Change to directory DIR for duration of the phase.
  33. #
  34. # * `-c CASEID` Inherit CASEID as Case ID to all asserts in this
  35. # phase. Individual asserts can extend the final case id for
  36. # extra granularity and profit.
  37. #
  38. #
  39. # STRINGS
  40. # =======
  41. #
  42. # Many strings, when passed to jat functions, are internally used
  43. # as paths, prefixes or identifiers. For this reason, they may be
  44. # restricted to contain only "safe" chars. Following classes are
  45. # defined:
  46. #
  47. # * *simple word* - must begin with letter or underscore; second
  48. # and further characters may be letter, digit or underscore.
  49. #
  50. #
  51. # Path to result directory
  52. #
  53. JAT__DIR=${JAT__DIR:-/var/tmp/jat}
  54. #
  55. # Path to result log
  56. #
  57. # If unset, another identical log is *still* created under a unique
  58. # subdirectory of $JAT__DIR. Set this if you want to have log at
  59. # a predictable path. This can be useful eg. if you want to send
  60. # log into a named pipe.
  61. #
  62. JAT__YLOG=${JAT__YLOG:-}
  63. #
  64. # Behavior Evidence ID namespace
  65. #
  66. # All BEIDs will be prefixed by this namespace qualifier,
  67. # and meaning of them must be defined within that namespace.
  68. #
  69. JAT__BEID_NS=${JAT__BEID_NS:-_jat_anon_beid_ns_}
  70. #
  71. # Test name
  72. #
  73. JAT__TEST_ID=${JAT__TEST_ID:-_jat_anon_test_}
  74. #
  75. # Test version
  76. #
  77. JAT__TEST_VERSION=${JAT__TEST_VERSION:-_jat_no_test_version_}
  78. #
  79. # Path to main logfile
  80. #
  81. __JAT__SDIR=
  82. #
  83. # Colored words for `jat__show_*` functions
  84. #
  85. __JAT__SWORD_ASSERT=${TERMCOLORS_YELLOW}assert${TERMCOLORS_NONE}
  86. __JAT__SWORD_END=${TERMCOLORS_LBLACK}END${TERMCOLORS_NONE}
  87. __JAT__SWORD_FAIL=${TERMCOLORS_RED}FAIL${TERMCOLORS_NONE}
  88. __JAT__SWORD_FINALIZE=${TERMCOLORS_LBLACK}FINALIZE${TERMCOLORS_NONE}
  89. __JAT__SWORD_PASS=${TERMCOLORS_GREEN}PASS${TERMCOLORS_NONE}
  90. __JAT__SWORD_PHASE=${TERMCOLORS_YELLOW}phase${TERMCOLORS_NONE}
  91. __JAT__SWORD_RELOAD=${TERMCOLORS_LBLUE}RELOAD${TERMCOLORS_NONE}
  92. __JAT__SWORD_SESSION=${TERMCOLORS_YELLOW}session${TERMCOLORS_NONE}
  93. __JAT__SWORD_START=${TERMCOLORS_LBLACK}START${TERMCOLORS_NONE}
  94. __JAT__SWORD_TERROR=${TERMCOLORS_RED}ERROR${TERMCOLORS_NONE}
  95. __JAT__SWORD_TEST=${TERMCOLORS_YELLOW}test${TERMCOLORS_NONE}
  96. __JAT__SWORD_TINFO=${TERMCOLORS_LBLACK}INFO${TERMCOLORS_NONE}
  97. __JAT__SWORD_TWARNING=${TERMCOLORS_RED}WARNING${TERMCOLORS_NONE}
  98. #
  99. # Make result log deterministic?
  100. #
  101. # Determines whether result should be made deterministic. Currently
  102. # this means that instead of wall clock time, timestamps will be simply
  103. # ordinal stamps like `time-1`, `time-2`...
  104. #
  105. # This makes test log deterministic, enabling comparison of multiple
  106. # runs against each other or against fixed oracle. The disadvantage
  107. # is that you can't assess performance data from the log.
  108. #
  109. __JAT__DETERMINISTIC=false
  110. #
  111. # Log format version
  112. #
  113. __JAT__LOG_FMT='jat/0.0'
  114. #
  115. # Self version (in variable to enable overriding in unit tests)
  116. #
  117. __JAT__SELF_VERSION=__MKIT_PROJ_VERSION__
  118. jat__cmd() {
  119. #
  120. # Assert that command $@ exits with zero (or other)
  121. #
  122. # Usage:
  123. #
  124. # jat__cmd [-o R_OUT] [-e R_ERR] [-s R_ESF] [-S ES_EXPR] [--] CMD [ARG]..
  125. #
  126. # Run CMD with all ARGs and if exit status is zero, announce assertion
  127. # success (ASSERT.PASS), otherwise announce assertion failure (ASSERT.FAIL).
  128. #
  129. # Exit status expectation can be changed from zero to ES_EXPR, which
  130. # must be in form of comma-separated list of integer values or ranges
  131. # thereof. E, g. expression '0,10-20' would be a valid expression,
  132. # matching 0, 11, 20, but not 2 or 21.
  133. #
  134. # If R_ESF is passed, it will be treated as path to file where exit status
  135. # of CODE is written. This is recommended over consulting $?, since the
  136. # latter is not reliable; see below.
  137. #
  138. # If path R_OUT or R_ERR are provided, standard output and standard
  139. # error are saved to respective files.
  140. #
  141. # See COMMON ARGUMENTS section for common assert options.
  142. #
  143. # Exit status of the this function normally will, but IS NOT GUARRANTEED TO
  144. # correspond to the exit status of the CODE. For example, if the function
  145. # call is incomplete, the exit status will be 2.
  146. #
  147. local __jat__cmd=()
  148. local __jat__r_es
  149. local __jat__o_es=0
  150. local __jat__hint
  151. local __jat__beids=()
  152. local __jat__caseid
  153. local __jat__etype=MESSAGE.ERROR
  154. local __jat__r_out
  155. local __jat__r_err
  156. local __jat__r_esf
  157. #
  158. # NOTE: names need to be qualified because they might interfere
  159. # with the actual execution of the assert command.
  160. #
  161. while true; do case $1 in
  162. -b) __jat__beids+=("$2"); shift 2 || { __jat__usage "missing BEID"; return 2; } ;;
  163. -c) __jat__caseid="$2"; shift 2 || { __jat__usage "missing CASEID"; return 2; } ;;
  164. -h) __jat__hint=$2; shift 2 || { __jat__usage "missing HINT"; return 2; } ;;
  165. -e) __jat__r_err=$2; shift 2 || { __jat__usage "missing R_ERR"; return 2; } ;;
  166. -o) __jat__r_out=$2; shift 2 || { __jat__usage "missing R_OUT"; return 2; } ;;
  167. -s) __jat__r_esf=$2; shift 2 || { __jat__usage "missing R_ESF"; return 2; } ;;
  168. -S) __jat__o_es=$2; shift 2 || { __jat__usage "missing ES_EXPR"; return 2; } ;;
  169. --) shift; break ;;
  170. *) break ;;
  171. esac done
  172. __jat__cmd=("$@")
  173. test -n "${__jat__cmd[*]}" || { __jat__usage "no CMD?"; return 2; }
  174. test -n "$__jat__hint" || __jat__hint="${__jat__cmd[*]}"
  175. debug -v __jat__cmd __jat__o_es __jat__hint __jat__beids
  176. jat__log_info "CMD: ${__jat__cmd[*]}"
  177. case ${#__jat__r_out}:${#__jat__r_err} in
  178. 0:0) "${__jat__cmd[@]}" ;;
  179. 0:*) "${__jat__cmd[@]}" 2>"$__jat__r_err" ;;
  180. *:0) "${__jat__cmd[@]}" >"$__jat__r_out" ;;
  181. *:*) "${__jat__cmd[@]}" >"$__jat__r_out" 2>"$__jat__r_err" ;;
  182. esac; __jat__r_es=$?
  183. debug -v __jat__r_es __jat__r_esf
  184. if test -n "$__jat__r_esf"; then
  185. echo $__jat__r_es > "$__jat__r_esf" \
  186. || jat__log_error "error writing R_ESF file: $__jat__r_esf"
  187. fi
  188. if __jat__es_match "$__jat__o_es" "$__jat__r_es"; then
  189. __jat__etype=ASSERT.PASS
  190. else
  191. __jat__etype=ASSERT.FAIL
  192. fi
  193. __jat__assert $__jat__etype "$__jat__hint" "$__jat__caseid" "${__jat__beids[@]}" \
  194. "t.cmd=${__jat__cmd[*]}" "o.es_expr=$__jat__o_es" "r.es=$__jat__r_es"
  195. return "$__jat__r_es"
  196. }
  197. jat__cmp() {
  198. #
  199. # Assert comparison of $1 and $3 using operator $2
  200. #
  201. # Usage:
  202. #
  203. # jat__cmp [assert-options] RVAL OP OVAL
  204. #
  205. # Compare RVAL (result value) to OVAL (oracle value) and announce
  206. # assertion success (ASSERT.PASS) or assertion failure (ASSERT.FAIL).
  207. #
  208. # OP can be any of `eq`, `ne`, `lt`, `gt`, `le`, `ge` for numeric values,
  209. # `==` and `re` for string values. In case of `re`, OVAL is expected to
  210. # be basic regular expression and is matched against whole string (ie.
  211. # anchors `^` and `$` are included automatically.
  212. #
  213. # Note that in most cases using jat__cmd() with `test` or `grep` commands
  214. # is all you need.
  215. #
  216. # See COMMON ARGUMENTS section for common assert options.
  217. #
  218. local RVal # "result" value
  219. local Op # operator
  220. local OVal # "oracle" value
  221. local hint # log hint
  222. local caseid # case id
  223. local beids=() # Behavior Evidence IDs
  224. local cmpes # comparison exit status
  225. while true; do case $1 in
  226. -b) beids+=("$2"); shift 2 || { __jat__usage "missing BEID"; return 2; } ;;
  227. -c) caseid="$2"; shift 2 || { __jat__usage "missing CASEID"; return 2; } ;;
  228. -h) hint=$2; shift 2 || { __jat__usage "missing HINT"; return 2; } ;;
  229. *) break ;;
  230. esac done
  231. RVal=$1; Op=$2; OVal=$3
  232. test -n "$RVal" || { __jat__usage "no RVAL?"; return 2; }
  233. test -n "$Op" || { __jat__usage "no OP?"; return 2; }
  234. test -n "$OVal" || { __jat__usage "no OVAL?"; return 2; }
  235. test -n "$hint" || hint="$RVal $Op $OVal"
  236. debug -v RVal Op OVal hint beids
  237. __jat__cmp_match; cmpes=$?
  238. case $cmpes in
  239. 0)
  240. __jat__assert ASSERT.PASS "$hint" "$caseid" "${beids[@]}" \
  241. "r.val=$RVal" "t.op=$Op" "o.val=$OVal"
  242. ;;
  243. 1)
  244. __jat__assert ASSERT.FAIL "$hint" "$caseid" "${beids[@]}" \
  245. "r.val=$RVal" "t.op=$Op" "o.val=$OVal"
  246. ;;
  247. *)
  248. __jat__usage "bad syntax: $RVal $Op $OVal"
  249. return 2
  250. ;;
  251. esac
  252. }
  253. jat__die() {
  254. #
  255. # Log error $1 and exit this script abruptly
  256. #
  257. local msg
  258. local msgs=()
  259. for msg in "$@"; do
  260. msgs+=("fatal: $msg")
  261. done
  262. jat__log_error "${msgs[@]}"
  263. jat__sfinish
  264. die "${msgs[@]}"
  265. }
  266. jat__eval() {
  267. #
  268. # Assert that code $1 exits with zero (or other)
  269. #
  270. # Usage:
  271. #
  272. # jat__eval [assert-options] [-s R_ESF] [-S ES_EXPR] [--] CODE
  273. #
  274. # Run CODE using eval builtin and if exit status is zero, announce assertion
  275. # success (ASSERT.PASS), otherwise announce assertion failure (ASSERT.FAIL).
  276. #
  277. # Exit status expectation can be changed from zero to ES_EXPR, which
  278. # must be in form of comma-separated list of integer values or ranges
  279. # thereof. E, g. expression '0,10-20' would be a valid expression,
  280. # matching 0, 11, 20, but not 2 or 21.
  281. #
  282. # If R_ESF is passed, it will be treated as path to file where exit status
  283. # of CODE is written. This is recommended over consulting $?, since the
  284. # latter is not reliable; see below.
  285. #
  286. # This is similar to jat__cmd(), except that code is checked against syntax
  287. # errors and user is given full control over redirections.
  288. #
  289. # See COMMON ARGUMENTS section for common assert options.
  290. #
  291. # Exit status of the this function normally will, but IS NOT GUARRANTEED TO
  292. # correspond to the exit status of the CODE. For example, if the function
  293. # call is incomplete, the exit status will be 2. If there is syntax error
  294. # in CODE, the exit status will be 3.
  295. #
  296. local __jat__caseid
  297. local __jat__code=""
  298. local __jat__r_es
  299. local __jat__r_esf
  300. local __jat__o_es=0
  301. local __jat__hint
  302. local __jat__beids=()
  303. local __jat__etype=MESSAGE.ERROR
  304. #
  305. # NOTE: names need to be qualified because they might interfere
  306. # with the actual execution of the assert command.
  307. #
  308. while true; do case $1 in
  309. -b) __jat__beids+=("$2"); shift 2 || { __jat__usage "missing BEID"; return 2; } ;;
  310. -c) __jat__caseid="$2"; shift 2 || { __jat__usage "missing CASEID"; return 2; } ;;
  311. -h) __jat__hint=$2; shift 2 || { __jat__usage "missing HINT"; return 2; } ;;
  312. -s) __jat__r_esf=$2; shift 2 || { __jat__usage "missing R_ESF"; return 2; } ;;
  313. -S) __jat__o_es=$2; shift 2 || { __jat__usage "missing ES_EXPR"; return 2; } ;;
  314. --) shift; break ;;
  315. *) break ;;
  316. esac done
  317. __jat__code=$1
  318. test -n "$__jat__code" || { __jat__usage "no CODE?"; return 2; }
  319. test -n "$2" && { __jat__usage "extra args!: $*"; return 2; }
  320. test -n "$__jat__hint" || __jat__hint="$__jat__code"
  321. debug -v __jat__code __jat__o_es __jat__hint __jat__beids
  322. bash -n <<<"$__jat__code" || {
  323. jat__log_error "syntax error in test code: $__jat__code"
  324. return 3
  325. }
  326. jat__log_info "CODE: $__jat__code"
  327. eval "$__jat__code"; __jat__r_es=$?
  328. if test -n "$__jat__r_esf"; then
  329. echo $__jat__r_es > "$__jat__r_esf" \
  330. || jat__log_error "error writing R_ESF file: $__jat__r_esf"
  331. fi
  332. debug -v __jat__r_es
  333. if __jat__es_match "$__jat__o_es" "$__jat__r_es"; then
  334. __jat__etype=ASSERT.PASS
  335. else
  336. __jat__etype=ASSERT.FAIL
  337. fi
  338. __jat__assert $__jat__etype "$__jat__hint" "$__jat__caseid" "${__jat__beids[@]}" \
  339. "t.code=$__jat__code" "o.es_expr=$__jat__o_es" "r.es=$__jat__r_es"
  340. return "$__jat__r_es"
  341. }
  342. jat__fail() {
  343. #
  344. # Assert test failure
  345. #
  346. # Usage:
  347. #
  348. # jat__fail [assert-options]
  349. #
  350. # Simply log event that an assert has failed. Use other assert functions
  351. # if you can, since they probably provide more useful information to log
  352. # reader.
  353. #
  354. # See COMMON ARGUMENTS section for common assert options.
  355. #
  356. local hint # log hint
  357. local caseid # case ID
  358. while true; do case $1 in
  359. -c) caseid="$2"; shift 2 || { __jat__usage "missing CASEID"; return 2; } ;;
  360. -h) hint=$2; shift 2 || { __jat__usage "missing HINT"; return 2; } ;;
  361. *) break ;;
  362. esac done
  363. test -n "$1" && { __jat__usage "extra arguments: $*"; return 2; }
  364. debug -v hint beids
  365. __jat__assert ASSERT.FAIL "$hint" "$caseid" "${beids[@]}"
  366. }
  367. jat__filebackup() {
  368. #
  369. # Back up file $1
  370. #
  371. # Usage:
  372. #
  373. # jat__filebackup [-n NS] [-c] [--] PATH
  374. #
  375. # Back up PATH aside to be able to restore it later using
  376. # jat__filerestore().
  377. #
  378. # Provide `-c` switch to ensure that path is removed before restoring.
  379. # If NS is given, it must be "simple word" (see STRINGS section).
  380. #
  381. local path
  382. local abspath
  383. local ns=_jat_anon_backupns_
  384. local store
  385. local dest
  386. local clean=false
  387. local pdig
  388. local nshint
  389. while true; do case $1 in
  390. --) shift; break ;;
  391. -c|--clean)
  392. clean=true
  393. shift ;;
  394. -n|--namespace)
  395. ns=$2; nshint="(NS=$ns) "
  396. shift 2 || { __jat__usage "no NS?"; return 2; }
  397. ;;
  398. -*) __jat__usage "unknown argument: $1"; return 2 ;;
  399. *) break ;;
  400. esac done
  401. path=$1
  402. test -n "$path" || { __jat__usage "no PATH?"; return 2; }
  403. isa__name "$ns" || { __jat__usage "NS must be simple word"; return 2; }
  404. abspath=$(readlink -e "$path") || {
  405. jat__log_error "no such file: $1"
  406. return 3
  407. }
  408. pdig=$(md5sum <<<"$abspath" | cut -d' ' -f1)
  409. __jat__sd_keya "backup/$ns/rainbow" "$pdig $abspath"
  410. $clean && __jat__sd_keya "backup/$ns/clean" "$abspath"
  411. store=$(__jat__sd_path "backup/$ns/store")
  412. mkdir -p "$store"
  413. dest="$store/$pdig"
  414. test -e "$store/$pdig" && {
  415. jat__log_error "already backed up, giving up: $nshint$path"
  416. return 2
  417. }
  418. cp -ar "$abspath" "$store/$pdig" || {
  419. jat__log_error "failed to create backup: $abspath"
  420. return 3
  421. }
  422. jat__log_info "backed up: $nshint$path"
  423. debug -c tree "$(__jat__sd_path backup)"
  424. }
  425. jat__filerestore() {
  426. #
  427. # Restore paths stored by jat__filebackup()
  428. #
  429. # Usage:
  430. #
  431. # jat__filerestore [-n NS]
  432. #
  433. # Restore all paths backed up using jat__filebackup(). Paths
  434. # are restored in opposite order than backed up.
  435. #
  436. local abspath
  437. local ns=_jat_anon_backupns_
  438. local store
  439. local pdig
  440. local nshint
  441. local rainbow
  442. local cleanlog
  443. while true; do case $1 in
  444. -n|--namespace)
  445. ns=$2; nshint="(NS=$ns) "
  446. shift 2 || { __jat__usage "no NS?"; return 2; }
  447. ;;
  448. "") break ;;
  449. *) __jat__usage "unknown argument: $1"; return 2 ;;
  450. esac done
  451. isa__name "$ns" || { __jat__usage "NS must be simple word"; return 2; }
  452. test -d "$__JAT__SDIR/internal/backup/$ns" || {
  453. jat__log_error "unknown backup namespace: $ns"
  454. return 3
  455. }
  456. rainbow=$(__jat__sd_path "backup/$ns/rainbow")
  457. cleanlog=$(__jat__sd_path "backup/$ns/clean")
  458. store=$(__jat__sd_path "backup/$ns/store")
  459. while read -r pdig abspath; do
  460. debug -v pdig abspath
  461. grep -qxF "$abspath" "$cleanlog" 2>/dev/null && {
  462. rm -rf "$abspath" || {
  463. jat__log_error "failed to pre-clean original path: $abspath"
  464. return 3
  465. }
  466. }
  467. debug -c ls -l "$store"
  468. cp -Tar "$store/$pdig" "$abspath" || {
  469. jat__log_error "failed to restore backup: $nshint$abspath"
  470. return 3
  471. }
  472. jat__log_info "restored: $nshint$abspath"
  473. :
  474. done <"$rainbow"
  475. }
  476. jat__sfinish() {
  477. #
  478. # Finalize session
  479. #
  480. # Log event to announce that log is finalized and remove test
  481. # session.
  482. #
  483. # This is necessary because in order to support persistent multi
  484. # PID tests (ie, re-booting within test), jat__sinit() will reload
  485. # leftover session and whole log will be merged.
  486. #
  487. local fileas # session id for filing in 'finished' folder
  488. local es # final exit status
  489. test -d "$JAT__DIR/session" || {
  490. __jat__show_error "no active session: no $JAT__DIR/session"
  491. return 2
  492. }
  493. es=$(__jat__final_es)
  494. __jat__show_sfinish
  495. __jat__log_event SESSION.END "finishing session"
  496. __jat__writelog <<<"finalized: true"
  497. __jat__writelog <<<"end: $(__jat__newstamp)"
  498. fileas=$(__jat__sd_keyr fileas)
  499. mkdir -p "$JAT__DIR/finished"
  500. mv "$JAT__DIR/session" "$JAT__DIR/finished/$fileas"
  501. rm -f "$JAT__DIR/last"
  502. ln -s "finished/$fileas" "$JAT__DIR/last"
  503. return "$es"
  504. }
  505. jat__log_error() {
  506. #
  507. # Log internal error unrelated to SUT
  508. #
  509. local line
  510. local msg
  511. local head=true
  512. for line in "$@"; do
  513. __jat__show_terror "$line"
  514. $head || msg+=$'\n'; head=false
  515. msg+="$line"
  516. done
  517. echo "MESSAGE.ERROR" >> "$(__jat__sd_path "llog")"
  518. echo "MESSAGE.ERROR" >> "$(__jat__sd_path "P.llog")"
  519. __jat__log_event MESSAGE.ERROR "$msg"
  520. }
  521. jat__log_info() {
  522. #
  523. # Log internal info unrelated to SUT
  524. #
  525. local line
  526. local msg
  527. local head=true
  528. for line in "$@"; do
  529. __jat__show_tinfo "$line"
  530. $head || msg+=$'\n'; head=false
  531. msg+="$line"
  532. done
  533. echo "MESSAGE.INFO" >> "$(__jat__sd_path "llog")"
  534. echo "MESSAGE.INFO" >> "$(__jat__sd_path "P.llog")"
  535. __jat__log_event MESSAGE.INFO "$msg"
  536. }
  537. jat__log_warning() {
  538. #
  539. # Log internal error unrelated to SUT
  540. #
  541. local line
  542. local msg
  543. local head=true
  544. for line in "$@"; do
  545. __jat__show_twarning "$line"
  546. $head || msg+=$'\n'; head=false
  547. msg+="$line"
  548. done
  549. echo "MESSAGE.WARNING" >> "$(__jat__sd_path "llog")"
  550. echo "MESSAGE.WARNING" >> "$(__jat__sd_path "P.llog")"
  551. __jat__log_event MESSAGE.WARNING "$msg"
  552. }
  553. jat__pass() {
  554. #
  555. # Assert test pass
  556. #
  557. # Usage:
  558. #
  559. # jat__pass [assert-options]
  560. #
  561. # Simply log event that an assert has passed. Use other assert functions
  562. # if you can, since they probably provide more useful information to log
  563. # reader.
  564. #
  565. # See COMMON ARGUMENTS section for common assert options.
  566. #
  567. local hint # log hint
  568. local beids=() # Behavior Evidence IDs
  569. local caseid # case ID
  570. while true; do case $1 in
  571. -b) beids+=("$2"); shift 2 || { __jat__usage "missing BEID"; return 2; } ;;
  572. -c) caseid="$2"; shift 2 || { __jat__usage "missing CASEID"; return 2; } ;;
  573. -h) hint=$2; shift 2 || { __jat__usage "missing HINT"; return 2; } ;;
  574. *) break ;;
  575. esac done
  576. test -n "$1" && { __jat__usage "extra arguments: $*"; return 2; }
  577. debug -v hint beids
  578. __jat__assert ASSERT.PASS "$hint" "$caseid" "${beids[@]}"
  579. }
  580. jat__promise_asserts() {
  581. #
  582. # Promise number of asserts will be $1
  583. #
  584. local num=$1
  585. test -n "$num" || {
  586. __jat__usage "no NUM?"
  587. return 2
  588. }
  589. __jat__log_event TEST.PROMISE "assert number: $num" "" \
  590. "num=$num"
  591. }
  592. jat__sinit() {
  593. #
  594. # Initialize session
  595. #
  596. # Load active session if found; otherwise initialize new one.
  597. #
  598. local reload=false
  599. local old_test_id
  600. local old_test_version
  601. local sessid
  602. mkdir -p "$JAT__DIR" \
  603. || die "could not initialize JAT__DIR: $JAT__DIR"
  604. __JAT__SDIR=$JAT__DIR/session
  605. test -d "$__JAT__SDIR" && reload=true
  606. mkdir -p "$__JAT__SDIR"
  607. __jat__sd_keyw fileas "session-$(date +%s-%N)"
  608. export __JAT__SDIR
  609. export JAT__YLOG
  610. debug -v reload __JAT__SDIR
  611. if $reload; then
  612. sessid=$(__jat__sd_keyr sessid)
  613. old_test_id=$(__jat__sd_keyr test_id)
  614. old_test_version=$(__jat__sd_keyr test_version)
  615. test "$old_test_id" == "$JAT__TEST_ID" || {
  616. warn -v JAT__TEST_ID old_test_id
  617. die "cannot reload session of different test"
  618. }
  619. test "$old_test_version" == "$JAT__TEST_VERSION" || {
  620. warn -v JAT__TEST_VERSION old_test_version
  621. die "cannot reload session of different test version"
  622. }
  623. __jat__log_event SESSION.RELOAD "reloaded session" "" \
  624. "JAT__LOG_FMT=$__JAT__LOG_FMT" \
  625. "JAT__VERSION=$__JAT__SELF_VERSION"
  626. __jat__show_sinitr
  627. else
  628. sessid=$(__jat__newsid)
  629. debug -v __JAT__SDIR JAT__YLOG
  630. {
  631. echo "---"
  632. echo "format: $__JAT__LOG_FMT"
  633. echo "jat_version: $__JAT__SELF_VERSION"
  634. echo "test:"
  635. echo " id: $JAT__TEST_ID"
  636. echo " version: $JAT__TEST_VERSION"
  637. echo "start: $(__jat__newstamp)"
  638. echo "id: $sessid"
  639. echo "events:"
  640. } | __jat__writelog
  641. __jat__sd_keyw sessid "$sessid"
  642. __jat__sd_keyw test_id "$JAT__TEST_ID"
  643. __jat__sd_keyw test_version "$JAT__TEST_VERSION"
  644. __jat__pdummy
  645. __jat__log_event SESSION.START "started new session"
  646. __jat__show_sinitn
  647. fi
  648. }
  649. jat__stat() {
  650. #
  651. # Print session statistic $1
  652. #
  653. # Usage:
  654. # jat__stat sasrtc # session assert count
  655. # jat__stat spassc # session pass count
  656. # jat__stat sfailc # session fail count
  657. # jat__stat pasrtc # phase assert count
  658. # jat__stat ppassc # phase pass count
  659. # jat__stat pfailc # phase fail count
  660. # jat__stat swarc # session warning count
  661. # jat__stat pwarc # phase warning count
  662. # jat__stat serrc # session error count
  663. # jat__stat perrc # phase error count
  664. #
  665. # Print statistics about session state.
  666. #
  667. local which=$1
  668. case $which in
  669. swarc) grep -cxF MESSAGE.WARNING "$(__jat__sd_path "llog")" ;;
  670. pwarc) grep -cxF MESSAGE.WARNING "$(__jat__sd_path "P.llog")" ;;
  671. serrc) grep -cxF MESSAGE.ERROR "$(__jat__sd_path "llog")" ;;
  672. perrc) grep -cxF MESSAGE.ERROR "$(__jat__sd_path "P.llog")" ;;
  673. sfailc) grep -cxF ASSERT.FAIL "$(__jat__sd_path "vlog")" ;;
  674. pfailc) grep -cxF ASSERT.FAIL "$(__jat__sd_path "P.vlog")" ;;
  675. ppassc) grep -cxF ASSERT.PASS "$(__jat__sd_path "P.vlog")" ;;
  676. spassc) grep -cxF ASSERT.PASS "$(__jat__sd_path "vlog")" ;;
  677. pasrtc) grep -cx 'ASSERT.PASS\|ASSERT.FAIL' "$(__jat__sd_path "P.vlog")" ;;
  678. sasrtc) grep -cx 'ASSERT.PASS\|ASSERT.FAIL' "$(__jat__sd_path "vlog")" ;;
  679. *) __jat__usage "invalid statistic field: $which"
  680. return 2 ;;
  681. esac
  682. }
  683. jat__pend() {
  684. #
  685. # End active phase
  686. #
  687. local oldpdir
  688. local oldpwd
  689. oldpdir=$(__jat__sd_keyR P.pdir)
  690. oldpwd=$(__jat__sd_keyR P.PWD)
  691. test -n "$oldpdir" && {
  692. jat__log_info "changing back from phase dir: $oldpdir"
  693. popd >/dev/null \
  694. || jat__die "failed to go back from phase dir: $oldpdir to $oldpwd"
  695. }
  696. __jat__show_pend
  697. __jat__pdummy
  698. __jat__log_event PHASE.END
  699. }
  700. jat__pstartd() {
  701. #
  702. # Start diagnostic phase named $1
  703. #
  704. # See COMMON ARGUMENTS section of this manual.
  705. #
  706. __jat__pstart -t diag "$@"
  707. }
  708. jat__pstartc() {
  709. #
  710. # Start cleanup phase named $1
  711. #
  712. # See COMMON ARGUMENTS section of this manual.
  713. #
  714. __jat__pstart -t cleanup "$@"
  715. }
  716. jat__pstarts() {
  717. #
  718. # Start setup phase named $1
  719. #
  720. # See COMMON ARGUMENTS section of this manual.
  721. #
  722. __jat__pstart -t setup "$@"
  723. }
  724. jat__pstartt() {
  725. #
  726. # Start test phase named $1
  727. #
  728. # See COMMON ARGUMENTS section of this manual.
  729. #
  730. __jat__pstart -t test "$@"
  731. }
  732. jat__submit() {
  733. #
  734. # Submit file $1 as part of test result
  735. #
  736. # Usage:
  737. #
  738. # jat__submit PATH [ALTNAME]
  739. #
  740. # Last element of PATH is used as result name, unless alternative
  741. # name ALTNAME is given, in which case it's used instead.
  742. #
  743. local path=$1
  744. local altname=$2
  745. local dest=$__JAT__SDIR/results
  746. local nhint
  747. test -n "$path" || {
  748. __jat__usage "no SRC?"
  749. return 2
  750. }
  751. mkdir -p "$dest" || {
  752. jat__log_error "cannot write to result storage: $dest"
  753. return 3
  754. }
  755. test -n "$altname" && {
  756. dest="$dest/$altname"
  757. nhint=" as $altname"
  758. mkdir -p "$(dirname "$dest")" # needed if altname has slashes
  759. }
  760. jat__log_info "submitting result: $path$nhint"
  761. cp -ar "$path" "$dest"
  762. }
  763. __jat__assert() {
  764. #
  765. # Make assert event
  766. #
  767. # Use this function to create own asserts
  768. #
  769. case $1 in
  770. ASSERT.PASS) __jat__show_pass "$2" ;;
  771. ASSERT.FAIL) __jat__show_fail "$2" ;;
  772. esac
  773. __jat__log_event "$@"
  774. }
  775. __jat__bumpid() {
  776. #
  777. # Create and/or bump ID named $1
  778. #
  779. # Print next ID from series named $1. Series starts
  780. # with 1, so on firts call, value of ID is 1, then 2.
  781. # etc.
  782. #
  783. # Isession is not initialized, ID value will be 0 and
  784. # return status will be 3.
  785. #
  786. local name=$1 # name of id
  787. local cache # ordinal cache
  788. local ord # ordinal
  789. if test -n "$__JAT__SDIR"; then
  790. cache=$(__jat__sd_path "series/$name")
  791. else
  792. echo 0; return 3
  793. fi
  794. if test -f "$cache"; then
  795. ord=$(<"$cache")
  796. ((ord++))
  797. else
  798. ord=1
  799. fi
  800. echo $ord >"$cache"
  801. echo "$name-$ord"
  802. }
  803. __jat__cmp_match() {
  804. #
  805. # True if $RVal matches $OVal by $Op
  806. #
  807. case $Op in
  808. eq|ne|lt|gt|le|ge) test "$RVal" -"$Op" "$OVal" ;;
  809. ==) test "$RVal" == "$OVal" ;;
  810. re) grep -qx "$OVal" <<< "$RVal" ;;
  811. *) return 2 ;;
  812. esac
  813. }
  814. __jat__es_match() {
  815. #
  816. # True if exit status $1 matches expression $EsExpr
  817. #
  818. local expr=$1
  819. local es=$2
  820. local part
  821. for part in ${expr//,/ }; do
  822. test -n "$part" || continue
  823. #FIXME: a rather funny implementation (works, though...)
  824. eval "echo {${part/-/..}}" | grep -qwF "$es" && return 0
  825. done
  826. return 1
  827. }
  828. __jat__final_es() {
  829. #
  830. # Print final exit status
  831. #
  832. # Roughly follows TFKit's semantic; unoffical link:
  833. #
  834. # https://github.com/AloisMahdal/shellfu/tree/master/utils/tfkit/doc
  835. #
  836. test "$(jat__stat serrc)" -gt 0 && echo 4 && return
  837. test "$(jat__stat swarc)" -gt 0 && echo 3 && return
  838. test "$(jat__stat sfailc)" -gt 0 && echo 1 && return
  839. echo 0
  840. }
  841. __jat__log_event() {
  842. #
  843. # Log event to YAML log via __jat__writelog()
  844. #
  845. # Usage:
  846. #
  847. # __jat__log_event SESSION.START|SESSION.RELOAD|SESSION.END
  848. # __jat__log_event PHASE.END
  849. # __jat__log_event PHASE.START ID TYPE NAME [K=V]
  850. # __jat__log_event ASSERT.PASS|ASSERT.FAIL [-h HINT] [-c CASEID] [-b BEID..] [-d K=V..]
  851. # __jat__log_event MESSAGE.INFO|MESSAGE.WARNING|MESSAGE.ERROR MSG [K=V]
  852. #
  853. local EType=$1; shift
  854. local msg
  855. local meatfn
  856. local beids=()
  857. local data=()
  858. local origin=${FUNCNAME[1]}
  859. local pcaseid
  860. local CaseIdBase=$JAT__TEST_ID
  861. pcaseid=$(__jat__sd_keyR P.pcaseid)
  862. test -n "$pcaseid" && CaseIdBase+=":$pcaseid"
  863. case $EType in
  864. ASSERT.PASS|ASSERT.FAIL) : ;;
  865. MESSAGE.INFO|MESSAGE.ERROR|MESSAGE.WARNING) : ;;
  866. PHASE.START|PHASE.END) : ;;
  867. SESSION.START|SESSION.RELOAD|SESSION.END) : ;;
  868. *) __jat__usage "bad ETYPE: $EType"
  869. return 2 ;;
  870. esac
  871. case $EType:$origin in
  872. PHASE.END:jat__pend) meatfn="true" ;;
  873. PHASE.START:__jat__pstart) meatfn="__jat__log_evt_pstart" ;;
  874. SESSION.END:jat__sfinish) meatfn="true" ;;
  875. SESSION.RELOAD:jat__sinit) meatfn="true" ;;
  876. SESSION.START:jat__sinit) meatfn="true" ;;
  877. ASSERT.FAIL:__jat__assert) meatfn="__jat__log_evt_assert" ;;
  878. ASSERT.PASS:__jat__assert) meatfn="__jat__log_evt_assert" ;;
  879. MESSAGE.ERROR:__jat__usage) meatfn="__jat__log_evt_message" ;;
  880. MESSAGE.ERROR:jat__log_error) meatfn="__jat__log_evt_message" ;;
  881. MESSAGE.INFO:jat__log_info) meatfn="__jat__log_evt_message" ;;
  882. MESSAGE.WARNING:jat__log_warning) meatfn="__jat__log_evt_message" ;;
  883. *) __jat__show_error "illegal call of __jat__log_event: $EType from $origin"
  884. EType=MESSAGE.ERROR ;;
  885. esac
  886. echo "$EType" >> "$(__jat__sd_path "vlog")"
  887. echo "$EType" >> "$(__jat__sd_path "P.vlog")"
  888. {
  889. echo " -"
  890. echo " etype: $EType"
  891. echo " stamp: $(__jat__newstamp)"
  892. $meatfn "$@"
  893. } | __jat__writelog
  894. }
  895. __jat__log_evt_pstart() {
  896. #
  897. # __jat__log_event PHASE.START ID NAME TYPE [pcaseid=CASEID] [pdir=PDIR]
  898. #
  899. local pid=$1; shift
  900. local pname=$1; shift
  901. local ptype=$1; shift
  902. local data=("$@")
  903. echo " phase:"
  904. echo " id: $pid"
  905. echo " type: $ptype"
  906. echo " name: |-4"
  907. echo " $pname"
  908. __jat__yamld 2 data "${data[@]}"
  909. }
  910. __jat__log_evt_message() {
  911. # __jat__log_event MESSAGE.INFO|MESSAGE.WARNING|MESSAGE.ERROR MSG
  912. local msg=$1; shift
  913. local data=("$@")
  914. __jat__yamls 2 message "$msg"
  915. __jat__yamld 2 data "${data[@]}"
  916. }
  917. __jat__log_evt_assert() {
  918. # __jat__log_event ASSERT.PASS|ASSERT.FAIL HINT CASEID [K=V..]
  919. local hint=$1; shift
  920. local caseid=$1; shift
  921. local data=("$@")
  922. case $caseid in
  923. "") caseid=$CaseIdBase ;;
  924. *) caseid=$CaseIdBase:$caseid ;;
  925. esac
  926. __jat__yamls 2 hint "$hint"
  927. __jat__yamls 2 caseid "$caseid"
  928. __jat__yamld 2 data "${data[@]}"
  929. }
  930. __jat__newstamp() {
  931. #
  932. # Create new timestamp
  933. #
  934. case $__JAT__DETERMINISTIC in
  935. true) __jat__bumpid time ;;
  936. false) date +%s ;;
  937. *) die "bad value of __JAT__DETERMINISTIC: $__JAT__DETERMINISTIC" ;;
  938. esac
  939. }
  940. __jat__newsid() {
  941. #
  942. # Create new timestamp
  943. #
  944. case $__JAT__DETERMINISTIC in
  945. true) echo noid ;;
  946. false) date +%s-%N ;;
  947. *) die "bad value of __JAT__DETERMINISTIC: $__JAT__DETERMINISTIC" ;;
  948. esac
  949. }
  950. __jat__pdummy() {
  951. #
  952. # Create dummy phase
  953. #
  954. __jat__sd_keyw phase "dummy"
  955. __jat__sd_keyw P.name "_jat_dummy_phase_"
  956. __jat__sd_keyw P.type "none"
  957. }
  958. __jat__pstart() {
  959. #
  960. # Start phase of type $1 and name $2
  961. #
  962. local type # phase name
  963. local name # ^^ name
  964. local pdir # ^^ directory
  965. local pcaseid # ^^ case id, if assigned
  966. local oldphase # current phase id
  967. local newphase # new phase id
  968. while true; do case $1 in
  969. -t) type="$2"; shift 2 || return 2 ;;
  970. -c) pcaseid="$2"; shift 2 || return 2 ;;
  971. -C) pdir="$2"; shift 2 || return 2 ;;
  972. *) break ;;
  973. esac done
  974. name="${1:-_jat_anon_phase_}"
  975. oldphase=$(__jat__sd_keyr phase)
  976. test "$oldphase" == dummy || {
  977. jat__log_error "old phase not ended; ending automatically: $oldphase"
  978. jat__pend
  979. }
  980. debug -v type name pdir
  981. newphase=$(__jat__bumpid phasid)
  982. __jat__sd_keyw phase "$newphase"
  983. __jat__sd_keyw P.type "$type"
  984. __jat__sd_keyw P.name "$name"
  985. __jat__sd_keyw P.pcaseid "$pcaseid"
  986. __jat__show_pstart "$name"
  987. __jat__log_event PHASE.START "$newphase" "$name" "$type" "pcaseid=$pcaseid" "pdir=$pdir"
  988. test -n "$pdir" && {
  989. __jat__sd_keyw P.pdir "$pdir"
  990. __jat__sd_keyw P.PWD "$PWD"
  991. jat__log_info "changing directory for phase: $pdir"
  992. pushd "$pdir" >/dev/null || {
  993. jat__log_error "failed to change to phase directory: $pdir"
  994. return 3
  995. }
  996. }
  997. }
  998. __jat__sd_keya() {
  999. #
  1000. # Append line $2 to session key $1
  1001. #
  1002. local key=$1
  1003. local value=$2
  1004. local path
  1005. path=$(__jat__sd_path "$key")
  1006. echo "$value" >> "$path" || {
  1007. __jat__show_error "error appending session data: key '$key' to '$path'"
  1008. return 3
  1009. }
  1010. }
  1011. __jat__sd_keyR() {
  1012. #
  1013. # Read session key $1 if it exists, return 1 otherwise
  1014. #
  1015. local key=$1
  1016. local path
  1017. path=$(__jat__sd_path "$key") || return 1
  1018. cat "$path" || {
  1019. __jat__show_error "error reading session data: key '$key' from '$path'"
  1020. return 3
  1021. }
  1022. }
  1023. __jat__sd_keyr() {
  1024. #
  1025. # Read session key $1
  1026. #
  1027. local key=$1
  1028. local path
  1029. path=$(__jat__sd_path "$key")
  1030. cat "$path" || {
  1031. __jat__show_error "error reading session data: key '$key' from '$path'"
  1032. return 3
  1033. }
  1034. }
  1035. __jat__sd_keyw() {
  1036. #
  1037. # Write $2 to session key $1
  1038. #
  1039. local key=$1
  1040. local value=$2
  1041. local path
  1042. path=$(__jat__sd_path "$key")
  1043. echo "$value" > "$path" || {
  1044. __jat__show_error "error writing session data: key '$key' to '$path'"
  1045. return 3
  1046. }
  1047. }
  1048. __jat__sd_path() {
  1049. #
  1050. # Dereference path to session key $1; true if exists
  1051. #
  1052. # Key starting with 'P.' will be phase-specific.
  1053. #
  1054. local key=$1
  1055. local path="$__JAT__SDIR/internal/"
  1056. case $key in
  1057. "") __jat__show_error "no KEY?"; return 2 ;;
  1058. P.*) path+="phase-$(__jat__sd_keyr phase)/$key" ;;
  1059. *) path+="$key" ;;
  1060. esac
  1061. mkdir -p "${path%/*}" 2>/dev/null || {
  1062. __jat__show_error "could not create key: $path"
  1063. return 2
  1064. }
  1065. echo "$path"
  1066. test -e "$path"
  1067. }
  1068. __jat__show() {
  1069. #
  1070. # Show to user and also keep in ansi log
  1071. #
  1072. local msg
  1073. for msg in "$@"; do
  1074. echo -e "$msg" >> "$__JAT__SDIR/log.ansi"
  1075. echo -e "$msg" >&2
  1076. done
  1077. }
  1078. __jat__show_fail() {
  1079. #
  1080. # Show assert fail message $1 to stderr
  1081. #
  1082. __jat__show \
  1083. " $__JAT__SWORD_ASSERT.$__JAT__SWORD_FAIL: $1"
  1084. }
  1085. __jat__show_pass() {
  1086. #
  1087. # Show assert fail message $1 to stderr
  1088. #
  1089. __jat__show \
  1090. " $__JAT__SWORD_ASSERT.$__JAT__SWORD_PASS: $1"
  1091. }
  1092. __jat__show_pend() {
  1093. #
  1094. # Show phase end
  1095. #
  1096. local pverd # phase verdict text
  1097. local pname # ^^ name
  1098. local pnhint # ^^ ^^ display
  1099. local errors # count of errors
  1100. local warnings # ^^ warnings
  1101. local asserts # ^^ asserts
  1102. local fails # ^^ fails
  1103. pname=$(__jat__sd_keyr P.name)
  1104. errors=$(jat__stat perrc)
  1105. warnings=$(jat__stat pwarc)
  1106. asserts=$(jat__stat pasrtc)
  1107. fails=$(jat__stat pfailc)
  1108. test "$pname" == "_jat_anon_phase_" || pnhint=" '$pname'"
  1109. case $errors:$warnings:$asserts:$fails in
  1110. 0:0:0:*) pverd="" ;;
  1111. 0:0:*:0) pverd=".$__JAT__SWORD_PASS" ;;
  1112. 0:0:*:*) pverd=".$__JAT__SWORD_FAIL" ;;
  1113. 0:*:*:*) pverd=".$__JAT__SWORD_TWARNING" ;;
  1114. *:*:*:*) pverd=".$__JAT__SWORD_TERROR " ;;
  1115. esac
  1116. __jat__show \
  1117. "$__JAT__SWORD_PHASE.$__JAT__SWORD_END$pverd$pnhint" \
  1118. ""
  1119. }
  1120. __jat__show_pstart() {
  1121. #
  1122. # Show phase start
  1123. #
  1124. local pname=$1 # phase name
  1125. local pnhint # ^^ ^^ display
  1126. test "$pname" == "_jat_anon_phase_" || pnhint=" '$pname'"
  1127. __jat__show \
  1128. "$__JAT__SWORD_PHASE.$__JAT__SWORD_START$pnhint"
  1129. }
  1130. __jat__show_sfinish() {
  1131. #
  1132. # Show message about session $1 finalization to stderr
  1133. #
  1134. local sverd # session verdict text
  1135. local errors # count of errors
  1136. local warnings # ^^ warnings
  1137. local asserts # ^^ asserts
  1138. local fails # ^^ fails
  1139. errors=$(jat__stat serrc)
  1140. warnings=$(jat__stat swarc)
  1141. asserts=$(jat__stat sasrtc)
  1142. fails=$(jat__stat sfailc)
  1143. case $errors:$warnings:$asserts:$fails in
  1144. 0:0:0:*) sverd="" ;;
  1145. 0:0:*:0) sverd=".$__JAT__SWORD_PASS" ;;
  1146. 0:0:*:*) sverd=".$__JAT__SWORD_FAIL" ;;
  1147. 0:*:*:*) sverd=".$__JAT__SWORD_TWARNING" ;;
  1148. *:*:*:*) sverd=".$__JAT__SWORD_TERROR" ;;
  1149. esac
  1150. __jat__show \
  1151. "$__JAT__SWORD_SESSION.$__JAT__SWORD_FINALIZE$sverd"
  1152. }
  1153. __jat__show_sinitn() {
  1154. #
  1155. # Show message about session initialization to stderr
  1156. #
  1157. __jat__show \
  1158. "$__JAT__SWORD_TEST.$__JAT__SWORD_TINFO: id: $JAT__TEST_ID" \
  1159. "$__JAT__SWORD_TEST.$__JAT__SWORD_TINFO: version: $JAT__TEST_VERSION" \
  1160. "" \
  1161. "$__JAT__SWORD_SESSION.$__JAT__SWORD_START" \
  1162. ""
  1163. }
  1164. __jat__show_sinitr() {
  1165. #
  1166. # Show message about session reload to stderr
  1167. #
  1168. __jat__show \
  1169. "$__JAT__SWORD_TEST.$__JAT__SWORD_TINFO: id: $JAT__TEST_ID" \
  1170. "$__JAT__SWORD_TEST.$__JAT__SWORD_TINFO: version: $JAT__TEST_VERSION" \
  1171. "" \
  1172. "$__JAT__SWORD_SESSION.$__JAT__SWORD_RELOAD" \
  1173. ""
  1174. }
  1175. __jat__show_tinfo() {
  1176. #
  1177. # Show test info
  1178. #
  1179. local sp=""
  1180. test "$(__jat__sd_keyr phase)" == dummy || sp=" "
  1181. __jat__show \
  1182. "$sp$__JAT__SWORD_TEST.$__JAT__SWORD_TINFO: $1"
  1183. }
  1184. __jat__show_terror() {
  1185. #
  1186. # Show test error
  1187. #
  1188. local sp=""
  1189. test "$(__jat__sd_keyr phase)" == dummy || sp=" "
  1190. __jat__show \
  1191. "$sp$__JAT__SWORD_TEST.$__JAT__SWORD_TERROR: $1"
  1192. }
  1193. __jat__show_twarning() {
  1194. #
  1195. # Show test warning
  1196. #
  1197. local sp=""
  1198. test "$(__jat__sd_keyr phase)" == dummy || sp=" "
  1199. __jat__show \
  1200. "$sp$__JAT__SWORD_TEST.$__JAT__SWORD_TWARNING: $1"
  1201. }
  1202. __jat__show_error() {
  1203. #
  1204. # Emit general jat warning (not related to SUT)
  1205. #
  1206. warn "jat.ERROR" "$1"
  1207. }
  1208. __jat__usage() {
  1209. #
  1210. # Print usage error $1 and hint according to FUNCNAME
  1211. #
  1212. local msg=$1
  1213. local parent=${FUNCNAME[1]}
  1214. local patt
  1215. local patts=()
  1216. __jat__show_error "bad usage: $msg"
  1217. case $parent in
  1218. jat__cmd) patts=("[assert-options] [-o R_OUT] [-e R_ERR] [-s R_ESF] [-S ES_EXPR] [--] CMD [ARG]..") ;;
  1219. jat__eval) patts=("[assert-options] [-s R_ESF] [-S ES_EXPR] [--] CODE") ;;
  1220. jat__cmp) patts=("[assert-options] RVAL OP OVAL") ;;
  1221. jat__fail) patts=("[assert-options]") ;;
  1222. jat__pass) patts=("[assert-options]") ;;
  1223. jat__submit) patts=("SRC [ALTNAME]") ;;
  1224. jat__stat) patts=(spassc sfailc ppassc pfailc) ;;
  1225. esac
  1226. for patt in "${patts[@]}"; do
  1227. __jat__show_error "usage: $parent $patt"
  1228. done
  1229. __jat__log_event MESSAGE.ERROR "bad usage: $parent()" "" \
  1230. "PATTERNS=${patts[*]}"
  1231. }
  1232. __jat__writelog() {
  1233. #
  1234. # Write logged output (copy if needed)
  1235. #
  1236. case $JAT__YLOG in
  1237. "") cat >> "$__JAT__SDIR/log.yaml" ;;
  1238. -) tee -a "$__JAT__SDIR/log.yaml" ;;
  1239. *) tee -a "$__JAT__SDIR/log.yaml" >> "$JAT__YLOG" ;;
  1240. esac
  1241. }
  1242. __jat__valid_beid() {
  1243. #
  1244. # True if $1 is a valid BEID
  1245. #
  1246. local tainted=$1
  1247. grep -qx '[[:alpha:]_][[:alnum:]_.]*' <<<"$tainted"
  1248. }
  1249. __jat__valid_pair() {
  1250. #
  1251. # True if $1 is a valid BEID
  1252. #
  1253. local tainted=$1
  1254. grep -qx '\([tor][.]\)\?[[:alpha:]_][[:alnum:]_]*=.*' <<<"$tainted"
  1255. }
  1256. __jat__yamls() {
  1257. #
  1258. # Print a scalar field named $1 with value $2
  1259. #
  1260. local nest=$1; shift
  1261. local name=$1
  1262. local value=$2
  1263. local sp
  1264. sp=$(__jat__yamlnest "$nest")
  1265. case $value in
  1266. "") echo "$sp$name: ~" ;;
  1267. *) echo "$sp$name: |-4"
  1268. sed "s/^/$sp /" <<<"$value" ;;
  1269. esac
  1270. }
  1271. __jat__yamla() {
  1272. #
  1273. # Print an array field named $1 with array of values $2..
  1274. #
  1275. local nest=$1; shift
  1276. local name=$1; shift
  1277. local value
  1278. local sp
  1279. sp=$(__jat__yamlnest "$nest")
  1280. test $# -eq 0 && echo " $name: []" && return 0
  1281. echo "$sp$name:"
  1282. for value in "$@"; do
  1283. case $value in
  1284. "") echo "$sp - ~" ;;
  1285. *) echo "$sp - |-4"
  1286. echo "$sp $value" ;;
  1287. esac
  1288. done
  1289. }
  1290. __jat__yamlnest() {
  1291. #
  1292. # Print $1 x 4 of spaces
  1293. #
  1294. local n=$1
  1295. while test "$n" -gt 0; do
  1296. echo -n ' '
  1297. (( n-- ))
  1298. done
  1299. }
  1300. __jat__yamld() {
  1301. #
  1302. # Print a dict field named $1 and composed of KEY=VALUE pairs $2..
  1303. #
  1304. local nest=$1; shift
  1305. local name=$1; shift
  1306. local pair
  1307. local key
  1308. local value
  1309. local sp
  1310. sp=$(__jat__yamlnest "$nest")
  1311. test $# -eq 0 && echo "$sp$name: {}" && return 0
  1312. echo "$sp$name:"
  1313. for pair in "$@"; do
  1314. key=${pair%%=*}; value=${pair#$key=}
  1315. case $value in
  1316. "") echo "$sp $key: ~" ;;
  1317. *) echo "$sp $key: |-4"
  1318. echo "$sp $value" ;;
  1319. esac
  1320. done
  1321. }
  1322. #shellfu module-version=__MKIT_PROJ_VERSION__