Skip to content

Improve Windows support (CI coverage and test portability) #18

@GingerAdonis

Description

@GingerAdonis

Summary

The project already ships Windows binaries (release matrix + GOOS=windows cross-compile in CI) and includes Windows-specific implementations (internal/filelock, internal/daemonctl, internal/daemon, internal/childproc). Automated verification still happens only on Linux. This issue tracks making Windows a first-class CI target and cleaning up test code so go test ./... is reliable on windows/amd64 (and optionally windows/arm64).

Current state

Area Today
CI unit tests ubuntu-latest only (.github/workflows/ci.ymlgo-checks)
Windows in CI Cross-compile GOOS=windows GOARCH=amd64 go build ./... — compiles, does not execute tests on Windows
Integration tests //go:build integration; import golang.org/x/sys/unix (e.g. unix.Flock in integration_test.go) — will not build on Windows as-is
Unix-only unit tests daemonctl_unix_test.go, childproc_unix_test.go — correctly gated with //go:build !windows
Portable tests with Unix path literals Several tests assert paths like /tmp/hostmux.sock and /tmp/hostmux.toml (run_test.go, root_test.go, parts of internal/config/config_test.go, internal/tlsconfig/tlsconfig_test.go) — fragile or wrong on Windows

Goals

  1. CI: Run the same quality gate (fmt check, vet, staticcheck, tests) on Windows runners so regressions in Windows-specific files are caught early.
  2. Tests: Prefer t.TempDir(), filepath.Join, and explicit path expectations so default unit tests pass on Windows without duplicating entire files.
  3. Scope clarity: Keep integration tests explicitly Unix-oriented until someone invests in a Windows path (or split unix-only helpers behind build tags).

Proposed plan

Phase 1 — Windows job in CI (minimal behavior change)

  • Add a Windows job (recommend windows-latest) that mirrors go-checks:
    • Checkout, actions/setup-go with go-version-file: go.mod.
    • Formatting: same gofmt -l . gate (use shell: bash on the Windows job so the existing bash snippet works unchanged, or provide an equivalent PowerShell check).
    • Static analysis: go vet ./..., go tool staticcheck ./....
    • Tests: go test ./... -count=1.
  • Decide on architecture:
    • Option A (simplest): windows-amd64 only — matches most users and keeps minutes down.
    • Option B: matrix windows-latest × [amd64, arm64] if you want parity with release artifacts early (ARM64 Windows runners may differ; verify availability/cost).
  • Wire needs / concurrency so PRs stay fast (e.g. Windows job in parallel with Linux go-checks, both required for merge once stable).

Note: Until Phase 2 fixes land, the new job may fail — use a draft PR or allow-failure-style branch to land test fixes first, then make the Windows job required.

Phase 2 — Fix or adjust unit tests for Windows

  • CLI / config tests that hardcode /tmp/...:
    • Replace with t.TempDir()-backed paths and filepath.Join, or assert on normalized behavior (parsed path equals the input string you passed in) using a temp path that is valid on all platforms.
    • Files called out by a quick audit: run_test.go, root_test.go, internal/config/config_test.go (TOML with socket = "/tmp/x.sock"), internal/tlsconfig/tlsconfig_test.go (cert paths under /tmp).
  • Subprocess / binary name: Any test that builds or runs hostmux should use hostmux.exe under GOOS=windows when constructing paths (pattern already partially used with filepath.Join in integration_test.go for the output name — align all call sites).
  • Line endings / shell: If any test assumes \n vs \r\n for config or discovery files, normalize assertions.

Phase 3 — Windows-specific coverage (optional but valuable)

  • Add small targeted tests that compile only on Windows (e.g. *_windows_test.go) for behaviors that differ from Unix: detached process spawn, daemonctl, file locking edge cases — without duplicating large suites from *_unix_test.go.
  • Consider running go test -race on Windows for packages where race detection is supported and useful (optional; can be nightly if too slow).

Phase 4 — Integration tests on Windows (larger effort)

  • Today, integration_test.go is unsuitable for Windows because of golang.org/x/sys/unix usage.
  • Options:
    1. Tag integration tests as Unix-only: e.g. //go:build integration && unix (or !windows) and document that go test -tags=integration is supported on Linux/macOS only until Windows support exists.
    2. Refactor: Move unix.Flock usage behind the existing internal/filelock abstraction (or a tiny test helper with //go:build files) so the main integration test file does not import unix on Windows.
    3. Windows E2E: After (2), validate AF_UNIX / named pipe / TCP assumptions against real Windows behavior (Go supports Unix domain sockets on modern Windows; confirm hostmux’s socket story matches what you want to test).

Recommendation: do (1) quickly for honesty in docs/CI; treat (2)+(3) as a follow-up epic if Windows E2E is a product requirement.

Phase 5 — Docs and contributor expectations

  • AGENTS.md: Add a short note that Windows CI runs go test ./... and that integration tests may remain Unix-only.
  • Optionally document one-liners for local Windows verification (PowerShell vs Git Bash).

Acceptance criteria

  • CI runs a Windows job that executes gofmt check, go vet, staticcheck, and go test ./... successfully on a supported Windows runner.
  • Default unit tests do not rely on /tmp-style paths for assertions unless guarded for Windows.
  • Clear policy for integration tests: either build on Windows or are explicitly excluded with build tags and documented.

Risks / notes

  • CI time & cost: Adding Windows doubles cold CI time unless jobs are parallelized; tune with path filters or merge queue rules if needed.
  • staticcheck / Go toolchain: Keep go-version-file in sync so Windows and Linux use the same toolchain.
  • Release job can stay cross-compiling from Ubuntu; native Windows CI is about test execution, not replacing release builds.

References (codebase)

  • CI: .github/workflows/ci.yml (go-checks, cross-compile step, release matrix).
  • Windows implementations: internal/filelock/filelock_windows.go, internal/daemonctl/daemonctl_windows.go, internal/daemon/spawn_windows.go, internal/childproc/childproc_windows.go.
  • Unix-only tests: internal/daemonctl/daemonctl_unix_test.go, internal/childproc/childproc_unix_test.go.
  • Integration: integration_test.go (//go:build integration, unix import).

If this plan looks right, suggested execution order is Phase 2 (test fixes) → Phase 1 (enable Windows CI) → Phase 5 (docs), with Phases 3–4 as follow-ups.

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:mediumLLM agent needs moderate codebase understandingciCI/CD and build pipelineenhancementNew feature or requestgithub_actionsPull requests that update GitHub Actions codetestingTests (unit and integration)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions