212 lines
8.1 KiB
Rust
212 lines
8.1 KiB
Rust
// tests/m7_11_acceptance.rs --- T M7.11 REPL migration acceptance.
|
|
//
|
|
//! Acceptance suite for T M7.11. Spec acceptance bullets
|
|
//! (`pmacs-tasks.tex:3557-3572`):
|
|
//!
|
|
//! 1. Package author's guide published, including v1.0 address-scheme
|
|
//! set and forge-alias extension path.
|
|
//! 2. REPL package source moved to `builtin/packages/repl/`, manifest
|
|
//! present, exports declared, bootstrap loads it through the
|
|
//! package system.
|
|
//! 3. All M6.4-M6.9 acceptance tests still pass against the migrated
|
|
//! REPL. (Verified by their own suites; this file does not
|
|
//! re-run them but documents the dependency.)
|
|
//! 4. T M7.9 audit lint reports zero Error-severity findings against
|
|
//! the migrated REPL package; any Warning-level findings are
|
|
//! classified into the M11 watchlist with a one-line rationale.
|
|
//! 5. M7 tag created. (Manual: outside test scope.)
|
|
//! 6. `TRANSITION-M7.md` published. (Outside test scope; the file
|
|
//! exists at the repo root and contains the M7.11 section.)
|
|
//!
|
|
//! Bullets 1, 5, and 6 are doc / git-tag deliverables; the four
|
|
//! tests below cover bullets 2 and 4 plus a smoke check that the
|
|
//! migrated REPL is reachable via `require("repl")` after editor
|
|
//! init.
|
|
|
|
use std::path::Path;
|
|
|
|
use pmacs::audit::{AuditEngine, Severity};
|
|
use pmacs::builtin_packages::{BUNDLED_PACKAGES, materialize_all};
|
|
use pmacs::editor::EditorState;
|
|
use pmacs::packages::PackageManifest;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Bullet 2a: source layout
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn repl_package_lives_under_builtin_packages_with_manifest() {
|
|
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
|
|
let pkg_dir = root.join("builtin").join("packages").join("repl");
|
|
assert!(
|
|
pkg_dir.is_dir(),
|
|
"expected migrated REPL package dir at {pkg_dir:?}"
|
|
);
|
|
assert!(
|
|
pkg_dir.join("init.lua").is_file(),
|
|
"expected entry at {:?}",
|
|
pkg_dir.join("init.lua")
|
|
);
|
|
assert!(
|
|
pkg_dir.join("pmacs.toml").is_file(),
|
|
"expected manifest at {:?}",
|
|
pkg_dir.join("pmacs.toml")
|
|
);
|
|
|
|
// The pre-M7.11 home of the source must be gone.
|
|
let old = root.join("builtin").join("runtime").join("repl.lua");
|
|
assert!(
|
|
!old.exists(),
|
|
"pre-migration repl source should be moved, but found at {old:?}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn repl_manifest_declares_exports_and_pmacs_required() {
|
|
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
|
|
let toml = std::fs::read_to_string(root.join("builtin/packages/repl/pmacs.toml"))
|
|
.expect("read repl manifest");
|
|
let m = PackageManifest::from_toml(&toml).expect("parse repl manifest");
|
|
assert_eq!(m.name.as_str(), "repl");
|
|
assert_eq!(m.entry.to_str().unwrap(), "init.lua");
|
|
assert!(
|
|
m.exports.iter().any(|e| e == "repl"),
|
|
"manifest must export `repl`; got {:?}",
|
|
m.exports
|
|
);
|
|
// pmacs_required must accept the running pmacs version, else
|
|
// the package is unloadable on its own host.
|
|
let running =
|
|
semver::Version::parse(env!("CARGO_PKG_VERSION")).expect("CARGO_PKG_VERSION is semver");
|
|
assert!(
|
|
m.pmacs_required.matches(&running),
|
|
"manifest pmacs_required {} should accept running version {running}",
|
|
m.pmacs_required
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Bullet 2b: bootstrap loads through the package system
|
|
// ---------------------------------------------------------------------------
|
|
//
|
|
// `EditorState::new()` runs the M7.11 bootstrap that materializes
|
|
// the bundled REPL and pushes its InstalledPackage record into
|
|
// the roster. After that, `require("repl")` from Lua resolves
|
|
// through the M7.7 searcher (not the legacy direct eval).
|
|
|
|
#[test]
|
|
fn editor_init_makes_repl_loadable_via_require() {
|
|
let state = EditorState::new_with_roots(&crate::iso::roots());
|
|
let result: bool = state
|
|
.lua_host
|
|
.lua()
|
|
.load(
|
|
r#"
|
|
-- pmacs.packages.load returns true on success and logs to *errors* on
|
|
-- failure. The bundled REPL must already be loaded by the time the
|
|
-- editor finishes its bootstrap (the editor's own setup calls
|
|
-- pmacs.packages.load("repl") through eval), so a follow-up
|
|
-- pmacs.packages.installed() must list it.
|
|
local roster = pmacs.packages.installed()
|
|
for _, p in ipairs(roster) do
|
|
if p.name == "repl" then return true end
|
|
end
|
|
return false
|
|
"#,
|
|
)
|
|
.eval()
|
|
.expect("query installed roster");
|
|
assert!(
|
|
result,
|
|
"bundled REPL must be in pmacs.packages.installed() after editor init"
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Bullet 4: audit lint reports zero Error-severity findings against the
|
|
// migrated REPL; Warning findings are classified.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn audit_lint_against_migrated_repl_reports_no_error_findings() {
|
|
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
|
|
let pkg_dir = root.join("builtin/packages/repl");
|
|
let engine = AuditEngine::new().expect("audit engine");
|
|
let findings = engine
|
|
.audit_dir(&pkg_dir)
|
|
.expect("audit migrated REPL package");
|
|
|
|
let errors: Vec<_> = findings
|
|
.iter()
|
|
.filter(|f| f.severity == Severity::Error)
|
|
.collect();
|
|
assert!(
|
|
errors.is_empty(),
|
|
"migrated REPL must have zero Error-severity audit findings; got {errors:#?}"
|
|
);
|
|
|
|
// M11 watchlist classification (per spec acceptance bullet 4).
|
|
// Each Warning-level finding is documented inline so a future
|
|
// reader can see why it is permitted:
|
|
//
|
|
// * `no-process-spawn-pmacs` --- the REPL *is* the
|
|
// process-spawning subsystem. The bundled package's manifest
|
|
// declares process access by virtue of being shipped with
|
|
// the editor; once the manifest gains a formal
|
|
// `permissions` field (post-M7), the audit engine will
|
|
// verify the declaration and stop emitting this warning.
|
|
//
|
|
// Any new warning class outside this rationale fails the
|
|
// acceptance --- a real surprise should surface as a
|
|
// classification request in M11.
|
|
let permitted_warnings = ["no-process-spawn-pmacs"];
|
|
for f in findings.iter().filter(|f| f.severity == Severity::Warning) {
|
|
assert!(
|
|
permitted_warnings.contains(&f.rule.as_str()),
|
|
"migrated REPL warning `{}` is not classified; \
|
|
add a rationale to tests/m7_11_acceptance.rs or fix the source.\nfinding: {f:?}",
|
|
f.rule
|
|
);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Sanity check on the bundled-package machinery itself
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn bundled_packages_table_includes_repl() {
|
|
let names: Vec<&str> = BUNDLED_PACKAGES.iter().map(|p| p.name).collect();
|
|
assert!(
|
|
names.contains(&"repl"),
|
|
"BUNDLED_PACKAGES must include `repl`; got {names:?}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn materialize_all_produces_one_record_per_bundled_package() {
|
|
let tmp = std::env::temp_dir().join(format!(
|
|
"pmacs-builtin-m7_11-{}-{}",
|
|
std::process::id(),
|
|
std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.map_or(0, |d| d.as_nanos())
|
|
));
|
|
let pkgs = materialize_all(&tmp).expect("materialize_all");
|
|
assert_eq!(pkgs.len(), BUNDLED_PACKAGES.len());
|
|
let repl = pkgs
|
|
.iter()
|
|
.find(|p| p.manifest.name.as_str() == "repl")
|
|
.expect("repl record");
|
|
assert_eq!(repl.tag, "bundled");
|
|
assert!(repl.entry_path().exists());
|
|
let _ = std::fs::remove_dir_all(&tmp);
|
|
}
|
|
|
|
// Isolated bootstrap storage roots (see the module docs): an
|
|
// integration test is compiled without `cfg(test)`, so a raw
|
|
// `EditorState::new()` would read the developer's real `init.lua` and
|
|
// write into their real data root.
|
|
#[path = "common/iso.rs"]
|
|
mod iso;
|