Merge "v.io/jiri/profiles: add tests as per v.io/i/1177."
diff --git a/jiri/.api b/jiri/.api
index 2358c18..948dd2d 100644
--- a/jiri/.api
+++ b/jiri/.api
@@ -15,6 +15,7 @@
 pkg jiri, method (*X) Clone(tool.ContextOpts) *X
 pkg jiri, method (*X) JiriManifestFile() string
 pkg jiri, method (*X) ProfilesDBDir() string
+pkg jiri, method (*X) ProfilesRootDir() string
 pkg jiri, method (*X) ResolveManifestPath(string) (string, error)
 pkg jiri, method (*X) RootMetaDir() string
 pkg jiri, method (*X) ScriptsDir() string
diff --git a/jiri/x.go b/jiri/x.go
index abe6b31..4e119f2 100644
--- a/jiri/x.go
+++ b/jiri/x.go
@@ -162,6 +162,11 @@
 	return filepath.Join(x.RootMetaDir(), "profile_db")
 }
 
+// ProfilesRootDir returns the path to the root of the profiles installation.
+func (x *X) ProfilesRootDir() string {
+	return filepath.Join(x.RootMetaDir(), "profiles")
+}
+
 // UpdateHistoryLatestLink returns the path to a symlink that points to the
 // latest update in the update history directory.
 func (x *X) UpdateHistoryLatestLink() string {
diff --git a/profiles/profilescmdline/manager_cmdline.go b/profiles/profilescmdline/manager_cmdline.go
index b4e0578..fd91958 100644
--- a/profiles/profilescmdline/manager_cmdline.go
+++ b/profiles/profilescmdline/manager_cmdline.go
@@ -216,7 +216,8 @@
 	return append(cv.commonFlagValues.args(),
 		fmt.Sprintf("--%s=%v", "gc", cv.gc),
 		fmt.Sprintf("--%s=%v", "rewrite-profiles-db", cv.rewriteDB),
-		fmt.Sprintf("--%s=%v", "v", cv.verbose))
+		fmt.Sprintf("--%s=%v", "v", cv.verbose),
+		fmt.Sprintf("--%s=%v", "rm-all", cv.rmAll))
 }
 
 type updateFlagValues struct {
diff --git a/profiles/profilescmdline/manager_test.go b/profiles/profilescmdline/manager_test.go
index ac82c59..d0a8a6d 100644
--- a/profiles/profilescmdline/manager_test.go
+++ b/profiles/profilescmdline/manager_test.go
@@ -7,6 +7,7 @@
 import (
 	"bufio"
 	"bytes"
+	"flag"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -62,8 +63,7 @@
 	buildInstallersBinDir, buildJiriBinDir = "", ""
 )
 
-// TODO(sadovsky): This code leaves a lot of temp dirs behind. It would be nice
-// to restructure things so that all temporary artifacts get cleaned up.
+// TestMain must cleanup these directories created by this function.
 func buildInstallers(t *testing.T) string {
 	buildInstallersOnce.Do(func() {
 		binDir, err := ioutil.TempDir("", "")
@@ -81,6 +81,21 @@
 	return buildInstallersBinDir
 }
 
+func TestMain(m *testing.M) {
+	flag.Parse()
+	r := m.Run()
+	if buildInstallersBinDir != "" {
+		os.RemoveAll(buildInstallersBinDir)
+	}
+	os.Exit(r)
+}
+
+func createProfilesDB(t *testing.T, jirix *jiri.X) {
+	if err := os.MkdirAll(jirix.ProfilesDBDir(), os.FileMode(0755)); err != nil {
+		t.Fatalf("%s:%s", loc(), err)
+	}
+}
+
 func buildJiri(t *testing.T) string {
 	buildJiriOnce.Do(func() {
 		binDir, err := ioutil.TempDir("", "")
@@ -107,6 +122,7 @@
 	fake, cleanup := jiritest.NewFakeJiriRoot(t)
 	defer cleanup()
 	dir, sh := buildInstallers(t), gosh.NewShell(t)
+	createProfilesDB(t, fake.X)
 	sh.Vars["JIRI_ROOT"] = fake.X.Root
 	sh.Vars["PATH"] = envvar.PrependUniqueToken(sh.Vars["PATH"], ":", dir)
 	stdout := run(sh, dir, "jiri", "profile", "available", "-v")
@@ -169,11 +185,11 @@
 func cmpFiles(t *testing.T, gotFilename, wantFilename string) {
 	g, err := ioutil.ReadFile(gotFilename)
 	if err != nil {
-		t.Fatal(err)
+		t.Fatalf("%s: %s", loc(), err)
 	}
 	w, err := ioutil.ReadFile(wantFilename)
 	if err != nil {
-		t.Fatal(err)
+		t.Fatalf("%s: %s", loc(), err)
 	}
 	if got, want := removeDate(strings.TrimSpace(string(g))), removeDate(strings.TrimSpace(string(w))); got != want {
 		t.Errorf("%s: got %v, want %v from %q and %q", loc(), got, want, gotFilename, wantFilename)
@@ -184,6 +200,7 @@
 	fake, cleanup := jiritest.NewFakeJiriRoot(t)
 	defer cleanup()
 	dir, sh := buildInstallers(t), gosh.NewShell(t)
+	createProfilesDB(t, fake.X)
 	sh.Vars["JIRI_ROOT"] = fake.X.Root
 	sh.Vars["PATH"] = envvar.PrependUniqueToken(sh.Vars["PATH"], ":", dir)
 
@@ -237,11 +254,12 @@
 	fake, cleanup := jiritest.NewFakeJiriRoot(t)
 	defer cleanup()
 	dir, sh := buildInstallers(t), gosh.NewShell(t)
+	createProfilesDB(t, fake.X)
 	sh.Vars["JIRI_ROOT"] = fake.X.Root
-	sh.Vars["PATH"] = dir
+	sh.Vars["PATH"] = envvar.PrependUsingSeparator(dir, os.Getenv("PATH"), ":")
 
-	i1 := filepath.Join(fake.X.Root, ".jiri_root/profile_db/i1")
-	i2 := filepath.Join(fake.X.Root, ".jiri_root/profile_db/i2")
+	i1 := filepath.Join(fake.X.ProfilesDBDir(), "i1")
+	i2 := filepath.Join(fake.X.ProfilesDBDir(), "i2")
 
 	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg", "i2:eg")
 	cmpFiles(t, i1, filepath.Join("testdata", "i1d.xml"))
@@ -254,6 +272,28 @@
 	run(sh, dir, "jiri", "profile", "cleanup", "-gc")
 	cmpFiles(t, i1, filepath.Join("testdata", "i1f.xml"))
 	cmpFiles(t, i2, filepath.Join("testdata", "i2f.xml"))
+
+	run(sh, dir, "jiri", "profile", "cleanup", "-rm-all")
+	profiledir := filepath.Join(fake.X.Root, jiri.ProfilesRootDir)
+	for _, dir := range []string{
+		fake.X.ProfilesDBDir(),
+		filepath.Join(profiledir, "i1"),
+		filepath.Join(profiledir, "i2"),
+	} {
+		_, err := os.Stat(dir)
+		if !os.IsNotExist(err) {
+			t.Errorf("%v still exists: %v", dir, err)
+		}
+	}
+	// Start over, make sure update is idempotent.
+	createProfilesDB(t, fake.X)
+	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg")
+	run(sh, dir, "jiri", "profile", "update")
+	run(sh, dir, "jiri", "profile", "update")
+	cmpFiles(t, i1, filepath.Join("testdata", "i1g.xml"))
+	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@4", "i1:eg")
+	run(sh, dir, "jiri", "profile", "update")
+	cmpFiles(t, i1, filepath.Join("testdata", "i1h.xml"))
 }
 
 // Test using a fake jiri root.
diff --git a/profiles/profilescmdline/profile_manager.go b/profiles/profilescmdline/profile_manager.go
index a2d3530..529d6dc 100644
--- a/profiles/profilescmdline/profile_manager.go
+++ b/profiles/profilescmdline/profile_manager.go
@@ -142,13 +142,16 @@
 	s := jirix.NewSeq()
 	if err := s.AssertFileExists(db.Path()).Remove(db.Path()).Done(); err != nil && !runutil.IsNotExist(err) {
 		return err
+	} else {
+		if err := s.AssertDirExists(db.Path()).RemoveAll(db.Path()).Done(); err != nil && !runutil.IsNotExist(err) {
+			return err
+		}
 	}
 	d := root.Abs(jirix)
 	err := s.AssertDirExists(d).
 		Run("chmod", "-R", "u+w", d).
 		RemoveAll(d).
 		Done()
-	fmt.Fprintf(jirix.Stdout(), "Cleanup: -rm-all: ")
 	if err == nil || runutil.IsNotExist(err) {
 		fmt.Fprintf(jirix.Stdout(), "success\n")
 		return nil
diff --git a/profiles/profilescmdline/testdata/i1g.xml b/profiles/profilescmdline/testdata/i1g.xml
new file mode 100644
index 0000000..0ec4340
--- /dev/null
+++ b/profiles/profilescmdline/testdata/i1g.xml
@@ -0,0 +1,12 @@
+<profiles version="5" installer="i1">
+  <profile name="eg" root=".jiri_root/profiles/i1">
+    <target arch="arch" os="os" installation-directory=".jiri_root/profiles/i1" version="3" date="2016-02-01T17:43:12.745706-08:00">
+      <envvars></envvars>
+      <command-line></command-line>
+    </target>
+    <target arch="arch" os="os" installation-directory=".jiri_root/profiles/i1" version="2" date="2016-02-01T17:43:12.745706-08:00">
+      <envvars></envvars>
+      <command-line></command-line>
+    </target>
+  </profile>
+</profiles>
diff --git a/profiles/profilescmdline/testdata/i1h.xml b/profiles/profilescmdline/testdata/i1h.xml
new file mode 100644
index 0000000..4d2e3ae
--- /dev/null
+++ b/profiles/profilescmdline/testdata/i1h.xml
@@ -0,0 +1,16 @@
+<profiles version="5" installer="i1">
+  <profile name="eg" root=".jiri_root/profiles/i1">
+    <target arch="arch" os="os" installation-directory=".jiri_root/profiles/i1" version="4" date="2016-02-01T17:43:12.745706-08:00">
+      <envvars></envvars>
+      <command-line></command-line>
+    </target>
+    <target arch="arch" os="os" installation-directory=".jiri_root/profiles/i1" version="3" date="2016-02-01T17:43:12.745706-08:00">
+      <envvars></envvars>
+      <command-line></command-line>
+    </target>
+    <target arch="arch" os="os" installation-directory=".jiri_root/profiles/i1" version="2" date="2016-02-01T17:43:12.745706-08:00">
+      <envvars></envvars>
+      <command-line></command-line>
+    </target>
+  </profile>
+</profiles>