delta_test 0.1.0
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 +7 -0
- data/.gitignore +15 -0
- data/.rspec +4 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +165 -0
- data/Rakefile +17 -0
- data/bin/delta_test +12 -0
- data/circle.yml +12 -0
- data/delta_test.gemspec +30 -0
- data/lib/delta_test/analyzer.rb +47 -0
- data/lib/delta_test/cli.rb +224 -0
- data/lib/delta_test/configuration.rb +173 -0
- data/lib/delta_test/dependencies_table.rb +83 -0
- data/lib/delta_test/errors.rb +55 -0
- data/lib/delta_test/generator.rb +101 -0
- data/lib/delta_test/git.rb +88 -0
- data/lib/delta_test/related_spec_list.rb +64 -0
- data/lib/delta_test/spec_helpers.rb +42 -0
- data/lib/delta_test/utils.rb +93 -0
- data/lib/delta_test/version.rb +9 -0
- data/lib/delta_test.rb +47 -0
- data/spec/fixtures/sample/alpha.rb +19 -0
- data/spec/fixtures/sample/beta.rb +15 -0
- data/spec/fixtures/sample/gamma.rb +9 -0
- data/spec/lib/delta_test/analyzer_spec.rb +126 -0
- data/spec/lib/delta_test/cli_spec.rb +422 -0
- data/spec/lib/delta_test/configuration_spec.rb +353 -0
- data/spec/lib/delta_test/dependencies_table_spec.rb +129 -0
- data/spec/lib/delta_test/generator_spec.rb +201 -0
- data/spec/lib/delta_test/git_spec.rb +178 -0
- data/spec/lib/delta_test/related_spec_list_spec.rb +182 -0
- data/spec/lib/delta_test/spec_helpers_spec.rb +72 -0
- data/spec/lib/delta_test/utils_spec.rb +244 -0
- data/spec/lib/delta_test_spec.rb +119 -0
- data/spec/rails/.gitignore +19 -0
- data/spec/rails/.rspec +3 -0
- data/spec/rails/Gemfile +15 -0
- data/spec/rails/Gemfile.lock +163 -0
- data/spec/rails/README.rdoc +28 -0
- data/spec/rails/Rakefile +6 -0
- data/spec/rails/app/controllers/application_controller.rb +5 -0
- data/spec/rails/app/controllers/concerns/.keep +0 -0
- data/spec/rails/app/helpers/application_helper.rb +2 -0
- data/spec/rails/app/mailers/.keep +0 -0
- data/spec/rails/app/models/.keep +0 -0
- data/spec/rails/app/models/concerns/.keep +0 -0
- data/spec/rails/app/views/layouts/application.html.haml +7 -0
- data/spec/rails/bin/bundle +3 -0
- data/spec/rails/bin/rails +4 -0
- data/spec/rails/bin/rake +4 -0
- data/spec/rails/bin/setup +29 -0
- data/spec/rails/config/application.rb +35 -0
- data/spec/rails/config/boot.rb +3 -0
- data/spec/rails/config/database.yml +25 -0
- data/spec/rails/config/environment.rb +5 -0
- data/spec/rails/config/environments/development.rb +41 -0
- data/spec/rails/config/environments/production.rb +79 -0
- data/spec/rails/config/environments/test.rb +42 -0
- data/spec/rails/config/initializers/assets.rb +11 -0
- data/spec/rails/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails/config/initializers/cookies_serializer.rb +3 -0
- data/spec/rails/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/rails/config/initializers/inflections.rb +16 -0
- data/spec/rails/config/initializers/mime_types.rb +4 -0
- data/spec/rails/config/initializers/session_store.rb +3 -0
- data/spec/rails/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails/config/locales/en.yml +23 -0
- data/spec/rails/config/routes.rb +56 -0
- data/spec/rails/config/secrets.yml +22 -0
- data/spec/rails/config.ru +4 -0
- data/spec/rails/db/seeds.rb +7 -0
- data/spec/rails/delta_test.yml +5 -0
- data/spec/rails/lib/assets/.keep +0 -0
- data/spec/rails/lib/tasks/.keep +0 -0
- data/spec/rails/log/.keep +0 -0
- data/spec/rails/public/404.html +67 -0
- data/spec/rails/public/422.html +67 -0
- data/spec/rails/public/500.html +66 -0
- data/spec/rails/public/favicon.ico +0 -0
- data/spec/rails/public/robots.txt +5 -0
- data/spec/rails/spec/features/sample_spec.rb +7 -0
- data/spec/rails/spec/spec_helper.rb +16 -0
- data/spec/rails/vendor/assets/javascripts/.keep +0 -0
- data/spec/rails/vendor/assets/stylesheets/.keep +0 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/supports/create_table_file.rb +21 -0
- metadata +283 -0
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'pathname'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
require_relative 'git'
|
6
|
+
require_relative 'utils'
|
7
|
+
|
8
|
+
module DeltaTest
|
9
|
+
class Configuration
|
10
|
+
|
11
|
+
CONFIG_FILES = [
|
12
|
+
'delta_test.yml',
|
13
|
+
'delta_test.yaml',
|
14
|
+
].freeze
|
15
|
+
|
16
|
+
attr_accessor *%i[
|
17
|
+
base_path
|
18
|
+
files
|
19
|
+
|
20
|
+
table_file
|
21
|
+
patterns
|
22
|
+
exclude_patterns
|
23
|
+
custom_mappings
|
24
|
+
]
|
25
|
+
|
26
|
+
# for precalculated values
|
27
|
+
attr_reader *%i[
|
28
|
+
filtered_files
|
29
|
+
table_file_path
|
30
|
+
]
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
update do |c|
|
34
|
+
c.base_path = File.expand_path('.')
|
35
|
+
c.table_file = 'tmp/.delta_test_dt'
|
36
|
+
c.files = []
|
37
|
+
c.patterns = []
|
38
|
+
c.exclude_patterns = []
|
39
|
+
c.custom_mappings = {}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
# Override setters
|
45
|
+
#-----------------------------------------------
|
46
|
+
###
|
47
|
+
# Store base_path as Pathname
|
48
|
+
#
|
49
|
+
# @params {String|Pathname} path
|
50
|
+
# @return {Pathname}
|
51
|
+
###
|
52
|
+
def base_path=(path)
|
53
|
+
@base_path = Pathname.new(path)
|
54
|
+
end
|
55
|
+
|
56
|
+
###
|
57
|
+
# Store table_file as Pathname
|
58
|
+
#
|
59
|
+
# @params {String|Pathname} path
|
60
|
+
# @return {Pathname}
|
61
|
+
###
|
62
|
+
def table_file=(path)
|
63
|
+
@table_file = Pathname.new(path)
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
# Update
|
68
|
+
#-----------------------------------------------
|
69
|
+
###
|
70
|
+
# Update, verify and precalculate
|
71
|
+
#
|
72
|
+
# @block
|
73
|
+
###
|
74
|
+
def update
|
75
|
+
yield self if block_given?
|
76
|
+
validate!
|
77
|
+
precalculate!
|
78
|
+
end
|
79
|
+
|
80
|
+
###
|
81
|
+
# Validate option values
|
82
|
+
###
|
83
|
+
def validate!
|
84
|
+
if self.base_path.relative?
|
85
|
+
raise ValidationError.new(:base_path, 'need to be an absolute path')
|
86
|
+
end
|
87
|
+
|
88
|
+
unless self.files.is_a?(Array)
|
89
|
+
raise ValidationError.new(:files, 'need to be an array')
|
90
|
+
end
|
91
|
+
|
92
|
+
unless self.patterns.is_a?(Array)
|
93
|
+
raise ValidationError.new(:patterns, 'need to be an array')
|
94
|
+
end
|
95
|
+
|
96
|
+
unless self.exclude_patterns.is_a?(Array)
|
97
|
+
raise ValidationError.new(:exclude_patterns, 'need to be an array')
|
98
|
+
end
|
99
|
+
|
100
|
+
unless self.custom_mappings.is_a?(Hash)
|
101
|
+
raise ValidationError.new(:custom_mappings, 'need to be a hash')
|
102
|
+
|
103
|
+
unless self.custom_mappings.values.all? { |v| v.is_a?(Array) }
|
104
|
+
raise ValidationError.new(:custom_mappings, 'need to have an array in the contents')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
###
|
110
|
+
# Precalculate some values
|
111
|
+
###
|
112
|
+
def precalculate!
|
113
|
+
filtered_files = self.files
|
114
|
+
.map { |f| Utils.regulate_filepath(f, self.base_path) }
|
115
|
+
.uniq
|
116
|
+
|
117
|
+
filtered_files = Utils.files_grep(filtered_files, self.patterns, self.exclude_patterns)
|
118
|
+
|
119
|
+
@filtered_files = Set.new(filtered_files)
|
120
|
+
|
121
|
+
@table_file_path = Pathname.new(File.absolute_path(self.table_file, self.base_path))
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# Auto configuration
|
126
|
+
#-----------------------------------------------
|
127
|
+
###
|
128
|
+
# Use configuration file and git
|
129
|
+
###
|
130
|
+
def auto_configure!
|
131
|
+
load_from_file!
|
132
|
+
retrive_files_from_git_index!
|
133
|
+
update
|
134
|
+
end
|
135
|
+
|
136
|
+
###
|
137
|
+
# Load configuration file
|
138
|
+
# And update `base_path` to the directory
|
139
|
+
###
|
140
|
+
def load_from_file!
|
141
|
+
config_file = Utils.find_file_upward(*CONFIG_FILES)
|
142
|
+
|
143
|
+
unless config_file
|
144
|
+
raise NoConfigurationFileFoundError
|
145
|
+
end
|
146
|
+
|
147
|
+
yaml = YAML.load_file(config_file)
|
148
|
+
|
149
|
+
self.base_path = File.dirname(config_file)
|
150
|
+
|
151
|
+
yaml.each do |k, v|
|
152
|
+
if self.respond_to?("#{k}=")
|
153
|
+
self.send("#{k}=", v)
|
154
|
+
else
|
155
|
+
raise InvalidOptionError.new(k)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
###
|
161
|
+
# Retrive files from git index
|
162
|
+
# And update `files`
|
163
|
+
###
|
164
|
+
def retrive_files_from_git_index!
|
165
|
+
unless Git.git_repo?
|
166
|
+
raise NotInGitRepositoryError
|
167
|
+
end
|
168
|
+
|
169
|
+
self.files = Git.ls_files
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
require_relative 'utils'
|
5
|
+
|
6
|
+
module DeltaTest
|
7
|
+
class DependenciesTable < ::Hash
|
8
|
+
|
9
|
+
DEFAULT_PROC = -> (h, k) { h[k] = ::Set.new }
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
super
|
13
|
+
|
14
|
+
self.default_proc = DEFAULT_PROC
|
15
|
+
end
|
16
|
+
|
17
|
+
###
|
18
|
+
# Restore a table object from a file
|
19
|
+
#
|
20
|
+
# @params {String|Pathname} file
|
21
|
+
###
|
22
|
+
def self.load(file)
|
23
|
+
begin
|
24
|
+
data = File.binread(file)
|
25
|
+
dt = Marshal.load(data)
|
26
|
+
dt.default_proc = DEFAULT_PROC
|
27
|
+
dt
|
28
|
+
rescue
|
29
|
+
self.new
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
###
|
34
|
+
# Add a dependency for a spec file
|
35
|
+
#
|
36
|
+
# @params {String} spec_file
|
37
|
+
# @params {String} source_file
|
38
|
+
###
|
39
|
+
def add(spec_file, source_file)
|
40
|
+
source_file = Utils.regulate_filepath(source_file, DeltaTest.config.base_path)
|
41
|
+
self[spec_file] << source_file if DeltaTest.config.filtered_files.include?(source_file)
|
42
|
+
end
|
43
|
+
|
44
|
+
###
|
45
|
+
# Temporary disable default_proc
|
46
|
+
# Because Marshal can't dump Hash with default_proc
|
47
|
+
#
|
48
|
+
# @block
|
49
|
+
###
|
50
|
+
def without_default_proc
|
51
|
+
self.default_proc = nil
|
52
|
+
|
53
|
+
begin
|
54
|
+
yield
|
55
|
+
ensure
|
56
|
+
self.default_proc = DEFAULT_PROC
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
###
|
61
|
+
# Cleanup empty sets from the table
|
62
|
+
###
|
63
|
+
def cleanup!
|
64
|
+
self.reject! { |k, v| v.empty? }
|
65
|
+
end
|
66
|
+
|
67
|
+
###
|
68
|
+
# Dump the table object to a file
|
69
|
+
#
|
70
|
+
# @params {String|Pathname} file
|
71
|
+
###
|
72
|
+
def dump(file)
|
73
|
+
# Marshal can't dump hash with default proc
|
74
|
+
without_default_proc do
|
75
|
+
cleanup!
|
76
|
+
data = Marshal.dump(self)
|
77
|
+
FileUtils.mkdir_p(File.dirname(file))
|
78
|
+
File.open(file, 'wb') { |f| f.write data }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module DeltaTest
|
2
|
+
|
3
|
+
class TableNotFoundError < IOError
|
4
|
+
|
5
|
+
def initialize(table_file_path)
|
6
|
+
@table_file_path = table_file_path
|
7
|
+
end
|
8
|
+
|
9
|
+
def message
|
10
|
+
'table file not found at: `%s`' % @table_file_path
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
class NotInGitRepositoryError < StandardError
|
16
|
+
|
17
|
+
def message
|
18
|
+
'the directory is not managed by git'
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
class NoConfigurationFileFoundError < IOError
|
24
|
+
|
25
|
+
def message
|
26
|
+
'no configuration file found'
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
class InvalidOptionError < StandardError
|
32
|
+
|
33
|
+
def initialize(option)
|
34
|
+
@option = option
|
35
|
+
end
|
36
|
+
|
37
|
+
def message
|
38
|
+
'invalid option: %s' % @option
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
class ValidationError < StandardError
|
44
|
+
|
45
|
+
def initialize(name, message)
|
46
|
+
@name, @_message = name, message
|
47
|
+
end
|
48
|
+
|
49
|
+
def message
|
50
|
+
'`%s` %s' % [@name, @_message]
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require_relative 'analyzer'
|
2
|
+
require_relative 'dependencies_table'
|
3
|
+
|
4
|
+
require_relative 'utils'
|
5
|
+
|
6
|
+
module DeltaTest
|
7
|
+
class Generator
|
8
|
+
|
9
|
+
attr_reader *%i[
|
10
|
+
current_spec_file
|
11
|
+
table
|
12
|
+
]
|
13
|
+
|
14
|
+
###
|
15
|
+
# Setup analyzer and table
|
16
|
+
#
|
17
|
+
# @params {Boolean} _auto_teardown
|
18
|
+
###
|
19
|
+
def setup!(_auto_teardown = true)
|
20
|
+
return unless DeltaTest.active?
|
21
|
+
|
22
|
+
return if @_setup
|
23
|
+
@_setup = true
|
24
|
+
|
25
|
+
DeltaTest.log('--- setup!')
|
26
|
+
|
27
|
+
@analyzer = Analyzer.new
|
28
|
+
@table = DependenciesTable.load(DeltaTest.config.table_file_path)
|
29
|
+
|
30
|
+
@current_spec_file = nil
|
31
|
+
|
32
|
+
hook_on_exit { teardown! } if _auto_teardown
|
33
|
+
end
|
34
|
+
|
35
|
+
###
|
36
|
+
# Start analyzer for the spec file
|
37
|
+
#
|
38
|
+
# @params {String} spec_file
|
39
|
+
###
|
40
|
+
def start!(spec_file)
|
41
|
+
return unless DeltaTest.active?
|
42
|
+
|
43
|
+
DeltaTest.log('--- start!(%s)' % spec_file)
|
44
|
+
|
45
|
+
@current_spec_file = Utils.regulate_filepath(spec_file, DeltaTest.config.base_path).to_s
|
46
|
+
@analyzer.start
|
47
|
+
end
|
48
|
+
|
49
|
+
###
|
50
|
+
# Stop analyzer and update table
|
51
|
+
###
|
52
|
+
def stop!
|
53
|
+
return unless DeltaTest.active?
|
54
|
+
|
55
|
+
DeltaTest.log('--- stop!')
|
56
|
+
|
57
|
+
spec_file = @current_spec_file
|
58
|
+
@current_spec_file = nil
|
59
|
+
|
60
|
+
@analyzer.stop
|
61
|
+
|
62
|
+
if spec_file
|
63
|
+
@analyzer.related_source_files.each do |file|
|
64
|
+
@table.add(spec_file, file)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
###
|
70
|
+
# Save table to the file
|
71
|
+
###
|
72
|
+
def teardown!
|
73
|
+
return unless @_setup
|
74
|
+
return if @_teardown
|
75
|
+
@_teardown = true
|
76
|
+
|
77
|
+
DeltaTest.log('--- teardown!')
|
78
|
+
|
79
|
+
@analyzer.stop
|
80
|
+
@table.dump(DeltaTest.config.table_file_path)
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
###
|
87
|
+
# Handle exit event
|
88
|
+
###
|
89
|
+
def hook_on_exit(&block)
|
90
|
+
at_exit do
|
91
|
+
if defined?(ParallelTests)
|
92
|
+
break unless ParallelTests.first_process?
|
93
|
+
ParallelTests.wait_for_other_processes_to_finish
|
94
|
+
end
|
95
|
+
|
96
|
+
block.call
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'shellwords'
|
3
|
+
|
4
|
+
module DeltaTest
|
5
|
+
module Git
|
6
|
+
class << self
|
7
|
+
|
8
|
+
###
|
9
|
+
# Check if in git managed directory
|
10
|
+
#
|
11
|
+
# @return {Boolean}
|
12
|
+
###
|
13
|
+
def git_repo?
|
14
|
+
o, e, s = exec(%q{git rev-parse --is-inside-work-tree}) rescue []
|
15
|
+
!!s && s.success?
|
16
|
+
end
|
17
|
+
|
18
|
+
###
|
19
|
+
# Get root directory of git
|
20
|
+
#
|
21
|
+
# @return {String}
|
22
|
+
###
|
23
|
+
def root_dir
|
24
|
+
o, e, s = exec(%q{git rev-parse --show-toplevel})
|
25
|
+
s.success? ? o.strip : nil
|
26
|
+
end
|
27
|
+
|
28
|
+
###
|
29
|
+
# Get commit id from rev name
|
30
|
+
#
|
31
|
+
# @params {String} rev - e.g., branch name
|
32
|
+
#
|
33
|
+
# @return {String}
|
34
|
+
###
|
35
|
+
def rev_parse(rev)
|
36
|
+
o, e, s = exec(%q{git rev-parse %s}, rev)
|
37
|
+
s.success? ? o.strip : nil
|
38
|
+
end
|
39
|
+
|
40
|
+
###
|
41
|
+
# Compare two rev names by their commit ids
|
42
|
+
#
|
43
|
+
# @params {String} r1
|
44
|
+
# @params {String} r2
|
45
|
+
#
|
46
|
+
# @return {Boolean}
|
47
|
+
###
|
48
|
+
def same_commit?(r1, r2)
|
49
|
+
rev_parse(r1) == rev_parse(r2)
|
50
|
+
end
|
51
|
+
|
52
|
+
###
|
53
|
+
# Get file list from git index
|
54
|
+
#
|
55
|
+
# @return {Array<String>}
|
56
|
+
###
|
57
|
+
def ls_files
|
58
|
+
o, e, s = exec(%q{git ls-files -z})
|
59
|
+
s.success? ? o.split("\x0") : []
|
60
|
+
end
|
61
|
+
|
62
|
+
###
|
63
|
+
# Get list of modified files in diff
|
64
|
+
#
|
65
|
+
# @params {String} base
|
66
|
+
# @params {String} head
|
67
|
+
#
|
68
|
+
# @return {Array<String>}
|
69
|
+
###
|
70
|
+
def changed_files(base = 'master', head = 'HEAD')
|
71
|
+
o, e, s = exec(%q{git --no-pager diff --name-only -z %s %s}, base, head)
|
72
|
+
s.success? ? o.split("\x0") : []
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
###
|
79
|
+
# Util for executing command
|
80
|
+
###
|
81
|
+
def exec(command, *args)
|
82
|
+
args = args.map { |a| Shellwords.escape(a) }
|
83
|
+
Open3.capture3(command % args, chdir: DeltaTest.config.base_path)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require_relative 'git'
|
4
|
+
require_relative 'dependencies_table'
|
5
|
+
|
6
|
+
module DeltaTest
|
7
|
+
class RelatedSpecList
|
8
|
+
|
9
|
+
attr_reader *%i[
|
10
|
+
table
|
11
|
+
changed_files
|
12
|
+
]
|
13
|
+
|
14
|
+
###
|
15
|
+
# Load table from the file
|
16
|
+
###
|
17
|
+
def load_table!
|
18
|
+
unless File.exist?(DeltaTest.config.table_file_path)
|
19
|
+
raise TableNotFoundError.new(DeltaTest.config.table_file_path)
|
20
|
+
end
|
21
|
+
|
22
|
+
@table = DependenciesTable.load(DeltaTest.config.table_file_path)
|
23
|
+
end
|
24
|
+
|
25
|
+
###
|
26
|
+
# Retrive changed files in git diff
|
27
|
+
#
|
28
|
+
# @params {String} base
|
29
|
+
# @params {String} head
|
30
|
+
###
|
31
|
+
def retrive_changed_files!(base, head)
|
32
|
+
unless Git.git_repo?
|
33
|
+
raise NotInGitRepositoryError
|
34
|
+
end
|
35
|
+
|
36
|
+
@changed_files = Git.changed_files(base, head)
|
37
|
+
end
|
38
|
+
|
39
|
+
###
|
40
|
+
# Calculate related spec files
|
41
|
+
#
|
42
|
+
# @return {Set<String>}
|
43
|
+
###
|
44
|
+
def related_spec_files
|
45
|
+
spec_files = Set.new
|
46
|
+
|
47
|
+
@table.each do |spec_file, dependencies|
|
48
|
+
related = @changed_files.include?(spec_file) \
|
49
|
+
|| (dependencies & @changed_files).any?
|
50
|
+
|
51
|
+
spec_files << spec_file if related
|
52
|
+
end
|
53
|
+
|
54
|
+
DeltaTest.config.custom_mappings.each do |spec_file, patterns|
|
55
|
+
if Utils.files_grep(@changed_files, patterns).any?
|
56
|
+
spec_files << spec_file
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
spec_files
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative 'generator'
|
2
|
+
|
3
|
+
module DeltaTest
|
4
|
+
module SpecHelpers
|
5
|
+
|
6
|
+
###
|
7
|
+
# Setup generator and hook analyzer on contexts
|
8
|
+
###
|
9
|
+
def use_delta_test(example)
|
10
|
+
$delta_test_generator ||= DeltaTest::Generator.new
|
11
|
+
$delta_test_generator.setup!
|
12
|
+
|
13
|
+
example.before(:context) do
|
14
|
+
$delta_test_generator.start!(example.metadata[:file_path])
|
15
|
+
end
|
16
|
+
|
17
|
+
example.after(:context) do
|
18
|
+
$delta_test_generator.stop!
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
###
|
23
|
+
# Extend
|
24
|
+
#
|
25
|
+
# @params {} example
|
26
|
+
###
|
27
|
+
def self.extended(example)
|
28
|
+
example.use_delta_test(example)
|
29
|
+
end
|
30
|
+
|
31
|
+
###
|
32
|
+
# Include
|
33
|
+
# calls `extend` internally
|
34
|
+
#
|
35
|
+
# @params {} example
|
36
|
+
###
|
37
|
+
def self.included(example)
|
38
|
+
example.extend(self)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module DeltaTest
|
2
|
+
module Utils
|
3
|
+
class << self
|
4
|
+
|
5
|
+
###
|
6
|
+
# Convert to relative and clean path
|
7
|
+
#
|
8
|
+
# @params {String|Pathname} file
|
9
|
+
# @params {Pathname} base_path
|
10
|
+
#
|
11
|
+
# @return {Pathname}
|
12
|
+
###
|
13
|
+
def regulate_filepath(file, base_path)
|
14
|
+
file = Pathname.new(file)
|
15
|
+
file = file.relative_path_from(base_path) rescue file
|
16
|
+
file.cleanpath
|
17
|
+
end
|
18
|
+
|
19
|
+
###
|
20
|
+
# Find file upward from pwd
|
21
|
+
#
|
22
|
+
# @params {String} file_names
|
23
|
+
#
|
24
|
+
# @return {String}
|
25
|
+
###
|
26
|
+
def find_file_upward(*file_names)
|
27
|
+
pwd = Dir.pwd
|
28
|
+
base = Hash.new { |h, k| h[k] = pwd }
|
29
|
+
file = {}
|
30
|
+
|
31
|
+
while base.values.all? { |b| '.' != b && '/' != b }
|
32
|
+
file_names.each do |name|
|
33
|
+
file[name] = File.join(base[name], name)
|
34
|
+
base[name] = File.dirname(base[name])
|
35
|
+
|
36
|
+
return file[name] if File.exists?(file[name])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
###
|
44
|
+
# Wildcard pattern matching against a file list
|
45
|
+
#
|
46
|
+
# @params {Array<T as String|Pathname>} files
|
47
|
+
# @params {Array<String>} patterns
|
48
|
+
# @params {Array<String>} exclude_patterns
|
49
|
+
#
|
50
|
+
# @return {Array<T>}
|
51
|
+
###
|
52
|
+
def files_grep(files, patterns = [], exclude_patterns = [])
|
53
|
+
patterns = patterns
|
54
|
+
.map { |p| grep_pattern_to_regexp(p) }
|
55
|
+
exclude_patterns = exclude_patterns
|
56
|
+
.map { |p| grep_pattern_to_regexp(p) }
|
57
|
+
|
58
|
+
any_patterns = patterns.any?
|
59
|
+
any_exclude_patterns = exclude_patterns.any?
|
60
|
+
|
61
|
+
files.select do |file|
|
62
|
+
matcher = ->(p) { p === file.to_s }
|
63
|
+
|
64
|
+
(
|
65
|
+
!any_patterns || patterns.any?(&matcher)
|
66
|
+
) && (
|
67
|
+
!any_exclude_patterns || !exclude_patterns.any?(&matcher)
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
###
|
76
|
+
# Convert file wildcard pattern to a regular expression
|
77
|
+
#
|
78
|
+
# @params {String} pattern
|
79
|
+
#
|
80
|
+
# @return {String}
|
81
|
+
###
|
82
|
+
def grep_pattern_to_regexp(pattern)
|
83
|
+
pattern = Regexp.escape(pattern)
|
84
|
+
.gsub('\*\*/', '.*/?')
|
85
|
+
.gsub('\*\*', '.*')
|
86
|
+
.gsub('\*', '[^/]*')
|
87
|
+
|
88
|
+
Regexp.new('^%s$' % pattern)
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|