polytrix 0.1.2 → 0.1.3
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/.rubocop-todo.yml +14 -5
- data/Gemfile +2 -1
- data/README.md +139 -177
- data/Rakefile +5 -12
- data/bin/polytrix +0 -1
- data/features/bootstrapping.feature +0 -3
- data/features/cloning.feature +0 -3
- data/features/show.feature +38 -0
- data/features/states.feature +12 -13
- data/features/step_definitions/sdk_steps.rb +0 -4
- data/lib/polytrix/challenge.rb +135 -53
- data/lib/polytrix/challenge_result.rb +0 -2
- data/lib/polytrix/challenge_runner.rb +28 -18
- data/lib/polytrix/cli.rb +53 -69
- data/lib/polytrix/color.rb +2 -2
- data/lib/polytrix/command/action.rb +4 -3
- data/lib/polytrix/command/list.rb +39 -28
- data/lib/polytrix/command/report.rb +9 -86
- data/lib/polytrix/command/reports/code2doc.rb +72 -0
- data/lib/polytrix/command/reports/dashboard.rb +125 -0
- data/lib/polytrix/command/show.rb +148 -0
- data/lib/polytrix/command.rb +37 -104
- data/lib/polytrix/configuration.rb +14 -18
- data/lib/polytrix/{core/hashie.rb → dash.rb} +4 -3
- data/lib/polytrix/documentation/code_segmenter.rb +8 -8
- data/lib/polytrix/documentation/comment_styles.rb +1 -1
- data/lib/polytrix/documentation/helpers/code_helper.rb +9 -0
- data/lib/polytrix/documentation_generator.rb +11 -14
- data/lib/polytrix/error.rb +104 -97
- data/lib/polytrix/executor.rb +33 -0
- data/lib/polytrix/{runners → executors}/buff_shellout_executor.rb +1 -1
- data/lib/polytrix/executors/linux_challenge_executor.rb +29 -0
- data/lib/polytrix/executors/mixlib_shellout_executor.rb +55 -0
- data/lib/polytrix/{runners/windows_challenge_runner.rb → executors/windows_challenge_executor.rb} +4 -11
- data/lib/polytrix/{core/implementor.rb → implementor.rb} +10 -6
- data/lib/polytrix/manifest.rb +2 -31
- data/lib/polytrix/{reports → reporters}/hash_reporter.rb +6 -2
- data/lib/polytrix/{reports → reporters}/json_reporter.rb +2 -2
- data/lib/polytrix/{reports → reporters}/markdown_reporter.rb +7 -2
- data/lib/polytrix/{reports → reporters}/yaml_reporter.rb +2 -2
- data/lib/polytrix/reporters.rb +27 -0
- data/lib/polytrix/result.rb +6 -5
- data/lib/polytrix/spies/file_system_spy.rb +15 -0
- data/lib/polytrix/spies.rb +61 -0
- data/lib/polytrix/state_file.rb +1 -20
- data/lib/polytrix/util.rb +157 -62
- data/lib/polytrix/validation.rb +41 -2
- data/lib/polytrix/validator.rb +9 -4
- data/lib/polytrix/version.rb +1 -1
- data/lib/polytrix.rb +110 -105
- data/polytrix.gemspec +7 -2
- data/polytrix.yml +16 -13
- data/resources/assets/pygments/autumn.css +58 -0
- data/resources/assets/pygments/borland.css +46 -0
- data/resources/assets/pygments/bw.css +34 -0
- data/resources/assets/pygments/colorful.css +61 -0
- data/resources/assets/pygments/default.css +62 -0
- data/resources/assets/pygments/emacs.css +61 -0
- data/resources/assets/pygments/friendly.css +61 -0
- data/resources/assets/pygments/fruity.css +69 -0
- data/resources/assets/pygments/github.css +61 -0
- data/resources/assets/pygments/manni.css +61 -0
- data/resources/assets/pygments/monokai.css +64 -0
- data/resources/assets/pygments/murphy.css +61 -0
- data/resources/assets/pygments/native.css +69 -0
- data/resources/assets/pygments/pastie.css +60 -0
- data/resources/assets/pygments/perldoc.css +58 -0
- data/resources/assets/pygments/tango.css +69 -0
- data/resources/assets/pygments/trac.css +59 -0
- data/resources/assets/pygments/vim.css +69 -0
- data/resources/assets/pygments/vs.css +33 -0
- data/resources/assets/pygments/zenburn.css +1 -0
- data/resources/assets/style.css +41 -0
- data/resources/templates/dashboard/files/dashboard.html.tt +82 -0
- data/resources/templates/dashboard/templates/_test_report.html.tt +87 -0
- data/samples/bootstrap.sh +2 -0
- data/samples/clone.sh +2 -0
- data/samples/code2doc.sh +4 -0
- data/samples/docs/samples/code2doc/java/katas-hello_world-java.md +17 -0
- data/samples/docs/samples/code2doc/java/katas-quine-java.md +35 -0
- data/samples/docs/samples/code2doc/python/katas-hello_world-python.md +5 -0
- data/samples/docs/samples/code2doc/python/katas-quine-python.md +6 -0
- data/samples/docs/samples/code2doc/ruby/katas-hello_world-ruby.md +11 -0
- data/samples/exec.sh +2 -0
- data/samples/polytrix.rb +2 -2
- data/samples/polytrix.yml +5 -2
- data/samples/show.sh +4 -0
- data/samples/test.sh +2 -0
- data/samples/tests/polytrix/validators.rb +2 -2
- data/samples/verify.sh +3 -0
- data/scripts/wrapper +4 -7
- data/spec/fabricators/challenge_fabricator.rb +2 -9
- data/spec/fabricators/implementor_fabricator.rb +0 -8
- data/spec/fabricators/manifest_fabricator.rb +2 -9
- data/spec/fabricators/validator_fabricator.rb +2 -4
- data/spec/polytrix/challenge_runner_spec.rb +20 -0
- data/spec/polytrix/documentation/helpers/code_helper_spec.rb +7 -7
- data/spec/polytrix/file_finder_spec.rb +5 -5
- data/spec/polytrix/manifest_spec.rb +0 -21
- data/spec/polytrix/result_spec.rb +14 -14
- data/spec/polytrix/validator_registry_spec.rb +4 -4
- data/spec/polytrix/validator_spec.rb +9 -9
- data/spec/polytrix_spec.rb +1 -25
- data/spec/spec_helper.rb +8 -1
- metadata +130 -38
- data/features/execution.feature +0 -53
- data/features/fixtures/spec/polytrix_spec.rb +0 -7
- data/lib/polytrix/cli/report.rb +0 -84
- data/lib/polytrix/command/rundoc.rb +0 -27
- data/lib/polytrix/core/file_system_helper.rb +0 -75
- data/lib/polytrix/core/manifest_section.rb +0 -4
- data/lib/polytrix/core/string_helpers.rb +0 -15
- data/lib/polytrix/documentation/view_helper.rb +0 -21
- data/lib/polytrix/rspec/documentation_formatter.rb +0 -66
- data/lib/polytrix/rspec/yaml_report.rb +0 -51
- data/lib/polytrix/rspec.rb +0 -56
- data/lib/polytrix/runners/executor.rb +0 -34
- data/lib/polytrix/runners/linux_challenge_runner.rb +0 -23
- data/lib/polytrix/runners/middleware/change_directory.rb +0 -20
- data/lib/polytrix/runners/middleware/feature_executor.rb +0 -24
- data/lib/polytrix/runners/middleware/setup_env_vars.rb +0 -42
- data/lib/polytrix/runners/mixlib_shellout_executor.rb +0 -83
- data/lib/polytrix/validations.rb +0 -23
- data/samples/scripts/wrapper +0 -7
- data/spec/polytrix/middleware/feature_executor_spec.rb +0 -48
- 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
|
-
#
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
+
'&' => '&',
|
|
180
|
+
'<' => '<',
|
|
181
|
+
'>' => '>',
|
|
182
|
+
"'" => ''',
|
|
183
|
+
'"' => '"',
|
|
184
|
+
'/' => '/'
|
|
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
|
data/lib/polytrix/validation.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
data/lib/polytrix/validator.rb
CHANGED
|
@@ -5,21 +5,26 @@ module Polytrix
|
|
|
5
5
|
include RSpec::Matchers
|
|
6
6
|
|
|
7
7
|
UNIVERSAL_MATCHER = //
|
|
8
|
-
attr_reader :description, :suite, :
|
|
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
|
-
@
|
|
13
|
+
@scenario = scope[:scenario] ||= UNIVERSAL_MATCHER
|
|
14
14
|
@callback = validator
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def should_validate?(challenge)
|
|
18
|
-
|
|
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
|
|
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
|
data/lib/polytrix/version.rb
CHANGED
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/
|
|
12
|
-
|
|
13
|
-
require 'polytrix/
|
|
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::
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
configuration.manifest
|
|
48
|
-
end
|
|
56
|
+
Polytrix.configuration.documentation_dir = options[:target_dir]
|
|
57
|
+
Polytrix.configuration.documentation_format = options[:format]
|
|
49
58
|
|
|
50
|
-
|
|
51
|
-
def implementors
|
|
52
|
-
manifest.implementors.values
|
|
53
|
-
end
|
|
59
|
+
manifest.build_challenges
|
|
54
60
|
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
#
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
109
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
#
|
|
118
|
-
def
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
137
|
-
|
|
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
|
|
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
|