Skip to content

Commit

Permalink
Use reference instead of definition when using message in async state…
Browse files Browse the repository at this point in the history
… machine (#527)
  • Loading branch information
GeertvanHorrik authored May 23, 2023
1 parent 4bb81b7 commit 398f36d
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 133 deletions.
9 changes: 9 additions & 0 deletions AssemblyWithInterceptorAndFormatting/ClassWithAsyncMethod.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MethodTimer;
#pragma warning disable 414
Expand Down Expand Up @@ -80,6 +81,14 @@ public async Task MethodWithFastPathAsync(bool recurse, string fileName, int id)
isRunning = false;
}

[Time("some message")]
public async Task<List<T>> MethodWithGenericResultAsync<T>()
{
await Task.Delay(50);

return new List<T>();
}

public override string ToString() =>
"TEST VALUE";
}
21 changes: 21 additions & 0 deletions AssemblyWithoutInterceptor/ClassWithAsyncMethod.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MethodTimer;
#pragma warning disable 414
Expand Down Expand Up @@ -102,3 +103,23 @@ public async Task<bool> ComplexMethodWithAwaitAsync(int instructionsToHandle)
public async Task MethodWithExceptionAsync() =>
await Task.Factory.StartNew(() => throw new ArgumentOutOfRangeException());
}

public class ClassWithGenericResultAsyncMethodBase
{
public virtual async Task<List<T>> DoSomethingAsync<T>()
{
await Task.Delay(50);

return new List<T>();
}
}

public class ClassWithGenericResultAsyncMethod : ClassWithGenericResultAsyncMethodBase
{
[Time()]
public async Task<List<T>> DoSomethingWithoutMessageAsync<T>()
{
var result = await base.DoSomethingAsync<T>();
return result;
}
}
4 changes: 2 additions & 2 deletions MethodTimer.Fody/AsyncMethodProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -348,14 +348,14 @@ IEnumerable<Instruction> GetWriteTimeInstruction(MethodBody methodBody)
{
yield return Instruction.Create(OpCodes.Call, ModuleWeaver.ElapsedMilliseconds);
yield return Instruction.Create(OpCodes.Ldarg_0);
yield return Instruction.Create(OpCodes.Ldfld, formattedFieldDefinition);
yield return Instruction.Create(OpCodes.Ldfld, formattedFieldReference);
yield return Instruction.Create(OpCodes.Call, logWithMessageMethodUsingLong);
}
else
{
yield return Instruction.Create(OpCodes.Call, ModuleWeaver.Elapsed);
yield return Instruction.Create(OpCodes.Ldarg_0);
yield return Instruction.Create(OpCodes.Ldfld, formattedFieldDefinition);
yield return Instruction.Create(OpCodes.Ldfld, formattedFieldReference);
yield return Instruction.Create(OpCodes.Call, logWithMessageMethodUsingTimeSpan);
}
}
Expand Down
24 changes: 24 additions & 0 deletions Tests/AssemblyTesterSets/WithInterceptorAndFormattingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,30 @@ public void ClassWithAsyncMethodWithFastPath(bool recurse)
Assert.Equal("File name '123' with id '42'", message);
}

[Fact]
public void ClassWithGenericAsyncMethod()
{
ClearMessage();

var type = testResult.Assembly.GetType("ClassWithAsyncMethod");
var instance = (dynamic)Activator.CreateInstance(type);
TraceRunner.Capture(() =>
{
var task = (Task)instance.MethodWithGenericResultAsync<int>();
task.Wait();
});

var methodBases = GetMethodInfoField();
var methodBase = methodBases.Last();
Assert.Equal("MethodWithGenericResultAsync", methodBase.Name);

var messages = GetMessagesField();
Assert.Single(messages);

var message = messages.First();
Assert.Equal("some message", message);
}

static void ClearMessage()
{
methodBaseField.SetValue(null, new List<MethodBase>());
Expand Down
151 changes: 151 additions & 0 deletions Tests/AssemblyTesterSets/WithoutInterceptorTests.async.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Xunit;

public partial class WithoutInterceptorTests
{
[Fact]
public void MethodWithEmptyAsync()
{
var type = testResult.Assembly.GetType("ClassWithAsyncMethod");
var instance = (dynamic)Activator.CreateInstance(type);
var message = TraceRunner.Capture(() =>
{
var task = (Task)instance.MethodWithEmptyAsync();
task.Wait();
});

Assert.Single(message);
Assert.StartsWith("ClassWithAsyncMethod.MethodWithEmptyAsync ", message.First());
}

[Fact]
public void ClassWithAsyncMethod()
{
var type = testResult.Assembly.GetType("ClassWithAsyncMethod");
var instance = (dynamic)Activator.CreateInstance(type);
var message = TraceRunner.Capture(() =>
{
var task = (Task)instance.MethodWithAwaitAsync();
task.Wait();
});

Assert.Single(message);
Assert.StartsWith("ClassWithAsyncMethod.MethodWithAwaitAsync ", message.First());
}

[Fact]
public void ClassWithAsyncMethodThatThrowsException()
{
var type = testResult.Assembly.GetType("ClassWithAsyncMethod");
var instance = (dynamic)Activator.CreateInstance(type);
var message = TraceRunner.Capture(() =>
{
try
{
var task = (Task)instance.MethodWithAwaitAndExceptionAsync();
task.Wait();
}
catch (Exception)
{
// Expected
}
});

Assert.Single(message);
Assert.StartsWith("ClassWithAsyncMethod.MethodWithAwaitAndExceptionAsync ", message.First());
}

[Fact]
public async Task ClassWithGenericTaskWithoutMessageAsyncMethod()
{
var type = testResult.Assembly.GetType("ClassWithGenericResultAsyncMethod");
var instance = (dynamic)Activator.CreateInstance(type);
var message = await TraceRunner.CaptureAsync(async () =>
{
var task = (Task)instance.DoSomethingWithoutMessageAsync<int>();
await task;
});

Assert.Single(message);
Assert.StartsWith("ClassWithGenericResultAsyncMethod.DoSomethingWithoutMessageAsync", message.First());
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void ClassWithAsyncMethodWithFastPath(bool recurse)
{
var type = testResult.Assembly.GetType("ClassWithAsyncMethod");
var instance = (dynamic)Activator.CreateInstance(type);
var message = TraceRunner.Capture(() =>
{
var task = (Task)instance.MethodWithFastPathAsync(recurse);
task.Wait();
});

Assert.Equal(recurse ? 2 : 1, message.Count);
Assert.StartsWith("ClassWithAsyncMethod.MethodWithFastPathAsync ", message.First());
}

[Fact]
public void ClassWithExceptionAsyncMethod()
{
var type = testResult.Assembly.GetType("ClassWithAsyncMethod");
var instance = (dynamic)Activator.CreateInstance(type);
var message = TraceRunner.Capture(() =>
{
var task = (Task)instance.ComplexMethodWithAwaitAsync(-1);
task.Wait();
});

Assert.Single(message);
Assert.StartsWith("ClassWithAsyncMethod.ComplexMethodWithAwaitAsync ", message.First());
}

[Fact]
public void ClassWithFastComplexAsyncMethod()
{
var type = testResult.Assembly.GetType("ClassWithAsyncMethod");
var instance = (dynamic)Activator.CreateInstance(type);
var message = TraceRunner.Capture(() =>
{
var task = (Task)instance.ComplexMethodWithAwaitAsync(0);
task.Wait();
});

Assert.Single(message);
Assert.StartsWith("ClassWithAsyncMethod.ComplexMethodWithAwaitAsync ", message.First());
}

[Fact]
public void ClassWithMediumComplexAsyncMethod()
{
var type = testResult.Assembly.GetType("ClassWithAsyncMethod");
var instance = (dynamic)Activator.CreateInstance(type);
var message = TraceRunner.Capture(() =>
{
var task = (Task)instance.ComplexMethodWithAwaitAsync(2);
task.Wait();
});

Assert.Single(message);
Assert.StartsWith("ClassWithAsyncMethod.ComplexMethodWithAwaitAsync ", message.First());
}

[Fact]
public void ClassWithSlowComplexAsyncMethod()
{
var type = testResult.Assembly.GetType("ClassWithAsyncMethod");
var instance = (dynamic)Activator.CreateInstance(type);
var message = TraceRunner.Capture(() =>
{
var task = (Task)instance.ComplexMethodWithAwaitAsync(100);
task.Wait();
});

Assert.Single(message);
Assert.StartsWith("ClassWithAsyncMethod.ComplexMethodWithAwaitAsync ", message.First());
}
}
Loading

0 comments on commit 398f36d

Please sign in to comment.