diff options
author | Flu0r1ne <flur01ne@flu0r1ne.net> | 2021-08-05 15:56:10 -0500 |
---|---|---|
committer | Flu0r1ne <flur01ne@flu0r1ne.net> | 2021-08-05 15:56:10 -0500 |
commit | 72a70b127636ae8a83e2a2bd53b69143e3c5ded0 (patch) | |
tree | fdddf5cccdb487d63909f97074d111e67c79ded7 | |
parent | f90a14d5d723c5d2b87f2eaa19f441dec33bb9b2 (diff) | |
download | deb-planr-72a70b127636ae8a83e2a2bd53b69143e3c5ded0.tar.xz deb-planr-72a70b127636ae8a83e2a2bd53b69143e3c5ded0.zip |
Runtime & reorganziation
-rw-r--r-- | adapters.go | 22 | ||||
-rw-r--r-- | adapters/gtest/adapter.go | 106 | ||||
-rw-r--r-- | adapters/gtest/config.go (renamed from adapters/gtest_adapter.go) | 120 | ||||
-rw-r--r-- | adapters/gtest/results.go | 75 | ||||
-rw-r--r-- | adapters/gtest/templating.go | 84 | ||||
-rw-r--r-- | adapters/template_defs.go | 50 | ||||
-rw-r--r-- | fs.go | 3 | ||||
-rw-r--r-- | runner.go | 59 |
8 files changed, 317 insertions, 202 deletions
diff --git a/adapters.go b/adapters.go index 7c7dd0b..8419c8b 100644 --- a/adapters.go +++ b/adapters.go @@ -5,24 +5,12 @@ package planr // Test cases matching adapter configurations will be // fed into the adapter interface type Adapter interface { - - /* CONFIGURATION HOOKS */ - + // Config() AdapterConfig - /* BUILD CYCLE */ + // Called once to preform expensive code generation + Build(testCase []*TestCase) - // Called once at the beginning of the build process - InitializeBuild() - // Called once with every registered test case - // Can access configuration directly - Build(testCase TestCase) - // Called once after all builds - FinalizeBuild() - // Called pre-evaluate - Make() - // Called once per test case after FinalizeBuild - Evaluate(testCase TestCase) TestResult - // Called once after each test has been evaluated - Cleanup() + // Called every time source changes + Evaluate(testCase []*TestCase) } diff --git a/adapters/gtest/adapter.go b/adapters/gtest/adapter.go new file mode 100644 index 0000000..09e6ac1 --- /dev/null +++ b/adapters/gtest/adapter.go @@ -0,0 +1,106 @@ +package gtest + +import ( + "context" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "time" + "path" + "errors" + "golang.flu0r1ne.net/planr" +) + +const GTEST_CMAKE = "CMakeLists.txt" + +func mkUnit(tc *planr.TestCase) cmakeUnit { + cfg := tc.AdapterConfig("gtest").(*GtestConfig) + + return cmakeUnit { + tc.Cname, + cfg.joinTests(*cfg.Testfile), + cfg.srcList(), + }; +} + +func chdir(dir string) { + if err := os.Chdir(dir); err != nil { + log.Fatal(err) + } +} + +type GtestAdapter struct {} + +func (a *GtestAdapter) Config() planr.AdapterConfig { + return planr.AdapterConfig { + Name: "gtest", + ParseConfig: ParseConfig, + ParseDefaultConfig: ParseDefaultConfig, + } +} + +func (adapter *GtestAdapter) Build(tcs []*planr.TestCase) { + buildDir := adapter.Config().Dir() + cmakeFile := path.Join(buildDir, GTEST_CMAKE) + + units := make([]cmakeUnit, 0) + for _, tc := range tcs { + + fmt.Printf("[R] Building %s (%s)\n", tc.Cname, tc.Path) + cfg := tc.AdapterConfig("gtest").(*GtestConfig) + cfg.ensureSatisfied(tc.Path) + + units = append(units, mkUnit(tc)) + } + + genCmake(cmakeFile, units) + + chdir(buildDir) + planr.RunCmd("cmake", "-S", ".", "-B", ".") +} + +func (adapter *GtestAdapter) Evaluate(tcs []*planr.TestCase) { + planr.RunCmd("make", "-k") + buildDir := adapter.Config().Dir() + + results := make([]planr.TestResult, 0) + for _, tc := range tcs { + fmt.Printf("[R] Evaluating %s (%s)\n", tc.Cname, tc.Path) + + exe := path.Join(buildDir, tc.Cname) + + f, err := ioutil.TempFile(buildDir, "gtest_adapter_*.json") + + if err != nil { + log.Fatal(err) + } + + ctx, cancel := context.WithTimeout(context.Background(), 9999*time.Millisecond) + cmd := exec.CommandContext(ctx, exe, "--gtest_output=json:" + f.Name()) + + defer cancel() + defer os.Remove(f.Name()) + + exitFail := false + if err := cmd.Run(); err != nil { + var exiterr *exec.ExitError + + if errors.As(err, &exiterr) && exiterr.ExitCode() == 1{ + exitFail = true + } else { + log.Printf("%v\n", err) + os.Exit(exiterr.ExitCode()) + } + } + + if exitFail { + fmt.Printf("Failure detected") + } + + results = append(results, decodeResults(f)...) + } + + fmt.Println(results) +} diff --git a/adapters/gtest_adapter.go b/adapters/gtest/config.go index fd06a48..6a6c8bf 100644 --- a/adapters/gtest_adapter.go +++ b/adapters/gtest/config.go @@ -1,20 +1,12 @@ -package adapters +package gtest import ( - "bytes" - "log" - "os" - "path" - "strings" - "text/template" - - "github.com/BurntSushi/toml" - "golang.flu0r1ne.net/planr" + "log" + "golang.flu0r1ne.net/planr" + "strings" + "github.com/BurntSushi/toml" ) -/* - CONFIGURATION -*/ type GtestDefaults struct { Name *string Suite *string @@ -40,6 +32,15 @@ type GtestConfig struct { GtestDefaults } +func (g GtestConfig) ensureSatisfied(path string) { + if g.Name == nil { + log.Fatalf("\"name\" is not defined for unit: %s\n", path) + } else if g.Suite == nil { + log.Fatalf("\"suite\" is not defined for unit: %s\n", path) + } else if g.Testfile == nil { + log.Fatalf("\"testfile\" is not defined for unit: %s\n", path) + } +} func (cfg GtestConfig) joinTests(path_ string) string { if cfg.Test_root == nil { @@ -72,16 +73,6 @@ func (cfg GtestConfig) srcList() string { return srcList } -func (g GtestConfig) EnsureSatisfied(path string) { - if g.Name == nil { - log.Fatalf("\"name\" is not defined for unit: %s\n", path) - } else if g.Suite == nil { - log.Fatalf("\"suite\" is not defined for unit: %s\n", path) - } else if g.Testfile == nil { - log.Fatalf("\"testfile\" is not defined for unit: %s\n", path) - } -} - func primitiveDecode(primitive toml.Primitive, config interface{}) { if err := toml.PrimitiveDecode(primitive, config); err != nil { log.Fatal(err) @@ -103,86 +94,3 @@ func ParseDefaultConfig(prim toml.Primitive) planr.InheritableConfig { return &config } - -/* - BUILD PROCESS -*/ - -type GtestAdapter struct { - unitTmpl *template.Template - cbuf bytes.Buffer -} - -func (a *GtestAdapter) Config() planr.AdapterConfig { - return planr.AdapterConfig { - Name: "gtest", - ParseConfig: ParseConfig, - ParseDefaultConfig: ParseDefaultConfig, - } -} - -func (a *GtestAdapter) InitializeBuild() { - a.unitTmpl = UnitTemplate() - a.cbuf = bytes.Buffer{} - - WriteCMakeBoiler(&a.cbuf) -} - -const GTEST_CMAKE = "CMakeLists.txt" - -func Chdir(dir string) { - if err := os.Chdir(dir); err != nil { - log.Fatal(err) - } -} - -func (a *GtestAdapter) Build(tc planr.TestCase) { - cfg := tc.AdapterConfig("gtest").(*GtestConfig) - cfg.EnsureSatisfied(tc.Path) - - cname := tc.Cname - testfile := cfg.joinTests(*cfg.Testfile) - srcList := cfg.srcList() - - err := a.unitTmpl.Execute(&a.cbuf, struct {Cname, File, Srcs string} { - cname, testfile, srcList, - }) - - if err != nil { - log.Fatal(err) - } -} - -func (a *GtestAdapter) FinalizeBuild() { - dir := a.Config().ConfigDir() - cmakeFile := path.Join(dir, GTEST_CMAKE) - - file, err := os.OpenFile(cmakeFile, os.O_RDWR | os.O_CREATE, 0644) - defer func () { - err := file.Close() - - if err != nil { - log.Fatal(err) - } - }() - - if err != nil { - log.Fatalf("Could not open CMakeFile (%s)\n%v", cmakeFile, err) - } - - file.Write(a.cbuf.Bytes()) - - Chdir(dir) - - planr.RunCmd("cmake", "-S", ".", "-B", ".") -} - -func (a *GtestAdapter) Make() { - planr.RunCmd("make", "-k") -} - -func (a *GtestAdapter) Evaluate(tc planr.TestCase) planr.TestResult { - return planr.TestResult {} -} - -func (a *GtestAdapter) Cleanup() { } diff --git a/adapters/gtest/results.go b/adapters/gtest/results.go new file mode 100644 index 0000000..f8c8a23 --- /dev/null +++ b/adapters/gtest/results.go @@ -0,0 +1,75 @@ +package gtest + +import ( + "bytes" + "encoding/json" + "io" + "log" + "time" + + "golang.flu0r1ne.net/planr" +) + +type gFailure struct { + Failure string `json:"failure"` + Type string `json:"type"` +} + +type gTestsuite struct { + Name string `json:"name"` + Status string `json:"status"` + Result string `json:"result"` + Timestamp time.Time `json:"timestamp"` + Time string `json:"time"` + Classname string `json:"classname"` + Failures []gFailure `json:"failures"` +} + +type gTestsuites struct { + Name string `json:"name"` + Tests int `json:"tests"` + Failures int `json:"failures"` + Disabled int `json:"disabled"` + Errors int `json:"errors"` + Timestamp time.Time `json:"timestamp"` + Time string `json:"time"` + Testsuite []gTestsuite `json:"testsuite"` +} + +type gResults struct { + Tests int `json:"tests"` + Failures int `json:"failures"` + Disabled int `json:"disabled"` + Errors int `json:"errors"` + Timestamp time.Time `json:"timestamp"` + Time string `json:"time"` + Name string `json:"name"` + Testsuites []gTestsuites `json:"testsuites"` +} + +func decodeResults(r io.Reader) []planr.TestResult { + var results gResults + buf := bytes.Buffer{} + + if _, err := buf.ReadFrom(r); err != nil { + log.Fatal(err) + } + + if err := json.Unmarshal(buf.Bytes(), &results); err != nil { + log.Fatal(err) + } + + decoded := make([]planr.TestResult, 0) + for _, suite := range results.Testsuites { + for _, test := range suite.Testsuite { + n := len(test.Failures) + + decoded = append(decoded, planr.TestResult { + Id: suite.Name + "_" + test.Name, + Pass: n == 0, + }) + } + } + + return decoded +} diff --git a/adapters/gtest/templating.go b/adapters/gtest/templating.go new file mode 100644 index 0000000..a78eaf8 --- /dev/null +++ b/adapters/gtest/templating.go @@ -0,0 +1,84 @@ +package gtest + +import ( + "io" + "log" + "text/template" + "os" +) + +type cmakeUnit struct { + Cname string + File string + Srcs string +}; + +func genCmake(out string, units []cmakeUnit) { + file, err := os.OpenFile(out, os.O_RDWR | os.O_CREATE, 0644) + defer func () { + err := file.Close() + + if err != nil { + log.Fatal(err) + } + }() + + if err != nil { + log.Fatalf("Could not open CMakeFile (%s)\n%v", out, err) + } + + writeBoiler(file) + + tmpl := unitTemplate() + + for _, unit := range units { + if err := tmpl.Execute(file, unit); err != nil { + log.Fatalf("Failed to generate unit %s: %v", unit.Cname, err); + } + } +} + + +func unitTemplate() *template.Template { + tmpl, err := template.New("gtest_unit").Parse(` +add_executable( + {{.Cname}} + {{.File}} + {{.Srcs}} +) + +target_link_libraries( + {{.Cname}} + gtest_main +) + +gtest_discover_tests( + {{.Cname}} +) +`) + + if err != nil { + log.Fatalf("Cannot load Gtest Unit Template %v", err) + } + + return tmpl +} + +func writeBoiler(w io.Writer) { + w.Write([]byte(` +cmake_minimum_required (VERSION 3.1.0) + +project(PlanRGtestAdapter) + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip +) + +include(GoogleTest) +FetchContent_MakeAvailable(googletest) +`)) +} + + diff --git a/adapters/template_defs.go b/adapters/template_defs.go deleted file mode 100644 index 54f2840..0000000 --- a/adapters/template_defs.go +++ /dev/null @@ -1,50 +0,0 @@ -package adapters - -import ( - "io" - "log" - "text/template" -) - - -func UnitTemplate() *template.Template { - tmpl, err := template.New("gtest_unit").Parse(` -add_executable( - {{.Cname}} - {{.File}} - {{.Srcs}} -) - -target_link_libraries( - {{.Cname}} - gtest_main -) - -gtest_discover_tests( - {{.Cname}} -) -`) - - if err != nil { - log.Fatalf("Cannot load Gtest Unit Template %v", err) - } - - return tmpl -} - -func WriteCMakeBoiler(w io.Writer) { - w.Write([]byte(` -cmake_minimum_required (VERSION 3.1.0) - -project(PlanRGtestAdapter) - -include(FetchContent) -FetchContent_Declare( - googletest - URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip -) - -include(GoogleTest) -FetchContent_MakeAvailable(googletest) -`)) -} @@ -103,7 +103,6 @@ func JoinConfigDir(path_ string, file string) string { return path.Join(ConfigDir(), path_, file) } - func RootDir() string { return path.Join(ConfigDir(), "..") } @@ -140,7 +139,7 @@ func CleanBuildDir() { } } -func (ac AdapterConfig) ConfigDir() string { +func (ac AdapterConfig) Dir() string { dir := BuildDir() dir = path.Join(dir, ac.Name) @@ -1,8 +1,6 @@ package planr -import ( - "fmt" -) +import "fmt" type Runner struct { adapters []Adapter @@ -22,45 +20,52 @@ func (r Runner) adapterCfgs() []AdapterConfig { return cgs } -// [Initialization] -> [Generation] -> [Finalization] -> -// [Build] -> [Evaluation] -> [Clean] +type TcTab map[string] []*TestCase -func (r Runner) cycle(tcs []TestCase) []TestResult { - results := make([]TestResult, 0) +func (r Runner) buildTcLUT(tcs []TestCase) TcTab { + m := make(TcTab, 0) - for _, adapter := range r.adapters { - aname := adapter.Config().Name - - adapter.InitializeBuild() - - for _, tc := range tcs { - if tc.ContainsAdapter(aname) { - fmt.Printf("[R] Building %s\n", tc.Path) - adapter.Build(tc) - } + for i := range tcs { + tc := &tcs[i] + for nm := range tc.Config.adapters_ { + m[nm] = append(m[nm], tc) } + } - adapter.FinalizeBuild() + return m +} - adapter.Make() +func (r Runner) Build(tcs []TestCase) { + tcTab := r.buildTcLUT(tcs) - for _, tc := range tcs { - if tc.ContainsAdapter(aname) { - fmt.Printf("[R] Evaluating %s\n", tc.Path) - results = append(results, adapter.Evaluate(tc)) - } - } + for _, adapter := range r.adapters { + nm := adapter.Config().Name + + fmt.Printf("[R] Building adapter \"%s\"\n", nm) + adapter.Build(tcTab[nm]) + } +} - adapter.Cleanup() +func (r Runner) Evaluate(tcs []TestCase) []TestResult { + tcTab := r.buildTcLUT(tcs) + results := make([]TestResult, 0) + + for _, adapter := range r.adapters { + nm := adapter.Config().Name + + fmt.Printf("[R] Evaluating adapter \"%s\"\n", nm) + adapter.Evaluate(tcTab[nm]) } return results } + func (r Runner) Run(root string) [] TestResult { tcs := collectUnits(root, r.adapterCfgs()) - trs := r.cycle(tcs) + r.Build(tcs) + trs := r.Evaluate(tcs) return trs } |