summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Allen <dan.j.allen@gmail.com>2022-07-15 23:55:02 -0600
committerDan Allen <dan.j.allen@gmail.com>2022-07-16 03:10:12 -0600
commit46b19817fbfc0c4e061beb513565721c5c359432 (patch)
tree793576a3dd825dd458579359c46817016685e7a2
parentb5139665f281d4c09c3b897fd16366e65edc30e3 (diff)
organize custom RSpec matchers into separate files
-rw-r--r--spec/spec_helper.rb156
-rw-r--r--spec/spec_helper/matchers.rb11
-rw-r--r--spec/spec_helper/matchers/annotate.rb9
-rw-r--r--spec/spec_helper/matchers/have_background.rb14
-rw-r--r--spec/spec_helper/matchers/have_message.rb31
-rw-r--r--spec/spec_helper/matchers/have_size.rb6
-rw-r--r--spec/spec_helper/matchers/log_message.rb28
-rw-r--r--spec/spec_helper/matchers/log_messages.rb15
-rw-r--r--spec/spec_helper/matchers/not_log_message.rb14
-rw-r--r--spec/spec_helper/matchers/not_raise_exception.rb3
-rw-r--r--spec/spec_helper/matchers/visually_match.rb40
11 files changed, 176 insertions, 151 deletions
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index aabfc8d5..90f73c2e 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -19,6 +19,7 @@ require 'pathname' unless defined? Pathname
require 'pdf/inspector'
require 'socket'
require 'tmpdir'
+require_relative 'spec_helper/matchers'
# NOTE: fix warning in Prawn::Font:TTF
Prawn::Font::TTF.prepend (Module.new do
@@ -436,6 +437,10 @@ RSpec.configure do |config|
File.join output_dir, path
end
+ def spec_dir
+ __dir__
+ end
+
def tmp_dir
File.join __dir__, 'tmp'
end
@@ -721,154 +726,3 @@ RSpec.configure do |config|
diff.length
end
end
-
-RSpec::Matchers.define_negated_matcher :not_raise_exception, :raise_exception
-
-RSpec::Matchers.define :have_size do |expected|
- match {|actual| actual.size == expected }
- failure_message {|actual| %(expected #{actual} to have size #{expected}, but was #{actual.size}) }
-end
-
-RSpec::Matchers.define :have_background do |expected|
- match do |actual|
- color = ((expected[:color].scan %r/../).map {|it| ((it.to_i 16) / 255.0).round 5 }.join ' ') + ' scn'
- # FIXME: shave off lines before this line
- (expect actual).to include color
- x1, y1 = expected[:top_left]
- x2, y2 = expected[:bottom_right]
- (expect actual).to include %(#{x2} #{y1} #{x2} #{y1} #{x2} #{y1} c)
- (expect actual).to include %(#{x1} #{y2} #{x1} #{y2} #{x1} #{y2} c)
- end
- failure_message {|actual| %(expected #{actual} to have background #{expected}, but was \n#{actual.join ?\n}) }
-end
-
-RSpec::Matchers.define :annotate do |text|
- match do |subject|
- left, bottom, right, top = subject[:Rect]
- left == text[:x] && ((text[:x] + text[:width]) - right).abs < 0.25 && bottom < text[:y] && top > (text[:y] + text[:font_size])
- end
- failure_message {|subject| %(expected #{subject} to annotate #{text}) }
-end
-
-RSpec::Matchers.define :have_message do |expected|
- actual = nil
- match notify_expectation_failures: true do |logger|
- result = false
- messages = logger.messages
- expected_index = expected[:index] || 0
- if (message = messages[expected_index])
- if message[:severity] == expected[:severity]
- message_text = Hash === (message_data = message[:message]) ? message_data[:text] : message_data
- if Regexp === (expected_message = expected[:message])
- result = true if expected_message.match? message_text
- elsif expected_message.start_with? '~'
- result = true if message_text.include? expected_message[1..-1]
- elsif message_text === expected_message
- result = true
- end
- result = false if (file = expected[:file]) && !(Hash === message_data && file == message_data[:source_location].file)
- result = false if (lineno = expected[:lineno]) && !(Hash === message_data && lineno == message_data[:source_location].lineno)
- end
- actual = message
- end
- (expect messages).to have_size expected_index + 1 if result && expected[:last]
- result
- end
-
- failure_message do
- %(expected #{expected[:severity]} message#{expected[:message].to_s.chr == '~' ? ' containing ' : ' matching '}`#{expected[:message]}' to have been logged) + (actual ? %(, but got #{actual[:severity]}: #{actual[:message]}) : '')
- end
-end
-
-RSpec::Matchers.define :log_message do |expected|
- match notify_expectation_failures: true do |actual|
- if expected
- log_level_override = expected.delete :using_log_level
- expected = nil if expected.empty?
- end
- with_memory_logger log_level_override do |logger|
- actual.call
- if expected
- (expect logger).to have_message expected
- else
- (expect logger).not_to be_empty
- end
- true
- end
- end
-
- #match_when_negated notify_expectation_failures: true do |actual|
- # with_memory_logger expected.to_h[:using_log_level] do |logger|
- # actual.call
- # logger ? logger.empty? : true
- # end
- #end
-
- supports_block_expectations
-end
-
-RSpec::Matchers.define :log_messages do |expecteds|
- match notify_expectation_failures: true do |actual|
- with_memory_logger do |logger|
- actual.call
- expecteds.each_with_index do |it, idx|
- (expect logger).to have_message (it.merge index: idx)
- end if logger
- true
- end
- end
-
- supports_block_expectations
-end
-
-# define matcher to replace `.not_to log_message` until notify_expectation_failures is supported for negated match
-# see https://github.com/rspec/rspec-expectations/issues/1124
-RSpec::Matchers.define :not_log_message do |expected|
- match notify_expectation_failures: true do |actual|
- with_memory_logger expected.to_h[:using_log_level] do |logger|
- actual.call
- logger ? logger.empty? : true
- end
- end
-
- supports_block_expectations
-end
-
-RSpec::Matchers.define :visually_match do |reference_filename|
- reference_path = (Pathname.new reference_filename).absolute? ? reference_filename : (File.join __dir__, 'reference', reference_filename)
- match do |actual_path|
- warn %(#{RSpec.current_example.location} uses visual comparison but is not tagged with visual: true) unless RSpec.current_example.metadata[:visual]
- return false unless File.exist? reference_path
- images_output_dir = output_file 'visual-comparison-workdir'
- Dir.mkdir images_output_dir unless Dir.exist? images_output_dir
- output_basename = File.join images_output_dir, (File.basename actual_path, '.pdf')
- pdftocairo_result = system 'pdftocairo', '-png', actual_path, %(#{output_basename}-actual)
- raise Errno::ENOENT, 'pdftocairo' if pdftocairo_result.nil?
- system 'pdftocairo', '-png', reference_path, %(#{output_basename}-reference)
-
- pixels = 0
- tmp_files = [actual_path]
-
- files = Dir[%(#{output_basename}-{actual,reference}-*.png)].map {|filename| (/-(?:actual|reference)-(\d+)\.png$/.match filename)[1] }.sort.uniq
- return false if files.empty?
- files.each do |idx|
- reference_page_filename = %(#{output_basename}-reference-#{idx}.png)
- reference_page_filename = nil unless File.exist? reference_page_filename
- tmp_files << reference_page_filename if reference_page_filename
- actual_page_filename = %(#{output_basename}-actual-#{idx}.png)
- actual_page_filename = nil unless File.exist? actual_page_filename
- tmp_files << actual_page_filename if actual_page_filename
- next if reference_page_filename && actual_page_filename && (FileUtils.compare_file reference_page_filename, actual_page_filename)
- pixels += compute_image_differences reference_page_filename, actual_page_filename, %(#{output_basename}-diff-#{idx}.png)
- end
-
- if pixels > 0
- false
- else
- tmp_files.each {|it| File.unlink it } unless ENV.key? 'DEBUG'
- true
- end
- end
-
- failure_message {|actual_path| %(expected #{actual_path} to be visually identical to #{reference_path}) }
-end
diff --git a/spec/spec_helper/matchers.rb b/spec/spec_helper/matchers.rb
new file mode 100644
index 00000000..3f7f6fb7
--- /dev/null
+++ b/spec/spec_helper/matchers.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require_relative 'matchers/annotate'
+require_relative 'matchers/have_background'
+require_relative 'matchers/have_message'
+require_relative 'matchers/have_size'
+require_relative 'matchers/log_message'
+require_relative 'matchers/log_messages'
+require_relative 'matchers/not_log_message'
+require_relative 'matchers/not_raise_exception'
+require_relative 'matchers/visually_match'
diff --git a/spec/spec_helper/matchers/annotate.rb b/spec/spec_helper/matchers/annotate.rb
new file mode 100644
index 00000000..a0877ab3
--- /dev/null
+++ b/spec/spec_helper/matchers/annotate.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define :annotate do |text|
+ match do |subject|
+ left, bottom, right, top = subject[:Rect]
+ left == text[:x] && ((text[:x] + text[:width]) - right).abs < 0.25 && bottom < text[:y] && top > (text[:y] + text[:font_size])
+ end
+ failure_message {|subject| %(expected #{subject} to annotate #{text}) }
+end
diff --git a/spec/spec_helper/matchers/have_background.rb b/spec/spec_helper/matchers/have_background.rb
new file mode 100644
index 00000000..60dbeaad
--- /dev/null
+++ b/spec/spec_helper/matchers/have_background.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define :have_background do |expected|
+ match do |actual|
+ color = ((expected[:color].scan %r/../).map {|it| ((it.to_i 16) / 255.0).round 5 }.join ' ') + ' scn'
+ # FIXME: shave off lines before this line
+ (expect actual).to include color
+ x1, y1 = expected[:top_left]
+ x2, y2 = expected[:bottom_right]
+ (expect actual).to include %(#{x2} #{y1} #{x2} #{y1} #{x2} #{y1} c)
+ (expect actual).to include %(#{x1} #{y2} #{x1} #{y2} #{x1} #{y2} c)
+ end
+ failure_message {|actual| %(expected #{actual} to have background #{expected}, but was \n#{actual.join ?\n}) }
+end
diff --git a/spec/spec_helper/matchers/have_message.rb b/spec/spec_helper/matchers/have_message.rb
new file mode 100644
index 00000000..4a3f2e72
--- /dev/null
+++ b/spec/spec_helper/matchers/have_message.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define :have_message do |expected|
+ actual = nil
+ match notify_expectation_failures: true do |logger|
+ result = false
+ messages = logger.messages
+ expected_index = expected[:index] || 0
+ if (message = messages[expected_index])
+ if message[:severity] == expected[:severity]
+ message_text = Hash === (message_data = message[:message]) ? message_data[:text] : message_data
+ if Regexp === (expected_message = expected[:message])
+ result = true if expected_message.match? message_text
+ elsif expected_message.start_with? '~'
+ result = true if message_text.include? expected_message[1..-1]
+ elsif message_text === expected_message
+ result = true
+ end
+ result = false if (file = expected[:file]) && !(Hash === message_data && file == message_data[:source_location].file)
+ result = false if (lineno = expected[:lineno]) && !(Hash === message_data && lineno == message_data[:source_location].lineno)
+ end
+ actual = message
+ end
+ (expect messages).to have_size expected_index + 1 if result && expected[:last]
+ result
+ end
+
+ failure_message do
+ %(expected #{expected[:severity]} message#{expected[:message].to_s.chr == '~' ? ' containing ' : ' matching '}`#{expected[:message]}' to have been logged) + (actual ? %(, but got #{actual[:severity]}: #{actual[:message]}) : '')
+ end
+end
diff --git a/spec/spec_helper/matchers/have_size.rb b/spec/spec_helper/matchers/have_size.rb
new file mode 100644
index 00000000..9b09d3ab
--- /dev/null
+++ b/spec/spec_helper/matchers/have_size.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define :have_size do |expected|
+ match {|actual| actual.size == expected }
+ failure_message {|actual| %(expected #{actual} to have size #{expected}, but was #{actual.size}) }
+end
diff --git a/spec/spec_helper/matchers/log_message.rb b/spec/spec_helper/matchers/log_message.rb
new file mode 100644
index 00000000..3b2b680d
--- /dev/null
+++ b/spec/spec_helper/matchers/log_message.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define :log_message do |expected|
+ match notify_expectation_failures: true do |actual|
+ if expected
+ log_level_override = expected.delete :using_log_level
+ expected = nil if expected.empty?
+ end
+ with_memory_logger log_level_override do |logger|
+ actual.call
+ if expected
+ (expect logger).to have_message expected
+ else
+ (expect logger).not_to be_empty
+ end
+ true
+ end
+ end
+
+ #match_when_negated notify_expectation_failures: true do |actual|
+ # with_memory_logger expected.to_h[:using_log_level] do |logger|
+ # actual.call
+ # logger ? logger.empty? : true
+ # end
+ #end
+
+ supports_block_expectations
+end
diff --git a/spec/spec_helper/matchers/log_messages.rb b/spec/spec_helper/matchers/log_messages.rb
new file mode 100644
index 00000000..8852e7be
--- /dev/null
+++ b/spec/spec_helper/matchers/log_messages.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define :log_messages do |expecteds|
+ match notify_expectation_failures: true do |actual|
+ with_memory_logger do |logger|
+ actual.call
+ expecteds.each_with_index do |it, idx|
+ (expect logger).to have_message (it.merge index: idx)
+ end if logger
+ true
+ end
+ end
+
+ supports_block_expectations
+end
diff --git a/spec/spec_helper/matchers/not_log_message.rb b/spec/spec_helper/matchers/not_log_message.rb
new file mode 100644
index 00000000..21715376
--- /dev/null
+++ b/spec/spec_helper/matchers/not_log_message.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+# define matcher to replace `.not_to log_message` until notify_expectation_failures is supported for negated match
+# see https://github.com/rspec/rspec-expectations/issues/1124
+RSpec::Matchers.define :not_log_message do |expected|
+ match notify_expectation_failures: true do |actual|
+ with_memory_logger expected.to_h[:using_log_level] do |logger|
+ actual.call
+ logger ? logger.empty? : true
+ end
+ end
+
+ supports_block_expectations
+end
diff --git a/spec/spec_helper/matchers/not_raise_exception.rb b/spec/spec_helper/matchers/not_raise_exception.rb
new file mode 100644
index 00000000..f1a044ce
--- /dev/null
+++ b/spec/spec_helper/matchers/not_raise_exception.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define_negated_matcher :not_raise_exception, :raise_exception
diff --git a/spec/spec_helper/matchers/visually_match.rb b/spec/spec_helper/matchers/visually_match.rb
new file mode 100644
index 00000000..947cf19a
--- /dev/null
+++ b/spec/spec_helper/matchers/visually_match.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define :visually_match do |reference_filename|
+ reference_path = (Pathname.new reference_filename).absolute? ? reference_filename : (File.join spec_dir, 'reference', reference_filename)
+ match do |actual_path|
+ warn %(#{RSpec.current_example.location} uses visual comparison but is not tagged with visual: true) unless RSpec.current_example.metadata[:visual]
+ return false unless File.exist? reference_path
+ images_output_dir = output_file 'visual-comparison-workdir'
+ Dir.mkdir images_output_dir unless Dir.exist? images_output_dir
+ output_basename = File.join images_output_dir, (File.basename actual_path, '.pdf')
+ pdftocairo_result = system 'pdftocairo', '-png', actual_path, %(#{output_basename}-actual)
+ raise Errno::ENOENT, 'pdftocairo' if pdftocairo_result.nil?
+ system 'pdftocairo', '-png', reference_path, %(#{output_basename}-reference)
+
+ pixels = 0
+ tmp_files = [actual_path]
+
+ files = Dir[%(#{output_basename}-{actual,reference}-*.png)].map {|filename| (/-(?:actual|reference)-(\d+)\.png$/.match filename)[1] }.sort.uniq
+ return false if files.empty?
+ files.each do |idx|
+ reference_page_filename = %(#{output_basename}-reference-#{idx}.png)
+ reference_page_filename = nil unless File.exist? reference_page_filename
+ tmp_files << reference_page_filename if reference_page_filename
+ actual_page_filename = %(#{output_basename}-actual-#{idx}.png)
+ actual_page_filename = nil unless File.exist? actual_page_filename
+ tmp_files << actual_page_filename if actual_page_filename
+ next if reference_page_filename && actual_page_filename && (FileUtils.compare_file reference_page_filename, actual_page_filename)
+ pixels += compute_image_differences reference_page_filename, actual_page_filename, %(#{output_basename}-diff-#{idx}.png)
+ end
+
+ if pixels > 0
+ false
+ else
+ tmp_files.each {|it| File.unlink it } unless ENV.key? 'DEBUG'
+ true
+ end
+ end
+
+ failure_message {|actual_path| %(expected #{actual_path} to be visually identical to #{reference_path}) }
+end