1 #!/usr/bin/env python3
2 # Copyright (c) 2019-present The Bitcoin Core developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 import pathlib
6 7 from test_framework.test_framework import BitcoinTestFramework
8 9 10 class TestShell:
11 """Wrapper Class for BitcoinTestFramework.
12 13 The TestShell class extends the BitcoinTestFramework
14 rpc & daemon process management functionality to external
15 python environments.
16 17 It is a singleton class, which ensures that users only
18 start a single TestShell at a time."""
19 20 class __TestShell(BitcoinTestFramework):
21 def set_test_params(self):
22 self.uses_wallet = None
23 24 def run_test(self):
25 pass
26 27 def setup(self, **kwargs):
28 if self.running:
29 print("TestShell is already running!")
30 return
31 32 # Num_nodes parameter must be set
33 # by BitcoinTestFramework child class.
34 self.num_nodes = 1
35 36 # User parameters override default values.
37 for key, value in kwargs.items():
38 if hasattr(self, key):
39 setattr(self, key, value)
40 elif hasattr(self.options, key):
41 setattr(self.options, key, value)
42 else:
43 raise KeyError(key + " not a valid parameter key!")
44 45 super().setup()
46 self.running = True
47 return self
48 49 def shutdown(self):
50 if not self.running:
51 print("TestShell is not running!")
52 else:
53 super().shutdown()
54 self.running = False
55 56 def reset(self):
57 if self.running:
58 print("Shutdown TestShell before resetting!")
59 else:
60 self.num_nodes = None
61 dummy_testshell_file = pathlib.Path(__file__).absolute().parent.parent / "testshell_dummy.py"
62 super().__init__(dummy_testshell_file)
63 64 instance = None
65 66 def __new__(cls):
67 # This implementation enforces singleton pattern, and will return the
68 # previously initialized instance if available
69 if not TestShell.instance:
70 # BitcoinTestFramework instances are supposed to be constructed with the path
71 # of the calling test in order to find shared data like configuration and the
72 # cache. Since TestShell is meant for interactive use, there is no concrete
73 # test; passing a dummy name is fine though, as only the containing directory
74 # is relevant for successful initialization.
75 dummy_testshell_file = pathlib.Path(__file__).absolute().parent.parent / "testshell_dummy.py"
76 TestShell.instance = TestShell.__TestShell(dummy_testshell_file)
77 TestShell.instance.running = False
78 return TestShell.instance
79 80 def __getattr__(self, name):
81 return getattr(self.instance, name)
82 83 def __setattr__(self, name, value):
84 return setattr(self.instance, name, value)
85