v23tests: use concrete rather than interface types.
MultiPart: 1/3

It's very convenient to be able to embed testing.T and
expect.Session in the v23tests.T type. We were doing this
using interfaces which was attractive because we envisaged being
able to substitute different implementations at some point in
the future. However, embedding in this way results in an autogenerated
stack frame which shows up as <autogenerated>:<some random line no>
when t.Logf, t.Errorf are called. The alternative of wrapping these
functions (i.e. have v23tests.T wrap testing.T.Errorf) can't be used
because the implementation of testing.T assumes a fixed call depth
of 3. This problem occurs if the concrete type (with the embedded
testing.T, expect.Session) that implements the interface type is
passed to a function that expects the interface as a parameter, hence
using structs instead of interfaces allows for embedding and correct
file, line number recording. We opt to lose the abstraction and
burden the v23tests.T implementor with having to deal with alternate
implementations in the future in order to give the test writer the
convenience of correct line number logging and embedded which leads
to a more concise API.

Change-Id: Icc3227434e61157e1b6208d643ab3535e71b86ae
diff --git a/tools/principal/principal_v23_test.go b/tools/principal/principal_v23_test.go
new file mode 100644
index 0000000..47b92b9
--- /dev/null
+++ b/tools/principal/principal_v23_test.go
@@ -0,0 +1,48 @@
+package main_test
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"regexp"
+	"testing"
+
+	"v.io/core/veyron/lib/modules"
+	"v.io/core/veyron/lib/testutil/v23tests"
+)
+
+//go:generate v23 test generate
+func TestHelperProcess(t *testing.T) {
+	modules.DispatchInTest()
+}
+
+// redirect redirects the stdout of the given invocation to the file at the
+// given path.
+func redirect(t *v23tests.T, inv *v23tests.Invocation, path string) {
+	if err := ioutil.WriteFile(path, []byte(inv.Output()), 0600); err != nil {
+		t.Fatalf("WriteFile(%q) failed: %v\n", path, err)
+	}
+}
+
+func V23TestBlessSelf(t *v23tests.T) {
+	var (
+		outputDir         = t.TempDir()
+		aliceDir          = filepath.Join(outputDir, "alice")
+		aliceBlessingFile = filepath.Join(outputDir, "aliceself")
+	)
+
+	bin := t.BuildGoPkg("v.io/core/veyron/tools/principal")
+	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)
+
+	bin = bin.WithEnv("VEYRON_CREDENTIALS=" + aliceDir)
+	redirect(t, bin.Start("blessself", "alicereborn"), aliceBlessingFile)
+	got := bin.Start("dumpblessings", aliceBlessingFile).Output()
+	want := `Blessings          : alicereborn
+PublicKey          : ([0-9a-f]{2}:){15}[0-9a-f]{2}
+Certificate chains : 1
+Chain #0 \(1 certificates\). Root certificate public key: ([0-9a-f]{2}:){15}[0-9a-f]{2}
+  Certificate #0: alicereborn with 0 caveats`
+	if regexp.MustCompile(want).FindString(got) == "" {
+		t.Fatalf("wanted something matching \n%s, got \n%s\n", want, got)
+	}
+}