#!/usr/bin/env python3 import sys import os import re from subprocess import Popen, PIPE import types if sys.version_info.major == 2: import copy_reg as copyreg else: import copyreg def _pickle_method(method): func_name = method.im_func.__name__ obj = method.im_self cls = method.im_class return _unpickle_method, (func_name, obj, cls) def _unpickle_method(func_name, obj, cls): for cls in cls.mro(): try: func = cls.__dict__[func_name] except KeyError: pass else: break return func.__get__(obj, cls) copyreg.pickle(types.MethodType, _pickle_method, _unpickle_method) class LedgerHarness: ledger = None sourcepath = None succeeded = 0 failed = 0 verify = False gmalloc = False python = False def __init__(self, argv): if not os.path.isfile(argv[1]): print("Cannot find ledger at '%s'" % argv[1]) sys.exit(1) if not os.path.isdir(argv[2]): print("Cannot find source path at '%s'" % argv[2]) sys.exit(1) self.ledger = os.path.realpath(argv[1]) self.sourcepath = os.path.realpath(argv[2]) self.succeeded = 0 self.failed = 0 self.verify = '--verify' in argv self.gmalloc = '--gmalloc' in argv self.python = '--python' in argv def run(self, command, verify=None, gmalloc=None, columns=True): env = os.environ.copy() if (gmalloc is not None and gmalloc) or \ (gmalloc is None and self.gmalloc): env['MallocGuardEdges'] = '1' env['MallocScribble'] = '1' env['MallocPreScribble'] = '1' env['MallocCheckHeapStart'] = '1000' env['MallocCheckHeapEach'] = '10000' env['DYLD_INSERT_LIBRARIES'] = '/usr/lib/libgmalloc.dylib' env['MALLOC_PROTECT_BEFORE'] = '1' env['MALLOC_FILL_SPACE'] = '1' env['MALLOC_STRICT_SIZE'] = '1' if (verify is not None and verify) or \ (verify is None and self.verify): insert = ' --verify' else: insert = '' if columns: insert += ' --columns=80' command = command.replace('$ledger', '"%s"%s %s' % \ (self.ledger, insert, '--args-only')) valgrind = '/usr/bin/valgrind' if not os.path.isfile(valgrind): valgrind = '/opt/local/bin/valgrind' if os.path.isfile(valgrind) and '--verify' in insert: command = valgrind + ' -q ' + command # If we are running under msys2, use bash to execute the test commands if 'MSYSTEM' in os.environ: bash_path = os.environ['MINGW_PREFIX'] + '/../usr/bin/bash.exe' return Popen([bash_path, '-c', command], shell=False, close_fds=False, env=env, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=self.sourcepath) return Popen(command, shell=True, close_fds=True, env=env, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=self.sourcepath) def read(self, fd): text = "" text_data = os.read(fd.fileno(), 8192) while text_data: if text_data: text += text_data text_data = os.read(fd.fileno(), 8192) return text def readlines(self, fd): lines = [] for line in fd.readlines(): if sys.version_info.major == 2: line = unicode(line, 'utf-8') else: line = line.decode('utf-8') if not line.startswith('GuardMalloc'): lines.append(line) return lines def wait(self, process, msg='Ledger invocation failed:'): if process.wait() != 0: print(msg) print(process.stderr.read()) self.failure() return False return True def success(self): sys.stdout.write(".") sys.stdout.flush() self.succeeded += 1 def failure(self, name=None): sys.stdout.write("E") if name: sys.stdout.write("[%s]" % name) sys.stdout.flush() self.failed += 1 def exit(self): print() if self.succeeded > 0: print("OK (%d) " % self.succeeded,) if self.failed > 0: print("FAILED (%d)" % self.failed,) print() sys.exit(self.failed) if __name__ == '__main__': harness = LedgerHarness(sys.argv) proc = harness.run('$ledger -f doc/sample.dat reg') print('STDOUT:') print(proc.stdout.read()) print('STDERR:') print(proc.stderr.read()) harness.success() harness.exit()