git » summer » commit 395325a

test: Add Go tests for errors that can't be covered by end-to-end tests

author Alberto Bertogli
2023-08-30 00:03:03 UTC
committer Alberto Bertogli
2023-08-30 00:03:03 UTC
parent 67bb7eb32c9c403aed863740a4d8f18ad9100d11

test: Add Go tests for errors that can't be covered by end-to-end tests

Some error cases cannot easily be covered by end-to-end tests, for
example db.Read calls are usually preceded by a db.Has and some
permissions checks, so we can't easily simulate db.Read returning errors
in end-to-end tests.

This patch adds some Go tests that use fake structures to simulate those
kinds of situations, filling in some important gaps in coverage, and
helping to make sure we handle those errors correctly.

errors_test.go +124 -0
test/cover.sh +2 -0

diff --git a/errors_test.go b/errors_test.go
new file mode 100644
index 0000000..3c9cd8a
--- /dev/null
+++ b/errors_test.go
@@ -0,0 +1,124 @@
+package main
+
+import (
+	"errors"
+	"io/fs"
+	"os"
+	"syscall"
+	"testing"
+)
+
+// Tests for errors that are not feasible to cover by the end to end tests.
+// For example, db.Read() calls are usually preceded by a db.Has(), so we
+// can't easily simulate Read seeing an "xattr not found" in an end-to-end
+// test.
+
+func TestDBReadError(t *testing.T) {
+	f, err := os.Open("/dev/null")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer f.Close()
+
+	db := XattrDB{}
+	_, err = db.Read(f)
+
+	if !errors.Is(err, syscall.ENODATA) {
+		t.Fatalf("expected ENODATA, got %v", err)
+	}
+}
+
+var testErr = errors.New("test error")
+
+type fakeDirEntry struct{}
+
+func (f fakeDirEntry) Name() string {
+	return "fake"
+}
+
+func (f fakeDirEntry) IsDir() bool {
+	return false
+}
+
+func (f fakeDirEntry) Type() fs.FileMode {
+	return fs.FileMode(0777)
+}
+
+func (f fakeDirEntry) Info() (os.FileInfo, error) {
+	return nil, testErr
+}
+
+func TestOpenAndInfoError(t *testing.T) {
+	ok, _, _, err := openAndInfo("fake", fakeDirEntry{}, nil, 0)
+	if !ok || err != testErr {
+		t.Fatalf("expected ok, testErr, got %v, %v", ok, err)
+	}
+}
+
+type fakeDB struct {
+	hasAttr bool
+	hasErr  error
+
+	readChecksum ChecksumV1
+	readErr      error
+
+	writeErr error
+}
+
+func (db fakeDB) Has(f *os.File) (bool, error) {
+	return db.hasAttr, db.hasErr
+}
+
+func (db fakeDB) Read(f *os.File) (ChecksumV1, error) {
+	return db.readChecksum, db.readErr
+}
+
+func (db fakeDB) Write(f *os.File, c ChecksumV1) error {
+	return db.writeErr
+}
+
+func (db fakeDB) Close() error {
+	return nil
+}
+
+func TestWalkingFunctionsHandleDBErrors(t *testing.T) {
+	// Test how the various walking functions (generate, update, verify)
+	// handle errors from the database.
+	cases := []struct {
+		fn       walkFn
+		db       fakeDB
+		expected error
+	}{
+		{generate, fakeDB{}, nil},
+		{generate, fakeDB{hasErr: testErr}, testErr},
+		{generate, fakeDB{writeErr: testErr}, testErr},
+
+		{verify, fakeDB{}, nil},
+		{verify, fakeDB{hasErr: testErr}, testErr},
+
+		{update, fakeDB{}, nil},
+		{update, fakeDB{hasAttr: true}, nil},
+		{update, fakeDB{hasErr: testErr}, testErr},
+		{update, fakeDB{hasAttr: true, readErr: testErr}, testErr},
+	}
+
+	p := NewProgress(false)
+
+	for _, c := range cases {
+		f, err := os.Open("/dev/null")
+		if err != nil {
+			t.Fatal(err)
+		}
+		info, err := f.Stat()
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		options.db = c.db
+
+		err = c.fn(f, info, p)
+		if err != c.expected {
+			t.Fatalf("expected %v, got %v", c.expected, err)
+		}
+	}
+}
diff --git a/test/cover.sh b/test/cover.sh
index 90dd6b4..259cf2a 100755
--- a/test/cover.sh
+++ b/test/cover.sh
@@ -12,6 +12,8 @@ export BUILDARGS="-cover"
 # Coverage tests require Go >= 1.20.
 go version
 
+go test -cover ../... -args -test.gocoverdir="${GOCOVERDIR?}"
+
 ./test.sh
 
 go tool covdata percent -i="${GOCOVERDIR?}"