From 24548e87decbdfea38bbf692cecad6d4eefc3ec0 Mon Sep 17 00:00:00 2001
From: Flu0r1ne <flur01ne@flu0r1ne.net>
Date: Sun, 22 Aug 2021 23:27:53 -0500
Subject: Refactoring & Enhanced logging

---
 adapters/gtest/adapter.go | 75 +++++++++++++++++++++++++++++++----------------
 adapters/gtest/config.go  | 50 +++++++++++++++----------------
 adapters/gtest/results.go | 12 ++++----
 cmd/planr/main.go         |  9 ++++--
 cmd/planr/sub/cli.go      |  2 ++
 config.go                 | 42 ++++++++++++++++++--------
 fs.go                     | 15 ++++++++--
 runner.go                 | 20 +++++++++----
 8 files changed, 144 insertions(+), 81 deletions(-)

diff --git a/adapters/gtest/adapter.go b/adapters/gtest/adapter.go
index 8dc333d..f4fde27 100644
--- a/adapters/gtest/adapter.go
+++ b/adapters/gtest/adapter.go
@@ -9,6 +9,7 @@ import (
 	"os"
 	"os/exec"
 	"path"
+	"sync"
 	"time"
 
 	"golang.flu0r1ne.net/planr"
@@ -26,11 +27,6 @@ func mkUnit(tc *planr.TestCase) cmakeUnit {
   };
 }
 
-func chdir(dir string) {
-  if err := os.Chdir(dir); err != nil {
-    log.Fatal(err)
-  }
-}
 
 type GtestAdapter struct {}
 
@@ -49,7 +45,6 @@ func (adapter *GtestAdapter) Build(tcs []*planr.TestCase) {
   units := make([]cmakeUnit, 0)
   for _, tc := range tcs {
     
-    fmt.Printf("[R] Building %s (%s)\n", tc.Cname, tc.Path)
     cfg := tc.AdapterConfig().(*GtestConfig)
     cfg.ensureSatisfied(tc.Path)
 
@@ -57,8 +52,7 @@ func (adapter *GtestAdapter) Build(tcs []*planr.TestCase) {
   }
 
   genCmake(cmakeFile, units)
-  
-  chdir(buildDir)
+
   planr.RunCmd("cmake", "-S", ".", "-B", ".")
 }
 
@@ -69,7 +63,6 @@ func (adapter *GtestAdapter) execTests(cnames []string) ResultFromId {
 
   lut := make(ResultFromId, 0)
   for _, exe := range cnames {
-      fmt.Printf("[R] Evaluating %s\n", exe)
 
       exePath := path.Join(buildDir, exe)
 
@@ -95,7 +88,14 @@ func (adapter *GtestAdapter) execTests(cnames []string) ResultFromId {
         }
       }
 
-      for _, r  := range decodeResults(f) {
+      results, err := decodeResults(f)
+
+      if err != nil {
+        log.Printf("Could not collect results from %s: %v", exe, err)
+        continue
+      }
+
+      for _, r  := range results {
         r.testOutput = string(out)
         lut[exe + "." + r.id] = r
       }
@@ -134,27 +134,34 @@ func id(tc *planr.TestCase) string {
   return tc.Cname + "." + *cfg.Suite + "." + *cfg.Name
 }
 
-func (adapter *GtestAdapter) Evaluate(tcs []*planr.TestCase) {
-  buildDir := adapter.Config().Dir()
-  chdir(buildDir)
+func compile(wg * sync.WaitGroup, tc *planr.TestCase) {
+  defer wg.Done()
 
-  for _, tc := range tcs {
-    cmd := exec.Command("make", tc.Cname)
-    out, err := cmd.CombinedOutput()
-    tc.Result = new(planr.TestResult)
-
-    // Don't treat command failure as anything but a build failure
-    if err != nil{
-      var exiterr *exec.ExitError
-      if errors.As(err, &exiterr) && exiterr.ExitCode() == 0 {
-        log.Fatal(err)
-      }
+  cmd := exec.Command("make", tc.Cname)
+  out, err := cmd.CombinedOutput()
+  tc.Result = new(planr.TestResult)
 
-      tc.Result.Status = planr.COMPILATION_FAILURE
+  // Don't treat command failure as anything but a build failure
+  if err != nil{
+    var exiterr *exec.ExitError
+    if errors.As(err, &exiterr) && exiterr.ExitCode() == 0 {
+      log.Fatal(err)
     }
 
-    tc.Result.DebugOutput = string(out)
+    tc.Result.Status = planr.COMPILATION_FAILURE
+  }
+
+  tc.Result.DebugOutput = string(out)
+}
+
+// ./planr eval  0.93s user 0.16s system 100% cpu 1.089 total
+func (adapter *GtestAdapter) Evaluate(tcs []*planr.TestCase) {
+  var wg sync.WaitGroup
+  for _, tc := range tcs {
+    wg.Add(1)
+    go compile(&wg, tc)
   }
+  wg.Wait()
 
   files := exes(tcs)
   resultById := adapter.execTests(files)
@@ -164,6 +171,22 @@ func (adapter *GtestAdapter) Evaluate(tcs []*planr.TestCase) {
 
     // compilation failure
     if !ok {
+      fmt.Printf("CAN'T FIND %s: status %d\n", tc.Cname, tc.Result.Status)
+      
+      if tc.Result.Status == planr.PASSING {
+        cfg := tc.AdapterConfig().(*GtestConfig)
+
+        log.Printf(
+          "Could not find testcase %s with name=\"%s\" and suite=\"%s\". Does such a test exist in the test source?",
+          tc.Cname,
+          *cfg.Name,
+          *cfg.Suite,
+        )
+
+        tc.Result.Status = planr.COMPILATION_FAILURE
+        tc.Result.DebugOutput += fmt.Sprintf("planr: Did not find testcase %s in any test executable\n", id(tc))
+      }
+
       continue
     }
  
diff --git a/adapters/gtest/config.go b/adapters/gtest/config.go
index 6a6c8bf..cb5ba75 100644
--- a/adapters/gtest/config.go
+++ b/adapters/gtest/config.go
@@ -8,23 +8,23 @@ import (
 )
 
 type GtestDefaults struct {
-  Name      *string
-  Suite     *string
-  Testfile  *string
-  Test_root *string
-  Srcs      *[]string
-  Srcs_root *string
+  Name          *string
+  Suite         *string
+  Testfile      *string
+  Test_root     *string
+  Srcs          *[]string
+  Srcs_root     *string
 }
 
 func (child *GtestDefaults) Inherit(p interface{}) {
   parent := p.(*GtestDefaults)
 
-  if(child.Name == nil)      { child.Name = parent.Name }
-  if(child.Suite == nil)     { child.Suite = parent.Suite }
-  if(child.Testfile == nil)  { child.Testfile = parent.Testfile }
-  if(child.Test_root == nil) { child.Test_root = parent.Test_root }
-  if(child.Srcs == nil)      { child.Srcs = parent.Srcs }
-  if(child.Srcs_root == nil) { child.Srcs_root = parent.Srcs_root }
+  if(child.Name == nil)          { child.Name = parent.Name }
+  if(child.Suite == nil)         { child.Suite = parent.Suite }
+  if(child.Testfile == nil)      { child.Testfile = parent.Testfile }
+  if(child.Test_root == nil)     { child.Test_root = parent.Test_root }
+  if(child.Srcs == nil)          { child.Srcs = parent.Srcs }
+  if(child.Srcs_root == nil)     { child.Srcs_root = parent.Srcs_root }
 }
 
 
@@ -73,24 +73,22 @@ func (cfg GtestConfig) srcList() string {
   return srcList
 }
 
-func primitiveDecode(primitive toml.Primitive, config interface{}) {
-  if err := toml.PrimitiveDecode(primitive, config); err != nil {
-    log.Fatal(err)
-  }
-}
-
-func ParseConfig(prim toml.Primitive) planr.InheritableConfig {
+func ParseConfig(prim toml.Primitive) (planr.InheritableConfig, error) {
     config := GtestConfig{}
-
-    primitiveDecode(prim, &config)
-
-    return &config
+  
+    if err := toml.PrimitiveDecode(prim, &config); err != nil {
+      return nil, err
+    }
+    
+    return &config, nil
 }
 
-func ParseDefaultConfig(prim toml.Primitive) planr.InheritableConfig {
+func ParseDefaultConfig(prim toml.Primitive) (planr.InheritableConfig, error) {
     config := GtestDefaults{}
 
-    primitiveDecode(prim, &config)
+    if err := toml.PrimitiveDecode(prim, &config); err != nil {
+      return nil, err
+    }
 
-    return &config
+    return &config, nil
 }
diff --git a/adapters/gtest/results.go b/adapters/gtest/results.go
index 2991823..14f5d1d 100644
--- a/adapters/gtest/results.go
+++ b/adapters/gtest/results.go
@@ -4,7 +4,6 @@ import (
 	"bytes"
 	"encoding/json"
 	"io"
-	"log"
 	"time"
 )
 
@@ -62,19 +61,20 @@ func failureMsg(failures []gFailure) string {
   return failure_msg
 }
 
-func decodeResults(r io.Reader) []Result {
+func decodeResults(r io.Reader) ([]Result, error) {
+  decoded := make([]Result, 0)
+  
   var results gResults
   buf := bytes.Buffer{}
 
   if _, err := buf.ReadFrom(r); err != nil {
-    log.Fatal(err) 
+    return decoded, err
   }
 
   if err := json.Unmarshal(buf.Bytes(), &results); err != nil {
-    log.Fatal(err)
+    return decoded, err
   }
 
-  decoded := make([]Result, 0)
   for _, suite := range results.Testsuites {
     for _, test := range suite.Testsuite {
       n := len(test.Failures)
@@ -91,5 +91,5 @@ func decodeResults(r io.Reader) []Result {
     }
   }
 
-  return decoded
+  return decoded, nil
 }
diff --git a/cmd/planr/main.go b/cmd/planr/main.go
index 3c1c298..83a60bc 100644
--- a/cmd/planr/main.go
+++ b/cmd/planr/main.go
@@ -1,9 +1,11 @@
 package main
 
 import (
-	"os"
-	"io"
 	"fmt"
+	"io"
+	"log"
+	"os"
+
 	"golang.flu0r1ne.net/planr/cmd/planr/sub"
 )
 
@@ -26,6 +28,9 @@ func dieUsage() {
 
 func main() {
 
+        log.SetFlags(log.Llongfile | log.Lmsgprefix)
+        log.SetPrefix("planr: ")
+
 	if len(os.Args) < 2 {
 	  dieUsage()
 	}
diff --git a/cmd/planr/sub/cli.go b/cmd/planr/sub/cli.go
index dff9089..0e6a942 100644
--- a/cmd/planr/sub/cli.go
+++ b/cmd/planr/sub/cli.go
@@ -75,6 +75,8 @@ func tcStatusLine(tc planr.TestCase) {
 func tcPprint(tc planr.TestCase) {
   tcStatusLine(tc)
 
+  pprintLabeled("id", tc.Cname)
+
   if tc.Config.Points != nil {
     points := fmt.Sprintf("%.1f", *tc.Config.Points)
     pprintLabeled("points", points)
diff --git a/config.go b/config.go
index 6efa90d..bc0fa6a 100644
--- a/config.go
+++ b/config.go
@@ -1,7 +1,6 @@
 package planr
 
 import (
-  // "fmt"
   "log"
   "github.com/BurntSushi/toml"
 )
@@ -41,7 +40,7 @@ type InheritableConfig interface {
 
 // A parser function takes a blob of TOML and decodes it into
 // configuration relevant to an adapter
-type TomlParser func (toml.Primitive) InheritableConfig
+type TomlParser func (toml.Primitive) (InheritableConfig, error)
 
 // The name under which an adapter registers corresponds
 // to a table under the super-table adapters. All corresponding
@@ -134,7 +133,10 @@ func (child *Defaults) Inherit(p interface{}) {
 // according to methods registered with the runner
 // Once parsed, they are stored alongside the registered name to determine
 // which adapter will receive the configuration
-func (defaults *Defaults) decodeAdapters(adapters []AdapterConfig, asDefault bool) {
+func (defaults *Defaults) decodeAdapters(
+  adapters []AdapterConfig,
+  asDefault bool,
+) error {
   defaults.configs_ = &adapters
   defaults.adapters_ = make(map[string]InheritableConfig)
 
@@ -144,40 +146,54 @@ func (defaults *Defaults) decodeAdapters(adapters []AdapterConfig, asDefault boo
     
       if exists {
         var parsed InheritableConfig
+        var err error
         if asDefault {
-          parsed = config.ParseDefaultConfig(primitive)
+          parsed, err = config.ParseDefaultConfig(primitive)
         } else {
-          parsed = config.ParseConfig(primitive)
+          parsed, err = config.ParseConfig(primitive)
+        }
+
+        if err != nil {
+          return err
         }
 
         defaults.adapters_[config.Name] = parsed
       }
     }
   }
+
+  return nil
 }
 
 // Decode defaults.toml
-func DecodeDefaults(path string, adapterCfg []AdapterConfig) Defaults {
+func DecodeDefaults(path string, adapterCfg []AdapterConfig) (Defaults, error) {
   defaults := Defaults { }
 
+
+
   if _, err := toml.DecodeFile(path, &defaults); err != nil {
-    log.Fatal(err)
+    return defaults, err
+  }
+
+  if err := defaults.decodeAdapters(adapterCfg, true); err != nil {
+    return defaults, err
   }
 
-  defaults.decodeAdapters(adapterCfg, true)
 
-  return defaults
+  return defaults, nil
 }
 
 // Decode an individual unit
-func DecodeConfig(path string, adapterCfg []AdapterConfig) TestCaseConfig {
+func DecodeConfig(path string, adapterCfg []AdapterConfig) (TestCaseConfig, error) {
   config := TestCaseConfig { }
 
   if _, err := toml.DecodeFile(path, &config); err != nil {
-    log.Fatal(err)
+    return config, nil
   }
 
-  config.decodeAdapters(adapterCfg, false)
+  if err := config.decodeAdapters(adapterCfg, false); err != nil {
+    return config, err
+  }
 
-  return config
+  return config, nil
 }
diff --git a/fs.go b/fs.go
index bd27cf8..86de16b 100644
--- a/fs.go
+++ b/fs.go
@@ -208,11 +208,15 @@ func collectFromDir(
   // Process defaults for this directory if a defaults.toml is found
   defaultsPath := path.Join(dir, DEFAULTS)
   if info, err := os.Stat(defaultsPath); err == nil && !info.IsDir() {
-    d := DecodeDefaults(defaultsPath, cfgs)
+    d, err  := DecodeDefaults(defaultsPath, cfgs)
+
+    if err != nil {
+      log.Fatalf("Error encounter in %s: %v\n", defaultsPath, err);
+    }
 
     // inherit the properties not defined in this defaults
     if defaults != nil {
-      d.Inherit(defaults)
+      d.Inherit(*defaults)
     }
 
     defaults = &d
@@ -240,7 +244,12 @@ func collectFromDir(
         }
 
         // Decode a unit
-        config := DecodeConfig(child, cfgs)
+        config, err := DecodeConfig(child, cfgs)
+
+        if err != nil {
+          log.Fatalf("Error encountered in %s: %v", child, config) 
+        }
+
         config.Inherit(*defaults)
 
         tc := TestCase {
diff --git a/runner.go b/runner.go
index 96bcd19..3bee17a 100644
--- a/runner.go
+++ b/runner.go
@@ -1,6 +1,9 @@
 package planr
 
-import "fmt"
+import (
+	"log"
+	"os"
+)
 
 type Runner struct {
   adapters    []Adapter
@@ -40,6 +43,14 @@ func (r Runner) checkConfig(tcs []TestCase) {
   }
 }
 
+func cdBuild(adapter Adapter) {
+  dir := adapter.Config().Dir()
+
+  if err := os.Chdir(dir); err != nil {
+    log.Fatal(err)
+  }
+}
+
 func (r Runner) build(tcs []TestCase) {
   r.checkConfig(tcs)
 
@@ -47,8 +58,8 @@ func (r Runner) build(tcs []TestCase) {
 
   for _, adapter := range r.adapters {
     nm := adapter.Config().Name
-  
-    fmt.Printf("[R] Building adapter \"%s\"\n", nm)
+    cdBuild(adapter)
+
     adapter.Build(tcTab[nm])
   }
 }
@@ -67,11 +78,10 @@ func (r Runner) evaluate(tcs []TestCase) {
   
   for _, adapter := range r.adapters {
     nm := adapter.Config().Name
+    cdBuild(adapter)
 
-    fmt.Printf("[R] Evaluating adapter \"%s\"\n", nm)
     adapter.Evaluate(tcTab[nm])
   }
-
 }
 
 func (r Runner) Evaluate(root string) []TestCase {
-- 
cgit v1.2.3