sleeping_king_studios-tasks 0.1.0.rc.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 (50) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +26 -0
  3. data/DEVELOPMENT.md +50 -0
  4. data/LICENSE +22 -0
  5. data/README.md +405 -0
  6. data/lib/sleeping_king_studios/tasks.rb +27 -0
  7. data/lib/sleeping_king_studios/tasks/apps.rb +49 -0
  8. data/lib/sleeping_king_studios/tasks/apps/app_configuration.rb +68 -0
  9. data/lib/sleeping_king_studios/tasks/apps/applications_task.rb +28 -0
  10. data/lib/sleeping_king_studios/tasks/apps/bundle.rb +8 -0
  11. data/lib/sleeping_king_studios/tasks/apps/bundle/install_runner.rb +21 -0
  12. data/lib/sleeping_king_studios/tasks/apps/bundle/install_task.rb +39 -0
  13. data/lib/sleeping_king_studios/tasks/apps/bundle/update_runner.rb +21 -0
  14. data/lib/sleeping_king_studios/tasks/apps/bundle/update_task.rb +39 -0
  15. data/lib/sleeping_king_studios/tasks/apps/ci.rb +8 -0
  16. data/lib/sleeping_king_studios/tasks/apps/ci/results_reporter.rb +69 -0
  17. data/lib/sleeping_king_studios/tasks/apps/ci/rspec_task.rb +29 -0
  18. data/lib/sleeping_king_studios/tasks/apps/ci/rspec_wrapper.rb +42 -0
  19. data/lib/sleeping_king_studios/tasks/apps/ci/rubocop_task.rb +29 -0
  20. data/lib/sleeping_king_studios/tasks/apps/ci/rubocop_wrapper.rb +29 -0
  21. data/lib/sleeping_king_studios/tasks/apps/ci/simplecov_task.rb +68 -0
  22. data/lib/sleeping_king_studios/tasks/apps/ci/step_wrapper.rb +49 -0
  23. data/lib/sleeping_king_studios/tasks/apps/ci/steps_runner.rb +37 -0
  24. data/lib/sleeping_king_studios/tasks/apps/ci/steps_task.rb +92 -0
  25. data/lib/sleeping_king_studios/tasks/ci.rb +8 -0
  26. data/lib/sleeping_king_studios/tasks/ci/cucumber_parser.rb +118 -0
  27. data/lib/sleeping_king_studios/tasks/ci/cucumber_results.rb +191 -0
  28. data/lib/sleeping_king_studios/tasks/ci/cucumber_runner.rb +53 -0
  29. data/lib/sleeping_king_studios/tasks/ci/cucumber_task.rb +47 -0
  30. data/lib/sleeping_king_studios/tasks/ci/results_helpers.rb +44 -0
  31. data/lib/sleeping_king_studios/tasks/ci/rspec_each_results.rb +118 -0
  32. data/lib/sleeping_king_studios/tasks/ci/rspec_each_task.rb +156 -0
  33. data/lib/sleeping_king_studios/tasks/ci/rspec_results.rb +126 -0
  34. data/lib/sleeping_king_studios/tasks/ci/rspec_runner.rb +62 -0
  35. data/lib/sleeping_king_studios/tasks/ci/rspec_task.rb +71 -0
  36. data/lib/sleeping_king_studios/tasks/ci/rubocop_results.rb +80 -0
  37. data/lib/sleeping_king_studios/tasks/ci/rubocop_runner.rb +46 -0
  38. data/lib/sleeping_king_studios/tasks/ci/rubocop_task.rb +44 -0
  39. data/lib/sleeping_king_studios/tasks/ci/simplecov_results.rb +62 -0
  40. data/lib/sleeping_king_studios/tasks/ci/simplecov_task.rb +25 -0
  41. data/lib/sleeping_king_studios/tasks/ci/steps_runner.rb +69 -0
  42. data/lib/sleeping_king_studios/tasks/ci/steps_task.rb +93 -0
  43. data/lib/sleeping_king_studios/tasks/configuration.rb +114 -0
  44. data/lib/sleeping_king_studios/tasks/file.rb +8 -0
  45. data/lib/sleeping_king_studios/tasks/file/new_task.rb +238 -0
  46. data/lib/sleeping_king_studios/tasks/process_runner.rb +70 -0
  47. data/lib/sleeping_king_studios/tasks/task.rb +95 -0
  48. data/lib/sleeping_king_studios/tasks/task_group.rb +37 -0
  49. data/lib/sleeping_king_studios/tasks/version.rb +58 -0
  50. metadata +271 -0
@@ -0,0 +1,53 @@
1
+ # lib/sleeping_king_studios/tasks/ci/cucumber_runner.rb
2
+
3
+ require 'json'
4
+
5
+ require 'sleeping_king_studios/tasks/ci'
6
+ require 'sleeping_king_studios/tasks/ci/cucumber_parser'
7
+ require 'sleeping_king_studios/tasks/process_runner'
8
+
9
+ module SleepingKingStudios::Tasks::Ci
10
+ # Service object to run Cucumber as an external process with the specified
11
+ # parameters.
12
+ class CucumberRunner < SleepingKingStudios::Tasks::ProcessRunner
13
+ def call env: {}, files: [], options: [], report: true
14
+ report = 'tmp/ci/cucumber.json' if report && !report.is_a?(String)
15
+ command =
16
+ build_command(
17
+ :env => env,
18
+ :files => files,
19
+ :options => options,
20
+ :report => report
21
+ ) # end build_command
22
+
23
+ stream_process(command)
24
+
25
+ report ? load_report(:report => report) : {}
26
+ end # method call
27
+
28
+ private
29
+
30
+ def base_command
31
+ 'bundle exec cucumber'
32
+ end # method base_command
33
+
34
+ def build_options files:, options:, report:, **_kwargs
35
+ options += ['--format=json', "--out=#{report}"] if report
36
+
37
+ super :files => files, :options => options
38
+ end # method build_options
39
+
40
+ def load_report report:
41
+ raw = File.read report
42
+
43
+ return {} if raw.empty?
44
+
45
+ json = JSON.parse raw
46
+ parser = SleepingKingStudios::Tasks::Ci::CucumberParser
47
+
48
+ parser.parse(json)
49
+ rescue
50
+ {}
51
+ end # method load_report
52
+ end # class
53
+ end # module
@@ -0,0 +1,47 @@
1
+ # lib/sleeping_king_studios/tasks/ci/cucumber_task.rb
2
+
3
+ require 'sleeping_king_studios/tasks/ci'
4
+ require 'sleeping_king_studios/tasks/ci/cucumber_results'
5
+ require 'sleeping_king_studios/tasks/ci/cucumber_runner'
6
+
7
+ module SleepingKingStudios::Tasks::Ci
8
+ # Defines a Thor task for running the full RSpec test suite.
9
+ class CucumberTask < SleepingKingStudios::Tasks::Task
10
+ def self.description
11
+ 'Runs the Cucumber feature suite.'
12
+ end # class method description
13
+
14
+ option :quiet,
15
+ :aliases => '-q',
16
+ :type => :boolean,
17
+ :default => false,
18
+ :desc => 'Do not write spec results to STDOUT.'
19
+ option :raw,
20
+ :aliases => '-r',
21
+ :type => :boolean,
22
+ :default => false,
23
+ :desc => 'Return a Hash instead of a results object.'
24
+
25
+ def call *files
26
+ files += default_files unless files.empty?
27
+
28
+ results = cucumber_runner.call(:files => files)
29
+
30
+ raw? ? results : CucumberResults.new(results)
31
+ end # method call
32
+
33
+ private
34
+
35
+ def cucumber_runner
36
+ opts = %w(--color)
37
+ opts << '--format=pretty' unless quiet?
38
+
39
+ CucumberRunner.new(:options => opts)
40
+ end # method cucumber_runner
41
+
42
+ def default_files
43
+ SleepingKingStudios::Tasks.configuration.ci.cucumber.
44
+ fetch('default_files', [])
45
+ end # method default_files
46
+ end # class
47
+ end # module
@@ -0,0 +1,44 @@
1
+ # lib/sleeping_king_studios/tasks/ci/results_helpers.rb
2
+
3
+ require 'sleeping_king_studios/tasks/ci'
4
+
5
+ module SleepingKingStudios::Tasks::Ci
6
+ # Helper methods for reporting CI results.
7
+ module ResultsHelpers
8
+ private
9
+
10
+ # Returns a terminal color corresponding to the state of the results object.
11
+ #
12
+ # @param results [Object] The results object.
13
+ #
14
+ # @return [Symbol] The terminal color.
15
+ def results_color results
16
+ if results.failing?
17
+ :red
18
+ elsif results.respond_to?(:errored?) && results.errored?
19
+ :red
20
+ elsif results.pending? || results.empty?
21
+ :yellow
22
+ else
23
+ :green
24
+ end # if-elsif-else
25
+ end # method set_results_color
26
+
27
+ # Returns a state string for the results object.
28
+ #
29
+ # @param results [Object] The results object.
30
+ #
31
+ # @return [String] The results state.
32
+ def results_state results
33
+ if results.respond_to?(:errored?) && results.errored?
34
+ 'Errored'
35
+ elsif results.failing?
36
+ 'Failing'
37
+ elsif results.pending? || results.empty?
38
+ 'Pending'
39
+ else
40
+ 'Passing'
41
+ end # if-elsif-else
42
+ end # method results_state
43
+ end # module
44
+ end # module
@@ -0,0 +1,118 @@
1
+ # lib/sleeping_king_studios/tasks/ci/rspec_each_results.rb
2
+
3
+ require 'sleeping_king_studios/tasks/ci'
4
+
5
+ module SleepingKingStudios::Tasks::Ci
6
+ # Encapsulates the results of an RSpecEach call.
7
+ class RSpecEachResults
8
+ # @param results [Hash] The raw results of the RSpecEach call.
9
+ def initialize results
10
+ @results = results
11
+ end # constructor
12
+
13
+ # @param other [RSpecEachResults] The other results object to compare.
14
+ #
15
+ # @return [Boolean] True if the results are equal, otherwise false.
16
+ def == other
17
+ if other.is_a?(Hash)
18
+ empty? ? other.empty? : to_h == other
19
+ elsif other.is_a?(RSpecEachResults)
20
+ to_h == other.to_h
21
+ else
22
+ false
23
+ end # if-elsif-else
24
+ end # method ==
25
+
26
+ # @return [Float] The duration value.
27
+ def duration
28
+ @results.fetch('duration', 0.0)
29
+ end # method duration
30
+
31
+ # @return [Boolean] True if there are no files, otherwise false.
32
+ def empty?
33
+ file_count.zero?
34
+ end # method empty?
35
+
36
+ # @return [Boolean] True if there are any errored files, otherwise false.
37
+ def errored?
38
+ !errored_count.zero?
39
+ end # method errored?
40
+
41
+ # @return [Integer] The number of errored files.
42
+ def errored_count
43
+ errored_files.count
44
+ end # method errored_count
45
+
46
+ # @return [Array] The list of errored file names.
47
+ def errored_files
48
+ @results.fetch('errored_files', [])
49
+ end # method errored_files
50
+
51
+ # @return [Boolean] True if there are any failing files, otherwise false.
52
+ def failing?
53
+ !failure_count.zero?
54
+ end # method failing?
55
+
56
+ # @return [Array] The list of failing file names.
57
+ def failing_files
58
+ @results.fetch('failing_files', [])
59
+ end # method failing_files
60
+
61
+ # @return [Integer] The number of failing files.
62
+ def failure_count
63
+ failing_files.count
64
+ end # method failing_files
65
+
66
+ # @return [Integer] The total file count.
67
+ def file_count
68
+ @results.fetch('file_count', 0)
69
+ end # method file_count
70
+
71
+ # @return [Boolean] True if there are any pending files, otherwise false.
72
+ def pending?
73
+ !pending_count.zero?
74
+ end # method pending?
75
+
76
+ # @return [Integer] The number of pending files.
77
+ def pending_count
78
+ pending_files.count
79
+ end # method pending_count
80
+
81
+ # @return [Array] The list of pending file names.
82
+ def pending_files
83
+ @results.fetch('pending_files', [])
84
+ end # method pending_files
85
+
86
+ def pluralize count, singular, plural = nil
87
+ "#{count} #{tools.integer.pluralize count, singular, plural}"
88
+ end # method pluralize
89
+
90
+ # @return [Hash] The hash representation of the results.
91
+ def to_h
92
+ {
93
+ 'failing_files' => failing_files,
94
+ 'pending_files' => pending_files,
95
+ 'errored_files' => errored_files,
96
+ 'file_count' => file_count,
97
+ 'duration' => duration
98
+ } # end hash
99
+ end # method to_h
100
+
101
+ # @return [String] The string representation of the results.
102
+ def to_s
103
+ str = "#{file_count} files"
104
+
105
+ str << ', ' << pluralize(failure_count, 'failure')
106
+
107
+ str << ", #{pending_count} pending" if pending?
108
+
109
+ str << ", #{errored_count} errored" if errored?
110
+
111
+ str << " in #{duration.round(2)} seconds"
112
+ end # method to_s
113
+
114
+ def tools
115
+ SleepingKingStudios::Tools::Toolbelt.instance
116
+ end # method tools
117
+ end # class
118
+ end # module
@@ -0,0 +1,156 @@
1
+ # lib/sleeping_king_studios/tasks/ci/rspec_each_task.rb
2
+
3
+ require 'sleeping_king_studios/tasks/ci'
4
+ require 'sleeping_king_studios/tasks/ci/results_helpers'
5
+ require 'sleeping_king_studios/tasks/ci/rspec_each_results'
6
+ require 'sleeping_king_studios/tasks/ci/rspec_results'
7
+ require 'sleeping_king_studios/tasks/ci/rspec_runner'
8
+
9
+ module SleepingKingStudios::Tasks::Ci
10
+ # rubocop:disable Metrics/ClassLength
11
+
12
+ # Defines a Thor task for running the full RSpec test suite.
13
+ class RSpecEachTask < SleepingKingStudios::Tasks::Task
14
+ include SleepingKingStudios::Tasks::Ci::ResultsHelpers
15
+
16
+ def self.description
17
+ 'Runs each spec file as an individual RSpec process.'
18
+ end # class method description
19
+
20
+ def self.task_name
21
+ 'rspec_each'
22
+ end # class method task_name
23
+
24
+ option :quiet,
25
+ :aliases => '-q',
26
+ :type => :boolean,
27
+ :default => false,
28
+ :desc => 'Do not write spec results to STDOUT.'
29
+ option :raw,
30
+ :aliases => '-r',
31
+ :type => :boolean,
32
+ :default => false,
33
+ :desc => 'Return a Hash instead of a results object.'
34
+
35
+ def call *groups
36
+ mute! if quiet?
37
+
38
+ files = files_list(groups)
39
+
40
+ say "Running #{files.count} spec files..."
41
+ say "\n"
42
+
43
+ results = run_files(files)
44
+
45
+ report_pending results
46
+ report_failing results
47
+ report_errored results
48
+ report_totals results
49
+
50
+ raw? ? results.to_h : results
51
+ end # method call
52
+
53
+ private
54
+
55
+ def aggregate_results file, results, totals
56
+ if results.errored?
57
+ totals['errored_files'] << file
58
+ elsif results.failing?
59
+ totals['failing_files'] << file
60
+ elsif results.pending?
61
+ totals['pending_files'] << file
62
+ end # if-else
63
+
64
+ totals['file_count'] += 1
65
+ end # method aggregate_results
66
+
67
+ def build_totals
68
+ {
69
+ 'pending_files' => [],
70
+ 'failing_files' => [],
71
+ 'errored_files' => [],
72
+ 'file_count' => 0
73
+ } # end results
74
+ end # method build_totals
75
+
76
+ def files_list groups
77
+ groups = %w(spec) if groups.empty?
78
+
79
+ groups.map do |group_or_file|
80
+ if File.extname(group_or_file).empty?
81
+ Dir[File.join group_or_file, pattern]
82
+ else
83
+ [group_or_file]
84
+ end # if-else
85
+ end. # map
86
+ flatten.
87
+ uniq
88
+ end # method files_list
89
+
90
+ def pattern
91
+ '**{,/*/**}/*_spec.rb'
92
+ end # method pattern
93
+
94
+ def report_errored results
95
+ return unless results.errored?
96
+
97
+ say "\nErrored:\n\n"
98
+
99
+ results.errored_files.each { |file| say " #{file}", :red }
100
+ end # report_pending
101
+
102
+ def report_failing results
103
+ return unless results.failing?
104
+
105
+ say "\nFailures:\n\n"
106
+
107
+ results.failing_files.each { |file| say " #{file}", :red }
108
+ end # report_pending
109
+
110
+ def report_pending results
111
+ return unless results.pending?
112
+
113
+ say "\nPending:\n\n"
114
+
115
+ results.pending_files.each { |file| say " #{file}", :yellow }
116
+ end # report_pending
117
+
118
+ def report_totals results
119
+ say "\nFinished in #{results.duration.round(2)} seconds"
120
+
121
+ if results.failing? || results.errored?
122
+ say results.to_s, :red
123
+ elsif results.pending? || results.empty?
124
+ say results.to_s, :yellow
125
+ else
126
+ say results.to_s, :green
127
+ end # if-elsif-else
128
+ end # method report_totals
129
+
130
+ def rspec_runner
131
+ RSpecRunner.new(:env => { :coverage => false })
132
+ end # method rspec_runner
133
+
134
+ def run_file file
135
+ results = rspec_runner.call(:files => [file])
136
+ results = RSpecResults.new(results)
137
+
138
+ say "#{set_color results_state(results), results_color(results)} #{file}"
139
+
140
+ results
141
+ end # method run_file
142
+
143
+ def run_files files
144
+ start = Time.now
145
+ totals = build_totals
146
+
147
+ files.each { |file| aggregate_results(file, run_file(file), totals) }
148
+
149
+ totals['duration'] = Time.now - start
150
+
151
+ RSpecEachResults.new(totals)
152
+ end # method run_files
153
+ end # class
154
+
155
+ # rubocop:enable Metrics/ClassLength
156
+ end # module
@@ -0,0 +1,126 @@
1
+ # lib/sleeping_king_studios/tasks/ci/rspec_results.rb
2
+
3
+ require 'sleeping_king_studios/tasks/ci'
4
+
5
+ module SleepingKingStudios::Tasks::Ci
6
+ # Encapsulates the results of an RSpec call.
7
+ class RSpecResults
8
+ # @param results [Hash] The raw results of the RSpec call.
9
+ def initialize results
10
+ @results = results
11
+ end # constructor
12
+
13
+ # @param other [RSpecResults] The other results object to compare.
14
+ #
15
+ # @return [Boolean] True if the results are equal, otherwise false.
16
+ def == other
17
+ if other.is_a?(Hash)
18
+ empty? ? other.empty? : to_h == other
19
+ elsif other.is_a?(RSpecResults)
20
+ to_h == other.to_h
21
+ else
22
+ false
23
+ end # if-elsif-else
24
+ end # method ==
25
+
26
+ # @return [Float] The duration value.
27
+ def duration
28
+ @results.fetch('duration', 0.0)
29
+ end # method duration
30
+
31
+ # @return [Boolean] True if there are no examples, otherwise false.
32
+ def empty?
33
+ example_count.zero?
34
+ end # method empty?
35
+
36
+ # @return [Boolean] True if there are no results or if errors occurred
37
+ # outside of examples, otherwise false.
38
+ def errored?
39
+ @results.empty? || !error_count.zero?
40
+ end # method errored?
41
+
42
+ # @return [Integer] The number of errors that occurred outside of examples.
43
+ def error_count
44
+ @results.fetch('error_count', 0)
45
+ end # method error_count
46
+
47
+ # @return [Integer] The total number of examples.
48
+ def example_count
49
+ @results.fetch('example_count', 0)
50
+ end # method example_count
51
+
52
+ # @return [Boolean] True if there are any failing examples, otherwise false.
53
+ def failing?
54
+ !failure_count.zero?
55
+ end # method failing?
56
+
57
+ # @return [Integer] The number of failing examples.
58
+ def failure_count
59
+ @results.fetch('failure_count', 0)
60
+ end # method failure_count
61
+
62
+ # Adds the given result values and returns a new results object with the
63
+ # sums.
64
+ #
65
+ # @param other [RSpecResults] The results to add.
66
+ #
67
+ # @return [RSpecResults] The total results.
68
+ def merge other
69
+ self.class.new(
70
+ 'duration' => duration + other.duration,
71
+ 'example_count' => example_count + other.example_count,
72
+ 'failure_count' => failure_count + other.failure_count,
73
+ 'pending_count' => pending_count + other.pending_count
74
+ ) # end new
75
+ end # method merge
76
+
77
+ # @return [Boolean] True if there are any pending examples, otherwise false.
78
+ def pending?
79
+ !pending_count.zero?
80
+ end # method pending?
81
+
82
+ # @return [Intger] The number of pending examples.
83
+ def pending_count
84
+ @results.fetch('pending_count', 0)
85
+ end # method pending_count
86
+
87
+ def pluralize count, singular, plural = nil
88
+ "#{count} #{tools.integer.pluralize count, singular, plural}"
89
+ end # method pluralize
90
+
91
+ # @return [Hash] The hash representation of the results.
92
+ def to_h
93
+ {
94
+ 'duration' => duration,
95
+ 'example_count' => example_count,
96
+ 'failure_count' => failure_count,
97
+ 'pending_count' => pending_count,
98
+ 'error_count' => error_count
99
+ } # end hash
100
+ end # method to_h
101
+
102
+ # rubocop:disable Metrics/AbcSize
103
+
104
+ # @return [String] The string representation of the results.
105
+ def to_s
106
+ str = pluralize(example_count, 'example')
107
+
108
+ str << ', ' << pluralize(failure_count, 'failure')
109
+
110
+ str << ', ' << pending_count.to_s << ' pending' if pending?
111
+
112
+ if error_count.zero?
113
+ str << " in #{duration.round(2)} seconds"
114
+ else
115
+ str << ', ' << pluralize(error_count, 'error') <<
116
+ ' occurred outside of examples'
117
+ end # if-else
118
+ end # method to_s
119
+
120
+ # rubocop:enable Metrics/AbcSize
121
+
122
+ def tools
123
+ SleepingKingStudios::Tools::Toolbelt.instance
124
+ end # method tools
125
+ end # class
126
+ end # module