Skip to content

Commit ddcbf26

Browse files
committed
Add test cases for exceeding 128kb cliff
Signed-off-by: Hiroshi Hatake <[email protected]>
1 parent 0e6f97f commit ddcbf26

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed

spec/streaming_128kb_cliff_spec.rb

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# spec/streaming_128kb_cliff_spec.rb
2+
require "spec_helper"
3+
require "stringio"
4+
require "zstd-ruby"
5+
6+
RSpec.describe "Zstd streaming 128KB cliff" do
7+
MAGIC = [0x28, 0xB5, 0x2F, 0xFD].freeze
8+
9+
def hex(str)
10+
str.unpack1("H*")
11+
end
12+
13+
def has_magic?(bin)
14+
bytes = bin.bytes
15+
bytes[0, 4] == MAGIC
16+
end
17+
18+
shared_examples "round-trip streaming compress" do |size|
19+
it "round-trips #{size} bytes and starts with zstd magic" do
20+
# Produce data
21+
src = "a" * size
22+
23+
sc = Zstd::StreamingCompress.new
24+
sc << src
25+
compressed = sc.finish
26+
27+
expect(has_magic?(compressed)).to be(true), "missing magic: #{hex(compressed)[0, 16]}"
28+
expect(Zstd.decompress(compressed)).to eq(src)
29+
end
30+
end
31+
32+
context "exactly around 128 KiB" do
33+
include_examples "round-trip streaming compress", 131_071
34+
include_examples "round-trip streaming compress", 131_072 # the cliff
35+
include_examples "round-trip streaming compress", 131_073
36+
end
37+
38+
context "multiple writes crossing the boundary" do
39+
it "64KiB + 64KiB (exact boundary) works" do
40+
part = "x" * 65_536
41+
sc = Zstd::StreamingCompress.new
42+
sc << part
43+
sc << part
44+
compressed = sc.finish
45+
expect(has_magic?(compressed)).to be(true)
46+
expect(Zstd.decompress(compressed)).to eq(part * 2)
47+
end
48+
49+
it "64KiB + 64KiB + 1 works" do
50+
a = "a" * 65_536
51+
b = "b" * 65_536
52+
c = "c"
53+
sc = Zstd::StreamingCompress.new
54+
sc << a << b << c
55+
compressed = sc.finish
56+
expect(has_magic?(compressed)).to be(true)
57+
expect(Zstd.decompress(compressed)).to eq(a + b + c)
58+
end
59+
end
60+
61+
context "flush/end draining" do
62+
it "returns all produced bytes across flush and finish" do
63+
sc = Zstd::StreamingCompress.new
64+
sc << ("a" * 70_000)
65+
out = sc.flush
66+
expect(out).not_to be_empty
67+
sc << ("b" * 70_000)
68+
out << sc.finish
69+
expect(has_magic?(out)).to be(true)
70+
expect(Zstd.decompress(out)).to eq(("a" * 70_000) + ("b" * 70_000))
71+
end
72+
end
73+
74+
context "GC.compact interaction" do
75+
it "survives compaction around the boundary" do
76+
sc = Zstd::StreamingCompress.new
77+
sc << ("a" * 80_000)
78+
GC.compact if GC.respond_to?(:compact)
79+
sc << ("b" * 60_000) # total now > 128KiB
80+
compressed = sc.finish
81+
expect(has_magic?(compressed)).to be(true)
82+
expect(Zstd.decompress(compressed)).to eq(("a" * 80_000) + ("b" * 60_000))
83+
end
84+
end
85+
86+
context "larger payload sanity" do
87+
it "round-trips ~1 MiB" do
88+
src = "z" * 1_048_576
89+
sc = Zstd::StreamingCompress.new(level: 3)
90+
sc << src
91+
compressed = sc.finish
92+
expect(has_magic?(compressed)).to be(true)
93+
expect(Zstd.decompress(compressed)).to eq(src)
94+
end
95+
end
96+
97+
describe Zstd::StreamWriter do
98+
it "produces a valid frame and round-trips at exactly 128KiB" do
99+
io = StringIO.new
100+
sw = Zstd::StreamWriter.new(io)
101+
sw.write("a" * 131_072)
102+
sw.finish
103+
104+
io.rewind
105+
bin = io.read
106+
expect(has_magic?(bin)).to be(true), "missing magic: #{hex(bin)[0, 16]}"
107+
108+
io.rewind
109+
sr = Zstd::StreamReader.new(io)
110+
out = sr.read(2_000_000)
111+
expect(out.size).to eq(131_072)
112+
expect(out).to eq("a" * 131_072)
113+
io.close
114+
end
115+
116+
it "handles boundary-crossing writes with flush in between" do
117+
io = StringIO.new
118+
sw = Zstd::StreamWriter.new(io)
119+
sw.write("a" * 70_000)
120+
sw.write("b" * 70_000) # crosses 128KiB internally
121+
sw.finish
122+
123+
io.rewind
124+
bin = io.read
125+
expect(has_magic?(bin)).to be(true)
126+
io.rewind
127+
sr = Zstd::StreamReader.new(io)
128+
out = sr.read(1_000_000)
129+
expect(out).to eq("a" * 70_000 + "b" * 70_000)
130+
io.close
131+
end
132+
133+
it "survives GC.compact mid-stream" do
134+
io = StringIO.new
135+
sw = Zstd::StreamWriter.new(io)
136+
sw.write("x" * 90_000)
137+
GC.compact if GC.respond_to?(:compact)
138+
sw.write("y" * 50_000)
139+
sw.finish
140+
141+
io.rewind
142+
bin = io.read
143+
expect(has_magic?(bin)).to be(true)
144+
io.rewind
145+
sr = Zstd::StreamReader.new(io)
146+
out = sr.read(200_000)
147+
expect(out).to eq("x" * 90_000 + "y" * 50_000)
148+
end
149+
end
150+
end

0 commit comments

Comments
 (0)