File: common.py

package info (click to toggle)
aiocoap 0.4.14-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,756 kB
  • sloc: python: 16,846; makefile: 23; sh: 9
file content (164 lines) | stat: -rw-r--r-- 5,404 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# SPDX-FileCopyrightText: Christian Amsüss and the aiocoap contributors
#
# SPDX-License-Identifier: MIT

"""Non-fixture utilities shared between tests"""

import sys
import os
import asyncio

import aiocoap.defaults

if os.environ.get("AIOCOAP_TESTS_LOOP", None) == "uvloop":
    import asyncio
    import uvloop

    asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
elif os.environ.get("AIOCOAP_TESTS_LOOP", None) == "gbulb":
    import gbulb

    gbulb.install()
elif os.environ.get("AIOCOAP_TESTS_LOOP", None) == "glib":
    from gi.events import GLibEventLoopPolicy

    policy = GLibEventLoopPolicy()
    asyncio.set_event_loop_policy(policy)

# All test servers are bound to loopback; if for any reason one'd want to run
# with particular transports, just set them explicitly.
os.environ["AIOCOAP_DTLSSERVER_ENABLED"] = "1"

if "coverage" in sys.modules:
    PYTHON_PREFIX = [sys.executable, "-m", "coverage", "run", "--parallel-mode"]
else:
    PYTHON_PREFIX = [sys.executable]


def _find_loopbacknames():
    """Try the lookup results of common 'localhost' names and variations to
    return, in order, a name that resolves to 127.0.0.1, one that resolves to
    ::1 and one that can be resolved to both. If there is no result for any of
    the categories, None is returned in that place."""

    import socket

    candidates = [
        # the obvious choice
        "localhost",
        # seen on debian; ip6-localhost is present on debian too
        "ip6-localhost",
        "ip6-loopback",
    ]

    v4 = []
    v6 = []
    for c in candidates:
        try:
            results = socket.getaddrinfo(c, 1234, family=socket.AF_INET)
        except socket.gaierror:
            pass
        else:
            if results and all(x[4] == ("127.0.0.1", 1234) for x in results):
                v4.append(c)
        try:
            # Not probing for AF_INET6 because Windows applies its regular
            # resolution rules (that appear to be "give out V6 addresses to
            # unspecified families only when a V6 route is available") even to
            # 'ip6-loopback' (while 'localhost' is exempt and returns both).
            #
            # If we probed AF_INET6 here, on win32 we'd see ip6-localhost as a
            # usable address, but when the simple6 client transport later asks
            # getaddrinf unspecified (because it's really generic, despite its
            # name), that would come up empty when no route is availasble.
            #
            # (An alternative here would be to query V6 in the first place,
            # check `all` instead of `any` againt com and before appending do
            # another check on whether it still returns something to an
            # unspecified query)
            results = socket.getaddrinfo(c, 1234)
        except socket.gaierror:
            pass
        else:
            if results and any(x[4][:2] == ("::1", 1234) for x in results):
                v6.append(c)

    v4only = [c for c in v4 if c not in v6]
    v6only = [c for c in v6 if c not in v4]
    v46 = [c for c in v4 if c in v6]

    return (
        v4only[0] if v4only else None,
        v6only[0] if v6only else None,
        v46[0] if v46 else None,
    )


loopbackname_v4, loopbackname_v6, loopbackname_v46 = _find_loopbacknames()

using_simple6 = "simple6" in list(aiocoap.defaults.get_default_clienttransports())

tcp_disabled = "tcp" not in os.environ.get("AIOCOAP_SERVER_TRANSPORT", "tcp is default")
ws_disabled = "ws" not in os.environ.get("AIOCOAP_SERVER_TRANSPORT", "ws is default")
dtls_disabled = "dtls" not in os.environ.get(
    "AIOCOAP_SERVER_TRANSPORT", "dtls is default"
)


class CapturingSubprocess(asyncio.SubprocessProtocol):
    """This protocol just captures stdout and stderr into properties of the
    same name.

    Unlike using communicate() on a create_subprocess_exec product, this does
    not discard any output that was collected when the task is cancelled, and
    thus allows cleanup.

    No way of passing data into the process is implemented, as it is not needed
    here."""

    def __init__(self):
        self.stdout = b""
        self.stderr = b""
        self.read_more = asyncio.get_running_loop().create_future()

    def pipe_data_received(self, fd, data):
        self.read_more.set_result(None)
        self.read_more = asyncio.get_running_loop().create_future()
        if fd == 1:
            self.stdout += data
        elif fd == 2:
            self.stderr += data
        else:
            raise ValueError("Data on unexpected fileno")

    def process_exited(self):
        self.read_more.set_result(None)


def run_fixture_as_standalone_server(fixture):
    import sys
    import logging

    if "-v" in sys.argv:
        logging.basicConfig()
        logging.getLogger("coap").setLevel(logging.DEBUG)
        logging.getLogger("coap-server").setLevel(logging.DEBUG)

    print("Running test server")
    s = fixture()
    s.setUp()
    try:
        s.loop.run_forever()
    except KeyboardInterrupt:
        print("Shutting down test server")
        s.tearDown()


if __name__ == "__main__":
    print("Python prefix:", PYTHON_PREFIX)
    print(
        "Loopback names:\n  %s (IPv4)\n  %s (IPv6),\n  %s (IPv4+IPv6)"
        % (loopbackname_v4, loopbackname_v6, loopbackname_v46)
    )
    print("simple6 transport in use:", using_simple6)
    print("TCP disabled:", tcp_disabled)