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
         |