X-Git-Url: https://git.kernel.dk/?a=blobdiff_plain;f=t%2Frun-fio-tests.py;h=c3091b68542dd952673d16e7aaac3457052b306f;hb=7d7a704638a1e957c845c04eeac82bdeda0c674c;hp=f183668aa8bb01480915237cc405e13c7a95e8cb;hpb=9e83aa16efc28e1d20e37f3da87411aeb7644254;p=fio.git diff --git a/t/run-fio-tests.py b/t/run-fio-tests.py index f183668a..c3091b68 100755 --- a/t/run-fio-tests.py +++ b/t/run-fio-tests.py @@ -55,7 +55,7 @@ import multiprocessing from pathlib import Path -class FioTest(object): +class FioTest(): """Base for all fio tests.""" def __init__(self, exe_path, parameters, success): @@ -286,6 +286,19 @@ class FioJobTest(FioExeTest): return file_data, success + def get_file_fail(self, filename): + """Safely read a file and fail the test upon error.""" + file_data = None + + try: + with open(filename, "r") as output_file: + file_data = output_file.read() + except OSError: + self.failure_reason += " unable to read file {0}".format(filename) + self.passed = False + + return file_data + def check_result(self): """Check fio job results.""" @@ -302,10 +315,8 @@ class FioJobTest(FioExeTest): if 'json' not in self.output_format: return - file_data, success = self.get_file(os.path.join(self.test_dir, self.fio_output)) - if not success: - self.failure_reason = "{0} unable to open output file,".format(self.failure_reason) - self.passed = False + file_data = self.get_file_fail(os.path.join(self.test_dir, self.fio_output)) + if not file_data: return # @@ -376,10 +387,13 @@ class FioJobTest_t0007(FioJobTest): class FioJobTest_t0008(FioJobTest): """Test consists of fio test job t0008 Confirm that read['io_kbytes'] = 32768 and that - write['io_kbytes'] ~ 16568 + write['io_kbytes'] ~ 16384 - I did runs with fio-ae2fafc8 and saw write['io_kbytes'] values of - 16585, 16588. With two runs of fio-3.16 I obtained 16568""" + This is a 50/50 seq read/write workload. Since fio flips a coin to + determine whether to issue a read or a write, total bytes written will not + be exactly 16384K. But total bytes read will be exactly 32768K because + reads will include the initial phase as well as the verify phase where all + the blocks originally written will be read.""" def check_result(self): super(FioJobTest_t0008, self).check_result() @@ -387,10 +401,10 @@ class FioJobTest_t0008(FioJobTest): if not self.passed: return - ratio = self.json_data['jobs'][0]['write']['io_kbytes'] / 16568 + ratio = self.json_data['jobs'][0]['write']['io_kbytes'] / 16384 logging.debug("Test %d: ratio: %f", self.testnum, ratio) - if ratio < 0.99 or ratio > 1.01: + if ratio < 0.97 or ratio > 1.03: self.failure_reason = "{0} bytes written mismatch,".format(self.failure_reason) self.passed = False if self.json_data['jobs'][0]['read']['io_kbytes'] != 32768: @@ -427,12 +441,11 @@ class FioJobTest_t0012(FioJobTest): return iops_files = [] - for i in range(1,4): - file_data, success = self.get_file(os.path.join(self.test_dir, "{0}_iops.{1}.log".format(os.path.basename(self.fio_job), i))) - - if not success: - self.failure_reason = "{0} unable to open output file,".format(self.failure_reason) - self.passed = False + for i in range(1, 4): + filename = os.path.join(self.test_dir, "{0}_iops.{1}.log".format(os.path.basename( + self.fio_job), i)) + file_data = self.get_file_fail(filename) + if not file_data: return iops_files.append(file_data.splitlines()) @@ -448,17 +461,15 @@ class FioJobTest_t0012(FioJobTest): ratio1 = iops3/iops2 ratio2 = iops3/iops1 - logging.debug( - "sample {0}: job1 iops={1} job2 iops={2} job3 iops={3} job3/job2={4:.3f} job3/job1={5:.3f}".format( - i, iops1, iops2, iops3, ratio1, ratio2 - ) - ) + logging.debug("sample {0}: job1 iops={1} job2 iops={2} job3 iops={3} " \ + "job3/job2={4:.3f} job3/job1={5:.3f}".format(i, iops1, iops2, iops3, ratio1, + ratio2)) # test job1 and job2 succeeded to recalibrate if ratio1 < 1 or ratio1 > 3 or ratio2 < 7 or ratio2 > 13: - self.failure_reason = "{0} iops ratio mismatch iops1={1} iops2={2} iops3={3} expected r1~2 r2~10 got r1={4:.3f} r2={5:.3f},".format( - self.failure_reason, iops1, iops2, iops3, ratio1, ratio2 - ) + self.failure_reason += " iops ratio mismatch iops1={0} iops2={1} iops3={2} " \ + "expected r1~2 r2~10 got r1={3:.3f} r2={4:.3f},".format(iops1, iops2, iops3, + ratio1, ratio2) self.passed = False return @@ -478,12 +489,11 @@ class FioJobTest_t0014(FioJobTest): return iops_files = [] - for i in range(1,4): - file_data, success = self.get_file(os.path.join(self.test_dir, "{0}_iops.{1}.log".format(os.path.basename(self.fio_job), i))) - - if not success: - self.failure_reason = "{0} unable to open output file,".format(self.failure_reason) - self.passed = False + for i in range(1, 4): + filename = os.path.join(self.test_dir, "{0}_iops.{1}.log".format(os.path.basename( + self.fio_job), i)) + file_data = self.get_file_fail(filename) + if not file_data: return iops_files.append(file_data.splitlines()) @@ -501,10 +511,9 @@ class FioJobTest_t0014(FioJobTest): if ratio1 < 0.43 or ratio1 > 0.57 or ratio2 < 0.21 or ratio2 > 0.45: - self.failure_reason = "{0} iops ratio mismatch iops1={1} iops2={2} iops3={3}\ - expected r1~0.5 r2~0.33 got r1={4:.3f} r2={5:.3f},".format( - self.failure_reason, iops1, iops2, iops3, ratio1, ratio2 - ) + self.failure_reason += " iops ratio mismatch iops1={0} iops2={1} iops3={2} " \ + "expected r1~0.5 r2~0.33 got r1={3:.3f} r2={4:.3f},".format( + iops1, iops2, iops3, ratio1, ratio2) self.passed = False iops1 = iops1 + float(iops_files[0][i].split(',')[1]) @@ -512,17 +521,14 @@ class FioJobTest_t0014(FioJobTest): ratio1 = iops1/iops2 ratio2 = iops1/iops3 - logging.debug( - "sample {0}: job1 iops={1} job2 iops={2} job3 iops={3} job1/job2={4:.3f} job1/job3={5:.3f}".format( - i, iops1, iops2, iops3, ratio1, ratio2 - ) - ) + logging.debug("sample {0}: job1 iops={1} job2 iops={2} job3 iops={3} " \ + "job1/job2={4:.3f} job1/job3={5:.3f}".format(i, iops1, iops2, iops3, + ratio1, ratio2)) # test job1 and job2 succeeded to recalibrate if ratio1 < 0.43 or ratio1 > 0.57: - self.failure_reason = "{0} iops ratio mismatch iops1={1} iops2={2} expected ratio~0.5 got ratio={3:.3f},".format( - self.failure_reason, iops1, iops2, ratio1 - ) + self.failure_reason += " iops ratio mismatch iops1={0} iops2={1} expected ratio~0.5 " \ + "got ratio={2:.3f},".format(iops1, iops2, ratio1) self.passed = False return @@ -556,7 +562,10 @@ class FioJobTest_t0019(FioJobTest): super(FioJobTest_t0019, self).check_result() bw_log_filename = os.path.join(self.test_dir, "test_bw.log") - file_data, success = self.get_file(bw_log_filename) + file_data = self.get_file_fail(bw_log_filename) + if not file_data: + return + log_lines = file_data.split('\n') prev = -4096 @@ -583,7 +592,10 @@ class FioJobTest_t0020(FioJobTest): super(FioJobTest_t0020, self).check_result() bw_log_filename = os.path.join(self.test_dir, "test_bw.log") - file_data, success = self.get_file(bw_log_filename) + file_data = self.get_file_fail(bw_log_filename) + if not file_data: + return + log_lines = file_data.split('\n') seq_count = 0 @@ -621,7 +633,10 @@ class FioJobTest_t0022(FioJobTest): super(FioJobTest_t0022, self).check_result() bw_log_filename = os.path.join(self.test_dir, "test_bw.log") - file_data, success = self.get_file(bw_log_filename) + file_data = self.get_file_fail(bw_log_filename) + if not file_data: + return + log_lines = file_data.split('\n') filesize = 1024*1024 @@ -646,17 +661,20 @@ class FioJobTest_t0022(FioJobTest): if len(offsets) == filesize/bs: self.passed = False - self.failure_reason += " no duplicate offsets found with norandommap=1".format(len(offsets)) + self.failure_reason += " no duplicate offsets found with norandommap=1" class FioJobTest_t0023(FioJobTest): - """Test consists of fio test job t0023""" + """Test consists of fio test job t0023 randtrimwrite test.""" def check_trimwrite(self, filename): """Make sure that trims are followed by writes of the same size at the same offset.""" bw_log_filename = os.path.join(self.test_dir, filename) - file_data, success = self.get_file(bw_log_filename) + file_data = self.get_file_fail(bw_log_filename) + if not file_data: + return + log_lines = file_data.split('\n') prev_ddir = 1 @@ -670,21 +688,25 @@ class FioJobTest_t0023(FioJobTest): if prev_ddir == 1: if ddir != 2: self.passed = False - self.failure_reason += " {0}: write not preceeded by trim: {1}".format(bw_log_filename, line) + self.failure_reason += " {0}: write not preceeded by trim: {1}".format( + bw_log_filename, line) break else: if ddir != 1: self.passed = False - self.failure_reason += " {0}: trim not preceeded by write: {1}".format(bw_log_filename, line) + self.failure_reason += " {0}: trim not preceeded by write: {1}".format( + bw_log_filename, line) break else: if prev_bs != bs: self.passed = False - self.failure_reason += " {0}: block size does not match: {1}".format(bw_log_filename, line) + self.failure_reason += " {0}: block size does not match: {1}".format( + bw_log_filename, line) break if prev_offset != offset: self.passed = False - self.failure_reason += " {0}: offset does not match: {1}".format(bw_log_filename, line) + self.failure_reason += " {0}: offset does not match: {1}".format( + bw_log_filename, line) break prev_ddir = ddir prev_bs = bs @@ -692,10 +714,10 @@ class FioJobTest_t0023(FioJobTest): def check_all_offsets(self, filename, sectorsize, filesize): - file_data, success = self.get_file(os.path.join(self.test_dir, filename)) - if not success: - self.passed = False - self.failure_reason = " could not open {0}".format(filename) + """Make sure all offsets were touched.""" + + file_data = self.get_file_fail(os.path.join(self.test_dir, filename)) + if not file_data: return log_lines = file_data.split('\n') @@ -710,20 +732,23 @@ class FioJobTest_t0023(FioJobTest): offset = int(vals[4]) if offset % sectorsize != 0: self.passed = False - self.failure_reason += " {0}: offset {1} not a multiple of sector size {2}".format(filename, offset, sectorsize) - break; + self.failure_reason += " {0}: offset {1} not a multiple of sector size {2}".format( + filename, offset, sectorsize) + break if bs % sectorsize != 0: self.passed = False - self.failure_reason += " {0}: block size {1} not a multiple of sector size {2}".format(filename, bs, sectorsize) - break; + self.failure_reason += " {0}: block size {1} not a multiple of sector size " \ + "{2}".format(filename, bs, sectorsize) + break for i in range(int(bs/sectorsize)): offsets.add(offset/sectorsize + i) if len(offsets) != filesize/sectorsize: self.passed = False - self.failure_reason += " {0}: only {1} offsets touched; expected {2}".format(filename, len(offsets), filesize/sectorsize) + self.failure_reason += " {0}: only {1} offsets touched; expected {2}".format( + filename, len(offsets), filesize/sectorsize) else: - logging.debug("{0}: {1} sectors touched".format(filename, len(offsets))) + logging.debug("%s: %d sectors touched", filename, len(offsets)) def check_result(self): @@ -746,6 +771,58 @@ class FioJobTest_t0023(FioJobTest): self.check_all_offsets("bssplit_bw.log", 512, filesize) +class FioJobTest_t0024(FioJobTest_t0023): + """Test consists of fio test job t0024 trimwrite test.""" + + def check_result(self): + # call FioJobTest_t0023's parent to skip checks done by t0023 + super(FioJobTest_t0023, self).check_result() + + filesize = 1024*1024 + + self.check_trimwrite("basic_bw.log") + self.check_trimwrite("bs_bw.log") + self.check_trimwrite("bsrange_bw.log") + self.check_trimwrite("bssplit_bw.log") + + self.check_all_offsets("basic_bw.log", 4096, filesize) + self.check_all_offsets("bs_bw.log", 8192, filesize) + self.check_all_offsets("bsrange_bw.log", 512, filesize) + self.check_all_offsets("bssplit_bw.log", 512, filesize) + + +class FioJobTest_t0025(FioJobTest): + """Test experimental verify read backs written data pattern.""" + def check_result(self): + super(FioJobTest_t0025, self).check_result() + + if not self.passed: + return + + if self.json_data['jobs'][0]['read']['io_kbytes'] != 128: + self.passed = False + +class FioJobTest_t0027(FioJobTest): + def setup(self, *args, **kws): + super(FioJobTest_t0027, self).setup(*args, **kws) + self.pattern_file = os.path.join(self.test_dir, "t0027.pattern") + self.output_file = os.path.join(self.test_dir, "t0027file") + self.pattern = os.urandom(16 << 10) + with open(self.pattern_file, "wb") as f: + f.write(self.pattern) + + def check_result(self): + super(FioJobTest_t0027, self).check_result() + + if not self.passed: + return + + with open(self.output_file, "rb") as f: + data = f.read() + + if data != self.pattern: + self.passed = False + class FioJobTest_iops_rate(FioJobTest): """Test consists of fio test job t0009 Confirm that job0 iops == 1000 @@ -774,7 +851,7 @@ class FioJobTest_iops_rate(FioJobTest): self.passed = False -class Requirements(object): +class Requirements(): """Requirements consists of multiple run environment characteristics. These are to determine if a particular test can be run""" @@ -1132,6 +1209,52 @@ TEST_LIST = [ 'pre_success': None, 'requirements': [], }, + { + 'test_id': 24, + 'test_class': FioJobTest_t0024, + 'job': 't0024.fio', + 'success': SUCCESS_DEFAULT, + 'pre_job': None, + 'pre_success': None, + 'requirements': [], + }, + { + 'test_id': 25, + 'test_class': FioJobTest_t0025, + 'job': 't0025.fio', + 'success': SUCCESS_DEFAULT, + 'pre_job': None, + 'pre_success': None, + 'output_format': 'json', + 'requirements': [], + }, + { + 'test_id': 26, + 'test_class': FioJobTest, + 'job': 't0026.fio', + 'success': SUCCESS_DEFAULT, + 'pre_job': None, + 'pre_success': None, + 'requirements': [Requirements.not_windows], + }, + { + 'test_id': 27, + 'test_class': FioJobTest_t0027, + 'job': 't0027.fio', + 'success': SUCCESS_DEFAULT, + 'pre_job': None, + 'pre_success': None, + 'requirements': [], + }, + { + 'test_id': 28, + 'test_class': FioJobTest, + 'job': 't0028-c6cade16.fio', + 'success': SUCCESS_DEFAULT, + 'pre_job': None, + 'pre_success': None, + 'requirements': [], + }, { 'test_id': 1000, 'test_class': FioExeTest,