blob: 45387247640259d6700569bcbef86671e5693a76 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
import subprocess
import tempfile
import threading
import unittest
ROOT = Path(__file__).resolve().parents[2]
HELPER = ROOT / "cpp/cmake/DownloadWithRetry.cmake"
class DependencyDownloadTest(unittest.TestCase):
def download(self, responses):
state = {"requests": 0}
payload = b"verified upstream archive payload"
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
index = state["requests"]
state["requests"] += 1
status = responses[min(index, len(responses) - 1)]
self.send_response(status)
self.send_header("Content-Length", str(len(payload)))
self.end_headers()
self.wfile.write(payload)
def log_message(self, *args):
pass
with ThreadingHTTPServer(("127.0.0.1", 0), Handler) as server:
worker = threading.Thread(target=server.serve_forever, daemon=True)
worker.start()
try:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
destination = root / "archive.part"
status_file = root / "status.txt"
script = root / "download.cmake"
script.write_text(
"cmake_minimum_required(VERSION 3.11)\n"
f'include("{HELPER.as_posix()}")\n'
f'tsfile_download_with_retry("http://127.0.0.1:{server.server_port}/archive" '
f'"{destination.as_posix()}" status)\n'
f'file(WRITE "{status_file.as_posix()}" "${{status}}")\n'
)
subprocess.run(
["cmake", "-P", str(script)],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
timeout=30,
)
status = int(status_file.read_text().split(";", 1)[0])
contents = (
destination.read_bytes() if destination.exists() else None
)
return status, state["requests"], contents, payload
finally:
server.shutdown()
worker.join(timeout=5)
def test_transient_server_failures_retry_then_succeed(self):
status, requests, contents, payload = self.download([503, 503, 200])
self.assertEqual(status, 0)
self.assertEqual(requests, 3)
self.assertEqual(contents, payload)
def test_repeated_failures_stop_after_three_attempts_and_remove_partial_file(self):
status, requests, contents, _ = self.download([503])
self.assertNotEqual(status, 0)
self.assertEqual(requests, 3)
self.assertIsNone(contents)
def test_permanent_http_error_is_not_retried(self):
status, requests, contents, _ = self.download([404])
self.assertNotEqual(status, 0)
self.assertEqual(requests, 1)
self.assertIsNone(contents)
def test_success_does_not_repeat_the_download(self):
status, requests, contents, payload = self.download([200])
self.assertEqual(status, 0)
self.assertEqual(requests, 1)
self.assertEqual(contents, payload)
if __name__ == "__main__":
unittest.main()