PageRenderTime 60ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/test/System.Web.OData.Test/OData/Query/Expressions/FilterBinderTests.cs

https://github.com/huyq2002/aspnetwebstack
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

  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. using System.Collections.Generic;
  3. using System.Data.Linq;
  4. using System.Globalization;
  5. using System.Linq;
  6. using System.Linq.Expressions;
  7. using System.Reflection;
  8. using System.Web.Http;
  9. using System.Web.Http.Dispatcher;
  10. using System.Web.OData.Builder;
  11. using System.Xml.Linq;
  12. using Microsoft.OData.Core;
  13. using Microsoft.OData.Core.UriParser;
  14. using Microsoft.OData.Core.UriParser.Semantic;
  15. using Microsoft.OData.Edm;
  16. using Microsoft.TestCommon;
  17. namespace System.Web.OData.Query.Expressions
  18. {
  19. public class FilterBinderTests
  20. {
  21. private const string NotTesting = "";
  22. private static readonly Uri _serviceBaseUri = new Uri("http://server/service/");
  23. private static Dictionary<Type, IEdmModel> _modelCache = new Dictionary<Type, IEdmModel>();
  24. public static TheoryDataSet<decimal?, bool, object> MathRoundDecimal_DataSet
  25. {
  26. get
  27. {
  28. return new TheoryDataSet<decimal?, bool, object>
  29. {
  30. { null, false, typeof(InvalidOperationException) },
  31. { 5.9m, true, true },
  32. { 5.4m, false, false },
  33. };
  34. }
  35. }
  36. public static TheoryDataSet<decimal?, bool, object> MathFloorDecimal_DataSet
  37. {
  38. get
  39. {
  40. return new TheoryDataSet<decimal?, bool, object>
  41. {
  42. { null, false, typeof(InvalidOperationException) },
  43. { 5.4m, true, true },
  44. { 4.4m, false, false },
  45. };
  46. }
  47. }
  48. public static TheoryDataSet<decimal?, bool, object> MathCeilingDecimal_DataSet
  49. {
  50. get
  51. {
  52. return new TheoryDataSet<decimal?, bool, object>
  53. {
  54. { null, false, typeof(InvalidOperationException) },
  55. { 4.1m, true, true },
  56. { 5.9m, false, false },
  57. };
  58. }
  59. }
  60. #region Inequalities
  61. [Theory]
  62. [InlineData(null, true, true)]
  63. [InlineData("", false, false)]
  64. [InlineData("Doritos", false, false)]
  65. public void EqualityOperatorWithNull(string productName, bool withNullPropagation, bool withoutNullPropagation)
  66. {
  67. var filters = VerifyQueryDeserialization(
  68. "ProductName eq null",
  69. "$it => ($it.ProductName == null)");
  70. RunFilters(filters,
  71. new Product { ProductName = productName },
  72. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  73. }
  74. [Theory]
  75. [InlineData(null, false, false)]
  76. [InlineData("", false, false)]
  77. [InlineData("Doritos", true, true)]
  78. public void EqualityOperator(string productName, bool withNullPropagation, bool withoutNullPropagation)
  79. {
  80. var filters = VerifyQueryDeserialization(
  81. "ProductName eq 'Doritos'",
  82. "$it => ($it.ProductName == \"Doritos\")");
  83. RunFilters(filters,
  84. new Product { ProductName = productName },
  85. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  86. }
  87. [Theory]
  88. [InlineData(null, true, true)]
  89. [InlineData("", true, true)]
  90. [InlineData("Doritos", false, false)]
  91. public void NotEqualOperator(string productName, bool withNullPropagation, bool withoutNullPropagation)
  92. {
  93. var filters = VerifyQueryDeserialization(
  94. "ProductName ne 'Doritos'",
  95. "$it => ($it.ProductName != \"Doritos\")");
  96. RunFilters(filters,
  97. new Product { ProductName = productName },
  98. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  99. }
  100. [Theory]
  101. [InlineData(null, false, false)]
  102. [InlineData(5.01, true, true)]
  103. [InlineData(4.99, false, false)]
  104. public void GreaterThanOperator(object unitPrice, bool withNullPropagation, bool withoutNullPropagation)
  105. {
  106. var filters = VerifyQueryDeserialization(
  107. "UnitPrice gt 5.00m",
  108. String.Format(CultureInfo.InvariantCulture, "$it => ($it.UnitPrice > Convert({0:0.00}))", 5.0),
  109. String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice > Convert({0:0.00})) == True)", 5.0));
  110. RunFilters(filters,
  111. new Product { UnitPrice = ToNullable<decimal>(unitPrice) },
  112. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  113. }
  114. [Theory]
  115. [InlineData(null, false, false)]
  116. [InlineData(5.0, true, true)]
  117. [InlineData(4.99, false, false)]
  118. public void GreaterThanEqualOperator(object unitPrice, bool withNullPropagation, bool withoutNullPropagation)
  119. {
  120. var filters = VerifyQueryDeserialization(
  121. "UnitPrice ge 5.00m",
  122. String.Format(CultureInfo.InvariantCulture, "$it => ($it.UnitPrice >= Convert({0:0.00}))", 5.0),
  123. String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice >= Convert({0:0.00})) == True)", 5.0));
  124. RunFilters(filters,
  125. new Product { UnitPrice = ToNullable<decimal>(unitPrice) },
  126. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  127. }
  128. [Theory]
  129. [InlineData(null, false, false)]
  130. [InlineData(4.99, true, true)]
  131. [InlineData(5.01, false, false)]
  132. public void LessThanOperator(object unitPrice, bool withNullPropagation, bool withoutNullPropagation)
  133. {
  134. var filters = VerifyQueryDeserialization(
  135. "UnitPrice lt 5.00m",
  136. String.Format(CultureInfo.InvariantCulture, "$it => ($it.UnitPrice < Convert({0:0.00}))", 5.0),
  137. NotTesting);
  138. RunFilters(filters,
  139. new Product { UnitPrice = ToNullable<decimal>(unitPrice) },
  140. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  141. }
  142. [Theory]
  143. [InlineData(null, false, false)]
  144. [InlineData(5.0, true, true)]
  145. [InlineData(5.01, false, false)]
  146. public void LessThanOrEqualOperator(object unitPrice, bool withNullPropagation, bool withoutNullPropagation)
  147. {
  148. var filters = VerifyQueryDeserialization(
  149. "UnitPrice le 5.00m",
  150. String.Format(CultureInfo.InvariantCulture, "$it => ($it.UnitPrice <= Convert({0:0.00}))", 5.0),
  151. NotTesting);
  152. RunFilters(filters,
  153. new Product { UnitPrice = ToNullable<decimal>(unitPrice) },
  154. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  155. }
  156. [Fact]
  157. public void NegativeNumbers()
  158. {
  159. VerifyQueryDeserialization(
  160. "UnitPrice le -5.00m",
  161. String.Format(CultureInfo.InvariantCulture, "$it => ($it.UnitPrice <= Convert({0:0.00}))", -5.0),
  162. NotTesting);
  163. }
  164. [Theory]
  165. [InlineData("DateTimeOffsetProp eq DateTimeOffsetProp", "$it => ($it.DateTimeOffsetProp == $it.DateTimeOffsetProp)")]
  166. [InlineData("DateTimeOffsetProp ne DateTimeOffsetProp", "$it => ($it.DateTimeOffsetProp != $it.DateTimeOffsetProp)")]
  167. [InlineData("DateTimeOffsetProp ge DateTimeOffsetProp", "$it => ($it.DateTimeOffsetProp >= $it.DateTimeOffsetProp)")]
  168. [InlineData("DateTimeOffsetProp le DateTimeOffsetProp", "$it => ($it.DateTimeOffsetProp <= $it.DateTimeOffsetProp)")]
  169. public void DateTimeOffsetInEqualities(string clause, string expectedExpression)
  170. {
  171. // There's currently a bug here. For now, the test checks for the presence of the bug (as a reminder to fix
  172. // the test once the bug is fixed).
  173. // The following assert shows the behavior with the bug and should be removed once the bug is fixed.
  174. Assert.Throws<ODataException>(() => Bind("" + clause));
  175. // TODO: Enable once ODataUriParser handles DateTimeOffsets
  176. // The following call shows the behavior without the bug, and should be enabled once the bug is fixed.
  177. //VerifyQueryDeserialization<DataTypes>("" + clause, expectedExpression);
  178. }
  179. [Theory]
  180. [InlineData("DateTimeProp eq DateTimeProp", "$it => ($it.DateTimeProp == $it.DateTimeProp)")]
  181. [InlineData("DateTimeProp ne DateTimeProp", "$it => ($it.DateTimeProp != $it.DateTimeProp)")]
  182. [InlineData("DateTimeProp ge DateTimeProp", "$it => ($it.DateTimeProp >= $it.DateTimeProp)")]
  183. [InlineData("DateTimeProp le DateTimeProp", "$it => ($it.DateTimeProp <= $it.DateTimeProp)")]
  184. public void DateInEqualities(string clause, string expectedExpression)
  185. {
  186. VerifyQueryDeserialization<DataTypes>(
  187. "" + clause,
  188. expectedExpression);
  189. }
  190. #endregion
  191. #region Logical Operators
  192. [Fact]
  193. [ReplaceCulture]
  194. public void BooleanOperatorNullableTypes()
  195. {
  196. VerifyQueryDeserialization(
  197. "UnitPrice eq 5.00m or CategoryID eq 0",
  198. "$it => (($it.UnitPrice == Convert(5.00)) OrElse ($it.CategoryID == 0))",
  199. NotTesting);
  200. }
  201. [Fact]
  202. public void BooleanComparisonOnNullableAndNonNullableType()
  203. {
  204. VerifyQueryDeserialization(
  205. "Discontinued eq true",
  206. "$it => ($it.Discontinued == Convert(True))",
  207. "$it => (($it.Discontinued == Convert(True)) == True)");
  208. }
  209. [Fact]
  210. public void BooleanComparisonOnNullableType()
  211. {
  212. VerifyQueryDeserialization(
  213. "Discontinued eq Discontinued",
  214. "$it => ($it.Discontinued == $it.Discontinued)",
  215. "$it => (($it.Discontinued == $it.Discontinued) == True)");
  216. }
  217. [Theory]
  218. [InlineData(null, null, false, false)]
  219. [InlineData(5.0, 0, true, true)]
  220. [InlineData(null, 1, false, false)]
  221. public void OrOperator(object unitPrice, object unitsInStock, bool withNullPropagation, bool withoutNullPropagation)
  222. {
  223. var filters = VerifyQueryDeserialization(
  224. "UnitPrice eq 5.00m or UnitsInStock eq 0",
  225. String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice == Convert({0:0.00})) OrElse (Convert($it.UnitsInStock) == Convert({1})))", 5.0, 0),
  226. NotTesting);
  227. RunFilters(filters,
  228. new Product { UnitPrice = ToNullable<decimal>(unitPrice), UnitsInStock = ToNullable<short>(unitsInStock) },
  229. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  230. }
  231. [Theory]
  232. [InlineData(null, null, false, false)]
  233. [InlineData(5.0, 10, true, true)]
  234. [InlineData(null, 1, false, false)]
  235. public void AndOperator(object unitPrice, object unitsInStock, bool withNullPropagation, bool withoutNullPropagation)
  236. {
  237. var filters = VerifyQueryDeserialization(
  238. "UnitPrice eq 5.00m and UnitsInStock eq 10.00m",
  239. String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice == Convert({0:0.00})) AndAlso (Convert($it.UnitsInStock) == Convert({1:0.00})))", 5.0, 10.0),
  240. NotTesting);
  241. RunFilters(filters,
  242. new Product { UnitPrice = ToNullable<decimal>(unitPrice), UnitsInStock = ToNullable<short>(unitsInStock) },
  243. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  244. }
  245. [Theory]
  246. [InlineData(null, false, true)] // This is an interesting cas for null propagation.
  247. [InlineData(5.0, false, false)]
  248. [InlineData(5.5, true, true)]
  249. public void Negation(object unitPrice, bool withNullPropagation, bool withoutNullPropagation)
  250. {
  251. var filters = VerifyQueryDeserialization(
  252. "not (UnitPrice eq 5.00m)",
  253. String.Format(CultureInfo.InvariantCulture, "$it => Not(($it.UnitPrice == Convert({0:0.00})))", 5.0),
  254. NotTesting);
  255. RunFilters(filters,
  256. new Product { UnitPrice = ToNullable<decimal>(unitPrice) },
  257. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  258. }
  259. [Theory]
  260. [InlineData(null, true, true)] // This is an interesting cas for null propagation.
  261. [InlineData(true, false, false)]
  262. [InlineData(false, true, true)]
  263. public void BoolNegation(bool discontinued, bool withNullPropagation, bool withoutNullPropagation)
  264. {
  265. var filters = VerifyQueryDeserialization(
  266. "not Discontinued",
  267. "$it => Convert(Not($it.Discontinued))",
  268. "$it => (Not($it.Discontinued) == True)");
  269. RunFilters(filters,
  270. new Product { Discontinued = ToNullable<bool>(discontinued) },
  271. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  272. }
  273. [Fact]
  274. public void NestedNegation()
  275. {
  276. VerifyQueryDeserialization(
  277. "not (not(not (Discontinued)))",
  278. "$it => Convert(Not(Not(Not($it.Discontinued))))",
  279. "$it => (Not(Not(Not($it.Discontinued))) == True)");
  280. }
  281. #endregion
  282. #region Arithmetic Operators
  283. [Theory]
  284. [InlineData(null, false, false)]
  285. [InlineData(5.0, true, true)]
  286. [InlineData(15.01, false, false)]
  287. public void Subtraction(object unitPrice, bool withNullPropagation, bool withoutNullPropagation)
  288. {
  289. var filters = VerifyQueryDeserialization(
  290. "UnitPrice sub 1.00m lt 5.00m",
  291. String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice - Convert({0:0.00})) < Convert({1:0.00}))", 1.0, 5.0),
  292. String.Format(CultureInfo.InvariantCulture, "$it => ((($it.UnitPrice - Convert({0:0.00})) < Convert({1:0.00})) == True)", 1.0, 5.0));
  293. RunFilters(filters,
  294. new Product { UnitPrice = ToNullable<decimal>(unitPrice) },
  295. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  296. }
  297. [Fact]
  298. public void Addition()
  299. {
  300. VerifyQueryDeserialization(
  301. "UnitPrice add 1.00m lt 5.00m",
  302. String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice + Convert({0:0.00})) < Convert({1:0.00}))", 1.0, 5.0),
  303. NotTesting);
  304. }
  305. [Fact]
  306. public void Multiplication()
  307. {
  308. VerifyQueryDeserialization(
  309. "UnitPrice mul 1.00m lt 5.00m",
  310. String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice * Convert({0:0.00})) < Convert({1:0.00}))", 1.0, 5.0),
  311. NotTesting);
  312. }
  313. [Fact]
  314. public void Division()
  315. {
  316. VerifyQueryDeserialization(
  317. "UnitPrice div 1.00m lt 5.00m",
  318. String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice / Convert({0:0.00})) < Convert({1:0.00}))", 1.0, 5.0),
  319. NotTesting);
  320. }
  321. [Fact]
  322. public void Modulo()
  323. {
  324. VerifyQueryDeserialization(
  325. "UnitPrice mod 1.00m lt 5.00m",
  326. String.Format(CultureInfo.InvariantCulture, "$it => (($it.UnitPrice % Convert({0:0.00})) < Convert({1:0.00}))", 1.0, 5.0),
  327. NotTesting);
  328. }
  329. #endregion
  330. # region NULL handling
  331. [Theory]
  332. [InlineData("UnitsInStock eq UnitsOnOrder", null, null, false, true)]
  333. [InlineData("UnitsInStock ne UnitsOnOrder", null, null, false, false)]
  334. [InlineData("UnitsInStock gt UnitsOnOrder", null, null, false, false)]
  335. [InlineData("UnitsInStock ge UnitsOnOrder", null, null, false, false)]
  336. [InlineData("UnitsInStock lt UnitsOnOrder", null, null, false, false)]
  337. [InlineData("UnitsInStock le UnitsOnOrder", null, null, false, false)]
  338. [InlineData("(UnitsInStock add UnitsOnOrder) eq UnitsInStock", null, null, false, true)]
  339. [InlineData("(UnitsInStock sub UnitsOnOrder) eq UnitsInStock", null, null, false, true)]
  340. [InlineData("(UnitsInStock mul UnitsOnOrder) eq UnitsInStock", null, null, false, true)]
  341. [InlineData("(UnitsInStock div UnitsOnOrder) eq UnitsInStock", null, null, false, true)]
  342. [InlineData("(UnitsInStock mod UnitsOnOrder) eq UnitsInStock", null, null, false, true)]
  343. [InlineData("UnitsInStock eq UnitsOnOrder", 1, null, false, false)]
  344. [InlineData("UnitsInStock eq UnitsOnOrder", 1, 1, true, true)]
  345. public void NullHandling(string filter, object unitsInStock, object unitsOnOrder, bool withNullPropagation, bool withoutNullPropagation)
  346. {
  347. var filters = VerifyQueryDeserialization("" + filter);
  348. RunFilters(filters,
  349. new Product { UnitsInStock = ToNullable<short>(unitsInStock), UnitsOnOrder = ToNullable<short>(unitsOnOrder) },
  350. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  351. }
  352. [Theory]
  353. [InlineData("UnitsInStock eq null", null, true, true)] // NULL == constant NULL is true when null propagation is enabled
  354. [InlineData("UnitsInStock ne null", null, false, false)] // NULL != constant NULL is false when null propagation is enabled
  355. public void NullHandling_LiteralNull(string filter, object unitsInStock, bool withNullPropagation, bool withoutNullPropagation)
  356. {
  357. var filters = VerifyQueryDeserialization("" + filter);
  358. RunFilters(filters,
  359. new Product { UnitsInStock = ToNullable<short>(unitsInStock) },
  360. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  361. }
  362. #endregion
  363. [Theory]
  364. [InlineData("StringProp gt 'Middle'", "Middle", false)]
  365. [InlineData("StringProp ge 'Middle'", "Middle", true)]
  366. [InlineData("StringProp lt 'Middle'", "Middle", false)]
  367. [InlineData("StringProp le 'Middle'", "Middle", true)]
  368. [InlineData("StringProp ge StringProp", "", true)]
  369. [InlineData("StringProp gt null", "", true)]
  370. [InlineData("null gt StringProp", "", false)]
  371. [InlineData("'Middle' gt StringProp", "Middle", false)]
  372. [InlineData("'a' lt 'b'", "", true)]
  373. public void StringComparisons_Work(string filter, string value, bool expectedResult)
  374. {
  375. var filters = VerifyQueryDeserialization<DataTypes>(filter);
  376. var result = RunFilter(filters.WithoutNullPropagation, new DataTypes { StringProp = value });
  377. Assert.Equal(result, expectedResult);
  378. }
  379. // Issue: 477
  380. [Theory]
  381. [InlineData("indexof('hello', StringProp) gt UIntProp")]
  382. [InlineData("indexof('hello', StringProp) gt ULongProp")]
  383. [InlineData("indexof('hello', StringProp) gt UShortProp")]
  384. [InlineData("indexof('hello', StringProp) gt NullableUShortProp")]
  385. [InlineData("indexof('hello', StringProp) gt NullableUIntProp")]
  386. [InlineData("indexof('hello', StringProp) gt NullableULongProp")]
  387. public void ComparisonsInvolvingCastsAndNullableValues(string filter)
  388. {
  389. var filters = VerifyQueryDeserialization<DataTypes>(filter);
  390. RunFilters(filters,
  391. new DataTypes(),
  392. new { WithNullPropagation = false, WithoutNullPropagation = typeof(ArgumentNullException) });
  393. }
  394. [Theory]
  395. [InlineData(null, null, true, true)]
  396. [InlineData("not doritos", 0, true, true)]
  397. [InlineData("Doritos", 1, false, false)]
  398. public void Grouping(string productName, object unitsInStock, bool withNullPropagation, bool withoutNullPropagation)
  399. {
  400. var filters = VerifyQueryDeserialization(
  401. "((ProductName ne 'Doritos') or (UnitPrice lt 5.00m))",
  402. String.Format(CultureInfo.InvariantCulture, "$it => (($it.ProductName != \"Doritos\") OrElse ($it.UnitPrice < Convert({0:0.00})))", 5.0),
  403. NotTesting);
  404. RunFilters(filters,
  405. new Product { ProductName = productName, UnitsInStock = ToNullable<short>(unitsInStock) },
  406. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  407. }
  408. [Fact]
  409. public void MemberExpressions()
  410. {
  411. var filters = VerifyQueryDeserialization(
  412. "Category/CategoryName eq 'Snacks'",
  413. "$it => ($it.Category.CategoryName == \"Snacks\")",
  414. "$it => (IIF(($it.Category == null), null, $it.Category.CategoryName) == \"Snacks\")");
  415. RunFilters(filters,
  416. new Product { },
  417. new { WithNullPropagation = false, WithoutNullPropagation = typeof(NullReferenceException) });
  418. RunFilters(filters,
  419. new Product { Category = new Category { CategoryName = "Snacks" } },
  420. new { WithNullPropagation = true, WithoutNullPropagation = true });
  421. }
  422. [Fact]
  423. public void MemberExpressionsRecursive()
  424. {
  425. var filters = VerifyQueryDeserialization(
  426. "Category/Product/Category/CategoryName eq 'Snacks'",
  427. "$it => ($it.Category.Product.Category.CategoryName == \"Snacks\")",
  428. NotTesting);
  429. RunFilters(filters,
  430. new Product { },
  431. new { WithNullPropagation = false, WithoutNullPropagation = typeof(NullReferenceException) });
  432. }
  433. [Fact]
  434. public void ComplexPropertyNavigation()
  435. {
  436. var filters = VerifyQueryDeserialization(
  437. "SupplierAddress/City eq 'Redmond'",
  438. "$it => ($it.SupplierAddress.City == \"Redmond\")",
  439. "$it => (IIF(($it.SupplierAddress == null), null, $it.SupplierAddress.City) == \"Redmond\")");
  440. RunFilters(filters,
  441. new Product { },
  442. new { WithNullPropagation = false, WithoutNullPropagation = typeof(NullReferenceException) });
  443. RunFilters(filters,
  444. new Product { SupplierAddress = new Address { City = "Redmond" } },
  445. new { WithNullPropagation = true, WithoutNullPropagation = true });
  446. }
  447. #region Any/All
  448. [Fact]
  449. public void AnyOnNavigationEnumerableCollections()
  450. {
  451. var filters = VerifyQueryDeserialization(
  452. "Category/EnumerableProducts/any(P: P/ProductName eq 'Snacks')",
  453. "$it => $it.Category.EnumerableProducts.Any(P => (P.ProductName == \"Snacks\"))",
  454. NotTesting);
  455. RunFilters(filters,
  456. new Product
  457. {
  458. Category = new Category
  459. {
  460. EnumerableProducts = new Product[]
  461. {
  462. new Product { ProductName = "Snacks" },
  463. new Product { ProductName = "NonSnacks" }
  464. }
  465. }
  466. },
  467. new { WithNullPropagation = true, WithoutNullPropagation = true });
  468. RunFilters(filters,
  469. new Product
  470. {
  471. Category = new Category
  472. {
  473. EnumerableProducts = new Product[]
  474. {
  475. new Product { ProductName = "NonSnacks" }
  476. }
  477. }
  478. },
  479. new { WithNullPropagation = false, WithoutNullPropagation = false });
  480. }
  481. [Fact]
  482. public void AnyOnNavigationQueryableCollections()
  483. {
  484. var filters = VerifyQueryDeserialization(
  485. "Category/QueryableProducts/any(P: P/ProductName eq 'Snacks')",
  486. "$it => $it.Category.QueryableProducts.Any(P => (P.ProductName == \"Snacks\"))",
  487. NotTesting);
  488. RunFilters(filters,
  489. new Product
  490. {
  491. Category = new Category
  492. {
  493. QueryableProducts = new Product[]
  494. {
  495. new Product { ProductName = "Snacks" },
  496. new Product { ProductName = "NonSnacks" }
  497. }.AsQueryable()
  498. }
  499. },
  500. new { WithNullPropagation = true, WithoutNullPropagation = true });
  501. RunFilters(filters,
  502. new Product
  503. {
  504. Category = new Category
  505. {
  506. QueryableProducts = new Product[]
  507. {
  508. new Product { ProductName = "NonSnacks" }
  509. }.AsQueryable()
  510. }
  511. },
  512. new { WithNullPropagation = false, WithoutNullPropagation = false });
  513. }
  514. [Fact]
  515. public void AnyOnNavigation_NullCollection()
  516. {
  517. var filters = VerifyQueryDeserialization(
  518. "Category/EnumerableProducts/any(P: P/ProductName eq 'Snacks')",
  519. "$it => $it.Category.EnumerableProducts.Any(P => (P.ProductName == \"Snacks\"))",
  520. NotTesting);
  521. RunFilters(filters,
  522. new Product
  523. {
  524. Category = new Category
  525. {
  526. }
  527. },
  528. new { WithNullPropagation = false, WithoutNullPropagation = typeof(ArgumentNullException) });
  529. RunFilters(filters,
  530. new Product
  531. {
  532. Category = new Category
  533. {
  534. EnumerableProducts = new Product[]
  535. {
  536. new Product { ProductName = "Snacks" }
  537. }
  538. }
  539. },
  540. new { WithNullPropagation = true, WithoutNullPropagation = true });
  541. }
  542. [Fact]
  543. public void AllOnNavigation_NullCollection()
  544. {
  545. var filters = VerifyQueryDeserialization(
  546. "Category/EnumerableProducts/all(P: P/ProductName eq 'Snacks')",
  547. "$it => $it.Category.EnumerableProducts.All(P => (P.ProductName == \"Snacks\"))",
  548. NotTesting);
  549. RunFilters(filters,
  550. new Product
  551. {
  552. Category = new Category
  553. {
  554. }
  555. },
  556. new { WithNullPropagation = false, WithoutNullPropagation = typeof(ArgumentNullException) });
  557. RunFilters(filters,
  558. new Product
  559. {
  560. Category = new Category
  561. {
  562. EnumerableProducts = new Product[]
  563. {
  564. new Product { ProductName = "Snacks" }
  565. }
  566. }
  567. },
  568. new { WithNullPropagation = true, WithoutNullPropagation = true });
  569. }
  570. [Fact]
  571. public void MultipleAnys_WithSameRangeVariableName()
  572. {
  573. VerifyQueryDeserialization(
  574. "AlternateIDs/any(n: n eq 42) and AlternateAddresses/any(n : n/City eq 'Redmond')",
  575. "$it => ($it.AlternateIDs.Any(n => (n == 42)) AndAlso $it.AlternateAddresses.Any(n => (n.City == \"Redmond\")))",
  576. NotTesting);
  577. }
  578. [Fact]
  579. public void MultipleAlls_WithSameRangeVariableName()
  580. {
  581. VerifyQueryDeserialization(
  582. "AlternateIDs/all(n: n eq 42) and AlternateAddresses/all(n : n/City eq 'Redmond')",
  583. "$it => ($it.AlternateIDs.All(n => (n == 42)) AndAlso $it.AlternateAddresses.All(n => (n.City == \"Redmond\")))",
  584. NotTesting);
  585. }
  586. [Fact]
  587. public void AnyOnNavigationEnumerableCollections_EmptyFilter()
  588. {
  589. VerifyQueryDeserialization(
  590. "Category/EnumerableProducts/any()",
  591. "$it => $it.Category.EnumerableProducts.Any()",
  592. NotTesting);
  593. }
  594. [Fact]
  595. public void AnyOnNavigationQueryableCollections_EmptyFilter()
  596. {
  597. VerifyQueryDeserialization(
  598. "Category/QueryableProducts/any()",
  599. "$it => $it.Category.QueryableProducts.Any()",
  600. NotTesting);
  601. }
  602. [Fact]
  603. public void AllOnNavigationEnumerableCollections()
  604. {
  605. VerifyQueryDeserialization(
  606. "Category/EnumerableProducts/all(P: P/ProductName eq 'Snacks')",
  607. "$it => $it.Category.EnumerableProducts.All(P => (P.ProductName == \"Snacks\"))",
  608. NotTesting);
  609. }
  610. [Fact]
  611. public void AllOnNavigationQueryableCollections()
  612. {
  613. VerifyQueryDeserialization(
  614. "Category/QueryableProducts/all(P: P/ProductName eq 'Snacks')",
  615. "$it => $it.Category.QueryableProducts.All(P => (P.ProductName == \"Snacks\"))",
  616. NotTesting);
  617. }
  618. [Fact]
  619. public void AnyInSequenceNotNested()
  620. {
  621. VerifyQueryDeserialization(
  622. "Category/QueryableProducts/any(P: P/ProductName eq 'Snacks') or Category/QueryableProducts/any(P2: P2/ProductName eq 'Snacks')",
  623. "$it => ($it.Category.QueryableProducts.Any(P => (P.ProductName == \"Snacks\")) OrElse $it.Category.QueryableProducts.Any(P2 => (P2.ProductName == \"Snacks\")))",
  624. NotTesting);
  625. }
  626. [Fact]
  627. public void AllInSequenceNotNested()
  628. {
  629. VerifyQueryDeserialization(
  630. "Category/QueryableProducts/all(P: P/ProductName eq 'Snacks') or Category/QueryableProducts/all(P2: P2/ProductName eq 'Snacks')",
  631. "$it => ($it.Category.QueryableProducts.All(P => (P.ProductName == \"Snacks\")) OrElse $it.Category.QueryableProducts.All(P2 => (P2.ProductName == \"Snacks\")))",
  632. NotTesting);
  633. }
  634. [Fact]
  635. public void AnyOnPrimitiveCollection()
  636. {
  637. var filters = VerifyQueryDeserialization(
  638. "AlternateIDs/any(id: id eq 42)",
  639. "$it => $it.AlternateIDs.Any(id => (id == 42))",
  640. NotTesting);
  641. RunFilters(
  642. filters,
  643. new Product { AlternateIDs = new[] { 1, 2, 42 } },
  644. new { WithNullPropagation = true, WithoutNullPropagation = true });
  645. RunFilters(
  646. filters,
  647. new Product { AlternateIDs = new[] { 1, 2 } },
  648. new { WithNullPropagation = false, WithoutNullPropagation = false });
  649. }
  650. [Fact]
  651. public void AllOnPrimitiveCollection()
  652. {
  653. VerifyQueryDeserialization(
  654. "AlternateIDs/all(id: id eq 42)",
  655. "$it => $it.AlternateIDs.All(id => (id == 42))",
  656. NotTesting);
  657. }
  658. [Fact]
  659. public void AnyOnComplexCollection()
  660. {
  661. var filters = VerifyQueryDeserialization(
  662. "AlternateAddresses/any(address: address/City eq 'Redmond')",
  663. "$it => $it.AlternateAddresses.Any(address => (address.City == \"Redmond\"))",
  664. NotTesting);
  665. RunFilters(
  666. filters,
  667. new Product { AlternateAddresses = new[] { new Address { City = "Redmond" } } },
  668. new { WithNullPropagation = true, WithoutNullPropagation = true });
  669. RunFilters(
  670. filters,
  671. new Product(),
  672. new { WithNullPropagation = false, WithoutNullPropagation = typeof(ArgumentNullException) });
  673. }
  674. [Fact]
  675. public void AllOnComplexCollection()
  676. {
  677. VerifyQueryDeserialization(
  678. "AlternateAddresses/all(address: address/City eq 'Redmond')",
  679. "$it => $it.AlternateAddresses.All(address => (address.City == \"Redmond\"))",
  680. NotTesting);
  681. }
  682. [Fact]
  683. public void RecursiveAllAny()
  684. {
  685. VerifyQueryDeserialization(
  686. "Category/QueryableProducts/all(P: P/Category/EnumerableProducts/any(PP: PP/ProductName eq 'Snacks'))",
  687. "$it => $it.Category.QueryableProducts.All(P => P.Category.EnumerableProducts.Any(PP => (PP.ProductName == \"Snacks\")))",
  688. NotTesting);
  689. }
  690. #endregion
  691. #region String Functions
  692. [Theory]
  693. [InlineData("Abcd", -1, "Abcd", true, typeof(ArgumentOutOfRangeException))]
  694. [InlineData("Abcd", 0, "Abcd", true, true)]
  695. [InlineData("Abcd", 1, "bcd", true, true)]
  696. [InlineData("Abcd", 3, "d", true, true)]
  697. [InlineData("Abcd", 4, "", true, true)]
  698. [InlineData("Abcd", 5, "", true, typeof(ArgumentOutOfRangeException))]
  699. public void StringSubstringStart(string productName, int startIndex, string compareString, bool withNullPropagation, object withoutNullPropagation)
  700. {
  701. string filter = String.Format("substring(ProductName, {0}) eq '{1}'", startIndex, compareString);
  702. var filters = VerifyQueryDeserialization(filter);
  703. RunFilters(filters,
  704. new Product { ProductName = productName },
  705. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  706. }
  707. [Theory]
  708. [InlineData("Abcd", -1, 4, "Abcd", true, typeof(ArgumentOutOfRangeException))]
  709. [InlineData("Abcd", -1, 3, "Abc", true, typeof(ArgumentOutOfRangeException))]
  710. [InlineData("Abcd", 0, 1, "A", true, true)]
  711. [InlineData("Abcd", 0, 4, "Abcd", true, true)]
  712. [InlineData("Abcd", 0, 3, "Abc", true, true)]
  713. [InlineData("Abcd", 0, 5, "Abcd", true, typeof(ArgumentOutOfRangeException))]
  714. [InlineData("Abcd", 1, 3, "bcd", true, true)]
  715. [InlineData("Abcd", 1, 5, "bcd", true, typeof(ArgumentOutOfRangeException))]
  716. [InlineData("Abcd", 2, 1, "c", true, true)]
  717. [InlineData("Abcd", 3, 1, "d", true, true)]
  718. [InlineData("Abcd", 4, 1, "", true, typeof(ArgumentOutOfRangeException))]
  719. [InlineData("Abcd", 0, -1, "", true, typeof(ArgumentOutOfRangeException))]
  720. [InlineData("Abcd", 5, -1, "", true, typeof(ArgumentOutOfRangeException))]
  721. public void StringSubstringStartAndLength(string productName, int startIndex, int length, string compareString, bool withNullPropagation, object withoutNullPropagation)
  722. {
  723. string filter = String.Format("substring(ProductName, {0}, {1}) eq '{2}'", startIndex, length, compareString);
  724. var filters = VerifyQueryDeserialization(filter);
  725. RunFilters(filters,
  726. new Product { ProductName = productName },
  727. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  728. }
  729. [Theory]
  730. [InlineData(null, false, typeof(NullReferenceException))]
  731. [InlineData("Abcd", true, true)]
  732. [InlineData("Abd", false, false)]
  733. public void StringSubstringOf(string productName, bool withNullPropagation, object withoutNullPropagation)
  734. {
  735. // In OData, the order of parameters is actually reversed in the resulting
  736. // String.Contains expression
  737. var filters = VerifyQueryDeserialization(
  738. "contains(ProductName, 'Abc')",
  739. "$it => $it.ProductName.Contains(\"Abc\")",
  740. NotTesting);
  741. RunFilters(filters,
  742. new Product { ProductName = productName },
  743. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  744. filters = VerifyQueryDeserialization(
  745. "contains(ProductName, 'Abc')",
  746. "$it => $it.ProductName.Contains(\"Abc\")",
  747. NotTesting);
  748. }
  749. [Theory]
  750. [InlineData(null, false, typeof(NullReferenceException))]
  751. [InlineData("Abcd", true, true)]
  752. [InlineData("Abd", false, false)]
  753. public void StringStartsWith(string productName, bool withNullPropagation, object withoutNullPropagation)
  754. {
  755. var filters = VerifyQueryDeserialization(
  756. "startswith(ProductName, 'Abc')",
  757. "$it => $it.ProductName.StartsWith(\"Abc\")",
  758. NotTesting);
  759. RunFilters(filters,
  760. new Product { ProductName = productName },
  761. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  762. }
  763. [Theory]
  764. [InlineData(null, false, typeof(NullReferenceException))]
  765. [InlineData("AAbc", true, true)]
  766. [InlineData("Abcd", false, false)]
  767. public void StringEndsWith(string productName, bool withNullPropagation, object withoutNullPropagation)
  768. {
  769. var filters = VerifyQueryDeserialization(
  770. "endswith(ProductName, 'Abc')",
  771. "$it => $it.ProductName.EndsWith(\"Abc\")",
  772. NotTesting);
  773. RunFilters(filters,
  774. new Product { ProductName = productName },
  775. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  776. }
  777. [Theory]
  778. [InlineData(null, false, typeof(NullReferenceException))]
  779. [InlineData("AAbc", true, true)]
  780. [InlineData("", false, false)]
  781. public void StringLength(string productName, bool withNullPropagation, object withoutNullPropagation)
  782. {
  783. var filters = VerifyQueryDeserialization(
  784. "length(ProductName) gt 0",
  785. "$it => ($it.ProductName.Length > 0)",
  786. "$it => ((IIF(($it.ProductName == null), null, Convert($it.ProductName.Length)) > Convert(0)) == True)");
  787. RunFilters(filters,
  788. new Product { ProductName = productName },
  789. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  790. }
  791. [Theory]
  792. [InlineData(null, false, typeof(NullReferenceException))]
  793. [InlineData("12345Abc", true, true)]
  794. [InlineData("1234Abc", false, false)]
  795. public void StringIndexOf(string productName, bool withNullPropagation, object withoutNullPropagation)
  796. {
  797. var filters = VerifyQueryDeserialization(
  798. "indexof(ProductName, 'Abc') eq 5",
  799. "$it => ($it.ProductName.IndexOf(\"Abc\") == 5)",
  800. NotTesting);
  801. RunFilters(filters,
  802. new Product { ProductName = productName },
  803. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  804. }
  805. [Theory]
  806. [InlineData(null, false, typeof(NullReferenceException))]
  807. [InlineData("123uctName", true, true)]
  808. [InlineData("1234Abc", false, false)]
  809. public void StringSubstring(string productName, bool withNullPropagation, object withoutNullPropagation)
  810. {
  811. var filters = VerifyQueryDeserialization(
  812. "substring(ProductName, 3) eq 'uctName'",
  813. "$it => ($it.ProductName.Substring(3) == \"uctName\")",
  814. NotTesting);
  815. RunFilters(filters,
  816. new Product { ProductName = productName },
  817. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  818. VerifyQueryDeserialization(
  819. "substring(ProductName, 3, 4) eq 'uctN'",
  820. "$it => ($it.ProductName.Substring(3, 4) == \"uctN\")",
  821. NotTesting);
  822. }
  823. [Theory]
  824. [InlineData(null, false, typeof(NullReferenceException))]
  825. [InlineData("Tasty Treats", true, true)]
  826. [InlineData("Tasty Treatss", false, false)]
  827. public void StringToLower(string productName, bool withNullPropagation, object withoutNullPropagation)
  828. {
  829. var filters = VerifyQueryDeserialization(
  830. "tolower(ProductName) eq 'tasty treats'",
  831. "$it => ($it.ProductName.ToLower() == \"tasty treats\")",
  832. NotTesting);
  833. RunFilters(filters,
  834. new Product { ProductName = productName },
  835. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  836. }
  837. [Theory]
  838. [InlineData(null, false, typeof(NullReferenceException))]
  839. [InlineData("Tasty Treats", true, true)]
  840. [InlineData("Tasty Treatss", false, false)]
  841. public void StringToUpper(string productName, bool withNullPropagation, object withoutNullPropagation)
  842. {
  843. var filters = VerifyQueryDeserialization(
  844. "toupper(ProductName) eq 'TASTY TREATS'",
  845. "$it => ($it.ProductName.ToUpper() == \"TASTY TREATS\")",
  846. NotTesting);
  847. RunFilters(filters,
  848. new Product { ProductName = productName },
  849. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  850. }
  851. [Theory]
  852. [InlineData(null, false, typeof(NullReferenceException))]
  853. [InlineData("Tasty Treats", true, true)]
  854. [InlineData("Tasty Treatss", false, false)]
  855. public void StringTrim(string productName, bool withNullPropagation, object withoutNullPropagation)
  856. {
  857. var filters = VerifyQueryDeserialization(
  858. "trim(ProductName) eq 'Tasty Treats'",
  859. "$it => ($it.ProductName.Trim() == \"Tasty Treats\")",
  860. NotTesting);
  861. RunFilters(filters,
  862. new Product { ProductName = productName },
  863. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  864. }
  865. [Fact]
  866. public void StringConcat()
  867. {
  868. var filters = VerifyQueryDeserialization(
  869. "concat('Food', 'Bar') eq 'FoodBar'",
  870. "$it => (\"Food\".Concat(\"Bar\") == \"FoodBar\")",
  871. NotTesting);
  872. RunFilters(filters,
  873. new Product { },
  874. new { WithNullPropagation = true, WithoutNullPropagation = true });
  875. }
  876. [Fact]
  877. public void RecursiveMethodCall()
  878. {
  879. var filters = VerifyQueryDeserialization(
  880. "floor(floor(UnitPrice)) eq 123m",
  881. "$it => ($it.UnitPrice.Value.Floor().Floor() == 123)",
  882. NotTesting);
  883. RunFilters(filters,
  884. new Product { },
  885. new { WithNullPropagation = false, WithoutNullPropagation = typeof(InvalidOperationException) });
  886. }
  887. #endregion
  888. #region Date Functions
  889. [Fact]
  890. public void DateDay()
  891. {
  892. var filters = VerifyQueryDeserialization(
  893. "day(DiscontinuedDate) eq 8",
  894. "$it => ($it.DiscontinuedDate.Value.Day == 8)",
  895. NotTesting);
  896. RunFilters(filters,
  897. new Product { },
  898. new { WithNullPropagation = false, WithoutNullPropagation = typeof(InvalidOperationException) });
  899. RunFilters(filters,
  900. new Product { DiscontinuedDate = new DateTime(2000, 10, 8) },
  901. new { WithNullPropagation = true, WithoutNullPropagation = true });
  902. }
  903. public void DateDayNonNullable()
  904. {
  905. VerifyQueryDeserialization(
  906. "day(NonNullableDiscontinuedDate) eq 8",
  907. "$it => ($it.NonNullableDiscontinuedDate.Day == 8)");
  908. }
  909. [Fact]
  910. public void DateMonth()
  911. {
  912. VerifyQueryDeserialization(
  913. "month(DiscontinuedDate) eq 8",
  914. "$it => ($it.DiscontinuedDate.Value.Month == 8)",
  915. NotTesting);
  916. }
  917. [Fact]
  918. public void DateYear()
  919. {
  920. VerifyQueryDeserialization(
  921. "year(DiscontinuedDate) eq 1974",
  922. "$it => ($it.DiscontinuedDate.Value.Year == 1974)",
  923. NotTesting);
  924. }
  925. [Fact]
  926. public void DateHour()
  927. {
  928. VerifyQueryDeserialization("hour(DiscontinuedDate) eq 8",
  929. "$it => ($it.DiscontinuedDate.Value.Hour == 8)",
  930. NotTesting);
  931. }
  932. [Fact]
  933. public void DateMinute()
  934. {
  935. VerifyQueryDeserialization(
  936. "minute(DiscontinuedDate) eq 12",
  937. "$it => ($it.DiscontinuedDate.Value.Minute == 12)",
  938. NotTesting);
  939. }
  940. [Fact]
  941. public void DateSecond()
  942. {
  943. VerifyQueryDeserialization(
  944. "second(DiscontinuedDate) eq 33",
  945. "$it => ($it.DiscontinuedDate.Value.Second == 33)",
  946. NotTesting);
  947. }
  948. [Theory]
  949. [InlineData("year(DiscontinuedOffset) eq 100", "$it => ($it.DiscontinuedOffset.Year == 100)")]
  950. [InlineData("month(DiscontinuedOffset) eq 100", "$it => ($it.DiscontinuedOffset.Month == 100)")]
  951. [InlineData("day(DiscontinuedOffset) eq 100", "$it => ($it.DiscontinuedOffset.Day == 100)")]
  952. [InlineData("hour(DiscontinuedOffset) eq 100", "$it => ($it.DiscontinuedOffset.Hour == 100)")]
  953. [InlineData("minute(DiscontinuedOffset) eq 100", "$it => ($it.DiscontinuedOffset.Minute == 100)")]
  954. [InlineData("second(DiscontinuedOffset) eq 100", "$it => ($it.DiscontinuedOffset.Second == 100)")]
  955. public void DateTimeOffsetFunctions(string filter, string expression)
  956. {
  957. VerifyQueryDeserialization(filter, expression);
  958. }
  959. [Theory]
  960. [InlineData("years(DiscontinuedSince) eq 100", "$it => $it.DiscontinuedSince.Years == 100")]
  961. [InlineData("months(DiscontinuedSince) eq 100", "$it => $it.DiscontinuedSince.Months == 100")]
  962. [InlineData("days(DiscontinuedSince) eq 100", "$it => $it.DiscontinuedSince.Days == 100")]
  963. [InlineData("hours(DiscontinuedSince) eq 100", "$it => $it.DiscontinuedSince.Hours == 100")]
  964. [InlineData("minutes(DiscontinuedSince) eq 100", "$it => $it.DiscontinuedSince.Minutes == 100")]
  965. [InlineData("seconds(DiscontinuedSince) eq 100", "$it => $it.DiscontinuedSince.Seconds == 100")]
  966. public void TimespanFunctions(string filter, string expression)
  967. {
  968. // There's currently a bug here. For now, the test checks for the presence of the bug (as a reminder to fix
  969. // the test once the bug is fixed).
  970. // The following assert shows the behavior with the bug and should be removed once the bug is fixed.
  971. Assert.Throws<ODataException>(() => Bind(filter));
  972. // TODO: Timespans are not handled well in the uri parser
  973. // The following call shows the behavior without the bug, and should be enabled once the bug is fixed.
  974. //VerifyQueryDeserialization(filter, expression);
  975. }
  976. #endregion
  977. #region Math Functions
  978. [Theory, PropertyData("MathRoundDecimal_DataSet")]
  979. public void MathRoundDecimal(decimal? unitPrice, bool withNullPropagation, object withoutNullPropagation)
  980. {
  981. var filters = VerifyQueryDeserialization(
  982. "round(UnitPrice) gt 5.00m",
  983. String.Format(CultureInfo.InvariantCulture, "$it => ($it.UnitPrice.Value.Round() > {0:0.00})", 5.0),
  984. NotTesting);
  985. RunFilters(filters,
  986. new Product { UnitPrice = ToNullable<decimal>(unitPrice) },
  987. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  988. }
  989. [Theory]
  990. [InlineData(null, false, typeof(InvalidOperationException))]
  991. [InlineData(5.9d, true, true)]
  992. [InlineData(5.4d, false, false)]
  993. public void MathRoundDouble(double? weight, bool withNullPropagation, object withoutNullPropagation)
  994. {
  995. var filters = VerifyQueryDeserialization(
  996. "round(Weight) gt 5d",
  997. String.Format(CultureInfo.InvariantCulture, "$it => ($it.Weight.Value.Round() > {0})", 5),
  998. NotTesting);
  999. RunFilters(filters,
  1000. new Product { Weight = ToNullable<double>(weight) },
  1001. new { WithNullPropagation = withNullPropagation, WithoutNullPropagation = withoutNullPropagation });
  1002. }
  1003. [Theory]
  1004. [InlineData(null, false, typeof(InvalidOperationException))]
  1005. [InlineData(5.9f, true, true)]
  1006. [InlineData(5.4f, false, false)]
  1007. public void M

Large files files are truncated, but you can click here to view the full file