-
Notifications
You must be signed in to change notification settings - Fork 36
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
Fix some missed hits #131
Fix some missed hits #131
Conversation
I'll take a look soon! <3 |
Hmmm, it seems all good, but there's been quite a lot of changes, so I'm unsure what exactly the problem was, and what fixed it. Can you elaborate on that? |
return new MethodContext(hitsPath, assemblyName, className, methodName); | ||
} | ||
|
||
public class MethodContext : IDisposable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed this to MethodScope and moved into a separate file. Because of this renaming the IL representation in all tests changed from MiniCover.HitServices.HitService/MethodContext
to MiniCover.HitServices.MethodScope
It is not working as I expected, I will change probably change it a bit before explaining further |
Some context. For every IL instruction executed, we need to record it was hit. public void MyMethod() {
MethodScope methodScope;
try {
methodScope = HitService.EnterMethod("MyAssembly", "MyClass", "MyMethod"); //We hardcode the string values during instrumentation
methodScope.Hit(1); //We give every instruction an unique integer id, and record a hit before it is executed. The relationship between those IDs and the source files is saved in a "coverage.json" during instrumentation.
...first IL instruction
methodScope.Hit(2);
...second IL instruction
...
} finally {
methodScope.Dispose();
}
} This instrumentation contract is solid enough to not be changed very often. So we can easily do fixes and change the behavior as long as HitContext.EnterMethod is still there and returns a MethodScope object with methods Hit and Dispose. When your code run, we save all hit ids in folder The HitContext class is a container to store hits that were triggered by the same entry method. One of the challenges of recording hits is when to write them to the disk. We tried in the past to save hits to disk on AppDomain.CurrentDomain.DomainUnload event. But when the domain is unloading, your event handler has only about 2 seconds to execute, and it might not be enough to write all hits to disk. Also, if the application crashes and skips domain unloading you wouldn't write anything. So, the approach we take now is to save the HitContext to disk when we exit the entry method. That would work well, but not for async methods. [Fact]
public async Task AsyncTest()
{
await Task.Delay(100);
var a = 1 + 2;
} Will kind of be invoked twice. The first time will execute up to the await. The second time will run the rest of the method. The dirty solution implemented in this PR is to rewrite the full HitContext to disk after the second execution finishes. I also added a refcount, so in case there are multiple methods sharing the same HitContext in parallel, it will only rewrite to the disk when the last method finishes (refcount = 0). I just realized maybe another fix would be to just replace AsyncLocal with just a static variable. So each time the state machine is invoked it creates a new HitContext, and writes it to disk once the state machine leaves. |
@ffMathy You could also try to get the running test from XUnit, Nunit... But some of them probably don't have mechanisms to know what's the running test. |
{ | ||
Directory.CreateDirectory(_hitsPath); | ||
|
||
var fileName = Path.Combine(_hitsPath, $"{Guid.NewGuid()}.hits"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still wrong. When it rewrites the HitContext to disk it will generate a new id and duplicate the hits in 2 files.
I will change it to make sure the same HitContext is always written to the same file. Maybe I can optimize as well to only write the diff the second time the hit context is saved.
Thank you for the elaborate description! I'll review once you've made the additional changes you're referring to. In addition, I think it'd be quite effective to have some integration tests that actually compile a sample project, and then instrument the assemblies, run them, gather a report, and read that the values in the report are correct. That way, we don't only just look at the IL - we also make sure it actually works in reality. What do you think of this? If that sounds good, I'll probably add some integration tests to my next contribution 😊 |
Sounds good, but maybe do that in a separate PR. |
Interesting. By the way, can we use some of this? See the async state machine part: https://dev.to/nikiforovall/awaitable-awaiter-pattern-and-logical-micro-threading-in-c-2701 |
a74269a
to
0dad525
Compare
0dad525
to
966f5c7
Compare
@ffMathy |
The solution in this PR is now better than what we had before and will handle async as well. |
Updates and fixes some missed hits because it happened after hits file were written.
@ffMathy
Maybe you can review this.
Might not cover all edge cases, but fixes #103 and #126