Skip to content

Commit 06c6fc0

Browse files
committed
Brought in ExpressionToString
- Amended tests to conform to breaking changes introduced by ExpressionToString. - Deleted ExpressionInspector class as it is no longer needed.
1 parent a358f1a commit 06c6fc0

File tree

6 files changed

+297
-23
lines changed

6 files changed

+297
-23
lines changed

TestStack.FluentMVCTesting.Tests/ViewResultTestTests.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public void Check_for_invalid_model_using_predicate()
7272
var exception = Assert.Throws<ViewResultModelAssertionException>(() =>
7373
_viewResultTest.WithModel<TestViewModel>(m => m.Property1 == null)
7474
);
75-
Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model {{\"Property1\":\"{0}\",\"Property2\":{1}}} to pass the given condition (m => (m.Property1 == null)), but it failed.", _model.Property1, _model.Property2)));
75+
Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model {{\"Property1\":\"{0}\",\"Property2\":{1}}} to pass the given condition ((m) => (m.Property1 == null)), but it failed.", _model.Property1, _model.Property2)));
7676
}
7777

7878
[Test]
@@ -81,7 +81,7 @@ public void Check_for_invalid_model_using_predicate_with_conditional_or()
8181
var exception = Assert.Throws<ViewResultModelAssertionException>(() =>
8282
_viewResultTest.WithModel<TestViewModel>(m => m.Property1 == null || m.Property2 == 1)
8383
);
84-
Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model {{\"Property1\":\"{0}\",\"Property2\":{1}}} to pass the given condition (m => ((m.Property1 == null) || (m.Property2 == 1))), but it failed.", _model.Property1, _model.Property2)));
84+
Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model {{\"Property1\":\"{0}\",\"Property2\":{1}}} to pass the given condition ((m) => ((m.Property1 == null) || (m.Property2 == 1))), but it failed.", _model.Property1, _model.Property2)));
8585
}
8686

8787
[Test]
@@ -91,7 +91,18 @@ public void Check_for_invalid_model_using_predicate_with_primitive_operand()
9191
var exception = Assert.Throws<ViewResultModelAssertionException>(() =>
9292
_viewResultTest.WithModel<string>(m => m == "ab")
9393
);
94-
Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model \"{0}\" to pass the given condition (m => (m == \"ab\")), but it failed.", _viewResult.ViewData.Model)));
94+
Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model \"{0}\" to pass the given condition ((m) => (m == \"ab\")), but it failed.", _viewResult.ViewData.Model)));
95+
}
96+
97+
[Test]
98+
public void Check_for_invalid_model_using_predicate_with_captured_var_operand()
99+
{
100+
var capturedOuterVar = "ab";
101+
_viewResult.ViewData.Model = "abc";
102+
var exception = Assert.Throws<ViewResultModelAssertionException>(() =>
103+
_viewResultTest.WithModel<string>(m => m == capturedOuterVar)
104+
);
105+
Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model \"{0}\" to pass the given condition ((m) => (m == capturedOuterVar)), but it failed.", _viewResult.ViewData.Model)));
95106
}
96107

97108
[Test]
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
using System;
2+
using System.Linq;
3+
using System.Linq.Expressions;
4+
using System.Runtime.CompilerServices;
5+
using System.Text;
6+
7+
// ReSharper disable CheckNamespace
8+
namespace ExpressionToString
9+
{
10+
class ExpressionStringBuilder : ExpressionVisitor
11+
{
12+
// ReSharper disable InconsistentNaming
13+
private readonly StringBuilder builder = new StringBuilder();
14+
private readonly bool trimLongArgumentList;
15+
bool skipDot;
16+
17+
private ExpressionStringBuilder(bool trimLongArgumentList)
18+
{
19+
this.trimLongArgumentList = trimLongArgumentList;
20+
}
21+
22+
/// <summary>
23+
/// A nicely formatted ToString of an expression
24+
/// </summary>
25+
/// <param name="expression">The expression to format</param>
26+
/// <param name="trimLongArgumentList">If true will replace large (>3) argument lists with an elipsis</param>
27+
/// <returns></returns>
28+
public static string ToString(Expression expression, bool trimLongArgumentList = false)
29+
{
30+
var visitor = new ExpressionStringBuilder(trimLongArgumentList);
31+
visitor.Visit(expression);
32+
var s = visitor.builder.ToString();
33+
return s;
34+
}
35+
36+
37+
protected override Expression VisitLambda<T>(Expression<T> node)
38+
{
39+
if (node.Parameters.Any())
40+
{
41+
Out("(");
42+
Out(String.Join(",", node.Parameters.Select(n => n.Name)));
43+
Out(") => ");
44+
}
45+
Visit(node.Body);
46+
return node;
47+
}
48+
49+
protected override Expression VisitInvocation(InvocationExpression node)
50+
{
51+
var visitInvocation = base.VisitInvocation(node);
52+
Out("()");
53+
return visitInvocation;
54+
}
55+
56+
protected override Expression VisitBinary(BinaryExpression node)
57+
{
58+
Out("(");
59+
Visit(node.Left);
60+
Out(" ");
61+
Out(ToString(node.NodeType));
62+
Out(" ");
63+
Visit(node.Right);
64+
Out(")");
65+
return node;
66+
}
67+
68+
protected override Expression VisitParameter(ParameterExpression node)
69+
{
70+
Out(node.Name);
71+
return node;
72+
}
73+
74+
protected override Expression VisitMember(MemberExpression node)
75+
{
76+
if (node.Expression.NodeType == ExpressionType.Constant)
77+
{
78+
Visit(node.Expression);
79+
if (skipDot)
80+
{
81+
skipDot = false;
82+
Out(node.Member.Name);
83+
}
84+
else
85+
Out("." + node.Member.Name);
86+
}
87+
else
88+
{
89+
Visit(node.Expression);
90+
Out("." + node.Member.Name);
91+
}
92+
93+
return node;
94+
}
95+
96+
private static bool CheckIfAnonymousType(Type type)
97+
{
98+
// hack: the only way to detect anonymous types right now
99+
var isDefined = type.IsDefined(typeof(CompilerGeneratedAttribute), false);
100+
return isDefined
101+
&& (type.IsGenericType && type.Name.Contains("AnonymousType") || type.Name.Contains("DisplayClass"))
102+
&& (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"));
103+
}
104+
105+
protected override Expression VisitConstant(ConstantExpression node)
106+
{
107+
if (CheckIfAnonymousType(node.Type))
108+
{
109+
skipDot = true;
110+
return node;
111+
}
112+
if (node.Value == null)
113+
{
114+
Out("null");
115+
}
116+
else
117+
{
118+
var stringValue = node.Value as string;
119+
if (stringValue != null)
120+
{
121+
Out("\"" + stringValue + "\"");
122+
}
123+
else
124+
{
125+
if (node.Value is bool)
126+
{
127+
Out(node.Value.ToString().ToLower());
128+
}
129+
else
130+
{
131+
var valueToString = node.Value.ToString();
132+
var type = node.Value.GetType();
133+
if (type.FullName != valueToString)
134+
{
135+
Out(valueToString);
136+
}
137+
else
138+
{
139+
skipDot = true;
140+
}
141+
}
142+
}
143+
}
144+
145+
return node;
146+
}
147+
148+
protected override Expression VisitUnary(UnaryExpression node)
149+
{
150+
if (node.NodeType == ExpressionType.Convert)
151+
{
152+
Visit(node.Operand);
153+
return node;
154+
}
155+
if (node.NodeType == ExpressionType.Not)
156+
{
157+
Out("!");
158+
Visit(node.Operand);
159+
return node;
160+
}
161+
if (node.NodeType == ExpressionType.TypeAs)
162+
{
163+
Out("(");
164+
Visit(node.Operand);
165+
Out(" As " + node.Type.Name + ")");
166+
return node;
167+
}
168+
169+
return base.VisitUnary(node);
170+
}
171+
172+
protected override Expression VisitNew(NewExpression node)
173+
{
174+
Out("new " + node.Type.Name + "(");
175+
VisitArguments(node.Arguments.ToArray());
176+
Out(")");
177+
return node;
178+
}
179+
180+
protected override Expression VisitMethodCall(MethodCallExpression node)
181+
{
182+
Visit(node.Object);
183+
184+
if (!skipDot && !node.Method.IsStatic)
185+
{
186+
Out(".");
187+
skipDot = false;
188+
}
189+
Out(node.Method.Name + "(");
190+
var args = node.Arguments.ToArray();
191+
if (args.Length > 3 && trimLongArgumentList)
192+
{
193+
Out("...");
194+
}
195+
else
196+
{
197+
VisitArguments(args);
198+
}
199+
200+
Out(")");
201+
return node;
202+
}
203+
204+
private void VisitArguments(Expression[] arguments)
205+
{
206+
int argindex = 0;
207+
while (argindex < arguments.Length)
208+
{
209+
Visit(arguments[argindex]);
210+
argindex++;
211+
212+
if (argindex < arguments.Length)
213+
{
214+
Out(", ");
215+
}
216+
}
217+
}
218+
219+
protected override Expression VisitConditional(ConditionalExpression node)
220+
{
221+
Out("IIF(");
222+
Visit(node.Test);
223+
Out(", ");
224+
Visit(node.IfTrue);
225+
Out(", ");
226+
Visit(node.IfFalse);
227+
Out(")");
228+
return node;
229+
}
230+
231+
private static string ToString(ExpressionType type)
232+
{
233+
switch (type)
234+
{
235+
case ExpressionType.Add:
236+
return "+";
237+
case ExpressionType.And:
238+
return "&";
239+
case ExpressionType.AndAlso:
240+
return "&&";
241+
case ExpressionType.Divide:
242+
return "/";
243+
case ExpressionType.Equal:
244+
return "==";
245+
case ExpressionType.GreaterThan:
246+
return ">";
247+
case ExpressionType.GreaterThanOrEqual:
248+
return ">=";
249+
case ExpressionType.LessThan:
250+
return "<";
251+
case ExpressionType.LessThanOrEqual:
252+
return "<=";
253+
case ExpressionType.Modulo:
254+
return "%";
255+
case ExpressionType.Multiply:
256+
return "*";
257+
case ExpressionType.Negate:
258+
return "-";
259+
case ExpressionType.Not:
260+
return "!";
261+
case ExpressionType.NotEqual:
262+
return "!=";
263+
case ExpressionType.Or:
264+
return "|";
265+
case ExpressionType.OrElse:
266+
return "||";
267+
case ExpressionType.Subtract:
268+
return "-";
269+
default:
270+
throw new NotImplementedException();
271+
}
272+
}
273+
274+
private void Out(string s)
275+
{
276+
builder.Append(s);
277+
}
278+
}
279+
}

TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs

Lines changed: 0 additions & 16 deletions
This file was deleted.

TestStack.FluentMvcTesting/TestStack.FluentMVCTesting.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,14 @@
7676
<Reference Include="System.Xml" />
7777
</ItemGroup>
7878
<ItemGroup>
79+
<Compile Include="App_Packages\ExpressionStringBuilder.0.9.2\ExpressionStringBuilder.cs" />
7980
<Compile Include="ControllerResultTest\ShouldGiveHttpStatus.cs" />
8081
<Compile Include="ControllerResultTest\ShouldRedirectTo.cs" />
8182
<Compile Include="ControllerResultTest\ShouldRenderFile.cs" />
8283
<Compile Include="ControllerResultTest\ShouldRenderView.cs" />
8384
<Compile Include="ControllerResultTest\ShouldReturnContent.cs" />
8485
<Compile Include="ControllerResultTest\ShouldReturnEmptyResult.cs" />
8586
<Compile Include="ControllerResultTest\ShouldReturnJson.cs" />
86-
<Compile Include="Internal\ExpressionInspector.cs" />
8787
<Compile Include="RouteValueDictionaryExtension.cs" />
8888
<Compile Include="ControllerExtensions.cs" />
8989
<Compile Include="ControllerResultTest\ControllerResultTest.cs" />

TestStack.FluentMvcTesting/ViewResultTest.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.Text.RegularExpressions;
44
using System.Web.Helpers;
55
using System.Web.Mvc;
6-
using TestStack.FluentMVCTesting.Internal;
6+
using ExpressionToString;
77

88
namespace TestStack.FluentMVCTesting
99
{
@@ -47,9 +47,8 @@ public ModelTest<TModel> WithModel<TModel>(Expression<Func<TModel, bool>> predic
4747

4848
var model = _viewResult.Model as TModel;
4949

50-
var inspector = new ExpressionInspector();
5150
var modelLex = Json.Encode(model);
52-
var predicateLex = inspector.Inspect(predicate);
51+
var predicateLex = ExpressionStringBuilder.ToString(predicate);
5352
var compiledPredicate = predicate.Compile();
5453

5554
if (!compiledPredicate(model))

TestStack.FluentMvcTesting/packages.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3+
<package id="ExpressionToString" version="0.9.2" targetFramework="net45" />
34
<package id="Microsoft.AspNet.Mvc" version="5.2.2" targetFramework="net45" />
45
<package id="Microsoft.AspNet.Razor" version="3.2.2" targetFramework="net45" />
56
<package id="Microsoft.AspNet.WebPages" version="3.2.2" targetFramework="net45" />

0 commit comments

Comments
 (0)