forked from conda/conda
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfixtures_jlap.py
141 lines (104 loc) · 3.66 KB
/
fixtures_jlap.py
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
# Copyright (C) 2012 Anaconda, Inc
# SPDX-License-Identifier: BSD-3-Clause
"""
Flask-based conda repository server for testing.
Change contents to simulate an updating repository.
Must be imported by conftest.py for pytest to see the fixtures.
"""
from __future__ import annotations
import multiprocessing
import shutil
import socket
import time
from pathlib import Path
import flask
import pytest
from werkzeug.serving import WSGIRequestHandler, generate_adhoc_ssl_context, make_server
app = flask.Flask(__name__)
TEST_REPOSITORY = Path(__file__).parents[0] / "data" / "conda_format_repo"
base = TEST_REPOSITORY # set to per-test-run value
LATENCY = 0
@app.route("/shutdown")
def shutdown():
server.shutdown()
return "Goodbye"
@app.route("/latency/<float:delay>")
def latency(delay):
"""Set delay before each file response."""
global LATENCY
LATENCY = delay
return "OK"
# flask.send_from_directory(directory, path, **kwargs)
# Send a file from within a directory using send_file().
@app.route("/test/<subdir>/<path:name>")
def download_file(subdir, name):
time.sleep(LATENCY)
# conditional=True is the default
# Could have layers, and while raises NotFound (check next base directory)
return flask.send_from_directory(Path(base, subdir), name)
class NoLoggingWSGIRequestHandler(WSGIRequestHandler):
def log(self, format, *args):
pass
def make_server_with_socket(socket: socket.socket, base_: Path = base, ssl=False):
global server, base
base = base_
assert isinstance(socket.fileno(), int)
ssl_context = None
if ssl:
# openssl may fail when mixing defaults + conda-forge
ssl_context = generate_adhoc_ssl_context()
server = make_server(
"127.0.0.1",
port=0,
app=app,
fd=socket.fileno(),
threaded=True,
request_handler=NoLoggingWSGIRequestHandler,
ssl_context=ssl_context,
)
server.serve_forever()
def run_on_random_port():
"""Run in a new process to minimize interference with test."""
return next(_package_server())
def prepare_socket() -> socket.socket:
"""Prepare a socket for use by the WSGI server.
Based on Werkzeug prepare_socket, removed in 2.2.3
"""
host = "127.0.0.1"
port = 0 # automatically choose an available port
server_address = (host, port)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.set_inheritable(True)
s.bind(server_address)
s.listen()
return s
def _package_server(cleanup=True, base: Path | None = None, ssl=False):
socket = prepare_socket()
context = multiprocessing.get_context("spawn")
process = context.Process(
target=make_server_with_socket, args=(socket, base, ssl), daemon=True
)
process.start()
yield socket
process.kill()
@pytest.fixture(scope="session")
def package_repository_base(tmp_path_factory):
"""
Copy tests/index_data to avoid writing changes to repository.
Could be made session-scoped if we don't mind re-using the index cache
during tests.
"""
# destination can't exist for shutil.copytree(); create its parent
destination = tmp_path_factory.mktemp("repo") / TEST_REPOSITORY.name
shutil.copytree(TEST_REPOSITORY, destination)
return destination
@pytest.fixture(scope="session")
def package_server(package_repository_base):
yield from _package_server(base=package_repository_base)
@pytest.fixture(scope="session")
def package_server_ssl(package_repository_base):
yield from _package_server(base=package_repository_base, ssl=True)
if __name__ == "__main__":
print(run_on_random_port())
time.sleep(60)