aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorMarcos Magueta <maguetamarcos@gmail.com>2026-07-12 00:35:33 -0300
committerMarcos Magueta <maguetamarcos@gmail.com>2026-07-12 00:35:33 -0300
commit7aacf596d73cc74eb9af381a72723b50b4069e30 (patch)
tree6970478e0866167285dc15820812b464407b75cb /test
parent6520b876af064d57bccf944a6fef9807a8c79de5 (diff)
downloadSOL-7aacf596d73cc74eb9af381a72723b50b4069e30.tar.gz
SOL-7aacf596d73cc74eb9af381a72723b50b4069e30.zip
symbolic discharge: universals over infinite domains decided by intervals
Diffstat (limited to 'test')
-rw-r--r--test/test_embedding.ml5
-rw-r--r--test/test_vm.ml54
2 files changed, 53 insertions, 6 deletions
diff --git a/test/test_embedding.ml b/test/test_embedding.ml
index ea55e4f..a2eab95 100644
--- a/test/test_embedding.ml
+++ b/test/test_embedding.ml
@@ -66,9 +66,12 @@ let test_kernel_scan () =
| Error error -> Alcotest.failf "solve error: %a" Sol.Vm.pp_error error
let test_kernel_totality () =
+ (* A universal over an infinite domain whose body the symbolic
+ fragment cannot read: neither enumerable nor provable, so it
+ must be rejected, embedding or not. *)
match
solve
- {| [ForAll variable: X quantifier: [Integer value: X] body: [True]] |}
+ {| [ForAll variable: X quantifier: [Integer value: X] body: [Sensor id: X]] |}
with
| Error (Sol.Vm.Non_total_plan _) -> ()
| Ok _ -> Alcotest.fail "the embedded engine must keep the totality guard"
diff --git a/test/test_vm.ml b/test/test_vm.ml
index f3d8484..16ca63c 100644
--- a/test/test_vm.ml
+++ b/test/test_vm.ml
@@ -263,13 +263,47 @@ let test_forall_fails () =
| [ _ :: _ ] -> Alcotest.fail "the universal must fail: Ana is 12"
| _ -> Alcotest.fail "expected one query"
-let test_forall_over_infinite_rejected () =
- match load {| Query expression: [ForAll variable: X quantifier: [Integer value: X] body: [True]] |} with
+let holds source = count_solutions source = 1
+
+(* Symbolic discharge: the specification's own two examples, decided
+ without enumerating anything. *)
+
+let test_symbolic_naturals_nonnegative () =
+ Alcotest.(check bool) "every natural is at least zero" true
+ (holds
+ {| Query expression: [ForAll variable: X quantifier: [Natural value: X] body: [GreaterEqual left: X right: 0]] |})
+
+let test_symbolic_integers_not_nonnegative () =
+ Alcotest.(check bool) "not every integer is at least zero" false
+ (holds
+ {| Query expression: [ForAll variable: X quantifier: [Integer value: X] body: [GreaterEqual left: X right: 0]] |})
+
+let test_symbolic_inclusion () =
+ (* Decision 12: subtype inclusion, decided symbolically. *)
+ Alcotest.(check bool) "every integer is a rational" true
+ (holds
+ {| Query expression: [ForAll variable: X quantifier: [Integer value: X] body: [Rational value: X]] |});
+ Alcotest.(check bool) "not every rational is an integer" false
+ (holds
+ {| Query expression: [ForAll variable: X quantifier: [Rational value: X] body: [Integer value: X]] |})
+
+let test_symbolic_interval_entailment () =
+ Alcotest.(check bool) "naturals from ten upward exceed five" true
+ (holds
+ {| Query expression: [ForAll variable: X quantifier: ([Natural value: X] :and [GreaterEqual left: X right: 10]) body: [GreaterThan left: X right: 5]] |})
+
+let test_forall_outside_fragment_still_rejected () =
+ match
+ load
+ {| Query expression: [ForAll variable: X quantifier: [Integer value: X] body: [Equal left: X right: 5]] |}
+ with
| Error error -> Alcotest.failf "load error: %a" Sol.Program.pp_error error
| Ok (world, [ query ]) -> (
match Sol.Vm.Standalone.solve world query with
| Error (Sol.Vm.Non_total_plan _) -> ()
- | Ok _ -> Alcotest.fail "a universal over an infinite domain must be rejected")
+ | Ok _ ->
+ Alcotest.fail
+ "a universal the symbolic fragment cannot read must still be rejected")
| Ok _ -> Alcotest.fail "expected one query"
let test_exists () =
@@ -444,10 +478,20 @@ let () =
[
Alcotest.test_case "forall holds" `Quick test_forall_holds;
Alcotest.test_case "forall fails" `Quick test_forall_fails;
- Alcotest.test_case "forall over infinite rejected" `Quick
- test_forall_over_infinite_rejected;
Alcotest.test_case "exists" `Quick test_exists;
] );
+ ( "symbolic",
+ [
+ Alcotest.test_case "naturals nonnegative" `Quick
+ test_symbolic_naturals_nonnegative;
+ Alcotest.test_case "integers not nonnegative" `Quick
+ test_symbolic_integers_not_nonnegative;
+ Alcotest.test_case "inclusion" `Quick test_symbolic_inclusion;
+ Alcotest.test_case "interval entailment" `Quick
+ test_symbolic_interval_entailment;
+ Alcotest.test_case "outside fragment still rejected" `Quick
+ test_forall_outside_fragment_still_rejected;
+ ] );
( "criteria",
[
Alcotest.test_case "criterion admits" `Quick test_criterion_admits;