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

jat.sh.skel 39KB

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