slather 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +30 -0
 - data/README.md +1 -1
 - data/lib/slather/command/coverage_command.rb +10 -4
 - data/lib/slather/profdata_coverage_file.rb +32 -5
 - data/lib/slather/project.rb +133 -45
 - data/lib/slather/version.rb +1 -1
 - data/spec/fixtures/fixtures.xcodeproj/project.pbxproj +106 -4
 - data/spec/fixtures/fixtures.xcodeproj/xcshareddata/xcschemes/fixturesTwo.xcscheme +80 -0
 - data/spec/fixtures/fixtures.xcworkspace/xcshareddata/xcschemes/fixturesTestsWorkspace.xcscheme +99 -0
 - data/spec/fixtures/fixturesTests/fixturesTests.m +8 -0
 - data/spec/fixtures/fixturesTwo/fixturesTwo.h +15 -0
 - data/spec/fixtures/fixturesTwo/fixturesTwo.m +21 -0
 - data/spec/fixtures/fixtures_html/Branches.m.html +4 -4
 - data/spec/fixtures/fixtures_html/BranchesTests.m.html +4 -4
 - data/spec/fixtures/fixtures_html/fixtures.m.html +4 -4
 - data/spec/fixtures/fixtures_html/fixturesTests.m.html +78 -38
 - data/spec/fixtures/fixtures_html/index.html +8 -8
 - data/spec/fixtures/fixtures_html/peekaviewTests.m.html +4 -4
 - data/spec/fixtures/gutter.json +1 -1
 - data/spec/slather/coverage_service/simple_output_spec.rb +9 -6
 - data/spec/slather/profdata_coverage_spec.rb +23 -5
 - data/spec/slather/project_spec.rb +53 -7
 - data/spec/spec_helper.rb +1 -1
 - metadata +10 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: eeb8cebaec1537b632d62c19245da2c00af5730e
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 0b7ca2c1e79d283e9899b969012f943d611ed0be
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 81c1fd17cba7d152635a93aaf546bd1cfa9fc1040dd92da8b690486a913178e254ce28ae753200d2bc171cad0d586f2304a4b21d6683e364cc321bb77c88f0e0
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 3f0a80e2845b883f7ed3867a68f5717da9d85ea62bfa07442195017b735e21e667a244cf90924cb99a43193d8256d2293f48bc6f5aa9cef6570d856537c3befd
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -3,6 +3,36 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            ## master
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         | 
| 
      
 6 
     | 
    
         
            +
            ## v2.2.0
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            * Fix nil crash in `project.rb` derived_data_path  
         
     | 
| 
      
 9 
     | 
    
         
            +
              [bootstraponline](https://github.com/bootstraponline)
         
     | 
| 
      
 10 
     | 
    
         
            +
              [#203](https://github.com/SlatherOrg/slather/pull/203)
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            * Fix for correct line number for lines that are hit thousands or millions of time in llvm-cov.  
         
     | 
| 
      
 13 
     | 
    
         
            +
              [Mihai Parv](https://github.com/mihaiparv)
         
     | 
| 
      
 14 
     | 
    
         
            +
              [#202](https://github.com/SlatherOrg/slather/pull/202), [#196](https://github.com/SlatherOrg/slather/issues/196)
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            * Generate coverate for multiple binaries by passing multiple `--binary-basename` or `--binary-file` arguments, or by using an array in `.slather.yml`  
         
     | 
| 
      
 17 
     | 
    
         
            +
              [Kent Sutherland](https://github.com/ksuther)
         
     | 
| 
      
 18 
     | 
    
         
            +
              [#188](https://github.com/SlatherOrg/slather/pull/188)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            * Support for specifying source file patterns using the `--source-files` option or the source_files key in `.slather.yml`  
         
     | 
| 
      
 21 
     | 
    
         
            +
              [Matej Bukovinski](https://github.com/matej)
         
     | 
| 
      
 22 
     | 
    
         
            +
              [#201](https://github.com/SlatherOrg/slather/pull/201)
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            * Improve getting schemes. Looks for user scheme in case no shared scheme is found.  
         
     | 
| 
      
 25 
     | 
    
         
            +
              [Matyas Hlavacek](https://github.com/matyashlavacek)
         
     | 
| 
      
 26 
     | 
    
         
            +
              [#182](https://github.com/SlatherOrg/slather/issues/182)
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            * Search Xcode workspaces for schemes. Workspaces are checked if no matching scheme is found in the project.  
         
     | 
| 
      
 29 
     | 
    
         
            +
              [Kent Sutherland](https://github.com/ksuther)
         
     | 
| 
      
 30 
     | 
    
         
            +
              [#193](https://github.com/SlatherOrg/slather/pull/193), [#191](https://github.com/SlatherOrg/slather/issues/191)
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            * Fix for hit counts in thousands or millions being output as floats intead of integers  
         
     | 
| 
      
 33 
     | 
    
         
            +
              [Carl Hill-Popper](https://github.com/chillpop)
         
     | 
| 
      
 34 
     | 
    
         
            +
              [#190](https://github.com/SlatherOrg/slather/pull/190)
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
       6 
36 
     | 
    
         
             
            ## v2.1.0
         
     | 
| 
       7 
37 
     | 
    
         | 
| 
       8 
38 
     | 
    
         
             
            * Support for Xcode workspaces. Define `workspace` configuration in `.slather.yml` or use the `--workspace` argument if you build in a workspace.
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -55,7 +55,7 @@ If you use a workspace in Xcode you need to specify it: 
     | 
|
| 
       55 
55 
     | 
    
         
             
            $ slather coverage -s --scheme YourXcodeSchemeName --workspace path/to/workspace.xcworkspace path/to/project.xcodeproj
         
     | 
| 
       56 
56 
     | 
    
         
             
            ```
         
     | 
| 
       57 
57 
     | 
    
         | 
| 
       58 
     | 
    
         
            -
            ### Setup for  
     | 
| 
      
 58 
     | 
    
         
            +
            ### Setup for Xcode 5 and 6
         
     | 
| 
       59 
59 
     | 
    
         | 
| 
       60 
60 
     | 
    
         
             
            Run this command to enable the `Generate Test Coverage` and `Instrument Program Flow` flags for your project:
         
     | 
| 
       61 
61 
     | 
    
         | 
| 
         @@ -24,8 +24,9 @@ class CoverageCommand < Clamp::Command 
     | 
|
| 
       24 
24 
     | 
    
         
             
              option ["--input-format"], "INPUT_FORMAT", "Input format (gcov, profdata)"
         
     | 
| 
       25 
25 
     | 
    
         
             
              option ["--scheme"], "SCHEME", "The scheme for which the coverage was generated"
         
     | 
| 
       26 
26 
     | 
    
         
             
              option ["--workspace"], "WORKSPACE", "The workspace that the project was built in"
         
     | 
| 
       27 
     | 
    
         
            -
              option ["--binary-file"], "BINARY_FILE", "The binary file against the which the coverage will be run"
         
     | 
| 
       28 
     | 
    
         
            -
              option ["--binary-basename"], "BINARY_BASENAME", "Basename of the file against which the coverage will be run"
         
     | 
| 
      
 27 
     | 
    
         
            +
              option ["--binary-file"], "BINARY_FILE", "The binary file against the which the coverage will be run", :multivalued => true
         
     | 
| 
      
 28 
     | 
    
         
            +
              option ["--binary-basename"], "BINARY_BASENAME", "Basename of the file against which the coverage will be run", :multivalued => true
         
     | 
| 
      
 29 
     | 
    
         
            +
              option ["--source-files"], "SOURCE_FILES", "A Dir.glob compatible pattern used to limit the lookup to specific source files. Ignored in gcov mode.", :multivalued => true
         
     | 
| 
       29 
30 
     | 
    
         | 
| 
       30 
31 
     | 
    
         
             
              def execute
         
     | 
| 
       31 
32 
     | 
    
         
             
                puts "Slathering..."
         
     | 
| 
         @@ -42,6 +43,7 @@ class CoverageCommand < Clamp::Command 
     | 
|
| 
       42 
43 
     | 
    
         
             
                setup_workspace
         
     | 
| 
       43 
44 
     | 
    
         
             
                setup_binary_file
         
     | 
| 
       44 
45 
     | 
    
         
             
                setup_binary_basename
         
     | 
| 
      
 46 
     | 
    
         
            +
                setup_source_files
         
     | 
| 
       45 
47 
     | 
    
         | 
| 
       46 
48 
     | 
    
         
             
                project.configure
         
     | 
| 
       47 
49 
     | 
    
         | 
| 
         @@ -127,10 +129,14 @@ class CoverageCommand < Clamp::Command 
     | 
|
| 
       127 
129 
     | 
    
         
             
              end
         
     | 
| 
       128 
130 
     | 
    
         | 
| 
       129 
131 
     | 
    
         
             
              def setup_binary_file
         
     | 
| 
       130 
     | 
    
         
            -
                project.binary_file =  
     | 
| 
      
 132 
     | 
    
         
            +
                project.binary_file = binary_file_list if !binary_file_list.empty?
         
     | 
| 
       131 
133 
     | 
    
         
             
              end
         
     | 
| 
       132 
134 
     | 
    
         | 
| 
       133 
135 
     | 
    
         
             
              def setup_binary_basename
         
     | 
| 
       134 
     | 
    
         
            -
                project.binary_basename =  
     | 
| 
      
 136 
     | 
    
         
            +
                project.binary_basename = binary_basename_list if !binary_basename_list.empty?
         
     | 
| 
      
 137 
     | 
    
         
            +
              end
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
              def setup_source_files
         
     | 
| 
      
 140 
     | 
    
         
            +
                project.source_files = source_files_list if !source_files_list.empty?
         
     | 
| 
       135 
141 
     | 
    
         
             
              end
         
     | 
| 
       136 
142 
     | 
    
         
             
            end
         
     | 
| 
         @@ -16,13 +16,18 @@ module Slather 
     | 
|
| 
       16 
16 
     | 
    
         
             
                end
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
       18 
18 
     | 
    
         
             
                def create_line_data
         
     | 
| 
       19 
     | 
    
         
            -
                  all_lines =  
     | 
| 
      
 19 
     | 
    
         
            +
                  all_lines = source_code_lines
         
     | 
| 
       20 
20 
     | 
    
         
             
                  line_data = Hash.new
         
     | 
| 
       21 
21 
     | 
    
         
             
                  all_lines.each { |line| line_data[line_number_in_line(line)] = line }
         
     | 
| 
       22 
22 
     | 
    
         
             
                  self.line_data = line_data
         
     | 
| 
       23 
23 
     | 
    
         
             
                end
         
     | 
| 
       24 
24 
     | 
    
         
             
                private :create_line_data
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
      
 26 
     | 
    
         
            +
                def path_on_first_line?
         
     | 
| 
      
 27 
     | 
    
         
            +
                  path = self.source.split("\n")[0].sub ":", ""
         
     | 
| 
      
 28 
     | 
    
         
            +
                  !path.include?("1|//")
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
       26 
31 
     | 
    
         
             
                def source_file_pathname
         
     | 
| 
       27 
32 
     | 
    
         
             
                  @source_file_pathname ||= begin
         
     | 
| 
       28 
33 
     | 
    
         
             
                    path = self.source.split("\n")[0].sub ":", ""
         
     | 
| 
         @@ -30,17 +35,25 @@ module Slather 
     | 
|
| 
       30 
35 
     | 
    
         
             
                  end
         
     | 
| 
       31 
36 
     | 
    
         
             
                end
         
     | 
| 
       32 
37 
     | 
    
         | 
| 
      
 38 
     | 
    
         
            +
                def source_file_pathname= (source_file_pathname)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @source_file_pathname = source_file_pathname
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
       33 
42 
     | 
    
         
             
                def source_file
         
     | 
| 
       34 
43 
     | 
    
         
             
                  File.new(source_file_pathname)
         
     | 
| 
       35 
44 
     | 
    
         
             
                end
         
     | 
| 
       36 
45 
     | 
    
         | 
| 
      
 46 
     | 
    
         
            +
                def source_code_lines
         
     | 
| 
      
 47 
     | 
    
         
            +
                  self.source.split("\n")[(path_on_first_line? ? 1 : 0)..-1]
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
       37 
50 
     | 
    
         
             
                def source_data
         
     | 
| 
       38 
51 
     | 
    
         
             
                  all_lines.join("\n")
         
     | 
| 
       39 
52 
     | 
    
         
             
                end
         
     | 
| 
       40 
53 
     | 
    
         | 
| 
       41 
54 
     | 
    
         
             
                def all_lines
         
     | 
| 
       42 
55 
     | 
    
         
             
                  if @all_lines == nil
         
     | 
| 
       43 
     | 
    
         
            -
                    @all_lines =  
     | 
| 
      
 56 
     | 
    
         
            +
                    @all_lines = source_code_lines
         
     | 
| 
       44 
57 
     | 
    
         
             
                  end
         
     | 
| 
       45 
58 
     | 
    
         
             
                  @all_lines
         
     | 
| 
       46 
59 
     | 
    
         
             
                end
         
     | 
| 
         @@ -61,12 +74,23 @@ module Slather 
     | 
|
| 
       61 
74 
     | 
    
         
             
                      when /[0-9]+/
         
     | 
| 
       62 
75 
     | 
    
         
             
                        return match.to_i
         
     | 
| 
       63 
76 
     | 
    
         
             
                    end
         
     | 
| 
      
 77 
     | 
    
         
            +
                  else
         
     | 
| 
      
 78 
     | 
    
         
            +
                    # llvm-cov outputs hit counts as 25.3k or 3.8M, so check this pattern as well
         
     | 
| 
      
 79 
     | 
    
         
            +
                    did_match = line =~ /^(\s*)(\d+\.\d+)(k|M)\|(\s*)(\d+)\|/
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                    if did_match
         
     | 
| 
      
 82 
     | 
    
         
            +
                      match = $5.strip
         
     | 
| 
      
 83 
     | 
    
         
            +
                      case match
         
     | 
| 
      
 84 
     | 
    
         
            +
                        when /[0-9]+/
         
     | 
| 
      
 85 
     | 
    
         
            +
                          return match.to_i
         
     | 
| 
      
 86 
     | 
    
         
            +
                      end
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end
         
     | 
| 
       64 
88 
     | 
    
         
             
                  end
         
     | 
| 
       65 
89 
     | 
    
         
             
                  0
         
     | 
| 
       66 
90 
     | 
    
         
             
                end
         
     | 
| 
       67 
91 
     | 
    
         | 
| 
       68 
92 
     | 
    
         
             
                def line_coverage_data
         
     | 
| 
       69 
     | 
    
         
            -
                   
     | 
| 
      
 93 
     | 
    
         
            +
                  source_code_lines.map do |line|
         
     | 
| 
       70 
94 
     | 
    
         
             
                    coverage_for_line(line)
         
     | 
| 
       71 
95 
     | 
    
         
             
                  end
         
     | 
| 
       72 
96 
     | 
    
         
             
                end
         
     | 
| 
         @@ -83,7 +107,7 @@ module Slather 
     | 
|
| 
       83 
107 
     | 
    
         
             
                      count = $2.strip
         
     | 
| 
       84 
108 
     | 
    
         
             
                      units = $3 == 'k' ? 1000 : 1000000
         
     | 
| 
       85 
109 
     | 
    
         | 
| 
       86 
     | 
    
         
            -
                      count.to_f * units
         
     | 
| 
      
 110 
     | 
    
         
            +
                      (count.to_f * units).to_i
         
     | 
| 
       87 
111 
     | 
    
         
             
                    else
         
     | 
| 
       88 
112 
     | 
    
         
             
                      return nil
         
     | 
| 
       89 
113 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -133,7 +157,10 @@ module Slather 
     | 
|
| 
       133 
157 
     | 
    
         
             
                def platform_ignore_list
         
     | 
| 
       134 
158 
     | 
    
         
             
                  ["MacOSX.platform/Developer/Library/Frameworks/XCTest.framework/Headers/XCTestAssertionsImpl.h",
         
     | 
| 
       135 
159 
     | 
    
         
             
                    "MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/objc/objc.h",
         
     | 
| 
       136 
     | 
    
         
            -
                    "MacOSX.platform/Developer/Library/Frameworks/XCTest.framework/Headers/XCTestAssertions.h" 
     | 
| 
      
 160 
     | 
    
         
            +
                    "MacOSX.platform/Developer/Library/Frameworks/XCTest.framework/Headers/XCTestAssertions.h",
         
     | 
| 
      
 161 
     | 
    
         
            +
                    # This indicates a llvm-cov coverage warning (occurs if a passed in source file 
         
     | 
| 
      
 162 
     | 
    
         
            +
                    # is not covered or with ccache in some cases).
         
     | 
| 
      
 163 
     | 
    
         
            +
                    "isn't covered."]
         
     | 
| 
       137 
164 
     | 
    
         
             
                end
         
     | 
| 
       138 
165 
     | 
    
         
             
                private :platform_ignore_list
         
     | 
| 
       139 
166 
     | 
    
         
             
              end
         
     | 
    
        data/lib/slather/project.rb
    CHANGED
    
    | 
         @@ -51,7 +51,7 @@ module Slather 
     | 
|
| 
       51 
51 
     | 
    
         
             
              class Project < Xcodeproj::Project
         
     | 
| 
       52 
52 
     | 
    
         | 
| 
       53 
53 
     | 
    
         
             
                attr_accessor :build_directory, :ignore_list, :ci_service, :coverage_service, :coverage_access_token, :source_directory,
         
     | 
| 
       54 
     | 
    
         
            -
                  :output_directory, :xcodeproj, :show_html, :verbose_mode, :input_format, :scheme, :workspace, :binary_file, :binary_basename
         
     | 
| 
      
 54 
     | 
    
         
            +
                  :output_directory, :xcodeproj, :show_html, :verbose_mode, :input_format, :scheme, :workspace, :binary_file, :binary_basename, :source_files
         
     | 
| 
       55 
55 
     | 
    
         | 
| 
       56 
56 
     | 
    
         
             
                alias_method :setup_for_coverage, :slather_setup_for_coverage
         
     | 
| 
       57 
57 
     | 
    
         | 
| 
         @@ -82,10 +82,13 @@ module Slather 
     | 
|
| 
       82 
82 
     | 
    
         
             
                    buildAction = nil
         
     | 
| 
       83 
83 
     | 
    
         
             
                  end
         
     | 
| 
       84 
84 
     | 
    
         | 
| 
       85 
     | 
    
         
            -
                   
     | 
| 
      
 85 
     | 
    
         
            +
                  # redirect stderr to avoid xcodebuild errors being printed.
         
     | 
| 
      
 86 
     | 
    
         
            +
                  build_settings = `xcodebuild #{projectOrWorkspaceArgument} #{schemeArgument} -showBuildSettings #{buildAction} 2>&1`
         
     | 
| 
       86 
87 
     | 
    
         | 
| 
       87 
88 
     | 
    
         
             
                  if build_settings
         
     | 
| 
       88 
     | 
    
         
            -
                    derived_data_path = build_settings.match(/ OBJROOT = (.+)/) 
     | 
| 
      
 89 
     | 
    
         
            +
                    derived_data_path = build_settings.match(/ OBJROOT = (.+)/)
         
     | 
| 
      
 90 
     | 
    
         
            +
                    # when match fails derived_data_path is nil
         
     | 
| 
      
 91 
     | 
    
         
            +
                    derived_data_path = derived_data_path[1] if derived_data_path
         
     | 
| 
       89 
92 
     | 
    
         
             
                  end
         
     | 
| 
       90 
93 
     | 
    
         | 
| 
       91 
94 
     | 
    
         
             
                  if derived_data_path == nil
         
     | 
| 
         @@ -121,12 +124,28 @@ module Slather 
     | 
|
| 
       121 
124 
     | 
    
         
             
                private :gcov_coverage_files
         
     | 
| 
       122 
125 
     | 
    
         | 
| 
       123 
126 
     | 
    
         
             
                def profdata_coverage_files
         
     | 
| 
       124 
     | 
    
         
            -
                   
     | 
| 
      
 127 
     | 
    
         
            +
                  coverage_files = []
         
     | 
| 
      
 128 
     | 
    
         
            +
                  source_files = find_source_files || []
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                  if self.binary_file
         
     | 
| 
      
 131 
     | 
    
         
            +
                    self.binary_file.each do |binary_path|
         
     | 
| 
      
 132 
     | 
    
         
            +
                      files = profdata_llvm_cov_output(binary_path, source_files).split("\n\n")
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                      coverage_files.concat(files.map do |source|
         
     | 
| 
      
 135 
     | 
    
         
            +
                        coverage_file = coverage_file_class.new(self, source)
         
     | 
| 
      
 136 
     | 
    
         
            +
                        # If a single source file is used, the resulting output does not contain the file name.
         
     | 
| 
      
 137 
     | 
    
         
            +
                        coverage_file.source_file_pathname = source_files.first if source_files.count == 1
         
     | 
| 
      
 138 
     | 
    
         
            +
                        !coverage_file.ignored? ? coverage_file : nil
         
     | 
| 
      
 139 
     | 
    
         
            +
                      end.compact)
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                      if !source_files.empty?
         
     | 
| 
      
 142 
     | 
    
         
            +
                        coverage_file_paths = coverage_files.map { |file| file.source_file_pathname }.to_set
         
     | 
| 
      
 143 
     | 
    
         
            +
                        source_files.select! { |path| !coverage_file_paths.include?(path) }
         
     | 
| 
      
 144 
     | 
    
         
            +
                      end
         
     | 
| 
      
 145 
     | 
    
         
            +
                    end
         
     | 
| 
      
 146 
     | 
    
         
            +
                  end
         
     | 
| 
       125 
147 
     | 
    
         | 
| 
       126 
     | 
    
         
            -
                   
     | 
| 
       127 
     | 
    
         
            -
                    coverage_file = coverage_file_class.new(self, source)
         
     | 
| 
       128 
     | 
    
         
            -
                    !coverage_file.ignored? ? coverage_file : nil
         
     | 
| 
       129 
     | 
    
         
            -
                  end.compact
         
     | 
| 
      
 148 
     | 
    
         
            +
                  coverage_files
         
     | 
| 
       130 
149 
     | 
    
         
             
                end
         
     | 
| 
       131 
150 
     | 
    
         
             
                private :profdata_coverage_files
         
     | 
| 
       132 
151 
     | 
    
         | 
| 
         @@ -173,23 +192,23 @@ module Slather 
     | 
|
| 
       173 
192 
     | 
    
         
             
                end
         
     | 
| 
       174 
193 
     | 
    
         
             
                private :profdata_file
         
     | 
| 
       175 
194 
     | 
    
         | 
| 
       176 
     | 
    
         
            -
                def unsafe_profdata_llvm_cov_output
         
     | 
| 
      
 195 
     | 
    
         
            +
                def unsafe_profdata_llvm_cov_output(binary_path, source_files)
         
     | 
| 
       177 
196 
     | 
    
         
             
                  profdata_file_arg = profdata_file
         
     | 
| 
       178 
197 
     | 
    
         
             
                  if profdata_file_arg == nil
         
     | 
| 
       179 
198 
     | 
    
         
             
                    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."
         
     | 
| 
       180 
199 
     | 
    
         
             
                  end
         
     | 
| 
       181 
200 
     | 
    
         | 
| 
       182 
     | 
    
         
            -
                  if  
     | 
| 
      
 201 
     | 
    
         
            +
                  if binary_path == nil
         
     | 
| 
       183 
202 
     | 
    
         
             
                    raise StandardError, "No binary file found."
         
     | 
| 
       184 
203 
     | 
    
         
             
                  end
         
     | 
| 
       185 
204 
     | 
    
         | 
| 
       186 
     | 
    
         
            -
                  llvm_cov_args = %W(show -instr-profile #{profdata_file_arg} #{ 
     | 
| 
       187 
     | 
    
         
            -
                  `xcrun llvm-cov #{llvm_cov_args.shelljoin}`
         
     | 
| 
      
 205 
     | 
    
         
            +
                  llvm_cov_args = %W(show -instr-profile #{profdata_file_arg} #{binary_path})
         
     | 
| 
      
 206 
     | 
    
         
            +
                  `xcrun llvm-cov #{llvm_cov_args.shelljoin} #{source_files.shelljoin}`
         
     | 
| 
       188 
207 
     | 
    
         
             
                end
         
     | 
| 
       189 
208 
     | 
    
         
             
                private :unsafe_profdata_llvm_cov_output
         
     | 
| 
       190 
209 
     | 
    
         | 
| 
       191 
     | 
    
         
            -
                def profdata_llvm_cov_output
         
     | 
| 
       192 
     | 
    
         
            -
                  unsafe_profdata_llvm_cov_output.encode!('UTF-8', 'binary', :invalid => :replace, undef: :replace)
         
     | 
| 
      
 210 
     | 
    
         
            +
                def profdata_llvm_cov_output(binary_path, source_files)
         
     | 
| 
      
 211 
     | 
    
         
            +
                  unsafe_profdata_llvm_cov_output(binary_path, source_files).encode!('UTF-8', 'binary', :invalid => :replace, undef: :replace)
         
     | 
| 
       193 
212 
     | 
    
         
             
                end
         
     | 
| 
       194 
213 
     | 
    
         
             
                private :profdata_llvm_cov_output
         
     | 
| 
       195 
214 
     | 
    
         | 
| 
         @@ -228,7 +247,11 @@ module Slather 
     | 
|
| 
       228 
247 
     | 
    
         | 
| 
       229 
248 
     | 
    
         
             
                  if self.verbose_mode
         
     | 
| 
       230 
249 
     | 
    
         
             
                    puts "\nProcessing coverage file: #{profdata_file}"
         
     | 
| 
       231 
     | 
    
         
            -
                    puts "Against binary  
     | 
| 
      
 250 
     | 
    
         
            +
                    puts "Against binary files:"
         
     | 
| 
      
 251 
     | 
    
         
            +
                    self.binary_file.each do |binary_file|
         
     | 
| 
      
 252 
     | 
    
         
            +
                      puts "\t#{binary_file}"
         
     | 
| 
      
 253 
     | 
    
         
            +
                    end
         
     | 
| 
      
 254 
     | 
    
         
            +
                    puts "\n"
         
     | 
| 
       232 
255 
     | 
    
         
             
                  end
         
     | 
| 
       233 
256 
     | 
    
         
             
                end
         
     | 
| 
       234 
257 
     | 
    
         | 
| 
         @@ -311,24 +334,46 @@ module Slather 
     | 
|
| 
       311 
334 
     | 
    
         | 
| 
       312 
335 
     | 
    
         
             
                def configure_binary_file
         
     | 
| 
       313 
336 
     | 
    
         
             
                  if self.input_format == "profdata"
         
     | 
| 
       314 
     | 
    
         
            -
                    self.binary_file  
     | 
| 
      
 337 
     | 
    
         
            +
                    self.binary_file = load_option_array("binary_file") || find_binary_files
         
     | 
| 
       315 
338 
     | 
    
         
             
                  end
         
     | 
| 
       316 
339 
     | 
    
         
             
                end
         
     | 
| 
       317 
340 
     | 
    
         | 
| 
       318 
341 
     | 
    
         
             
                def find_binary_file_in_bundle(bundle_file)
         
     | 
| 
       319 
     | 
    
         
            -
                   
     | 
| 
       320 
     | 
    
         
            -
             
     | 
| 
      
 342 
     | 
    
         
            +
                  if File.directory? bundle_file
         
     | 
| 
      
 343 
     | 
    
         
            +
                    bundle_file_noext = File.basename(bundle_file, File.extname(bundle_file))
         
     | 
| 
      
 344 
     | 
    
         
            +
                    Dir["#{bundle_file}/**/#{bundle_file_noext}"].first
         
     | 
| 
      
 345 
     | 
    
         
            +
                  else
         
     | 
| 
      
 346 
     | 
    
         
            +
                    bundle_file
         
     | 
| 
      
 347 
     | 
    
         
            +
                  end
         
     | 
| 
       321 
348 
     | 
    
         
             
                end
         
     | 
| 
       322 
349 
     | 
    
         | 
| 
       323 
     | 
    
         
            -
                def  
     | 
| 
       324 
     | 
    
         
            -
                  binary_basename =  
     | 
| 
      
 350 
     | 
    
         
            +
                def find_binary_files
         
     | 
| 
      
 351 
     | 
    
         
            +
                  binary_basename = load_option_array("binary_basename")
         
     | 
| 
      
 352 
     | 
    
         
            +
                  found_binaries = []
         
     | 
| 
       325 
353 
     | 
    
         | 
| 
       326 
354 
     | 
    
         
             
                  # Get scheme info out of the xcodeproj
         
     | 
| 
       327 
355 
     | 
    
         
             
                  if self.scheme
         
     | 
| 
       328 
356 
     | 
    
         
             
                    schemes_path = Xcodeproj::XCScheme.shared_data_dir(self.path)
         
     | 
| 
       329 
357 
     | 
    
         
             
                    xcscheme_path = "#{schemes_path + self.scheme}.xcscheme"
         
     | 
| 
       330 
358 
     | 
    
         | 
| 
       331 
     | 
    
         
            -
                     
     | 
| 
      
 359 
     | 
    
         
            +
                    # Try to look inside 'xcuserdata' if the scheme is not found in 'xcshareddata'
         
     | 
| 
      
 360 
     | 
    
         
            +
                    if !File.file?(xcscheme_path)
         
     | 
| 
      
 361 
     | 
    
         
            +
                      schemes_path = Xcodeproj::XCScheme.user_data_dir(self.path)
         
     | 
| 
      
 362 
     | 
    
         
            +
                      xcscheme_path = "#{schemes_path + self.scheme}.xcscheme"
         
     | 
| 
      
 363 
     | 
    
         
            +
                    end
         
     | 
| 
      
 364 
     | 
    
         
            +
             
     | 
| 
      
 365 
     | 
    
         
            +
                    if self.workspace and !File.file?(xcscheme_path)
         
     | 
| 
      
 366 
     | 
    
         
            +
                      # No scheme was found in the xcodeproj, check the workspace
         
     | 
| 
      
 367 
     | 
    
         
            +
                      schemes_path = Xcodeproj::XCScheme.shared_data_dir(self.workspace)
         
     | 
| 
      
 368 
     | 
    
         
            +
                      xcscheme_path = "#{schemes_path + self.scheme}.xcscheme"
         
     | 
| 
      
 369 
     | 
    
         
            +
             
     | 
| 
      
 370 
     | 
    
         
            +
                      if !File.file?(xcscheme_path)
         
     | 
| 
      
 371 
     | 
    
         
            +
                        schemes_path = Xcodeproj::XCScheme.user_data_dir(self.workspace)
         
     | 
| 
      
 372 
     | 
    
         
            +
                        xcscheme_path = "#{schemes_path + self.scheme}.xcscheme"
         
     | 
| 
      
 373 
     | 
    
         
            +
                      end
         
     | 
| 
      
 374 
     | 
    
         
            +
                    end
         
     | 
| 
      
 375 
     | 
    
         
            +
             
     | 
| 
      
 376 
     | 
    
         
            +
                    raise StandardError, "No scheme named '#{self.scheme}' found in #{self.path}" unless File.exists? xcscheme_path
         
     | 
| 
       332 
377 
     | 
    
         | 
| 
       333 
378 
     | 
    
         
             
                    xcscheme = Xcodeproj::XCScheme.new(xcscheme_path)
         
     | 
| 
       334 
379 
     | 
    
         | 
| 
         @@ -345,19 +390,26 @@ module Slather 
     | 
|
| 
       345 
390 
     | 
    
         | 
| 
       346 
391 
     | 
    
         
             
                    configuration = xcscheme.test_action.build_configuration
         
     | 
| 
       347 
392 
     | 
    
         | 
| 
       348 
     | 
    
         
            -
                     
     | 
| 
       349 
     | 
    
         
            -
             
     | 
| 
       350 
     | 
    
         
            -
             
     | 
| 
       351 
     | 
    
         
            -
                       
     | 
| 
       352 
     | 
    
         
            -
             
     | 
| 
       353 
     | 
    
         
            -
             
     | 
| 
       354 
     | 
    
         
            -
             
     | 
| 
       355 
     | 
    
         
            -
             
     | 
| 
      
 393 
     | 
    
         
            +
                    search_list = binary_basename || [buildable_name]
         
     | 
| 
      
 394 
     | 
    
         
            +
             
     | 
| 
      
 395 
     | 
    
         
            +
                    search_list.each do |search_for|
         
     | 
| 
      
 396 
     | 
    
         
            +
                      found_product = Dir["#{profdata_coverage_dir}/Products/#{configuration}*/#{search_for}*"].sort { |x, y|
         
     | 
| 
      
 397 
     | 
    
         
            +
                        # Sort the matches without the file extension to ensure better matches when there are multiple candidates
         
     | 
| 
      
 398 
     | 
    
         
            +
                        # For example, if the binary_basename is Test then we want Test.app to be matched before Test Helper.app
         
     | 
| 
      
 399 
     | 
    
         
            +
                        File.basename(x, File.extname(x)) <=> File.basename(y, File.extname(y))
         
     | 
| 
      
 400 
     | 
    
         
            +
                      }.reject { |path|
         
     | 
| 
      
 401 
     | 
    
         
            +
                        path.end_with? ".dSYM"
         
     | 
| 
      
 402 
     | 
    
         
            +
                      }.first
         
     | 
| 
      
 403 
     | 
    
         
            +
             
     | 
| 
      
 404 
     | 
    
         
            +
                      if found_product and File.directory? found_product
         
     | 
| 
      
 405 
     | 
    
         
            +
                        found_binary = find_binary_file_in_bundle(found_product)
         
     | 
| 
      
 406 
     | 
    
         
            +
                      else
         
     | 
| 
      
 407 
     | 
    
         
            +
                        found_binary = found_product
         
     | 
| 
      
 408 
     | 
    
         
            +
                      end
         
     | 
| 
       356 
409 
     | 
    
         | 
| 
       357 
     | 
    
         
            -
             
     | 
| 
       358 
     | 
    
         
            -
             
     | 
| 
       359 
     | 
    
         
            -
             
     | 
| 
       360 
     | 
    
         
            -
                      found_binary = found_product
         
     | 
| 
      
 410 
     | 
    
         
            +
                      if found_binary
         
     | 
| 
      
 411 
     | 
    
         
            +
                        found_binaries.push(found_binary)
         
     | 
| 
      
 412 
     | 
    
         
            +
                      end
         
     | 
| 
       361 
413 
     | 
    
         
             
                    end
         
     | 
| 
       362 
414 
     | 
    
         
             
                  else
         
     | 
| 
       363 
415 
     | 
    
         
             
                    xctest_bundle = Dir["#{profdata_coverage_dir}/**/*.xctest"].reject { |bundle|
         
     | 
| 
         @@ -366,26 +418,62 @@ module Slather 
     | 
|
| 
       366 
418 
     | 
    
         
             
                    }.first
         
     | 
| 
       367 
419 
     | 
    
         | 
| 
       368 
420 
     | 
    
         
             
                    # Find the matching binary file
         
     | 
| 
       369 
     | 
    
         
            -
                     
     | 
| 
       370 
     | 
    
         
            -
             
     | 
| 
       371 
     | 
    
         
            -
                     
     | 
| 
       372 
     | 
    
         
            -
             
     | 
| 
       373 
     | 
    
         
            -
             
     | 
| 
      
 421 
     | 
    
         
            +
                    search_list = binary_basename || ['*']
         
     | 
| 
      
 422 
     | 
    
         
            +
             
     | 
| 
      
 423 
     | 
    
         
            +
                    search_list.each do |search_for|
         
     | 
| 
      
 424 
     | 
    
         
            +
                      xctest_bundle_file_directory = Pathname.new(xctest_bundle).dirname
         
     | 
| 
      
 425 
     | 
    
         
            +
                      app_bundle = Dir["#{xctest_bundle_file_directory}/#{search_for}.app"].first
         
     | 
| 
      
 426 
     | 
    
         
            +
                      matched_xctest_bundle = Dir["#{xctest_bundle_file_directory}/#{search_for}.xctest"].first
         
     | 
| 
      
 427 
     | 
    
         
            +
                      dynamic_lib_bundle = Dir["#{xctest_bundle_file_directory}/#{search_for}.{framework,dylib}"].first
         
     | 
| 
       374 
428 
     | 
    
         | 
| 
       375 
     | 
    
         
            -
             
     | 
| 
      
 429 
     | 
    
         
            +
                      if app_bundle != nil
         
     | 
| 
       376 
430 
     | 
    
         
             
                        found_binary = find_binary_file_in_bundle(app_bundle)
         
     | 
| 
       377 
     | 
    
         
            -
             
     | 
| 
       378 
     | 
    
         
            -
                        found_binary = find_binary_file_in_bundle(dynamic_lib_bundle)
         
     | 
| 
       379 
     | 
    
         
            -
                    elsif matched_xctest_bundle != nil
         
     | 
| 
      
 431 
     | 
    
         
            +
                      elsif matched_xctest_bundle != nil
         
     | 
| 
       380 
432 
     | 
    
         
             
                        found_binary = find_binary_file_in_bundle(matched_xctest_bundle)
         
     | 
| 
       381 
     | 
    
         
            -
             
     | 
| 
      
 433 
     | 
    
         
            +
                      elsif dynamic_lib_bundle != nil
         
     | 
| 
      
 434 
     | 
    
         
            +
                        found_binary = find_binary_file_in_bundle(dynamic_lib_bundle)
         
     | 
| 
      
 435 
     | 
    
         
            +
                      else
         
     | 
| 
       382 
436 
     | 
    
         
             
                        found_binary = find_binary_file_in_bundle(xctest_bundle)
         
     | 
| 
      
 437 
     | 
    
         
            +
                      end
         
     | 
| 
      
 438 
     | 
    
         
            +
             
     | 
| 
      
 439 
     | 
    
         
            +
                      if found_binary
         
     | 
| 
      
 440 
     | 
    
         
            +
                        found_binaries.push(found_binary)
         
     | 
| 
      
 441 
     | 
    
         
            +
                      end
         
     | 
| 
       383 
442 
     | 
    
         
             
                    end
         
     | 
| 
       384 
443 
     | 
    
         
             
                  end
         
     | 
| 
       385 
444 
     | 
    
         | 
| 
       386 
     | 
    
         
            -
                  raise StandardError, "No product binary found in #{profdata_coverage_dir}." unless  
     | 
| 
      
 445 
     | 
    
         
            +
                  raise StandardError, "No product binary found in #{profdata_coverage_dir}." unless found_binaries.count > 0
         
     | 
| 
       387 
446 
     | 
    
         | 
| 
       388 
     | 
    
         
            -
                   
     | 
| 
      
 447 
     | 
    
         
            +
                  found_binaries.map { |binary| File.expand_path(binary) }
         
     | 
| 
      
 448 
     | 
    
         
            +
                end
         
     | 
| 
      
 449 
     | 
    
         
            +
             
     | 
| 
      
 450 
     | 
    
         
            +
                def find_source_files
         
     | 
| 
      
 451 
     | 
    
         
            +
                  source_files = load_option_array("source_files")
         
     | 
| 
      
 452 
     | 
    
         
            +
                  return if source_files.nil?
         
     | 
| 
      
 453 
     | 
    
         
            +
             
     | 
| 
      
 454 
     | 
    
         
            +
                  current_dir = Pathname("./").realpath
         
     | 
| 
      
 455 
     | 
    
         
            +
                  paths = source_files.flat_map { |pattern| Dir.glob(pattern) }.uniq
         
     | 
| 
      
 456 
     | 
    
         
            +
             
     | 
| 
      
 457 
     | 
    
         
            +
                  paths.map do |path|
         
     | 
| 
      
 458 
     | 
    
         
            +
                    source_file_absolute_path = Pathname(path).realpath
         
     | 
| 
      
 459 
     | 
    
         
            +
                    source_file_relative_path = source_file_absolute_path.relative_path_from(current_dir)
         
     | 
| 
      
 460 
     | 
    
         
            +
                    self.ignore_list.any? { |ignore| File.fnmatch(ignore, source_file_relative_path) } ? nil : source_file_absolute_path
         
     | 
| 
      
 461 
     | 
    
         
            +
                  end.compact
         
     | 
| 
      
 462 
     | 
    
         
            +
                end
         
     | 
| 
      
 463 
     | 
    
         
            +
             
     | 
| 
      
 464 
     | 
    
         
            +
                def load_option_array(option)
         
     | 
| 
      
 465 
     | 
    
         
            +
                  value = self.send(option.to_sym)
         
     | 
| 
      
 466 
     | 
    
         
            +
                  # Only load if a value is not already set
         
     | 
| 
      
 467 
     | 
    
         
            +
                  if !value
         
     | 
| 
      
 468 
     | 
    
         
            +
                    value_yml = self.class.yml[option]
         
     | 
| 
      
 469 
     | 
    
         
            +
                    # Need to check the type in the config file because it can be a string or array
         
     | 
| 
      
 470 
     | 
    
         
            +
                    if value_yml and value_yml.is_a? Array
         
     | 
| 
      
 471 
     | 
    
         
            +
                      value = value_yml
         
     | 
| 
      
 472 
     | 
    
         
            +
                    elsif value_yml
         
     | 
| 
      
 473 
     | 
    
         
            +
                      value = [value_yml]
         
     | 
| 
      
 474 
     | 
    
         
            +
                    end
         
     | 
| 
      
 475 
     | 
    
         
            +
                  end
         
     | 
| 
      
 476 
     | 
    
         
            +
                  value
         
     | 
| 
       389 
477 
     | 
    
         
             
                end
         
     | 
| 
       390 
478 
     | 
    
         
             
              end
         
     | 
| 
       391 
479 
     | 
    
         
             
            end
         
     |