rspec-sequencing 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6f55448a904178a9d9de76f150466e1a16a62b20
4
- data.tar.gz: 840557a2288455add1ff877359371601965e44f3
2
+ SHA256:
3
+ metadata.gz: 9ff6bcb858f98c3a60578bb99c9b7c5944ed85fe174d97f4d2b1d808b3cf3568
4
+ data.tar.gz: 19db7c75c1c8ae858070a430a2c9c3983abc4dd4ba02b4ea0aa82e616343fa0c
5
5
  SHA512:
6
- metadata.gz: 17e9d48eaddc44c682889bbc52ae29f0db672f2fccaf0a322c4d5f2b71b6e595cc1550a87c33de796f31d281c98daff737e64912cf7e6403c921eb91c82ab085
7
- data.tar.gz: 8fb78869e9c0e63cf5306c7cd927eeb864aa5614d0ad87176f4585db942eaf4d29d85220a7bca900c2519dbfc8fa5399acd04902d8eb5e23a8345de9bffeb228
6
+ metadata.gz: 2446014dd4416e4e774adcddf6d1fdbffe6cd546a90f2a6f5a45fd7039bb97c2a6eefd68e2518522e29bf3744cdf3fd94a9681c3c8fd1e9b8bd9ff3ef7e3ffaf
7
+ data.tar.gz: 0aa4c7910af0457c715fc5b7b9e2a161c0ae8a1c708821fee8d1241326b9365a191f76c0fa5c242e17df9ea6a9c19fc37719ad443ff9451f9a0370ab3c05497e
data/README.md CHANGED
@@ -101,71 +101,36 @@ You might be tempted to think, I can just do:
101
101
  ```
102
102
  and you would be correct, for this contrived example.
103
103
 
104
- Here is a real example from the ruby-file_watch library. Here we use Sequencing to let files age and other elapsed time mechanisms.
104
+ Here is a real example from the Logstash file input plugin tests. Here we use Sequencing to let files age and other elapsed time mechanisms.
105
105
  ```ruby
106
- describe FileWatch::Watch do
107
- before(:all) do
108
- @thread_abort = Thread.abort_on_exception
109
- Thread.abort_on_exception = true
110
- end
111
-
112
- after(:all) do
113
- Thread.abort_on_exception = @thread_abort
114
- end
115
-
116
- let(:directory) { Stud::Temporary.directory }
117
- let(:watch_dir) { File.join(directory, "*.log") }
118
- let(:file_path) { File.join(directory, "1.log") }
119
- let(:loggr) { double("loggr", :debug? => true) }
120
- let(:results) { [] }
121
- let(:stat_interval) { 0.1 }
122
- let(:discover_interval) { 4 }
123
-
124
- let(:subscribe_proc) do
125
- lambda do
126
- formatted_puts("subscribing")
127
- # subject subscribe does not return until subject.quit is called
128
- subject.subscribe(stat_interval, discover_interval) do |event, watched_file|
129
- results.push([event, watched_file.path])
106
+ context "when ignore_older is less than close_older and all files are not expired" do
107
+ let(:opts) { super.merge(:ignore_older => 1, :close_older => 1.1) }
108
+ let(:suffix) { "N" }
109
+ let(:actions) do
110
+ RSpec::Sequencing
111
+ .run_after(0.1, "file created") do
112
+ File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
113
+ end
114
+ .then("start watching before file age reaches ignore_older") do
115
+ tailing.watch_this(watch_dir)
116
+ end
117
+ .then("wait for lines") do
118
+ wait(1.2).for{listener1.calls}.to eq([:open, :accept, :accept, :timed_out])
119
+ end
120
+ .then("quit after allowing time to close the file") do
121
+ tailing.quit
122
+ end
130
123
  end
131
- end
132
- end
133
-
134
- subject { FileWatch::Watch.new(:logger => loggr) }
135
-
136
- before do
137
- allow(loggr).to receive(:debug)
138
- end
139
- after do
140
- FileUtils.rm_rf(directory)
141
- end
142
124
 
143
- context "when ignore older and close older expiry is enabled and after timeout the file is appended-to" do
144
- before do
145
- subject.ignore_older = 2
146
- subject.close_older = 2
147
-
148
- RSpec::Sequencing
149
- .run("create file") do
150
- File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
151
- end
152
- .then_after(3.1, "start watching after the file ages more than two seconds") do
153
- subject.watch(watch_dir)
154
- end
155
- .then("append more lines to file when its 'ignored'") do
156
- File.open(file_path, "ab") { |file| file.write("line3\nline4\n") }
157
- end
158
- .then_after(3.1, "quit after allowing time for the close mechanism (timeout)") do
159
- subject.quit #<--- this unblocks the subscribe loop
160
- end
161
- end
162
-
163
- it "yields unignore, modify then timeout file events" do
164
- subscribe_proc.call #<--- the rspec thread is in a forever loop until quit is called in another thread
165
- expect(results).to eq([[:unignore, file_path], [:modify, file_path], [:timeout, file_path]])
125
+ it "reads lines normally" do
126
+ actions.activate
127
+ # subscribe is a blocking operation until tailing.quit is called on the sequence threads
128
+ # and that last step is dependent on the wait(1.2).for rspec expectation to succeed.
129
+ tailing.subscribe(observer)
130
+ actions.assert_no_errors # if the rspec `wait` call times out then this raises the RSpec failed assertion exception.
131
+ expect(listener1.lines).to eq(["line1", "line2"])
132
+ end
166
133
  end
167
- end
168
- end
169
134
  ```
170
135
 
171
136
  ### API
@@ -180,13 +145,22 @@ and some instance methods:
180
145
  ```ruby
181
146
  then(description = '', &block) # when the previous action completed the block runs without delay
182
147
  then_after(delay, description = '', &block) # when the previous action completed the block runs after delay seconds
148
+
149
+ # use `activate` or `activate_quietly`, when defining a sequence in a `let` block,
150
+ # to get RSpec to instantiate the sequence.
151
+ activate # this will print 'sequence activated' to the RSpec output_stream
152
+ activate_quietly # this will print nothing to the RSpec output_stream
153
+ value # this will block until the value from the last step is available
183
154
  ```
184
- Note that the description is optional, however by adding a description you document the action in the code and the spec output.
155
+ #### Note:
156
+ The description is optional, however by adding a description you document
157
+ the action in the code and the spec output. The description is printed after the delay and before the block is executed - use present tense, e.g. "Creating file"
185
158
 
186
- This library is multithreaded and uses the `concurrent-ruby` Dataflow and ScheduledTask mechanisms so you should set `Thread.abort_on_exception = true`
187
- in your specs so any exceptions in your actions bubble up to spec execution.
159
+ This library is multithreaded and uses the `concurrent-ruby` Dataflow feature.
160
+ Dataflow will absorb any exceptions and cause the dataflow to be rejected with the `reason` set to the Exception.
161
+ In this case, use the `assert_no_errors` method - this will re-raise the first exception it finds.
188
162
 
189
- ### Please note:
163
+ #### Note:
190
164
  - The sequence executes in different threads from the main RSpec thread. You will need to wait for the sequence value (see this library's specs) otherwise the test will end before the sequence ends and any expectations based on side effects of the sequence will not be met. However, if you are testing scenarios where the main RSpec thread is blocked in some way, e.g. a subscribe loop, then one, usually the last, task should act to unblock the RSpec main thread. In this case you should not wait on the sequence value.
191
165
  - The system that you are testing needs to be thread safe.
192
166
 
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'concurrent'
2
4
 
3
5
  module RSpec
@@ -10,42 +12,49 @@ module RSpec
10
12
  new.then_after(delay, description, &block)
11
13
  end
12
14
 
15
+ attr_reader :flows
16
+
13
17
  def initialize
14
- @flow = nil
18
+ @flows = []
15
19
  end
16
20
 
17
21
  def then_after(delay, description = '', &block)
18
- @flow = dataflow(delay, description, [@flow], &block)
22
+ @flows << Concurrent.dataflow(*@flows) do
23
+ sleep delay
24
+ formatted_puts(description) unless description.empty?
25
+ block.call
26
+ end
19
27
  self
20
28
  end
21
29
 
22
- def then(description, &block)
30
+ def then(description = '', &block)
23
31
  then_after(0, description, &block)
24
32
  end
25
33
 
26
- def dataflow(delay, description = '', inputs = [], &block)
27
- Concurrent.dataflow(*inputs.compact) do
28
- task(delay, &block).execute.value.tap do
29
- formatted_puts(description)
30
- end
31
- end
32
- end
33
-
34
- def task(delay, &block)
35
- Concurrent::ScheduledTask.new(delay) do
36
- block.call
37
- true
38
- end
34
+ def activate_quietly
35
+ # use this method if you define the sequencing in a let block so RSpec instantiates it
39
36
  end
40
37
 
41
38
  def activate
42
- # use this method if you define the sequencing in a let block
43
- # so RSpec runs it
39
+ # use this method if you define the sequencing in a let block so RSpec instantiates it
44
40
  formatted_puts "sequence activated"
45
41
  end
46
42
 
43
+ def assert_no_errors
44
+ # if you think you might get exceptions raised in any step the use this to get rspec to see them
45
+ # the raised error gets set as the dataflow `reason`.
46
+ value
47
+ @flows.map do |flow|
48
+ if flow.rejected?
49
+ raise flow.reason
50
+ end
51
+ flow.value
52
+ end
53
+ end
54
+
47
55
  def value
48
- @flow.value
56
+ # this is a blocking operation
57
+ @flows.last.value
49
58
  end
50
59
 
51
60
  private
@@ -1 +1,3 @@
1
+ # encoding: utf-8
2
+
1
3
  require "rspec/sequencing"
@@ -4,7 +4,8 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "rspec-sequencing"
7
- spec.version = "0.1.0"
7
+ spec.version = "0.1.1"
8
+ spec.licenses = ['Apache-2.0']
8
9
  spec.authors = ["Elastic"]
9
10
  spec.email = ["info@elastic.co"]
10
11
 
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-sequencing
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-19 00:00:00.000000000 Z
11
+ date: 2018-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - '>='
16
+ - - ">="
17
17
  - !ruby/object:Gem::Version
18
18
  version: 3.0.0
19
19
  name: rspec
@@ -21,13 +21,13 @@ dependencies:
21
21
  type: :runtime
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 3.0.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
- - - '>='
30
+ - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: '0'
33
33
  name: concurrent-ruby
@@ -35,13 +35,13 @@ dependencies:
35
35
  type: :runtime
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - ~>
44
+ - - "~>"
45
45
  - !ruby/object:Gem::Version
46
46
  version: '1.10'
47
47
  name: bundler
@@ -49,13 +49,13 @@ dependencies:
49
49
  type: :development
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ~>
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.10'
55
55
  - !ruby/object:Gem::Dependency
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - ~>
58
+ - - "~>"
59
59
  - !ruby/object:Gem::Version
60
60
  version: '10.0'
61
61
  name: rake
@@ -63,7 +63,7 @@ dependencies:
63
63
  type: :development
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ~>
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '10.0'
69
69
  description:
@@ -73,7 +73,7 @@ executables: []
73
73
  extensions: []
74
74
  extra_rdoc_files: []
75
75
  files:
76
- - .gitignore
76
+ - ".gitignore"
77
77
  - Gemfile
78
78
  - LICENSE
79
79
  - README.md
@@ -82,7 +82,8 @@ files:
82
82
  - lib/rspec_sequencing.rb
83
83
  - rspec-sequencing.gemspec
84
84
  homepage: https://github.com/elastic/rspec-sequencing
85
- licenses: []
85
+ licenses:
86
+ - Apache-2.0
86
87
  metadata: {}
87
88
  post_install_message:
88
89
  rdoc_options: []
@@ -90,17 +91,17 @@ require_paths:
90
91
  - lib
91
92
  required_ruby_version: !ruby/object:Gem::Requirement
92
93
  requirements:
93
- - - '>='
94
+ - - ">="
94
95
  - !ruby/object:Gem::Version
95
96
  version: '0'
96
97
  required_rubygems_version: !ruby/object:Gem::Requirement
97
98
  requirements:
98
- - - '>='
99
+ - - ">="
99
100
  - !ruby/object:Gem::Version
100
101
  version: '0'
101
102
  requirements: []
102
103
  rubyforge_project:
103
- rubygems_version: 2.4.8
104
+ rubygems_version: 2.6.13
104
105
  signing_key:
105
106
  specification_version: 4
106
107
  summary: Define sequenced actions that simulate real-world scenarios