Skip to content

Commit c781a17

Browse files
committed
Add clock implementation to vfs and tsc test
1 parent b09de27 commit c781a17

File tree

3 files changed

+86
-22
lines changed

3 files changed

+86
-22
lines changed

internal/execute/testsys_test.go

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"slices"
1010
"strconv"
1111
"strings"
12+
"sync"
1213
"time"
1314

1415
"github.com/microsoft/typescript-go/internal/collections"
@@ -54,6 +55,26 @@ interface Symbol {
5455
declare const console: { log(msg: any): void; };
5556
`)
5657

58+
type TestClock struct {
59+
start time.Time
60+
now time.Time
61+
nowMu sync.Mutex
62+
}
63+
64+
func (t *TestClock) Now() time.Time {
65+
t.nowMu.Lock()
66+
defer t.nowMu.Unlock()
67+
if t.now.IsZero() {
68+
t.now = t.start
69+
}
70+
t.now = t.now.Add(1 * time.Second) // Simulate some time passing
71+
return t.now
72+
}
73+
74+
func (t *TestClock) SinceStart() time.Duration {
75+
return t.Now().Sub(t.start)
76+
}
77+
5778
func newTestSys(tscInput *tscInput) *testSys {
5879
cwd := tscInput.cwd
5980
if cwd == "" {
@@ -64,10 +85,11 @@ func newTestSys(tscInput *tscInput) *testSys {
6485
libPath = tscInput.windowsStyleRoot + libPath[1:]
6586
}
6687
currentWrite := &strings.Builder{}
88+
clock := &TestClock{start: time.Now()}
6789
sys := &testSys{
6890
fs: &incrementaltestutil.FsHandlingBuildInfo{
6991
FS: &testFs{
70-
FS: vfstest.FromMap(tscInput.files, !tscInput.ignoreCase),
92+
FS: vfstest.FromMapWithClock(tscInput.files, !tscInput.ignoreCase, clock),
7193
},
7294
},
7395
defaultLibraryPath: libPath,
@@ -77,7 +99,7 @@ func newTestSys(tscInput *tscInput) *testSys {
7799
UseCaseSensitiveFileNames: !tscInput.ignoreCase,
78100
CurrentDirectory: cwd,
79101
}, currentWrite),
80-
start: time.Now(),
102+
clock: clock,
81103
env: tscInput.env,
82104
}
83105

@@ -113,8 +135,7 @@ type testSys struct {
113135
defaultLibraryPath string
114136
cwd string
115137
env map[string]string
116-
117-
start time.Time
138+
clock *TestClock
118139
}
119140

120141
var (
@@ -123,12 +144,11 @@ var (
123144
)
124145

125146
func (s *testSys) Now() time.Time {
126-
// todo: make a "test time" structure
127-
return time.Now()
147+
return s.clock.Now()
128148
}
129149

130150
func (s *testSys) SinceStart() time.Duration {
131-
return time.Since(s.start)
151+
return s.clock.SinceStart()
132152
}
133153

134154
func (s *testSys) FS() vfs.FS {
@@ -143,6 +163,10 @@ func (s *testSys) fsFromFileMap() iovfs.FsWithSys {
143163
return s.testFs().FS.(iovfs.FsWithSys)
144164
}
145165

166+
func (s *testSys) mapFs() *vfstest.MapFS {
167+
return s.fsFromFileMap().FSys().(*vfstest.MapFS)
168+
}
169+
146170
func (s *testSys) ensureLibPathExists(path string) {
147171
path = s.defaultLibraryPath + "/" + path
148172
if _, ok := s.fsFromFileMap().ReadFile(path); !ok {
@@ -368,7 +392,7 @@ func (s *testSys) baselineFSwithDiff(baseline io.Writer) {
368392

369393
fileInfo := d.Type()
370394
if fileInfo&fs.ModeSymlink != 0 {
371-
target, ok := s.fsFromFileMap().FSys().(*vfstest.MapFS).GetTargetOfSymlink(path)
395+
target, ok := s.mapFs().GetTargetOfSymlink(path)
372396
if !ok {
373397
panic("Failed to resolve symlink target: " + path)
374398
}

internal/vfs/vfstest/vfstest.go

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,25 @@ type MapFS struct {
2828
useCaseSensitiveFileNames bool
2929

3030
symlinks map[canonicalPath]canonicalPath
31+
32+
clock Clock
33+
}
34+
35+
type Clock interface {
36+
Now() time.Time
37+
SinceStart() time.Duration
38+
}
39+
40+
type clockImpl struct {
41+
start time.Time
42+
}
43+
44+
func (c *clockImpl) Now() time.Time {
45+
return time.Now()
46+
}
47+
48+
func (c *clockImpl) SinceStart() time.Duration {
49+
return time.Since(c.start)
3150
}
3251

3352
var (
@@ -47,6 +66,16 @@ type sys struct {
4766
// without trailing directory separators.
4867
// The paths must be all POSIX-style or all Windows-style, but not both.
4968
func FromMap[File any](m map[string]File, useCaseSensitiveFileNames bool) vfs.FS {
69+
return FromMapWithClock(m, useCaseSensitiveFileNames, &clockImpl{start: time.Now()})
70+
}
71+
72+
// FromMap creates a new [vfs.FS] from a map of paths to file contents.
73+
// Those file contents may be strings, byte slices, or [fstest.MapFile]s.
74+
//
75+
// The paths must be normalized absolute paths according to the tspath package,
76+
// without trailing directory separators.
77+
// The paths must be all POSIX-style or all Windows-style, but not both.
78+
func FromMapWithClock[File any](m map[string]File, useCaseSensitiveFileNames bool, clock Clock) vfs.FS {
5079
posix := false
5180
windows := false
5281

@@ -67,17 +96,23 @@ func FromMap[File any](m map[string]File, useCaseSensitiveFileNames bool) vfs.FS
6796
}
6897

6998
mfs := make(fstest.MapFS, len(m))
70-
for p, f := range m {
99+
// Sorted creation to ensure times are always guaranteed to be in order.
100+
keys := slices.Collect(maps.Keys(m))
101+
slices.SortFunc(keys, comparePathsByParts)
102+
for _, p := range keys {
103+
f := m[p]
71104
checkPath(p)
72105

73106
var file *fstest.MapFile
74107
switch f := any(f).(type) {
75108
case string:
76-
file = &fstest.MapFile{Data: []byte(f)}
109+
file = &fstest.MapFile{Data: []byte(f), ModTime: clock.Now()}
77110
case []byte:
78-
file = &fstest.MapFile{Data: f}
111+
file = &fstest.MapFile{Data: f, ModTime: clock.Now()}
79112
case *fstest.MapFile:
80-
file = f
113+
fCopy := *f
114+
fCopy.ModTime = clock.Now()
115+
file = &fCopy
81116
default:
82117
panic(fmt.Sprintf("invalid file type %T", f))
83118
}
@@ -100,13 +135,17 @@ func FromMap[File any](m map[string]File, useCaseSensitiveFileNames bool) vfs.FS
100135
panic("mixed posix and windows paths")
101136
}
102137

103-
return iovfs.From(convertMapFS(mfs, useCaseSensitiveFileNames), useCaseSensitiveFileNames)
138+
return iovfs.From(convertMapFS(mfs, useCaseSensitiveFileNames, clock), useCaseSensitiveFileNames)
104139
}
105140

106-
func convertMapFS(input fstest.MapFS, useCaseSensitiveFileNames bool) *MapFS {
141+
func convertMapFS(input fstest.MapFS, useCaseSensitiveFileNames bool, clock Clock) *MapFS {
142+
if clock == nil {
143+
clock = &clockImpl{start: time.Now()}
144+
}
107145
m := &MapFS{
108146
m: make(fstest.MapFS, len(input)),
109147
useCaseSensitiveFileNames: useCaseSensitiveFileNames,
148+
clock: clock,
110149
}
111150

112151
// Verify that the input is well-formed.
@@ -320,7 +359,8 @@ func (m *MapFS) mkdirAll(p string, perm fs.FileMode) error {
320359

321360
for _, dir := range toCreate {
322361
m.setEntry(dir, m.getCanonicalPath(dir), fstest.MapFile{
323-
Mode: fs.ModeDir | perm&^umask,
362+
Mode: fs.ModeDir | perm&^umask,
363+
ModTime: m.clock.Now(),
324364
})
325365
}
326366

@@ -482,7 +522,7 @@ func (m *MapFS) WriteFile(path string, data []byte, perm fs.FileMode) error {
482522

483523
m.setEntry(path, cp, fstest.MapFile{
484524
Data: data,
485-
ModTime: time.Now(),
525+
ModTime: m.clock.Now(),
486526
Mode: perm &^ umask,
487527
})
488528

internal/vfs/vfstest/vfstest_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func TestInsensitive(t *testing.T) {
3434
Data: contents,
3535
Sys: 1234,
3636
},
37-
}, false /*useCaseSensitiveFileNames*/)
37+
}, false /*useCaseSensitiveFileNames*/, nil)
3838

3939
sensitive, err := fs.ReadFile(vfs, "foo/bar/baz")
4040
assert.NilError(t, err)
@@ -97,7 +97,7 @@ func TestInsensitiveUpper(t *testing.T) {
9797
Data: contents,
9898
Sys: 1234,
9999
},
100-
}, false /*useCaseSensitiveFileNames*/)
100+
}, false /*useCaseSensitiveFileNames*/, nil)
101101

102102
sensitive, err := fs.ReadFile(vfs, "foo/bar/baz")
103103
assert.NilError(t, err)
@@ -142,7 +142,7 @@ func TestSensitive(t *testing.T) {
142142
Data: contents,
143143
Sys: 1234,
144144
},
145-
}, true /*useCaseSensitiveFileNames*/)
145+
}, true /*useCaseSensitiveFileNames*/, nil)
146146

147147
sensitive, err := fs.ReadFile(vfs, "foo/bar/baz")
148148
assert.NilError(t, err)
@@ -170,7 +170,7 @@ func TestSensitiveDuplicatePath(t *testing.T) {
170170
}
171171

172172
testutil.AssertPanics(t, func() {
173-
convertMapFS(testfs, false /*useCaseSensitiveFileNames*/)
173+
convertMapFS(testfs, false /*useCaseSensitiveFileNames*/, nil)
174174
}, `duplicate path: "Foo" and "foo" have the same canonical path`)
175175
}
176176

@@ -186,7 +186,7 @@ func TestInsensitiveDuplicatePath(t *testing.T) {
186186
},
187187
}
188188

189-
convertMapFS(testfs, true /*useCaseSensitiveFileNames*/)
189+
convertMapFS(testfs, true /*useCaseSensitiveFileNames*/, nil)
190190
}
191191

192192
func dirEntriesToNames(entries []fs.DirEntry) []string {
@@ -303,7 +303,7 @@ func TestParentDirFile(t *testing.T) {
303303
}
304304

305305
testutil.AssertPanics(t, func() {
306-
convertMapFS(testfs, false /*useCaseSensitiveFileNames*/)
306+
convertMapFS(testfs, false /*useCaseSensitiveFileNames*/, nil)
307307
}, `failed to create intermediate directories for "foo/oops": mkdir "foo": path exists but is not a directory`)
308308
}
309309

0 commit comments

Comments
 (0)