From eadcdc753d7678b25e0407638ce11b1313618fcd Mon Sep 17 00:00:00 2001 From: Julio Lopez <1953782+julio-lopez@users.noreply.github.com> Date: Wed, 28 May 2025 19:01:01 -0700 Subject: [PATCH] test(general): fix `TestSnapshotNoLeftoverCheckpoints` slowness (#4611) Avoid allocating 1GB of RAM to write a test file. Exclude test from race detector. - Fixes: #4610 - Ref: #4439 nits: - use `require.Greater`. - add types to constants to used them with `require.*`. - factor out function to write file with random data. --- tests/end_to_end_test/norace_test.go | 73 +++++++++++++++++++ tests/end_to_end_test/snapshot_create_test.go | 60 --------------- 2 files changed, 73 insertions(+), 60 deletions(-) create mode 100644 tests/end_to_end_test/norace_test.go diff --git a/tests/end_to_end_test/norace_test.go b/tests/end_to_end_test/norace_test.go new file mode 100644 index 000000000..a829a9c58 --- /dev/null +++ b/tests/end_to_end_test/norace_test.go @@ -0,0 +1,73 @@ +//go:build !race +// +build !race + +package endtoend_test + +import ( + "io" + "math/rand" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/kopia/kopia/internal/clock" + "github.com/kopia/kopia/internal/testutil" + "github.com/kopia/kopia/tests/clitestutil" + "github.com/kopia/kopia/tests/testenv" +) + +// Exclude tests below from the -race detection test, because they are resource +// intensive and way too slow. + +func TestSnapshotNoLeftoverCheckpoints(t *testing.T) { + // 1 GiB of data seems to be enough for the snapshot time to exceed one second. + const ( + fileSize = int64(1) << 30 + checkpointInterval = "1s" + checkpointIntervalSeconds = float64(1) + ) + + t.Parallel() + + runner := testenv.NewInProcRunner(t) + e := testenv.NewCLITest(t, testenv.RepoFormatNotImportant, runner) + + defer e.RunAndExpectSuccess(t, "repo", "disconnect") + + e.RunAndExpectSuccess(t, "repo", "create", "filesystem", "--path", e.RepoDir) + + baseDir := testutil.TempDirectory(t) + writeRandomFile(t, filepath.Join(baseDir, "foo"), fileSize) + + startTime := clock.Now() + + e.RunAndExpectSuccess(t, "snapshot", "create", baseDir, "--checkpoint-interval", checkpointInterval) + + require.Greater(t, clock.Now().Sub(startTime).Seconds(), checkpointIntervalSeconds) + + // This exploits the implementation detail of `ListSnapshotsAndExpectSuccess`, that it does + // not sanitize `targets` to exclude flags. + si := clitestutil.ListSnapshotsAndExpectSuccess(t, e, "--incomplete", baseDir) + require.Len(t, si, 1) + require.Len(t, si[0].Snapshots, 1) + require.False(t, si[0].Snapshots[0].Incomplete) +} + +func writeRandomFile(t *testing.T, name string, fileSize int64) { + t.Helper() + + f, err := os.Create(name) + + require.NoError(t, err) + require.NotNil(t, f) + + defer func() { + require.NoError(t, f.Close()) + }() + + n, err := io.CopyN(f, rand.New(rand.NewSource(0)), fileSize) + require.NoError(t, err) + require.Equal(t, fileSize, n) +} diff --git a/tests/end_to_end_test/snapshot_create_test.go b/tests/end_to_end_test/snapshot_create_test.go index 8ffef9121..400ab9b93 100644 --- a/tests/end_to_end_test/snapshot_create_test.go +++ b/tests/end_to_end_test/snapshot_create_test.go @@ -1,7 +1,6 @@ package endtoend_test import ( - "math/rand" "os" "path" "path/filepath" @@ -18,7 +17,6 @@ "github.com/kopia/kopia/cli" "github.com/kopia/kopia/internal/cachedir" - "github.com/kopia/kopia/internal/clock" "github.com/kopia/kopia/internal/testutil" "github.com/kopia/kopia/snapshot" "github.com/kopia/kopia/snapshot/policy" @@ -801,61 +799,3 @@ func TestSnapshotCreateWithAllAndPath(t *testing.T) { e.RunAndExpectSuccess(t, "snapshot", "create", sharedTestDataDir2) e.RunAndExpectFailure(t, "snapshot", "create", sharedTestDataDir1, "--all") } - -func TestSnapshotNoLeftoverCheckpoints(t *testing.T) { - // 1 GiB of data seems to be enough for the snapshot time to exceed one second. - const ( - fileSize = 1 << 30 - checkpointInterval = "1s" - checkpointIntervalSeconds = 1 - ) - - t.Parallel() - - runner := testenv.NewInProcRunner(t) - e := testenv.NewCLITest(t, testenv.RepoFormatNotImportant, runner) - - defer e.RunAndExpectSuccess(t, "repo", "disconnect") - - e.RunAndExpectSuccess(t, "repo", "create", "filesystem", "--path", e.RepoDir) - - baseDir := testutil.TempDirectory(t) - files := []testFileEntry{ - { - Name: "/foo", - Content: []string{ - generateRandomLetters(fileSize), - }, - }, - } - - require.NoError(t, createFileStructure(baseDir, files)) - - startTime := clock.Now() - - e.RunAndExpectSuccess(t, "snapshot", "create", baseDir, "--checkpoint-interval", checkpointInterval) - - endTime := clock.Now() - - snapshotTimeSurpassedCheckpointInterval := endTime.Sub(startTime).Seconds() > checkpointIntervalSeconds - require.True(t, snapshotTimeSurpassedCheckpointInterval) - - // This exploits the implementation detail of `ListSnapshotsAndExpectSuccess`, that it does - // not sanitize `targets` to exclude flags. - si := clitestutil.ListSnapshotsAndExpectSuccess(t, e, "--incomplete", baseDir) - require.Len(t, si, 1) - require.Len(t, si[0].Snapshots, 1) - require.False(t, si[0].Snapshots[0].Incomplete) -} - -// https://stackoverflow.com/a/31832326 -func generateRandomLetters(n int) string { - const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - - b := make([]byte, n) - for i := range b { - b[i] = letterBytes[rand.Int63()%int64(len(letterBytes))] - } - - return string(b) -}