Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6470906d0 |
@@ -279,13 +279,9 @@ All three subcommands operate on a single SQLite database file:
|
||||
trees: database /srv/tmp/sfdupes/db.db (from SFDUPES_DATABASE)
|
||||
```
|
||||
|
||||
The working directory put in front is the one `pwd -P` prints, with its
|
||||
symlinks resolved. After a `cd` through a symlink to a directory, a path
|
||||
starting `../` therefore names a file beside the directory the symlink leads
|
||||
to, as it does for any other program. `scan` says `new database` when no
|
||||
file is at that path as it starts. `report` and `trees` print the line
|
||||
before they open the database, so the error for a missing database comes
|
||||
after it.
|
||||
`scan` says `new database` when no file is at that path as it starts.
|
||||
`report` and `trees` print the line before they open the database, so the
|
||||
error for a missing database comes after it.
|
||||
|
||||
- `scan` creates the database (and its parent directory) on first use. `report`
|
||||
and `trees` require an existing database; a missing database file is a fatal
|
||||
|
||||
@@ -93,9 +93,9 @@ var errScanRunning = errors.New("another scan is running")
|
||||
|
||||
// databasePath resolves the database location: SFDUPES_DATABASE when
|
||||
// set and non-empty, the compiled-in default otherwise. It returns the
|
||||
// path, with the working directory, its symlinks resolved, put in front
|
||||
// of it when it is relative, and where the path came from, "default" or
|
||||
// "from SFDUPES_DATABASE", for the line each subcommand prints first on
|
||||
// path, with the working directory put in front of it when it is
|
||||
// relative, and where the path came from, "default" or "from
|
||||
// SFDUPES_DATABASE", for the line each subcommand prints first on
|
||||
// stderr.
|
||||
func databasePath() (string, string, error) {
|
||||
path, source := defaultDatabasePath, "default"
|
||||
@@ -107,12 +107,7 @@ func databasePath() (string, string, error) {
|
||||
return path, source, nil
|
||||
}
|
||||
|
||||
// The working directory as the kernel reports it, with no symlink
|
||||
// in it. os.Getwd may return PWD instead, which can name it through
|
||||
// a symlink; a ".." after that symlink then leads somewhere else
|
||||
// once the path is cleaned, as scan cleans it to create the
|
||||
// database's directory.
|
||||
wd, err := unix.Getwd()
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("resolve database path %s: %w", path, err)
|
||||
}
|
||||
|
||||
+3
-8
@@ -44,14 +44,9 @@ func openTestDB(t *testing.T) *sql.DB {
|
||||
|
||||
func TestDatabasePath(t *testing.T) {
|
||||
// README §Database: SFDUPES_DATABASE when set and non-empty, the
|
||||
// default otherwise, with the working directory, its symlinks
|
||||
// resolved, put in front of a relative path. Nothing is cleaned, so
|
||||
// a ".." stays.
|
||||
dir, err := filepath.EvalSymlinks(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// default otherwise, with the working directory put in front of a
|
||||
// relative path. Nothing is cleaned, so a ".." stays.
|
||||
dir := t.TempDir()
|
||||
t.Chdir(dir)
|
||||
|
||||
fromEnv := "from " + databaseEnv
|
||||
|
||||
+2
-83
@@ -359,14 +359,10 @@ func TestRunUsesDatabaseThroughSymlinkedDirectory(t *testing.T) {
|
||||
// use that file, relative or absolute, and its first line must name
|
||||
// the path as set, with the working directory put in front of a
|
||||
// relative one.
|
||||
dir, err := filepath.EvalSymlinks(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dir := t.TempDir()
|
||||
t.Chdir(dir)
|
||||
|
||||
err = os.MkdirAll(filepath.Join(dir, "real", "sub"), 0o750)
|
||||
err := os.MkdirAll(filepath.Join(dir, "real", "sub"), 0o750)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -422,83 +418,6 @@ func TestRunUsesDatabaseThroughSymlinkedDirectory(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunScanFromSymlinkedWorkingDirectory(t *testing.T) {
|
||||
// README §Database: a relative path gets the working directory, its
|
||||
// symlinks resolved, put in front of it. With home/proj a symlink
|
||||
// to data/proj, entered as the working directory through the
|
||||
// symlink, ../new/db.sqlite names data/new/db.sqlite. scan must
|
||||
// create that directory and file, and nothing beside home/proj.
|
||||
dir, err := filepath.EvalSymlinks(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.MkdirAll(filepath.Join(dir, "data", "proj"), 0o750)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.Mkdir(filepath.Join(dir, "home"), 0o750)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.Symlink(filepath.Join(dir, "data", "proj"),
|
||||
filepath.Join(dir, "home", "proj"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// t.Chdir also sets PWD to the path through the symlink, as a
|
||||
// shell does after cd.
|
||||
t.Chdir(filepath.Join(dir, "home", "proj"))
|
||||
t.Setenv(databaseEnv, "../new/db.sqlite")
|
||||
|
||||
want := "scan: new database " + dir + "/data/proj/../new/db.sqlite" +
|
||||
" (from " + databaseEnv + ")\n"
|
||||
if got := scanOK(t, t.TempDir()); !strings.HasPrefix(got, want) {
|
||||
t.Errorf("scan stderr = %q, want it to start %q", got, want)
|
||||
}
|
||||
|
||||
_, err = os.Stat(filepath.Join(dir, "data", "new", "db.sqlite"))
|
||||
if err != nil {
|
||||
t.Errorf("database the path names: %v", err)
|
||||
}
|
||||
|
||||
_, err = os.Lstat(filepath.Join(dir, "home", "new"))
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
t.Errorf("home/new: stat error %v, want it missing", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunEscapesDatabasePath(t *testing.T) {
|
||||
// README §Database: each subcommand's first line escapes the
|
||||
// database path like the paths in a report, so a tab, newline or
|
||||
// backslash in it keeps the line one line.
|
||||
dir := t.TempDir()
|
||||
t.Setenv(databaseEnv, dir+"/a\tb\nc\\d.sqlite")
|
||||
|
||||
escaped := dir + `/a\tb\nc\\d.sqlite`
|
||||
from := " (from " + databaseEnv + ")\n"
|
||||
|
||||
want := "scan: new database " + escaped + from
|
||||
if got := scanOK(t, t.TempDir()); !strings.HasPrefix(got, want) {
|
||||
t.Errorf("scan stderr = %q, want it to start %q", got, want)
|
||||
}
|
||||
|
||||
for _, name := range []string{cmdReport, cmdTrees} {
|
||||
stderr := captureStderr(t)
|
||||
|
||||
code := run([]string{name}, io.Discard, os.Stderr)
|
||||
|
||||
want = name + ": database " + escaped + from
|
||||
if got := stderr(); code != exitOK || !strings.HasPrefix(got, want) {
|
||||
t.Errorf("run(%s) = %d with stderr %q, want %d and it to "+
|
||||
"start %q", name, code, got, exitOK, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunNamesDefaultDatabase(t *testing.T) {
|
||||
// README §Database: with SFDUPES_DATABASE empty, each subcommand's
|
||||
// first line on stderr names the default database. A database may
|
||||
|
||||
Reference in New Issue
Block a user