Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | #!/usr/bin/perl |
2 | # | |
3 | # This program is free software; you can redistribute it and/or modify | |
4 | # it under the terms of the GNU General Public License as published by | |
5 | # the Free Software Foundation; either version 2 of the License, or | |
6 | # (at your option) any later version. | |
7 | # | |
8 | # This program is distributed in the hope that it will be useful, | |
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | # GNU General Public License for more details. | |
12 | # | |
13 | # You should have received a copy of the GNU General Public License | |
14 | # along with this program. If not, see <https://www.gnu.org/licenses/>. | |
15 | ||
16 | use strict; | |
17 | use warnings; | |
18 | ||
19 | use Test::More tests => 70; | |
20 | ||
21 | use Dpkg::Arch qw(get_host_arch); | |
22 | use Dpkg::Version; | |
23 | ||
24 | use_ok('Dpkg::Deps'); | |
25 | ||
26 | is(deps_concat(), '', 'Concatenate an empty list'); | |
27 | is(deps_concat(undef), '', 'Concatenate list with undef'); | |
28 | is(deps_concat(''), '', 'Concatenate an empty string'); | |
29 | is(deps_concat('', undef), '', 'Concatenate empty string with undef'); | |
30 | is(deps_concat('dep-a', undef, 'dep-b'), 'dep-a, dep-b', | |
31 | 'Concatenate two strings with intermixed undef'); | |
32 | ||
33 | sub test_dep_parse_option { | |
34 | my %options = @_; | |
35 | ||
36 | eval { | |
37 | my $dep_croak = deps_parse('pkg', %options); | |
38 | }; | |
39 | my $options = join ' ', map { "$_=$options{$_}" } keys %options; | |
40 | ok(defined $@, "Parse with bogus arch options $options"); | |
41 | } | |
42 | ||
43 | test_dep_parse_option(host_arch => 'all'); | |
44 | test_dep_parse_option(host_arch => 'any'); | |
45 | test_dep_parse_option(host_arch => 'linux-any'); | |
46 | test_dep_parse_option(host_arch => 'unknown-arch'); | |
47 | test_dep_parse_option(build_arch => 'all'); | |
48 | test_dep_parse_option(build_arch => 'any'); | |
49 | test_dep_parse_option(build_arch => 'linux-any'); | |
50 | test_dep_parse_option(build_arch => 'unknown-arch'); | |
51 | ||
52 | my $field_multiline = ' , , libgtk2.0-common (= 2.10.13-1) , libatk1.0-0 (>= | |
53 | 1.13.2), libc6 (>= 2.5-5), libcairo2 (>= 1.4.0), libcupsys2 (>= 1.2.7), | |
54 | libfontconfig1 (>= 2.4.0), libglib2.0-0 ( >= 2.12.9), libgnutls13 (>= | |
55 | 1.6.3-0), libjpeg62, python (<< 2.5) , , '; | |
56 | my $field_multiline_sorted = 'libatk1.0-0 (>= 1.13.2), libc6 (>= 2.5-5), libcairo2 (>= 1.4.0), libcupsys2 (>= 1.2.7), libfontconfig1 (>= 2.4.0), libglib2.0-0 (>= 2.12.9), libgnutls13 (>= 1.6.3-0), libgtk2.0-common (= 2.10.13-1), libjpeg62, python (<< 2.5)'; | |
57 | ||
58 | my $dep_multiline = deps_parse($field_multiline); | |
59 | $dep_multiline->sort(); | |
60 | is($dep_multiline->output(), $field_multiline_sorted, 'Parse, sort and output'); | |
61 | ||
62 | my $dep_sorted = deps_parse('pkgz, pkgz | pkgb, pkgz | pkga, pkga (>= 1.0), pkgz (<= 2.0)'); | |
63 | $dep_sorted->sort(); | |
64 | is($dep_sorted->output(), 'pkga (>= 1.0), pkgz, pkgz | pkga, pkgz | pkgb, pkgz (<= 2.0)', 'Check sort() algorithm'); | |
65 | ||
66 | my $dep_subset = deps_parse('libatk1.0-0 (>> 1.10), libc6, libcairo2'); | |
67 | is($dep_multiline->implies($dep_subset), 1, 'Dep implies subset of itself'); | |
68 | is($dep_subset->implies($dep_multiline), undef, "Subset doesn't imply superset"); | |
69 | my $dep_opposite = deps_parse('python (>= 2.5)'); | |
70 | is($dep_opposite->implies($dep_multiline), 0, 'Opposite condition implies NOT the depends'); | |
71 | ||
72 | my $dep_or1 = deps_parse('a|b (>=1.0)|c (>= 2.0)'); | |
73 | my $dep_or2 = deps_parse('x|y|a|b|c (<= 0.5)|c (>=1.5)|d|e'); | |
74 | is($dep_or1->implies($dep_or2), 1, 'Implication between OR 1/2'); | |
75 | is($dep_or2->implies($dep_or1), undef, 'Implication between OR 2/2'); | |
76 | ||
77 | my $dep_ma_host = deps_parse('libcairo2'); | |
78 | my $dep_ma_any = deps_parse('libcairo2:any'); | |
79 | my $dep_ma_build = deps_parse('libcairo2:native', build_dep => 1); | |
80 | my $dep_ma_explicit = deps_parse('libcairo2:amd64'); | |
81 | is($dep_ma_host->implies($dep_ma_any), undef, 'foo !-> foo:any'); | |
82 | is($dep_ma_build->implies($dep_ma_any), undef, 'foo:native !-> foo:any'); | |
83 | is($dep_ma_explicit->implies($dep_ma_any), undef, 'foo:<arch> !-> foo:any'); | |
84 | is($dep_ma_any->implies($dep_ma_host), undef, 'foo:any !-> foo'); | |
85 | is($dep_ma_any->implies($dep_ma_build), undef, 'foo:any !-> foo:native'); | |
86 | is($dep_ma_any->implies($dep_ma_explicit), undef, 'foo:any !-> foo:<arch>'); | |
87 | is($dep_ma_host->implies($dep_ma_host), 1, 'foo -> foo'); | |
88 | is($dep_ma_any->implies($dep_ma_any), 1, 'foo:any -> foo:any'); | |
89 | is($dep_ma_build->implies($dep_ma_build), 1, 'foo:native -> foo:native'); | |
90 | is($dep_ma_explicit->implies($dep_ma_explicit), 1, 'foo:<arch>-> foo:<arch>'); | |
91 | ||
92 | my $field_tests = 'self, @, @builddeps@'; | |
93 | $SIG{__WARN__} = sub {}; | |
94 | my $dep_tests_fail = deps_parse($field_tests); | |
95 | is($dep_tests_fail, undef, 'normal deps with @ in pkgname'); | |
96 | delete $SIG{__WARN__}; | |
97 | my $dep_tests_pass = deps_parse($field_tests, tests_dep => 1); | |
98 | is($dep_tests_pass->output(), $field_tests, 'tests deps with @ in pkgname'); | |
99 | ||
100 | my $field_arch = 'libc6 (>= 2.5) [!alpha !hurd-i386], libc6.1 [alpha], libc0.1 [hurd-i386]'; | |
101 | my $dep_i386 = deps_parse($field_arch, reduce_arch => 1, host_arch => 'i386'); | |
102 | my $dep_alpha = deps_parse($field_arch, reduce_arch => 1, host_arch => 'alpha'); | |
103 | my $dep_hurd = deps_parse($field_arch, reduce_arch => 1, host_arch => 'hurd-i386'); | |
104 | is($dep_i386->output(), 'libc6 (>= 2.5)', 'Arch reduce 1/3'); | |
105 | is($dep_alpha->output(), 'libc6.1', 'Arch reduce 2/3'); | |
106 | is($dep_hurd->output(), 'libc0.1', 'Arch reduce 3/3'); | |
107 | ||
108 | my $field_profile = 'dep1 <!stage1 !nocheck>, ' . | |
109 | 'dep2 <stage1 !nocheck>, ' . | |
110 | 'dep3 <nocheck !stage1>, ' . | |
111 | 'dep4 <stage1 nocheck>, ' . | |
112 | 'dep5 <stage1>, dep6 <!stage1>, ' . | |
113 | 'dep7 <stage1> | dep8 <nocheck>, ' . | |
114 | 'dep9 <!stage1> <!nocheck>, ' . | |
115 | 'dep10 <stage1> <!nocheck>, ' . | |
116 | 'dep11 <stage1> <nocheck>, '. | |
117 | 'dep12 <!nocheck> <!stage1>, ' . | |
118 | 'dep13 <nocheck> <!stage1>, ' . | |
119 | 'dep14 <nocheck> <stage1>'; | |
120 | my $dep_noprof = deps_parse($field_profile, reduce_profiles => 1, build_profiles => []); | |
121 | my $dep_stage1 = deps_parse($field_profile, reduce_profiles => 1, build_profiles => ['stage1']); | |
122 | my $dep_nocheck = deps_parse($field_profile, reduce_profiles => 1, build_profiles => ['nocheck']); | |
123 | my $dep_stage1nocheck = deps_parse($field_profile, reduce_profiles => 1, build_profiles => ['stage1', 'nocheck']); | |
124 | is($dep_noprof->output(), 'dep1, dep6, dep9, dep10, dep12, dep13', 'Profile reduce 1/4'); | |
125 | is($dep_stage1->output(), 'dep2, dep5, dep7, dep9, dep10, dep11, dep12, dep14', 'Profile reduce 2/4'); | |
126 | is($dep_nocheck->output(), 'dep3, dep6, dep8, dep9, dep11, dep12, dep13, dep14', 'Profile reduce 3/4'); | |
127 | is($dep_stage1nocheck->output(), 'dep4, dep5, dep7 | dep8, dep10, dep11, dep13, dep14', 'Profile reduce 4/4'); | |
128 | ||
129 | $dep_noprof = deps_parse($field_profile); | |
130 | $dep_noprof->reduce_profiles([]); | |
131 | $dep_stage1 = deps_parse($field_profile); | |
132 | $dep_stage1->reduce_profiles(['stage1']); | |
133 | $dep_nocheck = deps_parse($field_profile); | |
134 | $dep_nocheck->reduce_profiles(['nocheck']); | |
135 | $dep_stage1nocheck = deps_parse($field_profile); | |
136 | $dep_stage1nocheck->reduce_profiles(['stage1', 'nocheck']); | |
137 | is($dep_noprof->output(), 'dep1, dep6, dep9, dep10, dep12, dep13', 'Profile post-reduce 1/4'); | |
138 | is($dep_stage1->output(), 'dep2, dep5, dep7, dep9, dep10, dep11, dep12, dep14', 'Profile post-reduce 2/4'); | |
139 | is($dep_nocheck->output(), 'dep3, dep6, dep8, dep9, dep11, dep12, dep13, dep14', 'Profile post-reduce 3/4'); | |
140 | is($dep_stage1nocheck->output(), 'dep4, dep5, dep7 | dep8, dep10, dep11, dep13, dep14', 'Profile post-reduce 4/4'); | |
141 | ||
142 | my $field_restrict = 'dep1 <!bootstrap !restrict>, ' . | |
143 | 'dep2 <bootstrap restrict>, ' . | |
144 | 'dep3 <!restrict>, ' . | |
145 | 'dep4 <restrict>, ' . | |
146 | 'dep5 <!bootstrap> <!restrict>, ' . | |
147 | 'dep6 <bootstrap> <restrict>'; | |
148 | my $dep_restrict = deps_parse($field_restrict, reduce_restrictions => 1, build_profiles => []); | |
149 | is($dep_restrict->output(), 'dep1, dep3, dep5', 'Unknown restrictions reduce'); | |
150 | ||
151 | $dep_restrict = deps_parse($field_restrict); | |
152 | $dep_restrict->reduce_profiles([]); | |
153 | is($dep_restrict->output(), 'dep1, dep3, dep5', 'Unknown restrictions post-reduce'); | |
154 | ||
155 | my $facts = Dpkg::Deps::KnownFacts->new(); | |
156 | $facts->add_installed_package('mypackage', '1.3.4-1', get_host_arch(), 'no'); | |
157 | $facts->add_installed_package('mypackage2', '1.3.4-1', 'somearch', 'no'); | |
158 | $facts->add_installed_package('pkg-ma-foreign', '1.3.4-1', 'somearch', 'foreign'); | |
159 | $facts->add_installed_package('pkg-ma-foreign2', '1.3.4-1', get_host_arch(), 'foreign'); | |
160 | $facts->add_installed_package('pkg-ma-allowed', '1.3.4-1', 'somearch', 'allowed'); | |
161 | $facts->add_installed_package('pkg-ma-allowed2', '1.3.4-1', 'somearch', 'allowed'); | |
162 | $facts->add_installed_package('pkg-ma-allowed3', '1.3.4-1', get_host_arch(), 'allowed'); | |
163 | $facts->add_provided_package('myvirtual', undef, undef, 'mypackage'); | |
164 | $facts->add_provided_package('myvirtual2', REL_EQ, '1.0-1', 'mypackage'); | |
165 | $facts->add_provided_package('myvirtual3', REL_GE, '2.0-1', 'mypackage'); | |
166 | ||
167 | my $field_duplicate = 'libc6 (>= 2.3), libc6 (>= 2.6-1), mypackage (>= | |
168 | 1.3), myvirtual | something, python (>= 2.5), mypackage2, pkg-ma-foreign, | |
169 | pkg-ma-foreign2, pkg-ma-allowed:any, pkg-ma-allowed2, pkg-ma-allowed3'; | |
170 | my $dep_dup = deps_parse($field_duplicate); | |
171 | $dep_dup->simplify_deps($facts, $dep_opposite); | |
172 | is($dep_dup->output(), 'libc6 (>= 2.6-1), mypackage2, pkg-ma-allowed2', 'Simplify deps'); | |
173 | ||
174 | my $field_virtual = 'myvirtual | other'; | |
175 | my $dep_virtual = deps_parse($field_virtual); | |
176 | $dep_virtual->simplify_deps($facts); | |
177 | is($dep_virtual->output(), '', | |
178 | 'Simplify unversioned depends with unversioned virtual (satisfied)'); | |
179 | ||
180 | $field_virtual = 'myvirtual (>= 1.0) | other'; | |
181 | $dep_virtual = deps_parse($field_virtual); | |
182 | $dep_virtual->simplify_deps($facts); | |
183 | is($dep_virtual->output(), 'myvirtual (>= 1.0) | other', | |
184 | 'Simplify versioned depends on unversioned virtual (unsatisfied)'); | |
185 | ||
186 | $field_virtual = 'myvirtual2 (>= 0.0) | other'; | |
187 | $dep_virtual = deps_parse($field_virtual); | |
188 | $dep_virtual->simplify_deps($facts); | |
189 | is($dep_virtual->output(), '', | |
190 | 'Simplify versioned depends on versioned virtual (satisfied)'); | |
191 | ||
192 | $field_virtual = 'myvirtual2 (>= 2.0) | other'; | |
193 | $dep_virtual = deps_parse($field_virtual); | |
194 | $dep_virtual->simplify_deps($facts); | |
195 | is($dep_virtual->output(), 'myvirtual2 (>= 2.0) | other', | |
196 | 'Simplify versioned depends on versioned virtual (unsatisfied)'); | |
197 | ||
198 | $field_virtual = 'myvirtual3 (= 2.0-1)'; | |
199 | $dep_virtual = deps_parse($field_virtual); | |
200 | $dep_virtual->simplify_deps($facts); | |
201 | is($dep_virtual->output(), 'myvirtual3 (= 2.0-1)', | |
202 | 'Simplify versioned depends on GT versioned virtual (unsatisfied/ignored)'); | |
203 | ||
204 | my $field_dup_union = 'libc6 (>> 2.3), libc6 (>= 2.6-1), fake (<< 2.0), | |
205 | fake(>> 3.0), fake (= 2.5), python (<< 2.5), python (= 2.4)'; | |
206 | my $dep_dup_union = deps_parse($field_dup_union, union => 1); | |
207 | $dep_dup_union->simplify_deps($facts); | |
208 | is($dep_dup_union->output(), 'libc6 (>> 2.3), fake (<< 2.0), fake (>> 3.0), fake (= 2.5), python (<< 2.5)', 'Simplify union deps'); | |
209 | ||
210 | $dep_dup_union = deps_parse('sipsak (<= 0.9.6-2.1), sipsak (<= 0.9.6-2.2)', union => 1); | |
211 | $dep_dup_union->simplify_deps($facts); | |
212 | is($dep_dup_union->output(), 'sipsak (<= 0.9.6-2.2)', 'Simplify union deps 2'); | |
213 | ||
214 | my $dep_red = deps_parse('abc | xyz, two, abc'); | |
215 | $dep_red->simplify_deps($facts, $dep_opposite); | |
216 | is($dep_red->output(), 'abc, two', 'Simplification respect order'); | |
217 | is("$dep_red", $dep_red->output(), 'Stringification == output()'); | |
218 | ||
219 | my $dep_profiles = deps_parse('dupe <stage1 cross>, dupe <stage1 cross>'); | |
220 | $dep_profiles->simplify_deps($facts); | |
221 | is($dep_profiles->output(), 'dupe <stage1 cross>', | |
222 | 'Simplification respects duplicated profiles'); | |
223 | ||
224 | $dep_profiles = deps_parse('tool <!cross>, tool <stage1 cross>'); | |
225 | $dep_profiles->simplify_deps($facts); | |
226 | # XXX: Ideally this would get simplified to "tool <!cross> <stage1 cross>". | |
227 | is($dep_profiles->output(), 'tool <!cross>, tool <stage1 cross>', | |
228 | 'Simplification respects profiles'); | |
229 | ||
230 | $dep_profiles = deps_parse('libfoo-dev:native <!stage1>, libfoo-dev <!stage1 cross>', build_dep => 1); | |
231 | $dep_profiles->simplify_deps($facts); | |
232 | is($dep_profiles->output(), | |
233 | 'libfoo-dev:native <!stage1>, libfoo-dev <!stage1 cross>', | |
234 | 'Simplification respects archqualifiers and profiles'); | |
235 | ||
236 | my $dep_version = deps_parse('pkg, pkg (= 1.0)'); | |
237 | $dep_version->simplify_deps($facts); | |
238 | is($dep_version->output(), 'pkg (= 1.0)', 'Simplification merges versions'); | |
239 | ||
240 | my $dep_empty1 = deps_parse(''); | |
241 | is($dep_empty1->output(), '', 'Empty dependency'); | |
242 | ||
243 | my $dep_empty2 = deps_parse(' , , ', union => 1); | |
244 | is($dep_empty2->output(), '', "' , , ' is also an empty dependency"); | |
245 | ||
246 | # Check sloppy but acceptable dependencies | |
247 | ||
248 | my $dep_sloppy_version = deps_parse('package (= 1.0 )'); | |
249 | is($dep_sloppy_version->output(), 'package (= 1.0)', 'sloppy version restriction'); | |
250 | ||
251 | my $dep_sloppy_arch = deps_parse('package [ alpha ]'); | |
252 | is($dep_sloppy_arch->output(), 'package [alpha]', 'sloppy arch restriction'); | |
253 | ||
254 | my $dep_sloppy_profile = deps_parse('package < !profile > < other >'); | |
255 | is($dep_sloppy_profile->output(), 'package <!profile> <other>', | |
256 | 'sloppy profile restriction'); | |
257 | ||
258 | $SIG{__WARN__} = sub {}; | |
259 | ||
260 | my $dep_bad_version = deps_parse('package (= 1.0) (>= 2.0)'); | |
261 | is($dep_bad_version, undef, 'Bogus repeated version restriction'); | |
262 | ||
263 | my $dep_bad_arch = deps_parse('package [alpha] [amd64]'); | |
264 | is($dep_bad_arch, undef, 'Bogus repeated arch restriction'); | |
265 | ||
266 | my $dep_bad_multiline = deps_parse("a, foo\nbar, c"); | |
267 | is($dep_bad_multiline, undef, 'invalid dependency split over multiple line'); | |
268 | ||
269 | delete $SIG{__WARN__}; | |
270 | ||
271 | my $dep_iter = deps_parse('a, b:armel, c | d:armhf, d:mips (>> 1.2)'); | |
272 | my %dep_arches; | |
273 | my %dep_pkgs; | |
274 | deps_iterate($dep_iter, sub { | |
275 | my $dep = shift; | |
276 | ||
277 | $dep_pkgs{$dep->{package}} = 1; | |
278 | if ($dep->{archqual}) { | |
279 | $dep_arches{$dep->{archqual}} = 1; | |
280 | } | |
281 | return 1; | |
282 | }); | |
283 | my @dep_arches = sort keys %dep_arches; | |
284 | my @dep_pkgs = sort keys %dep_pkgs; | |
285 | is("@dep_arches", 'armel armhf mips', 'Dependency iterator, get arches'); | |
286 | is("@dep_pkgs", 'a b c d', 'Dependency iterator, get packages'); |