aruba 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -1
  3. data/History.md +75 -55
  4. data/Rakefile +1 -1
  5. data/aruba.gemspec +0 -1
  6. data/features/api/command/run_simple.feature +37 -2
  7. data/features/api/command/stderr.feature +46 -0
  8. data/features/api/command/stdout.feature +46 -0
  9. data/features/configuration/activate_announcer_on_command_failure.feature +38 -0
  10. data/features/steps/command/run.feature +28 -0
  11. data/features/steps/command/shell.feature +155 -0
  12. data/features/steps/core/announce.feature +80 -0
  13. data/features/support/aruba.rb +3 -2
  14. data/lib/aruba/api/command.rb +7 -10
  15. data/lib/aruba/colorizer.rb +108 -0
  16. data/lib/aruba/config.rb +4 -2
  17. data/lib/aruba/cucumber/command.rb +12 -0
  18. data/lib/aruba/cucumber/hooks.rb +10 -0
  19. data/lib/aruba/errors.rb +6 -0
  20. data/lib/aruba/event_bus.rb +59 -0
  21. data/lib/aruba/event_bus/name_resolver.rb +168 -0
  22. data/lib/aruba/generators/script_file.rb +46 -0
  23. data/lib/aruba/matchers/path/have_permissions.rb +1 -1
  24. data/lib/aruba/platforms/announcer.rb +30 -23
  25. data/lib/aruba/platforms/filesystem_status.rb +68 -0
  26. data/lib/aruba/platforms/simple_table.rb +14 -7
  27. data/lib/aruba/platforms/unix_platform.rb +11 -2
  28. data/lib/aruba/platforms/unix_which.rb +0 -2
  29. data/lib/aruba/platforms/windows_which.rb +0 -2
  30. data/lib/aruba/processes/basic_process.rb +8 -0
  31. data/lib/aruba/processes/spawn_process.rb +31 -12
  32. data/lib/aruba/rspec.rb +12 -8
  33. data/lib/aruba/runtime.rb +2 -2
  34. data/lib/aruba/setup.rb +5 -2
  35. data/lib/aruba/version.rb +1 -1
  36. data/script/bootstrap +8 -0
  37. data/spec/aruba/api_spec.rb +1 -1
  38. data/spec/aruba/platform/simple_table_spec.rb +2 -2
  39. data/spec/event_bus/name_resolver_spec.rb +68 -0
  40. data/spec/event_bus_spec.rb +160 -0
  41. data/spec/spec_helper.rb +0 -3
  42. data/spec/support/configs/aruba.rb +5 -0
  43. metadata +22 -17
@@ -201,3 +201,83 @@ Feature: Announce output during test run
201
201
  """
202
202
  $ export MY_VAR=my\ value\ \
203
203
  """
204
+
205
+ Scenario: Announce file system status of command
206
+ This will output information like owner, group, atime, mtime, ctime, size,
207
+ mode and if command is executable.
208
+
209
+ Given an executable named "bin/cli" with:
210
+ """bash
211
+ #!/usr/bin/env bash
212
+
213
+ echo 'Hello World'
214
+ """
215
+ And a file named "features/exit_status.feature" with:
216
+ """cucumber
217
+ Feature: Announce
218
+ @announce-command-filesystem-status
219
+ Scenario: Run command
220
+ And I run `cli`
221
+ Then the exit status should be 0
222
+ """
223
+ When I run `cucumber`
224
+ Then the features should all pass
225
+ And the output should contain:
226
+ """
227
+ # mode => 755
228
+ """
229
+ And the output should contain:
230
+ """
231
+ # owner
232
+ """
233
+ And the output should contain:
234
+ """
235
+ # group
236
+ """
237
+ And the output should contain:
238
+ """
239
+ # ctime
240
+ """
241
+ And the output should contain:
242
+ """
243
+ # mtime
244
+ """
245
+ And the output should contain:
246
+ """
247
+ # atime
248
+ """
249
+ And the output should contain:
250
+ """
251
+ # size
252
+ """
253
+ And the output should contain:
254
+ """
255
+ # executable
256
+ """
257
+
258
+ Scenario: Announce content of command
259
+ This will output the content of the executable command. Be careful doing
260
+ this with binary executables. This hook should be used with scripts only.
261
+
262
+ Given an executable named "bin/cli" with:
263
+ """bash
264
+ #!/usr/bin/env bash
265
+
266
+ echo 'Hello World'
267
+ """
268
+ And a file named "features/exit_status.feature" with:
269
+ """cucumber
270
+ Feature: Announce
271
+ @announce-command-content
272
+ Scenario: Run command
273
+ And I run `cli`
274
+ Then the exit status should be 0
275
+ """
276
+ When I run `cucumber`
277
+ Then the features should all pass
278
+ And the output should contain:
279
+ """
280
+ #!/usr/bin/env bash
281
+
282
+ echo 'Hello World'
283
+ """
@@ -1,6 +1,7 @@
1
1
  require 'aruba/cucumber'
2
2
 
3
3
  Aruba.configure do |config|
4
- config.exit_timeout = 120
5
- config.io_wait_timeout = 2
4
+ config.exit_timeout = 120
5
+ config.io_wait_timeout = 2
6
+ config.activate_announcer_on_command_failure = [:stderr, :stdout, :command]
6
7
  end
@@ -168,14 +168,6 @@ module Aruba
168
168
  working_directory = expand_path('.')
169
169
  event_bus = aruba.event_bus
170
170
 
171
- aruba.announcer.announce(:full_environment, environment)
172
- aruba.announcer.announce(:timeout, 'exit', exit_timeout)
173
- aruba.announcer.announce(:timeout, 'io wait', io_wait_timeout)
174
- aruba.announcer.announce(:wait_time, 'startup wait time', startup_wait_time)
175
-
176
- aruba.announcer.announce(:directory, working_directory)
177
- aruba.announcer.announce(:command, cmd)
178
-
179
171
  cmd = Aruba.platform.detect_ruby(cmd)
180
172
 
181
173
  mode = if Aruba.process
@@ -285,8 +277,13 @@ module Aruba
285
277
  end
286
278
 
287
279
  if fail_on_error
288
- expect(command).to have_finished_in_time
289
- expect(command).to be_successfully_executed
280
+ begin
281
+ expect(command).to have_finished_in_time
282
+ expect(command).to be_successfully_executed
283
+ rescue RSpec::Expectations::ExpectationNotMetError => e
284
+ aruba.announcer.activate(aruba.config.activate_announcer_on_command_failure)
285
+ raise e
286
+ end
290
287
  end
291
288
  end
292
289
  # rubocop:enable Metrics/CyclomaticComplexity
@@ -0,0 +1,108 @@
1
+ module Aruba
2
+ # The ANSIColor module can be used for namespacing and mixed into your own
3
+ # classes.
4
+ module AnsiColor
5
+ # :stopdoc:
6
+ ATTRIBUTES = [
7
+ [ :clear , 0 ],
8
+ [ :reset , 0 ], # synonym for :clear
9
+ [ :bold , 1 ],
10
+ [ :dark , 2 ],
11
+ [ :italic , 3 ], # not widely implemented
12
+ [ :underline , 4 ],
13
+ [ :underscore , 4 ], # synonym for :underline
14
+ [ :blink , 5 ],
15
+ [ :rapid_blink , 6 ], # not widely implemented
16
+ [ :negative , 7 ], # no reverse because of String#reverse
17
+ [ :concealed , 8 ],
18
+ [ :strikethrough, 9 ], # not widely implemented
19
+ [ :black , 30 ],
20
+ [ :red , 31 ],
21
+ [ :green , 32 ],
22
+ [ :yellow , 33 ],
23
+ [ :blue , 34 ],
24
+ [ :magenta , 35 ],
25
+ [ :cyan , 36 ],
26
+ [ :white , 37 ],
27
+ [ :on_black , 40 ],
28
+ [ :on_red , 41 ],
29
+ [ :on_green , 42 ],
30
+ [ :on_yellow , 43 ],
31
+ [ :on_blue , 44 ],
32
+ [ :on_magenta , 45 ],
33
+ [ :on_cyan , 46 ],
34
+ [ :on_white , 47 ]
35
+ ]
36
+
37
+ ATTRIBUTE_NAMES = ATTRIBUTES.transpose.first
38
+ # :startdoc:
39
+
40
+ # Returns true, if the coloring function of this module
41
+ # is switched on, false otherwise.
42
+ def self.coloring?
43
+ @coloring
44
+ end
45
+
46
+ # Turns the coloring on or off globally, so you can easily do
47
+ # this for example:
48
+ # Cucumber::Term::ANSIColor::coloring = STDOUT.isatty
49
+ def self.coloring=(val)
50
+ @coloring = val
51
+ end
52
+ self.coloring = true
53
+
54
+ ATTRIBUTES.each do |c, v|
55
+ define_method(c) do |string|
56
+ result = ''
57
+ result << "\e[#{v}m" if Aruba::AnsiColor.coloring?
58
+ if block_given?
59
+ result << yield
60
+ elsif string
61
+ result << string
62
+ elsif respond_to?(:to_str)
63
+ result << to_str
64
+ else
65
+ return result #only switch on
66
+ end
67
+ result << "\e[0m" if Aruba::AnsiColor.coloring?
68
+ result
69
+ end
70
+ end
71
+
72
+ # Regular expression that is used to scan for ANSI-sequences while
73
+ # uncoloring strings.
74
+ COLORED_REGEXP = /\e\[(?:[34][0-7]|[0-9])?m/
75
+
76
+ def self.included(klass)
77
+ if klass == String
78
+ ATTRIBUTES.delete(:clear)
79
+ ATTRIBUTE_NAMES.delete(:clear)
80
+ end
81
+ end
82
+
83
+ # Returns an uncolored version of the string, that is all
84
+ # ANSI-sequences are stripped from the string.
85
+ def uncolored(string = nil) # :yields:
86
+ if block_given?
87
+ yield.gsub(COLORED_REGEXP, '')
88
+ elsif string
89
+ string.gsub(COLORED_REGEXP, '')
90
+ elsif respond_to?(:to_str)
91
+ to_str.gsub(COLORED_REGEXP, '')
92
+ else
93
+ ''
94
+ end
95
+ end
96
+
97
+ # Returns an array of all Aruba::Platforms::AnsiColor attributes as symbols.
98
+ def attributes
99
+ ATTRIBUTE_NAMES
100
+ end
101
+ end
102
+ end
103
+
104
+ module Aruba
105
+ class Colorizer
106
+ include Aruba::AnsiColor
107
+ end
108
+ end
data/lib/aruba/config.rb CHANGED
@@ -26,9 +26,9 @@ module Aruba
26
26
  option_accessor :working_directory, :contract => { Aruba::Contracts::RelativePath => Aruba::Contracts::RelativePath }, :default => 'tmp/aruba'
27
27
 
28
28
  if RUBY_VERSION < '1.9'
29
- option_reader :fixtures_path_prefix, :contract => { None => String }, :default => '%'
29
+ option_reader :fixtures_path_prefix, :contract => { None => String }, :default => '%'
30
30
  else
31
- option_reader :fixtures_path_prefix, :contract => { None => String }, :default => ?%
31
+ option_reader :fixtures_path_prefix, :contract => { None => String }, :default => ?%
32
32
  end
33
33
 
34
34
  option_accessor :exit_timeout, :contract => { Num => Num }, :default => 15
@@ -64,6 +64,8 @@ module Aruba
64
64
 
65
65
  option_accessor :physical_block_size, :contract => { Aruba::Contracts::IsPowerOfTwo => Aruba::Contracts::IsPowerOfTwo }, :default => 512
66
66
  option_accessor :console_history_file, :contract => { String => String }, :default => '~/.aruba_history'
67
+
68
+ option_accessor :activate_announcer_on_command_failure, :contract => { ArrayOf[Symbol] => ArrayOf[Symbol] }, :default => []
67
69
  end
68
70
  end
69
71
 
@@ -1,6 +1,7 @@
1
1
  if Aruba::VERSION < '1.0.0'
2
2
  require 'aruba/cucumber/core'
3
3
  end
4
+ require 'aruba/generators/script_file'
4
5
 
5
6
  When(/^I run "(.*)"$/)do |cmd|
6
7
  warn(%{\e[35m The /^I run "(.*)"$/ step definition is deprecated. Please use the `backticks` version\e[0m})
@@ -28,6 +29,17 @@ When(/^I successfully run `(.*?)`(?: for up to (\d+) seconds)?$/)do |cmd, secs|
28
29
  run_simple(cmd, :fail_on_error => true, :exit_timeout => secs && secs.to_i)
29
30
  end
30
31
 
32
+ When(/^I run the following (?:commands|script)(?: (?:with|in) `([^`]+)`)?:$/) do |shell, commands|
33
+ prepend_environment_variable('PATH', expand_path('bin') + ':')
34
+
35
+ Aruba.platform.mkdir(expand_path('bin'))
36
+ shell ||= Aruba.platform.default_shell
37
+
38
+ Aruba::ScriptFile.new(:interpreter => shell, :content => commands,
39
+ :path => expand_path('bin/myscript')).call
40
+ step 'I run `myscript`'
41
+ end
42
+
31
43
  When(/^I run "([^"]*)" interactively$/) do |cmd|
32
44
  Aruba.platform.deprecated(%{\e[35m The /^I run "([^"]*)" interactively$/ step definition is deprecated. Please use the `backticks` version\e[0m})
33
45
 
@@ -33,6 +33,14 @@ Before('@announce-command') do
33
33
  aruba.announcer.activate :command
34
34
  end
35
35
 
36
+ Before('@announce-command-content') do
37
+ aruba.announcer.activate :command_content
38
+ end
39
+
40
+ Before('@announce-command-filesystem-status') do
41
+ aruba.announcer.activate :command_filesystem_status
42
+ end
43
+
36
44
  Before('@announce-cmd') do
37
45
  Aruba.platform.deprecated 'The use of "@announce-cmd"-hook is deprecated. Please use "@announce-command"'
38
46
 
@@ -112,6 +120,8 @@ Before('@announce') do
112
120
  aruba.announcer.activate :stop_signal
113
121
  aruba.announcer.activate :timeout
114
122
  aruba.announcer.activate :wait_time
123
+ aruba.announcer.activate :command_content
124
+ aruba.announcer.activate :command_filesystem_status
115
125
  end
116
126
 
117
127
  Before('@debug') do
data/lib/aruba/errors.rb CHANGED
@@ -28,4 +28,10 @@ module Aruba
28
28
  # Raised if command was already started, otherwise aruba forgets about the
29
29
  # previous pid and you've got hidden commands run
30
30
  class CommandAlreadyStartedError < Error; end
31
+
32
+ # Raised if an event name cannot be resolved
33
+ class EventNameResolveError < StandardError; end
34
+
35
+ # Raised if given object is not an event
36
+ class NoEventError < StandardError; end
31
37
  end
@@ -0,0 +1,59 @@
1
+ require 'aruba/event_bus/name_resolver'
2
+ require 'aruba/errors'
3
+
4
+ module Aruba
5
+ # Event bus
6
+ #
7
+ # Implements and in-process pub-sub events broadcaster allowing multiple observers
8
+ # to subscribe to different events that fire as your tests are executed.
9
+ #
10
+ class EventBus
11
+ # Create EventBus
12
+ #
13
+ # @param [#transform] resolver
14
+ # A resolver which transforms Symbol, String, Class into an event Class.
15
+ def initialize(resolver)
16
+ @resolver = resolver
17
+ @handlers = Hash.new { |h, k| h[k] = [] }
18
+ end
19
+
20
+ # Register for an event
21
+ #
22
+ # @param [String, Symbol, Class, Array] event_ids
23
+ # If Array, register multiple events witht the same handler. If String,
24
+ # Symbol, Class register handler for given event.
25
+ #
26
+ # @param [#call] handler_object
27
+ # The handler object, needs to have method `#call`. Either
28
+ # `handler_object` or `block` can be defined. The handler object gets the
29
+ # event passed to `#call`.
30
+ #
31
+ # @yield
32
+ # Handler block which gets the event passed as parameter.
33
+ def register(event_ids, handler_object = nil, &handler_proc)
34
+ handler = handler_proc || handler_object
35
+
36
+ fail ArgumentError, 'Please pass either an object#call or a handler block' if handler.nil? || !handler.respond_to?(:call)
37
+
38
+ Array(event_ids).flatten.each do |id|
39
+ @handlers[
40
+ @resolver.transform(id).to_s
41
+ ] << handler
42
+ end
43
+
44
+ nil
45
+ end
46
+
47
+ # Broadcast an event
48
+ #
49
+ # @param [Object] event
50
+ # An object of registered event class. This object is passed to the event
51
+ # handler.
52
+ #
53
+ def notify(event)
54
+ fail NoEventError, 'Please pass an event object, not a class' if event.is_a?(Class)
55
+
56
+ @handlers[event.class.to_s].each { |handler| handler.call(event) }
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,168 @@
1
+ require 'aruba/errors'
2
+
3
+ # Event notification library
4
+ module Aruba
5
+ # EventBus
6
+ class EventBus
7
+ # Resolve name to Event name
8
+ class NameResolver
9
+ # @private
10
+ # Helpers for Resolvers
11
+ module ResolveHelpers
12
+ def camel_case(underscored_name)
13
+ if RUBY_VERSION < '1.9.3'
14
+ underscored_name.to_s.split('_').map { |word| word.upcase.chars.to_a[0] + word.chars.to_a[1..-1].join }.join
15
+ else
16
+ underscored_name.to_s.split('_').map { |word| word.upcase[0] + word[1..-1] }.join
17
+ end
18
+ end
19
+
20
+ # Thanks ActiveSupport
21
+ # (Only needed to support Ruby 1.9.3 and JRuby)
22
+ # rubocop:disable Metrics/CyclomaticComplexity
23
+ # rubocop:disable Metrics/MethodLength
24
+ def constantize(camel_cased_word)
25
+ names = camel_cased_word.split('::')
26
+
27
+ # Trigger a built-in NameError exception including the ill-formed constant in the message.
28
+ Object.const_get(camel_cased_word) if names.empty?
29
+
30
+ # Remove the first blank element in case of '::ClassName' notation.
31
+ names.shift if names.size > 1 && names.first.empty?
32
+
33
+ names.inject(Object) do |constant, name|
34
+ if constant == Object
35
+ constant.const_get(name)
36
+ else
37
+ candidate = constant.const_get(name)
38
+
39
+ if RUBY_VERSION < '1.9.3'
40
+ next candidate if constant.const_defined?(name)
41
+ else
42
+ next candidate if constant.const_defined?(name, false)
43
+ end
44
+
45
+ next candidate unless Object.const_defined?(name)
46
+
47
+ # Go down the ancestors to check if it is owned directly. The check
48
+ # stops when we reach Object or the end of ancestors tree.
49
+ # rubocop:disable Style/EachWithObject
50
+ constant = constant.ancestors.inject do |const, ancestor|
51
+ break const if ancestor == Object
52
+ break ancestor if ancestor.const_defined?(name, false)
53
+ const
54
+ end
55
+ # rubocop:enable Style/EachWithObject
56
+
57
+ # owner is in Object, so raise
58
+ constant.const_get(name, false)
59
+ end
60
+ end
61
+ end
62
+ # rubocop:enable Metrics/CyclomaticComplexity
63
+ # rubocop:enable Metrics/MethodLength
64
+ end
65
+
66
+ # @private
67
+ # Convert a class in to an event class
68
+ class ClassResolver
69
+ class << self
70
+ def match?(event_id)
71
+ event_id.is_a? Class
72
+ end
73
+
74
+ # Which types are supported
75
+ def supports
76
+ [Class]
77
+ end
78
+ end
79
+
80
+ def transform(_, event_id)
81
+ event_id
82
+ end
83
+ end
84
+
85
+ # @private
86
+ # Convert a string in to an event class
87
+ class StringResolver
88
+ include ResolveHelpers
89
+
90
+ class << self
91
+ def match?(event_id)
92
+ event_id.is_a? String
93
+ end
94
+
95
+ # Which types are supported
96
+ def supports
97
+ [String]
98
+ end
99
+ end
100
+
101
+ def transform(_, event_id)
102
+ constantize(event_id)
103
+ end
104
+ end
105
+
106
+ # @private
107
+ # Convert a symbol in to an event class
108
+ class SymbolResolver
109
+ include ResolveHelpers
110
+
111
+ class << self
112
+ def match?(event_id)
113
+ event_id.is_a? Symbol
114
+ end
115
+
116
+ # Which types are supported
117
+ def supports
118
+ [Symbol]
119
+ end
120
+ end
121
+
122
+ def transform(default_namespace, event_id)
123
+ constantize("#{default_namespace}::#{camel_case(event_id)}")
124
+ end
125
+ end
126
+
127
+ # @private
128
+ # Default failing resolver
129
+ #
130
+ # This comes into play if the user passes an invalid event type
131
+ class FailingResolver
132
+ class << self
133
+ def match?(event_id)
134
+ fail ArgumentError, %(Input type "#{event_id.class}" of event_id "#{event_id}" is invalid)
135
+ end
136
+
137
+ def supports
138
+ []
139
+ end
140
+ end
141
+ end
142
+
143
+ protected
144
+
145
+ attr_reader :resolvers, :default_namespace
146
+
147
+ public
148
+
149
+ def initialize(default_namespace)
150
+ @default_namespace = default_namespace
151
+
152
+ @resolvers = []
153
+ @resolvers << ClassResolver
154
+ @resolvers << StringResolver
155
+ @resolvers << SymbolResolver
156
+ @resolvers << FailingResolver
157
+ end
158
+
159
+ def transform(event_id)
160
+ resolvers.find { |r| r.match? event_id }.new.transform(default_namespace, event_id)
161
+ rescue => e
162
+ # rubocop:disable Metrics/LineLength
163
+ raise EventNameResolveError, %(Transforming "#{event_id}" into an event class failed. Supported types are: #{@resolvers.map(&:supports).flatten.join(', ')}. #{e.message}.\n\n#{e.backtrace.join("\n")})
164
+ # rubocop:enable Metrics/LineLength
165
+ end
166
+ end
167
+ end
168
+ end