From 2af7117891cdb67758219a7075f39c05dc02f3f5 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Thu, 2 Sep 2021 03:14:47 -0500 Subject: Make adapters and internals complient with new directory structure --- runner.go | 48 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 14 deletions(-) (limited to 'runner.go') diff --git a/runner.go b/runner.go index 3bee17a..f2eb8e9 100644 --- a/runner.go +++ b/runner.go @@ -3,10 +3,12 @@ package planr import ( "log" "os" + "path" ) type Runner struct { adapters []Adapter + DirConfig } func (r *Runner) RegisterAdapter(a Adapter) { @@ -43,12 +45,17 @@ func (r Runner) checkConfig(tcs []TestCase) { } } -func cdBuild(adapter Adapter) { - dir := adapter.Config().Dir() +func (r Runner) setupEnv(adapter Adapter) { + nm := adapter.Config().Name + wd := path.Join(r.BuildDir(), nm) - if err := os.Chdir(dir); err != nil { - log.Fatal(err) - } + if !directoryExists(wd) { + if err := os.Mkdir(wd, 0755); err != nil { + log.Fatalf("Could not create adapter config %s %v\n", wd, err) + } + } + + safeCd(wd) } func (r Runner) build(tcs []TestCase) { @@ -58,18 +65,25 @@ func (r Runner) build(tcs []TestCase) { for _, adapter := range r.adapters { nm := adapter.Config().Name - cdBuild(adapter) + r.setupEnv(adapter) adapter.Build(tcTab[nm]) } + + safeCd(r.ConfigDir()) } -func (r Runner) units(root string) []TestCase { - return collectUnits(root, r.adapterCfgs()) +func (r Runner) units() []TestCase { + return collectUnits(r.RubricDir(), r.adapterCfgs()) } -func (r Runner) Build(root string) { - units := r.units(root) +func (r Runner) Build() { + units := r.units() + + if !directoryExists(r.BuildDir()) { + r.MkBuildDir() + } + r.build(units) } @@ -78,16 +92,22 @@ func (r Runner) evaluate(tcs []TestCase) { for _, adapter := range r.adapters { nm := adapter.Config().Name - cdBuild(adapter) - + + r.setupEnv(adapter) adapter.Evaluate(tcTab[nm]) } + + safeCd(r.ConfigDir()) } -func (r Runner) Evaluate(root string) []TestCase { - units := r.units(root) +func (r Runner) Evaluate() []TestCase { + units := r.units() r.evaluate(units) return units } + +func (r Runner) Clean() { + r.CleanBuildDir() +} -- cgit v1.2.3 From 571abf917bb3a6aa7ac7dd40969ed59ce84d78db Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Fri, 3 Sep 2021 00:02:31 -0500 Subject: Add clean option and fix issue with dir propegation --- Makefile | 8 +++++++- adapters/gtest/adapter.go | 4 ++-- adapters/gtest/config.go | 4 ---- cmd/planr/main.go | 4 ++++ cmd/planr/sub/build.go | 2 +- cmd/planr/sub/clean.go | 5 +++++ runner.go | 32 +++++++++++++++++++++++--------- stddirs.go | 41 ++++++++++++++++++++--------------------- 8 files changed, 62 insertions(+), 38 deletions(-) create mode 100644 cmd/planr/sub/clean.go (limited to 'runner.go') diff --git a/Makefile b/Makefile index 424c67d..dd6031f 100644 --- a/Makefile +++ b/Makefile @@ -8,8 +8,14 @@ endif CMD := planr +ifdef DEBUG + FLAGS=-ldflags=-w +endif + +MAIN_PKG := ./cmd/planr/main.go + $(CMD): - go build -o $(CMD) ./cmd/planr/main.go + go build $(FLAGS) -o $(CMD) $(MAIN_PKG) install: mkdir -p $(DESTDIR)$(PREFIX)$(BINDIR)/ diff --git a/adapters/gtest/adapter.go b/adapters/gtest/adapter.go index f91a524..29e2df7 100644 --- a/adapters/gtest/adapter.go +++ b/adapters/gtest/adapter.go @@ -19,8 +19,8 @@ const GTEST_CMAKE = "CMakeLists.txt" func makeUnit(tc *planr.TestCase, dirs planr.DirConfig) cmakeUnit { cfg := tc.AdapterConfig().(*GtestConfig) - testpath := path.Join(dirs.TestsDir(), *cfg.Testfile) - srclist := cfg.srcList(dirs.SrcDir()) + testpath := path.Join(dirs.Tests(), *cfg.Testfile) + srclist := cfg.srcList(dirs.Src()) return cmakeUnit { tc.Cname, diff --git a/adapters/gtest/config.go b/adapters/gtest/config.go index bf5c9f2..173809d 100644 --- a/adapters/gtest/config.go +++ b/adapters/gtest/config.go @@ -12,9 +12,7 @@ type GtestDefaults struct { Name *string Suite *string Testfile *string - Test_root *string Srcs *[]string - Srcs_root *string } func (child *GtestDefaults) Inherit(p interface{}) { @@ -23,9 +21,7 @@ func (child *GtestDefaults) Inherit(p interface{}) { 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 } } diff --git a/cmd/planr/main.go b/cmd/planr/main.go index 4e0e05b..85b766e 100644 --- a/cmd/planr/main.go +++ b/cmd/planr/main.go @@ -19,6 +19,8 @@ func printUsage(w io.Writer) { fmt.Fprintln(w, " version ") fmt.Fprintln(w, " build ") fmt.Fprintln(w, " evaluate ") + fmt.Fprintln(w, " clean ") + } func dieUsage() { @@ -45,6 +47,8 @@ func main() { sub.Build(subargs) case "evaluate","eval": sub.Evaluate(subargs) + case "clean": + sub.Clean(subargs) case "help", "-h", "-help", "--help": printUsage(os.Stdout) default: diff --git a/cmd/planr/sub/build.go b/cmd/planr/sub/build.go index 066245c..d6e7456 100644 --- a/cmd/planr/sub/build.go +++ b/cmd/planr/sub/build.go @@ -11,7 +11,7 @@ func Runner() planr.Runner { r.RegisterAdapter(>est.GtestAdapter{}) - if wd, err := os.Getwd(); err != nil { + if wd, err := os.Getwd(); err == nil { r.SetConfigDirFromTree(wd) } diff --git a/cmd/planr/sub/clean.go b/cmd/planr/sub/clean.go new file mode 100644 index 0000000..d40e967 --- /dev/null +++ b/cmd/planr/sub/clean.go @@ -0,0 +1,5 @@ +package sub + +func Clean(params []string) { + Runner().Clean() +} diff --git a/runner.go b/runner.go index f2eb8e9..0a6243c 100644 --- a/runner.go +++ b/runner.go @@ -8,7 +8,7 @@ import ( type Runner struct { adapters []Adapter - DirConfig + dirs DirConfig } func (r *Runner) RegisterAdapter(a Adapter) { @@ -47,7 +47,7 @@ func (r Runner) checkConfig(tcs []TestCase) { func (r Runner) setupEnv(adapter Adapter) { nm := adapter.Config().Name - wd := path.Join(r.BuildDir(), nm) + wd := path.Join(r.dirs.Build(), nm) if !directoryExists(wd) { if err := os.Mkdir(wd, 0755); err != nil { @@ -70,18 +70,26 @@ func (r Runner) build(tcs []TestCase) { adapter.Build(tcTab[nm]) } - safeCd(r.ConfigDir()) + safeCd(r.dirs.Config()) +} + +func (r Runner) init_adapters() { + for _, adapter := range r.adapters { + adapter.Init(r.dirs) + } } func (r Runner) units() []TestCase { - return collectUnits(r.RubricDir(), r.adapterCfgs()) + return collectUnits(r.dirs.Rubric(), r.adapterCfgs()) } func (r Runner) Build() { - units := r.units() + r.init_adapters() - if !directoryExists(r.BuildDir()) { - r.MkBuildDir() + units := r.units() + + if !directoryExists(r.dirs.Build()) { + r.dirs.MkBuild() } r.build(units) @@ -97,10 +105,12 @@ func (r Runner) evaluate(tcs []TestCase) { adapter.Evaluate(tcTab[nm]) } - safeCd(r.ConfigDir()) + safeCd(r.dirs.Config()) } func (r Runner) Evaluate() []TestCase { + r.init_adapters() + units := r.units() r.evaluate(units) @@ -109,5 +119,9 @@ func (r Runner) Evaluate() []TestCase { } func (r Runner) Clean() { - r.CleanBuildDir() + r.dirs.CleanBuild() +} + +func (r * Runner) SetConfigDirFromTree(childPath string) { + r.dirs.SetConfigFromTree(childPath) } diff --git a/stddirs.go b/stddirs.go index 14776d8..2385529 100644 --- a/stddirs.go +++ b/stddirs.go @@ -66,22 +66,22 @@ func dirFromEnv(name, env string) *string { return nil } -func (c *DirConfig) SetSrcDir(srcDir string) { +func (c *DirConfig) SetSrc(srcDir string) { dieDirAbsent("src", srcDir) c.src = srcDir } -func (c *DirConfig) SetConfigDir(configDir string) { +func (c *DirConfig) SetConfig(configDir string) { dieDirAbsent("planr (config)", configDir) c.config = configDir } -func (c *DirConfig) SetBuildDir(buildDir string) { +func (c *DirConfig) SetBuild(buildDir string) { dieDirAbsent("build", buildDir) c.build = buildDir } -func (c *DirConfig) SetConfigDirFromTree(cdir string) { +func (c *DirConfig) SetConfigFromTree(cdir string) { var configDir string found := traverseUp(cdir, func (path string) bool { @@ -103,12 +103,11 @@ func (c *DirConfig) SetConfigDirFromTree(cdir string) { } } -func (c DirConfig) ConfigDir() string { +func (c DirConfig) Config() string { if c.config != "" { return c.config } - if dir := dirFromEnv("config", ENV_CONFIG_DIR); dir != nil { c.config = *dir return c.config @@ -122,7 +121,7 @@ func (c DirConfig) ConfigDir() string { return c.config } -func (c DirConfig) SrcDir() string { +func (c DirConfig) Src() string { if c.src != "" { return c.src } @@ -133,11 +132,11 @@ func (c DirConfig) SrcDir() string { } // set path relative to config - dir := c.ConfigDir() + dir := c.Config() return path.Join(dir, DEFAULT_PATH_SRC) } -func (c DirConfig) BuildDir() string { +func (c DirConfig) Build() string { if c.src != "" { return c.src } @@ -147,38 +146,38 @@ func (c DirConfig) BuildDir() string { return c.build } - dir := c.ConfigDir() + dir := c.Config() return path.Join(dir, DEFAULT_PATH_BUILD) } -func (c DirConfig) CleanBuildDir() { - build := c.BuildDir() +func (c DirConfig) CleanBuild() { + build := c.Build() if err := os.RemoveAll(build); err != nil { log.Fatalf("Cannot build directory %v\n", err) } - if err := os.Remove(build); err != nil { - log.Fatalf("Could not remove build directory %v\n", err) - } + // if err := os.Remove(build); err != nil { + // log.Fatalf("Could not remove build directory %v\n", err) + // } } -func (c DirConfig) MkBuildDir() { - build := c.BuildDir() +func (c DirConfig) MkBuild() { + build := c.Build() if err := os.Mkdir(build, 0755); err != nil { log.Fatalf("Could not create build directory %v\n", err) } } -func (c DirConfig) RubricDir() string { - rubric := path.Join(c.ConfigDir(), "rubric") +func (c DirConfig) Rubric() string { + rubric := path.Join(c.Config(), "rubric") dieDirAbsent("rubric", rubric) return rubric } -func (c DirConfig) TestsDir() string { - tests := path.Join(c.ConfigDir(), "tests") +func (c DirConfig) Tests() string { + tests := path.Join(c.Config(), "tests") dieDirAbsent("tests", tests) return tests } -- cgit v1.2.3 From d670eeb256094deaceef13656e6c60e0f9ff5427 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Fri, 3 Sep 2021 18:13:49 -0500 Subject: Add config command to retrieve configuration values and clean command to clean build files --- cmd/planr/main.go | 51 ++++++++++++++++++++++++++++++++++++++++------- cmd/planr/sub/build.go | 17 ++-------------- cmd/planr/sub/clean.go | 6 ++++-- cmd/planr/sub/config.go | 28 ++++++++++++++++++++++++++ cmd/planr/sub/evaluate.go | 4 ++-- runner.go | 24 ++++++++++++++++++++++ 6 files changed, 104 insertions(+), 26 deletions(-) create mode 100644 cmd/planr/sub/config.go (limited to 'runner.go') diff --git a/cmd/planr/main.go b/cmd/planr/main.go index 85b766e..4b4a965 100644 --- a/cmd/planr/main.go +++ b/cmd/planr/main.go @@ -5,8 +5,11 @@ import ( "io" "log" "os" + "flag" + "golang.furkistan.com/planr" "golang.furkistan.com/planr/cmd/planr/sub" + "golang.furkistan.com/planr/adapters/gtest" ) const ( @@ -20,7 +23,7 @@ func printUsage(w io.Writer) { fmt.Fprintln(w, " build ") fmt.Fprintln(w, " evaluate ") fmt.Fprintln(w, " clean ") - + fmt.Fprintln(w, " config ") } func dieUsage() { @@ -28,6 +31,36 @@ func dieUsage() { os.Exit(1) } +func NewRunner() planr.Runner { + r := planr.Runner {} + + r.RegisterAdapter(>est.GtestAdapter{}) + + if wd, err := os.Getwd(); err == nil { + r.SetConfigDirFromTree(wd) + } + + src := flag.String("srcdir", "", "source directory") + config := flag.String("configdir", "", "config directory") + build := flag.String("builddir", "", "build directory") + + flag.Parse() + + if src != nil && *src != "" { + r.SetSrcDir(*src) + } + + if config != nil && *config != "" { + r.SetConfigDir(*config) + } + + if build != nil && *build != "" { + r.SetBuildDir(*build) + } + + return r +} + func main() { log.SetFlags(log.Llongfile | log.Lmsgprefix) @@ -37,18 +70,22 @@ func main() { dieUsage() } - subcommand := os.Args[1] - subargs := os.Args[2:] + runner := NewRunner() + + subcommand := flag.Arg(0) + subargs := flag.Args()[1:] switch subcommand { case "version": fmt.Printf("%s\n", VERSION) case "build": - sub.Build(subargs) - case "evaluate","eval": - sub.Evaluate(subargs) + sub.Build(runner, subargs) + case "evaluate", "eval": + sub.Evaluate(runner, subargs) case "clean": - sub.Clean(subargs) + sub.Clean(runner, subargs) + case "config": + sub.Config(runner, subargs) case "help", "-h", "-help", "--help": printUsage(os.Stdout) default: diff --git a/cmd/planr/sub/build.go b/cmd/planr/sub/build.go index d6e7456..bddeb4b 100644 --- a/cmd/planr/sub/build.go +++ b/cmd/planr/sub/build.go @@ -2,22 +2,9 @@ package sub import ( "golang.furkistan.com/planr" - "golang.furkistan.com/planr/adapters/gtest" - "os" ) -func Runner() planr.Runner { - r := planr.Runner {} - r.RegisterAdapter(>est.GtestAdapter{}) - - if wd, err := os.Getwd(); err == nil { - r.SetConfigDirFromTree(wd) - } - - return r -} - -func Build(params []string) { - Runner().Build() +func Build(runner planr.Runner, params []string) { + runner.Build() } diff --git a/cmd/planr/sub/clean.go b/cmd/planr/sub/clean.go index d40e967..ca27027 100644 --- a/cmd/planr/sub/clean.go +++ b/cmd/planr/sub/clean.go @@ -1,5 +1,7 @@ package sub -func Clean(params []string) { - Runner().Clean() +import "golang.furkistan.com/planr" + +func Clean(runner planr.Runner, params []string) { + runner.Clean() } diff --git a/cmd/planr/sub/config.go b/cmd/planr/sub/config.go new file mode 100644 index 0000000..ce03f9e --- /dev/null +++ b/cmd/planr/sub/config.go @@ -0,0 +1,28 @@ +package sub + +import ( + "golang.furkistan.com/planr" + "fmt" + "os" +) + + +func Config(runner planr.Runner, params []string) { + if len(params) != 1 { + fmt.Fprintf(os.Stderr, "Usage: planr config \n") + os.Exit(1) + } + + key := params[0] + + switch key { + case "builddir": + fmt.Printf("%s\n", runner.BuildDir()) + case "configdir": + fmt.Printf("%s\n", runner.ConfigDir()) + case "srcdir": + fmt.Printf("%s\n", runner.SrcDir()) + default: + fmt.Fprintf(os.Stderr, "\"%s\" not found in configuration\n", key) + } +} diff --git a/cmd/planr/sub/evaluate.go b/cmd/planr/sub/evaluate.go index 3223304..f8ec509 100644 --- a/cmd/planr/sub/evaluate.go +++ b/cmd/planr/sub/evaluate.go @@ -4,8 +4,8 @@ import ( "golang.furkistan.com/planr" ) -func Evaluate(params []string) { - tcs := Runner().Evaluate() +func Evaluate(runner planr.Runner, params []string) { + tcs := runner.Evaluate() earned := 0.0 total := 0.0 diff --git a/runner.go b/runner.go index 0a6243c..9014b8c 100644 --- a/runner.go +++ b/runner.go @@ -125,3 +125,27 @@ func (r Runner) Clean() { func (r * Runner) SetConfigDirFromTree(childPath string) { r.dirs.SetConfigFromTree(childPath) } + +func (r * Runner) SetBuildDir(dir string) { + r.dirs.SetBuild(dir) +} + +func (r * Runner) SetConfigDir(dir string) { + r.dirs.SetConfig(dir) +} + +func (r * Runner) SetSrcDir(dir string) { + r.dirs.SetSrc(dir) +} + +func (r Runner) BuildDir() string { + return r.dirs.Build(); +} + +func (r Runner) ConfigDir() string { + return r.dirs.Config() +} + +func (r Runner) SrcDir() string { + return r.dirs.Src() +} -- cgit v1.2.3 From 9a8d57fe0e86645a124a7ff30ba6e171d4325ddf Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Fri, 3 Sep 2021 20:41:11 -0500 Subject: Don't export testcase LUT --- runner.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runner.go') diff --git a/runner.go b/runner.go index 9014b8c..c0c926a 100644 --- a/runner.go +++ b/runner.go @@ -25,10 +25,10 @@ func (r Runner) adapterCfgs() []AdapterConfig { return cgs } -type TcTab map[string] []*TestCase +type tcTab map[string] []*TestCase -func (r Runner) buildTcLUT(tcs []TestCase) TcTab { - m := make(TcTab, 0) +func (r Runner) buildTcLUT(tcs []TestCase) tcTab { + m := make(tcTab, 0) for i := range tcs { tc := &tcs[i] -- cgit v1.2.3 From a70cfc735be53ac01cd6065c13fc9884bd96ae03 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Fri, 3 Sep 2021 21:14:28 -0500 Subject: Add new test runner API --- adapters/gtest/adapter.go | 7 +++++-- cmd/planr/main.go | 21 ++++++++++----------- runner.go | 20 -------------------- runner_builder.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 33 deletions(-) create mode 100644 runner_builder.go (limited to 'runner.go') diff --git a/adapters/gtest/adapter.go b/adapters/gtest/adapter.go index 29e2df7..2cc5603 100644 --- a/adapters/gtest/adapter.go +++ b/adapters/gtest/adapter.go @@ -188,8 +188,7 @@ 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) @@ -214,3 +213,7 @@ func (adapter *GtestAdapter) Evaluate(tcs []*planr.TestCase) { tc.Result.TestOutput = result.testOutput } } + +func New() *GtestAdapter { + return new(GtestAdapter) +} diff --git a/cmd/planr/main.go b/cmd/planr/main.go index 028c776..413787b 100644 --- a/cmd/planr/main.go +++ b/cmd/planr/main.go @@ -27,15 +27,14 @@ func dieUsage() { os.Exit(1) } -func NewRunner() planr.Runner { - r := planr.Runner {} - - r.RegisterAdapter(>est.GtestAdapter{}) +func getConfiguredRunner() planr.Runner { + r := planr.ConfigureRunner() + r = planr.RegisterAdapter(r, gtest.New()) if wd, err := os.Getwd(); err == nil { - r.SetConfigDirFromTree(wd) + r = planr.SetConfigDirFromTree(r, wd) } - + src := flag.String("srcdir", "", "source directory") config := flag.String("configdir", "", "config directory") build := flag.String("builddir", "", "build directory") @@ -43,18 +42,18 @@ func NewRunner() planr.Runner { flag.Parse() if src != nil && *src != "" { - r.SetSrcDir(*src) + r = planr.SetSrcDir(r, *src) } if config != nil && *config != "" { - r.SetConfigDir(*config) + r = planr.SetConfigDir(r, *config) } if build != nil && *build != "" { - r.SetBuildDir(*build) + r = planr.SetBuildDir(r, *build) } - return r + return r.New() } func main() { @@ -66,7 +65,7 @@ func main() { dieUsage() } - runner := NewRunner() + runner := getConfiguredRunner() subcommand := flag.Arg(0) subargs := flag.Args()[1:] diff --git a/runner.go b/runner.go index c0c926a..1cc509e 100644 --- a/runner.go +++ b/runner.go @@ -11,10 +11,6 @@ type Runner struct { dirs DirConfig } -func (r *Runner) RegisterAdapter(a Adapter) { - r.adapters = append(r.adapters, a) -} - func (r Runner) adapterCfgs() []AdapterConfig { cgs := make([]AdapterConfig, len(r.adapters)) @@ -122,22 +118,6 @@ func (r Runner) Clean() { r.dirs.CleanBuild() } -func (r * Runner) SetConfigDirFromTree(childPath string) { - r.dirs.SetConfigFromTree(childPath) -} - -func (r * Runner) SetBuildDir(dir string) { - r.dirs.SetBuild(dir) -} - -func (r * Runner) SetConfigDir(dir string) { - r.dirs.SetConfig(dir) -} - -func (r * Runner) SetSrcDir(dir string) { - r.dirs.SetSrc(dir) -} - func (r Runner) BuildDir() string { return r.dirs.Build(); } diff --git a/runner_builder.go b/runner_builder.go new file mode 100644 index 0000000..848d09b --- /dev/null +++ b/runner_builder.go @@ -0,0 +1,42 @@ +package planr + +type RunnerBuilder struct { + adapters [] Adapter + dirs DirConfig +} + +func ConfigureRunner() RunnerBuilder { + return RunnerBuilder {} +} + +func RegisterAdapter(b RunnerBuilder, a Adapter) RunnerBuilder { + b.adapters = append(b.adapters, a) + return b +} + +func SetConfigDirFromTree(b RunnerBuilder, childPath string) RunnerBuilder { + b.dirs.SetConfigFromTree(childPath) + return b +} + +func SetConfigDir(b RunnerBuilder, dir string) RunnerBuilder { + b.dirs.SetConfig(dir) + return b +} + +func SetBuildDir(b RunnerBuilder, dir string) RunnerBuilder { + b.dirs.SetBuild(dir) + return b +} + +func SetSrcDir(b RunnerBuilder, dir string) RunnerBuilder { + b.dirs.SetSrc(dir) + return b +} + +func (b RunnerBuilder) New() Runner { + return Runner { + dirs: b.dirs, + adapters: b.adapters, + } +} -- cgit v1.2.3 From 832cc5bf6e4bd3812bacbe6191e29ad1d34368fe Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Sat, 4 Sep 2021 13:41:52 -0500 Subject: Add runner initalization --- runner.go | 19 ++++++++++--------- runner_builder.go | 5 +---- 2 files changed, 11 insertions(+), 13 deletions(-) (limited to 'runner.go') diff --git a/runner.go b/runner.go index 1cc509e..d3d4b08 100644 --- a/runner.go +++ b/runner.go @@ -69,19 +69,11 @@ func (r Runner) build(tcs []TestCase) { safeCd(r.dirs.Config()) } -func (r Runner) init_adapters() { - for _, adapter := range r.adapters { - adapter.Init(r.dirs) - } -} - func (r Runner) units() []TestCase { return collectUnits(r.dirs.Rubric(), r.adapterCfgs()) } func (r Runner) Build() { - r.init_adapters() - units := r.units() if !directoryExists(r.dirs.Build()) { @@ -105,7 +97,6 @@ func (r Runner) evaluate(tcs []TestCase) { } func (r Runner) Evaluate() []TestCase { - r.init_adapters() units := r.units() @@ -129,3 +120,13 @@ func (r Runner) ConfigDir() string { func (r Runner) SrcDir() string { return r.dirs.Src() } + +func NewRunner(adapters []Adapter, dirs DirConfig) Runner { + r := Runner{adapters, dirs} + + for _, adapter := range r.adapters { + adapter.Init(dirs) + } + + return r +} diff --git a/runner_builder.go b/runner_builder.go index 848d09b..b369635 100644 --- a/runner_builder.go +++ b/runner_builder.go @@ -35,8 +35,5 @@ func SetSrcDir(b RunnerBuilder, dir string) RunnerBuilder { } func (b RunnerBuilder) New() Runner { - return Runner { - dirs: b.dirs, - adapters: b.adapters, - } + return NewRunner(b.adapters, b.dirs) } -- cgit v1.2.3 From 80c205a1d6d25f5a80853a76afcd1f527a894f56 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Sat, 4 Sep 2021 15:38:30 -0500 Subject: Refactor build/eval pipeline to use clearer IO model and adapter segmentation methods --- adapters.go | 4 +- adapters/gtest/adapter.go | 44 ++++++++++---------- adapters/gtest/config.go | 18 ++++----- cmd/planr/main.go | 2 +- cmd/planr/sub/build.go | 3 +- cmd/planr/sub/evaluate.go | 3 +- runner.go | 100 ++++++++++++++++++++++++++++------------------ runner_builder.go | 9 +++-- 8 files changed, 107 insertions(+), 76 deletions(-) (limited to 'runner.go') diff --git a/adapters.go b/adapters.go index f4e53ce..b7c4b27 100644 --- a/adapters.go +++ b/adapters.go @@ -14,10 +14,10 @@ type Adapter interface { Init(dirs DirConfig) // Called once to preform expensive code generation - Build(testCase []*TestCase) + Build(testCase []TestCase) // Called every time source changes - Evaluate(testCase []*TestCase) + Evaluate(testCase []TestCase) []TestCase } // A parser function takes a blob of TOML and decodes it into diff --git a/adapters/gtest/adapter.go b/adapters/gtest/adapter.go index 2cc5603..c727805 100644 --- a/adapters/gtest/adapter.go +++ b/adapters/gtest/adapter.go @@ -16,8 +16,8 @@ import ( const GTEST_CMAKE = "CMakeLists.txt" -func makeUnit(tc *planr.TestCase, dirs planr.DirConfig) cmakeUnit { - cfg := tc.AdapterConfig().(*GtestConfig) +func makeUnit(tc planr.TestCase, dirs planr.DirConfig) cmakeUnit { + cfg := tc.AdapterConfig().(*Config) testpath := path.Join(dirs.Tests(), *cfg.Testfile) srclist := cfg.srcList(dirs.Src()) @@ -41,7 +41,7 @@ func safeWd() string{ type ResultFromId map[string] Result -func (adapter *GtestAdapter) execTests(cnames []string) ResultFromId { +func (adapter *Adapter) execTests(cnames []string) ResultFromId { buildDir := safeWd() lut := make(ResultFromId, 0) @@ -89,7 +89,7 @@ func (adapter *GtestAdapter) execTests(cnames []string) ResultFromId { // An executable may contain more than one test // Gather all executables and deduplicate them -func exes(tcs []*planr.TestCase) []string { +func exes(tcs []planr.TestCase) []string { set := make(map[string] bool, 0) for _, tc := range tcs { @@ -112,12 +112,12 @@ func exes(tcs []*planr.TestCase) []string { return exes } -func id(tc *planr.TestCase) string { - cfg := tc.AdapterConfig().(*GtestConfig) +func id(tc planr.TestCase) string { + cfg := tc.AdapterConfig().(*Config) return tc.Cname + "." + *cfg.Suite + "." + *cfg.Name } -func compile(wg * sync.WaitGroup, tc *planr.TestCase) { +func compile(wg * sync.WaitGroup, tc * planr.TestCase) { defer wg.Done() cmd := exec.Command("make", tc.Cname) @@ -137,11 +137,11 @@ func compile(wg * sync.WaitGroup, tc *planr.TestCase) { tc.Result.DebugOutput = string(out) } -type GtestAdapter struct { +type Adapter struct { dirs planr.DirConfig } -func (a *GtestAdapter) Config() planr.AdapterConfig { +func (a *Adapter) Config() planr.AdapterConfig { return planr.AdapterConfig { Name: "gtest", ParseConfig: ParseConfig, @@ -149,18 +149,18 @@ func (a *GtestAdapter) Config() planr.AdapterConfig { } } -func (a *GtestAdapter) Init(dirs planr.DirConfig) { +func (a *Adapter) Init(dirs planr.DirConfig) { a.dirs = dirs } -func (adapter *GtestAdapter) Build(tcs []*planr.TestCase) { +func (adapter Adapter) Build(tcs []planr.TestCase) { buildDir := safeWd() cmakeFile := path.Join(buildDir, GTEST_CMAKE) units := make([]cmakeUnit, 0) for _, tc := range tcs { - cfg := tc.AdapterConfig().(*GtestConfig) + cfg := tc.AdapterConfig().(*Config) cfg.ensureSatisfied(tc.Path) units = append(units, makeUnit(tc, adapter.dirs)) @@ -172,9 +172,10 @@ func (adapter *GtestAdapter) Build(tcs []*planr.TestCase) { } // ./planr eval 0.93s user 0.16s system 100% cpu 1.089 total -func (adapter *GtestAdapter) Evaluate(tcs []*planr.TestCase) { +func (adapter *Adapter) Evaluate(tcs []planr.TestCase) [] planr.TestCase { var wg sync.WaitGroup - for _, tc := range tcs { + for i := range tcs { + tc := &tcs[i] wg.Add(1) go compile(&wg, tc) } @@ -183,14 +184,15 @@ func (adapter *GtestAdapter) Evaluate(tcs []*planr.TestCase) { files := exes(tcs) resultById := adapter.execTests(files) - for _, tc := range tcs { - result, ok := resultById[id(tc)] + for i := range tcs { + tc := &tcs[i] + result, ok := resultById[id(*tc)] // compilation failure if !ok { if tc.Result.Status == planr.PASSING { - cfg := tc.AdapterConfig().(*GtestConfig) + cfg := tc.AdapterConfig().(*Config) log.Printf( "Could not find testcase %s with name=\"%s\" and suite=\"%s\". Does such a test exist in the test source?", @@ -200,7 +202,7 @@ func (adapter *GtestAdapter) Evaluate(tcs []*planr.TestCase) { ) tc.Result.Status = planr.COMPILATION_FAILURE - tc.Result.DebugOutput += fmt.Sprintf("planr: Did not find testcase %s in any test executable\n", id(tc)) + tc.Result.DebugOutput += fmt.Sprintf("planr: Did not find testcase %s in any test executable\n", id(*tc)) } continue @@ -212,8 +214,10 @@ func (adapter *GtestAdapter) Evaluate(tcs []*planr.TestCase) { tc.Result.TestOutput = result.testOutput } + + return tcs } -func New() *GtestAdapter { - return new(GtestAdapter) +func NewAdapter() *Adapter { + return new(Adapter) } diff --git a/adapters/gtest/config.go b/adapters/gtest/config.go index ba7efb1..8057d94 100644 --- a/adapters/gtest/config.go +++ b/adapters/gtest/config.go @@ -12,7 +12,7 @@ const ( DEFAULT_TIMEOUT = 1000 ) -type GtestDefaults struct { +type Defaults struct { Name *string Suite *string Testfile *string @@ -20,8 +20,8 @@ type GtestDefaults struct { Timeout *uint } -func (child *GtestDefaults) Inherit(p interface{}) { - parent := p.(*GtestDefaults) +func (child *Defaults) Inherit(p interface{}) { + parent := p.(*Defaults) if(child.Name == nil) { child.Name = parent.Name } if(child.Suite == nil) { child.Suite = parent.Suite } @@ -31,11 +31,11 @@ func (child *GtestDefaults) Inherit(p interface{}) { } -type GtestConfig struct { - GtestDefaults +type Config struct { + Defaults } -func (g GtestConfig) ensureSatisfied(path string) { +func (g Config) ensureSatisfied(path string) { if g.Name == nil { log.Fatalf("\"name\" is not defined for unit: %s\n", path) } else if g.Suite == nil { @@ -50,7 +50,7 @@ func (g GtestConfig) ensureSatisfied(path string) { } } -func (cfg GtestConfig) srcList(srcDir string) string { +func (cfg Config) srcList(srcDir string) string { var srcList string if cfg.Srcs != nil { @@ -66,7 +66,7 @@ func (cfg GtestConfig) srcList(srcDir string) string { } func ParseConfig(prim toml.Primitive) (planr.InheritableConfig, error) { - config := GtestConfig{} + config := Config{} if err := toml.PrimitiveDecode(prim, &config); err != nil { return nil, err @@ -76,7 +76,7 @@ func ParseConfig(prim toml.Primitive) (planr.InheritableConfig, error) { } func ParseDefaultConfig(prim toml.Primitive) (planr.InheritableConfig, error) { - config := GtestDefaults{} + config := Defaults{} if err := toml.PrimitiveDecode(prim, &config); err != nil { return nil, err diff --git a/cmd/planr/main.go b/cmd/planr/main.go index 413787b..527d277 100644 --- a/cmd/planr/main.go +++ b/cmd/planr/main.go @@ -29,7 +29,7 @@ func dieUsage() { func getConfiguredRunner() planr.Runner { r := planr.ConfigureRunner() - r = planr.RegisterAdapter(r, gtest.New()) + r = planr.RegisterAdapter(r, gtest.NewAdapter()) if wd, err := os.Getwd(); err == nil { r = planr.SetConfigDirFromTree(r, wd) diff --git a/cmd/planr/sub/build.go b/cmd/planr/sub/build.go index bddeb4b..caf7bde 100644 --- a/cmd/planr/sub/build.go +++ b/cmd/planr/sub/build.go @@ -6,5 +6,6 @@ import ( func Build(runner planr.Runner, params []string) { - runner.Build() + tcs := runner.CollectCases() + runner.Build(tcs) } diff --git a/cmd/planr/sub/evaluate.go b/cmd/planr/sub/evaluate.go index f8ec509..32e19a6 100644 --- a/cmd/planr/sub/evaluate.go +++ b/cmd/planr/sub/evaluate.go @@ -5,7 +5,8 @@ import ( ) func Evaluate(runner planr.Runner, params []string) { - tcs := runner.Evaluate() + tcs := runner.CollectCases() + tcs = runner.Evaluate(tcs) earned := 0.0 total := 0.0 diff --git a/runner.go b/runner.go index d3d4b08..f613d44 100644 --- a/runner.go +++ b/runner.go @@ -7,8 +7,8 @@ import ( ) type Runner struct { - adapters []Adapter - dirs DirConfig + adapters map[string] Adapter + dirs DirConfig } func (r Runner) adapterCfgs() []AdapterConfig { @@ -21,20 +21,7 @@ func (r Runner) adapterCfgs() []AdapterConfig { return cgs } -type tcTab map[string] []*TestCase - -func (r Runner) buildTcLUT(tcs []TestCase) tcTab { - m := make(tcTab, 0) - - for i := range tcs { - tc := &tcs[i] - nm := *tc.Config.Adapter - m[nm] = append(m[nm], tc) - } - - return m -} - +// TODO: Move into configuration parsing func (r Runner) checkConfig(tcs []TestCase) { for _, tc := range tcs { tc.Config.ensureSatisfied(tc.Path) @@ -54,55 +41,90 @@ func (r Runner) setupEnv(adapter Adapter) { safeCd(wd) } -func (r Runner) build(tcs []TestCase) { +type adapterTestSet struct { + adapter Adapter + tcs []TestCase +} + +func (r Runner) groupByAdapter(tcs []TestCase) []adapterTestSet { r.checkConfig(tcs) + + pairs := make(map[string] adapterTestSet, 0) - tcTab := r.buildTcLUT(tcs) + for _, tc := range tcs { + // TODO: Make non-pointer + adptNm := *tc.Config.Adapter + + // See if adapter if contained in map + adapter, contained := r.adapters[adptNm] + + if !contained { + log.Fatalf("Cannot find adapter \"%s\" for testcase \"%s\"", adptNm, tc.Cname) + } - for _, adapter := range r.adapters { - nm := adapter.Config().Name - r.setupEnv(adapter) + pair, exists := pairs[adptNm] - adapter.Build(tcTab[nm]) + if !exists { + pair.adapter = adapter + } + + pair.tcs = append(pair.tcs, tc) + + pairs[adptNm] = pair } - safeCd(r.dirs.Config()) + + // Convert to slice + set := make([]adapterTestSet, 0) + + for _, pair := range pairs { + set = append(set, pair) + } + + return set } -func (r Runner) units() []TestCase { +func (r Runner) CollectCases() []TestCase { return collectUnits(r.dirs.Rubric(), r.adapterCfgs()) } -func (r Runner) Build() { - units := r.units() +func (r Runner) Build(tcs []TestCase) { if !directoryExists(r.dirs.Build()) { r.dirs.MkBuild() } - r.build(units) -} + testSets := r.groupByAdapter(tcs) -func (r Runner) evaluate(tcs []TestCase) { - tcTab := r.buildTcLUT(tcs) - - for _, adapter := range r.adapters { - nm := adapter.Config().Name + for _, pair := range testSets { + adapter := pair.adapter + cases := pair.tcs r.setupEnv(adapter) - adapter.Evaluate(tcTab[nm]) + + adapter.Build(cases) } safeCd(r.dirs.Config()) } -func (r Runner) Evaluate() []TestCase { +func (r Runner) Evaluate(tcs []TestCase) []TestCase { + testSets := r.groupByAdapter(tcs) + results := make([]TestCase, 0) - units := r.units() + for _, pair := range testSets { + adapter := pair.adapter + cases := pair.tcs + + r.setupEnv(adapter) + resultSet := adapter.Evaluate(cases) - r.evaluate(units) + results = append(results, resultSet...) + } + + safeCd(r.dirs.Config()) - return units + return results } func (r Runner) Clean() { @@ -121,7 +143,7 @@ func (r Runner) SrcDir() string { return r.dirs.Src() } -func NewRunner(adapters []Adapter, dirs DirConfig) Runner { +func NewRunner(adapters map[string]Adapter, dirs DirConfig) Runner { r := Runner{adapters, dirs} for _, adapter := range r.adapters { diff --git a/runner_builder.go b/runner_builder.go index b369635..b3c07d9 100644 --- a/runner_builder.go +++ b/runner_builder.go @@ -1,16 +1,19 @@ package planr type RunnerBuilder struct { - adapters [] Adapter + adapters map[string] Adapter dirs DirConfig } func ConfigureRunner() RunnerBuilder { - return RunnerBuilder {} + builder := RunnerBuilder{} + builder.adapters = make(map[string] Adapter, 0) + return builder } func RegisterAdapter(b RunnerBuilder, a Adapter) RunnerBuilder { - b.adapters = append(b.adapters, a) + nm := a.Config().Name + b.adapters[nm] = a return b } -- cgit v1.2.3 From d4f9c927e0efad402e1dbfded1a850fb9e0030e6 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Sun, 5 Sep 2021 00:37:23 -0500 Subject: Refactor gtest adapter to fit new pipeline --- adapters.go | 2 +- adapters/gtest/adapter.go | 187 +++++-------------------------------------- adapters/gtest/config.go | 58 +++++++++----- adapters/gtest/executable.go | 186 ++++++++++++++++++++++++++++++++++++++++++ adapters/gtest/templating.go | 14 ++-- cmd/planr/sub/cli.go | 46 +++++------ cmd/planr/sub/evaluate.go | 10 +-- fs.go | 4 +- runner.go | 4 +- testcase.go | 7 +- 10 files changed, 289 insertions(+), 229 deletions(-) create mode 100644 adapters/gtest/executable.go (limited to 'runner.go') diff --git a/adapters.go b/adapters.go index b7c4b27..f6c48cb 100644 --- a/adapters.go +++ b/adapters.go @@ -17,7 +17,7 @@ type Adapter interface { Build(testCase []TestCase) // Called every time source changes - Evaluate(testCase []TestCase) []TestCase + Evaluate(testCase []TestCase) []TestResult } // A parser function takes a blob of TOML and decodes it into diff --git a/adapters/gtest/adapter.go b/adapters/gtest/adapter.go index c727805..dfb035c 100644 --- a/adapters/gtest/adapter.go +++ b/adapters/gtest/adapter.go @@ -1,34 +1,14 @@ package gtest import ( - "context" - "errors" - "fmt" - "io/ioutil" "log" "os" - "os/exec" "path" - "sync" - "time" "golang.furkistan.com/planr" ) const GTEST_CMAKE = "CMakeLists.txt" -func makeUnit(tc planr.TestCase, dirs planr.DirConfig) cmakeUnit { - cfg := tc.AdapterConfig().(*Config) - - testpath := path.Join(dirs.Tests(), *cfg.Testfile) - srclist := cfg.srcList(dirs.Src()) - - return cmakeUnit { - tc.Cname, - testpath, - srclist, - }; -} - func safeWd() string{ wd, err := os.Getwd() @@ -39,104 +19,6 @@ func safeWd() string{ return wd } -type ResultFromId map[string] Result - -func (adapter *Adapter) execTests(cnames []string) ResultFromId { - buildDir := safeWd() - - lut := make(ResultFromId, 0) - for _, exe := range cnames { - - exePath := path.Join(buildDir, exe) - - 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, exePath, "--gtest_output=json:" + f.Name()) - - defer cancel() - defer os.Remove(f.Name()) - - out, err := cmd.CombinedOutput() - if err != nil { - var exiterr *exec.ExitError - - if !errors.As(err, &exiterr) { - log.Printf("%v\n", err) - os.Exit(exiterr.ExitCode()) - } - } - - 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 - } - } - - return lut -} - -// An executable may contain more than one test -// Gather all executables and deduplicate them -func exes(tcs []planr.TestCase) []string { - set := make(map[string] bool, 0) - - for _, tc := range tcs { - // Tests which have encountered a failure - // may not have an executable - if tc.Result.Status != planr.PASSING { - continue - } - - if(!set[tc.Cname]) { - set[tc.Cname] = true - } - } - - exes := make([]string, 0) - for k := range set { - exes = append(exes, k) - } - - return exes -} - -func id(tc planr.TestCase) string { - cfg := tc.AdapterConfig().(*Config) - return tc.Cname + "." + *cfg.Suite + "." + *cfg.Name -} - -func compile(wg * sync.WaitGroup, tc * planr.TestCase) { - defer wg.Done() - - 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) - } - - tc.Result.Status = planr.COMPILATION_FAILURE - } - - tc.Result.DebugOutput = string(out) -} - type Adapter struct { dirs planr.DirConfig } @@ -155,67 +37,42 @@ func (a *Adapter) Init(dirs planr.DirConfig) { func (adapter Adapter) Build(tcs []planr.TestCase) { buildDir := safeWd() - cmakeFile := path.Join(buildDir, GTEST_CMAKE) - units := make([]cmakeUnit, 0) - for _, tc := range tcs { - - cfg := tc.AdapterConfig().(*Config) - cfg.ensureSatisfied(tc.Path) + finalizeConfigs(tcs) - units = append(units, makeUnit(tc, adapter.dirs)) - } + exes := createExecutables(tcs) - genCmake(cmakeFile, units) + cmakeFile := path.Join(buildDir, GTEST_CMAKE) + cmakeUnits := cmakeUnits(exes, adapter.dirs) + + generateCmakeScript(cmakeFile, cmakeUnits) planr.RunCmd("cmake", "-S", ".", "-B", ".") } -// ./planr eval 0.93s user 0.16s system 100% cpu 1.089 total -func (adapter *Adapter) Evaluate(tcs []planr.TestCase) [] planr.TestCase { - var wg sync.WaitGroup - for i := range tcs { - tc := &tcs[i] - wg.Add(1) - go compile(&wg, tc) - } - wg.Wait() - - files := exes(tcs) - resultById := adapter.execTests(files) - - for i := range tcs { - tc := &tcs[i] - result, ok := resultById[id(*tc)] - - // compilation failure - if !ok { - - if tc.Result.Status == planr.PASSING { - cfg := tc.AdapterConfig().(*Config) +func (adapter *Adapter) Evaluate(tcs []planr.TestCase) [] planr.TestResult { + buildDir := safeWd() - 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, - ) + finalizeConfigs(tcs) + + results := make([]planr.TestResult, 0) + + exes := createExecutables(tcs) - tc.Result.Status = planr.COMPILATION_FAILURE - tc.Result.DebugOutput += fmt.Sprintf("planr: Did not find testcase %s in any test executable\n", id(*tc)) - } + for i := range exes { + succeed, buildFailures := exes[i].compile(buildDir) + if ! succeed { + results = append(results, buildFailures...) continue } - - if !result.pass { - tc.Result.Status = planr.RUNTIME_FAILURE - } - tc.Result.TestOutput = result.testOutput - } + runtimeResults := exes[i].execute(buildDir) - return tcs + results = append(results, runtimeResults...) + } + + return results } func NewAdapter() *Adapter { diff --git a/adapters/gtest/config.go b/adapters/gtest/config.go index 8057d94..4cd2030 100644 --- a/adapters/gtest/config.go +++ b/adapters/gtest/config.go @@ -2,10 +2,10 @@ package gtest import ( "log" - "golang.furkistan.com/planr" "strings" - "github.com/BurntSushi/toml" "path" + "golang.furkistan.com/planr" + "github.com/BurntSushi/toml" ) const ( @@ -16,7 +16,7 @@ type Defaults struct { Name *string Suite *string Testfile *string - Srcs *[]string + Srcs []string Timeout *uint } @@ -26,7 +26,7 @@ func (child *Defaults) Inherit(p interface{}) { 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.Srcs == nil) { child.Srcs = parent.Srcs } + if(len(child.Srcs) == 0) { child.Srcs = parent.Srcs } if(child.Timeout == nil) { child.Timeout = parent.Timeout } } @@ -35,34 +35,52 @@ type Config struct { Defaults } -func (g Config) ensureSatisfied(path string) { - if g.Name == nil { +func (c * Config) finalize(path string) { + if c.Name == nil { log.Fatalf("\"name\" is not defined for unit: %s\n", path) - } else if g.Suite == nil { + } else if c.Suite == nil { log.Fatalf("\"suite\" is not defined for unit: %s\n", path) - } else if g.Testfile == nil { + } else if c.Testfile == nil { log.Fatalf("\"testfile\" is not defined for unit: %s\n", path) } - if g.Timeout == nil { - g.Timeout = new(uint) - *g.Timeout = DEFAULT_TIMEOUT; + if c.Timeout == nil { + c.Timeout = new(uint) + *c.Timeout = DEFAULT_TIMEOUT; } } -func (cfg Config) srcList(srcDir string) string { - var srcList string +func srcList(srcdir string, srcs []string) string { + builder := strings.Builder {} - if cfg.Srcs != nil { - srcs := make([]string, len(*cfg.Srcs)) - for i, src := range *cfg.Srcs { - srcs[i] = "\"" + path.Join(srcDir, src) + "\"" - } + for _, src := range srcs { + builder.WriteString("\"") + builder.WriteString(path.Join(srcdir, src)) + builder.WriteString("\"\n ") + } + + return builder.String() +} + +func cmakeUnits(e []executable, dirs planr.DirConfig) []cmakeUnit { + + units := make([]cmakeUnit, len(e)) + for i, exe := range e { + testpath := path.Join(dirs.Tests(), exe.testpath) + srclist := srcList(dirs.Src(), exe.srcs) - srcList = strings.Join(srcs, "\n ") + units[i] = cmakeUnit { exe.exeNm, testpath, srclist } } - return srcList + return units +} + +func finalizeConfigs(tcs []planr.TestCase) { + for i := range tcs { + cfg := tcs[i].AdapterConfig().(*Config) + + cfg.finalize(tcs[i].Path) + } } func ParseConfig(prim toml.Primitive) (planr.InheritableConfig, error) { diff --git a/adapters/gtest/executable.go b/adapters/gtest/executable.go new file mode 100644 index 0000000..b8f79ec --- /dev/null +++ b/adapters/gtest/executable.go @@ -0,0 +1,186 @@ +package gtest + +import ( + "os" + "errors" + "time" + "io/ioutil" + "log" + "os/exec" + "path" + "reflect" + "sort" + "context" + + "golang.furkistan.com/planr" +) + +type executable struct { + exeNm string + testpath string + srcs []string + tcs []planr.TestCase +} + +func createExecutables(tcs []planr.TestCase) []executable { + exes := make(map[string] executable, 0) + + for _, tc := range tcs { + cfg := tc.AdapterConfig().(*Config) + file := *cfg.Testfile + exe, contained := exes[file] + + // For set comparison + sort.Strings(cfg.Srcs) + + if !contained { + exeTcs := make([]planr.TestCase, 1) + exeTcs[0] = tc + + + exe := executable { + planr.Cname("", file), + file, + cfg.Srcs, + exeTcs, + } + + exes[file] = exe + + continue + } + + // We could create two different executables for each source list + // But, that would be confusing so we're going to disallow it + if !reflect.DeepEqual(exe.srcs, cfg.Srcs) { + log.Fatalf( + "Two test case definitions %s and %s have different lists of sources", + exe.testpath, *cfg.Testfile, + ) + } + + exe.tcs = append(exe.tcs, tc) + + exes[file] = exe + } + + exesList := make([]executable, 0) + + for _, exe := range exes { + exesList = append(exesList, exe) + } + + return exesList +} + +func (exe executable) compile(builddir string) (succeeded bool, buildFailures []planr.TestResult) { + cmd := exec.Command("make", "-C", builddir, exe.exeNm) + out, err := cmd.CombinedOutput() + buildFailures = make([]planr.TestResult, 0) + + outputLog := string(out) + + if err != nil{ + var exiterr *exec.ExitError + if errors.As(err, &exiterr) && exiterr.ExitCode() == 0 { + log.Fatalf("Unrecoverable build failure: %v", err) + } + + for i := range exe.tcs { + res := planr.TestResult {} + res.Tc = exe.tcs[i] + res.DebugOutput = outputLog + res.Status = planr.COMPILATION_FAILURE + + buildFailures = append(buildFailures, res) + } + + succeeded = false + + return + } + + succeeded = true + return +} + +const TMPFILENAME = "gtest_adapter_*.json" + +func runGtest(exe string, tc planr.TestCase, builddir string) planr.TestResult { + result := planr.TestResult {} + result.Tc = tc + + exePath := path.Join(builddir, exe) + cfg := tc.AdapterConfig().(*Config) + + f, err := ioutil.TempFile(builddir, TMPFILENAME) + + if err != nil { + log.Fatal(err) + } + + timeout := time.Duration(*cfg.Timeout) * time.Millisecond + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + + jsonFlag := "--gtest_output=json:" + f.Name() + testFlag := "--gtest_filter=" + *cfg.Suite + "." + *cfg.Name + + cmd := exec.CommandContext(ctx, exePath, jsonFlag, testFlag) + + defer cancel() + defer os.Remove(f.Name()) + + out, err := cmd.CombinedOutput() + if err != nil { + var exiterr *exec.ExitError + + if !errors.As(err, &exiterr) { + log.Printf("%v\n", err) + os.Exit(exiterr.ExitCode()) + } + } + + results, err := decodeResults(f) + + if err != nil { + log.Fatalf("Could not collect results from %s: %v", exe, err) + } + + if len(results) < 1 { + log.Fatalf( + "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, + ) + } + + // TODO: Cleanup -- ZERO TESTS? + if len(results) > 1 { + log.Fatalf("Unexpected number of results") + } + + decodeResult := results[0] + + result.TestOutput = string(out) + + if decodeResult.pass { + result.Status = planr.PASSING + } else { + result.Status = planr.RUNTIME_FAILURE + } + + return result +} + +func (exe executable) execute(builddir string) []planr.TestResult { + results := make([]planr.TestResult, len(exe.tcs)) + + for i := range exe.tcs { + results[i] = runGtest(exe.exeNm, exe.tcs[i], builddir) + } + + return results +} + diff --git a/adapters/gtest/templating.go b/adapters/gtest/templating.go index a9a3b07..57532fa 100644 --- a/adapters/gtest/templating.go +++ b/adapters/gtest/templating.go @@ -8,12 +8,12 @@ import ( ) type cmakeUnit struct { - Cname string + ExeNm string File string Srcs string }; -func genCmake(out string, units []cmakeUnit) { +func generateCmakeScript(out string, units []cmakeUnit) { file, err := os.OpenFile(out, os.O_RDWR | os.O_CREATE, 0644) defer func () { err := file.Close() @@ -33,27 +33,29 @@ func genCmake(out string, units []cmakeUnit) { for _, unit := range units { if err := tmpl.Execute(file, unit); err != nil { - log.Fatalf("Failed to generate unit %s: %v", unit.Cname, err); + log.Fatalf("Failed to generate unit %s: %v", unit.ExeNm, err); } } } +// TODO: Add comments func unitTemplate() *template.Template { tmpl, err := template.New("gtest_unit").Parse(` + add_executable( - "{{.Cname}}" + "{{.ExeNm}}" "{{.File}}" {{.Srcs}} ) target_link_libraries( - "{{.Cname}}" + "{{.ExeNm}}" gtest_main ) gtest_discover_tests( - "{{.Cname}}" + "{{.ExeNm}}" ) `) diff --git a/cmd/planr/sub/cli.go b/cmd/planr/sub/cli.go index 64318c2..8dc837e 100644 --- a/cmd/planr/sub/cli.go +++ b/cmd/planr/sub/cli.go @@ -14,25 +14,23 @@ var ( col_label = color.New(color.FgCyan) ); -func tcTitle(tc planr.TestCase) string { - title := tc.Cname +func tcTitle(tr planr.TestResult) string { + title := tr.Tc.Cname - if tc.Config.Title != nil { - title = *tc.Config.Title + if tr.Tc.Config.Title != nil { + title = *tr.Tc.Config.Title } return title } -func tcStatus(tc planr.TestCase) string { +func tcStatus(tc planr.TestResult) string { status := "SILENT" - if tc.Result != nil { - if tc.Result.Status == planr.PASSING { - status = "PASS" - } else { - status = "FAIL" - } + if tc.Status == planr.PASSING { + status = "PASS" + } else { + status = "FAIL" } return status @@ -59,9 +57,9 @@ func pprintFenced(title, value string) { fmt.Println(fence) } -func tcStatusLine(tc planr.TestCase) { - title := tcTitle(tc) - status := tcStatus(tc) +func tcStatusLine(tr planr.TestResult) { + title := tcTitle(tr) + status := tcStatus(tr) if status == "PASS" { col_pass.Printf("[%s] ", status); @@ -72,8 +70,10 @@ func tcStatusLine(tc planr.TestCase) { col_title.Println(title); } -func tcPprint(tc planr.TestCase) { - tcStatusLine(tc) +func tcPprint(tr planr.TestResult) { + tcStatusLine(tr) + + tc := tr.Tc pprintLabeled("id", tc.Cname) @@ -86,22 +86,20 @@ func tcPprint(tc planr.TestCase) { pprintLabeled("description", *tc.Config.Description) } - res := tc.Result - - if res.Status == planr.COMPILATION_FAILURE { + if tr.Status == planr.COMPILATION_FAILURE { - if res.DebugOutput != "" { + if tr.DebugOutput != "" { fmt.Println() - pprintFenced("compilation output", tc.Result.DebugOutput); + pprintFenced("compilation output", tr.DebugOutput); } else { fmt.Println("WARN: No debug output provided") } - } else if res.Status == planr.RUNTIME_FAILURE { + } else if tr.Status == planr.RUNTIME_FAILURE { - if tc.Result.TestOutput != "" { + if tr.TestOutput != "" { fmt.Println() - pprintFenced("test output", tc.Result.TestOutput); + pprintFenced("test output", tr.TestOutput); } } diff --git a/cmd/planr/sub/evaluate.go b/cmd/planr/sub/evaluate.go index 32e19a6..fe864ad 100644 --- a/cmd/planr/sub/evaluate.go +++ b/cmd/planr/sub/evaluate.go @@ -6,26 +6,26 @@ import ( func Evaluate(runner planr.Runner, params []string) { tcs := runner.CollectCases() - tcs = runner.Evaluate(tcs) + trs := runner.Evaluate(tcs) earned := 0.0 total := 0.0 passed := 0 - for _, tc := range tcs { - cfg := tc.Config + for _, tr := range trs { + cfg := tr.Tc.Config if cfg.Points != nil { points := float64(*cfg.Points) total += points - if tc.Result.Status == planr.PASSING { + if tr.Status == planr.PASSING { earned += points passed++ } } - tcPprint(tc) + tcPprint(tr) } printResults( diff --git a/fs.go b/fs.go index 575517c..a42ff2c 100644 --- a/fs.go +++ b/fs.go @@ -50,7 +50,7 @@ func basename(path string) string { return path[0:len(path) - len(ext)] } -func cname(root string, path string) string { +func Cname(root string, path string) string { rel, err := filepath.Rel(root, path) if err != nil { @@ -76,7 +76,7 @@ func collectUnits(root string, cfgs []AdapterConfig) []TestCase { collectFromDir(root, nil, cfgs, &tcs) for i := range tcs { - tcs[i].Cname = cname(root, tcs[i].Path) + tcs[i].Cname = Cname(root, tcs[i].Path) } return tcs diff --git a/runner.go b/runner.go index f613d44..2d66dc6 100644 --- a/runner.go +++ b/runner.go @@ -108,9 +108,9 @@ func (r Runner) Build(tcs []TestCase) { safeCd(r.dirs.Config()) } -func (r Runner) Evaluate(tcs []TestCase) []TestCase { +func (r Runner) Evaluate(tcs []TestCase) []TestResult { testSets := r.groupByAdapter(tcs) - results := make([]TestCase, 0) + results := make([]TestResult, 0) for _, pair := range testSets { adapter := pair.adapter diff --git a/testcase.go b/testcase.go index 7e0bf17..19f1e58 100644 --- a/testcase.go +++ b/testcase.go @@ -7,7 +7,8 @@ import ( type TestStatus uint const ( - PASSING TestStatus = iota + NOT_RUN TestStatus = iota + PASSING COMPILATION_FAILURE RUNTIME_FAILURE ) @@ -17,6 +18,7 @@ type TestResult struct { Status TestStatus DebugOutput string TestOutput string + Tc TestCase } // Program-wide testcase config @@ -43,9 +45,6 @@ type TestCase struct { Cname string Config TestCaseConfig - - Result *TestResult - } func (tc TestCase) AdapterConfig() InheritableConfig { -- cgit v1.2.3 From 4d99cb9eb30ffe235196a75b566e1b8b9a5e6d0d Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Sun, 5 Sep 2021 04:40:39 -0500 Subject: Concurrent Eval Loop --- runner.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'runner.go') diff --git a/runner.go b/runner.go index 2d66dc6..9c1e385 100644 --- a/runner.go +++ b/runner.go @@ -111,15 +111,22 @@ func (r Runner) Build(tcs []TestCase) { func (r Runner) Evaluate(tcs []TestCase) []TestResult { testSets := r.groupByAdapter(tcs) results := make([]TestResult, 0) - + + c := make(chan []TestResult) for _, pair := range testSets { - adapter := pair.adapter - cases := pair.tcs + go func (pair adapterTestSet) { + adapter := pair.adapter + cases := pair.tcs - r.setupEnv(adapter) - resultSet := adapter.Evaluate(cases) + r.setupEnv(adapter) + resultSet := adapter.Evaluate(cases) + + c <- resultSet + }(pair) + } - results = append(results, resultSet...) + for range testSets { + results = append(results, (<-c)...) } safeCd(r.dirs.Config()) -- cgit v1.2.3 From 243c7db4b094fdb25c54d0017712a655da138eef Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Sun, 5 Sep 2021 13:38:27 -0500 Subject: Preserve original ordering --- fs.go | 1 + runner.go | 3 +++ testcase.go | 9 +++++++++ 3 files changed, 13 insertions(+) (limited to 'runner.go') diff --git a/fs.go b/fs.go index 04a3522..b76f182 100644 --- a/fs.go +++ b/fs.go @@ -77,6 +77,7 @@ func collectUnits(root string, cfgs []AdapterConfig) []TestCase { for i := range tcs { tcs[i].Cname = Cname(root, tcs[i].Path) + tcs[i].readIdx = i } return tcs diff --git a/runner.go b/runner.go index 9c1e385..15f08ae 100644 --- a/runner.go +++ b/runner.go @@ -4,6 +4,7 @@ import ( "log" "os" "path" + "sort" ) type Runner struct { @@ -128,6 +129,8 @@ func (r Runner) Evaluate(tcs []TestCase) []TestResult { for range testSets { results = append(results, (<-c)...) } + + sort.Sort(ByReadIdx(results)) safeCd(r.dirs.Config()) diff --git a/testcase.go b/testcase.go index 19f1e58..d1db292 100644 --- a/testcase.go +++ b/testcase.go @@ -45,8 +45,17 @@ type TestCase struct { Cname string Config TestCaseConfig + + // Reorder according to original read order after concurrent operation + readIdx int } func (tc TestCase) AdapterConfig() InheritableConfig { return tc.Config.adapters_[*tc.Config.Adapter] } + +type ByReadIdx []TestResult + +func (a ByReadIdx) Len() int { return len(a) } +func (a ByReadIdx) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByReadIdx) Less(i, j int) bool { return a[i].Tc.readIdx < a[j].Tc.readIdx } -- cgit v1.2.3