1*cda5da8dSAndroid Build Coastguard Worker"""Various utility functions.""" 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard Workerfrom collections import namedtuple, Counter 4*cda5da8dSAndroid Build Coastguard Workerfrom os.path import commonprefix 5*cda5da8dSAndroid Build Coastguard Worker 6*cda5da8dSAndroid Build Coastguard Worker__unittest = True 7*cda5da8dSAndroid Build Coastguard Worker 8*cda5da8dSAndroid Build Coastguard Worker_MAX_LENGTH = 80 9*cda5da8dSAndroid Build Coastguard Worker_PLACEHOLDER_LEN = 12 10*cda5da8dSAndroid Build Coastguard Worker_MIN_BEGIN_LEN = 5 11*cda5da8dSAndroid Build Coastguard Worker_MIN_END_LEN = 5 12*cda5da8dSAndroid Build Coastguard Worker_MIN_COMMON_LEN = 5 13*cda5da8dSAndroid Build Coastguard Worker_MIN_DIFF_LEN = _MAX_LENGTH - \ 14*cda5da8dSAndroid Build Coastguard Worker (_MIN_BEGIN_LEN + _PLACEHOLDER_LEN + _MIN_COMMON_LEN + 15*cda5da8dSAndroid Build Coastguard Worker _PLACEHOLDER_LEN + _MIN_END_LEN) 16*cda5da8dSAndroid Build Coastguard Workerassert _MIN_DIFF_LEN >= 0 17*cda5da8dSAndroid Build Coastguard Worker 18*cda5da8dSAndroid Build Coastguard Workerdef _shorten(s, prefixlen, suffixlen): 19*cda5da8dSAndroid Build Coastguard Worker skip = len(s) - prefixlen - suffixlen 20*cda5da8dSAndroid Build Coastguard Worker if skip > _PLACEHOLDER_LEN: 21*cda5da8dSAndroid Build Coastguard Worker s = '%s[%d chars]%s' % (s[:prefixlen], skip, s[len(s) - suffixlen:]) 22*cda5da8dSAndroid Build Coastguard Worker return s 23*cda5da8dSAndroid Build Coastguard Worker 24*cda5da8dSAndroid Build Coastguard Workerdef _common_shorten_repr(*args): 25*cda5da8dSAndroid Build Coastguard Worker args = tuple(map(safe_repr, args)) 26*cda5da8dSAndroid Build Coastguard Worker maxlen = max(map(len, args)) 27*cda5da8dSAndroid Build Coastguard Worker if maxlen <= _MAX_LENGTH: 28*cda5da8dSAndroid Build Coastguard Worker return args 29*cda5da8dSAndroid Build Coastguard Worker 30*cda5da8dSAndroid Build Coastguard Worker prefix = commonprefix(args) 31*cda5da8dSAndroid Build Coastguard Worker prefixlen = len(prefix) 32*cda5da8dSAndroid Build Coastguard Worker 33*cda5da8dSAndroid Build Coastguard Worker common_len = _MAX_LENGTH - \ 34*cda5da8dSAndroid Build Coastguard Worker (maxlen - prefixlen + _MIN_BEGIN_LEN + _PLACEHOLDER_LEN) 35*cda5da8dSAndroid Build Coastguard Worker if common_len > _MIN_COMMON_LEN: 36*cda5da8dSAndroid Build Coastguard Worker assert _MIN_BEGIN_LEN + _PLACEHOLDER_LEN + _MIN_COMMON_LEN + \ 37*cda5da8dSAndroid Build Coastguard Worker (maxlen - prefixlen) < _MAX_LENGTH 38*cda5da8dSAndroid Build Coastguard Worker prefix = _shorten(prefix, _MIN_BEGIN_LEN, common_len) 39*cda5da8dSAndroid Build Coastguard Worker return tuple(prefix + s[prefixlen:] for s in args) 40*cda5da8dSAndroid Build Coastguard Worker 41*cda5da8dSAndroid Build Coastguard Worker prefix = _shorten(prefix, _MIN_BEGIN_LEN, _MIN_COMMON_LEN) 42*cda5da8dSAndroid Build Coastguard Worker return tuple(prefix + _shorten(s[prefixlen:], _MIN_DIFF_LEN, _MIN_END_LEN) 43*cda5da8dSAndroid Build Coastguard Worker for s in args) 44*cda5da8dSAndroid Build Coastguard Worker 45*cda5da8dSAndroid Build Coastguard Workerdef safe_repr(obj, short=False): 46*cda5da8dSAndroid Build Coastguard Worker try: 47*cda5da8dSAndroid Build Coastguard Worker result = repr(obj) 48*cda5da8dSAndroid Build Coastguard Worker except Exception: 49*cda5da8dSAndroid Build Coastguard Worker result = object.__repr__(obj) 50*cda5da8dSAndroid Build Coastguard Worker if not short or len(result) < _MAX_LENGTH: 51*cda5da8dSAndroid Build Coastguard Worker return result 52*cda5da8dSAndroid Build Coastguard Worker return result[:_MAX_LENGTH] + ' [truncated]...' 53*cda5da8dSAndroid Build Coastguard Worker 54*cda5da8dSAndroid Build Coastguard Workerdef strclass(cls): 55*cda5da8dSAndroid Build Coastguard Worker return "%s.%s" % (cls.__module__, cls.__qualname__) 56*cda5da8dSAndroid Build Coastguard Worker 57*cda5da8dSAndroid Build Coastguard Workerdef sorted_list_difference(expected, actual): 58*cda5da8dSAndroid Build Coastguard Worker """Finds elements in only one or the other of two, sorted input lists. 59*cda5da8dSAndroid Build Coastguard Worker 60*cda5da8dSAndroid Build Coastguard Worker Returns a two-element tuple of lists. The first list contains those 61*cda5da8dSAndroid Build Coastguard Worker elements in the "expected" list but not in the "actual" list, and the 62*cda5da8dSAndroid Build Coastguard Worker second contains those elements in the "actual" list but not in the 63*cda5da8dSAndroid Build Coastguard Worker "expected" list. Duplicate elements in either input list are ignored. 64*cda5da8dSAndroid Build Coastguard Worker """ 65*cda5da8dSAndroid Build Coastguard Worker i = j = 0 66*cda5da8dSAndroid Build Coastguard Worker missing = [] 67*cda5da8dSAndroid Build Coastguard Worker unexpected = [] 68*cda5da8dSAndroid Build Coastguard Worker while True: 69*cda5da8dSAndroid Build Coastguard Worker try: 70*cda5da8dSAndroid Build Coastguard Worker e = expected[i] 71*cda5da8dSAndroid Build Coastguard Worker a = actual[j] 72*cda5da8dSAndroid Build Coastguard Worker if e < a: 73*cda5da8dSAndroid Build Coastguard Worker missing.append(e) 74*cda5da8dSAndroid Build Coastguard Worker i += 1 75*cda5da8dSAndroid Build Coastguard Worker while expected[i] == e: 76*cda5da8dSAndroid Build Coastguard Worker i += 1 77*cda5da8dSAndroid Build Coastguard Worker elif e > a: 78*cda5da8dSAndroid Build Coastguard Worker unexpected.append(a) 79*cda5da8dSAndroid Build Coastguard Worker j += 1 80*cda5da8dSAndroid Build Coastguard Worker while actual[j] == a: 81*cda5da8dSAndroid Build Coastguard Worker j += 1 82*cda5da8dSAndroid Build Coastguard Worker else: 83*cda5da8dSAndroid Build Coastguard Worker i += 1 84*cda5da8dSAndroid Build Coastguard Worker try: 85*cda5da8dSAndroid Build Coastguard Worker while expected[i] == e: 86*cda5da8dSAndroid Build Coastguard Worker i += 1 87*cda5da8dSAndroid Build Coastguard Worker finally: 88*cda5da8dSAndroid Build Coastguard Worker j += 1 89*cda5da8dSAndroid Build Coastguard Worker while actual[j] == a: 90*cda5da8dSAndroid Build Coastguard Worker j += 1 91*cda5da8dSAndroid Build Coastguard Worker except IndexError: 92*cda5da8dSAndroid Build Coastguard Worker missing.extend(expected[i:]) 93*cda5da8dSAndroid Build Coastguard Worker unexpected.extend(actual[j:]) 94*cda5da8dSAndroid Build Coastguard Worker break 95*cda5da8dSAndroid Build Coastguard Worker return missing, unexpected 96*cda5da8dSAndroid Build Coastguard Worker 97*cda5da8dSAndroid Build Coastguard Worker 98*cda5da8dSAndroid Build Coastguard Workerdef unorderable_list_difference(expected, actual): 99*cda5da8dSAndroid Build Coastguard Worker """Same behavior as sorted_list_difference but 100*cda5da8dSAndroid Build Coastguard Worker for lists of unorderable items (like dicts). 101*cda5da8dSAndroid Build Coastguard Worker 102*cda5da8dSAndroid Build Coastguard Worker As it does a linear search per item (remove) it 103*cda5da8dSAndroid Build Coastguard Worker has O(n*n) performance.""" 104*cda5da8dSAndroid Build Coastguard Worker missing = [] 105*cda5da8dSAndroid Build Coastguard Worker while expected: 106*cda5da8dSAndroid Build Coastguard Worker item = expected.pop() 107*cda5da8dSAndroid Build Coastguard Worker try: 108*cda5da8dSAndroid Build Coastguard Worker actual.remove(item) 109*cda5da8dSAndroid Build Coastguard Worker except ValueError: 110*cda5da8dSAndroid Build Coastguard Worker missing.append(item) 111*cda5da8dSAndroid Build Coastguard Worker 112*cda5da8dSAndroid Build Coastguard Worker # anything left in actual is unexpected 113*cda5da8dSAndroid Build Coastguard Worker return missing, actual 114*cda5da8dSAndroid Build Coastguard Worker 115*cda5da8dSAndroid Build Coastguard Workerdef three_way_cmp(x, y): 116*cda5da8dSAndroid Build Coastguard Worker """Return -1 if x < y, 0 if x == y and 1 if x > y""" 117*cda5da8dSAndroid Build Coastguard Worker return (x > y) - (x < y) 118*cda5da8dSAndroid Build Coastguard Worker 119*cda5da8dSAndroid Build Coastguard Worker_Mismatch = namedtuple('Mismatch', 'actual expected value') 120*cda5da8dSAndroid Build Coastguard Worker 121*cda5da8dSAndroid Build Coastguard Workerdef _count_diff_all_purpose(actual, expected): 122*cda5da8dSAndroid Build Coastguard Worker 'Returns list of (cnt_act, cnt_exp, elem) triples where the counts differ' 123*cda5da8dSAndroid Build Coastguard Worker # elements need not be hashable 124*cda5da8dSAndroid Build Coastguard Worker s, t = list(actual), list(expected) 125*cda5da8dSAndroid Build Coastguard Worker m, n = len(s), len(t) 126*cda5da8dSAndroid Build Coastguard Worker NULL = object() 127*cda5da8dSAndroid Build Coastguard Worker result = [] 128*cda5da8dSAndroid Build Coastguard Worker for i, elem in enumerate(s): 129*cda5da8dSAndroid Build Coastguard Worker if elem is NULL: 130*cda5da8dSAndroid Build Coastguard Worker continue 131*cda5da8dSAndroid Build Coastguard Worker cnt_s = cnt_t = 0 132*cda5da8dSAndroid Build Coastguard Worker for j in range(i, m): 133*cda5da8dSAndroid Build Coastguard Worker if s[j] == elem: 134*cda5da8dSAndroid Build Coastguard Worker cnt_s += 1 135*cda5da8dSAndroid Build Coastguard Worker s[j] = NULL 136*cda5da8dSAndroid Build Coastguard Worker for j, other_elem in enumerate(t): 137*cda5da8dSAndroid Build Coastguard Worker if other_elem == elem: 138*cda5da8dSAndroid Build Coastguard Worker cnt_t += 1 139*cda5da8dSAndroid Build Coastguard Worker t[j] = NULL 140*cda5da8dSAndroid Build Coastguard Worker if cnt_s != cnt_t: 141*cda5da8dSAndroid Build Coastguard Worker diff = _Mismatch(cnt_s, cnt_t, elem) 142*cda5da8dSAndroid Build Coastguard Worker result.append(diff) 143*cda5da8dSAndroid Build Coastguard Worker 144*cda5da8dSAndroid Build Coastguard Worker for i, elem in enumerate(t): 145*cda5da8dSAndroid Build Coastguard Worker if elem is NULL: 146*cda5da8dSAndroid Build Coastguard Worker continue 147*cda5da8dSAndroid Build Coastguard Worker cnt_t = 0 148*cda5da8dSAndroid Build Coastguard Worker for j in range(i, n): 149*cda5da8dSAndroid Build Coastguard Worker if t[j] == elem: 150*cda5da8dSAndroid Build Coastguard Worker cnt_t += 1 151*cda5da8dSAndroid Build Coastguard Worker t[j] = NULL 152*cda5da8dSAndroid Build Coastguard Worker diff = _Mismatch(0, cnt_t, elem) 153*cda5da8dSAndroid Build Coastguard Worker result.append(diff) 154*cda5da8dSAndroid Build Coastguard Worker return result 155*cda5da8dSAndroid Build Coastguard Worker 156*cda5da8dSAndroid Build Coastguard Workerdef _count_diff_hashable(actual, expected): 157*cda5da8dSAndroid Build Coastguard Worker 'Returns list of (cnt_act, cnt_exp, elem) triples where the counts differ' 158*cda5da8dSAndroid Build Coastguard Worker # elements must be hashable 159*cda5da8dSAndroid Build Coastguard Worker s, t = Counter(actual), Counter(expected) 160*cda5da8dSAndroid Build Coastguard Worker result = [] 161*cda5da8dSAndroid Build Coastguard Worker for elem, cnt_s in s.items(): 162*cda5da8dSAndroid Build Coastguard Worker cnt_t = t.get(elem, 0) 163*cda5da8dSAndroid Build Coastguard Worker if cnt_s != cnt_t: 164*cda5da8dSAndroid Build Coastguard Worker diff = _Mismatch(cnt_s, cnt_t, elem) 165*cda5da8dSAndroid Build Coastguard Worker result.append(diff) 166*cda5da8dSAndroid Build Coastguard Worker for elem, cnt_t in t.items(): 167*cda5da8dSAndroid Build Coastguard Worker if elem not in s: 168*cda5da8dSAndroid Build Coastguard Worker diff = _Mismatch(0, cnt_t, elem) 169*cda5da8dSAndroid Build Coastguard Worker result.append(diff) 170*cda5da8dSAndroid Build Coastguard Worker return result 171