Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions Src/System.Linq.Dynamic.Test/DynamicExpressionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,70 @@ public void Parse_TupleToStringMethodCall_ReturnsStringLambdaExpression()
Assert.AreEqual(typeof(string), expression.ReturnType);
}

[TestMethod]
public void Parse_StringLiteral_ReturnsBooleanLambdaExpression()
{
var expression = DynamicExpression.Parse(new[] { Expression.Parameter(typeof(string), "Property1") }, typeof(Boolean), "Property1 == \"test\"");
Assert.AreEqual(typeof(Boolean), expression.Type);
}

[TestMethod]
public void Parse_StringLiteralEmpty_ReturnsBooleanLambdaExpression()
{
var expression = DynamicExpression.Parse(new[] { Expression.Parameter(typeof(string), "Property1") }, typeof(Boolean), "Property1 == \"\"");
Assert.AreEqual(typeof(Boolean), expression.Type);
}

[TestMethod]
public void Parse_StringLiteralEmbeddedQuote_ReturnsBooleanLambdaExpression()
{
var expression = DynamicExpression.Parse(
new[] { Expression.Parameter(typeof(string), "Property1") },
typeof(Boolean),
string.Format("Property1 == {0}", "\"test \\\"string\""));

string rightValue = ((BinaryExpression) expression).Right.ToString();
Assert.AreEqual(typeof(Boolean), expression.Type);
Assert.AreEqual("\"test \"string\"", rightValue);
}

[TestMethod]
public void Parse_StringLiteralStartEmbeddedQuote_ReturnsBooleanLambdaExpression()
{
var expression = DynamicExpression.Parse(
new[] { Expression.Parameter(typeof(string), "Property1") },
typeof(Boolean),
string.Format("Property1 == {0}", "\"\\\"test\""));

string rightValue = ((BinaryExpression)expression).Right.ToString();
Assert.AreEqual(typeof(Boolean), expression.Type);
Assert.AreEqual("\"\"test\"", rightValue);
}

[ExpectedException(typeof(ParseException))]
[TestMethod]
public void Parse_StringLiteral_MissingClosingQuote()
{
string expectedRightValue = "\"test\\\"";
var expression = DynamicExpression.Parse(
new[] { Expression.Parameter(typeof(string), "Property1") },
typeof(Boolean),
string.Format("Property1 == {0}", expectedRightValue));
}

[TestMethod]
public void Parse_StringLiteralEscapedBackslash_ReturnsBooleanLambdaExpression()
{
var expression = DynamicExpression.Parse(
new[] { Expression.Parameter(typeof(string), "Property1") },
typeof(Boolean),
string.Format("Property1 == {0}", "\"test\\\\string\""));

string rightValue = ((BinaryExpression)expression).Right.ToString();
Assert.AreEqual(typeof(Boolean), expression.Type);
Assert.AreEqual("\"test\\string\"", rightValue);
}

[TestMethod]
public void ParseLambda_DelegateTypeMethodCall_ReturnsEventHandlerLambdaExpression()
{
Expand Down
121 changes: 121 additions & 0 deletions Src/System.Linq.Dynamic.Test/IntegrationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
using System.Linq.Dynamic;

namespace System.Linq.Dynamic.Test
{
[TestClass]
public class IntegrationTests
{
private class TestObject
{
public int Id { get; set; }
public string Color { get; set; }
public int Number { get; set; }
}

private IList<TestObject> GetTestData()
{
return new List<TestObject>()
{
new TestObject() {Id = 0, Color = "Red", Number = 10 },
new TestObject() {Id = 1, Color = "Red", Number = 20 },
new TestObject() {Id = 2, Color = "\"Bright\" Red", Number = 30 },
new TestObject() {Id = 3, Color = "Blue", Number = 5 },
new TestObject() {Id = 4, Color = "Brown", Number = 15 },
new TestObject() {Id = 5, Color = "\"Faded\" Blue", Number = 25 },
new TestObject() {Id = 6, Color = "Blue\\Green", Number = 30 },
new TestObject() {Id = 7, Color = "Yellow\tOrange", Number = 35 },
new TestObject() {Id = 8, Color = "Bright Orange", Number = 30 },
new TestObject() {Id = 9, Color = "Faded\u0009Orange", Number = 40 }
};
}

private bool TestItemsIncluded(IList<TestObject> data, IEnumerable<int> expectedIds)
{
Assert.AreEqual(expectedIds.Count(), data.Count(), "Unexpected number of items returned.");

foreach (int id in expectedIds)
{
var results = data.Where(x => x.Id == id);
Assert.IsTrue(results.Count() > 0, string.Format("Expected item {0} to be included.", id));
}

return true;
}

[TestMethod]
public void Where_StringEquality()
{
var testData = GetTestData();
var testResults = testData.Where("Color == \"Red\"").ToList<TestObject>();

TestItemsIncluded(testResults, new List<int>() { 0, 1 });
}

[TestMethod]
public void Where_StringContains()
{
var testData = GetTestData();
var testResults = testData.Where("Color.Contains(\"Red\")").ToList<TestObject>();

TestItemsIncluded(testResults, new List<int>() { 0, 1, 2 });
}

[TestMethod]
public void Where_EscapedStringContains()
{
var testData = GetTestData();
var testResults = testData.Where("Color.Contains(\"\\\"Bright\\\"\")").ToList<TestObject>();

TestItemsIncluded(testResults, new List<int>() { 2 });
}

[TestMethod]
public void Where_EscapedStringEquality()
{
var testData = GetTestData();
var testResults = testData.Where("Color == \"\\\"Faded\\\" Blue\"").ToList<TestObject>();

TestItemsIncluded(testResults, new List<int>() { 5 });
}

[TestMethod]
[ExpectedException(typeof(System.Linq.Dynamic.ParseException))]
public void Where_InvalidClause()
{
var testData = GetTestData();
var testResults = testData.Where("Color.Contains(\\\"Bright\\\")").ToList<TestObject>();
}

[TestMethod]
public void Where_NumberRange()
{
var testData = GetTestData();
var testResults = testData.Where("Number <= 25 && Number > 5").ToList<TestObject>();

TestItemsIncluded(testResults, new List<int>() { 0, 1, 4, 5 });
}

[TestMethod]
public void Where_EscapeChar()
{
var testData = GetTestData();
var testResults = testData.Where("Color.Contains(\"\\\\\")").ToList<TestObject>();

TestItemsIncluded(testResults, new List<int>() { 6 });
}

[TestMethod]
public void Where_EscapedTab()
{
var testData = GetTestData();
var testResults = testData.Where("Color.Contains(\"\\t\")").ToList<TestObject>();

TestItemsIncluded(testResults, new List<int>() { 7, 9 });
}
}
}
15 changes: 11 additions & 4 deletions Src/System.Linq.Dynamic.Test/System.Linq.Dynamic.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>System.Linq.Dynamic.Test</RootNamespace>
<AssemblyName>System.Linq.Dynamic.Test</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
Expand All @@ -23,6 +24,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
Expand All @@ -31,9 +33,12 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
Expand All @@ -48,19 +53,21 @@
<Compile Include="DynamicExpressionCultureTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="DynamicExpressionTests.cs" />
<Compile Include="IntegrationTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\System.Linq.Dynamic\System.Linq.Dynamic.csproj">
<Project>{b6edf157-6153-4684-a577-de33896dbaa8}</Project>
<Name>System.Linq.Dynamic</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\System.Linq.Dynamic\System.Linq.Dynamic.csproj">
<Project>{b6edf157-6153-4684-a577-de33896dbaa8}</Project>
<Name>System.Linq.Dynamic</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<WCFMetadata Include="Service References\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
64 changes: 53 additions & 11 deletions Src/System.Linq.Dynamic/DynamicLinq.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1162,19 +1162,45 @@ Expression ParsePrimaryStart()
}
}

string RemoveEscapes(string s)
{
StringBuilder newS = new StringBuilder(s.Length);

for(int i = 0; i < s.Length; i++)
{
if (s[i] == '\\')
{
i++;
if (i == s.Length)
break;

switch (s[i])
{
case '\\': newS.Append('\\'); break;
case '"': newS.Append('"'); break;
case '\'': newS.Append('\''); break;
case 't': newS.Append('\t'); break;
case 'n': newS.Append('\n'); break;
case 'r': newS.Append('\r'); break;
default: // Unrecognized. Copy as-is.
newS.Append('\\').Append(s[i]); break;
}
}
else
{
newS.Append(s[i]);
}
}

return newS.ToString();
}

Expression ParseStringLiteral()
{
ValidateToken(TokenId.StringLiteral);
char quote = token.text[0];
string s = token.text.Substring(1, token.text.Length - 2);
int start = 0;
while (true)
{
int i = s.IndexOf(quote, start);
if (i < 0) break;
s = s.Remove(i, 1);
start = i + 1;
}

if (quote == '\'')
{
if (s.Length != 1)
Expand All @@ -1183,6 +1209,9 @@ Expression ParseStringLiteral()
return CreateLiteral(s[0], s);
}
NextToken();

s = RemoveEscapes(s);

return CreateLiteral(s, s);
}

Expand Down Expand Up @@ -2327,8 +2356,21 @@ void NextToken()
char quote = ch;
do
{
NextChar();
while (textPos < textLen && ch != quote) NextChar();
bool escaped;

do
{
escaped = false;
NextChar();

if (ch == '\\')
{
escaped = true;
if (textPos < textLen) NextChar();
}
}
while (textPos < textLen && (ch != quote || escaped));

if (textPos == textLen)
throw ParseError(textPos, Res.UnterminatedStringLiteral);
NextChar();
Expand Down Expand Up @@ -2488,4 +2530,4 @@ static class Res
public const string CloseBracketOrCommaExpected = "']' or ',' expected";
public const string IdentifierExpected = "Identifier expected";
}
}
}