Skip to content

Commit

Permalink
windows pytests
Browse files Browse the repository at this point in the history
  • Loading branch information
andreasgriffin committed May 16, 2024
1 parent 51bf489 commit a11effc
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 39 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
python-version: ['3.10']
os: [ ubuntu-latest ] # [macos-latest, ubuntu-latest, windows-latest]
os: [ ubuntu-latest ] # [macos-latest, ubuntu-latest, windows-latest]
env:
DISPLAY: ":99.0" # Display setting for Xvfb on Linux
QT_SELECT: "qt6" # Environment variable to select Qt6
Expand Down Expand Up @@ -53,6 +53,7 @@ jobs:
sudo /usr/bin/Xvfb $DISPLAY -screen 0 1280x1024x24 &
- name: Start Xvfb
if: runner.os == 'Linux'
run: |
export DISPLAY=:99.0
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac +extension GLX +render -noreset
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ repos:
hooks:
- id: check-header
name: Check Python File Headers
entry: python tools/check_header.py
entry: poetry run python tools/check_header.py
language: system
types: [python]
files: '\.py$'
Expand Down
12 changes: 6 additions & 6 deletions bitcoin_safe/gui/qt/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1072,12 +1072,12 @@ def set_tab_widget_icon(tab: QWidget, icon: QIcon):
data=qt_wallet,
)

search_box = SearchWallets(
lambda: list(self.qt_wallets.values()),
signal_min=self.signals,
parent=self.tab_wallets,
)
qt_wallet.tabs.set_top_right_widget(search_box)
# search_box = SearchWallets(
# lambda: list(self.qt_wallets.values()),
# signal_min=self.signals,
# parent=self.tab_wallets,
# )
# qt_wallet.tabs.set_top_right_widget(search_box)

qt_wallet.wallet_steps.set_visibilities()
self.signals.event_wallet_tab_added.emit()
Expand Down
2 changes: 1 addition & 1 deletion tests/gui/qt/test_gui_setup_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def test_tutorial_wallet_setup(
logger.debug(f"start test_tutorial_wallet_setup")
frame = inspect.currentframe()
assert frame
shutter = Shutter(qtbot, name=f"{test_start_time}_{inspect.getframeinfo(frame).function }")
shutter = Shutter(qtbot, name=f"{test_start_time.timestamp()}_{inspect.getframeinfo(frame).function }")
shutter.create_symlink(test_config=test_config)
logger.debug(f"shutter = {shutter}")
with main_window_context(test_config=test_config) as (app, main_window):
Expand Down
2 changes: 1 addition & 1 deletion tests/gui/qt/test_gui_setup_wallet_custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def test_custom_wallet_setup_custom_single_sig(
) -> None: # bitcoin_core: Path,
frame = inspect.currentframe()
assert frame
shutter = Shutter(qtbot, name=f"{test_start_time}_{inspect.getframeinfo(frame).function }")
shutter = Shutter(qtbot, name=f"{test_start_time.timestamp()}_{inspect.getframeinfo(frame).function }")

shutter.create_symlink(test_config=test_config)
with main_window_context(test_config=test_config) as (app, main_window):
Expand Down
13 changes: 12 additions & 1 deletion tests/gui/qt/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

import inspect
import logging
import os
import platform
import shutil
import tempfile
from contextlib import contextmanager
Expand Down Expand Up @@ -167,7 +169,16 @@ def save_screenshot(widget: QMainWindow, qtbot: QtBot, name: str) -> Path:

def create_symlink(self, test_config: UserConfig):
screenshots_dir = Shutter.directory(self.name)
(screenshots_dir / "config_dir").symlink_to(test_config.config_dir)
link_name = screenshots_dir / "config_dir"

if platform.system() == "Windows":
# Use mklink to create a directory junction on Windows
if os.system(f'mklink /J "{link_name}" "{test_config.config_dir}"') != 0:
raise OSError(
f"Failed to create directory junction from {link_name} to {test_config.config_dir}"
)
else:
link_name.symlink_to(test_config.config_dir)


def _get_widget_top_level(cls: Type[T], title=None) -> Optional[T]:
Expand Down
172 changes: 150 additions & 22 deletions tests/test_setup_bitcoin_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@

import json
import logging
import os
import platform
import shlex
import shutil
import subprocess
import tarfile
import time
import zipfile
from pathlib import Path
from typing import Generator

Expand Down Expand Up @@ -81,33 +85,160 @@
BITCOIN_BIN_DIR = BITCOIN_EXTRACT_DIR / "bin"


def bitcoin_cli(command, bitcoin_core: Path):
def runcmd(cmd, background=False):
try:
system = platform.system()
if system == "Windows":
# On Windows, use subprocess.Popen to start the process without blocking
process = subprocess.Popen(
cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
)
else:
process = subprocess.Popen(
shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
)

if background:
return process
stdout, stderr = process.communicate()
if stderr:
raise Exception(stderr)
return stdout

except subprocess.CalledProcessError as e:
print(f"An error occurred while running bitcoind: {e}")
return None
except Exception as e:
print(f"An unexpected error occurred: {e}")
return None


def bitcoind():
""" """
system = platform.system()
executable = "bitcoind.exe" if system == "Windows" else "bitcoind"
cmd = f"{BITCOIN_BIN_DIR / executable} -regtest -conf={BITCOIN_CONF}"

if system != "Windows":
cmd += " -daemon"

runcmd(cmd, background=True)


def bitcoin_cli(
command,
bitcoin_core: Path,
bitcoin_host=BITCOIN_HOST,
bitcoin_port=BITCOIN_PORT,
rpc_user=RPC_USER,
rpc_password=RPC_PASSWORD,
):
"""
Run a bitcoin-cli command with the specified parameters.
Parameters:
- command: The bitcoin-cli command to execute.
- bitcoin_core: Path to the directory containing bitcoin-cli.
- bitcoin_host: Host for RPC connection (default: localhost).
- bitcoin_port: Port for RPC connection (default: 8332).
- rpc_user: RPC username (default: bitcoin).
- rpc_password: RPC password (default: bitcoin).
Returns:
- The result of subprocess.run containing stdout, stderr, and return code.
"""
system = platform.system()
executable = "bitcoin-cli.exe" if system == "Windows" else "bitcoin-cli"
cmd = (
str(bitcoin_core / "bitcoin-cli")
+ f" -rpcconnect={BITCOIN_HOST} -rpcport={BITCOIN_PORT} -regtest -rpcuser=bitcoin -rpcpassword=bitcoin "
str(bitcoin_core / executable)
+ f" -rpcconnect={bitcoin_host} -rpcport={bitcoin_port} -regtest -rpcuser={rpc_user} -rpcpassword={rpc_password} "
+ command
)
return subprocess.run(shlex.split(cmd), capture_output=True, text=True)
return runcmd(cmd)


def get_default_bitcoin_data_dir():
"""Get the default Bitcoin data directory based on the operating system."""
system = platform.system()
if system == "Windows":
appdata = os.getenv("APPDATA")
assert appdata
return Path(appdata) / "Bitcoin"
elif system == "Darwin": # macOS
return Path.home() / "Library" / "Application Support" / "Bitcoin"
else: # Assume Linux/Unix
return Path.home() / ".bitcoin"


def remove_bitcoin_regtest_folder(custom_datadir=None):
"""
Remove the regtest folder from the Bitcoin data directory.
Parameters:
- custom_datadir: Optional custom Bitcoin data directory.
"""
try:
if custom_datadir:
bitcoin_data_dir = Path(custom_datadir)
else:
bitcoin_data_dir = get_default_bitcoin_data_dir()

regtest_dir = bitcoin_data_dir / "regtest"

if regtest_dir.exists() and regtest_dir.is_dir():
shutil.rmtree(regtest_dir)
print(f"Removed {regtest_dir}")
else:
print(f"{regtest_dir} does not exist or is not a directory")
except Exception as e:
print(f"An error occurred: {e}")


def download_bitcoin():
system = platform.system()

if system == "Windows":
archive_extension = "zip"
url = (
f"https://bitcoincore.org/bin/bitcoin-core-{BITCOIN_VERSION}/bitcoin-{BITCOIN_VERSION}-win64.zip"
)
elif system == "Darwin": # macOS
archive_extension = "tar.gz"
url = f"https://bitcoincore.org/bin/bitcoin-core-{BITCOIN_VERSION}/bitcoin-{BITCOIN_VERSION}-x86_64-apple-darwin.tar.gz"
else: # Assume Linux
archive_extension = "tar.gz"
url = f"https://bitcoincore.org/bin/bitcoin-core-{BITCOIN_VERSION}/bitcoin-{BITCOIN_VERSION}-x86_64-linux-gnu.tar.gz"


@pytest.fixture(scope="session")
def bitcoin_core() -> Generator[Path, None, None]:
# Ensure Bitcoin Core directory exists
BITCOIN_DIR.mkdir(exist_ok=True)
BITCOIN_ARCHIVE = BITCOIN_DIR / f"bitcoin-{BITCOIN_VERSION}.{archive_extension}"

# Download Bitcoin Core if necessary
if not BITCOIN_ARCHIVE.exists():
print(f"Downloading Bitcoin Core {BITCOIN_VERSION}...")
url = f"https://bitcoincore.org/bin/bitcoin-core-{BITCOIN_VERSION}/bitcoin-{BITCOIN_VERSION}-x86_64-linux-gnu.tar.gz"
response = requests.get(url, timeout=2)
logger.info(f"Downloading Bitcoin Core {BITCOIN_VERSION}...")
response = requests.get(url, timeout=10)
response.raise_for_status()
BITCOIN_ARCHIVE.write_bytes(response.content)

# Extract Bitcoin Core if necessary
if not BITCOIN_BIN_DIR.exists():
print(f"Extracting Bitcoin Core {BITCOIN_VERSION}...")
with tarfile.open(BITCOIN_ARCHIVE, "r:gz") as tar:
tar.extractall(path=BITCOIN_DIR)
logger.info(f"Extracting Bitcoin Core {BITCOIN_VERSION}...")
if archive_extension == "tar.gz":
with tarfile.open(BITCOIN_ARCHIVE, "r:gz") as tar:
tar.extractall(path=BITCOIN_DIR)
elif archive_extension == "zip":
with zipfile.ZipFile(BITCOIN_ARCHIVE, "r") as zip_ref:
zip_ref.extractall(path=BITCOIN_DIR)
else:
raise ValueError(f"Unsupported archive extension: {archive_extension}")

logger.info(f"Bitcoin Core {BITCOIN_VERSION} is ready to use.")


@pytest.fixture(scope="session")
def bitcoin_core() -> Generator[Path, None, None]:
# Ensure Bitcoin Core directory exists
BITCOIN_DIR.mkdir(exist_ok=True)

download_bitcoin()
# Create bitcoin.conf
BITCOIN_CONF.write_text(BITCOIN_CONF_CONTENT)

Expand All @@ -116,10 +247,10 @@ def bitcoin_core() -> Generator[Path, None, None]:
# to ensure bitcoind is stopped
time.sleep(1)
# remove the previous chain
subprocess.run(shlex.split(f"rm -rf {Path.home() / '.bitcoin' / 'regtest'}"))
remove_bitcoin_regtest_folder()

# Start Bitcoin Core
subprocess.run(shlex.split(f"{BITCOIN_BIN_DIR / 'bitcoind'} -regtest -daemon -conf={BITCOIN_CONF}"))
bitcoind()

# Wait for Bitcoin Core to start
time.sleep(5)
Expand All @@ -135,11 +266,8 @@ def test_get_blockchain_info(bitcoin_core: Path):
# Execute the command to get blockchain information
result = bitcoin_cli("getblockchaininfo", bitcoin_core)

# Check if there was an error
assert result.stderr == "", f"Error getting blockchain info: {result.stderr}"

# Parse the output as JSON
blockchain_info = json.loads(result.stdout)
blockchain_info = json.loads(result)

# Verify some expected fields are present and correct
assert blockchain_info["chain"] == "regtest", "The chain type should be 'regtest'"
Expand All @@ -152,7 +280,7 @@ def mine_blocks(
):
# Mine n blocks to the specified address
result = bitcoin_cli(f"generatetoaddress {n} {address}", bitcoin_core)
return result.stdout.strip()
return result.strip()


class Faucet:
Expand Down
15 changes: 9 additions & 6 deletions tools/check_header.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@


import os
import platform
import sys
from pathlib import Path

Expand Down Expand Up @@ -95,13 +96,15 @@ def find_python_files():


if __name__ == "__main__":

success = True
for file_path in sys.argv[1:]:
if Path(file_path).name.startswith("_"):
continue
if not check_header(file_path):
print(f"Header missing or incorrect in file: {file_path}")
success = False
if platform.system() != "Windows":
for file_path in sys.argv[1:]:
if Path(file_path).name.startswith("_"):
continue
if not check_header(file_path):
print(f"Header missing or incorrect in file: {file_path}")
success = False
if not success:
sys.exit(1)

Expand Down

0 comments on commit a11effc

Please sign in to comment.