diff options
-rw-r--r-- | adapters.go | 24 | ||||
-rw-r--r-- | adapters/gtest/adapter.go | 77 | ||||
-rw-r--r-- | adapters/gtest/config.go | 21 | ||||
-rw-r--r-- | adapters/gtest/templating.go | 2 | ||||
-rw-r--r-- | cmd/planr/sub/build.go | 12 | ||||
-rw-r--r-- | cmd/planr/sub/evaluate.go | 4 | ||||
-rw-r--r-- | config.go | 33 | ||||
-rw-r--r-- | fs.go | 115 | ||||
-rw-r--r-- | runner.go | 48 | ||||
-rw-r--r-- | stddirs.go | 53 | ||||
-rw-r--r-- | testcase.go | 19 |
11 files changed, 170 insertions, 238 deletions
diff --git a/adapters.go b/adapters.go index 8419c8b..f4e53ce 100644 --- a/adapters.go +++ b/adapters.go @@ -1,16 +1,38 @@ package planr +import ( + "github.com/BurntSushi/toml" +) + // Test adapters must implement all life cycle hooks // This allows common config, code generation, etc // Test cases matching adapter configurations will be // fed into the adapter interface type Adapter interface { - // Config() AdapterConfig + Init(dirs DirConfig) + // Called once to preform expensive code generation Build(testCase []*TestCase) // Called every time source changes Evaluate(testCase []*TestCase) } + +// A parser function takes a blob of TOML and decodes it into +// configuration relevant to an adapter +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 +// TOML will be passed to the ParseConfig method or ParseDefaultConfig +// for parsing. The ParseConfig file parses options in test case files. +// The ParseDefaultConfig is parsed by `defaults.toml` files and can +// be used to establish default configuration that will be inherited +// by all units in a common directory (collection) +type AdapterConfig struct { + Name string + ParseConfig TomlParser + ParseDefaultConfig TomlParser +} diff --git a/adapters/gtest/adapter.go b/adapters/gtest/adapter.go index f4fde27..9331cf7 100644 --- a/adapters/gtest/adapter.go +++ b/adapters/gtest/adapter.go @@ -11,55 +11,38 @@ import ( "path" "sync" "time" - "golang.flu0r1ne.net/planr" ) const GTEST_CMAKE = "CMakeLists.txt" -func mkUnit(tc *planr.TestCase) cmakeUnit { +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()) + return cmakeUnit { tc.Cname, - cfg.joinTests(*cfg.Testfile), - cfg.srcList(), + testpath, + srclist, }; } +func safeWd() string{ + wd, err := os.Getwd() -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 { - - cfg := tc.AdapterConfig().(*GtestConfig) - cfg.ensureSatisfied(tc.Path) - - units = append(units, mkUnit(tc)) + if err != nil { + log.Fatalf("Could not get GtestBuildDir %s %v\n", wd, err) } - genCmake(cmakeFile, units) - - planr.RunCmd("cmake", "-S", ".", "-B", ".") + return wd } type ResultFromId map[string] Result func (adapter *GtestAdapter) execTests(cnames []string) ResultFromId { - buildDir := adapter.Config().Dir() + buildDir := safeWd() lut := make(ResultFromId, 0) for _, exe := range cnames { @@ -154,6 +137,40 @@ func compile(wg * sync.WaitGroup, tc *planr.TestCase) { tc.Result.DebugOutput = string(out) } +type GtestAdapter struct { + dirs planr.DirConfig +} + +func (a *GtestAdapter) Config() planr.AdapterConfig { + return planr.AdapterConfig { + Name: "gtest", + ParseConfig: ParseConfig, + ParseDefaultConfig: ParseDefaultConfig, + } +} + +func (a *GtestAdapter) Init(dirs planr.DirConfig) { + a.dirs = dirs +} + +func (adapter *GtestAdapter) 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.ensureSatisfied(tc.Path) + + units = append(units, makeUnit(tc, adapter.dirs)) + } + + genCmake(cmakeFile, units) + + planr.RunCmd("cmake", "-S", ".", "-B", ".") +} + // ./planr eval 0.93s user 0.16s system 100% cpu 1.089 total func (adapter *GtestAdapter) Evaluate(tcs []*planr.TestCase) { var wg sync.WaitGroup @@ -169,7 +186,7 @@ func (adapter *GtestAdapter) Evaluate(tcs []*planr.TestCase) { for _, tc := range tcs { result, ok := resultById[id(tc)] - // compilation failure + // compilation failure if !ok { fmt.Printf("CAN'T FIND %s: status %d\n", tc.Cname, tc.Result.Status) diff --git a/adapters/gtest/config.go b/adapters/gtest/config.go index cff45fa..4f8735f 100644 --- a/adapters/gtest/config.go +++ b/adapters/gtest/config.go @@ -5,6 +5,7 @@ import ( "golang.flu0r1ne.net/planr" "strings" "github.com/BurntSushi/toml" + "path" ) type GtestDefaults struct { @@ -42,29 +43,13 @@ func (g GtestConfig) ensureSatisfied(path string) { } } -func (cfg GtestConfig) joinTests(path_ string) string { - if cfg.Test_root == nil { - return planr.JoinConfigDir("tests", path_) - } - - return planr.JoinConfigDir(*cfg.Test_root, path_) -} - -func (cfg GtestConfig) joinSrcs(path_ string) string { - if cfg.Srcs_root == nil { - return planr.JoinConfigDir("../src", path_) - } - - return planr.JoinConfigDir(*cfg.Srcs_root, path_) -} - -func (cfg GtestConfig) srcList() string { +func (cfg GtestConfig) srcList(srcDir string) string { var srcList string if cfg.Srcs != nil { srcs := make([]string, len(*cfg.Srcs)) for i, src := range *cfg.Srcs { - srcs[i] = "\"" + cfg.joinSrcs(src) + "\"" + srcs[i] = "\"" + path.Join(srcDir, src) + "\"" } srcList = strings.Join(srcs, "\n ") diff --git a/adapters/gtest/templating.go b/adapters/gtest/templating.go index c49f170..a9a3b07 100644 --- a/adapters/gtest/templating.go +++ b/adapters/gtest/templating.go @@ -80,5 +80,3 @@ include(GoogleTest) FetchContent_MakeAvailable(googletest) `)) } - - diff --git a/cmd/planr/sub/build.go b/cmd/planr/sub/build.go index 58c3a38..a8b19ec 100644 --- a/cmd/planr/sub/build.go +++ b/cmd/planr/sub/build.go @@ -3,17 +3,21 @@ package sub import ( "golang.flu0r1ne.net/planr" "golang.flu0r1ne.net/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) { - - rd := planr.RubricDir() - - Runner().Build(rd) + Runner().Build() } diff --git a/cmd/planr/sub/evaluate.go b/cmd/planr/sub/evaluate.go index 8ce4d81..d183b86 100644 --- a/cmd/planr/sub/evaluate.go +++ b/cmd/planr/sub/evaluate.go @@ -5,9 +5,7 @@ import ( ) func Evaluate(params []string) { - rd := planr.RubricDir() - - tcs := Runner().Evaluate(rd) + tcs := Runner().Evaluate() earned := 0.0 total := 0.0 @@ -1,11 +1,8 @@ package planr import ( - "log" "github.com/BurntSushi/toml" ) - - /* TODO: Every property defined within the defaults currently has to implement the "inherit" method to conditionally inherit a @@ -38,22 +35,6 @@ type InheritableConfig interface { Inherit(parent interface{}) } -// A parser function takes a blob of TOML and decodes it into -// configuration relevant to an adapter -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 -// TOML will be passed to the ParseConfig method or ParseDefaultConfig -// for parsing. The ParseConfig file parses options in test case files. -// The ParseDefaultConfig is parsed by `defaults.toml` files and can -// be used to establish default configuration that will be inherited -// by all units in a common directory (collection) -type AdapterConfig struct { - Name string - ParseConfig TomlParser - ParseDefaultConfig TomlParser -} // Program-wide configuration which is recognized // in defaults.toml @@ -82,18 +63,6 @@ type Defaults struct { configs_ *[]AdapterConfig } -// Program-wide testcase config -type TestCaseConfig struct { - Defaults - Title *string - Description *string -} - -func (c TestCaseConfig) ensureSatisfied(name string) { - if (c.Adapter == nil) { - log.Fatalf("Adapter must be provided for testcase %s", name) - } -} // The default configuration must be able in inherit from // other defaults further up the tree @@ -169,8 +138,6 @@ func (defaults *Defaults) decodeAdapters( func DecodeDefaults(path string, adapterCfg []AdapterConfig) (Defaults, error) { defaults := Defaults { } - - if _, err := toml.DecodeFile(path, &defaults); err != nil { return defaults, err } @@ -45,115 +45,6 @@ func directoryExists(path string) bool { return info.IsDir() } -// Find the configuration directory -// Uses: -// 1. PlANR_DIRECTORY env if set -// 2. planr -// 3. .planr -func ConfigDir() string { - - // Return environmental override if set - if dir, isSet := os.LookupEnv("PLANR_DIRECTORY"); isSet { - - if !directoryExists(dir) { - log.Fatalf("Cannot find planr directory %s", dir); - } - - return dir; - } - - cwd, err := os.Getwd() - - if err != nil { - log.Fatal(err) - } - - var rubricDir string - - rubric_search_dirs := [2]string{ - "planr", - ".planr", - } - - found := traverseUp(cwd, func (path string) bool { - - for _, dir := range rubric_search_dirs { - rubricDir = filepath.Join(path, dir) - - if directoryExists(rubricDir) { - return true - } - } - - return false - }); - - if !found { - log.Fatal("Could not find planr directory"); - } - - return rubricDir -} - -func JoinConfigDir(path_ string, file string) string { - if path.IsAbs(path_) { - return path.Join(path_, file) - } - - return path.Join(ConfigDir(), path_, file) -} - -func RootDir() string { - return path.Join(ConfigDir(), "..") -} - -// Find rubric directory at PLANR_DIR/rubric -func RubricDir() string { - rubricDir := path.Join(ConfigDir(), "rubric"); - - if !directoryExists(rubricDir) { - log.Fatal("Could not find the rubric directory inside of planr") - } - - return rubricDir -} - -func BuildDir() string { - buildDir := path.Join(ConfigDir(), "build") - - if !directoryExists(buildDir) { - err := os.Mkdir(buildDir, 0755) - - if err != nil { - log.Fatalf("Cannot create build directory %v\n", err) - } - } - - return buildDir -} - -func CleanBuildDir() { - buildDir := path.Join(ConfigDir(), "build") - if err := os.RemoveAll(buildDir); err != nil { - log.Fatalf("Cannot clean (removeAll) in build directory %v\n", err) - } -} - -func (ac AdapterConfig) Dir() string { - dir := BuildDir() - dir = path.Join(dir, ac.Name) - - if !directoryExists(dir) { - err := os.Mkdir(dir, 0755) - - if err != nil { - log.Fatalf("Cannot create build/%s directory %v\n", ac.Name, err) - } - } - - return dir -} - func basename(path string) string { ext := filepath.Ext(path) return path[0:len(path) - len(ext)] @@ -262,3 +153,9 @@ func collectFromDir( } } } + +func safeCd(newWd string) { + if err := os.Chdir(newWd); err != nil { + log.Fatalf("Could not change into directory %v\n", err) + } +} @@ -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() +} @@ -40,10 +40,13 @@ const ( DEFAULT_PATH_TESTS="tests" ) -type dirConfig struct { - src string - config string - build string +type DirConfig struct { + src string + config string + build string + + // Config falls back to the config found in the parent directory if the env variable hasn't been overridden + pdFallback string } func dieDirAbsent(name, path string) { @@ -63,22 +66,22 @@ func dirFromEnv(name, env string) *string { return nil } -func (c *dirConfig) SetSrcDir(srcDir string) { +func (c *DirConfig) SetSrcDir(srcDir string) { dieDirAbsent("src", srcDir) c.src = srcDir } -func (c *dirConfig) SetConfigDir(configDir string) { +func (c *DirConfig) SetConfigDir(configDir string) { dieDirAbsent("planr (config)", configDir) c.config = configDir } -func (c *dirConfig) SetBuildDir(buildDir string) { +func (c *DirConfig) SetBuildDir(buildDir string) { dieDirAbsent("build", buildDir) c.build = buildDir } -func (c *dirConfig) SetConfigDirFromTree(cdir string) { +func (c *DirConfig) SetConfigDirFromTree(cdir string) { var configDir string found := traverseUp(cdir, func (path string) bool { @@ -94,28 +97,32 @@ func (c *dirConfig) SetConfigDirFromTree(cdir string) { return false }); - if !found { - log.Fatal("Could not find planr directory"); - } - c.config = configDir + if found { + c.pdFallback = configDir + } } -func (c dirConfig) ConfigDir() string { +func (c DirConfig) ConfigDir() string { if c.config != "" { return c.config } - dir := dirFromEnv("config", ENV_CONFIG_DIR) - if dir == nil { - log.Fatal("Could not find directory") + + if dir := dirFromEnv("config", ENV_CONFIG_DIR); dir != nil { + c.config = *dir + return c.config + } + + if c.pdFallback == "" { + log.Fatal("Could not find planr directory"); } - c.config = *dir + c.config = c.pdFallback; return c.config } -func (c dirConfig) SrcDir() string { +func (c DirConfig) SrcDir() string { if c.src != "" { return c.src } @@ -130,7 +137,7 @@ func (c dirConfig) SrcDir() string { return path.Join(dir, DEFAULT_PATH_SRC) } -func (c dirConfig) BuildDir() string { +func (c DirConfig) BuildDir() string { if c.src != "" { return c.src } @@ -144,7 +151,7 @@ func (c dirConfig) BuildDir() string { return path.Join(dir, DEFAULT_PATH_BUILD) } -func (c dirConfig) CleanBuildDir() { +func (c DirConfig) CleanBuildDir() { build := c.BuildDir() if err := os.RemoveAll(build); err != nil { @@ -156,7 +163,7 @@ func (c dirConfig) CleanBuildDir() { } } -func (c dirConfig) MkBuildDir() { +func (c DirConfig) MkBuildDir() { build := c.BuildDir() if err := os.Mkdir(build, 0755); err != nil { @@ -164,13 +171,13 @@ func (c dirConfig) MkBuildDir() { } } -func (c dirConfig) RubricDir() string { +func (c DirConfig) RubricDir() string { rubric := path.Join(c.ConfigDir(), "rubric") dieDirAbsent("rubric", rubric) return rubric } -func (c dirConfig) TestsDir() string { +func (c DirConfig) TestsDir() string { tests := path.Join(c.ConfigDir(), "tests") dieDirAbsent("tests", tests) return tests diff --git a/testcase.go b/testcase.go index 8506b84..7e0bf17 100644 --- a/testcase.go +++ b/testcase.go @@ -1,9 +1,13 @@ package planr +import ( + "log" +) + type TestStatus uint const ( - PASSING TestStatus = iota + PASSING TestStatus = iota COMPILATION_FAILURE RUNTIME_FAILURE ) @@ -15,6 +19,19 @@ type TestResult struct { TestOutput string } +// Program-wide testcase config +type TestCaseConfig struct { + Defaults + Title *string + Description *string +} + +func (c TestCaseConfig) ensureSatisfied(name string) { + if (c.Adapter == nil) { + log.Fatalf("Adapter must be provided for testcase %s", name) + } +} + type TestCase struct { // absolute path to the test case configuration Path string |