You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bloodhound.apache.org by as...@apache.org on 2013/05/20 13:47:24 UTC
svn commit: r1484438 - in /bloodhound/trunk:
bloodhound_relations/bhrelations/tests/api.py
bloodhound_relations/bhrelations/validation.py installer/bloodhound_setup.py
Author: astaric
Date: Mon May 20 11:47:24 2013
New Revision: 1484438
URL: http://svn.apache.org/r1484438
Log:
Cycle detection for blocker relations. Closes #528.
Modified:
bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py
bloodhound/trunk/bloodhound_relations/bhrelations/validation.py
bloodhound/trunk/installer/bloodhound_setup.py
Modified: bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py
URL: http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py?rev=1484438&r1=1484437&r2=1484438&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py (original)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py Mon May 20 11:47:24 2013
@@ -46,12 +46,13 @@ class BaseApiApiTestCase(MultiproductTes
enable=['trac.*', 'multiproduct.*', 'bhrelations.*']
)
env.config.set('bhrelations', 'global_validators',
- 'NoSelfReferenceValidator,ExclusiveValidator')
+ 'NoSelfReferenceValidator,ExclusiveValidator,'
+ 'BlockerValidator')
config_name = RelationsSystem.RELATIONS_CONFIG_NAME
env.config.set(config_name, 'dependency', 'dependson,dependent')
env.config.set(config_name, 'dependency.validators',
'NoCycles,SingleProduct')
- env.config.set(config_name, 'dependent.blocks', 'true')
+ env.config.set(config_name, 'dependson.blocks', 'true')
env.config.set(config_name, 'parent_children', 'parent,children')
env.config.set(config_name, 'parent_children.validators',
'OneToMany,SingleProduct,NoCycles')
@@ -65,6 +66,8 @@ class BaseApiApiTestCase(MultiproductTes
env.config.set(config_name, 'duplicate.validators', 'ReferencesOlder')
env.config.set(config_name, 'duplicateof.label', 'Duplicate of')
env.config.set(config_name, 'duplicatedby.label', 'Duplicated by')
+ env.config.set(config_name, 'blocker', 'blockedby,blocks')
+ env.config.set(config_name, 'blockedby.blocks', 'true')
self.global_env = env
self._upgrade_mp(self.global_env)
@@ -351,7 +354,7 @@ class ApiTestCase(BaseApiApiTestCase):
#arrange
ticket1 = self._insert_and_load_ticket("A1")
ticket2 = self._insert_and_load_ticket("A2")
- self.relations_system.add(ticket1, ticket2, "dependent")
+ self.relations_system.add(ticket1, ticket2, "dependson")
#act
self.req.args["action"] = 'resolve'
warnings = TicketRelationsSpecifics(self.env).validate_ticket(
@@ -363,7 +366,7 @@ class ApiTestCase(BaseApiApiTestCase):
#arrange
ticket1 = self._insert_and_load_ticket("A1")
ticket2 = self._insert_and_load_ticket("A2", status="closed")
- self.relations_system.add(ticket1, ticket2, "dependent")
+ self.relations_system.add(ticket1, ticket2, "dependson")
#act
self.req.args["action"] = 'resolve'
warnings = TicketRelationsSpecifics(self.env).validate_ticket(
@@ -547,6 +550,30 @@ class ApiTestCase(BaseApiApiTestCase):
)
self.relations_system.add(t2, t1, "duplicateof")
+ def test_detects_blocker_cycles(self):
+ t1, t2, t3, t4, t5 = map(self._insert_and_load_ticket, range(5))
+ self.relations_system.add(t1, t2, "blocks")
+ self.relations_system.add(t3, t2, "dependson")
+ self.relations_system.add(t4, t3, "blockedby")
+ self.relations_system.add(t4, t5, "dependent")
+
+ self.assertRaises(ValidationError,
+ self.relations_system.add, t2, t1, "blocks")
+ self.assertRaises(ValidationError,
+ self.relations_system.add, t3, t1, "dependent")
+ self.assertRaises(ValidationError,
+ self.relations_system.add, t1, t2, "blockedby")
+ self.assertRaises(ValidationError,
+ self.relations_system.add, t1, t5, "dependson")
+
+ self.relations_system.add(t1, t2, "dependent")
+ self.relations_system.add(t2, t3, "blocks")
+ self.relations_system.add(t4, t3, "dependson")
+ self.relations_system.add(t5, t4, "blockedby")
+
+ self.relations_system.add(t1, t2, "refersto")
+ self.relations_system.add(t2, t1, "refersto")
+
class RelationChangingListenerTestCase(BaseApiApiTestCase):
def test_can_sent_adding_event(self):
Modified: bloodhound/trunk/bloodhound_relations/bhrelations/validation.py
URL: http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/validation.py?rev=1484438&r1=1484437&r2=1484438&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/validation.py (original)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/validation.py Mon May 20 11:47:24 2013
@@ -63,14 +63,16 @@ class Validator(Component):
else:
relation = 'destination, source'
origin = 'destination'
+ relation_types = \
+ ','.join("'%s'" % r for r in relation_type.split(','))
query = """
SELECT %(relation)s
FROM bloodhound_relations
- WHERE type = '%(relation_type)s'
+ WHERE type IN (%(relation_type)s)
AND %(origin)s IN (%(new_nodes)s)
""" % dict(
relation=relation,
- relation_type=relation_type,
+ relation_type=relation_types,
new_nodes=', '.join("'%s'" % n for n in new_nodes),
origin=origin)
new_nodes = set()
@@ -202,6 +204,33 @@ class ReferencesOlderValidator(Validator
)
+class BlockerValidator(Validator):
+ def validate(self, relation):
+ """If a path exists from relation's destination to its source,
+ adding the relation will create a cycle.
+ """
+ rls = RelationsSystem(self.env)
+ if not rls.is_blocker(relation.type):
+ relation = rls.get_reverted_relation(relation)
+ if not relation or not rls.is_blocker(relation.type):
+ return
+
+ blockers = ','.join(b for b, is_blocker in rls._blockers.items()
+ if is_blocker)
+
+ path = self._find_path(relation.source,
+ relation.destination,
+ blockers)
+ if path:
+ cycle_str = map(self.get_resource_name, path)
+ error = 'Cycle in ''%s'': %s' % (
+ self.render_relation_type(relation.type),
+ ' -> '.join(cycle_str))
+ error = ValidationError(error)
+ error.failed_ids = path
+ raise error
+
+
class ValidationError(TracError):
def __init__(self, message, title=None, show_traceback=False):
super(ValidationError, self).__init__(
Modified: bloodhound/trunk/installer/bloodhound_setup.py
URL: http://svn.apache.org/viewvc/bloodhound/trunk/installer/bloodhound_setup.py?rev=1484438&r1=1484437&r2=1484438&view=diff
==============================================================================
--- bloodhound/trunk/installer/bloodhound_setup.py (original)
+++ bloodhound/trunk/installer/bloodhound_setup.py Mon May 20 11:47:24 2013
@@ -91,13 +91,14 @@ BASE_CONFIG = {'components': {'bhtheme.*
'bhsearch': {'is_default': 'true', 'enable_redirect': 'true'},
'bhrelations': {
'global_validators':
- 'NoSelfReferenceValidator,ExclusiveValidator',
+ 'NoSelfReferenceValidator,ExclusiveValidator,'
+ 'BlockerValidator',
},
'bhrelations_links': {
'children.label': 'Child',
'dependency': 'dependson,dependent',
'dependency.validators': 'NoCycles,SingleProduct',
- 'dependent.blocks': 'true',
+ 'dependson.blocks': 'true',
'dependson.label': 'Depends on',
'dependent.label': 'Dependent',
'oneway': 'refersto',