211 lines
5.8 KiB
Go
211 lines
5.8 KiB
Go
package sfdupes //nolint:testpackage // calls the unexported runDwim
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestRunDwimPrintsLargestTreesFirst(t *testing.T) {
|
|
// dwim scans its root, then prints the trees report, the larger of
|
|
// two duplicate-tree pairs first, and ends by saying how to print
|
|
// the report again.
|
|
t.Setenv(databaseEnv, testDBPath(t))
|
|
|
|
root := realTempDir(t)
|
|
|
|
small := []string{
|
|
filepath.Dir(writeFile(t, root, "a1/f.bin", pattern(1, 300))),
|
|
filepath.Dir(writeFile(t, root, "a2/f.bin", pattern(1, 300))),
|
|
}
|
|
large := []string{
|
|
filepath.Dir(writeFile(t, root, "b1/f.bin", pattern(2, 5000))),
|
|
filepath.Dir(writeFile(t, root, "b2/f.bin", pattern(2, 5000))),
|
|
}
|
|
|
|
var stdout bytes.Buffer
|
|
|
|
stderr := captureStderr(t)
|
|
|
|
err := runDwim(t.Context(), root, scanOptions{workers: 1}, &stdout)
|
|
if err != nil {
|
|
t.Fatalf("runDwim: %v; stderr: %s", err, stderr())
|
|
}
|
|
|
|
lines := strings.Split(strings.TrimSuffix(stdout.String(), "\n"), "\n")
|
|
if len(lines) != 3 {
|
|
t.Fatalf("stdout = %q, want a header and two groups",
|
|
stdout.String())
|
|
}
|
|
|
|
wantFirst := large[0] + "\t" + large[1] + "\t1\t5000\t"
|
|
if !strings.HasPrefix(lines[1], wantFirst) {
|
|
t.Errorf("first group = %q, want it to start %q", lines[1], wantFirst)
|
|
}
|
|
|
|
wantSecond := small[0] + "\t" + small[1] + "\t1\t300\t"
|
|
if !strings.HasPrefix(lines[2], wantSecond) {
|
|
t.Errorf("second group = %q, want it to start %q",
|
|
lines[2], wantSecond)
|
|
}
|
|
|
|
last := "\ndwim: run \"sfdupes trees\" to print this report again\n"
|
|
if got := stderr(); !strings.HasSuffix(got, last) {
|
|
t.Errorf("stderr = %q, want it to end %q", got, last)
|
|
}
|
|
}
|
|
|
|
func TestRunDwimScanErrorPrintsNoReport(t *testing.T) {
|
|
// A fatal scan error ends dwim before the report: a root that does
|
|
// not exist fails the scan as it fails scan itself.
|
|
t.Setenv(databaseEnv, testDBPath(t))
|
|
|
|
missing := filepath.Join(realTempDir(t), "missing")
|
|
|
|
var stdout bytes.Buffer
|
|
|
|
stderr := captureStderr(t)
|
|
|
|
err := runDwim(t.Context(), missing, scanOptions{workers: 1}, &stdout)
|
|
if err == nil {
|
|
t.Fatal("runDwim on a missing root: error = nil, want it fatal")
|
|
}
|
|
|
|
if got := stdout.String(); got != "" {
|
|
t.Errorf("stdout = %q, want no report", got)
|
|
}
|
|
|
|
if got := stderr(); strings.Contains(got, "trees:") ||
|
|
strings.Contains(got, "dwim:") {
|
|
t.Errorf("stderr = %q, want nothing from trees or dwim", got)
|
|
}
|
|
}
|
|
|
|
func TestRunDwimExcludeLeavesTreeOut(t *testing.T) {
|
|
// dwim passes its --exclude paths to the scan: an excluded copy is
|
|
// not recorded, so its pair is not reported.
|
|
t.Setenv(databaseEnv, testDBPath(t))
|
|
|
|
root := realTempDir(t)
|
|
|
|
kept := []string{
|
|
filepath.Dir(writeFile(t, root, "a1/f.bin", pattern(1, 300))),
|
|
filepath.Dir(writeFile(t, root, "a2/f.bin", pattern(1, 300))),
|
|
}
|
|
writeFile(t, root, "b1/f.bin", pattern(2, 5000))
|
|
excluded := filepath.Dir(writeFile(t, root, "b2/f.bin", pattern(2, 5000)))
|
|
|
|
var stdout bytes.Buffer
|
|
|
|
stderr := captureStderr(t)
|
|
|
|
opts := scanOptions{workers: 1, excludePaths: []string{excluded}}
|
|
|
|
err := runDwim(t.Context(), root, opts, &stdout)
|
|
if err != nil {
|
|
t.Fatalf("runDwim: %v; stderr: %s", err, stderr())
|
|
}
|
|
|
|
lines := strings.Split(strings.TrimSuffix(stdout.String(), "\n"), "\n")
|
|
if len(lines) != 2 {
|
|
t.Fatalf("stdout = %q, want a header and one group",
|
|
stdout.String())
|
|
}
|
|
|
|
want := kept[0] + "\t" + kept[1] + "\t1\t300\t"
|
|
if !strings.HasPrefix(lines[1], want) {
|
|
t.Errorf("group = %q, want it to start %q", lines[1], want)
|
|
}
|
|
|
|
skipped := "scan: skipped " + excluded + " (--exclude)\n"
|
|
if got := stderr(); !strings.Contains(got, skipped) {
|
|
t.Errorf("stderr = %q, want it to contain %q", got, skipped)
|
|
}
|
|
}
|
|
|
|
func TestRunDwimTakesScanFlags(t *testing.T) {
|
|
// The dwim command parses and checks the scan flags as scan does.
|
|
// Each case stops before the walk of / begins, and before the
|
|
// database is created. "dwim --exclude /" excludes its own operand.
|
|
cases := map[string]struct {
|
|
args []string
|
|
code int
|
|
want string
|
|
}{
|
|
"--exclude /": {
|
|
[]string{cmdDwim, excludeFlag, "/"},
|
|
exitFatal,
|
|
"sfdupes: scan /: operand excluded by --exclude /\n",
|
|
},
|
|
"--exclude empty": {
|
|
[]string{cmdDwim, excludeFlag, ""},
|
|
exitUsage,
|
|
"Error: --exclude must not be empty\n",
|
|
},
|
|
"--workers 0": {
|
|
[]string{cmdDwim, workersFlag, "0", "-x", "--no-default-excludes"},
|
|
exitUsage,
|
|
"Error: --workers must be at least 1, got 0\n",
|
|
},
|
|
}
|
|
|
|
for name, tc := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
path := testDBPath(t)
|
|
t.Setenv(databaseEnv, path)
|
|
|
|
stdout := captureStdout(t)
|
|
stderr := captureStderr(t)
|
|
|
|
code := run(tc.args, os.Stdout, os.Stderr)
|
|
if code != tc.code {
|
|
t.Errorf("run(%q) = %d, want %d", tc.args, code, tc.code)
|
|
}
|
|
|
|
if !strings.HasSuffix(stderr(), tc.want) || stdout() != "" {
|
|
t.Errorf("stdout, stderr = %q, %q, want \"\" and stderr "+
|
|
"ending %q", stdout(), stderr(), tc.want)
|
|
}
|
|
|
|
_, err := os.Lstat(path)
|
|
if !errors.Is(err, os.ErrNotExist) {
|
|
t.Errorf("lstat %s: %v, want no database", path, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRunDwimHelpListsScanFlags(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// README "Subcommands" shows this usage line for dwim.
|
|
const want = "\n sfdupes dwim [--workers N] [-x] [--no-default-excludes] " +
|
|
"[--exclude PATH]...\n"
|
|
|
|
var stdout, stderr bytes.Buffer
|
|
|
|
run([]string{cmdDwim, "-h"}, &stdout, &stderr)
|
|
|
|
if !strings.Contains(stderr.String(), want) {
|
|
t.Errorf("dwim -h stderr = %q, want the usage line %q",
|
|
stderr.String(), want)
|
|
}
|
|
|
|
if !strings.Contains(stderr.String(), excludeFlag+" PATH") {
|
|
t.Errorf("dwim -h stderr = %q, want %s PATH", stderr.String(),
|
|
excludeFlag)
|
|
}
|
|
|
|
// README "Settings": the help lists the default excludes and the
|
|
// filesystem types the walk never records anything on.
|
|
help := strings.TrimSpace(defaultExcludesHelp(runtime.GOOS))
|
|
if !strings.Contains(stderr.String(), help) {
|
|
t.Errorf("dwim -h stderr = %q, want it to hold %q", stderr.String(),
|
|
help)
|
|
}
|
|
}
|