|
| 1 | +# TestFlows Machine Examples |
| 2 | + |
| 3 | +Programs and Compose projects to run on [TestFlows Machine](https://testflows.com/machine/). |
| 4 | + |
| 5 | +## ✨ Getting Started |
| 6 | + |
| 7 | +```bash |
| 8 | +git clone https://github.com/testflows/Machine-Examples.git |
| 9 | +cd Machine-Examples |
| 10 | +``` |
| 11 | + |
| 12 | +## 🧪 Programs |
| 13 | + |
| 14 | +The programs are small and written to be run many times. Most hold a |
| 15 | +concurrency bug with a known shape, and what they print depends on how their |
| 16 | +threads interleaved, which is the thing a machine decides, records and can |
| 17 | +replay. The two in `stress/` hold no bug at all: they drive the kernel hard |
| 18 | +enough that a replay either matches or does not. |
| 19 | + |
| 20 | +| Example | What it does | The bug | |
| 21 | +|---|---|---| |
| 22 | +| [hello/hello-world.c](hello/hello-world.c) | Prints a greeting and the kernel it runs on | None. The smallest disk that boots and runs a program | |
| 23 | +| [races/data-race.c](races/data-race.c) | N threads increment one shared counter with no lock | Lost update. The read-add-write is three steps and another thread lands between two of them, so the total comes out short | |
| 24 | +| [starvation/timeout-starve.c](starvation/timeout-starve.c) | Main arms a timeout and waits; the worker must publish before it fires | The worker is starved for the whole wait and the timeout expires with nothing published. It is the ONLY runnable thread while main sleeps, so no pick-next policy finds this | |
| 25 | +| [starvation/pipe-wakeup.c](starvation/pipe-wakeup.c) | A reader blocks on a pipe, main writes one byte then waits | The reader is starved after becoming runnable and misses the timeout. Both threads are runnable after the write, so a scheduler can pick the reader | |
| 26 | +| [starvation/futex-wakeup.c](starvation/futex-wakeup.c) | A waiter blocks on a locked mutex, main unlocks then waits | The waiter is starved after the unlock. Same shape as pipe-wakeup with a futex instead of a pipe | |
| 27 | +| [starvation/narrow-window.c](starvation/narrow-window.c) | A producer raises a flag for a few iterations, then lowers it for a long gap | The consumer is starved through every window and never sees the flag up. Needs frequent short delays, not long ones | |
| 28 | +| [starvation/missed-wakeup.c](starvation/missed-wakeup.c) | A waiter checks a predicate, then waits on it, with no loop around the check | Missed wakeup. Held between the check and the wait, the signaler signals into nobody and the waiter sleeps through it. The timeout expires with the predicate already true | |
| 29 | +| [starvation/lease.c](starvation/lease.c) | Main waits on a lease long enough for the worker's fixed work | The lease expires before the worker publishes. Nobody is starved: virtual time jumps while the work is still running, so the timer fires early | |
| 30 | +| [stress/memstress.c](stress/memstress.c) | Allocates close to the VM's memory limit across threads, with madvise, fork/COW and fault cycles | Not a bug of its own. It drives the kernel's memory management hard, which is the work a replay has to reproduce exactly | |
| 31 | +| [stress/memswapstress.c](stress/memswapstress.c) | Allocates 150% of RAM against a swap file, forcing swap-out, swap-in and swap cache | Not a bug of its own. Same purpose one layer down, through swap | |
| 32 | + |
| 33 | +## 🚀 Run One From the Image |
| 34 | + |
| 35 | +Every program is in one image, `testflows/machine-examples`, at |
| 36 | +`/examples/<name>`. Pull it, then build a disk with `--entrypoint` naming the |
| 37 | +program; arguments go after `--`. A machine is x86_64, so ask for |
| 38 | +`linux/amd64` even on an ARM computer: |
| 39 | + |
| 40 | +```bash |
| 41 | +docker pull --platform linux/amd64 testflows/machine-examples |
| 42 | +machine disks build --image testflows/machine-examples --entrypoint /examples/hello-world hello |
| 43 | +machine disks build --image testflows/machine-examples --entrypoint /examples/data-race race -- 8 |
| 44 | +machine create --disk race |
| 45 | +``` |
| 46 | + |
| 47 | +Each program's arguments and defaults are at the top of its source. |
| 48 | + |
| 49 | +## 🔨 Build Them Yourself |
| 50 | + |
| 51 | +`make` compiles every program static and x86_64, which is what a disk built |
| 52 | +from a binary requires: |
| 53 | + |
| 54 | +```bash |
| 55 | +make # every program, into bin/ |
| 56 | +make check # and prove each one is static x86_64 |
| 57 | +make list # what would be built |
| 58 | +``` |
| 59 | + |
| 60 | +```bash |
| 61 | +machine disks build --binary bin/data-race race -- 8 |
| 62 | +machine create --disk race |
| 63 | +``` |
| 64 | + |
| 65 | +## 🐳 Compose Environments |
| 66 | + |
| 67 | +`compose/` holds whole environments rather than programs: a docker compose |
| 68 | +project that `machine disks build --compose` turns into a disk. The machine |
| 69 | +runs the project with `--abort-on-container-exit`, so it ends when a service |
| 70 | +exits. |
| 71 | + |
| 72 | +| Environment | What it does | What it shows | |
| 73 | +|---|---|---| |
| 74 | +| [compose/two-services](compose/two-services/docker-compose.yml) | A busybox server and a client that fetches from it by service name | Services reach each other by name, over the network Docker gives the project, the same as on any host | |
| 75 | +| [compose/clickhouse](compose/clickhouse/docker-compose.yml) | A ClickHouse server and a client that queries it | A real database server in a machine. `config.xml` replaces the image's: IPv4 only, because the machine's kernel has no IPv6, and every background pool capped, because the defaults hang startup in a small VM without an error | |
| 76 | +| [compose/clickhouse-regression](compose/clickhouse-regression/README.md) | Altinity's ClickHouse regression suite, which brings up its own Compose project inside the machine | A test suite that drives Docker Compose itself. The runner binds the machine's Docker socket, the sources sit at one path both sides of it agree on, and `scale: 0` carries the images the suite starts but no service here names | |
| 77 | + |
| 78 | +``` |
| 79 | +machine disks build --compose compose/clickhouse ch |
| 80 | +machine create --disk ch |
| 81 | +``` |
| 82 | + |
| 83 | +Each project is also runnable on the host, which is how to fix one without |
| 84 | +waiting for a boot: |
| 85 | + |
| 86 | +``` |
| 87 | +cd compose/clickhouse && docker compose up --abort-on-container-exit |
| 88 | +``` |
| 89 | + |
| 90 | +## 📚 Learn More |
| 91 | + |
| 92 | +- 🌐 **Website:** [testflows.com/machine](https://testflows.com/machine/) |
| 93 | +- 📖 **Documentation:** [testflows.com/docs/machine](https://testflows.com/docs/machine/) |
| 94 | +- ⬇️ **Download:** [testflows.com/machine/download](https://testflows.com/machine/download/) |
| 95 | + |
| 96 | +## 📜 License |
| 97 | + |
| 98 | +Apache 2.0; see [LICENSE](LICENSE). |
| 99 | + |
| 100 | +--- |
| 101 | + |
| 102 | +Learn to write test programs, not just tests 👽 |
0 commit comments