/- Infoductor.Test — generic Foundation end-to-end tests ===================================================== Compile-time exercise of the Foundation layer (Phases A–D'.5). Cubical-transport-specific tests live downstream in `infoductor-cubical/InfoductorCubical/Test.lean`. - Phase A: meta-mirror types. - Phase B: Edit monad + restructure. - Phase C: @[macroAlias]. - Phase D'/D'.5: @[methodology] + @[metaPath] + cubical_search. -/ import Infoductor.Foundation.Methodology import Infoductor.Foundation.MetaPath import Infoductor.Foundation.MetaParse namespace Infoductor.Test open Infoductor -- ── Phase A: meta-mirror types ───────────────────────────────────────────── /-- Meta-mirror types are inhabited and well-formed. -/ example : MetaCType := .theorem_ example : MetaCType := .file example : MetaCType := .classifierSet example : MetaClassifier := .always example : MetaClassifier := .never example : MetaClassifier := .meet (.atDecl `Foo) (.inFile "Bar.lean") example : MetaArtifact := .source "def x := 1" example : MetaArtifact := .empty example : MetaArtifact := .refTo `Existing example : MetaPosition := { declName := `Test, filePath := "Test.lean", range := none } /-- DecidableEq fires correctly on MetaClassifier. -/ example : decide (MetaClassifier.always = .always) = true := by decide example : decide (MetaClassifier.always = .never) = false := by decide example : decide ((.meet .always (.atDecl `Foo) : MetaClassifier) = (.meet .always (.atDecl `Foo))) = true := by decide -- ── Phase B: Edit + Context + restructure ─────────────────────────────────── /-- A simple restructure call produces an Edit with one op. -/ example : let pos : MetaPosition := { declName := `Foo, filePath := "F.lean", range := none } let e := restructure pos .theorem_ .always (.source "def x := 1") .empty e.ops.length = 1 := by rfl /-- The restructure picks `witness` when the classifier is `.always`. -/ example : let pos : MetaPosition := { declName := `Foo, filePath := "F.lean", range := none } let e := restructure pos .theorem_ .always (.source "yes") (.source "no") (e.ops.head?).isSome = true := by rfl /-- The restructure picks `fallback` when the classifier is `.never`. -/ example : let pos : MetaPosition := { declName := `Foo, filePath := "F.lean", range := none } let e := restructure pos .theorem_ .never (.source "yes") (.source "no") (e.ops.head?).isSome = true := by rfl /-- Self-consistency check on a single restructure op. -/ example : let pos : MetaPosition := { declName := `Foo, filePath := "F.lean", range := none } let e := restructure pos .theorem_ .always (.source "x") .empty e.selfConsistent = true := by rfl /-- A batch that removes a decl AND references it by `refTo` is flagged as inconsistent. -/ example : let pos₁ : MetaPosition := { declName := `Foo, filePath := "F.lean", range := none } let pos₂ : MetaPosition := { declName := `Bar, filePath := "F.lean", range := none } let e : Edit Unit := do restructure pos₁ .theorem_ .always .empty .empty -- removes Foo restructure pos₂ .theorem_ .always (.refTo `Foo) .empty -- refs Foo e.selfConsistent = false := by rfl -- ── Frozen aliases ────────────────────────────────────────────────────────── /-- The `transport_artifact` alias produces a single Edit op. -/ example : let pos : MetaPosition := { declName := `Foo, filePath := "F.lean", range := none } let e := transport_artifact pos .theorem_ (.source "x") e.ops.length = 1 := by rfl /-- The `materialize` alias emits raw Lean source. -/ example : let pos : MetaPosition := { declName := `Foo, filePath := "F.lean", range := none } let e := materialize pos .theorem_ "theorem x : True := trivial" e.ops.length = 1 := by rfl -- ── Phase C: @[macroAlias] registration ───────────────────────────────────── /-- A custom alias for testing. Registered via the attribute below. -/ @[macroAlias] def custom_alias_test (i : MetaPosition) : Edit Unit := restructure i .definition .always (.source "test") .empty -- ── Phase D': @[methodology] registration ─────────────────────────────────── /-- A trivial methodology: solves `True` goals. Registered against a placeholder classifier name `IsTrueGoal`. -/ @[methodology IsTrueGoal] theorem trueMethodology : True := True.intro /-- `cubical_search` finds the registered methodology for `True` goals. Demonstrates the dispatch loop end-to-end. -/ example : True := by cubical_search /-- Methodology for `Eq.refl`-shaped goals. Demonstrates that `cubical_search` can dispatch to non-trivial universe-equality goals. -/ @[methodology IsReflGoal] theorem reflMethodologyNat (n : Nat) : n = n := rfl example : 7 = 7 := by cubical_search -- ── Phase D'.5: @[metaPath] + methodology-transport ───────────────────────── /-- A trivial structural Path: every reflexivity goal is also a "trivial-truth" goal. Declares the meta-Path so methodology- transport can lift `IsTrueGoal` methodologies onto `IsReflGoal` targets and vice versa. -/ @[metaPath IsReflGoal IsTrueGoal] theorem refl_path_to_true (n : Nat) (_ : n = n) : True := True.intro /-- An identity methodology that closes any `Iff.refl`-shaped goal. -/ @[methodology IsIffReflGoal] theorem iffRefl (P : Prop) : P ↔ P := Iff.rfl example : True ↔ True := by cubical_search -- ── Source-rendering soundness ───────────────────────────────────────────── -- The renderer's output, when elaborated by Lean, reconstructs the -- same value. These checks witness round-trip via the elaborator -- itself: the rendered string is literally Lean source and the -- expected term-on-the-RHS is what Lean parses it to. /-- Empty MetaCTerm renders to its own constructor name. -/ example : MetaCTerm.toLeanSource .empty = "Infoductor.MetaCTerm.empty" := rfl /-- The MetaClassifier renderer renders atomic arms by name. -/ example : MetaClassifier.toLeanSource .always = "Infoductor.MetaClassifier.always" := rfl example : MetaClassifier.toLeanSource .never = "Infoductor.MetaClassifier.never" := rfl /-- The atomic MetaArtifact arm renders to its constructor name. -/ example : MetaArtifact.toLeanSource .empty = "Infoductor.MetaArtifact.empty" := rfl -- Non-atomic arms involve `s!`-interpolation and `repr`, which -- don't reduce in the kernel for `rfl`. Their soundness claim -- is round-trip via Lean's elaborator: parsing the rendered -- string gives back an expression that elaborates to the same -- value. Live `#eval`s display the rendered forms for inspection. #eval MetaCTerm.toLeanSource (.ident `Foo) #eval MetaCTerm.toLeanSource (.sym "x") #eval MetaCTerm.toLeanSource (.app (.ident `Foo) (.sym "x")) #eval MetaClassifier.toLeanSource (.atDecl `Bar) #eval MetaArtifact.toLeanSource (.cterm .empty) #eval MetaArtifact.toLeanSource (.cterm (.ident `Foo)) -- ── String → MetaCTerm parser round-trip ─────────────────────────────────── -- The hand-written parser in `Foundation.MetaParse` is the inverse -- of the renderer. Each example below feeds a MetaCTerm through -- the renderer, then through the parser, and demands identity. -- Verified at compile time via `native_decide` — Lean compiles the -- round-trip check to native code and runs it. example : Infoductor.MetaCTerm.fromLeanSource? (MetaCTerm.toLeanSource .empty) = some .empty := by native_decide example : Infoductor.MetaCTerm.fromLeanSource? (MetaCTerm.toLeanSource (.ident `Foo)) = some (.ident `Foo) := by native_decide example : Infoductor.MetaCTerm.fromLeanSource? (MetaCTerm.toLeanSource (.sym "x")) = some (.sym "x") := by native_decide example : Infoductor.MetaCTerm.fromLeanSource? (MetaCTerm.toLeanSource (.app (.ident `Foo) (.sym "x"))) = some (.app (.ident `Foo) (.sym "x")) := by native_decide example : Infoductor.MetaCTerm.fromLeanSource? (MetaCTerm.toLeanSource (.lam "x" (.sym "x"))) = some (.lam "x" (.sym "x")) := by native_decide example : Infoductor.MetaCTerm.fromLeanSource? (MetaCTerm.toLeanSource (.comp "i" (.ident `Univ) .always (.sym "u") (.sym "t"))) = some (.comp "i" (.ident `Univ) .always (.sym "u") (.sym "t")) := by native_decide example : Infoductor.MetaCTerm.fromLeanSource? (MetaCTerm.toLeanSource (.transp "i" (.ident `Univ) (.atDecl `j) (.sym "t"))) = some (.transp "i" (.ident `Univ) (.atDecl `j) (.sym "t")) := by native_decide example : Infoductor.MetaClassifier.fromLeanSource? (MetaClassifier.toLeanSource .always) = some .always := by native_decide example : Infoductor.MetaClassifier.fromLeanSource? (MetaClassifier.toLeanSource (.meet .always (.atDecl `Foo))) = some (.meet .always (.atDecl `Foo)) := by native_decide -- MetaArtifact has a `Lean.Syntax` arm (`.declAt`) which lacks -- `DecidableEq`, so we compare round-trips up to rendering — i.e., -- the parsed value re-renders to the same string. Since the -- forward rendering is injective on the arms we exercise, this is -- equivalent to structural equality on those arms. example : ((Infoductor.MetaArtifact.fromLeanSource? (MetaArtifact.toLeanSource .empty)).map MetaArtifact.toLeanSource) = some (MetaArtifact.toLeanSource .empty) := by native_decide example : ((Infoductor.MetaArtifact.fromLeanSource? (MetaArtifact.toLeanSource (.cterm (.ident `Foo)))).map MetaArtifact.toLeanSource) = some (MetaArtifact.toLeanSource (.cterm (.ident `Foo))) := by native_decide -- ── Compile-time registry diagnostics ─────────────────────────────────────── /-- A diagnostic action that prints registry sizes. Run via `#eval` inside a Lean session to verify that @[methodology] / @[macroAlias] / @[metaPath] declarations have populated the EnvExtensions. -/ def printRegistrySizes : Lean.CoreM Unit := do let aliases ← getAliases let methodologies ← getMethodologies let paths ← getMetaPaths IO.println s!"[Infoductor.Test] aliases={aliases.size} \ methodologies={methodologies.size} paths={paths.size}" end Infoductor.Test