俺様サーバー構築記 - Bash de TAP 〜 Subversionサーバーの動作確認の自動化@デスクトップパソコン

「[俺様サーバー構築記 - 基本方針](https://qiita.com/katz_engineer/items/dbabcff4e14e9db2b10f)」以来構築を続けてきた俺様パソコン環境ですが、いよいよ実際のサービスのインストールが始まり、[前回はSubversionサーバーを立てました](https://qiita.com/katz_engineer/items/b893316b4d1bb7731e87)。

[その動作確認](https://qiita.com/katz_engineer/items/b893316b4d1bb7731e87#%E7%A2%BA%E8%AA%8D)は手で行った訳ですが、この手順の自動化を試みました。

# テスト用シェルスクリプト

テストの形式は TAP (Test Anything Protocol) に従いました。これを bash で実施する為のフレームワークも無い訳では無いようですが、調べるとどうも大掛かりと言うか冗長と言うか… 私はもっと単純にしたいんです。

と言う訳で、フレームワークからすべて自力で書きました。と言ってもそれほど大したコードではありません。そう、大したコードじゃ無い筈なんですよね本来は。その為の簡素な仕様だったのではないかと思うんですけども。

```bash:/srv/svn/test.sh
#!/bin/bash

declare -a TAP_TESTS
declare -g TAP_EXPECTED_EXIT_CODE=0
declare -g TAP_EXIT_IF_ERROR

TAP_test() { TAP_TESTS+=( "$@" ); }

TAP_assert() {
	local testname="${FUNCNAME[1]}"
	local result	# "local" is a bash-command, thats exit-code $? is always 0
	result=$(diff -U0 \
			<("$@" 2>&1; echo "exit_code=$?")                --label "$testname" \
			<(cat; echo "exit_code=$TAP_EXPECTED_EXIT_CODE") --label "expected results" \
			2>&1)
	if [[ $? -eq 0 ]]; then	# check the exit-code of "diff" command
		echo "ok $TAP_COUNT - $testname"
	else
		echo "$result" | head -n4 | tail -n1 | xargs echo "not ok $TAP_COUNT - $testname:"
		echo "$result" | sed -e"s/^/# /"
		[[ -n "$TAP_EXIT_IF_ERROR" ]] && exit
	fi
}

TAP_do_testing() {
	echo "1..${#TAP_TESTS[@]}"
	local TAP_COUNT=0
	beforeAll
	trap afterAll EXIT
	local t
	for t in ${TAP_TESTS[@]}; do
		let ++TAP_COUNT
		setup
		$t
		teardown
	done
}

beforeAll() {
	declare -g PROJECT="Subversion_project_for_the_automated_test"
	declare -g TEXT="Subversion test"
	declare -g T_HOME1=$(mktemp -d)
	declare -g T_HOME2=$(mktemp -d)
	declare -g T_USR T_PWD
	IFS="$IFS=" read T_USR T_PWD <<< $(grep = /srv/svn/conf/passwd | head -n1)
}

afterAll() {
	cd "$T_HOME1/.."
	rm -r "$T_HOME1" "$T_HOME2"
	cd /srv/svn/repo
	rm -r "$PROJECT"
}

setup()    { TAP_EXIT_IF_ERROR=1; }
teardown() { :; }

TAP_test svn_createprj
svn_createprj() {
	TAP_assert /srv/svn/createprj.sh "$PROJECT" <<-___
		Edit svnserve.conf and authz in the directory /srv/svn/repo/$PROJECT/conf as needed.
		The file passwd is in the directory /srv/svn/conf, edit it as needed, too.
	___
}

TAP_test svn_checkout
svn_checkout() {
	cd "$T_HOME1"
	cmd() { svn co --username "$T_USR" --password "$T_PWD" svn://localhost/$PROJECT/; }
	TAP_assert cmd <<< "Checked out revision 0."
}

TAP_test svn_add
svn_add() {
	cd "$PROJECT"
	cmd() {
		echo "$TEXT" >document.txt
		svn add *
	}
	TAP_assert cmd <<< "A         document.txt"
}

TAP_test svn_commit 
svn_commit() {
	TAP_assert svn ci --password "$T_PWD" -m "first commit" <<-___
		Adding         document.txt
		Transmitting file data .done
		Committing transaction...
		Committed revision 1.
	___
}

TAP_test svn_cat
svn_cat() {
	TAP_assert svn cat --password "$T_PWD" svn://localhost/$PROJECT/document.txt <<< "$TEXT"
}

TAP_test svn_checkout_datetime
svn_checkout_datetime() {
	cmd() {
		cd "$T_HOME2"
		svn co -q --username "$T_USR" --password "$T_PWD" svn://localhost/$PROJECT/
		cd "$PROJECT"
		A=$(date -u -r document.txt +"%FT%T.%6NZ")
		B=$(svn log --password "$T_PWD" * --xml | xmllint --xpath "/log/logentry/date/text()" -)
		[[ $A = $B ]]
		echo $?
	}
	TAP_assert cmd <<< 0
}

TAP_do_testing
```

# 実行結果

```console
# /srv/svn/test.sh 
1..6
ok 1 - svn_createprj
ok 2 - svn_checkout
ok 3 - svn_add
ok 4 - svn_commit
ok 5 - svn_cat
ok 6 - svn_checkout_datetime
```

```prove``` コマンドも使えますよ勿論。

```console
# prove /srv/svn/test.sh
/srv/svn/test.sh .. ok   
All tests successful.
Files=1, Tests=6,  1 wallclock secs ( 0.03 usr  0.00 sys +  0.14 cusr  0.06 csys =  0.23 CPU)
Result: PASS
```

# テスト失敗を含む場合

```console
# /srv/svn/createprj.sh Subversion_project_for_the_automated_test
Edit svnserve.conf and authz in the directory /srv/svn/repo/Subversion_project_for_the_automated_test/conf as needed.
The file passwd is in the directory /srv/svn/conf, edit it as needed, too.
# prove /srv/svn/test.sh 
/srv/svn/test.sh .. Failed 6/6 subtests 

Test Summary Report
-------------------
/srv/svn/test.sh (Wstat: 0 Tests: 1 Failed: 1)
  Failed test:  1
  Parse errors: Bad plan.  You planned 6 tests but ran 1.
Files=1, Tests=1,  0 wallclock secs ( 0.04 usr  0.00 sys +  0.02 cusr  0.01 csys =  0.07 CPU)
Result: FAIL
```

```console
# /srv/svn/createprj.sh Subversion_project_for_the_automated_test
Edit svnserve.conf and authz in the directory /srv/svn/repo/Subversion_project_for_the_automated_test/conf as needed.
The file passwd is in the directory /srv/svn/conf, edit it as needed, too.
# /srv/svn/test.sh 
1..6
not ok 1 - svn_createprj: -svnadmin: E165002: /zfs/tank/sub0/srv/svn/repo/Subversion_project_for_the_automated_test is an existing repository
# --- svn_createprj
# +++ expected results
# @@ -1,2 +1,3 @@
# -svnadmin: E165002: '/zfs/tank/sub0/srv/svn/repo/Subversion_project_for_the_automated_test' is an existing repository
# -exit_code=1
# +Edit svnserve.conf and authz in the directory /srv/svn/repo/Subversion_project_for_the_automated_test/conf as needed.
# +The file passwd is in the directory /srv/svn/conf, edit it as needed, too.
# +exit_code=0
```

このように、エラーが発生した場合には、テスト結果にコメントの形でエラー内容が表示されます。その為 ```prove``` コマンドでも仕様通りに集計され、問題無く出力されます。

以上、手作業による動作確認を bash スクリプトで自動化し TAP による集計可能出力を表示しましたが、良い感じです。
やったね :thumbsup_tone1: