devapp: add stats page plus some minor cleanup
+ Adds a stats page that shows open CLs over time
+ Adds a command-line flag to reload templates on each request
+ Breaks some repeated code into some shared logic
+ Some minor cleanup to adhere to best practices
Change-Id: I2d84a1e5c77c7e9131c758d69e4a9bf9a9d815f4
Reviewed-on: https://go-review.googlesource.com/c/build/+/199637
Run-TryBot: Andrew Bonventre <andybons@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alexander Rakoczy <alex@golang.org>
diff --git a/devapp/stats_test.go b/devapp/stats_test.go
new file mode 100644
index 0000000..c15d996
--- /dev/null
+++ b/devapp/stats_test.go
@@ -0,0 +1,117 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "math"
+ "testing"
+ "time"
+
+ "golang.org/x/build/maintner"
+)
+
+func TestNewIntervalFromCL(t *testing.T) {
+ var (
+ t0 = time.Now()
+ t1 = t0.Add(1 * time.Hour)
+ )
+ testCases := []struct {
+ cl *maintner.GerritCL
+ start, end int64
+ }{
+ {
+ cl: &maintner.GerritCL{
+ Created: t0,
+ Status: "new",
+ },
+ start: t0.Unix(),
+ end: math.MaxInt64,
+ },
+ {
+ cl: &maintner.GerritCL{
+ Created: t0,
+ Status: "merged",
+ Metas: []*maintner.GerritMeta{
+ {
+ Commit: &maintner.GitCommit{
+ Msg: "autogenerated:gerrit:merged",
+ CommitTime: t1,
+ },
+ },
+ },
+ },
+ start: t0.Unix(),
+ end: t1.Unix(),
+ },
+ {
+ cl: &maintner.GerritCL{
+ Created: t0,
+ Status: "abandoned",
+ Metas: []*maintner.GerritMeta{
+ {
+ Commit: &maintner.GitCommit{
+ Msg: "autogenerated:gerrit:abandon",
+ CommitTime: t1,
+ },
+ },
+ },
+ },
+ start: t0.Unix(),
+ end: t1.Unix(),
+ },
+ }
+
+ for _, tc := range testCases {
+ ival := newIntervalFromCL(tc.cl)
+ if got, want := ival.start, tc.start; got != want {
+ t.Errorf("start: got %d; want %d", got, want)
+ }
+ if got, want := ival.end, tc.end; got != want {
+ t.Errorf("end: got %d; want %d", got, want)
+ }
+ if got, want := ival.cl, tc.cl; got != want {
+ t.Errorf("cl: got %+v; want %+v", got, want)
+ }
+ }
+}
+
+func TestIntervalIntersection(t *testing.T) {
+ testCases := []struct {
+ interval *clInterval
+ t0, t1 time.Time
+ intersects bool
+ }{
+ {
+ &clInterval{start: 0, end: 5},
+ time.Unix(0, 0),
+ time.Unix(10, 0),
+ true,
+ },
+ {
+ &clInterval{start: 10, end: 20},
+ time.Unix(0, 0),
+ time.Unix(10, 0),
+ true,
+ },
+ {
+ &clInterval{start: 10, end: 20},
+ time.Unix(0, 0),
+ time.Unix(9, 0),
+ false,
+ },
+ {
+ &clInterval{start: 0, end: 5},
+ time.Unix(6, 0),
+ time.Unix(10, 0),
+ false,
+ },
+ }
+
+ for _, tc := range testCases {
+ if got, want := tc.interval.intersects(tc.t0, tc.t1), tc.intersects; got != want {
+ t.Errorf("(%v).intersects(%v, %v): got %v; want %v", tc.interval, tc.t0, tc.t1, got, want)
+ }
+ }
+}