blob: 2e7922ddb37da53ef30413148f0ab2bb20282a39 [file] [log] [blame]
Tobin Ehlis35308dd2016-10-31 13:27:36 -06001#!/usr/bin/env python3
Mike Weiblenfe186122017-02-03 12:44:53 -07002# Copyright (c) 2015-2017 The Khronos Group Inc.
3# Copyright (c) 2015-2017 Valve Corporation
4# Copyright (c) 2015-2017 LunarG, Inc.
5# Copyright (c) 2015-2017 Google Inc.
Tobin Ehlis35308dd2016-10-31 13:27:36 -06006#
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
21import argparse
22import os
23import sys
24import 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 Ehlis20e32582016-12-05 14:50:03 -070038# 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 Ehlis35308dd2016-10-31 13:27:36 -060041# 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
Mark Lobodzinskie3787b42017-06-21 13:41:00 -060046db_file = '../layers/vk_validation_error_database.txt'
Mark Lobodzinski05849f02017-06-21 14:44:14 -060047generated_layer_source_directories = [
48'build',
49'dbuild',
50'release',
51]
52generated_layer_source_files = [
53'parameter_validation.h',
54]
Tobin Ehlis35308dd2016-10-31 13:27:36 -060055layer_source_files = [
Mark Lobodzinskie3787b42017-06-21 13:41:00 -060056'../layers/core_validation.cpp',
57'../layers/descriptor_sets.cpp',
58'../layers/parameter_validation.cpp',
59'../layers/object_tracker.cpp',
Mark Lobodzinskib2de97f2017-07-06 15:28:11 -060060'../layers/object_tracker_utils.cpp',
Mark Lobodzinskie3787b42017-06-21 13:41:00 -060061'../layers/shader_validation.cpp',
62'../layers/buffer_validation.cpp',
Tobin Ehlis35308dd2016-10-31 13:27:36 -060063]
Mark Lobodzinskie3787b42017-06-21 13:41:00 -060064header_file = '../layers/vk_validation_error_messages.h'
Tobin Ehlis35308dd2016-10-31 13:27:36 -060065# TODO : Don't hardcode linux path format if we want this to run on windows
66test_file = '../tests/layer_validation_tests.cpp'
Tobin Ehlis225b59c2016-12-22 13:59:42 -070067# List of enums that are allowed to be used more than once so don't warn on their duplicates
68duplicate_exceptions = [
Tobin Ehlis3c37fb32017-05-24 09:31:13 -060069'VALIDATION_ERROR_258004ea', # This covers the broad case that all child objects must be destroyed at DestroyInstance time
70'VALIDATION_ERROR_24a002f4', # This covers the broad case that all child objects must be destroyed at DestroyDevice time
71'VALIDATION_ERROR_0280006e', # Obj tracker check makes sure non-null framebuffer is valid & CV check makes sure it's compatible w/ renderpass framebuffer
72'VALIDATION_ERROR_12200682', # This is an aliasing error that we report twice, for each of the two allocations that are aliasing
73'VALIDATION_ERROR_1060d201', # Covers valid shader module handle for both Compute & Graphics pipelines
74'VALIDATION_ERROR_0c20c601', # This is a case for VkMappedMemoryRange struct that is used by both Flush & Invalidate MappedMemoryRange
75'VALIDATION_ERROR_0a400c01', # This is a blanket case for all invalid image aspect bit errors. The spec link has appropriate details for all separate cases.
76'VALIDATION_ERROR_0a8007fc', # This case covers two separate checks which are done independently
77'VALIDATION_ERROR_0a800800', # This case covers two separate checks which are done independently
78'VALIDATION_ERROR_15c0028a', # This is a descriptor set write update error that we use for a couple copy cases as well
79'VALIDATION_ERROR_1bc002de', # Single error for mis-matched stageFlags of vkCmdPushConstants() that is flagged for no stage flags & mis-matched flags
80'VALIDATION_ERROR_1880000e', # Handles both depth/stencil & compressed image errors for vkCmdClearColorImage()
81'VALIDATION_ERROR_0a600152', # Used for the mipLevel check of both dst & src images on vkCmdCopyImage call
82'VALIDATION_ERROR_0a600154', # Used for the arraySize check of both dst & src images on vkCmdCopyImage call
83'VALIDATION_ERROR_1500099e', # Used for both x & y bounds of viewport
84'VALIDATION_ERROR_1d8004a6', # Used for both x & y value of scissors to make sure they're not negative
85'VALIDATION_ERROR_1462ec01', # Surface of VkSwapchainCreateInfoKHR must be valid when creating both single or shared swapchains
86'VALIDATION_ERROR_1460de01', # oldSwapchain of VkSwapchainCreateInfoKHR must be valid when creating both single or shared swapchains
87'VALIDATION_ERROR_146009f2', # Single error for both imageFormat & imageColorSpace requirements when creating swapchain
88'VALIDATION_ERROR_15c00294', # Used twice for the same error codepath as both a param & to set a variable, so not really a duplicate
Tobin Ehlis225b59c2016-12-22 13:59:42 -070089]
Tobin Ehlis35308dd2016-10-31 13:27:36 -060090
91class ValidationDatabase:
92 def __init__(self, filename=db_file):
93 self.db_file = filename
94 self.delimiter = '~^~'
95 self.db_dict = {} # complete dict of all db values per error enum
96 # specialized data structs with slices of complete dict
97 self.db_implemented_enums = [] # list of all error enums claiming to be implemented in database file
Tobin Ehlis2bedc242017-01-12 13:45:55 -070098 self.db_unimplemented_implicit = [] # list of all implicit checks that aren't marked implemented
Tobin Ehlis35308dd2016-10-31 13:27:36 -060099 self.db_enum_to_tests = {} # dict where enum is key to lookup list of tests implementing the enum
Mike Weiblenfe186122017-02-03 12:44:53 -0700100 self.db_invalid_implemented = [] # list of checks with invalid check_implemented flags
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600101 #self.src_implemented_enums
102 def read(self):
103 """Read a database file into internal data structures, format of each line is <enum><implemented Y|N?><testname><api><errormsg><notes>"""
104 #db_dict = {} # This is a simple db of just enum->errormsg, the same as is created from spec
105 #max_id = 0
106 with open(self.db_file, "r") as infile:
107 for line in infile:
108 line = line.strip()
109 if line.startswith('#') or '' == line:
110 continue
111 db_line = line.split(self.delimiter)
Tobin Ehlisf7fc6672017-05-25 14:55:42 -0600112 if len(db_line) != 8:
113 print("ERROR: Bad database line doesn't have 8 elements: %s" % (line))
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600114 error_enum = db_line[0]
115 implemented = db_line[1]
116 testname = db_line[2]
117 api = db_line[3]
Tobin Ehlisf7fc6672017-05-25 14:55:42 -0600118 vuid_string = db_line[4]
119 core_ext = db_line[5]
120 error_str = db_line[6]
121 note = db_line[7]
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600122 # Read complete database contents into our class var for later use
123 self.db_dict[error_enum] = {}
124 self.db_dict[error_enum]['check_implemented'] = implemented
125 self.db_dict[error_enum]['testname'] = testname
126 self.db_dict[error_enum]['api'] = api
Tobin Ehlisf7fc6672017-05-25 14:55:42 -0600127 self.db_dict[error_enum]['vuid_string'] = vuid_string
128 self.db_dict[error_enum]['core_ext'] = core_ext
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600129 self.db_dict[error_enum]['error_string'] = error_str
130 self.db_dict[error_enum]['note'] = note
131 # Now build custom data structs
132 if 'Y' == implemented:
133 self.db_implemented_enums.append(error_enum)
Tobin Ehlis2bedc242017-01-12 13:45:55 -0700134 elif 'implicit' in note: # only make note of non-implemented implicit checks
135 self.db_unimplemented_implicit.append(error_enum)
Mike Weiblenfe186122017-02-03 12:44:53 -0700136 if implemented not in ['Y', 'N']:
137 self.db_invalid_implemented.append(error_enum)
Dave Houltona536fae2017-05-18 15:56:22 -0600138 if testname.lower() not in ['unknown', 'none', 'nottestable']:
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600139 self.db_enum_to_tests[error_enum] = testname.split(',')
140 #if len(self.db_enum_to_tests[error_enum]) > 1:
141 # print "Found check %s that has multiple tests: %s" % (error_enum, self.db_enum_to_tests[error_enum])
142 #else:
143 # print "Check %s has single test: %s" % (error_enum, self.db_enum_to_tests[error_enum])
144 #unique_id = int(db_line[0].split('_')[-1])
145 #if unique_id > max_id:
146 # max_id = unique_id
147 #print "Found %d total enums in database" % (len(self.db_dict.keys()))
148 #print "Found %d enums claiming to be implemented in source" % (len(self.db_implemented_enums))
149 #print "Found %d enums claiming to have tests implemented" % (len(self.db_enum_to_tests.keys()))
150
151class ValidationHeader:
152 def __init__(self, filename=header_file):
153 self.filename = header_file
154 self.enums = []
155 def read(self):
156 """Read unique error enum header file into internal data structures"""
157 grab_enums = False
158 with open(self.filename, "r") as infile:
159 for line in infile:
160 line = line.strip()
161 if 'enum UNIQUE_VALIDATION_ERROR_CODE {' in line:
162 grab_enums = True
163 continue
164 if grab_enums:
165 if 'VALIDATION_ERROR_MAX_ENUM' in line:
166 grab_enums = False
167 break # done
Tobin Ehlisf53eac32016-12-09 14:10:47 -0700168 elif 'VALIDATION_ERROR_UNDEFINED' in line:
169 continue
170 elif 'VALIDATION_ERROR_' in line:
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600171 enum = line.split(' = ')[0]
172 self.enums.append(enum)
173 #print "Found %d error enums. First is %s and last is %s." % (len(self.enums), self.enums[0], self.enums[-1])
174
175class ValidationSource:
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600176 def __init__(self, source_file_list, generated_source_file_list, generated_source_directories):
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600177 self.source_files = source_file_list
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600178 self.generated_source_files = generated_source_file_list
179 self.generated_source_dirs = generated_source_directories
180
181 if len(self.generated_source_files) > 0:
182 qualified_paths = []
183 for source in self.generated_source_files:
184 for build_dir in self.generated_source_dirs:
185 filepath = '../%s/layers/%s' % (build_dir, source)
186 if os.path.isfile(filepath):
187 qualified_paths.append(filepath)
188 continue
189 if len(self.generated_source_files) != len(qualified_paths):
Mark Lobodzinski2d193ac2017-06-27 09:38:15 -0600190 print("Error: Unable to locate one or more of the following source files in the %s directories" % (", ".join(generated_source_directories)))
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600191 print(self.generated_source_files)
Mark Lobodzinski2d193ac2017-06-27 09:38:15 -0600192 print("Skipping documentation validation test")
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600193 quit()
194 else:
195 self.source_files.extend(qualified_paths)
196
Tobin Ehlis3d1f2bd2016-12-22 11:19:15 -0700197 self.enum_count_dict = {} # dict of enum values to the count of how much they're used, and location of where they're used
Mark Lobodzinski996fd042017-06-06 13:59:27 -0600198 # 1500099c 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 Ehlis3c37fb32017-05-24 09:31:13 -0600199 self.enum_count_dict['VALIDATION_ERROR_1500099c'] = {}
200 self.enum_count_dict['VALIDATION_ERROR_1500099c']['count'] = 1
Mark Lobodzinski082844a2017-06-02 16:31:30 -0600201 self.enum_count_dict['VALIDATION_ERROR_1500099c']['file_line'] = []
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600202 def parse(self):
203 duplicate_checks = 0
204 for sf in self.source_files:
Tobin Ehlis3d1f2bd2016-12-22 11:19:15 -0700205 line_num = 0
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600206 with open(sf) as f:
207 for line in f:
Tobin Ehlis3d1f2bd2016-12-22 11:19:15 -0700208 line_num = line_num + 1
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600209 if True in [line.strip().startswith(comment) for comment in ['//', '/*']]:
210 continue
211 # Find enums
212 #if 'VALIDATION_ERROR_' in line and True not in [ignore in line for ignore in ['[VALIDATION_ERROR_', 'UNIQUE_VALIDATION_ERROR_CODE']]:
Tobin Ehlisf53eac32016-12-09 14:10:47 -0700213 if ' VALIDATION_ERROR_' in line:
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600214 # Need to isolate the validation error enum
215 #print("Line has check:%s" % (line))
216 line_list = line.split()
Tobin Ehlis928742e2016-12-09 17:11:13 -0700217 enum_list = []
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600218 for str in line_list:
Tobin Ehlisf53eac32016-12-09 14:10:47 -0700219 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 Ehlis928742e2016-12-09 17:11:13 -0700220 enum_list.append(str.strip(',);'))
221 #break
222 for enum in enum_list:
223 if enum != '':
224 if enum not in self.enum_count_dict:
Tobin Ehlis3d1f2bd2016-12-22 11:19:15 -0700225 self.enum_count_dict[enum] = {}
226 self.enum_count_dict[enum]['count'] = 1
227 self.enum_count_dict[enum]['file_line'] = []
228 self.enum_count_dict[enum]['file_line'].append('%s,%d' % (sf, line_num))
Tobin Ehlis928742e2016-12-09 17:11:13 -0700229 #print "Found enum %s implemented for first time in file %s" % (enum, sf)
230 else:
Tobin Ehlis3d1f2bd2016-12-22 11:19:15 -0700231 self.enum_count_dict[enum]['count'] = self.enum_count_dict[enum]['count'] + 1
232 self.enum_count_dict[enum]['file_line'].append('%s,%d' % (sf, line_num))
Tobin Ehlis928742e2016-12-09 17:11:13 -0700233 #print "Found enum %s implemented for %d time in file %s" % (enum, self.enum_count_dict[enum], sf)
234 duplicate_checks = duplicate_checks + 1
235 #else:
236 #print("Didn't find actual check in line:%s" % (line))
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600237 #print "Found %d unique implemented checks and %d are duplicated at least once" % (len(self.enum_count_dict.keys()), duplicate_checks)
238
239# Class to parse the validation layer test source and store testnames
240# TODO: Enhance class to detect use of unique error enums in the test
241class TestParser:
242 def __init__(self, test_file_list, test_group_name=['VkLayerTest', 'VkPositiveLayerTest', 'VkWsiEnabledLayerTest']):
243 self.test_files = test_file_list
Tobin Ehlis9a68c982016-12-29 14:51:17 -0700244 self.test_to_errors = {} # Dict where testname maps to list of error enums found in that test
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600245 self.test_trigger_txt_list = []
246 for tg in test_group_name:
247 self.test_trigger_txt_list.append('TEST_F(%s' % tg)
248 #print('Test trigger test list: %s' % (self.test_trigger_txt_list))
249
250 # Parse test files into internal data struct
251 def parse(self):
252 # For each test file, parse test names into set
253 grab_next_line = False # handle testname on separate line than wildcard
Tobin Ehlis9a68c982016-12-29 14:51:17 -0700254 testname = ''
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600255 for test_file in self.test_files:
256 with open(test_file) as tf:
257 for line in tf:
258 if True in [line.strip().startswith(comment) for comment in ['//', '/*']]:
259 continue
260
261 if True in [ttt in line for ttt in self.test_trigger_txt_list]:
262 #print('Test wildcard in line: %s' % (line))
263 testname = line.split(',')[-1]
264 testname = testname.strip().strip(' {)')
265 #print('Inserting test: "%s"' % (testname))
266 if ('' == testname):
267 grab_next_line = True
268 continue
Tobin Ehlis9a68c982016-12-29 14:51:17 -0700269 self.test_to_errors[testname] = []
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600270 if grab_next_line: # test name on its own line
271 grab_next_line = False
272 testname = testname.strip().strip(' {)')
Tobin Ehlis9a68c982016-12-29 14:51:17 -0700273 self.test_to_errors[testname] = []
274 if ' VALIDATION_ERROR_' in line:
275 line_list = line.split()
Tobin Ehlis71f38c12017-01-12 14:26:56 -0700276 for sub_str in line_list:
277 if 'VALIDATION_ERROR_' in sub_str and True not in [ignore_str in sub_str for ignore_str in ['VALIDATION_ERROR_UNDEFINED', 'UNIQUE_VALIDATION_ERROR_CODE', 'VALIDATION_ERROR_MAX_ENUM']]:
278 #print("Trying to add enums for line: %s" % ())
279 #print("Adding enum %s to test %s" % (sub_str.strip(',);'), testname))
280 self.test_to_errors[testname].append(sub_str.strip(',);'))
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600281
282# Little helper class for coloring cmd line output
283class bcolors:
284
285 def __init__(self):
286 self.GREEN = '\033[0;32m'
287 self.RED = '\033[0;31m'
288 self.YELLOW = '\033[1;33m'
289 self.ENDC = '\033[0m'
290 if 'Linux' != platform.system():
291 self.GREEN = ''
292 self.RED = ''
293 self.YELLOW = ''
294 self.ENDC = ''
295
296 def green(self):
297 return self.GREEN
298
299 def red(self):
300 return self.RED
301
302 def yellow(self):
303 return self.YELLOW
304
305 def endc(self):
306 return self.ENDC
307
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600308def main(argv):
Tobin Ehlis20e32582016-12-05 14:50:03 -0700309 result = 0 # Non-zero result indicates an error case
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600310 terse_mode = 'terse_mode' in sys.argv
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600311 # parse db
312 val_db = ValidationDatabase()
313 val_db.read()
314 # parse header
315 val_header = ValidationHeader()
316 val_header.read()
317 # Create parser for layer files
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600318 val_source = ValidationSource(layer_source_files, generated_layer_source_files, generated_layer_source_directories)
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600319 val_source.parse()
320 # Parse test files
321 test_parser = TestParser([test_file, ])
322 test_parser.parse()
323
324 # Process stats - Just doing this inline in main, could make a fancy class to handle
325 # all the processing of data and then get results from that
326 txt_color = bcolors()
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600327 if terse_mode == False:
328 print("Validation Statistics")
329 else:
330 print("Validation/Documentation Consistency Test)")
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600331 # First give number of checks in db & header and report any discrepancies
332 db_enums = len(val_db.db_dict.keys())
333 hdr_enums = len(val_header.enums)
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600334 if not terse_mode:
335 print(" Database file includes %d unique checks" % (db_enums))
336 print(" Header file declares %d unique checks" % (hdr_enums))
Mike Weiblenfe186122017-02-03 12:44:53 -0700337
338 # Report any checks that have an invalid check_implemented flag
339 if len(val_db.db_invalid_implemented) > 0:
340 result = 1
341 print(txt_color.red() + "The following checks have an invalid check_implemented flag (must be 'Y' or 'N'):" + txt_color.endc())
342 for invalid_imp_enum in val_db.db_invalid_implemented:
343 check_implemented = val_db.db_dict[invalid_imp_enum]['check_implemented']
344 print(txt_color.red() + " %s has check_implemented flag '%s'" % (invalid_imp_enum, check_implemented) + txt_color.endc())
345
346 # Report details about how well the Database and Header are synchronized.
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600347 tmp_db_dict = val_db.db_dict
348 db_missing = []
349 for enum in val_header.enums:
350 if not tmp_db_dict.pop(enum, False):
351 db_missing.append(enum)
352 if db_enums == hdr_enums and len(db_missing) == 0 and len(tmp_db_dict.keys()) == 0:
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600353 if not terse_mode:
354 print(txt_color.green() + " Database and Header match, GREAT!" + txt_color.endc())
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600355 else:
356 print(txt_color.red() + " Uh oh, Database doesn't match Header :(" + txt_color.endc())
Tobin Ehlis20e32582016-12-05 14:50:03 -0700357 result = 1
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600358 if len(db_missing) != 0:
359 print(txt_color.red() + " The following checks are in header but missing from database:" + txt_color.endc())
360 for missing_enum in db_missing:
361 print(txt_color.red() + " %s" % (missing_enum) + txt_color.endc())
362 if len(tmp_db_dict.keys()) != 0:
363 print(txt_color.red() + " The following checks are in database but haven't been declared in the header:" + txt_color.endc())
364 for extra_enum in tmp_db_dict:
365 print(txt_color.red() + " %s" % (extra_enum) + txt_color.endc())
Mike Weiblenfe186122017-02-03 12:44:53 -0700366
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600367 # Report out claimed implemented checks vs. found actual implemented checks
368 imp_not_found = [] # Checks claimed to implemented in DB file but no source found
369 imp_not_claimed = [] # Checks found implemented but not claimed to be in DB
370 multiple_uses = False # Flag if any enums are used multiple times
371 for db_imp in val_db.db_implemented_enums:
372 if db_imp not in val_source.enum_count_dict:
373 imp_not_found.append(db_imp)
374 for src_enum in val_source.enum_count_dict:
Tobin Ehlis225b59c2016-12-22 13:59:42 -0700375 if val_source.enum_count_dict[src_enum]['count'] > 1 and src_enum not in duplicate_exceptions:
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600376 multiple_uses = True
377 if src_enum not in val_db.db_implemented_enums:
378 imp_not_claimed.append(src_enum)
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600379 if not terse_mode:
380 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)))
Mike Weiblenfe186122017-02-03 12:44:53 -0700381
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600382 if len(val_db.db_unimplemented_implicit) > 0 and not terse_mode:
Tobin Ehlis2bedc242017-01-12 13:45:55 -0700383 print(" Database file claims %d implicit checks (%s) that are not implemented." % (len(val_db.db_unimplemented_implicit), "{0:.0f}%".format(float(len(val_db.db_unimplemented_implicit))/db_enums * 100)))
384 total_checks = len(val_db.db_implemented_enums) + len(val_db.db_unimplemented_implicit)
385 print(" If all implicit checks are handled by parameter validation this is a total of %d (%s) checks covered." % (total_checks, "{0:.0f}%".format(float(total_checks)/db_enums * 100)))
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600386 if len(imp_not_found) == 0 and len(imp_not_claimed) == 0:
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600387 if not terse_mode:
388 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())
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600389 else:
Tobin Ehlis20e32582016-12-05 14:50:03 -0700390 result = 1
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600391 print(txt_color.red() + " Uh oh, Database claimed implemented don't match Source :(" + txt_color.endc())
392 if len(imp_not_found) != 0:
Tobin Ehlis3f0b2772016-11-18 16:56:15 -0700393 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 Ehlis35308dd2016-10-31 13:27:36 -0600394 for not_imp_enum in imp_not_found:
395 print(txt_color.red() + " %s" % (not_imp_enum) + txt_color.endc())
396 if len(imp_not_claimed) != 0:
397 print(txt_color.red() + " The following checks are implemented in source, but not claimed to be in Database:" + txt_color.endc())
398 for imp_enum in imp_not_claimed:
399 print(txt_color.red() + " %s" % (imp_enum) + txt_color.endc())
Mike Weiblenfe186122017-02-03 12:44:53 -0700400
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600401 if multiple_uses and not terse_mode:
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600402 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())
403 print(txt_color.yellow() + " Here is a list of each check used multiple times with its number of uses:" + txt_color.endc())
404 for enum in val_source.enum_count_dict:
Tobin Ehlis225b59c2016-12-22 13:59:42 -0700405 if val_source.enum_count_dict[enum]['count'] > 1 and enum not in duplicate_exceptions:
Tobin Ehlis3d1f2bd2016-12-22 11:19:15 -0700406 print(txt_color.yellow() + " %s: %d uses in file,line:" % (enum, val_source.enum_count_dict[enum]['count']) + txt_color.endc())
407 for file_line in val_source.enum_count_dict[enum]['file_line']:
408 print(txt_color.yellow() + " \t%s" % (file_line) + txt_color.endc())
Mike Weiblenfe186122017-02-03 12:44:53 -0700409
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600410 # Now check that tests claimed to be implemented are actual test names
411 bad_testnames = []
Tobin Ehlis9a68c982016-12-29 14:51:17 -0700412 tests_missing_enum = {} # Report tests that don't use validation error enum to check for error case
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600413 for enum in val_db.db_enum_to_tests:
414 for testname in val_db.db_enum_to_tests[enum]:
Tobin Ehlis9a68c982016-12-29 14:51:17 -0700415 if testname not in test_parser.test_to_errors:
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600416 bad_testnames.append(testname)
Tobin Ehlis9a68c982016-12-29 14:51:17 -0700417 else:
418 enum_found = False
419 for test_enum in test_parser.test_to_errors[testname]:
420 if test_enum == enum:
421 #print("Found test that correctly checks for enum: %s" % (enum))
422 enum_found = True
423 if not enum_found:
424 #print("Test %s is not using enum %s to check for error" % (testname, enum))
425 if testname not in tests_missing_enum:
426 tests_missing_enum[testname] = []
427 tests_missing_enum[testname].append(enum)
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600428 if tests_missing_enum and not terse_mode:
Tobin Ehlis9a68c982016-12-29 14:51:17 -0700429 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())
430 for testname in tests_missing_enum:
431 print(txt_color.yellow() + " Testname %s does not explicitly check for these ids:" % (testname) + txt_color.endc())
432 for enum in tests_missing_enum[testname]:
433 print(txt_color.yellow() + " %s" % (enum) + txt_color.endc())
Mike Weiblenfe186122017-02-03 12:44:53 -0700434
Tobin Ehlis9a68c982016-12-29 14:51:17 -0700435 # TODO : Go through all enums found in the test file and make sure they're correctly documented in the database file
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600436 if not terse_mode:
437 print(" Database file claims that %d checks have tests written." % len(val_db.db_enum_to_tests))
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600438 if len(bad_testnames) == 0:
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600439 if not terse_mode:
440 print(txt_color.green() + " All claimed tests have valid names. That's good!" + txt_color.endc())
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600441 else:
442 print(txt_color.red() + " The following testnames in Database appear to be invalid:")
Tobin Ehlis20e32582016-12-05 14:50:03 -0700443 result = 1
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600444 for bt in bad_testnames:
Tobin Ehlisb04c2c62016-11-21 15:51:45 -0700445 print(txt_color.red() + " %s" % (bt) + txt_color.endc())
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600446
Tobin Ehlis20e32582016-12-05 14:50:03 -0700447 return result
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600448
449if __name__ == "__main__":
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600450 sys.exit(main(sys.argv[1:]))
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600451