From 91991a13de2807183a510b9cef4fd787e3b8bf5a Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 27 Jul 2024 09:42:50 +0000 Subject: [PATCH] support macOS (BTC-only, no autosign) Testing: $ test/test-release.sh -FCSTA Tested on macOS 13.6.7 (Ventura) + Xcode 15.0.1 --- mmgen/cfg.py | 2 +- mmgen/daemon.py | 2 +- mmgen/data/version | 2 +- mmgen/main.py | 2 +- mmgen/proto/btc/daemon.py | 3 ++- mmgen/rpc.py | 1 + mmgen/term.py | 7 ++++--- mmgen/tw/view.py | 1 + test/cmdtest.py | 8 ++++++-- test/cmdtest_py_d/ct_autosign.py | 1 + test/cmdtest_py_d/ct_base.py | 4 ++++ test/cmdtest_py_d/ct_input.py | 33 +++++++++++++++++++++---------- test/cmdtest_py_d/ct_misc.py | 2 +- test/cmdtest_py_d/ct_xmrwallet.py | 2 +- test/misc/term.py | 2 +- test/overlay/__init__.py | 2 +- test/test-release.sh | 5 ++++- test/unit_tests.py | 4 ++++ test/unit_tests_d/ut_testdep.py | 1 + 19 files changed, 59 insertions(+), 25 deletions(-) diff --git a/mmgen/cfg.py b/mmgen/cfg.py index ce255356..e5968887 100755 --- a/mmgen/cfg.py +++ b/mmgen/cfg.py @@ -55,7 +55,7 @@ class GlobalConstants(Lockable): prog_name = os.path.basename(sys.argv[0]) is_txprog = prog_name == 'mmgen-regtest' or prog_name.startswith('mmgen-tx') - if sys.platform not in ('linux','win32'): + if sys.platform not in ('linux', 'win32', 'darwin'): die2(1,f'{sys.platform!r}: platform not supported by {proj_name}') if os.getenv('HOME'): # Linux or MSYS2 diff --git a/mmgen/daemon.py b/mmgen/daemon.py index 0e5f3ca9..ac02a19e 100755 --- a/mmgen/daemon.py +++ b/mmgen/daemon.py @@ -133,7 +133,7 @@ class Daemon(Lockable): if len(pids) > 1: self.pids = pids return pids[0] - elif self.platform == 'linux': + elif self.platform in ('linux', 'darwin'): ss = ' '.join(self.start_cmd) cp = self.run_cmd(['pgrep','-f',ss],silent=True) if cp.stdout: diff --git a/mmgen/data/version b/mmgen/data/version index 3de2fb1e..c8c1d41a 100644 --- a/mmgen/data/version +++ b/mmgen/data/version @@ -1 +1 @@ -15.0.dev0 +15.0.dev1 diff --git a/mmgen/main.py b/mmgen/main.py index 8434db42..a7c5b72d 100755 --- a/mmgen/main.py +++ b/mmgen/main.py @@ -24,7 +24,7 @@ import sys,os def launch(*, mod=None, func=None, fqmod=None, package='mmgen'): - if sys.platform == 'linux' and sys.stdin.isatty(): + if sys.platform in ('linux', 'darwin') and sys.stdin.isatty(): import termios,atexit fd = sys.stdin.fileno() old = termios.tcgetattr(fd) diff --git a/mmgen/proto/btc/daemon.py b/mmgen/proto/btc/daemon.py index 3578ae31..1434e550 100755 --- a/mmgen/proto/btc/daemon.py +++ b/mmgen/proto/btc/daemon.py @@ -29,6 +29,7 @@ class bitcoin_core_daemon(CoinDaemon): nonstd_datadir = False datadirs = { 'linux': [gc.home_dir,'.bitcoin'], + 'darwin': [gc.home_dir, '.bitcoin'], 'win32': [os.getenv('APPDATA'),'Bitcoin'] } avail_opts = ('no_daemonize', 'online', 'bdb_wallet') @@ -76,7 +77,7 @@ class bitcoin_core_daemon(CoinDaemon): ['--rpcallowip=127.0.0.1'], [f'--rpcbind=127.0.0.1:{self.rpc_port}'], ['--pid='+self.pidfile, self.use_pidfile], - ['--daemon', self.platform == 'linux' and not self.opt.no_daemonize], + ['--daemon', self.platform in ('linux', 'darwin') and not self.opt.no_daemonize], ['--fallbackfee=0.0002', self.coin == 'BTC' and self.network == 'regtest'], ['--usecashaddr=0', self.coin == 'BCH'], ['--deprecatedrpc=create_bdb', self.coin == 'BTC' and self.opt.bdb_wallet], diff --git a/mmgen/rpc.py b/mmgen/rpc.py index 654f10de..18db42da 100755 --- a/mmgen/rpc.py +++ b/mmgen/rpc.py @@ -293,6 +293,7 @@ class RPCClient(MMGenObject): if backend_id == 'auto': return { 'linux': RPCBackends.httplib, + 'darwin': RPCBackends.httplib, 'win32': RPCBackends.requests }[sys.platform](self) else: diff --git a/mmgen/term.py b/mmgen/term.py index 34c2e4b5..55b3afda 100755 --- a/mmgen/term.py +++ b/mmgen/term.py @@ -27,9 +27,10 @@ from collections import namedtuple from .util import msg,msg_r,die -if sys.platform == 'linux': +if sys.platform in ('linux', 'darwin'): import tty,termios from select import select + hold_protect_timeout = 2 if sys.platform == 'darwin' else 0.3 elif sys.platform == 'win32': try: import msvcrt @@ -118,9 +119,8 @@ class MMGenTermLinux(MMGenTerm): if cls.cfg.hold_protect_disable: return tty.setcbreak(cls.stdin_fd) - timeout = 0.3 while True: - key = select([sys.stdin], [], [], timeout)[0] + key = select([sys.stdin], [], [], hold_protect_timeout)[0] if key: sys.stdin.read(1) else: @@ -285,6 +285,7 @@ class MMGenTermMSWinStub(MMGenTermMSWin): def get_term(): return { 'linux': (MMGenTermLinux if sys.stdin.isatty() else MMGenTermLinuxStub), + 'darwin': (MMGenTermLinux if sys.stdin.isatty() else MMGenTermLinuxStub), 'win32': (MMGenTermMSWin if sys.stdin.isatty() else MMGenTermMSWinStub), }[sys.platform] diff --git a/mmgen/tw/view.py b/mmgen/tw/view.py index 3af9eeed..4aecb2c9 100755 --- a/mmgen/tw/view.py +++ b/mmgen/tw/view.py @@ -181,6 +181,7 @@ class TwView(MMGenObject,metaclass=AsyncInit): '\xe0O': 'm_bot', } } + scroll_keys['darwin'] = scroll_keys['linux'] def __new__(cls,cfg,proto,*args,**kwargs): return MMGenObject.__new__(proto.base_proto_subclass(cls,cls.mod_subpath)) diff --git a/test/cmdtest.py b/test/cmdtest.py index 4ebc67a3..e6976cd7 100755 --- a/test/cmdtest.py +++ b/test/cmdtest.py @@ -30,7 +30,7 @@ def create_shm_dir(data_dir,trash_dir): # under '/dev/shm' and put datadir and tmpdirs here. import shutil from subprocess import run - if sys.platform == 'win32': + if sys.platform in ('win32', 'darwin'): for tdir in (data_dir,trash_dir): try: os.listdir(tdir) @@ -293,7 +293,7 @@ def do_between(): sys.stderr.write('\n') def create_tmp_dirs(shm_dir): - if sys.platform == 'win32': + if sys.platform in ('win32', 'darwin'): for cfg in sorted(cfgs): mk_tmpdir(cfgs[cfg]['tmpdir']) else: @@ -664,6 +664,10 @@ class CmdTestRunner: omsg(gray(f'INFO → skipping test {gname!r} (platform=win32)')) return None + if sys.platform == 'darwin' and ct_cls.mac_skip: + omsg(gray(f'INFO → skipping test {gname!r} (platform=darwin)')) + return None + for k in ('segwit','segwit_random','bech32'): if getattr(cfg,k): segwit_opt = k diff --git a/test/cmdtest_py_d/ct_autosign.py b/test/cmdtest_py_d/ct_autosign.py index 923f5fec..08273d9b 100755 --- a/test/cmdtest_py_d/ct_autosign.py +++ b/test/cmdtest_py_d/ct_autosign.py @@ -54,6 +54,7 @@ class CmdTestAutosignBase(CmdTestBase): tmpdir_nums = [18] color = True win_skip = True + mac_skip = True threaded = False daemon_coins = [] diff --git a/test/cmdtest_py_d/ct_base.py b/test/cmdtest_py_d/ct_base.py index c1fa5c21..a2aa4cf2 100755 --- a/test/cmdtest_py_d/ct_base.py +++ b/test/cmdtest_py_d/ct_base.py @@ -38,6 +38,7 @@ class CmdTestBase: color = False need_daemon = False win_skip = False + mac_skip = False tmpdir_nums = [] test_name = None @@ -94,6 +95,9 @@ class CmdTestBase: else: return False + def skip_for_mac(self, extra_msg=None): + return self.skip_for_platform('darwin', extra_msg) + def skip_for_win(self, extra_msg=None): return self.skip_for_platform('win32', extra_msg) diff --git a/test/cmdtest_py_d/ct_input.py b/test/cmdtest_py_d/ct_input.py index 1bf2c593..87f1f889 100755 --- a/test/cmdtest_py_d/ct_input.py +++ b/test/cmdtest_py_d/ct_input.py @@ -29,6 +29,8 @@ from .common import Ctrl_U,ref_dir from .ct_base import CmdTestBase from .input import stealth_mnemonic_entry,user_dieroll_entry +hold_protect_delay = 2 if sys.platform == 'darwin' else None + class CmdTestInput(CmdTestBase): 'user input' networks = ('btc',) @@ -238,7 +240,7 @@ class CmdTestInput(CmdTestBase): return t - def _input_func(self,func_name,arg_dfls,func_args,text,expect,term): + def _input_func(self, func_name, arg_dfls, func_args, text, expect, term, delay=None): if term and sys.platform == 'win32': return ('skip_warn','pexpect_spawn not supported on Windows platform') func_args = dict(zip(arg_dfls.keys(),func_args)) @@ -259,9 +261,9 @@ class CmdTestInput(CmdTestBase): prompt = func_args['prompt'] + prompt_add t.expect('Calling ') if prompt: - t.expect(prompt,text) + t.expect(prompt, text, delay=delay) else: - t.send(text) + t.send(text, delay=delay) ret = t.expect_getend(' ==> ') assert ret == repr(expect), f'Text mismatch! {ret} != {repr(expect)}' return t @@ -275,14 +277,14 @@ class CmdTestInput(CmdTestBase): } return self._input_func('get_char',arg_dfls,func_args,text,expect,term) - def _line_input(self,func_args,text,expect,term): + def _line_input(self, func_args, text, expect, term, delay=None): arg_dfls = { 'prompt': '', # positional 'echo': True, 'insert_txt': '', 'hold_protect': True, } - return self._input_func('line_input',arg_dfls,func_args,text+'\n',expect,term) + return self._input_func('line_input', arg_dfls, func_args, text+'\n', expect, term, delay=delay) def get_char1(self): return self._get_char(['prompt> ','',True,5],'x','x',False) @@ -321,7 +323,8 @@ class CmdTestInput(CmdTestBase): ['prompt> ', True, '', True], 'foo', 'foo', - True) + True, + hold_protect_delay) def line_input_term2(self): return self._line_input( @@ -338,13 +341,18 @@ class CmdTestInput(CmdTestBase): False) def line_input_insert_term1(self): + if self.skip_for_mac('readline text buffer issues'): + return 'skip' return self._line_input( ['prompt> ', True, 'foo', True], 'bar', 'foobar', - True) + True, + hold_protect_delay) def line_input_insert_term2(self): + if self.skip_for_mac('readline text buffer issues'): + return 'skip' return self._line_input( ['prompt> ', True, 'foo', False], 'bar', @@ -356,7 +364,8 @@ class CmdTestInput(CmdTestBase): ['prompt> ', True, '', True], '\b\bφυφυ\b\bβαρ', 'φυβαρ', - True) + True, + hold_protect_delay) def line_input_edit_term_insert(self): if self.skip_for_mac('readline text buffer issues'): @@ -365,14 +374,18 @@ class CmdTestInput(CmdTestBase): ['prompt> ', True, 'φυφυ', True], '\b\bβαρ', 'φυβαρ', - True) + True, + hold_protect_delay) def line_input_erase_term(self): + if self.skip_for_mac('readline text buffer issues'): + return 'skip' return self._line_input( ['prompt> ', True, 'foobarbaz', True], Ctrl_U + 'foobar', 'foobar', - True) + True, + hold_protect_delay) def _password_entry(self,prompt,opts=[],term=False): if term and sys.platform == 'win32': diff --git a/test/cmdtest_py_d/ct_misc.py b/test/cmdtest_py_d/ct_misc.py index d861757f..e7d87c00 100755 --- a/test/cmdtest_py_d/ct_misc.py +++ b/test/cmdtest_py_d/ct_misc.py @@ -135,7 +135,7 @@ class CmdTestMisc(CmdTestBase): die('TestSuiteException', 'Terminal echoed in noecho mode!') t.send('x') - if self.skip_for_win('no termios support'): + if self.skip_for_win('no termios support') or self.skip_for_mac('termios.ECHO issues'): return 'skip' t = self.spawn('test/misc/term_ni.py',['echo'],cmd_dir='.',pexpect_spawn=True,timeout=1) diff --git a/test/cmdtest_py_d/ct_xmrwallet.py b/test/cmdtest_py_d/ct_xmrwallet.py index 86b6fe08..89e88bb9 100755 --- a/test/cmdtest_py_d/ct_xmrwallet.py +++ b/test/cmdtest_py_d/ct_xmrwallet.py @@ -57,7 +57,7 @@ def stop_miner_wallet_daemon(self): async_run(self.users['miner'].wd_rpc.stop_daemon()) def kill_proxy(cls,args): - if sys.platform == 'linux': + if sys.platform in ('linux', 'darwin'): omsg(f'Killing SSH SOCKS server at localhost:{cls.socks_port}') cmd = [ 'pkill', '-f', ' '.join(args) ] run(cmd) diff --git a/test/misc/term.py b/test/misc/term.py index 264fb0f1..957963ca 100755 --- a/test/misc/term.py +++ b/test/misc/term.py @@ -21,7 +21,7 @@ commands = [ 'get_char_one', 'get_char_one_raw', ] -if sys.platform == 'linux': +if sys.platform in ('linux', 'darwin'): commands.extend([ 'get_char', 'get_char_immed_chars', diff --git a/test/overlay/__init__.py b/test/overlay/__init__.py index 3ae62444..1dd2b5f3 100644 --- a/test/overlay/__init__.py +++ b/test/overlay/__init__.py @@ -55,7 +55,7 @@ def overlay_setup(repo_root): sys.stderr.write(f'Setting up overlay tree: {pkgname}\n') - make_link = os.symlink if sys.platform == 'linux' else shutil.copy2 + make_link = os.symlink if sys.platform in ('linux', 'darwin') else shutil.copy2 shutil.rmtree(os.path.join(overlay_tree_dir,pkgname),ignore_errors=True) import configparser diff --git a/test/test-release.sh b/test/test-release.sh index 2b2e4b00..86432585 100755 --- a/test/test-release.sh +++ b/test/test-release.sh @@ -117,7 +117,7 @@ print_ver_hash() { } do_typescript() { - script -O "$1" -c "$2" + if [ "$DARWIN" ]; then script "$1" $2; else script -O "$1" -c "$2"; fi } install_package() { @@ -234,6 +234,9 @@ if [ "$(uname -m)" == 'armv7l' ]; then ARM32=1 elif [ "$(uname -m)" == 'aarch64' ]; then ARM64=1 +elif [ "$(uname -s)" == 'Darwin' ]; then + DARWIN=1 + DISTRO='DARWIN' elif [ "$MSYSTEM" ] && uname -a | grep -qi 'msys'; then MSYS2=1 DISTRO='MSYS2' diff --git a/test/unit_tests.py b/test/unit_tests.py index 7f3ce985..46b1747c 100755 --- a/test/unit_tests.py +++ b/test/unit_tests.py @@ -187,6 +187,7 @@ def run_test(test,subtest=None): t = getattr(mod,'unit_tests')() altcoin_deps = getattr(t,'altcoin_deps',()) win_skip = getattr(t, 'win_skip', ()) + mac_skip = getattr(t, 'mac_skip', ()) arm_skip = getattr(t, 'arm_skip', ()) subtests = ( [subtest] if subtest else @@ -202,6 +203,9 @@ def run_test(test,subtest=None): if sys.platform == 'win32' and subtest in win_skip: cfg._util.qmsg(gray(f'Skipping unit subtest {subtest_disp!r} for Windows platform')) continue + elif sys.platform == 'darwin' and subtest in mac_skip: + cfg._util.qmsg(gray(f'Skipping unit subtest {subtest_disp!r} for macOS platform')) + continue elif platform.machine() == 'aarch64' and subtest in arm_skip: cfg._util.qmsg(gray(f'Skipping unit subtest {subtest_disp!r} for ARM platform')) continue diff --git a/test/unit_tests_d/ut_testdep.py b/test/unit_tests_d/ut_testdep.py index 1ae8ca18..633a8c9f 100755 --- a/test/unit_tests_d/ut_testdep.py +++ b/test/unit_tests_d/ut_testdep.py @@ -16,6 +16,7 @@ class unit_tests: altcoin_deps = ('pycoin','monero_python','keyconv','zcash_mini','ethkey','ssh_socks_proxy') win_skip = ('losetup','zcash_mini') + mac_skip = ('losetup',) def pylint(self,name,ut): try: