delta_test 0.1.0 → 0.2.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -0
  3. data/README.md +63 -14
  4. data/Rakefile +16 -3
  5. data/circle.yml +8 -1
  6. data/delta_test.gemspec +4 -3
  7. data/ext/delta_test/delta_test_native.c +154 -0
  8. data/ext/delta_test/delta_test_native.h +15 -0
  9. data/ext/delta_test/extconf.rb +12 -0
  10. data/lib/delta_test.rb +8 -2
  11. data/lib/delta_test/cli.rb +108 -21
  12. data/lib/delta_test/configuration.rb +91 -35
  13. data/lib/delta_test/dependencies_table.rb +15 -1
  14. data/lib/delta_test/generator.rb +42 -29
  15. data/lib/delta_test/profiler.rb +5 -0
  16. data/lib/delta_test/related_spec_list.rb +67 -8
  17. data/lib/delta_test/spec_helpers.rb +9 -7
  18. data/lib/delta_test/version.rb +1 -1
  19. data/spec/lib/delta_test/cli_spec.rb +26 -5
  20. data/spec/lib/delta_test/configuration_spec.rb +12 -0
  21. data/spec/lib/delta_test/dependencies_table_spec.rb +35 -0
  22. data/spec/lib/delta_test/generator_spec.rb +34 -17
  23. data/spec/lib/delta_test/profiler_spec.rb +121 -0
  24. data/spec/lib/delta_test/related_spec_list_spec.rb +150 -34
  25. data/spec/lib/delta_test/spec_helpers_spec.rb +11 -5
  26. data/spec/rails/Gemfile +8 -2
  27. data/spec/rails/Gemfile.lock +37 -3
  28. data/spec/rails/app/models/category.rb +14 -0
  29. data/spec/rails/app/models/comment.rb +20 -0
  30. data/spec/rails/app/models/post.rb +23 -0
  31. data/spec/rails/app/models/post_categorizing.rb +14 -0
  32. data/spec/rails/app/models/user.rb +15 -0
  33. data/spec/rails/db/migrate/20150518052022_create_users.rb +9 -0
  34. data/spec/rails/db/migrate/20150518052057_create_posts.rb +11 -0
  35. data/spec/rails/db/migrate/20150518052332_create_comments.rb +11 -0
  36. data/spec/rails/db/migrate/20150518052523_create_categories.rb +9 -0
  37. data/spec/rails/db/migrate/20150518052604_create_post_categorizings.rb +10 -0
  38. data/spec/rails/db/schema.rb +59 -0
  39. data/spec/rails/spec/factories/categories.rb +5 -0
  40. data/spec/rails/spec/factories/comments.rb +8 -0
  41. data/spec/rails/spec/factories/post_categorizings.rb +6 -0
  42. data/spec/rails/spec/factories/posts.rb +7 -0
  43. data/spec/rails/spec/factories/users.rb +5 -0
  44. data/spec/rails/spec/models/category_spec.rb +3 -0
  45. data/spec/rails/spec/models/comment_spec.rb +3 -0
  46. data/spec/rails/spec/models/post_categorizing_spec.rb +3 -0
  47. data/spec/rails/spec/models/post_spec.rb +3 -0
  48. data/spec/rails/spec/models/user_spec.rb +20 -0
  49. data/spec/rails/spec/spec_helper.rb +53 -9
  50. data/spec/spec_helper.rb +2 -0
  51. metadata +79 -19
  52. data/lib/delta_test/analyzer.rb +0 -47
  53. data/spec/lib/delta_test/analyzer_spec.rb +0 -126
@@ -1,5 +1,7 @@
1
1
  require 'open3'
2
2
  require 'shellwords'
3
+ require 'thread'
4
+ require 'thwait'
3
5
 
4
6
  require_relative 'related_spec_list'
5
7
 
@@ -12,6 +14,10 @@ module DeltaTest
12
14
  'verbose' => false,
13
15
  }.freeze
14
16
 
17
+ BUNDLE_EXEC = ['bundle', 'exec'].freeze
18
+
19
+ SPLITTER = '--'
20
+
15
21
  attr_reader *%i[
16
22
  args
17
23
  command
@@ -54,6 +60,8 @@ module DeltaTest
54
60
  do_table
55
61
  when 'exec'
56
62
  do_exec
63
+ when 'clear'
64
+ do_clear
57
65
  when '-v', '--version'
58
66
  do_version
59
67
  else
@@ -111,8 +119,8 @@ module DeltaTest
111
119
  #
112
120
  # @return {Boolean}
113
121
  ###
114
- def run_full_tests?
115
- Git.same_commit?(@options['base'], @options['head'])
122
+ def profile_mode?
123
+ @profile_mode ||= Git.same_commit?(@options['base'], @options['head'])
116
124
  end
117
125
 
118
126
  ###
@@ -148,24 +156,53 @@ module DeltaTest
148
156
  spec_files = nil
149
157
  args = []
150
158
 
151
- if run_full_tests?
152
- args << ('%s=%s' % [VERBOSE_FLAG, true]) if DeltaTest.verbose?
153
- args << ('%s=%s' % [ACTIVE_FLAG, true])
154
- else
155
- args << 'cat'
156
- args << '|'
157
- args << 'xargs'
159
+ begin
160
+ unless profile_mode?
161
+ @list.load_table!
162
+ @list.retrive_changed_files!(@options['base'], @options['head'])
163
+
164
+ spec_files = @list.related_spec_files.to_a
165
+
166
+ if spec_files.empty?
167
+ exit_with_message(0, 'Nothing to test')
168
+ end
169
+ end
170
+ rescue TableNotFoundError
171
+ # force profile mode cuz we don't have a table
172
+ @profile_mode = true
173
+ end
158
174
 
159
- @list.load_table!
160
- @list.retrive_changed_files!(@options['base'], @options['head'])
175
+ @args.map! { |arg| Shellwords.escape(arg) }
161
176
 
162
- spec_files = @list.related_spec_files.to_a
177
+ if (splitter = @args.index(SPLITTER))
178
+ files = @args.drop(splitter + 1)
179
+ @args = @args.take(splitter)
163
180
 
164
- if spec_files.empty?
165
- exit_with_message(0, 'Nothing to test')
181
+ if files && files.any?
182
+ if spec_files
183
+ pattern = files.map { |file| Regexp.escape(file) }
184
+ pattern = '^(%s)' % pattern.join('|')
185
+ spec_files = spec_files.grep(pattern)
186
+ else
187
+ spec_files = files
188
+ end
166
189
  end
167
190
  end
168
191
 
192
+ if profile_mode?
193
+ args << ('%s=%s' % [VERBOSE_FLAG, true]) if DeltaTest.verbose?
194
+ args << ('%s=%s' % [ACTIVE_FLAG, true])
195
+ end
196
+
197
+ if spec_files
198
+ args.unshift('cat', '|')
199
+ args << 'xargs'
200
+ end
201
+
202
+ if bundler_enabled? && BUNDLE_EXEC != @args.take(2)
203
+ args += BUNDLE_EXEC
204
+ end
205
+
169
206
  args += @args
170
207
  args = args.join(' ')
171
208
 
@@ -174,11 +211,32 @@ module DeltaTest
174
211
  Open3.popen3(args) do |i, o, e, w|
175
212
  i.write(spec_files.join("\n")) if spec_files
176
213
  i.close
177
- o.each { |l| puts l }
178
- e.each { |l| $stderr.puts l }
214
+
215
+ threads = []
216
+ threads << Thread.new { o.each { |l| puts l } }
217
+ threads << Thread.new { e.each { |l| $stderr.puts l } }
218
+
219
+ ThreadsWait.all_waits(*threads)
220
+ exit w.value.exitstatus
179
221
  end
180
222
  end
181
223
 
224
+ ###
225
+ # Clean up tables and caches
226
+ ###
227
+ def do_clear
228
+ table_file_path = DeltaTest.config.table_file_path('')
229
+
230
+ return unless table_file_path
231
+
232
+ args = [
233
+ 'rm',
234
+ '%s*' % Shellwords.escape(table_file_path),
235
+ ]
236
+
237
+ Open3.capture3(args.join(' ')) rescue nil
238
+ end
239
+
182
240
  ###
183
241
  # Show version
184
242
  ###
@@ -194,9 +252,9 @@ module DeltaTest
194
252
  puts "Command not found: #{@command}"
195
253
  end
196
254
 
197
- puts <<-HELP
255
+ puts <<HELP
198
256
  usage: delta_test <command> [--base=<base>] [--head=<head>] [--verbose] [<args>]
199
- [-v]
257
+ [-v|--version]
200
258
 
201
259
  options:
202
260
  --base=<base> A branch or a commit id to diff from.
@@ -215,9 +273,38 @@ commands:
215
273
 
216
274
  table Show dependencies table.
217
275
 
218
- exec <script> Rxecute test script using delta_test.
219
- Run command something like `delta_test list | xargs script'.
220
- HELP
276
+ exec <script> [-- <files>]
277
+ Execute test script using delta_test.
278
+ if <base> and <head> is the same commit or no dependencies table is found,
279
+ it'll run full test cases with a profile mode to create a table.
280
+ Otherwise, it'll run test script with only related spec files
281
+ passed by its arguments, like `delta_test list | xargs script'.
282
+
283
+ clear Clean up tables and caches.
284
+ HELP
285
+ end
286
+
287
+
288
+ private
289
+
290
+ ###
291
+ # Check bundler existance
292
+ #
293
+ # @see http://github.com/carlhuda/bundler Bundler::SharedHelpers#find_gemfile
294
+ ###
295
+ def bundler_enabled?
296
+ return true if Object.const_defined?(:Bundler)
297
+
298
+ previous = nil
299
+ current = File.expand_path(Dir.pwd)
300
+
301
+ until !File.directory?(current) || current == previous
302
+ filename = File.join(current, 'Gemfile')
303
+ return true if File.exist?(filename)
304
+ current, previous = File.expand_path('..', current), current
305
+ end
306
+
307
+ false
221
308
  end
222
309
 
223
310
  end
@@ -8,11 +8,46 @@ require_relative 'utils'
8
8
  module DeltaTest
9
9
  class Configuration
10
10
 
11
+ module Validator
12
+
13
+ def self.included(base)
14
+ base.include(InstanceMethods)
15
+ base.extend(ClassMethods)
16
+ end
17
+
18
+ module ClassMethods
19
+
20
+ def _validators
21
+ @_validators ||= []
22
+ end
23
+
24
+ def validate(attr, message, &block)
25
+ _validators << [attr, message, block]
26
+ end
27
+
28
+ end
29
+
30
+ module InstanceMethods
31
+
32
+ def validate!
33
+ self.class._validators.each do |attr, message, block|
34
+ raise ValidationError.new(attr, message) unless self.instance_eval(&block)
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ include Validator
43
+
11
44
  CONFIG_FILES = [
12
45
  'delta_test.yml',
13
46
  'delta_test.yaml',
14
47
  ].freeze
15
48
 
49
+ PART_FILE_EXT = '.part-%s'
50
+
16
51
  attr_accessor *%i[
17
52
  base_path
18
53
  files
@@ -20,6 +55,7 @@ module DeltaTest
20
55
  table_file
21
56
  patterns
22
57
  exclude_patterns
58
+ full_test_patterns
23
59
  custom_mappings
24
60
  ]
25
61
 
@@ -29,14 +65,43 @@ module DeltaTest
29
65
  table_file_path
30
66
  ]
31
67
 
68
+ validate :base_path, 'need to be an absolute path' do
69
+ self.base_path.absolute?
70
+ end
71
+
72
+ validate :files, 'need to be an array' do
73
+ self.files.is_a?(Array)
74
+ end
75
+
76
+ validate :patterns, 'need to be an array' do
77
+ self.patterns.is_a?(Array)
78
+ end
79
+
80
+ validate :exclude_patterns, 'need to be an array' do
81
+ self.exclude_patterns.is_a?(Array)
82
+ end
83
+
84
+ validate :full_test_patterns, 'need to be an array' do
85
+ self.full_test_patterns.is_a?(Array)
86
+ end
87
+
88
+ validate :custom_mappings, 'need to be a hash' do
89
+ self.custom_mappings.is_a?(Hash)
90
+ end
91
+
92
+ validate :custom_mappings, 'need to have an array in the contents' do
93
+ self.custom_mappings.values.all? { |v| v.is_a?(Array) }
94
+ end
95
+
32
96
  def initialize
33
97
  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 = {}
98
+ c.base_path = File.expand_path('.')
99
+ c.table_file = 'tmp/.delta_test_dt'
100
+ c.files = []
101
+ c.patterns = []
102
+ c.exclude_patterns = []
103
+ c.full_test_patterns = []
104
+ c.custom_mappings = {}
40
105
  end
41
106
  end
42
107
 
@@ -64,6 +129,26 @@ module DeltaTest
64
129
  end
65
130
 
66
131
 
132
+ # Override getters
133
+ #-----------------------------------------------
134
+ ###
135
+ # Returns file path for the table
136
+ #
137
+ # @params {String} part
138
+ #
139
+ # @return {Pathname}
140
+ ###
141
+ def table_file_path(part = nil)
142
+ return unless @table_file_path
143
+
144
+ if part
145
+ @table_file_path.sub_ext(PART_FILE_EXT % part)
146
+ else
147
+ @table_file_path
148
+ end
149
+ end
150
+
151
+
67
152
  # Update
68
153
  #-----------------------------------------------
69
154
  ###
@@ -77,35 +162,6 @@ module DeltaTest
77
162
  precalculate!
78
163
  end
79
164
 
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
165
  ###
110
166
  # Precalculate some values
111
167
  ###
@@ -41,6 +41,21 @@ module DeltaTest
41
41
  self[spec_file] << source_file if DeltaTest.config.filtered_files.include?(source_file)
42
42
  end
43
43
 
44
+ ###
45
+ # Reverse merge other table
46
+ #
47
+ # @params {DependenciesTable} other
48
+ ###
49
+ def reverse_merge!(other)
50
+ raise TypeError unless other.is_a?(self.class)
51
+
52
+ other.each do |spec_file, source_files|
53
+ self[spec_file] |= source_files
54
+ end
55
+
56
+ nil
57
+ end
58
+
44
59
  ###
45
60
  # Temporary disable default_proc
46
61
  # Because Marshal can't dump Hash with default_proc
@@ -70,7 +85,6 @@ module DeltaTest
70
85
  # @params {String|Pathname} file
71
86
  ###
72
87
  def dump(file)
73
- # Marshal can't dump hash with default proc
74
88
  without_default_proc do
75
89
  cleanup!
76
90
  data = Marshal.dump(self)
@@ -1,4 +1,6 @@
1
- require_relative 'analyzer'
1
+ require 'singleton'
2
+
3
+ require_relative 'profiler'
2
4
  require_relative 'dependencies_table'
3
5
 
4
6
  require_relative 'utils'
@@ -12,11 +14,9 @@ module DeltaTest
12
14
  ]
13
15
 
14
16
  ###
15
- # Setup analyzer and table
16
- #
17
- # @params {Boolean} _auto_teardown
17
+ # Setup table
18
18
  ###
19
- def setup!(_auto_teardown = true)
19
+ def setup!
20
20
  return unless DeltaTest.active?
21
21
 
22
22
  return if @_setup
@@ -24,16 +24,13 @@ module DeltaTest
24
24
 
25
25
  DeltaTest.log('--- setup!')
26
26
 
27
- @analyzer = Analyzer.new
28
- @table = DependenciesTable.load(DeltaTest.config.table_file_path)
27
+ @table = DependenciesTable.load(DeltaTest.config.table_file_path)
29
28
 
30
29
  @current_spec_file = nil
31
-
32
- hook_on_exit { teardown! } if _auto_teardown
33
30
  end
34
31
 
35
32
  ###
36
- # Start analyzer for the spec file
33
+ # Start profiler for the spec file
37
34
  #
38
35
  # @params {String} spec_file
39
36
  ###
@@ -43,27 +40,30 @@ module DeltaTest
43
40
  DeltaTest.log('--- start!(%s)' % spec_file)
44
41
 
45
42
  @current_spec_file = Utils.regulate_filepath(spec_file, DeltaTest.config.base_path).to_s
46
- @analyzer.start
43
+
44
+ Profiler.start!
47
45
  end
48
46
 
49
47
  ###
50
- # Stop analyzer and update table
48
+ # Stop profiler and update table
51
49
  ###
52
50
  def stop!
53
51
  return unless DeltaTest.active?
54
52
 
53
+ Profiler.stop!
54
+
55
55
  DeltaTest.log('--- stop!')
56
56
 
57
57
  spec_file = @current_spec_file
58
58
  @current_spec_file = nil
59
59
 
60
- @analyzer.stop
61
-
62
60
  if spec_file
63
- @analyzer.related_source_files.each do |file|
61
+ Profiler.last_result.each do |file|
64
62
  @table.add(spec_file, file)
65
63
  end
66
64
  end
65
+
66
+ DeltaTest::Profiler.clean!
67
67
  end
68
68
 
69
69
  ###
@@ -76,26 +76,39 @@ module DeltaTest
76
76
 
77
77
  DeltaTest.log('--- teardown!')
78
78
 
79
- @analyzer.stop
80
- @table.dump(DeltaTest.config.table_file_path)
81
- end
79
+ Profiler.clean!
82
80
 
81
+ if defined?(ParallelTests)
82
+ if ParallelTests.first_process?
83
+ ParallelTests.wait_for_other_processes_to_finish
83
84
 
84
- private
85
+ table_file_path = DeltaTest.config.table_file_path('*')
85
86
 
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
87
+ Dir.glob(table_file_path).each do |part_file|
88
+ part_table = DependenciesTable.load(part_file)
89
+ @table.reverse_merge!(part_table)
90
+ end
91
+ else
92
+ table_file_path = DeltaTest.config.table_file_path(ENV['TEST_ENV_NUMBER'])
95
93
 
96
- block.call
94
+ @table.dump(table_file_path)
95
+ return
96
+ end
97
97
  end
98
+
99
+ @table.dump(DeltaTest.config.table_file_path)
98
100
  end
99
101
 
102
+ ###
103
+ # Hook teardown! on exit
104
+ ###
105
+ def hook_on_exit
106
+ at_exit { teardown! }
107
+ end
108
+
109
+ end
110
+
111
+ class GeneratorSingleton < Generator
112
+ include Singleton
100
113
  end
101
114
  end