Files
RustPython/scripts/update_lib/tests/test_deps.py
Jeong, YunWon 9071147db5 [update_lib] Apply test grouping to deps (#6867)
* quick resolve test path too

* Fix migrate resolve

* Remove test/ from deps data

* resolve test to lib
2026-01-24 18:40:57 +02:00

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()