docs: change listed type for log_window_value to str
[fio.git] / t / random_seed.py
... / ...
CommitLineData
1#!/usr/bin/env python3
2"""
3# random_seed.py
4#
5# Test fio's random seed options.
6#
7# - make sure that randseed overrides randrepeat and allrandrepeat
8# - make sure that seeds differ across invocations when [all]randrepeat=0 and randseed is not set
9# - make sure that seeds are always the same when [all]randrepeat=1 and randseed is not set
10#
11# USAGE
12# see python3 random_seed.py --help
13#
14# EXAMPLES
15# python3 t/random_seed.py
16# python3 t/random_seed.py -f ./fio
17#
18# REQUIREMENTS
19# Python 3.6
20#
21"""
22import os
23import sys
24import time
25import locale
26import logging
27import argparse
28from pathlib import Path
29from fiotestlib import FioJobCmdTest, run_fio_tests
30
31class FioRandTest(FioJobCmdTest):
32 """fio random seed test."""
33
34 def setup(self, parameters):
35 """Setup the test."""
36
37 fio_args = [
38 "--debug=random",
39 "--name=random_seed",
40 "--ioengine=null",
41 "--filesize=32k",
42 "--rw=randread",
43 f"--output={self.filenames['output']}",
44 ]
45 for opt in ['randseed', 'randrepeat', 'allrandrepeat']:
46 if opt in self.fio_opts:
47 option = f"--{opt}={self.fio_opts[opt]}"
48 fio_args.append(option)
49
50 super().setup(fio_args)
51
52 def get_rand_seeds(self):
53 """Collect random seeds from --debug=random output."""
54 with open(self.filenames['output'], "r",
55 encoding=locale.getpreferredencoding()) as out_file:
56 file_data = out_file.read()
57
58 offsets = 0
59 for line in file_data.split('\n'):
60 if 'random' in line and 'FIO_RAND_NR_OFFS=' in line:
61 tokens = line.split('=')
62 offsets = int(tokens[len(tokens)-1])
63 break
64
65 if offsets == 0:
66 pass
67 # find an exception to throw
68
69 seed_list = []
70 for line in file_data.split('\n'):
71 if 'random' not in line:
72 continue
73 if 'rand_seeds[' in line:
74 tokens = line.split('=')
75 seed = int(tokens[-1])
76 seed_list.append(seed)
77 # assume that seeds are in order
78
79 return seed_list
80
81
82class TestRR(FioRandTest):
83 """
84 Test object for [all]randrepeat. If run for the first time just collect the
85 seeds. For later runs make sure the seeds match or do not match those
86 previously collected.
87 """
88 # one set of seeds is for randrepeat=0 and the other is for randrepeat=1
89 seeds = { 0: None, 1: None }
90
91 def check_result(self):
92 """Check output for allrandrepeat=1."""
93
94 super().check_result()
95 if not self.passed:
96 return
97
98 opt = 'randrepeat' if 'randrepeat' in self.fio_opts else 'allrandrepeat'
99 rr = self.fio_opts[opt]
100 rand_seeds = self.get_rand_seeds()
101
102 if not TestRR.seeds[rr]:
103 TestRR.seeds[rr] = rand_seeds
104 logging.debug("TestRR: saving rand_seeds for [a]rr=%d", rr)
105 else:
106 if rr:
107 if TestRR.seeds[1] != rand_seeds:
108 self.passed = False
109 print(f"TestRR: unexpected seed mismatch for [a]rr={rr}")
110 else:
111 logging.debug("TestRR: seeds correctly match for [a]rr=%d", rr)
112 if TestRR.seeds[0] == rand_seeds:
113 self.passed = False
114 print("TestRR: seeds unexpectedly match those from system RNG")
115 else:
116 if TestRR.seeds[0] == rand_seeds:
117 self.passed = False
118 print(f"TestRR: unexpected seed match for [a]rr={rr}")
119 else:
120 logging.debug("TestRR: seeds correctly don't match for [a]rr=%d", rr)
121 if TestRR.seeds[1] == rand_seeds:
122 self.passed = False
123 print("TestRR: random seeds unexpectedly match those from [a]rr=1")
124
125
126class TestRS(FioRandTest):
127 """
128 Test object when randseed=something controls the generated seeds. If run
129 for the first time for a given randseed just collect the seeds. For later
130 runs with the same seed make sure the seeds are the same as those
131 previously collected.
132 """
133 seeds = {}
134
135 def check_result(self):
136 """Check output for randseed=something."""
137
138 super().check_result()
139 if not self.passed:
140 return
141
142 rand_seeds = self.get_rand_seeds()
143 randseed = self.fio_opts['randseed']
144
145 logging.debug("randseed = %s", randseed)
146
147 if randseed not in TestRS.seeds:
148 TestRS.seeds[randseed] = rand_seeds
149 logging.debug("TestRS: saving rand_seeds")
150 else:
151 if TestRS.seeds[randseed] != rand_seeds:
152 self.passed = False
153 print("TestRS: seeds don't match when they should")
154 else:
155 logging.debug("TestRS: seeds correctly match")
156
157 # Now try to find seeds generated using a different randseed and make
158 # sure they *don't* match
159 for key, value in TestRS.seeds.items():
160 if key != randseed:
161 if value == rand_seeds:
162 self.passed = False
163 print("TestRS: randseeds differ but generated seeds match.")
164 else:
165 logging.debug("TestRS: randseeds differ and generated seeds also differ.")
166
167
168def parse_args():
169 """Parse command-line arguments."""
170
171 parser = argparse.ArgumentParser()
172 parser.add_argument('-f', '--fio', help='path to file executable (e.g., ./fio)')
173 parser.add_argument('-a', '--artifact-root', help='artifact root directory')
174 parser.add_argument('-d', '--debug', help='enable debug output', action='store_true')
175 parser.add_argument('-s', '--skip', nargs='+', type=int,
176 help='list of test(s) to skip')
177 parser.add_argument('-o', '--run-only', nargs='+', type=int,
178 help='list of test(s) to run, skipping all others')
179 args = parser.parse_args()
180
181 return args
182
183
184def main():
185 """Run tests of fio random seed options"""
186
187 args = parse_args()
188
189 if args.debug:
190 logging.basicConfig(level=logging.DEBUG)
191 else:
192 logging.basicConfig(level=logging.INFO)
193
194 artifact_root = args.artifact_root if args.artifact_root else \
195 f"random-seed-test-{time.strftime('%Y%m%d-%H%M%S')}"
196 os.mkdir(artifact_root)
197 print(f"Artifact directory is {artifact_root}")
198
199 if args.fio:
200 fio_path = str(Path(args.fio).absolute())
201 else:
202 fio_path = 'fio'
203 print(f"fio path is {fio_path}")
204
205 test_list = [
206 {
207 "test_id": 1,
208 "fio_opts": {
209 "randrepeat": 0,
210 },
211 "test_class": TestRR,
212 },
213 {
214 "test_id": 2,
215 "fio_opts": {
216 "randrepeat": 0,
217 },
218 "test_class": TestRR,
219 },
220 {
221 "test_id": 3,
222 "fio_opts": {
223 "randrepeat": 1,
224 },
225 "test_class": TestRR,
226 },
227 {
228 "test_id": 4,
229 "fio_opts": {
230 "randrepeat": 1,
231 },
232 "test_class": TestRR,
233 },
234 {
235 "test_id": 5,
236 "fio_opts": {
237 "allrandrepeat": 0,
238 },
239 "test_class": TestRR,
240 },
241 {
242 "test_id": 6,
243 "fio_opts": {
244 "allrandrepeat": 0,
245 },
246 "test_class": TestRR,
247 },
248 {
249 "test_id": 7,
250 "fio_opts": {
251 "allrandrepeat": 1,
252 },
253 "test_class": TestRR,
254 },
255 {
256 "test_id": 8,
257 "fio_opts": {
258 "allrandrepeat": 1,
259 },
260 "test_class": TestRR,
261 },
262 {
263 "test_id": 9,
264 "fio_opts": {
265 "randrepeat": 0,
266 "randseed": "12345",
267 },
268 "test_class": TestRS,
269 },
270 {
271 "test_id": 10,
272 "fio_opts": {
273 "randrepeat": 0,
274 "randseed": "12345",
275 },
276 "test_class": TestRS,
277 },
278 {
279 "test_id": 11,
280 "fio_opts": {
281 "randrepeat": 1,
282 "randseed": "12345",
283 },
284 "test_class": TestRS,
285 },
286 {
287 "test_id": 12,
288 "fio_opts": {
289 "allrandrepeat": 0,
290 "randseed": "12345",
291 },
292 "test_class": TestRS,
293 },
294 {
295 "test_id": 13,
296 "fio_opts": {
297 "allrandrepeat": 1,
298 "randseed": "12345",
299 },
300 "test_class": TestRS,
301 },
302 {
303 "test_id": 14,
304 "fio_opts": {
305 "randrepeat": 0,
306 "randseed": "67890",
307 },
308 "test_class": TestRS,
309 },
310 {
311 "test_id": 15,
312 "fio_opts": {
313 "randrepeat": 1,
314 "randseed": "67890",
315 },
316 "test_class": TestRS,
317 },
318 {
319 "test_id": 16,
320 "fio_opts": {
321 "allrandrepeat": 0,
322 "randseed": "67890",
323 },
324 "test_class": TestRS,
325 },
326 {
327 "test_id": 17,
328 "fio_opts": {
329 "allrandrepeat": 1,
330 "randseed": "67890",
331 },
332 "test_class": TestRS,
333 },
334 ]
335
336 test_env = {
337 'fio_path': fio_path,
338 'fio_root': str(Path(__file__).absolute().parent.parent),
339 'artifact_root': artifact_root,
340 'basename': 'random',
341 }
342
343 _, failed, _ = run_fio_tests(test_list, test_env, args)
344 sys.exit(failed)
345
346
347if __name__ == '__main__':
348 main()