greenletters 0.0.1 → 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.
- data/README.org +10 -3
 - data/lib/greenletters.rb +117 -25
 - data/lib/greenletters/cucumber_steps.rb +40 -0
 - data/script/console +3 -0
 - data/version.txt +1 -1
 - metadata +3 -3
 
    
        data/README.org
    CHANGED
    
    | 
         @@ -10,15 +10,22 @@ 
     | 
|
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
            * Synopsis
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
      
 13 
     | 
    
         
            +
              *See introductory blog post [[http://avdi.org/devblog/2010/07/19/greenletters-painless-automation-and-testing-for-command-line-applications/][HERE]].*
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            #+BEGIN_SRC ruby
         
     | 
| 
       14 
16 
     | 
    
         
             
              require 'greenletters'
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
       15 
18 
     | 
    
         
             
              adv = Greenletters::Process.new("adventure", :transcript => $stdout)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              # Install a handler which may be triggered at any time
         
     | 
| 
       16 
21 
     | 
    
         
             
              adv.on(:output, /welcome to adventure/i) do |process, match_data|
         
     | 
| 
       17 
22 
     | 
    
         
             
                adv << "no\n"
         
     | 
| 
       18 
23 
     | 
    
         
             
              end
         
     | 
| 
       19 
24 
     | 
    
         | 
| 
       20 
25 
     | 
    
         
             
              puts "Starting adventure..."
         
     | 
| 
       21 
26 
     | 
    
         
             
              adv.start!
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              # Wait for the specified pattern before proceeding
         
     | 
| 
       22 
29 
     | 
    
         
             
              adv.wait_for(:output, /you are standing at the end of a road/i)
         
     | 
| 
       23 
30 
     | 
    
         
             
              adv << "east\n"
         
     | 
| 
       24 
31 
     | 
    
         
             
              adv.wait_for(:output, /inside a building/i)
         
     | 
| 
         @@ -27,7 +34,7 @@ 
     | 
|
| 
       27 
34 
     | 
    
         
             
              adv << "yes\n"
         
     | 
| 
       28 
35 
     | 
    
         
             
              adv.wait_for(:exit)
         
     | 
| 
       29 
36 
     | 
    
         
             
              puts "Adventure has exited."
         
     | 
| 
       30 
     | 
    
         
            -
            #+ 
     | 
| 
      
 37 
     | 
    
         
            +
            #+END_SRC
         
     | 
| 
       31 
38 
     | 
    
         | 
| 
       32 
39 
     | 
    
         
             
            Or, in Cucumber format:
         
     | 
| 
       33 
40 
     | 
    
         | 
| 
         @@ -68,7 +75,7 @@ Or, in Cucumber format: 
     | 
|
| 
       68 
75 
     | 
    
         
             
            * How
         
     | 
| 
       69 
76 
     | 
    
         
             
              Greenletters uses the pty.rb library under the covers to create a UNIX
         
     | 
| 
       70 
77 
     | 
    
         
             
              pseudoterminal under Ruby's control. Of course, this means that it is
         
     | 
| 
       71 
     | 
    
         
            -
               
     | 
| 
      
 78 
     | 
    
         
            +
              MacOSX/Linux/*NIX-only; Windows users need not apply.
         
     | 
| 
       72 
79 
     | 
    
         | 
| 
       73 
80 
     | 
    
         
             
              The advantage of using a PTY is that *any* output - inclding output written to
         
     | 
| 
       74 
81 
     | 
    
         
             
              the console instead of STDOUT/STDERR - will be captured by Greenletters.
         
     | 
    
        data/lib/greenletters.rb
    CHANGED
    
    | 
         @@ -123,6 +123,7 @@ module Greenletters 
     | 
|
| 
       123 
123 
     | 
    
         
             
                attr_accessor :exclusive
         
     | 
| 
       124 
124 
     | 
    
         
             
                attr_accessor :logger
         
     | 
| 
       125 
125 
     | 
    
         
             
                attr_accessor :interruption
         
     | 
| 
      
 126 
     | 
    
         
            +
                attr_reader   :options
         
     | 
| 
       126 
127 
     | 
    
         | 
| 
       127 
128 
     | 
    
         
             
                alias_method :exclusive?, :exclusive
         
     | 
| 
       128 
129 
     | 
    
         | 
| 
         @@ -131,6 +132,7 @@ module Greenletters 
     | 
|
| 
       131 
132 
     | 
    
         
             
                  @exclusive    = options.fetch(:exclusive) { false }
         
     | 
| 
       132 
133 
     | 
    
         
             
                  @logger       = ::Logger.new($stdout)
         
     | 
| 
       133 
134 
     | 
    
         
             
                  @interruption = :none
         
     | 
| 
      
 135 
     | 
    
         
            +
                  @options      = options
         
     | 
| 
       134 
136 
     | 
    
         
             
                end
         
     | 
| 
       135 
137 
     | 
    
         | 
| 
       136 
138 
     | 
    
         
             
                def call(process)
         
     | 
| 
         @@ -142,6 +144,7 @@ module Greenletters 
     | 
|
| 
       142 
144 
     | 
    
         
             
              class OutputTrigger < Trigger
         
     | 
| 
       143 
145 
     | 
    
         
             
                def initialize(pattern=//, options={}, &block)
         
     | 
| 
       144 
146 
     | 
    
         
             
                  super(options, &block)
         
     | 
| 
      
 147 
     | 
    
         
            +
                  options[:operation] ||= :all
         
     | 
| 
       145 
148 
     | 
    
         
             
                  @pattern = pattern
         
     | 
| 
       146 
149 
     | 
    
         
             
                end
         
     | 
| 
       147 
150 
     | 
    
         | 
| 
         @@ -150,6 +153,13 @@ module Greenletters 
     | 
|
| 
       150 
153 
     | 
    
         
             
                end
         
     | 
| 
       151 
154 
     | 
    
         | 
| 
       152 
155 
     | 
    
         
             
                def call(process)
         
     | 
| 
      
 156 
     | 
    
         
            +
                  case @pattern
         
     | 
| 
      
 157 
     | 
    
         
            +
                  when Array then match_multiple(process)
         
     | 
| 
      
 158 
     | 
    
         
            +
                  else match_one(process)
         
     | 
| 
      
 159 
     | 
    
         
            +
                  end
         
     | 
| 
      
 160 
     | 
    
         
            +
                end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                def match_one(process)
         
     | 
| 
       153 
163 
     | 
    
         
             
                  scanner = process.output_buffer
         
     | 
| 
       154 
164 
     | 
    
         
             
                  @logger.debug "matching #{@pattern.inspect} against #{scanner.rest.inspect}"
         
     | 
| 
       155 
165 
     | 
    
         
             
                  if scanner.scan_until(@pattern)
         
     | 
| 
         @@ -160,16 +170,66 @@ module Greenletters 
     | 
|
| 
       160 
170 
     | 
    
         
             
                    false
         
     | 
| 
       161 
171 
     | 
    
         
             
                  end
         
     | 
| 
       162 
172 
     | 
    
         
             
                end
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                def match_multiple(process)
         
     | 
| 
      
 175 
     | 
    
         
            +
                  op = options[:operation]
         
     | 
| 
      
 176 
     | 
    
         
            +
                  raise "Invalid operation #{op.inspect}" unless [:any, :all].include?(op)
         
     | 
| 
      
 177 
     | 
    
         
            +
                  scanner = process.output_buffer
         
     | 
| 
      
 178 
     | 
    
         
            +
                  @logger.debug "matching #{op} of multiple patterns against #{scanner.rest.inspect}"
         
     | 
| 
      
 179 
     | 
    
         
            +
                  starting_pos = scanner.pos
         
     | 
| 
      
 180 
     | 
    
         
            +
                  ending_pos   = starting_pos
         
     | 
| 
      
 181 
     | 
    
         
            +
                  result = @pattern.send("#{op}?") {|pattern|
         
     | 
| 
      
 182 
     | 
    
         
            +
                    scanner.pos = starting_pos
         
     | 
| 
      
 183 
     | 
    
         
            +
                    if (char_count = scanner.skip_until(pattern))
         
     | 
| 
      
 184 
     | 
    
         
            +
                      ending_pos = [ending_pos, starting_pos + char_count].max
         
     | 
| 
      
 185 
     | 
    
         
            +
                    end
         
     | 
| 
      
 186 
     | 
    
         
            +
                  }
         
     | 
| 
      
 187 
     | 
    
         
            +
                  if result
         
     | 
| 
      
 188 
     | 
    
         
            +
                    scanner.pos = ending_pos
         
     | 
| 
      
 189 
     | 
    
         
            +
                    true
         
     | 
| 
      
 190 
     | 
    
         
            +
                  else
         
     | 
| 
      
 191 
     | 
    
         
            +
                    scanner.pos = starting_pos
         
     | 
| 
      
 192 
     | 
    
         
            +
                    false
         
     | 
| 
      
 193 
     | 
    
         
            +
                  end
         
     | 
| 
      
 194 
     | 
    
         
            +
                end
         
     | 
| 
      
 195 
     | 
    
         
            +
              end
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
              class BytesTrigger < Trigger
         
     | 
| 
      
 198 
     | 
    
         
            +
                attr_reader :num_bytes
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
      
 200 
     | 
    
         
            +
                def initialize(num_bytes, options={}, &block)
         
     | 
| 
      
 201 
     | 
    
         
            +
                  super(options, &block)
         
     | 
| 
      
 202 
     | 
    
         
            +
                  @num_bytes = num_bytes
         
     | 
| 
      
 203 
     | 
    
         
            +
                end
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                def to_s
         
     | 
| 
      
 206 
     | 
    
         
            +
                  "#{num_bytes} bytes of output"
         
     | 
| 
      
 207 
     | 
    
         
            +
                end
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
                def call(process)
         
     | 
| 
      
 210 
     | 
    
         
            +
                  @logger.debug "checking if #{num_bytes} byes have been received"
         
     | 
| 
      
 211 
     | 
    
         
            +
                  process.rest_size >= num_bytes
         
     | 
| 
      
 212 
     | 
    
         
            +
                end
         
     | 
| 
       163 
213 
     | 
    
         
             
              end
         
     | 
| 
       164 
214 
     | 
    
         | 
| 
       165 
215 
     | 
    
         
             
              class TimeoutTrigger < Trigger
         
     | 
| 
      
 216 
     | 
    
         
            +
                attr_reader :expiration_time
         
     | 
| 
      
 217 
     | 
    
         
            +
             
     | 
| 
      
 218 
     | 
    
         
            +
                def initialize(expiration_time=Time.now+1.0, options={}, &block)
         
     | 
| 
      
 219 
     | 
    
         
            +
                  super(options, &block)
         
     | 
| 
      
 220 
     | 
    
         
            +
                  @expiration_time = case expiration_time
         
     | 
| 
      
 221 
     | 
    
         
            +
                                     when Time then expiration_time
         
     | 
| 
      
 222 
     | 
    
         
            +
                                     when Numeric then Time.now + expiration_time
         
     | 
| 
      
 223 
     | 
    
         
            +
                                     end
         
     | 
| 
      
 224 
     | 
    
         
            +
                end
         
     | 
| 
      
 225 
     | 
    
         
            +
             
     | 
| 
       166 
226 
     | 
    
         
             
                def to_s
         
     | 
| 
       167 
     | 
    
         
            -
                  "timeout"
         
     | 
| 
      
 227 
     | 
    
         
            +
                  "timeout at #{expiration_time}"
         
     | 
| 
       168 
228 
     | 
    
         
             
                end
         
     | 
| 
       169 
229 
     | 
    
         | 
| 
       170 
230 
     | 
    
         
             
                def call(process)
         
     | 
| 
       171 
231 
     | 
    
         
             
                  @block.call(process, process.blocker)
         
     | 
| 
       172 
     | 
    
         
            -
                   
     | 
| 
      
 232 
     | 
    
         
            +
                  process.time >= expiration_time
         
     | 
| 
       173 
233 
     | 
    
         
             
                end
         
     | 
| 
       174 
234 
     | 
    
         
             
              end
         
     | 
| 
       175 
235 
     | 
    
         | 
| 
         @@ -207,7 +267,8 @@ module Greenletters 
     | 
|
| 
       207 
267 
     | 
    
         
             
              end
         
     | 
| 
       208 
268 
     | 
    
         | 
| 
       209 
269 
     | 
    
         
             
              class Process
         
     | 
| 
       210 
     | 
    
         
            -
                END_MARKER 
     | 
| 
      
 270 
     | 
    
         
            +
                END_MARKER        = '__GREENLETTERS_PROCESS_ENDED__'
         
     | 
| 
      
 271 
     | 
    
         
            +
                DEFAULT_LOG_LEVEL = ::Logger::WARN
         
     | 
| 
       211 
272 
     | 
    
         | 
| 
       212 
273 
     | 
    
         
             
                # Shamelessly stolen from Rake
         
     | 
| 
       213 
274 
     | 
    
         
             
                RUBY_EXT =
         
     | 
| 
         @@ -230,8 +291,7 @@ module Greenletters 
     | 
|
| 
       230 
291 
     | 
    
         
             
                attr_reader   :cwd          # Working directory for the command
         
     | 
| 
       231 
292 
     | 
    
         | 
| 
       232 
293 
     | 
    
         
             
                def_delegators :input_buffer, :puts, :write, :print, :printf, :<<
         
     | 
| 
       233 
     | 
    
         
            -
                def_delegators :output_buffer, : 
     | 
| 
       234 
     | 
    
         
            -
                                               :getline
         
     | 
| 
      
 294 
     | 
    
         
            +
                def_delegators :output_buffer, :rest, :rest_size, :check_until
         
     | 
| 
       235 
295 
     | 
    
         
             
                def_delegators  :blocker, :interruption, :interruption=
         
     | 
| 
       236 
296 
     | 
    
         | 
| 
       237 
297 
     | 
    
         
             
                def initialize(*args)
         
     | 
| 
         @@ -245,7 +305,7 @@ module Greenletters 
     | 
|
| 
       245 
305 
     | 
    
         
             
                  @cwd            = options.fetch(:cwd) {Dir.pwd}
         
     | 
| 
       246 
306 
     | 
    
         
             
                  @logger   = options.fetch(:logger) {
         
     | 
| 
       247 
307 
     | 
    
         
             
                    l = ::Logger.new($stdout)
         
     | 
| 
       248 
     | 
    
         
            -
                    l.level =  
     | 
| 
      
 308 
     | 
    
         
            +
                    l.level = DEFAULT_LOG_LEVEL
         
     | 
| 
       249 
309 
     | 
    
         
             
                    l
         
     | 
| 
       250 
310 
     | 
    
         
             
                  }
         
     | 
| 
       251 
311 
     | 
    
         
             
                  @state         = :not_started
         
     | 
| 
         @@ -261,7 +321,7 @@ module Greenletters 
     | 
|
| 
       261 
321 
     | 
    
         
             
                end
         
     | 
| 
       262 
322 
     | 
    
         | 
| 
       263 
323 
     | 
    
         
             
                def on(event, *args, &block)
         
     | 
| 
       264 
     | 
    
         
            -
                  t =  
     | 
| 
      
 324 
     | 
    
         
            +
                  t = add_nonblocking_trigger(event, *args, &block)
         
     | 
| 
       265 
325 
     | 
    
         
             
                end
         
     | 
| 
       266 
326 
     | 
    
         | 
| 
       267 
327 
     | 
    
         
             
                def wait_for(event, *args, &block)
         
     | 
| 
         @@ -274,6 +334,12 @@ module Greenletters 
     | 
|
| 
       274 
334 
     | 
    
         
             
                  raise
         
     | 
| 
       275 
335 
     | 
    
         
             
                end
         
     | 
| 
       276 
336 
     | 
    
         | 
| 
      
 337 
     | 
    
         
            +
                def add_nonblocking_trigger(event, *args, &block)
         
     | 
| 
      
 338 
     | 
    
         
            +
                  t = add_trigger(event, *args, &block)
         
     | 
| 
      
 339 
     | 
    
         
            +
                  catchup_trigger!(t)
         
     | 
| 
      
 340 
     | 
    
         
            +
                  t
         
     | 
| 
      
 341 
     | 
    
         
            +
                end
         
     | 
| 
      
 342 
     | 
    
         
            +
             
     | 
| 
       277 
343 
     | 
    
         
             
                def add_trigger(event, *args, &block)
         
     | 
| 
       278 
344 
     | 
    
         
             
                  t = Trigger(event, *args, &block)
         
     | 
| 
       279 
345 
     | 
    
         
             
                  t.logger = @logger
         
     | 
| 
         @@ -296,6 +362,7 @@ module Greenletters 
     | 
|
| 
       296 
362 
     | 
    
         
             
                  t.time_to_live = 1
         
     | 
| 
       297 
363 
     | 
    
         
             
                  @logger.debug "waiting for #{t}"
         
     | 
| 
       298 
364 
     | 
    
         
             
                  self.blocker = t
         
     | 
| 
      
 365 
     | 
    
         
            +
                  catchup_trigger!(t)
         
     | 
| 
       299 
366 
     | 
    
         
             
                  t
         
     | 
| 
       300 
367 
     | 
    
         
             
                end
         
     | 
| 
       301 
368 
     | 
    
         | 
| 
         @@ -350,6 +417,10 @@ module Greenletters 
     | 
|
| 
       350 
417 
     | 
    
         
             
                  @state == :ended
         
     | 
| 
       351 
418 
     | 
    
         
             
                end
         
     | 
| 
       352 
419 
     | 
    
         | 
| 
      
 420 
     | 
    
         
            +
                def time
         
     | 
| 
      
 421 
     | 
    
         
            +
                  Time.now
         
     | 
| 
      
 422 
     | 
    
         
            +
                end
         
     | 
| 
      
 423 
     | 
    
         
            +
             
     | 
| 
       353 
424 
     | 
    
         
             
                private
         
     | 
| 
       354 
425 
     | 
    
         | 
| 
       355 
426 
     | 
    
         
             
                attr_reader :triggers
         
     | 
| 
         @@ -372,9 +443,10 @@ module Greenletters 
     | 
|
| 
       372 
443 
     | 
    
         
             
                      input_handles  = input_buffer.string.empty? ? [] : [@input]
         
     | 
| 
       373 
444 
     | 
    
         
             
                      output_handles = [@output]
         
     | 
| 
       374 
445 
     | 
    
         
             
                      error_handles  = [@input, @output]
         
     | 
| 
       375 
     | 
    
         
            -
                       
     | 
| 
      
 446 
     | 
    
         
            +
                      timeout        = shortest_timeout
         
     | 
| 
      
 447 
     | 
    
         
            +
                      @logger.debug "select() on #{[output_handles, input_handles, error_handles, timeout].inspect}"
         
     | 
| 
       376 
448 
     | 
    
         
             
                      ready_handles = IO.select(
         
     | 
| 
       377 
     | 
    
         
            -
                        output_handles, input_handles, error_handles,  
     | 
| 
      
 449 
     | 
    
         
            +
                        output_handles, input_handles, error_handles, timeout)
         
     | 
| 
       378 
450 
     | 
    
         
             
                      if ready_handles.nil?
         
     | 
| 
       379 
451 
     | 
    
         
             
                        process_timeout
         
     | 
| 
       380 
452 
     | 
    
         
             
                      else
         
     | 
| 
         @@ -402,8 +474,10 @@ module Greenletters 
     | 
|
| 
       402 
474 
     | 
    
         
             
                  @history << data
         
     | 
| 
       403 
475 
     | 
    
         
             
                  @logger.debug format_input_for_log(data)
         
     | 
| 
       404 
476 
     | 
    
         
             
                  @logger.debug "read #{data.size} bytes"
         
     | 
| 
      
 477 
     | 
    
         
            +
                  handle_triggers(:bytes)
         
     | 
| 
       405 
478 
     | 
    
         
             
                  handle_triggers(:output)
         
     | 
| 
       406 
479 
     | 
    
         
             
                  flush_triggers!(OutputTrigger) if ended?
         
     | 
| 
      
 480 
     | 
    
         
            +
                  flush_triggers!(BytesTrigger) if ended?
         
     | 
| 
       407 
481 
     | 
    
         
             
                  # flush_output_buffer! unless ended?
         
     | 
| 
       408 
482 
     | 
    
         
             
                end
         
     | 
| 
       409 
483 
     | 
    
         | 
| 
         @@ -463,29 +537,35 @@ module Greenletters 
     | 
|
| 
       463 
537 
     | 
    
         
             
                  matches = 0
         
     | 
| 
       464 
538 
     | 
    
         
             
                  triggers.grep(klass).each do |t|
         
     | 
| 
       465 
539 
     | 
    
         
             
                    @logger.debug "checking #{event} against #{t}"
         
     | 
| 
       466 
     | 
    
         
            -
                     
     | 
| 
      
 540 
     | 
    
         
            +
                    check_trigger(t) do
         
     | 
| 
       467 
541 
     | 
    
         
             
                      matches += 1
         
     | 
| 
       468 
     | 
    
         
            -
                      @logger.debug "match trigger #{t}"
         
     | 
| 
       469 
     | 
    
         
            -
                      if blocker.equal?(t)
         
     | 
| 
       470 
     | 
    
         
            -
                        unblock!
         
     | 
| 
       471 
     | 
    
         
            -
                      end
         
     | 
| 
       472 
     | 
    
         
            -
                      if t.time_to_live
         
     | 
| 
       473 
     | 
    
         
            -
                        if t.time_to_live > 1
         
     | 
| 
       474 
     | 
    
         
            -
                          t.time_to_live -= 1
         
     | 
| 
       475 
     | 
    
         
            -
                          @logger.debug "trigger ttl reduced to #{t.time_to_live}"
         
     | 
| 
       476 
     | 
    
         
            -
                        else
         
     | 
| 
       477 
     | 
    
         
            -
                          triggers.delete(t)
         
     | 
| 
       478 
     | 
    
         
            -
                          @logger.debug "trigger removed"
         
     | 
| 
       479 
     | 
    
         
            -
                        end
         
     | 
| 
       480 
     | 
    
         
            -
                      end
         
     | 
| 
       481 
542 
     | 
    
         
             
                      break if t.exclusive?
         
     | 
| 
       482 
     | 
    
         
            -
                    else
         
     | 
| 
       483 
     | 
    
         
            -
                      @logger.debug "no match"
         
     | 
| 
       484 
543 
     | 
    
         
             
                    end
         
     | 
| 
       485 
544 
     | 
    
         
             
                  end
         
     | 
| 
       486 
545 
     | 
    
         
             
                  matches > 0
         
     | 
| 
       487 
546 
     | 
    
         
             
                end
         
     | 
| 
       488 
547 
     | 
    
         | 
| 
      
 548 
     | 
    
         
            +
                def check_trigger(trigger)
         
     | 
| 
      
 549 
     | 
    
         
            +
                  if trigger.call(self)         # match
         
     | 
| 
      
 550 
     | 
    
         
            +
                    @logger.debug "match trigger #{trigger}"
         
     | 
| 
      
 551 
     | 
    
         
            +
                    if blocker.equal?(trigger)
         
     | 
| 
      
 552 
     | 
    
         
            +
                      unblock!
         
     | 
| 
      
 553 
     | 
    
         
            +
                    end
         
     | 
| 
      
 554 
     | 
    
         
            +
                    if trigger.time_to_live
         
     | 
| 
      
 555 
     | 
    
         
            +
                      if trigger.time_to_live > 1
         
     | 
| 
      
 556 
     | 
    
         
            +
                        trigger.time_to_live -= 1
         
     | 
| 
      
 557 
     | 
    
         
            +
                        @logger.debug "trigger ttl reduced to #{trigger.time_to_live}"
         
     | 
| 
      
 558 
     | 
    
         
            +
                      else
         
     | 
| 
      
 559 
     | 
    
         
            +
                        triggers.delete(trigger)
         
     | 
| 
      
 560 
     | 
    
         
            +
                        @logger.debug "trigger removed"
         
     | 
| 
      
 561 
     | 
    
         
            +
                      end
         
     | 
| 
      
 562 
     | 
    
         
            +
                    end
         
     | 
| 
      
 563 
     | 
    
         
            +
                    yield if block_given?
         
     | 
| 
      
 564 
     | 
    
         
            +
                  else
         
     | 
| 
      
 565 
     | 
    
         
            +
                    @logger.debug "no match"
         
     | 
| 
      
 566 
     | 
    
         
            +
                  end
         
     | 
| 
      
 567 
     | 
    
         
            +
                end
         
     | 
| 
      
 568 
     | 
    
         
            +
             
     | 
| 
       489 
569 
     | 
    
         
             
                def handle_end_marker
         
     | 
| 
       490 
570 
     | 
    
         
             
                  return false if ended?
         
     | 
| 
       491 
571 
     | 
    
         
             
                  @logger.debug "end marker found"
         
     | 
| 
         @@ -551,6 +631,10 @@ module Greenletters 
     | 
|
| 
       551 
631 
     | 
    
         
             
                  end
         
     | 
| 
       552 
632 
     | 
    
         
             
                end
         
     | 
| 
       553 
633 
     | 
    
         | 
| 
      
 634 
     | 
    
         
            +
                def catchup_trigger!(trigger)
         
     | 
| 
      
 635 
     | 
    
         
            +
                  check_trigger(trigger)
         
     | 
| 
      
 636 
     | 
    
         
            +
                end
         
     | 
| 
      
 637 
     | 
    
         
            +
             
     | 
| 
       554 
638 
     | 
    
         
             
                def format_output_for_log(text)
         
     | 
| 
       555 
639 
     | 
    
         
             
                  "\n" + text.split("\n").map{|l| ">> #{l}"}.join("\n")
         
     | 
| 
       556 
640 
     | 
    
         
             
                end
         
     | 
| 
         @@ -559,6 +643,14 @@ module Greenletters 
     | 
|
| 
       559 
643 
     | 
    
         
             
                  "\n" + text.split("\n").map{|l| "<< #{l}"}.join("\n")
         
     | 
| 
       560 
644 
     | 
    
         
             
                end
         
     | 
| 
       561 
645 
     | 
    
         | 
| 
      
 646 
     | 
    
         
            +
                def shortest_timeout
         
     | 
| 
      
 647 
     | 
    
         
            +
                  result = triggers.grep(TimeoutTrigger).map{|t|
         
     | 
| 
      
 648 
     | 
    
         
            +
                    t.expiration_time - Time.now
         
     | 
| 
      
 649 
     | 
    
         
            +
                  }.min
         
     | 
| 
      
 650 
     | 
    
         
            +
                  if result.nil? then result = 1.0 end
         
     | 
| 
      
 651 
     | 
    
         
            +
                  if result < 0 then result = 0 end
         
     | 
| 
      
 652 
     | 
    
         
            +
                  result
         
     | 
| 
      
 653 
     | 
    
         
            +
                end
         
     | 
| 
       562 
654 
     | 
    
         
             
              end
         
     | 
| 
       563 
655 
     | 
    
         
             
            end
         
     | 
| 
       564 
656 
     | 
    
         | 
| 
         @@ -48,6 +48,26 @@ When /^I execute the process(?: "([^\"]*)")?$/ do |name| 
     | 
|
| 
       48 
48 
     | 
    
         
             
              @greenletters_process_table[name].start!
         
     | 
| 
       49 
49 
     | 
    
         
             
            end
         
     | 
| 
       50 
50 
     | 
    
         | 
| 
      
 51 
     | 
    
         
            +
            When /^I wait for (\d+) bytes from the process(?: "([^\"]*)")?$/ do
         
     | 
| 
      
 52 
     | 
    
         
            +
              |byte_length, name|
         
     | 
| 
      
 53 
     | 
    
         
            +
              name ||= "default"
         
     | 
| 
      
 54 
     | 
    
         
            +
              byte_length = byte_length.to_i
         
     | 
| 
      
 55 
     | 
    
         
            +
              @greenletters_process_table[name].wait_for(:bytes, byte_length)
         
     | 
| 
      
 56 
     | 
    
         
            +
            end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
            When /^I wait ([0-9.]+) seconds for output from the process(?: "([^\"]*)")?$/ do
         
     | 
| 
      
 59 
     | 
    
         
            +
              |seconds, name|
         
     | 
| 
      
 60 
     | 
    
         
            +
              name ||= "default"
         
     | 
| 
      
 61 
     | 
    
         
            +
              seconds = seconds.to_i
         
     | 
| 
      
 62 
     | 
    
         
            +
              @greenletters_process_table[name].wait_for(:timeout, seconds)
         
     | 
| 
      
 63 
     | 
    
         
            +
            end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
            When /^I discard earlier outputs from the process(?: "([^\"]*)")?$/ do
         
     | 
| 
      
 66 
     | 
    
         
            +
              |name|
         
     | 
| 
      
 67 
     | 
    
         
            +
              name ||= "default"
         
     | 
| 
      
 68 
     | 
    
         
            +
              @greenletters_process_table[name].flush_output_buffer!
         
     | 
| 
      
 69 
     | 
    
         
            +
            end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
       51 
71 
     | 
    
         
             
            Then /^I should see the following output(?: from process "([^\"]*)")?:$/ do
         
     | 
| 
       52 
72 
     | 
    
         
             
              |name, pattern|
         
     | 
| 
       53 
73 
     | 
    
         
             
              name ||= "default"
         
     | 
| 
         @@ -55,6 +75,26 @@ Then /^I should see the following output(?: from process "([^\"]*)")?:$/ do 
     | 
|
| 
       55 
75 
     | 
    
         
             
              @greenletters_process_table[name].wait_for(:output, pattern)
         
     | 
| 
       56 
76 
     | 
    
         
             
            end
         
     | 
| 
       57 
77 
     | 
    
         | 
| 
      
 78 
     | 
    
         
            +
            Then /^I should see all the following outputs(?: from process "([^\"]*)")?:$/ do
         
     | 
| 
      
 79 
     | 
    
         
            +
              |name, table|
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
              name ||= "default"
         
     | 
| 
      
 82 
     | 
    
         
            +
              patterns = table.hashes.map { |hash|
         
     | 
| 
      
 83 
     | 
    
         
            +
                greenletters_massage_pattern(hash['text'])
         
     | 
| 
      
 84 
     | 
    
         
            +
              }
         
     | 
| 
      
 85 
     | 
    
         
            +
              @greenletters_process_table[name].wait_for(:output, patterns, :operation => :all)
         
     | 
| 
      
 86 
     | 
    
         
            +
            end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
            # Note: you may want to wait for output to be buffered before executing this
         
     | 
| 
      
 90 
     | 
    
         
            +
            # step. E.g. "When I wait on process for 1024 bytes or 0.1 seconds"
         
     | 
| 
      
 91 
     | 
    
         
            +
            Then /^I should not see the following output(?: from process "([^\"]*)")?:$/ do
         
     | 
| 
      
 92 
     | 
    
         
            +
              |name, pattern|
         
     | 
| 
      
 93 
     | 
    
         
            +
              name ||= "default"
         
     | 
| 
      
 94 
     | 
    
         
            +
              pattern = greenletters_massage_pattern(pattern)
         
     | 
| 
      
 95 
     | 
    
         
            +
              @greenletters_process_table[name].check_until(pattern).should be_nil
         
     | 
| 
      
 96 
     | 
    
         
            +
            end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
       58 
98 
     | 
    
         
             
            When /^I enter "([^\"]*)"(?: into process "([^\"]*)")?$/ do
         
     | 
| 
       59 
99 
     | 
    
         
             
              |input, name|
         
     | 
| 
       60 
100 
     | 
    
         
             
              name ||= "default"
         
     | 
    
        data/script/console
    CHANGED
    
    
    
        data/version.txt
    CHANGED
    
    | 
         @@ -1 +1 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            0.0 
     | 
| 
      
 1 
     | 
    
         
            +
            0.1.0
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version 
     | 
|
| 
       4 
4 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       5 
5 
     | 
    
         
             
              segments: 
         
     | 
| 
       6 
6 
     | 
    
         
             
              - 0
         
     | 
| 
       7 
     | 
    
         
            -
              - 0
         
     | 
| 
       8 
7 
     | 
    
         
             
              - 1
         
     | 
| 
       9 
     | 
    
         
            -
               
     | 
| 
      
 8 
     | 
    
         
            +
              - 0
         
     | 
| 
      
 9 
     | 
    
         
            +
              version: 0.1.0
         
     | 
| 
       10 
10 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       11 
11 
     | 
    
         
             
            authors: 
         
     | 
| 
       12 
12 
     | 
    
         
             
            - Avdi Grimm
         
     | 
| 
         @@ -14,7 +14,7 @@ autorequire: 
     | 
|
| 
       14 
14 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       15 
15 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
            date: 2010-07- 
     | 
| 
      
 17 
     | 
    
         
            +
            date: 2010-07-24 00:00:00 -04:00
         
     | 
| 
       18 
18 
     | 
    
         
             
            default_executable: 
         
     | 
| 
       19 
19 
     | 
    
         
             
            dependencies: 
         
     | 
| 
       20 
20 
     | 
    
         
             
            - !ruby/object:Gem::Dependency 
         
     |