著者:後藤大地
前回はコマンド開発目的で利用できるテストフレームワーク「Kyua」(https://github.com/jmmv/kyua(https://github.com/jmmv/kyua)で、実際にどのようにデプロイして開発に利用していけばよいのかを解説した。また、make(1) の仕組みを使い、どのディレクトリにいてもmake test でテストが実行されるようにして、テストと開発を同時に進める方法についても説明した。
今回は具体的にどんな手段で実際のコーティングとテストを実施していくのかを紹介する。
記事本文掲載のシェルスクリプトマガジンvol.51は以下リンク先でご購入できます。
|
#include <unistd.h> #include <stdlib.h> #include <sysexits.h> int main(int argc, char *argv[]) { exit(EX_OK); } |
|
#!/usr/bin/env atf-sh atf_test_case normal normal_head() { atf_set "" } normal_body() { true } atf_init_test_cases() { atf_add_test_case normal } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <limits.h> #include <err.h> #include <errno.h> #include <sysexits.h> int main(int argc, char *argv[]) { char *f = "/dev/stdin"; if (1 < argc) f = argv[1]; FILE *fp; fp = fopen(f, "r"); if (NULL == fp) err(errno, "%s", f); char buf[BUFSIZ], buf_num[BUFSIZ]; long sum, num; int buf_i, buf_num_i; while (NULL != fgets(buf, BUFSIZ, fp)) { sum = 0; buf_i = 0; while ('\n' != buf[buf_i] && '\0' != buf[buf_i]) { buf_num_i = 0; while (' ' != buf[buf_i] && '\n' != buf[buf_i] && '\0' != buf[buf_i]) buf_num[buf_num_i++] = buf[buf_i++]; buf_num[buf_num_i++] = '\0'; errno = 0; num = (int)strtol(buf_num, (char **)NULL, 10); if (EINVAL != errno) sum += num; if (' ' == buf[buf_i]) ++buf_i; } if ('\n' == buf[buf_i]) buf[buf_i] = '\0'; printf("%s %ld\n", buf, sum); } exit(EX_OK); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
% cat data_01.ssv 81486 95687 89401 70384 83237 21094 83312 82973 41971 39009 34446 91667 42813 36052 45730 91921 62011 68277 48535 22413 30537 45627 71052 56639 80746 13849 55037 71849 81486 95687 89401 70384 83237 21094 83312 82973 41971 39009 34446 91667 % ./../../../bin/sum data_01.ssv 81486 95687 89401 70384 336958 83237 21094 83312 82973 270616 41971 39009 34446 91667 207093 42813 36052 45730 91921 216516 62011 68277 48535 22413 201236 30537 45627 71052 56639 203855 80746 13849 55037 71849 221481 81486 95687 89401 70384 336958 83237 21094 83312 82973 270616 41971 39009 34446 91667 207093 % cat data_01.ssv | ./../../../bin/sum 81486 95687 89401 70384 336958 83237 21094 83312 82973 270616 41971 39009 34446 91667 207093 42813 36052 45730 91921 216516 62011 68277 48535 22413 201236 30537 45627 71052 56639 203855 80746 13849 55037 71849 221481 81486 95687 89401 70384 336958 83237 21094 83312 82973 270616 41971 39009 34446 91667 207093 % |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
#!/usr/bin/env atf-sh atf_test_case normal normal_head() { atf_set "descr" "Verify the normal operation" } normal_body() { atf_check -s ignore \ -o file:$(atf_get_srcdir)/out_01.ssv \ -x "sum $(atf_get_srcdir)/data_01.ssv" } atf_init_test_cases() { atf_add_test_case normal } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
#!/usr/bin/env atf-sh atf_test_case normal normal_head() { atf_set "descr" "Verify the normal operation" } normal_body() { atf_check -s ignore \ -o file:$(atf_get_srcdir)/out_01.ssv \ -x "sum $(atf_get_srcdir)/data_01.ssv" } atf_test_case stdin stdin_head() { atf_set "descr" "Verify the stdin operation" } stdin_body() { atf_check -s ignore \ -o file:$(atf_get_srcdir)/out_01.ssv \ -x "cat $(atf_get_srcdir)/data_01.ssv | sum" } atf_init_test_cases() { atf_add_test_case normal atf_add_test_case stdin } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
% cat data_02.ssv 81486 95687 @ 70384 83237 @ 83312 82973 @ 39009 34446 91667 @ 36052 45730 91921 62011 68277 A 22413 30537 45627 @ 56639 80746 B 55037 71849 81486 # 89401 70384 83237 21094 @ 82973 41971 39009 34446 K % ./../../../bin/sum data_02.ssv 81486 95687 @ 70384 247557 83237 @ 83312 82973 249522 @ 39009 34446 91667 165122 @ 36052 45730 91921 173703 62011 68277 A 22413 152701 30537 45627 @ 56639 132803 80746 B 55037 71849 207632 81486 # 89401 70384 241271 83237 21094 @ 82973 187304 41971 39009 34446 K 115426 % |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
% cat data_03.ssv 81486 95687 70384 83237 83312 82973 39009 34446 91667 36052 45730 91921 62011 68277 22413 30537 45627 56639 80746 55037 71849 81486 89401 70384 83237 21094 82973 41971 39009 34446 % ./../../../bin/sum data_03.ssv 81486 95687 70384 247557 83237 83312 82973 249522 39009 34446 91667 165122 36052 45730 91921 173703 62011 68277 22413 152701 30537 45627 56639 132803 80746 55037 71849 207632 81486 89401 70384 241271 83237 21094 82973 187304 41971 39009 34446 115426 % |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
|
#!/usr/bin/env atf-sh atf_test_case normal normal_head() { atf_set "descr" "Verify the normal operation" } normal_body() { atf_check -s ignore \ -o file:$(atf_get_srcdir)/out_01.ssv \ -x "sum $(atf_get_srcdir)/data_01.ssv" } atf_test_case stdin stdin_head() { atf_set "descr" "Verify the stdin operation" } stdin_body() { atf_check -s ignore \ -o file:$(atf_get_srcdir)/out_01.ssv \ -x "cat $(atf_get_srcdir)/data_01.ssv | sum" } atf_test_case invalid invalid_head() { atf_set "descr" "Verify the invalid data operation" } invalid_body() { atf_check -s ignore \ -o file:$(atf_get_srcdir)/out_02.ssv \ -x "sum $(atf_get_srcdir)/data_02.ssv" } atf_test_case null null_head() { atf_set "descr" "Verify the null data operation" } null_body() { atf_check -s ignore \ -o file:$(atf_get_srcdir)/out_03.ssv \ -x "sum $(atf_get_srcdir)/data_03.ssv" } atf_init_test_cases() { atf_add_test_case normal atf_add_test_case stdin atf_add_test_case invalid atf_add_test_case null } |
|
% tree -F tests/bin/sum tests/bin/sum ├── data_01.ssv ├── data_02.ssv ├── data_03.ssv ├── Kyuafile ├── Makefile ├── Makefile.inc ├── out_01.ssv ├── out_02.ssv ├── out_03.ssv └── test.atf-sh* 0 directories, 10 files % |
|
% make test env PATH=/Users/daichi/Documents/shellmagazine/201710/stuffs/ore2/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin kyua test test.atf-sh:invalid -> passed [0.013s] test.atf-sh:normal -> passed [0.013s] test.atf-sh:null -> passed [0.011s] test.atf-sh:stdin -> passed [0.014s] Results file id is Users_daichi_Documents_shellmagazine_201710_stuffs_ore2_tests_bin_sum.20170813-032208-089766 Results saved to /Users/daichi/.kyua/store/results.Users_daichi_Documents_shellmagazine_201710_stuffs_ore2_tests_bin_sum.20170813-032208-089766.db 4/4 passed (0 failed) % |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
|
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <limits.h> #include <err.h> #include <errno.h> #include <sysexits.h> static void usage(); int main(int argc, char *argv[]) { int ch; while (-1 != (ch = getopt(argc, argv, "h"))) switch (ch) { case 'h': usage(); break; default: usage(); break; } char *f = "/dev/stdin"; if (1 <= argc - optind) f = argv[optind]; FILE *fp; fp = fopen(f, "r"); if (NULL == fp) err(errno, "%s", f); char buf[BUFSIZ], buf_num[BUFSIZ]; long sum, num; int buf_i, buf_num_i; while (NULL != fgets(buf, BUFSIZ, fp)) { sum = 0; buf_i = 0; while ('\n' != buf[buf_i] && '\0' != buf[buf_i]) { buf_num_i = 0; while (' ' != buf[buf_i] && '\n' != buf[buf_i] && '\0' != buf[buf_i]) buf_num[buf_num_i++] = buf[buf_i++]; buf_num[buf_num_i++] = '\0'; errno = 0; num = (int)strtol(buf_num, (char **)NULL, 10); if (EINVAL != errno) sum += num; if (' ' == buf[buf_i]) ++buf_i; } if ('\n' == buf[buf_i]) buf[buf_i] = '\0'; printf("%s %ld\n", buf, sum); } exit(EX_OK); } #define USAGE_MESSAGE \ "名前\n" \ "\tsum - 列の値を加算して最後の列に合計値を出力\n" \ "\n" \ "書式\n" \ "\tsum [-h] [file ...]\n" \ "\n" \ "オプション\n" \ "\t-h\t使い方表示\n" \ "\tfile\tファイルを指定。指定がない場合には標準入力を使用\n" static void usage() { fprintf(stderr, USAGE_MESSAGE); exit(EX_USAGE); } |
|
% ./../../../bin/sum -h 名前 sum - 列の値を加算して最後の列に合計値を出力 書式 sum [-h] [file ...] オプション -h 使い方表示 file ファイルを指定。指定がない場合には標準入力を使用 % |
|
% ./../../../bin/sum -v sum: illegal option -- v 名前 sum - 列の値を加算して最後の列に合計値を出力 書式 sum [-h] [file ...] オプション -h 使い方表示 file ファイルを指定。指定がない場合には標準入力を使用 % |
|
--- ore3/src/bin/sum/sum.c 2017-10-19 14:24:46.507636000 +0900 +++ ore4/src/bin/sum/sum.c 2017-10-19 14:33:16.734744000 +0900 @@ -72,9 +72,10 @@ "\tsum - 列の値を加算して最後の列に合計値を出力\n" \ "\n" \ "書式\n" \ - "\tsum [-h] [file ...]\n" \ + "\tsum [-he] [file ...]\n" \ "\n" \ "オプション\n" \ + "\t-e\t値に数値以外が含まれていた場合は@を出力\n" \ "\t-h\t使い方表示\n" \ "\tfile\tファイルを指定。指定がない場合には標準入力を使用\n" |
|
% ./../../../bin/sum -h 名前 sum - 列の値を加算して最後の列に合計値を出力 書式 sum [-he] [file ...] オプション -e 値に数値以外が含まれていた場合は@を出力 -h 使い方表示 file ファイルを指定。指定がない場合には標準入力を使用 % |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
diff -urN ore3/tests/bin/sum/data_04.ssv ore4/tests/bin/sum/data_04.ssv --- ore3/tests/bin/sum/data_04.ssv 1970-01-01 09:00:00.000000000 +0900 +++ ore4/tests/bin/sum/data_04.ssv 2017-10-19 14:39:30.121347000 +0900 @@ -0,0 +1,10 @@ +81486 95687 @ 70384 +83237 @ 83312 82973 +@ 39009 34446 91667 +@ 36052 45730 91921 +62011 68277 A 22413 +30537 45627 @ 56639 +80746 B 55037 71849 +81486 # 89401 70384 +83237 21094 @ 82973 +41971 39009 34446 K diff -urN ore3/tests/bin/sum/out_04.ssv ore4/tests/bin/sum/out_04.ssv --- ore3/tests/bin/sum/out_04.ssv 1970-01-01 09:00:00.000000000 +0900 +++ ore4/tests/bin/sum/out_04.ssv 2017-10-19 14:40:08.650527000 +0900 @@ -0,0 +1,10 @@ +81486 95687 @ 70384 @ +83237 @ 83312 82973 @ +@ 39009 34446 91667 @ +@ 36052 45730 91921 @ +62011 68277 A 22413 @ +30537 45627 @ 56639 @ +80746 B 55037 71849 @ +81486 # 89401 70384 @ +83237 21094 @ 82973 @ +41971 39009 34446 K @ diff -urN ore3/tests/bin/sum/test.atf-sh ore4/tests/bin/sum/test.atf-sh --- ore3/tests/bin/sum/test.atf-sh 2017-10-19 12:59:19.334227000 +0900 +++ ore4/tests/bin/sum/test.atf-sh 2017-10-19 14:43:55.868031000 +0900 @@ -44,10 +44,22 @@ -x "sum $(atf_get_srcdir)/data_03.ssv" } +atf_test_case test_e +test_e_head() { + atf_set "descr" "Verify the -e operation" +} + +test_e_body() { + atf_check -s ignore \ + -o file:$(atf_get_srcdir)/out_04.ssv \ + -x "sum -e $(atf_get_srcdir)/data_04.ssv" +} + atf_init_test_cases() { atf_add_test_case normal atf_add_test_case stdin atf_add_test_case invalid atf_add_test_case null + atf_add_test_case test_e } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
% make test env PATH=/Users/daichi/Documents/shellmagazine/201712/stuffs/ore4/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin kyua test test.atf-sh:invalid -> passed [0.011s] test.atf-sh:normal -> passed [0.013s] test.atf-sh:null -> passed [0.012s] test.atf-sh:stdin -> passed [0.013s] test.atf-sh:test_e -> failed: atf-check failed; see the output of the test for details [0.017s] Results file id is Users_daichi_Documents_shellmagazine_201712_stuffs_ore4_tests_bin_sum.20171019-054816-646434 Results saved to /Users/daichi/.kyua/store/results.Users_daichi_Documents_shellmagazine_201712_stuffs_ore4_tests_bin_sum.20171019-054816-646434.db 4/5 passed (1 failed) *** Error code 1 Stop. make: stopped in /Users/daichi/Documents/shellmagazine/201712/stuffs/ore4/tests/bin/sum % |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
diff -u ore4/src/bin/sum/sum.c ore5/src/bin/sum/sum.c --- ore4/src/bin/sum/sum.c 2017-10-19 14:33:16.734744000 +0900 +++ ore5/src/bin/sum/sum.c 2017-10-19 15:18:56.658860000 +0900 @@ -12,8 +12,12 @@ main(int argc, char *argv[]) { int ch; - while (-1 != (ch = getopt(argc, argv, "h"))) + int eflag = 0; + while (-1 != (ch = getopt(argc, argv, "eh"))) switch (ch) { + case 'e': + eflag = 1; + break; case 'h': usage(); break; @@ -37,6 +41,7 @@ while (NULL != fgets(buf, BUFSIZ, fp)) { sum = 0; buf_i = 0; + errno = 0; while ('\n' != buf[buf_i] && '\0' != buf[buf_i]) { @@ -51,8 +56,14 @@ errno = 0; num = (int)strtol(buf_num, (char **)NULL, 10); - if (EINVAL != errno) + if (!eflag && EINVAL != errno) sum += num; + else if (eflag && EINVAL != errno) { + while ('\n' != buf[buf_i] && + '\0' != buf[buf_i]) + ++buf_i; + break; + } if (' ' == buf[buf_i]) ++buf_i; @@ -61,7 +72,12 @@ if ('\n' == buf[buf_i]) buf[buf_i] = '\0'; - printf("%s %ld\n", buf, sum); + if (eflag && EINVAL != errno) { + printf("%s @\n", buf); + errno = 0; + } + else + printf("%s %ld\n", buf, sum); } exit(EX_OK); |
|
% make test env PATH=/Users/daichi/Documents/shellmagazine/201712/stuffs/ore5/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin kyua test test.atf-sh:invalid -> passed [0.011s] test.atf-sh:normal -> passed [0.013s] test.atf-sh:null -> passed [0.013s] test.atf-sh:stdin -> passed [0.012s] test.atf-sh:test_e -> passed [0.015s] Results file id is Users_daichi_Documents_shellmagazine_201712_stuffs_ore5_tests_bin_sum.20171019-062132-649120 Results saved to /Users/daichi/.kyua/store/results.Users_daichi_Documents_shellmagazine_201712_stuffs_ore5_tests_bin_sum.20171019-062132-649120.db 5/5 passed (0 failed) % |