format_parser 0.1.0
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/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +11 -0
- data/CODE_OF_CONDUCT.md +46 -0
- data/CONTRIBUTING.md +157 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +20 -0
- data/README.md +53 -0
- data/Rakefile +12 -0
- data/format_parser.gemspec +43 -0
- data/lib/care.rb +123 -0
- data/lib/file_information.rb +70 -0
- data/lib/format_parser.rb +55 -0
- data/lib/format_parser/version.rb +3 -0
- data/lib/io_utils.rb +41 -0
- data/lib/parsers/aiff_parser.rb +86 -0
- data/lib/parsers/dpx_parser.rb +143 -0
- data/lib/parsers/exif_parser.rb +58 -0
- data/lib/parsers/gif_parser.rb +49 -0
- data/lib/parsers/jpeg_parser.rb +122 -0
- data/lib/parsers/png_parser.rb +80 -0
- data/lib/parsers/psd_parser.rb +21 -0
- data/lib/parsers/tiff_parser.rb +71 -0
- data/lib/read_limiter.rb +39 -0
- data/lib/remote_io.rb +89 -0
- data/spec/aiff_parser_spec.rb +25 -0
- data/spec/care_spec.rb +77 -0
- data/spec/file_information_spec.rb +16 -0
- data/spec/format_parser_spec.rb +23 -0
- data/spec/io_utils_spec.rb +42 -0
- data/spec/parsers/dpx_parser_spec.rb +29 -0
- data/spec/parsers/exif_parser_spec.rb +45 -0
- data/spec/parsers/gif_parser_spec.rb +35 -0
- data/spec/parsers/jpeg_parser_spec.rb +36 -0
- data/spec/parsers/png_parser_spec.rb +33 -0
- data/spec/parsers/psd_parser_spec.rb +21 -0
- data/spec/parsers/tiff_parser_spec.rb +37 -0
- data/spec/read_limiter_spec.rb +35 -0
- data/spec/remote_fetching_spec.rb +32 -0
- data/spec/remote_io_spec.rb +56 -0
- data/spec/spec_helper.rb +22 -0
- metadata +189 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FormatParser::AIFFParser do
|
4
|
+
it 'parses an AIFF sample file' do
|
5
|
+
parse_result = subject.information_from_io(File.open(__dir__ + '/fixtures/AIFF/fixture.aiff', 'rb'))
|
6
|
+
|
7
|
+
expect(parse_result.file_nature).to eq(:audio)
|
8
|
+
expect(parse_result.file_type).to eq(:aiff)
|
9
|
+
expect(parse_result.media_duration_frames).to eq(46433)
|
10
|
+
expect(parse_result.num_audio_channels).to eq(2)
|
11
|
+
expect(parse_result.audio_sample_rate_hz).to be_within(0.01).of(44100)
|
12
|
+
expect(parse_result.media_duration_seconds).to be_within(0.01).of(1.05)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'parses a Logic Pro created AIFF sample file having a COMT chunk before a COMM chunk' do
|
16
|
+
parse_result = subject.information_from_io(File.open(__dir__ + '/fixtures/AIFF/fixture-logic-aiff.aif', 'rb'))
|
17
|
+
|
18
|
+
expect(parse_result.file_nature).to eq(:audio)
|
19
|
+
expect(parse_result.file_type).to eq(:aiff)
|
20
|
+
expect(parse_result.media_duration_frames).to eq(302400)
|
21
|
+
expect(parse_result.num_audio_channels).to eq(2)
|
22
|
+
expect(parse_result.audio_sample_rate_hz).to be_within(0.01).of(44100)
|
23
|
+
expect(parse_result.media_duration_seconds).to be_within(0.01).of(6.85)
|
24
|
+
end
|
25
|
+
end
|
data/spec/care_spec.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Care do
|
4
|
+
describe Care::Cache do
|
5
|
+
let(:source) { StringIO.new("Hello there, this is our little caching reader") }
|
6
|
+
|
7
|
+
it 'performs correct reads at various offsets' do
|
8
|
+
cache = Care::Cache.new(3)
|
9
|
+
expect(cache.byteslice(source, 0, 3)).to eq("Hel")
|
10
|
+
expect(cache.byteslice(source, 0, 7)).to eq("Hello t")
|
11
|
+
expect(cache.byteslice(source, 1, 7)).to eq("ello th")
|
12
|
+
expect(cache.byteslice(source, 11, 8)).to eq(", this i")
|
13
|
+
expect(cache.byteslice(source, 12, 12)).to eq(" this is our")
|
14
|
+
expect(cache.byteslice(source, 120, 12)).to be_nil
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'can be cleared' do
|
18
|
+
cache = Care::Cache.new(3)
|
19
|
+
expect(cache.byteslice(source, 0, 3)).to eq("Hel")
|
20
|
+
expect(cache.instance_variable_get("@pages")).not_to be_empty
|
21
|
+
cache.clear
|
22
|
+
expect(cache.instance_variable_get("@pages")).to be_empty
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'fits all the reads into one if the input fits into one page' do
|
26
|
+
expect(source).to receive(:read).at_most(2).times.and_call_original
|
27
|
+
|
28
|
+
cache = Care::Cache.new(source.size)
|
29
|
+
|
30
|
+
cache.byteslice(source, 0, 3)
|
31
|
+
cache.byteslice(source, 11, 8)
|
32
|
+
cache.byteslice(source, 190, 12)
|
33
|
+
cache.byteslice(source, 290, 1)
|
34
|
+
cache.byteslice(source, 890, 1)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'permits oversized reads' do
|
38
|
+
cache = Care::Cache.new(4)
|
39
|
+
expect(cache.byteslice(source, 0, 999)).to eq(source.string)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'returns nil with an empty input' do
|
43
|
+
cache = Care::Cache.new(4)
|
44
|
+
expect(cache.byteslice(StringIO.new(''), 0, 1)).to be_nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe Care::IOWrapper do
|
49
|
+
it 'forwards calls to read() to the Care and adjusts internal offsets' do
|
50
|
+
fake_cache_class = Class.new do
|
51
|
+
attr_reader :recorded_calls
|
52
|
+
def byteslice(io, at, n_bytes)
|
53
|
+
@recorded_calls ||= []
|
54
|
+
@recorded_calls << [io, at, n_bytes]
|
55
|
+
# Pretend reads always succeed and return the requisite number of bytes
|
56
|
+
"x" * n_bytes
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
cache_double = fake_cache_class.new
|
61
|
+
io_double = double('IO')
|
62
|
+
|
63
|
+
subject = Care::IOWrapper.new(io_double, cache_double)
|
64
|
+
|
65
|
+
subject.read(2)
|
66
|
+
subject.read(3)
|
67
|
+
subject.seek(11)
|
68
|
+
subject.read(5)
|
69
|
+
|
70
|
+
expect(cache_double.recorded_calls).to be_kind_of(Array)
|
71
|
+
first, second, third = *cache_double.recorded_calls
|
72
|
+
expect(first).to eq([io_double, 0, 2])
|
73
|
+
expect(second).to eq([io_double, 2, 3])
|
74
|
+
expect(third).to eq([io_double, 11, 5])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FormatParser::FileInformation do
|
4
|
+
|
5
|
+
context "File data checks" do
|
6
|
+
it 'succeeds with relevant attributes' do
|
7
|
+
result = described_class.new(file_nature: :image, file_type: :jpg, width_px: 42, height_px: 10, image_orientation: 1)
|
8
|
+
expect(result.file_nature).to eq(:image)
|
9
|
+
expect(result.file_type).to eq(:jpg)
|
10
|
+
expect(result.width_px).to eq(42)
|
11
|
+
expect(result.height_px).to eq(10)
|
12
|
+
expect(result.image_orientation).to eq(1)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FormatParser do
|
4
|
+
it 'returns nil when trying to parse an empty IO' do
|
5
|
+
d = StringIO.new('')
|
6
|
+
expect(FormatParser.parse(d)).to be_nil
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'returns nil when parsing an IO no parser can make sense of' do
|
10
|
+
d = StringIO.new(Random.new.bytes(1))
|
11
|
+
expect(FormatParser.parse(d)).to be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'with fuzzing' do
|
15
|
+
it "returns either a valid result or a nil for all fuzzed inputs at seed #{RSpec.configuration.seed}" do
|
16
|
+
r = Random.new(RSpec.configuration.seed)
|
17
|
+
1024.times do
|
18
|
+
random_blob = StringIO.new(r.bytes(512 * 1024))
|
19
|
+
FormatParser.parse(random_blob) # If there is an error in one of the parsers the example will raise too
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "IOUtils" do
|
4
|
+
let(:io) {File.open(fixtures_dir + '/test.jpg', 'rb')}
|
5
|
+
include FormatParser::IOUtils
|
6
|
+
|
7
|
+
describe '#safe_read' do
|
8
|
+
it 'raises if the requested bytes are past the EOF' do
|
9
|
+
io.seek(268118) # Seek to the actual end of the file
|
10
|
+
expect {
|
11
|
+
safe_read(io, 10)
|
12
|
+
}.to raise_error(FormatParser::IOUtils::InvalidRead)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'raises if we ask for more bytes than are available' do
|
16
|
+
expect {
|
17
|
+
safe_read(io, 1_000_000)
|
18
|
+
}.to raise_error(FormatParser::IOUtils::InvalidRead)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#safe_skip' do
|
23
|
+
it 'raises on a negative skip byte amount' do
|
24
|
+
fake_io = double()
|
25
|
+
expect {
|
26
|
+
safe_skip(fake_io, -5)
|
27
|
+
}.to raise_error(FormatParser::IOUtils::InvalidRead)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'uses #pos if available on the object' do
|
31
|
+
fake_io = double(pos: 11)
|
32
|
+
expect(fake_io).to receive(:seek).with(11+5)
|
33
|
+
safe_skip(fake_io, 5)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'uses #read if no #pos is available on the object' do
|
37
|
+
fake_io = double()
|
38
|
+
expect(fake_io).to receive(:read).with(5).and_return('x' * 5)
|
39
|
+
safe_skip(fake_io, 5)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FormatParser::DPXParser do
|
4
|
+
describe 'with Depix example files' do
|
5
|
+
Dir.glob(fixtures_dir + '/dpx/*.*').each do |dpx_path|
|
6
|
+
it "is able to parse #{File.basename(dpx_path)}" do
|
7
|
+
parsed = subject.information_from_io(File.open(dpx_path, 'rb'))
|
8
|
+
|
9
|
+
expect(parsed).not_to be_nil
|
10
|
+
expect(parsed.file_nature).to eq(:image)
|
11
|
+
expect(parsed.file_type).to eq(:dpx)
|
12
|
+
|
13
|
+
# If we have an error in the struct offsets these values are likely to become
|
14
|
+
# the maximum value of a 4-byte uint, which is way higher
|
15
|
+
expect(parsed.width_px).to be_kind_of(Integer)
|
16
|
+
expect(parsed.width_px).to be_between(0, 2048)
|
17
|
+
expect(parsed.height_px).to be_kind_of(Integer)
|
18
|
+
expect(parsed.height_px).to be_between(0, 4000)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'correctly reads pixel dimensions' do
|
23
|
+
fi = File.open(fixtures_dir + '/dpx/026_FROM_HERO_TAPE_5-3-1_MOV.0029.dpx', 'rb')
|
24
|
+
parsed = subject.information_from_io(fi)
|
25
|
+
expect(parsed.width_px).to eq(1920)
|
26
|
+
expect(parsed.height_px).to eq(1080)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FormatParser::EXIFParser do
|
4
|
+
|
5
|
+
# ORIENTATIONS = [
|
6
|
+
# :top_left,
|
7
|
+
# :top_right,
|
8
|
+
# :bottom_right,
|
9
|
+
# :bottom_left,
|
10
|
+
# :left_top,
|
11
|
+
# :right_top,
|
12
|
+
# :right_bottom,
|
13
|
+
# :left_bottom
|
14
|
+
# ]
|
15
|
+
|
16
|
+
describe 'is able to correctly parse orientation for all the JPEG EXIF examples from FastImage' do
|
17
|
+
Dir.glob(fixtures_dir + '/exif-orientation-testimages/jpg/*.jpg').each do |jpeg_path|
|
18
|
+
filename = File.basename(jpeg_path)
|
19
|
+
it "is able to parse #{filename}" do
|
20
|
+
parser = FormatParser::EXIFParser.new(:jpeg, File.open(jpeg_path, 'rb'))
|
21
|
+
parser.scan_image_exif
|
22
|
+
expect(parser).not_to be_nil
|
23
|
+
|
24
|
+
expect(parser.orientation).to be_kind_of(Symbol)
|
25
|
+
# Filenames in this dir correspond with the orientation of the file
|
26
|
+
expect(filename.include?(parser.orientation.to_s)).to be true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'is able to correctly parse orientation for all the TIFF EXIF examples from FastImage' do
|
32
|
+
Dir.glob(fixtures_dir + '/exif-orientation-testimages/tiff-*/*.tif').each do |tiff_path|
|
33
|
+
filename = File.basename(tiff_path)
|
34
|
+
it "is able to parse #{filename}" do
|
35
|
+
parser = FormatParser::EXIFParser.new(:tiff, File.open(tiff_path, 'rb'))
|
36
|
+
parser.scan_image_exif
|
37
|
+
expect(parser).not_to be_nil
|
38
|
+
|
39
|
+
expect(parser.orientation).to be_kind_of(Symbol)
|
40
|
+
# Filenames in this dir correspond with the orientation of the file
|
41
|
+
expect(filename.include?(parser.orientation.to_s)).to be true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FormatParser::GIFParser do
|
4
|
+
describe 'is able to parse all the examples from FastImage' do
|
5
|
+
Dir.glob(fixtures_dir + '/*.gif').each do |gif_path|
|
6
|
+
it "is able to parse #{File.basename(gif_path)}" do
|
7
|
+
parsed = subject.information_from_io(File.open(gif_path, 'rb'))
|
8
|
+
|
9
|
+
expect(parsed).not_to be_nil
|
10
|
+
|
11
|
+
expect(parsed.file_nature).to eq(:image)
|
12
|
+
expect(parsed.file_type).to eq(:gif)
|
13
|
+
expect(parsed.color_mode).to eq(:indexed)
|
14
|
+
|
15
|
+
expect(parsed.width_px).to be_kind_of(Integer)
|
16
|
+
expect(parsed.width_px).to be > 0
|
17
|
+
|
18
|
+
expect(parsed.height_px).to be_kind_of(Integer)
|
19
|
+
expect(parsed.height_px).to be > 0
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'is able to correctly parse our own examples' do
|
25
|
+
it 'is able to parse the animated GIF' do
|
26
|
+
gif_path = fixtures_dir + "GIF/anim.gif"
|
27
|
+
|
28
|
+
parsed = subject.information_from_io(File.open(gif_path, 'rb'))
|
29
|
+
expect(parsed).not_to be_nil
|
30
|
+
|
31
|
+
expect(parsed.width_px).to eq(320)
|
32
|
+
expect(parsed.height_px).to eq(180)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FormatParser::JPEGParser do
|
4
|
+
describe 'is able to parse all the examples from FastImage' do
|
5
|
+
Dir.glob(fixtures_dir + '/*.jpg').each do |jpeg_path|
|
6
|
+
it "is able to parse #{File.basename(jpeg_path)}" do
|
7
|
+
parsed = subject.information_from_io(File.open(jpeg_path, 'rb'))
|
8
|
+
expect(parsed).not_to be_nil
|
9
|
+
expect(parsed.file_nature).to eq(:image)
|
10
|
+
expect(parsed.file_type).to eq(:jpg)
|
11
|
+
|
12
|
+
expect(parsed.width_px).to be_kind_of(Integer)
|
13
|
+
expect(parsed.width_px).to be > 0
|
14
|
+
|
15
|
+
expect(parsed.height_px).to be_kind_of(Integer)
|
16
|
+
expect(parsed.height_px).to be > 0
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'is able to parse all the JPEG exif examples from FastImage' do
|
22
|
+
Dir.glob(fixtures_dir + '/exif-orientation-testimages/jpg/*.jpg').each do |jpeg_path|
|
23
|
+
it "is able to parse #{File.basename(jpeg_path)}" do
|
24
|
+
parsed = subject.information_from_io(File.open(jpeg_path, 'rb'))
|
25
|
+
expect(parsed).not_to be_nil
|
26
|
+
|
27
|
+
expect(parsed.orientation).to be_kind_of(Symbol)
|
28
|
+
expect(parsed.width_px).to be > 0
|
29
|
+
|
30
|
+
expect(parsed.height_px).to be_kind_of(Integer)
|
31
|
+
expect(parsed.height_px).to be > 0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FormatParser::PNGParser do
|
4
|
+
describe 'is able to parse all the examples from FastImage' do
|
5
|
+
Dir.glob(fixtures_dir + '/*.png').each do |png_path|
|
6
|
+
it "is able to parse #{File.basename(png_path)}" do
|
7
|
+
parsed = subject.information_from_io(File.open(png_path, 'rb'))
|
8
|
+
expect(parsed).not_to be_nil
|
9
|
+
expect(parsed.file_nature).to eq(:image)
|
10
|
+
expect(parsed.file_type).to eq(:png)
|
11
|
+
expect(parsed.color_mode).to eq(:indexed)
|
12
|
+
|
13
|
+
expect(parsed.width_px).to be_kind_of(Integer)
|
14
|
+
expect(parsed.width_px).to be > 0
|
15
|
+
|
16
|
+
expect(parsed.height_px).to be_kind_of(Integer)
|
17
|
+
expect(parsed.height_px).to be > 0
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'is able to parse an animated PNG' do
|
23
|
+
gif_path = fixtures_dir + "PNG/anim.png"
|
24
|
+
|
25
|
+
parsed = subject.information_from_io(File.open(gif_path, 'rb'))
|
26
|
+
expect(parsed).not_to be_nil
|
27
|
+
|
28
|
+
expect(parsed.width_px).to eq(320)
|
29
|
+
expect(parsed.height_px).to eq(180)
|
30
|
+
expect(parsed.has_multiple_frames).to eq(true)
|
31
|
+
expect(parsed.num_animation_or_video_frames).to eq(17)
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FormatParser::PSDParser do
|
4
|
+
describe 'is able to parse all the examples from FastImage' do
|
5
|
+
Dir.glob(fixtures_dir + '/*.psd').each do |psd_path|
|
6
|
+
it "is able to parse #{File.basename(psd_path)}" do
|
7
|
+
parsed = subject.information_from_io(File.open(psd_path, 'rb'))
|
8
|
+
|
9
|
+
expect(parsed).not_to be_nil
|
10
|
+
expect(parsed.file_nature).to eq(:image)
|
11
|
+
expect(parsed.file_type).to eq(:psd)
|
12
|
+
|
13
|
+
expect(parsed.width_px).to be_kind_of(Integer)
|
14
|
+
expect(parsed.width_px).to be > 0
|
15
|
+
|
16
|
+
expect(parsed.height_px).to be_kind_of(Integer)
|
17
|
+
expect(parsed.height_px).to be > 0
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FormatParser::TIFFParser do
|
4
|
+
describe 'is able to parse all the examples from FastImage' do
|
5
|
+
Dir.glob(fixtures_dir + '/TIFF/*.tif').each do |tiff_path|
|
6
|
+
it "is able to parse #{File.basename(tiff_path)}" do
|
7
|
+
parsed = subject.information_from_io(File.open(tiff_path, 'rb'))
|
8
|
+
|
9
|
+
expect(parsed).not_to be_nil
|
10
|
+
expect(parsed.file_nature).to eq(:image)
|
11
|
+
expect(parsed.file_type).to eq(:tif)
|
12
|
+
|
13
|
+
expect(parsed.width_px).to be_kind_of(Integer)
|
14
|
+
expect(parsed.width_px).to be > 0
|
15
|
+
|
16
|
+
expect(parsed.height_px).to be_kind_of(Integer)
|
17
|
+
expect(parsed.height_px).to be > 0
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'is able to parse all the TIFF exif examples from FastImage' do
|
23
|
+
Dir.glob(fixtures_dir + '/exif-orientation-testimages/tiff-*/*.tif').each do |tiff_path|
|
24
|
+
it "is able to parse #{File.basename(tiff_path)}" do
|
25
|
+
parsed = subject.information_from_io(File.open(tiff_path, 'rb'))
|
26
|
+
expect(parsed).not_to be_nil
|
27
|
+
|
28
|
+
expect(parsed.orientation).to be_kind_of(Symbol)
|
29
|
+
expect(parsed.width_px).to be > 0
|
30
|
+
|
31
|
+
expect(parsed.height_px).to be_kind_of(Integer)
|
32
|
+
expect(parsed.height_px).to be > 0
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "ReadLimiter" do
|
4
|
+
let(:io) { StringIO.new(Random.new.bytes(1024)) }
|
5
|
+
|
6
|
+
it 'does not enforce any limits with default arguments' do
|
7
|
+
reader = FormatParser::ReadLimiter.new(io)
|
8
|
+
2048.times { reader.seek(1) }
|
9
|
+
2048.times { reader.read(4) }
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'enforces the number of seeks' do
|
13
|
+
reader = FormatParser::ReadLimiter.new(io, max_seeks: 4)
|
14
|
+
4.times { reader.seek(1) }
|
15
|
+
expect {
|
16
|
+
reader.seek(1)
|
17
|
+
}.to raise_error(/Seek budget exceeded/)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'enforces the number of reads' do
|
21
|
+
reader = FormatParser::ReadLimiter.new(io, max_reads: 4)
|
22
|
+
4.times { reader.read(1) }
|
23
|
+
expect {
|
24
|
+
reader.read(1)
|
25
|
+
}.to raise_error(/calls exceeded \(4 max\)/)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'enforces the number of bytes read' do
|
29
|
+
reader = FormatParser::ReadLimiter.new(io, max_bytes: 512)
|
30
|
+
reader.read(512)
|
31
|
+
expect {
|
32
|
+
reader.read(1)
|
33
|
+
}.to raise_error(/bytes budget \(512\) exceeded/)
|
34
|
+
end
|
35
|
+
end
|