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

Add ability to instrument non-referenced assemblies #213

Closed
wants to merge 16 commits into from
Closed

Add ability to instrument non-referenced assemblies #213

wants to merge 16 commits into from

Conversation

jherby2k
Copy link
Contributor

@jherby2k jherby2k commented Oct 12, 2018

As discussed in #192, I want to be able to instrument assemblies that aren't project references (loaded at runtime). I actually have 2 use cases:

  1. Instrumenting a PowerShell core binary module (and its references), which gets loaded via System.Management.Automation.Runspaces.InitialSessionState.ImportPSModule.
  2. Instrumenting "extensions" discovered at runtime using MEF (System.Composition.Hosting), and loaded via Assembly.LoadFrom.

To accomplish this, I added an "IncludeDirectories" parameter which allows additional paths to be searched for modules. It supports relative and absolute paths, and a wildcard (*) for including one level of subdirectories (useful for MEF extensions).

One side effect is the possibility that one or more assemblies exists in multiple locations. I dealt with this by including a hash of the file path in each backup file name, and then merging the results for all files with the same name after instrumentation.

Example that includes both a bunch of extension subdirectories and the PowerShell.Commands module directory (both relative paths from the project output directory):

dotnet test --no-build /p:CollectCoverage=true /p:IncludeDirectories="Extensions\*,PowerShell.Commands"

@codecov
Copy link

codecov bot commented Oct 12, 2018

Codecov Report

Merging #213 into master will decrease coverage by 0.52%.
The diff coverage is 81.08%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #213      +/-   ##
==========================================
- Coverage   94.91%   94.39%   -0.53%     
==========================================
  Files          15       16       +1     
  Lines        1633     1765     +132     
==========================================
+ Hits         1550     1666     +116     
- Misses         83       99      +16
Impacted Files Coverage Δ
src/coverlet.core/Reporters/LcovReporter.cs 96% <0%> (-1.96%) ⬇️
src/coverlet.core/Reporters/JsonReporter.cs 50% <0%> (-16.67%) ⬇️
src/coverlet.core/CoverageResult.cs 15.78% <0%> (-0.43%) ⬇️
src/coverlet.core/Reporters/TeamCityReporter.cs 100% <100%> (ø)
src/coverlet.core/Coverage.cs 99.52% <100%> (+0.02%) ⬆️
src/coverlet.core/Reporters/ReporterFactory.cs 100% <100%> (ø) ⬆️
src/coverlet.core/Reporters/CoberturaReporter.cs 98.55% <66.66%> (-0.72%) ⬇️
src/coverlet.core/Helpers/InstrumentationHelper.cs 92.12% <71.66%> (-6.35%) ⬇️
src/coverlet.core/CoverageSummary.cs 99.14% <87.5%> (+6.48%) ⬆️
src/coverlet.core/Reporters/OpenCoverReporter.cs 99.11% <92.3%> (-0.44%) ⬇️
... and 2 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b54c93c...57cf8d9. Read the comment docs.

@jherby2k
Copy link
Contributor Author

Looks like PR #204 also deals with the merging of files with the same name and different paths.

@pape77
Copy link
Contributor

pape77 commented Oct 14, 2018

@jherby2k check #211 i changed it to merge by file name outside the merge method. Adding modules by filename not path anymore in coverage.cs!

@jherby2k
Copy link
Contributor Author

@pape77 That's a nicer solution, so I've reverted the changes I made to CoverageResult.cs in my PR.

@pape77
Copy link
Contributor

pape77 commented Oct 14, 2018

@jherby2k nice :) Have you retested with this?

@jherby2k
Copy link
Contributor Author

jherby2k commented Oct 14, 2018 via email

@jherby2k
Copy link
Contributor Author

I'm happy to update the readme as well, I just want to confirm this looks good first.

@jherby2k
Copy link
Contributor Author

jherby2k commented Oct 16, 2018

@pape77 on further testing it does not work fine :(. It merges 2 copies of the same file no problem, but it can't instrument 2 different files with the same name at the same time. So I've reverted to my method, which uses the full path as key and then merges them together at the end if the names match. A bit more complicated, but more robust too.

@pape77
Copy link
Contributor

pape77 commented Oct 16, 2018

@jherby2k I couldn't quite follow. In the example of my pr i do a dotnet test on a solution that has several test projects.
The scenario there is more than one proj referencing the same dll from different paths. It adds the key by filename the first time and does compare without problems on the following merges with the same filenames bit different paths.
Can you illustrate a bit more when does it not work? Is it just for scenarios in which you include your extra param to include more files to be merged?

@jherby2k
Copy link
Contributor Author

jherby2k commented Oct 16, 2018 via email

@tonerdo
Copy link
Collaborator

tonerdo commented Nov 4, 2018

@jherby2k kindly fix the merge conflicts so I can review. Thanks!

@tonerdo
Copy link
Collaborator

tonerdo commented Nov 4, 2018

Still have some questions regarding merging results but will have a clearer picture once I can make a more wholesome review. Apologies for letting this slide for too long

@jherby2k
Copy link
Contributor Author

jherby2k commented Nov 4, 2018

@tonerdo No problem! Conflicts resolved. Sorry about all the commits - I had some issues with line endings in Coverage.cs.

@tonerdo
Copy link
Collaborator

tonerdo commented Nov 13, 2018

@jherby2k not to worry. I'm trying to wrap my head around your merging logic and trying to figure out if it's indeed needed. Will take a much deeper look after that

@jherby2k
Copy link
Contributor Author

jherby2k commented Nov 13, 2018 via email

@tonerdo
Copy link
Collaborator

tonerdo commented Nov 20, 2018

@jherby2k just a heads up that this is still on my radar

@ViktorHofer
Copy link
Contributor

@jherby2k Please see my PR #253 which deals with the same issue. Unfortunately I overlooked your PR. If you agree I will incorporate the changes I made in the InstrumentationHelper class into your PR. There's only one problem.

I wanted to avoid having duplicate assembly names at all as that could leave to different issues. My solution is to prioritize the assembly probing:

  1. The test assembly's directory.
  2. The additional passed in directories as they are passed in.

That allows us in corefx i.e. to use the IL version of System.Private.CoreLib.dll. Means, I need a way to load assembly a from directory A but not load assembly a from directory B.

@ViktorHofer
Copy link
Contributor

@jherby2k this feature is fairly time sensitive for us in corefx. It would be great if we could discuss it together with @tonerdo on a channel to get it in as soon as possible. We could either use Gitter, Teams or Skype. Does that work for both of you?

@tonerdo
Copy link
Collaborator

tonerdo commented Nov 26, 2018

Hi @jherby2k, what @ViktorHofer said is something I've been thinking about. Instead of trying to merge results of same name files which IMO could be quite error prone. How about we only add an assembly with a specific name once and ignore others even though they might be in different locations?

@jherby2k
Copy link
Contributor Author

jherby2k commented Nov 26, 2018

@ViktorHofer and @tonerdo

I wanted to avoid having duplicate assembly names at all as that could leave to different issues. My > solution is to prioritize the assembly probing:

  1. The test assembly's directory.
  2. The additional passed in directories as they are passed in.

I'm cool with selecting only one in principal, but in practice the above order isn't necessarily what I want. For example, I have a binary PowerShell module that depends on and returns types defined in "common.dll". The module exists in its own directory. The test assembly also references "common.dll" because it tests the results returned from that module. But I want the one in the module directory instrumented, not the one in the test directory.

@jherby2k
Copy link
Contributor Author

jherby2k commented Nov 26, 2018

How about a MergeIdenticalFileNames switch? Too esoteric?

@ViktorHofer
Copy link
Contributor

Having assemblies with the same assembly and module name isn't support well as it comes with different issues outside of the GAC world. I strongly recommend not going that path...

@ViktorHofer
Copy link
Contributor

@jherby2k
Copy link
Contributor Author

jherby2k commented Nov 26, 2018

Poorly named option then - not what I intended. How about MergeIdenticalAssemblies? With an additional check to see if the assemblies really are identical.

So, if MergeIdenticalAssemblies is enabled, and the assemblies have the same name and are identical, my behavior wins. Otherwise, your behavior.

@ViktorHofer
Copy link
Contributor

Poorly named option then - not what I intended. How about MergeIdenticalAssemblies? With an additional check to see if the assemblies really are identical.

Can you please explain what identical assemblies are and what they are for? Why do I need to instrument identical assemblies multiple times?

@jherby2k
Copy link
Contributor Author

jherby2k commented Nov 26, 2018

you don't. As you said, you just need to instrument one of them. But your intended probe order is the opposite of mine. That's why I just instrument them both and then merge the results. One will have 0 hits, one will have the results you want. It doesn't matter which is which and what the probe order is.

@ViktorHofer
Copy link
Contributor

But instrumenting both takes a considerable amount of time and doesn't seem right. If your issue is the priority, why don't we introduce an option to control it?

@ViktorHofer
Copy link
Contributor

ViktorHofer commented Nov 26, 2018

But I want the one in the module directory instrumented, not the one in the test directory.

I still don't understand why the order matters if they are identical? (Just wanna understand your case better, no offense!)

@jherby2k
Copy link
Contributor Author

jherby2k commented Nov 26, 2018

You might be right, and your solution may do the same thing for me. Let me test yours when I have a bit of time.

@ViktorHofer
Copy link
Contributor

Sure, let me know if I can help somehow. And sorry to be so pushy to get this feature in but we depend on it in corefx ⚡️

@ViktorHofer
Copy link
Contributor

@jherby2k could you accept my PR in the meantime so that @tonerdo can review your PR with my changes? That would be really great. I also noticed that with this work this issue will also be fixed: #246

ViktorHofer and others added 3 commits November 26, 2018 20:48
* Added TeamCityOutput command line flag for printing TeamCity coverage service messages

* Added unit tests for TeamCityServiceMessageWriterTests

* README updates

* Added teamcity reporter

* Removing teamcity output command line param

* README updates

* Cyclomatic complexity computation added

Signed-off-by: Norbert Žofák <nzofak@protank2.onmicrosoft.com>

* Changed UseConsoleOutput boolean to ReporterOutputType enum

* Merging TeamCityServiceMessageWriter with TeamCityReporter

* bump version numbers

* 243 - Swapping percentage with total lines for proper TeamCity statistics

* Allow relative output path for console runner

* Apply relative output path fix to msbuild task as well

* Fix bug in the performance test and document how to run it in README.md.

* fix description for include [*] filters

* Updated Mono.Cecil to fix hang when writing modules.

* Use Windows-style parameters to be consistent with other command examples

* Added an "IncludeDirectories" param so that assemblies that are not project references can also be instrumented.

* Remove WriteLine I accidentally left behind.

* Revert changes to CoverageResult, as merging the same file from multiple locations is already supported in the master branch.

* Go back to full module paths as keys - it wasn't working in all scenarios.

* Skip included directories that don't exist quietly, rather than throwing.

* Restore accidentally-removed #220.

* Apply PR from viktorhofer
@jherby2k
Copy link
Contributor Author

@ViktorHofer Sorry about the unnecessary back and forth - looks like your solution gives the results I want regardless. I've merged your PR and added one extra commit to revert my now-unnecessary backup file naming algorithm.

@ViktorHofer
Copy link
Contributor

Your PR seems wrong as it contains all the commits that happened since you opened yours. You probably want to fix that by rebasing or merging or just take my PR where I already did that: #253

@jherby2k
Copy link
Contributor Author

Blargh, I give up. I'll add my one change to #253.

@jherby2k jherby2k closed this Nov 27, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants