-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
Copy pathtest_backtracking.py
206 lines (151 loc) · 7.23 KB
/
test_backtracking.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
from __future__ import annotations
from typing import TYPE_CHECKING
from poetry.factory import Factory
from tests.mixology.helpers import add_to_repo
from tests.mixology.helpers import check_solver_result
if TYPE_CHECKING:
from poetry.core.packages.project_package import ProjectPackage
from poetry.repositories import Repository
from tests.mixology.version_solver.conftest import Provider
def test_circular_dependency_on_older_version(
root: ProjectPackage, provider: Provider, repo: Repository
) -> None:
root.add_dependency(Factory.create_dependency("a", ">=1.0.0"))
add_to_repo(repo, "a", "1.0.0")
add_to_repo(repo, "a", "2.0.0", deps={"b": "1.0.0"})
add_to_repo(repo, "b", "1.0.0", deps={"a": "1.0.0"})
check_solver_result(root, provider, {"a": "1.0.0"}, tries=2)
def test_diamond_dependency_graph(
root: ProjectPackage, provider: Provider, repo: Repository
) -> None:
root.add_dependency(Factory.create_dependency("a", "*"))
root.add_dependency(Factory.create_dependency("b", "*"))
add_to_repo(repo, "a", "2.0.0", deps={"c": "^1.0.0"})
add_to_repo(repo, "a", "1.0.0")
add_to_repo(repo, "b", "2.0.0", deps={"c": "^3.0.0"})
add_to_repo(repo, "b", "1.0.0", deps={"c": "^2.0.0"})
add_to_repo(repo, "c", "3.0.0")
add_to_repo(repo, "c", "2.0.0")
add_to_repo(repo, "c", "1.0.0")
check_solver_result(root, provider, {"a": "1.0.0", "b": "2.0.0", "c": "3.0.0"})
def test_backjumps_after_partial_satisfier(
root: ProjectPackage, provider: Provider, repo: Repository
) -> None:
# c 2.0.0 is incompatible with y 2.0.0 because it requires x 1.0.0, but that
# requirement only exists because of both a and b. The solver should be able
# to deduce c 2.0.0's incompatibility and select c 1.0.0 instead.
root.add_dependency(Factory.create_dependency("c", "*"))
root.add_dependency(Factory.create_dependency("y", "^2.0.0"))
add_to_repo(repo, "a", "1.0.0", deps={"x": ">=1.0.0"})
add_to_repo(repo, "b", "1.0.0", deps={"x": "<2.0.0"})
add_to_repo(repo, "c", "1.0.0")
add_to_repo(repo, "c", "2.0.0", deps={"a": "*", "b": "*"})
add_to_repo(repo, "x", "0.0.0")
add_to_repo(repo, "x", "1.0.0", deps={"y": "1.0.0"})
add_to_repo(repo, "x", "2.0.0")
add_to_repo(repo, "y", "1.0.0")
add_to_repo(repo, "y", "2.0.0")
check_solver_result(root, provider, {"c": "1.0.0", "y": "2.0.0"}, tries=4)
def test_rolls_back_leaf_versions_first(
root: ProjectPackage, provider: Provider, repo: Repository
) -> None:
# The latest versions of a and b disagree on c. An older version of either
# will resolve the problem. This test validates that b, which is farther
# in the dependency graph from myapp is downgraded first.
root.add_dependency(Factory.create_dependency("a", "*"))
add_to_repo(repo, "a", "1.0.0", deps={"b": "*"})
add_to_repo(repo, "a", "2.0.0", deps={"b": "*", "c": "2.0.0"})
add_to_repo(repo, "b", "1.0.0")
add_to_repo(repo, "b", "2.0.0", deps={"c": "1.0.0"})
add_to_repo(repo, "c", "1.0.0")
add_to_repo(repo, "c", "2.0.0")
check_solver_result(root, provider, {"a": "2.0.0", "b": "1.0.0", "c": "2.0.0"})
def test_simple_transitive(
root: ProjectPackage, provider: Provider, repo: Repository
) -> None:
# Only one version of baz, so foo and bar will have to downgrade
# until they reach it
root.add_dependency(Factory.create_dependency("foo", "*"))
add_to_repo(repo, "foo", "1.0.0", deps={"bar": "1.0.0"})
add_to_repo(repo, "foo", "2.0.0", deps={"bar": "2.0.0"})
add_to_repo(repo, "foo", "3.0.0", deps={"bar": "3.0.0"})
add_to_repo(repo, "bar", "1.0.0", deps={"baz": "*"})
add_to_repo(repo, "bar", "2.0.0", deps={"baz": "2.0.0"})
add_to_repo(repo, "bar", "3.0.0", deps={"baz": "3.0.0"})
add_to_repo(repo, "baz", "1.0.0")
check_solver_result(
root, provider, {"foo": "1.0.0", "bar": "1.0.0", "baz": "1.0.0"}, tries=3
)
def test_backjump_to_nearer_unsatisfied_package(
root: ProjectPackage, provider: Provider, repo: Repository
) -> None:
# This ensures it doesn't exhaustively search all versions of b when it's
# a-2.0.0 whose dependency on c-2.0.0-nonexistent led to the problem. We
# make sure b has more versions than a so that the solver tries a first
# since it sorts sibling dependencies by number of versions.
root.add_dependency(Factory.create_dependency("a", "*"))
root.add_dependency(Factory.create_dependency("b", "*"))
add_to_repo(repo, "a", "1.0.0", deps={"c": "1.0.0"})
add_to_repo(repo, "a", "2.0.0", deps={"c": "2.0.0-1"})
add_to_repo(repo, "b", "1.0.0")
add_to_repo(repo, "b", "2.0.0")
add_to_repo(repo, "b", "3.0.0")
add_to_repo(repo, "c", "1.0.0")
check_solver_result(
root, provider, {"a": "1.0.0", "b": "3.0.0", "c": "1.0.0"}, tries=2
)
def test_backjump_past_failed_package_on_disjoint_constraint(
root: ProjectPackage, provider: Provider, repo: Repository
) -> None:
root.add_dependency(Factory.create_dependency("a", "*"))
root.add_dependency(Factory.create_dependency("foo", ">2.0.0"))
add_to_repo(repo, "a", "1.0.0", deps={"foo": "*"}) # ok
add_to_repo(
repo, "a", "2.0.0", deps={"foo": "<1.0.0"}
) # disjoint with myapp's constraint on foo
add_to_repo(repo, "foo", "2.0.0")
add_to_repo(repo, "foo", "2.0.1")
add_to_repo(repo, "foo", "2.0.2")
add_to_repo(repo, "foo", "2.0.3")
add_to_repo(repo, "foo", "2.0.4")
check_solver_result(root, provider, {"a": "1.0.0", "foo": "2.0.4"})
def test_backtracking_performance_level_1(
root: ProjectPackage, provider: Provider, repo: Repository
) -> None:
"""
This test takes quite long if an unfavorable heuristics is chosen
to select the next package to resolve.
B depends on A, but does not support the latest version of A.
B has a lot more versions than A.
Test for boto3/botocore vs. urllib3 issue in its simple form.
"""
root.add_dependency(Factory.create_dependency("a", "*"))
root.add_dependency(Factory.create_dependency("b", "*"))
add_to_repo(repo, "a", "1")
add_to_repo(repo, "a", "2")
b_max = 500
for i in range(1, b_max + 1):
add_to_repo(repo, "b", str(i), deps={"a": "<=1"})
check_solver_result(root, provider, {"a": "1", "b": str(b_max)})
def test_backtracking_performance_level_2(
root: ProjectPackage, provider: Provider, repo: Repository
) -> None:
"""
Similar to test_backtracking_performance_level_1,
but with one more level of dependencies.
C depends on B depends on A, but B does not support the latest version of A.
The root dependency only requires A and C so there is no direct dependency between
these two.
B and C have a lot more versions than A.
Test for boto3/botocore vs. urllib3 issue in its more complex form.
"""
root.add_dependency(Factory.create_dependency("a", "*"))
root.add_dependency(Factory.create_dependency("c", "*"))
add_to_repo(repo, "a", "1")
add_to_repo(repo, "a", "2")
bc_max = 500
for i in range(1, bc_max + 1):
add_to_repo(repo, "b", str(i), deps={"a": "<=1"})
for i in range(1, bc_max + 1):
add_to_repo(repo, "c", str(i), deps={"b": f"<={i}"})
check_solver_result(root, provider, {"a": "1", "b": str(bc_max), "c": str(bc_max)})