Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

testexec: subtest ("then-*") feature #7

Merged
merged 2 commits into from
Oct 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions testexec/selfexercise.md
Original file line number Diff line number Diff line change
@@ -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
```
80 changes: 70 additions & 10 deletions testexec/testexec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -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)
}
Expand All @@ -195,9 +197,18 @@ 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 err := createFiles(fsEnt, "."); err != nil {
t.Errorf("test aborted: could not populate files to tempdir: %s", err)
// 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 fsEnt != nil {
if err := createFiles(fsEnt, "."); err != nil {
t.Errorf("test aborted: could not populate files to tempdir: %s", err)
}
}
}

Expand Down Expand Up @@ -273,7 +284,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)
})
}
}

}

Expand Down Expand Up @@ -326,3 +350,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
}
28 changes: 28 additions & 0 deletions testexec/testexec_test.go
Original file line number Diff line number Diff line change
@@ -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)
}