Created Started Finished
Run mutation children under the locale every other test gets
## What
`mutationCheck` builds its coverage and mutation phases with a bare
`pkgs.stdenv.mkDerivation`, which sets no locale. Every other test derivation
goes through the Haskell generic-builder, which sets `LANG=en_US.UTF-8` and
`LOCALE_ARCHIVE`. So the same test suite runs under a different locale here
than it does anywhere else, including under `cabal test` and under the
package's own check.
This gives it that locale.
## Why it matters
PostgreSQL's `initdb` takes the database encoding from the locale. With `LANG`
unset it makes an **SQL_ASCII** cluster; with a UTF-8 locale it makes a
**UTF8** one. On SQL_ASCII, `SUBSTRING` counts bytes rather than characters, so
a query that abbreviates a multi-byte string returns a half-finished character
and the connection fails with:
```
SqlError { sqlState = "22021"
, sqlErrorMsg = "invalid byte sequence for encoding \"UTF8\": 0xf3 0x94 0x84" }
```
That reads as a flaky test rather than as a difference in environment, and it
is reachable only through this check, which is what made it expensive to find:
it cost a day in nix-ci, where it looked like three separate flaky tests in a
row. Write-up: https://github.com/NorfairKing/nix-ci/issues/762
## Evidence
Same binary, same seed, same generated values; only the locale differs.
| environment | cluster | result |
|---|---|---|
| nix sandbox, no locale | SQL_ASCII | fails, deterministically on seed 42 |
| nix sandbox, `LANG=en_US.UTF-8` + `LOCALE_ARCHIVE` | UTF8 | passes |
| outside a sandbox, inherited `LANG=en_US.UTF-8` | UTF8 | passes, 400 iterations |
And the check this was blocking, built against this branch with
`--override-input sydtest`:
```
Killed: 2527 Survived: 0 Uncovered: 0
Controls passed: 197 Controls failed: 0
```
Before: the same check failed one control on four runs out of four.
## Note
`LOCALE_ARCHIVE` is set only when `hostPlatform.libc == "glibc"`, matching what
the Haskell generic-builder does.
Every mutation derivation's hash changes, so the first build after this is a
cold one everywhere.