From aa3d1eab87c123e67afc9731e895c384aa12b486 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Thu, 21 Oct 2021 20:04:55 +0200 Subject: [PATCH 1/2] testexec: the "then-*" feature. Not yet tested. --- testexec/testexec.go | 74 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 8 deletions(-) diff --git a/testexec/testexec.go b/testexec/testexec.go index f59ddb2..4e13da0 100644 --- a/testexec/testexec.go +++ b/testexec/testexec.go @@ -141,20 +141,20 @@ func (tcfg *Tester) init() { // the test will still be failed, even though in patch regen mode most assertions are usually skipped. func (tcfg Tester) TestSequence(t *testing.T, data testmark.DirEnt) { t.Helper() - tcfg.test(t, data, true, false) + tcfg.test(t, data, true, false, "") } func (tcfg Tester) TestScript(t *testing.T, data testmark.DirEnt) { t.Helper() - tcfg.test(t, data, false, true) + tcfg.test(t, data, false, true, "") } func (tcfg Tester) Test(t *testing.T, data testmark.DirEnt) { t.Helper() - tcfg.test(t, data, true, true) + tcfg.test(t, data, true, true, "") } -func (tcfg Tester) test(t *testing.T, data testmark.DirEnt, allowExec, allowScript bool) { +func (tcfg Tester) test(t *testing.T, data testmark.DirEnt, allowExec, allowScript bool, parentTmpdir string) { t.Helper() tcfg.init() @@ -179,9 +179,11 @@ func (tcfg Tester) test(t *testing.T, data testmark.DirEnt, allowExec, allowScri } // Create a tempdir, and fill it with any files. - if fsEnt, exists := data.Children["fs"]; exists { + var dir string + if fsEnt, exists := data.Children["fs"]; exists || parentTmpdir != "" { // Create and get into a tempdir. - dir, err := ioutil.TempDir("", "testmarkexec") + var err error + dir, err = ioutil.TempDir("", "testmarkexec") if err != nil { t.Errorf("test aborted: could not create tempdir: %s", err) } @@ -195,7 +197,14 @@ func (tcfg Tester) test(t *testing.T, data testmark.DirEnt, allowExec, allowScri t.Errorf("test aborted: could not chdir to tempdir: %s", err) } - // Create files. + // If there was a parent tempdir: copy those files first. + if parentTmpdir != "" { + if err := copyFiles(parentTmpdir, dir); err != nil { + t.Errorf("test aborted: could not populate files to tempdir: %s", err) + } + } + + // Create any new files. if err := createFiles(fsEnt, "."); err != nil { t.Errorf("test aborted: could not populate files to tempdir: %s", err) } @@ -273,7 +282,20 @@ func (tcfg Tester) test(t *testing.T, data testmark.DirEnt, allowExec, allowScri } }) - // TODO: look for "then-*" dirs. + // Look for "then-*" dirs. + // If we're already failed -- make the run block, but skip it. + // If we're going to procede: make a new tempdir, copy the contents, and then procede by recursing. + for _, child := range data.ChildrenList { + if len(child.Name) > 5 && strings.HasPrefix(child.Name, "then-") { + alreadyFailed := t.Failed() + t.Run(child.Name, func(t *testing.T) { + if alreadyFailed { + t.Skipf("parent commands failed, so while more commands are specified, testing them is not meaningful") + } + tcfg.test(t, child, allowExec, allowScript, dir) + }) + } + } } @@ -326,3 +348,39 @@ func createFiles(dir *testmark.DirEnt, prefix string) error { } return nil } + +// copyFiles does what it says on the tin, recursively, +// and handles dirs, files, and symlinks, with basic posix perms (the 0777 bits). +// It doesn't pay attention to dev nodes, uid/gid, etc etc. +func copyFiles(source, destination string) error { + var err error = filepath.Walk(source, func(path string, fileInfo os.FileInfo, err error) error { + var relPath string = strings.Replace(path, source, "", 1) + if relPath == "" { + return nil + } + switch fileInfo.Mode() & os.ModeType { + case os.ModeDir: + return os.Mkdir(filepath.Join(destination, relPath), fileInfo.Mode()) + case os.ModeSymlink: + link, err := os.Readlink(path) + if err != nil { + return err + } + return os.Symlink(link, filepath.Join(destination, relPath)) + default: + fnew, err := os.OpenFile(filepath.Join(destination, relPath), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, fileInfo.Mode()) + if err != nil { + return err + } + defer fnew.Close() + fold, err := os.OpenFile(path, os.O_RDONLY, 0) + if err != nil { + return err + } + defer fold.Close() + _, err = io.Copy(fnew, fold) + return err + } + }) + return err +} From 23ec2b65f42ee40f9d3c7e51061c4ba2e59f9d6c Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Thu, 21 Oct 2021 21:46:04 +0200 Subject: [PATCH 2/2] testexec: selftests (!) for both the filesystem provisioning and the then-subtest feature. One small fix, which makes sure it's fine for subtests to not continue to add files using the file fixture mechanism, which previously threw an NPE (whoopsie). --- testexec/selfexercise.md | 81 +++++++++++++++++++++++++++++++++++++++ testexec/testexec.go | 6 ++- testexec/testexec_test.go | 28 ++++++++++++++ 3 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 testexec/selfexercise.md create mode 100644 testexec/testexec_test.go diff --git a/testexec/selfexercise.md b/testexec/selfexercise.md new file mode 100644 index 0000000..99fa451 --- /dev/null +++ b/testexec/selfexercise.md @@ -0,0 +1,81 @@ +testexec selfexercise file +========================== + +We will run a script with some files provisioned: + +[testmark]:# (whee/fs/a) +``` +body-a +``` + +The script is simple: + +[testmark]:# (whee/script) +``` +echo hello +ls +``` + +The output is unsurprising: + +[testmark]:# (whee/output) +``` +hello +a +``` + +--- + +We can make subtests which add more files: + +[testmark]:# (whee/then-more-files/fs/b) +``` +body-b +``` + +[testmark]:# (whee/then-more-files/script) +``` +ls +``` + +[testmark]:# (whee/then-more-files/output) +``` +a +b +``` + +--- + +We can also affect the filesystem in subtests: + +[testmark]:# (whee/then-touching-files/script) +``` +echo "body-c" > ./c +ls +``` + +Notice that this doesn't inherit the file "b" from the last test -- +this is because they're siblings, so they each got a copy of the *parent* filesystem state. + +[testmark]:# (whee/then-touching-files/output) +``` +a +c +``` + +--- + +If we make a subtest of *that* subtest, it also inherits a copy of those effects: + +[testmark]:# (whee/then-touching-files/then-subtesting-again/script) +``` +ls +cat ./c +``` + +[testmark]:# (whee/then-touching-files/then-subtesting-again/output) +``` +a +c +body-c +``` diff --git a/testexec/testexec.go b/testexec/testexec.go index 4e13da0..d7214bc 100644 --- a/testexec/testexec.go +++ b/testexec/testexec.go @@ -205,8 +205,10 @@ func (tcfg Tester) test(t *testing.T, data testmark.DirEnt, allowExec, allowScri } // Create any new files. - if err := createFiles(fsEnt, "."); err != nil { - t.Errorf("test aborted: could not populate files to tempdir: %s", err) + if fsEnt != nil { + if err := createFiles(fsEnt, "."); err != nil { + t.Errorf("test aborted: could not populate files to tempdir: %s", err) + } } } diff --git a/testexec/testexec_test.go b/testexec/testexec_test.go new file mode 100644 index 0000000..0701f4a --- /dev/null +++ b/testexec/testexec_test.go @@ -0,0 +1,28 @@ +package testexec_test + +import ( + "testing" + + "github.com/warpfork/go-testmark" + "github.com/warpfork/go-testmark/testexec" +) + +func Test(t *testing.T) { + filename := "selfexercise.md" + doc, err := testmark.ReadFile(filename) + if err != nil { + t.Fatalf("spec file parse failed?!: %s", err) + } + + doc.BuildDirIndex() + patches := testmark.PatchAccumulator{} + for _, dir := range doc.DirEnt.ChildrenList { + t.Run(dir.Name, func(t *testing.T) { + test := testexec.Tester{ + Patches: &patches, + } + test.TestScript(t, dir) + }) + } + patches.WriteFileWithPatches(doc, filename) +}