diff --git a/scripts/download.sh b/scripts/download.sh index 92f40bd3f..9800583e8 100755 --- a/scripts/download.sh +++ b/scripts/download.sh @@ -152,11 +152,12 @@ EOF fi # Staged inside the destination: a pack is gigabytes, and /tmp is a RAM - # disk on the appliances these packs target. + # disk on the appliances these packs target. The folder is this run's own: + # a fixed name let a second download into the same folder empty it while + # the first was still writing its parts. mkdir -p "$dest" - local staging="${dest%/}/.retrobios-download" - rm -rf "$staging" - mkdir -p "$staging" + local staging + staging=$(mktemp -d "${dest%/}/.retrobios-download-XXXXXX") STAGING="$staging" trap cleanup_staging EXIT diff --git a/tests/test_download.py b/tests/test_download.py index fc0c51d6c..0b57a1daa 100644 --- a/tests/test_download.py +++ b/tests/test_download.py @@ -23,6 +23,7 @@ import subprocess import sys import tempfile import threading +import time import unittest import zipfile from pathlib import Path @@ -574,6 +575,34 @@ class TestDownloadShell(DownloaderCase): self.assertNotIn("unbound variable", proc.stderr) self.assertEqual(list(self.dest.iterdir()), []) + def test_two_runs_into_one_folder_both_finish(self): + """Both runs staged in `/.retrobios-download` and the second + emptied it while the first was still writing its parts.""" + shim = Path(_tempdir(self)) + real = shutil.which("curl") + (shim / "curl").write_text( + "#!/bin/sh\n" + 'case "$*" in *-o*) sleep 1 ;; esac\n' + f'exec "{real}" "$@"\n' + ) + (shim / "curl").chmod(0o755) + env = self.env() + env["PATH"] = f"{shim}{os.pathsep}{env['PATH']}" + command = ["bash", str(SHELL), self.platform, str(self.dest)] + first = subprocess.Popen( + command, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + text=True, + ) + time.sleep(0.5) + second = subprocess.run( + command, env=env, capture_output=True, text=True, timeout=120 + ) + out, err = first.communicate(timeout=120) + self.assertEqual(first.returncode, 0, out + err) + self.assertEqual(second.returncode, 0, second.stdout + second.stderr) + self.assert_extracted() + self.assert_no_leftovers() + class TestWholePackPython(WholePackCase, TestDownloadPython): def test_info_reports_every_volume(self):