/test/System.Web.OData.Test/OData/Query/Expressions/FilterBinderTests.cs
C# | 2089 lines | 1778 code | 256 blank | 55 comment | 13 complexity | 575e2f5ba89b2fee515a917c248a6fbe MD5 | raw file
Possible License(s): Apache-2.0
Large files files are truncated, but you can click here to view the full file
- // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
- using System.Collections.Generic;
- using System.Data.Linq;
- using System.Globalization;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
- using System.Web.Http;
- using System.Web.Http.Dispatcher;
- using System.Web.OData.Builder;
- using System.Xml.Linq;
- using Microsoft.OData.Core;
- using Microsoft.OData.Core.UriParser;
- using Microsoft.OData.Core.UriParser.Semantic;
- using Microsoft.OData.Edm;
- using Microsoft.TestCommon;
- namespace System.Web.OData.Query.Expressions
- {
- public class FilterBinderTests
- {
- private const string NotTesting = "";
- private static readonly Uri _serviceBaseUri = new Uri("http://server/service/");
- private static Dictionary<Type, IEdmModel> _modelCache = new Dictionary<Type, IEdmModel>();
- public static TheoryDataSet<decimal?, bool, object> MathRoundDecimal_DataSet
- {
- get
- {
- return new TheoryDataSet<decimal?, bool, object>
- {
- { null, false, typeof(InvalidOperationException) },
- { 5.9m, true, true },
- { 5.4m, false, false },
- };
- }
- }
- public static TheoryDataSet<decimal?, bool, object> MathFloorDecimal_DataSet
- {
- get
- {
- return new TheoryDataSet<decimal?, bool, object>
- {
- { null, false, typeof(InvalidOperationException) },
- { 5.4m, true, true },
- { 4.4m, false, false },
- };
- }
- }
- public static TheoryDataSet<decimal?, bool, object> MathCeilingDecimal_DataSet
- {
- get
- {
- return new TheoryDataSet<decimal?, bool, object>
- {
- { null, false, typeof(InvalidOperationException) },
- { 4.1m, true, true },
- { 5.9m, false, false },
- };
- }
- }
- #region Inequalities
- [Theory]
- [InlineData(null, true, true)]
- [InlineData("", false, false)]
- [InlineData("Doritos", false, false)]
- public void EqualityOperatorWithNull(string productName, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "ProductName eq null",
- "$it => ($it.ProductName == null)");
- RunFilters(filters,
- new Product { ProductName = productName },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, false)]
- [InlineData("", false, false)]
- [InlineData("Doritos", true, true)]
- public void EqualityOperator(string productName, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "ProductName eq 'Doritos'",
- "$it => ($it.ProductName == \"Doritos\")");
- RunFilters(filters,
- new Product { ProductName = productName },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, true, true)]
- [InlineData("", true, true)]
- [InlineData("Doritos", false, false)]
- public void NotEqualOperator(string productName, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "ProductName ne 'Doritos'",
- "$it => ($it.ProductName != \"Doritos\")");
- RunFilters(filters,
- new Product { ProductName = productName },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, false)]
- [InlineData(5.01, true, true)]
- [InlineData(4.99, false, false)]
- public void GreaterThanOperator(object unitPrice, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "UnitPrice gt 5.00m",
- String.Format(CultureInfo.InvariantCulture, "$it => ($it.UnitPrice > Convert({0:0.00}))", 5.0),
- String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice > Convert({0:0.00})) == True)", 5.0));
- RunFilters(filters,
- new Product { UnitPrice = ToNullable<decimal>(unitPrice) },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, false)]
- [InlineData(5.0, true, true)]
- [InlineData(4.99, false, false)]
- public void GreaterThanEqualOperator(object unitPrice, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "UnitPrice ge 5.00m",
- String.Format(CultureInfo.InvariantCulture, "$it => ($it.UnitPrice >= Convert({0:0.00}))", 5.0),
- String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice >= Convert({0:0.00})) == True)", 5.0));
- RunFilters(filters,
- new Product { UnitPrice = ToNullable<decimal>(unitPrice) },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, false)]
- [InlineData(4.99, true, true)]
- [InlineData(5.01, false, false)]
- public void LessThanOperator(object unitPrice, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "UnitPrice lt 5.00m",
- String.Format(CultureInfo.InvariantCulture, "$it => ($it.UnitPrice < Convert({0:0.00}))", 5.0),
- NotTesting);
- RunFilters(filters,
- new Product { UnitPrice = ToNullable<decimal>(unitPrice) },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, false)]
- [InlineData(5.0, true, true)]
- [InlineData(5.01, false, false)]
- public void LessThanOrEqualOperator(object unitPrice, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "UnitPrice le 5.00m",
- String.Format(CultureInfo.InvariantCulture, "$it => ($it.UnitPrice <= Convert({0:0.00}))", 5.0),
- NotTesting);
- RunFilters(filters,
- new Product { UnitPrice = ToNullable<decimal>(unitPrice) },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Fact]
- public void NegativeNumbers()
- {
- VerifyQueryDeserialization(
- "UnitPrice le -5.00m",
- String.Format(CultureInfo.InvariantCulture, "$it => ($it.UnitPrice <= Convert({0:0.00}))", -5.0),
- NotTesting);
- }
- [Theory]
- [InlineData("DateTimeOffsetProp eq DateTimeOffsetProp", "$it => ($it.DateTimeOffsetProp == $it.DateTimeOffsetProp)")]
- [InlineData("DateTimeOffsetProp ne DateTimeOffsetProp", "$it => ($it.DateTimeOffsetProp != $it.DateTimeOffsetProp)")]
- [InlineData("DateTimeOffsetProp ge DateTimeOffsetProp", "$it => ($it.DateTimeOffsetProp >= $it.DateTimeOffsetProp)")]
- [InlineData("DateTimeOffsetProp le DateTimeOffsetProp", "$it => ($it.DateTimeOffsetProp <= $it.DateTimeOffsetProp)")]
- public void DateTimeOffsetInEqualities(string clause, string expectedExpression)
- {
- // There's currently a bug here. For now, the test checks for the presence of the bug (as a reminder to fix
- // the test once the bug is fixed).
- // The following assert shows the behavior with the bug and should be removed once the bug is fixed.
- Assert.Throws<ODataException>(() => Bind("" + clause));
- // TODO: Enable once ODataUriParser handles DateTimeOffsets
- // The following call shows the behavior without the bug, and should be enabled once the bug is fixed.
- //VerifyQueryDeserialization<DataTypes>("" + clause, expectedExpression);
- }
- [Theory]
- [InlineData("DateTimeProp eq DateTimeProp", "$it => ($it.DateTimeProp == $it.DateTimeProp)")]
- [InlineData("DateTimeProp ne DateTimeProp", "$it => ($it.DateTimeProp != $it.DateTimeProp)")]
- [InlineData("DateTimeProp ge DateTimeProp", "$it => ($it.DateTimeProp >= $it.DateTimeProp)")]
- [InlineData("DateTimeProp le DateTimeProp", "$it => ($it.DateTimeProp <= $it.DateTimeProp)")]
- public void DateInEqualities(string clause, string expectedExpression)
- {
- VerifyQueryDeserialization<DataTypes>(
- "" + clause,
- expectedExpression);
- }
- #endregion
- #region Logical Operators
- [Fact]
- [ReplaceCulture]
- public void BooleanOperatorNullableTypes()
- {
- VerifyQueryDeserialization(
- "UnitPrice eq 5.00m or CategoryID eq 0",
- "$it => (($it.UnitPrice == Convert(5.00)) OrElse ($it.CategoryID == 0))",
- NotTesting);
- }
- [Fact]
- public void BooleanComparisonOnNullableAndNonNullableType()
- {
- VerifyQueryDeserialization(
- "Discontinued eq true",
- "$it => ($it.Discontinued == Convert(True))",
- "$it => (($it.Discontinued == Convert(True)) == True)");
- }
- [Fact]
- public void BooleanComparisonOnNullableType()
- {
- VerifyQueryDeserialization(
- "Discontinued eq Discontinued",
- "$it => ($it.Discontinued == $it.Discontinued)",
- "$it => (($it.Discontinued == $it.Discontinued) == True)");
- }
- [Theory]
- [InlineData(null, null, false, false)]
- [InlineData(5.0, 0, true, true)]
- [InlineData(null, 1, false, false)]
- public void OrOperator(object unitPrice, object unitsInStock, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "UnitPrice eq 5.00m or UnitsInStock eq 0",
- String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice == Convert({0:0.00})) OrElse (Convert($it.UnitsInStock) == Convert({1})))", 5.0, 0),
- NotTesting);
- RunFilters(filters,
- new Product { UnitPrice = ToNullable<decimal>(unitPrice), UnitsInStock = ToNullable<short>(unitsInStock) },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, null, false, false)]
- [InlineData(5.0, 10, true, true)]
- [InlineData(null, 1, false, false)]
- public void AndOperator(object unitPrice, object unitsInStock, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "UnitPrice eq 5.00m and UnitsInStock eq 10.00m",
- String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice == Convert({0:0.00})) AndAlso (Convert($it.UnitsInStock) == Convert({1:0.00})))", 5.0, 10.0),
- NotTesting);
- RunFilters(filters,
- new Product { UnitPrice = ToNullable<decimal>(unitPrice), UnitsInStock = ToNullable<short>(unitsInStock) },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, true)] // This is an interesting cas for null propagation.
- [InlineData(5.0, false, false)]
- [InlineData(5.5, true, true)]
- public void Negation(object unitPrice, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "not (UnitPrice eq 5.00m)",
- String.Format(CultureInfo.InvariantCulture, "$it => Not(($it.UnitPrice == Convert({0:0.00})))", 5.0),
- NotTesting);
- RunFilters(filters,
- new Product { UnitPrice = ToNullable<decimal>(unitPrice) },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, true, true)] // This is an interesting cas for null propagation.
- [InlineData(true, false, false)]
- [InlineData(false, true, true)]
- public void BoolNegation(bool discontinued, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "not Discontinued",
- "$it => Convert(Not($it.Discontinued))",
- "$it => (Not($it.Discontinued) == True)");
- RunFilters(filters,
- new Product { Discontinued = ToNullable<bool>(discontinued) },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Fact]
- public void NestedNegation()
- {
- VerifyQueryDeserialization(
- "not (not(not (Discontinued)))",
- "$it => Convert(Not(Not(Not($it.Discontinued))))",
- "$it => (Not(Not(Not($it.Discontinued))) == True)");
- }
- #endregion
- #region Arithmetic Operators
- [Theory]
- [InlineData(null, false, false)]
- [InlineData(5.0, true, true)]
- [InlineData(15.01, false, false)]
- public void Subtraction(object unitPrice, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "UnitPrice sub 1.00m lt 5.00m",
- String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice - Convert({0:0.00})) < Convert({1:0.00}))", 1.0, 5.0),
- String.Format(CultureInfo.InvariantCulture, "$it => ((($it.UnitPrice - Convert({0:0.00})) < Convert({1:0.00})) == True)", 1.0, 5.0));
- RunFilters(filters,
- new Product { UnitPrice = ToNullable<decimal>(unitPrice) },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Fact]
- public void Addition()
- {
- VerifyQueryDeserialization(
- "UnitPrice add 1.00m lt 5.00m",
- String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice + Convert({0:0.00})) < Convert({1:0.00}))", 1.0, 5.0),
- NotTesting);
- }
- [Fact]
- public void Multiplication()
- {
- VerifyQueryDeserialization(
- "UnitPrice mul 1.00m lt 5.00m",
- String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice * Convert({0:0.00})) < Convert({1:0.00}))", 1.0, 5.0),
- NotTesting);
- }
- [Fact]
- public void Division()
- {
- VerifyQueryDeserialization(
- "UnitPrice div 1.00m lt 5.00m",
- String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice / Convert({0:0.00})) < Convert({1:0.00}))", 1.0, 5.0),
- NotTesting);
- }
- [Fact]
- public void Modulo()
- {
- VerifyQueryDeserialization(
- "UnitPrice mod 1.00m lt 5.00m",
- String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice % Convert({0:0.00})) < Convert({1:0.00}))", 1.0, 5.0),
- NotTesting);
- }
- #endregion
- # region NULL handling
- [Theory]
- [InlineData("UnitsInStock eq UnitsOnOrder", null, null, false, true)]
- [InlineData("UnitsInStock ne UnitsOnOrder", null, null, false, false)]
- [InlineData("UnitsInStock gt UnitsOnOrder", null, null, false, false)]
- [InlineData("UnitsInStock ge UnitsOnOrder", null, null, false, false)]
- [InlineData("UnitsInStock lt UnitsOnOrder", null, null, false, false)]
- [InlineData("UnitsInStock le UnitsOnOrder", null, null, false, false)]
- [InlineData("(UnitsInStock add UnitsOnOrder) eq UnitsInStock", null, null, false, true)]
- [InlineData("(UnitsInStock sub UnitsOnOrder) eq UnitsInStock", null, null, false, true)]
- [InlineData("(UnitsInStock mul UnitsOnOrder) eq UnitsInStock", null, null, false, true)]
- [InlineData("(UnitsInStock div UnitsOnOrder) eq UnitsInStock", null, null, false, true)]
- [InlineData("(UnitsInStock mod UnitsOnOrder) eq UnitsInStock", null, null, false, true)]
- [InlineData("UnitsInStock eq UnitsOnOrder", 1, null, false, false)]
- [InlineData("UnitsInStock eq UnitsOnOrder", 1, 1, true, true)]
- public void NullHandling(string filter, object unitsInStock, object unitsOnOrder, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization("" + filter);
- RunFilters(filters,
- new Product { UnitsInStock = ToNullable<short>(unitsInStock), UnitsOnOrder = ToNullable<short>(unitsOnOrder) },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData("UnitsInStock eq null", null, true, true)] // NULL == constant NULL is true when null propagation is enabled
- [InlineData("UnitsInStock ne null", null, false, false)] // NULL != constant NULL is false when null propagation is enabled
- public void NullHandling_LiteralNull(string filter, object unitsInStock, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization("" + filter);
- RunFilters(filters,
- new Product { UnitsInStock = ToNullable<short>(unitsInStock) },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- #endregion
- [Theory]
- [InlineData("StringProp gt 'Middle'", "Middle", false)]
- [InlineData("StringProp ge 'Middle'", "Middle", true)]
- [InlineData("StringProp lt 'Middle'", "Middle", false)]
- [InlineData("StringProp le 'Middle'", "Middle", true)]
- [InlineData("StringProp ge StringProp", "", true)]
- [InlineData("StringProp gt null", "", true)]
- [InlineData("null gt StringProp", "", false)]
- [InlineData("'Middle' gt StringProp", "Middle", false)]
- [InlineData("'a' lt 'b'", "", true)]
- public void StringComparisons_Work(string filter, string value, bool expectedResult)
- {
- var filters = VerifyQueryDeserialization<DataTypes>(filter);
- var result = RunFilter(filters.WithoutNullPropagation, new DataTypes { StringProp = value });
- Assert.Equal(result, expectedResult);
- }
- // Issue: 477
- [Theory]
- [InlineData("indexof('hello', StringProp) gt UIntProp")]
- [InlineData("indexof('hello', StringProp) gt ULongProp")]
- [InlineData("indexof('hello', StringProp) gt UShortProp")]
- [InlineData("indexof('hello', StringProp) gt NullableUShortProp")]
- [InlineData("indexof('hello', StringProp) gt NullableUIntProp")]
- [InlineData("indexof('hello', StringProp) gt NullableULongProp")]
- public void ComparisonsInvolvingCastsAndNullableValues(string filter)
- {
- var filters = VerifyQueryDeserialization<DataTypes>(filter);
- RunFilters(filters,
- new DataTypes(),
- new { WithNullPropagation = false, WithoutNullPropagation = typeof(ArgumentNullException) });
- }
- [Theory]
- [InlineData(null, null, true, true)]
- [InlineData("not doritos", 0, true, true)]
- [InlineData("Doritos", 1, false, false)]
- public void Grouping(string productName, object unitsInStock, bool withNullPropagation, bool withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "((ProductName ne 'Doritos') or (UnitPrice lt 5.00m))",
- String.Format(CultureInfo.InvariantCulture, "$it => (($it.ProductName != \"Doritos\") OrElse ($it.UnitPrice < Convert({0:0.00})))", 5.0),
- NotTesting);
- RunFilters(filters,
- new Product { ProductName = productName, UnitsInStock = ToNullable<short>(unitsInStock) },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Fact]
- public void MemberExpressions()
- {
- var filters = VerifyQueryDeserialization(
- "Category/CategoryName eq 'Snacks'",
- "$it => ($it.Category.CategoryName == \"Snacks\")",
- "$it => (IIF(($it.Category == null), null, $it.Category.CategoryName) == \"Snacks\")");
- RunFilters(filters,
- new Product { },
- new { WithNullPropagation = false, WithoutNullPropagation = typeof(NullReferenceException) });
- RunFilters(filters,
- new Product { Category = new Category { CategoryName = "Snacks" } },
- new { WithNullPropagation = true, WithoutNullPropagation = true });
- }
- [Fact]
- public void MemberExpressionsRecursive()
- {
- var filters = VerifyQueryDeserialization(
- "Category/Product/Category/CategoryName eq 'Snacks'",
- "$it => ($it.Category.Product.Category.CategoryName == \"Snacks\")",
- NotTesting);
- RunFilters(filters,
- new Product { },
- new { WithNullPropagation = false, WithoutNullPropagation = typeof(NullReferenceException) });
- }
- [Fact]
- public void ComplexPropertyNavigation()
- {
- var filters = VerifyQueryDeserialization(
- "SupplierAddress/City eq 'Redmond'",
- "$it => ($it.SupplierAddress.City == \"Redmond\")",
- "$it => (IIF(($it.SupplierAddress == null), null, $it.SupplierAddress.City) == \"Redmond\")");
- RunFilters(filters,
- new Product { },
- new { WithNullPropagation = false, WithoutNullPropagation = typeof(NullReferenceException) });
- RunFilters(filters,
- new Product { SupplierAddress = new Address { City = "Redmond" } },
- new { WithNullPropagation = true, WithoutNullPropagation = true });
- }
- #region Any/All
- [Fact]
- public void AnyOnNavigationEnumerableCollections()
- {
- var filters = VerifyQueryDeserialization(
- "Category/EnumerableProducts/any(P: P/ProductName eq 'Snacks')",
- "$it => $it.Category.EnumerableProducts.Any(P => (P.ProductName == \"Snacks\"))",
- NotTesting);
- RunFilters(filters,
- new Product
- {
- Category = new Category
- {
- EnumerableProducts = new Product[]
- {
- new Product { ProductName = "Snacks" },
- new Product { ProductName = "NonSnacks" }
- }
- }
- },
- new { WithNullPropagation = true, WithoutNullPropagation = true });
- RunFilters(filters,
- new Product
- {
- Category = new Category
- {
- EnumerableProducts = new Product[]
- {
- new Product { ProductName = "NonSnacks" }
- }
- }
- },
- new { WithNullPropagation = false, WithoutNullPropagation = false });
- }
- [Fact]
- public void AnyOnNavigationQueryableCollections()
- {
- var filters = VerifyQueryDeserialization(
- "Category/QueryableProducts/any(P: P/ProductName eq 'Snacks')",
- "$it => $it.Category.QueryableProducts.Any(P => (P.ProductName == \"Snacks\"))",
- NotTesting);
- RunFilters(filters,
- new Product
- {
- Category = new Category
- {
- QueryableProducts = new Product[]
- {
- new Product { ProductName = "Snacks" },
- new Product { ProductName = "NonSnacks" }
- }.AsQueryable()
- }
- },
- new { WithNullPropagation = true, WithoutNullPropagation = true });
- RunFilters(filters,
- new Product
- {
- Category = new Category
- {
- QueryableProducts = new Product[]
- {
- new Product { ProductName = "NonSnacks" }
- }.AsQueryable()
- }
- },
- new { WithNullPropagation = false, WithoutNullPropagation = false });
- }
- [Fact]
- public void AnyOnNavigation_NullCollection()
- {
- var filters = VerifyQueryDeserialization(
- "Category/EnumerableProducts/any(P: P/ProductName eq 'Snacks')",
- "$it => $it.Category.EnumerableProducts.Any(P => (P.ProductName == \"Snacks\"))",
- NotTesting);
- RunFilters(filters,
- new Product
- {
- Category = new Category
- {
- }
- },
- new { WithNullPropagation = false, WithoutNullPropagation = typeof(ArgumentNullException) });
- RunFilters(filters,
- new Product
- {
- Category = new Category
- {
- EnumerableProducts = new Product[]
- {
- new Product { ProductName = "Snacks" }
- }
- }
- },
- new { WithNullPropagation = true, WithoutNullPropagation = true });
- }
- [Fact]
- public void AllOnNavigation_NullCollection()
- {
- var filters = VerifyQueryDeserialization(
- "Category/EnumerableProducts/all(P: P/ProductName eq 'Snacks')",
- "$it => $it.Category.EnumerableProducts.All(P => (P.ProductName == \"Snacks\"))",
- NotTesting);
- RunFilters(filters,
- new Product
- {
- Category = new Category
- {
- }
- },
- new { WithNullPropagation = false, WithoutNullPropagation = typeof(ArgumentNullException) });
- RunFilters(filters,
- new Product
- {
- Category = new Category
- {
- EnumerableProducts = new Product[]
- {
- new Product { ProductName = "Snacks" }
- }
- }
- },
- new { WithNullPropagation = true, WithoutNullPropagation = true });
- }
- [Fact]
- public void MultipleAnys_WithSameRangeVariableName()
- {
- VerifyQueryDeserialization(
- "AlternateIDs/any(n: n eq 42) and AlternateAddresses/any(n : n/City eq 'Redmond')",
- "$it => ($it.AlternateIDs.Any(n => (n == 42)) AndAlso $it.AlternateAddresses.Any(n => (n.City == \"Redmond\")))",
- NotTesting);
- }
- [Fact]
- public void MultipleAlls_WithSameRangeVariableName()
- {
- VerifyQueryDeserialization(
- "AlternateIDs/all(n: n eq 42) and AlternateAddresses/all(n : n/City eq 'Redmond')",
- "$it => ($it.AlternateIDs.All(n => (n == 42)) AndAlso $it.AlternateAddresses.All(n => (n.City == \"Redmond\")))",
- NotTesting);
- }
- [Fact]
- public void AnyOnNavigationEnumerableCollections_EmptyFilter()
- {
- VerifyQueryDeserialization(
- "Category/EnumerableProducts/any()",
- "$it => $it.Category.EnumerableProducts.Any()",
- NotTesting);
- }
- [Fact]
- public void AnyOnNavigationQueryableCollections_EmptyFilter()
- {
- VerifyQueryDeserialization(
- "Category/QueryableProducts/any()",
- "$it => $it.Category.QueryableProducts.Any()",
- NotTesting);
- }
- [Fact]
- public void AllOnNavigationEnumerableCollections()
- {
- VerifyQueryDeserialization(
- "Category/EnumerableProducts/all(P: P/ProductName eq 'Snacks')",
- "$it => $it.Category.EnumerableProducts.All(P => (P.ProductName == \"Snacks\"))",
- NotTesting);
- }
- [Fact]
- public void AllOnNavigationQueryableCollections()
- {
- VerifyQueryDeserialization(
- "Category/QueryableProducts/all(P: P/ProductName eq 'Snacks')",
- "$it => $it.Category.QueryableProducts.All(P => (P.ProductName == \"Snacks\"))",
- NotTesting);
- }
- [Fact]
- public void AnyInSequenceNotNested()
- {
- VerifyQueryDeserialization(
- "Category/QueryableProducts/any(P: P/ProductName eq 'Snacks') or Category/QueryableProducts/any(P2: P2/ProductName eq 'Snacks')",
- "$it => ($it.Category.QueryableProducts.Any(P => (P.ProductName == \"Snacks\")) OrElse $it.Category.QueryableProducts.Any(P2 => (P2.ProductName == \"Snacks\")))",
- NotTesting);
- }
- [Fact]
- public void AllInSequenceNotNested()
- {
- VerifyQueryDeserialization(
- "Category/QueryableProducts/all(P: P/ProductName eq 'Snacks') or Category/QueryableProducts/all(P2: P2/ProductName eq 'Snacks')",
- "$it => ($it.Category.QueryableProducts.All(P => (P.ProductName == \"Snacks\")) OrElse $it.Category.QueryableProducts.All(P2 => (P2.ProductName == \"Snacks\")))",
- NotTesting);
- }
- [Fact]
- public void AnyOnPrimitiveCollection()
- {
- var filters = VerifyQueryDeserialization(
- "AlternateIDs/any(id: id eq 42)",
- "$it => $it.AlternateIDs.Any(id => (id == 42))",
- NotTesting);
- RunFilters(
- filters,
- new Product { AlternateIDs = new[] { 1, 2, 42 } },
- new { WithNullPropagation = true, WithoutNullPropagation = true });
- RunFilters(
- filters,
- new Product { AlternateIDs = new[] { 1, 2 } },
- new { WithNullPropagation = false, WithoutNullPropagation = false });
- }
- [Fact]
- public void AllOnPrimitiveCollection()
- {
- VerifyQueryDeserialization(
- "AlternateIDs/all(id: id eq 42)",
- "$it => $it.AlternateIDs.All(id => (id == 42))",
- NotTesting);
- }
- [Fact]
- public void AnyOnComplexCollection()
- {
- var filters = VerifyQueryDeserialization(
- "AlternateAddresses/any(address: address/City eq 'Redmond')",
- "$it => $it.AlternateAddresses.Any(address => (address.City == \"Redmond\"))",
- NotTesting);
- RunFilters(
- filters,
- new Product { AlternateAddresses = new[] { new Address { City = "Redmond" } } },
- new { WithNullPropagation = true, WithoutNullPropagation = true });
- RunFilters(
- filters,
- new Product(),
- new { WithNullPropagation = false, WithoutNullPropagation = typeof(ArgumentNullException) });
- }
- [Fact]
- public void AllOnComplexCollection()
- {
- VerifyQueryDeserialization(
- "AlternateAddresses/all(address: address/City eq 'Redmond')",
- "$it => $it.AlternateAddresses.All(address => (address.City == \"Redmond\"))",
- NotTesting);
- }
- [Fact]
- public void RecursiveAllAny()
- {
- VerifyQueryDeserialization(
- "Category/QueryableProducts/all(P: P/Category/EnumerableProducts/any(PP: PP/ProductName eq 'Snacks'))",
- "$it => $it.Category.QueryableProducts.All(P => P.Category.EnumerableProducts.Any(PP => (PP.ProductName == \"Snacks\")))",
- NotTesting);
- }
- #endregion
- #region String Functions
- [Theory]
- [InlineData("Abcd", -1, "Abcd", true, typeof(ArgumentOutOfRangeException))]
- [InlineData("Abcd", 0, "Abcd", true, true)]
- [InlineData("Abcd", 1, "bcd", true, true)]
- [InlineData("Abcd", 3, "d", true, true)]
- [InlineData("Abcd", 4, "", true, true)]
- [InlineData("Abcd", 5, "", true, typeof(ArgumentOutOfRangeException))]
- public void StringSubstringStart(string productName, int startIndex, string compareString, bool withNullPropagation, object withoutNullPropagation)
- {
- string filter = String.Format("substring(ProductName, {0}) eq '{1}'", startIndex, compareString);
- var filters = VerifyQueryDeserialization(filter);
- RunFilters(filters,
- new Product { ProductName = productName },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData("Abcd", -1, 4, "Abcd", true, typeof(ArgumentOutOfRangeException))]
- [InlineData("Abcd", -1, 3, "Abc", true, typeof(ArgumentOutOfRangeException))]
- [InlineData("Abcd", 0, 1, "A", true, true)]
- [InlineData("Abcd", 0, 4, "Abcd", true, true)]
- [InlineData("Abcd", 0, 3, "Abc", true, true)]
- [InlineData("Abcd", 0, 5, "Abcd", true, typeof(ArgumentOutOfRangeException))]
- [InlineData("Abcd", 1, 3, "bcd", true, true)]
- [InlineData("Abcd", 1, 5, "bcd", true, typeof(ArgumentOutOfRangeException))]
- [InlineData("Abcd", 2, 1, "c", true, true)]
- [InlineData("Abcd", 3, 1, "d", true, true)]
- [InlineData("Abcd", 4, 1, "", true, typeof(ArgumentOutOfRangeException))]
- [InlineData("Abcd", 0, -1, "", true, typeof(ArgumentOutOfRangeException))]
- [InlineData("Abcd", 5, -1, "", true, typeof(ArgumentOutOfRangeException))]
- public void StringSubstringStartAndLength(string productName, int startIndex, int length, string compareString, bool withNullPropagation, object withoutNullPropagation)
- {
- string filter = String.Format("substring(ProductName, {0}, {1}) eq '{2}'", startIndex, length, compareString);
- var filters = VerifyQueryDeserialization(filter);
- RunFilters(filters,
- new Product { ProductName = productName },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, typeof(NullReferenceException))]
- [InlineData("Abcd", true, true)]
- [InlineData("Abd", false, false)]
- public void StringSubstringOf(string productName, bool withNullPropagation, object withoutNullPropagation)
- {
- // In OData, the order of parameters is actually reversed in the resulting
- // String.Contains expression
- var filters = VerifyQueryDeserialization(
- "contains(ProductName, 'Abc')",
- "$it => $it.ProductName.Contains(\"Abc\")",
- NotTesting);
- RunFilters(filters,
- new Product { ProductName = productName },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- filters = VerifyQueryDeserialization(
- "contains(ProductName, 'Abc')",
- "$it => $it.ProductName.Contains(\"Abc\")",
- NotTesting);
- }
- [Theory]
- [InlineData(null, false, typeof(NullReferenceException))]
- [InlineData("Abcd", true, true)]
- [InlineData("Abd", false, false)]
- public void StringStartsWith(string productName, bool withNullPropagation, object withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "startswith(ProductName, 'Abc')",
- "$it => $it.ProductName.StartsWith(\"Abc\")",
- NotTesting);
- RunFilters(filters,
- new Product { ProductName = productName },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, typeof(NullReferenceException))]
- [InlineData("AAbc", true, true)]
- [InlineData("Abcd", false, false)]
- public void StringEndsWith(string productName, bool withNullPropagation, object withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "endswith(ProductName, 'Abc')",
- "$it => $it.ProductName.EndsWith(\"Abc\")",
- NotTesting);
- RunFilters(filters,
- new Product { ProductName = productName },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, typeof(NullReferenceException))]
- [InlineData("AAbc", true, true)]
- [InlineData("", false, false)]
- public void StringLength(string productName, bool withNullPropagation, object withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "length(ProductName) gt 0",
- "$it => ($it.ProductName.Length > 0)",
- "$it => ((IIF(($it.ProductName == null), null, Convert($it.ProductName.Length)) > Convert(0)) == True)");
- RunFilters(filters,
- new Product { ProductName = productName },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, typeof(NullReferenceException))]
- [InlineData("12345Abc", true, true)]
- [InlineData("1234Abc", false, false)]
- public void StringIndexOf(string productName, bool withNullPropagation, object withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "indexof(ProductName, 'Abc') eq 5",
- "$it => ($it.ProductName.IndexOf(\"Abc\") == 5)",
- NotTesting);
- RunFilters(filters,
- new Product { ProductName = productName },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, typeof(NullReferenceException))]
- [InlineData("123uctName", true, true)]
- [InlineData("1234Abc", false, false)]
- public void StringSubstring(string productName, bool withNullPropagation, object withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "substring(ProductName, 3) eq 'uctName'",
- "$it => ($it.ProductName.Substring(3) == \"uctName\")",
- NotTesting);
- RunFilters(filters,
- new Product { ProductName = productName },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- VerifyQueryDeserialization(
- "substring(ProductName, 3, 4) eq 'uctN'",
- "$it => ($it.ProductName.Substring(3, 4) == \"uctN\")",
- NotTesting);
- }
- [Theory]
- [InlineData(null, false, typeof(NullReferenceException))]
- [InlineData("Tasty Treats", true, true)]
- [InlineData("Tasty Treatss", false, false)]
- public void StringToLower(string productName, bool withNullPropagation, object withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "tolower(ProductName) eq 'tasty treats'",
- "$it => ($it.ProductName.ToLower() == \"tasty treats\")",
- NotTesting);
- RunFilters(filters,
- new Product { ProductName = productName },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, typeof(NullReferenceException))]
- [InlineData("Tasty Treats", true, true)]
- [InlineData("Tasty Treatss", false, false)]
- public void StringToUpper(string productName, bool withNullPropagation, object withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "toupper(ProductName) eq 'TASTY TREATS'",
- "$it => ($it.ProductName.ToUpper() == \"TASTY TREATS\")",
- NotTesting);
- RunFilters(filters,
- new Product { ProductName = productName },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, typeof(NullReferenceException))]
- [InlineData("Tasty Treats", true, true)]
- [InlineData("Tasty Treatss", false, false)]
- public void StringTrim(string productName, bool withNullPropagation, object withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "trim(ProductName) eq 'Tasty Treats'",
- "$it => ($it.ProductName.Trim() == \"Tasty Treats\")",
- NotTesting);
- RunFilters(filters,
- new Product { ProductName = productName },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Fact]
- public void StringConcat()
- {
- var filters = VerifyQueryDeserialization(
- "concat('Food', 'Bar') eq 'FoodBar'",
- "$it => (\"Food\".Concat(\"Bar\") == \"FoodBar\")",
- NotTesting);
- RunFilters(filters,
- new Product { },
- new { WithNullPropagation = true, WithoutNullPropagation = true });
- }
- [Fact]
- public void RecursiveMethodCall()
- {
- var filters = VerifyQueryDeserialization(
- "floor(floor(UnitPrice)) eq 123m",
- "$it => ($it.UnitPrice.Value.Floor().Floor() == 123)",
- NotTesting);
- RunFilters(filters,
- new Product { },
- new { WithNullPropagation = false, WithoutNullPropagation = typeof(InvalidOperationException) });
- }
- #endregion
- #region Date Functions
- [Fact]
- public void DateDay()
- {
- var filters = VerifyQueryDeserialization(
- "day(DiscontinuedDate) eq 8",
- "$it => ($it.DiscontinuedDate.Value.Day == 8)",
- NotTesting);
- RunFilters(filters,
- new Product { },
- new { WithNullPropagation = false, WithoutNullPropagation = typeof(InvalidOperationException) });
- RunFilters(filters,
- new Product { DiscontinuedDate = new DateTime(2000, 10, 8) },
- new { WithNullPropagation = true, WithoutNullPropagation = true });
- }
- public void DateDayNonNullable()
- {
- VerifyQueryDeserialization(
- "day(NonNullableDiscontinuedDate) eq 8",
- "$it => ($it.NonNullableDiscontinuedDate.Day == 8)");
- }
- [Fact]
- public void DateMonth()
- {
- VerifyQueryDeserialization(
- "month(DiscontinuedDate) eq 8",
- "$it => ($it.DiscontinuedDate.Value.Month == 8)",
- NotTesting);
- }
- [Fact]
- public void DateYear()
- {
- VerifyQueryDeserialization(
- "year(DiscontinuedDate) eq 1974",
- "$it => ($it.DiscontinuedDate.Value.Year == 1974)",
- NotTesting);
- }
- [Fact]
- public void DateHour()
- {
- VerifyQueryDeserialization("hour(DiscontinuedDate) eq 8",
- "$it => ($it.DiscontinuedDate.Value.Hour == 8)",
- NotTesting);
- }
- [Fact]
- public void DateMinute()
- {
- VerifyQueryDeserialization(
- "minute(DiscontinuedDate) eq 12",
- "$it => ($it.DiscontinuedDate.Value.Minute == 12)",
- NotTesting);
- }
- [Fact]
- public void DateSecond()
- {
- VerifyQueryDeserialization(
- "second(DiscontinuedDate) eq 33",
- "$it => ($it.DiscontinuedDate.Value.Second == 33)",
- NotTesting);
- }
- [Theory]
- [InlineData("year(DiscontinuedOffset) eq 100", "$it => ($it.DiscontinuedOffset.Year == 100)")]
- [InlineData("month(DiscontinuedOffset) eq 100", "$it => ($it.DiscontinuedOffset.Month == 100)")]
- [InlineData("day(DiscontinuedOffset) eq 100", "$it => ($it.DiscontinuedOffset.Day == 100)")]
- [InlineData("hour(DiscontinuedOffset) eq 100", "$it => ($it.DiscontinuedOffset.Hour == 100)")]
- [InlineData("minute(DiscontinuedOffset) eq 100", "$it => ($it.DiscontinuedOffset.Minute == 100)")]
- [InlineData("second(DiscontinuedOffset) eq 100", "$it => ($it.DiscontinuedOffset.Second == 100)")]
- public void DateTimeOffsetFunctions(string filter, string expression)
- {
- VerifyQueryDeserialization(filter, expression);
- }
- [Theory]
- [InlineData("years(DiscontinuedSince) eq 100", "$it => $it.DiscontinuedSince.Years == 100")]
- [InlineData("months(DiscontinuedSince) eq 100", "$it => $it.DiscontinuedSince.Months == 100")]
- [InlineData("days(DiscontinuedSince) eq 100", "$it => $it.DiscontinuedSince.Days == 100")]
- [InlineData("hours(DiscontinuedSince) eq 100", "$it => $it.DiscontinuedSince.Hours == 100")]
- [InlineData("minutes(DiscontinuedSince) eq 100", "$it => $it.DiscontinuedSince.Minutes == 100")]
- [InlineData("seconds(DiscontinuedSince) eq 100", "$it => $it.DiscontinuedSince.Seconds == 100")]
- public void TimespanFunctions(string filter, string expression)
- {
- // There's currently a bug here. For now, the test checks for the presence of the bug (as a reminder to fix
- // the test once the bug is fixed).
- // The following assert shows the behavior with the bug and should be removed once the bug is fixed.
- Assert.Throws<ODataException>(() => Bind(filter));
- // TODO: Timespans are not handled well in the uri parser
- // The following call shows the behavior without the bug, and should be enabled once the bug is fixed.
- //VerifyQueryDeserialization(filter, expression);
- }
- #endregion
- #region Math Functions
- [Theory, PropertyData("MathRoundDecimal_DataSet")]
- public void MathRoundDecimal(decimal? unitPrice, bool withNullPropagation, object withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "round(UnitPrice) gt 5.00m",
- String.Format(CultureInfo.InvariantCulture, "$it => ($it.UnitPrice.Value.Round() > {0:0.00})", 5.0),
- NotTesting);
- RunFilters(filters,
- new Product { UnitPrice = ToNullable<decimal>(unitPrice) },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, typeof(InvalidOperationException))]
- [InlineData(5.9d, true, true)]
- [InlineData(5.4d, false, false)]
- public void MathRoundDouble(double? weight, bool withNullPropagation, object withoutNullPropagation)
- {
- var filters = VerifyQueryDeserialization(
- "round(Weight) gt 5d",
- String.Format(CultureInfo.InvariantCulture, "$it => ($it.Weight.Value.Round() > {0})", 5),
- NotTesting);
- RunFilters(filters,
- new Product { Weight = ToNullable<double>(weight) },
- new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
- }
- [Theory]
- [InlineData(null, false, typeof(InvalidOperationException))]
- [InlineData(5.9f, true, true)]
- [InlineData(5.4f, false, false)]
- public void M…
Large files files are truncated, but you can click here to view the full file