logstash-core 2.2.4.snapshot1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of logstash-core might be problematic. Click here for more details.

Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/lib/logstash-core.rb +1 -0
  3. data/lib/logstash-core/logstash-core.rb +3 -0
  4. data/lib/logstash-core/version.rb +8 -0
  5. data/lib/logstash/agent.rb +391 -0
  6. data/lib/logstash/codecs/base.rb +50 -0
  7. data/lib/logstash/config/config_ast.rb +550 -0
  8. data/lib/logstash/config/cpu_core_strategy.rb +32 -0
  9. data/lib/logstash/config/defaults.rb +12 -0
  10. data/lib/logstash/config/file.rb +39 -0
  11. data/lib/logstash/config/grammar.rb +3503 -0
  12. data/lib/logstash/config/mixin.rb +518 -0
  13. data/lib/logstash/config/registry.rb +13 -0
  14. data/lib/logstash/environment.rb +98 -0
  15. data/lib/logstash/errors.rb +12 -0
  16. data/lib/logstash/filters/base.rb +205 -0
  17. data/lib/logstash/inputs/base.rb +116 -0
  18. data/lib/logstash/inputs/threadable.rb +18 -0
  19. data/lib/logstash/java_integration.rb +116 -0
  20. data/lib/logstash/json.rb +61 -0
  21. data/lib/logstash/logging.rb +91 -0
  22. data/lib/logstash/namespace.rb +13 -0
  23. data/lib/logstash/output_delegator.rb +172 -0
  24. data/lib/logstash/outputs/base.rb +91 -0
  25. data/lib/logstash/patches.rb +5 -0
  26. data/lib/logstash/patches/bugfix_jruby_2558.rb +51 -0
  27. data/lib/logstash/patches/cabin.rb +35 -0
  28. data/lib/logstash/patches/profile_require_calls.rb +47 -0
  29. data/lib/logstash/patches/rubygems.rb +38 -0
  30. data/lib/logstash/patches/stronger_openssl_defaults.rb +68 -0
  31. data/lib/logstash/pipeline.rb +499 -0
  32. data/lib/logstash/pipeline_reporter.rb +114 -0
  33. data/lib/logstash/plugin.rb +120 -0
  34. data/lib/logstash/program.rb +14 -0
  35. data/lib/logstash/runner.rb +124 -0
  36. data/lib/logstash/shutdown_watcher.rb +100 -0
  37. data/lib/logstash/util.rb +203 -0
  38. data/lib/logstash/util/buftok.rb +139 -0
  39. data/lib/logstash/util/charset.rb +35 -0
  40. data/lib/logstash/util/decorators.rb +52 -0
  41. data/lib/logstash/util/defaults_printer.rb +31 -0
  42. data/lib/logstash/util/filetools.rb +186 -0
  43. data/lib/logstash/util/java_version.rb +66 -0
  44. data/lib/logstash/util/password.rb +25 -0
  45. data/lib/logstash/util/plugin_version.rb +56 -0
  46. data/lib/logstash/util/prctl.rb +10 -0
  47. data/lib/logstash/util/retryable.rb +40 -0
  48. data/lib/logstash/util/socket_peer.rb +7 -0
  49. data/lib/logstash/util/unicode_trimmer.rb +81 -0
  50. data/lib/logstash/util/worker_threads_default_printer.rb +29 -0
  51. data/lib/logstash/util/wrapped_synchronous_queue.rb +41 -0
  52. data/lib/logstash/version.rb +14 -0
  53. data/locales/en.yml +204 -0
  54. data/logstash-core.gemspec +58 -0
  55. data/spec/conditionals_spec.rb +429 -0
  56. data/spec/logstash/agent_spec.rb +85 -0
  57. data/spec/logstash/config/config_ast_spec.rb +146 -0
  58. data/spec/logstash/config/cpu_core_strategy_spec.rb +123 -0
  59. data/spec/logstash/config/defaults_spec.rb +10 -0
  60. data/spec/logstash/config/mixin_spec.rb +158 -0
  61. data/spec/logstash/environment_spec.rb +56 -0
  62. data/spec/logstash/filters/base_spec.rb +251 -0
  63. data/spec/logstash/inputs/base_spec.rb +74 -0
  64. data/spec/logstash/java_integration_spec.rb +304 -0
  65. data/spec/logstash/json_spec.rb +96 -0
  66. data/spec/logstash/output_delegator_spec.rb +144 -0
  67. data/spec/logstash/outputs/base_spec.rb +40 -0
  68. data/spec/logstash/patches_spec.rb +90 -0
  69. data/spec/logstash/pipeline_reporter_spec.rb +85 -0
  70. data/spec/logstash/pipeline_spec.rb +455 -0
  71. data/spec/logstash/plugin_spec.rb +169 -0
  72. data/spec/logstash/runner_spec.rb +68 -0
  73. data/spec/logstash/shutdown_watcher_spec.rb +113 -0
  74. data/spec/logstash/util/buftok_spec.rb +31 -0
  75. data/spec/logstash/util/charset_spec.rb +74 -0
  76. data/spec/logstash/util/defaults_printer_spec.rb +50 -0
  77. data/spec/logstash/util/java_version_spec.rb +79 -0
  78. data/spec/logstash/util/plugin_version_spec.rb +64 -0
  79. data/spec/logstash/util/unicode_trimmer_spec.rb +55 -0
  80. data/spec/logstash/util/worker_threads_default_printer_spec.rb +45 -0
  81. data/spec/logstash/util/wrapped_synchronous_queue_spec.rb +28 -0
  82. data/spec/logstash/util_spec.rb +35 -0
  83. metadata +364 -0
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+ require "logstash/environment"
4
+
5
+ describe LogStash::Environment do
6
+
7
+ context "when loading jars dependencies" do
8
+
9
+ let(:default_jars_location) { File.join("vendor", "jar-dependencies") }
10
+ let(:default_runtime_location) { File.join(default_jars_location,"runtime-jars","*.jar") }
11
+ let(:default_test_location) { File.join(default_jars_location,"test-jars","*.jar") }
12
+
13
+ it "raises an exception if jruby is not available" do
14
+ expect(subject).to receive(:jruby?).and_return(false)
15
+ expect { subject.load_runtime_jars! }.to raise_error
16
+ end
17
+
18
+ it "find runtime jars in the default location" do
19
+ expect(subject).to receive(:find_jars).with(default_runtime_location).and_return([])
20
+ subject.load_runtime_jars!
21
+ end
22
+
23
+ it "find test jars in the default location" do
24
+ expect(subject).to receive(:find_jars).with(default_test_location).and_return([])
25
+ subject.load_test_jars!
26
+ end
27
+
28
+ context "when loading a jar file" do
29
+
30
+ let(:dummy_jar_file) { File.join(default_jars_location,"runtime-jars","elasticsearch.jar") }
31
+
32
+ it "requires the jar files if there are jars to load" do
33
+ expect(subject).to receive(:find_jars).with(default_runtime_location).and_return([dummy_jar_file])
34
+ expect(subject).to receive(:require).with(dummy_jar_file)
35
+ subject.load_runtime_jars!
36
+ end
37
+
38
+ it "raises an exception if there are no jars to load" do
39
+ allow(Dir).to receive(:glob).and_return([])
40
+ expect { subject.load_runtime_jars! }.to raise_error
41
+ end
42
+ end
43
+ end
44
+
45
+ context "add_plugin_path" do
46
+ let(:path) { "/some/path" }
47
+
48
+ before(:each) { expect($LOAD_PATH).to_not include(path) }
49
+ after(:each) { $LOAD_PATH.delete(path) }
50
+
51
+ it "should add the path to $LOAD_PATH" do
52
+ expect{subject.add_plugin_path(path)}.to change{$LOAD_PATH.size}.by(1)
53
+ expect($LOAD_PATH).to include(path)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,251 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+ require "logstash/json"
4
+
5
+ # use a dummy NOOP filter to test Filters::Base
6
+ class LogStash::Filters::NOOP < LogStash::Filters::Base
7
+ config_name "noop"
8
+ milestone 2
9
+
10
+ def register; end
11
+
12
+ def filter(event)
13
+ return unless filter?(event)
14
+ filter_matched(event)
15
+ end
16
+ end
17
+
18
+ describe LogStash::Filters::Base do
19
+ subject {LogStash::Filters::Base.new({})}
20
+
21
+ it "should provide method interfaces to override" do
22
+ expect{subject.register}.to raise_error(RuntimeError)
23
+ expect{subject.filter(:foo)}.to raise_error(RuntimeError)
24
+ end
25
+
26
+ it "should provide class public API" do
27
+ [:register, :filter, :multi_filter, :execute, :threadsafe?, :filter_matched, :filter?, :close].each do |method|
28
+ expect(subject).to respond_to(method)
29
+ end
30
+ end
31
+
32
+ context "multi_filter" do
33
+ let(:event1){LogStash::Event.new}
34
+ let(:event2){LogStash::Event.new}
35
+
36
+ it "should multi_filter without new events" do
37
+ allow(subject).to receive(:filter) do |event, &block|
38
+ nil
39
+ end
40
+ expect(subject.multi_filter([event1])).to eq([event1])
41
+ end
42
+
43
+ it "should multi_filter with new events" do
44
+ allow(subject).to receive(:filter) do |event, &block|
45
+ block.call(event2)
46
+ end
47
+ expect(subject.multi_filter([event1])).to eq([event1, event2])
48
+ end
49
+ end
50
+ end
51
+
52
+ describe LogStash::Filters::NOOP do
53
+
54
+ describe "adding multiple values to one field" do
55
+ config <<-CONFIG
56
+ filter {
57
+ noop {
58
+ add_field => ["new_field", "new_value"]
59
+ add_field => ["new_field", "new_value_2"]
60
+ }
61
+ }
62
+ CONFIG
63
+
64
+ sample "example" do
65
+ insist { subject["new_field"] } == ["new_value", "new_value_2"]
66
+ end
67
+ end
68
+
69
+ describe "type parsing" do
70
+ config <<-CONFIG
71
+ filter {
72
+ noop {
73
+ add_tag => ["test"]
74
+ }
75
+ }
76
+ CONFIG
77
+
78
+ sample("type" => "noop") do
79
+ insist { subject["tags"] } == ["test"]
80
+ end
81
+ end
82
+
83
+ describe "tags parsing with one tag" do
84
+ config <<-CONFIG
85
+ filter {
86
+ noop {
87
+ add_tag => ["test"]
88
+ }
89
+ }
90
+ CONFIG
91
+
92
+ sample("type" => "noop") do
93
+ insist { subject["tags"] } == ["test"]
94
+ end
95
+
96
+ sample("type" => "noop", "tags" => ["t1", "t2"]) do
97
+ insist { subject["tags"] } == ["t1", "t2", "test"]
98
+ end
99
+ end
100
+
101
+ describe "tags parsing with multiple tags" do
102
+ config <<-CONFIG
103
+ filter {
104
+ noop {
105
+ add_tag => ["test"]
106
+ }
107
+ }
108
+ CONFIG
109
+
110
+ sample("type" => "noop") do
111
+ insist { subject["tags"] } == ["test"]
112
+ end
113
+
114
+ sample("type" => "noop", "tags" => ["t1"]) do
115
+ insist { subject["tags"] } == ["t1", "test"]
116
+ end
117
+
118
+ sample("type" => "noop", "tags" => ["t1", "t2"]) do
119
+ insist { subject["tags"] } == ["t1", "t2", "test"]
120
+ end
121
+
122
+ sample("type" => "noop", "tags" => ["t1", "t2", "t3"]) do
123
+ insist { subject["tags"] } == ["t1", "t2", "t3", "test"]
124
+ end
125
+ end
126
+
127
+ describe "remove_tag" do
128
+ config <<-CONFIG
129
+ filter {
130
+ noop {
131
+ remove_tag => ["t2", "t3"]
132
+ }
133
+ }
134
+ CONFIG
135
+
136
+ sample("type" => "noop", "tags" => ["t4"]) do
137
+ insist { subject["tags"] } == ["t4"]
138
+ end
139
+
140
+ sample("type" => "noop", "tags" => ["t1", "t2", "t3"]) do
141
+ insist { subject["tags"] } == ["t1"]
142
+ end
143
+
144
+ # also test from Json deserialized data to test the handling of native Java collections by JrJackson
145
+ # see https://github.com/elastic/logstash/issues/2261
146
+ sample(LogStash::Json.load("{\"type\":\"noop\", \"tags\":[\"t1\", \"t2\", \"t3\"]}")) do
147
+ insist { subject["tags"] } == ["t1"]
148
+ end
149
+
150
+ sample("type" => "noop", "tags" => ["t1", "t2"]) do
151
+ insist { subject["tags"] } == ["t1"]
152
+ end
153
+
154
+ # also test from Json deserialized data to test the handling of native Java collections by JrJackson
155
+ # see https://github.com/elastic/logstash/issues/2261
156
+ sample(LogStash::Json.load("{\"type\":\"noop\", \"tags\":[\"t1\", \"t2\"]}")) do
157
+ insist { subject["tags"] } == ["t1"]
158
+ end
159
+ end
160
+
161
+ describe "remove_tag with dynamic value" do
162
+ config <<-CONFIG
163
+ filter {
164
+ noop {
165
+ remove_tag => ["%{blackhole}"]
166
+ }
167
+ }
168
+ CONFIG
169
+
170
+ sample("type" => "noop", "tags" => ["t1", "goaway", "t3"], "blackhole" => "goaway") do
171
+ insist { subject["tags"] } == ["t1", "t3"]
172
+ end
173
+
174
+ # also test from Json deserialized data to test the handling of native Java collections by JrJackson
175
+ # see https://github.com/elastic/logstash/issues/2261
176
+ sample(LogStash::Json.load("{\"type\":\"noop\", \"tags\":[\"t1\", \"goaway\", \"t3\"], \"blackhole\":\"goaway\"}")) do
177
+ insist { subject["tags"] } == ["t1", "t3"]
178
+ end
179
+ end
180
+
181
+ describe "remove_field" do
182
+ config <<-CONFIG
183
+ filter {
184
+ noop {
185
+ remove_field => ["t2", "t3"]
186
+ }
187
+ }
188
+ CONFIG
189
+
190
+ sample("type" => "noop", "t4" => "four") do
191
+ insist { subject }.include?("t4")
192
+ end
193
+
194
+ sample("type" => "noop", "t1" => "one", "t2" => "two", "t3" => "three") do
195
+ insist { subject }.include?("t1")
196
+ reject { subject }.include?("t2")
197
+ reject { subject }.include?("t3")
198
+ end
199
+
200
+ sample("type" => "noop", "t1" => "one", "t2" => "two") do
201
+ insist { subject }.include?("t1")
202
+ reject { subject }.include?("t2")
203
+ end
204
+ end
205
+
206
+ describe "remove_field on deep objects" do
207
+ config <<-CONFIG
208
+ filter {
209
+ noop {
210
+ remove_field => ["[t1][t2]"]
211
+ }
212
+ }
213
+ CONFIG
214
+
215
+ sample("type" => "noop", "t1" => {"t2" => "two", "t3" => "three"}) do
216
+ insist { subject }.include?("t1")
217
+ reject { subject }.include?("[t1][t2]")
218
+ insist { subject }.include?("[t1][t3]")
219
+ end
220
+ end
221
+
222
+ describe "remove_field on array" do
223
+ config <<-CONFIG
224
+ filter {
225
+ noop {
226
+ remove_field => ["[t1][0]"]
227
+ }
228
+ }
229
+ CONFIG
230
+
231
+ sample("type" => "noop", "t1" => ["t2", "t3"]) do
232
+ insist { subject }.include?("t1")
233
+ insist { subject["[t1][0]"] } == "t3"
234
+ end
235
+ end
236
+
237
+ describe "remove_field with dynamic value in field name" do
238
+ config <<-CONFIG
239
+ filter {
240
+ noop {
241
+ remove_field => ["%{blackhole}"]
242
+ }
243
+ }
244
+ CONFIG
245
+
246
+ sample("type" => "noop", "blackhole" => "go", "go" => "away") do
247
+ insist { subject }.include?("blackhole")
248
+ reject { subject }.include?("go")
249
+ end
250
+ end
251
+ end
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ # use a dummy NOOP input to test Inputs::Base
5
+ class LogStash::Inputs::NOOP < LogStash::Inputs::Base
6
+ config_name "noop"
7
+ milestone 2
8
+
9
+ def register; end
10
+
11
+ end
12
+
13
+ describe "LogStash::Inputs::Base#decorate" do
14
+ it "should add tag" do
15
+ input = LogStash::Inputs::NOOP.new("tags" => "value")
16
+ evt = LogStash::Event.new({"type" => "noop"})
17
+ input.instance_eval {decorate(evt)}
18
+ expect(evt["tags"]).to eq(["value"])
19
+ end
20
+
21
+ it "should add multiple tag" do
22
+ input = LogStash::Inputs::NOOP.new("tags" => ["value1","value2"])
23
+ evt = LogStash::Event.new({"type" => "noop"})
24
+ input.instance_eval {decorate(evt)}
25
+ expect(evt["tags"]).to eq(["value1","value2"])
26
+ end
27
+
28
+ it "should allow duplicates tag" do
29
+ input = LogStash::Inputs::NOOP.new("tags" => ["value","value"])
30
+ evt = LogStash::Event.new({"type" => "noop"})
31
+ input.instance_eval {decorate(evt)}
32
+ expect(evt["tags"]).to eq(["value","value"])
33
+ end
34
+
35
+ it "should add tag with sprintf" do
36
+ input = LogStash::Inputs::NOOP.new("tags" => "%{type}")
37
+ evt = LogStash::Event.new({"type" => "noop"})
38
+ input.instance_eval {decorate(evt)}
39
+ expect(evt["tags"]).to eq(["noop"])
40
+ end
41
+
42
+ it "should add single field" do
43
+ input = LogStash::Inputs::NOOP.new("add_field" => {"field" => "value"})
44
+ evt = LogStash::Event.new({"type" => "noop"})
45
+ input.instance_eval {decorate(evt)}
46
+ expect(evt["field"]).to eq("value")
47
+ end
48
+
49
+ it "should add single field with sprintf" do
50
+ input = LogStash::Inputs::NOOP.new("add_field" => {"%{type}" => "%{type}"})
51
+ evt = LogStash::Event.new({"type" => "noop"})
52
+ input.instance_eval {decorate(evt)}
53
+ expect(evt["noop"]).to eq("noop")
54
+ end
55
+
56
+ it "should add multiple field" do
57
+ input = LogStash::Inputs::NOOP.new("add_field" => {"field" => ["value1", "value2"], "field2" => "value"})
58
+ evt = LogStash::Event.new({"type" => "noop"})
59
+ input.instance_eval {decorate(evt)}
60
+ expect(evt["field"]).to eq(["value1","value2"])
61
+ expect(evt["field2"]).to eq("value")
62
+ end
63
+ end
64
+
65
+ describe "LogStash::Inputs::Base#fix_streaming_codecs" do
66
+ it "should carry the charset setting along when switching" do
67
+ require "logstash/inputs/tcp"
68
+ require "logstash/codecs/plain"
69
+ plain = LogStash::Codecs::Plain.new("charset" => "CP1252")
70
+ tcp = LogStash::Inputs::Tcp.new("codec" => plain, "port" => 3333)
71
+ tcp.instance_eval { fix_streaming_codecs }
72
+ expect(tcp.codec.charset).to eq("CP1252")
73
+ end
74
+ end
@@ -0,0 +1,304 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+ require "logstash/java_integration"
4
+
5
+ describe "Java integration" do
6
+
7
+ context "type equivalence" do
8
+
9
+ # here we test for both is_a? and case/when usage of the Java types
10
+ # because these are the specific use-cases in our code and the expected
11
+ # behaviour.
12
+
13
+ context "Java::JavaUtil::ArrayList" do
14
+
15
+ it "should report to be a Ruby Array" do
16
+ expect(Java::JavaUtil::ArrayList.new.is_a?(Array)).to eq(true)
17
+ end
18
+
19
+ it "should be class equivalent to Ruby Array" do
20
+ expect do
21
+ case Java::JavaUtil::ArrayList.new
22
+ when Array
23
+ true
24
+ else
25
+ raise
26
+ end
27
+ end.not_to raise_error
28
+
29
+ expect(Array === Java::JavaUtil::ArrayList.new).to eq(true)
30
+ end
31
+ end
32
+
33
+ context "Java::JavaUtil::LinkedHashMap" do
34
+ it "should report to be a Ruby Hash" do
35
+ expect(Java::JavaUtil::LinkedHashMap.new.is_a?(Hash)).to eq(true)
36
+ end
37
+
38
+ it "should be class equivalent to Ruby Hash" do
39
+ expect do
40
+ case Java::JavaUtil::LinkedHashMap.new
41
+ when Hash
42
+ true
43
+ else
44
+ raise
45
+ end
46
+ end.not_to raise_error
47
+
48
+ expect(Hash === Java::JavaUtil::LinkedHashMap.new).to eq(true)
49
+ end
50
+ end
51
+ end
52
+
53
+ context "Java::JavaUtil::Map" do
54
+ # this is to test the Java 8 Map interface change for the merge method
55
+
56
+ let(:merger){{:a => 1, :b => 2}}
57
+ let(:mergee){{:b => 3, :c => 4}}
58
+
59
+ shared_examples "map merge" do
60
+ it "should support merging" do
61
+ expect(subject.merge(mergee)).to eq({:a => 1, :b => 3, :c => 4})
62
+ end
63
+
64
+ it "should return a new hash and not change original hash" do
65
+ expect{subject.merge(mergee)}.to_not change{subject}
66
+ end
67
+ end
68
+
69
+ context "with Java::JavaUtil::LinkedHashMap" do
70
+ it_behaves_like "map merge" do
71
+ subject{Java::JavaUtil::LinkedHashMap.new(merger)}
72
+ end
73
+ end
74
+
75
+ context "with Java::JavaUtil::HashMap" do
76
+ it_behaves_like "map merge" do
77
+ subject{Java::JavaUtil::HashMap.new(merger)}
78
+ end
79
+ end
80
+ end
81
+
82
+ context "Java::JavaUtil::Collection" do
83
+ subject{Java::JavaUtil::ArrayList.new(initial_array)}
84
+
85
+ context "when inspecting" do
86
+ let(:items) { [:a, {:b => :c}] }
87
+ subject { java.util.ArrayList.new(items) }
88
+
89
+ it "should include the contents of the Collection" do
90
+ expect(subject.inspect).to include(items.inspect)
91
+ end
92
+
93
+ it "should include the class name" do
94
+ expect(subject.inspect).to include("ArrayList")
95
+ end
96
+
97
+ it "should include the hash code of the collection" do
98
+ expect(subject.inspect).to include(subject.hashCode.to_s)
99
+ end
100
+ end
101
+
102
+ context "when deleting a unique instance" do
103
+ let(:initial_array) {["foo", "bar"]}
104
+
105
+ it "should return the deleted object" do
106
+ expect(subject.delete("foo")).to eq("foo")
107
+ end
108
+
109
+ it "should remove the object to delete" do
110
+ expect{subject.delete("foo")}.to change{subject.to_a}.from(initial_array).to(["bar"])
111
+ end
112
+ end
113
+
114
+ context "when deleting multiple instances" do
115
+ let(:initial_array) {["foo", "bar", "foo"]}
116
+
117
+ it "should return the last deleted object" do
118
+ expect(subject.delete("foo")).to eq("foo")
119
+ end
120
+
121
+ it "should remove all the objects to delete" do
122
+ expect{subject.delete("foo")}.to change{subject.to_a}.from(initial_array).to(["bar"])
123
+ end
124
+ end
125
+
126
+ context "when deleting non existing object" do
127
+ let(:initial_array) {["foo", "bar", "foo"]}
128
+
129
+ it "should return nil" do
130
+ expect(subject.delete("baz")).to be_nil
131
+ end
132
+
133
+ it "should not change the collection" do
134
+ expect{subject.delete("baz")}.to_not change{subject.to_a}
135
+ end
136
+
137
+ it "should yield to block when given" do
138
+ expect(subject.delete("baz"){"foobar"}).to eq("foobar")
139
+ end
140
+ end
141
+
142
+ context "when deleting on empty collection" do
143
+ let(:initial_array) {[]}
144
+
145
+ it "should return nil" do
146
+ expect(subject.delete("baz")).to be_nil
147
+ end
148
+
149
+ it "should not change the collection" do
150
+ expect{subject.delete("baz")}.to_not change{subject.to_a}
151
+ end
152
+ end
153
+
154
+ context "when intersecting with a Ruby Array" do
155
+
156
+ context "using string collection with duplicates and single result" do
157
+ let(:initial_array) {["foo", "bar", "foo"]}
158
+
159
+ it "should not change original collection" do
160
+ expect{subject & ["foo"]}.to_not change{subject.to_a}
161
+ end
162
+
163
+ it "should return a new array containing elements common to the two arrays, excluding any duplicate" do
164
+ expect((subject & ["foo"]).to_a).to eq(["foo"])
165
+ end
166
+ end
167
+
168
+ context "using string collection with duplicates and multiple results" do
169
+ let(:original) {["foo", "bar", "foo", "baz"]}
170
+ let(:target) {["baz", "foo"]}
171
+ let(:result) {["foo", "baz"]}
172
+
173
+ it "should return a new array containing elements common to the two arrays, excluding any duplicate and preserve order from the original array" do
174
+ # this is the Ruby contract
175
+ expect(original & target).to eq(result)
176
+
177
+ # this should work the same
178
+ expect((Java::JavaUtil::ArrayList.new(original) & target).to_a).to eq(result)
179
+ end
180
+ end
181
+
182
+ context "Ruby doc examples" do
183
+ it "should return a new array containing elements common to the two arrays, excluding any duplicate" do
184
+ expect(Java::JavaUtil::ArrayList.new(([1, 1, 3, 5]) & [1, 2, 3]).to_a).to eq([1, 3])
185
+ expect(Java::JavaUtil::ArrayList.new((['a', 'b', 'b', 'z']) & ['a', 'b', 'c']).to_a).to eq(['a', 'b'])
186
+ end
187
+ end
188
+ end
189
+
190
+ context "when unioning with a Ruby Array" do
191
+
192
+ context "using string collection with duplicates" do
193
+ let(:initial_array) {["foo", "bar", "foo"]}
194
+
195
+ it "should not change original collection" do
196
+ expect{subject | ["bar", "baz"]}.to_not change{subject.to_a}
197
+ end
198
+
199
+ it "should return a new array by joining excluding any duplicates and preserving the order from the original array" do
200
+ expect((subject | ["bar", "baz"]).to_a).to eq(["foo", "bar", "baz"])
201
+ end
202
+
203
+ it "should remove duplicates when joining empty array" do
204
+ expect((subject | []).to_a).to eq(["foo", "bar"])
205
+ end
206
+ end
207
+
208
+ context "Ruby doc examples" do
209
+ it "should return a new array containing elements common to the two arrays, excluding any duplicate" do
210
+ expect(Java::JavaUtil::ArrayList.new((["a", "b", "c"]) | ["c", "d", "a"]).to_a).to eq(["a", "b", "c", "d"])
211
+ end
212
+ end
213
+ end
214
+
215
+ context "when compacting" do
216
+ context "#compact with nils" do
217
+ let(:initial_array) { [1,2,3,nil,nil,6] }
218
+ it "should remove nil values from a copy" do
219
+ expect(subject.compact).to eq([1,2,3,6])
220
+ expect(subject).to eq([1,2,3,nil,nil,6])
221
+ end
222
+ end
223
+
224
+ context "#compact! with nils" do
225
+ let(:initial_array) { [1,2,3,nil,nil,6] }
226
+ it "should remove nil values" do
227
+ expect(subject.compact!).to eq([1,2,3,6])
228
+ expect(subject).to eq([1,2,3,6])
229
+ end
230
+
231
+ it "should return the original" do
232
+ expect(subject.compact!.object_id).to eq(subject.object_id)
233
+ end
234
+ end
235
+
236
+ context "#compact! without nils" do
237
+ let(:initial_array) { [1,2,3,6] }
238
+ it "should return nil" do
239
+ expect(subject.compact!).to be nil
240
+ expect(subject).to eq([1,2,3,6])
241
+ end
242
+ end
243
+ end
244
+ end
245
+
246
+ context "Enumerable implementation" do
247
+ context "Java Map interface should report key with nil value as included" do
248
+
249
+ it "should support include? method" do
250
+ expect(Java::JavaUtil::LinkedHashMap.new({"foo" => nil}).include?("foo")).to eq(true)
251
+ end
252
+
253
+ it "should support has_key? method" do
254
+ expect(Java::JavaUtil::LinkedHashMap.new({"foo" => nil}).has_key?("foo")).to eq(true)
255
+ end
256
+
257
+ it "should support member? method" do
258
+ expect(Java::JavaUtil::LinkedHashMap.new({"foo" => nil}).member?("foo")).to eq(true)
259
+ end
260
+
261
+ it "should support key? method" do
262
+ expect(Java::JavaUtil::LinkedHashMap.new({"foo" => nil}).key?("foo")).to eq(true)
263
+ end
264
+ end
265
+
266
+ context "Java Map interface should report key with a value as included" do
267
+
268
+ it "should support include? method" do
269
+ expect(Java::JavaUtil::LinkedHashMap.new({"foo" => 1}).include?("foo")).to eq(true)
270
+ end
271
+
272
+ it "should support has_key? method" do
273
+ expect(Java::JavaUtil::LinkedHashMap.new({"foo" => 1}).has_key?("foo")).to eq(true)
274
+ end
275
+
276
+ it "should support member? method" do
277
+ expect(Java::JavaUtil::LinkedHashMap.new({"foo" => 1}).member?("foo")).to eq(true)
278
+ end
279
+
280
+ it "should support key? method" do
281
+ expect(Java::JavaUtil::LinkedHashMap.new({"foo" => 1}).key?("foo")).to eq(true)
282
+ end
283
+ end
284
+
285
+ context "Java Map interface should report non existing key as not included" do
286
+
287
+ it "should support include? method" do
288
+ expect(Java::JavaUtil::LinkedHashMap.new({"foo" => 1})).not_to include("bar")
289
+ end
290
+
291
+ it "should support has_key? method" do
292
+ expect(Java::JavaUtil::LinkedHashMap.new({"foo" => 1}).has_key?("bar")).to eq(false)
293
+ end
294
+
295
+ it "should support member? method" do
296
+ expect(Java::JavaUtil::LinkedHashMap.new({"foo" => 1}).member?("bar")).to eq(false)
297
+ end
298
+
299
+ it "should support key? method" do
300
+ expect(Java::JavaUtil::LinkedHashMap.new({"foo" => 1}).key?("bar")).to eq(false)
301
+ end
302
+ end
303
+ end
304
+ end