Class: Capybara::Screenshot::Diff::ImageCompare

Inherits:
SimpleDelegator
  • Object
show all
Defined in:
lib/capybara/screenshot/diff/image_compare.rb

Overview

Compare two images and determine if they are equal, different, or within some comparison range considering color values and difference area size.

Constant Summary collapse

DIFF_COLOR =
[255, 0, 0, 255].freeze
SKIP_COLOR =
[255, 192, 0, 255].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(new_file_name, old_file_name = nil, **driver_options) ⇒ ImageCompare

Returns a new instance of ImageCompare.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 17

def initialize(new_file_name, old_file_name = nil, **driver_options)
  @new_file_name = new_file_name
  @old_file_name = old_file_name || "#{new_file_name}~"
  @annotated_old_file_name = "#{new_file_name.chomp(".png")}.committed.png"
  @annotated_new_file_name = "#{new_file_name.chomp(".png")}.latest.png"

  @driver_options = driver_options

  @color_distance_limit = driver_options[:color_distance_limit] || 0
  @area_size_limit = driver_options[:area_size_limit]
  @shift_distance_limit = driver_options[:shift_distance_limit]
  @dimensions = driver_options[:dimensions]
  @skip_area = driver_options[:skip_area]
  @tolerance = driver_options[:tolerance]
  @median_filter_window_size = driver_options[:median_filter_window_size]

  driver_klass = find_driver_class_for(@driver_options.fetch(:driver, :chunky_png))
  @driver = driver_klass.new(@new_file_name, @old_file_name, **@driver_options)

  super(@driver)
end

Instance Attribute Details

#annotated_new_file_nameObject (readonly)

Returns the value of attribute annotated_new_file_name.



13
14
15
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 13

def annotated_new_file_name
  @annotated_new_file_name
end

#annotated_old_file_nameObject (readonly)

Returns the value of attribute annotated_old_file_name.



13
14
15
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 13

def annotated_old_file_name
  @annotated_old_file_name
end

#area_size_limitObject (readonly)

Returns the value of attribute area_size_limit.



13
14
15
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 13

def area_size_limit
  @area_size_limit
end

#color_distance_limitObject (readonly)

Returns the value of attribute color_distance_limit.



13
14
15
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 13

def color_distance_limit
  @color_distance_limit
end

#driverObject (readonly)

Returns the value of attribute driver.



11
12
13
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 11

def driver
  @driver
end

#driver_optionsObject (readonly)

Returns the value of attribute driver_options.



11
12
13
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 11

def driver_options
  @driver_options
end

#new_file_nameObject (readonly)

Returns the value of attribute new_file_name.



13
14
15
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 13

def new_file_name
  @new_file_name
end

#old_file_nameObject (readonly)

Returns the value of attribute old_file_name.



13
14
15
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 13

def old_file_name
  @old_file_name
end

#shift_distance_limitObject (readonly)

Returns the value of attribute shift_distance_limit.



13
14
15
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 13

def shift_distance_limit
  @shift_distance_limit
end

#skip_areaObject (readonly)

Returns the value of attribute skip_area.



13
14
15
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 13

def skip_area
  @skip_area
end

Instance Method Details

#annotate_and_save(images, region = difference_region) ⇒ Object



125
126
127
128
129
130
131
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 125

def annotate_and_save(images, region = difference_region)
  annotated_images = driver.draw_rectangles(images, region, DIFF_COLOR)
  @skip_area.to_a.flatten.each_slice(4) do |region|
    annotated_images = driver.draw_rectangles(annotated_images, region, SKIP_COLOR)
  end
  save(*annotated_images, @annotated_old_file_name, @annotated_new_file_name)
end

#clean_tmp_filesObject



115
116
117
118
119
120
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 115

def clean_tmp_files
  FileUtils.cp @old_file_name, @new_file_name if old_file_exists?
  File.delete(@old_file_name) if old_file_exists?
  File.delete(@annotated_old_file_name) if File.exist?(@annotated_old_file_name)
  File.delete(@annotated_new_file_name) if File.exist?(@annotated_new_file_name)
end

#difference_regionObject



158
159
160
161
162
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 158

def difference_region
  return nil unless @left || @top || @right || @bottom

  [@left, @top, @right, @bottom]
end

#different?Boolean

Compare the two images referenced by this object, and return ‘true` if they are different, and `false` if they are the same. Return `nil` if the old file does not exist or if the image dimensions do not match.

Returns:

  • (Boolean)


79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 79

def different?
  return nil unless old_file_exists?

  images = driver.load_images(@old_file_name, @new_file_name)

  old_image, new_image = preprocess_images(images, driver)

  if driver.dimension_changed?(old_image, new_image)
    save(new_image, old_image, @annotated_new_file_name, @annotated_old_file_name)

    self.difference_region = 0, 0, driver.width_for(old_image), driver.height_for(old_image)

    return true
  end

  region, meta = driver.find_difference_region(
    new_image,
    old_image,
    @color_distance_limit,
    @shift_distance_limit,
    @area_size_limit
  )
  self.difference_region = region

  return not_different if difference_region_empty?(old_image, region)
  return not_different if @area_size_limit && driver.size(region) <= @area_size_limit
  return not_different if @tolerance && @tolerance > driver.difference_level(meta, old_image, region)

  # TODO: Remove this or find similar solution for vips
  return not_different if @shift_distance_limit && !driver.shift_distance_different?

  annotate_and_save(images, region)

  true
end

#error_messageObject



147
148
149
150
151
152
153
154
155
156
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 147

def error_message
  result = {
    area_size: driver.size(difference_region),
    region: difference_region
  }

  driver.adds_error_details_to(result)

  ["(#{result.to_json})", new_file_name, annotated_old_file_name, annotated_new_file_name].join("\n")
end

#old_file_exists?Boolean

Returns:

  • (Boolean)


138
139
140
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 138

def old_file_exists?
  @old_file_name && File.exist?(@old_file_name)
end

#quick_equal?Boolean

Compare the two image files and return ‘true` or `false` as quickly as possible. Return falsish if the old file does not exist or the image dimensions do not match.

Returns:

  • (Boolean)


41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 41

def quick_equal?
  return false unless old_file_exists?
  return true if new_file_size == old_file_size

  # old_bytes, new_bytes = load_image_files(@old_file_name, @new_file_name)
  # return true if old_bytes == new_bytes

  images = driver.load_images(@old_file_name, @new_file_name)
  old_image, new_image = preprocess_images(images, driver)

  return false if driver.dimension_changed?(old_image, new_image)

  region, meta = driver.find_difference_region(
    new_image,
    old_image,
    @color_distance_limit,
    @shift_distance_limit,
    @area_size_limit,
    fast_fail: true
  )

  self.difference_region = region

  return true if difference_region_empty?(new_image, region)

  return true if @area_size_limit && driver.size(region) <= @area_size_limit

  return true if @tolerance && @tolerance >= driver.difference_level(meta, old_image, region)

  # TODO: Remove this or find similar solution for vips
  return true if @shift_distance_limit && driver.shift_distance_equal?

  false
end

#resetObject



142
143
144
145
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 142

def reset
  self.difference_region = nil
  driver.reset
end

#save(old_img, new_img, annotated_old_file_name, annotated_new_file_name) ⇒ Object



133
134
135
136
# File 'lib/capybara/screenshot/diff/image_compare.rb', line 133

def save(old_img, new_img, annotated_old_file_name, annotated_new_file_name)
  driver.save_image_to(old_img, annotated_old_file_name)
  driver.save_image_to(new_img, annotated_new_file_name)
end