ml-puppetdb-terminus 3.2.1
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +202 -0
- data/NOTICE.txt +17 -0
- data/README.md +22 -0
- data/puppet/lib/puppet/application/storeconfigs.rb +4 -0
- data/puppet/lib/puppet/face/node/deactivate.rb +37 -0
- data/puppet/lib/puppet/face/node/status.rb +80 -0
- data/puppet/lib/puppet/face/storeconfigs.rb +193 -0
- data/puppet/lib/puppet/indirector/catalog/puppetdb.rb +400 -0
- data/puppet/lib/puppet/indirector/facts/puppetdb.rb +152 -0
- data/puppet/lib/puppet/indirector/facts/puppetdb_apply.rb +25 -0
- data/puppet/lib/puppet/indirector/node/puppetdb.rb +19 -0
- data/puppet/lib/puppet/indirector/resource/puppetdb.rb +108 -0
- data/puppet/lib/puppet/reports/puppetdb.rb +188 -0
- data/puppet/lib/puppet/util/puppetdb.rb +108 -0
- data/puppet/lib/puppet/util/puppetdb/char_encoding.rb +316 -0
- data/puppet/lib/puppet/util/puppetdb/command.rb +116 -0
- data/puppet/lib/puppet/util/puppetdb/command_names.rb +8 -0
- data/puppet/lib/puppet/util/puppetdb/config.rb +148 -0
- data/puppet/lib/puppet/util/puppetdb/http.rb +121 -0
- data/puppet/spec/README.markdown +8 -0
- data/puppet/spec/spec.opts +6 -0
- data/puppet/spec/spec_helper.rb +38 -0
- data/puppet/spec/unit/face/node/deactivate_spec.rb +28 -0
- data/puppet/spec/unit/face/node/status_spec.rb +43 -0
- data/puppet/spec/unit/face/storeconfigs_spec.rb +199 -0
- data/puppet/spec/unit/indirector/catalog/puppetdb_spec.rb +703 -0
- data/puppet/spec/unit/indirector/facts/puppetdb_apply_spec.rb +27 -0
- data/puppet/spec/unit/indirector/facts/puppetdb_spec.rb +347 -0
- data/puppet/spec/unit/indirector/node/puppetdb_spec.rb +61 -0
- data/puppet/spec/unit/indirector/resource/puppetdb_spec.rb +199 -0
- data/puppet/spec/unit/reports/puppetdb_spec.rb +249 -0
- data/puppet/spec/unit/util/puppetdb/char_encoding_spec.rb +212 -0
- data/puppet/spec/unit/util/puppetdb/command_spec.rb +98 -0
- data/puppet/spec/unit/util/puppetdb/config_spec.rb +227 -0
- data/puppet/spec/unit/util/puppetdb/http_spec.rb +138 -0
- data/puppet/spec/unit/util/puppetdb_spec.rb +33 -0
- metadata +115 -0
@@ -0,0 +1,249 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'puppet/reports'
|
5
|
+
require 'net/http'
|
6
|
+
require 'puppet/network/http_pool'
|
7
|
+
require 'puppet/util/puppetdb/command_names'
|
8
|
+
require 'puppet/util/puppetdb/config'
|
9
|
+
require 'json'
|
10
|
+
|
11
|
+
processor = Puppet::Reports.report(:puppetdb)
|
12
|
+
|
13
|
+
describe processor do
|
14
|
+
|
15
|
+
subject {
|
16
|
+
s = Puppet::Transaction::Report.new("foo").extend(processor)
|
17
|
+
s.configuration_version = 123456789
|
18
|
+
s.environment = "foo"
|
19
|
+
s
|
20
|
+
}
|
21
|
+
|
22
|
+
context "#process" do
|
23
|
+
|
24
|
+
let(:http) { mock "http" }
|
25
|
+
let(:httpok) { Net::HTTPOK.new('1.1', 200, '') }
|
26
|
+
|
27
|
+
def without_producer_timestamp(json_body)
|
28
|
+
parsed = JSON.parse(json_body)
|
29
|
+
parsed["payload"].delete("producer_timestamp")
|
30
|
+
parsed.to_json
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should POST the report command as a URL-encoded JSON string" do
|
34
|
+
httpok.stubs(:body).returns '{"uuid": "a UUID"}'
|
35
|
+
subject.stubs(:run_duration).returns(10)
|
36
|
+
|
37
|
+
expected_body = {
|
38
|
+
:command => Puppet::Util::Puppetdb::CommandNames::CommandStoreReport,
|
39
|
+
:version => 6,
|
40
|
+
:payload => subject.send(:report_to_hash)
|
41
|
+
}.to_json
|
42
|
+
|
43
|
+
Puppet::Network::HttpPool.expects(:http_instance).returns(http)
|
44
|
+
http.expects(:post).with {|path, body, headers|
|
45
|
+
expect(path).to include(Puppet::Util::Puppetdb::Command::CommandsUrl)
|
46
|
+
|
47
|
+
# producer_timestamp is generated at submission time, so remove it from
|
48
|
+
# the comparison
|
49
|
+
expect(without_producer_timestamp(body)).to eq(without_producer_timestamp(expected_body))
|
50
|
+
}.returns(httpok)
|
51
|
+
|
52
|
+
subject.process
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "#report_to_hash" do
|
57
|
+
let (:resource) {
|
58
|
+
stub("resource",
|
59
|
+
{ :pathbuilder => ["foo", "bar", "baz"],
|
60
|
+
:path => "foo",
|
61
|
+
:file => "foo",
|
62
|
+
:line => 1,
|
63
|
+
:tags => [],
|
64
|
+
:title => "foo",
|
65
|
+
:type => "foo" })
|
66
|
+
}
|
67
|
+
|
68
|
+
let (:status) {
|
69
|
+
Puppet::Resource::Status.new(resource)
|
70
|
+
}
|
71
|
+
|
72
|
+
before :each do
|
73
|
+
subject.add_resource_status(status)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should include the transaction uuid or nil" do
|
77
|
+
subject.transaction_uuid = 'abc123'
|
78
|
+
result = subject.send(:report_to_hash)
|
79
|
+
result["transaction_uuid"].should == 'abc123'
|
80
|
+
end
|
81
|
+
|
82
|
+
context "start/end time" do
|
83
|
+
before :each do
|
84
|
+
subject.add_metric("time", {"total" => 10})
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should base run duration off of the 'time'->'total' metric" do
|
88
|
+
subject.send(:run_duration).should == 10
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should use run_duration to calculate the end_time" do
|
92
|
+
result = subject.send(:report_to_hash)
|
93
|
+
duration = Time.parse(result["end_time"]) - Time.parse(result["start_time"])
|
94
|
+
duration.should == subject.send(:run_duration)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "events" do
|
99
|
+
before :each do
|
100
|
+
subject.stubs(:run_duration).returns(10)
|
101
|
+
end
|
102
|
+
|
103
|
+
context "resource without events" do
|
104
|
+
it "should not include the resource" do
|
105
|
+
result = subject.send(:report_to_hash)
|
106
|
+
# the server will populate the report id, so we validate that the
|
107
|
+
# client doesn't include one
|
108
|
+
result.has_key?("report").should be_false
|
109
|
+
result["certname"].should == subject.host
|
110
|
+
result["puppet_version"].should == subject.puppet_version
|
111
|
+
result["report_format"].should == subject.report_format
|
112
|
+
result["configuration_version"].should == subject.configuration_version.to_s
|
113
|
+
result["resources"].should == []
|
114
|
+
result["noop"].should == false
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "resource with events" do
|
119
|
+
it "should include the resource" do
|
120
|
+
|
121
|
+
event = Puppet::Transaction::Event.new()
|
122
|
+
event.property = "fooprop"
|
123
|
+
event.desired_value = "fooval"
|
124
|
+
event.previous_value = "oldfooval"
|
125
|
+
event.message = "foomessage"
|
126
|
+
status.add_event(event)
|
127
|
+
|
128
|
+
result = subject.send(:report_to_hash)
|
129
|
+
|
130
|
+
result["resources"].length.should == 1
|
131
|
+
res = result["resources"][0]
|
132
|
+
res["resource_type"].should == "Foo"
|
133
|
+
res["resource_title"].should == "foo"
|
134
|
+
res["file"].should == "foo"
|
135
|
+
res["line"].should == 1
|
136
|
+
res["containment_path"].should == ["foo", "bar", "baz"]
|
137
|
+
res["events"].length.should == 1
|
138
|
+
|
139
|
+
res_event = res["events"][0]
|
140
|
+
res_event["property"].should == "fooprop"
|
141
|
+
res_event["new_value"].should == "fooval"
|
142
|
+
res_event["old_value"].should == "oldfooval"
|
143
|
+
res_event["message"].should == "foomessage"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context "skipped resource status" do
|
148
|
+
it "should include the resource" do
|
149
|
+
status.skipped = true
|
150
|
+
result = subject.send(:report_to_hash)
|
151
|
+
|
152
|
+
result["resources"].length.should == 1
|
153
|
+
resource = result["resources"][0]
|
154
|
+
resource["resource_type"].should == "Foo"
|
155
|
+
resource["resource_title"].should == "foo"
|
156
|
+
resource["containment_path"].should == ["foo", "bar", "baz"]
|
157
|
+
resource["events"].length.should == 0
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context "failed resource status" do
|
162
|
+
before :each do
|
163
|
+
status.stubs(:failed).returns(true)
|
164
|
+
end
|
165
|
+
|
166
|
+
context "with no events" do
|
167
|
+
it "should have no events" do
|
168
|
+
result = subject.send(:report_to_hash)
|
169
|
+
result["resources"].length.should == 0
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context "with events" do
|
174
|
+
it "should include the actual event" do
|
175
|
+
event = Puppet::Transaction::Event.new
|
176
|
+
event.property = "barprop"
|
177
|
+
event.desired_value = "barval"
|
178
|
+
event.previous_value = "oldbarval"
|
179
|
+
event.message = "barmessage"
|
180
|
+
status.add_event(event)
|
181
|
+
|
182
|
+
result = subject.send(:report_to_hash)
|
183
|
+
result["resources"].length.should == 1
|
184
|
+
resource = result["resources"][0]
|
185
|
+
resource["resource_type"].should == "Foo"
|
186
|
+
resource["resource_title"].should == "foo"
|
187
|
+
resource["file"].should == "foo"
|
188
|
+
resource["line"].should == 1
|
189
|
+
resource["containment_path"].should == ["foo", "bar", "baz"]
|
190
|
+
resource["events"].length.should == 1
|
191
|
+
|
192
|
+
res_event = resource["events"][0]
|
193
|
+
res_event["property"].should == "barprop"
|
194
|
+
res_event["new_value"].should == "barval"
|
195
|
+
res_event["old_value"].should == "oldbarval"
|
196
|
+
res_event["message"].should == "barmessage"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context "with unchanged resources turned on" do
|
202
|
+
let (:config) {
|
203
|
+
Puppet::Util::Puppetdb.config
|
204
|
+
}
|
205
|
+
|
206
|
+
before :each do
|
207
|
+
config.stubs(:include_unchanged_resources?).returns(true)
|
208
|
+
|
209
|
+
notify_resource =
|
210
|
+
stub("notify_resource",
|
211
|
+
{ :pathbuilder => ["foo", "bar", "baz"],
|
212
|
+
:path => "foo",
|
213
|
+
:file => "foo",
|
214
|
+
:line => "foo",
|
215
|
+
:tags => [],
|
216
|
+
:type => "Notify",
|
217
|
+
:title => "Hello there" })
|
218
|
+
notify_status = Puppet::Resource::Status.new(notify_resource)
|
219
|
+
notify_status.changed = false
|
220
|
+
subject.add_resource_status(notify_status)
|
221
|
+
|
222
|
+
event = Puppet::Transaction::Event.new()
|
223
|
+
event.property = "fooprop"
|
224
|
+
event.desired_value = "fooval"
|
225
|
+
event.previous_value = "oldfooval"
|
226
|
+
event.message = "foomessage"
|
227
|
+
status.add_event(event)
|
228
|
+
end
|
229
|
+
|
230
|
+
context "with an unchanged resource" do
|
231
|
+
it "should include the actual event" do
|
232
|
+
result = subject.send(:report_to_hash)
|
233
|
+
unchanged_resources = result["resources"].select { |res| res["events"].empty? and ! (res["skipped"])}
|
234
|
+
unchanged_resources.length.should == 1
|
235
|
+
resource = unchanged_resources[0]
|
236
|
+
resource["resource_type"].should == "Notify"
|
237
|
+
resource["resource_title"].should == "Hello there"
|
238
|
+
resource["file"].should == "foo"
|
239
|
+
resource["line"].should == "foo"
|
240
|
+
resource["containment_path"].should == ["foo", "bar", "baz"]
|
241
|
+
resource["events"].length.should == 0
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'spec_helper'
|
5
|
+
|
6
|
+
require 'puppet/util/puppetdb/char_encoding'
|
7
|
+
|
8
|
+
describe Puppet::Util::Puppetdb::CharEncoding do
|
9
|
+
describe "#ruby_18_clean_utf8", :if => RUBY_VERSION =~ /^1.8/ do
|
10
|
+
|
11
|
+
def test_utf8_clean(in_bytes, expected_bytes)
|
12
|
+
instr = in_bytes.pack('c*')
|
13
|
+
out = described_class.ruby18_clean_utf8(instr)
|
14
|
+
out.should == expected_bytes.pack('c*')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
it "should recognize (and not modify) valid multi-byte characters" do
|
19
|
+
in_bytes = [0xE2, 0x9B, 0x87]
|
20
|
+
expected_bytes = [0xE2, 0x9B, 0x87]
|
21
|
+
test_utf8_clean(in_bytes, expected_bytes)
|
22
|
+
end
|
23
|
+
|
24
|
+
Utf8ReplacementChar = [0xEF, 0xBF, 0xBD]
|
25
|
+
it "should strip invalid UTF-8 characters from an invalid multi-byte sequence" do
|
26
|
+
in_bytes = [0xE2, 0xCB, 0x87]
|
27
|
+
test_utf8_clean(in_bytes, [0xCB, 0x87])
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should strip incomplete multi-byte characters" do
|
31
|
+
in_bytes = [0xE2, 0x9B]
|
32
|
+
test_utf8_clean(in_bytes, [])
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should replace invalid characters with the unicode replacement character" do
|
36
|
+
# This is related to ticket #14873; our utf8_string code for 1.9 is being
|
37
|
+
# much more aggressive about replacing bytes with the unicode replacement char;
|
38
|
+
# it appears to be more correct, as the way that the 1.8/IConv approach
|
39
|
+
# was handling it was causing certain strings to decode differently in
|
40
|
+
# clojure, thus causing checksum errors.
|
41
|
+
in_bytes = [0x21, 0x7F, 0xFD, 0x80, 0xBD, 0xBB, 0xB6, 0xA1]
|
42
|
+
expected_bytes = [0x21, 0x7F]
|
43
|
+
test_utf8_clean(in_bytes, expected_bytes)
|
44
|
+
end
|
45
|
+
|
46
|
+
# A multi-byte sequence beginning with any of the following bytes is
|
47
|
+
# illegal. For more info, see http://en.wikipedia.org/wiki/UTF-8
|
48
|
+
[[[0xC0, 0xC1], 2],
|
49
|
+
[[0xF5, 0xF6, 0xF7], 4],
|
50
|
+
[[0xF8, 0xF9, 0xFA, 0xFB], 5],
|
51
|
+
[[0xFC, 0xFD, 0xFE, 0xFF], 6]].each do |bytes, num_bytes|
|
52
|
+
bytes.each do |first_byte|
|
53
|
+
it "should strip the invalid bytes from a #{num_bytes}-byte character starting with 0x#{first_byte.to_s(16)}" do
|
54
|
+
in_bytes = [first_byte]
|
55
|
+
(num_bytes - 1).times { in_bytes << 0x80 }
|
56
|
+
test_utf8_clean(in_bytes, [])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when dealing with multi-byte sequences beginning with 0xF4" do
|
62
|
+
it "should accept characters that are below the 0x10FFFF limit of Unicode" do
|
63
|
+
in_bytes = [0xF4, 0x8f, 0xbf, 0xbf]
|
64
|
+
expected_bytes = [0xF4, 0x8f, 0xbf, 0xbf]
|
65
|
+
test_utf8_clean(in_bytes, expected_bytes)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should reject characters that are above the 0x10FFFF limit of Unicode" do
|
69
|
+
in_bytes = [0xF4, 0x90, 0xbf, 0xbf]
|
70
|
+
test_utf8_clean(in_bytes, [])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
describe "#utf8_string" do
|
77
|
+
describe "on ruby 1.8", :if => RUBY_VERSION =~ /^1.8/ do
|
78
|
+
it "should convert from ascii without a warning" do
|
79
|
+
Puppet.expects(:warning).never
|
80
|
+
|
81
|
+
str = "any ascii string"
|
82
|
+
subject.utf8_string(str, nil).should == str
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should strip invalid chars from non-overlapping latin-1 with a warning" do
|
86
|
+
Puppet.expects(:warning).with {|msg| msg =~ /Ignoring invalid UTF-8 byte sequences/}
|
87
|
+
|
88
|
+
str = "a latin-1 string \xd6"
|
89
|
+
subject.utf8_string(str, nil).should == "a latin-1 string "
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should strip invalid chars and warn if the string is invalid UTF-8" do
|
93
|
+
Puppet.expects(:warning).with {|msg| msg =~ /Ignoring invalid UTF-8 byte sequences/}
|
94
|
+
|
95
|
+
str = "an invalid utf-8 string \xff"
|
96
|
+
subject.utf8_string(str, nil).should == "an invalid utf-8 string "
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should return a valid utf-8 string without warning" do
|
100
|
+
Puppet.expects(:warning).never
|
101
|
+
|
102
|
+
str = "a valid utf-8 string \xc3\x96"
|
103
|
+
subject.utf8_string(str, nil).should == str
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "on ruby > 1.8", :if => RUBY_VERSION !~ /^1.8/ do
|
108
|
+
it "should convert from ascii without a warning" do
|
109
|
+
Puppet.expects(:warning).never
|
110
|
+
|
111
|
+
str = "any ascii string".force_encoding('us-ascii')
|
112
|
+
subject.utf8_string(str, nil).should == str
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should convert from latin-1 without a warning" do
|
116
|
+
Puppet.expects(:warning).never
|
117
|
+
|
118
|
+
str = "a latin-1 string Ö".force_encoding('ASCII-8BIT')
|
119
|
+
subject.utf8_string(str, nil).should == "a latin-1 string Ö"
|
120
|
+
end
|
121
|
+
|
122
|
+
# UndefinedConversionError
|
123
|
+
it "should replace undefined characters and warn when converting from binary" do
|
124
|
+
Puppet.expects(:warning).with {|msg| msg =~ /Error with command ignoring invalid UTF-8 byte sequences/}
|
125
|
+
|
126
|
+
str = "an invalid binary string \xff".force_encoding('binary')
|
127
|
+
# \ufffd == unicode replacement character
|
128
|
+
subject.utf8_string(str, "Error with command").should == "an invalid binary string \ufffd"
|
129
|
+
end
|
130
|
+
|
131
|
+
# InvalidByteSequenceError
|
132
|
+
it "should replace undefined characters and warn if the string is invalid UTF-8" do
|
133
|
+
Puppet.expects(:warning).with {|msg| msg =~ /Error with command ignoring invalid UTF-8 byte sequences/}
|
134
|
+
|
135
|
+
str = "an invalid utf-8 string \xff".force_encoding('utf-8')
|
136
|
+
subject.utf8_string(str, "Error with command").should == "an invalid utf-8 string \ufffd"
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should leave the string alone if it's valid UTF-8" do
|
140
|
+
Puppet.expects(:warning).never
|
141
|
+
|
142
|
+
str = "a valid utf-8 string".force_encoding('utf-8')
|
143
|
+
subject.utf8_string(str, nil).should == str
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should leave the string alone if it's valid UTF-8 with non-ascii characters" do
|
147
|
+
Puppet.expects(:warning).never
|
148
|
+
|
149
|
+
str = "a valid utf-8 string Ö"
|
150
|
+
subject.utf8_string(str.dup.force_encoding('ASCII-8BIT'), nil).should == str
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "Debug log testing of bad data" do
|
154
|
+
let!(:existing_log_level){ Puppet[:log_level]}
|
155
|
+
|
156
|
+
before :each do
|
157
|
+
Puppet[:log_level] = "debug"
|
158
|
+
end
|
159
|
+
|
160
|
+
after :each do
|
161
|
+
Puppet[:log_level] = "notice"
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should emit a warning and debug messages when bad characters are found" do
|
165
|
+
Puppet[:log_level] = "debug"
|
166
|
+
Puppet.expects(:warning).with {|msg| msg =~ /Error encoding a 'replace facts' command for host 'foo.com' ignoring invalid/}
|
167
|
+
Puppet.expects(:debug).with do |msg|
|
168
|
+
msg =~ /Error encoding a 'replace facts' command for host 'foo.com'/ &&
|
169
|
+
msg =~ /'some valid string' followed by 1 invalid\/undefined bytes then ''/
|
170
|
+
end
|
171
|
+
|
172
|
+
# This will create a UTF-8 string literal, then switch to ASCII-8Bit when the bad
|
173
|
+
# bytes are concated on below
|
174
|
+
str = "some valid string" << [192].pack('c*')
|
175
|
+
subject.utf8_string(str, "Error encoding a 'replace facts' command for host 'foo.com'").should == "some valid string\ufffd"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should emit a warning and no debug messages" do
|
180
|
+
Puppet.expects(:warning).with {|msg| msg =~ /Error on replace catalog ignoring invalid UTF-8 byte sequences/}
|
181
|
+
Puppet.expects(:debug).never
|
182
|
+
str = "some valid string" << [192].pack('c*')
|
183
|
+
subject.utf8_string(str, "Error on replace catalog").should == "some valid string\ufffd"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "on ruby > 1.8", :if => RUBY_VERSION !~ /^1.8/ do
|
189
|
+
it "finds all index of a given character" do
|
190
|
+
described_class.all_indexes_of_char("a\u2192b\u2192c\u2192d\u2192", "\u2192").should == [1, 3, 5, 7]
|
191
|
+
described_class.all_indexes_of_char("abcd", "\u2192").should == []
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should collapse consecutive integers into ranges" do
|
195
|
+
described_class.collapse_ranges((1..5).to_a).should == [1..5]
|
196
|
+
described_class.collapse_ranges([]).should == []
|
197
|
+
described_class.collapse_ranges([1,2,3,5,7,8,9]).should == [1..3, 5..5, 7..9]
|
198
|
+
end
|
199
|
+
|
200
|
+
it "gives error context around each bad character" do
|
201
|
+
described_class.error_char_context("abc\ufffddef", [3]).should ==
|
202
|
+
["'abc' followed by 1 invalid/undefined bytes then 'def'"]
|
203
|
+
|
204
|
+
described_class.error_char_context("abc\ufffd\ufffd\ufffd\ufffddef", [3,4,5,6]).should ==
|
205
|
+
["'abc' followed by 4 invalid/undefined bytes then 'def'"]
|
206
|
+
|
207
|
+
described_class.error_char_context("abc\ufffddef\ufffdg", [3, 7]).should ==
|
208
|
+
["'abc' followed by 1 invalid/undefined bytes then 'def'",
|
209
|
+
"'def' followed by 1 invalid/undefined bytes then 'g'"]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|