Compare commits
2
Commits
3dd2ebdea4
...
b60cb5a0bd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b60cb5a0bd | ||
|
|
a5f218e42a |
@@ -1369,6 +1369,51 @@ func TestGenerateSeveralPathsDefaultOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// testLink is the name of the symlink to a tree that the
|
||||||
|
// DirectoryNamedThroughSymlink tests make in a temp dir.
|
||||||
|
const testLink = "link"
|
||||||
|
|
||||||
|
// TestGenerateDirectoryNamedThroughSymlink runs gen on a directory named
|
||||||
|
// through a symlink, given as the argument or as the working directory: the
|
||||||
|
// manifest lists the files in the directory the symlink points to, and
|
||||||
|
// leaves out a symlink inside it, as gen does without --follow-symlinks.
|
||||||
|
//
|
||||||
|
//nolint:paralleltest // changes the process-global working directory
|
||||||
|
func TestGenerateDirectoryNamedThroughSymlink(t *testing.T) {
|
||||||
|
// Paths are relative to a temp dir holding data and link, a symlink to
|
||||||
|
// data.
|
||||||
|
for name, tc := range map[string]struct {
|
||||||
|
workDir string
|
||||||
|
args []string
|
||||||
|
}{
|
||||||
|
"argument": {".", []string{testLink}},
|
||||||
|
"working directory": {testLink, nil},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
root := t.TempDir()
|
||||||
|
data := filepath.Join(root, "data")
|
||||||
|
|
||||||
|
fs := afero.NewOsFs()
|
||||||
|
writeTestTree(t, fs, data)
|
||||||
|
require.NoError(t,
|
||||||
|
os.Symlink(testFileTxt, filepath.Join(data, "alias.txt")))
|
||||||
|
require.NoError(t, os.Symlink(data, filepath.Join(root, testLink)))
|
||||||
|
|
||||||
|
// t.Chdir sets PWD to the path it is given, as a shell does, and
|
||||||
|
// os.Getwd returns PWD when it names the working directory.
|
||||||
|
t.Chdir(filepath.Join(root, tc.workDir))
|
||||||
|
|
||||||
|
opts := testOpts(slices.Concat(
|
||||||
|
[]string{testApp, cmdGenerate, "-q"}, tc.args,
|
||||||
|
), fs)
|
||||||
|
require.Equal(t, 0, runCLI(opts), "stderr: %s", testStderr(t, opts))
|
||||||
|
|
||||||
|
assert.ElementsMatch(t, []string{testFileTxt, "sub/nested.txt"},
|
||||||
|
manifestPaths(t, fs, filepath.Join(data, defaultManifestName)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestGenerateBytesDoNotDependOnOutput runs gen --seed on one tree, each
|
// TestGenerateBytesDoNotDependOnOutput runs gen --seed on one tree, each
|
||||||
// time writing to another file, over a file already there and beside an
|
// time writing to another file, over a file already there and beside an
|
||||||
// earlier run's temp file: neither is listed, and what is listed depends
|
// earlier run's temp file: neither is listed, and what is listed depends
|
||||||
|
|||||||
@@ -379,6 +379,14 @@ func (mfa *CLIApp) freshenScan(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The walk does not follow a symlink at its top, so a base directory
|
||||||
|
// named through one is resolved first. If that fails, the base is
|
||||||
|
// walked as named and the walk reports the problem.
|
||||||
|
resolved, err := filepath.EvalSymlinks(absBase)
|
||||||
|
if err == nil {
|
||||||
|
absBase = resolved
|
||||||
|
}
|
||||||
|
|
||||||
scanner := &freshenScanner{
|
scanner := &freshenScanner{
|
||||||
fs: mfa.Fs,
|
fs: mfa.Fs,
|
||||||
absBase: absBase,
|
absBase: absBase,
|
||||||
@@ -390,7 +398,7 @@ func (mfa *CLIApp) freshenScan(
|
|||||||
existingByPath: existingByPath,
|
existingByPath: existingByPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := afero.Walk(mfa.Fs, absBase, scanner.walk)
|
err = afero.Walk(mfa.Fs, absBase, scanner.walk)
|
||||||
|
|
||||||
if showProgress {
|
if showProgress {
|
||||||
log.ProgressDone()
|
log.ProgressDone()
|
||||||
|
|||||||
@@ -336,6 +336,49 @@ func TestFreshenResolvesEntriesAgainstManifestDirectory(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestFreshenDirectoryNamedThroughSymlink adds a file to a tree after gen
|
||||||
|
// made its manifest, then freshens it with the tree named through a symlink,
|
||||||
|
// given as the argument or as the working directory: the manifest lists the
|
||||||
|
// files in the tree, and leaves out a symlink inside it, as freshen does
|
||||||
|
// without --follow-symlinks.
|
||||||
|
//
|
||||||
|
//nolint:paralleltest // changes the process-global working directory
|
||||||
|
func TestFreshenDirectoryNamedThroughSymlink(t *testing.T) {
|
||||||
|
// Paths are relative to a temp dir holding link, a symlink to the tree.
|
||||||
|
for name, tc := range map[string]struct {
|
||||||
|
workDir string
|
||||||
|
args []string
|
||||||
|
}{
|
||||||
|
"argument": {".", []string{testLink}},
|
||||||
|
"working directory": {testLink, nil},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
fs := afero.NewOsFs()
|
||||||
|
tree, manifestPath := setupFreshenDir(t, fs,
|
||||||
|
map[string]string{testFileTxt: "in the tree"})
|
||||||
|
writeTestFile(t, fs, filepath.Join(tree, "later.txt"), "added later")
|
||||||
|
require.NoError(t,
|
||||||
|
os.Symlink(testFileTxt, filepath.Join(tree, "alias.txt")))
|
||||||
|
|
||||||
|
root := t.TempDir()
|
||||||
|
require.NoError(t, os.Symlink(tree, filepath.Join(root, testLink)))
|
||||||
|
|
||||||
|
// t.Chdir sets PWD to the path it is given, as a shell does, and
|
||||||
|
// os.Getwd returns PWD when it names the working directory.
|
||||||
|
t.Chdir(filepath.Join(root, tc.workDir))
|
||||||
|
|
||||||
|
opts := testOpts(slices.Concat(
|
||||||
|
[]string{testApp, cmdFreshen, "-q"}, tc.args,
|
||||||
|
), fs)
|
||||||
|
require.Equal(t, 0, runCLI(opts), "stderr: %s", testStderr(t, opts))
|
||||||
|
|
||||||
|
assertManifestLists(t, fs, tree, manifestPath, map[string]string{
|
||||||
|
testFileTxt: "in the tree", "later.txt": "added later",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestFreshenRecordEntryMtimePresence pins the behavior of recordEntry
|
// TestFreshenRecordEntryMtimePresence pins the behavior of recordEntry
|
||||||
// with respect to MFFilePath.Mtime, which is a message pointer with
|
// with respect to MFFilePath.Mtime, which is a message pointer with
|
||||||
// proto3 field presence and may legitimately be absent.
|
// proto3 field presence and may legitimately be absent.
|
||||||
|
|||||||
+11
-13
@@ -139,25 +139,15 @@ func (s *Scanner) EnumerateFile(filePath string) error {
|
|||||||
return s.enumerateFileWithInfo(filepath.Base(abs), basePath, info, nil)
|
return s.enumerateFileWithInfo(filepath.Base(abs), basePath, info, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnumeratePath walks a directory path and adds all files to the scanner.
|
// EnumeratePath adds inputPath, a directory or a file, to the scanner as
|
||||||
|
// EnumeratePaths adds each of its paths.
|
||||||
// If progress is non-nil, status updates are sent as files are discovered.
|
// If progress is non-nil, status updates are sent as files are discovered.
|
||||||
// The progress channel is closed when the method returns.
|
// The progress channel is closed when the method returns.
|
||||||
func (s *Scanner) EnumeratePath(
|
func (s *Scanner) EnumeratePath(
|
||||||
inputPath string,
|
inputPath string,
|
||||||
progress chan<- EnumerateStatus,
|
progress chan<- EnumerateStatus,
|
||||||
) error {
|
) error {
|
||||||
if progress != nil {
|
return s.EnumeratePaths(progress, inputPath)
|
||||||
defer close(progress)
|
|
||||||
}
|
|
||||||
|
|
||||||
abs, err := filepath.Abs(inputPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
afs := afero.NewReadOnlyFs(afero.NewBasePathFs(s.fs, abs))
|
|
||||||
|
|
||||||
return s.enumerateFS(afs, abs, progress)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnumeratePaths adds to the scanner the files under each directory path,
|
// EnumeratePaths adds to the scanner the files under each directory path,
|
||||||
@@ -185,6 +175,14 @@ func (s *Scanner) EnumeratePaths(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
|
// The walk does not follow a symlink at its top, so a directory
|
||||||
|
// named through one is resolved first. If that fails, the
|
||||||
|
// directory is walked as named and the walk reports the problem.
|
||||||
|
resolved, evalErr := filepath.EvalSymlinks(abs)
|
||||||
|
if evalErr == nil {
|
||||||
|
abs = resolved
|
||||||
|
}
|
||||||
|
|
||||||
afs := afero.NewReadOnlyFs(afero.NewBasePathFs(s.fs, abs))
|
afs := afero.NewReadOnlyFs(afero.NewBasePathFs(s.fs, abs))
|
||||||
err = s.enumerateFS(afs, abs, progress)
|
err = s.enumerateFS(afs, abs, progress)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -118,6 +118,27 @@ func TestScannerEnumeratePathWithProgress(t *testing.T) {
|
|||||||
assert.Equal(t, FileSize(6), final.BytesFound)
|
assert.Equal(t, FileSize(6), final.BytesFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestScannerEnumeratePathFile gives EnumeratePath a file: it is listed
|
||||||
|
// by its name, as EnumerateFile lists it, and the manifest can be built.
|
||||||
|
func TestScannerEnumeratePathFile(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
fs := afero.NewMemMapFs()
|
||||||
|
require.NoError(t, fs.MkdirAll("/dir", 0o755))
|
||||||
|
require.NoError(t, afero.WriteFile(fs, "/dir/one.txt", []byte("1"), 0o644))
|
||||||
|
|
||||||
|
s := NewScannerWithOptions(&ScannerOptions{Fs: fs})
|
||||||
|
require.NoError(t, s.EnumeratePath("/dir/one.txt", nil))
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
require.NoError(t, s.ToManifest(context.Background(), &buf, nil))
|
||||||
|
|
||||||
|
m, err := NewManifestFromReader(&buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, m.Files(), 1)
|
||||||
|
assert.Equal(t, "one.txt", m.Files()[0].GetPath())
|
||||||
|
}
|
||||||
|
|
||||||
func TestScannerEnumeratePaths(t *testing.T) {
|
func TestScannerEnumeratePaths(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user