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

Feature profdata #99

Closed
wants to merge 18 commits into from
Closed

Feature profdata #99

wants to merge 18 commits into from

Conversation

cuteiosdev
Copy link
Contributor

This is a simple rebase of the work that @viteinfinite has done to get xcode7 coverage reports into slather.

Would be great for people to get this rebase looked over so that the functionality can be merged in.

This should bring #92 up to date so that it can be merged in.

viteinfinite and others added 6 commits August 29, 2015 16:32
…te/slather into feature-temp-profdata

* 'feature-temp-profdata' of https://github.com/viteinfinite/slather:
  Remove useless file
  Fix tests
  Remove useless files
  Fix unit tests
  Add more precise path handling
  Add scheme and ignore
  Export coverage base info into modules
  Fix coveralls_spec
  Minor fixes in spec_helper
  Re-add coveralls support
  Re-add coveralls support
  Add tests
  [WIP] Refactor interface of coverage_file
  [WIP] Add unit tests
  [WIP] Add experimental support to swift
raise StandardError, "No Coverage.profdata files found. Please make sure the \"Code Coverage\" checkbox is enabled in your scheme's Test action or the build_directory property is set."
end
xcode_path = `xcode-select -p`.strip
llvm_cov_command = File.join(xcode_path, "Toolchains/XcodeDefault.xctoolchain/usr/bin/llvm-cov show -instr-profile #{coverage_profdata} #{binary_file}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we use xcode_path.shellescape in case the xcode_path contains spaces, parentheses, or other characters that needs escaping?
Probably same for coverage_profdata and binary_file btw, so maybe even use shelljoin?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch

@AliSoftware
Copy link
Contributor

Very interested in this being available, as our CI can't build coverage reports using the legacy gcno files anymore when building with Xcode 7 see Apple forums so I'm hoping to migrate to profdata files instead.

What's missing before accepting this PR? How can we help making it mergable?

Thx a lot!

@marklarr
Copy link
Contributor

Those changes you mentioned with shellescape sound good. Also, it looks like we've got some failing tests out on CI. Granted we fix those two things, we could merge today or whenever.

@viteinfinite @AliSoftware @mattdelves

As an aside, obviously I'll continue to maintain this gem, but if anybody is ever eager to try out a new feature before it makes its way into the official Slather rubygem, you can always point your Gemfile to use a specific fork of slather

gem 'slather', :git => "https://github.com/viteinfinite/slather"

That'll pull master off of the fork. You can also specify a :branch if you need to.

@AliSoftware
Copy link
Contributor

Great news, thx!

I thought about testing that branch today, but lost a lot of time trying to debug that Xcode 7 bug with gcno files instead, so maybe I'll have time next week, but if it's merged and released in the meantime that would be much better 👍

PS: thx for the awesome tool, I just switched from gcovr to slather recently and loving it so far!

@mgrebenets
Copy link

Getting an error when trying to generate HTML report slather coverage --html ....

Slathering...
/Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bundler/gems/slather-8aa1c122603b/lib/slather/coverage_info.rb:74:in `source_file_pathname_relative_to_repo_root': undefined method `realpath' for nil:NilClass (NoMethodError)
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bundler/gems/slather-8aa1c122603b/lib/slather/coverage_info.rb:79:in `block in ignored?'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bundler/gems/slather-8aa1c122603b/lib/slather/coverage_info.rb:78:in `any?'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bundler/gems/slather-8aa1c122603b/lib/slather/coverage_info.rb:78:in `ignored?'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bundler/gems/slather-8aa1c122603b/lib/slather/project.rb:71:in `block in profdata_coverage_files'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bundler/gems/slather-8aa1c122603b/lib/slather/project.rb:69:in `map'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bundler/gems/slather-8aa1c122603b/lib/slather/project.rb:69:in `profdata_coverage_files'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bundler/gems/slather-8aa1c122603b/lib/slather/project.rb:44:in `coverage_files'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bundler/gems/slather-8aa1c122603b/lib/slather/coverage_service/html_output.rb:19:in `post'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bundler/gems/slather-8aa1c122603b/bin/slather:80:in `post'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bundler/gems/slather-8aa1c122603b/bin/slather:46:in `execute'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/gems/clamp-0.6.5/lib/clamp/command.rb:67:in `run'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/gems/clamp-0.6.5/lib/clamp/subcommand/execution.rb:11:in `execute'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/gems/clamp-0.6.5/lib/clamp/command.rb:67:in `run'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/gems/clamp-0.6.5/lib/clamp/command.rb:132:in `run'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/gems/clamp-0.6.5/lib/clamp.rb:6:in `Clamp'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bundler/gems/slather-8aa1c122603b/bin/slather:6:in `<top (required)>'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bin/slather:23:in `load'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bin/slather:23:in `<main>'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bin/ruby_executable_hooks:15:in `eval'
    from /Users/maksymgrebenets/.rvm/gems/ruby-2.2.1/bin/ruby_executable_hooks:15:in `<main>'

@mgrebenets
Copy link

Are there any plans to merge this PR or the other one (#92) soon?

@marklarr
Copy link
Contributor

We need to get

  • Tests working
  • slather coverage --html working
  • Paths escaped (thanks @AliSoftware )

…le CLANG_ENABLE_CODE_COVERAGE instead of GCC_GENERATE_TEST_COVERAGE_FILES

+ Print explicit error when missing parameter for "slather setup"

TODO: improve Xcodeproj gem so my new Xcodeproj::XCScheme implementation also allows to set codeCoverageEnabled = YES in every xcscheme
This will need us to update the dependency against the xcodeproj gem to "~> 0.27" (which requires to bump the dependency on cocoapods as well)
@AliSoftware
Copy link
Contributor

@marklarr I've created a PR on mattdelves/slather#1 to escape llvm-cov path and arguments and improve slather setup a bit when used with profdata. @mattdelves You can merge it so my commits are added in your branch thus also added in this very PR #99 here.

Note: Ideally to make slather setup --input-format profdata fully automatic, and allow it to modify the xcscheme to change the "Gather coverage data" flag by itself, we'd need at least xcodeproj-0.27.0; but since bumping the version of xcodeproj (and thus cocoapods which also depends on it) may have side effects maybe, I figured we could keep that for a later, dedicated PR, and only have a suggestion message in the meantime to at least have a minimal working version ASAP.

@mgrebenets
Copy link

@AliSoftware modify the xcscheme to change the "Gather coverage data" flag by itself.

Is this really required? The xcodebuild -enableCodeCoverage YES does the job without the need to modify original Xcode project.

@AliSoftware
Copy link
Contributor

@mgrebenets the whole point of slather setup is to prepare the project so it generates the coverage files if you execute your tests with Cmd-U in Xcode.

If you execute your tests in the CLI there is no real benefit of using slather setup to begin with, as you could already set everything from the command line, including the build setting, using CLANG_ENABLE_TEST_COVERAGE=YES xcodebuild -enableCodeCoverage … test

@mgrebenets
Copy link

@AliSoftware got it.

@mgrebenets
Copy link

I have tested this branch again lately, and I noticed that Slather's coverage reports are not the same as llvm-cov report.

Here's what I get directly from llvm-cov report

Filename                    Regions    Miss   Cover Functions  Executed
-----------------------------------------------------------------------
...usr/include/MacTypes.h         0       0    nan%         0      nan%
...sr/include/objc/objc.h         0       0    nan%         0      nan%
...r/include/sys/_types.h         0       0    nan%         0      nan%
...tchTests/AppDelegate.m        17       5  70.59%         7    57.14%
...DetailViewController.m         8       8   0.00%         4     0.00%
...MasterViewController.m        30      25  16.67%        10    40.00%
...MatchTests/Model.swift         1       1   0.00%         1     0.00%
...MatchTests/ObjCModel.m         1       0 100.00%         1   100.00%
...ixAndMatchTests/main.m         3       0 100.00%         1   100.00%
-----------------------------------------------------------------------
TOTAL                            60      39  35.00%        24    41.67%

And this is matching Xcode output one to one (ignore the system files, those don't really matter).
image

But then I ask slather to convert profdata to Cobertura XML

bundle exec slather coverage \
    --input-format profdata \
    --cobertura-xml \
    --ignore "../**/*/Xcode*" \
    --output-directory slather-report \
    --scheme MixAndMatchTests \
    MixAndMatchTests.xcodeproj

And results are like this

MixAndMatchTests/AppDelegate.m: 11 of 33 lines (33.33%)
MixAndMatchTests/DetailViewController.m: 9 of 23 lines (39.13%)
MixAndMatchTests/MasterViewController.m: 22 of 63 lines (34.92%)
MixAndMatchTests/Model.swift: 0 of 3 lines (0.00%)
MixAndMatchTests/ObjCModel.m: 3 of 3 lines (100.00%)
MixAndMatchTests/main.m: 5 of 5 lines (100.00%)
Test Coverage: 38.46%

If you examine them closely, they don't really match to llvm-cov output.

I assume there are some bugs with the logic that converts profile data to other formats.

You can grab Xcode project with few shell scripts to reproduce the issue.

AliSoftware and others added 4 commits September 22, 2015 21:19
* Change "--input-format" option of "slather setup" to "--format", more appropriate name in the context of setup
* Supporting auto for `slather setup --format` and `slather coverage --input-format` which will use profdata if Xcode 7, gcov if earlier
* Checking the validity of the parameters given to `--format` / `--input-format`
Fixes for your profdata support
@AliSoftware
Copy link
Contributor

@marklarr you can now check one of the 3 boxes ("Paths escaped") in your previous comment above 👍

The issue pointed out by @mgrebenets and the one about the HTML report will probably need more work though :-/

@mgrebenets
Copy link

I would stick with using gcov combined with gcda and gcno, but since these files are not generated for Swift code it becomes less of an option. We have more and more Swift added to our projects (

@jenkinsja
Copy link

Taking a bit of a look at the tests, they seem to all be relating to a specific type of failure. That is, the integer value isn't being read correctly. An example of this is:

  •    "short_text": "1"
    
  •    "short_text": "32768"
    

It seems odd to me that we are expecting the binary value 000000000000001, and getting the binary value 1000000000000000. Is it possible that Apple decided to change the format of their numbers in Swift 2?

@cuteiosdev
Copy link
Contributor Author

That was my initial thought about the binary stuff. What slather does though is run the gcov command which generates the files. Looks to me that the issue may be in gcov.

@AliSoftware
Copy link
Contributor

Any news on this, anyone have any idea what's wrong or started to investigate?

@yakimant
Copy link

yakimant commented Oct 2, 2015

I can help with testing if needed.

@cuteiosdev
Copy link
Contributor Author

Sorry about the lack of comment on my behalf, having been moving house and job so not had the time to look at it. @yakimant feel free to take a look, from what I can tell it is a change in the gcov output though am unsure what the expected behaviour is.

@stevepeak
Copy link
Contributor

Hey for whats it worth: Codecov has been using the following Bash script to upload many of swift coverage reports:

profdata=$(find ~/Library/Developer/Xcode/DerivedData -name 'Coverage.profdata' | head -1)
if [ -f "$profdata" ];
then
  _dir=$(dirname "$profdata")
  for _type in app framework xctest
  do
    _file=$(find "$_dir" -name "*.$_type" | head -1)
    if [ "$_file" != "" ];
    then
      _proj=${_file##*/}
      _proj=${_proj%."$_type"}
      xcrun llvm-cov show -instr-profile "$profdata" "$_file/$_proj" > "$_type.coverage.txt" || true
    fi
  done
fi

Extract from codecov/codecov-bash

@mgrebenets
Copy link

@stevepeak. This reports are then uploaded to Codecov right?

What if I want code coverage reports integrated into my build job summary, can I still achieve it with Codecov?

For example Cobertura coverage report plugin for Jenkins gives me functionality like this:

  • display browsable coverage data with ability to navigate down to the lines of code in the source file
  • use coverage data and custom threshold values to mark builds as healthy, stable, failed, and so on
  • pick up difference in code coverage between builds

All of that I get right in the build summary without the need to follow external links.

This is exactly why I'm looking for a way to turn output of llvm-cov show into Cobertura (gcov) format.

When I scanned through the slather source, I think I saw that llvm-cov show is used there too, the current problem is with converting this output to gcov format. Sounds like Codecov might have solved this problem though :)

@stevepeak
Copy link
Contributor

@mgrebenets Yes, this is our uploading tool.

What if I want code coverage reports integrated into my build job summary, can I still achieve it with Codecov?

Codecov is SaaS product that archives these reports. It may be a great solution for you. Here is an example repository: https://codecov.io/gh/wikimedia/wikipedia-ios

This is exactly why I'm looking for a way to turn output of llvm-cov show into Cobertura (gcov) format.

This is not necessary if you are using Codecov. I do not have much insight into llvm-cov show => cobertura.

@mAu888
Copy link

mAu888 commented Oct 16, 2015

@mgrebenets On my project I get different output from llvm-cov and Xcode (using the same build data of course).

# llvm-cov output
Filename                    Regions    Miss   Cover Functions  Executed
-----------------------------------------------------------------------
...ay+JSONEncodable.swift         2       0 100.00%         1   100.00%
...es/BabbelTracker.swift        13       3  76.92%         5   100.00%
...nt+JSONEncodable.swift         6       0 100.00%         1   100.00%
...ent+Serializable.swift        15       7  53.33%         2   100.00%
...er/Sources/Event.swift         2       0 100.00%         2   100.00%
...urces/EventCache.swift        38       5  86.84%         8   100.00%
...s/EventPublisher.swift        27       0 100.00%        10   100.00%
...ces/Serializable.swift         4       1  75.00%         2    50.00%
-----------------------------------------------------------------------
TOTAL                           107      16  85.05%        31    96.77%

And Xcode shows me: Xcode output

Check Event+Serializable.swift for example. The progress in Xcode doesn't seem to match 53.33 %.

@dbarden
Copy link

dbarden commented Oct 16, 2015

@mAu888 From what I understand, that is because the llvm-cov report is reporting the number of regions and Xcode is reporting the number of lines of code.

So, if you have two methods, methodA with 8 lines and methodB with 2 lines and you test only methodB, you'll end up with a region coverage of 50% (that you'll get from llvm-cov report) while from Xcode you'll get 20% of coverage.

@mAu888
Copy link

mAu888 commented Oct 16, 2015

@dbarden I see. Makes sense, as the progress from Xcode matches the output of running slather.

# Slather output for the above tests
Slathering...
****/Sources/Array+JSONEncodable.swift: 3 of 3 lines (100.00%)
****/Sources/****.swift: 31 of 41 lines (75.61%)
****/Sources/Event+JSONEncodable.swift: 14 of 14 lines (100.00%)
****/Sources/Event+Serializable.swift: 30 of 41 lines (73.17%)
****/Sources/Event.swift: 10 of 10 lines (100.00%)
****/Sources/EventCache.swift: 66 of 82 lines (80.49%)
****/Sources/EventPublisher.swift: 71 of 71 lines (100.00%)
****/Sources/Serializable.swift: 7 of 10 lines (70.00%)
Test Coverage: 85.29%
Slathered

@yakimant
Copy link

@mAu888, we are generating the report and calculating the total using (llvm-cov report)

Smth, like this:

find Source -type f \( -name "*.m" -o -name "*.h" \) -print0 | xargs -0 xcrun llvm-cov report -instr-profile "$profdata" "$_file/$_proj" | grep -e "File" -e "TOTAL" -e "Regions.*Miss" | grep -B2 "TOTAL"

@mgrebenets
Copy link

@mAu888, @dbarden.

I get inconsistent results from llvm-cov and slather in a project that mixes both Objective-C and Swift, not just in the main app target, but in the tests too. Here's the link to sample repo: https://github.com/mgrebenets/MixAndMatchTests and the blog post where I go into some details: http://mgrebenets.github.io/mobile%20ci/2015/09/21/code-coverage-for-ios-xcode-7/.

The llv-com and Xcode in my case match each other 100%.

Both slather and Xcode report Model.swift coverage as 0, while I know for sure that all methods are covered, and codecov also reports it as 3 out of 3: https://codecov.io/github/mgrebenets/MixAndMatchTests?branch=master.

@dyundt
Copy link

dyundt commented Oct 26, 2015

Whats the ETA on this or the other profdata PR being merged?

@Legoless
Copy link

@dyundt This still needs multiple fixes. For first we need slather to work with both ObjC and Swift projects.

@HEYGUL
Copy link

HEYGUL commented Nov 4, 2015

@mgrebenets

In your main target you need to set the Enable Testability build option to Yes.
Then in your Model.swift, uncheck the MixAndMatchTestsTests target.
Finally, at the beginning of the ModelTests.swift you have to import the tested module with @testable import MixAndMatchTests.

Run your test and enjoy the 100% code coverage for the Model.swift class 👍

NB: With this, you cannot test swift classes with objective-C, or at least I didn't find how to import the tested module in the test class.

Hope this help

@mgrebenets
Copy link

@GUL-
Thanks, that helped!

To test Swift classes with Objective-C code I would have to import Swift umbrella header (the ProductName-Swift.h one), but for the Swift class interface to be in that header, have to add Swift class to unit tests target, which then leads to no coverage again...

I think I can live with it. If I have a Swift class, why not write tests for it with Swift too.

@bixbarton
Copy link

I keep getting the output below from this PR....

bundle exec slather coverage --input-format profdata --simple-output --ignore "..///Xcode" --output-directory slather-report --scheme ${SCHEME} ${PROJECT}
Slathering...
warning: 146 functions have mismatched data.
/Users/cbutcher/.bundler/ruby/2.0.0/slather-d09ce4f5d592/lib/slather/project.rb:98:in split': invalid byte sequence in UTF-8 (ArgumentError) from /Users/cbutcher/.bundler/ruby/2.0.0/slather-d09ce4f5d592/lib/slather/project.rb:98:inprofdata_coverage_files'
from /Users/cbutcher/.bundler/ruby/2.0.0/slather-d09ce4f5d592/lib/slather/project.rb:75:in coverage_files' from /Users/cbutcher/.bundler/ruby/2.0.0/slather-d09ce4f5d592/lib/slather/coverage_service/simple_output.rb:17:inpost'
from /Users/cbutcher/.bundler/ruby/2.0.0/slather-d09ce4f5d592/bin/slather:80:in post' from /Users/cbutcher/.bundler/ruby/2.0.0/slather-d09ce4f5d592/bin/slather:46:inexecute'
from /Library/Ruby/Gems/2.0.0/gems/clamp-0.6.5/lib/clamp/command.rb:67:in run' from /Library/Ruby/Gems/2.0.0/gems/clamp-0.6.5/lib/clamp/subcommand/execution.rb:11:inexecute'
from /Library/Ruby/Gems/2.0.0/gems/clamp-0.6.5/lib/clamp/command.rb:67:in run' from /Library/Ruby/Gems/2.0.0/gems/clamp-0.6.5/lib/clamp/command.rb:132:inrun'
from /Library/Ruby/Gems/2.0.0/gems/clamp-0.6.5/lib/clamp.rb:6:in Clamp' from /Users/cbutcher/.bundler/ruby/2.0.0/slather-d09ce4f5d592/bin/slather:6:in<top (required)>'
from /Library/Ruby/Gems/2.0.0/bin/slather:23:in load' from /Library/Ruby/Gems/2.0.0/bin/slather:23:in

'

And if I try slather setup, I get...
slather setup
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/pathname.rb:330:in initialize': no implicit conversion of nil into String (TypeError) from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/pathname.rb:330:innew'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/pathname.rb:330:in +' from /Library/Ruby/Gems/2.0.0/gems/xcodeproj-0.26.3/lib/xcodeproj/project.rb:90:inopen'
from /Library/Ruby/Gems/2.0.0/gems/slather-1.8.1/lib/slather/project.rb:27:in open' from /Library/Ruby/Gems/2.0.0/gems/slather-1.8.1/bin/slather:108:inexecute'
from /Library/Ruby/Gems/2.0.0/gems/clamp-0.6.5/lib/clamp/command.rb:67:in run' from /Library/Ruby/Gems/2.0.0/gems/clamp-0.6.5/lib/clamp/subcommand/execution.rb:11:inexecute'
from /Library/Ruby/Gems/2.0.0/gems/clamp-0.6.5/lib/clamp/command.rb:67:in run' from /Library/Ruby/Gems/2.0.0/gems/clamp-0.6.5/lib/clamp/command.rb:132:inrun'
from /Library/Ruby/Gems/2.0.0/gems/clamp-0.6.5/lib/clamp.rb:6:in Clamp' from /Library/Ruby/Gems/2.0.0/gems/slather-1.8.1/bin/slather:6:in<top (required)>'
from /usr/bin/slather:23:in load' from /usr/bin/slather:23:in

'

@micnguyen
Copy link

What seems to be the prominent branch that we are using for this profdata discussion? We should close one and use just one for this topic.

@dhardiman
Copy link
Contributor

For anyone interested, I've rebased this branch and attempted to fix up the html output for prof data over at https://github.com/dhardiman/slather/tree/feature-profdata. I've not raised a pull request as it doesn't attempt to fix the gcov integration with Xcode 7, and is probably not well enough tested (my ruby skills are weak). From what I can tell, they've changed the endianness (or something) of the gcda output, which seems to break gcov itself. Which makes me think supporting gcov with Xcode 7 is probably not worth worrying about any more. So my branch serves my purposes, and if it helps anyone else, that's great.

@viteinfinite
Copy link
Contributor

@dhardiman I agree with you. One of the next versions of slather should stop supporting gcov.

@viteinfinite
Copy link
Contributor

I've continued working on the integration of profdata into slather. More commits are available in the PR #92.

@jkrumow
Copy link
Collaborator

jkrumow commented Dec 14, 2015

@viteinfinite Ok. I will close this PR then in favor of #92

@jkrumow jkrumow closed this Dec 14, 2015
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.

None yet