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

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.
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