trace_schema_validator can be used to verify that trace2 event output
conforms to the expectations set by the API documentation and codified
in event_schema.json (or strict_schema.json). This allows us to build a
regression test to verify that trace2 output does not change
unexpectedly.

Signed-off-by: Josh Steadmon <stead...@google.com>
---
 t/trace_schema_validator/.gitignore           |  1 +
 t/trace_schema_validator/Makefile             | 18 ++++
 .../trace_schema_validator.go                 | 82 +++++++++++++++++++
 3 files changed, 101 insertions(+)
 create mode 100644 t/trace_schema_validator/.gitignore
 create mode 100644 t/trace_schema_validator/Makefile
 create mode 100644 t/trace_schema_validator/trace_schema_validator.go

diff --git a/t/trace_schema_validator/.gitignore 
b/t/trace_schema_validator/.gitignore
new file mode 100644
index 0000000000..c3f1e04e9e
--- /dev/null
+++ b/t/trace_schema_validator/.gitignore
@@ -0,0 +1 @@
+trace_schema_validator
diff --git a/t/trace_schema_validator/Makefile 
b/t/trace_schema_validator/Makefile
new file mode 100644
index 0000000000..50bd266518
--- /dev/null
+++ b/t/trace_schema_validator/Makefile
@@ -0,0 +1,18 @@
+RM = rm -f
+PROGRAMS = trace_schema_validator
+GOCMD = go
+GOBUILD = $(GOCMD) build
+GOGET = $(GOCMD) get
+
+.PHONY: fetch_deps clean
+
+all: $(PROGRAMS)
+
+trace_schema_validator: fetch_deps trace_schema_validator.go
+       $(GOBUILD) -o trace_schema_validator
+
+fetch_deps:
+       $(GOGET) github.com/xeipuuv/gojsonschema
+
+clean:
+       $(RM) $(PROGRAMS)
diff --git a/t/trace_schema_validator/trace_schema_validator.go 
b/t/trace_schema_validator/trace_schema_validator.go
new file mode 100644
index 0000000000..bfda3c405e
--- /dev/null
+++ b/t/trace_schema_validator/trace_schema_validator.go
@@ -0,0 +1,82 @@
+// trace_schema_validator validates individual lines of an input file against a
+// provided JSON-Schema for git trace2 event output.
+//
+// Note that this expects each object to validate to be on its own line in the
+// input file (AKA JSON-Lines format). This is what Git natively writes with
+// GIT_TRACE2_EVENT enabled.
+//
+// Traces can be collected by setting the GIT_TRACE2_EVENT environment variable
+// to an absolute path and running any Git command; traces will be appended to
+// the file.
+//
+// Traces can then be verified like so:
+//   trace_schema_validator \
+//     --trace2-event-file /path/to/trace/output \
+//     --schema-file /path/to/schema
+package main
+
+import (
+       "bufio"
+       "flag"
+       "log"
+       "os"
+       "path/filepath"
+
+       "github.com/xeipuuv/gojsonschema"
+)
+
+// Required flags
+var schemaFile = flag.String("schema-file", "", "JSON-Schema filename")
+var trace2EventFile = flag.String("trace2-event-file", "", "trace2 event 
filename")
+var progress = flag.Int("progress", 0, "Print progress message each time we 
have validated this many lines. --progress=0 means no messages are printed")
+
+func main() {
+       flag.Parse()
+       if *schemaFile == "" || *trace2EventFile == "" {
+               log.Fatal("Both --schema-file and --trace2-event-file are 
required.")
+       }
+       schemaURI, err := filepath.Abs(*schemaFile)
+       if err != nil {
+               log.Fatal("Can't get absolute path for schema file: ", err)
+       }
+       schemaURI = "file://" + schemaURI
+
+       schemaLoader := gojsonschema.NewReferenceLoader(schemaURI)
+       schema, err := gojsonschema.NewSchema(schemaLoader)
+       if err != nil {
+               log.Fatal("Problem loading schema: ", err)
+       }
+
+       tracesFile, err := os.Open(*trace2EventFile)
+       if err != nil {
+               log.Fatal("Problem opening trace file: ", err)
+       }
+       defer tracesFile.Close()
+
+       scanner := bufio.NewScanner(tracesFile)
+
+       count := 0
+       for ; scanner.Scan(); count++ {
+               if *progress != 0 && count%*progress == 0 {
+                       log.Print("Validated items: ", count)
+               }
+               event := gojsonschema.NewStringLoader(scanner.Text())
+               result, err := schema.Validate(event)
+               if err != nil {
+                       log.Fatal(err)
+               }
+               if !result.Valid() {
+                       log.Printf("Trace event line %d is invalid: %s", 
count+1, scanner.Text())
+                       for _, desc := range result.Errors() {
+                               log.Print("- ", desc)
+                       }
+                       os.Exit(1)
+               }
+       }
+
+       if err := scanner.Err(); err != nil {
+               log.Fatal("Scanning error: ", err)
+       }
+
+       log.Print("Validated events: ", count)
+}
-- 
2.22.0.709.g102302147b-goog

Reply via email to