From 714de8fc81cdb1fd12fc3ceba5bc9a61b5f4a291 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Mon, 20 Jan 2025 15:43:23 +0100 Subject: [PATCH] fianlized tests and fixed errors if needed. Including improved error handling --- pytest.ini | 6 ++ setup.cfg | 11 +++ setup.py | 2 +- tests/__init__.py | 0 tests/conftest.py | 33 +++++++++ tests/test_tree_structurer.py | 89 +++++++++++++++++++++++++ tree_structurer/__main__.py | 14 +++- tree_structurer/cpp/tree_structurer.cpp | 61 +++++++++-------- 8 files changed, 185 insertions(+), 31 deletions(-) create mode 100644 pytest.ini create mode 100644 setup.cfg create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/test_tree_structurer.py diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..b235d1a --- /dev/null +++ b/pytest.ini @@ -0,0 +1,6 @@ +[pytest] +python_files = test_*.py +python_classes = Test* +python_functions = test_* +addopts = -v --tb=short +testpaths = tests \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..39474e9 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,11 @@ +[tool:pytest] +minversion = 6.0 +addopts = -ra -q +testpaths = + tests +python_files = + test_*.py + *_test.py +markers = + slow: marks tests as slow (deselect with '-m "not slow"') + integration: marks tests as integration tests \ No newline at end of file diff --git a/setup.py b/setup.py index 576a701..44c083e 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ tree_structurer_module = Extension( setup( name='tree_structurer', - version='0.0.5', + version='0.0.6', description='A module for analyzing directory structures', ext_modules=[tree_structurer_module], packages=['tree_structurer'], diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..a99412c --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,33 @@ +import os +import pytest +import shutil +from pathlib import Path + +@pytest.fixture +def temp_directory(tmp_path): + """Create a temporary directory structure for testing.""" + # Create main test directory + test_dir = tmp_path / "test_project" + test_dir.mkdir() + + # Create some regular directories + (test_dir / "src").mkdir() + (test_dir / "src" / "utils").mkdir() + (test_dir / "docs").mkdir() + + # Create some files + (test_dir / "src" / "main.py").write_text("print('hello')") + (test_dir / "src" / "utils" / "helper.py").write_text("# helper") + (test_dir / "src" / "__init__.py").touch() + (test_dir / "README.md").write_text("# Test Project") + + # Create some directories that should be ignored + (test_dir / "__pycache__").mkdir() + (test_dir / ".git").mkdir() + (test_dir / "venv").mkdir() + + # Create some files that should be ignored + (test_dir / "src" / "main.pyc").touch() + (test_dir / ".gitignore").touch() + + yield test_dir \ No newline at end of file diff --git a/tests/test_tree_structurer.py b/tests/test_tree_structurer.py new file mode 100644 index 0000000..aade0d0 --- /dev/null +++ b/tests/test_tree_structurer.py @@ -0,0 +1,89 @@ +import pytest +from pathlib import Path +from tree_structurer import create_tree_structurer, get_structure + +def test_basic_structure(temp_directory): + """Test that the basic directory structure is correctly represented.""" + create_tree_structurer() + structure = get_structure(str(temp_directory)) + + # Convert structure to set for easier comparison + structure_set = set(structure) + + # Print actual structure for debugging + print("\nActual structure:") + for line in structure: + print(f"'{line}'") + + # Expected entries (adjusted based on actual implementation) + must_contain = [ + "README.md", + "docs", + "src", + "__init__.py", + "main.py", + "utils", + "helper.py" + ] + + # Check that all required components are present somewhere in the structure + for entry in must_contain: + assert any(entry in line for line in structure), \ + f"Required entry '{entry}' not found in structure" + + # Check that ignored directories/files are not present + ignored_patterns = { + "__pycache__", + ".git", + "venv", + "main.pyc", + ".gitignore" + } + + for entry in structure: + for ignored in ignored_patterns: + assert ignored not in entry, \ + f"Ignored pattern '{ignored}' found in entry '{entry}'" + +def test_empty_directory(tmp_path): + """Test handling of an empty directory.""" + create_tree_structurer() + try: + structure = get_structure(str(tmp_path)) + pytest.fail("Expected an exception for nonexistent directory") + except Exception as e: # Changed from RuntimeError + assert "Directory is empty" in str(e) + +def test_nonexistent_directory(): + """Test handling of a nonexistent directory.""" + create_tree_structurer() + try: + get_structure("/path/that/does/not/exist") + pytest.fail("Expected an exception for nonexistent directory") + except Exception as e: # Changed from RuntimeError + assert "Directory does not exist" in str(e) + +def test_nested_structure(temp_directory): + """Test deeply nested directory structure.""" + # Create deep nested structure + deep_path = temp_directory / "deep" / "nested" / "structure" + deep_path.mkdir(parents=True) + (deep_path / "test.py").touch() + + create_tree_structurer() + structure = get_structure(str(temp_directory)) + + # Print actual structure for debugging + print("\nDeep structure:") + for line in structure: + print(f"'{line}'") + + # Verify that all components of the deep path are present + deep_components = ["deep", "nested", "structure", "test.py"] + for component in deep_components: + assert any(component in line for line in structure), \ + f"Deep component '{component}' not found in structure" + + # Verify tree-like formatting is present + assert any("└" in line or "├" in line for line in structure), \ + "Tree-like formatting characters not found in structure" diff --git a/tree_structurer/__main__.py b/tree_structurer/__main__.py index cb5c6d4..35fdff9 100644 --- a/tree_structurer/__main__.py +++ b/tree_structurer/__main__.py @@ -14,15 +14,23 @@ def main(): try: if args.verbose: print(f"Analyzing directory: {args.path}") + create_tree_structurer() - # Get and print structure directly structure = get_structure(args.path) + for line in structure: print(line) except Exception as e: - print(f"Error: {e}", file=sys.stderr) + error_msg = str(e) + if "Directory does not exist" in error_msg: + print(f"Error: The specified directory does not exist: {args.path}", file=sys.stderr) + elif "Directory is empty" in error_msg: + print(f"Error: The specified directory is empty: {args.path}", file=sys.stderr) + elif "Path is not a directory" in error_msg: + print(f"Error: The specified path is not a directory: {args.path}", file=sys.stderr) + else: + print(f"Error: {error_msg}", file=sys.stderr) sys.exit(1) - if __name__ == "__main__": main() \ No newline at end of file diff --git a/tree_structurer/cpp/tree_structurer.cpp b/tree_structurer/cpp/tree_structurer.cpp index 5e35bed..2ef3be2 100644 --- a/tree_structurer/cpp/tree_structurer.cpp +++ b/tree_structurer/cpp/tree_structurer.cpp @@ -66,29 +66,41 @@ std::vector TreeStructurer::get_filtered_paths(const fs::path& start) fs::directory_options options = fs::directory_options::skip_permission_denied; try { - if (fs::exists(start) && fs::is_directory(start)) { - paths.push_back(start); + if (!fs::exists(start)) { + throw std::runtime_error("Directory does not exist: " + start.string()); + } + + if (!fs::is_directory(start)) { + throw std::runtime_error("Path is not a directory: " + start.string()); + } - for (const auto& entry : fs::recursive_directory_iterator(start, options)) { - const auto& path = entry.path(); + paths.push_back(start); - bool should_skip = false; - for (const auto& component : path) { - if (should_ignore_dir(component.string())) { - should_skip = true; - break; - } + // Check if directory is empty + bool is_empty = fs::directory_iterator(start) == fs::directory_iterator(); + if (is_empty) { + throw std::runtime_error("Directory is empty: " + start.string()); + } + + for (const auto& entry : fs::recursive_directory_iterator(start, options)) { + const auto& path = entry.path(); + + bool should_skip = false; + for (const auto& component : path) { + if (should_ignore_dir(component.string())) { + should_skip = true; + break; } - if (should_skip) continue; + } + if (should_skip) continue; - if (entry.is_directory()) { - if (!should_ignore_dir(path.filename().string())) { - paths.push_back(path); - } - } else { - if (!should_ignore_file(path.filename().string())) { - paths.push_back(path); - } + if (entry.is_directory()) { + if (!should_ignore_dir(path.filename().string())) { + paths.push_back(path); + } + } else { + if (!should_ignore_file(path.filename().string())) { + paths.push_back(path); } } } @@ -113,14 +125,9 @@ std::vector TreeStructurer::get_directory_structure(const std::stri try { auto paths = get_filtered_paths(start); - if (paths.empty()) return result; - - // Don't add the start directory name to the output - // Remove the following lines: - // if (start != fs::current_path()) { - // result.push_back(start.filename().string()); - // } - + if (paths.empty()) { + throw std::runtime_error("No valid files or directories found in: " + start.string()); + } std::vector is_last_at_level(256, false); for (size_t i = 1; i < paths.size(); ++i) {