test_file_finder 0.1.0 → 0.1.1
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/.gitlab-ci.yml +1 -1
- data/Gemfile.lock +1 -1
- data/README.md +45 -3
- data/exe/tff +15 -2
- data/feature/tff_exe_spec.rb +49 -0
- data/fixtures/mapping.json +9 -0
- data/fixtures/mapping.yml +9 -0
- data/fixtures/spec/controllers/projects_controller_spec.rb +1 -0
- data/fixtures/spec/db/schema_spec.rb +1 -0
- data/fixtures/spec/models/project_spec.rb +1 -0
- data/fixtures/spec/models/test_file_finder_gem_executable_widget_spec.rb +1 -0
- data/lib/test_file_finder.rb +1 -0
- data/lib/test_file_finder/file_finder.rb +14 -9
- data/lib/test_file_finder/mapping.rb +2 -58
- data/lib/test_file_finder/mapping_strategies.rb +8 -0
- data/lib/test_file_finder/mapping_strategies/direct_matching.rb +34 -0
- data/lib/test_file_finder/mapping_strategies/pattern_matching.rb +70 -0
- data/lib/test_file_finder/option_parser.rb +9 -1
- data/lib/test_file_finder/version.rb +1 -1
- metadata +12 -3
- data/tff_test.sh +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f430ea47bf579ef3674aaf0e0dc9cd41dcb667496e095fe5186a3fdf4763e3d0
|
4
|
+
data.tar.gz: f22662e4c8e9e20af318eeca9d6a6de2f24ef494a962406e202781c464cd38c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01da410e1a12bd34d58d0d3d0212d793ba99bdaef7d7b97d3fb03fb68a9b059af700a4da5a695be1976a61ef0dec7385f19e89660630b219acaf88be4b4e7601
|
7
|
+
data.tar.gz: 3e3cf7a6f6a74a7d7fcef43b462ad0b78e971cc1f1116ddf37607aa2837dce44e1b42635b7d0e275929f3d74146ba74151d3c32f717c6bdb151c49ec4afc73bb
|
data/.gitlab-ci.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -44,7 +44,9 @@ $ rspec $(tff $(git diff --name-only master..head))
|
|
44
44
|
|
45
45
|
#### Options
|
46
46
|
|
47
|
-
|
47
|
+
##### `-f mapping`
|
48
|
+
|
49
|
+
`TestFileFinder` can be used with an optional YAML mapping file to specify the mapping from a source file to a test file.
|
48
50
|
|
49
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:
|
50
52
|
|
@@ -72,10 +74,50 @@ spec/models/widget_spec.rb
|
|
72
74
|
Ruby example:
|
73
75
|
|
74
76
|
```ruby
|
75
|
-
TestFileFinder::FileFinder.new(paths: file_paths
|
77
|
+
tff = TestFileFinder::FileFinder.new(paths: file_paths)
|
78
|
+
tff.use TestFileFinder::MappingStrategies::PatternMatching.load('mapping.yml')
|
79
|
+
tff.test_files
|
80
|
+
```
|
81
|
+
|
82
|
+
An example mapping file is available in `fixtures/mapping.yml`.
|
83
|
+
|
84
|
+
##### `--json mapping`
|
85
|
+
|
86
|
+
`TestFileFinder` can be used with an optional JSON mapping file to specify the mapping from a source file to test files.
|
87
|
+
|
88
|
+
The mapping file is a JSON file containing a JSON object. The keys in the JSON are the source files, and the values are
|
89
|
+
arrays containing the test files. For example:
|
90
|
+
|
91
|
+
```json
|
92
|
+
{
|
93
|
+
"app/models/project.rb": [
|
94
|
+
"spec/models/project_spec.rb",
|
95
|
+
"spec/controllers/projects_controller_spec.rb"
|
96
|
+
],
|
97
|
+
"app/controllers/projects_controller.rb": [
|
98
|
+
"spec/controllers/projects_controller_spec.rb"
|
99
|
+
]
|
100
|
+
}
|
101
|
+
```
|
102
|
+
|
103
|
+
Command line example:
|
104
|
+
|
105
|
+
```bash
|
106
|
+
$ file_paths="app/models/project.rb"
|
107
|
+
$ tff --json mapping.json $file_paths
|
108
|
+
spec/models/project_spec.rb
|
109
|
+
spec/controllers/projects_controller_spec.rb
|
110
|
+
```
|
111
|
+
|
112
|
+
Ruby example:
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
tff = TestFileFinder::FileFinder.new(paths: file_paths)
|
116
|
+
tff.use TestFileFinder::MappingStrategies::DirectMatching.load('mapping.json')
|
117
|
+
tff.test_files
|
76
118
|
```
|
77
119
|
|
78
|
-
An example mapping file is available in `
|
120
|
+
An example mapping file is available in `fixtures/mapping.json`.
|
79
121
|
|
80
122
|
## Development
|
81
123
|
|
data/exe/tff
CHANGED
@@ -2,5 +2,18 @@
|
|
2
2
|
require 'test_file_finder'
|
3
3
|
|
4
4
|
options = TestFileFinder::OptionParser.parse!(ARGV)
|
5
|
-
|
6
|
-
|
5
|
+
|
6
|
+
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
|
13
|
+
|
14
|
+
if file_finder.strategies.empty?
|
15
|
+
file_finder.use TestFileFinder::MappingStrategies::PatternMatching.default_rails_mapping
|
16
|
+
end
|
17
|
+
|
18
|
+
puts file_finder.test_files
|
19
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
RSpec.describe 'tff exe' do
|
2
|
+
subject { `ruby -Ilib ../exe/tff #{options} #{files}` }
|
3
|
+
|
4
|
+
around do |example|
|
5
|
+
Dir.chdir(File.join(__dir__, '../fixtures')) do
|
6
|
+
example.run
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'without any mapping' do
|
11
|
+
let(:options) {}
|
12
|
+
let(:files) { 'app/models/test_file_finder_gem_executable_widget.rb' }
|
13
|
+
|
14
|
+
it 'prints matching test files using default rails mapping' do
|
15
|
+
expect(subject).to eq("spec/models/test_file_finder_gem_executable_widget_spec.rb\n")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with a yaml mapping' do
|
20
|
+
let(:options) { '-f mapping.yml' }
|
21
|
+
let(:files) { 'db/schema.rb' }
|
22
|
+
|
23
|
+
it 'prints matching test files using given yaml mapping' do
|
24
|
+
expect(subject).to eq("spec/db/schema_spec.rb\n")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with a yaml mapping and json mapping' do
|
29
|
+
let(:options) { '-f mapping.yml --json mapping.json' }
|
30
|
+
let(:files) { 'db/schema.rb app/models/project.rb ' }
|
31
|
+
|
32
|
+
it 'prints matching test files using both yaml and json mappings' do
|
33
|
+
expect(subject.split("\n")).to match_array(%w[spec/db/schema_spec.rb spec/models/project_spec.rb spec/controllers/projects_controller_spec.rb])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with only json mapping' do
|
38
|
+
let(:options) { '--json mapping.json' }
|
39
|
+
let(:files) { 'app/models/test_file_finder_gem_executable_widget.rb app/models/project.rb' }
|
40
|
+
|
41
|
+
it 'prints matching test files using json mapping' do
|
42
|
+
expect(subject.split("\n")).to match_array(%w[spec/models/project_spec.rb spec/controllers/projects_controller_spec.rb])
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'does not match test file using rails mapping' do
|
46
|
+
expect(subject.split("\n")).not_to include('spec/models/test_file_finder_gem_executable_widget_spec.rb')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
test projects controller spec
|
@@ -0,0 +1 @@
|
|
1
|
+
test schema spec
|
@@ -0,0 +1 @@
|
|
1
|
+
test project spec
|
@@ -0,0 +1 @@
|
|
1
|
+
existing specs
|
data/lib/test_file_finder.rb
CHANGED
@@ -1,8 +1,16 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module TestFileFinder
|
2
4
|
class FileFinder
|
3
|
-
|
5
|
+
attr_reader :strategies
|
6
|
+
|
7
|
+
def initialize(paths: [])
|
4
8
|
@paths = [paths].flatten
|
5
|
-
@
|
9
|
+
@strategies = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def use(strategy)
|
13
|
+
@strategies << strategy
|
6
14
|
end
|
7
15
|
|
8
16
|
def test_files
|
@@ -18,13 +26,10 @@ module TestFileFinder
|
|
18
26
|
end
|
19
27
|
|
20
28
|
def file_path_guesses
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
def guess_test_files_for(path)
|
27
|
-
@mapping.match(path)
|
29
|
+
strategies.each_with_object(Set.new) do |strategy, result|
|
30
|
+
matches = strategy.match(paths)
|
31
|
+
result.merge(matches)
|
32
|
+
end
|
28
33
|
end
|
29
34
|
end
|
30
35
|
end
|
@@ -1,63 +1,7 @@
|
|
1
1
|
require 'set'
|
2
2
|
require 'yaml'
|
3
|
+
require 'test_file_finder/mapping_strategies'
|
3
4
|
|
4
5
|
module TestFileFinder
|
5
|
-
|
6
|
-
attr_reader :pattern_matchers
|
7
|
-
|
8
|
-
def self.load(mapping_file = nil)
|
9
|
-
return default_rails_mapping unless mapping_file
|
10
|
-
|
11
|
-
content = File.read(mapping_file)
|
12
|
-
maps = YAML.load(content)['mapping']
|
13
|
-
|
14
|
-
validate(maps)
|
15
|
-
|
16
|
-
new do |mapping|
|
17
|
-
maps.each do |map|
|
18
|
-
source = map['source']
|
19
|
-
test = map['test']
|
20
|
-
mapping.relate(source, test)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.validate(maps)
|
26
|
-
raise InvalidMappingFileError, 'missing `mapping` in test mapping file' if maps.nil?
|
27
|
-
raise InvalidMappingFileError, 'missing `source` or `test` in test mapping file' if maps.any? { |map| incomplete?(map) }
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.incomplete?(map)
|
31
|
-
map['source'].nil? || map['test'].nil?
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.default_rails_mapping
|
35
|
-
new do |mapping|
|
36
|
-
mapping.relate(%r{^app/(.+)\.rb$}, 'spec/%s_spec.rb')
|
37
|
-
mapping.relate(%r{^lib/(.+)\.rb$}, 'spec/lib/%s_spec.rb')
|
38
|
-
mapping.relate(%r{^spec/(.+)_spec.rb$}, 'spec/%s_spec.rb')
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def initialize
|
43
|
-
@pattern_matchers = Hash.new { |h, k| h[k] = [] }
|
44
|
-
|
45
|
-
yield self if block_given?
|
46
|
-
end
|
47
|
-
|
48
|
-
def relate(source, test)
|
49
|
-
@pattern_matchers[source] << test
|
50
|
-
end
|
51
|
-
|
52
|
-
def match(file)
|
53
|
-
@pattern_matchers.each_with_object(Set.new) do |(source, tests), result|
|
54
|
-
regexp = %r{^#{source}$}
|
55
|
-
|
56
|
-
if (match = regexp.match(file))
|
57
|
-
test_files = tests.flat_map { |test| test % match.captures }
|
58
|
-
result.merge(Array(test_files))
|
59
|
-
end
|
60
|
-
end.to_a
|
61
|
-
end
|
62
|
-
end
|
6
|
+
Mapping = MappingStrategies::PatternMatching
|
63
7
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module TestFileFinder
|
4
|
+
module MappingStrategies
|
5
|
+
class DirectMatching
|
6
|
+
JSON_ERROR_MESSAGE = 'json file should contain a json object, with array of test files as the values'.freeze
|
7
|
+
|
8
|
+
def self.load_json(json_file)
|
9
|
+
map = JSON.parse(File.read(json_file))
|
10
|
+
|
11
|
+
validate(map)
|
12
|
+
|
13
|
+
new.tap do |strategy|
|
14
|
+
strategy.map = map
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.validate(map)
|
19
|
+
return if map.is_a?(Hash) && map.values.all? { |value| value.is_a?(Array) }
|
20
|
+
|
21
|
+
raise InvalidMappingFileError, JSON_ERROR_MESSAGE
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_accessor :map
|
25
|
+
|
26
|
+
def match(files)
|
27
|
+
Array(files).inject(Set.new) do |result, file|
|
28
|
+
test_files = @map.fetch(file, [])
|
29
|
+
result.merge(test_files)
|
30
|
+
end.to_a
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module TestFileFinder
|
2
|
+
module MappingStrategies
|
3
|
+
class PatternMatching
|
4
|
+
attr_reader :pattern_matchers
|
5
|
+
|
6
|
+
def self.load(mapping_file)
|
7
|
+
content = File.read(mapping_file)
|
8
|
+
maps = YAML.load(content)['mapping']
|
9
|
+
|
10
|
+
validate(maps)
|
11
|
+
|
12
|
+
new do |mapping|
|
13
|
+
maps.each do |map|
|
14
|
+
source = map['source']
|
15
|
+
test = map['test']
|
16
|
+
mapping.relate(source, test)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.validate(maps)
|
22
|
+
raise InvalidMappingFileError, 'missing `mapping` in test mapping file' if maps.nil?
|
23
|
+
raise InvalidMappingFileError, 'missing `source` or `test` in test mapping file' if maps.any? { |map| incomplete?(map) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.incomplete?(map)
|
27
|
+
map['source'].nil? || map['test'].nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.default_rails_mapping
|
31
|
+
new do |mapping|
|
32
|
+
mapping.relate(%r{^app/(.+)\.rb$}, 'spec/%s_spec.rb')
|
33
|
+
mapping.relate(%r{^lib/(.+)\.rb$}, 'spec/lib/%s_spec.rb')
|
34
|
+
mapping.relate(%r{^spec/(.+)_spec.rb$}, 'spec/%s_spec.rb')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize
|
39
|
+
@pattern_matchers = []
|
40
|
+
|
41
|
+
yield self if block_given?
|
42
|
+
end
|
43
|
+
|
44
|
+
def relate(source, test)
|
45
|
+
@pattern_matchers << pattern_matcher_for(source, test)
|
46
|
+
end
|
47
|
+
|
48
|
+
def match(files)
|
49
|
+
@pattern_matchers.inject(Set.new) do |result, pattern_matcher|
|
50
|
+
test_files = pattern_matcher.call(files)
|
51
|
+
result.merge(test_files)
|
52
|
+
end.to_a
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def pattern_matcher_for(source, test)
|
58
|
+
regexp = %r{^#{source}$}
|
59
|
+
|
60
|
+
proc do |files|
|
61
|
+
Array(files).flat_map do |file|
|
62
|
+
if (match = regexp.match(file))
|
63
|
+
test % match.captures
|
64
|
+
end
|
65
|
+
end.compact
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
|
3
3
|
module TestFileFinder
|
4
|
-
Options = Struct.new(:mapping_file)
|
4
|
+
Options = Struct.new(:mapping_file, :json)
|
5
5
|
|
6
6
|
class OptionParser
|
7
7
|
def self.parse!(argv)
|
@@ -12,6 +12,14 @@ module TestFileFinder
|
|
12
12
|
opts.on('-f', '--mapping-file FILE', String, 'Use a custom test mapping file') do |mapping_file|
|
13
13
|
options.mapping_file = mapping_file
|
14
14
|
end
|
15
|
+
|
16
|
+
opts.on('--yaml FILE', String, 'Use a YAML test mapping file') do |mapping_file|
|
17
|
+
options.mapping_file = mapping_file
|
18
|
+
end
|
19
|
+
|
20
|
+
opts.on('--json FILE', String, 'Use a JSON mapping file') do |json|
|
21
|
+
options.json = json
|
22
|
+
end
|
15
23
|
end.parse!(argv)
|
16
24
|
end
|
17
25
|
end
|
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.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitLab
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -85,14 +85,23 @@ files:
|
|
85
85
|
- bin/console
|
86
86
|
- bin/setup
|
87
87
|
- exe/tff
|
88
|
+
- feature/tff_exe_spec.rb
|
89
|
+
- fixtures/mapping.json
|
90
|
+
- fixtures/mapping.yml
|
91
|
+
- fixtures/spec/controllers/projects_controller_spec.rb
|
92
|
+
- fixtures/spec/db/schema_spec.rb
|
93
|
+
- fixtures/spec/models/project_spec.rb
|
94
|
+
- fixtures/spec/models/test_file_finder_gem_executable_widget_spec.rb
|
88
95
|
- lib/test_file_finder.rb
|
89
96
|
- lib/test_file_finder/file_finder.rb
|
90
97
|
- lib/test_file_finder/mapping.rb
|
98
|
+
- lib/test_file_finder/mapping_strategies.rb
|
99
|
+
- lib/test_file_finder/mapping_strategies/direct_matching.rb
|
100
|
+
- lib/test_file_finder/mapping_strategies/pattern_matching.rb
|
91
101
|
- lib/test_file_finder/option_parser.rb
|
92
102
|
- lib/test_file_finder/version.rb
|
93
103
|
- test_file_finder.gemspec
|
94
104
|
- tests.yml
|
95
|
-
- tff_test.sh
|
96
105
|
homepage: https://gitlab.com/gitlab-org/ci-cd/test_file_finder
|
97
106
|
licenses: []
|
98
107
|
metadata:
|
data/tff_test.sh
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
|
2
|
-
expected="spec/models/test_file_finder_gem_executable_widget_spec.rb"
|
3
|
-
mkdir -p "spec/models"
|
4
|
-
echo "existing specs" >> $expected
|
5
|
-
|
6
|
-
actual=$( ruby -Ilib exe/tff app/models/test_file_finder_gem_executable_widget.rb)
|
7
|
-
rm $expected
|
8
|
-
|
9
|
-
if [[ -n $actual && $expected = $actual ]]
|
10
|
-
then
|
11
|
-
echo "Executable verified"
|
12
|
-
else
|
13
|
-
echo "Could not verify executable"
|
14
|
-
exit 1
|
15
|
-
fi
|
16
|
-
|
17
|
-
expected="spec/db/schema_spec.rb"
|
18
|
-
mkdir -p "spec/db"
|
19
|
-
echo "existing schema specs" >> $expected
|
20
|
-
|
21
|
-
actual=$( ruby -Ilib exe/tff -f spec/fixtures/mapping.yml db/schema.rb)
|
22
|
-
rm $expected
|
23
|
-
|
24
|
-
if [[ -n $actual && $expected = $actual ]]
|
25
|
-
then
|
26
|
-
echo "Executable using mapping verified"
|
27
|
-
else
|
28
|
-
echo "Could not verify executable using mapping"
|
29
|
-
exit 1
|
30
|
-
fi
|