hazzik / DelegateDecompiler

A library which is able to decompile a delegate or a method body to its lambda representation
MIT License
526 stars 62 forks source link

Unit Tests that test methods with Decompile fail while trying to calculate code coverage #15

Open VenolVl opened 10 years ago

VenolVl commented 10 years ago

Hello, i have encountered an issue with DelegateDecompiler: When i try to test methods that use Decompile(), those tests are passing when i just run them, but when i try to calculate code coverage i receive following error in every test that use Decompile():

System.AggregateException: One or more errors occurred. ---> System.InvalidCastException: Specified cast is not valid. at DelegateDecompiler.Processor.Process(Instruction instruction, Instruction last) at DelegateDecompiler.Processor.Process(Instruction instruction, Type returnType) at DelegateDecompiler.MethodBodyDecompiler.Decompile() at DelegateDecompiler.DecompileExtensions.<>cDisplayClass1.b0(MethodInfo m) at DelegateDecompiler.Cache2.GetOrAdd(TKey key, Func2 func) at DelegateDecompiler.DecompileExtensions.Decompile(MethodInfo method) at DelegateDecompiler.DecompileExpressionVisitor.Decompile(MethodInfo method, Expression instance, IList1 arguments) at DelegateDecompiler.DecompileExpressionVisitor.VisitMember(MemberExpression node) at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor) at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) at System.Linq.Expressions.ExpressionVisitor.VisitMemberAssignment(MemberAssignment node) at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node) at System.Linq.Expressions.ExpressionVisitor.Visit[T](ReadOnlyCollection1 nodes, Func2 elementVisitor) at System.Linq.Expressions.ExpressionVisitor.VisitMemberInit(MemberInitExpression node) at System.Linq.Expressions.MemberInitExpression.Accept(ExpressionVisitor visitor) at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression1 node) at System.Linq.Expressions.Expression1.Accept(ExpressionVisitor visitor) at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) at System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression node) at System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor) at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) at System.Linq.Expressions.ExpressionVisitor.VisitArguments(IArgumentProvider nodes) at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node) at DelegateDecompiler.DecompileExpressionVisitor.VisitMethodCall(MethodCallExpression node) at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor) at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) at DelegateDecompiler.DecompileExtensions.Decompile[T](IQueryable1 self) at IBCM.Models.Readers.TaskReader.d__6.MoveNext() in D:\dev\ibcm\src\Models\Readers\TaskReader.cs:line 41 --- End of stack trace from previous location where exception was thrown ---

I tried using nCrunch, dotCover and Visual Studio Coverage Analyzer, but tests are failing no matter which test engine i am using. As i said it happens only when i try to calculate code coverage, tests are working when i run them manually. Example of method with Decompile:

public async virtual Task<Tuple<IEnumerable<TodoInfo>, IEnumerable<MeetingInfo>>> GetPastTasksByContactId(Guid contactId)
        {
            var todoes = await taskService.GetPastTodoesByContactId(contactId).Project().To<TodoInfo>().Decompile().ToListAsync();
            var meetings = await appointmentService.GetPastMeetingsByContactId(contactId).Project().To<MeetingInfo>().Decompile().ToListAsync();

            return new Tuple<IEnumerable<TodoInfo>, IEnumerable<MeetingInfo>>(todoes, meetings);
        }
mwillebrands commented 9 years ago

Is probably fixed, https://github.com/hazzik/DelegateDecompiler/issues/39

BrettBock commented 3 years ago

Still seeing this issue in Visual Studio 2019 Enterprise 16.9.4, using EFCore 5.0.5. However the error I see now is as follows:

... Test Outcome: Failed Result StackTrace:
at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, String methodParamName, String argumentParamName, Int32 index) at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression arg0) at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable1 arguments) at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression[] arguments) at DelegateDecompiler.Processor.BuildMethodCallExpression(MethodInfo m, Address instance, Expression[] arguments) at DelegateDecompiler.Processor.Call(ProcessorState state, MethodInfo m) at DelegateDecompiler.Processor.Process() at DelegateDecompiler.Processor.Process(VariableInfo[] locals, IList1 args, Instruction instruction, Type returnType) at DelegateDecompiler.MethodBodyDecompiler.Decompile(MethodInfo method, Type declaringType) at DelegateDecompiler.DecompileExtensions.<>cDisplayClass6_0.<.cctor>b__1() at System.Lazy1.ViaFactory(LazyThreadSafetyMode mode) --- End of stack trace from previous location where exception was thrown --- at System.Lazy1.CreateValue() at System.Lazy1.get_Value() at DelegateDecompiler.DecompileExtensions.Decompile(MethodInfo method, Type declaringType) at DelegateDecompiler.DecompileExpressionVisitor.Decompile(MethodInfo method, Expression instance, IList1 arguments) at DelegateDecompiler.DecompileExpressionVisitor.VisitMember(MemberExpression node) at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor) at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression1 node) at System.Linq.Expressions.Expression1.Accept(ExpressionVisitor visitor) at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) at System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression node) at System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor) at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes) at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node) at DelegateDecompiler.DecompileExpressionVisitor.VisitMethodCall(MethodCallExpression node) at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor) at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes) at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node) at DelegateDecompiler.DecompileExpressionVisitor.VisitMethodCall(MethodCallExpression node) at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor) at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes) at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node) at DelegateDecompiler.DecompileExpressionVisitor.VisitMethodCall(MethodCallExpression node) at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor) at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) at DelegateDecompiler.DecompileExpressionVisitor.Decompile(Expression expression) at DelegateDecompiler.DecompiledQueryProvider.CreateQuery[TElement](Expression expression) at DelegateDecompiler.DecompileExtensions.Decompile[T](IQueryable`1 self) ... Result Message: System.ArgumentException : Expression of type 'System.Int64' cannot be used for parameter of type 'System.UInt64' of method 'Void 482083558coverage_runtime_method__13811_20409__(UInt64)' (Parameter 'arg0')

GravlLift commented 2 years ago

I'm encountering this as well in 0.30.0. dotnet test works just fine, as does dotnet test "--collect:XPlat Code Coverage". But dotnet test "--collect:Code Coverage" is problematic.

Stepped into it with the debugger. The instructions generated below are radically different depending on if --collect:Code Coverage is specified or not: https://github.com/hazzik/DelegateDecompiler/blob/6a2046e8b79e54822bf8cf85f21a4b3b16ec0178/src/DelegateDecompiler/MethodBodyDecompiler.cs#L42-L46

In my case, that results in a stack with a bunch of extraneous ints in it, crashing when it tries to call HasValue on an Int32 when it's supposed to be calling it on a nullable DateTimeOffset.

Good instructions: image Bad instructions: image

Bad stack, after popping off an Int32 to throw an ArgumentException (correct target is item 1 in the stack): image

Good stack, empty after correctly popping the nullable DateTimeOffset: image

I guess the integers aren't really a problem, the real problem is why isn't instructions[10] on the top of the stack?

EDIT: First time working with IL, so this is taking me a bit, but it appears that the stloc instruction at index 3/9 pops whatevers on top of the stack into the local variable array at index 1. In the non-code coverage version of the code, that's the value that was just returned from 0002, the call to get that DateTimeOffset. In the code coverage version, that's instead the value 1, which was loaded onto the top of the stack by 001b.

Seems like DelegateDecompiler is interpreting these instructions correctly, it's just that the instructions themselves are garbled.

GravlLift commented 2 years ago

@hazzik, I've managed to put together a minimal reproduction for this one. It's very strange.