chef 11.14.0.alpha.2-x86-mingw32 → 11.14.0.alpha.3-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/bin/chef-service-manager +1 -1
  3. data/lib/chef/application.rb +8 -2
  4. data/lib/chef/chef_fs/command_line.rb +4 -4
  5. data/lib/chef/chef_fs/file_system.rb +3 -3
  6. data/lib/chef/chef_fs/parallelizer.rb +66 -90
  7. data/lib/chef/chef_fs/parallelizer/flatten_enumerable.rb +35 -0
  8. data/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb +279 -0
  9. data/lib/chef/config.rb +36 -2
  10. data/lib/chef/cookbook/cookbook_version_loader.rb +0 -1
  11. data/lib/chef/cookbook/synchronizer.rb +64 -42
  12. data/lib/chef/cookbook_uploader.rb +4 -25
  13. data/lib/chef/cookbook_version.rb +12 -11
  14. data/lib/chef/formatters/error_inspectors/api_error_formatting.rb +18 -1
  15. data/lib/chef/formatters/error_inspectors/cookbook_sync_error_inspector.rb +1 -3
  16. data/lib/chef/knife/bootstrap.rb +23 -1
  17. data/lib/chef/knife/bootstrap/chef-aix.erb +58 -0
  18. data/lib/chef/knife/bootstrap/chef-full.erb +16 -13
  19. data/lib/chef/knife/core/bootstrap_context.rb +25 -1
  20. data/lib/chef/knife/list.rb +9 -8
  21. data/lib/chef/knife/serve.rb +44 -0
  22. data/lib/chef/knife/show.rb +2 -3
  23. data/lib/chef/knife/ssh.rb +1 -0
  24. data/lib/chef/mixin/create_path.rb +20 -4
  25. data/lib/chef/node.rb +19 -3
  26. data/lib/chef/platform/provider_mapping.rb +0 -1
  27. data/lib/chef/platform/query_helpers.rb +4 -3
  28. data/lib/chef/provider/env/windows.rb +10 -3
  29. data/lib/chef/provider/file.rb +1 -1
  30. data/lib/chef/provider/mount.rb +84 -42
  31. data/lib/chef/provider/package/freebsd/base.rb +92 -0
  32. data/lib/chef/provider/package/freebsd/pkg.rb +113 -0
  33. data/lib/chef/provider/package/freebsd/pkgng.rb +80 -0
  34. data/lib/chef/provider/package/freebsd/port.rb +70 -0
  35. data/lib/chef/providers.rb +3 -1
  36. data/lib/chef/resource/chef_gem.rb +2 -1
  37. data/lib/chef/resource/freebsd_package.rb +39 -3
  38. data/lib/chef/resource/lwrp_base.rb +2 -2
  39. data/lib/chef/resource/mount.rb +9 -9
  40. data/lib/chef/util/threaded_job_queue.rb +61 -0
  41. data/lib/chef/version.rb +1 -1
  42. data/lib/chef/version/platform.rb +2 -0
  43. data/lib/chef/whitelist.rb +82 -0
  44. data/lib/chef/win32/registry.rb +0 -1
  45. data/lib/chef/win32/version.rb +4 -3
  46. data/spec/functional/win32/versions_spec.rb +4 -4
  47. data/spec/integration/client/ipv6_spec.rb +1 -1
  48. data/spec/integration/knife/chef_fs_data_store_spec.rb +1 -1
  49. data/spec/integration/knife/chef_repo_path_spec.rb +4 -1
  50. data/spec/integration/knife/common_options_spec.rb +9 -9
  51. data/spec/integration/knife/cookbook_api_ipv6_spec.rb +2 -2
  52. data/spec/integration/knife/deps_spec.rb +3 -0
  53. data/spec/integration/knife/list_spec.rb +3 -0
  54. data/spec/integration/knife/raw_spec.rb +5 -2
  55. data/spec/integration/knife/redirection_spec.rb +4 -1
  56. data/spec/integration/knife/serve_spec.rb +57 -0
  57. data/spec/integration/knife/show_spec.rb +3 -0
  58. data/spec/support/pedant/run_pedant.rb +1 -0
  59. data/spec/support/platform_helpers.rb +7 -5
  60. data/spec/support/shared/context/config.rb +21 -0
  61. data/spec/support/shared/functional/file_resource.rb +52 -0
  62. data/spec/unit/chef_fs/parallelizer.rb +482 -0
  63. data/spec/unit/client_spec.rb +4 -2
  64. data/spec/unit/config_spec.rb +66 -12
  65. data/spec/unit/knife/bootstrap_spec.rb +6 -0
  66. data/spec/unit/knife/core/bootstrap_context_spec.rb +31 -1
  67. data/spec/unit/node_spec.rb +73 -3
  68. data/spec/unit/provider/mount_spec.rb +102 -79
  69. data/spec/unit/provider/package/{freebsd_spec.rb → freebsd/pkg_spec.rb} +19 -32
  70. data/spec/unit/provider/package/freebsd/pkgng_spec.rb +155 -0
  71. data/spec/unit/provider/package/freebsd/port_spec.rb +160 -0
  72. data/spec/unit/resource/chef_gem_spec.rb +5 -0
  73. data/spec/unit/resource/freebsd_package_spec.rb +63 -11
  74. data/spec/unit/resource/mount_spec.rb +11 -0
  75. data/spec/unit/role_spec.rb +5 -1
  76. data/spec/unit/run_lock_spec.rb +2 -0
  77. data/spec/unit/util/threaded_job_queue_spec.rb +51 -0
  78. data/spec/unit/version/platform_spec.rb +1 -1
  79. metadata +176 -161
  80. data/lib/chef/provider/package/freebsd.rb +0 -149
@@ -16,6 +16,7 @@
16
16
  # limitations under the License.
17
17
 
18
18
  require 'support/shared/integration/integration_helper'
19
+ require 'support/shared/context/config'
19
20
  require 'chef/knife/list'
20
21
 
21
22
  describe 'redirection' do
@@ -23,13 +24,15 @@ describe 'redirection' do
23
24
  include KnifeSupport
24
25
  include AppServerSupport
25
26
 
27
+ include_context "default config options"
28
+
26
29
  when_the_chef_server 'has a role' do
27
30
  role 'x', {}
28
31
 
29
32
  context 'and another server redirects to it with 302' do
30
33
  before :each do
31
34
  real_chef_server_url = Chef::Config.chef_server_url
32
- Chef::Config.chef_server_url = "http://127.0.0.1:9018"
35
+ Chef::Config.chef_server_url = "http://localhost:9018"
33
36
  app = lambda do |env|
34
37
  [302, {'Content-Type' => 'text','Location' => "#{real_chef_server_url}#{env['PATH_INFO']}" }, ['302 found'] ]
35
38
  end
@@ -0,0 +1,57 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2013 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'support/shared/integration/integration_helper'
19
+ require 'chef/knife/serve'
20
+ require 'chef/server_api'
21
+
22
+ describe 'knife serve' do
23
+ extend IntegrationSupport
24
+ include KnifeSupport
25
+ include AppServerSupport
26
+
27
+ when_the_repository 'also has one of each thing' do
28
+ file 'nodes/x.json', { 'foo' => 'bar' }
29
+
30
+ it 'knife serve serves up /nodes/x' do
31
+ exception = nil
32
+ t = Thread.new do
33
+ begin
34
+ knife('serve')
35
+ rescue
36
+ exception = $!
37
+ end
38
+ end
39
+ begin
40
+ Chef::Config.log_level = :debug
41
+ Chef::Config.chef_server_url = 'http://localhost:8889'
42
+ Chef::Config.node_name = nil
43
+ Chef::Config.client_key = nil
44
+ api = Chef::ServerAPI.new
45
+ api.get('nodes/x')['name'].should == 'x'
46
+ rescue
47
+ if exception
48
+ raise exception
49
+ else
50
+ raise
51
+ end
52
+ ensure
53
+ t.kill
54
+ end
55
+ end
56
+ end
57
+ end
@@ -16,12 +16,15 @@
16
16
  # limitations under the License.
17
17
 
18
18
  require 'support/shared/integration/integration_helper'
19
+ require 'support/shared/context/config'
19
20
  require 'chef/knife/show'
20
21
 
21
22
  describe 'knife show' do
22
23
  extend IntegrationSupport
23
24
  include KnifeSupport
24
25
 
26
+ include_context "default config options"
27
+
25
28
  when_the_chef_server "has one of each thing" do
26
29
  client 'x', '{}'
27
30
  cookbook 'x', '1.0.0', { 'metadata.rb' => 'version "1.0.0"' }
@@ -25,6 +25,7 @@ def start_server(chef_repo_path)
25
25
  Chef::Config.versioned_cookbooks = true
26
26
  chef_fs = Chef::ChefFS::Config.new.local_fs
27
27
  data_store = Chef::ChefFS::ChefFSDataStore.new(chef_fs)
28
+ data_store = ChefZero::DataStore::V1ToV2Adapter.new(data_store, 'chef', :org_defaults => ChefZero::DataStore::V1ToV2Adapter::ORG_DEFAULTS)
28
29
  server = ChefZero::Server.new(:port => 8889, :data_store => data_store)#, :log_level => :debug)
29
30
  server.start_background
30
31
  server
@@ -27,18 +27,20 @@ def windows?
27
27
  !!(RUBY_PLATFORM =~ /mswin|mingw|windows/)
28
28
  end
29
29
 
30
- require 'ruby-wmi' if windows?
30
+ require 'wmi-lite/wmi' if windows?
31
31
 
32
32
  def windows_domain_joined?
33
33
  return false unless windows?
34
- WMI::Win32_ComputerSystem.find(:first).PartOfDomain
34
+ wmi = WmiLite::Wmi.new
35
+ computer_system = wmi.first_of('Win32_ComputerSystem')
36
+ computer_system['partofdomain']
35
37
  end
36
38
 
37
39
  def windows_win2k3?
38
40
  return false unless windows?
39
-
40
- host = WMI::Win32_OperatingSystem.find(:first)
41
- (host.version && host.version.start_with?("5.2"))
41
+ wmi = WmiLite::Wmi.new
42
+ host = wmi.first_of('Win32_OperatingSystem')
43
+ (host['version'] && host['version'].start_with?("5.2"))
42
44
  end
43
45
 
44
46
  def mac_osx_106?
@@ -0,0 +1,21 @@
1
+
2
+ #
3
+ # Define config file setups for spec tests here.
4
+ # https://www.relishapp.com/rspec/rspec-core/docs/example-groups/shared-context
5
+ #
6
+
7
+ # Required chef files here:
8
+ require 'chef/config'
9
+
10
+ # Required spec files here:
11
+ require 'spec_helper'
12
+
13
+ # Basic config. Nothing fancy.
14
+ shared_context "default config options" do
15
+ before do
16
+ Chef::Config[:cache_path] = windows? ? 'C:\chef' : '/var/chef'
17
+ end
18
+
19
+ # Don't need to have an after block to reset the config...
20
+ # The spec_helper.rb takes care of resetting the config state.
21
+ end
@@ -645,6 +645,58 @@ shared_examples_for "a configured file resource" do
645
645
  end
646
646
  end
647
647
 
648
+ context "when the target file does not exist" do
649
+ before(:each) do
650
+ FileUtils.rm_rf(path)
651
+ end
652
+
653
+ after(:each) do
654
+ FileUtils.rm_rf(path)
655
+ end
656
+
657
+ def symlink?(file_path)
658
+ if windows?
659
+ Chef::ReservedNames::Win32::File.symlink?(file_path)
660
+ else
661
+ File.symlink?(file_path)
662
+ end
663
+ end
664
+
665
+ def real_file?(file_path)
666
+ !symlink?(file_path) && File.file?(file_path)
667
+ end
668
+
669
+ describe "when force_unlink is set to true" do
670
+ it ":create updates the target" do
671
+ resource.force_unlink(true)
672
+ resource.run_action(:create)
673
+ real_file?(path).should be_true
674
+ binread(path).should == expected_content
675
+ resource.should be_updated_by_last_action
676
+ end
677
+ end
678
+
679
+ describe "when force_unlink is set to false" do
680
+ it ":create updates the target" do
681
+ resource.force_unlink(true)
682
+ resource.run_action(:create)
683
+ real_file?(path).should be_true
684
+ binread(path).should == expected_content
685
+ resource.should be_updated_by_last_action
686
+ end
687
+ end
688
+
689
+ describe "when force_unlink is not set (default)" do
690
+ it ":create updates the target" do
691
+ resource.force_unlink(true)
692
+ resource.run_action(:create)
693
+ real_file?(path).should be_true
694
+ binread(path).should == expected_content
695
+ resource.should be_updated_by_last_action
696
+ end
697
+ end
698
+ end
699
+
648
700
  context "when the target file is a directory" do
649
701
  before(:each) do
650
702
  FileUtils.mkdir_p(path)
@@ -0,0 +1,482 @@
1
+ require 'spec_helper'
2
+ require 'chef/chef_fs/parallelizer'
3
+
4
+ describe Chef::ChefFS::Parallelizer do
5
+ before :each do
6
+ @start_time = Time.now
7
+ end
8
+
9
+ def elapsed_time
10
+ Time.now - @start_time
11
+ end
12
+
13
+ after :each do
14
+ parallelizer.kill
15
+ end
16
+
17
+ context 'With a Parallelizer with 5 threads' do
18
+ let :parallelizer do
19
+ Chef::ChefFS::Parallelizer.new(5)
20
+ end
21
+
22
+ def parallelize(inputs, options = {}, &block)
23
+ parallelizer.parallelize(inputs, { :main_thread_processing => false }.merge(options), &block)
24
+ end
25
+
26
+ it "parallel_do creates unordered output as soon as it is available" do
27
+ outputs = []
28
+ parallelizer.parallel_do([0.5,0.3,0.1]) do |val|
29
+ sleep val
30
+ outputs << val
31
+ end
32
+ elapsed_time.should < 0.6
33
+ outputs.should == [ 0.1, 0.3, 0.5 ]
34
+ end
35
+
36
+ context "With :ordered => false (unordered output)" do
37
+ it "An empty input produces an empty output" do
38
+ parallelize([], :ordered => false) do
39
+ sleep 10
40
+ end.to_a == []
41
+ elapsed_time.should < 0.1
42
+ end
43
+
44
+ it "10 sleep(0.2)s complete within 0.5 seconds" do
45
+ parallelize(1.upto(10), :ordered => false) do |i|
46
+ sleep 0.2
47
+ 'x'
48
+ end.to_a.should == %w(x x x x x x x x x x)
49
+ elapsed_time.should < 0.5
50
+ end
51
+
52
+ it "The output comes as soon as it is available" do
53
+ enum = parallelize([0.5,0.3,0.1], :ordered => false) do |val|
54
+ sleep val
55
+ val
56
+ end
57
+ enum.map do |value|
58
+ elapsed_time.should < value+0.1
59
+ value
60
+ end.should == [ 0.1, 0.3, 0.5 ]
61
+ end
62
+
63
+ it "An exception in input is passed through but does NOT stop processing" do
64
+ input = TestEnumerable.new(0.5,0.3,0.1) do
65
+ raise 'hi'
66
+ end
67
+ enum = parallelize(input, :ordered => false) { |x| sleep(x); x }
68
+ results = []
69
+ expect { enum.each { |value| results << value } }.to raise_error 'hi'
70
+ results.should == [ 0.1, 0.3, 0.5 ]
71
+ elapsed_time.should < 0.6
72
+ end
73
+
74
+ it "Exceptions in output are raised after all processing is done" do
75
+ processed = 0
76
+ enum = parallelize([1,2,'x',3], :ordered => false) do |x|
77
+ if x == 'x'
78
+ sleep 0.1
79
+ raise 'hi'
80
+ end
81
+ sleep 0.2
82
+ processed += 1
83
+ x
84
+ end
85
+ results = []
86
+ expect { enum.each { |value| results << value } }.to raise_error 'hi'
87
+ results.sort.should == [ 1, 2, 3 ]
88
+ elapsed_time.should < 0.3
89
+ processed.should == 3
90
+ end
91
+
92
+ it "Exceptions with :stop_on_exception are raised after all processing is done" do
93
+ processed = 0
94
+ parallelized = parallelize([0.3,0.3,'x',0.3,0.3,0.3,0.3,0.3], :ordered => false, :stop_on_exception => true) do |x|
95
+ if x == 'x'
96
+ sleep(0.1)
97
+ raise 'hi'
98
+ end
99
+ sleep(x)
100
+ processed += 1
101
+ x
102
+ end
103
+ expect { parallelized.to_a }.to raise_error 'hi'
104
+ processed.should == 4
105
+ end
106
+ end
107
+
108
+ context "With :ordered => true (ordered output)" do
109
+ it "An empty input produces an empty output" do
110
+ parallelize([]) do
111
+ sleep 10
112
+ end.to_a == []
113
+ elapsed_time.should < 0.1
114
+ end
115
+
116
+ it "10 sleep(0.2)s complete within 0.5 seconds" do
117
+ parallelize(1.upto(10), :ordered => true) do |i|
118
+ sleep 0.2
119
+ 'x'
120
+ end.to_a.should == %w(x x x x x x x x x x)
121
+ elapsed_time.should < 0.5
122
+ end
123
+
124
+ it "Output comes in the order of the input" do
125
+ enum = parallelize([0.5,0.3,0.1]) do |val|
126
+ sleep val
127
+ val
128
+ end.enum_for(:each_with_index)
129
+ enum.next.should == [ 0.5, 0 ]
130
+ enum.next.should == [ 0.3, 1 ]
131
+ enum.next.should == [ 0.1, 2 ]
132
+ elapsed_time.should < 0.6
133
+ end
134
+
135
+ it "Exceptions in input are raised in the correct sequence but do NOT stop processing" do
136
+ input = TestEnumerable.new(0.5,0.3,0.1) do
137
+ raise 'hi'
138
+ end
139
+ results = []
140
+ enum = parallelize(input) { |x| sleep(x); x }
141
+ expect { enum.each { |value| results << value } }.to raise_error 'hi'
142
+ elapsed_time.should < 0.6
143
+ results.should == [ 0.5, 0.3, 0.1 ]
144
+ end
145
+
146
+ it "Exceptions in output are raised in the correct sequence and running processes do NOT stop processing" do
147
+ processed = 0
148
+ enum = parallelize([1,2,'x',3]) do |x|
149
+ if x == 'x'
150
+ sleep(0.1)
151
+ raise 'hi'
152
+ end
153
+ sleep(0.2)
154
+ processed += 1
155
+ x
156
+ end
157
+ results = []
158
+ expect { enum.each { |value| results << value } }.to raise_error 'hi'
159
+ results.should == [ 1, 2 ]
160
+ elapsed_time.should < 0.3
161
+ processed.should == 3
162
+ end
163
+
164
+ it "Exceptions with :stop_on_exception are raised after all processing is done" do
165
+ processed = 0
166
+ parallelized = parallelize([0.3,0.3,'x',0.3,0.3,0.3,0.3,0.3], :ordered => false, :stop_on_exception => true) do |x|
167
+ if x == 'x'
168
+ sleep(0.1)
169
+ raise 'hi'
170
+ end
171
+ sleep(x)
172
+ processed += 1
173
+ x
174
+ end
175
+ expect { parallelized.to_a }.to raise_error 'hi'
176
+ processed.should == 4
177
+ end
178
+ end
179
+
180
+ it "When the input is slow, output still proceeds" do
181
+ input = TestEnumerable.new do |&block|
182
+ block.call(1)
183
+ sleep 0.1
184
+ block.call(2)
185
+ sleep 0.1
186
+ block.call(3)
187
+ sleep 0.1
188
+ end
189
+ enum = parallelize(input) { |x| x }
190
+ enum.map do |value|
191
+ elapsed_time.should < (value+1)*0.1
192
+ value
193
+ end.should == [ 1, 2, 3 ]
194
+ end
195
+ end
196
+
197
+ context "With a Parallelizer with 1 thread" do
198
+ let :parallelizer do
199
+ Chef::ChefFS::Parallelizer.new(1)
200
+ end
201
+
202
+ context "when the thread is occupied with a job" do
203
+ before :each do
204
+ parallelizer
205
+ started = false
206
+ @occupying_job_finished = occupying_job_finished = [ false ]
207
+ @thread = Thread.new do
208
+ begin
209
+ parallelizer.parallelize([0], :main_thread_processing => false) do |x|
210
+ started = true
211
+ sleep(0.3)
212
+ occupying_job_finished[0] = true
213
+ end.wait
214
+ ensure
215
+ end
216
+ end
217
+ while !started
218
+ sleep(0.01)
219
+ end
220
+ end
221
+
222
+ after :each do
223
+ if RUBY_VERSION.to_f > 1.8
224
+ Thread.kill(@thread)
225
+ end
226
+ end
227
+
228
+ it "parallelize with :main_thread_processing = true does not block" do
229
+ parallelizer.parallelize([1]) do |x|
230
+ sleep(0.1)
231
+ x
232
+ end.to_a.should == [ 1 ]
233
+ elapsed_time.should < 0.2
234
+ end
235
+
236
+ it "parallelize with :main_thread_processing = false waits for the job to finish" do
237
+ parallelizer.parallelize([1], :main_thread_processing => false) do |x|
238
+ sleep(0.1)
239
+ x+1
240
+ end.to_a.should == [ 2 ]
241
+ elapsed_time.should > 0.3
242
+ end
243
+
244
+ it "resizing the Parallelizer to 0 waits for the job to stop" do
245
+ elapsed_time.should < 0.2
246
+ parallelizer.resize(0)
247
+ parallelizer.num_threads.should == 0
248
+ elapsed_time.should > 0.25
249
+ @occupying_job_finished.should == [ true ]
250
+ end
251
+
252
+ it "stopping the Parallelizer waits for the job to finish" do
253
+ elapsed_time.should < 0.2
254
+ parallelizer.stop
255
+ parallelizer.num_threads.should == 0
256
+ elapsed_time.should > 0.25
257
+ @occupying_job_finished.should == [ true ]
258
+ end
259
+
260
+ it "resizing the Parallelizer to 2 does not stop the job" do
261
+ elapsed_time.should < 0.2
262
+ parallelizer.resize(2)
263
+ parallelizer.num_threads.should == 2
264
+ elapsed_time.should < 0.2
265
+ sleep(0.3)
266
+ @occupying_job_finished.should == [ true ]
267
+ end
268
+ end
269
+
270
+ context "enumerable methods should run efficiently" do
271
+ it ".count does not process anything" do
272
+ outputs_processed = 0
273
+ input_mapper = TestEnumerable.new(1,2,3,4,5,6)
274
+ enum = parallelizer.parallelize(input_mapper) do |x|
275
+ outputs_processed += 1
276
+ sleep(0.05) # Just enough to yield and get other inputs in the queue
277
+ x
278
+ end
279
+ enum.count.should == 6
280
+ outputs_processed.should == 0
281
+ input_mapper.num_processed.should == 6
282
+ end
283
+
284
+ it ".count with arguments works normally" do
285
+ outputs_processed = 0
286
+ input_mapper = TestEnumerable.new(1,1,1,1,2,2,2,3,3,4)
287
+ enum = parallelizer.parallelize(input_mapper) do |x|
288
+ outputs_processed += 1
289
+ x
290
+ end
291
+ enum.count { |x| x > 1 }.should == 6
292
+ enum.count(2).should == 3
293
+ outputs_processed.should == 20
294
+ input_mapper.num_processed.should == 20
295
+ end
296
+
297
+ it ".first does not enumerate anything other than the first result(s)" do
298
+ outputs_processed = 0
299
+ input_mapper = TestEnumerable.new(1,2,3,4,5,6)
300
+ enum = parallelizer.parallelize(input_mapper) do |x|
301
+ outputs_processed += 1
302
+ sleep(0.05) # Just enough to yield and get other inputs in the queue
303
+ x
304
+ end
305
+ enum.first.should == 1
306
+ enum.first(2).should == [1,2]
307
+ outputs_processed.should == 3
308
+ input_mapper.num_processed.should == 3
309
+ end
310
+
311
+ it ".take does not enumerate anything other than the first result(s)" do
312
+ outputs_processed = 0
313
+ input_mapper = TestEnumerable.new(1,2,3,4,5,6)
314
+ enum = parallelizer.parallelize(input_mapper) do |x|
315
+ outputs_processed += 1
316
+ sleep(0.05) # Just enough to yield and get other inputs in the queue
317
+ x
318
+ end
319
+ enum.take(2).should == [1,2]
320
+ outputs_processed.should == 2
321
+ input_mapper.num_processed.should == 2
322
+ end
323
+
324
+ it ".drop does not process anything other than the last result(s)" do
325
+ outputs_processed = 0
326
+ input_mapper = TestEnumerable.new(1,2,3,4,5,6)
327
+ enum = parallelizer.parallelize(input_mapper) do |x|
328
+ outputs_processed += 1
329
+ sleep(0.05) # Just enough to yield and get other inputs in the queue
330
+ x
331
+ end
332
+ enum.drop(2).should == [3,4,5,6]
333
+ outputs_processed.should == 4
334
+ input_mapper.num_processed.should == 6
335
+ end
336
+
337
+ if Enumerable.method_defined?(:lazy)
338
+ it ".lazy.take does not enumerate anything other than the first result(s)" do
339
+ outputs_processed = 0
340
+ input_mapper = TestEnumerable.new(1,2,3,4,5,6)
341
+ enum = parallelizer.parallelize(input_mapper) do |x|
342
+ outputs_processed += 1
343
+ sleep(0.05) # Just enough to yield and get other inputs in the queue
344
+ x
345
+ end
346
+ enum.lazy.take(2).to_a.should == [1,2]
347
+ outputs_processed.should == 2
348
+ input_mapper.num_processed.should == 2
349
+ end
350
+
351
+ it ".drop does not process anything other than the last result(s)" do
352
+ outputs_processed = 0
353
+ input_mapper = TestEnumerable.new(1,2,3,4,5,6)
354
+ enum = parallelizer.parallelize(input_mapper) do |x|
355
+ outputs_processed += 1
356
+ sleep(0.05) # Just enough to yield and get other inputs in the queue
357
+ x
358
+ end
359
+ enum.lazy.drop(2).to_a.should == [3,4,5,6]
360
+ outputs_processed.should == 4
361
+ input_mapper.num_processed.should == 6
362
+ end
363
+
364
+ it "lazy enumerable is actually lazy" do
365
+ outputs_processed = 0
366
+ input_mapper = TestEnumerable.new(1,2,3,4,5,6)
367
+ enum = parallelizer.parallelize(input_mapper) do |x|
368
+ outputs_processed += 1
369
+ sleep(0.05) # Just enough to yield and get other inputs in the queue
370
+ x
371
+ end
372
+ enum.lazy.take(2)
373
+ enum.lazy.drop(2)
374
+ sleep(0.1)
375
+ outputs_processed.should == 0
376
+ input_mapper.num_processed.should == 0
377
+ end
378
+ end
379
+ end
380
+
381
+ context "running enumerable multiple times should function correctly" do
382
+ it ".map twice on the same parallel enumerable returns the correct results and re-processes the input" do
383
+ outputs_processed = 0
384
+ input_mapper = TestEnumerable.new(1,2,3)
385
+ enum = parallelizer.parallelize(input_mapper) do |x|
386
+ outputs_processed += 1
387
+ x
388
+ end
389
+ enum.map { |x| x }.should == [1,2,3]
390
+ enum.map { |x| x }.should == [1,2,3]
391
+ outputs_processed.should == 6
392
+ input_mapper.num_processed.should == 6
393
+ end
394
+
395
+ it ".first and then .map on the same parallel enumerable returns the correct results and re-processes the input" do
396
+ outputs_processed = 0
397
+ input_mapper = TestEnumerable.new(1,2,3)
398
+ enum = parallelizer.parallelize(input_mapper) do |x|
399
+ outputs_processed += 1
400
+ x
401
+ end
402
+ enum.first.should == 1
403
+ enum.map { |x| x }.should == [1,2,3]
404
+ outputs_processed.should >= 4
405
+ input_mapper.num_processed.should >= 4
406
+ end
407
+
408
+ it "two simultaneous enumerations throws an exception" do
409
+ enum = parallelizer.parallelize([1,2,3]) { |x| x }
410
+ a = enum.enum_for(:each)
411
+ a.next
412
+ expect do
413
+ b = enum.enum_for(:each)
414
+ b.next
415
+ end.to raise_error
416
+ end
417
+ end
418
+ end
419
+
420
+ context "With a Parallelizer with 0 threads" do
421
+ let :parallelizer do
422
+ Chef::ChefFS::Parallelizer.new(0)
423
+ end
424
+
425
+ context "And main_thread_processing on" do
426
+ it "succeeds in running" do
427
+ parallelizer.parallelize([0.5]) { |x| x*2 }.to_a.should == [1]
428
+ end
429
+ end
430
+ end
431
+
432
+ context "With a Parallelizer with 10 threads" do
433
+ let :parallelizer do
434
+ Chef::ChefFS::Parallelizer.new(10)
435
+ end
436
+
437
+ it "does not have contention issues with large numbers of inputs" do
438
+ parallelizer.parallelize(1.upto(500)) { |x| x+1 }.to_a.should == 2.upto(501).to_a
439
+ end
440
+
441
+ it "does not have contention issues with large numbers of inputs with ordering off" do
442
+ parallelizer.parallelize(1.upto(500), :ordered => false) { |x| x+1 }.to_a.sort.should == 2.upto(501).to_a
443
+ end
444
+
445
+ it "does not have contention issues with large numbers of jobs and inputs with ordering off" do
446
+ parallelizers = 0.upto(99).map do
447
+ parallelizer.parallelize(1.upto(500)) { |x| x+1 }
448
+ end
449
+ outputs = []
450
+ threads = 0.upto(99).map do |i|
451
+ Thread.new { outputs[i] = parallelizers[i].to_a }
452
+ end
453
+ threads.each { |thread| thread.join }
454
+ outputs.each { |output| output.sort.should == 2.upto(501).to_a }
455
+ end
456
+ end
457
+
458
+ class TestEnumerable
459
+ include Enumerable
460
+
461
+ def initialize(*values, &block)
462
+ @values = values
463
+ @block = block
464
+ @num_processed = 0
465
+ end
466
+
467
+ attr_reader :num_processed
468
+
469
+ def each(&each_block)
470
+ @values.each do |value|
471
+ @num_processed += 1
472
+ each_block.call(value)
473
+ end
474
+ if @block
475
+ @block.call do |value|
476
+ @num_processed += 1
477
+ each_block.call(value)
478
+ end
479
+ end
480
+ end
481
+ end
482
+ end