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 +4 -4
- data/lib/fastlane/plugin/stream_actions/actions/build_test_coverage_map.rb +37 -4
- data/lib/fastlane/plugin/stream_actions/actions/select_impacted_tests.rb +4 -3
- data/lib/fastlane/plugin/stream_actions/helper/test_coverage_map_helper.rb +20 -0
- data/lib/fastlane/plugin/stream_actions/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 026ec8f8ee8ff9ab781bd947a48ce316225ca8f933f86e1870e095cd44c1ab18
|
|
4
|
+
data.tar.gz: 83ed49f3b21c9ca683823b56c8e71b067211d01de73a5f9fffcf41b27c03f02f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
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.
|
|
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-
|
|
11
|
+
date: 2026-10-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: xctest_list
|