Merge "services/binary/tidy/*: extend tidy to clean envelopes"
diff --git a/services/binary/tidy/appd/mock.go b/services/binary/tidy/appd/mock.go
index 3043304..d401402 100644
--- a/services/binary/tidy/appd/mock.go
+++ b/services/binary/tidy/appd/mock.go
@@ -37,6 +37,10 @@
 	return r.Env, r.Err
 }
 
+func (mdi *mockAppdInvoker) TidyNow(ctx *context.T, _ rpc.ServerCall) error {
+	return mdi.SimpleCore("TidyNow", "TidyNow")
+}
+
 type dispatcher struct {
 	tape *servicetest.Tape
 	t    *testing.T
diff --git a/services/binary/tidy/doc.go b/services/binary/tidy/doc.go
index 209fb4c..300c929 100644
--- a/services/binary/tidy/doc.go
+++ b/services/binary/tidy/doc.go
@@ -6,13 +6,14 @@
 // DO NOT UPDATE MANUALLY
 
 /*
-Tidy tidies the Vanadium binary repository by removing unused binaries.
+Tidy tidies the Vanadium repository by removing unused envelopes and binaries.
 
 Usage:
    tidy <command>
 
 The tidy commands are:
    binary      Binary sub-command tidies a specified binaryd
+   application Application sub-command tidies a specified applicationd
    help        Display help for commands or topics
 
 The global flags are:
@@ -70,6 +71,17 @@
 <applicationd> is the name or endpoint of the applicationd instance sourcing the
 envelopes. <binaryd> is the name or endpoint of a binaryd instance to clean up.
 
+Tidy application - Application sub-command tidies a specified applicationd
+
+Application sub-command uses the Tidy RPC to clean up outdated envelopes from
+the specified appilcationd. Call this before tidying a binaryd instance for
+maximum tidiness.
+
+Usage:
+   tidy application <applicationd>
+
+<applicationd> is the name or endpoint of the applicationd instance to tidy.
+
 Tidy help - Display help for commands or topics
 
 Help with no args displays the usage of the parent command.
diff --git a/services/binary/tidy/impl.go b/services/binary/tidy/impl.go
index 12bbe7d..1f17f80 100644
--- a/services/binary/tidy/impl.go
+++ b/services/binary/tidy/impl.go
@@ -25,8 +25,8 @@
 	"v.io/x/ref/services/repository"
 )
 
-var cmdTidy = &cmdline.Command{
-	Runner: v23cmd.RunnerFunc(runTidyUp),
+var cmdBinaryTidy = &cmdline.Command{
+	Runner: v23cmd.RunnerFunc(runBinaryTidy),
 	Name:   "binary",
 	Short:  "Binary sub-command tidies a specified binaryd",
 	Long: `
@@ -109,7 +109,7 @@
 	return s, nil
 }
 
-func runTidyUp(ctx *context.T, env *cmdline.Env, args []string) error {
+func runBinaryTidy(ctx *context.T, env *cmdline.Env, args []string) error {
 	if expected, got := 2, len(args); expected != got {
 		return env.UsageErrorf("match: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
@@ -184,14 +184,39 @@
 	return nil
 }
 
+var cmdApplicationTidy = &cmdline.Command{
+	Runner: v23cmd.RunnerFunc(runApplicationTidy),
+	Name:   "application",
+	Short:  "Application sub-command tidies a specified applicationd",
+	Long: `
+Application sub-command uses the Tidy RPC to clean up outdated
+envelopes from the specified appilcationd. Call this before tidying a
+binaryd instance for maximum tidiness.
+`,
+	ArgsName: "<applicationd>",
+	ArgsLong: `
+<applicationd> is the name or endpoint of the applicationd instance
+to tidy.
+`,
+}
+
+func runApplicationTidy(ctx *context.T, env *cmdline.Env, args []string) error {
+	vlog.Infof("runApplicationTidy")
+	if expected, got := 1, len(args); expected != got {
+		return env.UsageErrorf("match: incorrect number of arguments, expected %d, got %d", expected, got)
+	}
+	ac := repository.ApplicationClient(args[0])
+	return ac.TidyNow(ctx)
+}
+
 var cmdRoot = &cmdline.Command{
 	Name:  "tidy",
 	Short: "Tidy binary repositories",
 	Long: `
-Tidy tidies the Vanadium binary repository by removing unused
-binaries.
+Tidy tidies the Vanadium repository by removing unused
+envelopes and binaries.
 `,
-	Children: []*cmdline.Command{cmdTidy},
+	Children: []*cmdline.Command{cmdBinaryTidy, cmdApplicationTidy},
 }
 
 func main() {
diff --git a/services/binary/tidy/impl_test.go b/services/binary/tidy/impl_test.go
index 44e5d00..8b05eab 100644
--- a/services/binary/tidy/impl_test.go
+++ b/services/binary/tidy/impl_test.go
@@ -24,7 +24,47 @@
 
 //go:generate v23 test generate
 
-func TestBinaryClient(t *testing.T) {
+func TestApplicationTidying(t *testing.T) {
+	ctx, shutdown := test.V23Init()
+	defer shutdown()
+
+	apptape := servicetest.NewTape()
+	appserver, err := xrpc.NewDispatchingServer(ctx, "", appd.NewDispatcher(t, apptape))
+	if err != nil {
+		t.Fatalf("applicationd NewDispatchingServer failed: %v", err)
+	}
+
+	// Setup the command-line.
+	var stdout, stderr bytes.Buffer
+	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
+	applicationName := appserver.Status().Endpoints[0].Name()
+
+	apptape.SetResponses(
+		// TidyNow()
+		nil,
+	)
+
+	if err := v23cmd.ParseAndRunForTest(cmdApplicationTidy, ctx, env, []string{applicationName}); err != nil {
+		t.Errorf("error: %v", err)
+	}
+
+	// Verify no output.
+	if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
+		t.Errorf("Unexpected output from tidy application. Got %q, expected %q", got, expected)
+	}
+	if expected, got := "", strings.TrimSpace(stderr.String()); got != expected {
+		t.Errorf("Unexpected error from tidy application. Got %q, expected %q", got, expected)
+	}
+
+	// Verify application tape.
+	if got, expected := apptape.Play(), []interface{}{
+		"TidyNow",
+	}; !reflect.DeepEqual(expected, got) {
+		t.Errorf("apptape invalid call sequence. Got %#v, want %#v", got, expected)
+	}
+}
+
+func TestBinaryTidying(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
@@ -249,16 +289,16 @@
 		},
 	)
 
-	if err := v23cmd.ParseAndRunForTest(cmdTidy, ctx, env, []string{applicationName, binaryName}); err != nil {
-		t.Fatalf("error: %v", err)
+	if err := v23cmd.ParseAndRunForTest(cmdBinaryTidy, ctx, env, []string{applicationName, binaryName}); err != nil {
+		t.Errorf("error: %v", err)
 	}
 
 	// Verify no output.
 	if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
-		t.Errorf("Unexpected output from bintidy. Got %q, expected %q", got, expected)
+		t.Errorf("Unexpected output from tidy binary. Got %q, expected %q", got, expected)
 	}
 	if expected, got := "", strings.TrimSpace(stderr.String()); got != expected {
-		t.Errorf("Unexpected error from bintidy. Got %q, expected %q", got, expected)
+		t.Errorf("Unexpected error from tidy binary. Got %q, expected %q", got, expected)
 	}
 
 	// Verify binaryd tape.