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
- Host: GitHub
- URL: https://github.com/marco-m/xprog
- Owner: marco-m
- License: mit
- Created: 2021-12-19T19:56:40.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2024-12-14T13:44:47.000Z (over 1 year ago)
- Last Synced: 2025-04-12T01:16:28.644Z (about 1 year ago)
- Topics: go, remote, ssh, testing, vm
- Language: Go
- Homepage:
- Size: 59.6 KB
- Stars: 3
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
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)