Files
test2/tests/python/bl_pyapi_bpy_app_tempdir.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

207 lines
6.7 KiB
Python
Raw Permalink Normal View History

# SPDX-FileCopyrightText: 2025 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
# ./blender.bin --background --factory-startup --python tests/python/bl_pyapi_bpy_app_tempdir.py -- --verbose
# NOTE(ideasman42):
#
# - Creating a directory without write permissions across all supported platforms
# is it not trivial, for the purpose of these tests simply create a file as a way of
# pointing to a temporary path which can't have sub-directories created under it.
#
# - Internally, when no temporary path is set the hard coded path `/tmp/` is used.
# For the purpose of the tests being complete - control over this path would be needed too.
#
# - When changing the temp directory & on exit
# Blender's *session* temp directory is recursively removed.
#
# These tests were added as part of a fix for a serious flaw (see #144042)
# which would recursively delete the users `C:\` since the tests aren't sand-boxed
# all of the following tests reference paths within a newly creating temporary directory
# instead of referencing root paths to avoid risks for developers who run tests.
#
# - A related test exists `BLI_tempfile_test.cc` however this doesn't deal with
# Blender's user preferences and fallbacks used in Blender.
#
__all__ = (
"main",
)
import os
import unittest
import tempfile
is_win32 = os.name == "nt"
TEMP_ENV = "TEMP" if is_win32 else "TMPDIR"
def system_temp_set(path: str) -> None:
os.environ[TEMP_ENV] = path
def prefs_temp_set(path: str) -> None:
import bpy # type: ignore
bpy.context.preferences.filepaths.temporary_directory = path
def prefs_temp_get() -> str:
import bpy
result = bpy.context.preferences.filepaths.temporary_directory
assert isinstance(result, str)
return result
def blender_tempdir_session_get() -> str:
import bpy
result = bpy.app.tempdir
assert isinstance(result, str)
return result
def empty_file(path: str) -> None:
with open(path, 'wb') as _fh:
pass
def commonpath_safe(paths: list[str]) -> str:
if is_win32:
try:
return os.path.commonpath(paths)
except ValueError:
# Workaround error on Windows.
# ValueError: Paths don't have the same drive
return ""
return os.path.commonpath(paths)
class TestTempDir(unittest.TestCase):
def setUp(self) -> None:
print(prefs_temp_get())
assert prefs_temp_get() == ""
system_temp_set("")
prefs_temp_set("")
def tearDown(self) -> None:
prefs_temp_set("")
system_temp_set("")
def test_fallback(self) -> None:
# Set a file for preferences & system temp, ensure neither are used.
with tempfile.TemporaryDirectory() as tempdir:
temp_sys = os.path.join(tempdir, "a_sys")
temp_bpy = os.path.join(tempdir, "b_bpy")
empty_file(temp_sys)
empty_file(temp_bpy)
system_temp_set(temp_sys)
prefs_temp_set(temp_bpy)
temp_session = blender_tempdir_session_get()
# Ensure neither are used.
# This will try to use `/tmp/`.
commonpath_test = commonpath_safe([temp_sys, temp_session])
self.assertFalse(commonpath_test and os.path.samefile(temp_sys, commonpath_test))
commonpath_test = commonpath_safe([temp_bpy, temp_session])
self.assertFalse(commonpath_test and os.path.samefile(temp_bpy, commonpath_test))
def test_system(self) -> None:
# Set an file as the preferences temp directory, ensure the system path is used.
with tempfile.TemporaryDirectory() as tempdir:
temp_sys = os.path.join(tempdir, "a_sys")
temp_bpy = os.path.join(tempdir, "b_bpy")
os.mkdir(temp_sys)
empty_file(temp_bpy)
system_temp_set(temp_sys)
prefs_temp_set(temp_bpy)
temp_session = blender_tempdir_session_get()
self.assertTrue(os.path.samefile(temp_sys, os.path.commonpath([temp_sys, temp_session])))
def test_prefs(self) -> None:
# Set an file as the system temp directory, ensure the preferences path is used.
with tempfile.TemporaryDirectory() as tempdir:
temp_sys = os.path.join(tempdir, "a_sys")
temp_bpy = os.path.join(tempdir, "b_bpy")
empty_file(temp_sys)
os.mkdir(temp_bpy)
system_temp_set(temp_sys)
prefs_temp_set(temp_bpy)
temp_session = blender_tempdir_session_get()
self.assertTrue(os.path.samefile(temp_bpy, os.path.commonpath([temp_bpy, temp_session])))
def test_system_to_prefs(self) -> None:
with tempfile.TemporaryDirectory() as tempdir:
temp_sys = os.path.join(tempdir, "a_sys")
temp_bpy = os.path.join(tempdir, "b_bpy")
os.mkdir(temp_sys)
empty_file(temp_bpy)
system_temp_set(temp_sys)
prefs_temp_set(temp_bpy)
temp_session = blender_tempdir_session_get()
self.assertTrue(os.path.samefile(temp_sys, os.path.commonpath([temp_sys, temp_session])))
# Now set the preferences and ensure the previous directory gets purged and the new one set.
os.unlink(temp_bpy)
os.mkdir(temp_bpy)
# Ensure the preferences path is now used.
prefs_temp_set(temp_bpy)
self.assertFalse(os.path.exists(temp_session))
temp_session = blender_tempdir_session_get()
self.assertTrue(os.path.samefile(temp_bpy, os.path.commonpath([temp_bpy, temp_session])))
def test_prefs_to_system(self) -> None:
with tempfile.TemporaryDirectory() as tempdir:
temp_sys = os.path.join(tempdir, "a_sys")
temp_bpy = os.path.join(tempdir, "b_bpy")
empty_file(temp_sys)
os.mkdir(temp_bpy)
system_temp_set(temp_sys)
prefs_temp_set(temp_bpy)
temp_session = blender_tempdir_session_get()
self.assertTrue(os.path.samefile(temp_bpy, os.path.commonpath([temp_bpy, temp_session])))
# Now set the preferences and ensure the previous directory gets purged and the new one set.
os.unlink(temp_sys)
os.mkdir(temp_sys)
# Ensure the system path is now used.
prefs_temp_set(temp_sys)
self.assertFalse(os.path.exists(temp_session))
temp_session = blender_tempdir_session_get()
self.assertTrue(os.path.samefile(temp_sys, os.path.commonpath([temp_sys, temp_session])))
def main():
import sys
unittest.main(argv=[__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []))
if __name__ == "__main__":
main()