def compare_ordered_element(a_element, b_element, *args, **kwargs): """ Compares two element trees and returns (same,message) where same is true if they are the same, and message is a list of the differences @ In, a_element, ET.Element, the first element tree @ In, b_element, ET.Element, the second element tree @ In, args, dict, arguments @ In, kwargs, dict, keyword arguments accepted args: - none - accepted kwargs: path: a string to describe where the element trees are located (mainly used recursively) @ Out, compare_ordered_element, (bool,[string]), results of comparison """ same = True message = [] options = kwargs path = kwargs.get('path', '') counter = kwargs.get('counter', 0) DU.set_default_options(options) def fail_message(*args): """ adds the fail message to the list @ In, args, list, The arguments to the fail message (will be converted with str()) @ Out, fail_message, (bool,string), results """ print_args = [path] print_args.extend(args) args_expanded = " ".join([str(x) for x in print_args]) message.append(args_expanded) if a_element.tag != b_element.tag: same = False fail_message("mismatch tags ", a_element.tag, b_element.tag) else: path += a_element.tag + "/" if a_element.text != b_element.text: succeeded, note = cswf( a_element.text, b_element.text, rel_err=options["rel_err"], zero_threshold=options["zero_threshold"], remove_whitespace=options["remove_whitespace"], remove_unicode_identifier=options["remove_unicode_identifier"]) if not succeeded: same = False fail_message(note) return (same, message) different_keys = set(a_element.keys()).symmetric_difference( set(b_element.keys())) same_keys = set(a_element.keys()).intersection(set(b_element.keys())) if len(different_keys) != 0: same = False fail_message("mismatch attribute keys ", different_keys) for key in same_keys: if a_element.attrib[key] != b_element.attrib[key]: same = False fail_message("mismatch attribute ", key, a_element.attrib[key], b_element.attrib[key]) if len(a_element) != len(b_element): same = False fail_message("mismatch number of children ", len(a_element), len(b_element)) else: if a_element.tag == b_element.tag: #find all matching XML paths #WARNING: this will mangle the XML, so other testing should happen above this! found = [] for i in range(len(a_element)): sub_options = dict(options) sub_options["path"] = path (same_child, _) = compare_ordered_element(a_element[i], b_element[i], *args, **sub_options) if same_child: found.append((a_element[i], b_element[i])) same = same and same_child #prune matches from trees for children in found: a_element.remove(children[0]) b_element.remove(children[1]) #once all pruning done, error on any remaining structure if counter == 0: #on head now, recursion is finished if len(a_element) > 0: a_string = ET.tostring(a_element) if len(a_string) > 80: message.append( 'Branches in gold not matching test...\n{}'.format( path)) else: message.append( 'Branches in gold not matching test...\n{} {}'. format(path, a_string)) if len(b_element) > 0: b_string = ET.tostring(b_element) if len(b_string) > 80: message.append( 'Branches in test not matching gold...\n{}'.format( path)) else: message.append( 'Branches in test not matching gold...\n{} {}'. format(path, b_string)) return (same, message)
def diff(self): """ Run the comparison. returns (same,messages) where same is true if the image files are the same, and messages is a string with all the differences. In, None Out, None """ # read in files files_read = False for outfile in self.__out_file: test_filename = os.path.join(self.__test_dir, outfile) gold_filename = os.path.join(self.__test_dir, 'gold', outfile) if not os.path.exists(test_filename): self.__same = False self.__messages += 'Test file does not exist: ' + test_filename elif not os.path.exists(gold_filename): self.__same = False self.__messages += 'Gold file does not exist: ' + gold_filename else: files_read = True #read in files if files_read: if not correctImport: self.__messages += 'ImageDiff cannot run with scipy version less '+\ 'than 0.15.0, and requires the PIL installed; scipy version is '+\ str(scipy.__version__) self.__same = False return (self.__same, self.__messages) try: # RAK - The original line... # test_image = imread(open(test_filename,'r')) # ...didn't work on Windows Python because it couldn't sense the file type test_image = imread(test_filename) except IOError: self.__messages += 'Unrecognized file type for test image in scipy.imread: ' + test_filename files_read = False return (False, self.__messages) try: # RAK - The original line... # gold_image = imread(open(gold_filename,'r')) # ...didn't work on Windows Python because it couldn't sense the file type gold_image = imread(gold_filename) except IOError: files_read = False self.__messages += 'Unrecognized file type for test image in scipy.imread: ' + gold_filename return (False, self.__messages) #first check dimensionality if gold_image.shape != test_image.shape: self.__messages += 'Gold and test image are not the same shape: '+\ str(gold_image.shape)+', '+str(test_image.shape) self.__same = False return (self.__same, self.__messages) #set default options DU.set_default_options(self.__options) #pixelwise comparison #TODO in the future we can add greyscale, normalized coloring, etc. # For now just do raw comparison of right/wrong pixels diff = gold_image - test_image only_diffs = diff[abs(diff) > self.__options['zero_threshold']] pct_num_diff = only_diffs.size / float(diff.size) if pct_num_diff > self.__options['rel_err']: self.__messages += 'Difference between images is too large:'+\ ' %2.2f pct (allowable: %2.2f)' %(100*pct_num_diff, 100*self.__options['rel_err']) self.__same = False return (self.__same, self.__messages)
def compare_unordered_element(a_element, b_element, **kwargs): """ Compares two element trees and returns (same,message) where same is true if they are the same, and message is a list of the differences. Uses list of tree entries to find best match, instead of climbing the tree @ In, a_element, ET.Element, the first element @ In, b_element, ET.Element, the second element @ Out, compare_unordered_element, (bool,[string]), results of comparison """ same = True message = [] options = kwargs matchvals = {} diffs = {} DU.set_default_options(options) def fail_message(*args): """ adds the fail message to the list @ In, args, list, The arguments to the fail message (will be converted with str()) @ Out, fail_message, (bool,string), results """ print_args = [] print_args.extend(args) args_expanded = " ".join([str(x) for x in print_args]) message.append(args_expanded) if a_element.text != b_element.text: succeeded, note = cswf( a_element.text, b_element.text, rel_err=options["rel_err"], zero_threshold=options["zero_threshold"], remove_whitespace=options["remove_whitespace"], remove_unicode_identifier=options["remove_unicode_identifier"]) if not succeeded: same = False fail_message(note) return (same, message) a_list = tree_to_list(a_element) b_list = tree_to_list(b_element) #search a for matches in b for a_entry in a_list: matchvals[a_entry] = {} diffs[a_entry] = {} for b_entry in b_list: same, matchval, diff = compare_list_entry(a_entry, b_entry, **options) if same: b_list.remove(b_entry) del matchvals[a_entry] del diffs[a_entry] #since we found the match, remove from other near matches for close_key in diffs: if b_entry in diffs[close_key].keys(): del diffs[close_key][b_entry] del matchvals[close_key][b_entry] break matchvals[a_entry][b_entry] = matchval diffs[a_entry][b_entry] = diff if len(matchvals) == 0: #all matches found return (True, '') note = '' for unmatched, close in matchvals.items(): #print the path without a match path = '/'.join(list(m.tag for m in unmatched)) note += 'No match for gold node {}\n'.format(path) note += ' tag: {}\n'.format(unmatched[-1].tag) note += ' attr: {}\n'.format(unmatched[-1].attrib) note += ' text: {}\n'.format(unmatched[-1].text) #print the tree of the nearest match note += ' Nearest unused match: ' close = sorted(list(close.items()), key=lambda x: x[1], reverse=True) if close: closest = '/'.join(list(c.tag for c in close[0][0])) else: closest = '-none found-' note += ' ' + closest + '\n' #print what was different between them if len(close): diff = diffs[unmatched][close[0][0]] for b_diff, code, right, miss in diff: if b_diff is None: b_diff = str(b_diff) if code is None: code = str(code) if right is None: right = str(right) if miss is None: miss = str(miss) if code == XMLDiff.missingChildNode: note += ' <' + b_diff.tag + '> is missing child node: <' + right + '> vs <' + miss + '>\n' elif code == XMLDiff.missingAttribute: note += ' <' + b_diff.tag + '> is missing attribute: "' + right + '"\n' elif code == XMLDiff.extraChildNode: note += ' <' + b_diff.tag + '> has extra child node: <' + right + '>\n' elif code == XMLDiff.extraAttribute: note += ' <'+b_diff.tag+'> has extra attribute: "'+right+\ '" = "'+b_diff.attrib[right]+'"\n' elif code == XMLDiff.notMatchTag: note += ' <' + b_diff.tag + '> tag does not match: <' + right + '> vs <' + miss + '>\n' elif code == XMLDiff.notMatchAttribute: note += ' <'+b_diff.tag+'> attribute does not match: "'+right[1]+\ '" = "'+right[0].attrib[right[1]]+'" vs "'+miss[0].attrib[miss[1]]+'"\n' elif code == XMLDiff.notMatchText: note += ' <' + b_diff.tag + '> text does not match: "' + right + '" vs "' + miss + '"\n' else: note += ' UNRECOGNIZED OPTION: "'+b_diff.tag+'" "'+str(code)+\ '": "'+str(right)+'" vs "'+str(miss)+'"\n' return (False, [note])
def compare_list_entry(a_list, b_list, **kwargs): """ Comparse flattened XML entries for equality return bool is True if all tag, text, and attributes match, False otherwise return qual is percent of matching terms @ In, a_list, list(ET.Element), first set @ In, b_list, list(ET.Element), second set @ Out, compare_list_entry, (bool,val), results """ num_match = 0 #number of matching points between entries total_matchable = 0 #total tag, text, and attributes available to match match = True #True if entries match diff = [ ] #tuple of (element, diff code, correct (a) value, test (b) value) options = kwargs for i in range(len(a_list)): if i > len(b_list) - 1: match = False diff.append( (b_list[-1], XMLDiff.missingChildNode, a_list[i].tag, None)) #could have matched the tag and attributes total_matchable += 1 + len(a_list[i].attrib.keys()) #if text isn't empty, could have matched text, too if a_list[i].text is not None and len(a_list[i].text.strip()) > 0: total_matchable += 1 continue a_item = a_list[i] b_item = b_list[i] #match tag same, _ = DU.compare_strings_with_floats( a_item.tag, b_item.tag, options["rel_err"], options["zero_threshold"], options["remove_whitespace"], options["remove_unicode_identifier"]) total_matchable += 1 if not same: match = False diff.append((b_item, XMLDiff.notMatchTag, a_item.tag, b_item.tag)) else: num_match += 1 #match text #if (a_item.text is None or len(a_item.text)>0) and (b_item.text is None or len(b_item.text)>0): same, _ = DU.compare_strings_with_floats( a_item.text, b_item.text, options["rel_err"], options["zero_threshold"], options["remove_whitespace"], options["remove_unicode_identifier"]) if not same: match = False diff.append((b_item, XMLDiff.notMatchText, str(a_item.text), str(b_item.text))) total_matchable += 1 else: if not (a_item.text is None or a_item.text.strip() != ''): num_match += 1 total_matchable += 1 #match attributes for attrib in a_item.attrib.keys(): total_matchable += 1 if attrib not in b_item.attrib.keys(): match = False diff.append((b_item, XMLDiff.missingAttribute, attrib, None)) continue same, _ = DU.compare_strings_with_floats( a_item.attrib[attrib], b_item.attrib[attrib], options["rel_err"], options["zero_threshold"], options["remove_whitespace"], options["remove_unicode_identifier"]) if not same: match = False diff.append((b_item, XMLDiff.notMatchAttribute, (a_item, attrib), (b_item, attrib))) else: num_match += 1 #note attributes in b_item not in a_item for attrib in b_item.attrib.keys(): if attrib not in a_item.attrib.keys(): match = False diff.append((b_item, XMLDiff.extraAttribute, attrib, None)) total_matchable += 1 # note elements in b not in a if len(b_list) > len(a_list): match = False i = len(a_list) - 1 for j in range(i, len(b_list)): diff.append( (a_list[-1], XMLDiff.extraChildNode, b_list[j].tag, None)) #count tag and attributes total_matchable += 1 + len(b_list[j].attrib.keys()) #if text isn't empty, count text, too if b_list[i].text is not None and len(b_list[i].text.strip()) > 0: total_matchable += 1 return (match, float(num_match) / float(total_matchable), diff)