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 | 8ef54c9 | 2017-01-04 08:11:01 -0700 | [diff] [blame^] | 62 | 'VALIDATION_ERROR_00112', # Obj tracker check makes sure non-null framebuffer is valid & CV check makes sure it's compatible w/ renderpass framebuffer |
| 63 | 'VALIDATION_ERROR_00324', # This is an aliasing error that we report twice, for each of the two allocations that are aliasing |
Tobin Ehlis | 2cb8eb2 | 2017-01-03 14:09:57 -0700 | [diff] [blame] | 64 | 'VALIDATION_ERROR_00515', # Covers valid shader module handle for both Compute & Graphics pipelines |
Tobin Ehlis | e664a2b | 2016-12-22 14:10:21 -0700 | [diff] [blame] | 65 | '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] | 66 | '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. |
| 67 | 'VALIDATION_ERROR_00768', # This case covers two separate checks which are done independently |
| 68 | 'VALIDATION_ERROR_00769', # This case covers two separate checks which are done independently |
| 69 | 'VALIDATION_ERROR_00942', # This is a descriptor set write update error that we use for a couple copy cases as well |
Tobin Ehlis | 2cb8eb2 | 2017-01-03 14:09:57 -0700 | [diff] [blame] | 70 | 'VALIDATION_ERROR_00988', # Single error for mis-matched stageFlags of vkCmdPushConstants() that is flagged for no stage flags & mis-matched flags |
| 71 | 'VALIDATION_ERROR_01088', # Handles both depth/stencil & compressed image errors for vkCmdClearColorImage() |
| 72 | 'VALIDATION_ERROR_01223', # Used for the mipLevel check of both dst & src images on vkCmdCopyImage call |
| 73 | 'VALIDATION_ERROR_01224', # Used for the arraySize check of both dst & src images on vkCmdCopyImage call |
| 74 | 'VALIDATION_ERROR_01450', # Used for both x & y bounds of viewport |
| 75 | 'VALIDATION_ERROR_01489', # Used for both x & y value of scissors to make sure they're not negative |
| 76 | 'VALIDATION_ERROR_01926', # Surface of VkSwapchainCreateInfoKHR must be valid when creating both single or shared swapchains |
Tobin Ehlis | 8ef54c9 | 2017-01-04 08:11:01 -0700 | [diff] [blame^] | 77 | 'VALIDATION_ERROR_01935', # oldSwapchain of VkSwapchainCreateInfoKHR must be valid when creating both single or shared swapchains |
Tobin Ehlis | 2cb8eb2 | 2017-01-03 14:09:57 -0700 | [diff] [blame] | 78 | 'VALIDATION_ERROR_02333', # Single error for both imageFormat & imageColorSpace requirements when creating swapchain |
Tobin Ehlis | 1fd9eaf | 2016-12-22 14:31:01 -0700 | [diff] [blame] | 79 | '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] | 80 | ] |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 81 | |
| 82 | class ValidationDatabase: |
| 83 | def __init__(self, filename=db_file): |
| 84 | self.db_file = filename |
| 85 | self.delimiter = '~^~' |
| 86 | self.db_dict = {} # complete dict of all db values per error enum |
| 87 | # specialized data structs with slices of complete dict |
| 88 | self.db_implemented_enums = [] # list of all error enums claiming to be implemented in database file |
| 89 | self.db_enum_to_tests = {} # dict where enum is key to lookup list of tests implementing the enum |
| 90 | #self.src_implemented_enums |
| 91 | def read(self): |
| 92 | """Read a database file into internal data structures, format of each line is <enum><implemented Y|N?><testname><api><errormsg><notes>""" |
| 93 | #db_dict = {} # This is a simple db of just enum->errormsg, the same as is created from spec |
| 94 | #max_id = 0 |
| 95 | with open(self.db_file, "r") as infile: |
| 96 | for line in infile: |
| 97 | line = line.strip() |
| 98 | if line.startswith('#') or '' == line: |
| 99 | continue |
| 100 | db_line = line.split(self.delimiter) |
| 101 | if len(db_line) != 6: |
Tobin Ehlis | 027f321 | 2016-12-09 12:15:26 -0700 | [diff] [blame] | 102 | print("ERROR: Bad database line doesn't have 6 elements: %s" % (line)) |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 103 | error_enum = db_line[0] |
| 104 | implemented = db_line[1] |
| 105 | testname = db_line[2] |
| 106 | api = db_line[3] |
| 107 | error_str = db_line[4] |
| 108 | note = db_line[5] |
| 109 | # Read complete database contents into our class var for later use |
| 110 | self.db_dict[error_enum] = {} |
| 111 | self.db_dict[error_enum]['check_implemented'] = implemented |
| 112 | self.db_dict[error_enum]['testname'] = testname |
| 113 | self.db_dict[error_enum]['api'] = api |
| 114 | self.db_dict[error_enum]['error_string'] = error_str |
| 115 | self.db_dict[error_enum]['note'] = note |
| 116 | # Now build custom data structs |
| 117 | if 'Y' == implemented: |
| 118 | self.db_implemented_enums.append(error_enum) |
| 119 | if testname.lower() not in ['unknown', 'none']: |
| 120 | self.db_enum_to_tests[error_enum] = testname.split(',') |
| 121 | #if len(self.db_enum_to_tests[error_enum]) > 1: |
| 122 | # print "Found check %s that has multiple tests: %s" % (error_enum, self.db_enum_to_tests[error_enum]) |
| 123 | #else: |
| 124 | # print "Check %s has single test: %s" % (error_enum, self.db_enum_to_tests[error_enum]) |
| 125 | #unique_id = int(db_line[0].split('_')[-1]) |
| 126 | #if unique_id > max_id: |
| 127 | # max_id = unique_id |
| 128 | #print "Found %d total enums in database" % (len(self.db_dict.keys())) |
| 129 | #print "Found %d enums claiming to be implemented in source" % (len(self.db_implemented_enums)) |
| 130 | #print "Found %d enums claiming to have tests implemented" % (len(self.db_enum_to_tests.keys())) |
| 131 | |
| 132 | class ValidationHeader: |
| 133 | def __init__(self, filename=header_file): |
| 134 | self.filename = header_file |
| 135 | self.enums = [] |
| 136 | def read(self): |
| 137 | """Read unique error enum header file into internal data structures""" |
| 138 | grab_enums = False |
| 139 | with open(self.filename, "r") as infile: |
| 140 | for line in infile: |
| 141 | line = line.strip() |
| 142 | if 'enum UNIQUE_VALIDATION_ERROR_CODE {' in line: |
| 143 | grab_enums = True |
| 144 | continue |
| 145 | if grab_enums: |
| 146 | if 'VALIDATION_ERROR_MAX_ENUM' in line: |
| 147 | grab_enums = False |
| 148 | break # done |
Tobin Ehlis | f53eac3 | 2016-12-09 14:10:47 -0700 | [diff] [blame] | 149 | elif 'VALIDATION_ERROR_UNDEFINED' in line: |
| 150 | continue |
| 151 | elif 'VALIDATION_ERROR_' in line: |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 152 | enum = line.split(' = ')[0] |
| 153 | self.enums.append(enum) |
| 154 | #print "Found %d error enums. First is %s and last is %s." % (len(self.enums), self.enums[0], self.enums[-1]) |
| 155 | |
| 156 | class ValidationSource: |
| 157 | def __init__(self, source_file_list): |
| 158 | self.source_files = source_file_list |
Tobin Ehlis | 3d1f2bd | 2016-12-22 11:19:15 -0700 | [diff] [blame] | 159 | 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] | 160 | # 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] | 161 | self.enum_count_dict['VALIDATION_ERROR_01790'] = {} |
| 162 | self.enum_count_dict['VALIDATION_ERROR_01790']['count'] = 1 |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 163 | def parse(self): |
| 164 | duplicate_checks = 0 |
| 165 | for sf in self.source_files: |
Tobin Ehlis | 3d1f2bd | 2016-12-22 11:19:15 -0700 | [diff] [blame] | 166 | line_num = 0 |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 167 | with open(sf) as f: |
| 168 | for line in f: |
Tobin Ehlis | 3d1f2bd | 2016-12-22 11:19:15 -0700 | [diff] [blame] | 169 | line_num = line_num + 1 |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 170 | if True in [line.strip().startswith(comment) for comment in ['//', '/*']]: |
| 171 | continue |
| 172 | # Find enums |
| 173 | #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] | 174 | if ' VALIDATION_ERROR_' in line: |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 175 | # Need to isolate the validation error enum |
| 176 | #print("Line has check:%s" % (line)) |
| 177 | line_list = line.split() |
Tobin Ehlis | 928742e | 2016-12-09 17:11:13 -0700 | [diff] [blame] | 178 | enum_list = [] |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 179 | for str in line_list: |
Tobin Ehlis | f53eac3 | 2016-12-09 14:10:47 -0700 | [diff] [blame] | 180 | 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] | 181 | enum_list.append(str.strip(',);')) |
| 182 | #break |
| 183 | for enum in enum_list: |
| 184 | if enum != '': |
| 185 | if enum not in self.enum_count_dict: |
Tobin Ehlis | 3d1f2bd | 2016-12-22 11:19:15 -0700 | [diff] [blame] | 186 | self.enum_count_dict[enum] = {} |
| 187 | self.enum_count_dict[enum]['count'] = 1 |
| 188 | self.enum_count_dict[enum]['file_line'] = [] |
| 189 | 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] | 190 | #print "Found enum %s implemented for first time in file %s" % (enum, sf) |
| 191 | else: |
Tobin Ehlis | 3d1f2bd | 2016-12-22 11:19:15 -0700 | [diff] [blame] | 192 | self.enum_count_dict[enum]['count'] = self.enum_count_dict[enum]['count'] + 1 |
| 193 | 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] | 194 | #print "Found enum %s implemented for %d time in file %s" % (enum, self.enum_count_dict[enum], sf) |
| 195 | duplicate_checks = duplicate_checks + 1 |
| 196 | #else: |
| 197 | #print("Didn't find actual check in line:%s" % (line)) |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 198 | #print "Found %d unique implemented checks and %d are duplicated at least once" % (len(self.enum_count_dict.keys()), duplicate_checks) |
| 199 | |
| 200 | # Class to parse the validation layer test source and store testnames |
| 201 | # TODO: Enhance class to detect use of unique error enums in the test |
| 202 | class TestParser: |
| 203 | def __init__(self, test_file_list, test_group_name=['VkLayerTest', 'VkPositiveLayerTest', 'VkWsiEnabledLayerTest']): |
| 204 | self.test_files = test_file_list |
Tobin Ehlis | 9a68c98 | 2016-12-29 14:51:17 -0700 | [diff] [blame] | 205 | 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] | 206 | self.test_trigger_txt_list = [] |
| 207 | for tg in test_group_name: |
| 208 | self.test_trigger_txt_list.append('TEST_F(%s' % tg) |
| 209 | #print('Test trigger test list: %s' % (self.test_trigger_txt_list)) |
| 210 | |
| 211 | # Parse test files into internal data struct |
| 212 | def parse(self): |
| 213 | # For each test file, parse test names into set |
| 214 | grab_next_line = False # handle testname on separate line than wildcard |
Tobin Ehlis | 9a68c98 | 2016-12-29 14:51:17 -0700 | [diff] [blame] | 215 | testname = '' |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 216 | for test_file in self.test_files: |
| 217 | with open(test_file) as tf: |
| 218 | for line in tf: |
| 219 | if True in [line.strip().startswith(comment) for comment in ['//', '/*']]: |
| 220 | continue |
| 221 | |
| 222 | if True in [ttt in line for ttt in self.test_trigger_txt_list]: |
| 223 | #print('Test wildcard in line: %s' % (line)) |
| 224 | testname = line.split(',')[-1] |
| 225 | testname = testname.strip().strip(' {)') |
| 226 | #print('Inserting test: "%s"' % (testname)) |
| 227 | if ('' == testname): |
| 228 | grab_next_line = True |
| 229 | continue |
Tobin Ehlis | 9a68c98 | 2016-12-29 14:51:17 -0700 | [diff] [blame] | 230 | self.test_to_errors[testname] = [] |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 231 | if grab_next_line: # test name on its own line |
| 232 | grab_next_line = False |
| 233 | testname = testname.strip().strip(' {)') |
Tobin Ehlis | 9a68c98 | 2016-12-29 14:51:17 -0700 | [diff] [blame] | 234 | self.test_to_errors[testname] = [] |
| 235 | if ' VALIDATION_ERROR_' in line: |
| 236 | line_list = line.split() |
| 237 | for str in line_list: |
| 238 | 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']]: |
| 239 | print("Trying to add enums for line: %s") |
| 240 | print("Adding enum %s to test %s" % (str.strip(',);'), testname)) |
| 241 | self.test_to_errors[testname].append(str.strip(',);')) |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 242 | |
| 243 | # Little helper class for coloring cmd line output |
| 244 | class bcolors: |
| 245 | |
| 246 | def __init__(self): |
| 247 | self.GREEN = '\033[0;32m' |
| 248 | self.RED = '\033[0;31m' |
| 249 | self.YELLOW = '\033[1;33m' |
| 250 | self.ENDC = '\033[0m' |
| 251 | if 'Linux' != platform.system(): |
| 252 | self.GREEN = '' |
| 253 | self.RED = '' |
| 254 | self.YELLOW = '' |
| 255 | self.ENDC = '' |
| 256 | |
| 257 | def green(self): |
| 258 | return self.GREEN |
| 259 | |
| 260 | def red(self): |
| 261 | return self.RED |
| 262 | |
| 263 | def yellow(self): |
| 264 | return self.YELLOW |
| 265 | |
| 266 | def endc(self): |
| 267 | return self.ENDC |
| 268 | |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 269 | def main(argv=None): |
Tobin Ehlis | 20e3258 | 2016-12-05 14:50:03 -0700 | [diff] [blame] | 270 | result = 0 # Non-zero result indicates an error case |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 271 | # parse db |
| 272 | val_db = ValidationDatabase() |
| 273 | val_db.read() |
| 274 | # parse header |
| 275 | val_header = ValidationHeader() |
| 276 | val_header.read() |
| 277 | # Create parser for layer files |
| 278 | val_source = ValidationSource(layer_source_files) |
| 279 | val_source.parse() |
| 280 | # Parse test files |
| 281 | test_parser = TestParser([test_file, ]) |
| 282 | test_parser.parse() |
| 283 | |
| 284 | # Process stats - Just doing this inline in main, could make a fancy class to handle |
| 285 | # all the processing of data and then get results from that |
| 286 | txt_color = bcolors() |
| 287 | print("Validation Statistics") |
| 288 | # First give number of checks in db & header and report any discrepancies |
| 289 | db_enums = len(val_db.db_dict.keys()) |
| 290 | hdr_enums = len(val_header.enums) |
| 291 | print(" Database file includes %d unique checks" % (db_enums)) |
| 292 | print(" Header file declares %d unique checks" % (hdr_enums)) |
| 293 | tmp_db_dict = val_db.db_dict |
| 294 | db_missing = [] |
| 295 | for enum in val_header.enums: |
| 296 | if not tmp_db_dict.pop(enum, False): |
| 297 | db_missing.append(enum) |
| 298 | if db_enums == hdr_enums and len(db_missing) == 0 and len(tmp_db_dict.keys()) == 0: |
| 299 | print(txt_color.green() + " Database and Header match, GREAT!" + txt_color.endc()) |
| 300 | else: |
| 301 | 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] | 302 | result = 1 |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 303 | if len(db_missing) != 0: |
| 304 | print(txt_color.red() + " The following checks are in header but missing from database:" + txt_color.endc()) |
| 305 | for missing_enum in db_missing: |
| 306 | print(txt_color.red() + " %s" % (missing_enum) + txt_color.endc()) |
| 307 | if len(tmp_db_dict.keys()) != 0: |
| 308 | print(txt_color.red() + " The following checks are in database but haven't been declared in the header:" + txt_color.endc()) |
| 309 | for extra_enum in tmp_db_dict: |
| 310 | print(txt_color.red() + " %s" % (extra_enum) + txt_color.endc()) |
| 311 | # Report out claimed implemented checks vs. found actual implemented checks |
| 312 | imp_not_found = [] # Checks claimed to implemented in DB file but no source found |
| 313 | imp_not_claimed = [] # Checks found implemented but not claimed to be in DB |
| 314 | multiple_uses = False # Flag if any enums are used multiple times |
| 315 | for db_imp in val_db.db_implemented_enums: |
| 316 | if db_imp not in val_source.enum_count_dict: |
| 317 | imp_not_found.append(db_imp) |
| 318 | for src_enum in val_source.enum_count_dict: |
Tobin Ehlis | 225b59c | 2016-12-22 13:59:42 -0700 | [diff] [blame] | 319 | 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] | 320 | multiple_uses = True |
| 321 | if src_enum not in val_db.db_implemented_enums: |
| 322 | imp_not_claimed.append(src_enum) |
| 323 | 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))) |
| 324 | if len(imp_not_found) == 0 and len(imp_not_claimed) == 0: |
| 325 | 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()) |
| 326 | else: |
Tobin Ehlis | 20e3258 | 2016-12-05 14:50:03 -0700 | [diff] [blame] | 327 | result = 1 |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 328 | print(txt_color.red() + " Uh oh, Database claimed implemented don't match Source :(" + txt_color.endc()) |
| 329 | if len(imp_not_found) != 0: |
Tobin Ehlis | 3f0b277 | 2016-11-18 16:56:15 -0700 | [diff] [blame] | 330 | 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] | 331 | for not_imp_enum in imp_not_found: |
| 332 | print(txt_color.red() + " %s" % (not_imp_enum) + txt_color.endc()) |
| 333 | if len(imp_not_claimed) != 0: |
| 334 | print(txt_color.red() + " The following checks are implemented in source, but not claimed to be in Database:" + txt_color.endc()) |
| 335 | for imp_enum in imp_not_claimed: |
| 336 | print(txt_color.red() + " %s" % (imp_enum) + txt_color.endc()) |
| 337 | if multiple_uses: |
| 338 | 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()) |
| 339 | print(txt_color.yellow() + " Here is a list of each check used multiple times with its number of uses:" + txt_color.endc()) |
| 340 | for enum in val_source.enum_count_dict: |
Tobin Ehlis | 225b59c | 2016-12-22 13:59:42 -0700 | [diff] [blame] | 341 | 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] | 342 | print(txt_color.yellow() + " %s: %d uses in file,line:" % (enum, val_source.enum_count_dict[enum]['count']) + txt_color.endc()) |
| 343 | for file_line in val_source.enum_count_dict[enum]['file_line']: |
| 344 | print(txt_color.yellow() + " \t%s" % (file_line) + txt_color.endc()) |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 345 | # Now check that tests claimed to be implemented are actual test names |
| 346 | bad_testnames = [] |
Tobin Ehlis | 9a68c98 | 2016-12-29 14:51:17 -0700 | [diff] [blame] | 347 | 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] | 348 | for enum in val_db.db_enum_to_tests: |
| 349 | for testname in val_db.db_enum_to_tests[enum]: |
Tobin Ehlis | 9a68c98 | 2016-12-29 14:51:17 -0700 | [diff] [blame] | 350 | if testname not in test_parser.test_to_errors: |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 351 | bad_testnames.append(testname) |
Tobin Ehlis | 9a68c98 | 2016-12-29 14:51:17 -0700 | [diff] [blame] | 352 | else: |
| 353 | enum_found = False |
| 354 | for test_enum in test_parser.test_to_errors[testname]: |
| 355 | if test_enum == enum: |
| 356 | #print("Found test that correctly checks for enum: %s" % (enum)) |
| 357 | enum_found = True |
| 358 | if not enum_found: |
| 359 | #print("Test %s is not using enum %s to check for error" % (testname, enum)) |
| 360 | if testname not in tests_missing_enum: |
| 361 | tests_missing_enum[testname] = [] |
| 362 | tests_missing_enum[testname].append(enum) |
| 363 | if tests_missing_enum: |
| 364 | 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()) |
| 365 | for testname in tests_missing_enum: |
| 366 | print(txt_color.yellow() + " Testname %s does not explicitly check for these ids:" % (testname) + txt_color.endc()) |
| 367 | for enum in tests_missing_enum[testname]: |
| 368 | print(txt_color.yellow() + " %s" % (enum) + txt_color.endc()) |
| 369 | # 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] | 370 | print(" Database file claims that %d checks have tests written." % len(val_db.db_enum_to_tests)) |
| 371 | if len(bad_testnames) == 0: |
| 372 | print(txt_color.green() + " All claimed tests have valid names. That's good!" + txt_color.endc()) |
| 373 | else: |
| 374 | 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] | 375 | result = 1 |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 376 | for bt in bad_testnames: |
Tobin Ehlis | b04c2c6 | 2016-11-21 15:51:45 -0700 | [diff] [blame] | 377 | print(txt_color.red() + " %s" % (bt) + txt_color.endc()) |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 378 | |
Tobin Ehlis | 20e3258 | 2016-12-05 14:50:03 -0700 | [diff] [blame] | 379 | return result |
Tobin Ehlis | 35308dd | 2016-10-31 13:27:36 -0600 | [diff] [blame] | 380 | |
| 381 | if __name__ == "__main__": |
| 382 | sys.exit(main()) |
| 383 | |