20230322
[shlib.git] / bin / scripttest
blobb7692013c96a0557e09764ab80704c889a9aa258
1 #!/bin/bash
2 ############################################################
3 # source: scripttest
4 # author: devenkong(18151155@qq.com)
5 # date: 2021-09-30
6 ############################################################
7 # Copyright (C) 2022- Free Software Foundation, Inc.
8 # This configure script is free software; the Free Software
9 # Foundation gives unlimited permission to copy, distribute
10 # and modify it.
11 ############################################################
12 # note:
13 # scripttest is a program used to running test script one
14 # by one in testing dir. it's used to test script, or do some
15 # integerated testing work for binary program.
16 # the hi-light of this program is that, every test item
17 # orgnized by catalog id, tester can specify id or count number
18 # or id range to running test script.
19 # another point is that, it manage failed item, tester can
20 # test the failed item only. it's usefull for developer.
21 # it's usefull for script module/shlib unit trst, and program
22 # integerated testing, and soft-pkg test before install, and
23 # loop-back test after some feature append or bug fixed.
25 ############################################################
27 # Testing:
28 # @ nest testing, and check if the corresponding testing tmp dir is correct.
29 # @ test unit for scripttest will be updated.
30 # @ copy test unit from shlib-lite for paramter -bes.
31 # @ some place will generate misc files should not be become.
34 asdfas='
35 -O , --output-dir=<output_dir> 指定测试时的临时文件目录。默认值为~/用户目录下的
36 .testing目录。
37 -f , --force 指定测试信息的目录。默认值为当前目录下的testing
38 目录。
39 -y , --sync 根据测试信息描述文件,生成测试目录及测试脚本等文
40 =[sync_to_dir] 件,用于用户编写各种测试用例。
41 # todo:输出文件目录异常。暂时disable该功能。
42 # tbd:使用同步输出为-g参数,-y用于sync-back
47 # todo:
48 # - @ 使用-O参数设置测试的tmp目录路径。
49 # - @ 同一个project不同目录下的测试。
50 # @ 一些参数可使用环境变量设置,而不在命令行参数设置。环境变量改成大写字母。
51 # @ 遍历testcase目录,同步比对func_list,差异项进行创建或存放到trash目录
52 # @ taskloader运行一个测试项的脚本
54 # @ 代码中IFS的设置的代码,或许会使命令行字符串解析不正确。通常命令行使用空格将命令行字符串
55 # 分成一个数组。IFS设置成非' '后,命令行解析就异常了。
58 # @ 需要添加测试的单位时间.例如标准测试例子测试时间为N秒,其它测试时间都以标准测试时间为单位.
59 # 记录测试时间以基本测试时间为单位.
60 # 以一个单独的参数进行测试,保存到参数中。
62 # @ 设置默认的函数定义为export,include一次,sub-process可以不include。但是一些环境变量无法使用-g参数,所以这样的
63 # include是不完整的。
64 # @ -F参数运行时,测试项只有所在目录的id,没有full-id。需要根据测试cataid获取不同level的desc字符串显示。
67 # @ log test output to log file, that can be reviewd by tester, if use -r -q paramter.
68 # @ append -r -q attribute to -a option.
69 # @ append time cost display switch option. when test for scripttest it self, it will display
70 # different cpu cost, and leading to report test err.
71 # @ seperate diff result to L2R and R2L, and use different color for prefix '<' or '>'.
74 # @ add testing.shlib, append dbgout_hdr(), it is used to output testing content hint with two lines and
75 # colorfull dispaly.
77 # @ append test count when -n is specified.
79 # @ default is test in range. -F test for failed list, -a for all items, -A for all items continously.
81 # @ accessing of /var/log/dbgoutlog directory is permission denied. write a program, create dir under public
82 # dir like /var/log/dbgoutlog. dir name is the program name of request process.
88 . shlibinc
90 include stdio.shlib
91 include dbgout.shlib
92 include term.shlib
93 include args.shlib
94 include gplib.shlib
96 include cataid.shlib
97 include dirgen.shlib
98 #include strsyntax.shlib
99 #include attr.shlib
100 #include catalog.shlib
102 #dsource imifile.shlib
105 #loadini scripttest.ini
106 #loadimi scripttest.imi
109 ##############################
110 # section: public comment info
111 ##############################
115 ##############################
116 # section: variable define
117 ##############################
121 # time cost of a standard testing example, the time unit is ms.
122 # the basic testing example is running on cpu that have performence
123 # between 100MHz and 5GHz. and the shortest test time is 10ms, the max
124 # test time is 5s.
125 # if it is running on a best cpu at 5GHz, it must longer then 0.01s.
126 # and it also should be shorter then 5s with a 100MHz cpu.
127 # cpu performence is not only decided by frequent. so a 5GHz cpu maybe
128 # more then 5GHz instruction per-second. so, the best cpu maybe 5*50
129 # times then lowest cpu.
130 # let the example testing code cost 0.02s with best cpu, and it will
131 # cost about 5s with lowest cpu.
132 # if one test script cost 0.2s on the best cpu, and save as 10 unit
133 # times. when this script is tested on a middle cpu, UNIT_TEST_MS is
134 # 500ms, it will cost 5s, and it will be displayed on testing info.
135 # if the cpu performence is better then it, it would not display on
136 # screen.
138 UNIT_TEST_MS=
142 TESTING_ROOT_DIR=
143 TESTING_TMP_DIR=~/.testing/$(basename $0)/
145 test_cnt=0
146 test_cnt_num=99999
148 err_continue_cnt=0
149 err_cnt=0
151 FCCOLOR=$FCGREEN
152 IFCCOLOR=$FCMAGENTA
153 EFCCOLOR=$FCRED
154 INFOCOLOR=$FCGREEN
156 readonly FCR2L="${FCCYAN}"
157 readonly FCL2R="${FCCYAN}${CREV}"
159 verbos_pfx=" ${INFOCOLOR}+$CNORMAL "
162 # do not delete this comment, it is used for var define code intert.
164 # args-var-define-begin
165 # args-var-define-end
169 # ATTENTION:
170 # @ the description string can not contain char of "'".
175 # desc-str for scripttest
176 # @ every paramter desc-str-line start with 'param'.
177 # @ every colum start with '|', and seperated by blanks.
178 # @ '|blank' means a blank line dispalyed in helper.
179 # @ follow with a '' quoated string is the option category string.
180 # @ comment '#' in desc-str is also supported, and can be used to
181 # disable some option have not been implemented.
182 # @ desc-str can be in multiple var. and some of them maybe re-used
183 # for other program by 'source xxx --loadshlib'.
185 scripttest_desc_str="
186 |prog $0 '应用测试程序。用于对指定目录下的一系列测试脚本运行测试,并输出测试结果。如未指定测试目录参数,以当前目录下的testing目录作为默认测试信息目录,并进行测试。'
188 |blank 'Design File Auto Gen Paramters:'
189 |param |-l |--list |--- |= |%<list_test_list> |&<args_list_item> |'list items and dirs under testing dir.'
190 |param |-S |--save |--- |=<save_desc_file> |%<save_desc_file> |&<args_save_catalog> |'generate catalog from testing dir. if the catalog file is exist, just display desc info only. append -f option to recover an original catalog file. '
191 # -y参数存在bug,不添加到程序功能中。
192 |param |-y |--sync |--- |=[sync_to_dir] |%<sync_to_dir> |&<args_sync_to_dir> |'generate 根据测试信息描述文件,生成测试目录及测试脚本等文件,用于用户编写各种测试用例。'
193 |blank
195 |blank 'General Testing Paramters:'
196 |param |-n |--num |--- |=<test_cnt_num> |%<test_cnt_num> |& |'running N test items continously.'
197 |param |-L |--failed-begin |--- |= |%<failed_begin> |& |'从failed列表中的第一项开始测试。'
198 |param |-F |--failed |--- |= |%<failed_test> |&<args_failed_test> |'test the failed items listed by -a option. it\x27s equal to -L -r -q option.'
199 |param |-r |--ignor-err |--- |= |%<test_ignor_err> |& |'ignor testing error, and running continously.'
200 |param |-a |--all |---all |= |%<test_all> |& |'test for all.'
201 |param |-A |-- |--- |= |%<continouse_all> |&<args_all> |'test for all with -r -q option.'
202 |blank
204 |blank 'Testing Range Paramters:'
205 |param |-t |--test |--- |=<test_id> |%<test_id> |&<args_test_id> |'test a specified item by test item id.'
206 |'the id can be a dir id, that means test the items under the dir. if no id specified, it will test from the beginning id of normal testing.'
207 |param |-b |--begin |--- |=<begin_test_id> |%<begin_test_id> |& |'begin id of testing range.'
208 |param |-e |--end |--- |=<end_test_id> |%<end_test_id> |& |'end id of the testing range.'
209 |param |-x |--exclude |--- |=<exclude_id> |%<exclude_id> |&<args_exclude> |'exclude id that should not be tested.'
210 |param |-R |--rollup |--- |= |%<test_id_rollup> |& |'rollup begin id, if the corresponding item is ok.'
211 |param |-B |--rollback |--- |= |%<test_id_rollback> |& |'rollback begin id to the previous value. it is not simplly decrease the count of id, it will check the test item file first.'
212 |param |-s |--set |--- |= |%<set_test_info> |& |'save the range setted by -b and -e and -x option.'
213 |param |-c |--clean |--- |= |%<clean> |&<args_clean> |'clean testing temp dir.'
214 |blank
216 |blank 'Testing Output Info Paramters:'
217 |param |-u |--update |--- |= |%<update_test_file> |& |'if the current test item is ok, save stdout output to test item as the standard output content.'
218 |param |-v |--verbose |--- |= |%<verbose> |&<args_verbose> |'output the test script outputed with \'+ \' prefix. it is the detail output info for testing.'
219 |param |-q |--quiet |--- |= |%<quiet> |& |'disable comparation string output. but it is not conflect with -v option. use -v -q means output detail string info instead of comparation string.'
220 |blank
222 |blank 'Misc Paramters:'
223 |param |-f |--force |--- |= |%<test_force> |& |'force operation option. it\'s a general option combine with other options.'
224 |param |-j |--multi-task |--- |=<multi_task> |%<multi_task> |& |'multi-task running. set the parall num of tasks.'
225 |blank
228 another_desc_str="
229 |blank 'Other Paramters:'
230 |param |-d |--dir |--- |=<test_dir> |%<test_dir> |& |'指定测试信息的目录。默认值为当前目录下的testing目录。'
231 |param |-O |--output-dir |--- |=<output_dir> |%<output_dir> |& |'指定测试时的临时文件目录。默认值为~/用户目录下的.testing目录。'
232 |param |-p |--print-vars |--- |= |%<print_vars> |&<args_print_vars> |'输出参数定义的变量信息。'
233 |param |-m |--mono |--- |= |%<mono> |&<args_mono> |'输出非彩色的字符串信息。'
234 |param |-g |--logfile |--- |= |%<test_logfile> |&<test_logfile> |'测试failed时,不输出差异信息。'
235 |blank
237 |blank 'Version & Helper & Debug:'
238 |param |-V |--version |--- |= |%<version_info> |&<args_version> |'output version info of the program.'
239 |param |-h |-- |--- |= |%<h_info> |&<args_h_info> |'simplly helper doc only for option.'
240 |param |- |--help |--- |= |%<help_info> |&<args_help_info> |'this helper doc.'
241 |param |- |--debug |--- |= |%<test_debug> |&<args_test_debug> |'debug info for arguments dispatch. add the option follow with cmd.'
245 readonly PROG=`basename $0`
248 # this comment is used for version id auto update.
250 # @{prog-ver-begin}
251 readonly V1=0
252 readonly V2=1
253 readonly V3=0
254 readonly VEXT=
255 readonly VER_DATE=20220101
256 # @{prog-ver-end}
258 readonly eval_PROG_VERSION="v${V1}.${V2}\${V3:+\".$V3\"}\${VEXT:+\"$VEXT\"}\${VER_DATE:+\"-$VER_DATE\"}"
259 PROG_VERSION="`eval echo $eval_PROG_VERSION`
260 Copyright (C) 2022- Free Software Foundation, Inc.
261 This configure script is free software; the Free Software
262 Foundation gives unlimited permission to copy, distribute
263 and modify it.
265 Writen by deven(devenkong@126.com).
268 PROG_BANNER="$PROG $PROG_VERSION"
269 PROG_SYNTAX="Usage: $PROG -[lS:n:LFrat:b:e:x:RBscuvd:O:qpmVhfj:g]"
270 PROG_DESC=" this is a test-suite program to orgnize a lot of test item script. as a direct structure on file system, and execute them. when testing error,it show differece betwin current output content and the correct output content, which saved in the item files.
271 this prog can be used as a unit testing tool, and also can be used as a functional testing tool, to running the program (set) testing before installization, and check if it can be running in current enviroument.
272 i use this util to execute loop-back testing that i tested before. it will save time to test some repeated problem in detail. if i write a new feature for a program, i will check if the new code would make original code error.
273 some of the option description is writen by chinese char, it is used to test utf-8 char dispaly when it works in words bundary truncate feature."
274 PROG_OTHER_DESC="
275 EXAMPLES:
277 @ test soft-pkg before installation:
279 scripttest -a -r -q
280 this cmd run a full test continously without faild hint string. it is the same as a loop-back testing for soft-pkg.
282 @ for program loop-back testing:
284 scripttest -a -r -q
285 this cmd run a full test continously without faild hint string.
287 scripttest -F -v -q
288 test the failed items listed in full testing. fix them one by one.
290 @ for function feature unit testing:
292 scripttest -b 1.1 -e 1.5 -x 1.3 -s
293 this cmd set the test range between 1.1 and 1.5, and exclude 1.3. save the info to testing temp dir. it will effect on next test work.
295 scripttest -n 1 -v -q
296 for unit testing, it test one item, dispaly full output string, disable comparation string output. it is equal to run test script with corresponding feature.
298 scripttest -n 1 -u
299 if the testing is ok, save test script output string as a standard file. it will effactive on next testing.
303 fmt_usage="$PROG_BANNER
304 @{PROG_SYNTAX}
305 $PROG_DESC
306 @{helper}
307 $PROG_OTHER_DESC
311 ##############################
312 # section: private function
313 ##############################
318 # testcase目录下测试程序的调用,及输出比较。
322 # fsyntax: test_item <id> <script_name> <desc_str>
323 # fdesc: one item testing.
325 test_item ()
327 local ret=
328 local tmp=
329 local script_name=
330 local test_desc=
331 local exetime=
332 local tmp_vch_output=
334 script_name="$1.$2.sh"
335 test_desc="$3"
337 # paramter validation judgement.
338 if [[ ! -f $script_name ]]; then
339 err "err: file $script_name is not exist.\n"
340 return 1
343 if [[ -z "$test_desc" ]]; then
344 err "err: descript string is missing.\n"
345 return 1
348 if [[ ! -f $script_name."$test_desc".txt ]]; then
349 err "err: file $script_name.$test_desc.txt is not exist.\n"
350 return 1
353 # item testing string output.
354 # info "$CHIGHL$IFCCOLOR$TPREFIX[item] $1 $2 \"$3\" ... "
355 info "$CHIGHL$IFCCOLOR$TPREFIX[item] $1.\"$3\" ... "
358 # copy test item file to temp dir, it keeps every testing
359 # file can not be modified by test script.
361 rm "$TESTING_TMP_DIR/$1.$2/" -rf
362 mkdir -p "$TESTING_TMP_DIR/$1.$2/"{,scripts}
363 cp "$1".* "$TESTING_TMP_DIR/$1.$2/" -rf
364 cp -rf scripts/"$1".* "$TESTING_TMP_DIR/$1.$2"/scripts/ 2> /dev/null
365 # XXX: here should be modified, if the scripts dir is very large,
366 # it will copy every times for every test script.
367 cp -rf scripts/* "$TESTING_TMP_DIR/$1.$2"/scripts/ 2> /dev/null
368 cp -rf scripts/testing/ "$TESTING_TMP_DIR/$1.$2"/scripts/ 2> /dev/null
369 cd "$TESTING_TMP_DIR/$1.$2"
370 # dbgout_cmd ls -l
371 chmod +x *.sh scripts/* 2> /dev/null
373 # dispaly testing time cost if this item would cost more then 5 seconds.
374 if [[ -f $1.$2.time.txt ]]; then
375 exetime=`head -n 2 $1.$2.time.txt`
376 tmp=${exetime##$'\nreal '}
377 tmp=${tmp%%m*}
378 [[ -z $tmp ]] && err "err: the syntax of test time info is not correct.\n"
379 exetime=${exetime##*m}
380 exetime=${exetime%.*}
381 exetime=$(( tmp * 60 + exetime ))
382 # dispaly only longer then 5 seconds.
383 [[ -n $exetime && $exetime -gt 5 ]] && info "(time:${exetime}s) "
387 # here, re-direct output of test script, include stdout and stderr.
388 # using ttyout to force string output when debug this code, event if
389 # the stdout and stderr is re-directed.
391 # ttyout dbgout_ttydev=$dbgout_ttydev
393 # set output stream pipe.
394 tmp_vch_output=$vch_progstdout
395 [[ $output_disable_list =~ "info" ]] && tmp_vch_output=$vch_null
397 info "$CNORMAL"
398 # 带运行时间信息输出的测试项测试
399 if [[ $verbose == "enable" ]]; then
401 # time语句的code-block,stdout调试信息输出使用stderr,time语句输出到stderr,设置stderr输出为time.txt
402 # time语句内的code-block,stderr输出到stdout
403 # 这样,time的stderr输出到time.txt文件,time之内的code-block输出到vch_dbgout(stderr)
406 time ( {
407 info "\n${INFOCOLOR} + ###############################################${CNORMAL}\n" ;
408 { { ./$script_name; echo; } 2>&1 | tee log.txt; } | { IFS=$'\n'; while read -r line; do echo -ne "$verbos_pfx$line\n"; done } ;
409 info "${INFOCOLOR} + ###############################################${CNORMAL}\n" ;
410 } 2>&1 )
411 } >&$tmp_vch_output 2>time.txt
413 # ( time (
414 # info "abc\n";
415 # ttyout "testing\n";
416 # info "\n###############################################\n" ;
417 # ttyout "testing\n";
418 # # 输出测试脚本的stdout和stderr到管道,管道的调试信息使用tee在控制台stdout输出,并保存到日志文件
419 # ( ./$script_name 2>&1 ) | tee log.txt;
420 # info "###############################################\n" ;
421 # ttyout "testing\n";
422 # ) 2>&1 # | { IFS=$'\n'; while read -r line; do echo "$verbos_pfx$line"; done } >&$vch_dbgout #2> /dev/null
423 # ) 2> time.txt #2>&1
424 else
425 # an echo cmd append at the end of test script.
426 # script output string though pipe, but there must be a '\n' at the end
427 # of string, or it will not be displayed on screen.
428 ( time ( ( ./$script_name; echo; ) > log.txt 2>&1 ) ) 2> time.txt #2>&1
431 # append to log file.
432 if [[ ! $logout_disable_list =~ "info" ]]; then
433 echo -ne "$INFO_PFX$@$NEWLINE_SFX" | tee -a $f_logout >&$vch_progstdout
434 while read tmp; do
435 echo "$tmp" >> $f_logout
436 log_size_limit
437 done < <(cat log.txt)
440 # TBD:
441 # cp log.txt $PWD/log/
442 diff log.txt "$script_name.$test_desc".txt > diff.txt
443 if [[ -z "`head -n 5 diff.txt`" ]]; then
444 info "$CHIGHL$INFOCOLOR[ ok ]$CNORMAL\n"
445 ret=0
447 # 如果有failed record,进行记录。
448 sed -i $TESTING_TMP_DIR/testing_failed_list.txt -e "/^$(catalog_id_get TEST)$/d" 2>/dev/null
449 else
450 info "$CHIGHL$EFCCOLOR[failed]$CNORMAL\n"
451 if [[ $quiet != "enable" ]]; then
452 info "testing result difference from correct output.\n"
453 info "stdout > testout\n"
454 info "###############################################\n"
455 cat diff.txt |
456 while read data; do
457 case ${data:0:1} in
458 '>' )
459 info -ne "${FCL2R}>${CNORMAL}${data:1}\n"
461 '<' )
462 info -ne "${FCR2L}<${CNORMAL}${data:1}\n"
465 info -ne "${data}\n"
467 esac
468 done
469 # dbgout_cmd cat diff.txt # >&2
470 # 将测试failed项的日志保存到log目录,便于测试后的查看。
471 info "###############################################\n"
473 ret=1
475 # faled record
476 sed -i $TESTING_TMP_DIR/testing_failed_list.txt -e "/^$(catalog_id_get TEST)$/d" 2>/dev/null
477 catalog_id_get TEST >> $TESTING_TMP_DIR/testing_failed_list.txt
480 cd - > /dev/null
481 if [[ $update_test_file == "enable" ]]; then
482 cp "$TESTING_TMP_DIR/$1.$2"/log.txt "$script_name.$test_desc".txt
483 exetime=`head -n 2 $TESTING_TMP_DIR/$1.$2/time.txt`
484 tmp=${exetime%%m*}
485 tmp=${tmp##`echo -ne "\nreal "`} # }
486 exetime=${exetime##*m}
487 exetime=${exetime%.*}
488 exetime=$(( tmp * 60 + exetime ))
489 [[ -n $exetime && $exetime -gt 5 ]] && cp "$TESTING_TMP_DIR/$1.$2"/time.txt "$1.$2".time.txt
492 # clean testing info
493 rm "$TESTING_TMP_DIR/$1.$2/" -rf
495 return $ret
499 # test_unit <test_dir>
500 test_unit ()
502 local tmp=
503 local test_dir=$1
504 local ret=0
505 local chkret=0
506 local saved_test_id=
508 cd "$1"
509 test_cnt=`test_list_curr_content "$test_dirs" |
510 ( while read line; do
511 eval tmp=( $line )
512 catalog_id_step_to TEST "${tmp[1]}"
514 # id范围判断
515 catalog_id_compare TEST "${tmp[1]}"
516 chk_ret=$?
517 # dbgoutd "chk_ret=$chk_ret\n"
518 case $chk_ret in
519 0 | 3 )
523 continue
526 break
528 esac
530 case ${tmp[0]} in
531 "item" )
532 TPREFIX="$(printf '%*s' $(( $TEST_DIR_DEPTH * 4 + 4)) ' ')"
533 #dbgoutd "TEST_DIR_DEPTH=$TEST_DIR_DEPTH\n"
535 test_cnt=$(( test_cnt + 1 ))
537 test_item "${tmp[1]}" "${tmp[3]}" "${tmp[2]}"
539 if [[ $? != 0 ]]; then
540 err_cnt=$(( err_cnt + 1 ))
541 err_continue_cnt=$(( err_continue_cnt + 1 ))
542 if [[ $test_ignor_err != "enable" ]]; then
543 err "err interrupt ...\n"
544 ret=1
545 else
546 ret=0
547 [[ $err_continue_cnt -ge 10 ]] && ret=1
549 else
550 if [[ $err_continue_cnt != 0 ]]; then
551 err_continue_cnt=0
553 ret=0
555 if [[ -n $test_cnt_num && $test_cnt -ge $test_cnt_num ]]; then
556 ret=1
559 # curr_test_id[$saved_test_id]="${tmp[1]}"
560 # TEST_DIR_DEPTH=$(( TEST_DIR_DEPTH - 1 ))
561 # TPREFIX=$(printf "%*s" $(( $TEST_DIR_DEPTH * 4)) " ")
563 [[ $ret == 1 ]] && break
565 "module" | "unit" | "dir" )
566 if [[ -n $test_cnt_num && $test_cnt -gt $test_cnt_num ]]; then
567 ret=1
568 break
571 catalog_id_step_into TEST "${tmp[1]}"
573 TPREFIX="$(printf '%*s' $(( $TEST_DIR_DEPTH * 4)) ' ')"
575 info "$CHIGHL$FCCOLOR$TPREFIX[${tmp[0]}] ${tmp[1]}.\"${tmp[2]}\" begin testing ...$CNORMAL\n"
577 test_unit "${tmp[1]}.${tmp[3]}.${tmp[2]}.${tmp[0]}"
578 ret=$?
580 catalog_id_step_out TEST "${tmp[1]}"
582 TPREFIX=$(printf "%*s" $(( $TEST_DIR_DEPTH * 4)) " ")
583 [[ $ret != 0 ]] && break
586 break
588 esac
589 [[ $chk_ret == 3 ]] && break
590 done; echo $test_cnt $err_cnt $err_continue_cnt $TEST_CID_STATE $TEST_DIR_DEPTH ; return $ret ); return $?`
592 # while语句由于|pipe操作符,以一个sub-process运行,所以使用return和exit都只是从sub-process中退出。
593 # sub-proc中的环境变量test_cnt在return之前echo,在循环结束时echo,以stdout赋值给外部的test_cnt。
594 ret=$?
596 test_cnt=( $test_cnt )
597 err_cnt=${test_cnt[1]}
598 err_continue_cnt=${test_cnt[2]}
599 catalog_id_set_init_state TEST "${test_cnt[3]}"
601 TEST_DIR_DEPTH=${test_cnt[4]}
602 unset test_cnt[1]
603 unset test_cnt[2]
605 [[ $err_continue_cnt -ge 10 ]] && info "连续10个测试项failed,暂停测试。\n" && ret=1
607 cd ..
608 return $ret
611 readonly skip_file_pfx="="
613 # 列出指定目录下的测试内容,包括模块、单元、目录、测试项
614 # test_list_content <path>
615 test_list_curr_content ()
617 local tmp=
618 local script_name=
619 local test_desc=
621 test_list_depth=$((test_list_depth++))
623 if [[ -z $1 ]]; then
624 tmp=
625 else
626 tmp="\"$1\""
629 while read file; do
630 # 忽略.txt文件,在处理.sh文件时再进行处理
631 [[ $file =~ ".txt" ]] && continue
633 # 以小数点分隔,包含3个字符串的为测试脚本
634 # 包含3个字符串的为测试模块/单元/目录
635 # 包含5个字符串的为测试项信息描述文件
636 OLD_IFS=$IFS
637 IFS="."
638 tmp=( $file )
639 IFS=$OLD_IFS
641 # todo:添加非数字idx过滤
642 # [[ ${tmp[1]} =~ "^[:digital:]" ]] && continue
644 if [[ ${tmp[0]:0:1} == $skip_file_pfx ]]; then
645 # ignor this file with "=" pfx.
646 dbgoutd "'$file' is ignored.\n"
647 elif [[ ${#tmp[@]} == 5 ]]; then
648 test_desc=`ls -d -1 "$file.*.txt"`
649 [[ -z "$test_desc" ]] && echo "err: 123aaa txt description file not found for $file." >&2 && return 1
650 test_desc="${test_desc#$file\.}"
651 test_desc="${test_desc%\.txt}"
652 script_name=${tmp[1]}
653 elif [[ ${#tmp[@]} == 4 ]]; then
654 case ${tmp[3]} in
655 "module" )
656 echo "\"${tmp[3]}\" \"${tmp[0]}\" \"${tmp[2]}\" \"${tmp[1]}\""
658 "unit" )
659 echo "\"${tmp[3]}\" \"${tmp[0]}\" \"${tmp[2]}\" \"${tmp[1]}\""
661 "dir" )
662 echo "\"${tmp[3]}\" \"${tmp[0]}\" \"${tmp[2]}\" \"${tmp[1]}\""
664 esac
665 elif [[ ${#tmp[@]} == 3 ]]; then
666 if [[ "${tmp[2]}" == "sh" ]]; then
667 test_desc=`eval ls -d -1 "$file.*.txt"`
669 [[ -z $test_desc ]] && echo "err: txt descript file not found for $file." >&2 && return 1
670 test_desc=${test_desc#$file\.}
671 test_desc=${test_desc%\.txt}
673 echo "\"item\" \"${tmp[0]}\" \"$test_desc\" \"${tmp[1]}\""
676 done < <(eval ls -1 "$tmp" | sort -n)
678 test_list_depth=$((test_list_depth--))
683 # test_list_full_content <test_dir>
684 test_list_full_content ()
686 local tmp=
687 local test_dir=$1
689 cd "$1"
690 test_list_curr_content $test_dirs |
691 while read line; do
692 OLD_IFS=$IFS
693 IFS=' '
694 # line=${line//./
696 eval tmp=( $line )
697 IFS=$OLD_IFS
699 case ${tmp[0]} in
700 "item" )
701 test_dir_level=$(( test_dir_level + 1 ))
702 TPREFIX=`printf "%*s" $(( $test_dir_level * 4)) " "`
704 echo -ne "$TPREFIX[item] ${tmp[1]} ${tmp[3]} \"${tmp[2]}\"\n"
706 test_dir_level=$(( test_dir_level - 1 ))
707 TPREFIX=`printf "%*s" $(( $test_dir_level * 4)) " "`
710 "module" | "unit" | "dir" )
711 test_dir_level=$(( test_dir_level + 1 ))
712 TPREFIX=`printf "%*s" $(( $test_dir_level * 4)) " "`
714 echo -ne "$TPREFIX[${tmp[0]}] ${tmp[1]} ${tmp[3]} \"${tmp[2]}\"\n"
716 test_list_full_content "${tmp[1]}.${tmp[3]}.${tmp[2]}.${tmp[0]}"
718 test_dir_level=$(( test_dir_level - 1 ))
719 TPREFIX=`printf "%*s" $(( $test_dir_level * 4)) " "`
721 esac
722 done
723 cd ..
726 # replace tab with blank string.
727 TAB_BLANK_STR=" "
729 # defined in global.
730 line=
732 # node var.
733 type=
735 name=
736 desc=
738 cnt=0
739 lastcnt=0
741 # eg: shlibsrcdir
742 SRC_DIR_TYPE=
743 # output dir under srcpkg.
744 SUBDIR=
745 # output dir
746 OUTPUT_DIR=
748 STR_FMT=
749 FILE_NAME_LIST=
754 # @ catalog文件的差异输出。
755 # 根据node的名称,id-position,desc,ntype进行顺序的comp(不包含cont的node),并设置{new}/{del}{modify}的tag。同时将对应的修改的node保存到new/del/modify.tmp文件。
756 # 在new catalog文件之中,匹配org文件之中的new/del/modify的node,是否有相同/相近的node,以fullcataid的{=>}表示,并添加{mov}的tag。
757 # 对于modify的node,比对NCATAID,NTYPE,NNAME,NDESC,输出{=>}修改信息
758 # 比对imi定义,输出{=>}修改信息
760 # @ 修改
761 # # 对于node的new/del/modify/mov进行文件操作
762 # # node-info
763 # + NCATAID
764 # + NTYPE
765 # + NNAME
766 # + NDESC
767 # # on dir enter/exit
768 # # on node proc
769 # + NCATAID=>NFULLCATAID
770 # + NTYPE=>NODE_TYPE
771 # + NTYPE=>SRC_TYPE if NODE_TYPE=srcdir
772 # + NTYPE=>ITEM_TYPE if NODE_TYPE=item
773 # # ITEM_TYPE=>NFEXT
774 # + NFEXT=>DIR_TYPE=>DIR_TYPE_LIST,
775 # + NFEXT=>FILE_EXT_LIST
776 # # for one output dir,
777 # + DIR_TYPE+SRC_TYPE=>SRC_DIR_TYPE
778 # + OUTPUT_SUBDIR_FMT[$DIR_TYPE]=>
779 # OUTPUT_SUBDIR(dir under SRCPKG_DIR)
780 # + OUTPUT_${SRC_TYPE^^}_SUBDIR_FMT[$DIR_TYPE]=>
781 # OUTPUT_${SRC_TYPE^^}_SUBDIR(dir under OUTPUT_SUBDIR)
782 # + NODE_DIR_FMT[$DIR_TYPE]=>
783 # NODE_DIR(dir name if NODE_TYPE=dir,append to CATALOG_DIRS)
784 # + NODE_ITEMDIR_FMT[$DIR_TYPE]=>
785 # NODE_ITEMDIR(ITEM dir if needed/defined)
786 # + NODE_${SRC_TYPE^^}_ITEMDIR_FMT[$DIR_TYPE]=>
787 # NODE_ITEMDIR(if NODE_ITEMDIR is not exist)
788 # + NODE_DIR=>CATALOG_DIRS(if NODE_TYPE=dir)
789 # + OUTPUT_SUBDIR+
790 # OUTPUT_${SRC_TYPE^^}_SUBDIR+
791 # CATALOG_DIRS+
792 # OUTPUT_DIR_FMT[$DIR_TYPE]=>
793 # OUTPUT_DIR()
794 # # for one file in one output dir of a node。
795 # + FILE_EXT_LIST=>FEXT
796 # + FNAME_FMT[$FEXT]+
797 # NODE_ITEMDIR=>FNAME,
798 # + FNAME_REGEX[$FEXT],
799 # + NXXX_REGEX[$FEXT],
800 # + FNAME=>FNAME_LIST(used for existing chk & gen)
801 # # 参数backup,使用{=>}修改之后的再输出参数,进行org=>new的输出chk,并mv
802 # # 如果未包含修改flag,使用org的参数chk,并输出文件。
804 # @ existing chk
807 # @ cont输出
808 # 遍历node,对于包含cont的item node,解析后的信息(包含uniq-id一家文件路径)保存到设计文件。
810 # 包含cont的item node,这些node需要输出code对于。先进行chk,输出需要进行new/del/modify/mov操作的操作信息和文件列表。
816 # OUTPUT_DIR, output dir.
817 # CATA_DIR, catalog in dir.
818 # NODE_DIR, dir for node if needed.
820 # NFULLCATAID, $(echo ${NCATAID[@]} | tr ' ' '.')
821 # NODE_TYPE: srcdir/ignrdir/dir/item/cont, default is srcdir.
822 # SRC_TYPE, if NODE_TYPE == srcdir, it is bin/shlib/lib/src/example.
823 # ITEM_TYPE, if NODE_TYPE == item, it is equal to NTYPE. it's used to get item cfg-info.
825 # @ node proc
826 # DIR_TYPE_LIST, output dirs for current node.
827 # FILE_NAME_LIST[]/NODE_FILES[], file list in one node under one kind of output dir.
828 # @ one output dir in a node.
829 # DIR_TYPE, it's used in defination of NFTYPE. srcdir/docdir/designdir/testdir.
830 # SRC_DIR_TYPE, src dir name for defination in imi (catalog::${SRC_DIR_TYPE}::).
831 # "$SRC_TYPE$DIR_TYPE"
832 # SUBDIR, current SRC_DIR_TYPE output dir prefix.
833 # DIR_NAME,
837 # G_OUTPUT_SUBDIR[docdir]="doc/UM.md.zh_CN"
838 # G_OUTPUT_SUBDIR[designdir]="doc/design"
839 # G_OUTPUT_SUBDIR[testdir]="testing"
840 # G_OUTPUT_SUBDIR[srcdir]="src" # lib/bin/shlib
842 # G_NODE_DIR_FMT[]='@{NCATAID}.@{NNAME}'
843 # G_OUTPUT_DIR_FMT[]='@{SRCPKG_DIR}/@{SUBDIR}/@{LAYEDDIR}/@(NODEDIR)'
844 # G_FILE_NAME_FMT[]='@{NFULLCATAID}.@{NNAME}.@{NFEXT}.md'
846 # G_FILE_EXT_LIST[], generated in various item node.
849 InitInfo ()
851 # NTYPE => NFTYPE
852 while read line; do
853 $IFS_OLD=$IFS
854 IFS='/'
855 DIR_TYPE_LIST="${line%::*}"$'\n'
856 FILE_EXT_LIST=( ${line#*::} )
857 for DIR_TYPE in ${line%::*}; do
858 SRC_DIR_TYPE=${SRC_TYPE}${DIR_TYPE}
859 SUBDIR="$(attr_get catalog::${DIR_TYPE}::SUBDIR)"
860 OUTPUT_DIR_FMT="$(attr_get catalog::${DIR_TYPE}::OUTPUT_DIR_FMT)"
861 STR_FMT="$(attr_get catalog::${DIR_TYPE}::STR_FMT)"
862 strfmt @STR_FMT
863 DIR_NAME="${STR}"
864 for ext in $FILE_EXT_LIST; do
865 STR_FMT="$(attr_get catalog::$ext::STR_FMT)"
866 strfmt @STR_FMT
867 FILE_NAME_LIST[$DIR_TYPE]+="touch $(STR)"$'\n'
868 done
869 done
870 done <<< "$NFTYPE"
874 # get info from $NCATAID $NTYPE $NNAME $NDESC
876 GetNodeExtInfo ()
878 local FILE_EXT_LIST=
879 local ext=
880 local DIR_TYPE_LIST=
882 # NTYPE => node-type, src-type
883 # dir/item/cont
884 if [[ "$NTYPE" =~ dir$ ]]; then
885 # NULL/src/lib/bin/shlib/ex
886 SRC_TYPE="${NTYPE%dir}"
887 NODE_TYPE="dir"
889 [[ "$NTYPE" == 'ignr' ]] && retur 1
890 elif [[ "$NTYPE" =~ ^cont ]]; then
891 # keep SRC_TYPE processed before.
892 NODE_TYPE="cont"
893 else
895 NODE_TYPE="${NTYPE}"
898 # NTYPE => NFTYPE
899 while read line; do
900 $IFS_OLD=$IFS
901 IFS='/'
902 DIR_TYPE_LIST="${line%::*}"$'\n'
903 FILE_EXT_LIST=( ${line#*::} )
904 for DIR_TYPE in ${line%::*}; do
905 SRC_DIR_TYPE=${SRC_TYPE}${DIR_TYPE}
906 SUBDIR="$(attr_get catalog::designdir::SUBDIR)"
907 OUTPUT_DIR_FMT="$(attr_get catalog::designdir::OUTPUT_DIR_FMT)"
908 STR_FMT="$(attr_get catalog::designdir::STR_FMT)"
909 strfmt @STR_FMT
910 DIR_NAME="${STR}"
911 for ext in $FILE_EXT_LIST; do
912 STR_FMT="$(attr_get catalog::$ext::STR_FMT)"
913 strfmt @STR_FMT
914 FILE_NAME_LIST[$DIR_TYPE]+="touch $(STR)"$'\n'
915 done
916 done
917 done <<< "$NFTYPE"
920 for DIR_TYPE in $DIR_TYPE_LIST; do
921 # switch dir content.
922 ${OUTPUT_DIR[$DIR_TYPE]}
923 ${CATA_DIR}=""
924 ${NODE_DIR}=""
925 ${NODE_FILES}=""
927 # check file
929 # output file.
930 eval "${FILE_NAME_LIST[$DIR_TYPE]}"
931 done
933 # $NCATAID
934 # $NTYPE
935 # $NNAME
936 # $NDESC
942 GetNodeOutputFileList ()
947 CreateOutputFileInList ()
952 CheckNodeInDir ()
959 OnDirEnterProc ()
961 if [[ -n $lastdir ]]; then
962 [[ ! -d $lastdir ]] && info "create dir ($lastdir)\n" && mkdir -p "$lastdir"
963 cd "$lastdir"
964 # info "cd $lastdir\n"
968 OnDirExitProc ()
970 local i=
972 if [[ "${lastdir##*.}" =~ "txt" ]]; then
973 # [[ -n $lastdir ]] && info "item $lastdir\n"
974 [[ ! -e "$lastdir" ]] && info "create item ($lastdir)\n" && touch "$lastdir" && touch "${lastdir%.sh*}.sh"
975 else
976 [[ ! -d "$lastdir" ]] && info "create dir ($lastdir)\n" && mkdir -p "$lastdir"
979 for (( i=cnt/tabcnt; i < lastcnt/tabcnt; i++ )); do
980 cd ..
981 done
982 # info "cd ..\n"
985 OnItemProc ()
987 if [[ "${lastdir##*.}" =~ "txt" ]]; then
988 # [[ -n $lastdir ]] && info "item $lastdir\n"
989 [[ ! -e "$lastdir" ]] && info "create item ($lastdir)\n" && touch "$lastdir" && touch "${lastdir%.sh*}.sh"
990 else
991 [[ ! -d "$lastdir" ]] && info "create dir ($lastdir)\n" && mkdir -p "$lastdir"
996 # design file analyzing.
999 Item2Files ()
1001 local i=
1003 GetNodeExtInfo
1005 #CheckNodeInDir
1006 #GetNodeOutputFileList
1007 #CreateOutputFileInList
1009 # 根据缩进字符数判断所需创建的目录结构
1010 if [[ $cnt -gt $lastcnt ]]; then
1011 OnDirEnterProc
1012 elif [[ $cnt -eq $lastcnt ]]; then
1013 OnItemProc
1014 else
1015 OnDirExitProc
1018 # NODE_TYPE
1019 # case $NODE_TYPE in
1020 # "dir" )
1021 # lastdir="$id.$name.$desc.$type"
1022 # ;;
1023 # "item" )
1024 # lastdir="$id.$name.sh.$desc.txt"
1025 # ;;
1026 # "cont" )
1027 # ;;
1028 # * )
1029 # warn "unkown type [$type] ($line)\n"
1030 # ;;
1031 # esac
1033 case $type in
1034 "module" | "unit" | "dir" )
1035 # printf "%*s" $((tab*4)) " "
1036 # info "$id.$name.$type\n"
1037 lastdir="$id.$name.$desc.$type"
1039 "item" )
1040 # printf "%*s" $((tab*4)) " "
1041 # info "$id.$name.sh.$desc.txt\n"
1043 lastdir="$id.$name.sh.$desc.txt"
1046 warn "unkown type [$type] ($line)\n"
1048 esac
1051 bak_proc ()
1053 # 字符串冗余符号处理
1054 line=`echo $line | tr '.' ' ' | tr -s "[\t| ]"`
1055 IFS=' '
1056 # TBD: this operation should use str2var() to get variables.
1057 line=( $line )
1058 IFS=$'\n'
1060 NTYPE=${line[0]}
1061 NCATAID=${line[1]}
1062 NNAME=${line[2]}
1063 NDESC=${line[3]}
1065 NTYPE=${type:1:-1}
1066 # NTYPE=${NTYPE%]*}
1068 TEST_STR_CATANODE_SRCDIR_1='<1> [srcdir] TestDir1 "test dir 1"'
1069 TEST_STR_CATANODE_SRCDIR_1_FMT='<@{NCATAID}> [@{NTYPE}] @{NNAME} "@{NDESC}"'
1070 TEST_STR_CATANODE_SRCDIR_1_VAR='NCATAID=1; NTYPE=srcdir; NNAME=TestDir1; NDESC=test dir 1; '
1073 # design file analyzing.
1076 OnCataNodeProc ()
1078 local origin=
1079 local dest=
1081 # get data in var by node format
1082 STR_FMT="$(attr_get catalog::catanode::STR_FMT)"
1083 echo STR_FMT="$STR_FMT"
1084 echo data="$line"
1085 PAIRCHARS='<>[]""'
1086 str2var @line
1088 # PAIRCHARS='<>[]""'
1089 # STR_FMT="${TEST_STR_CATANODE_SRCDIR_1_FMT}"
1090 # str2var @TEST_STR_CATANODE_SRCDIR_1
1092 # change info
1093 # if [[ $NCATAID =~ .*[\{](.*)=>(.*)[\}].* ]]; then
1094 # NCATAID_OLD=""
1095 # fi
1097 declare -p NTYPE NCATAID NNAME NDESC
1099 catalog_id_get CATALOG
1101 # 根据缩进字符数判断所需创建的目录结构
1102 if [[ $cnt -gt $lastcnt ]]; then
1103 catalog_id_step_into CATALOG "$id"
1104 OnDirEnterProc
1105 elif [[ $cnt -eq $lastcnt ]]; then
1106 catalog_id_step_to CATALOG "$id"
1107 OnItemProc
1108 else
1109 OnDirExitProc
1110 catalog_id_step_out CATALOG "$id"
1113 Item2Files
1116 OnEndofCataNodeProc ()
1118 # last配置项
1119 if [[ $cnt -gt $lastcnt ]]; then
1120 if [[ -n "$lastdir" ]]; then
1121 [[ ! -d "$lastdir" ]] && info "create dir ($lastdir)\n" && mkdir -p "$lastdir"
1122 cd "$lastdir"
1123 # info "cd $lastdir\n"
1125 elif [[ $cnt -eq $lastcnt ]]; then
1126 if [[ "${lastdir##*.}" =~ "txt" ]]; then
1127 [[ ! -e "$lastdir" ]] && info "create item ($lastdir)\n" && touch "$lastdir" && touch "${lastdir%.sh*}.sh"
1128 else
1129 [[ ! -d "$lastdir" ]] && info "create dir ($lastdir)\n" && mkdir -p "$lastdir"
1131 else
1132 if [[ "${lastdir##*.}" =~ "txt" ]]; then
1133 [[ ! -e "$lastdir" ]] && info "create item ($lastdir)\n" && touch "$lastdir" && touch "${lastdir%.sh*}.sh"
1134 else
1135 [[ ! -d "$lastdir" ]] && info "create dir ($lastdir)\n" && mkdir -p "$lastdir"
1137 for (( i=cnt/tabcnt; i < lastcnt/tabcnt; i++ )); do
1138 cd ..
1139 done
1140 # info "cd ..\n"
1145 # design file analyzing.
1148 catalog_each_line ()
1150 local tmp=
1152 local tabcnt= # blank count equals to a tab.
1153 local tabstr= # a string equals to a tab.
1154 local tab= # number of tabs in line post-stage.
1156 while read -r line; do
1157 [[ $verbose == "enable" ]] && info "line=$line\n"
1158 [[ ! $line =~ "[" || ${line:0:1} =~ ^[[:blank:]]*\# ]] && continue
1161 # get tab and blank count as catalog level number.
1163 lastcnt=${cnt}
1164 IFS=' '
1165 cnt=${line%%[*}
1166 cnt=${cnt//[^ ]/}
1167 eval "cnt=\"\${cnt//$'\t'/$TAB_BLANK_STR}\""
1168 # cnt=`echo "$cnt" | wc -c`
1169 cnt="${#cnt}"
1170 cnt=$(( cnt-1 ))
1171 lastcnt=${lastcnt:-"$cnt"}
1173 # value in the first line to be the size unit in current file.
1174 [[ -z $tabcnt ]] && tabcnt=$(( cnt-1 )) && tabstr=`printf "%*s" $((tab*tabcnt)) " "`
1175 [[ $tabcnt == 0 ]] && tabcnt=4 && tabstr=`printf "%*s" $((tab*4)) " "`
1176 tab=$(( cnt/${tabcnt:-'4'} ))
1178 # get full cataid paramter.
1179 # TBD:
1181 # analyze string to node variable.
1182 # NCATAID, NTYPE, NNAME, NDESC
1183 OnCataNodeProc
1184 done < $1
1185 OnEndofCataNodeProc
1188 testcase_framework_gen_new ()
1190 local tmp=$1
1191 local i
1192 local xxx
1194 cd "$test_path"
1196 cataid_create CATALOG
1197 catalog_id_set_init_state CATALOG "inner"
1199 # attr_create catalog::catanode::STR_FMT='<@{NCATAID}>@{ALIAN}[@{NTYPE}]@{ALIAN}@{NNAME}@{ALIAN}[@{NDESC}]'
1200 attr_create catalog::catanode::STR_FMT='<@{NCATAID}>@{ALIAN}[@{NTYPE}]@{ALIAN}@{NNAME}@{ALIAN}[@{NDESC}]'
1202 tmp=${tmp:-"funclist.txt"}
1203 if [[ ! -e $tmp ]]; then
1204 err "function list file '$tmp' is not exist.\n"
1205 exit
1208 cp "$tmp" "$TESTING_TMP_DIR/"
1209 tmp=$TESTING_TMP_DIR/`basename $tmp`
1210 dos2unix $tmp
1212 # analyze catalog file.
1213 catalog_each_line "$tmp"
1217 # 描述文件中第一个有效行的空格数或tab数为缩进字符串的单位,用于计算目录层数。
1218 # tabh空格不能混用。
1220 testcase_framework_gen ()
1222 local type=
1223 local id=
1224 local name=
1225 local desc=
1226 local tmp=$1
1227 local lastcnt=0
1228 local cnt=0
1229 local tab
1230 local tabcnt
1231 local i
1232 local xxx
1233 local tabstr
1235 # 使用' '时 read读取数据,' '用于分隔。
1236 IFS_OLD=$IFS
1237 IFS=$'\n'
1239 cd "$test_path"
1241 tmp=${tmp:-"funclist.txt"}
1242 if [[ ! -e $tmp ]]; then
1243 err "function list file '$tmp' is not exist.\n"
1244 exit
1247 cp "$tmp" "$TESTING_TMP_DIR/"
1248 tmp=$TESTING_TMP_DIR/`basename $tmp`
1249 dos2unix $tmp
1250 while read -r line; do
1251 [[ $verbose == "enable" ]] && info "line=$line\n"
1252 [[ ! $line =~ "[" || ${line:0:1} == '#' ]] && continue
1254 # 使用第一个有效行的空格数为tabcnt
1255 IFS=' '
1256 tmp=${line%%[*}
1257 tmp=${tmp//[^ ]/}
1259 tmp=`echo "$tmp" | wc -c`
1261 lastcnt=${cnt:-"$((tmp-1))"}
1262 cnt=$(( tmp-1 ))
1263 [[ -z $tabcnt ]] && tabcnt=$(( tmp-1 )) && tabstr=`printf "%*s" $((tab*tabcnt)) " "`
1264 [[ $tabcnt == 0 ]] && tabcnt=4 && tabstr=`printf "%*s" $((tab*4)) " "`
1265 tab=$(( cnt/${tabcnt:-'4'} ))
1267 # 字符串冗余符号处理
1268 line=`echo $line | tr '.' ' ' | tr -s "[\t| ]"`
1269 IFS=' '
1270 line=( $line )
1271 IFS=$'\n'
1273 type=${line[0]}
1274 id=${line[1]}
1275 name=${line[2]}
1276 desc=${line[3]}
1278 type=${type:1}
1279 type=${type%]*}
1281 # 根据缩进字符数判断所需创建的目录结构
1282 if [[ $cnt -gt $lastcnt ]]; then
1283 if [[ -n $lastdir ]]; then
1284 [[ ! -d $lastdir ]] && info "create dir ($lastdir)\n" && mkdir -p "$lastdir"
1285 cd "$lastdir"
1286 # info "cd $lastdir\n"
1288 elif [[ $cnt -eq $lastcnt ]]; then
1289 if [[ "${lastdir##*.}" =~ "txt" ]]; then
1290 # [[ -n $lastdir ]] && info "item $lastdir\n"
1291 [[ ! -e "$lastdir" ]] && info "create item ($lastdir)\n" && touch "$lastdir" && touch "${lastdir%.sh*}.sh"
1292 else
1293 [[ ! -d "$lastdir" ]] && info "create dir ($lastdir)\n" && mkdir -p "$lastdir"
1295 else
1296 if [[ "${lastdir##*.}" =~ "txt" ]]; then
1297 # [[ -n $lastdir ]] && info "item $lastdir\n"
1298 [[ ! -e "$lastdir" ]] && info "create item ($lastdir)\n" && touch "$lastdir" && touch "${lastdir%.sh*}.sh"
1299 else
1300 [[ ! -d "$lastdir" ]] && info "create dir ($lastdir)\n" && mkdir -p "$lastdir"
1302 for (( i=cnt/tabcnt; i < lastcnt/tabcnt; i++ )); do
1303 cd ..
1304 done
1305 # info "cd ..\n"
1308 case $type in
1309 "module" | "unit" | "dir" )
1310 # printf "%*s" $((tab*4)) " "
1311 # info "$id.$name.$type\n"
1312 lastdir="$id.$name.$desc.$type"
1314 "item" )
1315 # printf "%*s" $((tab*4)) " "
1316 # info "$id.$name.sh.$desc.txt\n"
1318 lastdir="$id.$name.sh.$desc.txt"
1321 warn "unkown type [$type] ($line)\n"
1323 esac
1325 done < $tmp
1327 # last配置项
1328 if [[ $cnt -gt $lastcnt ]]; then
1329 if [[ -n "$lastdir" ]]; then
1330 [[ ! -d "$lastdir" ]] && info "create dir ($lastdir)\n" && mkdir -p "$lastdir"
1331 cd "$lastdir"
1332 # info "cd $lastdir\n"
1334 elif [[ $cnt -eq $lastcnt ]]; then
1335 if [[ "${lastdir##*.}" =~ "txt" ]]; then
1336 [[ ! -e "$lastdir" ]] && info "create item ($lastdir)\n" && touch "$lastdir" && touch "${lastdir%.sh*}.sh"
1337 else
1338 [[ ! -d "$lastdir" ]] && info "create dir ($lastdir)\n" && mkdir -p "$lastdir"
1340 else
1341 if [[ "${lastdir##*.}" =~ "txt" ]]; then
1342 [[ ! -e "$lastdir" ]] && info "create item ($lastdir)\n" && touch "$lastdir" && touch "${lastdir%.sh*}.sh"
1343 else
1344 [[ ! -d "$lastdir" ]] && info "create dir ($lastdir)\n" && mkdir -p "$lastdir"
1346 for (( i=cnt/tabcnt; i < lastcnt/tabcnt; i++ )); do
1347 cd ..
1348 done
1349 # info "cd ..\n"
1352 IFS=$OLD_IFS
1356 # fsyntax: testing_paths_init [<base-tmp-dir>]
1357 # fdesc:
1358 testing_paths_init ()
1361 # 测试目录使用-d参数对应的test_dir环境变量
1362 # 指定的目录作为测试信息目录。
1363 # 默认为当前路径下的testing目录。
1365 if [[ -n $test_dir ]]; then
1366 if [[ -d $test_dir ]]; then
1367 test_path=$test_dir
1368 else
1369 err "err: parameter -d specified a invalid path $test_dir\n"
1370 exit
1374 if [[ ! -d $test_path ]]; then
1375 test_path=testing/
1378 [[ ! -d $test_path ]] && err "err: testcase dir is not exist.\n" && exit
1380 mkdir -p "$test_path"
1381 cd "$test_path"
1382 test_path=$PWD
1383 cd - > /dev/null
1386 # 测试的tmp目录设置
1387 # 目录名称添加testcase路径的cksum,避免同一个软件包不同目录时,
1388 # 使用同一个tmp目录。
1390 TESTING_TMP_DIR=$(basename $0)-$TESTING_TMP_DIR
1391 if [[ -n $1 ]]; then
1392 TESTING_TMP_DIR="$1/"
1393 [[ -d $1 ]] && TESTING_TMP_DIR=~/.testing/ && warn "directoy specified \"$1\" is not existing, use default directrory \"$TESTING_TMP_DIR\" instead.\n"
1394 else
1395 TESTING_TMP_DIR=~/.testing/
1398 mkdir -p "$TESTING_TMP_DIR"
1399 cd "$TESTING_TMP_DIR"
1400 TESTING_TMP_DIR=$PWD
1401 cd - > /dev/null
1403 TESTING_TMP_DIR=$TESTING_TMP_DIR/$(basename $0)-$(echo $test_path | cksum - | cut -d ' ' -f1)
1405 mkdir -p "$TESTING_TMP_DIR"
1409 # fsyntax: testing_catalog_id_init
1410 # fdesc: range id init is a general feature programs. this function can be used in
1411 # other programs.
1413 testing_catalog_id_init ()
1415 local tmp
1417 cataid_create TEST
1419 # load保存的id参数
1420 local tmp_exclude_id="$exclude_id"
1421 local tmp_begin_test_id="$begin_test_id"
1422 local tmp_end_test_id="$end_test_id"
1424 [[ -e $TESTING_TMP_DIR/test_id.txt ]] && source $TESTING_TMP_DIR/test_id.txt
1425 [[ -n $tmp_exclude_id && $tmp_exclude_id =~ [^\ ] ]] && exclude_id="$tmp_exclude_id"
1426 [[ -n $tmp_begin_test_id ]] && begin_test_id="$tmp_begin_test_id"
1427 [[ -n $tmp_end_test_id ]] && end_test_id="$tmp_end_test_id"
1428 unset tmp_exclude_id
1429 unset tmp_begin_test_id
1430 unset tmp_end_test_id
1433 # 测试id起止范围和状态的初始化
1434 # 以testing目录中保存的起止id、参数设置、failed测试项的
1435 # 顺序设置id值。
1437 if [[ $failed_begin == "enable" && -f $TESTING_TMP_DIR/testing_failed_list.txt ]]; then
1438 tmp=$(head -n 1 $TESTING_TMP_DIR/testing_failed_list.txt)
1439 [[ -n $tmp ]] && begin_test_id=$tmp
1442 if [[ $test_all == "enable" ]]; then
1443 exclude_id=""
1444 test_cnt_num=
1445 # test_ignor_err="enable"
1446 else
1447 exclude_id=$(echo "${exclude_id}" | tr ' ' '\n' | sort - | uniq)
1448 exclude_id=" $(echo "${exclude_id}" | tr "$'\n'" ' ') "
1451 # inner
1452 catalog_id_set_init_state TEST "inner"
1453 if [[ -n $end_test_id ]]; then
1454 catalog_id_set_end_id TEST end_test_id
1457 if [[ -n $begin_test_id ]]; then
1458 catalog_id_set_begin_id TEST begin_test_id
1460 # 如果有begin-id,默认起始状态为 lower
1461 catalog_id_set_init_state TEST "lower"
1464 # dbgoutd "TEST_CID_STATE=${TEST_CID_STATE[@]}\n"
1465 # dbgoutd "TEST_CID_BEGIN=${TEST_CID_BEGIN[@]}\n"
1466 # dbgoutd "TEST_CID_END=${TEST_CID_END[@]}\n"
1468 if [[ $test_all == "enable" ]]; then
1469 begin_test_id=""
1470 end_test_id=""
1472 catalog_id_set_begin_id TEST begin_test_id
1473 catalog_id_set_end_id TEST end_test_id
1475 # 如果有begin-id,默认起始状态为 lower
1476 catalog_id_set_init_state TEST "inner"
1479 # id起止范围有效性判断
1480 catalog_id_begin_end_chk TEST
1482 # 测试起始范围递增1
1483 if [[ $test_id_rollup == "enable" ]]; then
1484 [[ -z $begin_test_id ]] && echo "请使用-b参数指定起始测试编号" && exit
1486 catalog_id_rollup_in_dir TEST begin_test_id $test_path
1487 ret=$?
1488 [[ $ret != 0 ]] && exit
1490 begin_test_id=${TEST_CID_BEGIN[@]}
1491 begin_test_id=${begin_test_id// /.}
1492 set_test_info="enable"
1495 # 测试起始范围回退1
1496 if [[ $test_id_rollback == "enable" ]]; then
1497 [[ -z $begin_test_id ]] && echo "请使用-b参数指定起始测试编号" && exit
1499 catalog_id_rollback_in_dir TEST begin_test_id $test_path
1500 [[ $? != 0 ]] && exit
1502 begin_test_id=${TEST_CID_BEGIN[@]}
1503 begin_test_id=${begin_test_id// /.}
1504 set_test_info="enable"
1507 # 保存-b -e参数指定的id
1508 if [[ $set_test_info == "enable" ]]; then
1509 [[ -z $begin_test_id && -z $end_test_id && -z $exclude_id && ! $exclude_id =~ [^\ ] ]] && echo "请使用-x-b-e参数指定起止测试编号和exclude-id" && exit
1511 info "begin test_id: ${begin_test_id[@]}\n"
1512 info "end test_id: ${end_test_id[@]}\n"
1513 info "exclude test_id: '$exclude_id'\n"
1515 # 保存参数
1516 mkdir -p "$TESTING_TMP_DIR"
1517 echo "# [paramters]" > $TESTING_TMP_DIR/test_id.txt
1518 [[ -n $begin_test_id ]] && echo "begin_test_id=${begin_test_id}" >> $TESTING_TMP_DIR/test_id.txt
1519 [[ -n $end_test_id ]] && echo "end_test_id=${end_test_id}" >> $TESTING_TMP_DIR/test_id.txt
1520 [[ -n $exclude_id && $exclude_id =~ [^\ ] ]] && echo "exclude_id='`echo ${exclude_id}`'" >> $TESTING_TMP_DIR/test_id.txt
1522 info "test id saved!\n"
1524 # 测试id保存时,不进行测试,便于testcase中测试起止id编号
1525 exit
1528 # dispaly test id range.
1529 if [[ -n ${begin_test_id[@]} || -n ${end_test_id[@]} ]]; then
1530 info "Test from <${begin_test_id[@]}> to <${end_test_id[@]}>.\n"
1531 [[ -n $exclude_id ]] && info "Except: ${exclude_id}\n"
1532 else
1533 info "Full item testing.\n"
1534 [[ $exclude_id =~ [^\ ] ]] && info "Except: ${exclude_id}\n"
1536 [[ -n ${test_cnt_num} ]] && info "Test item count: ${test_cnt_num}\n"
1539 test_result_info ()
1541 if [[ $err_cnt != 0 ]]; then
1542 info "$EFCCOLOR($FCGREEN$test_cnt$EFCCOLOR) items is tested,Error count is ($FCGREEN$err_cnt$EFCCOLOR)$CNORMAL\n"
1543 else
1544 info "$FCGREEN($test_cnt) items is tested,Error count is ($err_cnt)$CNORMAL\n"
1549 # option action function list.
1555 args_list_item ()
1557 # Init Testing Path
1558 testing_paths_init
1560 if [[ $test_force == "enable" ]]; then
1561 test_list_full_content $test_path
1562 else
1563 test_list_full_content $test_path | more
1566 exit
1571 args_save_catalog ()
1573 info "generate catalog from testing dir!\n"
1575 [[ -z $1 ]] && return
1577 test_force="enable"
1578 args_list_item > $1
1580 exit
1584 # 描述文件中第一个有效行的空格数或tab数为缩进字符串的单位,用于计算目录层数。
1585 # tabh空格不能混用。
1587 args_sync_to_dir ()
1589 local tmp="$1"
1590 local path
1592 info "generate testing dir framework from catalog!\n"
1594 [[ -z $tmp || ! -f $tmp ]] && tmp=$3
1596 # 初始化测试目录
1597 testing_paths_init
1599 [[ ! -f $tmp ]] && tmp="$test_path/funclist.txt"
1600 [[ ! -f $tmp ]] && err "err: file ($tmp) does not exist.\n" && exit
1602 path=$(basename $tmp)
1603 tmp=$(dirname $tmp)
1604 cd "$tmp"
1605 path=$(pwd)/$path
1606 cd - 2>/dev/null
1609 # todo:
1610 # 比较已有的testcase目录与func_list,对差异部分进行更新。
1612 testcase_framework_gen_new $path
1614 exit
1618 # todo:-F参数的-n未添加,以及err_cnt信息
1619 args_failed_test ()
1621 local test_id
1622 local id
1623 local i
1624 local cnt
1625 local tmp
1626 local curr_path=$PWD
1627 local ret
1629 catalog_id_set_begin_id TEST ""
1630 catalog_id_set_end_id TEST ""
1631 # begin_test_id=""
1632 # end_test_id=""
1634 # 初始化测试目录
1635 testing_paths_init
1636 catalog_id_init TEST
1638 info "failed item testing!\n"
1640 # dbgout_cmd cat $TESTING_TMP_DIR/testing_failed_list.txt
1642 [[ ! -f $TESTING_TMP_DIR/testing_failed_list.txt ]] && warn "testing failed recode is not exist.\n" && exit
1644 while read test_id; do
1645 [[ -z $test_id ]] && continue
1646 args_test_id $test_id "continue"
1647 ret=$?
1648 if [[ $test_ignor_err != "enable" ]]; then
1649 err "err interrupt ...\n"
1650 break
1652 continue
1653 done < $TESTING_TMP_DIR/testing_failed_list.txt
1655 if [[ -e $TESTING_TMP_DIR/testing_failed_list.txt ]]; then
1656 cat $TESTING_TMP_DIR/testing_failed_list.txt | sort -n | uniq > ~/testing_failed_list.txt
1657 mv ~/testing_failed_list.txt $TESTING_TMP_DIR/testing_failed_list.txt
1660 exit
1666 args_all ()
1668 declare -g -x test_all="enable"
1669 declare -g -x test_ignor_err="enable"
1670 declare -g -x quiet="enable"
1675 args_test_id ()
1677 local test_id
1678 local id
1679 local i
1680 local cnt
1681 local tmp
1682 local curr_path=$PWD
1683 local ret
1685 catalog_id_set_begin_id TEST ""
1686 catalog_id_set_end_id TEST ""
1687 # begin_test_id=""
1688 # end_test_id=""
1690 # 初始化测试目录
1691 testing_paths_init
1692 catalog_id_init TEST
1694 test_id=$1
1695 [[ ! ( -n $2 && "$2" == "continue" ) ]] && info "test specified item($test_id).!\n"
1697 [[ "$1" =~ [^0-9.] ]] && test_id=$3
1698 [[ "$test_id" =~ [^0-9.] ]] && err "test id($test_id) is not correct.\n" && exit
1700 OLD_IFS=$IFS
1701 IFS="."
1702 test_id=( $test_id )
1703 cnt=${#test_id[@]}
1704 IFS=$OLD_IFS
1706 cd "$test_path"
1707 IFS=$'\n'
1708 for (( i=0; i<cnt; i++ )); do
1709 [[ -z ${test_id[$i]} ]] && err "err: test_id[$i] does not invalid.\n" && exit
1710 id=${test_id[$i]}
1711 tmp="`find $id.* -maxdepth 1 -type d 2>/dev/null`"
1712 tmp=( $tmp )
1714 if [[ -d $tmp ]]; then
1715 cd "$tmp"
1716 tmp=""
1717 catalog_id_step_into TEST "$id"
1718 continue
1720 tmp=( `ls -1 "$id".*.sh 2>/dev/null` )
1722 [[ -e ${tmp[0]} ]] && break
1723 warn "test id '$1' is not a valid id.\n"
1724 exit
1725 done
1726 IFS=$OLD_IFS
1728 # use the script code to retest for loop back testing.
1729 if [[ $i == $cnt ]]; then
1730 test_unit .
1731 ret=$?
1732 test_result_info
1734 [[ -z $2 || "$2" == "continue" ]] && exit
1736 return $ret
1739 # todo:这里可以改为测试一个unit
1740 [[ -z $tmp ]] && err "err: test id ($1) does not specify a valide test item file.\n" && exit
1742 tmp=`ls -d "$id".*.txt`
1743 [[ -z $tmp ]] && err "err: test id ($i) does not contain a corresponding txt description file.\n" && exit
1745 OLD_IFS=$IFS
1746 IFS="."
1747 tmp=( $tmp )
1748 IFS=$OLD_IFS
1750 catalog_id_step_to TEST "${tmp[0]}"
1752 test_item "${tmp[0]}" "${tmp[1]}" "${tmp[3]}"
1753 ret=$?
1755 [[ ! ( -n $2 && "$2" == "continue" ) ]] && exit
1757 return $ret
1760 args_clean ()
1762 info "clean testing temp dir!\n"
1767 args_exclude ()
1769 exclude_id+=" $1"
1773 # 测试运脚本输出信息的显示。
1774 args_verbose ()
1776 IFCCOLOR="$FCBLUE"
1780 # 输出程序中的变量信息。
1782 args_print_vars ()
1784 info "printf vars in scripttest!\n"
1786 OptDescParamPrint
1788 exit
1792 # 输出信息单色显示。
1793 # args_mono
1794 args_mono ()
1796 FCCOLOR=""
1797 IFCCOLOR=""
1798 EFCCOLOR=""
1802 # 显示版本信息。
1803 # args_version
1804 args_version ()
1806 echo "$PROG_VERSION"
1808 exit
1813 args_h_info ()
1815 echo -ne "Options:\n"
1816 opt_helper
1817 echo -ne "\nuse '$PROG --help' for more details.\n"
1818 exit
1822 # args_help_info
1823 args_help_info ()
1825 [[ -z $term_width ]] && term_width=80
1827 echo -ne "${PROG_SYNTAX}\n\n"
1828 echo -ne "$PROG_BANNER\n"
1829 echo -ne "$PROG_DESC\n" | fold -b -s -w $term_width - 2>/dev/null
1830 echo -ne "\nOptions:\n"
1831 opt_helper
1832 echo -ne "$PROG_OTHER_DESC"
1834 exit
1838 # 显示调试信息,这里显示desc-str解析和参数解析后的环境变量信息。
1840 args_test_debug ()
1846 # main function
1849 main ()
1852 # program arguments resolve.
1853 # it invoke opt_desc_str_dispatch and prog_opt_proc seperatly,
1854 # instead of prog_opt_dispatch, for multiple desc-str.
1855 # 测试起止范围id的初始化
1856 # 初始化测试的tmp目录路径,设置testcase的目录
1858 # prog_opt_dispatch "$@"
1859 opt_desc_str_dispatch scripttest_desc_str
1860 opt_desc_str_dispatch another_desc_str
1861 prog_opt_proc "$@"
1863 # todo:
1864 # ini文件加载环境变量
1866 init_dbglogout 2 testing 20000
1867 # set_output_prefix info ""
1868 set_auto_newline ""
1871 # running action list function for options after init.
1872 # the paramter for init will be effact, then execute the
1873 # action function.
1875 action_list_exec
1877 testing_paths_init $output_dir
1878 if [[ $clean == "enable" ]]; then
1879 info "re-init tmp dir.\n"
1880 rm "$TESTING_TMP_DIR" -rf
1881 exit
1884 # 使用测试目录中的begin-id和end-id
1885 catalog_id_init TEST
1886 testing_catalog_id_init
1889 # test the specified dir.
1891 info "begin testing ... \n"
1892 test_unit "$test_path"
1893 if [[ -e $TESTING_TMP_DIR/testing_failed_list.txt ]]; then
1894 cat $TESTING_TMP_DIR/testing_failed_list.txt | sort -n | uniq > ~/testing_failed_list.txt
1895 mv ~/testing_failed_list.txt $TESTING_TMP_DIR/testing_failed_list.txt
1897 info "end testing ...\n"
1900 # testing result info
1902 test_result_info
1905 ##############################
1906 # section: public function
1907 ##############################
1911 # fdesc: the wrap of main(), and it append init code of some feature.
1913 ExeMain ()
1915 if [[ "$@" =~ "--debug" ]]; then
1916 declare -g DBGOUTD_OUTPUT=1
1918 # does not need to shift args if the --debug option is not the
1919 # first option. it will be ignore in process.
1920 # shift
1923 if [[ $1 != "--loadshlib" ]]; then
1924 main "$@"
1925 else
1926 shift
1930 ExeMain "$@"
1931 unset DBGOUTD_OUTPUT
1933 ##############################
1934 # section: file tail
1935 ##############################
1938 # 遍历每个目录及子目录
1939 foreach_dir ()
1941 local item_proc=$2
1943 [[ -z $1 || -z $z || ! -f $1 ]] && err "err: file ($1) does not exist.\n" && exit
1945 test_list_depth=$((test_list_depth++))
1947 # dbgoutd "curr content $1\n"
1949 while read file; do
1950 # 忽略.txt文件,在处理.sh文件时再进行处理
1951 [[ ! -d $file ]] && continue
1953 $item_proc $file
1955 cd "$file"
1956 foreach_dir $PWD/$file $item_proc
1957 done < <(ls "$1" | sort -n)
1959 test_list_depth=$((test_list_depth--))
1964 # 遍历每个目录及子目录下的文件
1965 foreach_file ()
1967 local item_proc=$2
1969 [[ -z $1 || -z $z || ! -f $1 ]] && err "err: file ($1) does not exist.\n" && exit
1971 test_list_depth=$((test_list_depth++))
1973 # dbgoutd "curr content $1\n"
1975 while read file; do
1976 # 忽略.txt文件,在处理.sh文件时再进行处理
1977 [[ -d $file ]] && continue
1979 $item_proc $file
1981 cd "$file"
1982 foreach_dir $PWD/$file $item_proc
1983 done < <(ls "$1" | sort -n)
1985 test_list_depth=$((test_list_depth--))