• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

numtide / treefmt / 17909814735

22 Sep 2025 08:46AM UTC coverage: 38.886% (-0.5%) from 39.36%
17909814735

push

github

web-flow
Merge pull request #635 from jfly/fix-flaky-tests

chore: fix flaky tests

0 of 24 new or added lines in 1 file covered. (0.0%)

775 of 1993 relevant lines covered (38.89%)

17.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/test/test.go
1
package test
2

3
import (
4
        "fmt"
5
        "os"
6
        "path/filepath"
7
        "testing"
8
        "time"
9

10
        "github.com/BurntSushi/toml"
11
        "github.com/numtide/treefmt/v2/config"
12
        cp "github.com/otiai10/copy"
13
        "github.com/stretchr/testify/require"
14
        "golang.org/x/sys/unix"
15
)
16

17
func SetenvXdgConfigDir(t *testing.T) {
×
18
        t.Helper()
×
19

×
20
        configPath, err := filepath.Abs("../test/config")
×
21
        if err != nil {
×
22
                t.Fatalf("failed to get the path to the config directory: %v", err)
×
23
        }
×
24

25
        t.Setenv("XDG_CONFIG_HOME", configPath)
×
26
}
27

28
func WriteConfig(t *testing.T, path string, cfg *config.Config) {
×
29
        t.Helper()
×
30

×
NEW
31
        oldInfo, err := os.Lstat(path)
×
NEW
32

×
NEW
33
        switch {
×
NEW
34
        case os.IsNotExist(err):
×
NEW
35
                // It's fine if there was no old config file, the new file is guaranteed to appear new =)
×
NEW
36
                oldInfo = nil
×
NEW
37
        case err != nil:
×
NEW
38
                t.Fatalf("failed to stat old config file: %v", path)
×
39
        }
40

41
        f, err := os.Create(path)
×
42
        if err != nil {
×
43
                t.Fatalf("failed to create a new config file: %v", err)
×
44
        }
×
45

46
        encoder := toml.NewEncoder(f)
×
47
        if err = encoder.Encode(cfg); err != nil {
×
48
                t.Fatalf("failed to write to config file: %v", err)
×
49
        }
×
50

51
        // Ensure the modtime of the config file always increases
52
        // (even if this requires setting it to the future!)
53
        // If we don't do this, we end up with flaky tests that behave differently
54
        // depending on if they run quickly enough for the modtime to stay constant throughout the test.
NEW
55
        newInfo, err := os.Lstat(path)
×
NEW
56
        if err != nil {
×
NEW
57
                t.Fatalf("failed to create a new config file: %v", err)
×
NEW
58
        }
×
59
        // Note: we're comparing `Unix()` (which is only 1 second granularity) for consistency with
60
        // `walk.go::formatSignature`.
NEW
61
        if oldInfo != nil && oldInfo.ModTime().Unix() == newInfo.ModTime().Unix() {
×
NEW
62
                // Ideally we wouldn't change the atime at all, but it's hard to fetch
×
NEW
63
                // the original atime in a cross-platform manner.
×
NEW
64
                newAtime := time.Now()
×
NEW
65

×
NEW
66
                // Increase the mtime so it's different.
×
NEW
67
                newMtime := oldInfo.ModTime().Add(time.Second)
×
NEW
68

×
NEW
69
                err = Lutimes(t, path, newAtime, newMtime)
×
NEW
70
                if err != nil {
×
NEW
71
                        t.Fatalf("failed to change file times: %v", err)
×
NEW
72
                }
×
73
        }
74
}
75

76
func TempExamples(t *testing.T) string {
×
77
        t.Helper()
×
78
        tempDir := t.TempDir()
×
79
        TempExamplesInDir(t, tempDir)
×
80

×
81
        return tempDir
×
82
}
×
83

84
func TempExamplesInDir(t *testing.T, dir string) {
×
85
        t.Helper()
×
86
        require.NoError(t, cp.Copy("../test/examples", dir), "failed to copy test data to dir")
×
87

×
88
        // we have second precision mod time tracking, so we wait a second before returning, so we don't trigger false
×
89
        // positives for things like fail on change
×
90
        time.Sleep(time.Second)
×
91
}
×
92

93
func TempFile(t *testing.T, dir string, pattern string, contents *string) *os.File {
×
94
        t.Helper()
×
95

×
96
        file, err := os.CreateTemp(dir, pattern)
×
97
        require.NoError(t, err, "failed to create temp file")
×
98

×
99
        if contents == nil {
×
100
                return file
×
101
        }
×
102

103
        _, err = file.WriteString(*contents)
×
104
        require.NoError(t, err, "failed to write contents to temp file")
×
105
        require.NoError(t, file.Close(), "failed to close temp file")
×
106

×
107
        file, err = os.Open(file.Name())
×
108
        require.NoError(t, err, "failed to open temp file")
×
109

×
110
        return file
×
111
}
112

113
// Lutimes is a convenience wrapper for using unix.Lutimes
114
// TODO: this will need to be adapted if we support Windows.
115
func Lutimes(t *testing.T, path string, atime time.Time, mtime time.Time) error {
×
116
        t.Helper()
×
117

×
118
        var utimes [2]unix.Timeval
×
119
        utimes[0] = unix.NsecToTimeval(atime.UnixNano())
×
120
        utimes[1] = unix.NsecToTimeval(mtime.UnixNano())
×
121

×
122
        // Change the timestamps of the path. If it's a symlink, it updates the symlink's timestamps, not the target's.
×
123
        err := unix.Lutimes(path, utimes[0:])
×
124
        if err != nil {
×
125
                return fmt.Errorf("failed to change times: %w", err)
×
126
        }
×
127

128
        return nil
×
129
}
130

131
func LutimesBump(t *testing.T, path string, atime time.Duration, mtime time.Duration) {
×
132
        t.Helper()
×
133

×
134
        now := time.Now()
×
135
        newAtime := now.Add(atime)
×
136
        newMtime := now.Add(mtime)
×
137

×
138
        err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
×
139
                if err != nil || info.IsDir() {
×
140
                        return err
×
141
                }
×
142

143
                return Lutimes(t, path, newAtime, newMtime)
×
144
        })
145
        if err != nil {
×
146
                t.Fatalf("failed to bump modtimes: %v", err)
×
147
        }
×
148
}
149

150
// ChangeWorkDir changes the current working directory for the duration of the test.
151
// The original directory is restored when the test ends.
152
func ChangeWorkDir(t *testing.T, dir string) {
×
153
        t.Helper()
×
154

×
155
        // capture current cwd, so we can replace it after the test is finished
×
156
        cwd, err := os.Getwd()
×
157
        if err != nil {
×
158
                t.Fatal(fmt.Errorf("failed to get current working directory: %w", err))
×
159
        }
×
160

161
        t.Cleanup(func() {
×
162
                // return to the previous working directory
×
163
                t.Chdir(cwd)
×
164
        })
×
165

166
        // change to the new directory
167
        t.Chdir(dir)
×
168
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc