polytrix 0.1.0.pre → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +7 -2
- data/.travis.yml +2 -1
- data/Gemfile +1 -0
- data/README.md +190 -98
- data/Rakefile +8 -6
- data/bin/polytrix +2 -1
- data/docs/samples/code2doc/java/HelloWorld.md +4 -0
- data/docs/samples/code2doc/java/Quine.md +2 -0
- data/docs/samples/code2doc/python/hello_world.md +2 -0
- data/docs/samples/code2doc/python/quine.md +2 -0
- data/docs/samples/code2doc/ruby/hello_world.md +4 -0
- data/features/bootstrapping.feature +36 -0
- data/features/cloning.feature +34 -0
- data/features/execution.feature +2 -16
- data/features/fixtures/configs/empty.yml +12 -1
- data/features/fixtures/configs/hello_world.yml +11 -1
- data/features/fixtures/spec/polytrix_spec.rb +1 -4
- data/features/solo.feature +12 -0
- data/features/states.feature +40 -0
- data/features/step_definitions/sdk_steps.rb +11 -1
- data/features/support/env.rb +2 -1
- data/lib/polytrix/challenge.rb +211 -13
- data/lib/polytrix/challenge_result.rb +9 -0
- data/lib/polytrix/challenge_runner.rb +4 -11
- data/lib/polytrix/challenges.rb +16 -0
- data/lib/polytrix/cli/report.rb +0 -4
- data/lib/polytrix/cli.rb +229 -137
- data/lib/polytrix/color.rb +40 -0
- data/lib/polytrix/command/action.rb +26 -0
- data/lib/polytrix/command/list.rb +53 -0
- data/lib/polytrix/command/rundoc.rb +27 -0
- data/lib/polytrix/command/test.rb +24 -0
- data/lib/polytrix/command.rb +209 -0
- data/lib/polytrix/configuration.rb +30 -40
- data/lib/polytrix/core/file_system_helper.rb +2 -5
- data/lib/polytrix/core/hashie.rb +14 -0
- data/lib/polytrix/core/implementor.rb +52 -12
- data/lib/polytrix/core/manifest_section.rb +4 -0
- data/lib/polytrix/core/string_helpers.rb +15 -0
- data/lib/polytrix/documentation/helpers/code_helper.rb +3 -1
- data/lib/polytrix/error.rb +209 -0
- data/lib/polytrix/logger.rb +365 -8
- data/lib/polytrix/logging.rb +34 -0
- data/lib/polytrix/manifest.rb +40 -26
- data/lib/polytrix/result.rb +1 -0
- data/lib/polytrix/rspec.rb +7 -5
- data/lib/polytrix/runners/buff_shellout_executor.rb +19 -0
- data/lib/polytrix/runners/executor.rb +32 -0
- data/lib/polytrix/runners/mixlib_shellout_executor.rb +83 -0
- data/lib/polytrix/state_file.rb +60 -0
- data/lib/polytrix/util.rb +155 -0
- data/lib/polytrix/validation.rb +1 -1
- data/lib/polytrix/validator.rb +9 -5
- data/lib/polytrix/version.rb +1 -1
- data/lib/polytrix.rb +55 -33
- data/polytrix.gemspec +4 -2
- data/polytrix.rb +0 -5
- data/{polytrix_tests.yml → polytrix.yml} +5 -0
- data/samples/default_bootstrap.rb +0 -7
- data/samples/polytrix.rb +0 -9
- data/samples/{polytrix_tests.yml → polytrix.yml} +11 -0
- data/samples/polytrix_cli.sh +1 -1
- data/spec/fabricators/implementor_fabricator.rb +20 -0
- data/spec/fabricators/manifest_fabricator.rb +4 -1
- data/spec/fixtures/{polytrix_tests.yml → polytrix.yml} +10 -0
- data/spec/polytrix/challenge_runner_spec.rb +3 -2
- data/spec/polytrix/challenge_spec.rb +5 -4
- data/spec/polytrix/cli_spec.rb +23 -26
- data/spec/polytrix/configuration_spec.rb +4 -43
- data/spec/polytrix/documentation/helpers/code_helper_spec.rb +1 -1
- data/spec/polytrix/documentation_generator_spec.rb +2 -0
- data/spec/polytrix/implementor_spec.rb +44 -2
- data/spec/polytrix/manifest_spec.rb +7 -4
- data/spec/polytrix_spec.rb +9 -11
- data/spec/thor_spy.rb +2 -0
- metadata +66 -16
- data/features/fixtures/spec/polytrix_merge.rb +0 -5
- data/features/reporting.feature +0 -140
- data/lib/polytrix/executor.rb +0 -89
- data/samples/sdks/custom/polytrix.yml +0 -2
data/lib/polytrix/logger.rb
CHANGED
@@ -1,17 +1,374 @@
|
|
1
|
+
require 'fileutils'
|
1
2
|
require 'logger'
|
2
3
|
|
3
4
|
module Polytrix
|
4
|
-
|
5
|
-
|
6
|
-
|
5
|
+
class Logger
|
6
|
+
include ::Logger::Severity
|
7
|
+
|
8
|
+
# @return [IO] the log device
|
9
|
+
attr_reader :logdev
|
10
|
+
|
11
|
+
# Constructs a new logger.
|
12
|
+
#
|
13
|
+
# @param options [Hash] configuration for a new logger
|
14
|
+
# @option options [Symbol] :color color to use when when outputting
|
15
|
+
# messages
|
16
|
+
# @option options [Integer] :level the logging severity threshold
|
17
|
+
# (default: `Polytrix::DEFAULT_LOG_LEVEL`)
|
18
|
+
# @option options [String,IO] :logdev filepath String or IO object to be
|
19
|
+
# used for logging (default: `nil`)
|
20
|
+
# @option options [String] :progname program name to include in log
|
21
|
+
# messages (default: `"Polytrix"`)
|
22
|
+
# @option options [IO] :stdout a standard out IO object to use
|
23
|
+
# (default: `$stdout`)
|
24
|
+
def initialize(options = {})
|
25
|
+
color = options[:color]
|
26
|
+
|
27
|
+
@loggers = []
|
28
|
+
@loggers << @logdev = logdev_logger(options[:logdev]) if options[:logdev]
|
29
|
+
@loggers << stdout_logger(options[:stdout], color) if options[:stdout]
|
30
|
+
@loggers << stdout_logger($stdout, color) if @loggers.empty?
|
31
|
+
|
32
|
+
self.progname = options[:progname] || 'Polytrix'
|
33
|
+
self.level = options[:level] || default_log_level
|
7
34
|
end
|
8
|
-
end
|
9
35
|
|
10
|
-
|
11
|
-
|
36
|
+
def self.new_logger(implementor) # (test, implementor, index)
|
37
|
+
name = implementor.name # instance_name(test, implementor)
|
38
|
+
index = Polytrix.implementors.index(implementor) || 0
|
39
|
+
Logger.new(
|
40
|
+
stdout: STDOUT,
|
41
|
+
color: Color::COLORS[index % Color::COLORS.size].to_sym,
|
42
|
+
logdev: File.join(Polytrix.configuration.log_root, "#{name}.log"),
|
43
|
+
level: Polytrix::Util.to_logger_level(Polytrix.configuration.log_level),
|
44
|
+
progname: name
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
class << self
|
49
|
+
private
|
50
|
+
|
51
|
+
# @api private
|
52
|
+
# @!macro delegate_to_first_logger
|
53
|
+
# @method $1()
|
54
|
+
def delegate_to_first_logger(meth)
|
55
|
+
define_method(meth) { |*args| @loggers.first.public_send(meth, *args) }
|
56
|
+
end
|
57
|
+
|
58
|
+
# @api private
|
59
|
+
# @!macro delegate_to_all_loggers
|
60
|
+
# @method $1()
|
61
|
+
def delegate_to_all_loggers(meth)
|
62
|
+
define_method(meth) do |*args|
|
63
|
+
result = nil
|
64
|
+
@loggers.each { |l| result = l.public_send(meth, *args) }
|
65
|
+
result
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [Integer] the logging severity threshold
|
71
|
+
# @see http://is.gd/Okuy5p
|
72
|
+
delegate_to_first_logger :level
|
73
|
+
|
74
|
+
# Sets the logging severity threshold.
|
75
|
+
#
|
76
|
+
# @param level [Integer] the logging severity threshold
|
77
|
+
# @see http://is.gd/H1VBFH
|
78
|
+
delegate_to_all_loggers :level=
|
79
|
+
|
80
|
+
# @return [String] program name to include in log messages
|
81
|
+
# @see http://is.gd/5uHGK0
|
82
|
+
delegate_to_first_logger :progname
|
83
|
+
|
84
|
+
# Sets the program name to include in log messages.
|
85
|
+
#
|
86
|
+
# @param progname [String] the program name to include in log messages
|
87
|
+
# @see http://is.gd/f2U5Xj
|
88
|
+
delegate_to_all_loggers :progname=
|
89
|
+
|
90
|
+
# @return [String] the date format being used
|
91
|
+
# @see http://is.gd/btmFWJ
|
92
|
+
delegate_to_first_logger :datetime_format
|
93
|
+
|
94
|
+
# Sets the date format being used.
|
95
|
+
#
|
96
|
+
# @param format [String] the date format
|
97
|
+
# @see http://is.gd/M36ml8
|
98
|
+
delegate_to_all_loggers :datetime_format=
|
99
|
+
|
100
|
+
# Log a message if the given severity is high enough.
|
101
|
+
#
|
102
|
+
# @see http://is.gd/5opBW0
|
103
|
+
delegate_to_all_loggers :add
|
104
|
+
|
105
|
+
# Dump one or more messages to info.
|
106
|
+
#
|
107
|
+
# @param message [#to_s] the message to log
|
108
|
+
# @see http://is.gd/BCp5KV
|
109
|
+
delegate_to_all_loggers :<<
|
110
|
+
|
111
|
+
# Log a message with severity of banner (high level).
|
112
|
+
#
|
113
|
+
# @param message_or_progname [#to_s] the message to log. In the block
|
114
|
+
# form, this is the progname to use in the log message.
|
115
|
+
# @yield evaluates to the message to log. This is not evaluated unless the
|
116
|
+
# logger's level is sufficient to log the message. This allows you to
|
117
|
+
# create potentially expensive logging messages that are only called when
|
118
|
+
# the logger is configured to show them.
|
119
|
+
# @return [nil,true] when the given severity is not high enough (for this
|
120
|
+
# particular logger), log no message, and return true
|
121
|
+
# @see http://is.gd/pYUCYU
|
122
|
+
delegate_to_all_loggers :banner
|
123
|
+
|
124
|
+
# Log a message with severity of debug.
|
125
|
+
#
|
126
|
+
# @param message_or_progname [#to_s] the message to log. In the block
|
127
|
+
# form, this is the progname to use in the log message.
|
128
|
+
# @yield evaluates to the message to log. This is not evaluated unless the
|
129
|
+
# logger's level is sufficient to log the message. This allows you to
|
130
|
+
# create potentially expensive logging messages that are only called when
|
131
|
+
# the logger is configured to show them.
|
132
|
+
# @return [nil,true] when the given severity is not high enough (for this
|
133
|
+
# particular logger), log no message, and return true
|
134
|
+
# @see http://is.gd/Re97Zp
|
135
|
+
delegate_to_all_loggers :debug
|
136
|
+
|
137
|
+
# @return [true,false] whether or not the current severity level
|
138
|
+
# allows for the printing of debug messages
|
139
|
+
# @see http://is.gd/Iq08xB
|
140
|
+
delegate_to_first_logger :debug?
|
141
|
+
|
142
|
+
# Log a message with severity of info.
|
143
|
+
#
|
144
|
+
# @param message_or_progname [#to_s] the message to log. In the block
|
145
|
+
# form, this is the progname to use in the log message.
|
146
|
+
# @yield evaluates to the message to log. This is not evaluated unless the
|
147
|
+
# logger's level is sufficient to log the message. This allows you to
|
148
|
+
# create potentially expensive logging messages that are only called when
|
149
|
+
# the logger is configured to show them.
|
150
|
+
# @return [nil,true] when the given severity is not high enough (for this
|
151
|
+
# particular logger), log no message, and return true
|
152
|
+
# @see http://is.gd/pYUCYU
|
153
|
+
delegate_to_all_loggers :info
|
154
|
+
|
155
|
+
# @return [true,false] whether or not the current severity level
|
156
|
+
# allows for the printing of info messages
|
157
|
+
# @see http://is.gd/lBtJkT
|
158
|
+
delegate_to_first_logger :info?
|
159
|
+
|
160
|
+
# Log a message with severity of error.
|
161
|
+
#
|
162
|
+
# @param message_or_progname [#to_s] the message to log. In the block
|
163
|
+
# form, this is the progname to use in the log message.
|
164
|
+
# @yield evaluates to the message to log. This is not evaluated unless the
|
165
|
+
# logger's level is sufficient to log the message. This allows you to
|
166
|
+
# create potentially expensive logging messages that are only called when
|
167
|
+
# the logger is configured to show them.
|
168
|
+
# @return [nil,true] when the given severity is not high enough (for this
|
169
|
+
# particular logger), log no message, and return true
|
170
|
+
# @see http://is.gd/mLwYMl
|
171
|
+
delegate_to_all_loggers :error
|
172
|
+
|
173
|
+
# @return [true,false] whether or not the current severity level
|
174
|
+
# allows for the printing of error messages
|
175
|
+
# @see http://is.gd/QY19JL
|
176
|
+
delegate_to_first_logger :error?
|
177
|
+
|
178
|
+
# Log a message with severity of warn.
|
179
|
+
#
|
180
|
+
# @param message_or_progname [#to_s] the message to log. In the block
|
181
|
+
# form, this is the progname to use in the log message.
|
182
|
+
# @yield evaluates to the message to log. This is not evaluated unless the
|
183
|
+
# logger's level is sufficient to log the message. This allows you to
|
184
|
+
# create potentially expensive logging messages that are only called when
|
185
|
+
# the logger is configured to show them.
|
186
|
+
# @return [nil,true] when the given severity is not high enough (for this
|
187
|
+
# particular logger), log no message, and return true
|
188
|
+
# @see http://is.gd/PX9AIS
|
189
|
+
delegate_to_all_loggers :warn
|
190
|
+
|
191
|
+
# @return [true,false] whether or not the current severity level
|
192
|
+
# allows for the printing of warn messages
|
193
|
+
# @see http://is.gd/Gdr4lD
|
194
|
+
delegate_to_first_logger :warn?
|
195
|
+
|
196
|
+
# Log a message with severity of fatal.
|
197
|
+
#
|
198
|
+
# @param message_or_progname [#to_s] the message to log. In the block
|
199
|
+
# form, this is the progname to use in the log message.
|
200
|
+
# @yield evaluates to the message to log. This is not evaluated unless the
|
201
|
+
# logger's level is sufficient to log the message. This allows you to
|
202
|
+
# create potentially expensive logging messages that are only called when
|
203
|
+
# the logger is configured to show them.
|
204
|
+
# @return [nil,true] when the given severity is not high enough (for this
|
205
|
+
# particular logger), log no message, and return true
|
206
|
+
# @see http://is.gd/5ElFPK
|
207
|
+
delegate_to_all_loggers :fatal
|
208
|
+
|
209
|
+
# @return [true,false] whether or not the current severity level
|
210
|
+
# allows for the printing of fatal messages
|
211
|
+
# @see http://is.gd/7PgwRl
|
212
|
+
delegate_to_first_logger :fatal?
|
213
|
+
|
214
|
+
# Log a message with severity of unknown.
|
215
|
+
#
|
216
|
+
# @param message_or_progname [#to_s] the message to log. In the block
|
217
|
+
# form, this is the progname to use in the log message.
|
218
|
+
# @yield evaluates to the message to log. This is not evaluated unless the
|
219
|
+
# logger's level is sufficient to log the message. This allows you to
|
220
|
+
# create potentially expensive logging messages that are only called when
|
221
|
+
# the logger is configured to show them.
|
222
|
+
# @return [nil,true] when the given severity is not high enough (for this
|
223
|
+
# particular logger), log no message, and return true
|
224
|
+
# @see http://is.gd/Y4hqpf
|
225
|
+
delegate_to_all_loggers :unknown
|
226
|
+
|
227
|
+
# Close the logging devices.
|
228
|
+
#
|
229
|
+
# @see http://is.gd/b13cVn
|
230
|
+
delegate_to_all_loggers :close
|
231
|
+
|
232
|
+
private
|
233
|
+
|
234
|
+
# @return [Integer] the default logger level
|
235
|
+
# @api private
|
236
|
+
def default_log_level
|
237
|
+
Polytrix::Util.to_logger_level(Polytrix.configuration.log_level)
|
238
|
+
end
|
239
|
+
|
240
|
+
# Construct a new standard out logger.
|
241
|
+
#
|
242
|
+
# @param stdout [IO] the IO object that represents stdout (or similar)
|
243
|
+
# @param color [Symbol] color to use when outputing messages
|
244
|
+
# @return [StdoutLogger] a new logger
|
245
|
+
# @api private
|
246
|
+
def stdout_logger(stdout, color)
|
247
|
+
logger = StdoutLogger.new(stdout)
|
248
|
+
if Polytrix.tty?
|
249
|
+
logger.formatter = proc do |_severity, _datetime, _progname, msg|
|
250
|
+
Color.colorize("#{msg}", color).concat("\n")
|
251
|
+
end
|
252
|
+
else
|
253
|
+
logger.formatter = proc do |_severity, _datetime, _progname, msg|
|
254
|
+
msg.concat("\n")
|
255
|
+
end
|
256
|
+
end
|
257
|
+
logger
|
258
|
+
end
|
259
|
+
|
260
|
+
# Construct a new logdev logger.
|
261
|
+
#
|
262
|
+
# @param filepath_or_logdev [String,IO] a filepath String or IO object
|
263
|
+
# @return [LogdevLogger] a new logger
|
264
|
+
# @api private
|
265
|
+
def logdev_logger(filepath_or_logdev)
|
266
|
+
LogdevLogger.new(resolve_logdev(filepath_or_logdev))
|
267
|
+
end
|
268
|
+
|
269
|
+
# Return an IO object from a filepath String or the IO object itself.
|
270
|
+
#
|
271
|
+
# @param filepath_or_logdev [String,IO] a filepath String or IO object
|
272
|
+
# @return [IO] an IO object
|
273
|
+
# @api private
|
274
|
+
def resolve_logdev(filepath_or_logdev)
|
275
|
+
if filepath_or_logdev.is_a? String
|
276
|
+
FileUtils.mkdir_p(File.dirname(filepath_or_logdev))
|
277
|
+
file = File.open(File.expand_path(filepath_or_logdev), 'ab')
|
278
|
+
file.sync = true
|
279
|
+
file
|
280
|
+
else
|
281
|
+
filepath_or_logdev
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# Internal class which adds a #banner method call that displays the
|
286
|
+
# message with a callout arrow.
|
287
|
+
class LogdevLogger < ::Logger
|
288
|
+
alias_method :super_info, :info
|
289
|
+
|
290
|
+
# Dump one or more messages to info.
|
291
|
+
#
|
292
|
+
# @param msg [String] a message
|
293
|
+
def <<(msg)
|
294
|
+
@buffer ||= ''
|
295
|
+
lines, _, remainder = msg.rpartition("\n")
|
296
|
+
if lines.empty?
|
297
|
+
@buffer << remainder
|
298
|
+
else
|
299
|
+
lines.insert(0, @buffer)
|
300
|
+
lines.split("\n").each { |l| format_line(l.chomp) }
|
301
|
+
@buffer = ''
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# Log a banner message.
|
306
|
+
#
|
307
|
+
# @param msg [String] a message
|
308
|
+
def banner(msg = nil, &block)
|
309
|
+
super_info("-----> #{msg}", &block)
|
310
|
+
end
|
311
|
+
|
312
|
+
private
|
313
|
+
|
314
|
+
# Reformat a line if it already contains log formatting.
|
315
|
+
#
|
316
|
+
# @param line [String] a message line
|
317
|
+
# @api private
|
318
|
+
def format_line(line)
|
319
|
+
case line
|
320
|
+
when /^-----> / then banner(line.gsub(/^[ >-]{6} /, ''))
|
321
|
+
when /^>>>>>> / then error(line.gsub(/^[ >-]{6} /, ''))
|
322
|
+
when /^ / then info(line.gsub(/^[ >-]{6} /, ''))
|
323
|
+
else info(line)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
# Internal class which reformats logging methods for display as console
|
329
|
+
# output.
|
330
|
+
class StdoutLogger < LogdevLogger
|
331
|
+
# Log a debug message
|
332
|
+
#
|
333
|
+
# @param msg [String] a message
|
334
|
+
def debug(msg = nil, &block)
|
335
|
+
super("D #{msg}", &block)
|
336
|
+
end
|
337
|
+
|
338
|
+
# Log an info message
|
339
|
+
#
|
340
|
+
# @param msg [String] a message
|
341
|
+
def info(msg = nil, &block)
|
342
|
+
super(" #{msg}", &block)
|
343
|
+
end
|
344
|
+
|
345
|
+
# Log a warn message
|
346
|
+
#
|
347
|
+
# @param msg [String] a message
|
348
|
+
def warn(msg = nil, &block)
|
349
|
+
super("$$$$$$ #{msg}", &block)
|
350
|
+
end
|
351
|
+
|
352
|
+
# Log an error message
|
353
|
+
#
|
354
|
+
# @param msg [String] a message
|
355
|
+
def error(msg = nil, &block)
|
356
|
+
super(">>>>>> #{msg}", &block)
|
357
|
+
end
|
358
|
+
|
359
|
+
# Log a fatal message
|
360
|
+
#
|
361
|
+
# @param msg [String] a message
|
362
|
+
def fatal(msg = nil, &block)
|
363
|
+
super("!!!!!! #{msg}", &block)
|
364
|
+
end
|
12
365
|
|
13
|
-
|
14
|
-
|
366
|
+
# Log an unknown message
|
367
|
+
#
|
368
|
+
# @param msg [String] a message
|
369
|
+
def unknown(msg = nil, &block)
|
370
|
+
super("?????? #{msg}", &block)
|
371
|
+
end
|
15
372
|
end
|
16
373
|
end
|
17
374
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Polytrix
|
2
|
+
module DefaultLogger
|
3
|
+
module ClassMethods
|
4
|
+
def logger
|
5
|
+
@logger ||= Polytrix.configuration.default_logger
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.extend(ClassMethods)
|
11
|
+
end
|
12
|
+
|
13
|
+
include ClassMethods
|
14
|
+
end
|
15
|
+
|
16
|
+
module Logging
|
17
|
+
class << self
|
18
|
+
private
|
19
|
+
|
20
|
+
def logger_method(meth)
|
21
|
+
define_method(meth) do |*args|
|
22
|
+
logger.public_send(meth, *args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
logger_method :banner
|
28
|
+
logger_method :debug
|
29
|
+
logger_method :info
|
30
|
+
logger_method :warn
|
31
|
+
logger_method :error
|
32
|
+
logger_method :fatal
|
33
|
+
end
|
34
|
+
end
|
data/lib/polytrix/manifest.rb
CHANGED
@@ -32,45 +32,59 @@ module Polytrix
|
|
32
32
|
# The *global_env* values will be made available to all tests as environment variables, along with the *env*
|
33
33
|
# values for that specific test.
|
34
34
|
#
|
35
|
-
class Manifest <
|
36
|
-
include
|
35
|
+
class Manifest < Polytrix::ManifestSection
|
36
|
+
include Polytrix::DefaultLogger
|
37
|
+
include Polytrix::Logging
|
37
38
|
include Hashie::Extensions::DeepMerge
|
38
39
|
|
40
|
+
def initialize(hash = {})
|
41
|
+
super
|
42
|
+
implementors.each do | name, implementor |
|
43
|
+
implementor.name = name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
39
47
|
class Environment < Hashie::Mash
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
48
|
+
end
|
49
|
+
|
50
|
+
class CodeSample < Polytrix::ManifestSection
|
51
|
+
property :name, required: true
|
52
|
+
|
53
|
+
def self.coerce(data)
|
54
|
+
data = { name: data } if data.is_a? String
|
55
|
+
new(data)
|
47
56
|
end
|
48
57
|
end
|
49
58
|
|
50
|
-
class Suite <
|
51
|
-
include Hashie::Extensions::Coercion
|
59
|
+
class Suite < Polytrix::ManifestSection
|
52
60
|
property :env, default: {}
|
53
|
-
property :samples,
|
61
|
+
property :samples, required: true
|
62
|
+
coerce_key :samples, Array[CodeSample]
|
54
63
|
property :results
|
55
64
|
end
|
56
65
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
66
|
+
property :implementors, required: true
|
67
|
+
coerce_key :implementors, Hash[String => Polytrix::Implementor]
|
68
|
+
property :global_env
|
69
|
+
coerce_key :global_env, Environment
|
70
|
+
property :suites
|
71
|
+
coerce_key :suites, Hash[String => Suite]
|
72
|
+
|
73
|
+
attr_accessor :challenges
|
74
|
+
|
75
|
+
def build_challenges
|
76
|
+
@challenges ||= Challenges.new
|
77
|
+
|
78
|
+
suites.each do | suite_name, suite |
|
79
|
+
suite.samples.each do | sample |
|
80
|
+
implementors.each_value do | implementor |
|
81
|
+
challenge = implementor.build_challenge suite: suite_name, name: sample.name, vars: suite.env
|
82
|
+
@challenges[challenge.slug] = challenge
|
83
|
+
end
|
63
84
|
end
|
64
|
-
new data
|
65
85
|
end
|
66
86
|
end
|
67
87
|
|
68
|
-
include Hashie::Extensions::Coercion
|
69
|
-
property :global_env
|
70
|
-
coerce_key :global_env, Polytrix::Manifest::Environment
|
71
|
-
property :suites
|
72
|
-
coerce_key :suites, Polytrix::Manifest::Suites
|
73
|
-
|
74
88
|
# Parses a YAML file to create a {Manifest} object.
|
75
89
|
def self.from_yaml(yaml_file)
|
76
90
|
ENV['POLYTRIX_SEED'] ||= $PROCESS_ID.to_s
|
@@ -82,7 +96,7 @@ module Polytrix
|
|
82
96
|
end
|
83
97
|
|
84
98
|
def find_suite(suite_name)
|
85
|
-
_, suite = suites.find { |name, _| name.downcase == suite_name.downcase }
|
99
|
+
_, suite = suites.find { |name, _| name.to_s.downcase == suite_name.to_s.downcase }
|
86
100
|
suite
|
87
101
|
end
|
88
102
|
|
data/lib/polytrix/result.rb
CHANGED
data/lib/polytrix/rspec.rb
CHANGED
@@ -13,18 +13,20 @@ module Polytrix
|
|
13
13
|
def shared_examples(caller) # rubocop:disable MethodLength
|
14
14
|
# FIXME: Long method because it's hard to eval in the right context
|
15
15
|
caller.instance_eval do
|
16
|
-
Polytrix.manifest
|
16
|
+
Polytrix.manifest.suites.each do |suite_name, suite_config|
|
17
17
|
describe suite_name do
|
18
|
-
samples = suite_config
|
18
|
+
samples = suite_config.samples || []
|
19
19
|
samples.each do |scenario|
|
20
|
-
describe scenario do
|
20
|
+
describe scenario.name do
|
21
21
|
Polytrix.implementors.each do |sdk|
|
22
22
|
it sdk.name, sdk.name.to_sym => true do
|
23
23
|
begin
|
24
24
|
skip "#{sdk.name} is not setup" unless File.directory? sdk.basedir
|
25
|
-
|
25
|
+
slug = Polytrix::Challenge.slugify(suite_name, scenario.name, sdk.name)
|
26
|
+
challenge = Polytrix.manifest.challenges[slug]
|
27
|
+
# sdk.build_challenge suite: suite_name, name: scenario, vars: suite_config.env
|
26
28
|
example.metadata[:polytrix_challenge] = challenge
|
27
|
-
challenge.
|
29
|
+
challenge.exec
|
28
30
|
validators = Polytrix::ValidatorRegistry.validators_for challenge
|
29
31
|
validators.each do |validator|
|
30
32
|
instance_exec challenge, &validator.callback
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'buff/shell_out'
|
2
|
+
|
3
|
+
module Polytrix
|
4
|
+
module Runners
|
5
|
+
class BuffShellOutExecutor
|
6
|
+
def execute(command, opts)
|
7
|
+
cwd = opts.delete(:cwd) || Dir.pwd
|
8
|
+
execution_result = nil
|
9
|
+
Dir.chdir(cwd) do
|
10
|
+
shell = Buff::ShellOut.shell_out(command)
|
11
|
+
# Buff doesn't have a live_stream like Mixlib
|
12
|
+
puts shell.stdout unless Polytrix.configuration.suppress_output
|
13
|
+
execution_result = ExecutionResult.new exitstatus: shell.exitstatus, stdout: shell.stdout, stderr: shell.stderr
|
14
|
+
end
|
15
|
+
execution_result
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'hashie/dash'
|
2
|
+
require 'thor'
|
3
|
+
require 'polytrix/core/manifest_section'
|
4
|
+
|
5
|
+
module Polytrix
|
6
|
+
module Runners
|
7
|
+
autoload :BuffShellOutExecutor, 'polytrix/runners/buff_shellout_executor'
|
8
|
+
autoload :MixlibShellOutExecutor, 'polytrix/runners/mixlib_shellout_executor'
|
9
|
+
|
10
|
+
class ExecutionResult < Polytrix::ManifestSection
|
11
|
+
property :exitstatus, require: true
|
12
|
+
property :stdout, required: true
|
13
|
+
property :stderr, required: true
|
14
|
+
end
|
15
|
+
|
16
|
+
module Executor
|
17
|
+
attr_writer :executor
|
18
|
+
|
19
|
+
def executor
|
20
|
+
@executor ||= if RUBY_PLATFORM == 'java'
|
21
|
+
Polytrix::Runners::BuffShellOutExecutor.new
|
22
|
+
else
|
23
|
+
Polytrix::Runners::MixlibShellOutExecutor.new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def execute(command, opts = {})
|
28
|
+
executor.execute(command, opts)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'mixlib/shellout'
|
2
|
+
|
3
|
+
module Polytrix
|
4
|
+
module Runners
|
5
|
+
class IOToLog < IO
|
6
|
+
def initialize(logger)
|
7
|
+
@logger = logger
|
8
|
+
@buffer = ''
|
9
|
+
end
|
10
|
+
|
11
|
+
def write(string)
|
12
|
+
(@buffer + string).lines.each do |line|
|
13
|
+
if line.end_with? "\n"
|
14
|
+
@buffer = ''
|
15
|
+
@logger.info(line.rstrip)
|
16
|
+
else
|
17
|
+
@buffer = line
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class MixlibShellOutExecutor
|
24
|
+
include Polytrix::DefaultLogger
|
25
|
+
|
26
|
+
def log_decorator(io, prefix)
|
27
|
+
# OutputDecorator.new(io, prefix) unless Polytrix.configuration.suppress_output
|
28
|
+
# logger = Logging.logger['polytrix::exec']
|
29
|
+
IOToLog.new(logger)
|
30
|
+
end
|
31
|
+
|
32
|
+
def execute(command, opts)
|
33
|
+
prefix = opts.delete :prefix
|
34
|
+
shell = Mixlib::ShellOut.new(command, opts)
|
35
|
+
shell.live_stream = log_decorator $stdout, prefix
|
36
|
+
shell.run_command
|
37
|
+
execution_result = ExecutionResult.new exitstatus: shell.exitstatus, stdout: shell.stdout, stderr: shell.stderr
|
38
|
+
begin
|
39
|
+
shell.error!
|
40
|
+
rescue Mixlib::ShellOut::ShellCommandFailed => e
|
41
|
+
execution_error = ExecutionError.new(e)
|
42
|
+
execution_error.execution_result = execution_result
|
43
|
+
raise execution_error
|
44
|
+
end
|
45
|
+
|
46
|
+
execution_result
|
47
|
+
end
|
48
|
+
|
49
|
+
class OutputDecorator
|
50
|
+
# Reserve :red, :black, :white
|
51
|
+
COLORS = [:green, :yellow, :blue, :magenta, :cyan]
|
52
|
+
|
53
|
+
def self.next_color
|
54
|
+
@next_color ||= 0
|
55
|
+
@next_color += 1
|
56
|
+
COLORS[@next_color % COLORS.size]
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize(real_io, prefix = nil)
|
60
|
+
@real_io = real_io
|
61
|
+
# @prefix = set_color(prefix, :cyan)
|
62
|
+
@prefix = "#{prefix}: " if prefix
|
63
|
+
@color = self.class.next_color
|
64
|
+
@thor_shell = Thor::Shell::Color.new
|
65
|
+
end
|
66
|
+
|
67
|
+
def puts(line)
|
68
|
+
line = line.gsub(/^/, @prefix) if @prefix
|
69
|
+
@real_io.puts @thor_shell.set_color(line, @color)
|
70
|
+
end
|
71
|
+
|
72
|
+
def <<(line)
|
73
|
+
line = line.gsub(/^/, @prefix) if @prefix
|
74
|
+
@real_io << @thor_shell.set_color(line, @color)
|
75
|
+
end
|
76
|
+
|
77
|
+
def method_missing(meth, *args, &block)
|
78
|
+
@real_io.send meth, *args, &block
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|