slather 2.4.4 → 2.4.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f140d82b4571c441455df1b9eb9c247f8f52e310
4
- data.tar.gz: eeaac677b8b341cb92479c16cbdd2bd6254c85de
2
+ SHA256:
3
+ metadata.gz: 1e8bb7fa0d1346a2a81a47272a1f55d2625a8457d8c4b88a82f2b737792aa39a
4
+ data.tar.gz: a375044a21453e30695adf31c20f5a9ea885091d1eb3ac6fafc3f3ef9d44d752
5
5
  SHA512:
6
- metadata.gz: ebac089dfe9785ab980a86d6f3020a9c700f01edd1d3dcdb170c82faccc553cb5a4d7143a30c6432af7c606de9902fd42737f12233d136240aeb01e0ce415a21
7
- data.tar.gz: e3a00efe762b6062a33ef42d30cbdadc4b03444f3f36538bc23b98988659587aace4a6d9fa346ab8a7d5367072fded337dd600efff2e28d5181fd98b366ff10c
6
+ metadata.gz: 9a9dddfd5dcf8160cd31327f170030f3520c2a885b7c56f6f2d7d470e5442b8c49c3f30ba289664767cc43d597415c2309654699a5ac2fd59db90ac5bc796697
7
+ data.tar.gz: 5f67d766eb09e18df9a2bf1016f49a3a7a7f912be2bf5e05ed31c925540fb51ccaef34e53f1dffaed6aadfef827a3e69aa1c7144fa30a024f9f72a1ce2d47605
data/.gitignore CHANGED
@@ -46,4 +46,8 @@ html
46
46
  *.gcno
47
47
 
48
48
  # JetBrains IDE
49
- .idea/
49
+ .idea/
50
+
51
+ # Test output
52
+ report.llcov
53
+ report.json
@@ -1,20 +1,10 @@
1
1
  language: objective-c
2
2
  script: bundle exec rake
3
- osx_image: xcode8.3
4
-
5
- # Sets Travis to run the Ruby specs on OS X machines which are required to
6
- # build the native extensions of Xcodeproj.
7
-
8
- env:
9
- - RVM_RUBY_VERSION=system
3
+ osx_image: xcode9.2
10
4
 
11
5
  before_install:
12
6
  - curl http://curl.haxx.se/ca/cacert.pem -o /usr/local/share/cacert.pem
13
- - source ~/.rvm/scripts/rvm
14
- - if [[ $RVM_RUBY_VERSION != 'system' ]]; then rvm install $RVM_RUBY_VERSION; fi
15
- - rvm use $RVM_RUBY_VERSION
16
- - if [[ $RVM_RUBY_VERSION == 'system' ]]; then export ARCHFLAGS=-Wno-error=unused-command-line-argument-hard-error-in-future; fi
17
- - if [[ $RVM_RUBY_VERSION == 'system' ]]; then sudo gem install bundler --no-ri --no-rdoc; else gem install bundler --no-ri --no-rdoc; fi
7
+ - gem install bundler -v "~> 1.0" --no-ri --no-rdoc
18
8
 
19
9
  install:
20
10
  - bundle install --without=documentation
@@ -1,9 +1,71 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v2.4.9
4
+
5
+ * Added support for Sonarqube output
6
+ [adellibovi](https://github.com/adellibovi)
7
+ [#456](https://github.com/SlatherOrg/slather/pull/456)
8
+
9
+ ## v2.4.8
10
+
11
+ * Optimize performance for many binaries
12
+ [cltnschlosser](https://github.com/cltnschlosser)
13
+ [#455](https://github.com/SlatherOrg/slather/pull/455)
14
+
15
+ * Don't generate line 0 in profdata_coverage_file.rb from line with error
16
+ [tthbalazs](https://github.com/tthbalazs)
17
+ [#449](https://github.com/SlatherOrg/slather/pull/449)
18
+
19
+ * coveralls dependency update
20
+ [GRiMe2D](https://github.com/GRiMe2D)
21
+ [#448](https://github.com/SlatherOrg/slather/pull/448)
22
+
23
+ ## v2.4.7
24
+
25
+ * Update dependencies
26
+ [dnedrow](https://github.com/dnedrow)
27
+
28
+ * Fixed errors when llvm-cov argument length exceeds ARG_MAX
29
+ [weibel](https://github.com/weibel)
30
+ [#414](https://github.com/SlatherOrg/slather/pull/414)
31
+
32
+ * Show "No coverage directory found." instead of "implicit conversion nil into String"
33
+ [phimage](https://github.com/phimage)
34
+ [#381](https://github.com/SlatherOrg/slather/pull/381) [#341](https://github.com/SlatherOrg/slather/issues/341)
35
+
36
+ ## v2.4.6
37
+
38
+ * Fix .dSYM and .swiftmodule files filtering in find_binary_files()
39
+ [krin-san](https://github.com/krin-san)
40
+ [#368](https://github.com/SlatherOrg/slather/pull/368)
41
+
42
+ * Fixed loading coverage for a single source file
43
+ [blackm00n](https://github.com/blackm00n)
44
+ [#377](https://github.com/SlatherOrg/slather/pull/377) [#398](https://github.com/SlatherOrg/slather/pull/398)
45
+
46
+ * Fixed truncated file list in HTML export
47
+ [miroslavkovac](https://github.com/miroslavkovac)
48
+ [#402](https://github.com/SlatherOrg/slather/pull/402) [#261](https://github.com/SlatherOrg/slather/issues/261)
49
+
50
+ ## v2.4.5
51
+
52
+ * Support for specifying a specific binary architecture
53
+ [ksuther](https://github.com/ksuther), [nickolas-pohilets](https://github.com/nickolas-pohilets)
54
+ [#367](https://github.com/SlatherOrg/slather/pull/367)
55
+
56
+ * Added absolute statement count to simple output (instead of showing just a percentage)
57
+ [barrault01](https://github.com/barrault01), [ivanbrunel](https://github.com/ivanbruel)
58
+ [#365](https://github.com/SlatherOrg/slather/pull/365)
59
+
60
+ * Updated nokogiri dependency version
61
+ [#363](https://github.com/SlatherOrg/slather/issues/363), [#366](https://github.com/SlatherOrg/slather/pull/366)
62
+
63
+ * slather now requires ruby 2.1 or later (10.13 ships with 2.3.3)
64
+
3
65
  ## v2.4.4
4
66
 
5
67
  * Added llvm-cov output format
6
- [lampietti](https://github.com/sgtsquiggs) [#354](https://github.com/SlatherOrg/slather/pull/354)
68
+ [sgtsquiggs](https://github.com/sgtsquiggs) [#354](https://github.com/SlatherOrg/slather/pull/354)
7
69
 
8
70
  * Exclude swiftmodule from product search
9
71
  [lampietti](https://github.com/lampietti) [#352](https://github.com/SlatherOrg/slather/pull/352)
data/README.md CHANGED
@@ -61,6 +61,26 @@ If you use a different configuration for your tests:
61
61
  $ slather coverage -s --scheme YourXcodeSchemeName --configuration YourBuildConfigurationName path/to/project.xcodeproj
62
62
  ```
63
63
 
64
+ If your configuration produces a universal binary you need to specify a specific architecture to use:
65
+
66
+ ```sh
67
+ $ slather coverage -s --arch x86_64 --scheme YourXcodeSchemeName --configuration YourBuildConfigurationName path/to/project.xcodeproj
68
+ ```
69
+
70
+ ### For multiple modules
71
+
72
+ If you want to run some modules, but not all (like modules created by CocoaPods) you can do it like this:
73
+
74
+ ```sh
75
+ $ slather coverage --binary-basename module1 --binary-basename module2 path/to/project.xcodeproj
76
+ ```
77
+ You can also add it to the `.slather.yml` file as an array:
78
+ ```yml
79
+ binary_basename:
80
+ - module1
81
+ - module2
82
+ ```
83
+
64
84
  ### Setup for Xcode 5 and 6
65
85
 
66
86
  Run this command to enable the `Generate Test Coverage` and `Instrument Program Flow` flags for your project:
@@ -1 +1 @@
1
- !function(){function a(b,c,d){var e=a.resolve(b);if(null==e){d=d||b,c=c||"root";var f=new Error('Failed to require "'+d+'" from "'+c+'"');throw f.path=d,f.parent=c,f.require=!0,f}var g=a.modules[e];if(!g._resolving&&!g.exports){var h={};h.exports={},h.client=h.component=!0,g._resolving=!0,g.call(this,h.exports,a.relative(e),h),delete g._resolving,g.exports=h.exports}return g.exports}a.modules={},a.aliases={},a.resolve=function(b){"/"===b.charAt(0)&&(b=b.slice(1));for(var c=[b,b+".js",b+".json",b+"/index.js",b+"/index.json"],d=0;di;i++)if(h.test(f[i].className)){if(c)return f[i];d[j]=f[i],j++}return d}}()}),a.register("javve-get-attribute/index.js",function(a,b,c){c.exports=function(a,b){var c=a.getAttribute&&a.getAttribute(b)||null;if(!c)for(var d=a.attributes,e=d.length,f=0;e>f;f++)void 0!==b[f]&&b[f].nodeName===b&&(c=b[f].nodeValue);return c}}),a.register("javve-natural-sort/index.js",function(a,b,c){c.exports=function(a,b,c){var d,e,f=/(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,g=/(^[ ]*|[ ]*$)/g,h=/(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,i=/^0x[0-9a-f]+$/i,j=/^0/,c=c||{},k=function(a){return c.insensitive&&(""+a).toLowerCase()||""+a},l=k(a).replace(g,"")||"",m=k(b).replace(g,"")||"",n=l.replace(f,"\x00$1\x00").replace(/\0$/,"").replace(/^\0/,"").split("\x00"),o=m.replace(f,"\x00$1\x00").replace(/\0$/,"").replace(/^\0/,"").split("\x00"),p=parseInt(l.match(i))||1!=n.length&&l.match(h)&&Date.parse(l),q=parseInt(m.match(i))||p&&m.match(h)&&Date.parse(m)||null,r=c.desc?-1:1;if(q){if(q>p)return-1*r;if(p>q)return 1*r}for(var s=0,t=Math.max(n.length,o.length);t>s;s++){if(d=!(n[s]||"").match(j)&&parseFloat(n[s])||n[s]||0,e=!(o[s]||"").match(j)&&parseFloat(o[s])||o[s]||0,isNaN(d)!==isNaN(e))return isNaN(d)?1:-1;if(typeof d!=typeof e&&(d+="",e+=""),e>d)return-1*r;if(d>e)return 1*r}return 0}}),a.register("javve-to-string/index.js",function(a,b,c){c.exports=function(a){return a=void 0===a?"":a,a=null===a?"":a,a=a.toString()}}),a.register("component-type/index.js",function(a,b,c){var d=Object.prototype.toString;c.exports=function(a){switch(d.call(a)){case"[object Date]":return"date";case"[object RegExp]":return"regexp";case"[object Arguments]":return"arguments";case"[object Array]":return"array";case"[object Error]":return"error"}return null===a?"null":void 0===a?"undefined":a!==a?"nan":a&&1===a.nodeType?"element":typeof a.valueOf()}}),a.register("list.js/index.js",function(a,b,c){!function(a,d){"use strict";var e=a.document,f=b("get-by-class"),g=b("extend"),h=b("indexof"),i=function(a,c,i){var j,k=this,l=b("./src/item")(k),m=b("./src/add-async")(k),n=b("./src/parse")(k);j={start:function(){k.listClass="list",k.searchClass="search",k.sortClass="sort",k.page=200,k.i=1,k.items=[],k.visibleItems=[],k.matchingItems=[],k.searched=!1,k.filtered=!1,k.handlers={updated:[]},k.plugins={},k.helpers={getByClass:f,extend:g,indexOf:h},g(k,c),k.listContainer="string"==typeof a?e.getElementById(a):a,k.listContainer&&(k.list=f(k.listContainer,k.listClass,!0),k.templater=b("./src/templater")(k),k.search=b("./src/search")(k),k.filter=b("./src/filter")(k),k.sort=b("./src/sort")(k),this.items(),k.update(),this.plugins())},items:function(){n(k.list),i!==d&&k.add(i)},plugins:function(){for(var a=0;af;f++){var h=null;a[f]instanceof l?(h=a[f],h.reload()):(e=k.items.length>k.page?!0:!1,h=new l(a[f],d,e)),k.items.push(h),c.push(h)}return k.update(),c},this.show=function(a,b){return this.i=a,this.page=b,k.update(),k},this.remove=function(a,b,c){for(var d=0,e=0,f=k.items.length;f>e;e++)k.items[e].values()[a]==b&&(k.templater.remove(k.items[e],c),k.items.splice(e,1),f--,e--,d++);return k.update(),d},this.get=function(a,b){for(var c=[],d=0,e=k.items.length;e>d;d++){var f=k.items[d];f.values()[a]==b&&c.push(f)}return c},this.size=function(){return k.items.length},this.clear=function(){return k.templater.clear(),k.items=[],k},this.on=function(a,b){return k.handlers[a].push(b),k},this.off=function(a,b){var c=k.handlers[a],d=h(c,b);return d>-1&&c.splice(d,1),k},this.trigger=function(a){for(var b=k.handlers[a].length;b--;)k.handlers[a][b](k);return k},this.reset={filter:function(){for(var a=k.items,b=a.length;b--;)a[b].filtered=!1;return k},search:function(){for(var a=k.items,b=a.length;b--;)a[b].found=!1;return k}},this.update=function(){var a=k.items,b=a.length;k.visibleItems=[],k.matchingItems=[],k.templater.clear();for(var c=0;b>c;c++)a[c].matching()&&k.matchingItems.length+1>=k.i&&k.visibleItems.lengthb;b++)j.item(a.items[b])},item:function(a){a.found=!1;for(var b=0,d=c.length;d>b;b++)if(j.values(a.values(),c[b]))return a.found=!0,void 0},values:function(a,c){return a.hasOwnProperty(c)&&(b=f(a[c]).toLowerCase(),""!==g&&b.search(g)>-1)?!0:!1},reset:function(){a.reset.search(),a.searched=!1}},k=function(b){return a.trigger("searchStart"),i.resetList(),i.setSearchString(b),i.setOptions(arguments),i.setColumns(),""===g?j.reset():(a.searched=!0,h?h(g,c):j.list()),a.update(),a.trigger("searchComplete"),a.visibleItems};return a.handlers.searchStart=a.handlers.searchStart||[],a.handlers.searchComplete=a.handlers.searchComplete||[],d.bind(e(a.listContainer,a.searchClass),"keyup",function(b){var c=b.target||b.srcElement,d=""===c.value&&!a.searched;d||k(c.value)}),d.bind(e(a.listContainer,a.searchClass),"input",function(a){var b=a.target||a.srcElement;""===b.value&&k("")}),a.helpers.toString=f,k}}),a.register("list.js/src/sort.js",function(a,b,c){var d=b("natural-sort"),e=b("classes"),f=b("events"),g=b("get-by-class"),h=b("get-attribute");c.exports=function(a){a.sortFunction=a.sortFunction||function(a,b,c){return c.desc="desc"==c.order?!0:!1,d(a.values()[c.valueName],b.values()[c.valueName],c)};var b={els:void 0,clear:function(){for(var a=0,c=b.els.length;c>a;a++)e(b.els[a]).remove("asc"),e(b.els[a]).remove("desc")},getOrder:function(a){var b=h(a,"data-order");return"asc"==b||"desc"==b?b:e(a).has("desc")?"asc":e(a).has("asc")?"desc":"asc"},getInSensitive:function(a,b){var c=h(a,"data-insensitive");b.insensitive="true"===c?!0:!1},setOrder:function(a){for(var c=0,d=b.els.length;d>c;c++){var f=b.els[c];if(h(f,"data-sort")===a.valueName){var g=h(f,"data-order");"asc"==g||"desc"==g?g==a.order&&e(f).add(a.order):e(f).add(a.order)}}}},c=function(){a.trigger("sortStart"),options={};var c=arguments[0].currentTarget||arguments[0].srcElement||void 0;c?(options.valueName=h(c,"data-sort"),b.getInSensitive(c,options),options.order=b.getOrder(c)):(options=arguments[1]||options,options.valueName=arguments[0],options.order=options.order||"asc",options.insensitive="undefined"==typeof options.insensitive?!0:options.insensitive),b.clear(),b.setOrder(options),options.sortFunction=options.sortFunction||a.sortFunction,a.items.sort(function(a,b){return options.sortFunction(a,b,options)}),a.update(),a.trigger("sortComplete")};return a.handlers.sortStart=a.handlers.sortStart||[],a.handlers.sortComplete=a.handlers.sortComplete||[],b.els=g(a.listContainer,a.sortClass),f.bind(b.els,"click",c),a.on("searchStart",b.clear),a.on("filterStart",b.clear),a.helpers.classes=e,a.helpers.naturalSort=d,a.helpers.events=f,a.helpers.getAttribute=h,c}}),a.register("list.js/src/item.js",function(a,b,c){c.exports=function(a){return function(b,c,d){var e=this;this._values={},this.found=!1,this.filtered=!1;var f=function(b,c,d){if(void 0===c)d?e.values(b,d):e.values(b);else{e.elm=c;var f=a.templater.get(e,b);e.values(f)}};this.values=function(b,c){if(void 0===b)return e._values;for(var d in b)e._values[d]=b[d];c!==!0&&a.templater.set(e,e.values())},this.show=function(){a.templater.show(e)},this.hide=function(){a.templater.hide(e)},this.matching=function(){return a.filtered&&a.searched&&e.found&&e.filtered||a.filtered&&!a.searched&&e.filtered||!a.filtered&&a.searched&&e.found||!a.filtered&&!a.searched},this.visible=function(){return e.elm.parentNode==a.list?!0:!1},f(b,c,d)}}}),a.register("list.js/src/templater.js",function(a,b,c){var d=b("get-by-class"),e=function(a){function b(b){if(void 0===b){for(var c=a.list.childNodes,d=0,e=c.length;e>d;d++)if(void 0===c[d].data)return c[d];return null}if(-1!==b.indexOf("<")){var f=document.createElement("div");return f.innerHTML=b,f.firstChild}return document.getElementById(a.item)}var c=b(a.item),e=this;this.get=function(a,b){e.create(a);for(var c={},f=0,g=b.length;g>f;f++){var h=d(a.elm,b[f],!0);c[b[f]]=h?h.innerHTML:""}return c},this.set=function(a,b){if(!e.create(a))for(var c in b)if(b.hasOwnProperty(c)){var f=d(a.elm,c,!0);f&&("IMG"===f.tagName&&""!==b[c]?f.src=b[c]:f.innerHTML=b[c])}},this.create=function(a){if(void 0!==a.elm)return!1;var b=c.cloneNode(!0);return b.removeAttribute("id"),a.elm=b,e.set(a,a.values()),!0},this.remove=function(b){a.list.removeChild(b.elm)},this.show=function(b){e.create(b),a.list.appendChild(b.elm)},this.hide=function(b){void 0!==b.elm&&b.elm.parentNode===a.list&&a.list.removeChild(b.elm)},this.clear=function(){if(a.list.hasChildNodes())for(;a.list.childNodes.length>=1;)a.list.removeChild(a.list.firstChild)}};c.exports=function(a){return new e(a)}}),a.register("list.js/src/filter.js",function(a,b,c){c.exports=function(a){return a.handlers.filterStart=a.handlers.filterStart||[],a.handlers.filterComplete=a.handlers.filterComplete||[],function(b){if(a.trigger("filterStart"),a.i=1,a.reset.filter(),void 0===b)a.filtered=!1;else{a.filtered=!0;for(var c=a.items,d=0,e=c.length;e>d;d++){var f=c[d];f.filtered=b(f)?!0:!1}}return a.update(),a.trigger("filterComplete"),a.visibleItems}}}),a.register("list.js/src/add-async.js",function(a,b,c){c.exports=function(a){return function(b,c,d){var e=b.splice(0,100);d=d||[],d=d.concat(a.add(e)),b.length>0?setTimeout(function(){addAsync(b,c,d)},10):(a.update(),c(d))}}}),a.register("list.js/src/parse.js",function(a,b,c){c.exports=function(a){var c=b("./item")(a),d=function(a){for(var b=a.childNodes,c=[],d=0,e=b.length;e>d;d++)void 0===b[d].data&&c.push(b[d]);return c},e=function(b,d){for(var e=0,f=b.length;f>e;e++)a.items.push(new c(d,b[e]))},f=function(b,c){var d=b.splice(0,100);e(d,c),b.length>0?setTimeout(function(){init.items.indexAsync(b,c)},10):a.update()};return function(){var b=d(a.list),c=a.valueNames;a.indexAsync?f(b,c):e(b,c)}}}),a.alias("component-classes/index.js","list.js/deps/classes/index.js"),a.alias("component-classes/index.js","classes/index.js"),a.alias("component-indexof/index.js","component-classes/deps/indexof/index.js"),a.alias("segmentio-extend/index.js","list.js/deps/extend/index.js"),a.alias("segmentio-extend/index.js","extend/index.js"),a.alias("component-indexof/index.js","list.js/deps/indexof/index.js"),a.alias("component-indexof/index.js","indexof/index.js"),a.alias("javve-events/index.js","list.js/deps/events/index.js"),a.alias("javve-events/index.js","events/index.js"),a.alias("component-event/index.js","javve-events/deps/event/index.js"),a.alias("timoxley-to-array/index.js","javve-events/deps/to-array/index.js"),a.alias("javve-get-by-class/index.js","list.js/deps/get-by-class/index.js"),a.alias("javve-get-by-class/index.js","get-by-class/index.js"),a.alias("javve-get-attribute/index.js","list.js/deps/get-attribute/index.js"),a.alias("javve-get-attribute/index.js","get-attribute/index.js"),a.alias("javve-natural-sort/index.js","list.js/deps/natural-sort/index.js"),a.alias("javve-natural-sort/index.js","natural-sort/index.js"),a.alias("javve-to-string/index.js","list.js/deps/to-string/index.js"),a.alias("javve-to-string/index.js","list.js/deps/to-string/index.js"),a.alias("javve-to-string/index.js","to-string/index.js"),a.alias("javve-to-string/index.js","javve-to-string/index.js"),a.alias("component-type/index.js","list.js/deps/type/index.js"),a.alias("component-type/index.js","type/index.js"),"object"==typeof exports?module.exports=a("list.js"):"function"==typeof define&&define.amd?define(function(){return a("list.js")}):this.List=a("list.js")}();
1
+ var List=function(t){function e(n){if(r[n])return r[n].exports;var i=r[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var r={};return e.m=t,e.c=r,e.i=function(t){return t},e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=11)}([function(t,e,r){function n(t){if(!t||!t.nodeType)throw new Error("A DOM element reference is required");this.el=t,this.list=t.classList}var i=r(4),s=/\s+/;Object.prototype.toString;t.exports=function(t){return new n(t)},n.prototype.add=function(t){if(this.list)return this.list.add(t),this;var e=this.array(),r=i(e,t);return~r||e.push(t),this.el.className=e.join(" "),this},n.prototype.remove=function(t){if(this.list)return this.list.remove(t),this;var e=this.array(),r=i(e,t);return~r&&e.splice(r,1),this.el.className=e.join(" "),this},n.prototype.toggle=function(t,e){return this.list?("undefined"!=typeof e?e!==this.list.toggle(t,e)&&this.list.toggle(t):this.list.toggle(t),this):("undefined"!=typeof e?e?this.add(t):this.remove(t):this.has(t)?this.remove(t):this.add(t),this)},n.prototype.array=function(){var t=this.el.getAttribute("class")||"",e=t.replace(/^\s+|\s+$/g,""),r=e.split(s);return""===r[0]&&r.shift(),r},n.prototype.has=n.prototype.contains=function(t){return this.list?this.list.contains(t):!!~i(this.array(),t)}},function(t,e,r){var n=window.addEventListener?"addEventListener":"attachEvent",i=window.removeEventListener?"removeEventListener":"detachEvent",s="addEventListener"!==n?"on":"",a=r(5);e.bind=function(t,e,r,i){t=a(t);for(var o=0;o0?setTimeout(function(){e(r,n,i)},1):(t.update(),n(i))};return e}},function(t,e){t.exports=function(t){return t.handlers.filterStart=t.handlers.filterStart||[],t.handlers.filterComplete=t.handlers.filterComplete||[],function(e){if(t.trigger("filterStart"),t.i=1,t.reset.filter(),void 0===e)t.filtered=!1;else{t.filtered=!0;for(var r=t.items,n=0,i=r.length;nv.page,a=new m(t[i],void 0,n),v.items.push(a),r.push(a)}return v.update(),r}},this.show=function(t,e){return this.i=t,this.page=e,v.update(),v},this.remove=function(t,e,r){for(var n=0,i=0,s=v.items.length;i-1&&r.splice(n,1),v},this.trigger=function(t){for(var e=v.handlers[t].length;e--;)v.handlers[t][e](v);return v},this.reset={filter:function(){for(var t=v.items,e=t.length;e--;)t[e].filtered=!1;return v},search:function(){for(var t=v.items,e=t.length;e--;)t[e].found=!1;return v}},this.update=function(){var t=v.items,e=t.length;v.visibleItems=[],v.matchingItems=[],v.templater.clear();for(var r=0;r=v.i&&v.visibleItems.lengthe},innerWindow:function(t,e,r){return t>=e-r&&t<=e+r},dotted:function(t,e,r,n,i,s,a){return this.dottedLeft(t,e,r,n,i,s)||this.dottedRight(t,e,r,n,i,s,a)},dottedLeft:function(t,e,r,n,i,s){return e==r+1&&!this.innerWindow(e,i,s)&&!this.right(e,n)},dottedRight:function(t,e,r,n,i,s,a){return!t.items[a-1].values().dotted&&(e==n&&!this.innerWindow(e,i,s)&&!this.right(e,n))}},a=function(e,r,n){i.bind(e,"click",function(){t.show((r-1)*n+1,n)})};return function(r){var n=new s(t.listContainer.id,{listClass:r.paginationClass||"pagination",item:"
  • ",valueNames:["page","dotted"],searchClass:"pagination-search-that-is-not-supposed-to-exist",sortClass:"pagination-sort-that-is-not-supposed-to-exist"});t.on("updated",function(){e(n,r)}),e(n,r)}}},function(t,e,r){t.exports=function(t){var e=r(2)(t),n=function(t){for(var e=t.childNodes,r=[],n=0,i=e.length;n0?setTimeout(function(){s(e,r)},1):(t.update(),t.trigger("parseComplete"))};return t.handlers.parseComplete=t.handlers.parseComplete||[],function(){var e=n(t.list),r=t.valueNames;t.indexAsync?s(e,r):i(e,r)}}},function(t,e){t.exports=function(t){var e,r,n,i,s={resetList:function(){t.i=1,t.templater.clear(),i=void 0},setOptions:function(t){2==t.length&&t[1]instanceof Array?r=t[1]:2==t.length&&"function"==typeof t[1]?(r=void 0,i=t[1]):3==t.length?(r=t[1],i=t[2]):r=void 0},setColumns:function(){0!==t.items.length&&void 0===r&&(r=void 0===t.searchColumns?s.toArray(t.items[0].values()):t.searchColumns)},setSearchString:function(e){e=t.utils.toString(e).toLowerCase(),e=e.replace(/[-[\]{}()*+?.,\\^$|#]/g,"\\$&"),n=e},toArray:function(t){var e=[];for(var r in t)e.push(r);return e}},a={list:function(){for(var e=0,r=t.items.length;e-1))},reset:function(){t.reset.search(),t.searched=!1}},o=function(e){return t.trigger("searchStart"),s.resetList(),s.setSearchString(e),s.setOptions(arguments),s.setColumns(),""===n?a.reset():(t.searched=!0,i?i(n,r):a.list()),t.update(),t.trigger("searchComplete"),t.visibleItems};return t.handlers.searchStart=t.handlers.searchStart||[],t.handlers.searchComplete=t.handlers.searchComplete||[],t.utils.events.bind(t.utils.getByClass(t.listContainer,t.searchClass),"keyup",function(e){var r=e.target||e.srcElement,n=""===r.value&&!t.searched;n||o(r.value)}),t.utils.events.bind(t.utils.getByClass(t.listContainer,t.searchClass),"input",function(t){var e=t.target||t.srcElement;""===e.value&&o("")}),o}},function(t,e){t.exports=function(t){var e={els:void 0,clear:function(){for(var r=0,n=e.els.length;r]/g.exec(e)){var s=document.createElement("tbody");return s.innerHTML=e,s.firstChild}if(e.indexOf("<")!==-1){var a=document.createElement("div");return a.innerHTML=e,a.firstChild}var o=document.getElementById(t.item);if(o)return o}},this.get=function(e,n){r.create(e);for(var i={},s=0,a=n.length;s=1;)t.list.removeChild(t.list.firstChild)},n()};t.exports=function(t){return new r(t)}},function(t,e){t.exports=function(t,e){var r=t.getAttribute&&t.getAttribute(e)||null;if(!r)for(var n=t.attributes,i=n.length,s=0;s=48&&t<=57}function i(t,e){for(var r=(t+="").length,i=(e+="").length,s=0,l=0;s32)return!1;var o=i,l=function(){var t,r={};for(t=0;t=p;b--){var w=l[t.charAt(b-1)];if(0===g?y[b]=(y[b+1]<<1|1)&w:y[b]=(y[b+1]<<1|1)&w|((v[b+1]|v[b])<<1|1)|v[b+1],y[b]&f){var x=n(g,b-1);if(x<=u){if(u=x,c=b-1,!(c>o))break;p=Math.max(1,2*o-c)}}}if(n(g+1,o)>u)break;v=y}return!(c<0)}}]);
    @@ -12,6 +12,7 @@ require 'slather/coverage_service/simple_output'
    12
    12
      require 'slather/coverage_service/html_output'
    13
    13
      require 'slather/coverage_service/json_output'
    14
    14
      require 'slather/coverage_service/llvm_cov_output'
    15
    + require 'slather/coverage_service/sonarqube_xml_output'
    15
    16
      require 'cfpropertylist'
    16
    17
     
    17
    18
      module Slather
    @@ -13,6 +13,7 @@ class CoverageCommand < Clamp::Command
    13
    13
      option ["--simple-output", "-s"], :flag, "Output coverage results to the terminal"
    14
    14
      option ["--gutter-json", "-g"], :flag, "Output coverage results as Gutter JSON format"
    15
    15
      option ["--cobertura-xml", "-x"], :flag, "Output coverage results as Cobertura XML format"
    16
    + option ["--sonarqube-xml", "-sq"], :flag, "Output coverage results as Cobertura XML format"
    16
    17
      option ["--llvm-cov", "-r"], :flag, "Output coverage as llvm-cov format"
    17
    18
      option ["--json"], :flag, "Output coverage results as simple JSON"
    18
    19
      option ["--html"], :flag, "Output coverage results as static html pages"
    @@ -30,6 +31,7 @@ class CoverageCommand < Clamp::Command
    30
    31
      option ["--workspace"], "WORKSPACE", "The workspace that the project was built in"
    31
    32
      option ["--binary-file"], "BINARY_FILE", "The binary file against the which the coverage will be run", :multivalued => true
    32
    33
      option ["--binary-basename"], "BINARY_BASENAME", "Basename of the file against which the coverage will be run", :multivalued => true
    34
    + option ["--arch"], "ARCH", "Architecture to use from universal binaries"
    33
    35
      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
    34
    36
      option ["--decimals"], "DECIMALS", "The amount of decimals to use for % coverage reporting"
    35
    37
     
    @@ -49,6 +51,7 @@ class CoverageCommand < Clamp::Command
    49
    51
      setup_workspace
    50
    52
      setup_binary_file
    51
    53
      setup_binary_basename
    54
    + setup_arch
    52
    55
      setup_source_files
    53
    56
      setup_decimals
    54
    57
     
    @@ -122,6 +125,8 @@ class CoverageCommand < Clamp::Command
    122
    125
      project.show_html = show?
    123
    126
      elsif json?
    124
    127
      project.coverage_service = :json
    128
    + elsif sonarqube_xml?
    129
    + project.coverage_service = :sonarqube_xml
    125
    130
      end
    126
    131
      end
    127
    132
     
    @@ -153,6 +158,10 @@ class CoverageCommand < Clamp::Command
    153
    158
      project.binary_basename = binary_basename_list if !binary_basename_list.empty?
    154
    159
      end
    155
    160
     
    161
    + def setup_arch
    162
    + project.arch = arch
    163
    + end
    164
    +
    156
    165
      def setup_source_files
    157
    166
      project.source_files = source_files_list if !source_files_list.empty?
    158
    167
      end
    @@ -43,6 +43,7 @@ module Slather
    43
    43
      end
    44
    44
     
    45
    45
      total_percentage = decimal_f([(total_project_lines_tested / total_project_lines.to_f) * 100.0])
    46
    + puts "Tested #{total_project_lines_tested}/#{total_project_lines} statements"
    46
    47
      puts "Test Coverage: #{total_percentage}%"
    47
    48
      end
    48
    49
     
    @@ -0,0 +1,61 @@
    1
    + require 'nokogiri'
    2
    + require 'date'
    3
    +
    4
    + module Slather
    5
    + module CoverageService
    6
    + module SonarqubeXmlOutput
    7
    +
    8
    + def coverage_file_class
    9
    + if input_format == "profdata"
    10
    + Slather::ProfdataCoverageFile
    11
    + else
    12
    + Slather::CoverageFile
    13
    + end
    14
    + end
    15
    + private :coverage_file_class
    16
    +
    17
    + def post
    18
    + cobertura_xml_report = create_xml_report(coverage_files)
    19
    + store_report(cobertura_xml_report)
    20
    + end
    21
    +
    22
    + def store_report(report)
    23
    + output_file = 'sonarqube-generic-coverage.xml'
    24
    + if output_directory
    25
    + FileUtils.mkdir_p(output_directory)
    26
    + output_file = File.join(output_directory, output_file)
    27
    + end
    28
    + File.write(output_file, report.to_s)
    29
    + end
    30
    +
    31
    + def create_xml_report(coverage_files)
    32
    + create_empty_xml_report
    33
    + coverage_node = @doc.root
    34
    + coverage_node['version'] = "1"
    35
    +
    36
    + coverage_files.each do |coverage_file|
    37
    + file_node = Nokogiri::XML::Node.new "file", @doc
    38
    + file_node.parent = coverage_node
    39
    + file_node['path'] = coverage_file.source_file_pathname_relative_to_repo_root.to_s
    40
    + coverage_file.all_lines.each do |line|
    41
    + if coverage_file.coverage_for_line(line)
    42
    + line_node = Nokogiri::XML::Node.new "lineToCover", @doc
    43
    + line_node['lineNumber'] = coverage_file.line_number_in_line(line)
    44
    + line_node['covered'] = coverage_file.coverage_for_line(line) == 0 ? "false" : "true"
    45
    + line_node.parent = file_node
    46
    + end
    47
    + end
    48
    + end
    49
    + @doc.to_xml
    50
    + end
    51
    +
    52
    + def create_empty_xml_report
    53
    + builder = Nokogiri::XML::Builder.new do |xml|
    54
    + xml.coverage
    55
    + end
    56
    + @doc = builder.doc
    57
    + end
    58
    +
    59
    + end
    60
    + end
    61
    + end
    @@ -18,7 +18,6 @@ module Slather
    18
    18
      end
    19
    19
     
    20
    20
      def create_line_data
    21
    - all_lines = source_code_lines
    22
    21
      line_data = Hash.new
    23
    22
      all_lines.each { |line| line_data[line_number_in_line(line, self.line_numbers_first)] = line }
    24
    23
      self.line_data = line_data
    @@ -26,14 +25,20 @@ module Slather
    26
    25
      private :create_line_data
    27
    26
     
    28
    27
      def path_on_first_line?
    29
    - path = self.source.split("\n")[0].sub ":", ""
    30
    - !path.include?("|//")
    28
    + !source.lstrip.start_with?("1|")
    31
    29
      end
    32
    30
     
    33
    31
      def source_file_pathname
    34
    32
      @source_file_pathname ||= begin
    35
    33
      if path_on_first_line?
    36
    - path = self.source.split("\n")[0].sub ":", ""
    34
    + end_index = self.source.index(/:?\n/)
    35
    + if end_index != nil
    36
    + end_index -= 1
    37
    + path = self.source[0..end_index]
    38
    + else
    39
    + # Empty file, output just contains path
    40
    + path = self.source.sub ":", ""
    41
    + end
    37
    42
      path &&= Pathname(path)
    38
    43
      else
    39
    44
      # llvm-cov was run with just one matching source file
    @@ -64,7 +69,16 @@ module Slather
    64
    69
      end
    65
    70
     
    66
    71
      def source_code_lines
    67
    - self.source.split("\n")[(path_on_first_line? ? 1 : 0)..-1]
    72
    + lines = self.source.split("\n")[(path_on_first_line? ? 1 : 0)..-1]
    73
    + ignore_error_lines(lines)
    74
    + end
    75
    +
    76
    + def ignore_error_lines(lines, line_numbers_first = self.line_numbers_first)
    77
    + if line_numbers_first
    78
    + lines.reject { |line| line.lstrip.start_with?('|', '--') }
    79
    + else
    80
    + lines
    81
    + end
    68
    82
      end
    69
    83
     
    70
    84
      def source_data
    @@ -72,10 +86,7 @@ module Slather
    72
    86
      end
    73
    87
     
    74
    88
      def all_lines
    75
    - if @all_lines == nil
    76
    - @all_lines = source_code_lines
    77
    - end
    78
    - @all_lines
    89
    + @all_lines ||= source_code_lines
    79
    90
      end
    80
    91
     
    81
    92
      def raw_source
    @@ -94,6 +105,9 @@ module Slather
    94
    105
     
    95
    106
      def line_number_in_line(line, line_numbers_first = self.line_numbers_first)
    96
    107
      if line_numbers_first
    108
    + # Skip regex if the number is the first thing in the line
    109
    + fastpath_number = line.to_i
    110
    + return fastpath_number if fastpath_number != 0
    97
    111
      line =~ /^(\s*)(\d*)/
    98
    112
      group = $2
    99
    113
      else
    @@ -123,7 +137,7 @@ module Slather
    123
    137
      end
    124
    138
     
    125
    139
      def line_coverage_data
    126
    - source_code_lines.map do |line|
    140
    + all_lines.map do |line|
    127
    141
      coverage_for_line(line, self.line_numbers_first)
    128
    142
      end
    129
    143
      end
    @@ -44,7 +44,7 @@ module Slather
    44
    44
      class Project < Xcodeproj::Project
    45
    45
     
    46
    46
      attr_accessor :build_directory, :ignore_list, :ci_service, :coverage_service, :coverage_access_token, :source_directory,
    47
    - :output_directory, :xcodeproj, :show_html, :verbose_mode, :input_format, :scheme, :workspace, :binary_file, :binary_basename, :source_files,
    47
    + :output_directory, :xcodeproj, :show_html, :verbose_mode, :input_format, :scheme, :workspace, :binary_file, :binary_basename, :arch, :source_files,
    48
    48
      :decimals, :llvm_version, :configuration
    49
    49
     
    50
    50
      alias_method :setup_for_coverage, :slather_setup_for_coverage
    @@ -118,24 +118,11 @@ module Slather
    118
    118
     
    119
    119
      def profdata_coverage_files
    120
    120
      coverage_files = []
    121
    - source_files = find_source_files || []
    122
    - line_numbers_first = Gem::Version.new(self.llvm_version) >= Gem::Version.new('8.1.0')
    123
    121
     
    124
    122
      if self.binary_file
    125
    123
      self.binary_file.each do |binary_path|
    126
    - files = profdata_llvm_cov_output(binary_path, source_files).split("\n\n")
    127
    -
    128
    - coverage_files.concat(files.map do |source|
    129
    - coverage_file = coverage_file_class.new(self, source, line_numbers_first)
    130
    - # If a single source file is used, the resulting output does not contain the file name.
    131
    - coverage_file.source_file_pathname = source_files.first if source_files.count == 1
    132
    - !coverage_file.ignored? ? coverage_file : nil
    133
    - end.compact)
    134
    -
    135
    - if !source_files.empty?
    136
    - coverage_file_paths = coverage_files.map { |file| file.source_file_pathname }.to_set
    137
    - source_files.select! { |path| !coverage_file_paths.include?(path) }
    138
    - end
    124
    + pathnames_per_binary = pathnames_per_binary(binary_path)
    125
    + coverage_files.concat(create_coverage_files_for_binary(binary_path, pathnames_per_binary))
    139
    126
      end
    140
    127
      end
    141
    128
     
    @@ -143,6 +130,55 @@ module Slather
    143
    130
      end
    144
    131
      private :profdata_coverage_files
    145
    132
     
    133
    + def pathnames_per_binary(binary_path)
    134
    + coverage_json_string = llvm_cov_export_output(binary_path)
    135
    + coverage_json = JSON.parse(coverage_json_string)
    136
    + coverage_json["data"].reduce([]) do |result, chunk|
    137
    + result.concat(chunk["files"].map do |file|
    138
    + Pathname(file["filename"]).realpath
    139
    + end)
    140
    + end
    141
    + end
    142
    + private :pathnames_per_binary
    143
    +
    144
    + def create_coverage_files_for_binary(binary_path, pathnames_per_binary)
    145
    + coverage_files = []
    146
    +
    147
    + begin
    148
    + coverage_files.concat(create_coverage_files(binary_path, pathnames_per_binary))
    149
    + rescue Errno::E2BIG => e
    150
    + # pathnames_per_binary is too big for the OS to handle so it's split in two halfs which are processed independently
    151
    + if pathnames_per_binary.count > 1
    152
    + left, right = pathnames_per_binary.each_slice( (pathnames_per_binary.size/2.0).round ).to_a
    153
    + coverage_files.concat(create_coverage_files_for_binary(binary_path, left))
    154
    + coverage_files.concat(create_coverage_files_for_binary(binary_path, right))
    155
    + else
    156
    + # pathnames_per_binary contains one element which is too big for the OS to handle.
    157
    + raise e, "#{e}. A path in your project is close to the E2BIG limit. https://github.com/SlatherOrg/slather/pull/414", e.backtrace
    158
    + end
    159
    + end
    160
    +
    161
    + coverage_files
    162
    + end
    163
    + private :create_coverage_files_for_binary
    164
    +
    165
    + def create_coverage_files(binary_path, pathnames)
    166
    + line_numbers_first = Gem::Version.new(self.llvm_version) >= Gem::Version.new('8.1.0')
    167
    + files = create_profdata(binary_path, pathnames)
    168
    + files.map do |source|
    169
    + coverage_file = coverage_file_class.new(self, source, line_numbers_first)
    170
    + # If a single source file is used, the resulting output does not contain the file name.
    171
    + coverage_file.source_file_pathname = pathnames.first if pathnames.count == 1
    172
    + !coverage_file.ignored? ? coverage_file : nil
    173
    + end.compact
    174
    + end
    175
    + private :create_coverage_files
    176
    +
    177
    + def create_profdata(binary_path, pathnames)
    178
    + profdata_llvm_cov_output(binary_path, pathnames).split("\n\n")
    179
    + end
    180
    + private :create_profdata
    181
    +
    146
    182
      def remove_extension(path)
    147
    183
      path.split(".")[0..-2].join(".")
    148
    184
      end
    @@ -155,36 +191,38 @@ module Slather
    155
    191
      end
    156
    192
     
    157
    193
      def profdata_coverage_dir
    158
    - raise StandardError, "The specified build directory (#{self.build_directory}) does not exist" unless File.exists?(self.build_directory)
    159
    - dir = nil
    160
    - if self.scheme
    161
    - dir = Dir[File.join(build_directory,"/**/CodeCoverage/#{self.scheme}")].first
    162
    - else
    163
    - dir = Dir[File.join(build_directory,"/**/#{first_product_name}")].first
    164
    - end
    194
    + @profdata_coverage_dir ||= begin
    195
    + raise StandardError, "The specified build directory (#{self.build_directory}) does not exist" unless File.exists?(self.build_directory)
    196
    + dir = nil
    197
    + if self.scheme
    198
    + dir = Dir[File.join(build_directory,"/**/CodeCoverage/#{self.scheme}")].first
    199
    + else
    200
    + dir = Dir[File.join(build_directory,"/**/#{first_product_name}")].first
    201
    + end
    165
    202
     
    166
    - if dir == nil
    167
    - # Xcode 7.3 moved the location of Coverage.profdata
    168
    - dir = Dir[File.join(build_directory,"/**/CodeCoverage")].first
    169
    - end
    203
    + if dir == nil
    204
    + # Xcode 7.3 moved the location of Coverage.profdata
    205
    + dir = Dir[File.join(build_directory,"/**/CodeCoverage")].first
    206
    + end
    170
    207
     
    171
    - if dir == nil && Slather.xcode_version[0] >= 9
    172
    - # Xcode 9 moved the location of Coverage.profdata
    173
    - coverage_files = Dir[File.join(build_directory, "/**/ProfileData/*/Coverage.profdata")]
    208
    + if dir == nil && Slather.xcode_version[0] >= 9
    209
    + # Xcode 9 moved the location of Coverage.profdata
    210
    + coverage_files = Dir[File.join(build_directory, "/**/ProfileData/*/Coverage.profdata")]
    174
    211
     
    175
    - if coverage_files.count == 0
    176
    - # Look up one directory
    177
    - # The ProfileData directory is next to Intermediates.noindex (in previous versions of Xcode the coverage was inside Intermediates)
    178
    - coverage_files = Dir[File.join(build_directory, "../**/ProfileData/*/Coverage.profdata")]
    179
    - end
    212
    + if coverage_files.count == 0
    213
    + # Look up one directory
    214
    + # The ProfileData directory is next to Intermediates.noindex (in previous versions of Xcode the coverage was inside Intermediates)
    215
    + coverage_files = Dir[File.join(build_directory, "../**/ProfileData/*/Coverage.profdata")]
    216
    + end
    180
    217
     
    181
    - if coverage_files != nil
    182
    - dir = Pathname.new(coverage_files.first).parent()
    218
    + if coverage_files != nil && coverage_files.count != 0
    219
    + dir = Pathname.new(coverage_files.first).parent()
    220
    + end
    183
    221
      end
    184
    - end
    185
    222
     
    186
    - raise StandardError, "No coverage directory found." unless dir != nil
    187
    - dir
    223
    + raise StandardError, "No coverage directory found." unless dir != nil
    224
    + dir
    225
    + end
    188
    226
      end
    189
    227
     
    190
    228
      def profdata_file
    @@ -201,6 +239,30 @@ module Slather
    201
    239
      end
    202
    240
      private :profdata_file
    203
    241
     
    242
    + def unsafe_llvm_cov_export_output(binary_path)
    243
    + profdata_file_arg = profdata_file
    244
    + if profdata_file_arg == nil
    245
    + 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."
    246
    + end
    247
    +
    248
    + if binary_path == nil
    249
    + raise StandardError, "No binary file found."
    250
    + end
    251
    +
    252
    + llvm_cov_args = %W(export -instr-profile #{profdata_file_arg} #{binary_path})
    253
    + if self.arch
    254
    + llvm_cov_args << "--arch" << self.arch
    255
    + end
    256
    + `xcrun llvm-cov #{llvm_cov_args.shelljoin}`
    257
    + end
    258
    + private :unsafe_llvm_cov_export_output
    259
    +
    260
    + def llvm_cov_export_output(binary_path)
    261
    + output = unsafe_llvm_cov_export_output(binary_path)
    262
    + output.valid_encoding? ? output : output.encode!('UTF-8', 'binary', :invalid => :replace, undef: :replace)
    263
    + end
    264
    + private :llvm_cov_export_output
    265
    +
    204
    266
      def unsafe_profdata_llvm_cov_output(binary_path, source_files)
    205
    267
      profdata_file_arg = profdata_file
    206
    268
      if profdata_file_arg == nil
    @@ -212,6 +274,9 @@ module Slather
    212
    274
      end
    213
    275
     
    214
    276
      llvm_cov_args = %W(show -instr-profile #{profdata_file_arg} #{binary_path})
    277
    + if self.arch
    278
    + llvm_cov_args << "--arch" << self.arch
    279
    + end
    215
    280
      `xcrun llvm-cov #{llvm_cov_args.shelljoin} #{source_files.shelljoin}`
    216
    281
      end
    217
    282
      private :unsafe_profdata_llvm_cov_output
    @@ -248,6 +313,7 @@ module Slather
    248
    313
      configure_source_directory
    249
    314
      configure_output_directory
    250
    315
      configure_input_format
    316
    + configure_arch
    251
    317
      configure_binary_file
    252
    318
      configure_decimals
    253
    319
     
    @@ -358,6 +424,8 @@ module Slather
    358
    424
      extend(Slather::CoverageService::HtmlOutput)
    359
    425
      when :json
    360
    426
      extend(Slather::CoverageService::JsonOutput)
    427
    + when :sonarqube_xml
    428
    + extend(Slather::CoverageService::SonarqubeXmlOutput)
    361
    429
      else
    362
    430
      raise ArgumentError, "`#{coverage_service}` is not a valid coverage service. Try `terminal`, `coveralls`, `gutter_json`, `cobertura_xml` or `html`"
    363
    431
      end
    @@ -370,6 +438,10 @@ module Slather
    370
    438
      end
    371
    439
      end
    372
    440
     
    441
    + def configure_arch
    442
    + self.arch ||= self.class.yml["arch"] if self.class.yml["arch"]
    443
    + end
    444
    +
    373
    445
      def decimal_f decimal_arg
    374
    446
      configure_decimals unless decimals
    375
    447
      decimal = "%.#{decimals}f" % decimal_arg
    @@ -435,10 +507,17 @@ module Slather
    435
    507
      # Sort the matches without the file extension to ensure better matches when there are multiple candidates
    436
    508
      # For example, if the binary_basename is Test then we want Test.app to be matched before Test Helper.app
    437
    509
      File.basename(x, File.extname(x)) <=> File.basename(y, File.extname(y))
    438
    - }.reject { |path|
    439
    - path.end_with? ".dSYM"
    440
    - path.end_with? ".swiftmodule"
    441
    - }.first
    510
    + }.find { |path|
    511
    + next if path.end_with? ".dSYM"
    512
    + next if path.end_with? ".swiftmodule"
    513
    +
    514
    + if File.directory? path
    515
    + path = find_binary_file_in_bundle(path)
    516
    + next if path.nil?
    517
    + end
    518
    +
    519
    + matches_arch(path)
    520
    + }
    442
    521
     
    443
    522
      if found_product and File.directory? found_product
    444
    523
      found_binary = find_binary_file_in_bundle(found_product)
    @@ -517,6 +596,16 @@ module Slather
    517
    596
      found_buildable_names.uniq
    518
    597
      end
    519
    598
     
    599
    + def matches_arch(binary_path)
    600
    + if self.arch
    601
    + lipo_output = `lipo -info "#{binary_path}"`
    602
    + archs_in_binary = lipo_output.split(':').last.split(' ')
    603
    + archs_in_binary.include? self.arch
    604
    + else
    605
    + true
    606
    + end
    607
    + end
    608
    +
    520
    609
      def find_source_files
    521
    610
      source_files = load_option_array("source_files")
    522
    611
      return if source_files.nil?