Coverage for tests/test_diff.py: 100.00%
24 statements
« prev ^ index » next coverage.py v7.6.2, created at 2024-10-12 01:34 +0200
« prev ^ index » next coverage.py v7.6.2, created at 2024-10-12 01:34 +0200
1"""Tests for the `diff` module."""
3from __future__ import annotations
5import pytest
7from griffe import Breakage, BreakageKind, find_breaking_changes, temporary_visited_module, temporary_visited_package
10@pytest.mark.parametrize(
11 ("old_code", "new_code", "expected_breakages"),
12 [
13 (
14 "a = True",
15 "a = False",
16 [BreakageKind.ATTRIBUTE_CHANGED_VALUE],
17 ),
18 (
19 "class a(int, str): ...",
20 "class a(int): ...",
21 [BreakageKind.CLASS_REMOVED_BASE],
22 ),
23 (
24 "a = 0",
25 "class a: ...",
26 [BreakageKind.OBJECT_CHANGED_KIND],
27 ),
28 (
29 "a = True",
30 "",
31 [BreakageKind.OBJECT_REMOVED],
32 ),
33 (
34 "def a(): ...",
35 "def a(x): ...",
36 [BreakageKind.PARAMETER_ADDED_REQUIRED],
37 ),
38 (
39 "def a(x=0): ...",
40 "def a(x=1): ...",
41 [BreakageKind.PARAMETER_CHANGED_DEFAULT],
42 ),
43 (
44 # positional-only to keyword-only
45 "def a(x, /): ...",
46 "def a(*, x): ...",
47 [BreakageKind.PARAMETER_CHANGED_KIND],
48 ),
49 (
50 # keyword-only to positional-only
51 "def a(*, x): ...",
52 "def a(x, /): ...",
53 [BreakageKind.PARAMETER_CHANGED_KIND],
54 ),
55 (
56 # positional or keyword to positional-only
57 "def a(x): ...",
58 "def a(x, /): ...",
59 [BreakageKind.PARAMETER_CHANGED_KIND],
60 ),
61 (
62 # positional or keyword to keyword-only
63 "def a(x): ...",
64 "def a(*, x): ...",
65 [BreakageKind.PARAMETER_CHANGED_KIND],
66 ),
67 # to variadic positional
68 (
69 # positional-only to variadic positional
70 "def a(x, /): ...",
71 "def a(*x): ...",
72 [],
73 ),
74 (
75 # positional or keyword to variadic positional
76 "def a(x): ...",
77 "def a(*x): ...",
78 [BreakageKind.PARAMETER_CHANGED_KIND],
79 ),
80 (
81 # keyword-only to variadic positional
82 "def a(*, x): ...",
83 "def a(*x): ...",
84 [BreakageKind.PARAMETER_CHANGED_KIND],
85 ),
86 (
87 # variadic keyword to variadic positional
88 "def a(**x): ...",
89 "def a(*x): ...",
90 [BreakageKind.PARAMETER_CHANGED_KIND],
91 ),
92 (
93 # positional or keyword to variadic positional, with variadic keyword
94 "def a(x): ...",
95 "def a(*x, **y): ...",
96 [],
97 ),
98 (
99 # keyword-only to variadic positional, with variadic keyword
100 "def a(*, x): ...",
101 "def a(*x, **y): ...",
102 [],
103 ),
104 # to variadic keyword
105 (
106 # positional-only to variadic keyword
107 "def a(x, /): ...",
108 "def a(**x): ...",
109 [BreakageKind.PARAMETER_CHANGED_KIND],
110 ),
111 (
112 # positional or keyword to variadic keyword
113 "def a(x): ...",
114 "def a(**x): ...",
115 [BreakageKind.PARAMETER_CHANGED_KIND],
116 ),
117 (
118 # keyword-only to variadic keyword
119 "def a(*, x): ...",
120 "def a(**x): ...",
121 [],
122 ),
123 (
124 # variadic positional to variadic keyword
125 "def a(*x): ...",
126 "def a(**x): ...",
127 [BreakageKind.PARAMETER_CHANGED_KIND],
128 ),
129 (
130 # positional-only to variadic keyword, with variadic positional
131 "def a(x, /): ...",
132 "def a(*y, **x): ...",
133 [],
134 ),
135 (
136 # positional or keyword to variadic keyword, with variadic positional
137 "def a(x): ...",
138 "def a(*y, **x): ...",
139 [],
140 ),
141 (
142 "def a(x=1): ...",
143 "def a(x): ...",
144 [BreakageKind.PARAMETER_CHANGED_REQUIRED],
145 ),
146 (
147 "def a(x, y): ...",
148 "def a(y, x): ...",
149 [BreakageKind.PARAMETER_MOVED, BreakageKind.PARAMETER_MOVED],
150 ),
151 (
152 "def a(x, y): ...",
153 "def a(x): ...",
154 [BreakageKind.PARAMETER_REMOVED],
155 ),
156 (
157 "class a:\n\tb: int | None = None",
158 "class a:\n\tb: int",
159 [BreakageKind.ATTRIBUTE_CHANGED_VALUE],
160 ),
161 (
162 "def a() -> int: ...",
163 "def a() -> str: ...",
164 [], # not supported yet: BreakageKind.RETURN_CHANGED_TYPE
165 ),
166 ],
167)
168def test_diff_griffe(old_code: str, new_code: str, expected_breakages: list[Breakage]) -> None:
169 """Test the different incompatibility finders.
171 Parameters:
172 old_code: Parametrized code of the old module version.
173 new_code: Parametrized code of the new module version.
174 expected_breakages: A list of breakage kinds to expect.
175 """
176 # check without any alias
177 with temporary_visited_module(old_code) as old_package, temporary_visited_module(new_code) as new_package:
178 breaking = list(find_breaking_changes(old_package, new_package))
179 assert len(breaking) == len(expected_breakages)
180 for breakage, expected_kind in zip(breaking, expected_breakages):
181 assert breakage.kind is expected_kind
182 # check with aliases
183 import_a = "from ._mod_a import a\n__all__ = ['a']"
184 old_modules = {"__init__.py": import_a, "_mod_a.py": old_code}
185 new_modules = {"__init__.py": new_code and import_a, "_mod_a.py": new_code}
186 with temporary_visited_package("package_old", old_modules) as old_package: # noqa: SIM117
187 with temporary_visited_package("package_new", new_modules) as new_package:
188 breaking = list(find_breaking_changes(old_package, new_package))
189 assert len(breaking) == len(expected_breakages)
190 for breakage, expected_kind in zip(breaking, expected_breakages):
191 assert breakage.kind is expected_kind
194def test_moving_members_in_parent_classes() -> None:
195 """Test that moving an object from a base class to a parent class doesn't trigger a breakage."""
196 old_code = """
197 class Parent:
198 ...
200 class Base(Parent):
201 def method(self):
202 ...
203 """
204 new_code = """
205 class Parent:
206 def method(self):
207 ...
209 class Base(Parent):
210 ...
211 """
212 with temporary_visited_module(old_code) as old_package, temporary_visited_module(new_code) as new_package:
213 assert not list(find_breaking_changes(old_package, new_package))