Skip to content

Commit

Permalink
Use fail-fast pattern in matchers
Browse files Browse the repository at this point in the history
- Fixes bug where paths did not consider whether pathlinks matched
- Fixes bug where paths were parsed in reverse order
  • Loading branch information
Mats-SX committed Mar 3, 2016
1 parent 0de79ea commit 587044a
Show file tree
Hide file tree
Showing 16 changed files with 114 additions and 58 deletions.
Expand Up @@ -255,8 +255,10 @@ public void exitPath( FeatureResultsParser.PathContext ctx )
} }
else else
{ {
workload.push( new PathMatcher( new ArrayList<>( pathElements ) ) ); ArrayList<PathLinkMatcher> pathLinkMatchers = new ArrayList<>();
pathElements.descendingIterator().forEachRemaining( pathLinkMatchers::add );
pathElements.clear(); pathElements.clear();
workload.push( new PathMatcher( pathLinkMatchers ) );
} }
} }
} }
Expand Up @@ -36,22 +36,24 @@ public boolean matches( Object value )
if ( value instanceof List ) if ( value instanceof List )
{ {
List realList = (List) value; List realList = (List) value;
boolean match = realList.size() == list.size(); if ( realList.size() == list.size() )
if ( match )
{ {
for ( int i = 0; i < list.size(); ++i ) for ( int i = 0; i < list.size(); ++i )
{ {
match &= list.get( i ).matches( realList.get( i ) ); if ( !list.get( i ).matches( realList.get( i ) ) )
{
return false;
}
} }
return true;
} }
return match;
} }
return false; return false;
} }


@Override @Override
public String toString() public String toString()
{ {
return "ListMatcher for " + list.toString(); return "ListMatcher" + list.toString();
} }
} }
Expand Up @@ -39,18 +39,20 @@ public boolean matches( Object value )
{ {
if ( value instanceof Map ) if ( value instanceof Map )
{ {
Map<String,Object> realMap = (Map<String,Object>) value; Map realMap = (Map) value;
Set<String> expectedKeys = map.keySet(); Set<String> expectedKeys = map.keySet();
Set<String> actualKeys = realMap.keySet(); Set actualKeys = realMap.keySet();
boolean match = expectedKeys.equals( actualKeys );


if ( match ) if ( expectedKeys.equals( actualKeys ) )
{ {
for ( String key : expectedKeys ) for ( Map.Entry<String,ValueMatcher> entry : map.entrySet() )
{ {
match &= map.get( key ).matches( realMap.get( key ) ); if ( !entry.getValue().matches( realMap.get( entry.getKey() ) ) )
{
return false;
}
} }
return match; return true;
} }
} }
return false; return false;
Expand All @@ -59,6 +61,6 @@ public boolean matches( Object value )
@Override @Override
public String toString() public String toString()
{ {
return "MapMatcher {" + map.toString(); return "MapMatcher" + map.toString();
} }
} }
Expand Up @@ -22,9 +22,4 @@
public interface Matcher<T> public interface Matcher<T>
{ {
boolean matches( T value ); boolean matches( T value );

interface OrderedMatcher<T> extends Matcher<T>
{
boolean matchesOrdered( T value );
}
} }
Expand Up @@ -41,26 +41,24 @@ public boolean matches( Object value )
if ( value instanceof Node ) if ( value instanceof Node )
{ {
Node node = (Node) value; Node node = (Node) value;
Iterable<Label> labels = node.getLabels();
boolean match = true;
int nbrOfLabels = 0; int nbrOfLabels = 0;
for ( Label l : labels ) for ( Label l : node.getLabels() )
{ {
match &= labelNames.contains( l.name() ); if ( !labelNames.contains( l.name() ) )
{
return false;
}
++nbrOfLabels; ++nbrOfLabels;
} }
match &= nbrOfLabels == labelNames.size();


match &= propertyMatcher.matches( node.getAllProperties() ); return labelNames.size() == nbrOfLabels && propertyMatcher.matches( node.getAllProperties() );

return match;
} }
return false; return false;
} }


@Override @Override
public String toString() public String toString()
{ {
return "NodeMatcher (" + labelNames + ") " + propertyMatcher; return "NodeMatcher" + labelNames + propertyMatcher;
} }
} }
Expand Up @@ -41,18 +41,23 @@ public boolean matches( Object value )
if ( value instanceof Relationship ) if ( value instanceof Relationship )
{ {
Relationship real = (Relationship) value; Relationship real = (Relationship) value;
boolean matches = relMatcher.matches( real ); if ( !relMatcher.matches( real ) )
{
return false;
}
if ( outgoing ) if ( outgoing )
{ {
matches &= leftNode.matches( real.getStartNode() ); if ( !leftNode.matches( real.getStartNode() ) || !rightNode.matches( real.getEndNode() ) )
matches &= rightNode.matches( real.getEndNode() ); {
return false;
}
} }
else // incoming
else if ( !leftNode.matches( real.getEndNode() ) || !rightNode.matches( real.getStartNode() ) )
{ {
matches &= leftNode.matches( real.getEndNode() ); return false;
matches &= rightNode.matches( real.getStartNode() );
} }
return matches; return true;
} }
return false; return false;
} }
Expand All @@ -65,9 +70,9 @@ public void setRightNode( NodeMatcher rightNode )
@Override @Override
public String toString() public String toString()
{ {
return "PathLinkMatcher " return "PathLinkMatcher <<"
+ leftNode + (outgoing ? "-" : "<-") + leftNode + (outgoing ? "-" : "<-")
+ relMatcher + (outgoing ? "->" : "-") + relMatcher + (outgoing ? "->" : "-")
+ rightNode; + rightNode + ">>";
} }
} }
Expand Up @@ -49,25 +49,28 @@ public boolean matches( Object value )
if ( value instanceof Path ) if ( value instanceof Path )
{ {
Path path = (Path) value; Path path = (Path) value;
if ( path.length() != pathLinks.size() )
{
return false;
}
if ( pathLinks.isEmpty() ) if ( pathLinks.isEmpty() )
{ {
boolean matches = path.length() == 0; return path.length() == 0
matches &= singleNodePath.matches( path.startNode() ); && singleNodePath.matches( path.startNode() )
matches &= singleNodePath.matches( path.endNode() ); && singleNodePath.matches( path.endNode() );
return matches;
} }
else else
{ {
boolean matches = path.length() == pathLinks.size(); Iterator<Relationship> relationships = path.relationships().iterator();
if ( matches ) for ( PathLinkMatcher pathLink : pathLinks )
{ {
Iterator<Relationship> relationships = path.relationships().iterator(); Relationship next = relationships.next();
for ( PathLinkMatcher pathLink : pathLinks ) if ( !pathLink.matches( next ) )
{ {
pathLink.matches( relationships.next() ); return false;
} }
} }
return matches; return true;
} }
} }
return false; return false;
Expand All @@ -78,11 +81,11 @@ public String toString()
{ {
if ( pathLinks.isEmpty() ) if ( pathLinks.isEmpty() )
{ {
return "PathMatcher for " + singleNodePath; return "PathMatcher" + singleNodePath;
} }
else else
{ {
return "PathMatcher for " + pathLinks; return "PathMatcher" + pathLinks;
} }
} }
} }
Expand Up @@ -48,6 +48,6 @@ public boolean matches( Object value )
@Override @Override
public String toString() public String toString()
{ {
return "RelationshipMatcher [" + relationshipTypeName + "] " + propertyMatcher; return "RelationshipMatcher[" + relationshipTypeName + "]" + propertyMatcher;
} }
} }
Expand Up @@ -25,7 +25,7 @@


import org.neo4j.graphdb.Result; import org.neo4j.graphdb.Result;


public class ResultMatcher implements Matcher<Result>, Matcher.OrderedMatcher<Result> public class ResultMatcher implements Matcher<Result>
{ {
private final List<RowMatcher> rowMatchers; private final List<RowMatcher> rowMatchers;


Expand Down Expand Up @@ -61,7 +61,6 @@ else if ( i == mutableCopy.size() - 1 )
return nothingLeftInMatcher && nothingLeftInReal; return nothingLeftInMatcher && nothingLeftInReal;
} }


@Override
public boolean matchesOrdered( Result value ) public boolean matchesOrdered( Result value )
{ {
boolean matches = true; boolean matches = true;
Expand Down
Expand Up @@ -35,20 +35,23 @@ public RowMatcher( Map<String,ValueMatcher> values )
public boolean matches( Map<String,Object> value ) public boolean matches( Map<String,Object> value )
{ {
Set<String> keys = values.keySet(); Set<String> keys = values.keySet();
boolean matches = keys.equals( value.keySet() ); if ( keys.equals( value.keySet() ) )
if ( matches )
{ {
for ( String key : keys ) for ( String key : keys )
{ {
matches &= values.get( key ).matches( value.get( key ) ); if ( !values.get( key ).matches( value.get( key ) ) )
{
return false;
}
} }
return true;
} }
return matches; return false;
} }


@Override @Override
public String toString() public String toString()
{ {
return "RowMatcher(" + values + ")"; return "RowMatcher" + values;
} }
} }
Expand Up @@ -64,9 +64,10 @@ abstract class ParsingTestSupport extends FunSuite with Matchers with DecorateAs
def path(relationships: Relationship*): Path = { def path(relationships: Relationship*): Path = {
val path = mock(classOf[Path]) val path = mock(classOf[Path])
when(path.length()).thenReturn(relationships.length) when(path.length()).thenReturn(relationships.length)
println(relationships.toIterable.asJava)
when(path.relationships()).thenReturn(relationships.toIterable.asJava) when(path.relationships()).thenReturn(relationships.toIterable.asJava)
// Mockito bug makes mocks unable to refer to other mocks in stubs; can't inline this // Mockito bug makes mocks unable to refer to other mocks in stubs; can't inline this
val pathString = s"<${relationships.mkString}>" val pathString = s"<${relationships.mkString(",")}>"
when(path.toString).thenReturn(pathString) when(path.toString).thenReturn(pathString)
path path
} }
Expand Down
Expand Up @@ -44,4 +44,8 @@ class ListMatcherTest extends ParsingTestSupport {
new ListMatcher(asList(new ListMatcher(asList(new IntegerMatcher(0), new BooleanMatcher(false))), new StringMatcher(""))) should accept(asList(asList(0L, false), "")) new ListMatcher(asList(new ListMatcher(asList(new IntegerMatcher(0), new BooleanMatcher(false))), new StringMatcher(""))) should accept(asList(asList(0L, false), ""))
} }


test("should not match different lists") {
new ListMatcher(asList(new IntegerMatcher(-1L))) shouldNot accept(asList(1L))
}

} }
Expand Up @@ -39,4 +39,8 @@ class MapMatcherTest extends ParsingTestSupport {
new MapMatcher(Map("k" -> NULL_MATCHER, "k2" -> new BooleanMatcher(true)).asJava) shouldNot accept(Map("k" -> NULL_MATCHER)) new MapMatcher(Map("k" -> NULL_MATCHER, "k2" -> new BooleanMatcher(true)).asJava) shouldNot accept(Map("k" -> NULL_MATCHER))
} }


test("should not accept different maps") {
new MapMatcher(Map[String, ValueMatcher]("key" -> new StringMatcher("value1")).asJava) shouldNot accept(Map("key" -> "value2").asJava)
}

} }
Expand Up @@ -53,4 +53,8 @@ class NodeMatcherTest extends ParsingTestSupport {


matcher shouldNot accept(node(properties = Map("key" -> List("").asJava))) matcher shouldNot accept(node(properties = Map("key" -> List("").asJava)))
} }

test("should not match a non-node") {
new NodeMatcher(Set.empty[String].asJava, MapMatcher.EMPTY) shouldNot accept("a string")
}
} }
Expand Up @@ -45,4 +45,25 @@ class PathLinkMatcherTest extends ParsingTestSupport {
matcher shouldNot accept(pathLink(node(Seq("L")), relationship("T"), node())) matcher shouldNot accept(pathLink(node(Seq("L")), relationship("T"), node()))
} }


test("should not match a different relationship") {
val matcher = new PathLinkMatcher(new RelationshipMatcher("T1", EMPTY), new NodeMatcher(Set("L").asJava, EMPTY), true)
matcher.setRightNode(new NodeMatcher(Set.empty[String].asJava, EMPTY))

matcher shouldNot accept(pathLink(node(Seq("L")), relationship("T2"), node()))
}

test("should not match a different startNode") {
val matcher = new PathLinkMatcher(new RelationshipMatcher("T", EMPTY), new NodeMatcher(Set("L1").asJava, EMPTY), true)
matcher.setRightNode(new NodeMatcher(Set.empty[String].asJava, EMPTY))

matcher shouldNot accept(pathLink(node(Seq("L2")), relationship("T"), node()))
}

test("should not match a non-relationship") {
val matcher = new PathLinkMatcher(new RelationshipMatcher("T", EMPTY), new NodeMatcher(Set("L1").asJava, EMPTY), true)
matcher.setRightNode(new NodeMatcher(Set.empty[String].asJava, EMPTY))

matcher shouldNot accept("a string")
}

} }
Expand Up @@ -71,4 +71,17 @@ class PathMatcherTest extends ParsingTestSupport {
pathMatcher shouldNot accept(expected) pathMatcher shouldNot accept(expected)
} }


test("should not match a path with wrong link") {
val linkMatcher1 = new PathLinkMatcher(new RelationshipMatcher("T1", EMPTY),
new NodeMatcher(Set.empty[String].asJava, EMPTY), true)
linkMatcher1.setRightNode(new NodeMatcher(Set.empty[String].asJava, EMPTY))
val pathMatcher = new PathMatcher(List(linkMatcher1).asJava)

pathMatcher shouldNot accept(path(pathLink(node(), relationship("T2"), node())))
}

test("should not match a non-path") {
new PathMatcher(new NodeMatcher(Set("L").asJava, EMPTY)) shouldNot accept("a string")
}

} }

0 comments on commit 587044a

Please sign in to comment.