예제 #1
0
 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() + "]")
예제 #2
0
 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()
예제 #3
0
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()
    )
예제 #4
0
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')
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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))]
예제 #8
0
def test_bright_dark():
    assert color('yellow') != color('dark yellow') != color('bright yellow') != ""
예제 #9
0
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"
예제 #10
0
def test_no_colors_on_windows(win32):
    a = color('error')
    assert not a
    win32.off()
    assert a != color('error')
예제 #11
0
def _cov_color(pct):
    return color(next(i[1] for i in COVERAGE_COLORS if pct <= i[0]))
예제 #12
0
 def __repr__(self):
     c = {-1: 'pending', 0: 'error', 1: None}
     return "<Transaction object '{}{}{}'>".format(
         color(c[self.status]), self.txid, color
     )
예제 #13
0
def test_bright_dark():
    assert color("yellow") != color("dark yellow") != color(
        "bright yellow") != ""
예제 #14
0
def test_call_getitem():
    assert color("success") == color["success"] != ""
    assert str(color) == "\x1b[0;m"
예제 #15
0
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))