5 # Test fio's random seed options.
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
12 # see python3 random_seed.py --help
15 # python3 t/random_seed.py
16 # python3 t/random_seed.py -f ./fio
28 from pathlib import Path
29 from fiotestlib import FioJobCmdTest, run_fio_tests
31 class FioRandTest(FioJobCmdTest):
32 """fio random seed test."""
34 def setup(self, parameters):
43 f"--output={self.filenames['output']}",
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)
50 super().setup(fio_args)
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()
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])
67 # find an exception to throw
70 for line in file_data.split('\n'):
71 if 'random' not in line:
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
82 class TestRR(FioRandTest):
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
88 # one set of seeds is for randrepeat=0 and the other is for randrepeat=1
89 seeds = { 0: None, 1: None }
91 def check_result(self):
92 """Check output for allrandrepeat=1."""
94 super().check_result()
98 opt = 'randrepeat' if 'randrepeat' in self.fio_opts else 'allrandrepeat'
99 rr = self.fio_opts[opt]
100 rand_seeds = self.get_rand_seeds()
102 if not TestRR.seeds[rr]:
103 TestRR.seeds[rr] = rand_seeds
104 logging.debug("TestRR: saving rand_seeds for [a]rr=%d", rr)
107 if TestRR.seeds[1] != rand_seeds:
109 print(f"TestRR: unexpected seed mismatch for [a]rr={rr}")
111 logging.debug("TestRR: seeds correctly match for [a]rr=%d", rr)
112 if TestRR.seeds[0] == rand_seeds:
114 print("TestRR: seeds unexpectedly match those from system RNG")
116 if TestRR.seeds[0] == rand_seeds:
118 print(f"TestRR: unexpected seed match for [a]rr={rr}")
120 logging.debug("TestRR: seeds correctly don't match for [a]rr=%d", rr)
121 if TestRR.seeds[1] == rand_seeds:
123 print("TestRR: random seeds unexpectedly match those from [a]rr=1")
126 class TestRS(FioRandTest):
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.
135 def check_result(self):
136 """Check output for randseed=something."""
138 super().check_result()
142 rand_seeds = self.get_rand_seeds()
143 randseed = self.fio_opts['randseed']
145 logging.debug("randseed = %s", randseed)
147 if randseed not in TestRS.seeds:
148 TestRS.seeds[randseed] = rand_seeds
149 logging.debug("TestRS: saving rand_seeds")
151 if TestRS.seeds[randseed] != rand_seeds:
153 print("TestRS: seeds don't match when they should")
155 logging.debug("TestRS: seeds correctly match")
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():
161 if value == rand_seeds:
163 print("TestRS: randseeds differ but generated seeds match.")
165 logging.debug("TestRS: randseeds differ and generated seeds also differ.")
169 """Parse command-line arguments."""
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()
185 """Run tests of fio random seed options"""
190 logging.basicConfig(level=logging.DEBUG)
192 logging.basicConfig(level=logging.INFO)
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}")
200 fio_path = str(Path(args.fio).absolute())
203 print(f"fio path is {fio_path}")
211 "test_class": TestRR,
218 "test_class": TestRR,
225 "test_class": TestRR,
232 "test_class": TestRR,
239 "test_class": TestRR,
246 "test_class": TestRR,
253 "test_class": TestRR,
260 "test_class": TestRR,
268 "test_class": TestRS,
276 "test_class": TestRS,
284 "test_class": TestRS,
292 "test_class": TestRS,
300 "test_class": TestRS,
308 "test_class": TestRS,
316 "test_class": TestRS,
324 "test_class": TestRS,
332 "test_class": TestRS,
337 'fio_path': fio_path,
338 'fio_root': str(Path(__file__).absolute().parent.parent),
339 'artifact_root': artifact_root,
340 'basename': 'random',
343 _, failed, _ = run_fio_tests(test_list, test_env, args)
347 if __name__ == '__main__':