diff --git a/cmd/fdroidcl/endtoend_test.go b/cmd/fdroidcl/endtoend_test.go index 7198a75..9d6b1a4 100644 --- a/cmd/fdroidcl/endtoend_test.go +++ b/cmd/fdroidcl/endtoend_test.go @@ -37,50 +37,50 @@ func TestCommands(t *testing.T) { defer os.RemoveAll(dir) testBasedir = dir - mustSucceed := func(t *testing.T, want string, cmd *Command, args ...string) { - mustRun(t, true, want, cmd, args...) + mustSucceed := func(t *testing.T, wantRe, negRe string, cmd *Command, args ...string) { + mustRun(t, true, wantRe, negRe, cmd, args...) } - mustFail := func(t *testing.T, want string, cmd *Command, args ...string) { - mustRun(t, false, want, cmd, args...) + mustFail := func(t *testing.T, wantRe, negRe string, cmd *Command, args ...string) { + mustRun(t, false, wantRe, negRe, cmd, args...) } t.Run("Version", func(t *testing.T) { - mustSucceed(t, `^v`, cmdVersion) + mustSucceed(t, `^v`, ``, cmdVersion) }) t.Run("SearchBeforeUpdate", func(t *testing.T) { - mustFail(t, `could not open index`, cmdSearch) + mustFail(t, `could not open index`, ``, cmdSearch) }) t.Run("UpdateFirst", func(t *testing.T) { - mustSucceed(t, `done`, cmdUpdate) + mustSucceed(t, `done`, ``, cmdUpdate) }) t.Run("UpdateCached", func(t *testing.T) { - mustSucceed(t, `not modified`, cmdUpdate) + mustSucceed(t, `not modified`, ``, cmdUpdate) }) t.Run("SearchNoArgs", func(t *testing.T) { - mustSucceed(t, `F-Droid`, cmdSearch) + mustSucceed(t, `F-Droid`, ``, cmdSearch) }) t.Run("SearchWithArgs", func(t *testing.T) { - mustSucceed(t, `F-Droid`, cmdSearch, "fdroid.fdroid") + mustSucceed(t, `F-Droid`, ``, cmdSearch, "fdroid.fdroid") }) t.Run("SearchWithArgsNone", func(t *testing.T) { - mustSucceed(t, `^$`, cmdSearch, "nomatches") + mustSucceed(t, `^$`, ``, cmdSearch, "nomatches") }) t.Run("SearchOnlyPackageNames", func(t *testing.T) { - mustSucceed(t, `^[^ ]*$`, cmdSearch, "-q", "fdroid.fdroid") + mustSucceed(t, `^[^ ]*$`, ``, cmdSearch, "-q", "fdroid.fdroid") }) t.Run("ShowOne", func(t *testing.T) { - mustSucceed(t, `fdroid/fdroidclient`, cmdShow, "org.fdroid.fdroid") + mustSucceed(t, `fdroid/fdroidclient`, ``, cmdShow, "org.fdroid.fdroid") }) t.Run("ShowMany", func(t *testing.T) { - mustSucceed(t, `fdroid/fdroidclient.*fdroid/privileged-extension`, + mustSucceed(t, `fdroid/fdroidclient.*fdroid/privileged-extension`, ``, cmdShow, "org.fdroid.fdroid", "org.fdroid.fdroid.privileged") }) t.Run("ListCategories", func(t *testing.T) { - mustSucceed(t, `Development`, cmdList, "categories") + mustSucceed(t, `Development`, ``, cmdList, "categories") }) if err := startAdbIfNeeded(); err != nil { @@ -99,29 +99,49 @@ func TestCommands(t *testing.T) { t.Log("skipping the device tests as too many were found via ADB") } + t.Run("DevicesOne", func(t *testing.T) { + mustSucceed(t, `\n`, ``, cmdDevices) + }) + // try to uninstall the app first devices[0].Uninstall(chosenApp) t.Run("UninstallMissing", func(t *testing.T) { - mustFail(t, `not installed$`, cmdUninstall, chosenApp) + mustFail(t, `not installed$`, ``, cmdUninstall, chosenApp) + }) + t.Run("SearchInstalledMissing", func(t *testing.T) { + mustSucceed(t, ``, regexp.QuoteMeta(chosenApp), cmdSearch, "-i", "-q") + }) + t.Run("SearchUpgradableMissing", func(t *testing.T) { + mustSucceed(t, ``, regexp.QuoteMeta(chosenApp), cmdSearch, "-u", "-q") }) t.Run("InstallVersioned", func(t *testing.T) { - mustSucceed(t, `Installing `+regexp.QuoteMeta(chosenApp), + mustSucceed(t, `Installing `+regexp.QuoteMeta(chosenApp), ``, cmdInstall, chosenApp+":1") }) + t.Run("SearchInstalled", func(t *testing.T) { + time.Sleep(3 * time.Second) + mustSucceed(t, regexp.QuoteMeta(chosenApp), ``, cmdSearch, "-i", "-q") + }) + t.Run("SearchUpgradable", func(t *testing.T) { + mustSucceed(t, regexp.QuoteMeta(chosenApp), ``, cmdSearch, "-u", "-q") + }) t.Run("Upgrade", func(t *testing.T) { - mustSucceed(t, `Upgrading `+regexp.QuoteMeta(chosenApp), + mustSucceed(t, `Upgrading `+regexp.QuoteMeta(chosenApp), ``, cmdUpgrade, chosenApp) }) + t.Run("SearchUpgradableUpToDate", func(t *testing.T) { + mustSucceed(t, ``, regexp.QuoteMeta(chosenApp), cmdSearch, "-u", "-q") + }) t.Run("UpgradeAlreadyInstalled", func(t *testing.T) { - mustFail(t, `is up to date$`, cmdUpgrade, chosenApp) + mustFail(t, `is up to date$`, ``, cmdUpgrade, chosenApp) }) t.Run("UninstallExisting", func(t *testing.T) { - mustSucceed(t, `Uninstalling `+regexp.QuoteMeta(chosenApp), + mustSucceed(t, `Uninstalling `+regexp.QuoteMeta(chosenApp), ``, cmdUninstall, chosenApp) }) } -func mustRun(t *testing.T, success bool, wantRe string, cmd *Command, args ...string) { +func mustRun(t *testing.T, success bool, wantRe, negRe string, cmd *Command, args ...string) { var buf bytes.Buffer stdout, stderr = &buf, &buf err := cmd.Run(args) @@ -139,4 +159,10 @@ func mustRun(t *testing.T, success bool, wantRe string, cmd *Command, args ...st if !regexp.MustCompile(wantRe).MatchString(out) { t.Fatalf("output does not match %#q:\n%s", wantRe, out) } + if negRe != "" { + negRe = "(?sm)" + negRe + if regexp.MustCompile(negRe).MatchString(out) { + t.Fatalf("output does match %#q:\n%s", negRe, out) + } + } } diff --git a/cmd/fdroidcl/main.go b/cmd/fdroidcl/main.go index 774a448..21943be 100644 --- a/cmd/fdroidcl/main.go +++ b/cmd/fdroidcl/main.go @@ -115,9 +115,6 @@ type Command struct { // Short is the short description. Short string - - // Flag is a set of flags specific to this command. - Flag flag.FlagSet } // Name returns the command's name: the first word in the usage line. @@ -130,13 +127,13 @@ func (c *Command) Name() string { return name } -func (c *Command) Usage() { +func (c *Command) usage(flagSet *flag.FlagSet) { fmt.Fprintf(stderr, "Usage: %s %s [-h]\n", cmdName, c.UsageLine) anyFlags := false - c.Flag.VisitAll(func(f *flag.Flag) { anyFlags = true }) + flagSet.VisitAll(func(f *flag.Flag) { anyFlags = true }) if anyFlags { fmt.Fprintf(stderr, "\nAvailable options:\n") - c.Flag.PrintDefaults() + flagSet.PrintDefaults() } os.Exit(2) } @@ -202,10 +199,7 @@ func main() { continue } readConfig() - cmd.Flag.Usage = func() { cmd.Usage() } - cmd.Flag.Parse(args[1:]) - args = cmd.Flag.Args() - if err := cmd.Run(args); err != nil { + if err := cmd.Run(args[1:]); err != nil { errExit("%s: %v\n", cmdName, err) } return diff --git a/cmd/fdroidcl/search.go b/cmd/fdroidcl/search.go index daa6f6f..b49a06a 100644 --- a/cmd/fdroidcl/search.go +++ b/cmd/fdroidcl/search.go @@ -4,6 +4,7 @@ package main import ( + "flag" "fmt" "regexp" "sort" @@ -19,20 +20,22 @@ var cmdSearch = &Command{ Short: "Search available apps", } -var ( - quiet = cmdSearch.Flag.Bool("q", false, "Print package names only") - installed = cmdSearch.Flag.Bool("i", false, "Filter installed apps") - updates = cmdSearch.Flag.Bool("u", false, "Filter apps with updates") - days = cmdSearch.Flag.Int("d", 0, "Select apps last updated in the last days; a negative value drops them instead") - category = cmdSearch.Flag.String("c", "", "Filter apps by category") - sortBy = cmdSearch.Flag.String("o", "", "Sort order (added, updated)") -) - func init() { cmdSearch.Run = runSearch } func runSearch(args []string) error { + var fset flag.FlagSet + var ( + quiet = fset.Bool("q", false, "Print package names only") + installed = fset.Bool("i", false, "Filter installed apps") + updates = fset.Bool("u", false, "Filter apps with updates") + days = fset.Int("d", 0, "Select apps last updated in the last days; a negative value drops them instead") + category = fset.String("c", "", "Filter apps by category") + sortBy = fset.String("o", "", "Sort order (added, updated)") + ) + fset.Parse(args) + args = fset.Args() if *installed && *updates { return fmt.Errorf("-i is redundant if -u is specified") } @@ -137,8 +140,7 @@ func printApps(apps []fdroidcl.App, inst map[string]adb.Package, device *adb.Dev } func descVersion(app fdroidcl.App, inst *adb.Package, device *adb.Device) string { - // With "-u" or "-i" option there must be a connected device - if *updates || *installed { + if inst != nil { suggested := app.SuggestedApk(device) if suggested != nil && inst.VCode < suggested.VCode { return fmt.Sprintf("%s (%d) -> %s (%d)", inst.VName, inst.VCode, @@ -146,7 +148,6 @@ func descVersion(app fdroidcl.App, inst *adb.Package, device *adb.Device) string } return fmt.Sprintf("%s (%d)", inst.VName, inst.VCode) } - // Without "-u" or "-i" we only have repositories indices return fmt.Sprintf("%s (%d)", app.CVName, app.CVCode) }