Skip to content

Sprint B8: test coverage Windows 82→168, split phpvm.ps1 jadi modul + build (v1.13.0)#27

Merged
devhardiyanto merged 8 commits into
mainfrom
b8-sprint-windows
Jul 25, 2026
Merged

Sprint B8: test coverage Windows 82→168, split phpvm.ps1 jadi modul + build (v1.13.0)#27
devhardiyanto merged 8 commits into
mainfrom
b8-sprint-windows

Conversation

@devhardiyanto

Copy link
Copy Markdown
Owner

What

Sprint B8 dalam satu branch: naikkan test coverage Windows dari 82 → 168, lalu pecah windows/phpvm.ps1 (1797 baris) jadi 15 modul windows/src/ yang di-concat balik saat build. Ikut serta B12 (iconv macOS) yang belum sempat di-PR. Bump 1.12.3 → 1.13.0.

Why

Syarat asli B8 di ROADMAP: "Risky refactor; do after test coverage feels solid." Saat dicek, 38 dari 65 fungsi .ps1 tak tersentuh test — dan yang kosong justru persis zona yang mau dipotong (seluruh subsistem ext, command handler inti, tools/maint). Memotong di atas jaring bolong = regresi senyap. Jadi test ditulis dulu sebagai enabler, baru split.

Versi 1.13.0 (bukan 1.20.0): ada restrukturisasi internal berarti, tapi nol perubahan user-facing.

How

Phase 1 — test dulu (3 commit). Ext subsystem, version commands, tools/maint. Dua teknik yang dipakai berulang:

  • & $info.Exe -m me-resolve Exe sebagai nama command, jadi sebuah fungsi global php bisa berdiri sebagai pengganti binary aslinya — tak perlu php.exe sungguhan.
  • Yang stateful dikurung: $env:PHPVM_DIR diarahkan ke TestDrive (dengan junction current sungguhan), $PROFILE di-redirect saat menguji hook.

Yang sengaja tidak dites: happy-path Invoke-Use. Ia menulis ulang User PATH dan mem-broadcast WM_SETTINGCHANGE — bukan sesuatu yang boleh dilakukan test run ke mesin developer. Hanya guard sebelum PATH yang di-assert.

Bug yang ketemu gara-gara test itu. Get-OlderPatch mengembalikan @(), tapi output pipeline PowerShell meng-unroll array 1-elemen jadi string telanjang. Show-OlderPatchHint lalu kena StrictMode di .Count (The property 'Count' cannot be found), dan $older[-1] bakal menghasilkan karakter, bukan versi. Efeknya: phpvm install 8.3.10 throw di akhir setiap kali ada tepat satu 8.3.x lama terinstall — kasus yang justru paling umum. Linux tak terpengaruh (_phpvm_older_patches pakai teks newline-joined + tail -1).

Phase 2 — split (strategi concat-release). Modul dev-time di windows/src/, build.ps1 menyatukannya kembali jadi satu windows/phpvm.ps1 yang tetap ter-commit dan ditandai GENERATED. Distribusi tidak berubah — installer dan phpvm upgrade tetap menarik satu file. Prefix angka mengunci urutan concat, dan urutan itu load-bearing: 00-header.ps1 dibuka param() (wajib statement pertama), 99-entry.ps1 ditutup dispatch switch (wajib melihat semua fungsi).

Tiga hal yang ketahuan saat assess dan ikut ditangani:

  1. Line ending. Repo simpan LF, working copy Windows CRLF. Perbandingan byte-per-byte akan false-fail, jadi drift check menormalkan CR dulu.
  2. PSScriptAnalyzer. -Recurse ke windows/src menghasilkan 13 temuan palsu — modul yang dibaca sendirian itu fragment, jadi tiap konstanta bersama terbaca "assigned but never used" dan tiap param() "declared but not used". Lint sekarang menyasar file hasil-generate (yang memang dikirim ke user) + build.ps1; drift gate yang menjamin file itu = isi modul.
  3. Bump versi jadi dua langkah (edit src/00-header.ps1, rebuild). Lupa rebuild = job version-consistency merah, bukan senyap.

Changes

  • windows/src/*.ps1 (baru, 15 modul) — hasil potong phpvm.ps1; nol baris ditambah, dihapus, atau diubah kata — hanya dipindah
  • windows/phpvm.ps1 — sekarang file hasil-generate, ditandai GENERATED - DO NOT EDIT
  • build.ps1 (baru) — concat, plus -Check untuk drift
  • .github/workflows/ci.yml — step drift check (gating); PSSA berhenti -Recurse ke src/, ikut melint build.ps1
  • windows/phpvm.ps1 — fix crash Show-OlderPatchHint (via src/40-install.ps1)
  • tests/windows/Ext.Tests.ps1, VersionCommands.Tests.ps1, ToolsMaint.Tests.ps1, Build.Tests.ps1 (baru) — +86 test
  • linux/phpvm.sh--with-iconv prefix Homebrew di macOS (B12, ikut dari branch sebelumnya)
  • README.md — section Development: windows/phpvm.ps1 generated, cara rebuild
  • version.txt + 4 sumber versi → 1.13.0

Testing Done

  • Pester 168/168 pass (dari 82) — dijalankan terhadap file hasil-generate, jadi split terbukti tidak mengubah perilaku
  • PSScriptAnalyzer clean pada scope baru
  • Drift check: negative test juga dilakukan — phpvm.ps1 sengaja di-drift → exit 1, rebuild → exit 0
  • Verifikasi split: multiset baris phpvm.ps1 lama vs baru identik, beda hanya banner GENERATED
  • Version-consistency (replika CI) PASS di 5 file
  • bats + shellcheck + macOS smoke — dijalankan CI (tak tersedia lokal)

Catatan review

Commit refactor(windows): split ... besar tapi murni pemindahan. Cara tercepat memverifikasi: jalankan pwsh ./build.ps1 -Check, lalu bandingkan phpvm.ps1 lama vs baru setelah membuang komentar # --- src/. Yang perlu mata manusia sebenarnya cuma build.ps1 dan perubahan ci.yml.

Belum dikerjakan (sengaja)

  • Split linux/phpvm.sh — sesuai keputusan, Windows dulu untuk membuktikan polanya. .sh masih ditulis tangan.
  • Happy-path Invoke-Use, Invoke-Install, Install-PECLExt/Install-XDebug jalur download — butuh network/PATH nyata; guard-nya sudah tertutup.

libiconv is keg-only on Homebrew and ships no pkg-config file, so
configure can't detect it on the default path. Pass the explicit
brew prefix on Darwin, mirroring the gmp/gettext handling. Linux
keeps --with-iconv bare (resolved from glibc).

devhardiyanto
The ext block is the largest slice earmarked for the B8 split and had
zero tests. Add Pester coverage for Ext-List/Loaded/Info, Get-PECLVersions,
the Install-PECLExt guards, Show-ExtRuntimeNotes, Ext-Laravel preset
composition and the full Invoke-Ext dispatch table.

php.exe is stood in for by a global 'php' function, since the call
operator resolves $info.Exe as a command name. Ext-Info is locked at
source level so the PHP snippet can't regress into PowerShell
interpolation again.

devhardiyanto
Get-OlderPatch returns @(), but the output pipeline unrolls a
one-element array into a bare string. Show-OlderPatchHint then hit
StrictMode on .Count ("The property 'Count' cannot be found") and
$older[-1] would have yielded a character instead of a version, so
`phpvm install 8.3.10` threw at the very end whenever exactly one
8.3.x was already installed. Re-wrap at the call site.

Linux is unaffected - _phpvm_older_patches passes newline-joined
text and reads the newest with `tail -1`.

devhardiyanto
Second slice of the B8 safety net: Get-CurrentVersion / Remove-Junction
against a real junction, the Invoke-Use guards, Invoke-List,
Invoke-Current, Invoke-Uninstall, Invoke-Which, Invoke-Ini and
Show-OlderPatchHint - the last of which is what surfaced the
single-older-patch crash.

Invoke-Use's happy path is deliberately left out: it rewrites the User
PATH and broadcasts WM_SETTINGCHANGE, which is not something a test
run should do to the machine. Only the pre-PATH guards are asserted.

devhardiyanto
Third slice of the B8 safety net: Invoke-Composer (shim guard, openssl
pre-step, download failure), Invoke-Cacert (status/update/usage),
Invoke-FixIni (all four extension_dir branches), the phpvm hook
install/uninstall/status cycle against a redirected $PROFILE, and the
two offline branches of Invoke-Upgrade.

Nothing here touches the network or the real $PROFILE. The FixIni
"already correct" case is written as a convergence test, since the
first pass legitimately rewrites a CRLF ini before settling.

devhardiyanto
B8. phpvm.ps1 had grown to 1797 lines in one file. Carve it into 15
domain modules under windows/src/ and concatenate them back at build
time, so distribution is untouched - the installer and `phpvm upgrade`
still fetch a single windows/phpvm.ps1, which stays committed and is
now marked GENERATED.

Filename prefixes fix the concat order, and that order is load-bearing:
00-header.ps1 opens with param(), which must be the first statement,
and 99-entry.ps1 closes with the dispatch switch, which must see every
function. No source line was added, dropped or reworded - only moved.

CI gains a drift check (./build.ps1 -Check) that fails when phpvm.ps1
and the modules disagree, mirrored by Build.Tests.ps1 for local runs.
PSScriptAnalyzer stops recursing into windows/src: a module read alone
is a fragment, and linting it that way flags every shared constant as
unused. The drift gate is what ties the linted file back to the source.

devhardiyanto
@devhardiyanto
devhardiyanto merged commit 28225d7 into main Jul 25, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant