[OLINGO-1028] stricter multiplicity tests in expression parser + clean-up

Signed-off-by: Christian Amend <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/72fcaa1a
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/72fcaa1a
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/72fcaa1a

Branch: refs/heads/master
Commit: 72fcaa1a54a3607ee2b94f66414677ab6f9c8e92
Parents: b9a71ff
Author: Klaus Straubinger <[email protected]>
Authored: Thu Sep 29 16:25:33 2016 +0200
Committer: Christian Amend <[email protected]>
Committed: Thu Sep 29 16:31:24 2016 +0200

----------------------------------------------------------------------
 .../core/uri/parser/ExpressionParser.java       |   59 +-
 .../uri/parser/UriParserSemanticException.java  |    3 +-
 .../uri/queryoption/expression/MemberImpl.java  |   14 +-
 .../server-core-exceptions-i18n.properties      |    1 +
 .../core/uri/parser/ExpandParserTest.java       |  561 ++
 .../core/uri/parser/ExpressionParserTest.java   | 2389 +++++++
 .../core/uri/parser/ResourcePathParserTest.java | 2100 ++++++
 .../core/uri/parser/SearchParserTest.java       |  187 +
 .../core/uri/parser/SelectParserTest.java       |  177 +
 .../core/uri/parser/TestFullResourcePath.java   | 6003 ------------------
 .../core/uri/parser/TestUriParserImpl.java      | 1047 ---
 .../server/core/uri/parser/UriParserTest.java   |  843 +++
 .../core/uri/testutil/ResourceValidator.java    |  221 +-
 13 files changed, 6388 insertions(+), 7217 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/72fcaa1a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java
index 23fd8a4..7a34095 100644
--- 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java
@@ -180,15 +180,19 @@ public class ExpressionParser {
     this.crossjoinEntitySetNames = crossjoinEntitySetNames;
     this.aliases = aliases;
 
-    return parseExpression();
+    final Expression expression = parseExpression();
+    checkNoCollection(expression);
+    return expression;
   }
 
   private Expression parseExpression() throws UriParserException, 
UriValidationException {
     Expression left = parseAnd();
     while (tokenizer.next(TokenKind.OrOperator)) {
-      final Expression right = parseAnd();
       checkType(left, EdmPrimitiveTypeKind.Boolean);
+      checkNoCollection(left);
+      final Expression right = parseAnd();
       checkType(right, EdmPrimitiveTypeKind.Boolean);
+      checkNoCollection(right);
       left = new BinaryImpl(left, BinaryOperatorKind.OR, right,
           odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean));
     }
@@ -198,9 +202,11 @@ public class ExpressionParser {
   private Expression parseAnd() throws UriParserException, 
UriValidationException {
     Expression left = parseExprEquality();
     while (tokenizer.next(TokenKind.AndOperator)) {
-      final Expression right = parseExprEquality();
       checkType(left, EdmPrimitiveTypeKind.Boolean);
+      checkNoCollection(left);
+      final Expression right = parseExprEquality();
       checkType(right, EdmPrimitiveTypeKind.Boolean);
+      checkNoCollection(right);
       left = new BinaryImpl(left, BinaryOperatorKind.AND, right,
           odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean));
     }
@@ -285,8 +291,8 @@ public class ExpressionParser {
         TokenKind.MulOperator, TokenKind.DivOperator, TokenKind.ModOperator);
     // Null for everything other than MUL or DIV or MOD
     while (operatorTokenKind != null) {
-      final Expression right = parseExprUnary();
       checkNumericType(left);
+      final Expression right = parseExprUnary();
       checkNumericType(right);
       left = new BinaryImpl(left, 
tokenToBinaryOperator.get(operatorTokenKind), right,
           odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double));
@@ -306,6 +312,7 @@ public class ExpressionParser {
     } else if (tokenizer.next(TokenKind.NotOperator)) {
       final Expression expression = parseExprPrimary();
       checkType(expression, EdmPrimitiveTypeKind.Boolean);
+      checkNoCollection(expression);
       return new UnaryImpl(UnaryOperatorKind.NOT, expression, 
getType(expression));
     } else if (tokenizer.next(TokenKind.CastMethod)) {
       return parseIsOfOrCastMethod(MethodKind.CAST);
@@ -443,6 +450,7 @@ public class ExpressionParser {
     case TRIM:
       final Expression stringParameter = parseExpression();
       checkType(stringParameter, EdmPrimitiveTypeKind.String);
+      checkNoCollection(stringParameter);
       parameters.add(stringParameter);
       break;
     case YEAR:
@@ -450,6 +458,7 @@ public class ExpressionParser {
     case DAY:
       final Expression dateParameter = parseExpression();
       checkType(dateParameter, EdmPrimitiveTypeKind.Date, 
EdmPrimitiveTypeKind.DateTimeOffset);
+      checkNoCollection(dateParameter);
       parameters.add(dateParameter);
       break;
     case HOUR:
@@ -458,6 +467,7 @@ public class ExpressionParser {
     case FRACTIONALSECONDS:
       final Expression timeParameter = parseExpression();
       checkType(timeParameter, EdmPrimitiveTypeKind.TimeOfDay, 
EdmPrimitiveTypeKind.DateTimeOffset);
+      checkNoCollection(timeParameter);
       parameters.add(timeParameter);
       break;
     case DATE:
@@ -465,11 +475,13 @@ public class ExpressionParser {
     case TOTALOFFSETMINUTES:
       final Expression dateTimeParameter = parseExpression();
       checkType(dateTimeParameter, EdmPrimitiveTypeKind.DateTimeOffset);
+      checkNoCollection(dateTimeParameter);
       parameters.add(dateTimeParameter);
       break;
     case TOTALSECONDS:
       final Expression durationParameter = parseExpression();
       checkType(durationParameter, EdmPrimitiveTypeKind.Duration);
+      checkNoCollection(durationParameter);
       parameters.add(durationParameter);
       break;
     case ROUND:
@@ -478,12 +490,14 @@ public class ExpressionParser {
       final Expression decimalParameter = parseExpression();
       checkType(decimalParameter,
           EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, 
EdmPrimitiveTypeKind.Double);
+      checkNoCollection(decimalParameter);
       parameters.add(decimalParameter);
       break;
     case GEOLENGTH:
       final Expression geoParameter = parseExpression();
       checkType(geoParameter,
           EdmPrimitiveTypeKind.GeographyLineString, 
EdmPrimitiveTypeKind.GeometryLineString);
+      checkNoCollection(geoParameter);
       parameters.add(geoParameter);
       break;
 
@@ -495,30 +509,36 @@ public class ExpressionParser {
     case CONCAT:
       final Expression stringParameter1 = parseExpression();
       checkType(stringParameter1, EdmPrimitiveTypeKind.String);
+      checkNoCollection(stringParameter1);
       parameters.add(stringParameter1);
       ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
       final Expression stringParameter2 = parseExpression();
       checkType(stringParameter2, EdmPrimitiveTypeKind.String);
+      checkNoCollection(stringParameter2);
       parameters.add(stringParameter2);
       break;
     case GEODISTANCE:
       final Expression geoParameter1 = parseExpression();
       checkType(geoParameter1, EdmPrimitiveTypeKind.GeographyPoint, 
EdmPrimitiveTypeKind.GeometryPoint);
+      checkNoCollection(geoParameter1);
       parameters.add(geoParameter1);
       ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
       final Expression geoParameter2 = parseExpression();
       checkType(geoParameter2, EdmPrimitiveTypeKind.GeographyPoint, 
EdmPrimitiveTypeKind.GeometryPoint);
+      checkNoCollection(geoParameter2);
       parameters.add(geoParameter2);
       break;
     case GEOINTERSECTS:
       final Expression geoPointParameter = parseExpression();
       checkType(geoPointParameter,
           EdmPrimitiveTypeKind.GeographyPoint, 
EdmPrimitiveTypeKind.GeometryPoint);
+      checkNoCollection(geoPointParameter);
       parameters.add(geoPointParameter);
       ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
       final Expression geoPolygonParameter = parseExpression();
       checkType(geoPolygonParameter,
           EdmPrimitiveTypeKind.GeographyPolygon, 
EdmPrimitiveTypeKind.GeometryPolygon);
+      checkNoCollection(geoPolygonParameter);
       parameters.add(geoPolygonParameter);
       break;
 
@@ -526,6 +546,7 @@ public class ExpressionParser {
     case SUBSTRING:
       final Expression parameterFirst = parseExpression();
       checkType(parameterFirst, EdmPrimitiveTypeKind.String);
+      checkNoCollection(parameterFirst);
       parameters.add(parameterFirst);
       ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
       final Expression parameterSecond = parseExpression();
@@ -905,8 +926,9 @@ public class ExpressionParser {
     } else if (tokenizer.next(TokenKind.ALL)) {
       uriInfo.addResourcePart(parseLambdaRest(TokenKind.ALL, lastResource));
     } else if (tokenizer.next(TokenKind.QualifiedName)) {
-      final FullQualifiedName fullQualifiedName = new 
FullQualifiedName(tokenizer.getText());
-      parseBoundFunction(fullQualifiedName, uriInfo, lastResource);
+      parseBoundFunction(new FullQualifiedName(tokenizer.getText()), uriInfo, 
lastResource);
+    } else {
+      throw new UriParserSyntaxException("Unexpected token.", 
UriParserSyntaxException.MessageKeys.SYNTAX);
     }
   }
 
@@ -1071,13 +1093,22 @@ public class ExpressionParser {
     }
   }
 
+  private void checkNoCollection(final Expression expression) throws 
UriParserException {
+    if (expression instanceof Member  && ((Member) expression).isCollection()) 
{
+      throw new UriParserSemanticException("Collection not allowed.",
+          UriParserSemanticException.MessageKeys.COLLECTION_NOT_ALLOWED);
+    }
+  }
+
   protected void checkIntegerType(final Expression expression) throws 
UriParserException {
+    checkNoCollection(expression);
     checkType(expression,
         EdmPrimitiveTypeKind.Int64, EdmPrimitiveTypeKind.Int32, 
EdmPrimitiveTypeKind.Int16,
         EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte);
   }
 
   protected void checkNumericType(final Expression expression) throws 
UriParserException {
+    checkNoCollection(expression);
     checkType(expression,
         EdmPrimitiveTypeKind.Int64, EdmPrimitiveTypeKind.Int32, 
EdmPrimitiveTypeKind.Int16,
         EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
@@ -1085,6 +1116,9 @@ public class ExpressionParser {
   }
 
   private void checkEqualityTypes(final Expression left, final Expression 
right) throws UriParserException {
+    checkNoCollection(left);
+    checkNoCollection(right);
+
     final EdmType leftType = getType(left);
     final EdmType rightType = getType(right);
     if (leftType == null || rightType == null || leftType.equals(rightType)) {
@@ -1141,11 +1175,10 @@ public class ExpressionParser {
   }
 
   private void checkRelationTypes(final Expression left, final Expression 
right) throws UriParserException {
+    checkNoCollection(left);
+    checkNoCollection(right);
     final EdmType leftType = getType(left);
     final EdmType rightType = getType(right);
-    if (leftType == null || rightType == null) {
-      return;
-    }
     checkType(left,
         EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, 
EdmPrimitiveTypeKind.Int64,
         EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
@@ -1160,6 +1193,9 @@ public class ExpressionParser {
         EdmPrimitiveTypeKind.Boolean, EdmPrimitiveTypeKind.Guid, 
EdmPrimitiveTypeKind.String,
         EdmPrimitiveTypeKind.Date, EdmPrimitiveTypeKind.TimeOfDay,
         EdmPrimitiveTypeKind.DateTimeOffset, EdmPrimitiveTypeKind.Duration);
+    if (leftType == null || rightType == null) {
+      return;
+    }
     if (!(((EdmPrimitiveType) leftType).isCompatible((EdmPrimitiveType) 
rightType)
         || ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) 
leftType))) {
       throw new UriParserSemanticException("Incompatible types.",
@@ -1171,11 +1207,10 @@ public class ExpressionParser {
 
   private EdmType getAddSubTypeAndCheckLeftAndRight(final Expression left, 
final Expression right, final boolean isSub)
       throws UriParserException {
+    checkNoCollection(left);
+    checkNoCollection(right);
     final EdmType leftType = getType(left);
     final EdmType rightType = getType(right);
-    if (leftType == null || rightType == null) {
-      return null;
-    }
     if (isType(leftType,
             EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, 
EdmPrimitiveTypeKind.Int64,
             EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/72fcaa1a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java
index f2c4634..8260af7 100644
--- 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java
@@ -74,7 +74,8 @@ public class UriParserSemanticException extends 
UriParserException {
     /** parameter: expression */
     ONLY_FOR_PRIMITIVE_TYPES,
     /** parameter: function name */
-    FUNCTION_MUST_USE_COLLECTIONS;
+    FUNCTION_MUST_USE_COLLECTIONS,
+    COLLECTION_NOT_ALLOWED;
 
     @Override
     public String getKey() {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/72fcaa1a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/MemberImpl.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/MemberImpl.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/MemberImpl.java
index 3306565..4a67231 100644
--- 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/MemberImpl.java
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/MemberImpl.java
@@ -21,6 +21,8 @@ package 
org.apache.olingo.server.core.uri.queryoption.expression;
 import org.apache.olingo.commons.api.edm.EdmType;
 import org.apache.olingo.server.api.ODataApplicationException;
 import org.apache.olingo.server.api.uri.UriInfoResource;
+import org.apache.olingo.server.api.uri.UriResource;
+import org.apache.olingo.server.api.uri.UriResourcePartTyped;
 import 
org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException;
 import 
org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitor;
 import org.apache.olingo.server.api.uri.queryoption.expression.Member;
@@ -85,14 +87,10 @@ public class MemberImpl implements Member {
   @Override
   public boolean isCollection() {
     UriInfoImpl uriInfo = (UriInfoImpl) path;
-    UriResourceImpl lastResourcePart = (UriResourceImpl) 
uriInfo.getLastResourcePart();
-    if (lastResourcePart instanceof UriResourceTypedImpl) {
-      UriResourceTypedImpl lastTyped = (UriResourceTypedImpl) lastResourcePart;
-      return lastTyped.isCollection();
-    } else if (lastResourcePart instanceof UriResourceActionImpl) {
-      return ((UriResourceActionImpl) lastResourcePart).isCollection();
-    }
-    return false;
+    UriResource lastResourcePart = uriInfo.getLastResourcePart();
+    return lastResourcePart instanceof UriResourcePartTyped ?
+        ((UriResourcePartTyped) lastResourcePart).isCollection() :
+        false;
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/72fcaa1a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties 
b/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
index 831fe37..5768565 100644
--- a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
+++ b/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
@@ -76,6 +76,7 @@ UriParserSemanticException.NOT_A_MEDIA_RESOURCE=The resource 
'%1$s' is not a med
 UriParserSemanticException.IS_PROPERTY=The identifier '%1$s' is already used 
as a property.
 UriParserSemanticException.ONLY_FOR_PRIMITIVE_TYPES='%1$s' is only allowed for 
primitive-type expressions.
 UriParserSemanticException.FUNCTION_MUST_USE_COLLECTIONS=Only bound functions 
with collections of structural types as binding parameter and as return type 
are allowed; '%1$s' is not such a function.
+UriParserSemanticException.COLLECTION_NOT_ALLOWED=A collection expression is 
not allowed.
 
 UriValidationException.UNSUPPORTED_QUERY_OPTION=The query option '%1$s' is not 
supported.
 UriValidationException.UNSUPPORTED_URI_KIND=The URI kind '%1$s' is not 
supported.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/72fcaa1a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ExpandParserTest.java
----------------------------------------------------------------------
diff --git 
a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ExpandParserTest.java
 
b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ExpandParserTest.java
new file mode 100644
index 0000000..ebc4cc6
--- /dev/null
+++ 
b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ExpandParserTest.java
@@ -0,0 +1,561 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.uri.parser;
+
+import java.util.Collections;
+
+import org.apache.olingo.commons.api.edm.Edm;
+import org.apache.olingo.commons.api.edmx.EdmxReference;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.uri.UriInfoKind;
+import 
org.apache.olingo.server.core.uri.parser.UriParserSemanticException.MessageKeys;
+import org.apache.olingo.server.core.uri.testutil.TestUriValidator;
+import org.apache.olingo.server.tecsvc.provider.ComplexTypeProvider;
+import org.apache.olingo.server.tecsvc.provider.EdmTechProvider;
+import org.apache.olingo.server.tecsvc.provider.EntityTypeProvider;
+import org.apache.olingo.server.tecsvc.provider.PropertyProvider;
+import org.junit.Test;
+
+/** Tests of the parts of the URI parser that parse the sytem query option 
$expand. */
+public class ExpandParserTest {
+
+  private static final Edm edm = OData.newInstance().createServiceMetadata(
+      new EdmTechProvider(), Collections.<EdmxReference> emptyList()).getEdm();
+
+  private final TestUriValidator testUri = new TestUriValidator().setEdm(edm);
+
+  @Test
+  public void expandStar() throws Exception {
+    testUri.run("ESKeyNav(1)", "$expand=*")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .isSegmentStar();
+
+    testUri.run("ESKeyNav(1)", "$expand=*/$ref")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .isSegmentStar()
+        .isSegmentRef();
+
+    testUri.run("ESKeyNav(1)", "$expand=*/$ref,NavPropertyETKeyNavMany")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .isSegmentStar().isSegmentRef()
+        .next()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true);
+
+    testUri.run("ESKeyNav(1)", "$expand=*($levels=3)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .isSegmentStar()
+        .isLevelText("3");
+
+    testUri.run("ESKeyNav(1)", "$expand=*($levels=max)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .isSegmentStar()
+        .isLevelText("max");
+  }
+
+  @Test
+  public void expandNavigationRef() throws Exception {
+    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$ref")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .n().isRef();
+
+    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavOne/$ref")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavOne", 
EntityTypeProvider.nameETKeyNav, false)
+        .isType(EntityTypeProvider.nameETKeyNav, false)
+        .n().isRef();
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany/$ref($filter=PropertyInt16 eq 1)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .n().isRef()
+        .goUpExpandValidator().goFilter().is("<<PropertyInt16> eq <1>>");
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany/$ref($orderby=PropertyInt16)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .n().isRef()
+        .goUpExpandValidator()
+        .isSortOrder(0, false)
+        .goOrder(0).goPath().isPrimitiveProperty("PropertyInt16", 
PropertyProvider.nameInt16, false);
+
+    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$ref($skip=1)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .n().isRef()
+        .goUpExpandValidator()
+        .isSkipText("1");
+
+    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$ref($top=2)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .n().isRef()
+        .goUpExpandValidator()
+        .isTopText("2");
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany/$ref($count=true)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .n().isRef()
+        .goUpExpandValidator()
+        .isInlineCountText("true");
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany/$ref($skip=1;$top=3)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .n().isRef()
+        .goUpExpandValidator()
+        .isSkipText("1")
+        .isTopText("3");
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany/$ref($skip=1%3b$top=3)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .n().isRef()
+        .goUpExpandValidator()
+        .isSkipText("1")
+        .isTopText("3");
+  }
+
+  @Test
+  public void expandNavigationCount() throws Exception {
+    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$count")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .n().isCount();
+
+    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavOne/$count")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavOne", 
EntityTypeProvider.nameETKeyNav, false)
+        .isType(EntityTypeProvider.nameETKeyNav, false)
+        .n().isCount();
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany/$count($filter=PropertyInt16 gt 1)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .n().isCount()
+        .goUpExpandValidator()
+        .goFilter().is("<<PropertyInt16> gt <1>>");
+  }
+
+  @Test
+  public void expandNavigationOptions() throws Exception {
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany($filter=PropertyInt16 eq 1)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .goUpExpandValidator().goFilter().is("<<PropertyInt16> eq <1>>");
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany($orderby=PropertyInt16)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .goUpExpandValidator()
+        .isSortOrder(0, false)
+        .goOrder(0).goPath().isPrimitiveProperty("PropertyInt16", 
PropertyProvider.nameInt16, false);
+
+    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($skip=1)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .goUpExpandValidator()
+        .isSkipText("1");
+
+    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($top=2)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .goUpExpandValidator()
+        .isTopText("2");
+
+    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($count=true)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .goUpExpandValidator()
+        .isInlineCountText("true");
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany($select=PropertyString)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .goUpExpandValidator()
+        .goSelectItem(0).isPrimitiveProperty("PropertyString", 
PropertyProvider.nameString, false);
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany($expand=NavPropertyETTwoKeyNavOne)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .goUpExpandValidator()
+        .goExpand()
+        .goPath().first()
+        .isNavProperty("NavPropertyETTwoKeyNavOne", 
EntityTypeProvider.nameETTwoKeyNav, false);
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany($expand=NavPropertyETKeyNavMany)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .goUpExpandValidator()
+        .goExpand()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true);
+
+    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavOne($levels=5)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavOne", 
EntityTypeProvider.nameETKeyNav, false)
+        .isType(EntityTypeProvider.nameETKeyNav, false)
+        .goUpExpandValidator()
+        .isLevelText("5");
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany($select=PropertyString)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .goUpExpandValidator()
+        .goSelectItem(0).isPrimitiveProperty("PropertyString", 
PropertyProvider.nameString, false);
+
+    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavOne($levels=max)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavOne", 
EntityTypeProvider.nameETKeyNav, false)
+        .isType(EntityTypeProvider.nameETKeyNav, false)
+        .goUpExpandValidator()
+        .isLevelText("max");
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany($skip=1;$top=2)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .goUpExpandValidator()
+        .isSkipText("1")
+        .isTopText("2");
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany($skip=1%3b$top=2)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true)
+        .goUpExpandValidator()
+        .isSkipText("1")
+        .isTopText("2");
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany($search=Country AND Western)")
+        .isKind(UriInfoKind.resource).goExpand()
+        .first().goPath().first().isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .goUpExpandValidator()
+        .isSearchSerialized("{'Country' AND 'Western'}");
+
+    testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='Hugo')", 
"$expand=NavPropertyETKeyNavMany")
+        .isKind(UriInfoKind.resource).goPath()
+        .first()
+        .isKeyPredicate(0, "PropertyInt16", "1")
+        .isKeyPredicate(1, "PropertyString", "'Hugo'")
+        .goUpUriValidator().goExpand()
+        .first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav, true);
+  }
+
+  @Test
+  public void expandTypeCasts() throws Exception {
+    testUri.run("ESTwoKeyNav", 
"$expand=olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETKeyNavMany")
+        .isKind(UriInfoKind.resource)
+        .goExpand().first()
+        .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true);
+
+    testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='Hugo')",
+        "$expand=olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETKeyNavMany")
+        .isKind(UriInfoKind.resource).goPath().first()
+        .isKeyPredicate(0, "PropertyInt16", "1")
+        .isKeyPredicate(1, "PropertyString", "'Hugo'")
+        .goUpUriValidator().goExpand().first()
+        .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
+        .goPath().first()
+        .isType(EntityTypeProvider.nameETKeyNav)
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true);
+
+    testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')",
+        
"$expand=olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETTwoKeyNavMany")
+        .isKind(UriInfoKind.resource).goPath().first()
+        .isKeyPredicate(0, "PropertyInt16", "1")
+        .isKeyPredicate(1, "PropertyString", "'2'")
+        .goUpUriValidator().goExpand().first()
+        .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
+        .goPath().first()
+        .isType(EntityTypeProvider.nameETTwoKeyNav)
+        .isNavProperty("NavPropertyETTwoKeyNavMany", 
EntityTypeProvider.nameETTwoKeyNav, true);
+
+    testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')",
+        
"$expand=olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETTwoKeyNavMany/olingo.odata.test1.ETTwoBaseTwoKeyNav")
+        .isKind(UriInfoKind.resource).goPath().first()
+        .isKeyPredicate(0, "PropertyInt16", "1")
+        .isKeyPredicate(1, "PropertyString", "'2'")
+        .goUpUriValidator().goExpand().first()
+        .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
+        .goPath().first()
+        .isNavProperty("NavPropertyETTwoKeyNavMany", 
EntityTypeProvider.nameETTwoKeyNav, true)
+        .isTypeFilterOnCollection(EntityTypeProvider.nameETTwoBaseTwoKeyNav);
+
+    testUri.run("ESTwoKeyNav", 
"$expand=olingo.odata.test1.ETBaseTwoKeyNav/PropertyCompNav/NavPropertyETTwoKeyNavOne")
+        .isKind(UriInfoKind.resource)
+        .goExpand().first()
+        .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
+        .goPath().first()
+        .isComplexProperty("PropertyCompNav", 
ComplexTypeProvider.nameCTBasePrimCompNav, false)
+        .n()
+        .isNavProperty("NavPropertyETTwoKeyNavOne", 
EntityTypeProvider.nameETTwoKeyNav, false);
+
+    testUri.run("ESTwoKeyNav", 
"$expand=olingo.odata.test1.ETBaseTwoKeyNav/PropertyCompNav/*")
+        .isKind(UriInfoKind.resource)
+        .goExpand().first()
+        .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
+        .isSegmentStar()
+        .goPath().first().isComplexProperty("PropertyCompNav", 
ComplexTypeProvider.nameCTBasePrimCompNav, false);
+
+    testUri.run("ESTwoKeyNav", 
"$expand=olingo.odata.test1.ETBaseTwoKeyNav/PropertyCompNav"
+        + "/olingo.odata.test1.CTTwoBasePrimCompNav/NavPropertyETTwoKeyNavOne")
+        .isKind(UriInfoKind.resource)
+        .goExpand().first()
+        .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
+        .goPath().first()
+        .isComplexProperty("PropertyCompNav", 
ComplexTypeProvider.nameCTBasePrimCompNav, false)
+        .isTypeFilter(ComplexTypeProvider.nameCTTwoBasePrimCompNav)
+        .n()
+        .isNavProperty("NavPropertyETTwoKeyNavOne", 
EntityTypeProvider.nameETTwoKeyNav, false);
+
+    testUri.run("ESKeyNav", 
"$expand=NavPropertyETTwoKeyNavMany/Namespace1_Alias.ETBaseTwoKeyNav"
+        + "($expand=NavPropertyETBaseTwoKeyNavOne)")
+        .isKind(UriInfoKind.resource)
+        .goExpand().goPath().first()
+        .isNavProperty("NavPropertyETTwoKeyNavMany", 
EntityTypeProvider.nameETTwoKeyNav, true)
+        .isType(EntityTypeProvider.nameETTwoKeyNav, true)
+        .isTypeFilterOnCollection(EntityTypeProvider.nameETBaseTwoKeyNav)
+        .goUpExpandValidator()
+        // go to the expand options of the current expand
+        .goExpand()
+        .goPath().first()
+        .isNavProperty("NavPropertyETBaseTwoKeyNavOne", 
EntityTypeProvider.nameETBaseTwoKeyNav, false);
+
+    testUri.run("ESKeyNav(1)", 
"$expand=NavPropertyETKeyNavMany/$ref,NavPropertyETTwoKeyNavMany($skip=2;$top=1)")
+        .isKind(UriInfoKind.resource)
+        .goExpand().first()
+        .goPath()
+        .first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .n().isRef()
+        .goUpExpandValidator()
+        .next()
+        .goPath()
+        .first().isNavProperty("NavPropertyETTwoKeyNavMany", 
EntityTypeProvider.nameETTwoKeyNav, true)
+        .goUpExpandValidator()
+        .isSkipText("2")
+        .isTopText("1");
+
+    testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')", 
"$expand=olingo.odata.test1.ETBaseTwoKeyNav"
+        + 
"/NavPropertyETTwoKeyNavMany/olingo.odata.test1.ETTwoBaseTwoKeyNav($select=PropertyString)")
+        .isKind(UriInfoKind.resource).goPath()
+        .first()
+        .isKeyPredicate(0, "PropertyInt16", "1")
+        .isKeyPredicate(1, "PropertyString", "'2'")
+        .goUpUriValidator().goExpand().first()
+        .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
+        .goPath().first()
+        .isNavProperty("NavPropertyETTwoKeyNavMany", 
EntityTypeProvider.nameETTwoKeyNav, true)
+        .isType(EntityTypeProvider.nameETTwoKeyNav)
+        .isTypeFilterOnCollection(EntityTypeProvider.nameETTwoBaseTwoKeyNav)
+        .goUpExpandValidator()
+        .goSelectItem(0).isPrimitiveProperty("PropertyString", 
PropertyProvider.nameString, false);
+
+    testUri.run("ESKeyNav", 
"$expand=NavPropertyETKeyNavOne($expand=NavPropertyETKeyNavMany("
+        + "$expand=NavPropertyETKeyNavOne))")
+        .isKind(UriInfoKind.resource)
+        .goExpand().first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavOne", 
EntityTypeProvider.nameETKeyNav, false)
+        .isType(EntityTypeProvider.nameETKeyNav)
+        .goUpExpandValidator()
+        .goExpand().first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavMany", 
EntityTypeProvider.nameETKeyNav, true)
+        .isType(EntityTypeProvider.nameETKeyNav)
+        .goUpExpandValidator()
+        .goExpand().first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavOne", 
EntityTypeProvider.nameETKeyNav, false)
+        .isType(EntityTypeProvider.nameETKeyNav);
+
+    testUri.run("ESKeyNav", 
"$expand=NavPropertyETKeyNavOne($select=PropertyInt16)")
+        .isKind(UriInfoKind.resource)
+        .goExpand().first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavOne", 
EntityTypeProvider.nameETKeyNav, false)
+        .isType(EntityTypeProvider.nameETKeyNav)
+        .goUpExpandValidator()
+        .goSelectItem(0).isPrimitiveProperty("PropertyInt16", 
PropertyProvider.nameInt16, false);
+
+    testUri.run("ESKeyNav", 
"$expand=NavPropertyETKeyNavOne($select=PropertyCompNav/PropertyInt16)")
+        .isKind(UriInfoKind.resource)
+        .goExpand().first()
+        .goPath().first()
+        .isNavProperty("NavPropertyETKeyNavOne", 
EntityTypeProvider.nameETKeyNav, false)
+        .isType(EntityTypeProvider.nameETKeyNav)
+        .goUpExpandValidator()
+        .goSelectItem(0)
+        .first().isComplexProperty("PropertyCompNav", 
ComplexTypeProvider.nameCTNavFiveProp, false)
+        .n().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, 
false);
+
+    testUri.runEx("ESKeyNav", "$expand=undefined")
+        .isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
+    testUri.runEx("ESTwoKeyNav", "$expand=PropertyCompNav/undefined")
+        .isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
+    testUri.runEx("ESTwoKeyNav", "$expand=PropertyCompNav/*+")
+        
.isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
+  }
+
+  @Test
+  public void duplicatedSystemQueryOptionsInExpand() throws Exception {
+    testUri.runEx("ESKeyNav", 
"$expand=NavPropertyETKeyNavOne($select=PropertyInt16;$select=PropertyInt16)")
+        
.isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
+
+    testUri.runEx("ESKeyNav", 
"$expand=NavPropertyETKeyNavOne($filter=true;$filter=true)")
+        
.isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
+
+    testUri.runEx("ESKeyNav", 
"$expand=NavPropertyETKeyNavOne($orderby=PropertyInt16;$orderby=PropertyInt16)")
+        
.isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
+
+    testUri.runEx("ESKeyNav", 
"$expand=NavPropertyETKeyNavOne($levels=2;$levels=3)")
+        
.isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
+
+    testUri.runEx("ESKeyNav", 
"$expand=NavPropertyETKeyNavOne($expand=*;$expand=*)")
+        
.isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
+
+    testUri.runEx("ESKeyNav", 
"$expand=NavPropertyETKeyNavOne($count=true;$count=true)")
+        
.isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
+
+    testUri.runEx("ESKeyNav", "$expand=NavPropertyETKeyNavOne($top=1;$top=1)")
+        
.isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
+
+    testUri.runEx("ESKeyNav", 
"$expand=NavPropertyETKeyNavOne($skip=2;$skip=2)")
+        
.isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
+
+    testUri.runEx("ESKeyNav", 
"$expand=NavPropertyETKeyNavOne($search=Test;$search=Test)")
+        
.isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
+  }
+
+  @Test
+  public void simpleKeyInExpandSystemQueryOption() throws Exception {
+    testUri.runEx("ESAllPrim(0)", 
"$expand=NavPropertyETTwoPrimMany(-365)($filter=PropertyString eq 'Test 
String1')")
+        .isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
+  }
+
+  @Test
+  public void compoundKeyInExpandSystemQueryOption() throws Exception {
+    testUri.runEx("ESAllPrim(0)", 
"$expand=NavPropertyETTwoPrimMany(PropertyInt16=1,PropertyString=2)"
+        + "($filter=PropertyString eq 'Test String1')")
+        .isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
+  }
+
+  @Test
+  public void keyPredicatesInExpandFilter() throws Exception {
+    testUri.run("ESKeyNav(0)", 
"$expand=NavPropertyETTwoKeyNavMany($filter=NavPropertyETTwoKeyNavMany"
+        + "(PropertyInt16=1,PropertyString='2')/PropertyInt16 eq 
1)").goExpand()
+        .first().goPath().isNavProperty("NavPropertyETTwoKeyNavMany", 
EntityTypeProvider.nameETTwoKeyNav, true)
+        .goUpExpandValidator().goFilter()
+        .is("<<NavPropertyETTwoKeyNavMany/PropertyInt16> eq <1>>");
+  }
+
+  @Test
+  public void keyPredicatesInDoubleExpandedFilter() throws Exception {
+    testUri.run("ESKeyNav(0)", 
"$expand=NavPropertyETTwoKeyNavMany($expand=NavPropertyETTwoKeyNavMany"
+        + 
"($filter=NavPropertyETTwoKeyNavMany(PropertyInt16=1,PropertyString='2')/PropertyInt16
 eq 1))")
+        .goExpand()
+        .first().goPath().isNavProperty("NavPropertyETTwoKeyNavMany", 
EntityTypeProvider.nameETTwoKeyNav, true)
+        .goUpExpandValidator().goExpand()
+        .first().goPath().isNavProperty("NavPropertyETTwoKeyNavMany", 
EntityTypeProvider.nameETTwoKeyNav, true)
+        .goUpExpandValidator().goFilter()
+        .is("<<NavPropertyETTwoKeyNavMany/PropertyInt16> eq <1>>");
+  }
+}

Reply via email to