Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature request] Make enable to specify caller location #30

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
72 changes: 72 additions & 0 deletions PowerAssert/CallerLocation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace PowerAssert
{
public class CallerLocation
{
static Assembly MyAssembly = typeof(CallerLocation).Assembly;

public string FileName { get; private set; }
public int LineNumber { get; private set; }
public MethodBase Method { get; private set; }

public CallerLocation(StackFrame f)
{
if (f == null)
{
FileName = "";
LineNumber = 0;
Method = null;
}
else
{
FileName = f.GetFileName();
LineNumber = f.GetFileLineNumber();
Method = f.GetMethod();
}
}

public Type DeclaringType
{
get { return Method == null ? null : Method.DeclaringType; }
}

public override string ToString()
{
if (Method == null)
{
return "(Unknown location)";
}
var ret = string.Format("in {0}.{1}", Method.DeclaringType.Name, Method.Name);

if (!string.IsNullOrEmpty(FileName))
{
ret += string.Format(" at {0}:{1}", FileName, LineNumber);
}
return ret;
}

public static CallerLocation Unknown = new CallerLocation(null);

public static CallerLocation FindFromStackFrames()
{
var stackFrames = new StackTrace(1, true).GetFrames();
var location = (
from f in stackFrames
let m = f.GetMethod()
where m != null && m.DeclaringType.Assembly != MyAssembly
select new CallerLocation(f)
).FirstOrDefault();

return location ?? CallerLocation.Unknown;
}

internal static CallerLocation Ensure(CallerLocation location)
{
return location ?? FindFromStackFrames();
}
}
}
9 changes: 1 addition & 8 deletions PowerAssert/Infrastructure/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,11 @@ public ExpressionParser(Expression expression, ParameterExpression[] parameters
RootExpression = expression;
_parameters = parameters ?? new ParameterExpression[0];
_parameterValues = parameterValues ?? new object[0];
TestClass = testClass ?? GetTestClass();
TestClass = testClass ?? CallerLocation.FindFromStackFrames().DeclaringType;
TextOnly = textOnly;
_nextParamIndex = baseParamIndex;
}

static Type GetTestClass()
{
var st = new StackTrace(1, false);
return st.GetFrames().Select(f => f.GetMethod().DeclaringType)
.FirstOrDefault(t => t != null && t.Assembly != MyAssembly);
}

public Node Parse()
{
return Parse(RootExpression);
Expand Down
29 changes: 4 additions & 25 deletions PowerAssert/MultipleAssertions/Error.cs
Original file line number Diff line number Diff line change
@@ -1,44 +1,23 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace PowerAssert.MultipleAssertions
{
public class Error
{
static Assembly MyAssembly = typeof(Error).Assembly;

internal static readonly string Crlf = Environment.NewLine;

static readonly string Seperator = new string('=', 60);


public Error(string message)
public Error(string message, CallerLocation location)
{
Message = message;
var stackFrames = from f in new StackTrace(1, true).GetFrames()
let m = f.GetMethod()
where m != null
let t = m.DeclaringType
where t.Assembly != MyAssembly
select f;
var frame = stackFrames.FirstOrDefault();
if (frame != null)
{
var method = frame.GetMethod();
var typeName = method.DeclaringType == null ? "" : method.DeclaringType.Name;
Location = string.Format("in {0}.{1} at {2}:{3}", typeName, method.Name, frame.GetFileName(), frame.GetFileLineNumber());
}
else
{
Location = "(Unknown location)";
}

Location = location.ToString();
}


public Error(Exception exception):this(exception.Message)
public Error(Exception exception, CallerLocation location):this(exception.Message, location)
{
Exception = exception;
CausesFail = true;
Expand All @@ -61,4 +40,4 @@ public override string ToString()

}
}
}
}
22 changes: 11 additions & 11 deletions PowerAssert/MultipleAssertions/PolyAssert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,52 +15,52 @@ public class PolyAssert : IDisposable
/// <summary>
/// Write a log message to be printed IF the PolyAssert has any errors
/// </summary>
public void Log(string s)
public void Log(string s, CallerLocation location = null)
{
_errors.Add(new Error(s));
_errors.Add(new Error(s, CallerLocation.Ensure(location)));
}

/// <summary>
/// Calls PAssert.IsTrue and stores the exception, if one occurs
/// </summary>
[MethodImpl(MethodImplOptions.NoInlining)]
public void IsTrue(Expression<Func<bool>> expression)
public void IsTrue(Expression<Func<bool>> expression, CallerLocation location = null)
{
Try(() => PAssert.IsTrue(expression));
Try(() => PAssert.IsTrue(expression), CallerLocation.Ensure(location));
}

/// <summary>
/// Calls PAssert.IsTrue and stores the exception, if one occurs
/// </summary>
[MethodImpl(MethodImplOptions.NoInlining)]
public void IsTrue<TTarget>(TTarget target, Expression<Func<TTarget, bool>> expression)
public void IsTrue<TTarget>(TTarget target, Expression<Func<TTarget, bool>> expression, CallerLocation location = null)
{
Try(() => PAssert.IsTrue(target, expression));
Try(() => PAssert.IsTrue(target, expression), CallerLocation.Ensure(location));
}

/// <summary>
/// Runs any action and stores the exception, if one occurs
/// </summary>
public void Try(Action action)
public void Try(Action action, CallerLocation location = null)
{
try
{
action();
}
catch (Exception e)
{
_errors.Add(new Error(e));
_errors.Add(new Error(e, CallerLocation.Ensure(location)));
}
}

/// <summary>
/// Stores a failure message, if shouldFail is true
/// </summary>
public void FailIf(bool shouldFail, string message)
public void FailIf(bool shouldFail, string message, CallerLocation location = null)
{
if (shouldFail)
{
_errors.Add(new Error(message) { CausesFail = true});
_errors.Add(new Error(message, CallerLocation.Ensure(location)) { CausesFail = true});
}
}

Expand All @@ -80,4 +80,4 @@ public void StopIfErrorsHaveOccurred()
}
}
}
}
}
21 changes: 11 additions & 10 deletions PowerAssert/PAssert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ static TException ValidateExceptionAssertion<TException>(Expression<Func<TExcept
}

[MethodImpl(MethodImplOptions.NoInlining)]
public static void IsTrue(Expression<Func<bool>> expression)
public static void IsTrue(Expression<Func<bool>> expression, CallerLocation location = null)
{
Func<bool> func = expression.Compile();
bool b;
Expand All @@ -75,17 +75,17 @@ public static void IsTrue(Expression<Func<bool>> expression)
}
catch (Exception e)
{
var output = RenderExpression(expression);
var output = RenderExpression(expression, location);
throw new Exception("IsTrue encountered " + e.GetType().Name + ", expression was:" + CRLF + CRLF + output + CRLF + CRLF, e);
}
if (!b)
{
throw CreateException(expression, "IsTrue failed");
throw CreateException(expression, "IsTrue failed", location);
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
public static void IsTrue<T>(T target, Expression<Func<T, bool>> expression)
public static void IsTrue<T>(T target, Expression<Func<T, bool>> expression, CallerLocation location = null)
{
Func<T, bool> func = expression.Compile();
bool b;
Expand All @@ -95,12 +95,12 @@ public static void IsTrue<T>(T target, Expression<Func<T, bool>> expression)
}
catch (Exception e)
{
var output = RenderExpression(expression, target);
var output = RenderExpression(expression, location, target);
throw new Exception("IsTrue encountered " + e.GetType().Name + ", expression was:" + CRLF + CRLF + output + CRLF + CRLF, e);
}
if (!b)
{
throw CreateException(expression, "IsTrue failed", target);
throw CreateException(expression, "IsTrue failed", location, target);
}
}

Expand Down Expand Up @@ -132,15 +132,16 @@ static Expression<Func<bool>> Substitute<TException>(Expression<Func<TException,
return Expression.Lambda<Func<bool>>(newBody);
}

static Exception CreateException(LambdaExpression expression, string message, params object []parameterValues)
static Exception CreateException(LambdaExpression expression, string message, CallerLocation location, params object []parameterValues)
{
var output = RenderExpression(expression, parameterValues);
var output = RenderExpression(expression, location, parameterValues);
return new Exception(message + ", expression was:" + CRLF + CRLF + output);
}

static string RenderExpression(LambdaExpression expression, params object []parameterValues)
static string RenderExpression(LambdaExpression expression, CallerLocation location, params object []parameterValues)
{
var parser = new ExpressionParser(expression.Body, expression.Parameters.ToArray(), parameterValues);
var testClass = CallerLocation.Ensure(location).DeclaringType;
var parser = new ExpressionParser(expression.Body, expression.Parameters.ToArray(), parameterValues, testClass: testClass);
Node constantNode = parser.Parse();
string[] lines = NodeFormatter.Format(constantNode);
return string.Join(CRLF, lines) + CRLF;
Expand Down
1 change: 1 addition & 0 deletions PowerAssert/PowerAssert.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
<DesignTime>True</DesignTime>
<DependentUpon>Util.tt</DependentUpon>
</Compile>
<Compile Include="CallerLocation.cs" />
<Compile Include="MultipleAssertions\Error.cs" />
<Compile Include="MultipleAssertions\PolyAssert.cs" />
<Compile Include="MultipleAssertions\PolyAssertException.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
| "foobar"
102

at PowerAssert.PAssert.IsTrue(Expression`1 expression) in ...\PAssert.cs
at PowerAssert.PAssert.IsTrue(Expression`1 expression, CallerLocation location) in ...\PAssert.cs
at PowerAssertTests.Approvals.EndToEndTest.ApproveException(Expression`1 func) in ...\EndToEndTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ values.Length == 1
| 0
[]

at PowerAssert.PAssert.IsTrue(Expression`1 expression) in ...\PAssert.cs
at PowerAssert.PAssert.IsTrue(Expression`1 expression, CallerLocation location) in ...\PAssert.cs
at PowerAssertTests.Approvals.EndToEndTest.ApproveException(Expression`1 func) in ...\EndToEndTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ String.Compare(x + "", y + "", False) == 0
| "foo"
1

at PowerAssert.PAssert.IsTrue(Expression`1 expression) in ...\PAssert.cs
at PowerAssert.PAssert.IsTrue(Expression`1 expression, CallerLocation location) in ...\PAssert.cs
at PowerAssertTests.Approvals.EndToEndTest.ApproveException(Expression`1 func) in ...\EndToEndTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ new int[]{x + 1, x + 2}[0] == 3
| 2
1

at PowerAssert.PAssert.IsTrue(Expression`1 expression) in ...\PAssert.cs
at PowerAssert.PAssert.IsTrue(Expression`1 expression, CallerLocation location) in ...\PAssert.cs
at PowerAssertTests.Approvals.EndToEndTest.ApproveException(Expression`1 func) in ...\EndToEndTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
| 1
2

at PowerAssert.PAssert.IsTrue(Expression`1 expression) in ...\PAssert.cs
at PowerAssert.PAssert.IsTrue(Expression`1 expression, CallerLocation location) in ...\PAssert.cs
at PowerAssertTests.Approvals.EndToEndTest.ApproveException(Expression`1 func) in ...\EndToEndTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ x.Equals(x)
| False, type BrokenClass has a broken equality implementation (both sides are the same object)
PowerAssertTests.Approvals.EndToEndTest+BrokenClass

at PowerAssert.PAssert.IsTrue(Expression`1 expression) in ...\PAssert.cs
at PowerAssert.PAssert.IsTrue(Expression`1 expression, CallerLocation location) in ...\PAssert.cs
at PowerAssertTests.Approvals.EndToEndTest.ApproveException(Expression`1 func) in ...\EndToEndTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
| 5
5

at PowerAssert.PAssert.IsTrue(Expression`1 expression) in ...\PAssert.cs
at PowerAssert.PAssert.IsTrue(Expression`1 expression, CallerLocation location) in ...\PAssert.cs
at PowerAssertTests.Approvals.EndToEndTest.ApproveException(Expression`1 func) in ...\EndToEndTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ Object.Equals(f, (object)x)
| delegate Func<int>, type: int ()
False, but would have been True if you had invoked 'f'

at PowerAssert.PAssert.IsTrue(Expression`1 expression) in ...\PAssert.cs
at PowerAssert.PAssert.IsTrue(Expression`1 expression, CallerLocation location) in ...\PAssert.cs
at PowerAssertTests.Approvals.EndToEndTest.ApproveException(Expression`1 func) in ...\EndToEndTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ d == (double)f
| False, but the values only differ by 1.49e-006%
0.1

at PowerAssert.PAssert.IsTrue(Expression`1 expression) in ...\PAssert.cs
at PowerAssert.PAssert.IsTrue(Expression`1 expression, CallerLocation location) in ...\PAssert.cs
at PowerAssertTests.Approvals.EndToEndTest.ApproveException(Expression`1 func) in ...\EndToEndTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ String.Join("", x.Select(y => y + x[0])) == new {x, Value = "foobarbaz"}.Value
| ["foo", "bar", "baz"]
"foofoobarfoobazfoo"

at PowerAssert.PAssert.IsTrue[T](T target, Expression`1 expression) in ...\PAssert.cs
at PowerAssert.PAssert.IsTrue[T](T target, Expression`1 expression, CallerLocation location) in ...\PAssert.cs
at PowerAssertTests.Approvals.EndToEndTest.ApproveException[T](T target, Expression`1 func) in ...\EndToEndTest.cs
2 changes: 1 addition & 1 deletion PowerAssertTests/Approvals/EndToEndTest.Enum.approved.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
| SequentialScan
134217728

at PowerAssert.PAssert.IsTrue(Expression`1 expression) in ...\PAssert.cs
at PowerAssert.PAssert.IsTrue(Expression`1 expression, CallerLocation location) in ...\PAssert.cs
at PowerAssertTests.Approvals.EndToEndTest.ApproveException(Expression`1 func) in ...\EndToEndTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
| 134217728
False, FileOptions.Encrypted != FileOptions.SequentialScan

at PowerAssert.PAssert.IsTrue(Expression`1 expression) in ...\PAssert.cs
at PowerAssert.PAssert.IsTrue(Expression`1 expression, CallerLocation location) in ...\PAssert.cs
at PowerAssertTests.Approvals.EndToEndTest.ApproveException(Expression`1 func) in ...\EndToEndTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ __ | __
| False, enumerables differ at index 5, '1' != '2'
"hello1"

at PowerAssert.PAssert.IsTrue(Expression`1 expression) in ...\PAssert.cs
at PowerAssert.PAssert.IsTrue(Expression`1 expression, CallerLocation location) in ...\PAssert.cs
at PowerAssertTests.Approvals.EndToEndTest.ApproveException(Expression`1 func) in ...\EndToEndTest.cs
Loading