All unit-tests now pass with Python 3
[zeroinstall/solver.git] / tests / testinstall.py
blob8b8bb469c37182fe7570bbf0c29525c341a01402
1 #!/usr/bin/env python
2 from basetest import BaseTest, TestStores
3 import sys, os, tempfile
4 if sys.version_info[0] > 2:
5 from io import StringIO, BytesIO
6 else:
7 from StringIO import StringIO
8 BytesIO = StringIO
9 import unittest
11 sys.path.insert(0, '..')
12 from zeroinstall import cmd
13 from zeroinstall.injector import model, selections, qdom, reader, handler, gpg, config
15 mydir = os.path.dirname(__file__)
17 class Reply:
18 def __init__(self, reply):
19 self.reply = reply
21 def readline(self):
22 return self.reply
24 class TestInstall(BaseTest):
25 def run_0install(self, args):
26 old_stdout = sys.stdout
27 old_stderr = sys.stderr
28 try:
29 sys.stdout = StringIO()
30 sys.stderr = StringIO()
31 ex = None
32 try:
33 cmd.main(args, config = self.config)
34 except NameError:
35 raise
36 except SystemExit:
37 pass
38 except TypeError:
39 raise
40 except AttributeError:
41 raise
42 except AssertionError:
43 raise
44 except ValueError:
45 raise
46 except Exception as ex2:
47 ex = ex2 # Python 3
48 raise
49 out = sys.stdout.getvalue()
50 err = sys.stderr.getvalue()
51 if ex is not None:
52 err += str(ex.__class__)
53 finally:
54 sys.stdout = old_stdout
55 sys.stderr = old_stderr
56 return (out, err)
58 def testHelp(self):
59 out, err = self.run_0install([])
60 assert out.lower().startswith("usage:")
61 assert 'add-feed' in out
62 assert '--version' in out
63 assert not err, err
65 out2, err = self.run_0install(['--help'])
66 assert not err, err
67 assert out2 == out
69 out, err = self.run_0install(['--version'])
70 assert 'Thomas Leonard' in out
71 assert not err, err
73 out, err = self.run_0install(['foobar'])
74 assert 'Unknown sub-command' in err, err
76 def testSelect(self):
77 out, err = self.run_0install(['select'])
78 assert out.lower().startswith("usage:")
79 assert '--xml' in out
81 out, err = self.run_0install(['select', 'Local.xml'])
82 assert not err, err
83 assert 'Version: 0.1' in out
85 out, err = self.run_0install(['select', 'Local.xml', '--command='])
86 assert not err, err
87 assert 'Version: 0.1' in out
89 local_uri = model.canonical_iface_uri('Local.xml')
90 out, err = self.run_0install(['select', 'Local.xml'])
91 assert not err, err
92 assert 'Version: 0.1' in out
94 out, err = self.run_0install(['select', 'Local.xml', '--xml'])
95 sels = selections.Selections(qdom.parse(BytesIO(str(out).encode('utf-8'))))
96 assert sels.selections[local_uri].version == '0.1'
98 out, err = self.run_0install(['select', 'selections.xml'])
99 assert not err, err
100 assert 'Version: 1\n' in out
101 assert '(not cached)' in out
103 out, err = self.run_0install(['select', 'runnable/RunExec.xml'])
104 assert not err, err
105 assert 'Runner' in out, out
107 def testDownload(self):
108 out, err = self.run_0install(['download'])
109 assert out.lower().startswith("usage:")
110 assert '--show' in out
112 out, err = self.run_0install(['download', 'Local.xml', '--show'])
113 assert not err, err
114 assert 'Version: 0.1' in out
116 local_uri = model.canonical_iface_uri('Local.xml')
117 out, err = self.run_0install(['download', 'Local.xml', '--xml'])
118 assert not err, err
119 sels = selections.Selections(qdom.parse(BytesIO(str(out).encode('utf-8'))))
120 assert sels.selections[local_uri].version == '0.1'
122 out, err = self.run_0install(['download', 'Local.xml', '--show', '--with-store=/foo'])
123 assert not err, err
124 assert self.config.stores.stores[-1].dir == '/foo'
126 out, err = self.run_0install(['download', '--offline', 'selections.xml'])
127 assert 'Would download' in err
128 self.config.network_use = model.network_full
130 self.config.stores = TestStores()
131 digest = 'sha1=3ce644dc725f1d21cfcf02562c76f375944b266a'
132 self.config.fetcher.allow_download(digest)
133 out, err = self.run_0install(['download', 'Hello.xml', '--show'])
134 assert not err, err
135 assert self.config.stores.lookup_any([digest]).startswith('/fake')
136 assert 'Version: 1\n' in out
138 out, err = self.run_0install(['download', '--offline', 'selections.xml', '--show'])
139 assert '/fake_store' in out
140 self.config.network_use = model.network_full
142 def testDownloadSelections(self):
143 self.config.stores = TestStores()
144 digest = 'sha1=3ce644dc725f1d21cfcf02562c76f375944b266a'
145 self.config.fetcher.allow_download(digest)
146 with open('Hello.xml') as stream: hello = stream.read()
147 self.config.fetcher.allow_feed_download('http://example.com:8000/Hello.xml', hello)
148 out, err = self.run_0install(['download', 'selections.xml', '--show'])
149 assert not err, err
150 assert self.config.stores.lookup_any([digest]).startswith('/fake')
151 assert 'Version: 1\n' in out
153 def testUpdate(self):
154 out, err = self.run_0install(['update'])
155 assert out.lower().startswith("usage:")
156 assert '--message' in out, out
158 # Updating a local feed with no dependencies
159 out, err = self.run_0install(['update', 'Local.xml'])
160 assert not err, err
161 assert 'No updates found' in out, out
163 # Using a remote feed for the first time
164 self.config.stores = TestStores()
165 with open('Binary.xml') as stream: binary_feed = stream.read()
166 self.config.fetcher.allow_download('sha1=123')
167 self.config.fetcher.allow_feed_download('http://foo/Binary.xml', binary_feed)
168 out, err = self.run_0install(['update', 'http://foo/Binary.xml'])
169 assert not err, err
170 assert 'Binary.xml: new -> 1.0' in out, out
172 # No updates.
173 self.config.fetcher.allow_feed_download('http://foo/Binary.xml', binary_feed)
174 out, err = self.run_0install(['update', 'http://foo/Binary.xml'])
175 assert not err, err
176 assert 'No updates found' in out, out
178 # New binary release available.
179 new_binary_feed = binary_feed.replace("version='1.0'", "version='1.1'")
180 assert binary_feed != new_binary_feed
181 self.config.fetcher.allow_feed_download('http://foo/Binary.xml', new_binary_feed)
182 out, err = self.run_0install(['update', 'http://foo/Binary.xml'])
183 assert not err, err
184 assert 'Binary.xml: 1.0 -> 1.1' in out, out
186 # Compiling from source for the first time.
187 with open('Source.xml') as stream: source_feed = stream.read()
188 with open('Compiler.xml') as stream: compiler_feed = stream.read()
189 self.config.fetcher.allow_download('sha1=234')
190 self.config.fetcher.allow_download('sha1=345')
191 self.config.fetcher.allow_feed_download('http://foo/Compiler.xml', compiler_feed)
192 self.config.fetcher.allow_feed_download('http://foo/Binary.xml', binary_feed)
193 self.config.fetcher.allow_feed_download('http://foo/Source.xml', source_feed)
194 out, err = self.run_0install(['update', 'http://foo/Binary.xml', '--source'])
195 assert not err, err
196 assert 'Binary.xml: new -> 1.0' in out, out
197 assert 'Compiler.xml: new -> 1.0' in out, out
199 # New compiler released.
200 new_compiler_feed = compiler_feed.replace(
201 "id='sha1=345' version='1.0'",
202 "id='sha1=345' version='1.1'")
203 assert new_compiler_feed != compiler_feed
204 self.config.fetcher.allow_feed_download('http://foo/Compiler.xml', new_compiler_feed)
205 self.config.fetcher.allow_feed_download('http://foo/Binary.xml', binary_feed)
206 self.config.fetcher.allow_feed_download('http://foo/Source.xml', source_feed)
207 out, err = self.run_0install(['update', 'http://foo/Binary.xml', '--source'])
208 assert not err, err
209 assert 'Compiler.xml: 1.0 -> 1.1' in out, out
211 # A dependency disappears.
212 with open('Source-missing-req.xml') as stream: new_source_feed = stream.read()
213 self.config.fetcher.allow_feed_download('http://foo/Compiler.xml', new_compiler_feed)
214 self.config.fetcher.allow_feed_download('http://foo/Binary.xml', binary_feed)
215 self.config.fetcher.allow_feed_download('http://foo/Source.xml', new_source_feed)
216 out, err = self.run_0install(['update', 'http://foo/Binary.xml', '--source'])
217 assert not err, err
218 assert 'No longer used: http://foo/Compiler.xml' in out, out
220 def testConfig(self):
221 out, err = self.run_0install(['config', '--help'])
222 assert out.lower().startswith("usage:")
223 assert '--console' in out
225 out, err = self.run_0install(['config'])
226 assert not err, err
227 assert 'full' in out, out
228 assert 'freshness = 0' in out, out
229 assert 'help_with_testing = False' in out, out
231 out, err = self.run_0install(['config', 'help_with_testing'])
232 assert out == 'False\n', out
234 file_config = config.load_config(handler.Handler())
235 def get_value(name):
236 old_stdout = sys.stdout
237 sys.stdout = StringIO()
238 try:
239 cmd.config.handle(file_config, None, [name])
240 cmd_output = sys.stdout.getvalue()
241 finally:
242 sys.stdout = old_stdout
243 return cmd_output
245 assert get_value('freshness') == '30d\n'
246 assert get_value('network_use') == 'full\n'
247 assert get_value('help_with_testing') == 'False\n'
249 cmd.config.handle(file_config, None, ['freshness', '5m'])
250 cmd.config.handle(file_config, None, ['help_with_testing', 'True'])
251 cmd.config.handle(file_config, None, ['network_use', 'minimal'])
252 assert file_config.freshness == 5 * 60
253 assert file_config.network_use == model.network_minimal
254 assert file_config.help_with_testing == True
256 file_config2 = config.load_config(handler.Handler())
257 assert file_config2.freshness == 5 * 60
258 assert file_config2.network_use == model.network_minimal
259 assert file_config2.help_with_testing == True
261 cmd.config.handle(file_config, None, ['help_with_testing', 'falsE'])
262 assert file_config.help_with_testing == False
264 for period in ['1s', '2d', '3.5m', '4h', '5d']:
265 secs = cmd.config.TimeInterval.parse(period)
266 assert cmd.config.TimeInterval.format(secs) == period
268 def testAddFeed(self):
269 binary_iface = self.config.iface_cache.get_interface('http://foo/Binary.xml')
271 out, err = self.run_0install(['list-feeds', binary_iface.uri])
272 assert "(no feeds)" in out, out
273 assert not err, err
275 out, err = self.run_0install(['add-feed'])
276 assert out.lower().startswith("usage:")
277 assert 'NEW-FEED' in out
279 sys.stdin = Reply('1')
280 assert binary_iface.extra_feeds == []
282 out, err = self.run_0install(['add-feed', 'Source.xml'])
283 assert not err, err
284 assert "Add as feed for 'http://foo/Binary.xml'" in out, out
285 assert len(binary_iface.extra_feeds) == 1
287 out, err = self.run_0install(['list-feeds', binary_iface.uri])
288 assert "Source.xml" in out
289 assert not err, err
291 out, err = self.run_0install(['remove-feed', 'Source.xml'])
292 assert not err, err
293 assert "Remove as feed for 'http://foo/Binary.xml'" in out, out
294 assert len(binary_iface.extra_feeds) == 0
296 with open('Source.xml') as stream: source_feed = stream.read()
297 self.config.fetcher.allow_feed_download('http://foo/Source.xml', source_feed)
298 out, err = self.run_0install(['add-feed', 'http://foo/Source.xml'])
299 assert not err, err
300 assert 'Downloading feed; please wait' in out, out
301 assert len(binary_iface.extra_feeds) == 1
303 def testImport(self):
304 out, err = self.run_0install(['import'])
305 assert out.lower().startswith("usage:")
306 assert 'FEED' in out
308 stream = open('6FCF121BE2390E0B.gpg')
309 gpg.import_key(stream)
310 stream.close()
311 sys.stdin = Reply('Y\n')
312 out, err = self.run_0install(['import', 'Hello.xml'])
313 assert not out, out
314 assert 'Trusting DE937DD411906ACF7C263B396FCF121BE2390E0B for example.com:8000' in err, out
316 def testList(self):
317 out, err = self.run_0install(['list', 'foo', 'bar'])
318 assert out.lower().startswith("usage:")
319 assert 'PATTERN' in out
321 out, err = self.run_0install(['list'])
322 assert not err, err
323 assert '' == out, repr(out)
325 self.testImport()
327 out, err = self.run_0install(['list'])
328 assert not err, err
329 assert 'http://example.com:8000/Hello.xml\n' == out, repr(out)
331 out, err = self.run_0install(['list', 'foo'])
332 assert not err, err
333 assert '' == out, repr(out)
335 out, err = self.run_0install(['list', 'hello'])
336 assert not err, err
337 assert 'http://example.com:8000/Hello.xml\n' == out, repr(out)
339 def testRun(self):
340 out, err = self.run_0install(['run'])
341 assert out.lower().startswith("usage:")
342 assert 'URI' in out, out
344 out, err = self.run_0install(['run', '--dry-run', 'runnable/Runnable.xml', '--help'])
345 assert not err, err
346 assert 'arg-for-runner' in out, out
347 assert '--help' in out, out
349 def testDigest(self):
350 hw = os.path.join(mydir, 'HelloWorld.tgz')
351 out, err = self.run_0install(['digest', '--algorithm=sha1', hw])
352 assert out == 'sha1=3ce644dc725f1d21cfcf02562c76f375944b266a\n', out
353 assert not err, err
355 out, err = self.run_0install(['digest', hw])
356 assert out == 'sha1new=290eb133e146635fe37713fd58174324a16d595f\n', out
357 assert not err, err
359 out, err = self.run_0install(['digest', hw, 'HelloWorld'])
360 assert out == 'sha1new=491678c37f77fadafbaae66b13d48d237773a68f\n', out
361 assert not err, err
363 tmp = tempfile.mkdtemp(prefix = '0install')
364 out, err = self.run_0install(['digest', tmp])
365 assert out == 'sha1new=da39a3ee5e6b4b0d3255bfef95601890afd80709\n', out
366 assert not err, err
367 os.rmdir(tmp)
369 def testApps(self):
370 out, err = self.run_0install(['add', 'local-app'])
371 assert out.lower().startswith("usage:")
373 out, err = self.run_0install(['destroy', 'local-app', 'uri'])
374 assert out.lower().startswith("usage:")
376 local_feed = os.path.join(mydir, 'Local.xml')
378 out, err = self.run_0install(['add', 'local:app', local_feed])
379 assert not out, out
380 assert "Invalid application name 'local:app'" in err, err
382 out, err = self.run_0install(['add', 'local-app', local_feed])
383 assert not out, out
384 assert not err, err
386 out, err = self.run_0install(['add', 'local-app', local_feed])
387 assert not out, out
388 assert "Application 'local-app' already exists" in err, err
390 out, err = self.run_0install(['select', 'local-app'])
391 assert "Version: 0.1" in out, out
392 assert not err, err
394 out, err = self.run_0install(['update', 'local-app'])
395 assert "No updates found. Continuing with version 0.1." in out, out
396 assert not err, err
398 # whatchanged
399 out, err = self.run_0install(['whatchanged', 'local-app', 'uri'])
400 assert out.lower().startswith("usage:")
402 out, err = self.run_0install(['whatchanged', 'local-app'])
403 assert "No previous history to compare against." in out, out
404 assert not err, err
406 app = self.config.app_mgr.lookup_app('local-app')
407 with open(os.path.join(app.path, "selections.xml")) as stream:
408 old_local = stream.read()
409 new_local = old_local.replace('0.1', '0.1-pre')
410 with open(os.path.join(app.path, "selections-2012-01-01.xml"), 'w') as stream:
411 stream.write(new_local)
413 out, err = self.run_0install(['whatchanged', 'local-app'])
414 assert "Local.xml: 0.1-pre -> 0.1" in out, out
415 assert not err, err
417 out, err = self.run_0install(['whatchanged', 'local-app', '--full'])
418 assert "--- 2012-01-01" in out, out
419 assert not err, err
421 out, err = self.run_0install(['destroy', 'local-app'])
422 assert not out, out
423 assert not err, err
425 out, err = self.run_0install(['destroy', 'local-app'])
426 assert not out, out
427 assert "No such application 'local-app'" in err, err
429 if __name__ == '__main__':
430 unittest.main()