PageRenderTime 63ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

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

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

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