def test_NEXT_check_ltl_onepb(self): with tests.Configure(self, __file__, "/example.smv"): # false in the initial state formula = parseLTL("() a") # however the violation is not detected when no move is allowed status, trace = check.check_ltl_onepb(formula, 0) self.assertEqual("Ok", status) self.assertIsNone(trace) status, trace = check.check_ltl_onepb(formula, 1) self.assertEqual("Violation", status) self.assertIsNotNone(trace) self.assertEqual(1, len(trace)) # true in the initial state formula = parseLTL("()() a") # not reachable status, trace = check.check_ltl_onepb(formula, 0) self.assertEqual("Ok", status) self.assertIsNone(trace) # not quite yet status, trace = check.check_ltl_onepb(formula, 1) self.assertEqual("Ok", status) self.assertIsNone(trace) # ok status, trace = check.check_ltl_onepb(formula, 2) self.assertEqual("Ok", status) self.assertIsNone(trace) # and even for longer traces status, trace = check.check_ltl_onepb(formula, 3) self.assertEqual("Ok", status) self.assertIsNone(trace)
def test_EVENTUALLY_check_ltl_onepb(self): with tests.Configure(self, __file__, "/example.smv"): # proving a violation of this prop necessitates at least two # steps: flip --> flop --> flip formula = parseLTL("<>(a <=> b)") status, trace = check.check_ltl_onepb(formula, 0) self.assertEqual("Ok", status) self.assertIsNone(trace) status, trace = check.check_ltl_onepb(formula, 1) self.assertEqual("Ok", status) self.assertIsNone(trace) # a loop is identified status, trace = check.check_ltl_onepb(formula, 2) self.assertEqual("Violation", status) self.assertIsNotNone(trace) self.assertEqual(2, len(trace)) # valid in the initial state (hence for any bound) formula = parseLTL("<>(a | b)") for k in range(10): status, trace = check.check_ltl_onepb(formula, k) self.assertEqual("Ok", status) self.assertIsNone(trace)
def test_arithmetic_unary_minus(self): # number can be negated ast = parsing.parseLTL("-12") self.assertEqual("Proposition(- (12))", str(ast)) # and so does a variable ast = parsing.parseLTL("-a") self.assertEqual("Proposition(- (a))", str(ast)) # or a nested expression ast = parsing.parseLTL("-( 6 * 8)") self.assertEqual("Proposition(- ((6) * (8)))", str(ast))
def test_parse_parenthesized(self): # no impact on vars ast = parsing.parseLTL("(a)") self.assertEqual("Proposition(a)", str(ast)) # it permits to change the side-associativity # can be chained (right associative) ast = parsing.parseLTL("(phi | psi) | chi") self.assertEqual("((Proposition(phi) Or Proposition(psi)) Or Proposition(chi))", str(ast)) ast = parsing.parseLTL("(phi & psi) & chi") self.assertEqual("((Proposition(phi) And Proposition(psi)) And Proposition(chi))", str(ast)) ast = parsing.parseLTL("(phi => psi) => chi") self.assertEqual("((Proposition(phi) Imply Proposition(psi)) Imply Proposition(chi))", str(ast)) ast = parsing.parseLTL("(phi <=> psi) <=> chi") self.assertEqual("((Proposition(phi) Equiv Proposition(psi)) Equiv Proposition(chi))", str(ast)) ast = parsing.parseLTL("([]a) & b") self.assertEqual("((Globally Proposition(a)) And Proposition(b))", str(ast)) ast = parsing.parseLTL("(<>a) & b") self.assertEqual("((Eventually Proposition(a)) And Proposition(b))", str(ast)) ast = parsing.parseLTL("([]a) U (<>b)") self.assertEqual("((Globally Proposition(a)) Until (Eventually Proposition(b)))", str(ast)) ast = parsing.parseLTL("([]a) W (<>b)") self.assertEqual("((Globally Proposition(a)) WeakUntil (Eventually Proposition(b)))", str(ast))
def test_NEXT_check_ltl(self): with tests.Configure(self, __file__, "/example.smv"): status, l, trace = check.check_ltl(parseLTL("()() a"), 5) self.assertEqual('Ok', status) self.assertEqual(5, l) self.assertIsNone(trace) status, l, trace = check.check_ltl(parseLTL("() a"), 5) self.assertEqual('Violation', status) self.assertEqual(1, l) self.assertIsNotNone(trace)
def test_EVENTUALLY_check_ltl(self): with tests.Configure(self, __file__, "/example.smv"): status, l, trace = check.check_ltl(parseLTL("<>(a <=> !b)"), 5) self.assertEqual('Ok', status) self.assertEqual(5, l) self.assertIsNone(trace) status, l, trace = check.check_ltl(parseLTL("<>(a & b)"), 5) self.assertEqual('Violation', status) self.assertEqual(2, l) self.assertIsNotNone(trace)
def test_parse_variable_containing_FALSE(self): ast = parsing.parseLTL("@FALSE") # everything from the unknown punctuation on is discarded self.assertEqual("Proposition(@FALSE)", str(ast)) ast = parsing.parseLTL("FALSE@") # everything from the unknown punctuation on is discarded self.assertEqual("Proposition(FALSE@)", str(ast)) ast = parsing.parseLTL("FALSE.") # everything from the unknown punctuation on is discarded self.assertEqual("Proposition(FALSE.)", str(ast))
def test_generate_problem_with_fairness(self): ''' This test clearly shows the difference in validating a property with or without fairness constraint ''' with tests.Configure(self, __file__, "/philo.smv"): # length 0 # nusmv has fairness always on. fml_node = Node.from_ptr( parse_ltl_spec("G (p1.waiting -> F !p1.waiting)")) smv = ltlspec.generate_ltl_problem(self.befsm, fml_node, 0) self.assertEqual(SatSolverResult.UNSATISFIABLE, self.satisfiability(smv)) formula = parseLTL("[](p1.waiting => <>!p1.waiting)") unfair = gen.generate_problem(formula, self.befsm, 0, no_fairness=True) self.assertEqual(SatSolverResult.UNSATISFIABLE, self.satisfiability(unfair)) fair = gen.generate_problem(formula, self.befsm, 0, no_fairness=False) self.assertEqual(SatSolverResult.UNSATISFIABLE, self.satisfiability(fair)) # length 1 fml_node = Node.from_ptr( parse_ltl_spec("G (p1.waiting -> F !p1.waiting)")) smv = ltlspec.generate_ltl_problem(self.befsm, fml_node, 1) self.assertEqual(SatSolverResult.UNSATISFIABLE, self.satisfiability(smv)) formula = parseLTL("[](p1.waiting => <>!p1.waiting)") unfair = gen.generate_problem(formula, self.befsm, 1, no_fairness=True) self.assertEqual(SatSolverResult.SATISFIABLE, self.satisfiability(unfair)) fair = gen.generate_problem(formula, self.befsm, 1, no_fairness=False) self.assertEqual(SatSolverResult.UNSATISFIABLE, self.satisfiability(fair))
def test_GLOBALLY_check_ltl_onepb(self): with tests.Configure(self, __file__, "/example.smv"): formula = parseLTL("[](a <=> !b)") for k in range(10): status, trace = check.check_ltl_onepb(formula, k) self.assertEqual("Ok", status) self.assertIsNone(trace) # already violated in the initial state formula = parseLTL("[](a <=> b)") status, trace = check.check_ltl_onepb(formula, 0) self.assertEqual("Violation", status) self.assertIsNotNone(trace) self.assertEqual(0, len(trace))
def test_arithmetic_modulus(self): # numbers ast = parsing.parseLTL("6 mod 8") self.assertEqual("Proposition((6) mod (8))", str(ast)) # vars ast = parsing.parseLTL("a mod b") self.assertEqual("Proposition((a) mod (b))", str(ast)) # chainable ast = parsing.parseLTL("a mod b mod c") self.assertEqual("Proposition((a) mod (b) mod (c))", str(ast)) # priority over +/- ast = parsing.parseLTL("a + b mod c") self.assertEqual("Proposition((a) + ((b) mod (c)))", str(ast)) ast = parsing.parseLTL("a mod b - c") self.assertEqual("Proposition(((a) mod (b)) - (c))", str(ast))
def test_arithmetic_subtraction(self): # numbers ast = parsing.parseLTL("6 - 8") self.assertEqual("Proposition((6) - (8))", str(ast)) # vars ast = parsing.parseLTL("a - b") self.assertEqual("Proposition((a) - (b))", str(ast)) # chainable ast = parsing.parseLTL("a - b - c") self.assertEqual("Proposition((a) - (b) - (c))", str(ast)) # priority over <</>> ast = parsing.parseLTL("a - b << c") self.assertEqual("Proposition(((a) - (b)) << (c))", str(ast)) ast = parsing.parseLTL("a - b >> c") self.assertEqual("Proposition(((a) - (b)) >> (c))", str(ast))
def test_arithmetic_multiplication(self): # numbers ast = parsing.parseLTL("6 * 8") self.assertEqual("Proposition((6) * (8))", str(ast)) # vars ast = parsing.parseLTL("a * b") self.assertEqual("Proposition((a) * (b))", str(ast)) # chainable ast = parsing.parseLTL("a * b * c") self.assertEqual("Proposition((a) * (b) * (c))", str(ast)) # priority over +/- ast = parsing.parseLTL("a + b * c") self.assertEqual("Proposition((a) + ((b) * (c)))", str(ast)) ast = parsing.parseLTL("a * b - c") self.assertEqual("Proposition(((a) * (b)) - (c))", str(ast))
def test_arithmetic_division(self): # numbers ast = parsing.parseLTL("6 / 8") self.assertEqual("Proposition((6) / (8))", str(ast)) # vars ast = parsing.parseLTL("a / b") self.assertEqual("Proposition((a) / (b))", str(ast)) # chainable ast = parsing.parseLTL("a / b / c") self.assertEqual("Proposition((a) / (b) / (c))", str(ast)) # priority over +/- ast = parsing.parseLTL("a + b / c") self.assertEqual("Proposition((a) + ((b) / (c)))", str(ast)) ast = parsing.parseLTL("a / b - c") self.assertEqual("Proposition(((a) / (b)) - (c))", str(ast))
def test_arithmetic_lshift(self): # numbers ast = parsing.parseLTL("6 << 8") self.assertEqual("Proposition((6) << (8))", str(ast)) # vars ast = parsing.parseLTL("a << b") self.assertEqual("Proposition((a) << (b))", str(ast)) # chainable ast = parsing.parseLTL("a << b << c") self.assertEqual("Proposition((a) << (b) << (c))", str(ast)) # priority over comparison ast = parsing.parseLTL("a << b <= c") self.assertEqual("Proposition(((a) << (b)) <= (c))", str(ast)) ast = parsing.parseLTL("a << b = c") self.assertEqual("Proposition(((a) << (b)) = (c))", str(ast))
def test_UNTIL_check_ltl(self): with tests.Configure(self, __file__, "/never_b.smv"): status, l, trace = check.check_ltl(parseLTL("b U a"), 5) self.assertEqual('Ok', status) self.assertEqual(5, l) self.assertIsNone(trace) status, l, trace = check.check_ltl(parseLTL("a U b"), 5) self.assertEqual('Violation', status) self.assertEqual(1, l) self.assertIsNotNone(trace) status, l, trace = check.check_ltl(parseLTL("b W !a"), 5) self.assertEqual('Violation', status) self.assertEqual(0, l) self.assertIsNotNone(trace)
def test_nnf_eventually(self): with tests.Configure(self, __file__, "/example.smv"): formula = parseLTL("<> a") not_neg = "(Eventually Proposition(a))" self.assertEqual(not_neg, str(formula.nnf(False))) negated = "(Globally (Not Proposition(a)))" self.assertEqual(negated, str(formula.nnf(True))) # when members need nnfing too formula = parseLTL("<> (!a)") not_neg = "(Eventually (Not Proposition(a)))" self.assertEqual(not_neg, str(formula.nnf(False))) negated = "(Globally Proposition(a))" self.assertEqual(negated, str(formula.nnf(True)))
def validate_generate_problem(self, bound, custom_text, nusmv_text): fsm = self.befsm # formulae formula = parseLTL(custom_text) fml_node = Node.from_ptr(parse_ltl_spec(nusmv_text)) # IMPORTANT NOTE: each instantiation of the problem creates new CNF # literal which appears in the clauses list (even in canonical form) # hence, the canonical forms of the different instantiations cannot # simply be compared as there is no a priori way to know what CNF # literal reconcile with what other. # However, the different expressions should all have the exact same # satisfiability. So, that's how this test proceeds. smv = ltlspec.generate_ltl_problem(fsm, fml_node, bound) tool = gen.generate_problem(formula, fsm, bound) manual = gen.model_problem(fsm, bound) &\ formula.nnf(True).bounded_semantics(fsm, bound) sat_smv = self.satisfiability(smv) sat_tool = self.satisfiability(tool) sat_man = self.satisfiability(manual) self.assertEqual(sat_tool, sat_man) self.assertEqual(sat_tool, sat_smv)
def test_arithmetic_rshift(self): # numbers ast = parsing.parseLTL("6 >> 8") self.assertEqual("Proposition((6) >> (8))", str(ast)) # vars ast = parsing.parseLTL("a >> b") self.assertEqual("Proposition((a) >> (b))", str(ast)) # chainable ast = parsing.parseLTL("a >> b >> c") self.assertEqual("Proposition((a) >> (b) >> (c))", str(ast)) # priority over comparison ast = parsing.parseLTL("a >> b <= c") self.assertEqual("Proposition(((a) >> (b)) <= (c))", str(ast)) ast = parsing.parseLTL("a >> b = c") self.assertEqual("Proposition(((a) >> (b)) = (c))", str(ast))
def test_nnf_next(self): with tests.Configure(self, __file__, "/example.smv"): formula = parseLTL("() a") # w/o member nnf-ing not_neg = "(Next Proposition(a))" self.assertEqual(not_neg, str(formula.nnf(False))) negated = "(Next (Not Proposition(a)))" self.assertEqual(negated, str(formula.nnf(True))) # w/ member nnf-ing formula = parseLTL("() !a") not_neg = "(Next (Not Proposition(a)))" self.assertEqual(not_neg, str(formula.nnf(False))) negated = "(Next Proposition(a))" self.assertEqual(negated, str(formula.nnf(True)))
def test_parse_next(self): ast = parsing.parseLTL("() phi") self.assertEqual("(Next Proposition(phi))", str(ast)) # spacing is irrelevant ast = parsing.parseLTL("()phi") self.assertEqual("(Next Proposition(phi))", str(ast)) # can be chained ast = parsing.parseLTL("() () () phi") self.assertEqual("(Next (Next (Next Proposition(phi))))", str(ast)) # space is irrelevant (even when chained) ast = parsing.parseLTL("()()()phi") self.assertEqual("(Next (Next (Next Proposition(phi))))", str(ast)) # it may accept a constant ast = parsing.parseLTL("()TRUE") self.assertEqual("(Next Constant(TRUE))", str(ast)) # it may accept a parenthesized expression ast = parsing.parseLTL("()(a | b)") self.assertEqual("(Next (Proposition(a) Or Proposition(b)))", str(ast)) # it may accept a timed expression ast = parsing.parseLTL("()[]a") self.assertEqual("(Next (Globally Proposition(a)))", str(ast))
def test_parse_globally(self): ast = parsing.parseLTL("[] phi") self.assertEqual("(Globally Proposition(phi))", str(ast)) # spacing is irrelevant ast = parsing.parseLTL("[]phi") self.assertEqual("(Globally Proposition(phi))", str(ast)) # can be chained ast = parsing.parseLTL("[] [] [] phi") self.assertEqual("(Globally (Globally (Globally Proposition(phi))))", str(ast)) # space is irrelevant (even when chained) ast = parsing.parseLTL("[][][]phi") self.assertEqual("(Globally (Globally (Globally Proposition(phi))))", str(ast)) # it may accept a constant ast = parsing.parseLTL("[]TRUE") self.assertEqual("(Globally Constant(TRUE))", str(ast)) # it may accept a parenthesized expression ast = parsing.parseLTL("[](a | b)") self.assertEqual("(Globally (Proposition(a) Or Proposition(b)))", str(ast)) # it may accept a timed expression ast = parsing.parseLTL("[]<>a") self.assertEqual("(Globally (Eventually Proposition(a)))", str(ast))
def test_nnf_imply(self): with tests.Configure(self, __file__, "/example.smv"): # when the operands dont need to be nnf-ed formula = parseLTL("a => b") not_neg = "((Not Proposition(a)) Or Proposition(b))" self.assertEqual(not_neg, str(formula.nnf(False))) negated = "(Proposition(a) And (Not Proposition(b)))" self.assertEqual(negated, str(formula.nnf(True))) # when the operands need to be nnf-ed formula = parseLTL("a => !b") not_neg = "((Not Proposition(a)) Or (Not Proposition(b)))" self.assertEqual(not_neg, str(formula.nnf(False))) negated = "(Proposition(a) And Proposition(b))" self.assertEqual(negated, str(formula.nnf(True)))
def check(formula, args): try: parsed_fml = parseLTL(formula.strip()) status,length,trace = check_ltl(parsed_fml, args.bound, args.no_fairness, args.no_invariants, args.dry_run) if status != 'Ok': print("-- {} for length {}".format(status, length)) print(trace) except Exception as e: print("The specification contains a syntax error") print(e)
def test_check_ltl_arithmetics(self): """ This tests the use of the invariants flag when performing the check. """ with tests.Configure(self, __file__, "/numbers.smv"): formula = parseLTL("[] a < 7") status, _, trace = check.check_ltl(formula, 10, no_invar=False) self.assertEqual("Ok", status) self.assertIsNone(trace)
def test_UNTIL_check_ltl_onepb(self): with tests.Configure(self, __file__, "/never_b.smv"): # entailed by the automaton formula = parseLTL("a U b") # not reachable status, trace = check.check_ltl_onepb(formula, 0) self.assertEqual("Ok", status) self.assertIsNone(trace) # this is where U differs from W: at infinity, b must hold status, trace = check.check_ltl_onepb(formula, 1) self.assertEqual("Violation", status) self.assertIsNotNone(trace) self.assertEqual(1, len(trace)) # true in the initial state formula = parseLTL("b U a") status, trace = check.check_ltl_onepb(formula, 0) self.assertEqual("Ok", status) self.assertIsNone(trace) status, trace = check.check_ltl_onepb(formula, 1) self.assertEqual("Ok", status) self.assertIsNone(trace) status, trace = check.check_ltl_onepb(formula, 2) self.assertEqual("Ok", status) self.assertIsNone(trace) status, trace = check.check_ltl_onepb(formula, 3) self.assertEqual("Ok", status) self.assertIsNone(trace) # violated right away formula = parseLTL("b U !a") status, trace = check.check_ltl_onepb(formula, 0) self.assertEqual("Violation", status) self.assertIsNotNone(trace) self.assertEqual(0, len(trace))
def test_nnf_weakuntil(self): with tests.Configure(self, __file__, "/example.smv"): # rules of pseudo duality: # (p U q) <-> (!q W (!p & !q)) # (p W q) <-> (!q U (!p & !q)) # without operands nnfing formula = parseLTL("(p W q)") not_neg = "(Proposition(p) WeakUntil Proposition(q))" self.assertEqual(not_neg, str(formula.nnf(False))) negated = "((Not Proposition(q)) Until ((Not Proposition(p)) And (Not Proposition(q))))" self.assertEqual(negated, str(formula.nnf(True))) # wit operands nnfing formula = parseLTL("((!p) W (!q))") not_neg = "((Not Proposition(p)) WeakUntil (Not Proposition(q)))" self.assertEqual(not_neg, str(formula.nnf(False))) negated = "(Proposition(q) Until (Proposition(p) And Proposition(q)))" self.assertEqual(negated, str(formula.nnf(True)))
def test_WEAKUNTIL_check_ltl_onepb(self): with tests.Configure(self, __file__, "/never_b.smv"): # entailed by the automaton formula = parseLTL("a W b") # not reachable status, trace = check.check_ltl_onepb(formula, 0) self.assertEqual("Ok", status) self.assertIsNone(trace) # already looping but no counter example status, trace = check.check_ltl_onepb(formula, 1) self.assertEqual("Ok", status) self.assertIsNone(trace) # true in the initial state formula = parseLTL("b W a") status, trace = check.check_ltl_onepb(formula, 0) self.assertEqual("Ok", status) self.assertIsNone(trace) status, trace = check.check_ltl_onepb(formula, 1) self.assertEqual("Ok", status) self.assertIsNone(trace) status, trace = check.check_ltl_onepb(formula, 2) self.assertEqual("Ok", status) self.assertIsNone(trace) status, trace = check.check_ltl_onepb(formula, 3) self.assertEqual("Ok", status) self.assertIsNone(trace) # violated right away formula = parseLTL("b W !a") status, trace = check.check_ltl_onepb(formula, 0) self.assertEqual("Violation", status) self.assertIsNotNone(trace) self.assertEqual(0, len(trace))
def validate_bounded_semantics(self, bound, custom_text, nusmv_text): fsm = self.befsm # formulae formula = parseLTL(custom_text) fml_node = Node.from_ptr(parse_ltl_spec(nusmv_text)) tool = formula.bounded_semantics(fsm, bound) smv = ltlspec.bounded_semantics(fsm, fml_node, bound) # canonical forms s_tool = tests.canonical_cnf(tool) s_smv = tests.canonical_cnf(smv) self.assertEqual(s_tool, s_smv)
def test_nnf_equiv(self): with tests.Configure(self, __file__, "/example.smv"): # when the operands dont need to be nnf-ed formula = parseLTL("a <=> b") # (!a | b) & (!b | a) not_neg = "(((Not Proposition(a)) Or Proposition(b)) And ((Not Proposition(b)) Or Proposition(a)))" self.assertEqual(not_neg, str(formula.nnf(False))) # (a & !b) | (b & !a) negated = "((Proposition(a) And (Not Proposition(b))) Or (Proposition(b) And (Not Proposition(a))))" self.assertEqual(negated, str(formula.nnf(True))) # when the operands need to be nnf-ed formula = parseLTL("a <=> !b") # (!a | !b) & (b | a) not_neg = "(((Not Proposition(a)) Or (Not Proposition(b))) And (Proposition(b) Or Proposition(a)))" self.assertEqual(not_neg, str(formula.nnf(False))) # (a & b) | (!b & !a) negated = "((Proposition(a) And Proposition(b)) Or ((Not Proposition(b)) And (Not Proposition(a))))" self.assertEqual(negated, str(formula.nnf(True)))
def test_check_ltl_invariants(self): """ This tests the use of the invariants flag when performing the check. """ with tests.Configure(self, __file__, "/dummy_with_invar.smv"): formula = parseLTL("[] v") # invariants enforced status, _, trace = check.check_ltl(formula, 10, no_invar=True) self.assertEqual("Violation", status) self.assertEqual(0, len(trace)) # invariants enforced status, _, trace = check.check_ltl(formula, 10, no_invar=False) self.assertEqual("Ok", status) self.assertIsNone(trace)