fastlane-plugin-stream_actions 0.4.15 → 0.4.16

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
2
  SHA256:
3
- metadata.gz: d8d838fe7bdba0809e724b8ceffcdd8a9681c65597a7315085c8eda719afd520
4
- data.tar.gz: e8e093b56d26b398288396cff51df3b063e776a80a9aa82231d9859d5b76317a
3
+ metadata.gz: 026ec8f8ee8ff9ab781bd947a48ce316225ca8f933f86e1870e095cd44c1ab18
4
+ data.tar.gz: 83ed49f3b21c9ca683823b56c8e71b067211d01de73a5f9fffcf41b27c03f02f
5
5
  SHA512:
6
- metadata.gz: abcc77e118f5c4c023c099544a850f0e8f2989d83510d15e9bd086b0f1a9fe00f6ea07ef753ca3cf382508032f5a26b6877ca06d3dbfb040776ab0c1a6df16f1
7
- data.tar.gz: 3d7c1c83fab37d3096946d1064d30e82a733995dd7cca21813f568de18d64f61dca101241a75ca1cd8f93a0f01f308257406fead0f15ff7e62f1f47a12c09839
6
+ metadata.gz: 3c73fcd00f53e169c3708a7f0205528175806fc739385f99a1ed3a5ccf0c90e7b25f218f556bced3bf3c54c59f8cf887e7200e492249f109b29f6e312d72c98f
7
+ data.tar.gz: c0bd2912bd7414870a310a4b20236b548adb0a35f23d05efa7e731052087cfe2549583d5c437ca70eff6346938a4248596cc9cd62e52ab49b24eb1e41128af1b
@@ -9,7 +9,14 @@ module Fastlane
9
9
  class BuildTestCoverageMapAction < Action
10
10
  def self.run(params)
11
11
  source_root = File.expand_path(params[:source_root] || Dir.pwd)
12
- sources, tests = params[:class_dirs] ? jacoco_coverage(params, source_root) : llvm_coverage(params, source_root)
12
+ sources, tests =
13
+ if params[:class_dirs]
14
+ jacoco_coverage(params, source_root)
15
+ elsif params[:binaries]
16
+ llvm_coverage(params, source_root)
17
+ else
18
+ lcov_coverage(params, source_root)
19
+ end
13
20
 
14
21
  carry_over_failed_tests(tests, sources, params) unless params[:failed_tests].to_a.empty?
15
22
  drop_untracked(tests, sources, source_root)
@@ -31,7 +38,6 @@ module Fastlane
31
38
  test_dirs = Dir.glob(File.join(params[:coverage_dir], '*', '*')).select { |dir| File.directory?(dir) }
32
39
  UI.user_error!("No per-test coverage found in '#{params[:coverage_dir]}'") if test_dirs.empty?
33
40
 
34
- UI.user_error!('Pass the instrumented binaries or the class_dirs') if params[:binaries].to_a.empty?
35
41
  binaries = params[:binaries].select { |binary| File.exist?(binary) }
36
42
  UI.user_error!('None of the instrumented binaries exist') if binaries.empty?
37
43
 
@@ -88,6 +94,24 @@ module Fastlane
88
94
  [source_of_class.values.flatten.uniq, tests]
89
95
  end
90
96
 
97
+ # Flutter: one folder of lcov files per test, `<coverage_dir>/<TestFile>/<test description>/`, e.g. from
98
+ # `flutter test --coverage --coverage-path <dir>/lcov.info --name <test>`. lcov lists every library the app loaded,
99
+ # so a file counts as executed when one of its lines has hits.
100
+ def self.lcov_coverage(params, source_root)
101
+ reports = Helper::TestCoverageMapHelper.lcov_reports(params[:coverage_dir])
102
+ UI.user_error!("No per-test coverage found in '#{params[:coverage_dir]}'") if reports.empty?
103
+
104
+ UI.message("Building the coverage map of #{reports.size} tests ⌛️")
105
+ base_dir = File.expand_path(params[:lcov_base_dir] || source_root)
106
+ sources = []
107
+ tests = reports.transform_values do |files|
108
+ files = files.map { |file, executed| [relative_path(File.expand_path(file, base_dir), source_root), executed] }.reject { |file, _| file.nil? }
109
+ sources |= files.map(&:first)
110
+ files.select(&:last).map(&:first).uniq
111
+ end
112
+ [sources, tests]
113
+ end
114
+
91
115
  # Class name => `<package path>/<source file name>`, from an XML report of the class files without execution data.
92
116
  # Classes without code (interfaces, annotations, constants) are empty `<class/>` elements and stay out, like
93
117
  # declaration-only files in llvm-cov. One report per folder, as JaCoCo fails on a class name that two folders
@@ -234,7 +258,7 @@ module Fastlane
234
258
  #####################################################
235
259
 
236
260
  def self.description
237
- 'Builds a map of which source files every test executes, from per-test LLVM or JaCoCo coverage'
261
+ 'Builds a map of which source files every test executes, from per-test LLVM, JaCoCo or lcov coverage'
238
262
  end
239
263
 
240
264
  def self.details
@@ -248,6 +272,9 @@ module Fastlane
248
272
  `<package>.<TestClass>#<testMethod>.ec`, which the test orchestrator writes with `-e coverage true
249
273
  -e coverageFilePath <dir>/`. A source file counts as executed when one of its classes ran a probe.
250
274
 
275
+ Flutter: pass neither, and a `coverage_dir` with one folder of lcov `.info` files per test, `<TestFile>/<test>/`,
276
+ e.g. from `flutter test --coverage --name <test>`. A source file counts as executed when one of its lines has hits.
277
+
251
278
  The resulting JSON map feeds `select_impacted_tests`.
252
279
  DETAILS
253
280
  end
@@ -260,7 +287,8 @@ module Fastlane
260
287
  [
261
288
  FastlaneCore::ConfigItem.new(
262
289
  key: :coverage_dir,
263
- description: 'Folder with per-test profiles, laid out as <TestClass>/<testMethod>/*.profraw',
290
+ description: 'Folder with per-test coverage: <TestClass>/<testMethod>/*.profraw (iOS), *.ec files (Android) ' \
291
+ 'or <TestFile>/<test>/*.info (Flutter)',
264
292
  verify_block: proc { |path| UI.user_error!("Coverage folder '#{path}' does not exist") unless File.directory?(path) }
265
293
  ),
266
294
  FastlaneCore::ConfigItem.new(
@@ -280,6 +308,11 @@ module Fastlane
280
308
  description: 'Android: path to the JaCoCo CLI jar (org.jacoco.cli nodeps). Downloaded when not set',
281
309
  optional: true
282
310
  ),
311
+ FastlaneCore::ConfigItem.new(
312
+ key: :lcov_base_dir,
313
+ description: 'Flutter: folder that relative lcov SF paths start from (the package that ran the tests), source_root by default',
314
+ optional: true
315
+ ),
283
316
  FastlaneCore::ConfigItem.new(
284
317
  key: :source_root,
285
318
  description: 'Repository root, the current directory by default (fastlane runs actions from the repository root). ' \
@@ -6,9 +6,10 @@ module Fastlane
6
6
  module Actions
7
7
  class SelectImpactedTestsAction < Action
8
8
  CODE_EXTENSIONS = %w[.swift .m .mm .h .kt .java .dart .js .jsx .ts .tsx].freeze
9
- # Kotlin's `enum class` and `fun interface` name the type after the second keyword.
10
- MODIFIERS = '(?:(?:public|open|internal|fileprivate|private|protected|final|indirect|nonisolated|data|sealed|abstract|annotation|value|inline|inner|enum(?=\\s+class\\b)|fun(?=\\s+interface\\b)|export|default)\\s+)*'
11
- DECLARATION = /^\s*(?:@\w+(?:\([^)\n]*\))?\s+)*#{MODIFIERS}(class|struct|enum|protocol|actor|typealias|extension|interface|object)\s+([A-Za-z_]\w*)/.freeze
9
+ # Kotlin's `enum class` and `fun interface`, and Dart's `interface class` and `mixin class`, name the type after the second keyword.
10
+ MODIFIERS = '(?:(?:public|open|internal|fileprivate|private|protected|final|indirect|nonisolated|data|sealed|abstract|base|annotation|value|inline|inner|enum(?=\\s+class\\b)|fun(?=\\s+interface\\b)|interface(?=\\s+class\\b)|mixin(?=\\s+class\\b)|export|default)\\s+)*'
11
+ # Dart's `extension type` declares a new type, unlike an `extension`.
12
+ DECLARATION = /^\s*(?:@\w+(?:\([^)\n]*\))?\s+)*#{MODIFIERS}(class|struct|enum|protocol|actor|typealias|typedef|mixin|extension\s+type(?:\s+const)?|extension|interface|object)\s+([A-Za-z_]\w*)/.freeze
12
13
 
13
14
  def self.run(params)
14
15
  source_root = File.expand_path(params[:source_root] || Dir.pwd)
@@ -58,6 +58,26 @@ module Fastlane
58
58
  end
59
59
  end
60
60
 
61
+ # `TestFile/test` => [[source file, executed?], ...] of the lcov reports in `<coverage_dir>/<TestFile>/<test>/`.
62
+ def self.lcov_reports(coverage_dir)
63
+ Dir.glob(File.join(coverage_dir, '*', '*', '*.info'))
64
+ .group_by { |report| File.dirname(report).split('/').last(2).join('/') }
65
+ .transform_values { |reports| reports.flat_map { |report| lcov_files(report) } }
66
+ end
67
+
68
+ # [[source file, executed?], ...] of an lcov report, in the order it lists them.
69
+ def self.lcov_files(report)
70
+ files = []
71
+ File.foreach(report) do |line|
72
+ if line.start_with?('SF:')
73
+ files << [line[3..-1].strip, false]
74
+ elsif line.start_with?('DA:') && !files.empty? && line.split(',')[1].to_i.positive?
75
+ files.last[1] = true
76
+ end
77
+ end
78
+ files
79
+ end
80
+
61
81
  def self.read_map(path)
62
82
  map = JSON.parse(File.read(path))
63
83
  UI.user_error!("Unsupported coverage map version #{map['version']}") unless map['version'] == MAP_VERSION
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module StreamActions
3
- VERSION = '0.4.15'
3
+ VERSION = '0.4.16'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-stream_actions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.15
4
+ version: 0.4.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - GetStream
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-20 00:00:00.000000000 Z
11
+ date: 2026-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: xctest_list