internal/testutil/fsbaselineutil/differ.go (106 lines of code) (raw):

package fsbaselineutil import ( "fmt" "io" "io/fs" "maps" "slices" "time" "github.com/microsoft/typescript-go/internal/collections" "github.com/microsoft/typescript-go/internal/vfs/iovfs" "github.com/microsoft/typescript-go/internal/vfs/vfstest" ) type DiffEntry struct { Content string MTime time.Time IsWritten bool SymlinkTarget string } type Snapshot struct { Snap map[string]*DiffEntry DefaultLibs *collections.SyncSet[string] } type FSDiffer struct { FS iovfs.FsWithSys DefaultLibs func() *collections.SyncSet[string] WrittenFiles *collections.SyncSet[string] serializedDiff *Snapshot } func (d *FSDiffer) MapFs() *vfstest.MapFS { return d.FS.FSys().(*vfstest.MapFS) } func (d *FSDiffer) SerializedDiff() *Snapshot { return d.serializedDiff } func (d *FSDiffer) BaselineFSwithDiff(baseline io.Writer) { // todo: baselines the entire fs, possibly doesn't correctly diff all cases of emitted files, since emit isn't fully implemented and doesn't always emit the same way as strada snap := map[string]*DiffEntry{} diffs := map[string]string{} for path, file := range d.MapFs().Entries() { if file.Mode&fs.ModeSymlink != 0 { target, ok := d.MapFs().GetTargetOfSymlink(path) if !ok { panic("Failed to resolve symlink target: " + path) } newEntry := &DiffEntry{SymlinkTarget: target} snap[path] = newEntry d.addFsEntryDiff(diffs, newEntry, path) continue } else if file.Mode.IsRegular() { newEntry := &DiffEntry{Content: string(file.Data), MTime: file.ModTime, IsWritten: d.WrittenFiles.Has(path)} snap[path] = newEntry d.addFsEntryDiff(diffs, newEntry, path) } } if d.serializedDiff != nil { for path := range d.serializedDiff.Snap { if fileInfo := d.MapFs().GetFileInfo(path); fileInfo == nil { // report deleted d.addFsEntryDiff(diffs, nil, path) } } } var defaultLibs collections.SyncSet[string] if d.DefaultLibs != nil && d.DefaultLibs() != nil { d.DefaultLibs().Range(func(libPath string) bool { defaultLibs.Add(libPath) return true }) } d.serializedDiff = &Snapshot{ Snap: snap, DefaultLibs: &defaultLibs, } diffKeys := slices.Collect(maps.Keys(diffs)) slices.Sort(diffKeys) for _, path := range diffKeys { fmt.Fprint(baseline, "//// ["+path+"] ", diffs[path], "\n") } fmt.Fprintln(baseline) *d.WrittenFiles = collections.SyncSet[string]{} // Reset written files after baseline } func (d *FSDiffer) addFsEntryDiff(diffs map[string]string, newDirContent *DiffEntry, path string) { var oldDirContent *DiffEntry var defaultLibs *collections.SyncSet[string] if d.serializedDiff != nil { oldDirContent = d.serializedDiff.Snap[path] defaultLibs = d.serializedDiff.DefaultLibs } // todo handle more cases of fs changes if oldDirContent == nil { if d.DefaultLibs == nil || d.DefaultLibs() == nil || !d.DefaultLibs().Has(path) { if newDirContent.SymlinkTarget != "" { diffs[path] = "-> " + newDirContent.SymlinkTarget + " *new*" } else { diffs[path] = "*new* \n" + newDirContent.Content } } } else if newDirContent == nil { diffs[path] = "*deleted*" } else if newDirContent.Content != oldDirContent.Content { diffs[path] = "*modified* \n" + newDirContent.Content } else if newDirContent.IsWritten { diffs[path] = "*rewrite with same content*" } else if newDirContent.MTime != oldDirContent.MTime { diffs[path] = "*mTime changed*" } else if defaultLibs != nil && defaultLibs.Has(path) && d.DefaultLibs != nil && d.DefaultLibs() != nil && !d.DefaultLibs().Has(path) { // Lib file that was read diffs[path] = "*Lib*\n" + newDirContent.Content } }