def _dir(self, obj=None): if obj is None: obj = self results = [(i, getattr(obj, i)) for i in builtins.dir(obj) if i[0] != "_"] print("[" + "{}, ".format(color()).join( _dir_color(i[1]) + i[0] for i in results) + color() + "]")
def info(self): '''Displays verbose information about the transaction, including decoded event logs.''' if self.contract_address: line = "New Contract Address: "+self.contract_address else: line = "To: {0.receiver}\nValue: {0.value}".format(self) if self.input != "0x00": line += "\nFunction: {}".format(self.fn_name) formatted = (TX_INFO.format( self, self.sender if type(self.sender) is str else self.sender.address, line, "" if self.status else " ({0[error]}{1}{0})".format( color, self.revert_msg or "reverted" ), self.gas_used / self.gas_limit )) color.print_colors(formatted) if self.events: print(" Events In This Transaction\n --------------------------") for event in self.events: print(" "+color('bright yellow')+event['name']+color()) for i in event['data']: color.print_colors( " {0[name]}: {0[value]}".format(i), value=None if i['decoded'] else "dull" ) print()
def _print_path(trace, idx, sep): col = "error" if trace['op'] in ("REVERT", "INVALID") else "pending" name = "{0[contractName]}.{1}{0[fn]}".format(trace, color(col)) print( (" "*sep*trace['depth']) + (" "*(trace['jumpDepth']-1)) + "{}{} {}{} ({})".format(color(col), name, color('dull'), idx, trace['address']) + color() )
def _dir_color(obj): if type(obj).__name__ == "module": return color('module') try: if issubclass(type(obj), _ContractBase): return color('contract') if issubclass(type(obj), _ContractMethod): return color('contract_method') except TypeError: pass if not callable(obj): return color('value') return color('callable')
def _step_print(step, last_step, indent, start, stop): print_str = f"\n{color['dull']}" if indent is not None: print_str += f"{indent}\u2500" if last_step['op'] in {"REVERT", "INVALID"} and _step_compare( step, last_step): contract_color = color("error") else: contract_color = color( "contract_method" if not step['jumpDepth'] else "") print_str += f"{contract_color}{step['fn']} {color['dull']}{start}:{stop}{color}" if not step['jumpDepth']: print_str += f" {color['dull']}({color}{step['address']}{color['dull']}){color}" return print_str
def _step_print(step: Dict, last_step: Any, indent: Any, start: int, stop: int) -> str: print_str = f"\n{color['dull']}" if indent is not None: print_str += f"{indent}\u2500" if last_step["op"] in {"REVERT", "INVALID"} and _step_compare(step, last_step): contract_color = color("error") else: contract_color = color("contract_method" if not step["jumpDepth"] else "") print_str += f"{contract_color}{step['fn']} {color['dull']}{start}:{stop}{color}" if not step["jumpDepth"]: print_str += ( f" {color['dull']}({color}{step['address']}{color['dull']}){color}" ) return print_str
def _run_test(module, fn_name, count, total): fn = getattr(module, fn_name) desc = fn.__doc__ or fn_name sys.stdout.write(" {1} - {0} ({1}/{2})... ".format(desc, count, total)) sys.stdout.flush() if fn.__defaults__: args = dict(zip( fn.__code__.co_varnames[:len(fn.__defaults__)], fn.__defaults__ )) if 'skip' in args and args['skip']: sys.stdout.write( "\r {0[pending]}\u229d{0[dull]} {1} ".format(color, desc) + "({0[pending]}skipped{0[dull]}){0}\n".format(color) ) return [] else: args = {} try: stime = time.time() fn() if 'pending' in args and args['pending']: raise ExpectedFailing("Test was expected to fail") sys.stdout.write("\r {0[success]}\u2713{0} {3} - {1} ({2:.4f}s)\n".format( color, desc, time.time()-stime, count )) sys.stdout.flush() return [] except Exception as e: if type(e) != ExpectedFailing and 'pending' in args and args['pending']: c = [color('success'), color('dull'), color()] else: c = [color('error'), color('dull'), color()] sys.stdout.write("\r {0[0]}{1}{0[1]} {2} ({0[0]}{3}{0[1]}){0[2]}\n".format( c, '\u2717' if type(e) in ( AssertionError, VirtualMachineError ) else '\u203C', desc, type(e).__name__, )) sys.stdout.flush() if type(e) != ExpectedFailing and 'pending' in args and args['pending']: return [] filename = str(Path(module.__file__).relative_to(CONFIG['folders']['project'])) fn_name = filename[:-2]+fn_name return [(fn_name, color.format_tb(sys.exc_info(), filename))]
def test_bright_dark(): assert color('yellow') != color('dark yellow') != color('bright yellow') != ""
def test_call_getitem(win32): assert color('success') == color['success'] == "" assert str(color) == "" win32.off() assert color('success') == color['success'] != "" assert str(color) == "\x1b[0;m"
def test_no_colors_on_windows(win32): a = color('error') assert not a win32.off() assert a != color('error')
def _cov_color(pct): return color(next(i[1] for i in COVERAGE_COLORS if pct <= i[0]))
def __repr__(self): c = {-1: 'pending', 0: 'error', 1: None} return "<Transaction object '{}{}{}'>".format( color(c[self.status]), self.txid, color )
def test_bright_dark(): assert color("yellow") != color("dark yellow") != color( "bright yellow") != ""
def test_call_getitem(): assert color("success") == color["success"] != "" assert str(color) == "\x1b[0;m"
def main(): args = docopt(__doc__) test_files = get_test_files(args['<filename>']) if len(test_files) == 1 and args['<range>']: try: idx = args['<range>'] if ':' in idx: idx = slice(*[int(i) - 1 for i in idx.split(':')]) else: idx = slice(int(idx) - 1, int(idx)) except: sys.exit( "{0[error]}ERROR{0}: Invalid range. Must be an integer or slice (eg. 1:4)" .format(color)) elif args['<range>']: sys.exit( "{0[error]}ERROR:{0} Cannot specify a range when running multiple tests files." .format(color)) else: idx = slice(0, None) compiled = compile_contracts( Path(CONFIG['folders']['project']).joinpath('contracts')) fn_map, line_map = get_coverage_map(compiled) network.connect(config.ARGV['network'], True) if args['--always-transact']: CONFIG['test']['always_transact'] = True print("Contract calls will be handled as: {0[value]}{1}{0}".format( color, "transactions" if CONFIG['test']['always_transact'] else "calls")) for filename in test_files: history, tb = run_test(filename, network, idx) if tb: sys.exit("\n{0[error]}ERROR{0}: Cannot ".format(color) + "calculate coverage while tests are failing\n") for tx in history: if not tx.receiver: continue for i in range(len(tx.trace)): t = tx.trace[i] pc = t['pc'] name = t['contractName'] if not name: continue try: # find the function map item and record the tx fn = next(i for i in fn_map[name] if pc in i['pc']) fn['tx'].add(tx) if t['op'] != "JUMPI": # if not a JUMPI, find the line map item and record ln = next(i for i in line_map[name] if pc in i['pc']) ln['tx'].add(tx) continue # if a JUMPI, we need to have hit the jump pc AND a related opcode ln = next(i for i in line_map[name] if pc == i['jump']) if tx not in ln['tx']: continue # if the next opcode is not pc+1, the JUMPI was executed truthy key = 'false' if tx.trace[i + 1]['pc'] == pc + 1 else 'true' ln[key].add(tx) # pc didn't exist in map except StopIteration: continue for ln in [x for v in line_map.values() for x in v]: if ln['jump']: ln['jump'] = [len(ln.pop('true')), len(ln.pop('false'))] ln['count'] = len(ln.pop('tx')) del ln['pc'] for contract in fn_map: for fn in fn_map[contract].copy(): fn['count'] = len(fn.pop('tx')) del fn['pc'] line_fn = [ i for i in line_map[contract] if i['method'] == fn['method'] ] if not fn['count'] or not [i for i in line_fn if i['count']]: for ln in line_fn: line_map[contract].remove(ln) elif line_fn: fn_map[contract].remove(fn) fn_map[contract].extend(line_map[contract]) json.dump(fn_map, Path(CONFIG['folders']['project']).joinpath( "build/coverage.json").open('w'), sort_keys=True, indent=4) print("\nCoverage analysis complete!\n") for contract in fn_map: fn_list = sorted( set(i['method'] for i in fn_map[contract] if i['method'])) if not fn_list: continue if not [i for i in fn_map[contract] if i['count']]: print(" contract: {0[contract]}{1}{0} - {0[bright red]}0.0%{0}". format(color, contract)) continue print(" contract: {0[contract]}{1}{0}".format(color, contract)) for fn in fn_list: map_ = [i for i in fn_map[contract] if i['method'] == fn] count = 0 for i in map_: if not i['count']: continue if not i['jump']: count += 1 continue if i['jump'][0]: count += 1 if i['jump'][1]: count += 1 total = sum([1 if not i['jump'] else 2 for i in map_]) pct = count / total c = next(i[1] for i in COVERAGE_COLORS if pct <= i[0]) print(" {0[contract_method]}{1}{0} - {2}{3:.1%}{0}".format( color, fn, color(c), pct)) print() print( "\nDetailed results saved to {0[string]}build/coverage.json{0}".format( color))