mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
* quick resolve test path too * Fix migrate resolve * Remove test/ from deps data * resolve test to lib
395 lines
13 KiB
Python
395 lines
13 KiB
Python
"""Tests for deps.py - dependency resolution."""
|
|
|
|
import pathlib
|
|
import tempfile
|
|
import unittest
|
|
|
|
from update_lib.deps import (
|
|
get_lib_paths,
|
|
get_soft_deps,
|
|
get_test_dependencies,
|
|
get_test_paths,
|
|
parse_lib_imports,
|
|
parse_test_imports,
|
|
)
|
|
|
|
|
|
class TestParseTestImports(unittest.TestCase):
|
|
"""Tests for parse_test_imports function."""
|
|
|
|
def test_from_test_import(self):
|
|
"""Test parsing 'from test import foo'."""
|
|
code = """
|
|
from test import string_tests
|
|
from test import lock_tests, other_tests
|
|
"""
|
|
imports = parse_test_imports(code)
|
|
self.assertEqual(imports, {"string_tests", "lock_tests", "other_tests"})
|
|
|
|
def test_from_test_dot_module(self):
|
|
"""Test parsing 'from test.foo import bar'."""
|
|
code = """
|
|
from test.string_tests import CommonTest
|
|
from test.support import verbose
|
|
"""
|
|
imports = parse_test_imports(code)
|
|
self.assertEqual(imports, {"string_tests"}) # support is excluded
|
|
|
|
def test_excludes_support(self):
|
|
"""Test that 'support' is excluded."""
|
|
code = """
|
|
from test import support
|
|
from test.support import verbose
|
|
"""
|
|
imports = parse_test_imports(code)
|
|
self.assertEqual(imports, set())
|
|
|
|
def test_regular_imports_ignored(self):
|
|
"""Test that regular imports are ignored."""
|
|
code = """
|
|
import os
|
|
from collections import defaultdict
|
|
from . import helper
|
|
"""
|
|
imports = parse_test_imports(code)
|
|
self.assertEqual(imports, set())
|
|
|
|
def test_syntax_error_returns_empty(self):
|
|
"""Test that syntax errors return empty set."""
|
|
code = "this is not valid python {"
|
|
imports = parse_test_imports(code)
|
|
self.assertEqual(imports, set())
|
|
|
|
|
|
class TestGetLibPaths(unittest.TestCase):
|
|
"""Tests for get_lib_paths function."""
|
|
|
|
def test_auto_detect_py_module(self):
|
|
"""Test auto-detection of _py{module}.py pattern."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
tmpdir = pathlib.Path(tmpdir)
|
|
lib_dir = tmpdir / "Lib"
|
|
lib_dir.mkdir()
|
|
(lib_dir / "mymodule.py").write_text("# mymodule")
|
|
(lib_dir / "_pymymodule.py").write_text("# _pymymodule")
|
|
|
|
paths = get_lib_paths("mymodule", str(tmpdir))
|
|
self.assertEqual(len(paths), 2)
|
|
self.assertIn(tmpdir / "Lib" / "mymodule.py", paths)
|
|
self.assertIn(tmpdir / "Lib" / "_pymymodule.py", paths)
|
|
|
|
def test_default_file(self):
|
|
"""Test default to .py file."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
tmpdir = pathlib.Path(tmpdir)
|
|
lib_dir = tmpdir / "Lib"
|
|
lib_dir.mkdir()
|
|
(lib_dir / "foo.py").write_text("# foo")
|
|
|
|
paths = get_lib_paths("foo", str(tmpdir))
|
|
self.assertEqual(paths, (tmpdir / "Lib" / "foo.py",))
|
|
|
|
def test_default_directory(self):
|
|
"""Test default to directory when file doesn't exist."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
tmpdir = pathlib.Path(tmpdir)
|
|
lib_dir = tmpdir / "Lib"
|
|
lib_dir.mkdir()
|
|
(lib_dir / "foo").mkdir()
|
|
|
|
paths = get_lib_paths("foo", str(tmpdir))
|
|
self.assertEqual(paths, (tmpdir / "Lib" / "foo",))
|
|
|
|
|
|
class TestGetTestPaths(unittest.TestCase):
|
|
"""Tests for get_test_paths function."""
|
|
|
|
def test_known_dependency(self):
|
|
"""Test test with known path override."""
|
|
paths = get_test_paths("regrtest", "cpython")
|
|
self.assertEqual(len(paths), 1)
|
|
self.assertEqual(paths[0], pathlib.Path("cpython/Lib/test/test_regrtest"))
|
|
|
|
def test_default_directory(self):
|
|
"""Test default to test_name/ directory."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
tmpdir = pathlib.Path(tmpdir)
|
|
test_dir = tmpdir / "Lib" / "test"
|
|
test_dir.mkdir(parents=True)
|
|
(test_dir / "test_foo").mkdir()
|
|
|
|
paths = get_test_paths("foo", str(tmpdir))
|
|
self.assertEqual(paths, (tmpdir / "Lib" / "test" / "test_foo",))
|
|
|
|
def test_default_file(self):
|
|
"""Test fallback to test_name.py file."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
tmpdir = pathlib.Path(tmpdir)
|
|
test_dir = tmpdir / "Lib" / "test"
|
|
test_dir.mkdir(parents=True)
|
|
(test_dir / "test_foo.py").write_text("# test")
|
|
|
|
paths = get_test_paths("foo", str(tmpdir))
|
|
self.assertEqual(paths, (tmpdir / "Lib" / "test" / "test_foo.py",))
|
|
|
|
|
|
class TestGetTestDependencies(unittest.TestCase):
|
|
"""Tests for get_test_dependencies function."""
|
|
|
|
def test_parse_file_imports(self):
|
|
"""Test parsing imports from test file."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
tmpdir = pathlib.Path(tmpdir)
|
|
test_dir = tmpdir / "test"
|
|
test_dir.mkdir()
|
|
|
|
# Create test file with import
|
|
test_file = test_dir / "test_foo.py"
|
|
test_file.write_text("""
|
|
from test import string_tests
|
|
|
|
class TestFoo:
|
|
pass
|
|
""")
|
|
# Create the dependency file
|
|
(test_dir / "string_tests.py").write_text("# string tests")
|
|
|
|
result = get_test_dependencies(test_file)
|
|
self.assertEqual(len(result["hard_deps"]), 1)
|
|
self.assertEqual(result["hard_deps"][0], test_dir / "string_tests.py")
|
|
self.assertEqual(result["data"], [])
|
|
|
|
def test_nonexistent_path(self):
|
|
"""Test nonexistent path returns empty."""
|
|
result = get_test_dependencies(pathlib.Path("/nonexistent/path"))
|
|
self.assertEqual(result, {"hard_deps": [], "data": []})
|
|
|
|
def test_transitive_data_dependency(self):
|
|
"""Test that data deps are resolved transitively.
|
|
|
|
Chain: test_codecencodings_kr -> multibytecodec_support -> cjkencodings
|
|
"""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
tmpdir = pathlib.Path(tmpdir)
|
|
test_dir = tmpdir / "test"
|
|
test_dir.mkdir()
|
|
|
|
# Create test_codecencodings_kr.py that imports multibytecodec_support
|
|
test_file = test_dir / "test_codecencodings_kr.py"
|
|
test_file.write_text("""
|
|
from test import multibytecodec_support
|
|
|
|
class TestKR:
|
|
pass
|
|
""")
|
|
# Create multibytecodec_support.py (the intermediate dependency)
|
|
(test_dir / "multibytecodec_support.py").write_text("# support module")
|
|
|
|
# Create cjkencodings directory (the data dependency of multibytecodec_support)
|
|
(test_dir / "cjkencodings").mkdir()
|
|
|
|
result = get_test_dependencies(test_file)
|
|
|
|
# Should find multibytecodec_support.py as a hard_dep
|
|
self.assertEqual(len(result["hard_deps"]), 1)
|
|
self.assertEqual(
|
|
result["hard_deps"][0], test_dir / "multibytecodec_support.py"
|
|
)
|
|
|
|
# Should find cjkencodings as data (from multibytecodec_support's TEST_DEPENDENCIES)
|
|
self.assertEqual(len(result["data"]), 1)
|
|
self.assertEqual(result["data"][0], test_dir / "cjkencodings")
|
|
|
|
|
|
class TestParseLibImports(unittest.TestCase):
|
|
"""Tests for parse_lib_imports function."""
|
|
|
|
def test_import_statement(self):
|
|
"""Test parsing 'import foo'."""
|
|
code = """
|
|
import os
|
|
import sys
|
|
import collections.abc
|
|
"""
|
|
imports = parse_lib_imports(code)
|
|
self.assertEqual(imports, {"os", "sys", "collections.abc"})
|
|
|
|
def test_from_import(self):
|
|
"""Test parsing 'from foo import bar'."""
|
|
code = """
|
|
from os import path
|
|
from collections.abc import Mapping
|
|
from typing import Optional
|
|
"""
|
|
imports = parse_lib_imports(code)
|
|
self.assertEqual(imports, {"os", "collections.abc", "typing"})
|
|
|
|
def test_mixed_imports(self):
|
|
"""Test mixed import styles."""
|
|
code = """
|
|
import sys
|
|
from os import path
|
|
from collections import defaultdict
|
|
import functools
|
|
"""
|
|
imports = parse_lib_imports(code)
|
|
self.assertEqual(imports, {"sys", "os", "collections", "functools"})
|
|
|
|
def test_syntax_error_returns_empty(self):
|
|
"""Test that syntax errors return empty set."""
|
|
code = "this is not valid python {"
|
|
imports = parse_lib_imports(code)
|
|
self.assertEqual(imports, set())
|
|
|
|
def test_relative_import_skipped(self):
|
|
"""Test that relative imports (no module) are skipped."""
|
|
code = """
|
|
from . import foo
|
|
from .. import bar
|
|
"""
|
|
imports = parse_lib_imports(code)
|
|
self.assertEqual(imports, set())
|
|
|
|
|
|
class TestGetSoftDeps(unittest.TestCase):
|
|
"""Tests for get_soft_deps function."""
|
|
|
|
def test_with_temp_files(self):
|
|
"""Test soft deps detection with temp files."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
tmpdir = pathlib.Path(tmpdir)
|
|
lib_dir = tmpdir / "Lib"
|
|
lib_dir.mkdir()
|
|
|
|
# Create a module that imports another module
|
|
(lib_dir / "foo.py").write_text("""
|
|
import bar
|
|
from baz import something
|
|
""")
|
|
# Create the imported modules
|
|
(lib_dir / "bar.py").write_text("# bar module")
|
|
(lib_dir / "baz.py").write_text("# baz module")
|
|
|
|
soft_deps = get_soft_deps("foo", str(tmpdir))
|
|
self.assertEqual(soft_deps, {"bar", "baz"})
|
|
|
|
def test_skips_self(self):
|
|
"""Test that module doesn't include itself in soft_deps."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
tmpdir = pathlib.Path(tmpdir)
|
|
lib_dir = tmpdir / "Lib"
|
|
lib_dir.mkdir()
|
|
|
|
# Create a module that imports itself (circular)
|
|
(lib_dir / "foo.py").write_text("""
|
|
import foo
|
|
import bar
|
|
""")
|
|
(lib_dir / "bar.py").write_text("# bar module")
|
|
|
|
soft_deps = get_soft_deps("foo", str(tmpdir))
|
|
self.assertNotIn("foo", soft_deps)
|
|
self.assertIn("bar", soft_deps)
|
|
|
|
def test_filters_nonexistent(self):
|
|
"""Test that nonexistent modules are filtered out."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
tmpdir = pathlib.Path(tmpdir)
|
|
lib_dir = tmpdir / "Lib"
|
|
lib_dir.mkdir()
|
|
|
|
# Create a module that imports nonexistent module
|
|
(lib_dir / "foo.py").write_text("""
|
|
import bar
|
|
import nonexistent
|
|
""")
|
|
(lib_dir / "bar.py").write_text("# bar module")
|
|
# nonexistent.py is NOT created
|
|
|
|
soft_deps = get_soft_deps("foo", str(tmpdir))
|
|
self.assertEqual(soft_deps, {"bar"})
|
|
|
|
|
|
class TestDircmpIsSame(unittest.TestCase):
|
|
"""Tests for _dircmp_is_same function."""
|
|
|
|
def test_identical_directories(self):
|
|
"""Test that identical directories return True."""
|
|
import filecmp
|
|
|
|
from update_lib.deps import _dircmp_is_same
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
tmpdir = pathlib.Path(tmpdir)
|
|
dir1 = tmpdir / "dir1"
|
|
dir2 = tmpdir / "dir2"
|
|
dir1.mkdir()
|
|
dir2.mkdir()
|
|
|
|
(dir1 / "file.py").write_text("content")
|
|
(dir2 / "file.py").write_text("content")
|
|
|
|
dcmp = filecmp.dircmp(dir1, dir2)
|
|
self.assertTrue(_dircmp_is_same(dcmp))
|
|
|
|
def test_different_files(self):
|
|
"""Test that directories with different files return False."""
|
|
import filecmp
|
|
|
|
from update_lib.deps import _dircmp_is_same
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
tmpdir = pathlib.Path(tmpdir)
|
|
dir1 = tmpdir / "dir1"
|
|
dir2 = tmpdir / "dir2"
|
|
dir1.mkdir()
|
|
dir2.mkdir()
|
|
|
|
(dir1 / "file.py").write_text("content1")
|
|
(dir2 / "file.py").write_text("content2")
|
|
|
|
dcmp = filecmp.dircmp(dir1, dir2)
|
|
self.assertFalse(_dircmp_is_same(dcmp))
|
|
|
|
def test_nested_identical(self):
|
|
"""Test that nested identical directories return True."""
|
|
import filecmp
|
|
|
|
from update_lib.deps import _dircmp_is_same
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
tmpdir = pathlib.Path(tmpdir)
|
|
dir1 = tmpdir / "dir1"
|
|
dir2 = tmpdir / "dir2"
|
|
(dir1 / "sub").mkdir(parents=True)
|
|
(dir2 / "sub").mkdir(parents=True)
|
|
|
|
(dir1 / "sub" / "file.py").write_text("content")
|
|
(dir2 / "sub" / "file.py").write_text("content")
|
|
|
|
dcmp = filecmp.dircmp(dir1, dir2)
|
|
self.assertTrue(_dircmp_is_same(dcmp))
|
|
|
|
def test_nested_different(self):
|
|
"""Test that nested directories with differences return False."""
|
|
import filecmp
|
|
|
|
from update_lib.deps import _dircmp_is_same
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
tmpdir = pathlib.Path(tmpdir)
|
|
dir1 = tmpdir / "dir1"
|
|
dir2 = tmpdir / "dir2"
|
|
(dir1 / "sub").mkdir(parents=True)
|
|
(dir2 / "sub").mkdir(parents=True)
|
|
|
|
(dir1 / "sub" / "file.py").write_text("content1")
|
|
(dir2 / "sub" / "file.py").write_text("content2")
|
|
|
|
dcmp = filecmp.dircmp(dir1, dir2)
|
|
self.assertFalse(_dircmp_is_same(dcmp))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|