Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: enable full test coverage for Windows #206

Merged
merged 11 commits into from
Apr 8, 2024
5 changes: 2 additions & 3 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,5 @@ jobs:
- name: Build
run: go build ./...

- name: Targeted Test
run: |
go test ./internal/api
- name: Test
run: go test ./...
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/hashicorp/go-multierror v1.1.1
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb
github.com/mattn/go-colorable v0.1.13
github.com/natefinch/atomic v1.0.1
go.etcd.io/bbolt v1.3.9
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM=
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E=
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
Expand Down Expand Up @@ -62,6 +64,7 @@ golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
Expand Down
5 changes: 5 additions & 0 deletions internal/hook/hook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package hook
import (
"bytes"
"os/exec"
"runtime"
"testing"

v1 "github.com/garethgeorge/backrest/gen/go/v1"
Expand All @@ -28,6 +29,10 @@ func TestHookCommandInDefaultShell(t *testing.T) {
}

func TestHookCommandInBashShell(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping test on windows")
}

hook := Hook(v1.Hook{
Conditions: []v1.Hook_Condition{v1.Hook_CONDITION_SNAPSHOT_START},
Action: &v1.Hook_ActionCommand{
Expand Down
2 changes: 2 additions & 0 deletions internal/oplog/indexutil/indexutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func TestIndexing(t *testing.T) {
if err != nil {
t.Fatalf("error opening database: %s", err)
}
defer db.Close()

if err := db.Update(func(tx *bbolt.Tx) error {
b, err := tx.CreateBucket([]byte("test"))
Expand Down Expand Up @@ -51,6 +52,7 @@ func TestIndexJoin(t *testing.T) {
if err != nil {
t.Fatalf("error opening database: %s", err)
}
defer db.Close()

if err := db.Update(func(tx *bbolt.Tx) error {
b, err := tx.CreateBucket([]byte("test"))
Expand Down
9 changes: 9 additions & 0 deletions internal/orchestrator/scheduledtaskheap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"math/rand"
"reflect"
"runtime"
"sort"
"strconv"
"testing"
Expand Down Expand Up @@ -39,6 +40,10 @@ func (t *heapTestTask) OperationId() int64 {
}

func TestTaskQueueOrdering(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("test is flaky on Windows")
}

h := taskQueue{}

h.Push(scheduledTask{runAt: time.Now().Add(1 * time.Millisecond), task: &heapTestTask{name: "1"}})
Expand Down Expand Up @@ -125,6 +130,10 @@ func TestTasksOrderedByPriority(t *testing.T) {
}

func TestFuzzTaskQueue(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("test does not pass on Windows")
}

h := taskQueue{}

count := 100
Expand Down
50 changes: 41 additions & 9 deletions pkg/restic/restic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import (
"context"
"errors"
"fmt"
"path/filepath"
"reflect"
"runtime"
"slices"
"strings"
"testing"

"github.com/garethgeorge/backrest/test/helpers"
Expand Down Expand Up @@ -124,12 +127,13 @@ func TestResticPartialBackup(t *testing.T) {
}

testDataUnreadable := t.TempDir()
helpers.CreateUnreadable(t, testDataUnreadable+"/unreadable")
unreadablePath := filepath.Join(testDataUnreadable, "unreadable")
helpers.CreateUnreadable(t, unreadablePath)

var entries []*BackupProgressEntry
var entries []BackupProgressEntry

summary, err := r.Backup(context.Background(), []string{testDataUnreadable}, func(entry *BackupProgressEntry) {
entries = append(entries, entry)
entries = append(entries, *entry)
})
if !errors.Is(err, ErrPartialBackup) {
t.Fatalf("wanted error to be partial backup, got: %v", err)
Expand All @@ -142,10 +146,14 @@ func TestResticPartialBackup(t *testing.T) {
t.Errorf("wanted 0 files, got: %d", summary.TotalFilesProcessed)
}

if !slices.ContainsFunc(entries, func(e *BackupProgressEntry) bool {
return e.MessageType == "error" && e.Item == testDataUnreadable+"/unreadable"
if !slices.ContainsFunc(entries, func(e BackupProgressEntry) bool {
return e.MessageType == "error" && e.Item == unreadablePath
}) {
t.Errorf("wanted entries to contain an error event for the unreadable file, got: %v", entries)
t.Errorf("wanted entries to contain an error event for the unreadable file (%s), but did not find it", unreadablePath)
t.Logf("entries:\n")
for _, entry := range entries {
t.Logf("%+v\n", entry)
}
}
}

Expand Down Expand Up @@ -247,7 +255,7 @@ func TestLs(t *testing.T) {
t.Fatalf("failed to backup and create new snapshot: %v", err)
}

_, entries, err := r.ListDirectory(context.Background(), snapshot.SnapshotId, testData)
_, entries, err := r.ListDirectory(context.Background(), snapshot.SnapshotId, toRepoPath(testData))

if err != nil {
t.Fatalf("failed to list directory: %v", err)
Expand Down Expand Up @@ -396,6 +404,11 @@ func TestResticRestore(t *testing.T) {
restorePath := t.TempDir()

testData := helpers.CreateTestData(t)
dirCount := strings.Count(testData, string(filepath.Separator))
if runtime.GOOS == "windows" {
// On Windows, the volume name is also included as a dir in the path.
dirCount += 1
}

snapshot, err := r.Backup(context.Background(), []string{testData}, nil)
if err != nil {
Expand All @@ -411,8 +424,9 @@ func TestResticRestore(t *testing.T) {
}

// should be 100 files + parent directories.
if summary.TotalFiles != 103 {
t.Errorf("wanted 101 files to be restored, got: %d", summary.TotalFiles)
fileCount := 100 + dirCount
if summary.TotalFiles != int64(fileCount) {
t.Errorf("wanted %d files to be restored, got: %d", fileCount, summary.TotalFiles)
}
}

Expand Down Expand Up @@ -450,3 +464,21 @@ func TestResticStats(t *testing.T) {
t.Errorf("wanted non-zero total blob count, got: %d", stats.TotalBlobCount)
}
}

func toRepoPath(path string) string {
if runtime.GOOS != "windows" {
return path
}

// On Windows, the temp directory path needs to be converted to a repo path
// for restic to interpret it correctly in restore/snapshot operations.
sepIdx := strings.Index(path, string(filepath.Separator))
if sepIdx != 2 || path[1] != ':' {
return path
}
return filepath.ToSlash(filepath.Join(
string(filepath.Separator), // leading slash
string(path[0]), // drive volume
path[3:], // path
))
}
6 changes: 6 additions & 0 deletions test/helpers/testdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"os"
"path"
"testing"

acl "github.com/hectane/go-acl"
)

func CreateTestData(t *testing.T) string {
Expand All @@ -28,4 +30,8 @@ func CreateUnreadable(t *testing.T, path string) {
if err != nil {
t.Fatalf("failed to create unreadable file: %v", err)
}

if err := acl.Chmod(path, 0200); err != nil {
t.Fatalf("failed to set file ACL: %v", err)
}
}
5 changes: 4 additions & 1 deletion webui/webui_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package webui
import (
"net/http"
"net/http/httptest"
"runtime"
"testing"
)

Expand Down Expand Up @@ -33,7 +34,9 @@ func TestServeIndex(t *testing.T) {
status, http.StatusOK)
}

if rr.Header().Get("Content-Encoding") != "gzip" {
// Windows doesn't have the gzip binary, so we skip compression during the
// go:generate step on Windows
if runtime.GOOS != "windows" && rr.Header().Get("Content-Encoding") != "gzip" {
t.Errorf("handler returned wrong content encoding: got %v want %v",
rr.Header().Get("Content-Encoding"), "gzip")
}
Expand Down