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