Start development series 1.9-post
[zeroinstall.git] / tests / testdriver.py
blob8f0db9a6345ba9479f44a6a5445414845cdfc94b
1 #!/usr/bin/env python
2 from basetest import BaseTest
3 import sys, tempfile, os, logging
4 from StringIO import StringIO
5 import unittest
7 sys.path.insert(0, '..')
9 from zeroinstall.injector import model, gpg, namespaces, reader, run, fetch
10 from zeroinstall.injector.requirements import Requirements
11 from zeroinstall.injector.driver import Driver
12 from zeroinstall.support import basedir, tasks
13 import data
15 foo_iface_uri = 'http://foo'
17 logger = logging.getLogger()
19 def recalculate(driver):
20 driver.need_download()
22 def download_and_execute(driver, prog_args, main = None, dry_run = True):
23 downloaded = driver.solve_and_download_impls()
24 if downloaded:
25 tasks.wait_for_blocker(downloaded)
26 run.execute_selections(driver.solver.selections, prog_args, stores = driver.config.stores, main = main, dry_run = dry_run)
28 class TestDriver(BaseTest):
29 def setUp(self):
30 BaseTest.setUp(self)
31 stream = tempfile.TemporaryFile()
32 stream.write(data.thomas_key)
33 stream.seek(0)
34 gpg.import_key(stream)
36 def cache_iface(self, name, data):
37 cached_ifaces = basedir.save_cache_path('0install.net',
38 'interfaces')
40 f = open(os.path.join(cached_ifaces, model.escape(name)), 'w')
41 f.write(data)
42 f.close()
44 def testNoNeedDl(self):
45 driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
46 assert driver.need_download()
48 driver = Driver(requirements = Requirements(os.path.abspath('Foo.xml')), config = self.config)
49 assert not driver.need_download()
50 assert driver.solver.ready
52 def testUnknownAlg(self):
53 self.cache_iface(foo_iface_uri,
54 """<?xml version="1.0" ?>
55 <interface
56 uri="%s"
57 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
58 <name>Foo</name>
59 <summary>Foo</summary>
60 <description>Foo</description>
61 <implementation main='.' id='unknown=123' version='1.0'>
62 <archive href='http://foo/foo.tgz' size='100'/>
63 </implementation>
64 </interface>""" % foo_iface_uri)
65 self.config.fetcher = fetch.Fetcher(self.config)
66 driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
67 try:
68 assert driver.need_download()
69 download_and_execute(driver, [])
70 except model.SafeException as ex:
71 assert 'Unknown digest algorithm' in str(ex)
73 def testDownload(self):
74 tmp = tempfile.NamedTemporaryFile()
75 tmp.write(
76 """<?xml version="1.0" ?>
77 <interface
78 main='ThisBetterNotExist'
79 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
80 <name>Foo</name>
81 <summary>Foo</summary>
82 <description>Foo</description>
83 <implementation version='1.0' id='/bin'/>
84 </interface>""")
85 tmp.flush()
86 driver = Driver(requirements = Requirements(tmp.name), config = self.config)
87 try:
88 download_and_execute(driver, ['Hello'])
89 assert 0
90 except model.SafeException as ex:
91 assert "ThisBetterNotExist" in str(ex)
92 tmp.close()
94 def testNoMain(self):
95 tmp = tempfile.NamedTemporaryFile()
96 tmp.write(
97 """<?xml version="1.0" ?>
98 <interface
99 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
100 <name>Foo</name>
101 <summary>Foo</summary>
102 <description>Foo</description>
103 <implementation version='1.0' id='/bin'/>
104 </interface>""")
105 tmp.flush()
106 driver = Driver(requirements = Requirements(tmp.name), config = self.config)
107 try:
108 download_and_execute(driver, ['Hello'])
109 assert 0
110 except model.SafeException as ex:
111 assert "library" in str(ex), ex
112 tmp.close()
114 def testNeedDL(self):
115 self.cache_iface(foo_iface_uri,
116 """<?xml version="1.0" ?>
117 <interface last-modified="0"
118 uri="%s"
119 main='ThisBetterNotExist'
120 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
121 <name>Foo</name>
122 <summary>Foo</summary>
123 <description>Foo</description>
124 <implementation version='1.0' id='sha1=123'>
125 <archive href='http://foo/foo.tgz' size='100'/>
126 </implementation>
127 </interface>""" % foo_iface_uri)
128 driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
129 self.config.network_use = model.network_full
130 recalculate(driver)
131 assert driver.need_download()
132 assert driver.solver.ready
134 def testBinding(self):
135 local_impl = os.path.dirname(os.path.abspath(__file__))
136 tmp = tempfile.NamedTemporaryFile()
137 tmp.write(
138 """<?xml version="1.0" ?>
139 <interface
140 main='testdriver.py'
141 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
142 <name>Bar</name>
143 <summary>Bar</summary>
144 <description>Bar</description>
145 <group>
146 <requires interface='%s'>
147 <environment name='FOO_PATH' insert='.'/>
148 <environment name='BAR_PATH' insert='.' default='/a:/b'/>
149 <environment name='NO_PATH' value='val'/>
150 <environment name='XDG_DATA_DIRS' insert='.'/>
151 </requires>
152 <environment name='SELF_GROUP' insert='group' mode='replace'/>
153 <implementation version='1.0' id='%s'>
154 <environment name='SELF_IMPL' insert='impl' mode='replace'/>
155 </implementation>
156 </group>
157 </interface>""" % (foo_iface_uri, local_impl))
158 tmp.flush()
159 self.cache_iface(foo_iface_uri,
160 """<?xml version="1.0" ?>
161 <interface last-modified="0"
162 uri="%s"
163 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
164 <name>Foo</name>
165 <summary>Foo</summary>
166 <description>Foo</description>
167 <implementation version='1.0' id='sha1=123'/>
168 </interface>""" % foo_iface_uri)
169 cached_impl = basedir.save_cache_path('0install.net',
170 'implementations',
171 'sha1=123')
172 driver = Driver(requirements = Requirements(tmp.name), config = self.config)
173 self.config.network_use = model.network_offline
174 os.environ['FOO_PATH'] = "old"
175 old, sys.stdout = sys.stdout, StringIO()
176 try:
177 download_and_execute(driver, ['Hello'])
178 finally:
179 sys.stdout = old
180 self.assertEqual(cached_impl + '/.:old',
181 os.environ['FOO_PATH'])
182 self.assertEqual(cached_impl + '/.:/a:/b',
183 os.environ['BAR_PATH'])
184 self.assertEqual('val', os.environ['NO_PATH'])
186 self.assertEqual(os.path.join(local_impl, 'group'), os.environ['SELF_GROUP'])
187 self.assertEqual(os.path.join(local_impl, 'impl'), os.environ['SELF_IMPL'])
189 del os.environ['FOO_PATH']
190 if 'XDG_DATA_DIRS' in os.environ:
191 del os.environ['XDG_DATA_DIRS']
192 os.environ['BAR_PATH'] = '/old'
193 old, sys.stdout = sys.stdout, StringIO()
194 try:
195 download_and_execute(driver, ['Hello'])
196 finally:
197 sys.stdout = old
198 self.assertEqual(cached_impl + '/.',
199 os.environ['FOO_PATH'])
200 self.assertEqual(cached_impl + '/.:/old',
201 os.environ['BAR_PATH'])
202 self.assertEqual(cached_impl + '/.:/usr/local/share:/usr/share',
203 os.environ['XDG_DATA_DIRS'])
205 def testFeeds(self):
206 self.cache_iface(foo_iface_uri,
207 """<?xml version="1.0" ?>
208 <interface last-modified="0"
209 uri="%s"
210 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
211 <name>Foo</name>
212 <summary>Foo</summary>
213 <description>Foo</description>
214 <feed src='http://bar'/>
215 </interface>""" % foo_iface_uri)
216 self.cache_iface('http://bar',
217 """<?xml version="1.0" ?>
218 <interface last-modified="0"
219 uri="http://bar"
220 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
221 <feed-for interface='%s'/>
222 <name>Bar</name>
223 <summary>Bar</summary>
224 <description>Bar</description>
225 <implementation version='1.0' id='sha1=123' main='dummy'>
226 <archive href='foo' size='10'/>
227 </implementation>
228 </interface>""" % foo_iface_uri)
229 driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
230 self.config.network_use = model.network_full
231 recalculate(driver)
232 assert driver.solver.ready
233 foo_iface = self.config.iface_cache.get_interface(foo_iface_uri)
234 self.assertEqual('sha1=123', driver.solver.selections[foo_iface].id)
236 def testBadConfig(self):
237 path = basedir.save_config_path(namespaces.config_site,
238 namespaces.config_prog)
239 glob = os.path.join(path, 'global')
240 assert not os.path.exists(glob)
241 stream = open(glob, 'w')
242 stream.write('hello!')
243 stream.close()
245 logger.setLevel(logging.ERROR)
246 Driver(requirements = Requirements(foo_iface_uri), config = self.config)
247 logger.setLevel(logging.WARN)
249 def testNoLocal(self):
250 self.cache_iface(foo_iface_uri,
251 """<?xml version="1.0" ?>
252 <interface last-modified="1110752708"
253 uri="%s"
254 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
255 <name>Foo</name>
256 <summary>Foo</summary>
257 <description>Foo</description>
258 <feed src='/etc/passwd'/>
259 </interface>""" % foo_iface_uri)
260 self.config.network_use = model.network_offline
261 try:
262 self.config.iface_cache.get_interface(foo_iface_uri)
263 assert False
264 except reader.InvalidInterface as ex:
265 assert 'Invalid feed URL' in str(ex)
267 def testDLfeed(self):
268 self.cache_iface(foo_iface_uri,
269 """<?xml version="1.0" ?>
270 <interface last-modified="1110752708"
271 uri="%s"
272 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
273 <name>Foo</name>
274 <summary>Foo</summary>
275 <description>Foo</description>
276 <feed src='http://example.com'/>
277 </interface>""" % foo_iface_uri)
278 driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
279 self.config.network_use = model.network_full
281 assert driver.need_download()
283 feed = self.config.iface_cache.get_feed(foo_iface_uri)
284 feed.feeds = [model.Feed('/BadFeed', None, False)]
286 logger.setLevel(logging.ERROR)
287 assert driver.need_download() # Triggers warning
288 logger.setLevel(logging.WARN)
290 def testBestUnusable(self):
291 self.cache_iface(foo_iface_uri,
292 """<?xml version="1.0" ?>
293 <interface last-modified="1110752708"
294 uri="%s"
295 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
296 <name>Foo</name>
297 <summary>Foo</summary>
298 <description>Foo</description>
299 <implementation id='sha1=123' version='1.0' arch='odd-weird' main='dummy'/>
300 </interface>""" % foo_iface_uri)
301 driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
302 self.config.network_use = model.network_offline
303 recalculate(driver)
304 assert not driver.solver.ready, driver.implementation
305 try:
306 download_and_execute(driver, [])
307 assert False
308 except model.SafeException as ex:
309 assert "has no usable implementations" in str(ex), ex
311 def testNoArchives(self):
312 self.cache_iface(foo_iface_uri,
313 """<?xml version="1.0" ?>
314 <interface last-modified="1110752708"
315 uri="%s"
316 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
317 <name>Foo</name>
318 <summary>Foo</summary>
319 <description>Foo</description>
320 <implementation id='sha1=123' version='1.0' main='dummy'/>
321 </interface>""" % foo_iface_uri)
322 driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
323 recalculate(driver)
324 assert not driver.solver.ready
326 def testCycle(self):
327 self.cache_iface(foo_iface_uri,
328 """<?xml version="1.0" ?>
329 <interface last-modified="1110752708"
330 uri="%s"
331 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
332 <name>Foo</name>
333 <summary>Foo</summary>
334 <description>Foo</description>
335 <group>
336 <requires interface='%s'/>
337 <implementation id='sha1=123' version='1.0'>
338 <archive href='foo' size='10'/>
339 </implementation>
340 </group>
341 </interface>""" % (foo_iface_uri, foo_iface_uri))
342 driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
343 recalculate(driver)
345 def testConstraints(self):
346 self.cache_iface('http://bar',
347 """<?xml version="1.0" ?>
348 <interface last-modified="1110752708"
349 uri="http://bar"
350 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
351 <name>Bar</name>
352 <summary>Bar</summary>
353 <description>Bar</description>
354 <implementation id='sha1=100' version='1.0'>
355 <archive href='foo' size='10'/>
356 </implementation>
357 <implementation id='sha1=150' stability='developer' version='1.5'>
358 <archive href='foo' size='10'/>
359 </implementation>
360 <implementation id='sha1=200' version='2.0'>
361 <archive href='foo' size='10'/>
362 </implementation>
363 </interface>""")
364 self.cache_iface(foo_iface_uri,
365 """<?xml version="1.0" ?>
366 <interface last-modified="1110752708"
367 uri="%s"
368 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
369 <name>Foo</name>
370 <summary>Foo</summary>
371 <description>Foo</description>
372 <group main='dummy'>
373 <requires interface='http://bar'>
374 <version/>
375 </requires>
376 <implementation id='sha1=123' version='1.0'>
377 <archive href='foo' size='10'/>
378 </implementation>
379 </group>
380 </interface>""" % foo_iface_uri)
381 driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
382 self.config.network_use = model.network_full
383 #logger.setLevel(logging.DEBUG)
384 recalculate(driver)
385 #logger.setLevel(logging.WARN)
386 foo_iface = self.config.iface_cache.get_interface(foo_iface_uri)
387 bar_iface = self.config.iface_cache.get_interface('http://bar')
388 assert driver.solver.selections[bar_iface].id == 'sha1=200'
390 dep = driver.solver.selections[foo_iface].dependencies['http://bar']
391 assert len(dep.restrictions) == 1
392 restriction = dep.restrictions[0]
394 restriction.before = model.parse_version('2.0')
395 recalculate(driver)
396 assert driver.solver.selections[bar_iface].id == 'sha1=100'
398 restriction.not_before = model.parse_version('1.5')
399 recalculate(driver)
400 assert driver.solver.selections[bar_iface].id == 'sha1=150'
402 def testSource(self):
403 iface_cache = self.config.iface_cache
405 foo = iface_cache.get_interface('http://foo/Binary.xml')
406 self.import_feed(foo.uri, 'Binary.xml')
407 foo_src = iface_cache.get_interface('http://foo/Source.xml')
408 self.import_feed(foo_src.uri, 'Source.xml')
409 compiler = iface_cache.get_interface('http://foo/Compiler.xml')
410 self.import_feed(compiler.uri, 'Compiler.xml')
412 self.config.freshness = 0
413 self.config.network_use = model.network_full
414 driver = Driver(requirements = Requirements('http://foo/Binary.xml'), config = self.config)
415 tasks.wait_for_blocker(driver.solve_with_downloads())
416 assert driver.solver.selections[foo].id == 'sha1=123'
418 # Now ask for source instead
419 driver.requirements.source = True
420 driver.requirements.command = 'compile'
421 tasks.wait_for_blocker(driver.solve_with_downloads())
422 assert driver.solver.ready, driver.solver.get_failure_reason()
423 assert driver.solver.selections[foo].id == 'sha1=234' # The source
424 assert driver.solver.selections[compiler].id == 'sha1=345' # A binary needed to compile it
426 if __name__ == '__main__':
427 unittest.main()