著者:後藤大地
 前回はコマンド開発目的で利用できるテストフレームワーク「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) %  |