author | Alberto Bertogli
<albertito@blitiri.com.ar> 2018-02-25 01:24:00 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2018-03-02 19:37:37 UTC |
parent | 751fbd4b554e34f434955b94f7c76c9a73735ee2 |
.gitignore | +3 | -0 |
cmd/chasquid-util/chasquid-util.go | +4 | -0 |
cmd/dovecot-auth-cli/dovecot-auth-cli.go | +2 | -0 |
cmd/mda-lmtp/mda-lmtp.go | +3 | -0 |
cmd/smtp-check/smtp-check.go | +3 | -0 |
cmd/spf-check/spf-check.go | +2 | -0 |
coverage_test.go | +39 | -0 |
test/cover.sh | +48 | -0 |
test/t-03-queue_persistency/addtoqueue.go | +3 | -0 |
test/util/gocovcat.go | +91 | -0 |
test/util/lib.sh | +23 | -0 |
diff --git a/.gitignore b/.gitignore index 02ae02d..2269e3c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,9 @@ cmd/smtp-check/smtp-check cmd/spf-check/spf-check cmd/mda-lmtp/mda-lmtp +# Test binary, generated during coverage tests. +chasquid.test + # Exclude any .pem files, to prevent accidentally including test keys and # certificates. *.pem diff --git a/cmd/chasquid-util/chasquid-util.go b/cmd/chasquid-util/chasquid-util.go index 8ced913..a617e0e 100644 --- a/cmd/chasquid-util/chasquid-util.go +++ b/cmd/chasquid-util/chasquid-util.go @@ -1,4 +1,8 @@ // chasquid-util is a command-line utility for chasquid-related operations. +// +// Don't include it in the coverage build. +// +build !coverage + package main import ( diff --git a/cmd/dovecot-auth-cli/dovecot-auth-cli.go b/cmd/dovecot-auth-cli/dovecot-auth-cli.go index aee3a92..fefb43d 100644 --- a/cmd/dovecot-auth-cli/dovecot-auth-cli.go +++ b/cmd/dovecot-auth-cli/dovecot-auth-cli.go @@ -1,6 +1,8 @@ // CLI used for testing the dovecot authentication package. // // NOT for production use. +// +build !coverage + package main import ( diff --git a/cmd/mda-lmtp/mda-lmtp.go b/cmd/mda-lmtp/mda-lmtp.go index f6ead8d..8be8bfa 100644 --- a/cmd/mda-lmtp/mda-lmtp.go +++ b/cmd/mda-lmtp/mda-lmtp.go @@ -1,6 +1,9 @@ // mda-lmtp is a very basic MDA that uses LMTP to do the delivery. // // See the usage below for details. +// +// +build !coverage + package main import ( diff --git a/cmd/smtp-check/smtp-check.go b/cmd/smtp-check/smtp-check.go index 6a1ac84..4ce912f 100644 --- a/cmd/smtp-check/smtp-check.go +++ b/cmd/smtp-check/smtp-check.go @@ -1,4 +1,7 @@ // smtp-check is a command-line too for checking SMTP setups. +// +// +build !coverage + package main import ( diff --git a/cmd/spf-check/spf-check.go b/cmd/spf-check/spf-check.go index 05fe925..53e609a 100644 --- a/cmd/spf-check/spf-check.go +++ b/cmd/spf-check/spf-check.go @@ -1,6 +1,8 @@ // Command line tool for playing with the SPF library. // // Not for use in production, just development and experimentation. +// +build !coverage + package main import ( diff --git a/coverage_test.go b/coverage_test.go new file mode 100644 index 0000000..99af46f --- /dev/null +++ b/coverage_test.go @@ -0,0 +1,39 @@ +// Test file used to build a coverage-enabled chasquid binary. +// +// Go lacks support for properly building a coverage binary, it can only build +// coverage test binaries. As a workaround, we have a test that just runs +// main. We then build a binary of this test, which we use instead of chasquid +// in integration tests. +// +// This is hacky and horrible. +// +// The test has a build label so it's not accidentally executed during normal +// "go test" invocations. +// +build coveragebin + +package main + +import ( + "os" + "os/signal" + "syscall" + "testing" +) + +func TestRunMain(t *testing.T) { + done := make(chan bool) + + signals := make(chan os.Signal, 1) + go func() { + <-signals + done <- true + }() + signal.Notify(signals, os.Interrupt, os.Kill, syscall.SIGTERM) + + go func() { + main() + done <- true + }() + + <-done +} diff --git a/test/cover.sh b/test/cover.sh new file mode 100755 index 0000000..d64b0eb --- /dev/null +++ b/test/cover.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Runs tests (both go and integration) in coverage-generation mode. +# Generates an HTML report with the results. +# +# The .coverage directory is used to store the data, it will be erased and +# recreated on each run. +# +# This is not very tidy, and relies on some hacky tricks (see +# coverage_test.go), but works for now. + +set -e +. $(dirname ${0})/util/lib.sh + +init + +cd "${TBASE}/.." + +# Recreate the coverage output directory, to avoid including stale results +# from previous runs. +rm -rf .coverage +mkdir -p .coverage +export COVER_DIR="$PWD/.coverage" + +# Normal go tests. +go test -tags coverage \ + -covermode=count \ + -coverprofile="$COVER_DIR/pkg-tests.out"\ + -coverpkg=./... ./... + +# Integration tests. +# Will run in coverage mode due to $COVER_DIR being set. +setsid -w ./test/run.sh + +# Merge all coverage output into a single file. +# Ignore protocol buffer-generated files, as they are not relevant. +go run "${UTILDIR}/gocovcat.go" .coverage/*.out \ + | grep -v ".pb.go:" \ + > .coverage/all.out + +# Generate reports based on the merged output. +go tool cover -func="$COVER_DIR/all.out" | sort -k 3 -n > "$COVER_DIR/func.txt" +go tool cover -html="$COVER_DIR/all.out" -o "$COVER_DIR/chasquid.html" + +echo +echo "Coverage report can be found in:" +echo file://$COVER_DIR/chasquid.html + diff --git a/test/t-03-queue_persistency/addtoqueue.go b/test/t-03-queue_persistency/addtoqueue.go index 6c770ba..446ceb0 100644 --- a/test/t-03-queue_persistency/addtoqueue.go +++ b/test/t-03-queue_persistency/addtoqueue.go @@ -3,6 +3,9 @@ // // Note that chasquid does NOT support this, we do it before starting up the // daemon for testing purposes only. +// +// +build ignore + package main import ( diff --git a/test/util/gocovcat.go b/test/util/gocovcat.go new file mode 100755 index 0000000..5b3e759 --- /dev/null +++ b/test/util/gocovcat.go @@ -0,0 +1,91 @@ +//usr/bin/env go run "$0" "$@"; exit $? +// +// From: https://git.lukeshu.com/go/cmd/gocovcat/ +// +// +build ignore + +// Copyright 2017 Luke Shumaker <lukeshu@parabola.nu> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +// Command gocovcat combines multiple go cover runs, and prints the +// result on stdout. +package main + +import ( + "bufio" + "fmt" + "os" + "sort" + "strconv" + "strings" +) + +func handleErr(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) + } +} + +func main() { + modeBool := false + blocks := map[string]int{} + for _, filename := range os.Args[1:] { + file, err := os.Open(filename) + handleErr(err) + buf := bufio.NewScanner(file) + for buf.Scan() { + line := buf.Text() + + if strings.HasPrefix(line, "mode: ") { + m := strings.TrimPrefix(line, "mode: ") + switch m { + case "set": + modeBool = true + case "count", "atomic": + // do nothing + default: + fmt.Fprintf(os.Stderr, "Unrecognized mode: %s\n", m) + os.Exit(1) + } + } else { + sp := strings.LastIndexByte(line, ' ') + block := line[:sp] + cntStr := line[sp+1:] + cnt, err := strconv.Atoi(cntStr) + handleErr(err) + blocks[block] += cnt + } + } + handleErr(buf.Err()) + } + keys := make([]string, 0, len(blocks)) + for key := range blocks { + keys = append(keys, key) + } + sort.Strings(keys) + modeStr := "count" + if modeBool { + modeStr = "set" + } + fmt.Printf("mode: %s\n", modeStr) + for _, block := range keys { + cnt := blocks[block] + if modeBool && cnt > 1 { + cnt = 1 + } + fmt.Printf("%s %d\n", block, cnt) + } +} diff --git a/test/util/lib.sh b/test/util/lib.sh index aed646d..563424b 100644 --- a/test/util/lib.sh +++ b/test/util/lib.sh @@ -29,6 +29,11 @@ function generate_cert() { } function chasquid() { + if [ "${COVER_DIR}" != "" ]; then + chasquid_cover "$@" + return + fi + # HOSTALIASES: so we "fake" hostnames. # PATH: so chasquid can call test-mda without path issues. # MDA_DIR: so our test-mda knows where to deliver emails. @@ -38,6 +43,24 @@ function chasquid() { go run ${RACE} ${TBASE}/../../chasquid.go "$@" } +function chasquid_cover() { + # Build the coverage-enabled binary. + # See coverage_test.go for more details. + ( cd ${TBASE}/../../; + go test -covermode=count -coverpkg=./... -c -tags coveragebin ) + + # Run the coverage-enabled binary, named "chasquid.test" for hacky + # reasons. See the chasquid function above for details on the + # environment variables. + HOSTALIASES=${TBASE}/hosts \ + PATH=${UTILDIR}:${PATH} \ + MDA_DIR=${TBASE}/.mail \ + ${TBASE}/../../chasquid.test \ + -test.run "^TestRunMain$" \ + -test.coverprofile="$COVER_DIR/test-`date +%s.%N`.out" \ + "$@" +} + function add_user() { CONFDIR="${CONFDIR:-config}" DOMAIN=$(echo $1 | cut -d @ -f 2)