def test_processes(): q = Queue() @timewinder.process def writer(q): while True: yield "Write" q.queue.append("msg") @timewinder.process def reader(q): while True: yield "Read" _ = q.queue[0] q.queue = q.queue[1:] ev = timewinder.Evaluator( objects=[q], threads=[writer(q), reader(q)], specs=[bounded_queue(q, 2)], ) got_error = False try: ev.evaluate() except timewinder.ProcessException as e: got_error = True print(e) assert got_error
def test_overdraft_1(): @timewinder.object class Account: def __init__(self, name): self.name = name self.acc = 5 alice = Account("alice") bob = Account("bob") @timewinder.step def withdraw(state, sender, amount): sender.acc = sender.acc - amount @timewinder.step def deposit(state, reciever, amount): reciever.acc = reciever.acc + amount alg = timewinder.FuncProcess( withdraw(alice, 3), deposit(bob, 3), ) no_overdrafts = timewinder.ForAll(Account, lambda a: a.acc >= 0) ev = timewinder.Evaluator( objects=[alice, bob], threads=[alg], specs=[no_overdrafts], ) ev.evaluate() ev._print_state_space() assert ev.stats.states == 3
def test_emulate_await_p2(): q = Queue() @timewinder.process def writer(q): while True: yield "Write" q.queue.append("msg") while len(q.queue) >= 2: yield "Waiting" @timewinder.process def reader(q): while True: yield "Read" if len(q.queue) == 0: continue _ = q.queue[0] q.queue = q.queue[1:] ev = timewinder.Evaluator( objects=[q], threads=[writer(q), reader(q)], specs=[bounded_queue(q, 2)], ) try: ev.evaluate(steps=100) except timewinder.ConstraintError as e: print(e.name) ev.replay_thunk(e.thunk) assert False assert ev.stats.states == 13
def test_overdraft_initial_conditions(): alice = Account("alice", 5) bob = Account("bob", 5) @timewinder.process def withdraw(sender, reciever, amount): sender.acc = sender.acc - amount yield "deposit" reciever.acc = reciever.acc + amount no_overdrafts = timewinder.ForAll(Account, lambda a: a.acc >= 0) ev = timewinder.Evaluator( objects=[alice, bob], threads=[withdraw(alice, bob, Set(range(1, 7)))], specs=[no_overdrafts], ) got_error = False try: ev.evaluate() except ConstraintError as e: got_error = True print(e.name) print(e.thunk) assert got_error assert ev.stats.states == 12
def test_emulate_await_p1(): q = Queue() @timewinder.process def writer(q): while True: yield "Write" q.queue.append("msg") @timewinder.process def reader(q): while True: yield "Read" if len(q.queue) == 0: continue _ = q.queue[0] q.queue = q.queue[1:] ev = timewinder.Evaluator( objects=[q], threads=[writer(q), reader(q)], specs=[bounded_queue(q, 2)], ) got_error = False try: ev.evaluate() except timewinder.ConstraintError as e: got_error = True assert e.name.find("bounded_queue") >= 0 print(e.name) ev.replay_thunk(e.thunk) assert got_error
def test_run_deep_calls(): object = A() @timewinder.step def a(state, m): print("in a") m.foo = "b" @timewinder.step def b(state, m): m.foo = "c" @timewinder.step def c(state, m): m.foo = "end1" @timewinder.step def d(state, m): m.foo = "end2" alg = timewinder.FuncProcess( a(object), b(object), c(object), d(object), ) ev = timewinder.Evaluator( objects=[object], threads=[alg], ) ev.evaluate() assert ev.stats.states == 5 ev._print_state_space()
def reset_and_eval(): alice = Account("alice", 5) bob = Account("bob", 5) ev = timewinder.Evaluator( objects=[alice, bob], threads=[ check_and_withdraw(alice, bob, Set(range(1, 6))), check_and_withdraw(alice, bob, Set(range(1, 6))), ], specs=[no_overdrafts], ) ev.evaluate(steps=10) return ev.stats
def test_multiple_processes_if(): @timewinder.object class Account: def __init__(self, name): self.name = name self.acc = 5 alice = Account("alice") bob = Account("bob") @timewinder.step def withdraw(state, sender): sender.acc = sender.acc - state["amt"] @timewinder.step def deposit(state, reciever): reciever.acc = reciever.acc + state["amt"] @timewinder.step def check_funds(state, sender): if state["amt"] > sender.acc: raise timewinder.StopProcess() def alg(): return timewinder.FuncProcess( check_funds(alice), withdraw(alice), deposit(bob), state={"amt": Set(range(1, 6))}, ) no_overdrafts = timewinder.ForAll(Account, lambda a: a.acc >= 0) ev = timewinder.Evaluator( objects=[alice, bob], threads=[alg(), alg()], specs=[no_overdrafts], ) got_error = False try: ev.evaluate(steps=10) except ConstraintError as e: got_error = True print(e.name, " failed") ev.replay_thunk(e.thunk) assert got_error assert ev.stats.states == 295
def test_abi_thread_idx(): @timewinder.object @dataclass class Result: result: int @timewinder.process def get_thread_id(res): res.result = ThreadID() r = Result(-1) ev = timewinder.Evaluator( objects=[r], threads=[get_thread_id(r)], ) ev.evaluate() assert r.result == 0
def reset_and_eval(): alice = Account("alice") bob = Account("bob") def alg(): return timewinder.FuncProcess( check_and_withdraw(alice), deposit(bob), state={"amt": Set(range(1, 6))}, ) ev = timewinder.Evaluator( objects=[alice, bob], threads=[alg(), alg()], specs=[no_overdrafts], ) ev.evaluate(steps=10) return ev.stats
def test_overdraft_initial_conditions(): @timewinder.object class Account: def __init__(self, name): self.name = name self.acc = 5 alice = Account("alice") bob = Account("bob") @timewinder.step def withdraw(state, sender): sender.acc = sender.acc - state["amt"] @timewinder.step def deposit(state, reciever): reciever.acc = reciever.acc + state["amt"] alg = timewinder.FuncProcess( withdraw(alice), deposit(bob), state={ "amt": Set(range(1, 7)), }, ) no_overdrafts = timewinder.ForAll(Account, lambda a: a.acc >= 0) ev = timewinder.Evaluator( objects=[alice, bob], threads=[alg], specs=[no_overdrafts], ) got_error = False try: ev.evaluate() except ConstraintError as e: got_error = True print(e.name) print(e.thunk) assert got_error assert ev.stats.states == 12
def bounded_queue_example(n_producers, n_consumers, queue_size): n_threads = n_producers + n_consumers runnable = CondWait(n_producers, n_consumers) bqueue = BoundedQueue(queue_size) threads = [] for i in range(n_threads): if i < n_producers: threads.append(producer(bqueue, runnable, i)) else: threads.append(consumer(bqueue, runnable, i)) no_deadlocks = timewinder.ForAll(CondWait, lambda c: any(c.status)) return timewinder.Evaluator( objects=[runnable, bqueue], specs=[no_deadlocks], threads=threads, )
def test_liveness_reinterp(): @timewinder.process def check_and_withdraw(sender, reciever, amt): if amt <= sender.acc: sender.acc = sender.acc - amt yield "deposit" reciever.acc = reciever.acc + amt no_overdrafts = timewinder.ForAll(Account, lambda a: a.acc >= 0) @timewinder.predicate def consistent_total(a, b): total = a.acc + b.acc return total == 10 alice = Account("alice", 5) bob = Account("bob", 5) eventually_consistent = timewinder.Eventually( timewinder.Always(consistent_total(alice, bob))) ev = timewinder.Evaluator( objects=[alice, bob], threads=[ check_and_withdraw(alice, bob, Set(range(1, 6))), check_and_withdraw(alice, bob, Set(range(1, 6))), ], specs=[ no_overdrafts, eventually_consistent, ], ) got_error = False try: ev.evaluate(steps=10) except StutterConstraintError as s: got_error = True print("\n" + s.name + "\n") ev.replay_thunk(s.thunk) assert got_error
def test_overdraft_1(): alice = Account("alice", 5) bob = Account("bob", 5) @timewinder.process def withdraw(sender, reciever, amount): sender.acc = sender.acc - amount yield "deposit" reciever.acc = reciever.acc + amount no_overdrafts = timewinder.ForAll(Account, lambda a: a.acc >= 0) ev = timewinder.Evaluator( objects=[alice, bob], threads=[withdraw(alice, bob, 3)], specs=[no_overdrafts], ) ev.evaluate() ev._print_state_space() assert ev.stats.states == 3
def test_transmit_extension(): q = Queue(40) max_queue_length = 7 @timewinder.process def writer(q, max): while q.to_transmit != 0: yield "Write" q.queue.append("msg") q.to_transmit -= 1 while len(q.queue) >= max: yield "Waiting" @timewinder.process def reader(q): while q.to_transmit > 0 or len(q.queue) > 0: yield "Read" if len(q.queue) == 0: continue _ = q.queue[0] q.queue = q.queue[1:] ev = timewinder.Evaluator( objects=[q], threads=[writer(q, max_queue_length), reader(q)], specs=[ bounded_queue(q, max_queue_length), ], ) try: ev.evaluate(steps=None) except timewinder.ConstraintError as e: print(e.name) ev.replay_thunk(e.thunk) raise e assert ev.stats.states == 555
threads = [] for i in range(THREADS): if i < PRODUCERS: threads.append(producer(bqueue, runnable, i)) else: threads.append(consumer(bqueue, runnable, i)) no_deadlocks = timewinder.ForAll(CondWait, lambda c: any(c.status)) ev = timewinder.Evaluator( objects=[runnable, bqueue], specs=[no_deadlocks], threads=threads, ) err = None try: ev.evaluate(steps=None) except timewinder.ConstraintError as e: err = e if err is None: print(ev.stats) else: print(f"\nConstraint Violated: {err.name}\n")
# objects must carry positive balances. no_overdrafts = timewinder.ForAll(Account, lambda a: a.acc >= 0) # Run the evaluator. ev = timewinder.Evaluator( # Pass our two objects objects=[alice, bob], # Declare the predicates we want to check. specs=[no_overdrafts], # Only have one thread do a withdrawal of too much # money, and it should fail. # threads=[withdraw(alice, bob, 6)], # Alternately, run two threads, both withdrawing from Alice and depositing # to Bob's account. The `Set` function is a generator that will # try every transfer amount from 1 to 5, as per Python's `range` # builtin. # # This should fail, as both must complete and will in some cases # (both amounts at 1) but will fail if too much is transferred. # # threads=[ # check_and_withdraw(alice, bob, Set(range(1, 6))), # check_and_withdraw(alice, bob, Set(range(1, 6))), # ], ) err = None try: ev.evaluate() except timewinder.ConstraintError as e: