test_file_finder 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 753b9d59524b70621af97db8e7167594fb4fa58e50cd0e48883791ad99f41ddf
4
- data.tar.gz: 2ae423a1fd377e425372eed3a362c234f715ef6626c5a67e475021a149a08809
3
+ metadata.gz: 42ce8ef84263c84c281c4e0002f011ce595841daeca4d5da6689a7a34edca1d7
4
+ data.tar.gz: 755701aacdd1fd4af7810366daf3e3c8a10d75a6bea3b749e9f2c1d375b78f76
5
5
  SHA512:
6
- metadata.gz: 3b668ecff7ea38f8dae220e69b3b45e7a35287cd93662e2f77c98342a118c41dacad758f48c450950c7a346f0bd6b3f0e71af261faa132dc0e2d2714840be9bc
7
- data.tar.gz: 27e0652756e2c9b59f31a1bda9f8a630ee986aa099f8a84697df65400e2fc4f3308d079e7941ad75380f2fb4d2bb0bfecb331b21ce3d9355858e0eb1c10391bc
6
+ metadata.gz: 600e982684b54d6b4c35fbdd613df7c6cb595dac85acddc3dc8cef300b882ee100565fa2eac362dfde1cc8cdca645a6c2dd5347f97411f7d06bb749e0520a946
7
+ data.tar.gz: 510046d569019470b0fbcf6ec24e007de694b3d6611a78fe58ff979db81208c66e7fe68ae0dfe071f78334a57f3ea68756c337f726bedd59722bb038000bc6b0
data/.gitlab-ci.yml CHANGED
@@ -6,9 +6,10 @@ stages:
6
6
  variables:
7
7
  BUNDLE_FROZEN: "true"
8
8
 
9
- rspec:
9
+ .default-test-job:
10
+ image: "ruby:${RUBY_VERSION}"
10
11
  stage: test
11
- image: "ruby:$RUBY_VERSION"
12
+ needs: []
12
13
  cache:
13
14
  paths:
14
15
  - vendor/ruby
@@ -17,18 +18,25 @@ rspec:
17
18
  - gem install bundler -v 2.4.13
18
19
  - bundle config set --local path "vendor/ruby/$RUBY_VERSION"
19
20
  - bundle install
20
- script:
21
- - bundle exec rspec
22
- - bundle exec rspec feature
23
21
  parallel:
24
22
  matrix:
25
23
  - RUBY_VERSION:
26
- - "2.7"
27
24
  - "3.0"
28
25
  - "3.1"
29
26
  - "3.2"
30
27
 
28
+ rspec:
29
+ extends: .default-test-job
30
+ script:
31
+ - bundle exec rspec
32
+ - bundle exec rspec feature
33
+
34
+ rubocop:
35
+ extends: .default-test-job
36
+ script:
37
+ - bundle exec rubocop --extra-details .
38
+
31
39
  include:
32
40
  - template: 'Workflows/MergeRequest-Pipelines.gitlab-ci.yml'
33
- - project: 'gitlab-org/quality/pipeline-common'
34
- file: '/ci/gem-release.yml'
41
+ - component: gitlab.com/gitlab-org/components/gem-release/gem-release@~latest
42
+ - component: gitlab.com/gitlab-org/components/danger-review/danger-review@~latest
data/.rubocop.yml ADDED
@@ -0,0 +1,21 @@
1
+ inherit_gem:
2
+ gitlab-styles:
3
+ - rubocop-default.yml
4
+
5
+ require:
6
+ - rubocop-rake
7
+
8
+ AllCops:
9
+ TargetRubyVersion: 3.0
10
+ NewCops: disable
11
+
12
+ # Disables Rails cops.
13
+ Rails:
14
+ Enabled: false
15
+
16
+ # Disables Rails-related cops
17
+ CodeReuse/ActiveRecord:
18
+ Enabled: false
19
+
20
+ RSpec/MultipleMemoizedHelpers:
21
+ Max: 20
data/Dangerfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gitlab-dangerfiles'
4
+
5
+ Gitlab::Dangerfiles.for_project(self, &:import_defaults)
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in test_file_finder.gemspec
data/Gemfile.lock CHANGED
@@ -1,25 +1,114 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- test_file_finder (0.2.1)
4
+ test_file_finder (0.3.0)
5
5
  faraday (>= 1.0, < 3.0, != 2.0.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
+ activesupport (7.1.3.2)
11
+ base64
12
+ bigdecimal
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ connection_pool (>= 2.2.5)
15
+ drb
16
+ i18n (>= 1.6, < 2)
17
+ minitest (>= 5.1)
18
+ mutex_m
19
+ tzinfo (~> 2.0)
10
20
  addressable (2.8.4)
11
21
  public_suffix (>= 2.0.2, < 6.0)
22
+ ast (2.4.2)
23
+ base64 (0.2.0)
24
+ bigdecimal (3.1.7)
12
25
  byebug (11.1.3)
26
+ claide (1.1.0)
27
+ claide-plugins (0.9.2)
28
+ cork
29
+ nap
30
+ open4 (~> 1.3)
31
+ colored2 (3.1.2)
32
+ concurrent-ruby (1.2.3)
33
+ connection_pool (2.4.1)
34
+ cork (0.3.0)
35
+ colored2 (~> 3.1)
13
36
  crack (0.4.5)
14
37
  rexml
38
+ danger (9.4.1)
39
+ claide (~> 1.0)
40
+ claide-plugins (>= 0.9.2)
41
+ colored2 (~> 3.1)
42
+ cork (~> 0.1)
43
+ faraday (>= 0.9.0, < 3.0)
44
+ faraday-http-cache (~> 2.0)
45
+ git (~> 1.13)
46
+ kramdown (~> 2.3)
47
+ kramdown-parser-gfm (~> 1.0)
48
+ no_proxy_fix
49
+ octokit (>= 6.0)
50
+ terminal-table (>= 1, < 4)
51
+ danger-gitlab (8.0.0)
52
+ danger
53
+ gitlab (~> 4.2, >= 4.2.0)
15
54
  diff-lcs (1.5.0)
55
+ drb (2.2.1)
16
56
  faraday (2.7.10)
17
57
  faraday-net_http (>= 2.0, < 3.1)
18
58
  ruby2_keywords (>= 0.0.4)
59
+ faraday-http-cache (2.5.0)
60
+ faraday (>= 0.8)
19
61
  faraday-net_http (3.0.2)
62
+ git (1.18.0)
63
+ addressable (~> 2.8)
64
+ rchardet (~> 1.8)
65
+ gitlab (4.19.0)
66
+ httparty (~> 0.20)
67
+ terminal-table (>= 1.5.1)
68
+ gitlab-dangerfiles (4.6.0)
69
+ danger (>= 9.3.0)
70
+ danger-gitlab (>= 8.0.0)
71
+ rake (~> 13.0)
72
+ gitlab-styles (11.0.0)
73
+ rubocop (~> 1.57.1)
74
+ rubocop-graphql (~> 0.18)
75
+ rubocop-performance (~> 1.15)
76
+ rubocop-rails (~> 2.17)
77
+ rubocop-rspec (~> 2.22)
20
78
  hashdiff (1.0.1)
79
+ httparty (0.21.0)
80
+ mini_mime (>= 1.0.0)
81
+ multi_xml (>= 0.5.2)
82
+ i18n (1.14.4)
83
+ concurrent-ruby (~> 1.0)
84
+ json (2.7.1)
85
+ kramdown (2.4.0)
86
+ rexml
87
+ kramdown-parser-gfm (1.1.0)
88
+ kramdown (~> 2.0)
89
+ language_server-protocol (3.17.0.3)
90
+ lefthook (1.6.5)
91
+ mini_mime (1.1.5)
92
+ minitest (5.22.3)
93
+ multi_xml (0.6.0)
94
+ mutex_m (0.2.0)
95
+ nap (1.1.0)
96
+ no_proxy_fix (0.1.2)
97
+ octokit (8.0.0)
98
+ faraday (>= 1, < 3)
99
+ sawyer (~> 0.9)
100
+ open4 (1.3.4)
101
+ parallel (1.24.0)
102
+ parser (3.3.0.5)
103
+ ast (~> 2.4.1)
104
+ racc
21
105
  public_suffix (5.0.1)
22
- rake (10.5.0)
106
+ racc (1.7.3)
107
+ rack (3.0.8)
108
+ rainbow (3.1.1)
109
+ rake (13.1.0)
110
+ rchardet (1.8.0)
111
+ regexp_parser (2.9.0)
23
112
  rexml (3.2.5)
24
113
  rspec (3.12.0)
25
114
  rspec-core (~> 3.12.0)
@@ -34,7 +123,48 @@ GEM
34
123
  diff-lcs (>= 1.2.0, < 2.0)
35
124
  rspec-support (~> 3.12.0)
36
125
  rspec-support (3.12.0)
126
+ rubocop (1.57.2)
127
+ json (~> 2.3)
128
+ language_server-protocol (>= 3.17.0)
129
+ parallel (~> 1.10)
130
+ parser (>= 3.2.2.4)
131
+ rainbow (>= 2.2.2, < 4.0)
132
+ regexp_parser (>= 1.8, < 3.0)
133
+ rexml (>= 3.2.5, < 4.0)
134
+ rubocop-ast (>= 1.28.1, < 2.0)
135
+ ruby-progressbar (~> 1.7)
136
+ unicode-display_width (>= 2.4.0, < 3.0)
137
+ rubocop-ast (1.31.2)
138
+ parser (>= 3.3.0.4)
139
+ rubocop-capybara (2.20.0)
140
+ rubocop (~> 1.41)
141
+ rubocop-factory_bot (2.25.1)
142
+ rubocop (~> 1.41)
143
+ rubocop-graphql (0.19.0)
144
+ rubocop (>= 0.87, < 2)
145
+ rubocop-performance (1.19.1)
146
+ rubocop (>= 1.7.0, < 2.0)
147
+ rubocop-ast (>= 0.4.0)
148
+ rubocop-rails (2.22.1)
149
+ activesupport (>= 4.2.0)
150
+ rack (>= 1.1)
151
+ rubocop (>= 1.33.0, < 2.0)
152
+ rubocop-rake (0.6.0)
153
+ rubocop (~> 1.0)
154
+ rubocop-rspec (2.27.1)
155
+ rubocop (~> 1.40)
156
+ rubocop-capybara (~> 2.17)
157
+ rubocop-factory_bot (~> 2.22)
158
+ ruby-progressbar (1.13.0)
37
159
  ruby2_keywords (0.0.5)
160
+ sawyer (0.9.2)
161
+ addressable (>= 2.3.5)
162
+ faraday (>= 0.17.3, < 3)
163
+ terminal-table (3.0.2)
164
+ unicode-display_width (>= 1.1.1, < 3)
165
+ tzinfo (2.0.6)
166
+ concurrent-ruby (~> 1.0)
167
+ unicode-display_width (2.5.0)
38
168
  webmock (3.18.1)
39
169
  addressable (>= 2.8.0)
40
170
  crack (>= 0.3.2)
@@ -46,8 +176,12 @@ PLATFORMS
46
176
  DEPENDENCIES
47
177
  bundler (~> 2.1)
48
178
  byebug
49
- rake (~> 10.0)
179
+ gitlab-dangerfiles (~> 4.6.0)
180
+ gitlab-styles (~> 11.0)
181
+ lefthook (~> 1.6)
182
+ rake (~> 13.0)
50
183
  rspec (~> 3.0)
184
+ rubocop-rake (~> 0.6)
51
185
  test_file_finder!
52
186
  webmock (~> 3.18)
53
187
 
data/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011-2024 GitLab B.V.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the “Software”), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md CHANGED
@@ -48,7 +48,9 @@ $ rspec $(tff $(git diff --name-only master..head))
48
48
 
49
49
  `TestFileFinder` can be used with an optional YAML mapping file to specify the mapping from a `source` file to a `test` file. Both, `source` and `test` can be lists to map multiple files.
50
50
 
51
- The mapping file is a yaml file containing to entries to match file patterns to its test files. The patterns may include capturing groups to be used to identify the test file. To refer to a captured value in the test file, use the `%s` placeholder. For example:
51
+ The mapping file is a yaml file containing to entries to match source file patterns to its test files.
52
+
53
+ The source file pattern may be an exact file path or a regular expression. The regular expression may include capturing groups to be used to identify the test file. To refer to a captured value in the test file, use the `%s` placeholder. For example:
52
54
 
53
55
  ```yaml
54
56
  mapping:
@@ -67,6 +69,32 @@ mapping:
67
69
  - 'ee/spec/%s%s_spec.rb'
68
70
  ```
69
71
 
72
+ The patterns may include named captures in test files and referenced by its
73
+ name in source files. For example:
74
+
75
+ ```yaml
76
+ mapping:
77
+ # maps `lib/api/issues.rb` to `spec/requests/api/issues/issues_spec.rb`
78
+ - source: 'lib/api/(?<name>.*)\.rb'
79
+ test: 'spec/requests/api/%{name}/%{name}_spec.rb'
80
+ ```
81
+
82
+ Numbered and named captures cannot be mixed in a single pattern.
83
+
84
+ A test file containing metacharacters like `*`, `{}`, `[]`, or `?` is
85
+ considered a file name pattern and [globbing is used to match](https://rubyapi.org/o/dir#method-c-glob)
86
+ the resulting test files.
87
+
88
+ For example:
89
+
90
+ ```yaml
91
+ mapping:
92
+ # maps `lib/api/issues.rb` to tests following this pattern
93
+ # `spec/requests/api/issues/*_spec.rb`
94
+ - source: 'lib/api/(.*)\.rb'
95
+ test: 'spec/requests/api/%s/*_spec.rb'
96
+ ```
97
+
70
98
  Command line example:
71
99
 
72
100
  ```bash
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5
 
data/exe/tff CHANGED
@@ -1,17 +1,18 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require 'test_file_finder'
3
5
 
4
6
  options = TestFileFinder::OptionParser.parse!(ARGV)
5
7
 
6
8
  TestFileFinder::FileFinder.new(paths: ARGV).tap do |file_finder|
7
- if options.json
8
- file_finder.use TestFileFinder::MappingStrategies::DirectMatching.load_json(options.json)
9
- end
10
- if options.mapping_file
11
- file_finder.use TestFileFinder::MappingStrategies::PatternMatching.load(options.mapping_file)
12
- end
9
+ file_finder.use TestFileFinder::MappingStrategies::DirectMatching.load_json(options.json) if options.json
10
+
11
+ file_finder.use TestFileFinder::MappingStrategies::PatternMatching.load(options.mapping_file) if options.mapping_file
12
+
13
13
  if options.project_path && options.merge_request_iid
14
- file_finder.use TestFileFinder::MappingStrategies::GitlabMergeRequestRspecFailure.new(project_path: options.project_path, merge_request_iid: options.merge_request_iid)
14
+ file_finder.use TestFileFinder::MappingStrategies::GitlabMergeRequestRspecFailure.new(
15
+ project_path: options.project_path, merge_request_iid: options.merge_request_iid)
15
16
  end
16
17
 
17
18
  if file_finder.strategies.empty?
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe 'tff exe' do
2
- subject { `ruby -Ilib ../exe/tff #{options} #{files}` }
4
+ subject(:output) { `ruby -Ilib ../exe/tff #{options} #{files}` }
3
5
 
4
6
  around do |example|
5
7
  Dir.chdir(File.join(__dir__, '../fixtures')) do
@@ -8,11 +10,11 @@ RSpec.describe 'tff exe' do
8
10
  end
9
11
 
10
12
  context 'without any mapping' do
11
- let(:options) {}
13
+ let(:options) { nil }
12
14
  let(:files) { 'app/models/test_file_finder_gem_executable_widget.rb' }
13
15
 
14
16
  it 'prints matching test files using default rails mapping' do
15
- expect(subject).to eq <<~OUTPUT
17
+ expect(output).to eq <<~OUTPUT
16
18
  spec/models/test_file_finder_gem_executable_widget_spec.rb
17
19
  OUTPUT
18
20
  end
@@ -23,11 +25,11 @@ RSpec.describe 'tff exe' do
23
25
 
24
26
  context 'with multiple sources' do
25
27
  ['db/schema.rb', 'db/migrate/001_init.rb'].each do |file|
26
- context "for file #{file}" do
28
+ context "with file #{file}" do
27
29
  let(:files) { file }
28
30
 
29
31
  it 'prints matching test files using given yaml mapping' do
30
- expect(subject).to eq <<~OUTPUT
32
+ expect(output).to eq <<~OUTPUT
31
33
  spec/db/schema_spec.rb
32
34
  OUTPUT
33
35
  end
@@ -39,7 +41,7 @@ RSpec.describe 'tff exe' do
39
41
  let(:files) { 'spec/models/project_spec.rb' }
40
42
 
41
43
  it 'prints matching test files using given yaml mapping' do
42
- expect(subject).to eq <<~OUTPUT
44
+ expect(output).to eq <<~OUTPUT
43
45
  spec/models/project_spec.rb
44
46
  spec/smoke_spec.rb
45
47
  OUTPUT
@@ -48,11 +50,11 @@ RSpec.describe 'tff exe' do
48
50
 
49
51
  context 'with multiple sources and tests' do
50
52
  ['views/main.html.haml', 'assets/application.css'].each do |file|
51
- context "for file #{file}" do
53
+ context "with file #{file}" do
52
54
  let(:files) { file }
53
55
 
54
56
  it 'prints matching test files using given yaml mapping' do
55
- expect(subject).to eq <<~OUTPUT
57
+ expect(output).to eq <<~OUTPUT
56
58
  spec/views/main_spec.rb
57
59
  features/smoke.feature
58
60
  OUTPUT
@@ -60,6 +62,16 @@ RSpec.describe 'tff exe' do
60
62
  end
61
63
  end
62
64
  end
65
+
66
+ context 'with named captures' do
67
+ let(:files) { 'lib/api/issues.rb' }
68
+
69
+ it 'prints matching test files using given yaml mapping' do
70
+ expect(output).to eq <<~OUTPUT
71
+ spec/requests/api/issues/issues_spec.rb
72
+ OUTPUT
73
+ end
74
+ end
63
75
  end
64
76
 
65
77
  context 'with a yaml mapping and json mapping' do
@@ -67,7 +79,7 @@ RSpec.describe 'tff exe' do
67
79
  let(:files) { 'db/schema.rb app/models/project.rb ' }
68
80
 
69
81
  it 'prints matching test files using both yaml and json mappings' do
70
- expect(subject).to eq <<~OUTPUT
82
+ expect(output).to eq <<~OUTPUT
71
83
  spec/models/project_spec.rb
72
84
  spec/controllers/projects_controller_spec.rb
73
85
  spec/db/schema_spec.rb
@@ -80,7 +92,7 @@ RSpec.describe 'tff exe' do
80
92
  let(:files) { 'app/models/test_file_finder_gem_executable_widget.rb app/models/project.rb' }
81
93
 
82
94
  it 'prints matching test files using json mapping' do
83
- expect(subject).to eq <<~OUTPUT
95
+ expect(output).to eq <<~OUTPUT
84
96
  spec/models/project_spec.rb
85
97
  spec/controllers/projects_controller_spec.rb
86
98
  OUTPUT
@@ -0,0 +1 @@
1
+ # frozen_string_literal: true
data/fixtures/mapping.yml CHANGED
@@ -7,7 +7,7 @@ mapping:
7
7
  - 'db/schema\.rb'
8
8
  - 'db/migrate/(.+)\.rb'
9
9
  test: 'spec/db/schema_spec.rb'
10
- - source: 'spec/(.+)_spec.rb'
10
+ - source: 'spec/(.+)_spec\.rb'
11
11
  test:
12
12
  - 'spec/%s_spec.rb'
13
13
  - 'spec/smoke_spec.rb'
@@ -17,3 +17,7 @@ mapping:
17
17
  test:
18
18
  - 'spec/views/main_spec.rb'
19
19
  - 'features/smoke.feature'
20
+ - source: '(?<ee>ee/)?lib/api/(?<name>.*)\.rb'
21
+ test:
22
+ - '%{ee}spec/requests/api/%{name}_spec.rb'
23
+ - '%{ee}spec/requests/api/%{name}/%{name}_spec.rb'
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  test projects controller spec
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  test schema spec
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  test project spec
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  existing specs
@@ -0,0 +1 @@
1
+ # frozen_string_literal: true
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  smoke specs
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  main page specs
data/lefthook.yml ADDED
@@ -0,0 +1,18 @@
1
+ # Lefthook configuration. For more information, see:
2
+ # https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md
3
+
4
+ pre-push:
5
+ parallel: true
6
+ commands:
7
+ # Run all tests (warn if there are any missing tools required for tests).
8
+ # Run undercover, looking for missing test coverage
9
+ rspec:
10
+ files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
11
+ run: bundle exec rspec -f progress
12
+ glob: '*.rb'
13
+
14
+ # Run ruby linting
15
+ rubocop:
16
+ files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
17
+ glob: '*.{rb,rake}'
18
+ run: bundle exec rubocop --parallel --force-exclusion {files}
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'set'
2
4
 
3
5
  module TestFileFinder
@@ -22,7 +24,18 @@ module TestFileFinder
22
24
  attr_reader :paths
23
25
 
24
26
  def search
25
- file_path_guesses.select { |path| File.exist?(path) }
27
+ file_name_patterns, plain_guesses = file_path_guesses.partition(&file_name_pattern?)
28
+
29
+ file_name_patterns.flat_map { |pattern| Dir.glob(pattern) } +
30
+ plain_guesses.select { |path| File.exist?(path) }
31
+ end
32
+
33
+ # Returns true if a test file name contains metacharacter like *, {, [, ?
34
+ # which indicates a file name pattern.
35
+ #
36
+ # See https://rubyapi.org/o/dir#method-c-glob
37
+ def file_name_pattern?
38
+ proc { |guess| guess.match?(/[*{\[?]/) }
26
39
  end
27
40
 
28
41
  def file_path_guesses
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'set'
2
4
  require 'yaml'
3
5
  require 'test_file_finder/mapping_strategies'
@@ -1,31 +1,58 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
 
3
5
  module TestFileFinder
4
6
  module MappingStrategies
5
7
  class DirectMatching
6
- JSON_ERROR_MESSAGE = 'json file should contain a json object, with array of test files as the values'.freeze
8
+ JSON_ERROR_MESSAGE = 'json file should contain a json object, with array of test files as the values'
9
+
10
+ attr_reader :map, :limit_percentage, :limit_min
7
11
 
8
- def self.load_json(json_file)
12
+ def self.load_json(json_file, **kwargs)
9
13
  map = JSON.parse(File.read(json_file))
10
14
 
11
- validate(map)
15
+ validate_map(map)
16
+ validate_params(**kwargs)
12
17
 
13
- new.tap do |strategy|
14
- strategy.map = map
15
- end
18
+ new(map, **kwargs)
16
19
  end
17
20
 
18
- def self.validate(map)
19
- return if map.is_a?(Hash) && map.values.all? { |value| value.is_a?(Array) }
21
+ def self.validate_map(map)
22
+ return if map.is_a?(Hash) && map.values.all?(Array)
20
23
 
21
24
  raise InvalidMappingFileError, JSON_ERROR_MESSAGE
22
25
  end
23
26
 
24
- attr_accessor :map
27
+ def self.validate_params(limit_percentage: nil, limit_min: nil)
28
+ return if limit_percentage.nil?
29
+
30
+ limit_percentage_valid = limit_percentage.is_a?(Integer) && (1..100).cover?(limit_percentage)
31
+ raise "Invalid value for limit_percentage: should be an integer between 1 and 100" unless limit_percentage_valid
32
+
33
+ limit_min_valid = limit_min.nil? || (limit_min.is_a?(Integer) && limit_min.positive?)
34
+ return if limit_min_valid
35
+
36
+ raise "Invalid value for limit_min: should be an integer strictly greater than zero"
37
+ end
38
+
39
+ def initialize(map, limit_percentage: nil, limit_min: nil)
40
+ @map = map
41
+ @limit_percentage = limit_percentage
42
+ @limit_min = limit_min
43
+ end
25
44
 
26
45
  def match(files)
27
46
  Array(files).inject(Set.new) do |result, file|
28
47
  test_files = @map.fetch(file, [])
48
+
49
+ if limit_percentage
50
+ sample_size = (limit_percentage * test_files.count).round
51
+ sample_size = limit_min if limit_min && sample_size <= limit_min
52
+
53
+ test_files = test_files.sample(sample_size)
54
+ end
55
+
29
56
  result.merge(test_files)
30
57
  end.to_a
31
58
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'faraday'
2
4
  require 'json'
3
5
  require 'set'
@@ -15,7 +17,7 @@ module TestFileFinder
15
17
  #
16
18
  # It returns file names of rspec failures in the pipeline.
17
19
  class GitlabMergeRequestRspecFailure
18
- TEST_REPORTS_URL_TEMPLATE = 'https://gitlab.com/%{project_path}/-/merge_requests/%{merge_request_iid}/test_reports.json'.freeze
20
+ TEST_REPORTS_URL_TEMPLATE = 'https://gitlab.com/%{project_path}/-/merge_requests/%{merge_request_iid}/test_reports.json'
19
21
 
20
22
  attr_reader :project_path, :merge_request_iid
21
23
 
@@ -53,21 +55,22 @@ module TestFileFinder
53
55
  def rspec_file(failure)
54
56
  file = failure['file'].sub('./', '')
55
57
 
56
- if file.end_with?('spec.rb')
57
- yield file
58
- end
58
+ yield(file) if file.end_with?('spec.rb')
59
59
  end
60
60
 
61
61
  def merge_request_test_reports
62
- test_reports_url = format(TEST_REPORTS_URL_TEMPLATE, { project_path: project_path, merge_request_iid: merge_request_iid })
62
+ test_reports_url = format(TEST_REPORTS_URL_TEMPLATE,
63
+ { project_path: project_path, merge_request_iid: merge_request_iid })
63
64
 
64
- response = Faraday.get(test_reports_url, {}, {'Accept' => 'application/json'})
65
+ response = Faraday.get(test_reports_url, {}, { 'Accept' => 'application/json' })
65
66
 
66
- if response.status == 204
67
- raise TestFileFinder::TestReportError, "Test report for merge request #{merge_request_iid} is not ready, please try again later."
68
- elsif response.status == 400
67
+ case response.status
68
+ when 204
69
+ raise TestFileFinder::TestReportError,
70
+ "Test report for merge request #{merge_request_iid} is not ready, please try again later."
71
+ when 400
69
72
  raise TestFileFinder::TestReportError, "The project #{project_path} does not have test reports configured."
70
- elsif response.status == 500
73
+ when 500
71
74
  raise TestFileFinder::TestReportError, 'Unable to retrieve test report, please try again later.'
72
75
  end
73
76
 
@@ -1,46 +1,53 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TestFileFinder
2
4
  module MappingStrategies
3
5
  class PatternMatching
4
6
  attr_reader :pattern_matchers
5
7
 
6
8
  def self.load(mapping_file)
7
- content = File.read(mapping_file)
8
- maps = YAML.load(content)['mapping']
9
+ maps = YAML.safe_load_file(mapping_file)['mapping']
9
10
 
10
11
  validate(maps)
11
12
 
12
- new do |mapping|
13
- maps.each do |map|
14
- Array(map['source']).each do |source|
15
- Array(map['test']).each do |test|
16
- mapping.relate(source, test)
17
- end
18
- end
19
- end
20
- end
13
+ new(maps)
21
14
  end
22
15
 
23
- def self.validate(maps)
24
- raise InvalidMappingFileError, 'missing `mapping` in test mapping file' if maps.nil?
25
- raise InvalidMappingFileError, 'missing `source` or `test` in test mapping file' if maps.any? { |map| incomplete?(map) }
16
+ def self.default_rails_mapping
17
+ mapping = new
18
+ mapping.relate(%r{^app/(.+)\.rb$}, 'spec/%s_spec.rb')
19
+ mapping.relate(%r{^lib/(.+)\.rb$}, 'spec/lib/%s_spec.rb')
20
+ mapping.relate(%r{^spec/(.+)_spec\.rb$}, 'spec/%s_spec.rb')
21
+ mapping
26
22
  end
27
23
 
28
- def self.incomplete?(map)
29
- map['source'].nil? || map['test'].nil?
24
+ def self.validate(maps = nil)
25
+ raise InvalidMappingFileError, 'missing `mapping` in test mapping file' if maps.nil?
26
+
27
+ return if maps.all? { |map| complete?(map) }
28
+
29
+ raise InvalidMappingFileError, 'missing `source` or `test` in test mapping file'
30
30
  end
31
31
 
32
- def self.default_rails_mapping
33
- new do |mapping|
34
- mapping.relate(%r{^app/(.+)\.rb$}, 'spec/%s_spec.rb')
35
- mapping.relate(%r{^lib/(.+)\.rb$}, 'spec/lib/%s_spec.rb')
36
- mapping.relate(%r{^spec/(.+)_spec.rb$}, 'spec/%s_spec.rb')
37
- end
32
+ def self.complete?(map)
33
+ !map['source'].nil? && !map['test'].nil?
38
34
  end
39
35
 
40
- def initialize
36
+ def initialize(maps = nil)
41
37
  @pattern_matchers = []
42
38
 
43
- yield self if block_given?
39
+ # Useful for the .default_rails_mapping class method
40
+ #
41
+ # We don't have a file to use, but we still need an instance to be returned
42
+ return unless maps
43
+
44
+ maps.each do |map|
45
+ Array(map['source']).each do |source|
46
+ Array(map['test']).each do |test|
47
+ relate(source, test)
48
+ end
49
+ end
50
+ end
44
51
  end
45
52
 
46
53
  def relate(source, test)
@@ -57,13 +64,29 @@ module TestFileFinder
57
64
  private
58
65
 
59
66
  def pattern_matcher_for(source, test)
60
- regexp = %r{^#{source}$}
67
+ regexp = /^#{source}$/
68
+
69
+ if regexp.named_captures.any?
70
+ pattern_matcher_with_named_captures_for(regexp, test)
71
+ else
72
+ pattern_matcher_with_numbered_captures_for(regexp, test)
73
+ end
74
+ end
61
75
 
76
+ def pattern_matcher_with_named_captures_for(regexp, test)
62
77
  proc do |files|
63
78
  Array(files).flat_map do |file|
64
- if (match = regexp.match(file))
65
- test % match.captures
66
- end
79
+ match = regexp.match(file)
80
+ format(test, match.named_captures.transform_keys(&:to_sym)) if match
81
+ end.compact
82
+ end
83
+ end
84
+
85
+ def pattern_matcher_with_numbered_captures_for(regexp, test)
86
+ proc do |files|
87
+ Array(files).flat_map do |file|
88
+ match = regexp.match(file)
89
+ format(test, *match.captures) if match
67
90
  end.compact
68
91
  end
69
92
  end
@@ -1,9 +1,9 @@
1
- require 'test_file_finder/mapping_strategies/direct_matching'
2
- require 'test_file_finder/mapping_strategies/gitlab_merge_request_rspec_failure'
3
- require 'test_file_finder/mapping_strategies/pattern_matching'
1
+ # frozen_string_literal: true
4
2
 
5
3
  module TestFileFinder
6
4
  module MappingStrategies
7
-
5
+ autoload :DirectMatching, 'test_file_finder/mapping_strategies/direct_matching'
6
+ autoload :PatternMatching, 'test_file_finder/mapping_strategies/pattern_matching'
7
+ autoload :GitlabMergeRequestRspecFailure, 'test_file_finder/mapping_strategies/gitlab_merge_request_rspec_failure'
8
8
  end
9
9
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
4
 
3
5
  module TestFileFinder
@@ -21,7 +23,8 @@ module TestFileFinder
21
23
  options.json = json
22
24
  end
23
25
 
24
- opts.on('--project-path PROJECT_PATH', String, 'Path of GitLab project, e.g `gitlab-org/gitlab`') do |project_path|
26
+ opts.on('--project-path PROJECT_PATH', String,
27
+ 'Path of GitLab project, e.g `gitlab-org/gitlab`') do |project_path|
25
28
  options.project_path = project_path
26
29
  end
27
30
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TestFileFinder
2
- VERSION = '0.2.1'
4
+ VERSION = '0.3.0'
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_file_finder/file_finder'
2
4
  require 'test_file_finder/mapping'
3
5
  require 'test_file_finder/mapping_strategies'
@@ -5,7 +7,7 @@ require 'test_file_finder/option_parser'
5
7
  require 'test_file_finder/version'
6
8
 
7
9
  module TestFileFinder
8
- class Error < StandardError; end
10
+ Error = Class.new(StandardError)
9
11
 
10
12
  InvalidMappingFileError = Class.new(Error)
11
13
  TestReportError = Class.new(Error)
@@ -1,32 +1,34 @@
1
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
  require 'test_file_finder/version'
4
6
 
5
7
  Gem::Specification.new do |spec|
6
- spec.name = 'test_file_finder'
7
- spec.version = TestFileFinder::VERSION
8
- spec.authors = ['GitLab']
9
- spec.email = ['rubygems-committee@gitlab.com']
8
+ spec.name = 'test_file_finder'
9
+ spec.version = TestFileFinder::VERSION
10
+ spec.licenses = ['MIT']
11
+ spec.authors = ['GitLab']
12
+ spec.email = ['rubygems-committee@gitlab.com']
13
+ spec.required_ruby_version = '>= 3.0'
10
14
 
11
- spec.summary = %q{Guesses spec file paths given input file paths.}
12
- spec.description = %q{A command-line tool for guessing which spec files are relavant to a passed-in set of file paths.}
15
+ spec.summary = %q(Guesses spec file paths given input file paths.)
16
+ spec.description = %q(Command-line tool for guessing which spec files are relevant to a set of input file paths.)
13
17
  spec.homepage = 'https://gitlab.com/gitlab-org/ruby/gems/test_file_finder'
14
18
 
15
19
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
16
20
  # to allow pushing to a single host or delete this section to allow pushing to any host.
17
- if spec.respond_to?(:metadata)
18
- spec.metadata['allowed_push_host'] = 'https://rubygems.org'
19
-
20
- spec.metadata['homepage_uri'] = spec.homepage
21
- spec.metadata['source_code_uri'] = 'https://gitlab.com/gitlab-org/ruby/gems/test_file_finder'
22
- spec.metadata['changelog_uri'] = 'https://gitlab.com/gitlab-org/ruby/gems/test_file_finder/-/blob/master/CHANGELOG.md'
23
- else
24
- raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
25
- end
21
+ raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.' unless spec.respond_to?(:metadata)
22
+
23
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
24
+
25
+ spec.metadata['homepage_uri'] = spec.homepage
26
+ spec.metadata['source_code_uri'] = 'https://gitlab.com/gitlab-org/ruby/gems/test_file_finder'
27
+ spec.metadata['changelog_uri'] = 'https://gitlab.com/gitlab-org/ruby/gems/test_file_finder/-/blob/master/CHANGELOG.md'
26
28
 
27
29
  # Specify which files should be added to the gem when it is released.
28
30
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
29
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
31
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
30
32
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
31
33
  end
32
34
  spec.bindir = 'exe'
@@ -37,7 +39,11 @@ Gem::Specification.new do |spec|
37
39
 
38
40
  spec.add_development_dependency 'bundler', '~> 2.1'
39
41
  spec.add_development_dependency 'byebug'
40
- spec.add_development_dependency 'rake', '~> 10.0'
42
+ spec.add_development_dependency 'gitlab-dangerfiles', '~> 4.6.0'
43
+ spec.add_development_dependency 'gitlab-styles', '~> 11.0'
44
+ spec.add_development_dependency 'lefthook', '~> 1.6'
45
+ spec.add_development_dependency 'rake', '~> 13.0'
41
46
  spec.add_development_dependency 'rspec', '~> 3.0'
47
+ spec.add_development_dependency 'rubocop-rake', '~> 0.6'
42
48
  spec.add_development_dependency 'webmock', '~> 3.18'
43
49
  end
data/tests.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  mapping:
2
- - source: lib/(.+).rb
2
+ - source: lib/(.+)\.rb
3
3
  test: spec/lib/%s_spec.rb
4
- - source: spec/spec_helper.rb
4
+ - source: spec/spec_helper\.rb
5
5
  test: spec
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: test_file_finder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitLab
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-10-30 00:00:00.000000000 Z
11
+ date: 2024-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -64,20 +64,62 @@ dependencies:
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
66
  version: '0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: gitlab-dangerfiles
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: 4.6.0
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: 4.6.0
81
+ - !ruby/object:Gem::Dependency
82
+ name: gitlab-styles
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '11.0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '11.0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: lefthook
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '1.6'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '1.6'
67
109
  - !ruby/object:Gem::Dependency
68
110
  name: rake
69
111
  requirement: !ruby/object:Gem::Requirement
70
112
  requirements:
71
113
  - - "~>"
72
114
  - !ruby/object:Gem::Version
73
- version: '10.0'
115
+ version: '13.0'
74
116
  type: :development
75
117
  prerelease: false
76
118
  version_requirements: !ruby/object:Gem::Requirement
77
119
  requirements:
78
120
  - - "~>"
79
121
  - !ruby/object:Gem::Version
80
- version: '10.0'
122
+ version: '13.0'
81
123
  - !ruby/object:Gem::Dependency
82
124
  name: rspec
83
125
  requirement: !ruby/object:Gem::Requirement
@@ -92,6 +134,20 @@ dependencies:
92
134
  - - "~>"
93
135
  - !ruby/object:Gem::Version
94
136
  version: '3.0'
137
+ - !ruby/object:Gem::Dependency
138
+ name: rubocop-rake
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '0.6'
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '0.6'
95
151
  - !ruby/object:Gem::Dependency
96
152
  name: webmock
97
153
  requirement: !ruby/object:Gem::Requirement
@@ -106,8 +162,8 @@ dependencies:
106
162
  - - "~>"
107
163
  - !ruby/object:Gem::Version
108
164
  version: '3.18'
109
- description: A command-line tool for guessing which spec files are relavant to a passed-in
110
- set of file paths.
165
+ description: Command-line tool for guessing which spec files are relevant to a set
166
+ of input file paths.
111
167
  email:
112
168
  - rubygems-committee@gitlab.com
113
169
  executables:
@@ -119,8 +175,11 @@ files:
119
175
  - ".gitlab-ci.yml"
120
176
  - ".gitlab/merge_request_templates/Release.md"
121
177
  - ".rspec"
178
+ - ".rubocop.yml"
179
+ - Dangerfile
122
180
  - Gemfile
123
181
  - Gemfile.lock
182
+ - LICENSE.txt
124
183
  - README.md
125
184
  - Rakefile
126
185
  - bin/console
@@ -129,15 +188,18 @@ files:
129
188
  - exe/tff
130
189
  - feature/tff_exe_spec.rb
131
190
  - fixtures/features/smoke.feature
191
+ - fixtures/lib/api/issues.rb
132
192
  - fixtures/mapping.json
133
193
  - fixtures/mapping.yml
134
194
  - fixtures/spec/controllers/projects_controller_spec.rb
135
195
  - fixtures/spec/db/schema_spec.rb
136
196
  - fixtures/spec/models/project_spec.rb
137
197
  - fixtures/spec/models/test_file_finder_gem_executable_widget_spec.rb
198
+ - fixtures/spec/requests/api/issues/issues_spec.rb
138
199
  - fixtures/spec/smoke_spec.rb
139
200
  - fixtures/spec/views/main_spec.rb
140
201
  - fixtures/test_reports.json
202
+ - lefthook.yml
141
203
  - lib/test_file_finder.rb
142
204
  - lib/test_file_finder/file_finder.rb
143
205
  - lib/test_file_finder/mapping.rb
@@ -150,13 +212,14 @@ files:
150
212
  - test_file_finder.gemspec
151
213
  - tests.yml
152
214
  homepage: https://gitlab.com/gitlab-org/ruby/gems/test_file_finder
153
- licenses: []
215
+ licenses:
216
+ - MIT
154
217
  metadata:
155
218
  allowed_push_host: https://rubygems.org
156
219
  homepage_uri: https://gitlab.com/gitlab-org/ruby/gems/test_file_finder
157
220
  source_code_uri: https://gitlab.com/gitlab-org/ruby/gems/test_file_finder
158
221
  changelog_uri: https://gitlab.com/gitlab-org/ruby/gems/test_file_finder/-/blob/master/CHANGELOG.md
159
- post_install_message:
222
+ post_install_message:
160
223
  rdoc_options: []
161
224
  require_paths:
162
225
  - lib
@@ -164,15 +227,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
164
227
  requirements:
165
228
  - - ">="
166
229
  - !ruby/object:Gem::Version
167
- version: '0'
230
+ version: '3.0'
168
231
  required_rubygems_version: !ruby/object:Gem::Requirement
169
232
  requirements:
170
233
  - - ">="
171
234
  - !ruby/object:Gem::Version
172
235
  version: '0'
173
236
  requirements: []
174
- rubygems_version: 3.1.6
175
- signing_key:
237
+ rubygems_version: 3.3.26
238
+ signing_key:
176
239
  specification_version: 4
177
240
  summary: Guesses spec file paths given input file paths.
178
241
  test_files: []