From f5fbb4404fb025dc8d49383b7b6827d3a622751d Mon Sep 17 00:00:00 2001 From: Eric O Date: Mon, 23 Dec 2024 16:59:55 -0500 Subject: [PATCH] Add more tests --- app/controllers/iiif/images_controller.rb | 2 +- .../resource/derivative_generation.rb | 259 ++++++++++++++++ app/models/resource.rb | 277 +----------------- .../triclops/resource/validations_spec.rb | 10 +- spec/models/resource_spec.rb | 67 ++++- spec/triclops/raster_cache_spec.rb | 8 + spec/triclops/utils/uri_utils_spec.rb | 29 ++ 7 files changed, 371 insertions(+), 281 deletions(-) create mode 100644 app/models/concerns/triclops/resource/derivative_generation.rb create mode 100644 spec/triclops/utils/uri_utils_spec.rb diff --git a/app/controllers/iiif/images_controller.rb b/app/controllers/iiif/images_controller.rb index 0864a24..54e205f 100644 --- a/app/controllers/iiif/images_controller.rb +++ b/app/controllers/iiif/images_controller.rb @@ -202,7 +202,7 @@ def info_json_for_resource(resource, base_type) Triclops::Iiif::Constants::ALLOWED_FORMATS.keys, Triclops::Iiif::Constants::ALLOWED_QUALITIES, Triclops::Iiif::Constants::TILE_SIZE, - resource.scale_factors_for_tile_size(width, height, Triclops::Iiif::Constants::TILE_SIZE) + Imogen::Iiif::Tiles.scale_factors_for(width, height, Triclops::Iiif::Constants::TILE_SIZE) ) end diff --git a/app/models/concerns/triclops/resource/derivative_generation.rb b/app/models/concerns/triclops/resource/derivative_generation.rb new file mode 100644 index 0000000..cf97dcb --- /dev/null +++ b/app/models/concerns/triclops/resource/derivative_generation.rb @@ -0,0 +1,259 @@ +module Triclops + module Resource + module DerivativeGeneration + extend ActiveSupport::Concern + + def raise_exception_if_base_derivative_dependency_missing! + missing_fields = [] + missing_fields << 'source_uri' if self.source_uri.nil? + missing_fields << 'featured_region' if self.featured_region.nil? + + return if missing_fields.empty? + + raise Triclops::Exceptions::MissingBaseImageDependencyException, + "Cannot generate base derivatives for #{self.identifier} because the following required fields are nil: " + + missing_fields.join(', ') + end + + # Generates base derivatives + # rubocop:disable Metrics/AbcSize + def generate_base_derivatives_if_not_exist! + raise_exception_if_base_derivative_dependency_missing! + standard_base_path = Triclops::RasterCache.instance.base_cache_path(Triclops::Iiif::Constants::BASE_TYPE_STANDARD, self.identifier, mkdir_p: true) + limited_base_path = Triclops::RasterCache.instance.base_cache_path(Triclops::Iiif::Constants::BASE_TYPE_LIMITED, self.identifier, mkdir_p: true) + featured_base_path = Triclops::RasterCache.instance.base_cache_path(Triclops::Iiif::Constants::BASE_TYPE_FEATURED, self.identifier, mkdir_p: true) + + return if File.exist?(standard_base_path) && File.exist?(limited_base_path) && File.exist?(featured_base_path) + + self.with_source_image_file do |source_image_file| + # Use the original image to generate standard base + unless File.exist?(standard_base_path) + Triclops::Raster.generate( + source_image_file.path, + standard_base_path, + { + region: 'full', + size: 'full', + rotation: 0, + quality: Triclops::Iiif::Constants::BASE_QUALITY, + format: Triclops::Iiif::Constants::BASE_IMAGE_FORMAT + } + ) + end + # Store standard base dimensions + # NOTE: Must use `revalidate: true` option below to avoid relying on underlying vips recent operation cache. + Imogen.with_image(standard_base_path, { revalidate: true }) do |img| + self.standard_width = img.width + self.standard_height = img.height + end + + # Use the original image to generate the limited base + # Note: Technically the 'limited' base can be larger than the source image, if the source image + # has a long side that's smaller than LIMITED_BASE_SIZE. But that case will be rare, and + # shouldn't cause any issues. + unless File.exist?(limited_base_path) + Triclops::Raster.generate( + source_image_file.path, + limited_base_path, + { + region: 'full', + size: "!#{Triclops::Iiif::Constants::LIMITED_BASE_SIZE},#{Triclops::Iiif::Constants::LIMITED_BASE_SIZE}", + rotation: 0, + quality: Triclops::Iiif::Constants::BASE_QUALITY, + format: Triclops::Iiif::Constants::BASE_IMAGE_FORMAT + } + ) + end + # Store limited base dimensions + # NOTE: Must use `revalidate: true` option below to avoid relying on underlying vips recent operation cache. + Imogen.with_image(limited_base_path, { revalidate: true }) do |img| + self.limited_width = img.width + self.limited_height = img.height + end + + # Use the original image to generate the featured base + # Note: Technically the 'featured' base can be larger than the standard base, if the standard base + # has a long side that's smaller than FEATURED_BASE_SIZE. But that case will be rare, and + # shouldn't cause any issues. + + unless File.exist?(featured_base_path) + Triclops::Raster.generate( + source_image_file.path, + featured_base_path, + { + region: self.featured_region, + size: "!#{Triclops::Iiif::Constants::FEATURED_BASE_SIZE},#{Triclops::Iiif::Constants::FEATURED_BASE_SIZE}", + rotation: 0, + quality: Triclops::Iiif::Constants::BASE_QUALITY, + format: Triclops::Iiif::Constants::BASE_IMAGE_FORMAT + } + ) + end + + # Store featured base dimensions + # NOTE: Must use `revalidate: true` option below to avoid relying on underlying vips recent operation cache. + Imogen.with_image(featured_base_path, { revalidate: true }) do |img| + self.featured_width = img.width + self.featured_height = img.height + end + end + + # Save so that width/height, limited_width/limited_height, featured_width/featured_height properties are persisted. + self.save! + + true + end + # rubocop:enable Metrics/AbcSize + + # Generates commonly requested standard, reduced, and featured derivatives. + def generate_commonly_requested_derivatives + generate_base_derivatives_if_not_exist! + + self.generate_commonly_requested_standard_derivatives + self.generate_commonly_requested_limited_derivatives + self.generate_commonly_requested_featured_derivatives + end + + # Generates the following "standard" derivatives: + # - Scaled versions at Triclops::Iiif::Constants::RECOMMENDED_SIZES. + # - IIIF zooming image viewer tiles + def generate_commonly_requested_standard_derivatives + standard_base_path = Triclops::RasterCache.instance.base_cache_path(Triclops::Iiif::Constants::BASE_TYPE_STANDARD, self.identifier) + + # Generate scaled rasters at Triclops::Iiif::Constants::RECOMMENDED_SIZES. + Triclops::Iiif::Constants::RECOMMENDED_SIZES.each do |size| + raster_opts = { + region: 'full', + size: "!#{size},#{size}", + rotation: 0, + quality: Triclops::Iiif::Constants::BASE_QUALITY, + format: Triclops::Iiif::Constants::DEFAULT_FORMAT + } + raster_path = Triclops::RasterCache.instance.iiif_cache_path_for_raster( + Triclops::Iiif::Constants::BASE_TYPE_STANDARD, + self.identifier, + raster_opts, + mkdir_p: true + ) + next if File.exist?(raster_path) + + Triclops::Raster.generate( + standard_base_path, + raster_path, + raster_opts + ) + end + + # Generate IIIF zooming image viewer tiles + Imogen.with_image(standard_base_path, { revalidate: true }) do |image| + Imogen::Iiif::Tiles.for( + image, + Triclops::RasterCache.instance.iiif_cache_directory_for_identifier( + Triclops::Iiif::Constants::BASE_TYPE_STANDARD, + self.identifier + ), + :jpg, + Triclops::Iiif::Constants::TILE_SIZE, + 'color' + ) do |img, suggested_tile_dest_path, format, iiif_opts| + FileUtils.mkdir_p(File.dirname(suggested_tile_dest_path)) + Imogen::Iiif.convert(img, suggested_tile_dest_path, format, iiif_opts) + end + end + # If the Imogen::Iiif::Tiles.generate_with_vips_dzsave method were fully implemented, + # we would call it like this: + # Imogen.with_image(standard_base_path, { revalidate: true }) do |image| + # Imogen::Iiif::Tiles.generate_with_vips_dzsave( + # image, + # Triclops::RasterCache.instance.iiif_cache_directory_for_identifier(self.identifier), + # format: :jpg, + # tile_size: Triclops::Iiif::Constants::TILE_SIZE, + # tile_filename_without_extension: 'color' + # ) + # end + + true + end + + # Generates the following "limited" derivatives: + # - Scaled versions at Triclops::Iiif::Constants::RECOMMENDED_LIMITED_SIZES. + # - IIIF zooming image viewer tiles + def generate_commonly_requested_limited_derivatives + limited_base_path = Triclops::RasterCache.instance.base_cache_path(Triclops::Iiif::Constants::BASE_TYPE_LIMITED, self.identifier) + + # Generate scaled rasters at Triclops::Iiif::Constants::RECOMMENDED_LIMITED_SIZES. + Triclops::Iiif::Constants::RECOMMENDED_LIMITED_SIZES.each do |size| + raster_opts = { + region: 'full', + size: "!#{size},#{size}", + rotation: 0, + quality: Triclops::Iiif::Constants::BASE_QUALITY, + format: Triclops::Iiif::Constants::DEFAULT_FORMAT + } + raster_path = Triclops::RasterCache.instance.iiif_cache_path_for_raster( + Triclops::Iiif::Constants::BASE_TYPE_LIMITED, + self.identifier, + raster_opts, + mkdir_p: true + ) + next if File.exist?(raster_path) + + Triclops::Raster.generate( + limited_base_path, + raster_path, + raster_opts + ) + end + + # Generate IIIF zooming image viewer tiles + Imogen.with_image(limited_base_path, { revalidate: true }) do |image| + Imogen::Iiif::Tiles.for( + image, + Triclops::RasterCache.instance.iiif_cache_directory_for_identifier( + Triclops::Iiif::Constants::BASE_TYPE_LIMITED, + self.identifier + ), + :jpg, + Triclops::Iiif::Constants::TILE_SIZE, + 'color' + ) do |img, suggested_tile_dest_path, format, iiif_opts| + FileUtils.mkdir_p(File.dirname(suggested_tile_dest_path)) + Imogen::Iiif.convert(img, suggested_tile_dest_path, format, iiif_opts) + end + end + end + + # Generates the following "featured" derivatives: + # - Scaled versions, at Triclops::Iiif::Constants::PRE_GENERATED_SQUARE_SIZES. + def generate_commonly_requested_featured_derivatives + featured_base_path = Triclops::RasterCache.instance.base_cache_path(Triclops::Iiif::Constants::BASE_TYPE_FEATURED, self.identifier) + + # Generate recommended featured versions at PRE_GENERATED_SQUARE_SIZES + Triclops::Iiif::Constants::PRE_GENERATED_SQUARE_SIZES.each do |size| + raster_opts = { + region: 'full', + size: "!#{size},#{size}", + rotation: 0, + quality: Triclops::Iiif::Constants::BASE_QUALITY, + format: Triclops::Iiif::Constants::DEFAULT_FORMAT + } + raster_path = Triclops::RasterCache.instance.iiif_cache_path_for_raster( + Triclops::Iiif::Constants::BASE_TYPE_FEATURED, + self.identifier, + raster_opts, + mkdir_p: true + ) + next if File.exist?(raster_path) + + Triclops::Raster.generate( + featured_base_path, + raster_path, + raster_opts + ) + end + + true + end + end + end +end diff --git a/app/models/resource.rb b/app/models/resource.rb index 61d2bca..0df0d25 100644 --- a/app/models/resource.rb +++ b/app/models/resource.rb @@ -2,6 +2,7 @@ class Resource < ApplicationRecord include Triclops::Resource::IiifInfo include Triclops::Resource::AsJson include Triclops::Resource::Validations + include Triclops::Resource::DerivativeGeneration PCDM_TEXT_TYPES = [ BestType::PcdmTypeLookup::EMAIL, BestType::PcdmTypeLookup::PAGE_DESCRIPTION, BestType::PcdmTypeLookup::STRUCTURED_TEXT, @@ -61,264 +62,6 @@ def queue_base_derivative_generation_if_pending CreateBaseDerivativesJob.perform_later(self.identifier) end - def raise_exception_if_base_derivative_dependency_missing! - missing_fields = [] - missing_fields << 'source_uri' if self.source_uri.nil? - missing_fields << 'featured_region' if self.featured_region.nil? - - return if missing_fields.empty? - - raise Triclops::Exceptions::MissingBaseImageDependencyException, - "Cannot generate base derivatives for #{self.identifier} because the following required fields are nil: " + - missing_fields.join(', ') - end - - # Generates base derivatives - # rubocop:disable Metrics/AbcSize - def generate_base_derivatives_if_not_exist! - raise_exception_if_base_derivative_dependency_missing! - standard_base_path = Triclops::RasterCache.instance.base_cache_path(Triclops::Iiif::Constants::BASE_TYPE_STANDARD, self.identifier, mkdir_p: true) - limited_base_path = Triclops::RasterCache.instance.base_cache_path(Triclops::Iiif::Constants::BASE_TYPE_LIMITED, self.identifier, mkdir_p: true) - featured_base_path = Triclops::RasterCache.instance.base_cache_path(Triclops::Iiif::Constants::BASE_TYPE_FEATURED, self.identifier, mkdir_p: true) - - return if File.exist?(standard_base_path) && File.exist?(limited_base_path) && File.exist?(featured_base_path) - - self.with_source_image_file do |source_image_file| - # Use the original image to generate standard base - unless File.exist?(standard_base_path) - Triclops::Raster.generate( - source_image_file.path, - standard_base_path, - { - region: 'full', - size: 'full', - rotation: 0, - quality: Triclops::Iiif::Constants::BASE_QUALITY, - format: Triclops::Iiif::Constants::BASE_IMAGE_FORMAT - } - ) - end - # Store standard base dimensions - # NOTE: Must use `revalidate: true` option below to avoid relying on underlying vips recent operation cache. - Imogen.with_image(standard_base_path, { revalidate: true }) do |img| - self.standard_width = img.width - self.standard_height = img.height - end - - # Use the original image to generate the limited base - # Note: Technically the 'limited' base can be larger than the source image, if the source image - # has a long side that's smaller than LIMITED_BASE_SIZE. But that case will be rare, and - # shouldn't cause any issues. - unless File.exist?(limited_base_path) - Triclops::Raster.generate( - source_image_file.path, - limited_base_path, - { - region: 'full', - size: "!#{Triclops::Iiif::Constants::LIMITED_BASE_SIZE},#{Triclops::Iiif::Constants::LIMITED_BASE_SIZE}", - rotation: 0, - quality: Triclops::Iiif::Constants::BASE_QUALITY, - format: Triclops::Iiif::Constants::BASE_IMAGE_FORMAT - } - ) - end - # Store limited base dimensions - # NOTE: Must use `revalidate: true` option below to avoid relying on underlying vips recent operation cache. - Imogen.with_image(limited_base_path, { revalidate: true }) do |img| - self.limited_width = img.width - self.limited_height = img.height - end - - # Use the original image to generate the featured base - # Note: Technically the 'featured' base can be larger than the standard base, if the standard base - # has a long side that's smaller than FEATURED_BASE_SIZE. But that case will be rare, and - # shouldn't cause any issues. - - unless File.exist?(featured_base_path) - Triclops::Raster.generate( - source_image_file.path, - featured_base_path, - { - region: self.featured_region, - size: "!#{Triclops::Iiif::Constants::FEATURED_BASE_SIZE},#{Triclops::Iiif::Constants::FEATURED_BASE_SIZE}", - rotation: 0, - quality: Triclops::Iiif::Constants::BASE_QUALITY, - format: Triclops::Iiif::Constants::BASE_IMAGE_FORMAT - } - ) - end - - # Store featured base dimensions - # NOTE: Must use `revalidate: true` option below to avoid relying on underlying vips recent operation cache. - Imogen.with_image(featured_base_path, { revalidate: true }) do |img| - self.featured_width = img.width - self.featured_height = img.height - end - end - - # Save so that width/height, limited_width/limited_height, featured_width/featured_height properties are persisted. - self.save! - - true - end - # rubocop:enable Metrics/AbcSize - - # Generates commonly requested standard, reduced, and featured derivatives. - def generate_commonly_requested_derivatives - generate_base_derivatives_if_not_exist! - - self.generate_commonly_requested_standard_derivatives - self.generate_commonly_requested_limited_derivatives - self.generate_commonly_requested_featured_derivatives - end - - # Generates the following "standard" derivatives: - # - Scaled versions at Triclops::Iiif::Constants::RECOMMENDED_SIZES. - # - IIIF zooming image viewer tiles - def generate_commonly_requested_standard_derivatives - standard_base_path = Triclops::RasterCache.instance.base_cache_path(Triclops::Iiif::Constants::BASE_TYPE_STANDARD, self.identifier) - - # Generate scaled rasters at Triclops::Iiif::Constants::RECOMMENDED_SIZES. - Triclops::Iiif::Constants::RECOMMENDED_SIZES.each do |size| - raster_opts = { - region: 'full', - size: "!#{size},#{size}", - rotation: 0, - quality: Triclops::Iiif::Constants::BASE_QUALITY, - format: Triclops::Iiif::Constants::DEFAULT_FORMAT - } - raster_path = Triclops::RasterCache.instance.iiif_cache_path_for_raster( - Triclops::Iiif::Constants::BASE_TYPE_STANDARD, - self.identifier, - raster_opts, - mkdir_p: true - ) - next if File.exist?(raster_path) - - Triclops::Raster.generate( - standard_base_path, - raster_path, - raster_opts - ) - end - - # Generate IIIF zooming image viewer tiles - Imogen.with_image(standard_base_path, { revalidate: true }) do |image| - Imogen::Iiif::Tiles.for( - image, - Triclops::RasterCache.instance.iiif_cache_directory_for_identifier( - Triclops::Iiif::Constants::BASE_TYPE_STANDARD, - self.identifier - ), - :jpg, - Triclops::Iiif::Constants::TILE_SIZE, - 'color' - ) do |img, suggested_tile_dest_path, format, iiif_opts| - FileUtils.mkdir_p(File.dirname(suggested_tile_dest_path)) - Imogen::Iiif.convert(img, suggested_tile_dest_path, format, iiif_opts) - end - end - # If the Imogen::Iiif::Tiles.generate_with_vips_dzsave method were fully implemented, - # we would call it like this: - # Imogen.with_image(standard_base_path, { revalidate: true }) do |image| - # Imogen::Iiif::Tiles.generate_with_vips_dzsave( - # image, - # Triclops::RasterCache.instance.iiif_cache_directory_for_identifier(self.identifier), - # format: :jpg, - # tile_size: Triclops::Iiif::Constants::TILE_SIZE, - # tile_filename_without_extension: 'color' - # ) - # end - - true - end - - # Generates the following "limited" derivatives: - # - Scaled versions at Triclops::Iiif::Constants::RECOMMENDED_LIMITED_SIZES. - # - IIIF zooming image viewer tiles - def generate_commonly_requested_limited_derivatives - limited_base_path = Triclops::RasterCache.instance.base_cache_path(Triclops::Iiif::Constants::BASE_TYPE_LIMITED, self.identifier) - - # Generate scaled rasters at Triclops::Iiif::Constants::RECOMMENDED_LIMITED_SIZES. - Triclops::Iiif::Constants::RECOMMENDED_LIMITED_SIZES.each do |size| - raster_opts = { - region: 'full', - size: "!#{size},#{size}", - rotation: 0, - quality: Triclops::Iiif::Constants::BASE_QUALITY, - format: Triclops::Iiif::Constants::DEFAULT_FORMAT - } - raster_path = Triclops::RasterCache.instance.iiif_cache_path_for_raster( - Triclops::Iiif::Constants::BASE_TYPE_LIMITED, - self.identifier, - raster_opts, - mkdir_p: true - ) - next if File.exist?(raster_path) - - Triclops::Raster.generate( - limited_base_path, - raster_path, - raster_opts - ) - end - - # Generate IIIF zooming image viewer tiles - Imogen.with_image(limited_base_path, { revalidate: true }) do |image| - Imogen::Iiif::Tiles.for( - image, - Triclops::RasterCache.instance.iiif_cache_directory_for_identifier( - Triclops::Iiif::Constants::BASE_TYPE_LIMITED, - self.identifier - ), - :jpg, - Triclops::Iiif::Constants::TILE_SIZE, - 'color' - ) do |img, suggested_tile_dest_path, format, iiif_opts| - FileUtils.mkdir_p(File.dirname(suggested_tile_dest_path)) - Imogen::Iiif.convert(img, suggested_tile_dest_path, format, iiif_opts) - end - end - end - - # Generates the following "featured" derivatives: - # - Scaled versions, at Triclops::Iiif::Constants::PRE_GENERATED_SQUARE_SIZES. - def generate_commonly_requested_featured_derivatives - featured_base_path = Triclops::RasterCache.instance.base_cache_path(Triclops::Iiif::Constants::BASE_TYPE_FEATURED, self.identifier) - - # Generate recommended featured versions at PRE_GENERATED_SQUARE_SIZES - Triclops::Iiif::Constants::PRE_GENERATED_SQUARE_SIZES.each do |size| - raster_opts = { - region: 'full', - size: "!#{size},#{size}", - rotation: 0, - quality: Triclops::Iiif::Constants::BASE_QUALITY, - format: Triclops::Iiif::Constants::DEFAULT_FORMAT - } - raster_path = Triclops::RasterCache.instance.iiif_cache_path_for_raster( - Triclops::Iiif::Constants::BASE_TYPE_FEATURED, - self.identifier, - raster_opts, - mkdir_p: true - ) - next if File.exist?(raster_path) - - Triclops::Raster.generate( - featured_base_path, - raster_path, - raster_opts - ) - end - - true - end - - # Returns an array of scale factors (e.g. [1, 2, 4, 8, 16]), based on the image dimensions - # and the given tile_size. - def scale_factors_for_tile_size(width, height, tile_size) - Imogen::Iiif::Tiles.scale_factors_for(width, height, tile_size) - end - # Certain property changes should will trigger a switch to the pending state (which will trigger base # derivative regeneration). def switch_to_pending_state_if_core_properties_changed! @@ -354,24 +97,6 @@ def source_uri_is_readable? false end - # Yields a block with a File reference to the specified raster. - # @param raster_opts [Hash] - # A hash of IIIF options (e.g. {region: '...', size: '...', etc. }). - # @param cache_enabled [boolean] - # If true, serves a cached version of the raster (when available) or - # generates and caches a new raster. If false, always generates a new - # raster and does not cache that new raster. - # - # @yield raster_file [File] A file reference to the specified raster. - # @return [void] - # def raster(raster_opts, cache_enabled: false) - # if cache_enabled - # yield_cached_raster(base_type, raster_opts) { |raster_file| yield raster_file } - # else - # yield_uncached_raster(base_type, raster_opts) { |raster_file| yield raster_file } - # end - # end - def raster_exists?(base_type, raster_opts) File.exist?(iiif_cache_path_for_raster(base_type, raster_opts)) end diff --git a/spec/models/concerns/triclops/resource/validations_spec.rb b/spec/models/concerns/triclops/resource/validations_spec.rb index cb2d103..ad33b50 100644 --- a/spec/models/concerns/triclops/resource/validations_spec.rb +++ b/spec/models/concerns/triclops/resource/validations_spec.rb @@ -37,10 +37,16 @@ end end - context 'featured_region format' do - before { instance.featured_region = 'zzz' } + context 'featured_region' do it 'has an error when a supplied featured_region format is invalid' do + instance.featured_region = 'zzz' + expect(instance).not_to be_valid + expect(instance.errors.attribute_names).to eq([:featured_region]) + end + + it 'has an error if featured_region is blank when a source_uri is present' do + instance.featured_region = nil expect(instance).not_to be_valid expect(instance.errors.attribute_names).to eq([:featured_region]) end diff --git a/spec/models/resource_spec.rb b/spec/models/resource_spec.rb index 5301d8d..65ce51f 100644 --- a/spec/models/resource_spec.rb +++ b/spec/models/resource_spec.rb @@ -259,7 +259,7 @@ end end - context 'with an absolute file:// path' do + context 'with a file:// path' do it "returns the path to an existing file" do temp_file_path = Dir.tmpdir + '/triclops-test-file.png' FileUtils.touch(temp_file_path) @@ -279,11 +279,55 @@ end it "raises an error for an unsupported protocol" do - allow(ready_resource).to receive(:source_uri).and_return('abc://what/does/this/protocol/even/mean.png') + allow(ready_resource).to receive(:source_uri).and_return('abc:///what/does/this/protocol/even/mean.png') expect { ready_resource.with_source_image_file { |_file| } }.to raise_error(Errno::ENOENT) end end + context '#source_uri_is_readable?' do + it 'returns true when the source uri file is readable' do + expect(ready_resource.source_uri_is_readable?).to eq(true) + end + + it 'returns true when the source uri file is not readable' do + ready_resource.source_uri = 'file:///this/file/does/not/exist.txt' + expect(ready_resource.source_uri_is_readable?).to eq(false) + end + end + + context '#raster_exists?' do + it 'returns true when a raster file exists' do + allow(File).to receive(:exist?).with( + File.join( + Triclops::RasterCache.instance.cache_directory_for_identifier(ready_resource.identifier), + "standard/iiif/full/full/0/color.png" + ) + ).and_return(true) + expect(ready_resource.raster_exists?(base_type, raster_opts)).to eq(true) + end + + it 'returns false when a raster file does not exist' do + expect(ready_resource.raster_exists?(base_type, raster_opts)).to eq(false) + end + end + + context '#placeholder_identifier_for_pcdm_type' do + + { + BestType::PcdmTypeLookup::AUDIO => 'placeholder:sound', + BestType::PcdmTypeLookup::VIDEO => 'placeholder:moving_image', + BestType::PcdmTypeLookup::TEXT => 'placeholder:text', + BestType::PcdmTypeLookup::PAGE_DESCRIPTION => 'placeholder:text', + BestType::PcdmTypeLookup::SOFTWARE => 'placeholder:software', + BestType::PcdmTypeLookup::FONT => 'placeholder:unavailable', + }.each do |pcdm_type, expected_placeholder| + it "returns the expected value for a resource with a pcdm_type of #{pcdm_type}" do + ready_resource.pcdm_type = pcdm_type + expect(ready_resource.placeholder_identifier_for_pcdm_type).to eq(expected_placeholder) + end + end + end + context '.generate_raster_tempfile_path' do let(:triclops_tmp_directory) { Dir.tmpdir } let(:extension) { 'png' } @@ -340,6 +384,25 @@ expect(Kernel).not_to receive(:sleep) ready_resource.wait_for_source_uri_if_local_disk_file end + + it 'waits and performs multiple checks over time if the files does not exist' do + ready_resource.source_uri = 'file:///this/file/does/not/exist.tiff' + expect(File).to receive(:exist?).exactly(5).times.and_call_original + expect(ready_resource).to receive(:sleep).exactly(5).times + ready_resource.wait_for_source_uri_if_local_disk_file + end + end + + context '#raise_exception_if_base_derivative_dependency_missing!' do + before do + ready_resource.source_uri = nil + ready_resource.featured_region = nil + end + it 'raises an exception under the expected conditions' do + expect { ready_resource.raise_exception_if_base_derivative_dependency_missing! }.to raise_error( + Triclops::Exceptions::MissingBaseImageDependencyException + ) + end end context '.placeholder_resource_for' do diff --git a/spec/triclops/raster_cache_spec.rb b/spec/triclops/raster_cache_spec.rb index 01ddbc7..878e035 100644 --- a/spec/triclops/raster_cache_spec.rb +++ b/spec/triclops/raster_cache_spec.rb @@ -64,4 +64,12 @@ ) end end + + context '#base_type_directory_for_identifier' do + it 'raises an error if an invalid base_type is given' do + expect { + instance.base_type_directory_for_identifier('invalid-base-type', identifier) + }.to raise_error(ArgumentError) + end + end end diff --git a/spec/triclops/utils/uri_utils_spec.rb b/spec/triclops/utils/uri_utils_spec.rb new file mode 100644 index 0000000..790a3e8 --- /dev/null +++ b/spec/triclops/utils/uri_utils_spec.rb @@ -0,0 +1,29 @@ +require 'rails_helper' + +RSpec.describe Triclops::Utils::UriUtils do + context ".file_path_to_location_uri" do + it 'converts as expected' do + expect(described_class.file_path_to_location_uri('/a/b/c.png')).to eq('file:///a/b/c.png') + end + + it 'raises an ArgumentError if the given path is not an absolute file path' do + expect { described_class.file_path_to_location_uri('a/b/c.png') } .to raise_error(ArgumentError) + end + end + + context ".location_uri_to_file_path" do + { + 'file:///a/b/c.png' => '/a/b/c.png', + 'railsroot:///a/b/c.png' => Rails.root.join('a/b/c.png').to_s, + 'placeholder:///sound' => Rails.root.join('app/assets/images/placeholders/sound.png').to_s + }.each do |location_uri, expected_file_path| + it 'converts as expected ' do + expect(described_class.location_uri_to_file_path(location_uri)).to eq(expected_file_path) + end + end + + it 'raises an ArgumentError if the given uri uses an unsupported scheme' do + expect { described_class.location_uri_to_file_path('unknown:///a/b/c.png') } .to raise_error(ArgumentError) + end + end +end