Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # Copyright (c) 2015-2016 The Khronos Group Inc. |
| 3 | # Copyright (c) 2015-2016 Valve Corporation |
| 4 | # Copyright (c) 2015-2016 LunarG, Inc. |
| 5 | # Copyright (c) 2015-2016 Google Inc. |
| 6 | # |
| 7 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 8 | # you may not use this file except in compliance with the License. |
| 9 | # You may obtain a copy of the License at |
| 10 | # |
| 11 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 12 | # |
| 13 | # Unless required by applicable law or agreed to in writing, software |
| 14 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 16 | # See the License for the specific language governing permissions and |
| 17 | # limitations under the License. |
| 18 | # |
| 19 | # Author: Tobin Ehlis <tobine@google.com> |
| 20 | |
| 21 | import argparse |
| 22 | import os |
| 23 | import sys |
| 24 | import platform |
| 25 | |
| 26 | # vk_validation_stats.py overview |
| 27 | # This script is intended to generate statistics on the state of validation code |
| 28 | # based on information parsed from the source files and the database file |
| 29 | # Here's what it currently does: |
| 30 | # 1. Parse vk_validation_error_database.txt to store claimed state of validation checks |
| 31 | # 2. Parse vk_validation_error_messages.h to verify the actual checks in header vs. the |
| 32 | # claimed state of the checks |
| 33 | # 3. Parse source files to identify which checks are implemented and verify that this |
| 34 | # exactly matches the list of checks claimed to be implemented in the database |
| 35 | # 4. Parse test file(s) and verify that reported tests exist |
| 36 | # 5. Report out stats on number of checks, implemented checks, and duplicated checks |
| 37 | # |
Tobin Ehlis | 20e3258 | 2016-12-05 14:50:03 -0700 | [diff] [blame] | 38 | # If a mis-match is found during steps 2, 3, or 4, then the script exits w/ a non-zero error code |
| 39 | # otherwise, the script will exit(0) |
| 40 | # |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 41 | # TODO: |
| 42 | # 1. Would also like to report out number of existing checks that don't yet use new, unique enum |
| 43 | # 2. Could use notes to store custom fields (like TODO) and print those out here |
| 44 | # 3. Update test code to check if tests use new, unique enums to check for errors instead of strings |
| 45 | |
| 46 | db_file = 'vk_validation_error_database.txt' |
| 47 | layer_source_files = [ |
| 48 | 'core_validation.cpp', |
| 49 | 'descriptor_sets.cpp', |
| 50 | 'parameter_validation.cpp', |
| 51 | 'object_tracker.cpp', |
Mike Weiblen | 6a27de5 | 2016-12-09 17:36:28 -0700 | [diff] [blame] | 52 | 'image.cpp', |
| 53 | 'swapchain.cpp' |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 54 | ] |
| 55 | header_file = 'vk_validation_error_messages.h' |
| 56 | # TODO : Don't hardcode linux path format if we want this to run on windows |
| 57 | test_file = '../tests/layer_validation_tests.cpp' |
Tobin Ehlis | 225b59c | 2016-12-22 13:59:42 -0700 | [diff] [blame] | 58 | # List of enums that are allowed to be used more than once so don't warn on their duplicates |
| 59 | duplicate_exceptions = [ |
Tobin Ehlis | 1fd9eaf | 2016-12-22 14:31:01 -0700 | [diff] [blame] | 60 | 'VALIDATION_ERROR_00018', # This covers the broad case that all child objects must be destroyed at DestroyInstance time |
| 61 | 'VALIDATION_ERROR_00049', # This covers the broad case that all child objects must be destroyed at DestroyDevice time |
Tobin Ehlis | e664a2b | 2016-12-22 14:10:21 -0700 | [diff] [blame] | 62 | 'VALIDATION_ERROR_00648', # This is a case for VkMappedMemoryRange struct that is used by both Flush & Invalidate MappedMemoryRange |
Tobin Ehlis | 1fd9eaf | 2016-12-22 14:31:01 -0700 | [diff] [blame] | 63 | 'VALIDATION_ERROR_00741', # This is a blanket case for all invalid image aspect bit errors. The spec link has appropriate details for all separate cases. |
| 64 | 'VALIDATION_ERROR_00768', # This case covers two separate checks which are done independently |
| 65 | 'VALIDATION_ERROR_00769', # This case covers two separate checks which are done independently |
| 66 | 'VALIDATION_ERROR_00942', # This is a descriptor set write update error that we use for a couple copy cases as well |
| 67 | 'VALIDATION_ERROR_02525', # Used twice for the same error codepath as both a param & to set a variable, so not really a duplicate |
Tobin Ehlis | 225b59c | 2016-12-22 13:59:42 -0700 | [diff] [blame] | 68 | ] |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 69 | |
| 70 | class ValidationDatabase: |
| 71 | def __init__(self, filename=db_file): |
| 72 | self.db_file = filename |
| 73 | self.delimiter = '~^~' |
| 74 | self.db_dict = {} # complete dict of all db values per error enum |
| 75 | # specialized data structs with slices of complete dict |
| 76 | self.db_implemented_enums = [] # list of all error enums claiming to be implemented in database file |
| 77 | self.db_enum_to_tests = {} # dict where enum is key to lookup list of tests implementing the enum |
| 78 | #self.src_implemented_enums |
| 79 | def read(self): |
| 80 | """Read a database file into internal data structures, format of each line is <enum><implemented Y|N?><testname><api><errormsg><notes>""" |
| 81 | #db_dict = {} # This is a simple db of just enum->errormsg, the same as is created from spec |
| 82 | #max_id = 0 |
| 83 | with open(self.db_file, "r") as infile: |
| 84 | for line in infile: |
| 85 | line = line.strip() |
| 86 | if line.startswith('#') or '' == line: |
| 87 | continue |
| 88 | db_line = line.split(self.delimiter) |
| 89 | if len(db_line) != 6: |
Tobin Ehlis | 027f321 | 2016-12-09 12:15:26 -0700 | [diff] [blame] | 90 | print("ERROR: Bad database line doesn't have 6 elements: %s" % (line)) |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 91 | error_enum = db_line[0] |
| 92 | implemented = db_line[1] |
| 93 | testname = db_line[2] |
| 94 | api = db_line[3] |
| 95 | error_str = db_line[4] |
| 96 | note = db_line[5] |
| 97 | # Read complete database contents into our class var for later use |
| 98 | self.db_dict[error_enum] = {} |
| 99 | self.db_dict[error_enum]['check_implemented'] = implemented |
| 100 | self.db_dict[error_enum]['testname'] = testname |
| 101 | self.db_dict[error_enum]['api'] = api |
| 102 | self.db_dict[error_enum]['error_string'] = error_str |
| 103 | self.db_dict[error_enum]['note'] = note |
| 104 | # Now build custom data structs |
| 105 | if 'Y' == implemented: |
| 106 | self.db_implemented_enums.append(error_enum) |
| 107 | if testname.lower() not in ['unknown', 'none']: |
| 108 | self.db_enum_to_tests[error_enum] = testname.split(',') |
| 109 | #if len(self.db_enum_to_tests[error_enum]) > 1: |
| 110 | # print "Found check %s that has multiple tests: %s" % (error_enum, self.db_enum_to_tests[error_enum]) |
| 111 | #else: |
| 112 | # print "Check %s has single test: %s" % (error_enum, self.db_enum_to_tests[error_enum]) |
| 113 | #unique_id = int(db_line[0].split('_')[-1]) |
| 114 | #if unique_id > max_id: |
| 115 | # max_id = unique_id |
| 116 | #print "Found %d total enums in database" % (len(self.db_dict.keys())) |
| 117 | #print "Found %d enums claiming to be implemented in source" % (len(self.db_implemented_enums)) |
| 118 | #print "Found %d enums claiming to have tests implemented" % (len(self.db_enum_to_tests.keys())) |
| 119 | |
| 120 | class ValidationHeader: |
| 121 | def __init__(self, filename=header_file): |
| 122 | self.filename = header_file |
| 123 | self.enums = [] |
| 124 | def read(self): |
| 125 | """Read unique error enum header file into internal data structures""" |
| 126 | grab_enums = False |
| 127 | with open(self.filename, "r") as infile: |
| 128 | for line in infile: |
| 129 | line = line.strip() |
| 130 | if 'enum UNIQUE_VALIDATION_ERROR_CODE {' in line: |
| 131 | grab_enums = True |
| 132 | continue |
| 133 | if grab_enums: |
| 134 | if 'VALIDATION_ERROR_MAX_ENUM' in line: |
| 135 | grab_enums = False |
| 136 | break # done |
Tobin Ehlis | f53eac3 | 2016-12-09 14:10:47 -0700 | [diff] [blame] | 137 | elif 'VALIDATION_ERROR_UNDEFINED' in line: |
| 138 | continue |
| 139 | elif 'VALIDATION_ERROR_' in line: |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 140 | enum = line.split(' = ')[0] |
| 141 | self.enums.append(enum) |
| 142 | #print "Found %d error enums. First is %s and last is %s." % (len(self.enums), self.enums[0], self.enums[-1]) |
| 143 | |
| 144 | class ValidationSource: |
| 145 | def __init__(self, source_file_list): |
| 146 | self.source_files = source_file_list |
Tobin Ehlis | 3d1f2bd | 2016-12-22 11:19:15 -0700 | [diff] [blame] | 147 | self.enum_count_dict = {} # dict of enum values to the count of how much they're used, and location of where they're used |
Tobin Ehlis | 3d9dd94 | 2016-11-23 13:08:01 -0700 | [diff] [blame] | 148 | # 1790 is a special case that provides an exception when an extension is enabled. No specific error is flagged, but the exception is handled so add it here |
Tobin Ehlis | 3d1f2bd | 2016-12-22 11:19:15 -0700 | [diff] [blame] | 149 | self.enum_count_dict['VALIDATION_ERROR_01790'] = {} |
| 150 | self.enum_count_dict['VALIDATION_ERROR_01790']['count'] = 1 |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 151 | def parse(self): |
| 152 | duplicate_checks = 0 |
| 153 | for sf in self.source_files: |
Tobin Ehlis | 3d1f2bd | 2016-12-22 11:19:15 -0700 | [diff] [blame] | 154 | line_num = 0 |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 155 | with open(sf) as f: |
| 156 | for line in f: |
Tobin Ehlis | 3d1f2bd | 2016-12-22 11:19:15 -0700 | [diff] [blame] | 157 | line_num = line_num + 1 |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 158 | if True in [line.strip().startswith(comment) for comment in ['//', '/*']]: |
| 159 | continue |
| 160 | # Find enums |
| 161 | #if 'VALIDATION_ERROR_' in line and True not in [ignore in line for ignore in ['[VALIDATION_ERROR_', 'UNIQUE_VALIDATION_ERROR_CODE']]: |
Tobin Ehlis | f53eac3 | 2016-12-09 14:10:47 -0700 | [diff] [blame] | 162 | if ' VALIDATION_ERROR_' in line: |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 163 | # Need to isolate the validation error enum |
| 164 | #print("Line has check:%s" % (line)) |
| 165 | line_list = line.split() |
Tobin Ehlis | 928742e | 2016-12-09 17:11:13 -0700 | [diff] [blame] | 166 | enum_list = [] |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 167 | for str in line_list: |
Tobin Ehlis | f53eac3 | 2016-12-09 14:10:47 -0700 | [diff] [blame] | 168 | if 'VALIDATION_ERROR_' in str and True not in [ignore_str in str for ignore_str in ['[VALIDATION_ERROR_', 'VALIDATION_ERROR_UNDEFINED', 'UNIQUE_VALIDATION_ERROR_CODE']]: |
Tobin Ehlis | 928742e | 2016-12-09 17:11:13 -0700 | [diff] [blame] | 169 | enum_list.append(str.strip(',);')) |
| 170 | #break |
| 171 | for enum in enum_list: |
| 172 | if enum != '': |
| 173 | if enum not in self.enum_count_dict: |
Tobin Ehlis | 3d1f2bd | 2016-12-22 11:19:15 -0700 | [diff] [blame] | 174 | self.enum_count_dict[enum] = {} |
| 175 | self.enum_count_dict[enum]['count'] = 1 |
| 176 | self.enum_count_dict[enum]['file_line'] = [] |
| 177 | self.enum_count_dict[enum]['file_line'].append('%s,%d' % (sf, line_num)) |
Tobin Ehlis | 928742e | 2016-12-09 17:11:13 -0700 | [diff] [blame] | 178 | #print "Found enum %s implemented for first time in file %s" % (enum, sf) |
| 179 | else: |
Tobin Ehlis | 3d1f2bd | 2016-12-22 11:19:15 -0700 | [diff] [blame] | 180 | self.enum_count_dict[enum]['count'] = self.enum_count_dict[enum]['count'] + 1 |
| 181 | self.enum_count_dict[enum]['file_line'].append('%s,%d' % (sf, line_num)) |
Tobin Ehlis | 928742e | 2016-12-09 17:11:13 -0700 | [diff] [blame] | 182 | #print "Found enum %s implemented for %d time in file %s" % (enum, self.enum_count_dict[enum], sf) |
| 183 | duplicate_checks = duplicate_checks + 1 |
| 184 | #else: |
| 185 | #print("Didn't find actual check in line:%s" % (line)) |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 186 | #print "Found %d unique implemented checks and %d are duplicated at least once" % (len(self.enum_count_dict.keys()), duplicate_checks) |
| 187 | |
| 188 | # Class to parse the validation layer test source and store testnames |
| 189 | # TODO: Enhance class to detect use of unique error enums in the test |
| 190 | class TestParser: |
| 191 | def __init__(self, test_file_list, test_group_name=['VkLayerTest', 'VkPositiveLayerTest', 'VkWsiEnabledLayerTest']): |
| 192 | self.test_files = test_file_list |
Tobin Ehlis | 9a68c98 | 2016-12-29 14:51:17 -0700 | [diff] [blame^] | 193 | self.test_to_errors = {} # Dict where testname maps to list of error enums found in that test |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 194 | self.test_trigger_txt_list = [] |
| 195 | for tg in test_group_name: |
| 196 | self.test_trigger_txt_list.append('TEST_F(%s' % tg) |
| 197 | #print('Test trigger test list: %s' % (self.test_trigger_txt_list)) |
| 198 | |
| 199 | # Parse test files into internal data struct |
| 200 | def parse(self): |
| 201 | # For each test file, parse test names into set |
| 202 | grab_next_line = False # handle testname on separate line than wildcard |
Tobin Ehlis | 9a68c98 | 2016-12-29 14:51:17 -0700 | [diff] [blame^] | 203 | testname = '' |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 204 | for test_file in self.test_files: |
| 205 | with open(test_file) as tf: |
| 206 | for line in tf: |
| 207 | if True in [line.strip().startswith(comment) for comment in ['//', '/*']]: |
| 208 | continue |
| 209 | |
| 210 | if True in [ttt in line for ttt in self.test_trigger_txt_list]: |
| 211 | #print('Test wildcard in line: %s' % (line)) |
| 212 | testname = line.split(',')[-1] |
| 213 | testname = testname.strip().strip(' {)') |
| 214 | #print('Inserting test: "%s"' % (testname)) |
| 215 | if ('' == testname): |
| 216 | grab_next_line = True |
| 217 | continue |
Tobin Ehlis | 9a68c98 | 2016-12-29 14:51:17 -0700 | [diff] [blame^] | 218 | self.test_to_errors[testname] = [] |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 219 | if grab_next_line: # test name on its own line |
| 220 | grab_next_line = False |
| 221 | testname = testname.strip().strip(' {)') |
Tobin Ehlis | 9a68c98 | 2016-12-29 14:51:17 -0700 | [diff] [blame^] | 222 | self.test_to_errors[testname] = [] |
| 223 | if ' VALIDATION_ERROR_' in line: |
| 224 | line_list = line.split() |
| 225 | for str in line_list: |
| 226 | if 'VALIDATION_ERROR_' in str and True not in [ignore_str in str for ignore_str in ['VALIDATION_ERROR_UNDEFINED', 'UNIQUE_VALIDATION_ERROR_CODE', 'VALIDATION_ERROR_MAX_ENUM']]: |
| 227 | print("Trying to add enums for line: %s") |
| 228 | print("Adding enum %s to test %s" % (str.strip(',);'), testname)) |
| 229 | self.test_to_errors[testname].append(str.strip(',);')) |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 230 | |
| 231 | # Little helper class for coloring cmd line output |
| 232 | class bcolors: |
| 233 | |
| 234 | def __init__(self): |
| 235 | self.GREEN = '\033[0;32m' |
| 236 | self.RED = '\033[0;31m' |
| 237 | self.YELLOW = '\033[1;33m' |
| 238 | self.ENDC = '\033[0m' |
| 239 | if 'Linux' != platform.system(): |
| 240 | self.GREEN = '' |
| 241 | self.RED = '' |
| 242 | self.YELLOW = '' |
| 243 | self.ENDC = '' |
| 244 | |
| 245 | def green(self): |
| 246 | return self.GREEN |
| 247 | |
| 248 | def red(self): |
| 249 | return self.RED |
| 250 | |
| 251 | def yellow(self): |
| 252 | return self.YELLOW |
| 253 | |
| 254 | def endc(self): |
| 255 | return self.ENDC |
| 256 | |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 257 | def main(argv=None): |
Tobin Ehlis | 20e3258 | 2016-12-05 14:50:03 -0700 | [diff] [blame] | 258 | result = 0 # Non-zero result indicates an error case |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 259 | # parse db |
| 260 | val_db = ValidationDatabase() |
| 261 | val_db.read() |
| 262 | # parse header |
| 263 | val_header = ValidationHeader() |
| 264 | val_header.read() |
| 265 | # Create parser for layer files |
| 266 | val_source = ValidationSource(layer_source_files) |
| 267 | val_source.parse() |
| 268 | # Parse test files |
| 269 | test_parser = TestParser([test_file, ]) |
| 270 | test_parser.parse() |
| 271 | |
| 272 | # Process stats - Just doing this inline in main, could make a fancy class to handle |
| 273 | # all the processing of data and then get results from that |
| 274 | txt_color = bcolors() |
| 275 | print("Validation Statistics") |
| 276 | # First give number of checks in db & header and report any discrepancies |
| 277 | db_enums = len(val_db.db_dict.keys()) |
| 278 | hdr_enums = len(val_header.enums) |
| 279 | print(" Database file includes %d unique checks" % (db_enums)) |
| 280 | print(" Header file declares %d unique checks" % (hdr_enums)) |
| 281 | tmp_db_dict = val_db.db_dict |
| 282 | db_missing = [] |
| 283 | for enum in val_header.enums: |
| 284 | if not tmp_db_dict.pop(enum, False): |
| 285 | db_missing.append(enum) |
| 286 | if db_enums == hdr_enums and len(db_missing) == 0 and len(tmp_db_dict.keys()) == 0: |
| 287 | print(txt_color.green() + " Database and Header match, GREAT!" + txt_color.endc()) |
| 288 | else: |
| 289 | print(txt_color.red() + " Uh oh, Database doesn't match Header :(" + txt_color.endc()) |
Tobin Ehlis | 20e3258 | 2016-12-05 14:50:03 -0700 | [diff] [blame] | 290 | result = 1 |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 291 | if len(db_missing) != 0: |
| 292 | print(txt_color.red() + " The following checks are in header but missing from database:" + txt_color.endc()) |
| 293 | for missing_enum in db_missing: |
| 294 | print(txt_color.red() + " %s" % (missing_enum) + txt_color.endc()) |
| 295 | if len(tmp_db_dict.keys()) != 0: |
| 296 | print(txt_color.red() + " The following checks are in database but haven't been declared in the header:" + txt_color.endc()) |
| 297 | for extra_enum in tmp_db_dict: |
| 298 | print(txt_color.red() + " %s" % (extra_enum) + txt_color.endc()) |
| 299 | # Report out claimed implemented checks vs. found actual implemented checks |
| 300 | imp_not_found = [] # Checks claimed to implemented in DB file but no source found |
| 301 | imp_not_claimed = [] # Checks found implemented but not claimed to be in DB |
| 302 | multiple_uses = False # Flag if any enums are used multiple times |
| 303 | for db_imp in val_db.db_implemented_enums: |
| 304 | if db_imp not in val_source.enum_count_dict: |
| 305 | imp_not_found.append(db_imp) |
| 306 | for src_enum in val_source.enum_count_dict: |
Tobin Ehlis | 225b59c | 2016-12-22 13:59:42 -0700 | [diff] [blame] | 307 | if val_source.enum_count_dict[src_enum]['count'] > 1 and src_enum not in duplicate_exceptions: |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 308 | multiple_uses = True |
| 309 | if src_enum not in val_db.db_implemented_enums: |
| 310 | imp_not_claimed.append(src_enum) |
| 311 | print(" Database file claims that %d checks (%s) are implemented in source." % (len(val_db.db_implemented_enums), "{0:.0f}%".format(float(len(val_db.db_implemented_enums))/db_enums * 100))) |
| 312 | if len(imp_not_found) == 0 and len(imp_not_claimed) == 0: |
| 313 | print(txt_color.green() + " All claimed Database implemented checks have been found in source, and no source checks aren't claimed in Database, GREAT!" + txt_color.endc()) |
| 314 | else: |
Tobin Ehlis | 20e3258 | 2016-12-05 14:50:03 -0700 | [diff] [blame] | 315 | result = 1 |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 316 | print(txt_color.red() + " Uh oh, Database claimed implemented don't match Source :(" + txt_color.endc()) |
| 317 | if len(imp_not_found) != 0: |
Tobin Ehlis | 3f0b277 | 2016-11-18 16:56:15 -0700 | [diff] [blame] | 318 | print(txt_color.red() + " The following %d checks are claimed to be implemented in Database, but weren't found in source:" % (len(imp_not_found)) + txt_color.endc()) |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 319 | for not_imp_enum in imp_not_found: |
| 320 | print(txt_color.red() + " %s" % (not_imp_enum) + txt_color.endc()) |
| 321 | if len(imp_not_claimed) != 0: |
| 322 | print(txt_color.red() + " The following checks are implemented in source, but not claimed to be in Database:" + txt_color.endc()) |
| 323 | for imp_enum in imp_not_claimed: |
| 324 | print(txt_color.red() + " %s" % (imp_enum) + txt_color.endc()) |
| 325 | if multiple_uses: |
| 326 | print(txt_color.yellow() + " Note that some checks are used multiple times. These may be good candidates for new valid usage spec language." + txt_color.endc()) |
| 327 | print(txt_color.yellow() + " Here is a list of each check used multiple times with its number of uses:" + txt_color.endc()) |
| 328 | for enum in val_source.enum_count_dict: |
Tobin Ehlis | 225b59c | 2016-12-22 13:59:42 -0700 | [diff] [blame] | 329 | if val_source.enum_count_dict[enum]['count'] > 1 and enum not in duplicate_exceptions: |
Tobin Ehlis | 3d1f2bd | 2016-12-22 11:19:15 -0700 | [diff] [blame] | 330 | print(txt_color.yellow() + " %s: %d uses in file,line:" % (enum, val_source.enum_count_dict[enum]['count']) + txt_color.endc()) |
| 331 | for file_line in val_source.enum_count_dict[enum]['file_line']: |
| 332 | print(txt_color.yellow() + " \t%s" % (file_line) + txt_color.endc()) |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 333 | # Now check that tests claimed to be implemented are actual test names |
| 334 | bad_testnames = [] |
Tobin Ehlis | 9a68c98 | 2016-12-29 14:51:17 -0700 | [diff] [blame^] | 335 | tests_missing_enum = {} # Report tests that don't use validation error enum to check for error case |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 336 | for enum in val_db.db_enum_to_tests: |
| 337 | for testname in val_db.db_enum_to_tests[enum]: |
Tobin Ehlis | 9a68c98 | 2016-12-29 14:51:17 -0700 | [diff] [blame^] | 338 | if testname not in test_parser.test_to_errors: |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 339 | bad_testnames.append(testname) |
Tobin Ehlis | 9a68c98 | 2016-12-29 14:51:17 -0700 | [diff] [blame^] | 340 | else: |
| 341 | enum_found = False |
| 342 | for test_enum in test_parser.test_to_errors[testname]: |
| 343 | if test_enum == enum: |
| 344 | #print("Found test that correctly checks for enum: %s" % (enum)) |
| 345 | enum_found = True |
| 346 | if not enum_found: |
| 347 | #print("Test %s is not using enum %s to check for error" % (testname, enum)) |
| 348 | if testname not in tests_missing_enum: |
| 349 | tests_missing_enum[testname] = [] |
| 350 | tests_missing_enum[testname].append(enum) |
| 351 | if tests_missing_enum: |
| 352 | print(txt_color.yellow() + " \nThe following tests do not use their reported enums to check for the validation error. You may want to update these to pass the expected enum to SetDesiredFailureMsg:" + txt_color.endc()) |
| 353 | for testname in tests_missing_enum: |
| 354 | print(txt_color.yellow() + " Testname %s does not explicitly check for these ids:" % (testname) + txt_color.endc()) |
| 355 | for enum in tests_missing_enum[testname]: |
| 356 | print(txt_color.yellow() + " %s" % (enum) + txt_color.endc()) |
| 357 | # TODO : Go through all enums found in the test file and make sure they're correctly documented in the database file |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 358 | print(" Database file claims that %d checks have tests written." % len(val_db.db_enum_to_tests)) |
| 359 | if len(bad_testnames) == 0: |
| 360 | print(txt_color.green() + " All claimed tests have valid names. That's good!" + txt_color.endc()) |
| 361 | else: |
| 362 | print(txt_color.red() + " The following testnames in Database appear to be invalid:") |
Tobin Ehlis | 20e3258 | 2016-12-05 14:50:03 -0700 | [diff] [blame] | 363 | result = 1 |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 364 | for bt in bad_testnames: |
Tobin Ehlis | b04c2c6 | 2016-11-21 15:51:45 -0700 | [diff] [blame] | 365 | print(txt_color.red() + " %s" % (bt) + txt_color.endc()) |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 366 | |
Tobin Ehlis | 20e3258 | 2016-12-05 14:50:03 -0700 | [diff] [blame] | 367 | return result |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 368 | |
| 369 | if __name__ == "__main__": |
| 370 | sys.exit(main()) |
| 371 | |