From f3a9999fb56acca4bf005a2b0bfa56821c6d4565 Mon Sep 17 00:00:00 2001 From: Josselin Date: Fri, 30 Nov 2018 06:25:59 +0000 Subject: [PATCH] Use deepdiff library to compare json output instead of custom script --- scripts/json_diff.py | 17 +++++ scripts/pretty_print_and_sort_json.py | 101 ------------------------- scripts/travis_install.sh | 2 + scripts/travis_test.sh | 47 +++++------- slither/detectors/abstract_detector.py | 5 +- 5 files changed, 43 insertions(+), 129 deletions(-) create mode 100644 scripts/json_diff.py delete mode 100644 scripts/pretty_print_and_sort_json.py diff --git a/scripts/json_diff.py b/scripts/json_diff.py new file mode 100644 index 000000000..80885becd --- /dev/null +++ b/scripts/json_diff.py @@ -0,0 +1,17 @@ +import sys +import json +from deepdiff import DeepDiff # pip install deepdiff +from pprint import pprint + +if len(sys.argv) !=3: + print('Usage: python json_diff.py 1.json 2.json') + exit(-1) + +with open(sys.argv[1]) as f: + d1 = json.load(f) + +with open(sys.argv[2]) as f: + d2 = json.load(f) + + +pprint(DeepDiff(d1, d2, ignore_order=True, verbose_level=2)) diff --git a/scripts/pretty_print_and_sort_json.py b/scripts/pretty_print_and_sort_json.py deleted file mode 100644 index 08c0002b2..000000000 --- a/scripts/pretty_print_and_sort_json.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/python3 - -''' -the purpose of this file is to sort the json output from the detectors such that -the order is deterministic - -- the keys of a json object are sorted -- json objects in a list will be sorted based on the values of their keys -- lists of strings/numbers are sorted - -''' - -import sys -import json - -raw_json_file = sys.argv[1] -pretty_json_file = sys.argv[2] - -from collections import OrderedDict - -def create_property_val_tuple(d, props_info): - p_names = props_info[0] - p_types = props_info[1] - result = [] - for p in p_names: - if not p in d: # not all objects have the same keys - if p_types[p] is 'number': - result.append(0) # to make sorting work - if p_types[p] is 'string': - result.append("") # to make sorting work - else: - result.append(d[p]) - return tuple(result) - -def get_props_info(list_of_dicts): - found_props = set() - prop_types = dict() - - # gather all prop names - for d in list_of_dicts: - for p in d: - found_props.add(p) - - # create a copy, since we are gonna possibly remove props - props_whose_value_we_can_sort_on = set(found_props) - - # for each object, loop through list of all found property names, - # if the object contains that property, check that it's of type string or number - # if it is, save it's type (used later on for sorting with objects that don't have that property) - # if it's not of type string/number remove it from list of properties to check - # since we cannot sort on non-string/number values - for p in list(found_props): - if p in props_whose_value_we_can_sort_on: # short circuit - for d in list_of_dicts: - if p in props_whose_value_we_can_sort_on: # less shorter short circuit - if p in d: - # we ae only gonna sort key values if they are of type string or number - if not isinstance(d[p], str) and not isinstance(d[p], int): - props_whose_value_we_can_sort_on.remove(p) - # we need to store the type of the value because not each object - # in a list of output objects for 1 detector will have the same - # keys, so if we want to sort based on the values then if a certain object - # does not have a key which another object does have we are gonna - # put in 0 for number and "" for string for that key such that sorting on values - # still works - elif isinstance(d[p], str): - prop_types[p] = 'string' - elif isinstance(d[p], int): - prop_types[p] = 'number' - return (sorted(list(props_whose_value_we_can_sort_on)), prop_types) - -def order_by_prop_value(list_of_dicts): - props_info = get_props_info(list_of_dicts) - return sorted(list_of_dicts, key=lambda d: create_property_val_tuple(d, props_info)) - -def order_dict(d): - result = OrderedDict() # such that we keep the order - for k, v in sorted(d.items()): - if isinstance(v, dict): - result[k] = order_dict(v) - elif type(v) is list: - result[k] = order_list(v) - else: # string/number - result[k] = v - return result - -def order_list(l): - if not l: - return [] - if isinstance(l[0], str): # it's a list of string - return sorted(l) - elif isinstance(l[0], int): # it's a list of numbers - return sorted(l) - elif isinstance(l[0], dict): # it's a list of objects - ordered_by_key = [order_dict(v) for v in l] - ordered_by_val = order_by_prop_value(ordered_by_key) - return ordered_by_val - -with open(raw_json_file, 'r') as json_data: - with open(pretty_json_file, 'w') as out_file: - out_file.write(json.dumps(order_list(json.load(json_data)), sort_keys=False, indent=4, separators=(',',': '))) diff --git a/scripts/travis_install.sh b/scripts/travis_install.sh index 21387729f..2525ac704 100755 --- a/scripts/travis_install.sh +++ b/scripts/travis_install.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash python setup.py install +# Used by travis_test.sh +pip install deepdiff function install_solc { sudo wget -O /usr/bin/solc https://github.com/ethereum/solidity/releases/download/v0.4.24/solc-static-linux diff --git a/scripts/travis_test.sh b/scripts/travis_test.sh index 9283218ff..5d555af6c 100755 --- a/scripts/travis_test.sh +++ b/scripts/travis_test.sh @@ -8,7 +8,6 @@ DIR="$(cd "$(dirname "$0")" && pwd)" test_slither(){ expected="$DIR/../tests/expected_json/$(basename $1 .sol).$2.json" - actual="$DIR/$(basename $1 .sol).$2.json" # run slither detector on input file and save output as json slither "$1" --disable-solc-warnings --detect "$2" --json "$DIR/tmp-test.json" @@ -18,56 +17,50 @@ test_slither(){ exit -1 fi - # convert json file to pretty print and write to destination folder - python "$DIR/pretty_print_and_sort_json.py" "$DIR/tmp-test.json" "$actual" - - # remove the raw un-prettified json file - rm "$DIR/tmp-test.json" - - if [ ! -f "$expected" ]; then - rm "$actual" + if [ ! -f "$DIR/tmp-test.json" ]; then echo "" - echo "Missing expected file" + echo "Missing generated file" echo "" - echo "$expected" exit 1 - fi + fi - result=$(diff "$expected" "$actual") + result=$(python "$DIR/json_diff.py" "$expected" "$DIR/tmp-test.json") - if [ "$result" != "" ]; then - rm "$actual" + rm "$DIR/tmp-test.json" + if [ "$result" != "{}" ]; then echo "" echo "failed test of file: $1, detector: $2" echo "" echo "$result" echo "" exit 1 - else - rm "$actual" fi # run slither detector on input file and save output as json slither "$1" --disable-solc-warnings --detect "$2" --compact-ast --json "$DIR/tmp-test.json" + if [ $? -eq 255 ] + then + echo "Slither crashed" + exit -1 + fi - # convert json file to pretty print and write to destination folder - python "$DIR/pretty_print_and_sort_json.py" "$DIR/tmp-test.json" "$actual" - - # remove the raw un-prettified json file - rm "$DIR/tmp-test.json" + if [ ! -f "$DIR/tmp-test.json" ]; then + echo "" + echo "Missing generated file" + echo "" + exit 1 + fi - result=$(diff "$expected" "$actual") + result=$(python "$DIR/json_diff.py" "$expected" "$DIR/tmp-test.json") - if [ "$result" != "" ]; then - rm "$actual" + rm "$DIR/tmp-test.json" + if [ "$result" != "{}" ]; then echo "" echo "failed test of file: $1, detector: $2" echo "" echo "$result" echo "" exit 1 - else - rm "$actual" fi } diff --git a/slither/detectors/abstract_detector.py b/slither/detectors/abstract_detector.py index 92d2aa90f..252cd41c1 100644 --- a/slither/detectors/abstract_detector.py +++ b/slither/detectors/abstract_detector.py @@ -3,6 +3,7 @@ import re from slither.utils.colors import green, yellow, red +from collections import OrderedDict class IncorrectDetectorInitialization(Exception): pass @@ -81,7 +82,9 @@ class AbstractDetector(metaclass=abc.ABCMeta): return classification_colors[self.IMPACT] def generate_json_result(self): - return {'check': self.ARGUMENT} + d = OrderedDict() + d['check'] = self.ARGUMENT + return d @staticmethod def add_variable_to_json(variable, d):