@@ -18,6 +18,7 @@ package de.danielbechler.diff.differ
1818
1919import de.danielbechler.diff.access.Instances
2020import de.danielbechler.diff.access.PropertyAwareAccessor
21+ import de.danielbechler.diff.access.RootAccessor
2122import de.danielbechler.diff.category.CategoryResolver
2223import de.danielbechler.diff.circular.CircularReferenceDetector
2324import de.danielbechler.diff.circular.CircularReferenceDetectorFactory
@@ -28,69 +29,85 @@ import de.danielbechler.diff.introspection.PropertyAccessExceptionHandler
2829import de.danielbechler.diff.introspection.PropertyAccessExceptionHandlerResolver
2930import de.danielbechler.diff.introspection.PropertyReadException
3031import de.danielbechler.diff.node.DiffNode
32+ import de.danielbechler.diff.path.NodePath
3133import spock.lang.Specification
32- import spock.lang.Subject
3334
34- import static de.danielbechler.diff.circular.CircularReferenceDetector.ReferenceMatchingMode.EQUALITY_OPERATOR
35+ import static de.danielbechler.diff.circular.CircularReferenceDetector.CircularReferenceException
3536
36- /**
37- * Created by Daniel Bechler.
38- */
39- @Subject (DifferDispatcher )
40- // @Ignore("Man, this class is a pain in the ass to test. Need to find a better way to do it.")
41- class DifferDispatcherTest extends Specification {
37+ public class DifferDispatcherTest extends Specification {
4238
43- def differ = Stub Differ
44- def differProvider = Stub DifferProvider , {
45- retrieveDifferForType(_ as Class<?> ) >> differ
46- }
47- def circularReferenceDetectorFactory = Stub CircularReferenceDetectorFactory , {
48- createCircularReferenceDetector() >> new CircularReferenceDetector (EQUALITY_OPERATOR )
39+ DifferDispatcher differDispatcher
40+
41+ DifferProvider differProvider = Mock ()
42+ CircularReferenceDetector circularReferenceDetector = Mock ()
43+ CircularReferenceDetectorFactory circularReferenceDetectorFactory = Mock ()
44+ CircularReferenceExceptionHandler circularReferenceExceptionHandler = Mock ()
45+ IsIgnoredResolver ignoredResolver = Mock ()
46+ CategoryResolver categoryResolver = Mock ()
47+ IsReturnableResolver returnableResolver = Mock ()
48+ PropertyAccessExceptionHandlerResolver propertyAccessExceptionHandlerResolver = Mock ()
49+
50+ def setup () {
51+ circularReferenceDetectorFactory. createCircularReferenceDetector() >> circularReferenceDetector
52+ categoryResolver. resolveCategories(_) >> []
53+ differDispatcher = new DifferDispatcher (differProvider,
54+ circularReferenceDetectorFactory,
55+ circularReferenceExceptionHandler,
56+ ignoredResolver,
57+ returnableResolver,
58+ propertyAccessExceptionHandlerResolver,
59+ categoryResolver);
4960 }
50- def circularReferenceExceptionHandler = Stub (CircularReferenceExceptionHandler )
51- def isIgnoredResolver = Stub IsIgnoredResolver , {
52- isIgnored(_ as DiffNode ) >> false
61+
62+ def ' when circular reference is detected the node should be marked as circular' () throws Exception {
63+ given :
64+ circularReferenceDetector. push(_, _) >> { instance , nodePath ->
65+ throw new CircularReferenceException (nodePath)
66+ }
67+ when :
68+ def node = differDispatcher. dispatch(DiffNode . ROOT , Instances . of(' *' , ' *' ), RootAccessor . instance);
69+ then :
70+ node. state == DiffNode.State . CIRCULAR
5371 }
54- def isReturnableResolver = Stub IsReturnableResolver , {
55- isReturnable(_ as DiffNode ) >> true
72+
73+ def ' when circular reference is detected the node should hold the path to the node it circles back to' () throws Exception {
74+ given :
75+ circularReferenceDetector. push(_, _) >> { instance , nodePath ->
76+ throw new CircularReferenceException (nodePath)
77+ }
78+ when :
79+ def node = differDispatcher. dispatch(DiffNode . ROOT , Instances . of(' *' , ' *' ), RootAccessor . instance);
80+ then : ' the node should be marked as circular'
81+ node. circleStartPath == NodePath . withRoot()
82+ and :
83+ 1 * circularReferenceExceptionHandler. onCircularReferenceException(_ as DiffNode )
5684 }
57- def categoryResolver = Stub CategoryResolver , {
58- resolveCategories(_ as DiffNode ) >> Collections . emptySet()
85+
86+ def ' when circular reference is detected the node should be passed to the exception handler before it is returned' () throws Exception {
87+ def handledNode = null
88+
89+ given :
90+ circularReferenceDetector. push(_, _) >> { instance , nodePath ->
91+ throw new CircularReferenceException (nodePath)
92+ }
93+ when :
94+ def node = differDispatcher. dispatch(DiffNode . ROOT , Instances . of(' *' , ' *' ), RootAccessor . instance);
95+ then :
96+ 1 * circularReferenceExceptionHandler. onCircularReferenceException(_ as DiffNode ) >> { DiffNode it -> handledNode = it }
97+ expect :
98+ node. is(handledNode)
5999 }
60- def propertyAccessExceptionHandlerResolver = Mock PropertyAccessExceptionHandlerResolver
61- def differDispatcher = new DifferDispatcher (
62- differProvider,
63- circularReferenceDetectorFactory,
64- circularReferenceExceptionHandler,
65- isIgnoredResolver,
66- isReturnableResolver,
67- propertyAccessExceptionHandlerResolver,
68- categoryResolver)
69100
70- // @Ignore
71- // def "when a circular reference is detected"() {
72- // given:
73- // def accessor = Stub Accessor
74- // def accessedInstances = Mock Instances, {
75- // areNull() >> false
76- // getBase() >> new Object()
77- // getWorking() >> new Object()
78- // }
79- // def instances = Mock Instances, {
80- // access(_ as Accessor) >> accessedInstances
81- // getSourceAccessor() >> accessor
82- // }
83- // def node = DiffNode.newRootNode()
84- //
85- // when:
86- // differDispatcher.dispatch(node, instances, accessor)
87- //
88- // then:
89- // differDispatcher.workingCircularReferenceDetector.size() == 1
90- // differDispatcher.baseCircularReferenceDetector.size() == 1
91- // }
101+ def ' throw exception if no differ can be found for instance type' () {
102+ given :
103+ differProvider. retrieveDifferForType(_) >> null
104+ when :
105+ differDispatcher. dispatch(DiffNode . ROOT , Instances . of(' *' , ' *' ), RootAccessor . instance);
106+ then :
107+ thrown(IllegalStateException )
108+ }
92109
93- def ' should delegate property read exception to exception handler ' () {
110+ def ' should delegate property read exception to PropertyAccessExceptionHandler ' () {
94111 def propertyExceptionHandler = Mock PropertyAccessExceptionHandler
95112 def propertyAccessor = Mock (PropertyAwareAccessor ) {
96113 getPropertyName() >> ' foo'
@@ -108,7 +125,7 @@ class DifferDispatcherTest extends Specification {
108125 1 * propertyExceptionHandler. onPropertyReadException(propertyAccessException, _ as DiffNode )
109126 }
110127
111- def ' should change node state to INACESSIBLE when reading a property value caused an exception' () {
128+ def ' when reading a property value caused an exception the returned node should have state INACESSIBLE ' () {
112129 def propertyAccessor = Mock (PropertyAwareAccessor )
113130 def instances = Mock Instances , {
114131 access(propertyAccessor) >> {
0 commit comments