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

jat.sh.skel 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293
  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_END=${TERMCOLORS_LBLACK}END${TERMCOLORS_NONE}
  86. __JAT__SWORD_FAIL=${TERMCOLORS_RED}FAIL${TERMCOLORS_NONE}
  87. __JAT__SWORD_FINALIZE=${TERMCOLORS_LBLACK}FINALIZE${TERMCOLORS_NONE}
  88. __JAT__SWORD_PASS=${TERMCOLORS_GREEN}PASS${TERMCOLORS_NONE}
  89. __JAT__SWORD_PHASE=${TERMCOLORS_YELLOW}phase${TERMCOLORS_NONE}
  90. __JAT__SWORD_RELOAD=${TERMCOLORS_LBLUE}RELOAD${TERMCOLORS_NONE}
  91. __JAT__SWORD_SESSION=${TERMCOLORS_YELLOW}session${TERMCOLORS_NONE}
  92. __JAT__SWORD_START=${TERMCOLORS_LBLACK}START${TERMCOLORS_NONE}
  93. __JAT__SWORD_SUT=${TERMCOLORS_YELLOW}sut${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=true
  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 $@ returns 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 (PASS), otherwise announce assertion failure (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=TEST_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=PASS
  190. else
  191. __jat__etype=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 that value $@ returns zero (or other)
  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 (PASS) or assertion failure (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 PASS "$hint" "$caseid" "${beids[@]}" \
  241. -- "r.val=$RVal" "t.op=$Op" "o.val=$OVal"
  242. ;;
  243. 1)
  244. __jat__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__eval() {
  254. #
  255. # Assert that command in code $1 returns zero (or other)
  256. #
  257. # Usage:
  258. #
  259. # jat__eval [-s R_ESF] [-S ES_EXPR] [--] CODE
  260. #
  261. # Run CODE using eval builtin and if exit status is zero, announce assertion
  262. # success (PASS), otherwise announce assertion failure (FAIL).
  263. #
  264. # Exit status expectation can be changed from zero to ES_EXPR, which
  265. # must be in form of comma-separated list of integer values or ranges
  266. # thereof. E, g. expression '0,10-20' would be a valid expression,
  267. # matching 0, 11, 20, but not 2 or 21.
  268. #
  269. # If R_ESF is passed, it will be treated as path to file where exit status
  270. # of CODE is written. This is recommended over consulting $?, since the
  271. # latter is not reliable; see below.
  272. #
  273. # This is similar to jat__cmd(), except that code is checked against syntax
  274. # errors and user is given full control over redirections.
  275. #
  276. # See COMMON ARGUMENTS section for common assert options.
  277. #
  278. # Exit status of the this function normally will, but IS NOT GUARRANTEED TO
  279. # correspond to the exit status of the CODE. For example, if the function
  280. # call is incomplete, the exit status will be 2. If there is syntax error
  281. # in CODE, the exit status will be 3.
  282. #
  283. local __jat__caseid
  284. local __jat__code=""
  285. local __jat__r_es
  286. local __jat__r_esf
  287. local __jat__o_es=0
  288. local __jat__hint
  289. local __jat__beids=()
  290. local __jat__etype=TEST_ERROR
  291. #
  292. # NOTE: names need to be qualified because they might interfere
  293. # with the actual execution of the assert command.
  294. #
  295. while true; do case $1 in
  296. -b) __jat__beids+=("$2"); shift 2 || { __jat__usage "missing BEID"; return 2; } ;;
  297. -c) __jat__caseid="$2"; shift 2 || { __jat__usage "missing CASEID"; return 2; } ;;
  298. -h) __jat__hint=$2; shift 2 || { __jat__usage "missing HINT"; return 2; } ;;
  299. -s) __jat__r_esf=$2; shift 2 || { __jat__usage "missing R_ESF"; return 2; } ;;
  300. -S) __jat__o_es=$2; shift 2 || { __jat__usage "missing ES_EXPR"; return 2; } ;;
  301. --) shift; break ;;
  302. *) break ;;
  303. esac done
  304. __jat__code=$1
  305. test -n "$__jat__code" || { __jat__usage "no CODE?"; return 2; }
  306. test -n "$2" && { __jat__usage "extra args!: $*"; return 2; }
  307. test -n "$__jat__hint" || __jat__hint="$__jat__code"
  308. debug -v __jat__code __jat__o_es __jat__hint __jat__beids
  309. bash -n <<<"$__jat__code" || {
  310. jat__log_error "syntax error in test code: $__jat__code"
  311. return 3
  312. }
  313. jat__log_info "CODE: $__jat__code"
  314. eval "$__jat__code"; __jat__r_es=$?
  315. if test -n "$__jat__r_esf"; then
  316. echo $__jat__r_es > "$__jat__r_esf" \
  317. || jat__log_error "error writing R_ESF file: $__jat__r_esf"
  318. fi
  319. debug -v __jat__r_es
  320. if __jat__es_match "$__jat__o_es" "$__jat__r_es"; then
  321. __jat__etype=PASS
  322. else
  323. __jat__etype=FAIL
  324. fi
  325. __jat__assert $__jat__etype "$__jat__hint" "$__jat__caseid" "${__jat__beids[@]}" \
  326. -- "t.code=$__jat__code" "o.es_expr=$__jat__o_es" "r.es=$__jat__r_es"
  327. return "$__jat__r_es"
  328. }
  329. jat__fail() {
  330. #
  331. # Assert test failure
  332. #
  333. # Usage:
  334. #
  335. # jat__fail [assert-options]
  336. #
  337. # Simply log event that an assert has failed. Use other assert functions
  338. # if you can, since they probably provide more useful information to log
  339. # reader.
  340. #
  341. # See COMMON ARGUMENTS section for common assert options.
  342. #
  343. local hint # log hint
  344. local caseid # case ID
  345. local beids=() # Behavior Evidence IDs
  346. while true; do case $1 in
  347. -b) beids+=("$2"); shift 2 || { __jat__usage "missing BEID"; return 2; } ;;
  348. -c) caseid="$2"; shift 2 || { __jat__usage "missing CASEID"; return 2; } ;;
  349. -h) hint=$2; shift 2 || { __jat__usage "missing HINT"; return 2; } ;;
  350. *) break ;;
  351. esac done
  352. test -n "$1" && { __jat__usage "extra arguments: $*"; return 2; }
  353. debug -v hint beids
  354. __jat__assert FAIL "$hint" "$caseid" "${beids[@]}"
  355. }
  356. jat__filebackup() {
  357. #
  358. # Back up file $1
  359. #
  360. # Usage:
  361. #
  362. # jat__filebackup [-n NS] [-c] [--] PATH
  363. #
  364. # Back up PATH aside to be able to restore it later using
  365. # jat__filerestore().
  366. #
  367. # Provide `-c` switch to ensure that path is removed before restoring.
  368. # If NS is given, it must be "simple word" (see STRINGS section).
  369. #
  370. local path
  371. local abspath
  372. local ns=_jat_anon_backupns_
  373. local store
  374. local dest
  375. local clean=false
  376. local pdig
  377. local nshint
  378. while true; do case $1 in
  379. --) shift; break ;;
  380. -c|--clean)
  381. clean=true
  382. shift ;;
  383. -n|--namespace)
  384. ns=$2; nshint="(NS=$ns) "
  385. shift 2 || { __jat__usage "no NS?"; return 2; }
  386. ;;
  387. -*) __jat__usage "unknown argument: $1"; return 2 ;;
  388. *) break ;;
  389. esac done
  390. path=$1
  391. test -n "$path" || { __jat__usage "no PATH?"; return 2; }
  392. isa__name "$ns" || { __jat__usage "NS must be simple word"; return 2; }
  393. abspath=$(readlink -e "$path") || {
  394. jat__log_error "no such file: $1"
  395. return 3
  396. }
  397. pdig=$(md5sum <<<"$abspath" | cut -d' ' -f1)
  398. __jat__sd_keya "backup/$ns/rainbow" "$pdig $abspath"
  399. $clean && __jat__sd_keya "backup/$ns/clean" "$abspath"
  400. store=$(__jat__sd_path "backup/$ns/store")
  401. mkdir -p "$store"
  402. dest="$store/$pdig"
  403. test -e "$store/$pdig" && {
  404. jat__log_error "already backed up, giving up: $nshint$path"
  405. return 2
  406. }
  407. cp -ar "$abspath" "$store/$pdig" || {
  408. jat__log_error "failed to create backup: $abspath"
  409. return 3
  410. }
  411. jat__log_info "backed up: $nshint$path"
  412. debug -c tree "$(__jat__sd_path backup)"
  413. }
  414. jat__filerestore() {
  415. #
  416. # Restore paths stored by jat__filebackup()
  417. #
  418. # Usage:
  419. #
  420. # jat__filerestore [-n NS]
  421. #
  422. # Restore all paths backed up using jat__filebackup(). Paths
  423. # are restored in opposite order than backed up.
  424. #
  425. local abspath
  426. local ns=_jat_anon_backupns_
  427. local store
  428. local pdig
  429. local nshint
  430. local rainbow
  431. local cleanlog
  432. while true; do case $1 in
  433. -n|--namespace)
  434. ns=$2; nshint="(NS=$ns) "
  435. shift 2 || { __jat__usage "no NS?"; return 2; }
  436. ;;
  437. "") break ;;
  438. *) __jat__usage "unknown argument: $1"; return 2 ;;
  439. esac done
  440. isa__name "$ns" || { __jat__usage "NS must be simple word"; return 2; }
  441. test -d "$__JAT__SDIR/internal/backup/$ns" || {
  442. jat__log_error "unknown backup namespace: $ns"
  443. return 3
  444. }
  445. rainbow=$(__jat__sd_path "backup/$ns/rainbow")
  446. cleanlog=$(__jat__sd_path "backup/$ns/clean")
  447. store=$(__jat__sd_path "backup/$ns/store")
  448. while read -r pdig abspath; do
  449. debug -v pdig abspath
  450. grep -qxF "$abspath" "$cleanlog" 2>/dev/null && {
  451. rm -rf "$abspath" || {
  452. jat__log_error "failed to pre-clean original path: $abspath"
  453. return 3
  454. }
  455. }
  456. debug -c ls -l "$store"
  457. cp -Tar "$store/$pdig" "$abspath" || {
  458. jat__log_error "failed to restore backup: $nshint$abspath"
  459. return 3
  460. }
  461. jat__log_info "restored: $nshint$abspath"
  462. :
  463. done <"$rainbow"
  464. }
  465. jat__sfinish() {
  466. #
  467. # Finalize session
  468. #
  469. # Log event to announce that log is finalized and remove test
  470. # session.
  471. #
  472. # This is necessary because in order to support persistent multi
  473. # PID tests (ie, re-booting within test), jat__sinit() will reload
  474. # leftover session and whole log will be merged.
  475. #
  476. local fileas # session id for filing in 'finished' folder
  477. test -d "$JAT__DIR/session" || {
  478. __jat__show_error "no active session: no $JAT__DIR/session"
  479. return 2
  480. }
  481. __jat__show_sfinish
  482. __jat__log_event SINFO "finishing session"
  483. __jat__writelog <<<"finalized: true"
  484. __jat__writelog <<<"end: $(__jat__newstamp)"
  485. fileas=$(__jat__sd_keyr fileas)
  486. mkdir -p "$JAT__DIR/finished"
  487. mv "$JAT__DIR/session" "$JAT__DIR/finished/$fileas"
  488. rm -f "$JAT__DIR/last"
  489. ln -s "finished/$fileas" "$JAT__DIR/last"
  490. }
  491. jat__log_error() {
  492. #
  493. # Log internal error unrelated to SUT
  494. #
  495. local msg=$1
  496. echo "error" >> "$(__jat__sd_path "llog")"
  497. echo "error" >> "$(__jat__sd_path "P.llog")"
  498. __jat__log_event TEST_ERROR "$msg"
  499. __jat__show_terror "$msg"
  500. }
  501. jat__log_info() {
  502. #
  503. # Log internal info unrelated to SUT
  504. #
  505. local msg=$1
  506. echo "info" >> "$(__jat__sd_path "llog")"
  507. echo "info" >> "$(__jat__sd_path "P.llog")"
  508. __jat__log_event SINFO "$msg"
  509. __jat__show_tinfo "$msg"
  510. }
  511. jat__log_warning() {
  512. #
  513. # Log internal error unrelated to SUT
  514. #
  515. local msg=$1
  516. echo "warning" >> "$(__jat__sd_path "llog")"
  517. echo "warning" >> "$(__jat__sd_path "P.llog")"
  518. __jat__log_event TEST_WARNING "$msg"
  519. __jat__show_twarning "$msg"
  520. }
  521. jat__pass() {
  522. #
  523. # Assert test pass
  524. #
  525. # Usage:
  526. #
  527. # jat__pass [assert-options]
  528. #
  529. # Simply log event that an assert has passed. Use other assert functions
  530. # if you can, since they probably provide more useful information to log
  531. # reader.
  532. #
  533. # See COMMON ARGUMENTS section for common assert options.
  534. #
  535. local hint # log hint
  536. local beids=() # Behavior Evidence IDs
  537. local caseid # case ID
  538. while true; do case $1 in
  539. -b) beids+=("$2"); shift 2 || { __jat__usage "missing BEID"; return 2; } ;;
  540. -c) caseid="$2"; shift 2 || { __jat__usage "missing CASEID"; return 2; } ;;
  541. -h) hint=$2; shift 2 || { __jat__usage "missing HINT"; return 2; } ;;
  542. *) break ;;
  543. esac done
  544. test -n "$1" && { __jat__usage "extra arguments: $*"; return 2; }
  545. debug -v hint beids
  546. __jat__assert PASS "$hint" "$caseid" "${beids[@]}"
  547. }
  548. jat__promise_asserts() {
  549. #
  550. # Promise number of asserts will be $1
  551. #
  552. local num=$1
  553. test -n "$num" || {
  554. __jat__usage "no NUM?"
  555. return 2
  556. }
  557. __jat__log_event PROMISE "assert number: $num" "" \
  558. -- "num=$num"
  559. }
  560. jat__sinit() {
  561. #
  562. # Initialize session
  563. #
  564. # Load active session if found; otherwise initialize new one.
  565. #
  566. local reload=false
  567. mkdir -p "$JAT__DIR" \
  568. || die "could not initialize JAT__DIR: $JAT__DIR"
  569. __JAT__SDIR=$JAT__DIR/session
  570. test -d "$__JAT__SDIR" && reload=true
  571. mkdir -p "$__JAT__SDIR"
  572. __jat__sd_keyw fileas "session-$(date +%s-%N)"
  573. export __JAT__SDIR
  574. export JAT__YLOG
  575. debug -v reload __JAT__SDIR
  576. if $reload; then
  577. __jat__log_event SINFO "reloaded session" "" \
  578. -- \
  579. "JAT__LOG_FMT=$__JAT__LOG_FMT" \
  580. "JAT__VERSION=$__JAT__SELF_VERSION"
  581. __jat__show_sinitr
  582. else
  583. debug -v __JAT__SDIR JAT__YLOG
  584. {
  585. echo "---"
  586. echo "format: $__JAT__LOG_FMT"
  587. echo "jat_version: $__JAT__SELF_VERSION"
  588. echo "test:"
  589. echo " id: $JAT__TEST_ID"
  590. echo " version: $JAT__TEST_VERSION"
  591. echo "start: $(__jat__newstamp)"
  592. echo "events:"
  593. } | __jat__writelog
  594. __jat__pdummy
  595. __jat__log_event SINFO "started new session"
  596. __jat__show_sinitn
  597. fi
  598. }
  599. jat__stat() {
  600. #
  601. # Print session statistic $1
  602. #
  603. # Usage:
  604. # jat__stat sasrtc # session assert count
  605. # jat__stat spassc # session pass count
  606. # jat__stat sfailc # session fail count
  607. # jat__stat pasrtc # phase assert count
  608. # jat__stat ppassc # phase pass count
  609. # jat__stat pfailc # phase fail count
  610. # jat__stat swarc # session warning count
  611. # jat__stat pwarc # phase warning count
  612. # jat__stat serrc # session error count
  613. # jat__stat perrc # phase error count
  614. #
  615. # Print statistics about session state.
  616. #
  617. local which=$1
  618. case $which in
  619. swarc) grep -cxF warning "$(__jat__sd_path "llog")" ;;
  620. pwarc) grep -cxF warning "$(__jat__sd_path "P.llog")" ;;
  621. serrc) grep -cxF error "$(__jat__sd_path "llog")" ;;
  622. perrc) grep -cxF error "$(__jat__sd_path "P.llog")" ;;
  623. sfailc) grep -cxF FAIL "$(__jat__sd_path "vlog")" ;;
  624. pfailc) grep -cxF FAIL "$(__jat__sd_path "P.vlog")" ;;
  625. ppassc) grep -cxF PASS "$(__jat__sd_path "P.vlog")" ;;
  626. spassc) grep -cxF PASS "$(__jat__sd_path "vlog")" ;;
  627. pasrtc) grep -cx 'PASS\|FAIL' "$(__jat__sd_path "P.vlog")" ;;
  628. sasrtc) grep -cx 'PASS\|FAIL' "$(__jat__sd_path "vlog")" ;;
  629. *) __jat__usage "invalid statistic field: $which"
  630. return 2 ;;
  631. esac
  632. }
  633. jat__pend() {
  634. #
  635. # End active phase
  636. #
  637. local oldpdir
  638. oldpdir=$(__jat__sd_keyR P.pdir)
  639. test -n "$oldpdir" && {
  640. jat__log_info "changing back from phase dir: $oldpdir"
  641. popd >/dev/null
  642. }
  643. __jat__show_pend
  644. __jat__pdummy
  645. __jat__log_event SINFO
  646. }
  647. jat__pstartd() {
  648. #
  649. # Start diagnostic phase named $1
  650. #
  651. # See COMMON ARGUMENTS section of this manual.
  652. #
  653. __jat__pstart -t diag "$@"
  654. }
  655. jat__pstartc() {
  656. #
  657. # Start cleanup phase named $1
  658. #
  659. # See COMMON ARGUMENTS section of this manual.
  660. #
  661. __jat__pstart -t cleanup "$@"
  662. }
  663. jat__pstarts() {
  664. #
  665. # Start setup phase named $1
  666. #
  667. # See COMMON ARGUMENTS section of this manual.
  668. #
  669. __jat__pstart -t setup "$@"
  670. }
  671. jat__pstartt() {
  672. #
  673. # Start test phase named $1
  674. #
  675. # See COMMON ARGUMENTS section of this manual.
  676. #
  677. __jat__pstart -t test "$@"
  678. }
  679. jat__submit() {
  680. #
  681. # Submit file $1 as part of test result
  682. #
  683. # Usage:
  684. #
  685. # jat__submit PATH [ALTNAME]
  686. #
  687. # Last element of PATH is used as result name, unless alternative
  688. # name ALTNAME is given, in which case it's used instead.
  689. #
  690. local path=$1
  691. local altname=$2
  692. local dest=$__JAT__SDIR/results
  693. local nhint
  694. test -n "$path" || {
  695. __jat__usage "no SRC?"
  696. return 2
  697. }
  698. test -n "$altname" && {
  699. dest="$dest/$altname"
  700. nhint=" as $altname"
  701. }
  702. mkdir -p "$(dirname "$dest")" || {
  703. jat__log_error "cannot write to result storage: $dest"
  704. return 3
  705. }
  706. jat__log_info "submitting result: $path$nhint"
  707. cp -ar "$path" "$dest"
  708. }
  709. __jat__assert() {
  710. #
  711. # Make assert event
  712. #
  713. # Use this function to create own asserts
  714. #
  715. case $1 in
  716. PASS) __jat__show_pass "$2" ;;
  717. FAIL) __jat__show_fail "$2" ;;
  718. esac
  719. __jat__log_event "$@"
  720. }
  721. __jat__bumpid() {
  722. #
  723. # Create and/or bump ID named $1
  724. #
  725. # Print next ID from series named $1. Series starts
  726. # with 1, so on firts call, value of ID is 1, then 2.
  727. # etc.
  728. #
  729. # Isession is not initialized, ID value will be 0 and
  730. # return status will be 3.
  731. #
  732. local name=$1 # name of id
  733. local cache # ordinal cache
  734. local ord # ordinal
  735. if test -n "$__JAT__SDIR"; then
  736. cache=$(__jat__sd_path "series/$name")
  737. else
  738. echo 0; return 3
  739. fi
  740. if test -f "$cache"; then
  741. ord=$(<"$cache")
  742. ((ord++))
  743. else
  744. ord=1
  745. fi
  746. echo $ord >"$cache"
  747. echo "$name-$ord"
  748. }
  749. __jat__cmp_match() {
  750. #
  751. # True if $RVal matches $OVal by $Op
  752. #
  753. case $Op in
  754. eq|ne|lt|gt|le|ge) test "$RVal" -"$Op" "$OVal" ;;
  755. ==) test "$RVal" == "$OVal" ;;
  756. re) grep -qx "$OVal" <<< "$RVal" ;;
  757. *) return 2 ;;
  758. esac
  759. }
  760. __jat__es_match() {
  761. #
  762. # True if exit status $1 matches expression $EsExpr
  763. #
  764. local expr=$1
  765. local es=$2
  766. local part
  767. for part in ${expr//,/ }; do
  768. test -n "$part" || continue
  769. #FIXME: a rather funny implementation (works, though...)
  770. eval "echo {${part/-/..}}" | grep -qwF "$es" && return 0
  771. done
  772. return 1
  773. }
  774. __jat__log_event() {
  775. #
  776. # Pass YAML log event to __jat__writelog()
  777. #
  778. # Usage:
  779. #
  780. # __jat__log_event ETYPE HINT CASEID [BEID].. -- [KEY=VALUE]..
  781. #
  782. # ETYPE can be FAIL, PASS, TEST_ERROR or SINFO (the latter two
  783. # are for events unrelated to SUT, like internal errors or
  784. # phase start/end events).
  785. #
  786. # HINT and BEIDs are explained in COMMON ARGUMENTS section of this
  787. # manual.
  788. #
  789. # You can provide any amount of KEY=VALUE pairs, meaning of which
  790. # is specific to every assert function. (The assert function name
  791. # is auto-detected and logged so that you can infer KEY meanings
  792. # later.) Each KEY must be simple word, except that prefixes of
  793. # `t.`, `r.`, and `o.` are allowed to signify that the value logged
  794. # is relevant to test method, result or oracle, respectively.
  795. local etype=$1; shift
  796. local hint=$1; shift
  797. local acaseid=$1; shift
  798. local arg
  799. local beids=()
  800. local pairs=()
  801. local real_beids=()
  802. local caseid="$JAT__TEST_ID"
  803. local pcaseid
  804. local reading=beids
  805. local pair
  806. local origin=${FUNCNAME[1]}
  807. pcaseid=$(__jat__sd_keyR P.caseid)
  808. test -n "$pcaseid" && caseid+=":$pcaseid"
  809. test -n "$acaseid" && caseid+=":$acaseid"
  810. case $etype in
  811. PASS|FAIL|SINFO|TEST_ERROR|TEST_WARNING|PROMISE) : ;;
  812. *) __jat__show_error "bad ETYPE, changing to TEST_ERROR: $etype"
  813. etype=TEST_ERROR ;;
  814. esac
  815. case $etype:$origin in
  816. PASS:__jat__assert) : ;;
  817. FAIL:__jat__assert) : ;;
  818. TEST_ERROR:__jat__usage) : ;;
  819. SINFO:jat__sinit) : ;;
  820. SINFO:jat__sfinish) : ;;
  821. SINFO:__jat__pstart) : ;;
  822. SINFO:jat__pend) : ;;
  823. SINFO:jat__log_info) : ;;
  824. TEST_ERROR:jat__log_error) : ;;
  825. TEST_WARNING:jat__log_warning) : ;;
  826. *) __jat__show_error "illegal call of __jat__log_event: $etype from $origin"
  827. etype=TEST_ERROR ;;
  828. esac
  829. for arg in "$@"; do
  830. case $reading:$arg in
  831. *:)
  832. shift
  833. ;;
  834. beids:--)
  835. shift
  836. reading=pairs
  837. ;;
  838. beids:*)
  839. __jat__valid_beid "$arg" || {
  840. beids=(); pairs=(); etype=API_BUG
  841. hint="bad BEID syntax (must be simple id): '$arg'"
  842. __jat__show_error "$hint"
  843. break
  844. }
  845. beids+=("$arg")
  846. shift
  847. ;;
  848. pairs:*)
  849. __jat__valid_pair "$arg" || {
  850. beids=(); pairs=(); etype=API_BUG
  851. hint="bad K=V syntax: '$arg'"
  852. __jat__show_error "$hint"
  853. break
  854. }
  855. pairs+=("$arg")
  856. shift
  857. ;;
  858. esac
  859. done
  860. for beid in "${beids[@]}"; do
  861. case $beid in
  862. "") : ;;
  863. *.*) real_beids+=("$beid") ;;
  864. *) real_beids+=("$JAT__BEID_NS.$beid") ;;
  865. esac
  866. done
  867. echo "$etype" >> "$(__jat__sd_path "vlog")"
  868. echo "$etype" >> "$(__jat__sd_path "P.vlog")"
  869. {
  870. # NOTE: some scalars are printed directly for performance
  871. # reasons (these will never be null nor will they
  872. # contain YAML special chars)
  873. #
  874. echo "-"
  875. echo " origin: $origin"
  876. echo " etype: $etype"
  877. echo " stamp: $(__jat__newstamp)"
  878. echo " caseid: $caseid"
  879. __jat__yamls hint "$hint"
  880. __jat__yamla beids "${real_beids[@]}"
  881. __jat__yamld data "${pairs[@]}"
  882. echo " phase:"
  883. echo " id: $(__jat__sd_keyr phase)"
  884. echo " name: $(__jat__sd_keyr P.name)"
  885. echo " type: $(__jat__sd_keyr P.type)"
  886. } | sed 's/^/ /' | __jat__writelog
  887. }
  888. __jat__newstamp() {
  889. #
  890. # Create new timestamp
  891. #
  892. case $__JAT__DETERMINISTIC in
  893. true) __jat__bumpid time ;;
  894. false) date +%s ;;
  895. *) die "bad value of __JAT__DETERMINISTIC: $__JAT__DETERMINISTIC" ;;
  896. esac
  897. }
  898. __jat__pdummy() {
  899. #
  900. # Create dummy phase
  901. #
  902. __jat__sd_keyw phase "dummy"
  903. __jat__sd_keyw P.name "_jat_dummy_phase_"
  904. __jat__sd_keyw P.type "none"
  905. }
  906. __jat__pstart() {
  907. #
  908. # Start phase of type $1 and name $2
  909. #
  910. local type # phase name
  911. local name # ^^ name
  912. local pdir # ^^ directory
  913. local pcaseid # ^^ case id, if assigned
  914. local oldphase # current phase id
  915. while true; do case $1 in
  916. -t) type="$2"; shift 2 || return 2 ;;
  917. -c) pcaseid="$2"; shift 2 || return 2 ;;
  918. -C) pdir="$2"; shift 2 || return 2 ;;
  919. *) break ;;
  920. esac done
  921. name="${1:-_jat_anon_phase_}"
  922. oldphase=$(__jat__sd_keyr phase)
  923. test "$oldphase" == dummy || {
  924. jat__log_error "old phase not ended; ending automatically: $oldphase"
  925. jat__pend
  926. }
  927. debug -v type name pdir
  928. __jat__sd_keyw phase "$(__jat__bumpid phasid)"
  929. __jat__sd_keyw P.type "$type"
  930. __jat__sd_keyw P.name "$name"
  931. __jat__sd_keyw P.caseid "$pcaseid"
  932. __jat__show_pstart "$name"
  933. __jat__log_event SINFO
  934. test -n "$pdir" && {
  935. __jat__sd_keyw P.pdir "$pdir"
  936. __jat__sd_keyw P.PWD "$PWD"
  937. jat__log_info "changing directory for phase: $pdir"
  938. pushd "$pdir" >/dev/null || {
  939. jat__log_error "failed to change to phase directory: $pdir"
  940. return 3
  941. }
  942. }
  943. }
  944. __jat__sd_keya() {
  945. #
  946. # Append line $2 to session key $1
  947. #
  948. local key=$1
  949. local value=$2
  950. local path
  951. path=$(__jat__sd_path "$key")
  952. echo "$value" >> "$path" || {
  953. __jat__show_error "error appending session data: key '$key' to '$path'"
  954. return 3
  955. }
  956. }
  957. __jat__sd_keyR() {
  958. #
  959. # Read session key $1 if it exists, return 1 otherwise
  960. #
  961. local key=$1
  962. local path
  963. path=$(__jat__sd_path "$key") || return 1
  964. cat "$path" || {
  965. __jat__show_error "error reading session data: key '$key' from '$path'"
  966. return 3
  967. }
  968. }
  969. __jat__sd_keyr() {
  970. #
  971. # Read session key $1
  972. #
  973. local key=$1
  974. local path
  975. path=$(__jat__sd_path "$key")
  976. cat "$path" || {
  977. __jat__show_error "error reading session data: key '$key' from '$path'"
  978. return 3
  979. }
  980. }
  981. __jat__sd_keyw() {
  982. #
  983. # Write $2 to session key $1
  984. #
  985. local key=$1
  986. local value=$2
  987. local path
  988. path=$(__jat__sd_path "$key")
  989. echo "$value" > "$path" || {
  990. __jat__show_error "error writing session data: key '$key' to '$path'"
  991. return 3
  992. }
  993. }
  994. __jat__sd_path() {
  995. #
  996. # Dereference path to session key $1; true if exists
  997. #
  998. # Key starting with 'P.' will be phase-specific.
  999. #
  1000. local key=$1
  1001. local path="$__JAT__SDIR/internal/"
  1002. case $key in
  1003. "") __jat__show_error "no KEY?"; return 2 ;;
  1004. P.*) path+="phase-$(__jat__sd_keyr phase)/$key" ;;
  1005. *) path+="$key" ;;
  1006. esac
  1007. mkdir -p "${path%/*}" 2>/dev/null || {
  1008. __jat__show_error "could not create key: $path"
  1009. return 2
  1010. }
  1011. echo "$path"
  1012. test -e "$path"
  1013. }
  1014. __jat__show() {
  1015. #
  1016. # Show to user and also keep in ansi log
  1017. #
  1018. local msg
  1019. for msg in "$@"; do
  1020. echo -e "$msg" >> "$__JAT__SDIR/log.ansi"
  1021. echo -e "$msg" >&2
  1022. done
  1023. }
  1024. __jat__show_fail() {
  1025. #
  1026. # Show assert fail message $1 to stderr
  1027. #
  1028. __jat__show \
  1029. " $__JAT__SWORD_SUT.$__JAT__SWORD_FAIL: $1"
  1030. }
  1031. __jat__show_pass() {
  1032. #
  1033. # Show assert fail message $1 to stderr
  1034. #
  1035. __jat__show \
  1036. " $__JAT__SWORD_SUT.$__JAT__SWORD_PASS: $1"
  1037. }
  1038. __jat__show_pend() {
  1039. #
  1040. # Show phase end
  1041. #
  1042. local pverd # phase verdict text
  1043. local pname # ^^ name
  1044. local pnhint # ^^ ^^ display
  1045. pname=$(__jat__sd_keyr P.name)
  1046. test "$pname" == "_jat_anon_phase_" || pnhint=" '$pname'"
  1047. case "$(jat__stat pfailc)" in
  1048. 0) pverd="$__JAT__SWORD_PASS" ;;
  1049. *) pverd="$__JAT__SWORD_FAIL" ;;
  1050. esac
  1051. __jat__show \
  1052. "$__JAT__SWORD_PHASE.$__JAT__SWORD_END.$pverd$pnhint" \
  1053. ""
  1054. }
  1055. __jat__show_pstart() {
  1056. #
  1057. # Show phase start
  1058. #
  1059. local pname=$1 # phase name
  1060. local pnhint # ^^ ^^ display
  1061. test "$pname" == "_jat_anon_phase_" || pnhint=" '$pname'"
  1062. __jat__show \
  1063. "$__JAT__SWORD_PHASE.$__JAT__SWORD_START$pnhint"
  1064. }
  1065. __jat__show_sfinish() {
  1066. #
  1067. # Show message about session $1 finalization to stderr
  1068. #
  1069. local sverd # session verdict text
  1070. case "$(jat__stat sfailc)" in
  1071. 0) sverd="$__JAT__SWORD_PASS" ;;
  1072. *) sverd="$__JAT__SWORD_FAIL" ;;
  1073. esac
  1074. __jat__show \
  1075. "$__JAT__SWORD_SESSION.$__JAT__SWORD_FINALIZE.$sverd"
  1076. }
  1077. __jat__show_sinitn() {
  1078. #
  1079. # Show message about session initialization to stderr
  1080. #
  1081. __jat__show \
  1082. "$__JAT__SWORD_TEST.$__JAT__SWORD_TINFO: id: $JAT__TEST_ID" \
  1083. "$__JAT__SWORD_TEST.$__JAT__SWORD_TINFO: version: $JAT__TEST_VERSION" \
  1084. "" \
  1085. "$__JAT__SWORD_SESSION.$__JAT__SWORD_START" \
  1086. ""
  1087. }
  1088. __jat__show_sinitr() {
  1089. #
  1090. # Show message about session reload to stderr
  1091. #
  1092. __jat__show \
  1093. "$__JAT__SWORD_TEST.$__JAT__SWORD_TINFO: id: $JAT__TEST_ID" \
  1094. "$__JAT__SWORD_TEST.$__JAT__SWORD_TINFO: version: $JAT__TEST_VERSION" \
  1095. "" \
  1096. "$__JAT__SWORD_SESSION.$__JAT__SWORD_RELOAD" \
  1097. ""
  1098. }
  1099. __jat__show_tinfo() {
  1100. #
  1101. # Show test info
  1102. #
  1103. local sp=""
  1104. test "$(__jat__sd_keyr phase)" == dummy || sp=" "
  1105. __jat__show \
  1106. "$sp$__JAT__SWORD_TEST.$__JAT__SWORD_TINFO: $1"
  1107. }
  1108. __jat__show_terror() {
  1109. #
  1110. # Show test error
  1111. #
  1112. local sp=""
  1113. test "$(__jat__sd_keyr phase)" == dummy || sp=" "
  1114. __jat__show \
  1115. "$sp$__JAT__SWORD_TEST.$__JAT__SWORD_TERROR: $1"
  1116. }
  1117. __jat__show_twarning() {
  1118. #
  1119. # Show test warning
  1120. #
  1121. local sp=""
  1122. test "$(__jat__sd_keyr phase)" == dummy || sp=" "
  1123. __jat__show \
  1124. "$sp$__JAT__SWORD_TEST.$__JAT__SWORD_TWARNING: $1"
  1125. }
  1126. __jat__show_error() {
  1127. #
  1128. # Emit general jat warning (not related to SUT)
  1129. #
  1130. warn "jat.ERROR" "$1"
  1131. }
  1132. __jat__usage() {
  1133. #
  1134. # Print usage error $1 and hint according to FUNCNAME
  1135. #
  1136. local msg=$1
  1137. local parent=${FUNCNAME[1]}
  1138. local patt
  1139. local patts=()
  1140. __jat__show_error "bad usage: $msg"
  1141. case $parent in
  1142. jat__cmd) patts=("[assert-options] [-o R_OUT] [-e R_ERR] [-s R_ESF] [-S ES_EXPR] [--] CMD [ARG]..") ;;
  1143. jat__eval) patts=("[assert-options] [-s R_ESF] [-S ES_EXPR] [--] CODE") ;;
  1144. jat__cmp) patts=("[assert-options] RVAL OP OVAL") ;;
  1145. jat__fail) patts=("[assert-options]") ;;
  1146. jat__pass) patts=("[assert-options]") ;;
  1147. jat__submit) patts=("SRC [ALTNAME]") ;;
  1148. jat__stat) patts=(spassc sfailc ppassc pfailc) ;;
  1149. esac
  1150. for patt in "${patts[@]}"; do
  1151. __jat__show_error "usage: $parent $patt"
  1152. done
  1153. __jat__log_event TEST_ERROR "bad usage: $parent()" "" \
  1154. -- "PATTERNS=${patts[*]}"
  1155. }
  1156. __jat__writelog() {
  1157. #
  1158. # Write logged output (copy if needed)
  1159. #
  1160. case $JAT__YLOG in
  1161. "") cat >> "$__JAT__SDIR/log.yaml" ;;
  1162. -) tee -a "$__JAT__SDIR/log.yaml" ;;
  1163. *) tee -a "$__JAT__SDIR/log.yaml" >> "$JAT__YLOG" ;;
  1164. esac
  1165. }
  1166. __jat__valid_beid() {
  1167. #
  1168. # True if $1 is a valid BEID
  1169. #
  1170. local tainted=$1
  1171. grep -qx '[[:alpha:]_][[:alnum:]_.]*' <<<"$tainted"
  1172. }
  1173. __jat__valid_pair() {
  1174. #
  1175. # True if $1 is a valid BEID
  1176. #
  1177. local tainted=$1
  1178. grep -qx '\([tor][.]\)\?[[:alpha:]_][[:alnum:]_]*=.*' <<<"$tainted"
  1179. }
  1180. __jat__yamls() {
  1181. #
  1182. # Print a scalar field named $1 with value $2
  1183. #
  1184. local name=$1
  1185. local value=$2
  1186. case $value in
  1187. "") echo " $name: ~" ;;
  1188. *) echo " $name: |-"
  1189. echo " $value" ;;
  1190. esac
  1191. }
  1192. __jat__yamla() {
  1193. #
  1194. # Print an array field named $1 with array of values $2..
  1195. #
  1196. local name=$1; shift
  1197. local value
  1198. test $# -eq 0 && echo " $name: []" && return 0
  1199. echo " $name:"
  1200. for value in "$@"; do
  1201. case $value in
  1202. "") echo " - ~" ;;
  1203. *) echo " - |"
  1204. echo " $value" ;;
  1205. esac
  1206. done
  1207. }
  1208. __jat__yamld() {
  1209. #
  1210. # Print a dict field named $1 and composed of KEY=VALUE pairs $2..
  1211. #
  1212. local name=$1; shift
  1213. local pair
  1214. local key
  1215. local value
  1216. test $# -eq 0 && echo " $name: {}" && return 0
  1217. echo " $name:"
  1218. for pair in "$@"; do
  1219. key=${pair%%=*}; value=${pair#$key=}
  1220. case $value in
  1221. "") echo " $key: ~" ;;
  1222. *) echo " $key: |"
  1223. echo " $value" ;;
  1224. esac
  1225. done
  1226. }
  1227. #shellfu module-version=__MKIT_PROJ_VERSION__