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