2 from basetest
import BaseTest
6 sys
.path
.insert(0, '..')
7 from zeroinstall
.injector
import solver
, arch
, model
8 from zeroinstall
.injector
.requirements
import Requirements
11 logger
= logging
.getLogger()
12 #logger.setLevel(logging.DEBUG)
14 mydir
= os
.path
.dirname(os
.path
.abspath(__file__
))
15 command_dep
= os
.path
.join(mydir
, 'command-dep.xml')
17 class TestSolver(BaseTest
):
19 iface_cache
= self
.config
.iface_cache
20 s
= solver
.DefaultSolver(self
.config
)
22 foo
= iface_cache
.get_interface('http://foo/Binary.xml')
23 self
.import_feed(foo
.uri
, 'Binary.xml')
24 foo_src
= iface_cache
.get_interface('http://foo/Source.xml')
25 self
.import_feed(foo_src
.uri
, 'Source.xml')
26 compiler
= iface_cache
.get_interface('http://foo/Compiler.xml')
27 self
.import_feed(compiler
.uri
, 'Compiler.xml')
29 binary_arch
= arch
.Architecture({None: 1}, {None: 1})
30 assert str(binary_arch
).startswith("<Arch")
31 s
.solve('http://foo/Binary.xml', binary_arch
)
34 assert s
.feeds_used
== set([foo
.uri
]), s
.feeds_used
35 assert s
.selections
[foo
].id == 'sha1=123'
37 # Now ask for source instead
38 s
.solve('http://foo/Binary.xml',
39 arch
.SourceArchitecture(binary_arch
),
40 command_name
= 'compile')
41 assert s
.ready
, s
.get_failure_reason()
42 assert s
.feeds_used
== set([foo
.uri
, foo_src
.uri
, compiler
.uri
]), s
.feeds_used
43 assert s
.selections
[foo
].id == 'sha1=234' # The source
44 assert s
.selections
[compiler
].id == 'sha1=345' # A binary needed to compile it
48 def testCommand(self
):
49 s
= solver
.DefaultSolver(self
.config
)
50 binary_arch
= arch
.Architecture({None: 1}, {None: 1})
51 s
.solve(command_dep
, binary_arch
)
52 command
= s
.selections
.selections
[s
.selections
.interface
].get_command("run")
53 dep
, = command
.requires
54 dep_impl
= s
.selections
.selections
[dep
.interface
]
55 assert dep_impl
.get_command("run").path
== "test-gui"
57 def testDetails(self
):
58 iface_cache
= self
.config
.iface_cache
59 s
= solver
.DefaultSolver(self
.config
)
61 foo_binary_uri
= 'http://foo/Binary.xml'
62 foo
= iface_cache
.get_interface(foo_binary_uri
)
63 self
.import_feed(foo_binary_uri
, 'Binary.xml')
64 foo_src
= iface_cache
.get_interface('http://foo/Source.xml')
65 self
.import_feed(foo_src
.uri
, 'Source.xml')
66 compiler
= iface_cache
.get_interface('http://foo/Compiler.xml')
67 self
.import_feed(compiler
.uri
, 'Compiler.xml')
69 r
= Requirements('http://foo/Binary.xml')
73 s
.record_details
= True
75 assert s
.ready
, s
.get_failure_reason()
77 foo_bin_impls
= iface_cache
.get_feed(foo_binary_uri
).implementations
78 foo_src_impls
= iface_cache
.get_feed(foo_src
.uri
).implementations
79 foo_impls
= iface_cache
.get_feed(foo
.uri
).implementations
80 compiler_impls
= iface_cache
.get_feed(compiler
.uri
).implementations
82 assert len(s
.details
) == 2
84 (foo_src_impls
['impossible'], None),
85 (foo_src_impls
['sha1=234'], None),
86 (foo_impls
['sha1=123'], 'Not source code'),
87 (foo_src_impls
['old'], None),
88 ], sorted(s
.details
[foo
]))
90 (compiler_impls
['sha1=999'], None),
91 (compiler_impls
['sha1=345'], None),
92 (compiler_impls
['sha1=678'], None),
93 ], s
.details
[compiler
])
95 def justify(uri
, impl
, expected
):
96 iface
= iface_cache
.get_interface(uri
)
97 e
= s
.justify_decision(r
, iface
, impl
)
98 self
.assertEqual(expected
, e
)
100 justify(foo_binary_uri
, foo_bin_impls
["sha1=123"],
101 'Binary 1.0 cannot be used (regardless of other components): Not source code')
102 justify(foo_binary_uri
, foo_src_impls
["sha1=234"],
103 'Binary 1.0 was selected as the preferred version.')
104 justify(foo_binary_uri
, foo_src_impls
["old"],
105 'Binary 0.1 is ranked lower than 1.0: newer versions are preferred')
106 justify(foo_binary_uri
, foo_src_impls
["impossible"],
107 '''There is no possible selection using Binary 3.\nCan't find all required implementations:\n- <Interface http://foo/Binary.xml> -> impossible\n- <Interface http://foo/Compiler.xml> -> None''')
108 justify(compiler
.uri
, compiler_impls
["sha1=999"],
109 '''Compiler 5 is selectable, but using it would produce a less optimal solution overall.\n\nThe changes would be:\n\nhttp://foo/Binary.xml: 1.0 to 0.1''')
111 def testRecursive(self
):
112 iface_cache
= self
.config
.iface_cache
113 s
= solver
.DefaultSolver(self
.config
)
115 foo
= iface_cache
.get_interface('http://foo/Recursive.xml')
116 self
.import_feed(foo
.uri
, 'Recursive.xml')
118 binary_arch
= arch
.Architecture({None: 1}, {None: 1})
119 s
.record_details
= True
120 s
.solve('http://foo/Recursive.xml', binary_arch
)
123 foo_impls
= iface_cache
.get_feed(foo
.uri
).implementations
125 assert len(s
.details
) == 1
126 assert s
.details
[foo
] == [(foo_impls
['sha1=abc'], None)]
128 def testMultiArch(self
):
129 iface_cache
= self
.config
.iface_cache
130 s
= solver
.DefaultSolver(self
.config
)
132 foo
= iface_cache
.get_interface('http://foo/MultiArch.xml')
133 self
.import_feed(foo
.uri
, 'MultiArch.xml')
134 lib
= iface_cache
.get_interface('http://foo/MultiArchLib.xml')
135 self
.import_feed(lib
.uri
, 'MultiArchLib.xml')
137 # On an i686 system we can only use the i486 implementation
139 binary_arch
= arch
.get_architecture('Linux', 'i686')
140 s
.solve('http://foo/MultiArch.xml', binary_arch
)
142 assert s
.selections
[foo
].machine
== 'i486'
143 assert s
.selections
[lib
].machine
== 'i486'
145 # On an 64 bit system we could use either, but we prefer the 64
146 # bit implementation. The i486 version of the library is newer,
147 # but we must pick one that is compatible with the main binary.
149 binary_arch
= arch
.get_architecture('Linux', 'x86_64')
150 s
.solve('http://foo/MultiArch.xml', binary_arch
)
152 assert s
.selections
[foo
].machine
== 'x86_64'
153 assert s
.selections
[lib
].machine
== 'x86_64'
156 host_arch
= arch
.get_host_architecture()
157 host_arch2
= arch
.get_architecture(None, None)
158 self
.assertEqual(host_arch
.os_ranks
, host_arch2
.os_ranks
)
159 self
.assertEqual(host_arch
.machine_ranks
, host_arch2
.machine_ranks
)
161 other
= arch
.get_architecture('FooBar', 'i486')
162 self
.assertEqual(3, len(other
.os_ranks
))
164 assert 'POSIX' in other
.os_ranks
165 assert 'FooBar' in other
.os_ranks
166 assert None in other
.os_ranks
167 assert 'i486' in other
.machine_ranks
168 assert 'ppc' not in other
.machine_ranks
170 win
= arch
.get_architecture('Windows', 'i486')
171 self
.assertEqual(2, len(win
.os_ranks
))
172 assert 'POSIX' not in win
.os_ranks
174 def testArchFor(self
):
175 s
= solver
.DefaultSolver(self
.config
)
176 r
= Requirements('http://foo/Binary.xml')
179 bin_arch
= s
.get_arch_for(r
)
180 self
.assertEqual({'i386': 0, None: 1}, bin_arch
.machine_ranks
)
183 src_arch
= s
.get_arch_for(r
)
184 self
.assertEqual({'src': 1}, src_arch
.machine_ranks
)
186 child
= self
.config
.iface_cache
.get_interface('http://foo/Dep.xml')
187 arch
= s
.get_arch_for(r
, child
)
188 self
.assertEqual(arch
.machine_ranks
, bin_arch
.machine_ranks
)
190 child
= self
.config
.iface_cache
.get_interface(r
.interface_uri
)
191 arch
= s
.get_arch_for(r
, child
)
192 self
.assertEqual(arch
.machine_ranks
, src_arch
.machine_ranks
)
194 def testRanking(self
):
195 iface_cache
= self
.config
.iface_cache
196 s
= solver
.DefaultSolver(self
.config
)
197 ranking
= os
.path
.join(os
.path
.abspath(os
.path
.dirname(__file__
)), 'Ranking.xml')
198 iface
= iface_cache
.get_interface(ranking
)
200 binary_arch
= arch
.get_architecture('Linux', 'x86_64')
203 s
.solve(ranking
, binary_arch
)
206 impl
= s
.selections
[iface
]
207 selected
.append(impl
.get_version() + ' ' + impl
.arch
)
208 impl
.arch
= 'Foo-odd' # prevent reselection
210 '0.2 Linux-i386', # poor arch, but newest version
211 '0.1 Linux-x86_64', # 64-bit is best match for host arch
212 '0.1 Linux-i686', '0.1 Linux-i586', '0.1 Linux-i486'], # ordering of x86 versions
215 def testRestricts(self
):
216 iface_cache
= self
.config
.iface_cache
217 s
= solver
.DefaultSolver(self
.config
)
218 uri
= os
.path
.join(os
.path
.abspath(os
.path
.dirname(__file__
)), 'Conflicts.xml')
219 versions
= os
.path
.join(os
.path
.abspath(os
.path
.dirname(__file__
)), 'Versions.xml')
220 iface
= iface_cache
.get_interface(uri
)
222 r
= Requirements(uri
)
224 # Selects 0.2 as the highest version, applying the restriction to versions < 4.
227 self
.assertEqual("0.2", s
.selections
.selections
[uri
].version
)
228 self
.assertEqual("3", s
.selections
.selections
[versions
].version
)
230 s
.extra_restrictions
[iface
] = [model
.VersionRestriction(model
.parse_version('0.1'))]
233 self
.assertEqual("0.1", s
.selections
.selections
[uri
].version
)
234 self
.assertEqual(None, s
.selections
.selections
.get(versions
, None))
236 s
.extra_restrictions
[iface
] = [model
.VersionRestriction(model
.parse_version('0.3'))]
241 iface_cache
= self
.config
.iface_cache
243 locale
.setlocale(locale
.LC_ALL
, 'en_US.UTF-8')
245 s
= solver
.DefaultSolver(self
.config
)
246 iface
= iface_cache
.get_interface('http://foo/Langs.xml')
247 self
.import_feed(iface
.uri
, 'Langs.xml')
249 # 1 is the oldest, but the only one in our language
250 binary_arch
= arch
.get_architecture(None, 'arch_1')
251 s
.solve('http://foo/Langs.xml', binary_arch
)
253 self
.assertEqual('sha1=1', s
.selections
[iface
].id)
255 # 6 is the newest, and close enough, even though not
256 # quite the right locale
257 binary_arch
= arch
.get_architecture(None, 'arch_2')
258 s
.solve('http://foo/Langs.xml', binary_arch
)
260 self
.assertEqual('sha1=6', s
.selections
[iface
].id)
262 # 9 is the newest, although 7 is a closer match
263 binary_arch
= arch
.get_architecture(None, 'arch_3')
264 s
.solve('http://foo/Langs.xml', binary_arch
)
266 self
.assertEqual('sha1=9', s
.selections
[iface
].id)
268 # 11 is the newest we understand
269 binary_arch
= arch
.get_architecture(None, 'arch_4')
270 s
.solve('http://foo/Langs.xml', binary_arch
)
272 self
.assertEqual('sha1=11', s
.selections
[iface
].id)
274 # 13 is the newest we understand
275 binary_arch
= arch
.get_architecture(None, 'arch_5')
276 s
.solve('http://foo/Langs.xml', binary_arch
)
278 self
.assertEqual('sha1=13', s
.selections
[iface
].id)
280 def check(target_arch
, langs
, expected
):
282 binary_arch
= arch
.get_architecture(None, target_arch
)
283 s
.solve('http://foo/Langs.xml', binary_arch
)
285 self
.assertEqual(expected
, s
.selections
[iface
].id)
287 # We don't understand any, so pick the newest
288 check('arch_2', ['es_ES'], 'sha1=6')
290 # These two have the same version number. Choose the
291 # one most appropriate to our country
292 check('arch_6', ['zh_CN'], 'sha1=15')
293 check('arch_6', ['zh_TW'], 'sha1=16')
295 # Same, but one doesn't have a country code
296 check('arch_7', ['bn'], 'sha1=17')
297 check('arch_7', ['bn_IN'], 'sha1=18')
299 locale
.setlocale(locale
.LC_ALL
, '')
301 def testDecideBug(self
):
302 s
= solver
.DefaultSolver(self
.config
)
303 watch_xml
= os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'watchdog.xml')
304 s
.solve(watch_xml
, arch
.get_architecture(None, None), command_name
= 'test')
306 def testRecommendBug(self
):
307 s
= solver
.DefaultSolver(self
.config
)
308 optional_missing_xml
= os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'OptionalMissing.xml')
309 s
.solve(optional_missing_xml
, arch
.get_architecture(None, None), command_name
= None)
311 def testFeedBug(self
):
312 self
.import_feed('http://foo/Build.xml', 'Build.xml')
313 self
.import_feed('http://foo/Compiler.xml', 'Compiler.xml')
314 self
.import_feed('http://foo/Compiler-new.xml', 'Compiler-new.xml')
315 s
= solver
.DefaultSolver(self
.config
)
316 s
.solve('http://foo/Build.xml', arch
.get_architecture(None, None))
317 assert s
.ready
, s
.get_failure_reason()
320 if __name__
== '__main__':