Skip to content

Commit bb6de1b

Browse files
committed
Merge bitcoin/bitcoin#29034: test: detect OS in functional tests consistently using platform.system()
878d914 doc: test: mention OS detection preferences in style guideline (Sebastian Falbesoner) 4c65ac9 test: detect OS consistently using `platform.system()` (Sebastian Falbesoner) 37324ae test: use `skip_if_platform_not_linux` helper where possible (Sebastian Falbesoner) Pull request description: There are at least three ways to detect the operating system in Python3: - `os.name` (https://docs.python.org/3.9/library/os.html#os.name) - `sys.platform` (https://docs.python.org/3.9/library/sys.html#sys.platform) - `platform.system()` (https://docs.python.org/3.9/library/platform.html#platform.system) We are currently using all of them in functional tests (both in individual tests and shared test framework code), which seems a bit messy. This PR consolidates into using `platform.system()`, as it appears to be one most consistent and easy to read (see also [IRC discussion](https://bitcoin-irc.chaincode.com/bitcoin-core-dev/2023-12-08#989301;) and table below). `sys.platform` is inconsistent as it has the major version number encoded for BSD systems, which doesn't make much sense for e.g. OpenBSD, where there is no concept of major versions, but instead the version is simply increased by 0.1 on each release. Note that `os.name` is still useful to detect whether we are running a POSIX system (see `BitcoinTestFramework.skip_if_platform_not_posix`), so for this use-case it is kept as only exception. The following table shows values for common operating systems, found via ``` $ python3 -c "import os; import sys; import platform; print(os.name, sys.platform, platform.system())" ``` | OS | os.name | sys.platform | platform.system() | |--------------|---------|--------------|--------------------| | Linux 6.2.0 | posix | linux | Linux | | MacOS* | posix | darwin | Darwin | | OpenBSD 7.4 | posix | openbsd7 | OpenBSD | | Windows* | nt | win32 | Windows | \* = I neither have a MacOS nor a Windows machine available, so I extracted the values from documentation and our current code. Also I'm relying on CI for testing the relevant code-paths. Having reviewers to this this locally would be very appreciated, if this gets Concept ACKed. ACKs for top commit: kevkevinpal: ACK [878d914](bitcoin/bitcoin@878d914) achow101: ACK 878d914 hebasto: ACK 878d914, I have reviewed the code and it looks OK. pablomartin4btc: tACK 878d914 Tree-SHA512: 24513d493e47f572028c843260b81c47c2c29bfb701991050255c9f9529cd19065ecbc7b3b6e15619da7f3f608b4825c345ce6fee30d8fd1eaadbd08cff400fc
2 parents dff6d18 + 878d914 commit bb6de1b

12 files changed

+35
-34
lines changed

test/functional/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ don't have test cases for.
3737
`set_test_params()`, `add_options()` and `setup_xxxx()` methods at the top of
3838
the subclass, then locally-defined helper methods, then the `run_test()` method.
3939
- Use `f'{x}'` for string formatting in preference to `'{}'.format(x)` or `'%s' % x`.
40+
- Use `platform.system()` for detecting the running operating system and `os.name` to
41+
check whether it's a POSIX system (see also the `skip_if_platform_not_{linux,posix}`
42+
methods in the `BitcoinTestFramework` class, which can be used to skip a whole test
43+
depending on the platform).
4044

4145
#### Naming guidelines
4246

test/functional/feature_bind_extra.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,12 @@
77
that bind happens on the expected ports.
88
"""
99

10-
import sys
11-
1210
from test_framework.netutil import (
1311
addr_to_hex,
1412
get_bind_addrs,
1513
)
1614
from test_framework.test_framework import (
1715
BitcoinTestFramework,
18-
SkipTest,
1916
)
2017
from test_framework.util import (
2118
assert_equal,
@@ -32,12 +29,11 @@ def set_test_params(self):
3229
self.bind_to_localhost_only = False
3330
self.num_nodes = 2
3431

35-
def setup_network(self):
32+
def skip_test_if_missing_module(self):
3633
# Due to OS-specific network stats queries, we only run on Linux.
37-
self.log.info("Checking for Linux")
38-
if not sys.platform.startswith('linux'):
39-
raise SkipTest("This test can only be run on Linux.")
34+
self.skip_if_platform_not_linux()
4035

36+
def setup_network(self):
4137
loopback_ipv4 = addr_to_hex("127.0.0.1")
4238

4339
# Start custom ports by reusing unused p2p ports

test/functional/feature_config_args.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
import os
88
from pathlib import Path
9+
import platform
910
import re
10-
import sys
1111
import tempfile
1212
import time
1313

@@ -116,7 +116,7 @@ def test_config_file_parser(self):
116116
def test_config_file_log(self):
117117
# Disable this test for windows currently because trying to override
118118
# the default datadir through the environment does not seem to work.
119-
if sys.platform == "win32":
119+
if platform.system() == "Windows":
120120
return
121121

122122
self.log.info('Test that correct configuration path is changed when configuration file changes the datadir')
@@ -339,7 +339,7 @@ def test_ignored_conf(self):
339339
def test_ignored_default_conf(self):
340340
# Disable this test for windows currently because trying to override
341341
# the default datadir through the environment does not seem to work.
342-
if sys.platform == "win32":
342+
if platform.system() == "Windows":
343343
return
344344

345345
self.log.info('Test error is triggered when bitcoin.conf in the default data directory sets another datadir '

test/functional/feature_init.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
# Distributed under the MIT software license, see the accompanying
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Stress tests related to node initialization."""
6-
import os
76
from pathlib import Path
7+
import platform
88
import shutil
99

1010
from test_framework.test_framework import BitcoinTestFramework, SkipTest
@@ -36,7 +36,7 @@ def run_test(self):
3636
# and other approaches (like below) don't work:
3737
#
3838
# os.kill(node.process.pid, signal.CTRL_C_EVENT)
39-
if os.name == 'nt':
39+
if platform.system() == 'Windows':
4040
raise SkipTest("can't SIGTERM on Windows")
4141

4242
self.stop_node(0)

test/functional/feature_notifications.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Test the -alertnotify, -blocknotify and -walletnotify options."""
66
import os
7+
import platform
78

89
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
910
from test_framework.descriptors import descsum_create
@@ -14,13 +15,13 @@
1415

1516
# Linux allow all characters other than \x00
1617
# Windows disallow control characters (0-31) and /\?%:|"<>
17-
FILE_CHAR_START = 32 if os.name == 'nt' else 1
18+
FILE_CHAR_START = 32 if platform.system() == 'Windows' else 1
1819
FILE_CHAR_END = 128
19-
FILE_CHARS_DISALLOWED = '/\\?%*:|"<>' if os.name == 'nt' else '/'
20+
FILE_CHARS_DISALLOWED = '/\\?%*:|"<>' if platform.system() == 'Windows' else '/'
2021
UNCONFIRMED_HASH_STRING = 'unconfirmed'
2122

2223
def notify_outputname(walletname, txid):
23-
return txid if os.name == 'nt' else f'{walletname}_{txid}'
24+
return txid if platform.system() == 'Windows' else f'{walletname}_{txid}'
2425

2526

2627
class NotificationsTest(BitcoinTestFramework):
@@ -181,7 +182,7 @@ def expect_wallet_notify(self, tx_details):
181182
# Universal newline ensures '\n' on 'nt'
182183
assert_equal(text[-1], '\n')
183184
text = text[:-1]
184-
if os.name == 'nt':
185+
if platform.system() == 'Windows':
185186
# On Windows, echo as above will append a whitespace
186187
assert_equal(text[-1], ' ')
187188
text = text[:-1]

test/functional/feature_remove_pruned_files_on_startup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Test removing undeleted pruned blk files on startup."""
66

7+
import platform
78
import os
89
from test_framework.test_framework import BitcoinTestFramework
910

@@ -32,7 +33,7 @@ def run_test(self):
3233
self.nodes[0].pruneblockchain(600)
3334

3435
# Windows systems will not remove files with an open fd
35-
if os.name != 'nt':
36+
if platform.system() != 'Windows':
3637
assert not os.path.exists(blk0)
3738
assert not os.path.exists(rev0)
3839
assert not os.path.exists(blk1)

test/functional/rpc_bind.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Test running bitcoind with the -rpcbind and -rpcallowip options."""
66

7-
import sys
8-
97
from test_framework.netutil import all_interfaces, addr_to_hex, get_bind_addrs, test_ipv6_local
108
from test_framework.test_framework import BitcoinTestFramework, SkipTest
119
from test_framework.util import assert_equal, assert_raises_rpc_error, get_rpc_proxy, rpc_port, rpc_url
@@ -17,6 +15,10 @@ def set_test_params(self):
1715
self.num_nodes = 1
1816
self.supports_cli = False
1917

18+
def skip_test_if_missing_module(self):
19+
# due to OS-specific network stats queries, this test works only on Linux
20+
self.skip_if_platform_not_linux()
21+
2022
def setup_network(self):
2123
self.add_nodes(self.num_nodes, None)
2224

@@ -61,14 +63,9 @@ def run_allowip_test(self, allow_ips, rpchost, rpcport):
6163
self.stop_nodes()
6264

6365
def run_test(self):
64-
# due to OS-specific network stats queries, this test works only on Linux
6566
if sum([self.options.run_ipv4, self.options.run_ipv6, self.options.run_nonloopback]) > 1:
6667
raise AssertionError("Only one of --ipv4, --ipv6 and --nonloopback can be set")
6768

68-
self.log.info("Check for linux")
69-
if not sys.platform.startswith('linux'):
70-
raise SkipTest("This test can only be run on linux.")
71-
7269
self.log.info("Check for ipv6")
7370
have_ipv6 = test_ipv6_local()
7471
if not have_ipv6 and not (self.options.run_ipv4 or self.options.run_nonloopback):

test/functional/test_framework/p2p.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from collections import defaultdict
2525
from io import BytesIO
2626
import logging
27+
import platform
2728
import struct
2829
import sys
2930
import threading
@@ -592,7 +593,7 @@ def __init__(self):
592593

593594
NetworkThread.listeners = {}
594595
NetworkThread.protos = {}
595-
if sys.platform == 'win32':
596+
if platform.system() == 'Windows':
596597
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
597598
NetworkThread.network_event_loop = asyncio.new_event_loop()
598599

test/functional/test_framework/test_node.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
import json
1313
import logging
1414
import os
15+
import platform
1516
import re
1617
import subprocess
1718
import tempfile
1819
import time
1920
import urllib.parse
2021
import collections
2122
import shlex
22-
import sys
2323
from pathlib import Path
2424

2525
from .authproxy import (
@@ -566,7 +566,7 @@ def test_success(cmd):
566566
cmd, shell=True,
567567
stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL) == 0
568568

569-
if not sys.platform.startswith('linux'):
569+
if platform.system() != 'Linux':
570570
self.log.warning("Can't profile with perf; only available on Linux platforms")
571571
return None
572572

test/functional/test_framework/util.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
import logging
1414
import os
1515
import pathlib
16+
import platform
1617
import re
17-
import sys
1818
import time
1919

2020
from . import coverage
@@ -414,12 +414,12 @@ def get_temp_default_datadir(temp_dir: pathlib.Path) -> tuple[dict, pathlib.Path
414414
"""Return os-specific environment variables that can be set to make the
415415
GetDefaultDataDir() function return a datadir path under the provided
416416
temp_dir, as well as the complete path it would return."""
417-
if sys.platform == "win32":
417+
if platform.system() == "Windows":
418418
env = dict(APPDATA=str(temp_dir))
419419
datadir = temp_dir / "Bitcoin"
420420
else:
421421
env = dict(HOME=str(temp_dir))
422-
if sys.platform == "darwin":
422+
if platform.system() == "Darwin":
423423
datadir = temp_dir / "Library/Application Support/Bitcoin"
424424
else:
425425
datadir = temp_dir / ".bitcoin"

0 commit comments

Comments
 (0)