1+ import contextlib
12import os
23import stat
34import time
4- from typing import Optional , cast
5+ from typing import Any , Generator , Optional , cast
56from unittest import mock
67from unittest .mock import MagicMock
78
@@ -64,6 +65,15 @@ def fixture_copy_tree_paths(tmppath: Path) -> CopyTreePathConfig:
6465 return source_dir , dest_dir , symlinks_supported
6566
6667
68+ @pytest .fixture (name = "tmpdir_creator" )
69+ def fixture_tmpdir_creator (tmppath : Path ) -> tmt .utils .filesystem .TmpDirCreator :
70+ @contextlib .contextmanager
71+ def _tmpdir_creator (prefix : Optional [str ] = None , suffix : Optional [str ] = None ) -> Generator [Path , None , None ]:
72+ yield tmppath
73+
74+ return _tmpdir_creator
75+
76+
6777def _assert_permissions_copied (src_path : Path , dest_path : Path ) -> None :
6878 """
6979 Assert that file/directory permissions are copied correctly.
@@ -120,6 +130,7 @@ def _run_metadata_test_for_item(
120130@pytest .mark .parametrize ('strategy' , _STRATEGIES )
121131def test_copy_tree_basic (
122132 strategy : tmt .utils .filesystem .CopyStrategy ,
133+ tmpdir_creator : tmt .utils .filesystem .TmpDirCreator ,
123134 copy_tree_paths : CopyTreePathConfig ,
124135 root_logger : tmt .log .Logger ,
125136) -> None :
@@ -129,7 +140,7 @@ def test_copy_tree_basic(
129140
130141 source_dir , dest_dir , symlinks_supported = copy_tree_paths
131142
132- strategy (source_dir , dest_dir , root_logger )
143+ strategy (src = source_dir , dst = dest_dir , tmpdir_creator = tmpdir_creator , logger = root_logger )
133144
134145 # Check if all files were copied and their content is correct
135146 for path , content in _EXPECTED_TEST_FILES .items ():
@@ -147,7 +158,7 @@ def test_copy_tree_basic(
147158
148159@pytest .mark .parametrize ('strategy' , _STRATEGIES )
149160def test_copy_empty_source_directory (
150- strategy : tmt .utils .filesystem .CopyStrategy , tmppath : Path , root_logger : tmt .log .Logger
161+ strategy : tmt .utils .filesystem .CopyStrategy , tmpdir_creator : tmt . utils . filesystem . TmpDirCreator , tmppath : Path , root_logger : tmt .log .Logger
151162) -> None :
152163 """
153164 Test copying an empty source directory.
@@ -157,7 +168,7 @@ def test_copy_empty_source_directory(
157168 empty_dst = tmppath / "empty_dst"
158169 empty_src .mkdir ()
159170
160- strategy (empty_src , empty_dst , root_logger )
171+ strategy (src = empty_src , dst = empty_dst , tmpdir_creator = tmpdir_creator , logger = root_logger )
161172
162173 # Verify the destination directory exists and is empty
163174 assert empty_dst .exists ()
@@ -167,7 +178,7 @@ def test_copy_empty_source_directory(
167178
168179@pytest .mark .parametrize ('strategy' , _STRATEGIES )
169180def test_deeply_nested_directories (
170- strategy : tmt .utils .filesystem .CopyStrategy , tmppath : Path , root_logger : tmt .log .Logger
181+ strategy : tmt .utils .filesystem .CopyStrategy , tmpdir_creator : tmt . utils . filesystem . TmpDirCreator , tmppath : Path , root_logger : tmt .log .Logger
171182) -> None :
172183 """Test copying deeply nested directory structures."""
173184 deep_src = tmppath / "deep_src"
@@ -181,7 +192,7 @@ def test_deeply_nested_directories(
181192 current_dir .mkdir ()
182193 (current_dir / f"file_at_level_{ level } .txt" ).write_text (f"{ test_content } at level { level } " )
183194
184- strategy (deep_src , deep_dst , root_logger )
195+ strategy (src = deep_src , dst = deep_dst , tmpdir_creator = tmpdir_creator , logger = root_logger )
185196
186197 # Check if the deepest directory and file exist in the copied structure
187198 deepest_path = Path (
@@ -207,7 +218,7 @@ def test_permission_error_handling(
207218 dest_dir .chmod (0o444 )
208219
209220 with pytest .raises (tmt .utils .GeneralError , match = r'(?i)Failed to copy tree' ):
210- tmt .utils .filesystem .copy_tree (source_dir , dest_dir , root_logger )
221+ tmt .utils .filesystem .copy_tree (src = source_dir , dst = dest_dir , logger = root_logger )
211222
212223
213224def test_nonexistent_source_directory (tmppath : Path , root_logger : tmt .log .Logger ) -> None :
@@ -219,7 +230,7 @@ def test_nonexistent_source_directory(tmppath: Path, root_logger: tmt.log.Logger
219230 destination = tmppath / "destination"
220231
221232 with pytest .raises (tmt .utils .GeneralError , match = r'.*not a directory or does not exist.*' ):
222- tmt .utils .filesystem .copy_tree (nonexistent_src , destination , root_logger )
233+ tmt .utils .filesystem .copy_tree (src = nonexistent_src , dst = destination , logger = root_logger )
223234
224235
225236@mock .patch (
@@ -241,10 +252,10 @@ def test_fallback(
241252
242253 mock_copy_tree_cp , mock_copy_tree_shutil = tmt .utils .filesystem ._COPY_TREE_STRATEGIES
243254
244- tmt .utils .filesystem .copy_tree (source_dir , dest_dir , root_logger )
255+ tmt .utils .filesystem .copy_tree (src = source_dir , dst = dest_dir , logger = root_logger )
245256
246- cast (MagicMock , mock_copy_tree_cp ).assert_called_once_with (source_dir , dest_dir , mock .ANY )
247- cast (MagicMock , mock_copy_tree_shutil ).assert_called_once_with (source_dir , dest_dir , mock .ANY )
257+ cast (MagicMock , mock_copy_tree_cp ).assert_called_once_with (src = source_dir , dst = dest_dir , tmpdir_creator = None , logger = mock .ANY )
258+ cast (MagicMock , mock_copy_tree_shutil ).assert_called_once_with (src = source_dir , dst = dest_dir , tmpdir_creator = None , logger = mock .ANY )
248259
249260 # Verify files were copied using the fallback approach (shutil.copytree)
250261 for file_path in _EXPECTED_TEST_FILES :
@@ -254,6 +265,7 @@ def test_fallback(
254265@pytest .mark .parametrize ('strategy' , _STRATEGIES )
255266def test_metadata_preservation (
256267 strategy : tmt .utils .filesystem .CopyStrategy ,
268+ tmpdir_creator : tmt .utils .filesystem .TmpDirCreator ,
257269 copy_tree_paths : CopyTreePathConfig ,
258270 root_logger : tmt .log .Logger ,
259271) -> None :
@@ -277,7 +289,7 @@ def test_metadata_preservation(
277289 source_dir , "meta_dir_shutil" , is_dir = True , mode = 0o500 , atime = timestamp , mtime = timestamp
278290 )
279291
280- strategy (source_dir , dest_dir , root_logger )
292+ strategy (src = source_dir , dst = dest_dir , tmpdir_creator = tmpdir_creator , logger = root_logger )
281293
282294 _run_metadata_test_for_item (dest_dir , test_file )
283295 _run_metadata_test_for_item (dest_dir , test_dir )
@@ -302,7 +314,7 @@ def test_all_strategies_fail(
302314 mock_copy_tree_cp , mock_copy_tree_shutil = tmt .utils .filesystem ._COPY_TREE_STRATEGIES
303315
304316 with pytest .raises (tmt .utils .GeneralError ):
305- tmt .utils .filesystem .copy_tree (source_dir , dest_dir , root_logger )
317+ tmt .utils .filesystem .copy_tree (src = source_dir , dst = dest_dir , logger = root_logger )
306318
307319 cast (MagicMock , mock_copy_tree_cp ).assert_called_once ()
308320 cast (MagicMock , mock_copy_tree_shutil ).assert_called_once ()
@@ -311,6 +323,7 @@ def test_all_strategies_fail(
311323@pytest .mark .parametrize ('strategy' , _STRATEGIES )
312324def test_copy_to_existing_destination (
313325 strategy : tmt .utils .filesystem .CopyStrategy ,
326+ tmpdir_creator : tmt .utils .filesystem .TmpDirCreator ,
314327 copy_tree_paths : CopyTreePathConfig ,
315328 root_logger : tmt .log .Logger ,
316329) -> None :
@@ -326,7 +339,7 @@ def test_copy_to_existing_destination(
326339 (dest_dir / "subdir" / "existing_in_subdir.txt" ).write_text ("pre-existing in subdir" )
327340 (dest_dir / "file1.txt" ).write_text ("old file1 content" )
328341
329- strategy (source_dir , dest_dir , root_logger )
342+ strategy (src = source_dir , dst = dest_dir , tmpdir_creator = tmpdir_creator , logger = root_logger )
330343
331344 # Check that source files were copied and overwrite conflicting ones
332345 assert (dest_dir / "file1.txt" ).read_text () == _EXPECTED_TEST_FILES ["file1.txt" ]
0 commit comments