Skip to content

Commit

Permalink
tests: Improve functional_tests_rpc startup
Browse files Browse the repository at this point in the history
* Use a global startup timeout, and apply it to each socket's connect
  call, rather than repeating connection attempts with short timeouts.

* Avoid leaking sockets by closing each one, unless the connection
  fails, in which case the program abourts.
  • Loading branch information
iamamyth committed Feb 1, 2025
1 parent 90359e3 commit 714b56a
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 44 deletions.
36 changes: 20 additions & 16 deletions tests/functional_tests/functional_tests_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import socket
import string
import os
import time

USAGE = 'usage: functional_tests_rpc.py <python> <srcdir> <builddir> [<tests-to-run> | all]'
DEFAULT_TESTS = [
Expand Down Expand Up @@ -120,23 +121,26 @@ def kill():
except: pass

# wait for error/startup
for i in range(10):
time.sleep(1)
all_open = True
for port in ports:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
if s.connect_ex(('127.0.0.1', port)) != 0:
all_open = False
break
startup_timeout = 10
deadline = time.monotonic() + startup_timeout
for port in ports:
addr = ('127.0.0.1', port)
delay = 0
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
while True:
timeout = deadline - time.monotonic() - delay
if timeout <= 0:
print('Failed to start wallet or daemon')
kill()
sys.exit(1)
time.sleep(delay)
s.settimeout(timeout)
if s.connect_ex(addr) == 0:
break
delay = .1
finally:
s.close()
if all_open:
break

if not all_open:
print('Failed to start wallet or daemon')
kill()
sys.exit(1)

# online daemons need some time to connect to peers to be ready
time.sleep(2)
Expand Down
46 changes: 18 additions & 28 deletions utils/python-rpc/framework/rpc.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
# Copyright (c) 2018-2024, The Monero Project

#
#
# All rights reserved.
#
#
# Redistribution and use in source and binary forms, with or without modification, are
# permitted provided that the following conditions are met:
#
#
# 1. Redistributions of source code must retain the above copyright notice, this list of
# conditions and the following disclaimer.
#
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
# of conditions and the following disclaimer in the documentation and/or other
# materials provided with the distribution.
#
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be
# used to endorse or promote products derived from this software without specific
# prior written permission.
#
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
Expand All @@ -33,32 +33,22 @@

class Response(dict):
def __init__(self, d):
for k in d.keys():
if type(d[k]) == dict:
self[k] = Response(d[k])
elif type(d[k]) == list:
self[k] = []
for i in range(len(d[k])):
if type(d[k][i]) == dict:
self[k].append(Response(d[k][i]))
else:
self[k].append(d[k][i])
else:
self[k] = d[k]
for k, v in d.items():
self[k] = self._decode(v)

@staticmethod
def _decode(o):
if isinstance(o, dict):
return Response(o)
elif isinstance(o, list):
return [Response._decode(i) for i in o]
else:
return o

def __getattr__(self, key):
return self[key]
def __setattr__(self, key, value):
self[key] = value
def __eq__(self, other):
if type(other) == dict:
return self == Response(other)
if self.keys() != other.keys():
return False
for k in self.keys():
if self[k] != other[k]:
return False
return True

class JSONRPC(object):
def __init__(self, url, username=None, password=None):
Expand All @@ -73,7 +63,7 @@ def send_request(self, path, inputs, result_field = None):
headers={'content-type': 'application/json'},
auth=HTTPDigestAuth(self.username, self.password) if self.username is not None else None)
res = res.json()

assert 'error' not in res, res

if result_field:
Expand Down

0 comments on commit 714b56a

Please sign in to comment.