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

Instrumenting non used assemblies produces non existing branches (whith hits =0) in branch coverage. Lowering coverage when using /p:MergeWith #223

Open
pape77 opened this issue Oct 23, 2018 · 23 comments
Labels
feature-request New feature request stale

Comments

@pape77
Copy link
Contributor

pape77 commented Oct 23, 2018

@tonerdo When calculating the coverage from an assembly that's referenced in another test project, I get some differences on branch coverage than when i run the tests for the actual test project that is ment to test that assembly

+----------------------+--------+--------+--------+
| Module               | Line   | Branch | Method |
+----------------------+--------+--------+--------+
| ProjectName.Api         | 100%   | 100%   | 100%   |
+----------------------+--------+--------+--------+
| ProjectName.Application | 100%   | 60%    | 100%   |
+----------------------+--------+--------+--------+
| ProjectName.Data        | 100%   | 62.5%  | 100%   |
+----------------------+--------+--------+--------+

But separately:

ProjectName.Data:

+---------------+--------+--------+--------+
| Module        | Line   | Branch | Method |
+---------------+--------+--------+--------+
| ProjectName.Data | 100%   | 100%   | 100%   |
+---------------+--------+--------+--------+
ProjectName.Application:

+----------------------+--------+--------+--------+
| Module               | Line   | Branch | Method |
+----------------------+--------+--------+--------+
| ProjectName.Application | 100%   | 75%    | 100%   |
+----------------------+--------+--------+--------+
|ProjectName.Data        | 0%     | 0%     | 0%     |
+----------------------+--------+--------+--------+
ProjectName.Api:

+----------------------+--------+--------+--------+
| Module               | Line   | Branch | Method |
+----------------------+--------+--------+--------+
| ProjectName.Api         | 100%   | 100%   | 100%   |
+----------------------+--------+--------+--------+
| ProjectNameApplication | 0%     | 0%     | 0%     |
+----------------------+--------+--------+--------+
| ProjectName.Data        | 0%     | 0%     | 0%     |
+----------------------+--------+--------+--------+

So my idea was to get:

+----------------------+--------+--------+--------+
| Module               | Line   | Branch | Method |
+----------------------+--------+--------+--------+
| ProjectName.Application | 100%   | 75%    | 100%   |
+---------------+--------+--------+--------+
| ProjectName.Data | 100%   | 100%   | 100%   |
+---------------+--------+--------+--------+
| ProjectName.Api         | 100%   | 100%   | 100%   |
+----------------------+--------+--------+--------+

Just noticed ProjectName.Application detects some branches of the ProjectName.Data that ProjectName.Data does not detect when running the coverage for itself alone.

Is this normal/possible?

"ProjectName.Repositories.KeyRepository/<AddAsync>d__4": {
        "System.Void ProjectName.Repositories.KeyRepository/<AddAsync>d__4::MoveNext()": {
          "Lines": {
            "34": 4,
            "35": 4,
            "36": 1,
            "37": 1,
            "40": 3,
            "41": 3,
            "42": 3
          },
"Branches": [
            {
              "Line": 41,
              "Offset": 98,
              "EndOffset": 100,
              "Path": 0,
              "Ordinal": 2,
              "Hits": 0
            },
            {
              "Line": 41,
              "Offset": 98,
              "EndOffset": 165,
              "Path": 1,
              "Ordinal": 3,
              "Hits": 0
            }
          ]
"ProjectName.Data.Repositories.KeyRepository/<AddAsync>d__4": {
        "System.Void ProjectName.Data.Repositories.KeyRepository/<AddAsync>d__4::MoveNext()": {
          "Lines": {
            "34": 4,
            "35": 4,
            "36": 1,
            "37": 1,
            "40": 3,
            "41": 3,
            "42": 3
          },
          "Branches": [
            {
              "Line": 41,
              "Offset": 98,
              "EndOffset": 165,
              "Path": 1,
              "Ordinal": 3,
              "Hits": 1
            }
          ]
@pape77 pape77 changed the title Merge with making average when 0% is found Merge with making average instead of taking the best it found Oct 23, 2018
@pape77 pape77 changed the title Merge with making average instead of taking the best it found MergeWith not properly merging branch coverage? Oct 23, 2018
@pape77 pape77 changed the title MergeWith not properly merging branch coverage? Intrumentation for the same assembly produces different branch coverage Oct 23, 2018
@tonerdo
Copy link
Collaborator

tonerdo commented Oct 26, 2018

@pape77 I don't quite understand what the problem is. Naturally if you run the coverage under different contexts, the final output would be different. Can you perhaps share your project structure and what commands you run at each different stage

@pape77
Copy link
Contributor Author

pape77 commented Oct 26, 2018

@tonerdo I'll try to explain better:

ProjectName.Api references ProjectName.Application and ProjectName.Data

ProjectName.Application references ProjectName.Data

ProjectName.Data doesn't reference any of the other two projects

I named my projects ProjectName.X.Tests where X is the corresponding project that I want to test.
In these test projects I only (and emphasis in only) test functionality that's in X module. The rest I mock it out.

So for example when I run ProjectName.Application.Tests, since ProjectName.Application references ProjectName.Data, coverlet instruments these two. And includes coverage for ProjectName.Data in the resulting coverage file too (with all coverage being 0, so hits = 0 everywhere).

This is as expected. The problem is that this coverage file includes some branchs with hits = 0 that don't exist at all when running a particular coverlet command over ProjectName.Data.Tests

As a result of the latest, when merging ProjectName.Application.Tests and ProjectName.Data.Test coverage files, it will indicate that ProjectName.Data has less coverage that it has on ProjectName.Data.Test on it's own, because of the mentioned non existing but generated branches with hits = 0 in ProjectName.Application.Test result

Codes to exec this are really simple:

dotnet test .\test\ProjectName.Application.Tests\  -l trx -r /results /p:CollectCoverage=true /p:Coverlet
OutputFormat='json' /p:CoverletOutput=/results/coverage

and then compare that output file with coverage file for ProjectName.Data module on it's own

dotnet test .\test\ProjectName.Data.Tests\  -l trx -r /results /p:CollectCoverage=true /p:Coverlet
OutputFormat='json' /p:CoverletOutput=/results/coverage

With the pr at #225 what I do is don't instrument the dlls that are referenced in test projects and not used/tested at all (as in my case, in which i mock everything, but still have the reference because i use the project's classes, and because it's referenced in the project I'm actually testing)

Here are the two files that I got generated with the commands above
Coverage.json for Application.Tests:
https://textuploader.com/dwtb4

Coverage.json for Data.Tests:
https://textuploader.com/dwtbp

Methods like FindAsync and AddAsync have branches with 0 hits in ProjectName.Application.Tests which don't exist in ProjectName.Data.Tests and the instrumented dll is the same.

I think this is not ok, I've tried writing all kind of tests for those silly 2 methods and I couldn't get the branch that is marked with 0 hits in Application to pop in the Data coverage file even with 0 hits whatsoever

Hopefully it's more clear now

Note: I only made focus on Application and Data projects. But same happens for Api Project in my first comment of the issue, since it references Application and Data projects

Note2: Note that the merging does work correctly but I just made a quick mention of it here since it's not part of the real issue i want to illustrate, if i run


dotnet test .\ProjectName.Data.Tests -l trx -r /results /p:CollectCoverage=true /p:CoverletOutputFormat='json%2
copencover' /p:CoverletOutput=/results/coverage

dotnet test .\test\ProjectName.Application.Tests\  -l trx -r /results /p:CollectCoverage=true /p:Coverlet
OutputFormat='json' /p:CoverletOutput=/results/coverage  /p:MergeWith='/results/coverage.json'

the result file will be the merged one between these 2 projects. Problem being, since the second run produces branches with 0 hits that don't exist in the output of the first command line, the merged coverage file will indeed increment hits only in the branches that are in the first run. Making the global coverage for ProjectName.Data to decrease (since branches with 0 hits exist in the final, merged, output)

@pape77
Copy link
Contributor Author

pape77 commented Oct 26, 2018

In when trying to solve this (pr #225) i found a comment in the code that called my attention

In Coverage.cs line 169 :
// File not instrumented, or nothing in it called. Warn about this?
So when nothing in it is called, it's still instrumented and included in coverage.json, this is what i want to avoid, because if would be ok if it didn't pollute coverage, but since it does and we are not calling anything within this dll, then why should it pop in the final coverage file?
This is why, when finding something like this, i remove it from the _results variable

@pape77
Copy link
Contributor Author

pape77 commented Nov 1, 2018

@tonerdo have you had the chance to take a second look at this? Hope it's clear now

@tonerdo
Copy link
Collaborator

tonerdo commented Nov 4, 2018

@pape77 I think I get it now. I've noticed branch coverage reporting defaults to 0 when there are no branches, which shouldn't be the case. In terms of nothing being called I think I want to keep that default behaviour and I suggest you simply exclude the offending assembly

@pape77
Copy link
Contributor Author

pape77 commented Nov 4, 2018

@tonerdo I cannot exclude the assembly since it's being used by the classes at my other project . If you ment by command line param, again i can't since i run this for the whole solution in one line.

But most importantly, the problem is not branch coverage defaulting to 0 if not covered. The problem is that the non used assembly is generating NON EXISTING branches, with coverage 0 which will never be covered by the actual tests in other projects and therefore not overrided when merging. This will make code coverage % to go down, not reflecting reality. That's why i want to exclude the assembly if nothing there is used, which... Make sense right? What's your reason for keeping it?

@pape77
Copy link
Contributor Author

pape77 commented Nov 4, 2018

@tonerdo you mean the /p:Exclude param then. I have to specify what to exclude and i cannot make a generic test run for all my projects if I do, which is my final goal (probably not the only one with it).
Moreover, I would have to make one command line per test proj to exclude what i need, instead of one for the solution doing all the job.
Why should we keep this if it's polluting the coverage? :/ I doubt if anybody has noticed this behaviour, or they just see the file being generated and don't pay attention to what's happening.

@pape77
Copy link
Contributor Author

pape77 commented Nov 4, 2018

@tonerdo One option could be adding a command line option param to exclude non used assemblies, if you really want to keep current behaviour as default. Wouldn't mind modifying my pr to do so.
But I really think the default bahaviour should be outputing the correct coverage when merging 😅

@pape77 pape77 changed the title Intrumentation for the same assembly produces different branch coverage Intrumenting non used assemblies produces non existing branches (whith hits =0) in branch coverage. Lowering coverage when using /p:MergeWith Nov 5, 2018
@tonerdo tonerdo changed the title Intrumenting non used assemblies produces non existing branches (whith hits =0) in branch coverage. Lowering coverage when using /p:MergeWith Instrumenting non used assemblies produces non existing branches (whith hits =0) in branch coverage. Lowering coverage when using /p:MergeWith Nov 6, 2018
@pape77
Copy link
Contributor Author

pape77 commented Nov 9, 2018

@tonerdo i updated my pr to add this as an option so default behaviour remains the same and fixes the issues for those who need to exclude non used files from coverage (#225)

I got an error when doing some http post call for a vs 2015 test on app veyor. Probably if you re run it, it will work.
Would you consider this for merging with this as an option? :)

@ghost
Copy link

ghost commented Feb 1, 2019

Any news on this issue? I can't really tell my coworkers to use Coverlet in our builds if the coverage decreases in complex solutions where many project references are used. Our coverage will be lower numbers that don't make much sense, if I understand the issue correctly.

Like pape77, we really insist on running our tests per solution and having good results without any manual setup in our builds.

Thanks!

@pape77
Copy link
Contributor Author

pape77 commented Feb 1, 2019

@vlef i already gave up and forked away. If you use current coverlet, even more things that I don't care about (and maybe you don't either) gets into the report. Like certain external dlls i use.
So decided to implement this in my own fork, sadly

@ghost
Copy link

ghost commented Feb 1, 2019

Thanks for the heads up! That is quite unfortunate, especially since even the official Microsoft docs link to Coverlet.

@pape77
Copy link
Contributor Author

pape77 commented Feb 1, 2019

Yep. I first wanted to exclude them by default. Then came with this option variable to keep the original behavior for whoever uses it. But no luck it seems 😢

@pape77
Copy link
Contributor Author

pape77 commented Feb 1, 2019

@vlef if you plan to fork, you can copy the code from my original pr (so not the last commit but the first one). That will instrument only the dlls that you are touching with your tests. Although, i believe (as i mentioned) that now more things get instrumented even, not only the dlls with pdb files, but all. So you may have to remove a couple more things to make it look like the version i changed in my pr by that time

@MarcoRossignoli MarcoRossignoli added the feature-request New feature request label Sep 25, 2019
@pkrukp
Copy link

pkrukp commented Jan 26, 2021

@MarcoRossignoli why is it marked as feature request? As I understand this is a bug:

The problem is that the non used assembly is generating NON EXISTING branches

That is coverlet might detect fake branches.

@MarcoRossignoli
Copy link
Collaborator

Is this issue still present?

So when nothing in it is called, it's still instrumented and included in coverage.json, this is what i want to avoid, because if would be ok if it didn't pollute coverage, but since it does and we are not calling anything within this dll,....

I moved to feature because IIRW the idea is to remove module with no coverage to avoid to mess stats and to me is better add a feat like --exlude... I don't think it's a bug, can you provide a repro with the issue just in case?

@pkrukp
Copy link

pkrukp commented Jan 26, 2021

I don't know if this issue is still present, don't have repro, maybe @pape77 could comment?

@pape77
Copy link
Contributor Author

pape77 commented Jan 26, 2021

Hi guys, i forked away from this project since as @MarcoRossignoli knows, i made a pr for trying to fix this and other issues i have when running coverlet and trying to merge coverage when having tests in different projects.
That pr didn't have the blessing of the repo owner, not even adding optional parameters so the original functionality of coverlet is not affected.
I once in a while try the newest version of coverlet only to see the same issues are still there.
I haven't tried the latest in a while, feel free to try to repro. For now, my fork (and quite an old one sadly) works for my purposes just good.
So a valid repro would be having a project with some code in it (preferably a real one, samples usually don't have many branches or code complexity at all), create at least two test projects (csproj) and try running coverlet on both merging afterwards.
What i saw back then was:

  • Weird dlls were instrumented, which weren't related to the project itself. Like you include a library you want to use, and it gets instrumented as part of your project, with coverage 0 obviously
  • Projects containing only models, which were referenced by another projects and naturally had 0 branches to cover(since they are all models), were showing branch coverage 100% when running coverage on the project itself, but were showing insufficient branch coverage (and these "ghost" branches) when running coverage on a project that references them
  • Merge of coverage being completely off when running coverage on different test projects covering the same codebase, and merging the results

Mainly problem 1 i tried to fix in an old pr, and i think it was fixing 1&2.

You can try to repro this if you want, or ignore it. As you wish.
I'm not using coverlet these days as it doesn't suffice my purposes (or my company's)

Good luck!

@pkrukp
Copy link

pkrukp commented Jan 26, 2021

Thanks, I hoped you have some small repro that shows the "ghost branches" that you could share, I understand if you don't have it.

@pape77
Copy link
Contributor Author

pape77 commented Jan 26, 2021

I don't, i tested this with a repo of my company which I can't share
Was long time ago, so I don't even remember which one it was, sorry

@pape77
Copy link
Contributor Author

pape77 commented Jan 26, 2021

how does it work currently for merging coverage in different test projects? I see no more merge option in the docs, is that done automatically?
In case i run it again with latest version

Copy link

This issue is stale because it has been open for 90 days with no activity.

@github-actions github-actions bot added the stale label Jul 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request New feature request stale
Projects
None yet
Development

No branches or pull requests

4 participants