Force file names to be lower case to avoid Windows issues around case sensitivity when checking if a file already exists. Path names can be uppercase as these will be defined on a users system and pre-exist. Files are created by us so we can force sanitation
This commit is contained in:
parent
35f7adcf07
commit
2c2f97cb56
3 changed files with 55 additions and 12 deletions
|
|
@ -164,7 +164,9 @@ def make_path_safe(unsafe_path_string: str) -> str:
|
||||||
else:
|
else:
|
||||||
sanitised = unsafe_character_pattern.sub("_", sanitised)
|
sanitised = unsafe_character_pattern.sub("_", sanitised)
|
||||||
|
|
||||||
sanitised_components.append(_sanitise_reserved(sanitised))
|
sanitised_components.append(
|
||||||
|
_sanitise_reserved(sanitised, is_filename=False)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
sanitised_components.append(component)
|
sanitised_components.append(component)
|
||||||
|
|
||||||
|
|
@ -194,20 +196,26 @@ def make_name_safe(unsafe_name_string: str) -> str:
|
||||||
return _sanitise_reserved(name)
|
return _sanitise_reserved(name)
|
||||||
|
|
||||||
|
|
||||||
def _sanitise_reserved(name: str) -> str:
|
def _sanitise_reserved(name: str, is_filename: bool = True) -> str:
|
||||||
"""Check a name against Windows reserved names.
|
"""Check a name against Windows reserved names.
|
||||||
|
|
||||||
:param name: The component to check.
|
:param name: The component to check.
|
||||||
:returns: The sanitised component.
|
:param is_filename: Whether the component is a filename.
|
||||||
|
If True, names will be forced into lowercase.
|
||||||
|
:returns: The sanitised component, in lower case.
|
||||||
"""
|
"""
|
||||||
# Check for Windows reserved names.
|
# Check for Windows reserved names.
|
||||||
# These are reserved even with an extension (e.g. NUL.txt).
|
# These are reserved even with an extension (e.g. NUL.txt).
|
||||||
# We check the part before the first dot.
|
# We check the part before the first dot.
|
||||||
base_name = name.split(".", maxsplit=1)[0].upper()
|
base_name = name.split(".", maxsplit=1)[0].upper()
|
||||||
if base_name in _WINDOWS_RESERVED_NAMES:
|
if base_name in _WINDOWS_RESERVED_NAMES:
|
||||||
return f"{name}_"
|
return f"{name.lower()}_" if is_filename else f"{name}_"
|
||||||
|
|
||||||
return name
|
# We force names to be lowercase to avoid issues on
|
||||||
|
# Windows where files with the same name
|
||||||
|
# but different case can cause problems when checking if
|
||||||
|
# a file already exists.
|
||||||
|
return name.lower() if is_filename else name
|
||||||
|
|
||||||
|
|
||||||
class VersionData(BaseModel):
|
class VersionData(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,36 @@ def test_scan_sequence_and_listing(caplog):
|
||||||
assert len(caplog.records) == 0
|
assert len(caplog.records) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_scan_sequence_case_sensitive():
|
||||||
|
"""Check created scans are added in order and listed correctly, even when cases are different."""
|
||||||
|
_clear_scan_dir()
|
||||||
|
scan_dir_manager = ScanDirectoryManager(BASE_SCAN_DIR)
|
||||||
|
|
||||||
|
# Create some scan data and mark it as successful to get an end date.
|
||||||
|
scan_data = fake_active_scan_data()
|
||||||
|
scan_data.set_final_data(result="Success")
|
||||||
|
# Make 4 scans
|
||||||
|
scan_dir = scan_dir_manager.new_scan_dir("fake_scan")
|
||||||
|
scan_dir.save_scan_data(scan_data)
|
||||||
|
scan_dir = scan_dir_manager.new_scan_dir("FAKE_SCAN")
|
||||||
|
scan_dir.save_scan_data(scan_data)
|
||||||
|
scan_dir = scan_dir_manager.new_scan_dir("Fake_Scan")
|
||||||
|
scan_dir.save_scan_data(scan_data)
|
||||||
|
scan_dir = scan_dir_manager.new_scan_dir("FAKE_scan")
|
||||||
|
scan_dir.save_scan_data(scan_data)
|
||||||
|
scan_dir = scan_dir_manager.new_scan_dir("fAkE_sCaN")
|
||||||
|
scan_dir.save_scan_data(scan_data)
|
||||||
|
|
||||||
|
# Check they exist and are numbered sequentially
|
||||||
|
all_scans = scan_dir_manager.all_scans
|
||||||
|
assert_unique_of_length(all_scans, 5)
|
||||||
|
assert "fake_scan_0001" in all_scans
|
||||||
|
assert "fake_scan_0002" in all_scans
|
||||||
|
assert "fake_scan_0003" in all_scans
|
||||||
|
assert "fake_scan_0004" in all_scans
|
||||||
|
assert "fake_scan_0005" in all_scans
|
||||||
|
|
||||||
|
|
||||||
def test_scan_name_non_sequential():
|
def test_scan_name_non_sequential():
|
||||||
"""Check new scan has the correct name if the directories are not sequential."""
|
"""Check new scan has the correct name if the directories are not sequential."""
|
||||||
_clear_scan_dir()
|
_clear_scan_dir()
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ def test_make_name_safe_basic():
|
||||||
assert make_name_safe("name with spaces") == "name_with_spaces"
|
assert make_name_safe("name with spaces") == "name_with_spaces"
|
||||||
assert make_name_safe("name/with/slashes") == "name_with_slashes"
|
assert make_name_safe("name/with/slashes") == "name_with_slashes"
|
||||||
assert make_name_safe("name\\with\\backslashes") == "name_with_backslashes"
|
assert make_name_safe("name\\with\\backslashes") == "name_with_backslashes"
|
||||||
|
assert make_name_safe("sPoNgEmoCk") == "spongemock"
|
||||||
|
|
||||||
|
|
||||||
def test_make_name_safe_trailing_chars():
|
def test_make_name_safe_trailing_chars():
|
||||||
|
|
@ -34,21 +35,25 @@ def test_make_name_safe_trailing_chars():
|
||||||
def test_make_name_safe_reserved_names(name):
|
def test_make_name_safe_reserved_names(name):
|
||||||
"""Test Windows reserved names."""
|
"""Test Windows reserved names."""
|
||||||
# Base name should be sanitized
|
# Base name should be sanitized
|
||||||
assert make_name_safe(name) == f"{name}_"
|
assert make_name_safe(name) == f"{name.lower()}_"
|
||||||
# Case-insensitive
|
# Case-insensitive
|
||||||
assert make_name_safe(name.lower()) == f"{name.lower()}_"
|
assert make_name_safe(name.lower()) == f"{name.lower()}_"
|
||||||
# With extension
|
# With extension
|
||||||
assert make_name_safe(f"{name}.txt") == f"{name}.txt_"
|
assert make_name_safe(f"{name}.txt") == f"{name.lower()}.txt_"
|
||||||
# Multiple extensions
|
# Multiple extensions
|
||||||
assert make_name_safe(f"{name}.tar.gz") == f"{name}.tar.gz_"
|
assert make_name_safe(f"{name}.tar.gz") == f"{name.lower()}.tar.gz_"
|
||||||
|
|
||||||
|
|
||||||
def test_make_name_safe_reserved_names_false_positives():
|
def test_make_name_safe_reserved_names_false_positives():
|
||||||
"""Test that names containing but not equal to reserved names are safe."""
|
"""Test that names containing but not equal to reserved names are safe."""
|
||||||
assert make_name_safe("CONSTANT") == "CONSTANT"
|
assert make_name_safe("CONSTANT") == "constant"
|
||||||
assert make_name_safe("CON2") == "CON2"
|
assert make_name_safe("CON2") == "con2"
|
||||||
assert make_name_safe("ICON") == "ICON"
|
assert make_name_safe("ICON") == "icon"
|
||||||
assert make_name_safe("CHILLI CON CARNE") == "CHILLI_CON_CARNE"
|
assert make_name_safe("icon") == "icon"
|
||||||
|
assert make_name_safe("iCon") == "icon"
|
||||||
|
assert make_name_safe("iCOn") == "icon"
|
||||||
|
assert make_name_safe("icOn") == "icon"
|
||||||
|
assert make_name_safe("CHILLI CON CARNE") == "chilli_con_carne"
|
||||||
|
|
||||||
|
|
||||||
def test_make_path_safe_basic():
|
def test_make_path_safe_basic():
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue