mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
98 lines
2.6 KiB
Go
98 lines
2.6 KiB
Go
package ltl
|
|
|
|
import (
|
|
"testing"
|
|
"testing/quick"
|
|
"time"
|
|
)
|
|
|
|
// leaf builds a small set of representative atomic formulas indexed by a seed.
|
|
func leafFor(seed uint8) Formula {
|
|
switch seed % 3 {
|
|
case 0:
|
|
return Pure(true)
|
|
case 1:
|
|
return Pure(false)
|
|
default:
|
|
return ThunkNamed("p", func() (bool, error) { return true, nil })
|
|
}
|
|
}
|
|
|
|
func TestNNF_DoubleNegationIsIdentity(t *testing.T) {
|
|
law := func(seed uint8) bool {
|
|
leaf := leafFor(seed)
|
|
doubled := nnf(Not(Not(leaf)))
|
|
direct := nnf(leaf)
|
|
return doubled.describe() == direct.describe()
|
|
}
|
|
if err := quick.Check(law, nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestNNF_NotAlwaysIsEventuallyNot(t *testing.T) {
|
|
law := func(seed uint8) bool {
|
|
leaf := leafFor(seed)
|
|
negated := nnf(Not(Always(leaf)))
|
|
expected := nnf(Eventually(Not(leaf)))
|
|
return negated.describe() == expected.describe()
|
|
}
|
|
if err := quick.Check(law, nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestNNF_NotEventuallyIsAlwaysNot(t *testing.T) {
|
|
law := func(seed uint8) bool {
|
|
leaf := leafFor(seed)
|
|
negated := nnf(Not(Eventually(leaf)))
|
|
expected := nnf(Always(Not(leaf)))
|
|
return negated.describe() == expected.describe()
|
|
}
|
|
if err := quick.Check(law, nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestNNF_BoundedEventuallyDualKeepsBound(t *testing.T) {
|
|
negated := nnf(Not(EventuallyWithinSteps(Pure(true), 4)))
|
|
always, ok := negated.(AlwaysFormula)
|
|
if !ok {
|
|
t.Fatalf("expected AlwaysFormula, got %T", negated)
|
|
}
|
|
if !always.HasStepBound || always.StepBound != 4 {
|
|
t.Errorf("bound not preserved: %+v", always)
|
|
}
|
|
}
|
|
|
|
func TestNNF_PushesNotToThunkLeaf(t *testing.T) {
|
|
formula := nnf(Always(Not(Always(ThunkNamed("p", func() (bool, error) { return true, nil })))))
|
|
always, ok := formula.(AlwaysFormula)
|
|
if !ok {
|
|
t.Fatalf("expected AlwaysFormula, got %T", formula)
|
|
}
|
|
eventually, ok := always.Inner.(EventuallyFormula)
|
|
if !ok {
|
|
t.Fatalf("expected inner EventuallyFormula, got %T", always.Inner)
|
|
}
|
|
not, ok := eventually.Inner.(NotFormula)
|
|
if !ok {
|
|
t.Fatalf("expected NotFormula leaf, got %T", eventually.Inner)
|
|
}
|
|
if _, ok := not.Inner.(ThunkFormula); !ok {
|
|
t.Errorf("expected Not to wrap a Thunk, got %T", not.Inner)
|
|
}
|
|
}
|
|
|
|
func TestNNF_NotAlwaysTrueViaEvaluatorReportsViolated(t *testing.T) {
|
|
evaluator := NewEvaluator(Always(Not(Always(ThunkNamed("p", func() (bool, error) { return true, nil })))))
|
|
for index := range 4 {
|
|
if got := evaluator.ObserveAt(time.Unix(int64(index), 0)); got == VerdictViolated {
|
|
t.Fatalf("step %d latched violated prematurely", index)
|
|
}
|
|
}
|
|
if got := evaluator.Finalize(); got != VerdictViolated {
|
|
t.Errorf("Finalize = %v, want violated", got)
|
|
}
|
|
}
|