Skip to content

Commit 1e2b599

Browse files
committed
🐛 Fix inconsistently frozen SequenceSet#[] result
This maybe isn't actually documented very well (or at all...) but most SequenceSet transform methods return a frozen result when +self+ is frozen and a mutable result when +self+ is mutable. Except +limit+ which always returns a frozen result. And (before this commit) +slice+, which inconsistently returned with matching frozen status when the result wasn't empty, but always returned a frozen set when the result _was_ empty. Adding these tests exposed a much more significant bug: `SequenceSet#xor` mutates the reciever.
1 parent 16e60a4 commit 1e2b599

File tree

2 files changed

+30
-3
lines changed

2 files changed

+30
-3
lines changed

lib/net/imap/sequence_set.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,16 +1242,16 @@ def slice_range(range)
12421242
first = range.begin || 0
12431243
last = range.end || -1
12441244
if range.exclude_end?
1245-
return SequenceSet.empty if last.zero?
1245+
return remain_frozen_empty if last.zero?
12461246
last -= 1 if range.end && last != STAR_INT
12471247
end
12481248
if (first * last).positive? && last < first
1249-
SequenceSet.empty
1249+
remain_frozen_empty
12501250
elsif (min = at(first))
12511251
max = at(last)
12521252
if max == :* then self & (min..)
12531253
elsif min <= max then self & (min..max)
1254-
else SequenceSet.empty
1254+
else remain_frozen_empty
12551255
end
12561256
end
12571257
end
@@ -1391,6 +1391,7 @@ def init_with(coder) # :nodoc:
13911391
private
13921392

13931393
def remain_frozen(set) frozen? ? set.freeze : set end
1394+
def remain_frozen_empty; frozen? ? SequenceSet.empty : SequenceSet.new end
13941395

13951396
# frozen clones are shallow copied
13961397
def initialize_clone(other)

test/net/imap/test_sequence_set.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,32 @@ def compare_to_reference_set(nums, set, seqset)
8282
end
8383
end
8484

85+
data "#slice(length)", {transform: ->{ _1.slice(0, 10) }, }
86+
data "#slice(range)", {transform: ->{ _1.slice(0...10) }, }
87+
data "#slice => empty", {transform: ->{ _1.slice(0...0) }, }
88+
data "#slice => empty", {transform: ->{ _1.slice(10..9) }, }
89+
data "#union", {transform: ->{ _1 | (1..100) }, }
90+
data "#intersection", {transform: ->{ _1 & (1..100) }, }
91+
data "#difference", {transform: ->{ _1 - (1..100) }, }
92+
# data "#xor", {transform: ->{ _1 ^ (1..100) }, }
93+
data "#complement", {transform: ->{ ~_1 }, }
94+
data "#normalize", {transform: ->{ _1.normalize }, }
95+
data "#limit", {transform: ->{ _1.limit(max: 22) }, freeze: :always }
96+
data "#limit => empty", {transform: ->{ _1.limit(max: 1) }, freeze: :always }
97+
test "transforms keep frozen status" do |data|
98+
data => {transform:}
99+
set = SequenceSet.new("2:4,7:11,99,999")
100+
result = transform.to_proc.(set)
101+
if data in {freeze: :always}
102+
assert result.frozen?, "this transform always returns frozen"
103+
else
104+
refute result.frozen?, "transform of non-frozen returned frozen"
105+
end
106+
set.freeze
107+
result = transform.to_proc.(set)
108+
assert result.frozen?, "transform of frozen returned non-frozen"
109+
end
110+
85111
%i[clone dup].each do |method|
86112
test "##{method}" do
87113
orig = SequenceSet.new "2:4,7:11,99,999"

0 commit comments

Comments
 (0)