veyron/lib/stats: Let Glob return inner nodes

With this change, Glob will include inner nodes that don't have a value
when includeValues is false. This means that Glob can be used to
traverse the tree one level at a time. WatchGlob will return all nodes
on the first cycle, and then only the nodes that change afterwards.

Change-Id: I221b4f4672992b3173810c7e6a4b63985eaba1b1
diff --git a/lib/stats/glob.go b/lib/stats/glob.go
index 2ba7cb7..c630e1e 100644
--- a/lib/stats/glob.go
+++ b/lib/stats/glob.go
@@ -37,12 +37,14 @@
 
 // globStepLocked applies a glob recursively.
 func globStepLocked(prefix string, g *glob.Glob, n *node, updatedSince time.Time, includeValues bool, result *[]KeyValue) {
-	if g.Len() == 0 && n.object != nil && (updatedSince.IsZero() || !n.object.LastUpdate().Before(updatedSince)) {
-		var v interface{}
-		if includeValues {
-			v = n.object.Value()
+	if g.Len() == 0 {
+		if updatedSince.IsZero() || (n.object != nil && !n.object.LastUpdate().Before(updatedSince)) {
+			var v interface{}
+			if includeValues && n.object != nil {
+				v = n.object.Value()
+			}
+			*result = append(*result, KeyValue{prefix, v})
 		}
-		*result = append(*result, KeyValue{prefix, v})
 	}
 	if g.Finished() {
 		return
diff --git a/lib/stats/stats_test.go b/lib/stats/stats_test.go
index 9d24b8c..37a68e0 100644
--- a/lib/stats/stats_test.go
+++ b/lib/stats/stats_test.go
@@ -13,8 +13,8 @@
 	"veyron2/rt"
 )
 
-func doGlob(root, pattern string, since time.Time) ([]libstats.KeyValue, error) {
-	it := libstats.Glob(root, pattern, since, true)
+func doGlob(root, pattern string, since time.Time, includeValues bool) ([]libstats.KeyValue, error) {
+	it := libstats.Glob(root, pattern, since, includeValues)
 	out := []libstats.KeyValue{}
 	for it.Advance() {
 		v := it.Value()
@@ -87,7 +87,8 @@
 		t.Errorf("unexpected result. Got %v, want %v", got, expected)
 	}
 
-	result, err := doGlob("", "...", now)
+	// Glob showing only nodes with a value.
+	result, err := doGlob("", "...", now, true)
 	if err != nil {
 		t.Errorf("unexpected error: %v", err)
 	}
@@ -110,7 +111,7 @@
 		t.Errorf("unexpected result. Got %#v, want %#v", result, expected)
 	}
 
-	result, err = doGlob("", "ipc/test/*", now)
+	result, err = doGlob("", "ipc/test/*", now, true)
 	if err != nil {
 		t.Errorf("unexpected error: %v", err)
 	}
@@ -124,10 +125,33 @@
 		t.Errorf("unexpected result. Got %#v, want %#v", result, expected)
 	}
 
+	// Glob showing all nodes without values
+	result, err = doGlob("", "ipc/...", time.Time{}, false)
+	if err != nil {
+		t.Errorf("unexpected error: %v", err)
+	}
+	expected = []libstats.KeyValue{
+		libstats.KeyValue{Key: "ipc"},
+		libstats.KeyValue{Key: "ipc/test"},
+		libstats.KeyValue{Key: "ipc/test/aaa"},
+		libstats.KeyValue{Key: "ipc/test/bbb"},
+		libstats.KeyValue{Key: "ipc/test/ccc"},
+		libstats.KeyValue{Key: "ipc/test/ddd"},
+		libstats.KeyValue{Key: "ipc/test/ddd/delta10m"},
+		libstats.KeyValue{Key: "ipc/test/ddd/delta1h"},
+		libstats.KeyValue{Key: "ipc/test/ddd/delta1m"},
+		libstats.KeyValue{Key: "ipc/test/ddd/rate10m"},
+		libstats.KeyValue{Key: "ipc/test/ddd/rate1h"},
+		libstats.KeyValue{Key: "ipc/test/ddd/rate1m"},
+	}
+	if !reflect.DeepEqual(result, expected) {
+		t.Errorf("unexpected result. Got %#v, want %#v", result, expected)
+	}
+
 	// Test the rate counter.
 	now = now.Add(10 * time.Second)
 	d.Incr(100)
-	result, err = doGlob("", "ipc/test/ddd/*", now)
+	result, err = doGlob("", "ipc/test/ddd/*", now, true)
 	if err != nil {
 		t.Errorf("unexpected error: %v", err)
 	}
@@ -144,7 +168,7 @@
 	}
 
 	// Test Glob on non-root object.
-	result, err = doGlob("ipc/test", "*", time.Time{})
+	result, err = doGlob("ipc/test", "*", time.Time{}, true)
 	if err != nil {
 		t.Errorf("unexpected error: %v", err)
 	}
@@ -158,7 +182,7 @@
 		t.Errorf("unexpected result. Got %#v, want %#v", result, expected)
 	}
 
-	result, err = doGlob("ipc/test/aaa", "", time.Time{})
+	result, err = doGlob("ipc/test/aaa", "", time.Time{}, true)
 	if err != nil {
 		t.Errorf("unexpected error: %v", err)
 	}
@@ -170,7 +194,7 @@
 	}
 
 	// Test LastUpdate. The test only works on Counters.
-	result, err = doGlob("ipc/test", "ddd", now)
+	result, err = doGlob("ipc/test", "ddd", now, true)
 	if err != nil {
 		t.Errorf("unexpected error: %v", err)
 	}
@@ -181,7 +205,7 @@
 		t.Errorf("unexpected result. Got %#v, want %#v", result, expected)
 	}
 
-	result, err = doGlob("ipc/test", "ddd", now.Add(time.Second))
+	result, err = doGlob("ipc/test", "ddd", now.Add(time.Second), true)
 	if err != nil {
 		t.Errorf("unexpected error: %v", err)
 	}
@@ -195,7 +219,7 @@
 	h.Add(1)
 	h.Add(2)
 
-	result, err = doGlob("", "ipc/test/hhh", now)
+	result, err = doGlob("", "ipc/test/hhh", now, true)
 	if err != nil {
 		t.Errorf("unexpected error: %v", err)
 	}
@@ -224,7 +248,7 @@
 	now = now.Add(30 * time.Second)
 	h.Add(3)
 
-	result, err = doGlob("", "ipc/test/hhh/delta1m", now)
+	result, err = doGlob("", "ipc/test/hhh/delta1m", now, true)
 	if err != nil {
 		t.Errorf("unexpected error: %v", err)
 	}
diff --git a/services/mgmt/stats/impl/stats_invoker_test.go b/services/mgmt/stats/impl/stats_invoker_test.go
index c06e0ee..46e751e 100644
--- a/services/mgmt/stats/impl/stats_invoker_test.go
+++ b/services/mgmt/stats/impl/stats_invoker_test.go
@@ -92,6 +92,7 @@
 			t.Errorf("gstream.Finish failed: %v", err)
 		}
 		expected := []string{
+			"testing/foo",
 			"testing/foo/bar",
 			"testing/foo/bar/delta10m",
 			"testing/foo/bar/delta1h",