Class: SnapDiff::Comparison
- Inherits:
-
Object
- Object
- SnapDiff::Comparison
- Defined in:
- lib/snap_diff/comparison.rb
Overview
Handles comparison of two images with a focus on performance and accuracy.
This class implements a multi-layered optimization strategy for image comparison:
-
Early File-based Checks (Fastest):
- Verifies both images exist (raises ArgumentError if not)
- Compares file sizes (different sizes → different images)
- Performs byte-by-byte comparison for identical files (exact match)
-
Quick Comparison (Fast):
- Compares image dimensions (different dimensions → different images)
- Performs pixel-by-pixel comparison if dimensions match
-
Detailed Analysis (Slower):
- Only performed if quick comparison finds differences
- Handles anti-aliasing, color tolerance, and shift detection
- Respects skip_area and other comparison parameters
This layered approach ensures optimal performance by:
- Using the fastest possible method for early rejection
- Only performing expensive operations when absolutely necessary
- Maintaining high accuracy for complex comparisons
Defined Under Namespace
Classes: Images
Constant Summary collapse
- TOLERABLE_OPTIONS =
[:tolerance, :color_distance_limit, :shift_distance_limit, :area_size_limit].freeze
- KNOWN_OPTIONS =
Every key anything downstream of here actually reads -- capture options included, because ScreenshotMatcher hands the same hash to the screenshoter and only carves :crop / :stability_time_limit / :wait out of the copy it passes on.
Written out rather than derived from Config#default_options: that hash is what the gem merges in, so deriving from it would make the check agree with itself and validate nothing about the keys a USER adds.
%i[ area_size_limit capybara_screenshot_options color_distance_limit crop delayed driver median_filter_window_size perceptual_threshold screenshot_format shift_distance_limit skip_area stability_time_limit tolerance wait ].freeze
Instance Attribute Summary collapse
-
#base_image_path ⇒ Object
readonly
Returns the value of attribute base_image_path.
-
#difference ⇒ Object
readonly
Returns the value of attribute difference.
-
#driver ⇒ Object
readonly
Returns the value of attribute driver.
-
#driver_options ⇒ Object
readonly
Returns the value of attribute driver_options.
-
#error_message ⇒ Object
readonly
Returns the value of attribute error_message.
-
#image_path ⇒ Object
readonly
Returns the value of attribute image_path.
Instance Method Summary collapse
-
#different? ⇒ Boolean
Determines if the images are different according to the comparison rules.
- #dimensions_changed? ⇒ Boolean
- #ensure_files_exist! ⇒ Object
-
#initialize(image_path, base_image_path, options = {}) ⇒ Comparison
constructor
A new instance of Comparison.
- #processed ⇒ Object
- #processed? ⇒ Boolean
-
#quick_equal? ⇒ Boolean
Performs a quick comparison of two image files.
- #reporter ⇒ Object
Constructor Details
#initialize(image_path, base_image_path, options = {}) ⇒ Comparison
Returns a new instance of Comparison.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/snap_diff/comparison.rb', line 75 def initialize(image_path, base_image_path, = {}) @image_path = Pathname.new(image_path) @base_image_path = Pathname.new(base_image_path) ensure_files_exist! @driver_options = .freeze # THE no-silent-no-op check (ADR-010). Every option hash in the gem # reaches this constructor, so a key nothing reads is caught here # whichever entry point produced it. Frozen-but-unvalidated is how a # misspelt `tolerence:` bought a green suite that compared nothing. (.keys - KNOWN_OPTIONS).each do |key| Removal.warn_once(:"unknown_option_#{key}", Removal.unknown_option(key)) end # The per-comparison half of the shift_distance_limit removal (the # global half is Config#shift_distance_limit=). Presence is not enough: # config.default_options carries the key on EVERY comparison, nil for # everyone who never set it. if [:shift_distance_limit] Removal.warn_once(:shift_distance_limit, Removal::SHIFT_DISTANCE_LIMIT_REMOVED) end @driver = Drivers.for(@driver_options) @without_tolerable_options = (.keys & TOLERABLE_OPTIONS).empty? end |
Instance Attribute Details
#base_image_path ⇒ Object (readonly)
Returns the value of attribute base_image_path.
72 73 74 |
# File 'lib/snap_diff/comparison.rb', line 72 def base_image_path @base_image_path end |
#difference ⇒ Object
Returns the value of attribute difference.
73 74 75 |
# File 'lib/snap_diff/comparison.rb', line 73 def difference @difference end |
#driver ⇒ Object (readonly)
Returns the value of attribute driver.
71 72 73 |
# File 'lib/snap_diff/comparison.rb', line 71 def driver @driver end |
#driver_options ⇒ Object (readonly)
Returns the value of attribute driver_options.
71 72 73 |
# File 'lib/snap_diff/comparison.rb', line 71 def @driver_options end |
#error_message ⇒ Object (readonly)
Returns the value of attribute error_message.
73 74 75 |
# File 'lib/snap_diff/comparison.rb', line 73 def @error_message end |
#image_path ⇒ Object (readonly)
Returns the value of attribute image_path.
72 73 74 |
# File 'lib/snap_diff/comparison.rb', line 72 def image_path @image_path end |
Instance Method Details
#different? ⇒ Boolean
Determines if the images are different according to the comparison rules.
This method performs a full comparison if not already done, including any configured tolerances for color differences and shift distances.
134 135 136 |
# File 'lib/snap_diff/comparison.rb', line 134 def different? processed.difference.different? end |
#dimensions_changed? ⇒ Boolean
138 139 140 |
# File 'lib/snap_diff/comparison.rb', line 138 def dimensions_changed? difference.failed_by&.[](:different_dimensions) end |
#ensure_files_exist! ⇒ Object
118 119 120 121 |
# File 'lib/snap_diff/comparison.rb', line 118 def ensure_files_exist! raise ArgumentError, "There is no original (base) screenshot located at #{@base_image_path}" unless @base_image_path.exist? raise ArgumentError, "There is no new screenshot located at #{@image_path}" unless @image_path.exist? end |
#processed ⇒ Object
150 151 152 153 154 |
# File 'lib/snap_diff/comparison.rb', line 150 def processed self.difference = find_difference(quick_mode: false) unless processed? @error_message ||= reporter.generate self end |
#processed? ⇒ Boolean
146 147 148 |
# File 'lib/snap_diff/comparison.rb', line 146 def processed? !!difference end |
#quick_equal? ⇒ Boolean
This method will raise ArgumentError if either image file is missing.
Performs a quick comparison of two image files.
This method is optimized for speed and will return as soon as a difference is found. It's used for fast rejection before performing more expensive comparisons.
110 111 112 113 114 115 116 |
# File 'lib/snap_diff/comparison.rb', line 110 def quick_equal? return true if identical_files? result, difference = find_difference(quick_mode: true) self.difference = difference result end |
#reporter ⇒ Object
142 143 144 |
# File 'lib/snap_diff/comparison.rb', line 142 def reporter @reporter ||= build_reporter end |