File: conftest.py

package info (click to toggle)
itango 0.3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,984 kB
  • sloc: python: 1,476; makefile: 14
file content (118 lines) | stat: -rw-r--r-- 3,809 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
# SPDX-FileCopyrightText: All Contributors to the ITango project
# SPDX-License-Identifier: LGPL-3.0-or-later

import contextlib
import os
import socket
import subprocess
import sys
import time
from unittest import mock

import pytest
import tango
from IPython.terminal.interactiveshell import TerminalInteractiveShell


def get_free_port():
    """Return a free port

    There is a race condition as another process could take
    the port after the function returns,
    but it's probably good enough.
    """
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        # Bind to a free port provided by the OS
        s.bind(("127.0.0.1", 0))
        return s.getsockname()[1]


@pytest.fixture(scope="session")
def mock_tango_host():
    """Mock TANGO_HOST env variable"""
    PYTANGO_HOST = f"127.0.0.1:{get_free_port()}"
    with mock.patch.dict(os.environ, {"TANGO_HOST": PYTANGO_HOST}):
        yield PYTANGO_HOST


@pytest.fixture(scope="session")
def pytango_db(mock_tango_host):
    """Start PyTango database and TangoTest"""
    # Copy env to inherit PATH
    # When passing env to subprocess, it fully replaces os.environ
    env = os.environ.copy()
    # Don't write to disk
    env["PYTANGO_DATABASE_NAME"] = ":memory:"
    try:
        databaseds = subprocess.Popen(
            [sys.executable, "-m", "tango.databaseds.database", "2"],
            stderr=subprocess.PIPE,
            env=env,
        )
        waited = 0
        dt = 0.3
        while True:
            time.sleep(dt)
            waited += dt
            if databaseds.poll() is not None:
                stderr = databaseds.stderr.read().decode()
                print("------------------STDERR------------------")
                print(stderr)
                print("------------------------------------------")
                raise RuntimeError(f"Database stopped: {databaseds.returncode}")
            try:
                host, port = mock_tango_host.split(":")
                db = tango.Database(host, int(port))
                db.get_info()
                break
            except tango.DevFailed as exc:
                if waited > 10:
                    raise RuntimeError(
                        f"Tired of waiting for database...{exc}"
                    ) from exc

        # Start TangoTest
        tango_test = subprocess.Popen(
            ["TangoTest", "test"],
            stderr=subprocess.PIPE,
        )
        waited = 0
        dt = 0.3
        while True:
            time.sleep(dt)
            waited += dt
            if tango_test.poll() is not None:
                stderr = tango_test.stderr.read().decode()
                print("------------------STDERR------------------")
                print(stderr)
                print("------------------------------------------")
                raise RuntimeError(f"TangoTest stopped: {tango_test.returncode}")
            try:
                proxy = tango.DeviceProxy(f"tango://{mock_tango_host}/sys/tg_test/1")
                proxy.ping()
                if proxy.read_attribute("State").value == tango.DevState.RUNNING:
                    break
            except tango.DevFailed as exc:
                if waited > 10:
                    raise RuntimeError(
                        f"Tired of waiting for device proxy...{exc}"
                    ) from exc

        yield mock_tango_host

    finally:
        # Clean up
        try:
            tango_test.kill()
        except Exception:
            pass
        with contextlib.suppress(Exception):
            databaseds.kill()


@pytest.fixture(scope="session")
def ipython_shell(pytango_db):
    # Create and return a fresh IPython shell instance
    shell = TerminalInteractiveShell.instance()
    shell.extension_manager.load_extension("itango")
    yield shell