polytrix 0.1.0.pre → 0.1.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.
- 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
|