Skip to content

Commit

Permalink
Update tests; run fuzzing (#294)
Browse files Browse the repository at this point in the history
Run fuzzing tests in CI.

Use race detector when running tests.

Add missing `t.Helper()` calls.

Update test helpers in `pkg/bindfilter` to use `RtlGetNtVersionNumbers`
instead of reading registry, and skip tests if not running as admin.

Signed-off-by: Hamza El-Saawy <[email protected]>
  • Loading branch information
helsaawy authored Jul 21, 2023
1 parent 10d5703 commit 19a9f65
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 46 deletions.
13 changes: 11 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.52
version: v1.53
args: >-
--verbose
--timeout=5m
Expand Down Expand Up @@ -73,6 +73,7 @@ jobs:
- go-generate
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [windows-2019, windows-2022, ubuntu-latest]
steps:
Expand All @@ -88,7 +89,15 @@ jobs:
run: go install gotest.tools/gotestsum@${{ env.GOTESTSUM_VERSION }}

- name: Test repo
run: gotestsum --format standard-verbose --debug -- -gcflags=all=-d=checkptr -v ./...
run: gotestsum --format standard-verbose --debug -- -gcflags=all=-d=checkptr -race -v ./...

# Fuzzing was added in go1.18, so all stable/supported versions of go should support it.
# hvsock fuzzing fails on windows 2019, even though tests pass.
#
# If fuzzing tests are added to different packages, add them here.
- name: Fuzz repo
if: ${{ matrix.os == 'windows-2022' }}
run: gotestsum --format standard-verbose --debug -- -run "^#" -fuzztime 500x -fuzz "FuzzHvSock"

build:
name: Build Repo
Expand Down
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ linters:
- gofmt # files are gofmt'ed
- gosec # security
- nilerr # returns nil even with non-nil error
- thelper # test helpers without t.Helper()
- unparam # unused function params

issues:
Expand Down
18 changes: 18 additions & 0 deletions backuptar/tar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
)

func ensurePresent(t *testing.T, m map[string]string, keys ...string) {
t.Helper()

for _, k := range keys {
if _, ok := m[k]; !ok {
t.Error(k, "not present in tar header")
Expand All @@ -25,13 +27,17 @@ func ensurePresent(t *testing.T, m map[string]string, keys ...string) {
}

func setSparse(t *testing.T, f *os.File) {
t.Helper()

if err := windows.DeviceIoControl(windows.Handle(f.Fd()), windows.FSCTL_SET_SPARSE, nil, 0, nil, 0, nil, nil); err != nil {
t.Fatal(err)
}
}

// compareReaders validates that two readers contain the exact same data.
func compareReaders(t *testing.T, rActual io.Reader, rExpected io.Reader) {
t.Helper()

const size = 8 * 1024
var bufExpected, bufActual [size]byte
var readCount int64
Expand Down Expand Up @@ -71,13 +77,17 @@ func TestRoundTrip(t *testing.T) {
//nolint:gosec // G306: Expect WriteFile permissions to be 0600 or less
for name, setup := range map[string]func(*testing.T) string{
"normalFile": func(t *testing.T) string {
t.Helper()

path := filepath.Join(t.TempDir(), "foo.txt")
if err := os.WriteFile(path, []byte("testing 1 2 3\n"), 0644); err != nil {
t.Fatal(err)
}
return path
},
"normalFileEmpty": func(t *testing.T) string {
t.Helper()

path := filepath.Join(t.TempDir(), "foo.txt")
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
Expand All @@ -87,6 +97,8 @@ func TestRoundTrip(t *testing.T) {
return path
},
"sparseFileEmpty": func(t *testing.T) string {
t.Helper()

path := filepath.Join(t.TempDir(), "foo.txt")
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
Expand All @@ -97,6 +109,8 @@ func TestRoundTrip(t *testing.T) {
return path
},
"sparseFileWithNoAllocatedRanges": func(t *testing.T) string {
t.Helper()

path := filepath.Join(t.TempDir(), "foo.txt")
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
Expand All @@ -112,6 +126,8 @@ func TestRoundTrip(t *testing.T) {
return path
},
"sparseFileWithOneAllocatedRange": func(t *testing.T) string {
t.Helper()

path := filepath.Join(t.TempDir(), "foo.txt")
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
Expand All @@ -125,6 +141,8 @@ func TestRoundTrip(t *testing.T) {
return path
},
"sparseFileWithMultipleAllocatedRanges": func(t *testing.T) string {
t.Helper()

path := filepath.Join(t.TempDir(), "foo.txt")
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult

//todo (go1.19): switch to [atomic.Bool]

type atomicBool int32

func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
Expand Down
2 changes: 2 additions & 0 deletions fileinfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
// so we check that the current.AllocationSize is >= expected.AllocationSize.
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/5afa7f66-619c-48f3-955f-68c4ece704ae
func checkFileStandardInfo(t *testing.T, current, expected *FileStandardInfo) {
t.Helper()

if current.AllocationSize < expected.AllocationSize {
t.Fatalf("FileStandardInfo unexpectedly had AllocationSize %d, expecting >=%d", current.AllocationSize, expected.AllocationSize)
}
Expand Down
7 changes: 5 additions & 2 deletions hvsock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func serverListen(u testUtil) (l *HvsockListener, a *HvsockAddr) {
u.T.Logf("address collision %v", a)
continue
}
u.T.Logf("listening on %v", a)
break
}
u.Must(err, "could not listen")
Expand Down Expand Up @@ -579,9 +580,11 @@ type testUtil struct {
T testing.TB
}

func newUtil(t testing.TB) testUtil {
func newUtil(tb testing.TB) testUtil {
tb.Helper()

return testUtil{
T: t,
T: tb,
}
}

Expand Down
2 changes: 2 additions & 0 deletions pipe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ func TestCloseAbortsListen(t *testing.T) {
}

func ensureEOFOnClose(t *testing.T, r io.Reader, w io.Closer) {
t.Helper()

b := make([]byte, 10)
w.Close()
n, err := r.Read(b)
Expand Down
77 changes: 35 additions & 42 deletions pkg/bindfilter/bind_filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import (
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"testing"

"golang.org/x/sys/windows/registry"
"golang.org/x/sys/windows"
)

func TestApplyFileBinding(t *testing.T) {
requireElevated(t)

source := t.TempDir()
destination := t.TempDir()
fileName := "testFile.txt"
Expand Down Expand Up @@ -55,12 +56,15 @@ func TestApplyFileBinding(t *testing.T) {
}

func removeFileBinding(t *testing.T, mountpoint string) {
t.Helper()
if err := RemoveFileBinding(mountpoint); err != nil {
t.Logf("failed to remove file binding from %s: %q", mountpoint, err)
}
}

func TestApplyFileBindingReadOnly(t *testing.T) {
requireElevated(t)

source := t.TempDir()
destination := t.TempDir()
fileName := "testFile.txt"
Expand Down Expand Up @@ -108,19 +112,14 @@ func TestApplyFileBindingReadOnly(t *testing.T) {
}

func TestEnsureOnlyOneTargetCanBeMounted(t *testing.T) {
version, err := getWindowsBuildNumber()
if err != nil {
t.Fatalf("couldn't get version number: %s", err)
}
requireElevated(t)
requireBuild(t, RS5+1) // support added after RS5

if version <= 17763 {
t.Skip("not supported on RS5 or earlier")
}
source := t.TempDir()
secondarySource := t.TempDir()
destination := t.TempDir()

err = ApplyFileBinding(destination, source, false)
err := ApplyFileBinding(destination, source, false)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -159,14 +158,9 @@ func checkSourceIsMountedOnDestination(src, dst string) (bool, error) {
}

func TestGetBindMappings(t *testing.T) {
version, err := getWindowsBuildNumber()
if err != nil {
t.Fatalf("couldn't get version number: %s", err)
}
requireElevated(t)
requireBuild(t, RS5+1) // support added after RS5

if version <= 17763 {
t.Skip("not supported on RS5 or earlier")
}
// GetBindMappings will expand short paths like ADMINI~1 and PROGRA~1 to their
// full names. In order to properly match the names later, we expand them here.
srcShort := t.TempDir()
Expand Down Expand Up @@ -198,6 +192,8 @@ func TestGetBindMappings(t *testing.T) {
}

func TestRemoveFileBinding(t *testing.T) {
requireElevated(t)

srcShort := t.TempDir()
source, err := getFinalPath(srcShort)
if err != nil {
Expand Down Expand Up @@ -238,32 +234,9 @@ func TestRemoveFileBinding(t *testing.T) {
}
}

func getWindowsBuildNumber() (int, error) {
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
if err != nil {
return 0, fmt.Errorf("read CurrentVersion reg key: %w", err)
}
defer k.Close()
buildNumStr, _, err := k.GetStringValue("CurrentBuild")
if err != nil {
return 0, fmt.Errorf("read CurrentBuild reg value: %w", err)
}
buildNum, err := strconv.Atoi(buildNumStr)
if err != nil {
return 0, err
}
return buildNum, nil
}

func TestGetBindMappingsSymlinks(t *testing.T) {
version, err := getWindowsBuildNumber()
if err != nil {
t.Fatalf("couldn't get version number: %s", err)
}

if version <= 17763 {
t.Skip("not supported on RS5 or earlier")
}
requireElevated(t)
requireBuild(t, RS5+1) // support added after RS5

srcShort := t.TempDir()
sourceNested := filepath.Join(srcShort, "source")
Expand Down Expand Up @@ -307,3 +280,23 @@ func TestGetBindMappingsSymlinks(t *testing.T) {
t.Fatalf("expected to find %s mounted on %s, but could not", source, destination)
}
}

func requireElevated(tb testing.TB) {
tb.Helper()
if !windows.GetCurrentProcessToken().IsElevated() {
tb.Skip("requires elevated privileges")
}
}

const RS5 = 17763

//todo: also check that `bindfltapi.dll` exists

// require current build to be >= build
func requireBuild(tb testing.TB, build uint32) {
tb.Helper()
_, _, b := windows.RtlGetNtVersionNumbers()
if b < build {
tb.Skipf("requires build %d+; current build is %d", build, b)
}
}
2 changes: 2 additions & 0 deletions pkg/etw/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
)

func mustGUIDFromString(t *testing.T, s string) guid.GUID {
t.Helper()

g, err := guid.FromString(s)
if err != nil {
t.Fatal(err)
Expand Down
6 changes: 6 additions & 0 deletions pkg/guid/guid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
)

func mustNewV4(t *testing.T) GUID {
t.Helper()

g, err := NewV4()
if err != nil {
t.Fatal(err)
Expand All @@ -15,6 +17,8 @@ func mustNewV4(t *testing.T) GUID {
}

func mustNewV5(t *testing.T, namespace GUID, name []byte) GUID {
t.Helper()

g, err := NewV5(namespace, name)
if err != nil {
t.Fatal(err)
Expand All @@ -23,6 +27,8 @@ func mustNewV5(t *testing.T, namespace GUID, name []byte) GUID {
}

func mustFromString(t *testing.T, s string) GUID {
t.Helper()

g, err := FromString(s)
if err != nil {
t.Fatal(err)
Expand Down
2 changes: 2 additions & 0 deletions pkg/security/grantvmgroupaccess_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ func TestGrantVmGroupAccess(t *testing.T) {
}

func verifyVMAccountDACLs(t *testing.T, name string, permissions []string) {
t.Helper()

cmd := exec.Command("icacls", name)
outb, err := cmd.CombinedOutput()
if err != nil {
Expand Down

0 comments on commit 19a9f65

Please sign in to comment.