waitfor 0.1.0 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +30 -2
- data/Manifest.txt +10 -4
- data/README.rdoc +41 -24
- data/Rakefile +4 -4
- data/lib/waitfor.rb +26 -2
- data/lib/waitfor/fixnum.rb +29 -13
- data/lib/waitfor/settings/configuration.rb +35 -0
- data/lib/waitfor/settings/legacy_parser.rb +44 -0
- data/lib/waitfor/settings/parser.rb +59 -0
- data/lib/waitfor/timeout_error.rb +4 -0
- data/lib/waitfor/timer.rb +36 -27
- data/lib/waitfor/version.rb +6 -5
- data/spec/configuration_spec.rb +34 -0
- data/spec/fixnum_spec.rb +18 -20
- data/spec/legacy_parser_spec.rb +50 -0
- data/spec/parser_spec.rb +147 -0
- data/spec/spec.opts +3 -1
- data/spec/spec_helper.rb +9 -16
- data/spec/spec_helper_spec.rb +9 -19
- data/spec/timeout_error_spec.rb +10 -0
- data/spec/timer_spec.rb +90 -63
- data/spec/waitfor_spec.rb +44 -58
- metadata +36 -14
- data/lib/waitfor/waitfor_timeout_error.rb +0 -5
- data/spec/waitfor_timeout_error_spec.rb +0 -6
data/History.txt
CHANGED
@@ -1,4 +1,32 @@
|
|
1
|
+
=== 0.1.7 2011-04-19
|
2
|
+
|
3
|
+
* Bug fix related to not accumulating seconds when minutes was specified
|
4
|
+
* Enhanced rspec tests
|
5
|
+
|
6
|
+
=== 0.1.6 2011-03-23
|
7
|
+
|
8
|
+
* Various bug fixes.
|
9
|
+
|
10
|
+
=== 0.1.5 2011-03-23
|
11
|
+
|
12
|
+
* Additional refactoring to separate out the option parsing in preparation for new functionality
|
13
|
+
|
14
|
+
=== 0.1.3 2010-11-14
|
15
|
+
|
16
|
+
* Minor refactoring and enhancement of tests
|
17
|
+
|
18
|
+
=== 0.1.2 2010-11-13
|
19
|
+
|
20
|
+
* Updated tests to use mocks to increase their speed dramatically
|
21
|
+
|
22
|
+
=== 0.1.1 2010-02-05
|
23
|
+
|
24
|
+
* Updated tests to use standard benchmarking library
|
25
|
+
|
26
|
+
=== 0.1.0 2010-02-03
|
27
|
+
|
28
|
+
* Added new time formats, custom exceptions and messages
|
29
|
+
|
1
30
|
=== 0.0.1 2010-01-24
|
2
31
|
|
3
|
-
*
|
4
|
-
* Initial release
|
32
|
+
* Initial release
|
data/Manifest.txt
CHANGED
@@ -1,16 +1,22 @@
|
|
1
1
|
History.txt
|
2
2
|
Manifest.txt
|
3
|
-
README.rdoc
|
4
3
|
Rakefile
|
4
|
+
README.rdoc
|
5
5
|
lib/waitfor.rb
|
6
6
|
lib/waitfor/fixnum.rb
|
7
|
+
lib/waitfor/timeout_error.rb
|
7
8
|
lib/waitfor/timer.rb
|
8
9
|
lib/waitfor/version.rb
|
9
|
-
lib/waitfor/
|
10
|
+
lib/waitfor/settings/configuration.rb
|
11
|
+
lib/waitfor/settings/legacy_parser.rb
|
12
|
+
lib/waitfor/settings/parser.rb
|
13
|
+
spec/configuration_spec.rb
|
10
14
|
spec/fixnum_spec.rb
|
15
|
+
spec/legacy_parser_spec.rb
|
16
|
+
spec/parser_spec.rb
|
11
17
|
spec/spec.opts
|
12
18
|
spec/spec_helper.rb
|
13
19
|
spec/spec_helper_spec.rb
|
14
|
-
spec/
|
20
|
+
spec/timeout_error_spec.rb
|
15
21
|
spec/timer_spec.rb
|
16
|
-
spec/
|
22
|
+
spec/waitfor_spec.rb
|
data/README.rdoc
CHANGED
@@ -4,44 +4,62 @@
|
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
7
|
-
|
7
|
+
Simple solution to the sleep( ) test anti-pattern.
|
8
8
|
|
9
|
-
|
9
|
+
Blocks execution until a supplied block returns true, or a specified time interval is reached, at which point an error is raised.
|
10
10
|
|
11
|
-
|
11
|
+
object.some_nonblocking_operation
|
12
|
+
|
13
|
+
WaitFor.upto 30.seconds do
|
14
|
+
object.completed?
|
15
|
+
end
|
16
|
+
|
17
|
+
== FEATURES:
|
12
18
|
|
13
19
|
* Accepts time intervals in two formats
|
14
|
-
* Simple time DSL of seconds or minutes
|
15
|
-
* 30.seconds
|
16
|
-
* 5.minutes
|
17
20
|
* Hash with symbols :seconds or :minutes
|
18
21
|
* :seconds => 30
|
19
22
|
* :minutes => 5
|
20
23
|
* Singular forms ( 1.second or :minute => 1 ) accepted as well
|
24
|
+
* Legacy mode with seconds or minutes
|
25
|
+
* 30.seconds
|
26
|
+
* 5.minutes
|
21
27
|
* Allows for custom exceptions, custom error messages or both
|
22
28
|
* For custom exceptions, add ':exception => YourException' to the options
|
23
29
|
* Use either :exception or :error for custom exceptions ( both are identical in functionality )
|
24
30
|
* For custom messages, add ':message => "Your Custom Message"' to the options
|
25
31
|
|
26
|
-
* Resolution of delay between Ruby block executions is currently 1 second
|
27
|
-
* Must use hash if specifying custom exceptions or messages, can't mix and match simple time DSL with hash
|
32
|
+
* Resolution of delay between Ruby block executions is currently 1 second ( and not yet configurable )
|
33
|
+
* Must use hash if specifying custom exceptions or messages, can't mix and match simple time DSL with hash
|
28
34
|
|
29
35
|
== SYNOPSIS:
|
30
36
|
|
31
|
-
|
32
|
-
you want to 'wait for' that operation to either complete by continuously checking the status or fail at a specified timeout.
|
37
|
+
Simple solution to the sleep( ) test anti-pattern.
|
33
38
|
|
34
|
-
|
35
|
-
while it continuously runs a Ruby block supplied by the user. The Ruby block in this example determines whether the previous non-blocking operation has run to completion.
|
36
|
-
As the Ruby block returns false, the timer continues to count down. If it returns true, then it breaks out of the loop and continues on its way. However, if the time elapsed
|
37
|
-
becomes greater than the time permitted, 30 seconds in this example, then an exception is raised.
|
39
|
+
Blocks execution until a supplied block returns true, or a specified time interval is reached, at which point an error is raised.
|
38
40
|
|
39
|
-
|
41
|
+
Useful for adding elasticity to tests involving asynchronous or non-blocking operations. Instead of sleep( )'ing in a test, add a WaitFor.
|
42
|
+
Reduces test execution time by waiting only as long as required. Adds robustness by giving test steps enough time to execute, reducing
|
43
|
+
fail-positive failures from timeouts.
|
40
44
|
|
41
|
-
|
45
|
+
Whereas you might normally do something like this:
|
46
|
+
|
47
|
+
object.some_asynchronous_operation
|
48
|
+
|
49
|
+
# Wait for it to finish and let's hope it doesn't take 31 seconds, because then the test might fail without good reason!
|
50
|
+
# Let's also hope it takes no less than 29 seconds, because then we're making the test slow!
|
51
|
+
sleep( 30 )
|
52
|
+
|
53
|
+
It would be far superior to use WaitFor in this manner:
|
54
|
+
|
55
|
+
object.some_asynchronous_operation
|
56
|
+
|
57
|
+
WaitFor.upto 60.seconds do
|
42
58
|
object.completed?
|
43
59
|
end
|
44
60
|
|
61
|
+
Here the test could take as long as 60 seconds and still pass, likewise, if it took only 1 second, it would continue on with minimal delay. We've removed the brittleness that sleep() adds.
|
62
|
+
|
45
63
|
Custom exception and message are specified in the examples below.
|
46
64
|
|
47
65
|
WaitFor.upto( :minute => 1, :exception => EventFailedToOccurError ) do
|
@@ -58,17 +76,16 @@ Custom exception and message are specified in the examples below.
|
|
58
76
|
|
59
77
|
Finally, a few more "real world" examples.
|
60
78
|
|
61
|
-
|
79
|
+
page.click "Link"
|
62
80
|
|
63
|
-
WaitFor.upto( :minute => 1, :message => "New
|
64
|
-
|
81
|
+
WaitFor.upto( :minute => 1, :message => "New page did not appear within 1 minute after clicking link" ) do
|
82
|
+
page.has_loaded? && page.title == "Home"
|
65
83
|
end
|
66
84
|
|
67
|
-
website_cluster.propagate_new_entry "Good news everyone!"
|
68
85
|
|
69
|
-
|
70
|
-
|
71
|
-
|
86
|
+
print "/tmp/file"
|
87
|
+
|
88
|
+
WaitFor.upto( 30.seconds ) { files_in_queue( "/tmp/print/queue" ).count == 1 }
|
72
89
|
|
73
90
|
== REQUIREMENTS:
|
74
91
|
|
@@ -82,7 +99,7 @@ Finally, a few more "real world" examples.
|
|
82
99
|
|
83
100
|
(The MIT License)
|
84
101
|
|
85
|
-
Copyright (c) 2010 James Bobowski
|
102
|
+
Copyright (c) 2010-2011 James Bobowski
|
86
103
|
|
87
104
|
Permission is hereby granted, free of charge, to any person obtaining
|
88
105
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
@@ -9,10 +9,10 @@ Hoe.plugin :newgem
|
|
9
9
|
# Generate all the Rake tasks
|
10
10
|
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
11
11
|
$hoe = Hoe.spec 'waitfor' do
|
12
|
-
self.developer
|
13
|
-
self.rubyforge_name
|
14
|
-
self.version
|
15
|
-
self.description
|
12
|
+
self.developer 'James Bobowski', 'james.bobowski@gmail.com'
|
13
|
+
self.rubyforge_name = self.name
|
14
|
+
self.version = WaitFor::VERSION::STRING
|
15
|
+
self.description = "Blocks execution until a specified statement becomes true, or a specified time interval is reached, at which point an error is raised."
|
16
16
|
end
|
17
17
|
|
18
18
|
require 'newgem/tasks'
|
data/lib/waitfor.rb
CHANGED
@@ -1,16 +1,40 @@
|
|
1
|
+
# @author James Bobowski
|
2
|
+
|
1
3
|
$:.unshift( File.dirname( __FILE__ ) ) unless
|
2
4
|
$:.include?( File.dirname( __FILE__ ) ) || $:.include?( File.expand_path( File.dirname( __FILE__ ) ) )
|
3
5
|
|
4
6
|
require 'waitfor/fixnum'
|
7
|
+
require 'waitfor/timeout_error'
|
5
8
|
require 'waitfor/timer'
|
9
|
+
require 'waitfor/settings/configuration'
|
10
|
+
require 'waitfor/settings/legacy_parser'
|
11
|
+
require 'waitfor/settings/parser'
|
6
12
|
|
7
13
|
module WaitFor
|
8
14
|
class << self
|
15
|
+
# Execute the given block until it returns true or the specified timeout is reached.
|
16
|
+
#
|
17
|
+
# @param [Hash] options the options for upon( )
|
18
|
+
# @option options [Fixnum] :seconds Timeout in seconds
|
19
|
+
# @option options [String] :minutes Timeout in minutes
|
20
|
+
# @option options [String] :exception Exception to throw if timeout is reached
|
21
|
+
# @option options [String] :message Custom message for exception if timeout is reached
|
22
|
+
# @yield Block to execute until it returns true
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# WaitFor.upto( 30.seconds ) do
|
26
|
+
# has_event_happened?
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# WaitFor.upto( :minute => 1, :seconds => 30 ) do
|
31
|
+
# has_event_happened?
|
32
|
+
# end
|
9
33
|
def upto( options )
|
10
34
|
timer = Timer.new options
|
11
35
|
until yield or timer.out_of_time?
|
12
|
-
sleep 1
|
36
|
+
Kernel.sleep 1
|
13
37
|
end
|
14
38
|
end
|
15
39
|
end
|
16
|
-
end
|
40
|
+
end
|
data/lib/waitfor/fixnum.rb
CHANGED
@@ -1,14 +1,30 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
begin
|
2
|
+
# Attempt to access the Rails version because this will work in a Rails environment.
|
3
|
+
Rails.version
|
4
|
+
# If NameError is thrown, then we know we aren't in a Rails environment.
|
5
|
+
rescue NameError
|
6
|
+
# Since we aren't in a Rails environment, we'll need to monkeypatch Fixnum to support .seconds and .minutes
|
7
|
+
class Fixnum
|
8
|
+
# Return the current number unmodified since it is already in seconds.
|
9
|
+
#
|
10
|
+
# @return [Fixnum] the current unmodified.
|
11
|
+
def seconds
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
# Convert the current number into seconds.
|
16
|
+
#
|
17
|
+
# @return [Fixnum] the current number as seconds.
|
18
|
+
def minutes
|
19
|
+
self * 60
|
20
|
+
end
|
21
|
+
|
22
|
+
# Alias for seconds.
|
23
|
+
#
|
24
|
+
alias :second :seconds
|
25
|
+
|
26
|
+
# Alias for minutes.
|
27
|
+
#
|
28
|
+
alias :minute :minutes
|
4
29
|
end
|
5
|
-
|
6
|
-
self
|
7
|
-
end
|
8
|
-
def minute
|
9
|
-
self.minutes
|
10
|
-
end
|
11
|
-
def minutes
|
12
|
-
self * 60
|
13
|
-
end
|
14
|
-
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# @author James Bobowski
|
2
|
+
module WaitFor
|
3
|
+
module Settings
|
4
|
+
class Configuration
|
5
|
+
|
6
|
+
attr_accessor :seconds, :exception, :message
|
7
|
+
|
8
|
+
# A new instance of Configuration.
|
9
|
+
#
|
10
|
+
# @param [Hash|Fixnum] options settings for configuration such as `:seconds`, `:minutes`, `:exception` and `:message` or just a Fixnum representing seconds
|
11
|
+
def initialize( options )
|
12
|
+
# Initialize seconds to zero, so that from there we can add seconds to it.
|
13
|
+
@seconds = 0
|
14
|
+
# Determine which parser should be used, then call it, passing ourselves in for assignment of the parsed values.
|
15
|
+
parser_factory( options ).parse( options, self )
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# Determine which parser should be used based on the type of object passed in.
|
21
|
+
#
|
22
|
+
# @param [Hash|Fixnum] options settings for Configuration
|
23
|
+
# @return [Class] class of parser to use for this type of settings object
|
24
|
+
def parser_factory( options )
|
25
|
+
if options.instance_of? Fixnum
|
26
|
+
WaitFor::Settings::LegacyParser
|
27
|
+
elsif options.instance_of? Hash
|
28
|
+
WaitFor::Settings::Parser
|
29
|
+
else
|
30
|
+
raise "Unable to parse configuration options."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# @author James Bobowski
|
2
|
+
module WaitFor
|
3
|
+
module Settings
|
4
|
+
class LegacyParser
|
5
|
+
|
6
|
+
attr_accessor :params, :config
|
7
|
+
|
8
|
+
# Parse parameters and return Configuration object.
|
9
|
+
#
|
10
|
+
# @param [Fixnum] params parameters
|
11
|
+
# @param [Configuration] config new Configuration object to populate with options
|
12
|
+
# @return [Configuration] the configured Configuration object
|
13
|
+
def self.parse( params, config )
|
14
|
+
parser = WaitFor::Settings::LegacyParser.new( params, config )
|
15
|
+
parser.parse
|
16
|
+
parser.config
|
17
|
+
end
|
18
|
+
|
19
|
+
# Create a new Parser.
|
20
|
+
#
|
21
|
+
# @param [Fixnum] params parameters
|
22
|
+
# @param [Configuration] config new Configuration object to populate with options
|
23
|
+
# @return [Configuration] the configured Configuration object
|
24
|
+
def initialize( params, config )
|
25
|
+
@params, @config = params, config
|
26
|
+
|
27
|
+
# Default exception since legacy does not support specifying an exception.
|
28
|
+
@config.exception = WaitFor::TimeoutError
|
29
|
+
end
|
30
|
+
|
31
|
+
# Call all parsing methods in order.
|
32
|
+
def parse
|
33
|
+
parse_time
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# Parse seconds or minutes and set the Configuration's object seconds attribute.
|
39
|
+
def parse_time
|
40
|
+
@config.seconds = @params
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# @author James Bobowski
|
2
|
+
module WaitFor
|
3
|
+
module Settings
|
4
|
+
class Parser
|
5
|
+
|
6
|
+
attr_accessor :params, :config
|
7
|
+
|
8
|
+
# Parse parameters and return Configuration object.
|
9
|
+
#
|
10
|
+
# @param [Fixnum] params parameters
|
11
|
+
# @param [Configuration] config new Configuration object to populate with options
|
12
|
+
# @return [Configuration] the configured Configuration object
|
13
|
+
def self.parse( params, config )
|
14
|
+
parser = WaitFor::Settings::Parser.new( params, config )
|
15
|
+
parser.parse
|
16
|
+
parser.config
|
17
|
+
end
|
18
|
+
|
19
|
+
# Create a new Parser.
|
20
|
+
#
|
21
|
+
# @param [Fixnum] params parameters
|
22
|
+
# @param [Configuration] config new Configuration object to populate with options
|
23
|
+
# @return [Configuration] the configured Configuration object
|
24
|
+
def initialize( params, config )
|
25
|
+
@params, @config = params, config
|
26
|
+
end
|
27
|
+
|
28
|
+
# Call all parsing methods in order.
|
29
|
+
def parse
|
30
|
+
parse_seconds
|
31
|
+
parse_minutes
|
32
|
+
parse_exception
|
33
|
+
parse_message
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# Parse seconds and add to the Configuration's object seconds attribute.
|
39
|
+
def parse_seconds
|
40
|
+
@config.seconds += ( @params[ :second ] || 0 ) + ( @params[ :seconds ] || 0 )
|
41
|
+
end
|
42
|
+
|
43
|
+
# Parse minutes and add to the Configuration's object seconds attribute.
|
44
|
+
def parse_minutes
|
45
|
+
@config.seconds += ( ( @params[ :minute ] || 0 ) + ( @params[ :minutes ] || 0 ) ) * 60
|
46
|
+
end
|
47
|
+
|
48
|
+
# Parse exception and set the Configuration's object exception attribute.
|
49
|
+
def parse_exception
|
50
|
+
@config.exception = @params[ :exception ] || WaitFor::TimeoutError
|
51
|
+
end
|
52
|
+
|
53
|
+
# Parse message and set the Configuration's object message attribute.
|
54
|
+
def parse_message
|
55
|
+
@config.message = @params[ :message ] || "WaitFor timed out!"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/waitfor/timer.rb
CHANGED
@@ -1,35 +1,44 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
@
|
1
|
+
# @author James Bobowski
|
2
|
+
module WaitFor
|
3
|
+
class Timer
|
4
|
+
# A new instance of Timer.
|
5
|
+
#
|
6
|
+
# @param [Hash] options for Timer configuration such as `:seconds`, `:minutes`, `:exception` and `:message`
|
7
|
+
def initialize( options )
|
8
|
+
@config = WaitFor::Settings::Configuration.new( options )
|
9
|
+
@start_time = Time.now
|
9
10
|
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
seconds = options[ :second ] || 0
|
20
|
-
seconds = options[ :seconds ] || seconds
|
12
|
+
# Returns whether the elapsed time has surpassed the allotted time
|
13
|
+
#
|
14
|
+
# @return [Boolean] of elapsed time is greater or equal to the set number of seconds
|
15
|
+
# @raise [WaitFor::TimeoutError] Timeout error if time limit is reached
|
16
|
+
def out_of_time?
|
17
|
+
raise_exception if timed_out?
|
18
|
+
false
|
19
|
+
end
|
21
20
|
|
22
|
-
|
23
|
-
minutes = options[ :minutes ] || minutes
|
21
|
+
private
|
24
22
|
|
25
|
-
|
26
|
-
|
23
|
+
# Determine whether the amount of time elapsed is greater than the limit.
|
24
|
+
#
|
25
|
+
# @return [Boolean] whether the amount of time elapsed is greater than the limit.
|
26
|
+
def timed_out?
|
27
|
+
elapsed_time >= @config.seconds
|
28
|
+
end
|
27
29
|
|
28
|
-
|
29
|
-
|
30
|
-
|
30
|
+
# Calculate the number of seconds that have passed since object was initialized.
|
31
|
+
#
|
32
|
+
# @return [Fixnum] number seconds that have passed since object was initialized.
|
33
|
+
def elapsed_time
|
34
|
+
Time.now - @start_time
|
31
35
|
end
|
32
36
|
|
33
|
-
|
37
|
+
# Raise the exceptions for this object.
|
38
|
+
#
|
39
|
+
# @raise [WaitFor::TimeoutError] Timeout error
|
40
|
+
def raise_exception
|
41
|
+
raise @config.exception, @config.message
|
42
|
+
end
|
34
43
|
end
|
35
|
-
end
|
44
|
+
end
|