Skip to content

Commit b69b5d5

Browse files
committed
Merge pull request #3763 from gavinandresen/regression_tests
Python-based regression tests
2 parents 829f822 + 356cfe8 commit b69b5d5

File tree

14 files changed

+585
-14
lines changed

14 files changed

+585
-14
lines changed

README

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
AuthServiceProxy is an improved version of python-jsonrpc.
2+
3+
It includes the following generic improvements:
4+
5+
- HTTP connections persist for the life of the AuthServiceProxy object
6+
- sends protocol 'version', per JSON-RPC 1.1
7+
- sends proper, incrementing 'id'
8+
- uses standard Python json lib
9+
10+
It also includes the following bitcoin-specific details:
11+
12+
- sends Basic HTTP authentication headers
13+
- parses all JSON numbers that look like floats as Decimal
14+
15+
Installation:
16+
17+
- change the first line of setup.py to point to the directory of your installation of python 2.*
18+
- run setup.py
19+
20+
Note: This will only install bitcoinrpc. If you also want to install jsonrpc to preserve
21+
backwards compatibility, you have to replace 'bitcoinrpc' with 'jsonrpc' in setup.py and run it again.

bitcoinrpc/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.pyc
2+

bitcoinrpc/__init__.py

Whitespace-only changes.

bitcoinrpc/authproxy.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
2+
"""
3+
Copyright 2011 Jeff Garzik
4+
5+
AuthServiceProxy has the following improvements over python-jsonrpc's
6+
ServiceProxy class:
7+
8+
- HTTP connections persist for the life of the AuthServiceProxy object
9+
(if server supports HTTP/1.1)
10+
- sends protocol 'version', per JSON-RPC 1.1
11+
- sends proper, incrementing 'id'
12+
- sends Basic HTTP authentication headers
13+
- parses all JSON numbers that look like floats as Decimal
14+
- uses standard Python json lib
15+
16+
Previous copyright, from python-jsonrpc/jsonrpc/proxy.py:
17+
18+
Copyright (c) 2007 Jan-Klaas Kollhof
19+
20+
This file is part of jsonrpc.
21+
22+
jsonrpc is free software; you can redistribute it and/or modify
23+
it under the terms of the GNU Lesser General Public License as published by
24+
the Free Software Foundation; either version 2.1 of the License, or
25+
(at your option) any later version.
26+
27+
This software is distributed in the hope that it will be useful,
28+
but WITHOUT ANY WARRANTY; without even the implied warranty of
29+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30+
GNU Lesser General Public License for more details.
31+
32+
You should have received a copy of the GNU Lesser General Public License
33+
along with this software; if not, write to the Free Software
34+
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35+
"""
36+
37+
try:
38+
import http.client as httplib
39+
except ImportError:
40+
import httplib
41+
import base64
42+
import json
43+
import decimal
44+
try:
45+
import urllib.parse as urlparse
46+
except ImportError:
47+
import urlparse
48+
49+
USER_AGENT = "AuthServiceProxy/0.1"
50+
51+
HTTP_TIMEOUT = 30
52+
53+
54+
class JSONRPCException(Exception):
55+
def __init__(self, rpc_error):
56+
Exception.__init__(self)
57+
self.error = rpc_error
58+
59+
60+
class AuthServiceProxy(object):
61+
def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connection=None):
62+
self.__service_url = service_url
63+
self.__service_name = service_name
64+
self.__url = urlparse.urlparse(service_url)
65+
if self.__url.port is None:
66+
port = 80
67+
else:
68+
port = self.__url.port
69+
self.__id_count = 0
70+
(user, passwd) = (self.__url.username, self.__url.password)
71+
try:
72+
user = user.encode('utf8')
73+
except AttributeError:
74+
pass
75+
try:
76+
passwd = passwd.encode('utf8')
77+
except AttributeError:
78+
pass
79+
authpair = user + b':' + passwd
80+
self.__auth_header = b'Basic ' + base64.b64encode(authpair)
81+
82+
if connection:
83+
# Callables re-use the connection of the original proxy
84+
self.__conn = connection
85+
elif self.__url.scheme == 'https':
86+
self.__conn = httplib.HTTPSConnection(self.__url.hostname, port,
87+
None, None, False,
88+
timeout)
89+
else:
90+
self.__conn = httplib.HTTPConnection(self.__url.hostname, port,
91+
False, timeout)
92+
93+
def __getattr__(self, name):
94+
if name.startswith('__') and name.endswith('__'):
95+
# Python internal stuff
96+
raise AttributeError
97+
if self.__service_name is not None:
98+
name = "%s.%s" % (self.__service_name, name)
99+
return AuthServiceProxy(self.__service_url, name, connection=self.__conn)
100+
101+
def __call__(self, *args):
102+
self.__id_count += 1
103+
104+
postdata = json.dumps({'version': '1.1',
105+
'method': self.__service_name,
106+
'params': args,
107+
'id': self.__id_count})
108+
self.__conn.request('POST', self.__url.path, postdata,
109+
{'Host': self.__url.hostname,
110+
'User-Agent': USER_AGENT,
111+
'Authorization': self.__auth_header,
112+
'Content-type': 'application/json'})
113+
114+
response = self._get_response()
115+
if response['error'] is not None:
116+
raise JSONRPCException(response['error'])
117+
elif 'result' not in response:
118+
raise JSONRPCException({
119+
'code': -343, 'message': 'missing JSON-RPC result'})
120+
else:
121+
return response['result']
122+
123+
def _batch(self, rpc_call_list):
124+
postdata = json.dumps(list(rpc_call_list))
125+
self.__conn.request('POST', self.__url.path, postdata,
126+
{'Host': self.__url.hostname,
127+
'User-Agent': USER_AGENT,
128+
'Authorization': self.__auth_header,
129+
'Content-type': 'application/json'})
130+
131+
return self._get_response()
132+
133+
def _get_response(self):
134+
http_response = self.__conn.getresponse()
135+
if http_response is None:
136+
raise JSONRPCException({
137+
'code': -342, 'message': 'missing HTTP response from server'})
138+
139+
return json.loads(http_response.read().decode('utf8'),
140+
parse_float=decimal.Decimal)

jsonrpc/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .json import loads, dumps, JSONEncodeException, JSONDecodeException
2+
from jsonrpc.proxy import ServiceProxy, JSONRPCException

jsonrpc/authproxy.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
2+
3+
__all__ = ['AuthServiceProxy', 'JSONRPCException']

jsonrpc/json.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
_json = __import__('json')
2+
loads = _json.loads
3+
dumps = _json.dumps
4+
if hasattr(_json, 'JSONEncodeException'):
5+
JSONEncodeException = _json.JSONEncodeException
6+
JSONDecodeException = _json.JSONDecodeException
7+
else:
8+
JSONEncodeException = TypeError
9+
JSONDecodeException = ValueError

jsonrpc/proxy.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from bitcoinrpc.authproxy import AuthServiceProxy as ServiceProxy, JSONRPCException

qa/rpc-tests/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.pyc
2+
cache

qa/rpc-tests/README.md

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,36 @@
11
Regression tests of RPC interface
22
=================================
33

4-
Bash scripts that use the RPC interface and command-line bitcoin-cli to test
5-
full functionality in -regtest mode.
4+
python-bitcoinrpc: git subtree of https://github.com/jgarzik/python-bitcoinrpc
5+
Changes to python-bitcoinrpc should be made upstream, and then
6+
pulled here using git subtree
67

7-
wallet.sh : Exercise wallet send/receive code.
8+
skeleton.py : Copy this to create new regression tests.
89

9-
txnmall.sh : Test proper accounting of malleable transactions
10+
listtransactions.py : Tests for the listtransactions RPC call
11+
12+
util.py : generally useful functions
1013

14+
Bash-based tests, to be ported to Python:
15+
-----------------------------------------
16+
wallet.sh : Exercise wallet send/receive code.
17+
walletbackup.sh : Exercise wallet backup / dump / import
18+
txnmall.sh : Test proper accounting of malleable transactions
1119
conflictedbalance.sh : More testing of malleable transaction handling
1220

13-
util.sh : useful re-usable bash functions
21+
Notes
22+
=====
1423

24+
A 200-block -regtest blockchain and wallets for four nodes
25+
is created the first time a regression test is run and
26+
is stored in the cache/ directory. Each node has 25 mature
27+
blocks (25*50=1250 BTC) in their wallet.
1528

16-
Tips for creating new tests
17-
===========================
29+
After the first run, the cache/ blockchain and wallets are
30+
copied into a temporary directory and used as the initial
31+
test state.
1832

19-
To cleanup after a failed or interrupted test:
33+
If you get into a bad state, you should be able
34+
to recover with:
35+
rm -rf cache
2036
killall bitcoind
21-
rm -rf test.*
22-
23-
The most difficult part of writing reproducible tests is
24-
keeping multiple nodes in sync. See WaitBlocks,
25-
WaitPeers, and WaitMemPools for how other tests
26-
deal with this.

0 commit comments

Comments
 (0)