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

feat: don't create multiple snapshots when -test.count>1 #90

Merged
merged 1 commit into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ format: ## Format code
golines . -w

test: ## Run tests
go test -race -count=1 -cover ./...
go test -race -count=10 -shuffle on -cover ./...

test-verbose: ## Run tests with verbose output
go test -race -count=1 -v -cover ./...
go test -race -count=10 -shuffle on -v -cover ./...
15 changes: 11 additions & 4 deletions snaps/clean.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"slices"
"strconv"
"strings"
"sync"
"testing"
Expand Down Expand Up @@ -82,12 +83,14 @@ func Clean(m *testing.M, opts ...CleanOpts) {
// This is just for making sure Clean is called from TestMain
_ = m
runOnly := flag.Lookup("test.run").Value.String()
count, _ := strconv.Atoi(flag.Lookup("test.count").Value.String())

obsoleteFiles, usedFiles := examineFiles(testsRegistry.values, runOnly, shouldClean && !isCI)
obsoleteFiles, usedFiles := examineFiles(testsRegistry.cleanup, runOnly, shouldClean && !isCI)
obsoleteTests, err := examineSnaps(
testsRegistry.values,
testsRegistry.cleanup,
usedFiles,
runOnly,
count,
shouldClean && !isCI,
opt.Sort && !isCI,
)
Expand Down Expand Up @@ -200,6 +203,7 @@ func examineSnaps(
registry map[string]map[string]int,
used []string,
runOnly string,
count int,
update,
sort bool,
) ([]string, error) {
Expand All @@ -216,7 +220,7 @@ func examineSnaps(

var hasDiffs bool

registeredTests := occurrences(registry[snapPath])
registeredTests := occurrences(registry[snapPath], count)
s := snapshotScanner(f)

for s.Scan() {
Expand Down Expand Up @@ -405,9 +409,12 @@ e.g

as it means there are 3 snapshots created inside TestAdd
*/
func occurrences(tests map[string]int) set {
func occurrences(tests map[string]int, count int) set {
result := make(set, len(tests))
for testID, counter := range tests {
// divide a test's counter by count (how many times the go test suite ran)
// this gives us how many snapshots were created in a single test run.
counter = counter / count
if counter > 1 {
for i := 1; i <= counter; i++ {
result[fmt.Sprintf("%s - %d", testID, i)] = struct{}{}
Expand Down
61 changes: 42 additions & 19 deletions snaps/clean_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func TestExamineSnaps(t *testing.T) {
filepath.FromSlash(dir2 + "/test2.snap"),
}

obsolete, err := examineSnaps(tests, used, "", shouldUpdate, sort)
obsolete, err := examineSnaps(tests, used, "", 1, shouldUpdate, sort)

test.Equal(t, []string{}, obsolete)
test.NoError(t, err)
Expand All @@ -172,7 +172,7 @@ func TestExamineSnaps(t *testing.T) {
// Removing the test entirely
delete(tests[used[1]], "TestDir2_2/TestSimple")

obsolete, err := examineSnaps(tests, used, "", shouldUpdate, sort)
obsolete, err := examineSnaps(tests, used, "", 1, shouldUpdate, sort)
content1 := test.GetFileContent(t, used[0])
content2 := test.GetFileContent(t, used[1])

Expand Down Expand Up @@ -200,7 +200,7 @@ func TestExamineSnaps(t *testing.T) {
delete(tests[used[0]], "TestDir1_3/TestSimple")
delete(tests[used[1]], "TestDir2_1/TestSimple")

obsolete, err := examineSnaps(tests, used, "", shouldUpdate, sort)
obsolete, err := examineSnaps(tests, used, "", 1, shouldUpdate, sort)
content1 := test.GetFileContent(t, used[0])
content2 := test.GetFileContent(t, used[1])

Expand Down Expand Up @@ -258,7 +258,7 @@ string hello world 2 2 1
filepath.FromSlash(dir2 + "/test2.snap"),
}

obsolete, err := examineSnaps(tests, used, "", shouldUpdate, sort)
obsolete, err := examineSnaps(tests, used, "", 1, shouldUpdate, sort)

test.NoError(t, err)
test.Equal(t, 0, len(obsolete))
Expand Down Expand Up @@ -290,7 +290,7 @@ string hello world 2 2 1
delete(tests[used[0]], "TestDir1_3/TestSimple")
delete(tests[used[1]], "TestDir2_1/TestSimple")

obsolete, err := examineSnaps(tests, used, "", shouldUpdate, sort)
obsolete, err := examineSnaps(tests, used, "", 1, shouldUpdate, sort)

test.NoError(t, err)
test.Equal(t, []string{
Expand All @@ -313,22 +313,45 @@ string hello world 2 2 1
}

func TestOccurrences(t *testing.T) {
tests := map[string]int{
"add": 3,
"subtract": 1,
"divide": 2,
}
t.Run("when count 1", func(t *testing.T) {
tests := map[string]int{
"add": 3,
"subtract": 1,
"divide": 2,
}

expected := set{
"add - 1": {},
"add - 2": {},
"add - 3": {},
"subtract - 1": {},
"divide - 1": {},
"divide - 2": {},
}
expected := set{
"add - 1": {},
"add - 2": {},
"add - 3": {},
"subtract - 1": {},
"divide - 1": {},
"divide - 2": {},
}

test.Equal(t, expected, occurrences(tests, 1))
})

test.Equal(t, expected, occurrences(tests))
t.Run("when count 3", func(t *testing.T) {
tests := map[string]int{
"add": 12,
"subtract": 3,
"divide": 9,
}

expected := set{
"add - 1": {},
"add - 2": {},
"add - 3": {},
"add - 4": {},
"subtract - 1": {},
"divide - 1": {},
"divide - 2": {},
"divide - 3": {},
}

test.Equal(t, expected, occurrences(tests, 3))
})
}

func TestSummary(t *testing.T) {
Expand Down
3 changes: 3 additions & 0 deletions snaps/matchJSON.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ func matchJSON(c *config, t testingT, input any, matchers ...match.JSONMatcher)

snapPath, snapPathRel := snapshotPath(c)
testID := testsRegistry.getTestID(snapPath, t.Name())
t.Cleanup(func() {
testsRegistry.reset(snapPath, t.Name())
})

j, err := validateJSON(input)
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions snaps/matchJSON_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ func TestMatchJSON(t *testing.T) {
test.Equal(t, 2, line)
test.Equal(t, expected, snap)
test.Equal(t, 1, testEvents.items[added])
// clean up function called
test.Equal(t, 0, testsRegistry.running[snapPath]["mock-name"])
test.Equal(t, 1, testsRegistry.cleanup[snapPath]["mock-name"])
})
}
})
Expand Down
4 changes: 4 additions & 0 deletions snaps/matchSnapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ func matchSnapshot(c *config, t testingT, values ...any) {

snapPath, snapPathRel := snapshotPath(c)
testID := testsRegistry.getTestID(snapPath, t.Name())
t.Cleanup(func() {
testsRegistry.reset(snapPath, t.Name())
})

snapshot := takeSnapshot(values)
prevSnapshot, line, err := getPrevSnapshot(testID, snapPath)
if errors.Is(err, errSnapNotFound) {
Expand Down
3 changes: 3 additions & 0 deletions snaps/matchSnapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ func TestMatchSnapshot(t *testing.T) {
test.GetFileContent(t, snapPath),
)
test.Equal(t, 1, testEvents.items[added])
// clean up function called
test.Equal(t, 0, testsRegistry.running[snapPath]["mock-name"])
test.Equal(t, 1, testsRegistry.cleanup[snapPath]["mock-name"])
})

t.Run("if it's running on ci should skip", func(t *testing.T) {
Expand Down
25 changes: 18 additions & 7 deletions snaps/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ This also helps with keeping track with obsolete snaps
map[snap path]: map[testname]: <number of snapshots>
*/
type syncRegistry struct {
values map[string]map[string]int
running map[string]map[string]int
cleanup map[string]map[string]int
sync.Mutex
}

Expand All @@ -95,21 +96,31 @@ type syncRegistry struct {
func (s *syncRegistry) getTestID(snapPath, testName string) string {
s.Lock()

if _, exists := s.values[snapPath]; !exists {
s.values[snapPath] = make(map[string]int)
if _, exists := s.running[snapPath]; !exists {
s.running[snapPath] = make(map[string]int)
s.cleanup[snapPath] = make(map[string]int)
}

s.values[snapPath][testName]++
c := s.values[snapPath][testName]
s.running[snapPath][testName]++
s.cleanup[snapPath][testName]++
c := s.running[snapPath][testName]
s.Unlock()

return fmt.Sprintf("[%s - %d]", testName, c)
}

// reset sets only the number of running registry for the given test to 0.
func (s *syncRegistry) reset(snapPath, testName string) {
s.Lock()
s.running[snapPath][testName] = 0
s.Unlock()
}

func newRegistry() *syncRegistry {
return &syncRegistry{
values: make(map[string]map[string]int),
Mutex: sync.Mutex{},
running: make(map[string]map[string]int),
cleanup: make(map[string]map[string]int),
Mutex: sync.Mutex{},
}
}

Expand Down
24 changes: 24 additions & 0 deletions snaps/snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,30 @@ func TestSyncRegistry(t *testing.T) {

test.Equal(t, "[test - 6]", registry.getTestID("/file", "test"))
test.Equal(t, "[test-v2 - 1]", registry.getTestID("/file", "test-v2"))
test.Equal(t, registry.cleanup, registry.running)
})

t.Run("should reset running registry", func(t *testing.T) {
wg := sync.WaitGroup{}
registry := newRegistry()

for i := 0; i < 100; i++ {
wg.Add(1)

go func() {
registry.getTestID("/file", "test")
wg.Done()
}()
}

wg.Wait()

registry.reset("/file", "test")

// running registry start from 0 again
test.Equal(t, "[test - 1]", registry.getTestID("/file", "test"))
// cleanup registry still has 101
test.Equal(t, 101, registry.cleanup["/file"]["test"])
})
}

Expand Down