An open API service indexing awesome lists of open source software.

https://github.com/marco-m/xprog

xprog -- a test runner for go test -exec
https://github.com/marco-m/xprog

go remote ssh testing vm

Last synced: about 1 year ago
JSON representation

xprog -- a test runner for go test -exec

Awesome Lists containing this project

README

          

# xprog -- a test runner for `go test -exec`

Generic usage with `go test`:

$ go test -exec="xprog [opts] --" [go-test-flags]

Cross-compile the tests and run them on the target, connect via SSH:

$ GOOS=linux go test -exec="xprog ssh [opts] --" [go-test-flags]

As above, but collect also code coverage and show it on the host:

$ GOOS=linux go test -coverprofile=coverage.out -exec="xprog ssh [opts] --" [go-test-flags] &&
go tool cover -html=coverage.out

To see `xprog` output, pass `-v` both to `xprog` and `go test`:

$ go test -v -exec="xprog -v [opts] --" [go-test-flags]

## Install

```
$ go install github.com/marco-m/xprog/cmd/xprog@latest
```

## Writing tests that will not harm YOUR host

If you have at least one test that exercises a destructive or invasive function, it is of the utmost importance to ensure that running `go test` in the default Go way:

```
$ go test ./...
```

(maybe you forgot about xprog, or somebody new to the project) will not cause harm to the host system!

Consider functions `Harmless` and `Destructive` ([examples/example.go](examples/example.go)):

```go
package examples

func Harmless() {
fmt.Fprintln(os.Stderr, "hello from Harmless on", runtime.GOOS)
}

// For example: delete and add files, invoke programs, ...
func Destructive() {
fmt.Fprintln(os.Stderr, "hello from Destructive on", runtime.GOOS)
}
```

We can test `Harmless` as usual ([examples/example_test.go](examples/example_test.go)):

```go
func TestHarmless(t *testing.T) {
examples.Harmless()
}
```

On the other hand, we want to be sure that `Destructive` is tested ONLY on the VM target. This can be achieved as follows ([examples/example_test.go](examples/example_test.go)):

```go
import "github.com/marco-m/xprog"

func TestDestructiveXprog(t *testing.T) {
if xprog.Absent() {
t.Skip("skip: test requires xprog")
}
examples.Destructive()
}
```

Running the tests on the host as usual, note that `TestDestructiveXprog` is skipped:

```
$ go test -v ./examples
=== RUN TestHarmless
hello from Harmless on darwin
--- PASS: TestHarmless (0.00s)
=== RUN TestDestructiveXprog
example_test.go:21: skip: test requires xprog
--- SKIP: TestDestructiveXprog (0.00s)
PASS
ok github.com/marco-m/xprog/examples
```

Running the tests on the target VM via `xprog`, note that `TestDestructiveXprog` runs:

```
$ GOOS=linux go test -v -exec="xprog ssh -cfg $PWD/ssh_config --" ./examples
=== RUN TestHarmless
hello from Harmless on linux
--- PASS: TestHarmless (0.00s)
=== RUN TestDestructiveXprog
hello from Destructive on linux
--- PASS: TestDestructiveXprog (0.00s)
PASS
ok github.com/marco-m/xprog/examples
```

### Running tests as root

To do this, be sure that the tests are **safe** to run everywhere (see section above).

Then, add `--sudo` to the `ssh` command.

This assumes a passwordless sudo on the target (what you get by default on a Vagrant VM).

## Usage

For details, have a look at the targets in the [Taskfile](Taskfile.yml), they are commented.

### Quick preparation

```
$ virtualbox up
;; Generate a SSH configuration file
$ vagrant ssh-config > ssh_config
```

### Quick usage

Cross-compile the tests and run them on the target, using `xprog ssh`:

```
$ GOOS=linux go test -exec="xprog ssh --cfg $PWD/ssh_config --" ./... -v
```

Stop the VM when done

```
vagrant halt
```

### More controlled preparation

See `task prepare-vm` or

```
$ vagrant destroy --force
$ vagrant up
;; Take snapshot, name `pristine`
$ vagrant snapshot save pristine
;; Generate a SSH configuration file
$ vagrant ssh-config > ssh_config
```

### More controlled usage

See `task test:vm:...`

Run the VM-based tests on the VM current filesystem (faster but inaccurate):

```
$ GOOS=linux go test -coverprofile=coverage.out -exec="$PWD/bin/xprog ssh --cfg $PWD/ssh_config --" ./... -v
```

Or: run the VM-based tests from a clean snapshot:

```
$ vagrant snapshot restore pristine
$ GOOS=linux go test -coverprofile=coverage.out -exec="$PWD/bin/xprog ssh --cfg $PWD/ssh_config --" ./... -v
```

### Notes

`go test` will execute `xprog` in the directory (or directories) corresponding to the package(s) specified to the `go test` invocation. For example:

```
$ go test -exec="xprog ssh --cfg $PWD/ssh_config --" ./foo
```

will run `xprog` in directory `./foo`. This is why it is important to specify the ssh_config file with an absolute path: `--cfg $PWD/ssh_config`, so that it will be found no matter the `xprog` working directory.

## Limitations

### Configuration

`xprog ssh` expects a `ssh_config` file generated by `vagrant ssh-config` and will pick the first `Host` entry.

### Reserved environment variables

The prefix `XPROG_SYS_` is reserved for xprog internal usage. Messing with it can cause `xprog.Absent()` to return false positives and thus destructive tests will run also on your host.

## License

See [LICENSE](LICENSE).

## Credits

- [vmtest](https://github.com/anatol/vmtest)
- [dockexec](https://github.com/mvdan/dockexec)
- [Go tooling essentials](https://rakyll.org/go-tool-flags/)
- [go_android_exec](https://github.com/golang/go/blob/master/misc/android/go_android_exec.go)