Start development series 1.8-post
[zeroinstall/solver.git] / tests / testsolver.py
blob81c96573bcf0fd9b21506482a21a692a58680fea
1 #!/usr/bin/env python
2 from basetest import BaseTest
3 import sys, os, locale
4 import unittest
6 sys.path.insert(0, '..')
7 from zeroinstall.injector import solver, arch
8 from zeroinstall.injector.requirements import Requirements
10 import logging
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):
18 def testSimple(self):
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)
33 assert s.ready
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
46 assert not s.details
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')
70 r.source = True
71 r.command = 'compile'
73 s.record_details = True
74 s.solve_for(r)
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
83 self.assertEqual([
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]))
89 self.assertEqual([
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)
121 assert s.ready
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)
141 assert s.ready
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)
151 assert s.ready
152 assert s.selections[foo].machine == 'x86_64'
153 assert s.selections[lib].machine == 'x86_64'
155 def testArch(self):
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 testRanking(self):
175 iface_cache = self.config.iface_cache
176 s = solver.DefaultSolver(self.config)
177 ranking = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'Ranking.xml')
178 iface = iface_cache.get_interface(ranking)
180 binary_arch = arch.get_architecture('Linux', 'x86_64')
181 selected = []
182 while True:
183 s.solve(ranking, binary_arch)
184 if not s.ready:
185 break
186 impl = s.selections[iface]
187 selected.append(impl.get_version() + ' ' + impl.arch)
188 impl.arch = 'Foo-odd' # prevent reselection
189 self.assertEqual([
190 '0.2 Linux-i386', # poor arch, but newest version
191 '0.1 Linux-x86_64', # 64-bit is best match for host arch
192 '0.1 Linux-i686', '0.1 Linux-i586', '0.1 Linux-i486'], # ordering of x86 versions
193 selected)
195 def testLangs(self):
196 iface_cache = self.config.iface_cache
197 try:
198 locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
200 s = solver.DefaultSolver(self.config)
201 iface = iface_cache.get_interface('http://foo/Langs.xml')
202 self.import_feed(iface.uri, 'Langs.xml')
204 # 1 is the oldest, but the only one in our language
205 binary_arch = arch.get_architecture(None, 'arch_1')
206 s.solve('http://foo/Langs.xml', binary_arch)
207 assert s.ready
208 self.assertEqual('sha1=1', s.selections[iface].id)
210 # 6 is the newest, and close enough, even though not
211 # quite the right locale
212 binary_arch = arch.get_architecture(None, 'arch_2')
213 s.solve('http://foo/Langs.xml', binary_arch)
214 assert s.ready
215 self.assertEqual('sha1=6', s.selections[iface].id)
217 # 9 is the newest, although 7 is a closer match
218 binary_arch = arch.get_architecture(None, 'arch_3')
219 s.solve('http://foo/Langs.xml', binary_arch)
220 assert s.ready
221 self.assertEqual('sha1=9', s.selections[iface].id)
223 # 11 is the newest we understand
224 binary_arch = arch.get_architecture(None, 'arch_4')
225 s.solve('http://foo/Langs.xml', binary_arch)
226 assert s.ready
227 self.assertEqual('sha1=11', s.selections[iface].id)
229 # 13 is the newest we understand
230 binary_arch = arch.get_architecture(None, 'arch_5')
231 s.solve('http://foo/Langs.xml', binary_arch)
232 assert s.ready
233 self.assertEqual('sha1=13', s.selections[iface].id)
235 def check(target_arch, langs, expected):
236 s.langs = langs
237 binary_arch = arch.get_architecture(None, target_arch)
238 s.solve('http://foo/Langs.xml', binary_arch)
239 assert s.ready
240 self.assertEqual(expected, s.selections[iface].id)
242 # We don't understand any, so pick the newest
243 check('arch_2', ['es_ES'], 'sha1=6')
245 # These two have the same version number. Choose the
246 # one most appropriate to our country
247 check('arch_6', ['zh_CN'], 'sha1=15')
248 check('arch_6', ['zh_TW'], 'sha1=16')
250 # Same, but one doesn't have a country code
251 check('arch_7', ['bn'], 'sha1=17')
252 check('arch_7', ['bn_IN'], 'sha1=18')
253 finally:
254 locale.setlocale(locale.LC_ALL, '')
256 def testDecideBug(self):
257 s = solver.DefaultSolver(self.config)
258 watch_xml = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'watchdog.xml')
259 s.solve(watch_xml, arch.get_architecture(None, None), command_name = 'test')
261 def testRecommendBug(self):
262 s = solver.DefaultSolver(self.config)
263 optional_missing_xml = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'OptionalMissing.xml')
264 s.solve(optional_missing_xml, arch.get_architecture(None, None), command_name = None)
266 if __name__ == '__main__':
267 unittest.main()