def testNoNeedDl(self): driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config) assert driver.need_download() driver = Driver(requirements = Requirements(os.path.abspath('Foo.xml')), config = self.config) assert not driver.need_download() assert driver.solver.ready
def ensure_cached(uri, command = 'run', config = None): """Ensure that an implementation of uri is cached. If not, it downloads one. It uses the GUI if a display is available, or the console otherwise. @param uri: the required interface @type uri: str @return: the selected implementations, or None if the user cancelled @rtype: L{zeroinstall.injector.selections.Selections} """ from zeroinstall.injector.driver import Driver if config is None: from zeroinstall.injector.config import load_config config = load_config() from zeroinstall.injector.requirements import Requirements requirements = Requirements(uri) requirements.command = command d = Driver(config, requirements) if d.need_download() or not d.solver.ready: sels = get_selections_gui(uri, ['--command', command], use_gui = None) if sels != DontUseGUI: return sels done = d.solve_and_download_impls() tasks.wait_for_blocker(done) return d.solver.selections
def testReplacedConflicts(self): self.import_feed('http://localhost:8000/Hello', 'Hello') s = solver.DefaultSolver(self.config) replaced_path = model.canonical_iface_uri( os.path.join(mydir, 'Replaced.xml')) replaced_conflicts_path = model.canonical_iface_uri( os.path.join(mydir, 'ReplacedConflicts.xml')) r = Requirements(replaced_conflicts_path) s.solve_for(r) assert s.ready, s.get_failure_reason() assert s.selections self.assertEqual("b", s.selections.selections[replaced_conflicts_path].id) self.assertEqual( "2", s.selections.selections[replaced_conflicts_path].version) self.assertEqual( "sha1=3ce644dc725f1d21cfcf02562c76f375944b266a", s.selections.selections["http://localhost:8000/Hello"].id) self.assertEqual(2, len(s.selections.selections)) r.extra_restrictions[r.interface_uri] = '..!2' s.extra_restrictions = r.get_extra_restrictions( self.config.iface_cache) s.solve_for(r) assert s.ready, s.get_failure_reason() assert s.selections self.assertEqual( "1", s.selections.selections[replaced_conflicts_path].version) self.assertEqual("0", s.selections.selections[replaced_path].version) self.assertEqual(2, len(s.selections.selections))
def __init__(self, root=None, handler=None, src=None, command=-1, config=None, requirements=None): """ @param requirements: Details about the program we want to run @type requirements: L{requirements.Requirements} @param config: The configuration settings to use, or None to load from disk. @type config: L{config.Config} Note: all other arguments are deprecated (since 0launch 0.52) """ if requirements is None: from zeroinstall.injector.requirements import Requirements requirements = Requirements(root) requirements.source = bool( src) # Root impl must be a "src" machine type if command == -1: if src: command = 'compile' else: command = 'run' requirements.command = command else: assert root == src == None assert command == -1 if config is None: config = load_config(handler) else: assert handler is None, "can't pass a handler and a config" self.driver = driver.Driver(config=config, requirements=requirements)
def __init__(self, root = None, handler = None, src = None, command = -1, config = None, requirements = None): """ @param requirements: Details about the program we want to run @type requirements: L{requirements.Requirements} @param config: The configuration settings to use, or None to load from disk. @type config: L{config.Config} Note: all other arguments are deprecated (since 0launch 0.52) """ if requirements is None: from zeroinstall.injector.requirements import Requirements requirements = Requirements(root) requirements.source = bool(src) # Root impl must be a "src" machine type if command == -1: if src: command = 'compile' else: command = 'run' requirements.command = command else: assert root == src == None assert command == -1 if config is None: config = load_config(handler) else: assert handler is None, "can't pass a handler and a config" self.driver = driver.Driver(config = config, requirements = requirements)
def get_selections(config, options, iface_uri, select_only, download_only, test_callback, requirements = None): """Get selections for iface_uri, according to the options passed. Will switch to GUI mode if necessary. @param options: options from OptionParser @param iface_uri: canonical URI of the interface @param select_only: return immediately even if the selected versions aren't cached @param download_only: wait for stale feeds, and display GUI button as Download, not Run @param requirements: requirements to use; if None, requirements come from options (since 1.15) @type requirements: Requirements @return: the selected versions, or None if the user cancels @rtype: L{selections.Selections} | None """ if options.offline: config.network_use = model.network_offline iface_cache = config.iface_cache # Try to load it as a feed. If it is a feed, it'll get cached. If not, it's a # selections document and we return immediately. maybe_selections = iface_cache.get_feed(iface_uri, selections_ok = True) if isinstance(maybe_selections, selections.Selections): if not select_only: blocker = maybe_selections.download_missing(config) if blocker: logger.info(_("Waiting for selected implementations to be downloaded...")) tasks.wait_for_blocker(blocker) return maybe_selections if requirements is None: requirements = Requirements(iface_uri) requirements.parse_options(options) return get_selections_for(requirements, config, options, select_only, download_only, test_callback)
def testDetails(self): iface_cache = self.config.iface_cache s = solver.DefaultSolver(self.config) foo_binary_uri = 'http://foo/Binary.xml' foo = iface_cache.get_interface(foo_binary_uri) self.import_feed(foo_binary_uri, 'Binary.xml') foo_src = iface_cache.get_interface('http://foo/Source.xml') self.import_feed(foo_src.uri, 'Source.xml') compiler = iface_cache.get_interface('http://foo/Compiler.xml') self.import_feed(compiler.uri, 'Compiler.xml') r = Requirements('http://foo/Binary.xml') r.source = True r.command = 'compile' s.record_details = True s.solve_for(r) assert s.ready, s.get_failure_reason() foo_bin_impls = iface_cache.get_feed(foo_binary_uri).implementations foo_src_impls = iface_cache.get_feed(foo_src.uri).implementations foo_impls = iface_cache.get_feed(foo.uri).implementations compiler_impls = iface_cache.get_feed(compiler.uri).implementations assert len(s.details) == 2 self.assertEqual([ (foo_src_impls['impossible'], None), (foo_src_impls['sha1=234'], None), (foo_impls['sha1=123'], 'Not source code'), (foo_src_impls['old'], None), ], sorted(s.details[foo])) self.assertEqual([ (compiler_impls['sha1=999'], None), (compiler_impls['sha1=345'], None), (compiler_impls['sha1=678'], None), ], s.details[compiler]) def justify(uri, impl, expected): iface = iface_cache.get_interface(uri) e = s.justify_decision(r, iface, impl) self.assertEqual(expected, e) justify(foo_binary_uri, foo_bin_impls["sha1=123"], 'Binary 1.0 cannot be used (regardless of other components): Not source code') justify(foo_binary_uri, foo_src_impls["sha1=234"], 'Binary 1.0 was selected as the preferred version.') justify(foo_binary_uri, foo_src_impls["old"], 'Binary 0.1 is ranked lower than 1.0: newer versions are preferred') justify(foo_binary_uri, foo_src_impls["impossible"], "There is no possible selection using Binary 3.\n" "Can't find all required implementations:\n" "- http://foo/Binary.xml -> 3 (impossible)\n" " User requested implementation 3 (impossible)\n" "- http://foo/Compiler.xml -> (problem)\n" " http://foo/Binary.xml 3 requires version < 1.0, 1.0 <= version\n" " No usable implementations satisfy the restrictions") justify(compiler.uri, compiler_impls["sha1=999"], '''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''')
def testLocalArchive(self): local_iface = os.path.join(mydir, 'LocalArchive.xml') with open(local_iface, 'rb') as stream: root = qdom.parse(stream) # Not local => error feed = model.ZeroInstallFeed(root) impl = feed.implementations['impl1'] blocker = self.config.fetcher.download_impls([impl], self.config.stores) try: tasks.wait_for_blocker(blocker) assert 0 except model.SafeException as ex: assert "Relative URL 'HelloWorld.tgz' in non-local feed" in str( ex), ex feed = model.ZeroInstallFeed(root, local_path=local_iface) # Missing file impl2 = feed.implementations['impl2'] blocker = self.config.fetcher.download_impls([impl2], self.config.stores) try: tasks.wait_for_blocker(blocker) assert 0 except model.SafeException as ex: assert 'tests/IDONTEXIST.tgz' in str(ex), ex # Wrong size impl3 = feed.implementations['impl3'] blocker = self.config.fetcher.download_impls([impl3], self.config.stores) try: tasks.wait_for_blocker(blocker) assert 0 except model.SafeException as ex: assert 'feed says 177, but actually 176 bytes' in str(ex), ex self.config.network_use = model.network_offline r = Requirements(local_iface) r.command = None driver = Driver(requirements=r, config=self.config) driver.need_download() assert driver.solver.ready, driver.solver.get_failure_reason() # Local => OK impl = feed.implementations['impl1'] path = self.config.stores.lookup_maybe(impl.digests) assert not path blocker = self.config.fetcher.download_impls([impl], self.config.stores) tasks.wait_for_blocker(blocker) path = self.config.stores.lookup_any(impl.digests) assert os.path.exists(os.path.join(path, 'HelloWorld'))
def testExtractToNewSubdirectory(self): with output_suppressed(): run_server(('HelloWorld.tar.bz2',)) requirements = Requirements(os.path.abspath('HelloExtractToNewDest.xml')) requirements.command = None driver = Driver(requirements = requirements, config = self.config) driver_download(driver) digests = driver.solver.selections.selections[requirements.interface_uri].digests path = self.config.stores.lookup_any(digests) assert os.path.exists(os.path.join(path, 'src', 'HelloWorld', 'main'))
def testRecipeRemoveDir(self): with output_suppressed(): run_server(('HelloWorld.tar.bz2',)) requirements = Requirements(os.path.abspath('RecipeRemoveDir.xml')) requirements.command = None driver = Driver(requirements = requirements, config = self.config) driver_download(driver) digests = driver.solver.selections.selections[requirements.interface_uri].digests path = self.config.stores.lookup_any(digests) assert not os.path.exists(os.path.join(path, 'HelloWorld'))
def testDownloadFile(self): with output_suppressed(): run_server(('HelloWorldMain',)) requirements = Requirements(os.path.abspath('HelloSingleFile.xml')) requirements.command = None driver = Driver(requirements = requirements, config = self.config) driver_download(driver) digests = driver.solver.selections[requirements.interface_uri].digests path = self.config.stores.lookup_any(digests) assert os.path.exists(os.path.join(path, 'main'))
def testDownloadFile(self): with output_suppressed(): run_server(('HelloWorldMain',)) requirements = Requirements(os.path.abspath('HelloSingleFile.xml')) requirements.command = None driver = Driver(requirements = requirements, config = self.config) driver_download(driver) digests = driver.solver.selections.selections[requirements.interface_uri].digests path = self.config.stores.lookup_any(digests) with open(os.path.join(path, 'main'), 'rt') as stream: assert 'Hello World' in stream.read()
def testRecipeSingleFile(self): with output_suppressed(): run_server(('HelloWorldMain',)) requirements = Requirements(os.path.abspath('RecipeSingleFile.xml')) requirements.command = None driver = Driver(requirements = requirements, config = self.config) driver_download(driver) digests = driver.solver.selections.selections[requirements.interface_uri].digests path = self.config.stores.lookup_any(digests) with open(os.path.join(path, 'bin','main'), 'rt') as stream: assert 'Hello World' in stream.read()
def testRecipeExtractToExistingSubdirectory(self): with output_suppressed(): run_server(('HelloWorld.tar.bz2','HelloWorld.tar.bz2')) requirements = Requirements(os.path.abspath('RecipeExtractToExistingDest.xml')) requirements.command = None driver = Driver(requirements = requirements, config = self.config) driver_download(driver) digests = driver.solver.selections.selections[requirements.interface_uri].digests path = self.config.stores.lookup_any(digests) assert os.path.exists(os.path.join(path, 'HelloWorld', 'main')) # first archive's main assert os.path.exists(os.path.join(path, 'HelloWorld', 'HelloWorld', 'main')) # second archive, extracted to HelloWorld/
def __init__(self, root = None, handler = None, src = None, command = -1, config = None, requirements = None): """ @param requirements: Details about the program we want to run @type requirements: L{requirements.Requirements} @param config: The configuration settings to use, or None to load from disk. @type config: L{ConfigParser.ConfigParser} Note: all other arguments are deprecated (since 0launch 0.52) """ self.watchers = [] if requirements is None: from zeroinstall.injector.requirements import Requirements requirements = Requirements(root) requirements.source = bool(src) # Root impl must be a "src" machine type if command == -1: if src: command = 'compile' else: command = 'run' requirements.command = command self.target_arch = arch.get_host_architecture() else: assert root == src == None assert command == -1 self.target_arch = arch.get_architecture(requirements.os, requirements.cpu) self.requirements = requirements self.stale_feeds = set() if config is None: self.config = load_config(handler or Handler()) else: assert handler is None, "can't pass a handler and a config" self.config = config from zeroinstall.injector.solver import DefaultSolver self.solver = DefaultSolver(self.config) # If we need to download something but can't because we are offline, # warn the user. But only the first time. self._warned_offline = False debug(_("Supported systems: '%s'"), arch.os_ranks) debug(_("Supported processors: '%s'"), arch.machine_ranks) if requirements.before or requirements.not_before: self.solver.extra_restrictions[config.iface_cache.get_interface(requirements.interface_uri)] = [ model.VersionRangeRestriction(model.parse_version(requirements.before), model.parse_version(requirements.not_before))]
def testSource(self): iface_cache = self.config.iface_cache foo = iface_cache.get_interface('http://foo/Binary.xml') self.import_feed(foo.uri, 'Binary.xml') foo_src = iface_cache.get_interface('http://foo/Source.xml') self.import_feed(foo_src.uri, 'Source.xml') compiler = iface_cache.get_interface('http://foo/Compiler.xml') self.import_feed(compiler.uri, 'Compiler.xml') self.config.freshness = 0 self.config.network_use = model.network_full driver = Driver(requirements=Requirements('http://foo/Binary.xml'), config=self.config) tasks.wait_for_blocker(driver.solve_with_downloads()) assert driver.solver.selections.selections[foo.uri].id == 'sha1=123' # Now ask for source instead driver.requirements.source = True driver.requirements.command = 'compile' tasks.wait_for_blocker(driver.solve_with_downloads()) assert driver.solver.ready, driver.solver.get_failure_reason() assert driver.solver.selections.selections[ foo.uri].id == 'sha1=234' # The source assert driver.solver.selections.selections[ compiler.uri].id == 'sha1=345' # A binary needed to compile it
def testDistro(self): with output_suppressed(): native_url = 'http://example.com:8000/Native.xml' # Initially, we don't have the feed at all... master_feed = self.config.iface_cache.get_feed(native_url) assert master_feed is None, master_feed trust.trust_db.trust_key( 'DE937DD411906ACF7C263B396FCF121BE2390E0B', 'example.com:8000') run_server( 'Native.xml', '6FCF121BE2390E0B.gpg', '/key-info/key/DE937DD411906ACF7C263B396FCF121BE2390E0B') driver = Driver(requirements=Requirements(native_url), config=self.config) assert driver.need_download() solve = driver.solve_with_downloads() tasks.wait_for_blocker(solve) tasks.check(solve) master_feed = self.config.iface_cache.get_feed(native_url) assert master_feed is not None assert master_feed.implementations == {} distro_feed_url = master_feed.get_distro_feed() assert distro_feed_url is not None distro_feed = self.config.iface_cache.get_feed(distro_feed_url) assert distro_feed is not None assert len( distro_feed.implementations) == 2, distro_feed.implementations
def testAbsMain(self): with tempfile.NamedTemporaryFile(prefix='test-', delete=False) as tmp: tmp.write(("""<?xml version="1.0" ?> <interface last-modified="1110752708" uri="%s" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Foo</name> <summary>Foo</summary> <description>Foo</description> <group main='/bin/sh'> <implementation id='.' version='1'/> </group> </interface>""" % foo_iface_uri).encode('utf-8')) driver = Driver(requirements=Requirements(tmp.name), config=self.config) try: downloaded = driver.solve_and_download_impls() if downloaded: tasks.wait_for_blocker(downloaded) run.execute_selections(driver.solver.selections, [], stores=self.config.stores) assert False except SafeException as ex: assert 'Command path must be relative' in str(ex), ex
def testImplMirror(self): with resourcewarnings_suppressed(): # This is like testMirror, except we have a different archive (that generates the same content), # rather than an exact copy of the unavailable archive. trust.trust_db.trust_key( 'DE937DD411906ACF7C263B396FCF121BE2390E0B', 'example.com:8000') run_server( '/Hello.xml', '/6FCF121BE2390E0B.gpg', server.Give404('/HelloWorld.tgz'), server.Give404( '/0mirror/archive/http%3A%2F%2Flocalhost%3A8000%2FHelloWorld.tgz' ), '/0mirror/feeds/http/example.com:8000/Hello.xml/impl/sha1=3ce644dc725f1d21cfcf02562c76f375944b266a' ) driver = Driver( requirements=Requirements('http://example.com:8000/Hello.xml'), config=self.config) self.config.mirror = 'http://example.com:8000/0mirror' refreshed = driver.solve_with_downloads() tasks.wait_for_blocker(refreshed) assert driver.solver.ready getLogger().setLevel(logging.ERROR) downloaded = driver.download_uncached_implementations() tasks.wait_for_blocker(downloaded) path = self.config.stores.lookup_any( driver.solver.selections. selections['http://example.com:8000/Hello.xml'].digests) assert os.path.exists(os.path.join(path, 'HelloWorld', 'main'))
def testRestricts(self): iface_cache = self.config.iface_cache s = solver.DefaultSolver(self.config) uri = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'Conflicts.xml') versions = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'Versions.xml') iface = iface_cache.get_interface(uri) r = Requirements(uri) # Selects 0.2 as the highest version, applying the restriction to versions < 4. s.solve_for(r) assert s.ready self.assertEqual("0.2", s.selections.selections[uri].version) self.assertEqual("3", s.selections.selections[versions].version) s.extra_restrictions[iface] = [ model.VersionRestriction(model.parse_version('0.1')) ] s.solve_for(r) assert s.ready self.assertEqual("0.1", s.selections.selections[uri].version) self.assertEqual(None, s.selections.selections.get(versions, None)) s.extra_restrictions[iface] = [ model.VersionRestriction(model.parse_version('0.3')) ] s.solve_for(r) assert not s.ready
def testImplMirrorFails(self): with resourcewarnings_suppressed(): trust.trust_db.trust_key('DE937DD411906ACF7C263B396FCF121BE2390E0B', 'example.com:8000') run_server('/Hello.xml', '/6FCF121BE2390E0B.gpg', server.Give404('/HelloWorld.tgz'), server.Give404('/0mirror/archive/http%3A%23%23example.com%3A8000%23HelloWorld.tgz'), server.Give404('/0mirror/feeds/http/example.com:8000/Hello.xml/impl/sha1=3ce644dc725f1d21cfcf02562c76f375944b266a')) driver = Driver(requirements = Requirements('http://example.com:8000/Hello.xml'), config = self.config) self.config.mirror = 'http://example.com:8000/0mirror' refreshed = driver.solve_with_downloads() tasks.wait_for_blocker(refreshed) assert driver.solver.ready getLogger().setLevel(logging.ERROR) try: downloaded = driver.download_uncached_implementations() tasks.wait_for_blocker(downloaded) assert 0 except download.DownloadError as ex: assert 'Missing: HelloWorld.tgz' in str(ex), ex self.assertEqual([ 'http://example.com:8000/Hello.xml', 'http://example.com:8000/6FCF121BE2390E0B.gpg', # The original archive: 'http://example.com:8000/HelloWorld.tgz', # Mirror of original archive: 'http://example.com:8000/0mirror/archive/http%3A%23%23example.com%3A8000%23HelloWorld.tgz', # Mirror of implementation: 'http://example.com:8000/0mirror/feeds/http/example.com:8000/Hello.xml/impl/sha1=3ce644dc725f1d21cfcf02562c76f375944b266a' ], traced_downloads)
def testFeeds(self): self.cache_iface(foo_iface_uri, """<?xml version="1.0" ?> <interface last-modified="0" uri="%s" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Foo</name> <summary>Foo</summary> <description>Foo</description> <feed src='http://bar'/> </interface>""" % foo_iface_uri) self.cache_iface('http://bar', """<?xml version="1.0" ?> <interface last-modified="0" uri="http://bar" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <feed-for interface='%s'/> <name>Bar</name> <summary>Bar</summary> <description>Bar</description> <implementation version='1.0' id='sha1=123' main='dummy'> <archive href='foo' size='10'/> </implementation> </interface>""" % foo_iface_uri) driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config) self.config.network_use = model.network_full recalculate(driver) assert driver.solver.ready foo_iface = self.config.iface_cache.get_interface(foo_iface_uri) self.assertEqual('sha1=123', driver.solver.selections[foo_iface].id)
def testMirrors(self): with resourcewarnings_suppressed(): getLogger().setLevel(logging.ERROR) trust.trust_db.trust_key( 'DE937DD411906ACF7C263B396FCF121BE2390E0B', 'example.com:8000') run_server( server.Give404('/Hello.xml'), '/0mirror/feeds/http/example.com:8000/Hello.xml/latest.xml', '/0mirror/keys/6FCF121BE2390E0B.gpg', server.Give404('/HelloWorld.tgz'), '/0mirror/archive/http%3A%23%23example.com%3A8000%23HelloWorld.tgz' ) driver = Driver( requirements=Requirements('http://example.com:8000/Hello.xml'), config=self.config) self.config.mirror = 'http://example.com:8000/0mirror' refreshed = driver.solve_with_downloads() tasks.wait_for_blocker(refreshed) assert driver.solver.ready #getLogger().setLevel(logging.WARN) downloaded = driver.download_uncached_implementations() tasks.wait_for_blocker(downloaded) path = self.config.stores.lookup_any( driver.solver.selections. selections['http://example.com:8000/Hello.xml'].digests) assert os.path.exists(os.path.join(path, 'HelloWorld', 'main'))
def testAbsMain(self): p = Driver(requirements=Requirements(command_feed), config=self.config) self.config.handler.wait_for_blocker(p.solve_with_downloads()) old_stdout = sys.stdout try: sys.stdout = StringIO() run.execute_selections(p.solver.selections, [], main='/runnable/runner', dry_run=True, stores=self.config.stores) finally: sys.stdout = old_stdout try: old_stdout = sys.stdout try: sys.stdout = StringIO() run.execute_selections(p.solver.selections, [], main='/runnable/not-there', dry_run=True, stores=self.config.stores) finally: sys.stdout = old_stdout except SafeException as ex: assert 'not-there' in unicode(ex)
def test(top_xml, diag_xml, expected_error): root = qdom.parse( BytesIO("""<?xml version="1.0" ?> <interface xmlns="http://zero-install.sourceforge.net/2004/injector/interface" uri="{top}"> <name>Top-level</name> <summary>Top-level</summary> <group> {top_xml} </group> </interface>""".format(top=top_uri, top_xml=top_xml).encode("utf-8"))) self.import_feed(top_uri, root) root = qdom.parse( BytesIO("""<?xml version="1.0" ?> <interface xmlns="http://zero-install.sourceforge.net/2004/injector/interface" uri="{diag}"> <name>Diagnostics</name> <summary>Diagnostics</summary> <group> {impls} </group> </interface>""".format(diag=diag_uri, impls=diag_xml).encode("utf-8"))) self.import_feed(diag_uri, root) root = qdom.parse( BytesIO("""<?xml version="1.0" ?> <interface xmlns="http://zero-install.sourceforge.net/2004/injector/interface" uri="{old}"> <name>Old</name> <summary>Old</summary> <feed src='{diag}'/> <replaced-by interface='{diag}'/> </interface>""".format(diag=diag_uri, old=old_uri).encode("utf-8"))) self.import_feed(old_uri, root) r = Requirements(top_uri) r.os = "Windows" r.cpu = "x86_64" s = solver.DefaultSolver(self.config) s.solve_for(r) assert not s.ready, s.selections.selections if expected_error != str(s.get_failure_reason()): print(s.get_failure_reason()) self.assertEqual(expected_error, str(s.get_failure_reason())) return s
def solve(mountpoint, context): global _mountpoints, _client _mountpoints = [mountpoint] if mountpoint != '~': _mountpoints.append('~') if mountpoint != '/': _mountpoints.append('/') _client = IPCClient() try: requirement = Requirements(context) # TODO requirement.command = 'activity' return _solve(requirement) finally: _client.close()
def testBadMain(self): r = Requirements(command_feed) r.command = None d = Driver(requirements = r, config = self.config) self.config.handler.wait_for_blocker(d.solve_with_downloads()) try: run.execute_selections(d.solver.selections, [], dry_run = True, stores = self.config.stores) assert 0 except SafeException as ex: self.assertEqual("Can't run: no command specified!", unicode(ex)) try: run.execute_selections(d.solver.selections, [], main = 'relpath', dry_run = True, stores = self.config.stores) assert 0 except SafeException as ex: self.assertEqual("Can't use a relative replacement main when there is no original one!", unicode(ex))
def get_selections(config, options, iface_uri, select_only, download_only, test_callback, requirements=None): """Get selections for iface_uri, according to the options passed. Will switch to GUI mode if necessary. @type config: L{zeroinstall.injector.config.Config} @param options: options from OptionParser @param iface_uri: canonical URI of the interface @type iface_uri: str @param select_only: return immediately even if the selected versions aren't cached @type select_only: bool @param download_only: wait for stale feeds, and display GUI button as Download, not Run @type download_only: bool @param requirements: requirements to use; if None, requirements come from options (since 1.15) @type requirements: Requirements @return: the selected versions, or None if the user cancels @rtype: L{selections.Selections} | None""" if options.offline: config.network_use = model.network_offline iface_cache = config.iface_cache # Try to load it as a feed. If it is a feed, it'll get cached. If not, it's a # selections document and we return immediately. maybe_selections = iface_cache.get_feed(iface_uri, selections_ok=True) if isinstance(maybe_selections, selections.Selections): if not select_only: blocker = maybe_selections.download_missing(config) if blocker: logger.info( _("Waiting for selected implementations to be downloaded..." )) tasks.wait_for_blocker(blocker) return maybe_selections if requirements is None: requirements = Requirements(iface_uri) requirements.parse_options(options) return get_selections_for(requirements, config, options, select_only, download_only, test_callback)
def testConstraints(self): self.cache_iface( 'http://bar', """<?xml version="1.0" ?> <interface last-modified="1110752708" uri="http://bar" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Bar</name> <summary>Bar</summary> <description>Bar</description> <implementation id='sha1=100' version='1.0'> <archive href='foo' size='10'/> </implementation> <implementation id='sha1=150' stability='developer' version='1.5'> <archive href='foo' size='10'/> </implementation> <implementation id='sha1=200' version='2.0'> <archive href='foo' size='10'/> </implementation> </interface>""") self.cache_iface( foo_iface_uri, """<?xml version="1.0" ?> <interface last-modified="1110752708" uri="%s" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Foo</name> <summary>Foo</summary> <description>Foo</description> <group main='dummy'> <requires interface='http://bar'> <version/> </requires> <implementation id='sha1=123' version='1.0'> <archive href='foo' size='10'/> </implementation> </group> </interface>""" % foo_iface_uri) driver = Driver(requirements=Requirements(foo_iface_uri), config=self.config) self.config.network_use = model.network_full #logger.setLevel(logging.DEBUG) recalculate(driver) #logger.setLevel(logging.WARN) foo_iface = self.config.iface_cache.get_interface(foo_iface_uri) bar_iface = self.config.iface_cache.get_interface('http://bar') assert driver.solver.selections[bar_iface].id == 'sha1=200' dep = driver.solver.selections[foo_iface].dependencies['http://bar'] assert len(dep.restrictions) == 1 restriction = dep.restrictions[0] restriction.before = model.parse_version('2.0') recalculate(driver) assert driver.solver.selections[bar_iface].id == 'sha1=100' restriction.not_before = model.parse_version('1.5') recalculate(driver) assert driver.solver.selections[bar_iface].id == 'sha1=150'
def test(top_xml, diag_xml, expected_error): root = qdom.parse(BytesIO("""<?xml version="1.0" ?> <interface xmlns="http://zero-install.sourceforge.net/2004/injector/interface" uri="{top}"> <name>Top-level</name> <summary>Top-level</summary> <group> {top_xml} </group> </interface>""".format(top = top_uri, top_xml = top_xml).encode("utf-8"))) self.import_feed(top_uri, root) root = qdom.parse(BytesIO("""<?xml version="1.0" ?> <interface xmlns="http://zero-install.sourceforge.net/2004/injector/interface" uri="{diag}"> <name>Diagnostics</name> <summary>Diagnostics</summary> <group> {impls} </group> </interface>""".format(diag = diag_uri, impls = diag_xml).encode("utf-8"))) self.import_feed(diag_uri, root) root = qdom.parse(BytesIO("""<?xml version="1.0" ?> <interface xmlns="http://zero-install.sourceforge.net/2004/injector/interface" uri="{old}"> <name>Old</name> <summary>Old</summary> <feed src='{diag}'/> <replaced-by interface='{diag}'/> </interface>""".format(diag = diag_uri, old = old_uri).encode("utf-8"))) self.import_feed(old_uri, root) r = Requirements(top_uri) r.os = "Windows" r.cpu = "x86_64" s = solver.DefaultSolver(self.config) s.solve_for(r) assert not s.ready, s.selections.selections if expected_error != str(s.get_failure_reason()): print(s.get_failure_reason()) self.assertEqual(expected_error, str(s.get_failure_reason())) return s
def testArchFor(self): s = solver.DefaultSolver(self.config) r = Requirements('http://foo/Binary.xml') r.cpu = 'i386' bin_arch = s.get_arch_for(r) self.assertEqual({'i386': 0, None: 1}, bin_arch.machine_ranks) r.source = True src_arch = s.get_arch_for(r) self.assertEqual({'src': 1}, src_arch.machine_ranks) child = self.config.iface_cache.get_interface('http://foo/Dep.xml') arch = s.get_arch_for(r, child) self.assertEqual(arch.machine_ranks, bin_arch.machine_ranks) child = self.config.iface_cache.get_interface(r.interface_uri) arch = s.get_arch_for(r, child) self.assertEqual(arch.machine_ranks, src_arch.machine_ranks)
def testBadConfig(self): path = basedir.save_config_path(namespaces.config_site, namespaces.config_prog) glob = os.path.join(path, 'global') assert not os.path.exists(glob) stream = open(glob, 'w') stream.write('hello!') stream.close() logger.setLevel(logging.ERROR) Driver(requirements = Requirements(foo_iface_uri), config = self.config) logger.setLevel(logging.WARN)
def testAcceptKey(self): with output_suppressed(): run_server('Hello', '6FCF121BE2390E0B.gpg', '/key-info/key/DE937DD411906ACF7C263B396FCF121BE2390E0B', 'HelloWorld.tgz') driver = Driver(requirements = Requirements('http://localhost:8000/Hello'), config = self.config) assert driver.need_download() sys.stdin = Reply("Y\n") try: download_and_execute(driver, ['Hello'], main = 'Missing') assert 0 except model.SafeException as ex: if "HelloWorld/Missing" not in str(ex): raise
def testArgs(self): p = Driver(requirements=Requirements(runnable), config=self.config) self.config.handler.wait_for_blocker(p.solve_with_downloads()) old_stdout = sys.stdout try: sys.stdout = StringIO() run.execute_selections(p.solver.selections, [], dry_run=True, stores=self.config.stores) out = sys.stdout.getvalue() finally: sys.stdout = old_stdout assert 'runner-arg' in out, out
def testReplacedConflicts(self): self.import_feed('http://localhost:8000/Hello', 'Hello') s = solver.DefaultSolver(self.config) replaced_path = model.canonical_iface_uri(os.path.join(mydir, 'Replaced.xml')) replaced_conflicts_path = model.canonical_iface_uri(os.path.join(mydir, 'ReplacedConflicts.xml')) r = Requirements(replaced_conflicts_path) s.solve_for(r) assert s.ready, s.get_failure_reason() assert s.selections self.assertEqual("b", s.selections.selections[replaced_conflicts_path].id) self.assertEqual("2", s.selections.selections[replaced_conflicts_path].version) self.assertEqual("sha1=3ce644dc725f1d21cfcf02562c76f375944b266a", s.selections.selections["http://localhost:8000/Hello"].id) self.assertEqual(2, len(s.selections.selections)) r.extra_restrictions[r.interface_uri] = '..!2' s.extra_restrictions = r.get_extra_restrictions(self.config.iface_cache) s.solve_for(r) assert s.ready, s.get_failure_reason() assert s.selections self.assertEqual("1", s.selections.selections[replaced_conflicts_path].version) self.assertEqual("0", s.selections.selections[replaced_path].version) self.assertEqual(2, len(s.selections.selections))
def testArgList(self): d = Driver(requirements=Requirements(arglist), config=self.config) self.config.handler.wait_for_blocker(d.solve_with_downloads()) old_stdout = sys.stdout try: sys.stdout = StringIO() run.execute_selections(d.solver.selections, [], dry_run=True, stores=self.config.stores) out = sys.stdout.getvalue() finally: sys.stdout = old_stdout assert 'arg-for-runner -X ra1 -X ra2' in out, out assert 'command-arg ca1 ca2' in out, out
def testNoArchives(self): self.cache_iface(foo_iface_uri, """<?xml version="1.0" ?> <interface last-modified="1110752708" uri="%s" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Foo</name> <summary>Foo</summary> <description>Foo</description> <implementation id='sha1=123' version='1.0' main='dummy'/> </interface>""" % foo_iface_uri) driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config) recalculate(driver) assert not driver.solver.ready
def testAutopackage(self): old_out = sys.stdout try: sys.stdout = StringIO() run_server('HelloWorld.autopackage') driver = Driver(requirements = Requirements(os.path.abspath('Autopackage.xml')), config = self.config) try: download_and_execute(driver, []) assert False except model.SafeException as ex: if "HelloWorld/Missing" not in str(ex): raise finally: sys.stdout = old_out
def testRecipeFailure(self): with resourcewarnings_suppressed(): old_out = sys.stdout try: run_server('*') driver = Driver(requirements = Requirements(os.path.abspath('Recipe.xml')), config = self.config) try: download_and_execute(driver, []) assert False except download.DownloadError as ex: if "Connection" not in str(ex): raise finally: sys.stdout = old_out
def testSelections(self): requirements = Requirements('http://foo/Source.xml') requirements.source = True requirements.command = 'compile' driver = Driver(requirements = requirements, config = self.config) source = self.config.iface_cache.get_interface('http://foo/Source.xml') compiler = self.config.iface_cache.get_interface('http://foo/Compiler.xml') self.import_feed(source.uri, 'Source.xml') self.import_feed(compiler.uri, 'Compiler.xml') self.config.network_use = model.network_full #import logging #logging.getLogger().setLevel(logging.DEBUG) assert driver.need_download() def assertSel(s): self.assertEqual('http://foo/Source.xml', s.interface) self.assertEqual(2, len(s.selections)) sels = [(sel.interface, sel) for sel in s.selections.values()] sels.sort() sels = [sel for uri,sel in sels] self.assertEqual('http://foo/Compiler.xml', sels[0].interface) self.assertEqual('http://foo/Source.xml', sels[1].interface) self.assertEqual("sha1=345", sels[0].id) self.assertEqual("1.0", sels[0].version) self.assertEqual('sha1=234', sels[1].id) self.assertEqual("1.0", sels[1].version) self.assertEqual("bar", sels[1].attrs['http://namespace foo']) self.assertEqual("1.0", sels[1].attrs['version']) assert 'version-modifier' not in sels[1].attrs self.assertEqual(0, len(sels[0].bindings)) self.assertEqual(0, len(sels[0].dependencies)) self.assertEqual(3, len(sels[1].bindings)) self.assertEqual('.', sels[1].bindings[0].insert) self.assertEqual('/', sels[1].bindings[1].mount_point) self.assertEqual('source', sels[1].bindings[2].qdom.attrs['foo']) self.assertEqual(1, len(sels[1].dependencies)) dep = sels[1].dependencies[0] self.assertEqual('http://foo/Compiler.xml', dep.interface) self.assertEqual(4, len(dep.bindings)) self.assertEqual('bin', dep.bindings[0].insert) self.assertEqual('PATH', dep.bindings[0].name) self.assertEqual('prepend', dep.bindings[0].mode) assert dep.bindings[0].separator in ';:' self.assertEqual('bin', dep.bindings[1].value) self.assertEqual('NO_PATH', dep.bindings[1].name) self.assertEqual(',', dep.bindings[1].separator) self.assertEqual('bin', dep.bindings[2].insert) self.assertEqual('BINDIR', dep.bindings[2].name) self.assertEqual('replace', dep.bindings[2].mode) foo_binding = dep.bindings[3] self.assertEqual('compiler', foo_binding.qdom.attrs['foo']) self.assertEqual('child', foo_binding.qdom.childNodes[0].name) self.assertEqual('run', foo_binding.command) self.assertEqual(["sha1=345", 'sha256new_345'], sorted(sels[0].digests)) assert driver.solver.ready, driver.solver.get_failure_reason() s1 = driver.solver.selections s1.selections['http://foo/Source.xml'].attrs['http://namespace foo'] = 'bar' assertSel(s1) xml = s1.toDOM().toxml("utf-8") root = qdom.parse(BytesIO(xml)) self.assertEqual(namespaces.XMLNS_IFACE, root.uri) s2 = selections.Selections(root) assertSel(s2)
def testLocalArchive(self): local_iface = os.path.join(mydir, 'LocalArchive.xml') with open(local_iface, 'rb') as stream: root = qdom.parse(stream) # Not local => error feed = model.ZeroInstallFeed(root) impl = feed.implementations['impl1'] blocker = self.config.fetcher.download_impls([impl], self.config.stores) try: tasks.wait_for_blocker(blocker) assert 0 except model.SafeException as ex: assert "Relative URL 'HelloWorld.tgz' in non-local feed" in str(ex), ex feed = model.ZeroInstallFeed(root, local_path = local_iface) # Missing file impl2 = feed.implementations['impl2'] blocker = self.config.fetcher.download_impls([impl2], self.config.stores) try: tasks.wait_for_blocker(blocker) assert 0 except model.SafeException as ex: assert 'tests/IDONTEXIST.tgz' in str(ex), ex # Wrong size impl3 = feed.implementations['impl3'] blocker = self.config.fetcher.download_impls([impl3], self.config.stores) try: tasks.wait_for_blocker(blocker) assert 0 except model.SafeException as ex: assert 'feed says 177, but actually 176 bytes' in str(ex), ex self.config.network_use = model.network_offline r = Requirements(local_iface) r.command = None driver = Driver(requirements = r, config = self.config) driver.need_download() assert driver.solver.ready, driver.solver.get_failure_reason() # Local => OK impl = feed.implementations['impl1'] path = self.config.stores.lookup_maybe(impl.digests) assert not path blocker = self.config.fetcher.download_impls([impl], self.config.stores) tasks.wait_for_blocker(blocker) path = self.config.stores.lookup_any(impl.digests) assert os.path.exists(os.path.join(path, 'HelloWorld')) # Local <file> => OK impl = feed.implementations['impl4'] path = self.config.stores.lookup_maybe(impl.digests) assert not path blocker = self.config.fetcher.download_impls([impl], self.config.stores) tasks.wait_for_blocker(blocker) path = self.config.stores.lookup_any(impl.digests) assert os.path.exists(os.path.join(path, 'archive.tgz'))
refresh = True mydir = os.path.dirname(os.path.abspath(__file__)) site_config = os.path.join(mydir, 'site-config.xml') if not os.path.exists(site_config): print "Copy site-config.xml.template as site-config.xml and edit..." sys.exit(1) drupal = "http://repo.roscidus.com/drupal/core" config = load_config() import local_config local_config.apply_local_config(config) requirements = Requirements(site_config) requirements.command = None driver = Driver(config, requirements) tasks.wait_for_blocker(driver.solve_and_download_impls(refresh = refresh)) selections = driver.solver.selections.selections drupal_impl = selections[drupal] drupal_root = drupal_impl.local_path or config.stores.lookup_any(drupal_impl.digests) config_impl = selections[site_config] site_config_settings = {} for binding in config_impl.bindings: site_config_settings[binding.name] = binding.value doc_root = site_config_settings["doc_root"]
def testBackgroundUnsolvable(self): my_dbus.system_services = {"org.freedesktop.NetworkManager": {"/org/freedesktop/NetworkManager": NetworkManager()}} trust.trust_db.trust_key('DE937DD411906ACF7C263B396FCF121BE2390E0B', 'example.com:8000') global ran_gui # Select a version of Hello run_server('Hello.xml', '6FCF121BE2390E0B.gpg', 'HelloWorld.tgz') r = Requirements('http://example.com:8000/Hello.xml') driver = Driver(requirements = r, config = self.config) tasks.wait_for_blocker(driver.solve_with_downloads()) assert driver.solver.ready kill_server_process() # Save it as an app app = self.config.app_mgr.create_app('test-app', r) # Replace the selection with a bogus and unusable <package-implementation> sels = driver.solver.selections sel, = sels.selections.values() sel.attrs['id'] = "package:dummy:badpackage" sel.attrs['package'] = "badpackage" sel.get_command('run').qdom.attrs['path'] = '/i/dont/exist' app.set_selections(driver.solver.selections) # Not time for a background update yet, but the missing binary should trigger # an update anyway. self.config.freshness = 0 # Check we try to launch the GUI... os.environ['DISPLAY'] = 'dummy' try: app.get_selections(may_update = True) assert 0 except model.SafeException as ex: assert 'Aborted by user' in str(ex) assert ran_gui ran_gui = False # Check we can also work without the GUI... del os.environ['DISPLAY'] run_server('Hello.xml', 'HelloWorld.tgz') sels = app.get_selections(may_update = True) kill_server_process() dl = app.download_selections(sels) assert dl == None assert not ran_gui # Now trigger a background update which discovers that no solution is possible timestamp = os.path.join(app.path, 'last-checked') last_check_attempt = os.path.join(app.path, 'last-check-attempt') selections_path = os.path.join(app.path, 'selections.xml') def reset_timestamps(): global ran_gui ran_gui = False os.utime(timestamp, (1, 1)) # 1970 os.utime(selections_path, (1, 1)) if os.path.exists(last_check_attempt): os.unlink(last_check_attempt) reset_timestamps() r.source = True app.set_requirements(r) run_server('Hello.xml') with trapped_exit(1): sels = app.get_selections(may_update = True) kill_server_process()
def testDiagnostics(self): top_uri = 'http://localhost/top.xml' old_uri = 'http://localhost/diagnostics-old.xml' diag_uri = 'http://localhost/diagnostics.xml' def test(top_xml, diag_xml, expected_error): root = qdom.parse(BytesIO("""<?xml version="1.0" ?> <interface xmlns="http://zero-install.sourceforge.net/2004/injector/interface" uri="{top}"> <name>Top-level</name> <summary>Top-level</summary> <group> {top_xml} </group> </interface>""".format(top = top_uri, top_xml = top_xml).encode("utf-8"))) self.import_feed(top_uri, root) root = qdom.parse(BytesIO("""<?xml version="1.0" ?> <interface xmlns="http://zero-install.sourceforge.net/2004/injector/interface" uri="{diag}"> <name>Diagnostics</name> <summary>Diagnostics</summary> <group> {impls} </group> </interface>""".format(diag = diag_uri, impls = diag_xml).encode("utf-8"))) self.import_feed(diag_uri, root) root = qdom.parse(BytesIO("""<?xml version="1.0" ?> <interface xmlns="http://zero-install.sourceforge.net/2004/injector/interface" uri="{old}"> <name>Old</name> <summary>Old</summary> <feed src='{diag}'/> <replaced-by interface='{diag}'/> </interface>""".format(diag = diag_uri, old = old_uri).encode("utf-8"))) self.import_feed(old_uri, root) r = Requirements(top_uri) r.os = "Windows" r.cpu = "x86_64" s = solver.DefaultSolver(self.config) s.solve_for(r) assert not s.ready, s.selections.selections self.assertEqual(expected_error, str(s.get_failure_reason())) return s s = test("", "", "Can't find all required implementations:\n" + "- http://localhost/top.xml -> (problem)\n" + " No known implementations at all") s = test("<implementation version='1' id='1'><requires interface='{diag}'/></implementation>".format(diag = diag_uri), "", "Can't find all required implementations:\n" + "- http://localhost/top.xml -> (problem)\n" + " No usable implementations:\n" + " 1: No retrieval methods") s = test("""<implementation version='1' id='1'> <archive href='http://localhost:3000/foo.tgz' size='100'/> <requires interface='{diag}'> <version not-before='100'/> </requires> </implementation>""".format(diag = diag_uri), "", "Can't find all required implementations:\n" + "- http://localhost/top.xml -> (problem)\n" + " Rejected candidates:\n" + " 1: No run command") s = test("""<implementation version='1' id='1' main='foo'> <archive href='http://localhost:3000/foo.tgz' size='100'/> <requires interface='{diag}' version='100..!200'/> </implementation>""".format(diag = diag_uri), """<implementation version='5' id='diag-5'> <archive href='http://localhost:3000/diag.tgz' size='100'/> </implementation> """, "Can't find all required implementations:\n" "- http://localhost/diagnostics.xml -> (problem)\n" " http://localhost/top.xml 1 requires version 100..!200\n" " No usable implementations satisfy the restrictions\n" "- http://localhost/top.xml -> 1 (1)") logger.setLevel(logging.ERROR) s = test("""<implementation version='1' id='1' main='foo'> <archive href='http://localhost:3000/foo.tgz' size='100'/> <requires interface='{diag}' version='100..200'/> </implementation>""".format(diag = diag_uri), """<implementation version='5' id='diag-5'> <archive href='http://localhost:3000/diag.tgz' size='100'/> </implementation> """, "Can't find all required implementations:\n" "- http://localhost/diagnostics.xml -> (problem)\n" " http://localhost/top.xml 1 requires <impossible: Can't parse version restriction '100..200': End of range must be exclusive (use '..!200', not '..200')>\n" " No usable implementations satisfy the restrictions\n" "- http://localhost/top.xml -> 1 (1)") logger.setLevel(logging.WARNING) s = test("""<implementation version='1' id='1' main='foo'> <archive href='http://localhost:3000/foo.tgz' size='100'/> <requires interface='{diag}'> <version not-before='100'/> </requires> </implementation>""".format(diag = diag_uri), """<implementation version='5' id='diag-5'> <archive href='http://localhost:3000/diag.tgz' size='100'/> </implementation> """, "Can't find all required implementations:\n" "- http://localhost/diagnostics.xml -> (problem)\n" " http://localhost/top.xml 1 requires 100 <= version\n" " No usable implementations satisfy the restrictions\n" "- http://localhost/top.xml -> 1 (1)") s = test("""<group> <requires interface='{diag}'/> <implementation version='1' id='1' main='foo' arch='Windows-i486'> <archive href='http://localhost:3000/foo.tgz' size='100'/> </implementation> </group>""".format(diag = diag_uri), """<group> <implementation version='5' id='diag-5' arch='Windows-x86_64'> <archive href='http://localhost:3000/diag.tgz' size='100'/> </implementation> </group> """, "Can't find all required implementations:\n" "- http://localhost/diagnostics.xml -> (problem)\n" " Rejected candidates:\n" " diag-5: Can't use x86_64 with selection of Top-level (i486)\n" "- http://localhost/top.xml -> 1 (1)") s = test("""<group> <requires interface='{diag}'/> <implementation version='1' id='1' main='foo'> <archive href='http://localhost:3000/foo.tgz' size='100'/> </implementation> </group>""".format(diag = diag_uri), """<group> <implementation version='1' id='diag-1'/> <implementation version='2' id='diag-2'/> <implementation version='3' id='diag-3'/> <implementation version='4' id='diag-4'/> <implementation version='5' id='diag-5'/> <implementation version='6' id='diag-6'/> </group> """, "Can't find all required implementations:\n" "- http://localhost/diagnostics.xml -> (problem)\n" " No usable implementations:\n" " diag-6: No retrieval methods\n" " diag-5: No retrieval methods\n" " diag-4: No retrieval methods\n" " diag-3: No retrieval methods\n" " diag-2: No retrieval methods\n" " ...\n" "- http://localhost/top.xml -> 1 (1)") s = test("""<group> <requires interface='{diag}'> <version before='6'/> </requires> <implementation version='1' id='1' main='foo' arch='Windows-i486'> <archive href='http://localhost:3000/foo.tgz' size='100'/> </implementation> </group>""".format(diag = diag_uri), """<group> <implementation version='5' id='diag-5' arch='Windows-x86_64'> <archive href='http://localhost:3000/diag.tgz' size='100'/> </implementation> <implementation version='6' id='diag-6' arch='Windows-i486'> <archive href='http://localhost:3000/diag.tgz' size='100'/> </implementation> {others} </group> """.format(others = "\n".join( """<implementation version='{i}' id='diag-{i}' arch='Windows-x86_64'> <archive href='http://localhost:3000/diag.tgz' size='100'/> </implementation>""".format(i = i) for i in range(0, 5))), "Can't find all required implementations:\n" "- http://localhost/diagnostics.xml -> (problem)\n" " http://localhost/top.xml 1 requires version < 6\n" " Rejected candidates:\n" " diag-5: Can't use x86_64 with selection of Top-level (i486)\n" " diag-4: Can't use x86_64 with selection of Top-level (i486)\n" " diag-3: Can't use x86_64 with selection of Top-level (i486)\n" " diag-2: Can't use x86_64 with selection of Top-level (i486)\n" " diag-1: Can't use x86_64 with selection of Top-level (i486)\n" " ...\n" "- http://localhost/top.xml -> 1 (1)") iface = self.config.iface_cache.get_interface(diag_uri) impl = self.config.iface_cache.get_feed(diag_uri).implementations['diag-5'] r = Requirements(top_uri) r.os = 'Windows' self.assertEqual("There is no possible selection using Diagnostics 5.\n" "Can't find all required implementations:\n" "- http://localhost/diagnostics.xml -> (problem)\n" " http://localhost/top.xml 1 requires version < 6\n" " User requested implementation 5 (diag-5)\n" " Rejected candidates:\n" " diag-5: Can't use x86_64 with selection of Top-level (i486)\n" "- http://localhost/top.xml -> 1 (1)", s.justify_decision(r, iface, impl)) # Can't select old and diag because they conflict test("""<group> <requires interface='{diag}'/> <requires interface='{old}'/> <implementation version='1' id='1' main='foo'> <archive href='http://localhost:3000/foo.tgz' size='100'/> </implementation> </group>""".format(diag = diag_uri, old = old_uri), """<group> <implementation version='5' id='diag-5'> <archive href='http://localhost:3000/diag.tgz' size='100'/> </implementation> </group> """, "Can't find all required implementations:\n" "- http://localhost/diagnostics-old.xml -> (problem)\n" " Replaced by (and therefore conflicts with) http://localhost/diagnostics.xml\n" " No usable implementations satisfy the restrictions\n" "- http://localhost/diagnostics.xml -> 5 (diag-5)\n" " Replaces (and therefore conflicts with) http://localhost/diagnostics-old.xml\n" "- http://localhost/top.xml -> 1 (1)")