-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrandom_test.py
137 lines (112 loc) · 3.49 KB
/
random_test.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from time import sleep
from random import randint
from util import (
sc_read,
read,
put,
cas,
delete,
partition,
crash,
clear,
new_session,
snapshot,
print_log,
collect_results,
wing_gong,
get_availability,
print_availability,
)
N = 10
CRASH = True
NODES = [1, 2, 3]
KEYS = ["k1", "k2"]
VALUES = ["v1", "v2", "v3", "v5", "v6", "v7", "v8", "v9"]
### Helper functions
def random_node():
return NODES[randint(0, len(NODES)-1)]
def random_key():
return KEYS[randint(0, len(KEYS)-1)]
def random_value():
return VALUES[randint(0, len(VALUES)-1)]
def random_set_of_nodes():
nodes = NODES.copy()
for _ in range(randint(0, len(NODES))):
try:
nodes.remove(random_node())
except:
pass
return nodes
### Failures
def partition_at_random(session):
partitions = []
for _ in range(randint(2, 4)):
p = [f"etcd{node}" for node in random_set_of_nodes()]
partitions.append(p)
partition(session, partitions)
def unpartition(session):
partitions = [[f"etcd{node}" for node in NODES]]
partition(session, partitions)
def crash_random_nodes(session):
for node in random_set_of_nodes():
crash(session, node)
def random_failure(session):
if randint(0, 2) > 0 and CRASH:
crash_random_nodes(session)
else:
partition_at_random(session)
### Operations
def random_put(session, futures_list):
put(session, futures_list, random_node(), random_key(), random_value())
def random_delete(session, futures_list):
delete(session, futures_list, random_node(), random_key())
def random_cas(session, futures_list):
cas(session, futures_list, random_node(), random_key(), random_value(), random_value())
def random_read(session, futures_list):
read(session, futures_list, random_node(), random_key())
def random_clear(session, futures_list):
clear(session, futures_list, random_node())
def random_snapshot(session, _):
snapshot(session, random_node())
def random_operation(session, futures_list):
operations = [random_put, random_delete, random_cas, random_read, random_clear, random_snapshot]
operations[randint(0, len(operations)-1)](session, futures_list)
### Putting it together
def run_single_random_test(availability):
trace = []
try:
futures_list = []
session = new_session()
# ensure clean state
random_clear(session, futures_list)
sleep(1)
# write a value, so we are actually testing something interesting
random_put(session, futures_list)
sleep(0.5)
# now perform some random operations and inject random failures
for _ in range(3):
if randint(0, 2) > 0:
random_failure(session)
else:
unpartition(session)
sleep(0.5)
random_operation(session, futures_list)
random_operation(session, futures_list)
sleep(0.5)
random_operation(session, futures_list)
sleep(0.5)
unpartition(session)
sleep(1)
trace = collect_results(futures_list)
except Exception as e:
print(e)
get_availability(trace, availability=availability)
return wing_gong(trace)
### Run n rounds, or until we encounter a non-linearizable trace
availability = {}
is_linearizable = True
n = 0
while is_linearizable and n < N:
is_linearizable = run_single_random_test(availability)
n += 1
print_availability(availability)