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.
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