Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@sugarlabs.org>2013-08-05 14:14:55 (GMT)
committer Aleksey Lim <alsroot@sugarlabs.org>2013-08-05 14:14:55 (GMT)
commitab7f05de4862f2eec7b77001b34f5fb78d371298 (patch)
tree0626a8e98410d7519197a254824dd8943ddba61d
parent3f4d54c5bbd6acb49ed5b46f95db632be0c73fb7 (diff)
Invalidate cached solutions after changing stability preferences
-rw-r--r--TODO9
-rw-r--r--sugar_network/client/injector.py23
-rw-r--r--sugar_network/client/solver.py35
-rw-r--r--tests/__init__.py7
-rwxr-xr-xtests/units/client/injector.py151
-rwxr-xr-xtests/units/client/solver.py79
6 files changed, 161 insertions, 143 deletions
diff --git a/TODO b/TODO
index 3e87366..407cd85 100644
--- a/TODO
+++ b/TODO
@@ -1,13 +1,9 @@
-- invalidate solutions cache on config changes (layers ans stabilities might be changed)
-- delete outdated impls on PUTing new one
-
- (!) Editors' workflows:
- (?) log all (including editros) posters of particular document to minimize conflicts about why somthing was changed
or better, detailed log for every editor's change
- Remove temporal security hole with speciying guid in POST,
it was added as a fast hack to support offline creation (with later pushing to a node)
-- GC implementations cache
- get all localized strings from activity.info while populating local contexts
- activities migth need MIME registering while checking-in
- changed pulls should take into account accept_length
@@ -17,9 +13,4 @@
- increase granularity for sync.chunked_encode()
- slave._Pooler might leak events if pullers are not in time to call wait()
- revert per-document "downloads" property as "launches", a part of unpersonizalied user_stats
-- more useful offline workflow
- - keep offline Contexts synchronized with online source
- - (?) not only new content in offline mode
-- handle proxy prpos for prop= requests
- reuse "layer" for home volume instead of "clone" and "favorite"
-- local preferences regarding feeds
diff --git a/sugar_network/client/injector.py b/sugar_network/client/injector.py
index cdcdc5e..de1c6d1 100644
--- a/sugar_network/client/injector.py
+++ b/sugar_network/client/injector.py
@@ -145,7 +145,7 @@ def _clone(context):
shutil.rmtree(cloned.pop(), ignore_errors=True)
raise
- _set_cached_solution(context, solution)
+ _set_cached_solution(context, None, solution)
def _clone_impl(context_guid, params):
@@ -163,7 +163,7 @@ def _clone_impl(context_guid, params):
_logger.info('Clone implementation to %r', dst_path)
toolkit.cptree(src_path, dst_path)
- _set_cached_solution(context_guid, [{
+ _set_cached_solution(context_guid, None, [{
'id': dst_path,
'context': context_guid,
'version': impl['version'],
@@ -177,8 +177,9 @@ def _clone_impl(context_guid, params):
def _solve(context):
pipe.trace('Start solving %s feed', context)
+ stability = client.stability(context)
- solution, stale = _get_cached_solution(context)
+ solution, stale = _get_cached_solution(context, stability)
if stale is False:
pipe.trace('Reuse cached solution')
return solution
@@ -190,8 +191,8 @@ def _solve(context):
from sugar_network.client import solver
- solution = solver.solve(conn, context)
- _set_cached_solution(context, solution)
+ solution = solver.solve(conn, context, stability)
+ _set_cached_solution(context, stability, solution)
return solution
@@ -225,19 +226,21 @@ def _cached_solution_path(guid):
return client.path('cache', 'solutions', guid[:2], guid)
-def _get_cached_solution(guid):
+def _get_cached_solution(guid, stability):
path = _cached_solution_path(guid)
solution = None
if exists(path):
try:
with file(path) as f:
- api_url, solution = json.load(f)
+ cached_api_url, cached_stability, solution = json.load(f)
except Exception, error:
_logger.debug('Cannot open %r solution: %s', path, error)
if solution is None:
return None, None
- stale = (api_url != client.api_url.value)
+ stale = (cached_api_url != client.api_url.value)
+ if not stale and cached_stability is not None:
+ stale = set(cached_stability) != set(stability)
if not stale and _mtime is not None:
stale = (_mtime > os.stat(path).st_mtime)
if not stale and _pms_path is not None:
@@ -256,9 +259,9 @@ def _get_cached_solution(guid):
return solution, stale
-def _set_cached_solution(guid, solution):
+def _set_cached_solution(guid, stability, solution):
path = _cached_solution_path(guid)
if not exists(dirname(path)):
os.makedirs(dirname(path))
with file(path, 'w') as f:
- json.dump([client.api_url.value, solution], f)
+ json.dump([client.api_url.value, stability, solution], f)
diff --git a/sugar_network/client/solver.py b/sugar_network/client/solver.py
index 3d18cef..584d0c5 100644
--- a/sugar_network/client/solver.py
+++ b/sugar_network/client/solver.py
@@ -19,7 +19,6 @@ import sys
import logging
from os.path import isabs, join, dirname
-from sugar_network import client
from sugar_network.client import packagekit, SUGAR_API_COMPATIBILITY
from sugar_network.toolkit.spec import parse_version
from sugar_network.toolkit import http, lsb_release, pipe, exception
@@ -34,16 +33,14 @@ from zeroinstall.injector.arch import machine_ranks
from zeroinstall.injector.distro import try_cleanup_distro_version
-def _interface_init(self, url):
- self.uri = url
- self.reset()
-
-
-model.Interface.__init__ = _interface_init
-reader.check_readable = lambda * args, ** kwargs: True
-reader.update_from_cache = lambda * args, ** kwargs: None
+model.Interface.__init__ = lambda *args: _interface_init(*args)
+reader.check_readable = lambda *args, **kwargs: True
+reader.update_from_cache = lambda *args, **kwargs: None
+reader.load_feed_from_cache = lambda url, **kwargs: _load_feed(url)
_logger = logging.getLogger('zeroinstall')
+_stability = None
+_conn = None
def canonicalize_machine(arch):
@@ -70,9 +67,11 @@ def select_architecture(arches):
return result_arch
-def solve(conn, context):
- reader.load_feed_from_cache = lambda url, *args, **kwargs: \
- _load_feed(conn, url)
+def solve(conn, context, stability):
+ global _conn, _stability
+
+ _conn = conn
+ _stability = stability
req = Requirements(context)
# TODO
@@ -156,6 +155,11 @@ def solve(conn, context):
return solution
+def _interface_init(self, url):
+ self.uri = url
+ self.reset()
+
+
def _impl_new(config, iface, sel):
feed = config.iface_cache.get_feed(iface)
impl = {'id': sel.id,
@@ -186,7 +190,7 @@ def _impl_new(config, iface, sel):
return impl
-def _load_feed(conn, context):
+def _load_feed(context):
feed = _Feed(context)
if context == 'sugar':
@@ -203,9 +207,8 @@ def _load_feed(conn, context):
feed_content = None
try:
- feed_content = conn.get(['context', context], cmd='feed',
- stability=client.stability(context),
- distro=lsb_release.distributor_id())
+ feed_content = _conn.get(['context', context], cmd='feed',
+ stability=_stability, distro=lsb_release.distributor_id())
pipe.trace('Found %s feed: %r', context, feed_content)
except http.ServiceUnavailable:
pipe.trace('Failed to fetch %s feed', context)
diff --git a/tests/__init__.py b/tests/__init__.py
index c4d494d..fcd90a6 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -74,11 +74,10 @@ class Test(unittest.TestCase):
shutil.copy(join(root, 'data', 'owner.key.pub'), profile_dir)
adapters.DEFAULT_RETRIES = 5
- Option.unsorted_items = []
Option.items = {}
- Option.sections = {}
Option.config_files = []
- Option._config = None
+ Option.config = None
+ Option._parser = None
Option._config_to_save = None
db.index_flush_timeout.value = 0
db.index_flush_threshold.value = 1
@@ -113,6 +112,8 @@ class Test(unittest.TestCase):
injector._pms_path = None
journal._ds_root = tmpdir + '/datastore'
solver.nodeps = False
+ solver._stability = None
+ solver._conn = None
downloads._POOL_SIZE = 256
pipe._pipe = None
pipe._trace = None
diff --git a/tests/units/client/injector.py b/tests/units/client/injector.py
index 1010343..7a17748 100755
--- a/tests/units/client/injector.py
+++ b/tests/units/client/injector.py
@@ -21,6 +21,7 @@ from sugar_network.model.user import User
from sugar_network.model.context import Context
from sugar_network.model.implementation import Implementation
from sugar_network.client import IPCConnection, packagekit, injector, clones, solver
+from sugar_network.toolkit import Option
from sugar_network import client
@@ -153,7 +154,7 @@ Can't find all required implementations:
[i for i in pipe])
assert exists('cache/implementation/%s' % impl)
assert exists('Activities/topdir/probe')
- __, (solution,) = json.load(file('cache/solutions/%s/%s' % (context[:2], context)))
+ __, __, (solution,) = json.load(file('cache/solutions/%s/%s' % (context[:2], context)))
self.assertEqual(tests.tmpdir + '/Activities/topdir', solution['path'])
self.assertEqual('probe', file('Activities/topdir/probe').read())
@@ -193,7 +194,7 @@ Can't find all required implementations:
for event in injector.clone(context):
pass
self.assertEqual('exit', event['state'])
- __, (solution,) = json.load(file('cache/solutions/%s/%s' % (context[:2], context)))
+ __, __, (solution,) = json.load(file('cache/solutions/%s/%s' % (context[:2], context)))
self.assertEqual(tests.tmpdir + '/Activities/topdir', solution['path'])
def test_launch_Online(self):
@@ -419,7 +420,7 @@ Can't find all required implementations:
self.override(solver, 'solve', lambda *args: solution)
self.assertEqual(solution, injector._solve('context'))
- self.assertEqual([client.api_url.value, solution], json.load(file('cache/solutions/co/context')))
+ self.assertEqual([client.api_url.value, ['stable'], solution], json.load(file('cache/solutions/co/context')))
def test_SolutionsCache_InvalidateByAPIUrl(self):
solution = [{'name': 'name', 'context': 'context', 'id': 'id', 'version': 'version'}]
@@ -428,13 +429,13 @@ Can't find all required implementations:
cached_path = 'cache/solutions/co/context'
solution2 = [{'name': 'name2', 'context': 'context2', 'id': 'id2', 'version': 'version2'}]
- self.touch((cached_path, json.dumps([client.api_url.value, solution2])))
+ self.touch((cached_path, json.dumps([client.api_url.value, ['stable'], solution2])))
self.assertEqual(solution2, injector._solve('context'))
- self.assertEqual([client.api_url.value, solution2], json.load(file(cached_path)))
+ self.assertEqual([client.api_url.value, ['stable'], solution2], json.load(file(cached_path)))
client.api_url.value = 'fake'
self.assertEqual(solution, injector._solve('context'))
- self.assertEqual(['fake', solution], json.load(file(cached_path)))
+ self.assertEqual(['fake', ['stable'], solution], json.load(file(cached_path)))
def test_SolutionsCache_InvalidateByMtime(self):
solution = [{'name': 'name', 'context': 'context', 'id': 'id', 'version': 'version'}]
@@ -444,18 +445,18 @@ Can't find all required implementations:
solution2 = [{'name': 'name2', 'context': 'context2', 'id': 'id2', 'version': 'version2'}]
injector.invalidate_solutions(1)
- self.touch((cached_path, json.dumps([client.api_url.value, solution2])))
+ self.touch((cached_path, json.dumps([client.api_url.value, ['stable'], solution2])))
os.utime(cached_path, (1, 1))
self.assertEqual(solution2, injector._solve('context'))
- self.assertEqual([client.api_url.value, solution2], json.load(file(cached_path)))
+ self.assertEqual([client.api_url.value, ['stable'], solution2], json.load(file(cached_path)))
os.utime(cached_path, (2, 2))
self.assertEqual(solution2, injector._solve('context'))
- self.assertEqual([client.api_url.value, solution2], json.load(file(cached_path)))
+ self.assertEqual([client.api_url.value, ['stable'], solution2], json.load(file(cached_path)))
injector.invalidate_solutions(3)
self.assertEqual(solution, injector._solve('context'))
- self.assertEqual([client.api_url.value, solution], json.load(file(cached_path)))
+ self.assertEqual([client.api_url.value, ['stable'], solution], json.load(file(cached_path)))
def test_SolutionsCache_InvalidateByPMSMtime(self):
solution = [{'name': 'name', 'context': 'context', 'id': 'id', 'version': 'version'}]
@@ -467,18 +468,18 @@ Can't find all required implementations:
self.touch('pms')
os.utime('pms', (1, 1))
solution2 = [{'name': 'name2', 'context': 'context2', 'id': 'id2', 'version': 'version2'}]
- self.touch((cached_path, json.dumps([client.api_url.value, solution2])))
+ self.touch((cached_path, json.dumps([client.api_url.value, ['stable'], solution2])))
os.utime(cached_path, (1, 1))
self.assertEqual(solution2, injector._solve('context'))
- self.assertEqual([client.api_url.value, solution2], json.load(file(cached_path)))
+ self.assertEqual([client.api_url.value, ['stable'], solution2], json.load(file(cached_path)))
os.utime(cached_path, (2, 2))
self.assertEqual(solution2, injector._solve('context'))
- self.assertEqual([client.api_url.value, solution2], json.load(file(cached_path)))
+ self.assertEqual([client.api_url.value, ['stable'], solution2], json.load(file(cached_path)))
os.utime('pms', (3, 3))
self.assertEqual(solution, injector._solve('context'))
- self.assertEqual([client.api_url.value, solution], json.load(file(cached_path)))
+ self.assertEqual([client.api_url.value, ['stable'], solution], json.load(file(cached_path)))
def test_SolutionsCache_DeliberateReuseInOffline(self):
solution1 = [{'name': 'name', 'context': 'context', 'id': 'id', 'version': 'version'}]
@@ -487,13 +488,13 @@ Can't find all required implementations:
cached_path = 'cache/solutions/co/context'
self.override(client, 'IPCConnection', lambda: _FakeConnection(True))
- self.touch((cached_path, json.dumps([client.api_url.value, solution2])))
+ self.touch((cached_path, json.dumps([client.api_url.value, ['stable'], solution2])))
os.utime(cached_path, (1, 1))
injector.invalidate_solutions(2)
self.assertEqual(solution1, injector._solve('context'))
self.override(client, 'IPCConnection', lambda: _FakeConnection(False))
- self.touch((cached_path, json.dumps([client.api_url.value, solution2])))
+ self.touch((cached_path, json.dumps([client.api_url.value, ['stable'], solution2])))
os.utime(cached_path, (1, 1))
injector.invalidate_solutions(2)
self.assertEqual(solution2, injector._solve('context'))
@@ -508,18 +509,18 @@ Can't find all required implementations:
solution2 = [{'spec': 'spec', 'name': 'name2', 'context': 'context2', 'id': 'id2', 'version': 'version2'}]
self.touch('spec')
os.utime('spec', (1, 1))
- self.touch((cached_path, json.dumps([client.api_url.value, solution2])))
+ self.touch((cached_path, json.dumps([client.api_url.value, ['stable'], solution2])))
os.utime(cached_path, (1, 1))
self.assertEqual(solution2, injector._solve('context'))
- self.assertEqual([client.api_url.value, solution2], json.load(file(cached_path)))
+ self.assertEqual([client.api_url.value, ['stable'], solution2], json.load(file(cached_path)))
os.utime(cached_path, (2, 2))
self.assertEqual(solution2, injector._solve('context'))
- self.assertEqual([client.api_url.value, solution2], json.load(file(cached_path)))
+ self.assertEqual([client.api_url.value, ['stable'], solution2], json.load(file(cached_path)))
os.utime('spec', (3, 3))
self.assertEqual(solution, injector._solve('context'))
- self.assertEqual([client.api_url.value, solution], json.load(file(cached_path)))
+ self.assertEqual([client.api_url.value, ['stable'], solution], json.load(file(cached_path)))
def test_clone_SetExecPermissionsForActivities(self):
self.start_online_client([User, Context, Implementation])
@@ -711,7 +712,7 @@ Can't find all required implementations:
{'version': '1', 'id': 'dep3', 'context': 'dep3', 'name': 'title3', 'stability': 'packaged'},
{'name': 'title', 'version': '1', 'command': ['echo'], 'context': context, 'id': impl, 'stability': 'stable'},
]),
- sorted(solver.solve(conn, context)))
+ sorted(solver.solve(conn, context, ['stable'])))
def test_LoadFeed_SetPackages(self):
self.start_online_client([User, Context, Implementation])
@@ -756,7 +757,7 @@ Can't find all required implementations:
return dict([(i, {'name': i, 'pk_id': i, 'version': '1', 'arch': '*', 'installed': True}) for i in names])
self.override(packagekit, 'resolve', resolve)
- self.assertRaises(RuntimeError, solver.solve, conn, context)
+ self.assertRaises(RuntimeError, solver.solve, conn, context, ['stable'])
conn.put(['context', 'dep', 'aliases'], {
lsb_release.distributor_id(): {
@@ -764,7 +765,7 @@ Can't find all required implementations:
'binary': [['bin']],
},
})
- self.assertEqual('dep', solver.solve(conn, context)[-1]['context'])
+ self.assertEqual('dep', solver.solve(conn, context, ['stable'])[-1]['context'])
conn.put(['context', 'dep', 'aliases'], {
'foo': {
@@ -772,14 +773,14 @@ Can't find all required implementations:
'binary': [['bin']],
},
})
- self.assertRaises(RuntimeError, solver.solve, conn, context)
+ self.assertRaises(RuntimeError, solver.solve, conn, context, ['stable'])
conn.put(['context', 'dep', 'aliases'], {
lsb_release.distributor_id(): {
'binary': [['bin']],
},
})
- self.assertEqual('dep', solver.solve(conn, context)[-1]['context'])
+ self.assertEqual('dep', solver.solve(conn, context, ['stable'])[-1]['context'])
def test_SolveSugar(self):
self.touch(('__init__.py', ''))
@@ -829,7 +830,7 @@ Can't find all required implementations:
{'name': 'title', 'version': '1', 'command': ['echo'], 'context': context, 'id': impl, 'stability': 'stable'},
{'name': 'sugar', 'version': '0.94', 'context': 'sugar', 'path': '/', 'id': 'sugar-0.94', 'stability': 'packaged'},
],
- solver.solve(conn, context))
+ solver.solve(conn, context, ['stable']))
self.node_volume['implementation'].update(impl, {'data': {
'spec': {
@@ -849,7 +850,7 @@ Can't find all required implementations:
{'name': 'title', 'version': '1', 'command': ['echo'], 'context': context, 'id': impl, 'stability': 'stable'},
{'name': 'sugar', 'version': '0.86', 'context': 'sugar', 'path': '/', 'id': 'sugar-0.86', 'stability': 'packaged'},
],
- solver.solve(conn, context))
+ solver.solve(conn, context, ['stable']))
def test_StripSugarVersion(self):
self.touch(('__init__.py', ''))
@@ -899,7 +900,7 @@ Can't find all required implementations:
{'name': 'title', 'version': '1', 'command': ['echo'], 'context': context, 'id': impl, 'stability': 'stable'},
{'name': 'sugar', 'version': '0.94', 'context': 'sugar', 'path': '/', 'id': 'sugar-0.94', 'stability': 'packaged'},
],
- solver.solve(conn, context))
+ solver.solve(conn, context, ['stable']))
def test_PopupServiceUnavailableInOffline(self):
self.touch(('Activities/Activity/activity/activity.info', [
@@ -926,6 +927,100 @@ Can't find all required implementations:
],
[i for i in injector.make('context')])
+ def test_StabilityPreferences(self):
+ self.start_online_client()
+ ipc = IPCConnection()
+ data = {'spec': {'*-*': {'commands': {'activity': {'exec': 'echo'}}, 'extract': 'topdir'}}}
+
+ context = ipc.post(['context'], {
+ 'type': 'activity',
+ 'title': 'title',
+ 'summary': 'summary',
+ 'description': 'description',
+ })
+ impl1 = ipc.post(['implementation'], {
+ 'context': context,
+ 'license': 'GPLv3+',
+ 'version': '1',
+ 'stability': 'stable',
+ 'notes': '',
+ })
+ self.node_volume['implementation'].update(impl1, {'data': data})
+ impl2 = ipc.post(['implementation'], {
+ 'context': context,
+ 'license': 'GPLv3+',
+ 'version': '2',
+ 'stability': 'testing',
+ 'notes': '',
+ })
+ self.node_volume['implementation'].update(impl2, {'data': data})
+ impl3 = ipc.post(['implementation'], {
+ 'context': context,
+ 'license': 'GPLv3+',
+ 'version': '3',
+ 'stability': 'buggy',
+ 'notes': '',
+ })
+ self.node_volume['implementation'].update(impl3, {'data': data})
+ impl4 = ipc.post(['implementation'], {
+ 'context': context,
+ 'license': 'GPLv3+',
+ 'version': '4',
+ 'stability': 'insecure',
+ 'notes': '',
+ })
+ self.node_volume['implementation'].update(impl4, {'data': data})
+
+ self.assertEqual('1', injector._solve(context)[0]['version'])
+
+ self.touch(('config', [
+ '[stabilities]',
+ '%s = testing' % context,
+ ]))
+ Option.load(['config'])
+ self.assertEqual('2', injector._solve(context)[0]['version'])
+
+ self.touch(('config', [
+ '[stabilities]',
+ '%s = testing buggy' % context,
+ ]))
+ Option.load(['config'])
+ self.assertEqual('3', injector._solve(context)[0]['version'])
+
+ self.touch(('config', [
+ '[stabilities]',
+ 'default = insecure',
+ '%s = stable' % context,
+ ]))
+ Option.load(['config'])
+ self.assertEqual('1', injector._solve(context)[0]['version'])
+
+ self.touch(('config', [
+ '[stabilities]',
+ 'default = insecure',
+ ]))
+ Option.load(['config'])
+ self.assertEqual('4', injector._solve(context)[0]['version'])
+
+ def test_SolutionsCache_InvalidateByStabilityPreferences(self):
+ solution = [{'name': 'name', 'context': 'context', 'id': 'id', 'version': 'version'}]
+ self.override(client, 'IPCConnection', lambda: _FakeConnection(True))
+ self.override(solver, 'solve', lambda *args: solution)
+ cached_path = 'cache/solutions/co/context'
+
+ solution2 = [{'name': 'name2', 'context': 'context2', 'id': 'id2', 'version': 'version2'}]
+ self.touch((cached_path, json.dumps([client.api_url.value, ['stable'], solution2])))
+ self.assertEqual(solution2, injector._solve('context'))
+ self.assertEqual([client.api_url.value, ['stable'], solution2], json.load(file(cached_path)))
+
+ self.touch(('config', [
+ '[stabilities]',
+ 'context = buggy',
+ ]))
+ Option.load(['config'])
+ self.assertEqual(solution, injector._solve('context'))
+ self.assertEqual([client.api_url.value, ['buggy'], solution], json.load(file(cached_path)))
+
class _FakeConnection(object):
diff --git a/tests/units/client/solver.py b/tests/units/client/solver.py
index 84d5456..6e35a50 100755
--- a/tests/units/client/solver.py
+++ b/tests/units/client/solver.py
@@ -6,7 +6,7 @@ import os
from __init__ import tests
from sugar_network.client import IPCConnection, packagekit, solver, clones
-from sugar_network.toolkit import lsb_release, Option
+from sugar_network.toolkit import lsb_release
class SolverTest(tests.Test):
@@ -64,7 +64,7 @@ class SolverTest(tests.Test):
},
})
- solution = solver.solve(ipc, 'bundle_id')
+ solution = solver.solve(ipc, 'bundle_id', ['stable'])
self.assertEqual(
2, len(solution))
self.assertEqual(
@@ -74,81 +74,6 @@ class SolverTest(tests.Test):
('dep', '0'),
(solution[1]['context'], solution[1]['version']))
- def test_StabilityPreferences(self):
- self.start_online_client()
- ipc = IPCConnection()
- data = {'spec': {'*-*': {'commands': {'activity': {'exec': 'echo'}}, 'extract': 'topdir'}}}
-
- context = ipc.post(['context'], {
- 'type': 'activity',
- 'title': 'title',
- 'summary': 'summary',
- 'description': 'description',
- })
- impl1 = ipc.post(['implementation'], {
- 'context': context,
- 'license': 'GPLv3+',
- 'version': '1',
- 'stability': 'stable',
- 'notes': '',
- })
- self.node_volume['implementation'].update(impl1, {'data': data})
- impl2 = ipc.post(['implementation'], {
- 'context': context,
- 'license': 'GPLv3+',
- 'version': '2',
- 'stability': 'testing',
- 'notes': '',
- })
- self.node_volume['implementation'].update(impl2, {'data': data})
- impl3 = ipc.post(['implementation'], {
- 'context': context,
- 'license': 'GPLv3+',
- 'version': '3',
- 'stability': 'buggy',
- 'notes': '',
- })
- self.node_volume['implementation'].update(impl3, {'data': data})
- impl4 = ipc.post(['implementation'], {
- 'context': context,
- 'license': 'GPLv3+',
- 'version': '4',
- 'stability': 'insecure',
- 'notes': '',
- })
- self.node_volume['implementation'].update(impl4, {'data': data})
-
- self.assertEqual('1', solver.solve(ipc, context)[0]['version'])
-
- self.touch(('config', [
- '[stabilities]',
- '%s = testing' % context,
- ]))
- Option.load(['config'])
- self.assertEqual('2', solver.solve(ipc, context)[0]['version'])
-
- self.touch(('config', [
- '[stabilities]',
- '%s = testing buggy' % context,
- ]))
- Option.load(['config'])
- self.assertEqual('3', solver.solve(ipc, context)[0]['version'])
-
- self.touch(('config', [
- '[stabilities]',
- 'default = insecure',
- '%s = stable' % context,
- ]))
- Option.load(['config'])
- self.assertEqual('1', solver.solve(ipc, context)[0]['version'])
-
- self.touch(('config', [
- '[stabilities]',
- 'default = insecure',
- ]))
- Option.load(['config'])
- self.assertEqual('4', solver.solve(ipc, context)[0]['version'])
-
if __name__ == '__main__':
tests.main()