polytrix 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop-todo.yml +14 -5
  3. data/Gemfile +2 -1
  4. data/README.md +139 -177
  5. data/Rakefile +5 -12
  6. data/bin/polytrix +0 -1
  7. data/features/bootstrapping.feature +0 -3
  8. data/features/cloning.feature +0 -3
  9. data/features/show.feature +38 -0
  10. data/features/states.feature +12 -13
  11. data/features/step_definitions/sdk_steps.rb +0 -4
  12. data/lib/polytrix/challenge.rb +135 -53
  13. data/lib/polytrix/challenge_result.rb +0 -2
  14. data/lib/polytrix/challenge_runner.rb +28 -18
  15. data/lib/polytrix/cli.rb +53 -69
  16. data/lib/polytrix/color.rb +2 -2
  17. data/lib/polytrix/command/action.rb +4 -3
  18. data/lib/polytrix/command/list.rb +39 -28
  19. data/lib/polytrix/command/report.rb +9 -86
  20. data/lib/polytrix/command/reports/code2doc.rb +72 -0
  21. data/lib/polytrix/command/reports/dashboard.rb +125 -0
  22. data/lib/polytrix/command/show.rb +148 -0
  23. data/lib/polytrix/command.rb +37 -104
  24. data/lib/polytrix/configuration.rb +14 -18
  25. data/lib/polytrix/{core/hashie.rb → dash.rb} +4 -3
  26. data/lib/polytrix/documentation/code_segmenter.rb +8 -8
  27. data/lib/polytrix/documentation/comment_styles.rb +1 -1
  28. data/lib/polytrix/documentation/helpers/code_helper.rb +9 -0
  29. data/lib/polytrix/documentation_generator.rb +11 -14
  30. data/lib/polytrix/error.rb +104 -97
  31. data/lib/polytrix/executor.rb +33 -0
  32. data/lib/polytrix/{runners → executors}/buff_shellout_executor.rb +1 -1
  33. data/lib/polytrix/executors/linux_challenge_executor.rb +29 -0
  34. data/lib/polytrix/executors/mixlib_shellout_executor.rb +55 -0
  35. data/lib/polytrix/{runners/windows_challenge_runner.rb → executors/windows_challenge_executor.rb} +4 -11
  36. data/lib/polytrix/{core/implementor.rb → implementor.rb} +10 -6
  37. data/lib/polytrix/manifest.rb +2 -31
  38. data/lib/polytrix/{reports → reporters}/hash_reporter.rb +6 -2
  39. data/lib/polytrix/{reports → reporters}/json_reporter.rb +2 -2
  40. data/lib/polytrix/{reports → reporters}/markdown_reporter.rb +7 -2
  41. data/lib/polytrix/{reports → reporters}/yaml_reporter.rb +2 -2
  42. data/lib/polytrix/reporters.rb +27 -0
  43. data/lib/polytrix/result.rb +6 -5
  44. data/lib/polytrix/spies/file_system_spy.rb +15 -0
  45. data/lib/polytrix/spies.rb +61 -0
  46. data/lib/polytrix/state_file.rb +1 -20
  47. data/lib/polytrix/util.rb +157 -62
  48. data/lib/polytrix/validation.rb +41 -2
  49. data/lib/polytrix/validator.rb +9 -4
  50. data/lib/polytrix/version.rb +1 -1
  51. data/lib/polytrix.rb +110 -105
  52. data/polytrix.gemspec +7 -2
  53. data/polytrix.yml +16 -13
  54. data/resources/assets/pygments/autumn.css +58 -0
  55. data/resources/assets/pygments/borland.css +46 -0
  56. data/resources/assets/pygments/bw.css +34 -0
  57. data/resources/assets/pygments/colorful.css +61 -0
  58. data/resources/assets/pygments/default.css +62 -0
  59. data/resources/assets/pygments/emacs.css +61 -0
  60. data/resources/assets/pygments/friendly.css +61 -0
  61. data/resources/assets/pygments/fruity.css +69 -0
  62. data/resources/assets/pygments/github.css +61 -0
  63. data/resources/assets/pygments/manni.css +61 -0
  64. data/resources/assets/pygments/monokai.css +64 -0
  65. data/resources/assets/pygments/murphy.css +61 -0
  66. data/resources/assets/pygments/native.css +69 -0
  67. data/resources/assets/pygments/pastie.css +60 -0
  68. data/resources/assets/pygments/perldoc.css +58 -0
  69. data/resources/assets/pygments/tango.css +69 -0
  70. data/resources/assets/pygments/trac.css +59 -0
  71. data/resources/assets/pygments/vim.css +69 -0
  72. data/resources/assets/pygments/vs.css +33 -0
  73. data/resources/assets/pygments/zenburn.css +1 -0
  74. data/resources/assets/style.css +41 -0
  75. data/resources/templates/dashboard/files/dashboard.html.tt +82 -0
  76. data/resources/templates/dashboard/templates/_test_report.html.tt +87 -0
  77. data/samples/bootstrap.sh +2 -0
  78. data/samples/clone.sh +2 -0
  79. data/samples/code2doc.sh +4 -0
  80. data/samples/docs/samples/code2doc/java/katas-hello_world-java.md +17 -0
  81. data/samples/docs/samples/code2doc/java/katas-quine-java.md +35 -0
  82. data/samples/docs/samples/code2doc/python/katas-hello_world-python.md +5 -0
  83. data/samples/docs/samples/code2doc/python/katas-quine-python.md +6 -0
  84. data/samples/docs/samples/code2doc/ruby/katas-hello_world-ruby.md +11 -0
  85. data/samples/exec.sh +2 -0
  86. data/samples/polytrix.rb +2 -2
  87. data/samples/polytrix.yml +5 -2
  88. data/samples/show.sh +4 -0
  89. data/samples/test.sh +2 -0
  90. data/samples/tests/polytrix/validators.rb +2 -2
  91. data/samples/verify.sh +3 -0
  92. data/scripts/wrapper +4 -7
  93. data/spec/fabricators/challenge_fabricator.rb +2 -9
  94. data/spec/fabricators/implementor_fabricator.rb +0 -8
  95. data/spec/fabricators/manifest_fabricator.rb +2 -9
  96. data/spec/fabricators/validator_fabricator.rb +2 -4
  97. data/spec/polytrix/challenge_runner_spec.rb +20 -0
  98. data/spec/polytrix/documentation/helpers/code_helper_spec.rb +7 -7
  99. data/spec/polytrix/file_finder_spec.rb +5 -5
  100. data/spec/polytrix/manifest_spec.rb +0 -21
  101. data/spec/polytrix/result_spec.rb +14 -14
  102. data/spec/polytrix/validator_registry_spec.rb +4 -4
  103. data/spec/polytrix/validator_spec.rb +9 -9
  104. data/spec/polytrix_spec.rb +1 -25
  105. data/spec/spec_helper.rb +8 -1
  106. metadata +130 -38
  107. data/features/execution.feature +0 -53
  108. data/features/fixtures/spec/polytrix_spec.rb +0 -7
  109. data/lib/polytrix/cli/report.rb +0 -84
  110. data/lib/polytrix/command/rundoc.rb +0 -27
  111. data/lib/polytrix/core/file_system_helper.rb +0 -75
  112. data/lib/polytrix/core/manifest_section.rb +0 -4
  113. data/lib/polytrix/core/string_helpers.rb +0 -15
  114. data/lib/polytrix/documentation/view_helper.rb +0 -21
  115. data/lib/polytrix/rspec/documentation_formatter.rb +0 -66
  116. data/lib/polytrix/rspec/yaml_report.rb +0 -51
  117. data/lib/polytrix/rspec.rb +0 -56
  118. data/lib/polytrix/runners/executor.rb +0 -34
  119. data/lib/polytrix/runners/linux_challenge_runner.rb +0 -23
  120. data/lib/polytrix/runners/middleware/change_directory.rb +0 -20
  121. data/lib/polytrix/runners/middleware/feature_executor.rb +0 -24
  122. data/lib/polytrix/runners/middleware/setup_env_vars.rb +0 -42
  123. data/lib/polytrix/runners/mixlib_shellout_executor.rb +0 -83
  124. data/lib/polytrix/validations.rb +0 -23
  125. data/samples/scripts/wrapper +0 -7
  126. data/spec/polytrix/middleware/feature_executor_spec.rb +0 -48
  127. data/spec/polytrix/validations_spec.rb +0 -16
data/lib/polytrix/util.rb CHANGED
@@ -1,20 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  #
3
- # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
- #
5
- # Copyright (C) 2012, 2013, 2014, Fletcher Nichol
6
- #
7
- # Licensed under the Apache License, Version 2.0 (the "License");
8
- # you may not use this file except in compliance with the License.
9
- # You may obtain a copy of the License at
10
- #
11
- # http://www.apache.org/licenses/LICENSE-2.0
12
- #
13
- # Unless required by applicable law or agreed to in writing, software
14
- # distributed under the License is distributed on an "AS IS" BASIS,
15
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- # See the License for the specific language governing permissions and
17
- # limitations under the License.
3
+ # Much of this code has been adapted from Fletcher Nichol (<fnichol@nichol.ca>)
4
+ # work on test-kitchen.
18
5
 
19
6
  module Polytrix
20
7
  # Stateless utility methods used in different contexts. Essentially a mini
@@ -59,14 +46,12 @@ module Polytrix
59
46
  # @return [Object] a converted hash with all keys as symbols
60
47
  def self.symbolized_hash(obj)
61
48
  if obj.is_a?(Hash)
62
- obj.reduce({}) do |h, (k, v)|
49
+ obj.each_with_object({}) do |(k, v), h|
63
50
  h[k.to_sym] = symbolized_hash(v)
64
- h
65
51
  end
66
52
  elsif obj.is_a?(Array)
67
- obj.reduce([]) do |a, e|
53
+ obj.each_with_object([]) do |e, a|
68
54
  a << symbolized_hash(e)
69
- a
70
55
  end
71
56
  else
72
57
  obj
@@ -82,14 +67,12 @@ module Polytrix
82
67
  # @return [Object] a converted hash with all keys as strings
83
68
  def self.stringified_hash(obj)
84
69
  if obj.is_a?(Hash)
85
- obj.reduce({}) do |h, (k, v)|
70
+ obj.each_with_object({}) do |(k, v), h|
86
71
  h[k.to_s] = stringified_hash(v)
87
- h
88
72
  end
89
73
  elsif obj.is_a?(Array)
90
- obj.reduce([]) do |a, e|
74
+ obj.each_with_object([]) do |e, a|
91
75
  a << stringified_hash(e)
92
- a
93
76
  end
94
77
  else
95
78
  obj
@@ -107,49 +90,161 @@ module Polytrix
107
90
  format('(%dm%.2fs)', minutes, seconds)
108
91
  end
109
92
 
110
- # Generates a command (or series of commands) wrapped so that it can be
111
- # invoked on a remote instance or locally.
112
- #
113
- # This method uses the Bourne shell (/bin/sh) to maximize the chance of
114
- # cross platform portability on Unixlike systems.
115
- #
116
- # @param [String] the command
117
- # @return [String] a wrapped command string
118
- def self.wrap_command(cmd)
119
- cmd = 'false' if cmd.nil?
120
- cmd = 'true' if cmd.to_s.empty?
121
- cmd = cmd.sub(/\n\Z/, '') if cmd =~ /\n\Z/
122
-
123
- "sh -c '\n#{cmd}\n'"
93
+ module String
94
+ module ClassMethods
95
+ def slugify(*labels)
96
+ labels.map do |label|
97
+ label.downcase.gsub(/[\.\s-]/, '_')
98
+ end.join('-')
99
+ end
100
+
101
+ def ansi2html(text)
102
+ HTML.from_ansi(text)
103
+ end
104
+
105
+ def escape_html(text)
106
+ HTML.escape_html(text)
107
+ end
108
+ alias_method :h, :escape_html
109
+
110
+ def highlight(source, opts = {})
111
+ return nil if source.nil?
112
+
113
+ opts[:language] ||= 'ruby'
114
+ opts[:formatter] ||= 'terminal256'
115
+ Highlight.new(opts).highlight(source)
116
+ end
117
+ end
118
+
119
+ def self.included(base)
120
+ base.extend(ClassMethods)
121
+ end
122
+
123
+ include ClassMethods
124
124
  end
125
125
 
126
- # Modifes the given string to strip leading whitespace on each line, the
127
- # amount which is calculated by using the first line of text.
128
- #
129
- # @example
130
- #
131
- # string = <<-STRING
132
- # a
133
- # b
134
- # c
135
- # STRING
136
- # Util.outdent!(string) # => "a\n b\nc\n"
137
- #
138
- # @param string [String] the string that will be modified
139
- # @return [String] the modified string
140
- def self.outdent!(string)
141
- string.gsub!(/^ {#{string.index(/[^ ]/)}}/, '')
126
+ class Highlight
127
+ def initialize(opts)
128
+ @lexer = Rouge::Lexer.find(opts[:language]) || Rouge::Lexer.guess_by_filename(opts[:filename])
129
+ @formatter = opts[:formatter]
130
+ end
131
+
132
+ def highlight(source)
133
+ Rouge.highlight(source, @lexer, @formatter)
134
+ end
142
135
  end
143
136
 
144
- # Returns a set of Bourne Shell (AKA /bin/sh) compatible helper
145
- # functions. This function is usually called inline in a string that
146
- # will be executed remotely on a test instance.
147
- #
148
- # @return [String] a string representation of useful helper functions
149
- def self.shell_helpers
150
- IO.read(File.join(
151
- File.dirname(__FILE__), %w[.. .. support download_helpers.sh]
152
- ))
137
+ class HTML
138
+ ANSICODES = {
139
+ '1' => 'bold',
140
+ '4' => 'underline',
141
+ '30' => 'black',
142
+ '31' => 'red',
143
+ '32' => 'green',
144
+ '33' => 'yellow',
145
+ '34' => 'blue',
146
+ '35' => 'magenta',
147
+ '36' => 'cyan',
148
+ '37' => 'white',
149
+ '40' => 'bg-black',
150
+ '41' => 'bg-red',
151
+ '42' => 'bg-green',
152
+ '43' => 'bg-yellow',
153
+ '44' => 'bg-blue',
154
+ '45' => 'bg-magenta',
155
+ '46' => 'bg-cyan',
156
+ '47' => 'bg-white'
157
+ }
158
+
159
+ def self.from_ansi(text)
160
+ ansi = StringScanner.new(text)
161
+ html = StringIO.new
162
+ until ansi.eos?
163
+ if ansi.scan(/\e\[0?m/)
164
+ html.print(%(</span>))
165
+ elsif ansi.scan(/\e\[0?(\d+)m/)
166
+ # use class instead of style?
167
+ style = ANSICODES[ansi[1]] || 'text-reset'
168
+ html.print(%(<span class="#{style}">))
169
+ else
170
+ html.print(ansi.scan(/./m))
171
+ end
172
+ end
173
+ html.string
174
+ end
175
+
176
+ # From Rack
177
+
178
+ ESCAPE_HTML = {
179
+ '&' => '&amp;',
180
+ '<' => '&lt;',
181
+ '>' => '&gt;',
182
+ "'" => '&#x27;',
183
+ '"' => '&quot;',
184
+ '/' => '&#x2F;'
185
+ }
186
+ ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
187
+
188
+ # Escape ampersands, brackets and quotes to their HTML/XML entities.
189
+ def self.escape_html(string)
190
+ string.to_s.gsub(ESCAPE_HTML_PATTERN) { |c| ESCAPE_HTML[c] }
191
+ end
192
+ end
193
+
194
+ module FileSystem
195
+ include Polytrix::Logging
196
+ include Polytrix::Util::String
197
+
198
+ # Finds a file by loosely matching the file name to a scenario name
199
+ def find_file(search_path, scenario_name, ignored_patterns = nil)
200
+ ignored_patterns ||= read_gitignore(search_path)
201
+ glob_string = "#{search_path}/**/*#{slugify(scenario_name)}.*"
202
+ potential_files = Dir.glob(glob_string, File::FNM_CASEFOLD)
203
+ potential_files.concat Dir.glob(glob_string.gsub('_', '-'), File::FNM_CASEFOLD)
204
+ potential_files.concat Dir.glob(glob_string.gsub('_', ''), File::FNM_CASEFOLD)
205
+
206
+ # Filter out ignored filesFind the first file, not including generated files
207
+ files = potential_files.select do |f|
208
+ !ignored? ignored_patterns, search_path, f
209
+ end
210
+
211
+ # Select the shortest path, likely the best match
212
+ file = files.min_by(&:length)
213
+
214
+ fail Errno::ENOENT, "No file was found for #{scenario_name} within #{search_path}" if file.nil?
215
+ Pathname.new file
216
+ end
217
+
218
+ def relativize(file, base_path)
219
+ absolute_file = File.absolute_path(file)
220
+ absolute_base_path = File.absolute_path(base_path)
221
+ Pathname.new(absolute_file).relative_path_from Pathname.new(absolute_base_path)
222
+ end
223
+
224
+ private
225
+
226
+ # @api private
227
+ def read_gitignore(dir)
228
+ gitignore_file = "#{dir}/.gitignore"
229
+ File.read(gitignore_file)
230
+ rescue
231
+ ''
232
+ end
233
+
234
+ # @api private
235
+ def ignored?(ignored_patterns, base_path, target_file)
236
+ # Trying to match the git ignore rules but there's some discrepencies.
237
+ ignored_patterns.split.find do |pattern|
238
+ # if git ignores a folder, we should ignore all files it contains
239
+ pattern = "#{pattern}**" if pattern[-1] == '/'
240
+ started_with_slash = pattern.start_with? '/'
241
+
242
+ pattern.gsub!(/\A\//, '') # remove leading slashes since we're searching from root
243
+ file = relativize(target_file, base_path)
244
+ ignored = file.fnmatch? pattern
245
+ ignored || (file.fnmatch? "**/#{pattern}" unless started_with_slash)
246
+ end
247
+ end
153
248
  end
154
249
  end
155
250
  end
@@ -2,19 +2,58 @@ require 'hashie/dash'
2
2
 
3
3
  module Polytrix
4
4
  class Validation < Polytrix::ManifestSection
5
+ # TODO: Should we have (expectation) 'failed' vs (unexpected) 'error'?
5
6
  ALLOWABLE_STATES = %w(passed pending failed skipped)
6
- property :validated_by, required: true
7
- property :result
7
+
8
+ property :result, required: true
9
+ property :error
8
10
 
9
11
  def result=(state)
12
+ state = state.to_s
10
13
  fail invalidate_state_error unless ALLOWABLE_STATES.include? state
11
14
  super
12
15
  end
13
16
 
17
+ ALLOWABLE_STATES.each do |state|
18
+ define_method "#{state}?" do
19
+ result == state?
20
+ end
21
+ end
22
+
23
+ # Gets the source of the validation code block where a ValidationFailure occurred.
24
+ def error_source
25
+ return nil if error.nil?
26
+ source_from_error(error)
27
+ end
28
+
14
29
  protected
15
30
 
16
31
  def invalidate_state_error(state)
17
32
  ArgumentError.new "Invalid result state: #{state}, should be one of #{ALLOWABLE_STATES.inspect}"
18
33
  end
34
+
35
+ def source_from_error(e)
36
+ error_location = e.backtrace_locations.delete_if { |l| l.absolute_path =~ /gems\/rspec-/ }.first
37
+ error_source = File.read(error_location.absolute_path)
38
+ error_lineno = error_location.lineno - 1 # lineno counts from 1
39
+ get_dedented_block(error_source, error_lineno)
40
+ end
41
+
42
+ def get_dedented_block(source_text, target_lineno)
43
+ block = []
44
+ lines = source_text.lines
45
+ target_indent = lines[target_lineno][/\A */].size
46
+ lines[0...target_lineno].reverse.each do |line|
47
+ indent = line[/\A */].size
48
+ block.prepend line
49
+ break if indent < target_indent
50
+ end
51
+ lines[target_lineno..lines.size].each do |line|
52
+ indent = line[/\A */].size
53
+ block.push line
54
+ break if indent < target_indent
55
+ end
56
+ block.join
57
+ end
19
58
  end
20
59
  end
@@ -5,21 +5,26 @@ module Polytrix
5
5
  include RSpec::Matchers
6
6
 
7
7
  UNIVERSAL_MATCHER = //
8
- attr_reader :description, :suite, :sample, :level, :callback
8
+ attr_reader :description, :suite, :scenario, :level, :callback
9
9
 
10
10
  def initialize(description, scope = {}, &validator)
11
11
  @description = description
12
12
  @suite = scope[:suite] ||= UNIVERSAL_MATCHER
13
- @sample = scope[:sample] ||= UNIVERSAL_MATCHER
13
+ @scenario = scope[:scenario] ||= UNIVERSAL_MATCHER
14
14
  @callback = validator
15
15
  end
16
16
 
17
17
  def should_validate?(challenge)
18
- !!(@suite.match(challenge.suite.to_s) && @sample.match(challenge.name.to_s))
18
+ # TODO: Case-insensitive matching?
19
+ !!(@suite.match(challenge.suite.to_s) && @scenario.match(challenge.name.to_s)) # rubocop:disable Style/DoubleNegation
19
20
  end
20
21
 
21
22
  def validate(challenge)
22
- instance_exec challenge, &@callback if should_validate?(challenge)
23
+ instance_exec(challenge, &@callback) if should_validate?(challenge)
24
+ challenge.result.validations[description] = Validation.new(result: :passed)
25
+ rescue StandardError, RSpec::Expectations::ExpectationNotMetError => e
26
+ validation = Validation.new(result: :failed, error: ValidationFailure.new(e.message, e))
27
+ challenge.result.validations[description] = validation
23
28
  end
24
29
 
25
30
  def to_s
@@ -1,3 +1,3 @@
1
1
  module Polytrix
2
- VERSION = '0.1.2'
2
+ VERSION = '0.1.3'
3
3
  end
data/lib/polytrix.rb CHANGED
@@ -1,140 +1,162 @@
1
+ require 'thor'
1
2
  require 'pathname'
2
- require 'polytrix/error'
3
- require 'polytrix/core/hashie'
4
- require 'polytrix/core/string_helpers'
5
3
  require 'polytrix/version'
6
- require 'polytrix/util'
7
- require 'polytrix/color'
8
4
  require 'polytrix/logger'
9
5
  require 'polytrix/logging'
6
+ require 'polytrix/error'
7
+ require 'polytrix/dash'
8
+ require 'polytrix/util'
9
+ require 'polytrix/color'
10
10
  require 'polytrix/state_file'
11
- require 'polytrix/core/file_system_helper'
12
- require 'polytrix/runners/executor'
13
- require 'polytrix/core/manifest_section'
14
- require 'polytrix/core/implementor'
11
+ require 'polytrix/spies'
12
+ # TODO: Merge these two classes?
13
+ require 'polytrix/executor'
15
14
  require 'polytrix/challenge_runner'
15
+ require 'polytrix/implementor'
16
16
  require 'polytrix/challenge_result'
17
17
  require 'polytrix/challenge'
18
18
  require 'polytrix/challenges'
19
19
  require 'polytrix/manifest'
20
20
  require 'polytrix/configuration'
21
21
  require 'polytrix/validation'
22
- require 'polytrix/validations'
23
22
  require 'polytrix/result'
24
23
  require 'polytrix/documentation_generator'
25
24
  require 'polytrix/validator'
26
25
  require 'polytrix/validator_registry'
27
26
 
28
- require 'polytrix/rspec'
29
-
30
27
  module Polytrix
31
28
  include Polytrix::DefaultLogger
32
29
  include Polytrix::Logging
33
30
 
31
+ # File extensions that Polytrix can automatically detect/execute
32
+ SUPPORTED_EXTENSIONS = %w(py rb js)
33
+
34
34
  class << self
35
- include Polytrix::Core::FileSystemHelper
35
+ include Polytrix::Util::FileSystem
36
+
37
+ DEFAULT_MANIFEST_FILE = 'polytrix.yml'
36
38
 
37
39
  # @return [Mutex] a common mutex for global coordination
38
40
  attr_accessor :mutex
39
41
 
40
- def reset
41
- @configuration = nil
42
- Polytrix::ValidatorRegistry.clear
43
- end
42
+ # @return [Logger] the common Polytrix logger
43
+ attr_accessor :logger
44
+
45
+ def setup(options, manifest_file = DEFAULT_MANIFEST_FILE)
46
+ # manifest_file = File.expand_path manifest
47
+ if File.exist? manifest_file
48
+ logger.debug "Loading manifest file: #{manifest_file}"
49
+ Polytrix.configuration.manifest = manifest_file
50
+ elsif options[:solo]
51
+ solo_setup(options)
52
+ else
53
+ fail StandardError, "No manifest found at #{manifest_file} and not using --solo mode"
54
+ end
44
55
 
45
- # The {Polytrix::Manifest} that describes the test scenarios known to Polytrix.
46
- def manifest
47
- configuration.manifest
48
- end
56
+ Polytrix.configuration.documentation_dir = options[:target_dir]
57
+ Polytrix.configuration.documentation_format = options[:format]
49
58
 
50
- # The set of {Polytrix::Implementor}s registered with Polytrix.
51
- def implementors
52
- manifest.implementors.values
53
- end
59
+ manifest.build_challenges
54
60
 
55
- def find_implementor(file)
56
- existing_implementor = recursive_parent_search(File.dirname(file)) do |path|
57
- implementors.find do |implementor|
58
- File.absolute_path(implementor.basedir) == File.absolute_path(path)
59
- end
60
- end
61
- return existing_implementor if existing_implementor
61
+ test_dir = options[:test_dir].nil? ? nil : File.expand_path(options[:test_dir])
62
+ return nil unless test_dir && File.directory?(test_dir)
62
63
 
63
- nil
64
+ $LOAD_PATH.unshift test_dir
65
+ Dir["#{test_dir}/**/*.rb"].each do | file_to_require |
66
+ require relativize(file_to_require, test_dir).to_s.gsub('.rb', '')
67
+ end
64
68
  end
65
69
 
66
- # Invokes the clone action for each SDK.
67
- # @see Polytrix::Implementor#clone
68
- def clone(*sdks)
69
- select_implementors(sdks).each do |implementor|
70
- implementor.clone
70
+ def solo_setup(options)
71
+ suites = {}
72
+ solo_basedir = options[:solo]
73
+ solo_glob = options.fetch('solo_glob', "**/*.{#{SUPPORTED_EXTENSIONS.join(',')}}")
74
+ Dir[File.join(solo_basedir, solo_glob)].sort.each do | code_sample |
75
+ code_sample = Pathname.new(code_sample)
76
+ suite_name = relativize(code_sample.dirname, solo_basedir).to_s
77
+ suite_name = solo_basedir if suite_name == '.'
78
+ scenario_name = code_sample.basename(code_sample.extname).to_s
79
+ suite = suites[suite_name] ||= Polytrix::Manifest::Suite.new(samples: [])
80
+ suite.samples << scenario_name
71
81
  end
82
+ @manifest = Polytrix.configuration.manifest = Polytrix::Manifest.new(
83
+ implementors: {
84
+ File.basename(solo_basedir) => {
85
+ basedir: solo_basedir
86
+ }
87
+ },
88
+ suites: suites
89
+ )
72
90
  end
73
91
 
74
- # Invokes the bootstrap action for each SDK.
75
- # @see Polytrix::Implementor#bootstrap
76
- def bootstrap(*sdks)
77
- select_implementors(sdks).each do |implementor|
78
- implementor.bootstrap
92
+ def select_scenarios(regexp)
93
+ regexp ||= 'all'
94
+ scenarios = if regexp == 'all'
95
+ manifest.challenges.values
96
+ else
97
+ manifest.challenges.get(regexp) || manifest.challenges.get_all(/#{regexp}/)
98
+ end
99
+ if scenarios.is_a? Array
100
+ scenarios
101
+ else
102
+ [scenarios]
79
103
  end
80
104
  end
81
105
 
82
- def exec(*files)
83
- # files.map do | file |
84
- # Dir.glob file
85
- # end.flatten
86
-
87
- files.each do | file |
88
- implementor = find_implementor(file) # || exec_options[:default_implementor]
89
-
90
- extension = File.extname(file)
91
- name = File.basename(file, extension)
92
- challenge_data = {
93
- name: name,
94
- # language: extension,
95
- source_file: File.expand_path(file, Dir.pwd)
96
- }
97
- challenge = implementor.build_challenge challenge_data
98
- challenge.exec
106
+ def filter_scenarios(regexp, options = {})
107
+ select_scenarios(regexp).tap do |scenarios|
108
+ scenarios.keep_if { |scenario| scenario.failed? == options[:failed] } unless options[:failed].nil?
109
+ scenarios.keep_if { |scenario| scenario.skipped? == options[:skipped] } unless options[:skipped].nil?
110
+ scenarios.keep_if { |scenario| scenario.sample? == options[:samples] } unless options[:samples].nil?
99
111
  end
100
112
  end
101
113
 
102
- # Registers a {Polytrix::Validator} that will be used during test
103
- # execution on matching {Polytrix::Challenge}s.
104
- def validate(desc, scope = { suite: //, sample: // }, &block)
105
- fail ArgumentError 'You must pass block' unless block_given?
106
- validator = Polytrix::Validator.new(desc, scope, &block)
114
+ # Returns a default file logger which emits on standard output and to a
115
+ # log file.
116
+ #
117
+ # @return [Logger] a logger
118
+ def default_file_logger
119
+ logfile = File.expand_path(File.join('.polytrix', 'logs', 'polytrix.log'))
120
+ Logger.new(stdout: $stdout, logdev: logfile, level: env_log)
121
+ end
107
122
 
108
- Polytrix::ValidatorRegistry.register validator
109
- validator
123
+ # Determine the default log level from an environment variable, if it is
124
+ # set.
125
+ #
126
+ # @return [Integer,nil] a log level or nil if not set
127
+ # @api private
128
+ def env_log
129
+ level = ENV['POLYTRIX_LOG'] && ENV['POLYTRIX_LOG'].downcase.to_sym
130
+ level = Util.to_logger_level(level) unless level.nil?
131
+ level
110
132
  end
111
133
 
112
- def load_tests
113
- manifest.build_challenges
114
- Polytrix::RSpec.run_manifest(manifest)
134
+ # Default log level verbosity
135
+ DEFAULT_LOG_LEVEL = :info
136
+
137
+ def reset
138
+ @configuration = nil
139
+ Polytrix::ValidatorRegistry.clear
115
140
  end
116
141
 
117
- # Runs all of the tests described in the {manifest}
118
- def verify(implementors = [])
119
- test_env = ENV['TEST_ENV_NUMBER'].to_i
120
- rspec_options = %W[--color -f documentation -f Polytrix::RSpec::YAMLReport -o reports/test_report#{test_env}.yaml]
121
- rspec_options.concat Polytrix.configuration.rspec_options.split if Polytrix.configuration.rspec_options
122
- unless implementors.empty?
123
- target_sdks = implementors.map(&:name)
124
- Polytrix.implementors.map(&:name).each do |sdk|
125
- # We don't have an "or" for tags, so it's easier to exclude than include multiple tags
126
- rspec_options.concat %W[-t ~#{sdk.to_sym}] unless target_sdks.include? sdk
127
- end
128
- end
142
+ # The {Polytrix::Manifest} that describes the test scenarios known to Polytrix.
143
+ def manifest
144
+ configuration.manifest
145
+ end
129
146
 
130
- load_tests
131
- logger.info "polytrix:test\tTesting with rspec options: #{rspec_options.join ' '}"
132
- ::RSpec::Core::Runner.run rspec_options
133
- logger.info "polytrix:test\tTest execution completed"
147
+ # The set of {Polytrix::Implementor}s registered with Polytrix.
148
+ def implementors
149
+ manifest.implementors.values
134
150
  end
135
151
 
136
- def reset
137
- @configuration = nil
152
+ # Registers a {Polytrix::Validator} that will be used during test
153
+ # execution on matching {Polytrix::Challenge}s.
154
+ def validate(desc, scope = { suite: //, scenario: // }, &block)
155
+ fail ArgumentError 'You must pass block' unless block_given?
156
+ validator = Polytrix::Validator.new(desc, scope, &block)
157
+
158
+ Polytrix::ValidatorRegistry.register validator
159
+ validator
138
160
  end
139
161
 
140
162
  # @see Polytrix::Configuration
@@ -155,23 +177,6 @@ module Polytrix
155
177
  def tty?
156
178
  $stdout.tty?
157
179
  end
158
-
159
- protected
160
-
161
- def select_implementors(sdks)
162
- return implementors if sdks.empty?
163
-
164
- sdks.map do |sdk|
165
- if File.directory? sdk
166
- sdk_dir = File.absolute_path(sdk)
167
- implementors.find { |i| File.absolute_path(i.basedir) == sdk_dir } || configuration.implementor(sdk_dir)
168
- else
169
- implementor = implementors.find { |i| i.name.to_s.downcase == sdk.to_s.downcase }
170
- fail ArgumentError, "SDK #{sdk} not found" if implementor.nil?
171
- implementor
172
- end
173
- end
174
- end
175
180
  end
176
181
  end
177
182
 
data/polytrix.gemspec CHANGED
@@ -23,12 +23,17 @@ Gem::Specification.new do |spec|
23
23
  spec.add_dependency "mixlib-shellout", "~> 1.3" # Used for MRI
24
24
  spec.add_dependency "buff-shell_out", "~> 0.1" # Used for JRuby
25
25
  spec.add_dependency "middleware", "~> 0.1"
26
- spec.add_dependency "rspec", "~> 3.0"
26
+ spec.add_dependency "rspec-expectations", "~> 3.0"
27
27
  spec.add_dependency "hashie", "~> 3.0"
28
28
  spec.add_dependency "padrino-helpers", "~> 0.12"
29
+ spec.add_dependency "erubis", "~> 2.7"
30
+ spec.add_dependency "cause", "~> 0.1"
31
+ spec.add_dependency "rouge", "~> 1.7"
32
+ spec.add_development_dependency "rspec", "~> 3.0"
29
33
  spec.add_development_dependency "bundler", "~> 1.5"
30
34
  spec.add_development_dependency "rake"
31
35
  spec.add_development_dependency "aruba", "~> 0.5"
32
- spec.add_development_dependency 'rubocop', '~> 0.18.0'
36
+ spec.add_development_dependency 'rubocop', '~> 0.18'
37
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.2'
33
38
  spec.add_development_dependency 'fabrication', '~> 2.11'
34
39
  end