Never list index.mf at the top of the tree in gen and freshen (closes #233)
check / check (push) Waiting to run
check / check (push) Waiting to run
fetch saves the manifest as index.mf at the top of the tree and refuses a manifest that lists a file there. gen given --output, and freshen with its manifest outside the base directory, listed such a file, so they wrote manifests fetch would not take. Both now leave out each file the manifest would list as index.mf: one at the top of the tree, or a file given to gen by that name. The rule goes by that path alone and looks up no file, so a symlink named index.mf never hides the file it points to. index.mf in a subdirectory is still listed. The scanner in package mfer gets an option to leave out files by the path they would be listed by. Model: opus-5-5
This commit was merged in pull request #234.
This commit is contained in:
+150
-14
@@ -27,19 +27,20 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
testApp = "mfer"
|
||||
testGitrev = "abc123"
|
||||
testDir = "/testdir"
|
||||
testFile1 = "/testdir/file1.txt"
|
||||
testMF = "/testdir/test.mf"
|
||||
testOutput = "/output.mf"
|
||||
testManifest = "/manifest.mf"
|
||||
testFlagBase = "--base"
|
||||
testFlagNoExtra = "--no-extra-files"
|
||||
testFlagVersion = "--version"
|
||||
testFlagVerbose = "--verbose"
|
||||
testFlagHelp = "--help"
|
||||
testCmdHelp = "help"
|
||||
testApp = "mfer"
|
||||
testGitrev = "abc123"
|
||||
testDir = "/testdir"
|
||||
testFile1 = "/testdir/file1.txt"
|
||||
testMF = "/testdir/test.mf"
|
||||
testOutput = "/output.mf"
|
||||
testManifest = "/manifest.mf"
|
||||
testFlagBase = "--base"
|
||||
testFlagNoExtra = "--no-extra-files"
|
||||
testFlagFollowSymlinks = "--follow-symlinks"
|
||||
testFlagVersion = "--version"
|
||||
testFlagVerbose = "--verbose"
|
||||
testFlagHelp = "--help"
|
||||
testCmdHelp = "help"
|
||||
)
|
||||
|
||||
var errSimulatedWrite = errors.New("simulated write failure")
|
||||
@@ -1002,7 +1003,7 @@ func TestCheckReportsUnlistedSymlinkToFileOnly(t *testing.T) {
|
||||
exitCode int
|
||||
extras []string // what check reports as not in the manifest
|
||||
}{
|
||||
"gen --follow-symlinks": {[]string{"--follow-symlinks"}, 0, nil},
|
||||
"gen --follow-symlinks": {[]string{testFlagFollowSymlinks}, 0, nil},
|
||||
"plain gen": {nil, 1, []string{"filelink"}},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
@@ -1567,6 +1568,141 @@ func TestGenerateLeavesLeftoverTempFileOutOfListing(t *testing.T) {
|
||||
assert.Equal(t, []string{testFileTxt}, manifestPaths(t, fs, output))
|
||||
}
|
||||
|
||||
// TestGenerateLeavesTopIndexMFOutOfListing runs gen with --output outside
|
||||
// the paths it is given: index.mf at the top of each directory given, and
|
||||
// a file given by that name, are not listed, since fetch saves the manifest
|
||||
// as index.mf at the top of the tree, while sub/index.mf is listed.
|
||||
func TestGenerateLeavesTopIndexMFOutOfListing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Paths are relative to a temp dir holding tree, which holds file.txt,
|
||||
// index.mf and sub/index.mf, and second, which holds second.txt and
|
||||
// index.mf.
|
||||
for name, tc := range map[string]struct {
|
||||
paths []string
|
||||
listed []string
|
||||
}{
|
||||
"directory": {
|
||||
[]string{"tree"},
|
||||
[]string{testFileTxt, testSubdirManifest},
|
||||
},
|
||||
"two directories": {
|
||||
[]string{"tree", "second"},
|
||||
[]string{testFileTxt, testSubdirManifest, "second.txt"},
|
||||
},
|
||||
"files": {
|
||||
[]string{"tree/" + testFileTxt, "tree/" + defaultManifestName},
|
||||
[]string{testFileTxt},
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
root := t.TempDir()
|
||||
tree := filepath.Join(root, "tree")
|
||||
second := filepath.Join(root, "second")
|
||||
|
||||
fs := afero.NewOsFs()
|
||||
require.NoError(t, fs.MkdirAll(filepath.Join(tree, testSubdir), 0o750))
|
||||
require.NoError(t, fs.MkdirAll(second, 0o750))
|
||||
writeTestFile(t, fs, filepath.Join(tree, testFileTxt), "hello")
|
||||
writeTestFile(t, fs, filepath.Join(tree, defaultManifestName), "an ordinary file")
|
||||
writeTestFile(t, fs, filepath.Join(tree, testSubdirManifest), "an ordinary file")
|
||||
writeTestFile(t, fs, filepath.Join(second, "second.txt"), "second")
|
||||
writeTestFile(t, fs, filepath.Join(second, defaultManifestName), "an ordinary file")
|
||||
|
||||
paths := make([]string, 0, len(tc.paths))
|
||||
for _, path := range tc.paths {
|
||||
paths = append(paths, filepath.Join(root, path))
|
||||
}
|
||||
|
||||
output := filepath.Join(root, "other.mf")
|
||||
opts := testOpts(slices.Concat(
|
||||
[]string{testApp, cmdGenerate, "-q", "-o", output}, paths,
|
||||
), fs)
|
||||
require.Equal(t, 0, runCLI(opts), "stderr: %s", testStderr(t, opts))
|
||||
|
||||
assert.ElementsMatch(t, tc.listed, manifestPaths(t, fs, output))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestGenerateListsTargetOfTopIndexMFSymlink makes index.mf at the top of
|
||||
// the tree a symlink to another file in it, and runs gen with --output
|
||||
// outside the tree: the file it points to is listed under its own name,
|
||||
// and the symlink, which gen lists only with --follow-symlinks, is left out
|
||||
// like any index.mf at the top of the tree.
|
||||
func TestGenerateListsTargetOfTopIndexMFSymlink(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Paths are relative to tree, which holds file.txt, sub/index.mf and
|
||||
// index.mf, a symlink to target.
|
||||
for name, tc := range map[string]struct {
|
||||
target string
|
||||
flags []string
|
||||
path string // the path gen is given
|
||||
listed []string
|
||||
}{
|
||||
"directory given": {
|
||||
testSubdirManifest, nil, ".",
|
||||
[]string{testFileTxt, testSubdirManifest},
|
||||
},
|
||||
"directory given, --follow-symlinks": {
|
||||
testSubdirManifest,
|
||||
[]string{testFlagFollowSymlinks},
|
||||
".",
|
||||
[]string{testFileTxt, testSubdirManifest},
|
||||
},
|
||||
"file given": {
|
||||
testFileTxt, nil, testFileTxt,
|
||||
[]string{testFileTxt},
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
root := t.TempDir()
|
||||
tree := filepath.Join(root, "tree")
|
||||
|
||||
fs := afero.NewOsFs()
|
||||
require.NoError(t, fs.MkdirAll(filepath.Join(tree, testSubdir), 0o750))
|
||||
writeTestFile(t, fs, filepath.Join(tree, testFileTxt), "hello")
|
||||
writeTestFile(t, fs, filepath.Join(tree, testSubdirManifest), "an ordinary file")
|
||||
require.NoError(t,
|
||||
os.Symlink(tc.target, filepath.Join(tree, defaultManifestName)))
|
||||
|
||||
output := filepath.Join(root, "other.mf")
|
||||
opts := testOpts(slices.Concat(
|
||||
[]string{testApp, cmdGenerate, "-q", "-o", output}, tc.flags,
|
||||
[]string{filepath.Join(tree, tc.path)},
|
||||
), fs)
|
||||
require.Equal(t, 0, runCLI(opts), "stderr: %s", testStderr(t, opts))
|
||||
|
||||
assert.ElementsMatch(t, tc.listed, manifestPaths(t, fs, output))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestGenerateListsIndexMFBesideFileGiven runs gen with --output outside
|
||||
// the tree, given sub/b.txt and the tree: sub/index.mf, beside the file
|
||||
// given, is listed, since a file given is left out only when its own name
|
||||
// is index.mf.
|
||||
//
|
||||
//nolint:paralleltest // changes the process-global working directory
|
||||
func TestGenerateListsIndexMFBesideFileGiven(t *testing.T) {
|
||||
setupManifestInSubdir(t)
|
||||
|
||||
fs := afero.NewOsFs()
|
||||
output := filepath.Join(t.TempDir(), "other.mf")
|
||||
|
||||
opts := testOpts([]string{
|
||||
testApp, cmdGenerate, "-q", "-o", output, filepath.Join(testSubdir, "b.txt"), ".",
|
||||
}, fs)
|
||||
require.Equal(t, 0, runCLI(opts), "stderr: %s", testStderr(t, opts))
|
||||
|
||||
assert.Contains(t, manifestPaths(t, fs, output), testSubdirManifest)
|
||||
}
|
||||
|
||||
// writeTestTree writes file.txt and sub/nested.txt under dir.
|
||||
func writeTestTree(t *testing.T, fs afero.Fs, dir string) {
|
||||
t.Helper()
|
||||
|
||||
Reference in New Issue
Block a user