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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +7 -2
  4. data/.travis.yml +2 -1
  5. data/Gemfile +1 -0
  6. data/README.md +190 -98
  7. data/Rakefile +8 -6
  8. data/bin/polytrix +2 -1
  9. data/docs/samples/code2doc/java/HelloWorld.md +4 -0
  10. data/docs/samples/code2doc/java/Quine.md +2 -0
  11. data/docs/samples/code2doc/python/hello_world.md +2 -0
  12. data/docs/samples/code2doc/python/quine.md +2 -0
  13. data/docs/samples/code2doc/ruby/hello_world.md +4 -0
  14. data/features/bootstrapping.feature +36 -0
  15. data/features/cloning.feature +34 -0
  16. data/features/execution.feature +2 -16
  17. data/features/fixtures/configs/empty.yml +12 -1
  18. data/features/fixtures/configs/hello_world.yml +11 -1
  19. data/features/fixtures/spec/polytrix_spec.rb +1 -4
  20. data/features/solo.feature +12 -0
  21. data/features/states.feature +40 -0
  22. data/features/step_definitions/sdk_steps.rb +11 -1
  23. data/features/support/env.rb +2 -1
  24. data/lib/polytrix/challenge.rb +211 -13
  25. data/lib/polytrix/challenge_result.rb +9 -0
  26. data/lib/polytrix/challenge_runner.rb +4 -11
  27. data/lib/polytrix/challenges.rb +16 -0
  28. data/lib/polytrix/cli/report.rb +0 -4
  29. data/lib/polytrix/cli.rb +229 -137
  30. data/lib/polytrix/color.rb +40 -0
  31. data/lib/polytrix/command/action.rb +26 -0
  32. data/lib/polytrix/command/list.rb +53 -0
  33. data/lib/polytrix/command/rundoc.rb +27 -0
  34. data/lib/polytrix/command/test.rb +24 -0
  35. data/lib/polytrix/command.rb +209 -0
  36. data/lib/polytrix/configuration.rb +30 -40
  37. data/lib/polytrix/core/file_system_helper.rb +2 -5
  38. data/lib/polytrix/core/hashie.rb +14 -0
  39. data/lib/polytrix/core/implementor.rb +52 -12
  40. data/lib/polytrix/core/manifest_section.rb +4 -0
  41. data/lib/polytrix/core/string_helpers.rb +15 -0
  42. data/lib/polytrix/documentation/helpers/code_helper.rb +3 -1
  43. data/lib/polytrix/error.rb +209 -0
  44. data/lib/polytrix/logger.rb +365 -8
  45. data/lib/polytrix/logging.rb +34 -0
  46. data/lib/polytrix/manifest.rb +40 -26
  47. data/lib/polytrix/result.rb +1 -0
  48. data/lib/polytrix/rspec.rb +7 -5
  49. data/lib/polytrix/runners/buff_shellout_executor.rb +19 -0
  50. data/lib/polytrix/runners/executor.rb +32 -0
  51. data/lib/polytrix/runners/mixlib_shellout_executor.rb +83 -0
  52. data/lib/polytrix/state_file.rb +60 -0
  53. data/lib/polytrix/util.rb +155 -0
  54. data/lib/polytrix/validation.rb +1 -1
  55. data/lib/polytrix/validator.rb +9 -5
  56. data/lib/polytrix/version.rb +1 -1
  57. data/lib/polytrix.rb +55 -33
  58. data/polytrix.gemspec +4 -2
  59. data/polytrix.rb +0 -5
  60. data/{polytrix_tests.yml → polytrix.yml} +5 -0
  61. data/samples/default_bootstrap.rb +0 -7
  62. data/samples/polytrix.rb +0 -9
  63. data/samples/{polytrix_tests.yml → polytrix.yml} +11 -0
  64. data/samples/polytrix_cli.sh +1 -1
  65. data/spec/fabricators/implementor_fabricator.rb +20 -0
  66. data/spec/fabricators/manifest_fabricator.rb +4 -1
  67. data/spec/fixtures/{polytrix_tests.yml → polytrix.yml} +10 -0
  68. data/spec/polytrix/challenge_runner_spec.rb +3 -2
  69. data/spec/polytrix/challenge_spec.rb +5 -4
  70. data/spec/polytrix/cli_spec.rb +23 -26
  71. data/spec/polytrix/configuration_spec.rb +4 -43
  72. data/spec/polytrix/documentation/helpers/code_helper_spec.rb +1 -1
  73. data/spec/polytrix/documentation_generator_spec.rb +2 -0
  74. data/spec/polytrix/implementor_spec.rb +44 -2
  75. data/spec/polytrix/manifest_spec.rb +7 -4
  76. data/spec/polytrix_spec.rb +9 -11
  77. data/spec/thor_spy.rb +2 -0
  78. metadata +66 -16
  79. data/features/fixtures/spec/polytrix_merge.rb +0 -5
  80. data/features/reporting.feature +0 -140
  81. data/lib/polytrix/executor.rb +0 -89
  82. data/samples/sdks/custom/polytrix.yml +0 -2
@@ -1,17 +1,374 @@
1
+ require 'fileutils'
1
2
  require 'logger'
2
3
 
3
4
  module Polytrix
4
- module ClassMethods
5
- def logger
6
- @logger ||= Polytrix.configuration.logger
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
- module Logger
11
- include ClassMethods
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
- def self.included(base)
14
- base.extend(ClassMethods)
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
@@ -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 < Hashie::Dash
36
- include Logger
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
- # Hashie Coercion - automatically treat all values as String
41
- def self.coerce(obj)
42
- data = obj.reduce({}) do |h, (key, value)|
43
- h[key] = value.to_s
44
- h
45
- end
46
- new data
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 < Hashie::Dash
51
- include Hashie::Extensions::Coercion
59
+ class Suite < Polytrix::ManifestSection
52
60
  property :env, default: {}
53
- property :samples, default: []
61
+ property :samples, required: true
62
+ coerce_key :samples, Array[CodeSample]
54
63
  property :results
55
64
  end
56
65
 
57
- class Suites < Hashie::Mash
58
- # Hashie Coercion - automatically treat all values as Suite
59
- def self.coerce(obj)
60
- data = obj.reduce({}) do |h, (key, value)|
61
- h[key] = Polytrix::Manifest::Suite.new(value)
62
- h
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
 
@@ -4,6 +4,7 @@ module Polytrix
4
4
  class Result < Hashie::Dash
5
5
  extend Forwardable
6
6
  include Hashie::Extensions::Coercion
7
+
7
8
  property :execution_result # , required: true
8
9
  def_delegators :execution_result, :stdout, :stderr, :exitstatus
9
10
  property :source_file # , required: true
@@ -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['suites'].each do |suite_name, suite_config|
16
+ Polytrix.manifest.suites.each do |suite_name, suite_config|
17
17
  describe suite_name do
18
- samples = suite_config['samples'] || []
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
- challenge = sdk.build_challenge suite: suite_name, name: scenario, vars: suite_config['env']
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.run
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