forked from conda/conda
-
Notifications
You must be signed in to change notification settings - Fork 0
/
fixtures_jlap.py
129 lines (96 loc) · 3.28 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
# 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, 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):
global server, base
base = base_
assert isinstance(socket.fileno(), int)
server = make_server(
"127.0.0.1",
port=0,
app=app,
fd=socket.fileno(),
threaded=True,
request_handler=NoLoggingWSGIRequestHandler,
)
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):
socket = prepare_socket()
context = multiprocessing.get_context("spawn")
process = context.Process(
target=make_server_with_socket, args=(socket, base), 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)
if __name__ == "__main__":
print(run_on_random_port())
time.sleep(60)