Files
StableLlama-ComfyUI-basic_d…/tests/test_path_nodes.py
T
StableLlama 9ae4b57c00 Add file I/O support for strings, images, and masks
Extended functionality to load and save strings, RGB/RGBA images, and masks, with various format options and error handling. Updated tests to cover new capabilities, ensuring robust behavior across different use cases. Updated documentation and version to reflect these changes.
2025-07-05 00:03:03 +02:00

646 lines
25 KiB
Python

import os
import pytest
import platform
import numpy as np
import torch
from PIL import Image
from src.basic_data_handling.path_nodes import (
PathJoin, PathAbspath, PathExists, PathIsFile, PathIsDir, PathGetSize,
PathSplit, PathSplitExt, PathBasename, PathDirname, PathGetExtension,
PathSetExtension, PathNormalize, PathRelative, PathGlob, PathExpandVars, PathGetCwd,
PathListDir, PathIsAbsolute, PathCommonPrefix, PathLoadStringFile, PathSaveStringFile,
PathLoadImageRGB, PathSaveImageRGB, PathLoadImageRGBA, PathSaveImageRGBA,
PathLoadMaskFromAlpha, PathLoadMaskFromGreyscale,
)
def test_path_join():
node = PathJoin()
assert node.join_paths("folder", "file.txt") == (os.path.join("folder", "file.txt"),)
assert node.join_paths("folder", "") == (os.path.join("folder"),)
assert node.join_paths(".", "") == (".",)
# Test join with empty first path
assert node.join_paths("", "file.txt") == ("file.txt",)
# Test with multiple path components
nested_path = os.path.join("folder", "subfolder", "file.txt")
assert node.join_paths("folder", os.path.join("subfolder", "file.txt")) == (nested_path,)
def test_path_load_save_string_file(tmp_path):
# Test saving a string to a file
save_node = PathSaveStringFile()
test_string = "This is a test string\nwith multiple lines"
file_path = str(tmp_path / "test_string.txt")
# Save the string
assert save_node.save_text(test_string, file_path) == (True,)
# Verify file exists
assert os.path.exists(file_path)
# Test loading the string back
load_node = PathLoadStringFile()
loaded_string = load_node.load_text(file_path)
assert loaded_string == (test_string,)
# Test creating directories when saving
nested_path = str(tmp_path / "nested" / "dir" / "test.txt")
assert save_node.save_text("Test with nested dirs", nested_path) == (True,)
assert os.path.exists(nested_path)
# Test different encodings
utf8_text = "UTF-8 text with special chars: 你好, ñ, é, ö"
utf8_path = str(tmp_path / "utf8_test.txt")
assert save_node.save_text(utf8_text, utf8_path, encoding="utf-8") == (True,)
assert load_node.load_text(utf8_path) == (utf8_text,)
# Test error handling
with pytest.raises(FileNotFoundError):
load_node.load_text(str(tmp_path / "nonexistent.txt"))
def test_path_abspath():
node = PathAbspath()
assert node.get_abspath(".") == (os.path.abspath("."),)
assert node.get_abspath("folder/file.txt") == (os.path.abspath("folder/file.txt"),)
# Test with empty string (should return current directory)
assert node.get_abspath("") == (os.path.abspath(os.getcwd()),)
# Test with relative path with special characters
assert node.get_abspath("./file.txt") == (os.path.abspath("./file.txt"),)
assert node.get_abspath("../file.txt") == (os.path.abspath("../file.txt"),)
def test_path_exists(tmp_path):
node = PathExists()
existing_file = tmp_path / "file.txt"
existing_file.write_text("content")
existing_dir = tmp_path / "directory"
existing_dir.mkdir()
assert node.check_exists(str(existing_file)) == (True,)
assert node.check_exists(str(existing_dir)) == (True,)
assert node.check_exists("nonexistent.file") == (False,)
# Test with special paths
assert node.check_exists(".") == (True,)
# Test with path containing non-existent parent directory
assert node.check_exists(str(tmp_path / "nonexistent" / "file.txt")) == (False,)
def test_path_is_file(tmp_path):
node = PathIsFile()
file = tmp_path / "file.txt"
file.write_text("content")
directory = tmp_path / "directory"
directory.mkdir()
assert node.check_is_file(str(file)) == (True,)
assert node.check_is_file(str(directory)) == (False,)
assert node.check_is_file("nonexistent.file") == (False,)
# Test with empty path
assert node.check_is_file("") == (False,)
# Test with special paths
assert node.check_is_file(".") == (False,)
# Test with symlink to file if platform supports
if hasattr(os, 'symlink'):
try:
symlink = tmp_path / "symlink.txt"
os.symlink(str(file), str(symlink))
assert node.check_is_file(str(symlink)) == (True,)
except (OSError, NotImplementedError):
# Symlinks might not be supported on all platforms
pass
def test_path_is_dir(tmp_path):
node = PathIsDir()
directory = tmp_path / "directory"
directory.mkdir()
file = tmp_path / "file.txt"
file.write_text("content")
assert node.check_is_dir(str(directory)) == (True,)
assert node.check_is_dir(str(file)) == (False,)
assert node.check_is_dir("nonexistent.dir") == (False,)
# Test with special paths
assert node.check_is_dir(".") == (True,)
assert node.check_is_dir("") == (False,)
# Test with nested directory
nested_dir = directory / "nested"
nested_dir.mkdir()
assert node.check_is_dir(str(nested_dir)) == (True,)
# Test with symlink to directory if platform supports
if hasattr(os, 'symlink'):
try:
symlink = tmp_path / "symlink_dir"
os.symlink(str(directory), str(symlink), target_is_directory=True)
assert node.check_is_dir(str(symlink)) == (True,)
except (OSError, NotImplementedError, TypeError):
# Symlinks might not be supported on all platforms
pass
def test_path_get_size(tmp_path):
node = PathGetSize()
file = tmp_path / "file.txt"
file.write_text("content")
empty_file = tmp_path / "empty.txt"
empty_file.write_text("")
large_file = tmp_path / "large.txt"
large_file.write_text("a" * 1024) # 1KB file
assert node.get_size(str(file)) == (len("content"),)
assert node.get_size(str(empty_file)) == (0,)
assert node.get_size(str(large_file)) == (1024,)
with pytest.raises(FileNotFoundError):
node.get_size("nonexistent.file")
directory = tmp_path / "directory"
directory.mkdir()
with pytest.raises(ValueError):
node.get_size(str(directory))
def test_path_split():
node = PathSplit()
path = os.path.join("folder", "file.txt")
assert node.split_path(path) == (os.path.dirname(path), os.path.basename(path))
assert node.split_path("file.txt") == ("", "file.txt")
# Test with empty path
assert node.split_path("") == ("", "")
# Test with just directory separator
assert node.split_path("/") == ("/", "")
# Test with nested paths
nested_path = os.path.join("folder", "subfolder", "file.txt")
assert node.split_path(nested_path) == (os.path.dirname(nested_path), "file.txt")
# Test with trailing separator
dir_with_sep = "folder" + os.sep
assert node.split_path(dir_with_sep) == ("folder", "")
def test_path_splitext():
node = PathSplitExt()
assert node.split_ext("file.txt") == ("file", ".txt")
assert node.split_ext("file") == ("file", "")
# Test with multiple extensions
assert node.split_ext("file.tar.gz") == ("file.tar", ".gz")
# Test with path containing directory
path = os.path.join("folder", "file.txt")
assert node.split_ext(path) == (os.path.join("folder", "file"), ".txt")
# Test with hidden file
assert node.split_ext(".gitignore") == (".gitignore", "")
# Test with empty string
assert node.split_ext("") == ("", "")
def test_path_basename():
node = PathBasename()
assert node.get_basename("folder/file.txt") == ("file.txt",)
assert node.get_basename("file.txt") == ("file.txt",)
# Test with trailing slash
assert node.get_basename("folder/") == ("",)
# Test with empty path
assert node.get_basename("") == ("",)
# Test with root directory
assert node.get_basename("/") == ("",)
# Test with multiple nested directories
assert node.get_basename("folder/subfolder/file.txt") == ("file.txt",)
def test_path_dirname():
node = PathDirname()
assert node.get_dirname("folder/file.txt") == ("folder",)
assert node.get_dirname("file.txt") == ("",)
# Test with trailing slash
assert node.get_dirname("folder/") == ("folder",)
# Test with empty path
assert node.get_dirname("") == ("",)
# Test with root directory
assert node.get_dirname("/") == ("/",)
# Test with nested directories
assert node.get_dirname("folder/subfolder/file.txt") == (os.path.join("folder", "subfolder"),)
def test_path_get_extension():
node = PathGetExtension()
assert node.get_extension("file.txt") == (".txt",)
assert node.get_extension("file") == ("",)
# Test with multiple dots
assert node.get_extension("file.tar.gz") == (".gz",)
# Test with hidden file
assert node.get_extension(".gitignore") == ("",)
# Test with path containing directory
assert node.get_extension("folder/file.txt") == (".txt",)
# Test with empty string
assert node.get_extension("") == ("",)
def test_path_set_extension():
node = PathSetExtension()
# Test basic extension replacement
assert node.set_extension("file.txt", ".jpg") == ("file.jpg",)
# Test adding extension to file without extension
assert node.set_extension("file", ".png") == ("file.png",)
# Test with extension without dot prefix
assert node.set_extension("document.docx", "pdf") == ("document.pdf",)
# Test with path containing directory
assert node.set_extension("folder/image.jpg", ".png") == ("folder/image.png",)
# Test with multiple dots in filename
assert node.set_extension("archive.tar.gz", ".zip") == ("archive.tar.zip",)
# Test with empty extension (removes extension)
assert node.set_extension("config.ini", "") == ("config",)
# Test with empty string as path
assert node.set_extension("", ".txt") == (".txt",)
def test_path_load_mask_nodes(tmp_path, monkeypatch):
# Create test images
img_size = (64, 64)
# Image with alpha channel
rgba_img = Image.new('RGBA', img_size, color=(255, 0, 0, 128)) # Semi-transparent red
rgba_path = str(tmp_path / "alpha_mask.png")
rgba_img.save(rgba_path)
# Grayscale image
gray_img = Image.new('L', img_size)
# Create a gradient from black to white
for y in range(img_size[1]):
for x in range(img_size[0]):
gray_img.putpixel((x, y), int(255 * (x / img_size[0])))
gray_path = str(tmp_path / "gray_mask.png")
gray_img.save(gray_path)
# Mock the helper functions
def mock_load_image_helper(path):
if not os.path.exists(path):
raise FileNotFoundError(f"Image file not found: {path}")
return Image.open(path)
def mock_extract_mask_from_alpha(img):
if 'A' in img.getbands():
alpha = np.array(img.getchannel('A')).astype(np.float32) / 255.0
mask_tensor = 1.0 - torch.from_numpy(alpha)
return mask_tensor.unsqueeze(0)
return torch.zeros((1, img.height, img.width), dtype=torch.float32)
def mock_extract_mask_from_greyscale(img):
if img.mode == 'L':
gray = np.array(img).astype(np.float32) / 255.0
else:
gray = np.array(img.getchannel('R')).astype(np.float32) / 255.0
mask_tensor = 1.0 - torch.from_numpy(gray)
return mask_tensor.unsqueeze(0)
monkeypatch.setattr("src.basic_data_handling.path_nodes.load_image_helper", mock_load_image_helper)
monkeypatch.setattr("src.basic_data_handling.path_nodes.extract_mask_from_alpha", mock_extract_mask_from_alpha)
monkeypatch.setattr("src.basic_data_handling.path_nodes.extract_mask_from_greyscale", mock_extract_mask_from_greyscale)
# Test loading mask from alpha channel
alpha_node = PathLoadMaskFromAlpha()
alpha_mask = alpha_node.load_mask_from_alpha(rgba_path)
# Verify the mask shape
assert isinstance(alpha_mask, tuple)
assert len(alpha_mask) == 1
assert isinstance(alpha_mask[0], torch.Tensor)
assert alpha_mask[0].shape == (1, img_size[1], img_size[0])
# Test loading mask from grayscale image
gray_node = PathLoadMaskFromGreyscale()
gray_mask = gray_node.load_mask_from_greyscale(gray_path)
# Verify the mask shape
assert isinstance(gray_mask, tuple)
assert len(gray_mask) == 1
assert isinstance(gray_mask[0], torch.Tensor)
assert gray_mask[0].shape == (1, img_size[1], img_size[0])
# Test with invert option
inverted_mask = gray_node.load_mask_from_greyscale(gray_path, invert=True)
assert torch.allclose(inverted_mask[0], 1.0 - gray_mask[0])
# Test error handling
with pytest.raises(FileNotFoundError):
alpha_node.load_mask_from_alpha(str(tmp_path / "nonexistent.png"))
def test_path_load_save_image_rgba(tmp_path, monkeypatch):
# Create a test image with transparency
img_size = (64, 64)
test_img = Image.new('RGBA', img_size, color=(255, 0, 0, 128)) # Semi-transparent red
img_path = str(tmp_path / "test_rgba.png")
test_img.save(img_path)
# Mock the load_image_helper and extraction functions
def mock_load_image_helper(path):
if not os.path.exists(path):
raise FileNotFoundError(f"Image file not found: {path}")
return Image.open(path)
def mock_extract_mask_from_alpha(img):
if 'A' in img.getbands():
alpha = np.array(img.getchannel('A')).astype(np.float32) / 255.0
mask_tensor = 1.0 - torch.from_numpy(alpha)
return mask_tensor.unsqueeze(0)
return torch.zeros((1, img.height, img.width), dtype=torch.float32)
monkeypatch.setattr("src.basic_data_handling.path_nodes.load_image_helper", mock_load_image_helper)
monkeypatch.setattr("src.basic_data_handling.path_nodes.extract_mask_from_alpha", mock_extract_mask_from_alpha)
# Test loading an image with alpha
load_node = PathLoadImageRGBA()
loaded_img, loaded_mask = load_node.load_image_rgba(img_path)
# Verify that the returned objects are tensors with the right shapes
assert isinstance(loaded_img, torch.Tensor)
assert isinstance(loaded_mask, torch.Tensor)
assert loaded_img.shape == (1, img_size[1], img_size[0], 3) # (batch, height, width, channels)
assert loaded_mask.shape == (1, img_size[1], img_size[0]) # (batch, height, width)
# Test saving an image with mask
save_node = PathSaveImageRGBA()
output_path = str(tmp_path / "output_rgba")
# Create a simple red test image tensor and a gradient mask
red_img = torch.zeros(1, img_size[1], img_size[0], 3)
red_img[0, :, :, 0] = 1.0 # Red channel set to 1
# Create a gradient mask (0.0 to 1.0 from left to right)
mask = torch.zeros(1, img_size[1], img_size[0])
for i in range(img_size[0]):
mask[0, :, i] = i / img_size[0]
# Save the image with mask
assert save_node.save_image_with_mask(red_img, mask, output_path) == (True,)
# Verify the image was saved with the correct extension
assert os.path.exists(output_path + ".png")
# Test with invert_mask option
assert save_node.save_image_with_mask(red_img, mask, str(tmp_path / "inverted"), invert_mask=True) == (True,)
assert os.path.exists(str(tmp_path / "inverted.png"))
# Test with JPEG format (should switch to PNG for transparency)
assert save_node.save_image_with_mask(red_img, mask, str(tmp_path / "jpeg_test"), format="jpg") == (True,)
# Should be saved as PNG despite the request for JPEG
assert os.path.exists(str(tmp_path / "jpeg_test.png"))
def test_path_load_save_image_rgb(tmp_path, monkeypatch):
# Create a test image
img_size = (64, 64)
test_img = Image.new('RGB', img_size, color='red')
img_path = str(tmp_path / "test_rgb.png")
test_img.save(img_path)
# Mock the load_image_helper function to avoid PIL import issues in testing
def mock_load_image_helper(path):
if not os.path.exists(path):
raise FileNotFoundError(f"Image file not found: {path}")
return Image.open(path)
monkeypatch.setattr("src.basic_data_handling.path_nodes.load_image_helper", mock_load_image_helper)
# Test loading an image
load_node = PathLoadImageRGB()
loaded_img = load_node.load_image_rgb(img_path)
# Verify that the returned object is a tensor with the right shape
assert isinstance(loaded_img, tuple)
assert len(loaded_img) == 1
assert isinstance(loaded_img[0], torch.Tensor)
assert loaded_img[0].shape == (1, img_size[1], img_size[0], 3) # (batch, height, width, channels)
# Test saving an image
save_node = PathSaveImageRGB()
output_path = str(tmp_path / "output_rgb")
# Create a simple red test image tensor
red_img = torch.zeros(1, img_size[1], img_size[0], 3)
red_img[0, :, :, 0] = 1.0 # Red channel set to 1
# Save the image
assert save_node.save_image(red_img, output_path) == (True,)
# Verify the image was saved with the correct extension
assert os.path.exists(output_path + ".png")
# Test different formats
assert save_node.save_image(red_img, str(tmp_path / "jpeg_test"), format="jpg") == (True,)
assert os.path.exists(str(tmp_path / "jpeg_test.jpg"))
# Test with directories that don't exist
nested_path = str(tmp_path / "nested" / "images" / "test_rgb")
assert save_node.save_image(red_img, nested_path) == (True,)
assert os.path.exists(nested_path + ".png")
# Test error handling for loading
with pytest.raises(FileNotFoundError):
load_node.load_image_rgb(str(tmp_path / "nonexistent.png"))
def test_path_normalize():
node = PathNormalize()
assert node.normalize_path("folder/../file.txt") == (os.path.normpath("folder/../file.txt"),)
assert node.normalize_path("folder//file.txt") == (os.path.normpath("folder//file.txt"),)
# Test with empty path
assert node.normalize_path("") == (".",)
# Test with current directory references
assert node.normalize_path("./folder/./file.txt") == (os.path.normpath("./folder/./file.txt"),)
# Test with parent directory references that cancel out
assert node.normalize_path("folder/../folder/file.txt") == (os.path.normpath("folder/../folder/file.txt"),)
# Test with mixed separators
mixed_sep_path = "folder" + os.sep + "subfolder" + ("/" if os.sep == "\\" else "\\") + "file.txt"
assert node.normalize_path(mixed_sep_path) == (os.path.normpath(mixed_sep_path),)
def test_path_relative(tmp_path):
node = PathRelative()
start = tmp_path / "start"
start.mkdir()
target = tmp_path / "start/target"
target.mkdir()
assert node.get_relative_path(str(target), str(start)) == (os.path.relpath(str(target), str(start)),)
assert node.get_relative_path(str(target)) == (os.path.relpath(str(target), os.getcwd()),)
# Test with same path
assert node.get_relative_path(str(start), str(start)) == (".",)
# Test with sibling paths
sibling = tmp_path / "sibling"
sibling.mkdir()
expected = os.path.relpath(str(sibling), str(start))
assert node.get_relative_path(str(sibling), str(start)) == (expected,)
# Test with parent directory
parent_rel = os.path.relpath(str(tmp_path), str(target))
assert node.get_relative_path(str(tmp_path), str(target)) == (parent_rel,)
def test_path_glob(tmp_path):
node = PathGlob()
(tmp_path / "file1.txt").write_text("content")
(tmp_path / "file2.txt").write_text("content")
(tmp_path / "file3.txt").write_text("content")
(tmp_path / "image1.jpg").write_text("image")
(tmp_path / "image2.png").write_text("image")
# Create subdirectory with files
subdir = tmp_path / "subdir"
subdir.mkdir()
(subdir / "subfile1.txt").write_text("sub content")
(subdir / "subfile2.txt").write_text("sub content")
# Test simple glob pattern
result = node.glob_paths(str(tmp_path / "*.txt"))
expected = sorted([str(tmp_path / f"file{i}.txt") for i in range(1, 4)])
assert sorted(result[0]) == expected
# Test with image files
img_result = node.glob_paths(str(tmp_path / "image*"))
img_expected = sorted([str(tmp_path / "image1.jpg"), str(tmp_path / "image2.png")])
assert sorted(img_result[0]) == img_expected
# Test with character class
char_result = node.glob_paths(str(tmp_path / "file[1-2].txt"))
char_expected = sorted([str(tmp_path / f"file{i}.txt") for i in range(1, 3)])
assert sorted(char_result[0]) == char_expected
# Test recursive globbing
recursive_result = node.glob_paths(str(tmp_path / "**/*.txt"), recursive=True)
recursive_expected = sorted([
str(tmp_path / f"file{i}.txt") for i in range(1, 4)
] + [
str(subdir / f"subfile{i}.txt") for i in range(1, 3)
])
assert sorted(recursive_result[0]) == recursive_expected
# Test with no matches
no_match_result = node.glob_paths(str(tmp_path / "nomatch*.txt"))
assert len(no_match_result[0]) == 0
def test_path_expand_vars(monkeypatch):
node = PathExpandVars()
monkeypatch.setenv("TEST_VAR", "test_value")
monkeypatch.setenv("PATH_VAR", "path/to/somewhere")
monkeypatch.setenv("EMPTY_VAR", "")
assert node.expand_vars("$TEST_VAR/path") == ("test_value/path",)
assert node.expand_vars("${TEST_VAR}/path") == ("test_value/path",)
# Test with multiple variables
assert node.expand_vars("$TEST_VAR/$PATH_VAR") == ("test_value/path/to/somewhere",)
# Test with empty variable
assert node.expand_vars("$EMPTY_VAR/path") == ("/path",)
# Test with no variables
assert node.expand_vars("regular/path") == ("regular/path",)
# Test with empty string
assert node.expand_vars("") == ("",)
def test_path_get_cwd():
node = PathGetCwd()
assert node.get_cwd() == (os.getcwd(),)
# Verify that the result is an absolute path
assert os.path.isabs(node.get_cwd()[0])
# Verify that the directory exists
assert os.path.exists(node.get_cwd()[0])
assert os.path.isdir(node.get_cwd()[0])
def test_path_list_dir(tmp_path):
node = PathListDir()
# Set up a directory structure for testing
(tmp_path / "file1.txt").write_text("content")
(tmp_path / "file2.txt").write_text("content")
(tmp_path / "image.jpg").write_text("image")
(tmp_path / "dir1").mkdir()
(tmp_path / "dir2").mkdir()
(tmp_path / ".hidden_file").write_text("hidden")
(tmp_path / ".hidden_dir").mkdir()
# Test listing all entries
all_entries = node.list_directory(str(tmp_path))
assert "file1.txt" in all_entries[0]
assert "file2.txt" in all_entries[0]
assert "image.jpg" in all_entries[0]
assert "dir1" in all_entries[0]
assert "dir2" in all_entries[0]
assert ".hidden_file" in all_entries[0]
assert ".hidden_dir" in all_entries[0]
# Test listing only files
only_files = node.list_directory(str(tmp_path), files_only=True)
assert "file1.txt" in only_files[0]
assert "file2.txt" in only_files[0]
assert "image.jpg" in only_files[0]
assert ".hidden_file" in only_files[0]
assert "dir1" not in only_files[0]
assert "dir2" not in only_files[0]
assert ".hidden_dir" not in only_files[0]
# Test listing only directories
only_dirs = node.list_directory(str(tmp_path), dirs_only=True)
assert "file1.txt" not in only_dirs[0]
assert "file2.txt" not in only_dirs[0]
assert "image.jpg" not in only_dirs[0]
assert ".hidden_file" not in only_dirs[0]
assert "dir1" in only_dirs[0]
assert "dir2" in only_dirs[0]
assert ".hidden_dir" in only_dirs[0]
# Test with non-existent directory
with pytest.raises(FileNotFoundError):
node.list_directory(str(tmp_path / "nonexistent"))
# Test with file instead of directory
with pytest.raises(NotADirectoryError):
node.list_directory(str(tmp_path / "file1.txt"))
# Test with empty path (should use current directory)
assert len(node.list_directory("")[0]) > 0
def test_path_is_absolute():
node = PathIsAbsolute()
# Test with absolute paths
assert node.check_is_absolute("/") == (True,)
assert node.check_is_absolute(os.path.abspath(".")) == (True,)
# Test with relative paths
assert node.check_is_absolute("relative/path") == (False,)
assert node.check_is_absolute("./path") == (False,)
assert node.check_is_absolute("../path") == (False,)
# Test with empty path
assert node.check_is_absolute("") == (False,)
# Test Windows-specific paths if applicable
if platform.system() == "Windows":
assert node.check_is_absolute("C:\\Windows") == (True,)
assert node.check_is_absolute("C:/Windows") == (True,)
assert node.check_is_absolute("\\\\server\\share") == (True,)
assert node.check_is_absolute("C:relative\\path") == (False,)
def test_path_common_prefix():
node = PathCommonPrefix()
# Test with paths having common prefix
path1 = "/path/to/file1.txt"
path2 = "/path/to/file2.txt"
assert node.get_common_prefix(path1, path2) == ("/path/to/file",)
# Test with paths without common prefix
path3 = "/path/to/file.txt"
path4 = "/different/path/file.txt"
assert node.get_common_prefix(path3, path4) == ("/",)
# Test with exact same paths
assert node.get_common_prefix(path1, path1) == (path1,)
# Test with one path being a prefix of the other
path5 = "/path/to"
path6 = "/path/to/subdir/file.txt"
assert node.get_common_prefix(path5, path6) == ("/path/to",)
# Test with both empty paths
assert node.get_common_prefix("", "") == ("",)