From 66ee50109e486b7a0c29d02853bc4ea8ea5f303d Mon Sep 17 00:00:00 2001 From: Alexander Jeurissen Date: Wed, 23 Dec 2020 23:38:20 +0100 Subject: [PATCH 1/8] add fingerprint support --- cuke_modeler.gemspec | 1 + lib/cuke_modeler.rb | 1 + lib/cuke_modeler/fingerprint.rb | 34 +++++++ lib/cuke_modeler/models/model.rb | 1 + .../fingerprint_integration_spec.rb | 96 +++++++++++++++++++ .../models/background_integration_spec.rb | 6 ++ .../models/cell_integration_spec.rb | 5 + .../models/comment_integration_spec.rb | 4 + .../models/directory_integration_spec.rb | 7 ++ .../models/doc_string_integration_spec.rb | 7 ++ .../models/example_integration_spec.rb | 8 +- .../models/feature_file_integration_spec.rb | 10 ++ .../models/feature_integration_spec.rb | 11 +++ .../models/outline_integration_spec.rb | 12 +++ .../models/row_integration_spec.rb | 8 ++ .../models/rule_integration_spec.rb | 12 +++ .../models/scenario_integration_spec.rb | 11 +++ .../models/step_integration_spec.rb | 7 ++ testing/rspec/spec/spec_helper.rb | 2 + .../rspec/spec/unit/fingerprint_unit_spec.rb | 12 +++ 20 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 lib/cuke_modeler/fingerprint.rb create mode 100644 testing/rspec/spec/integration/fingerprint_integration_spec.rb create mode 100644 testing/rspec/spec/unit/fingerprint_unit_spec.rb diff --git a/cuke_modeler.gemspec b/cuke_modeler.gemspec index 0fb7923..a1a9518 100644 --- a/cuke_modeler.gemspec +++ b/cuke_modeler.gemspec @@ -44,4 +44,5 @@ Gem::Specification.new do |spec| # Coveralls gem does not support any newer version than this spec.add_development_dependency 'simplecov', '<= 0.16.1' spec.add_development_dependency 'test-unit', '< 4.0.0' + spec.add_development_dependency 'pry', '~> 0.13.1' end diff --git a/lib/cuke_modeler.rb b/lib/cuke_modeler.rb index d6cbe50..7c2b963 100644 --- a/lib/cuke_modeler.rb +++ b/lib/cuke_modeler.rb @@ -15,6 +15,7 @@ module CukeModeler require 'cuke_modeler/named' require 'cuke_modeler/described' require 'cuke_modeler/stepped' +require 'cuke_modeler/fingerprint' require 'cuke_modeler/models/model' require 'cuke_modeler/models/feature_file' require 'cuke_modeler/models/directory' diff --git a/lib/cuke_modeler/fingerprint.rb b/lib/cuke_modeler/fingerprint.rb new file mode 100644 index 0000000..efc324d --- /dev/null +++ b/lib/cuke_modeler/fingerprint.rb @@ -0,0 +1,34 @@ +require 'digest/md5' + +module CukeModeler + + # NOT A PART OF THE PUBLIC API + # A mix-in module containing methods used by models that represent an element that has a name. + + module Fingerprint + # NOTE: create a fingerprint for the given model using Digest::MD5 + def fingerprint() + if children.empty? + # NOTE: yield the result value of the block as the argument to .hexdigest + return Digest::MD5.hexdigest(yield self) if block_given? + + # NOTE: no block given, .hexdigest the to_s of the model + return Digest::MD5.hexdigest(to_s) + end + + # NOTE: aggregate the fingerprints of all it's children + children_fingerprints = children.map do |child| + if block_given? + # NOTE: this child has children of its own traverse those + child.fingerprint { |nested_child| yield nested_child } + else + # NOTE: this child is a leaf node, return the fingerprint + child.fingerprint + end + end + + # NOTE: create a .hexdigest of the combined fingerprint of all children + Digest::MD5.hexdigest(children_fingerprints.join) + end + end +end diff --git a/lib/cuke_modeler/models/model.rb b/lib/cuke_modeler/models/model.rb index 20d306f..b1e76ec 100644 --- a/lib/cuke_modeler/models/model.rb +++ b/lib/cuke_modeler/models/model.rb @@ -6,6 +6,7 @@ class Model include Nested include Containing + include Fingerprint # Creates a new Model object and, if *source_text* is provided, diff --git a/testing/rspec/spec/integration/fingerprint_integration_spec.rb b/testing/rspec/spec/integration/fingerprint_integration_spec.rb new file mode 100644 index 0000000..ec3ca6e --- /dev/null +++ b/testing/rspec/spec/integration/fingerprint_integration_spec.rb @@ -0,0 +1,96 @@ +require "#{File.dirname(__FILE__)}/../spec_helper" + +describe 'Fingerprint, Integration' do + + describe 'unique behavior' do + + describe 'An object without children' do + + let(:model) { CukeModeler::Step.new } + before do + model.keyword = 'Given' + model.text = 'I am testing the fingerprint' + end + + describe 'getting a fingerprint without args' do + + it 'returns the Digest::MD5 of the to_s representation' do + expect(model.fingerprint).to eq(Digest::MD5.hexdigest(model.to_s)) + end + + end + + + describe 'getting a fingerprint with a block' do + + it 'returns the Digest::MD5 of the block return value' do + fingerprint = model.fingerprint do |m| + m.text + end + + expect(fingerprint).to eq(Digest::MD5.hexdigest(model.text)) + end + + end + end + + describe 'An object with children' do + + let(:model) { CukeModeler::Scenario.new } + before do + model.name = 'Testing the fingerprint' + + model.steps = 3.times.map do |i| + step = CukeModeler::Step.new + step.keyword = 'When' + step.text = "Step #{i}" + step + end + + model.tags = 2.times.map do |i| + tag = CukeModeler::Tag.new + tag.name = "Tag #{i}" + tag + end + end + + describe 'getting a fingerprint without args' do + + it 'returns the Digest::MD5 of the to_s representation of its children' do + children_fingerprints = model.children.map(&:fingerprint) + + expect(children_fingerprints.compact.count) + .to eq(children_fingerprints.count) + + expect(model.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) + end + + end + + + describe 'getting a fingerprint with a block' do + + it 'returns the Digest::MD5 of the block return value' do + block = lambda do |m| + if m.respond_to?(:name) + m.name + elsif m.respond_to?(:text) + m.text + else + m.to_s + end + end + + children_values = [*model.steps.map(&:text), *model.tags.map(&:name)] + children_fingerprints = children_values.map { |v| Digest::MD5.hexdigest(v) } + + fingerprint = model.fingerprint(&block) + + expect(fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) + end + + end + end + end +end + diff --git a/testing/rspec/spec/integration/models/background_integration_spec.rb b/testing/rspec/spec/integration/models/background_integration_spec.rb index b6fcfb4..5b08691 100644 --- a/testing/rspec/spec/integration/models/background_integration_spec.rb +++ b/testing/rspec/spec/integration/models/background_integration_spec.rb @@ -246,6 +246,12 @@ expect(step_names).to eq(['a step', 'another step']) end + it "models the background's fingerprint" do + children_fingerprints = background.children.map(&:fingerprint) + + expect(children_fingerprints.compact).to match_array children_fingerprints + expect(background.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) + end end context 'an empty background' do diff --git a/testing/rspec/spec/integration/models/cell_integration_spec.rb b/testing/rspec/spec/integration/models/cell_integration_spec.rb index 698b0f6..a2d4955 100644 --- a/testing/rspec/spec/integration/models/cell_integration_spec.rb +++ b/testing/rspec/spec/integration/models/cell_integration_spec.rb @@ -268,6 +268,11 @@ expect(cell.source_line).to eq(5) end + + it "models the cell's fingerprint" do + expect(cell.fingerprint).to eq(Digest::MD5.hexdigest(cell.to_s)) + end + end end diff --git a/testing/rspec/spec/integration/models/comment_integration_spec.rb b/testing/rspec/spec/integration/models/comment_integration_spec.rb index 6cbf0d4..7837f20 100644 --- a/testing/rspec/spec/integration/models/comment_integration_spec.rb +++ b/testing/rspec/spec/integration/models/comment_integration_spec.rb @@ -138,6 +138,10 @@ expect(comment.source_line).to eq(1) end + it "models the comment's fingerprint" do + expect(comment.fingerprint).to eq(Digest::MD5.hexdigest(comment.to_s)) + end + it 'removes surrounding whitespace' do comment = clazz.new(' # a comment ') diff --git a/testing/rspec/spec/integration/models/directory_integration_spec.rb b/testing/rspec/spec/integration/models/directory_integration_spec.rb index 182f61e..925c3c0 100644 --- a/testing/rspec/spec/integration/models/directory_integration_spec.rb +++ b/testing/rspec/spec/integration/models/directory_integration_spec.rb @@ -67,6 +67,13 @@ expect(modeled_files).to match_array(feature_files) end + it "models the directory's fingerprint" do + children_fingerprints = directory_model.children.map(&:fingerprint) + + expect(children_fingerprints.compact).to match_array children_fingerprints + expect(directory_model.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) + end + it 'does not model non-feature files contained in the directory' do modeled_files = directory_model.feature_files.collect { |file| file.name[/test_file_\d/] } diff --git a/testing/rspec/spec/integration/models/doc_string_integration_spec.rb b/testing/rspec/spec/integration/models/doc_string_integration_spec.rb index a185ef1..3e379cd 100644 --- a/testing/rspec/spec/integration/models/doc_string_integration_spec.rb +++ b/testing/rspec/spec/integration/models/doc_string_integration_spec.rb @@ -255,6 +255,9 @@ expect(doc_string.content).to eq('bar') end + it "models the doc_string's fingerprint" do + expect(doc_string.fingerprint).to eq(Digest::MD5.hexdigest(doc_string.to_s)) + end end context 'an empty doc_string' do @@ -273,6 +276,10 @@ expect(doc_string.content).to eq('') end + it "models the doc_string's fingerprint" do + expect(doc_string.fingerprint).to eq(Digest::MD5.hexdigest(doc_string.to_s)) + end + end it "models the doc string's source line" do diff --git a/testing/rspec/spec/integration/models/example_integration_spec.rb b/testing/rspec/spec/integration/models/example_integration_spec.rb index 8c34f7a..7aa83d2 100644 --- a/testing/rspec/spec/integration/models/example_integration_spec.rb +++ b/testing/rspec/spec/integration/models/example_integration_spec.rb @@ -153,7 +153,6 @@ expect(example.source_line).to eq(5) end - context 'a filled example' do let(:source_text) { @@ -200,6 +199,13 @@ expect(example.parameters).to eq(['param']) end + it "models the example's fingerprint" do + children_fingerprints = example.children.map(&:fingerprint) + + expect(children_fingerprints.compact).to match_array children_fingerprints + expect(example.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) + end + end context 'an empty example' do diff --git a/testing/rspec/spec/integration/models/feature_file_integration_spec.rb b/testing/rspec/spec/integration/models/feature_file_integration_spec.rb index d26fdb6..78b5862 100644 --- a/testing/rspec/spec/integration/models/feature_file_integration_spec.rb +++ b/testing/rspec/spec/integration/models/feature_file_integration_spec.rb @@ -239,6 +239,12 @@ expect(comments).to match_array(expected_comments) end + it "models the feature_file's fingerprint" do + children_fingerprints = feature_file.children.map(&:fingerprint) + + expect(children_fingerprints.compact).to match_array children_fingerprints + expect(feature_file.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) + end context 'an empty feature file' do @@ -248,6 +254,10 @@ expect(feature_file.feature).to be_nil end + it "models the feature_file's fingerprint" do + expect(feature_file.fingerprint).to eq(Digest::MD5.hexdigest(feature_file.to_s)) + end + end end diff --git a/testing/rspec/spec/integration/models/feature_integration_spec.rb b/testing/rspec/spec/integration/models/feature_integration_spec.rb index 8ec470d..83e72c7 100644 --- a/testing/rspec/spec/integration/models/feature_integration_spec.rb +++ b/testing/rspec/spec/integration/models/feature_integration_spec.rb @@ -298,6 +298,13 @@ expect(tag_names).to eq(['@tag_1', '@tag_2']) end + it "models the feature's fingerprint" do + children_fingerprints = feature.children.map(&:fingerprint) + + expect(children_fingerprints.compact).to match_array children_fingerprints + expect(feature.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) + end + end @@ -335,6 +342,10 @@ expect(feature.tags).to eq([]) end + it "models the feature's fingerprint" do + expect(feature.fingerprint).to eq(Digest::MD5.hexdigest(feature.to_s)) + end + end end diff --git a/testing/rspec/spec/integration/models/outline_integration_spec.rb b/testing/rspec/spec/integration/models/outline_integration_spec.rb index ba5dd52..eee5a46 100644 --- a/testing/rspec/spec/integration/models/outline_integration_spec.rb +++ b/testing/rspec/spec/integration/models/outline_integration_spec.rb @@ -247,6 +247,14 @@ expect(example_names).to eq(['example 1', 'example 2']) end + + it "models the outline's fingerprint" do + children_fingerprints = outline.children.map(&:fingerprint) + + expect(children_fingerprints.compact).to match_array children_fingerprints + expect(outline.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) + end + end @@ -276,6 +284,10 @@ expect(outline.examples).to eq([]) end + it "models the outline's fingerprint" do + expect(outline.fingerprint).to eq(Digest::MD5.hexdigest(outline.to_s)) + end + end end diff --git a/testing/rspec/spec/integration/models/row_integration_spec.rb b/testing/rspec/spec/integration/models/row_integration_spec.rb index 2d14c8f..a8f9265 100644 --- a/testing/rspec/spec/integration/models/row_integration_spec.rb +++ b/testing/rspec/spec/integration/models/row_integration_spec.rb @@ -254,6 +254,14 @@ expect(row.source_line).to eq(6) end + + it "models the row's fingerprint" do + children_fingerprints = row.children.map(&:fingerprint) + + expect(children_fingerprints.compact).to match_array children_fingerprints + expect(row.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) + end + end end diff --git a/testing/rspec/spec/integration/models/rule_integration_spec.rb b/testing/rspec/spec/integration/models/rule_integration_spec.rb index e23d242..3fa7ef8 100644 --- a/testing/rspec/spec/integration/models/rule_integration_spec.rb +++ b/testing/rspec/spec/integration/models/rule_integration_spec.rb @@ -285,6 +285,14 @@ expect(outline_names).to eq(['Outline 1', 'Outline 2']) end + + it "models the rule's fingerprint" do + children_fingerprints = rule.children.map(&:fingerprint) + + expect(children_fingerprints.compact).to match_array children_fingerprints + expect(rule.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) + end + end @@ -314,6 +322,10 @@ expect(rule.outlines).to eq([]) end + it "models the rule's fingerprint" do + expect(rule.fingerprint).to eq(Digest::MD5.hexdigest(rule.to_s)) + end + end end diff --git a/testing/rspec/spec/integration/models/scenario_integration_spec.rb b/testing/rspec/spec/integration/models/scenario_integration_spec.rb index 222d85b..fefad3f 100644 --- a/testing/rspec/spec/integration/models/scenario_integration_spec.rb +++ b/testing/rspec/spec/integration/models/scenario_integration_spec.rb @@ -240,6 +240,13 @@ expect(tag_names).to eq(['@tag1', '@tag2', '@tag3']) end + it "models the scenario's fingerprint" do + children_fingerprints = scenario.children.map(&:fingerprint) + + expect(children_fingerprints.compact).to match_array children_fingerprints + expect(scenario.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) + end + end context 'an empty scenario' do @@ -264,6 +271,10 @@ expect(scenario.tags).to eq([]) end + it "models the scenario's fingerprint" do + expect(scenario.fingerprint).to eq(Digest::MD5.hexdigest(scenario.to_s)) + end + end end diff --git a/testing/rspec/spec/integration/models/step_integration_spec.rb b/testing/rspec/spec/integration/models/step_integration_spec.rb index 99f7c8a..cd0e66b 100644 --- a/testing/rspec/spec/integration/models/step_integration_spec.rb +++ b/testing/rspec/spec/integration/models/step_integration_spec.rb @@ -114,6 +114,10 @@ expect(step.text).to eq('a step') end + it "models the step's fingerprint" do + expect(step.fingerprint).to eq(Digest::MD5.hexdigest(step.to_s)) + end + it "models the step's source line" do source_text = "#{FEATURE_KEYWORD}: @@ -153,6 +157,9 @@ expect(table_cell_values).to eq([['value 1'], ['value 2']]) end + it "models the step's fingerprint" do + expect(step.fingerprint).to eq(Digest::MD5.hexdigest(step.to_s)) + end end context 'a step with a doc string' do diff --git a/testing/rspec/spec/spec_helper.rb b/testing/rspec/spec/spec_helper.rb index 97a2414..36f99e5 100644 --- a/testing/rspec/spec/spec_helper.rb +++ b/testing/rspec/spec/spec_helper.rb @@ -24,6 +24,8 @@ require_relative '../../helper_methods' require 'rubygems/mock_gem_ui' +require 'digest/md5' +require 'pry' # Use a random dialect for testing in order to avoid hard coded language assumptions in the diff --git a/testing/rspec/spec/unit/fingerprint_unit_spec.rb b/testing/rspec/spec/unit/fingerprint_unit_spec.rb new file mode 100644 index 0000000..c2afab6 --- /dev/null +++ b/testing/rspec/spec/unit/fingerprint_unit_spec.rb @@ -0,0 +1,12 @@ +require "#{File.dirname(__FILE__)}/../spec_helper" + +describe 'Fingerprint, Unit', unit_test: true do + + let(:nodule) { CukeModeler::Fingerprint } + let(:fingerprint_model) { Object.new.extend(nodule) } + + + it 'has a fingerprint' do + expect(model).to respond_to(:fingerprint) + end +end From 3d84d0aacfd9493fe1f8f04a7dfd55acee50fec8 Mon Sep 17 00:00:00 2001 From: Alexander Jeurissen Date: Thu, 24 Dec 2020 00:00:45 +0100 Subject: [PATCH 2/8] Add support for nil values --- lib/cuke_modeler/fingerprint.rb | 11 +++++++-- .../fingerprint_integration_spec.rb | 23 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/lib/cuke_modeler/fingerprint.rb b/lib/cuke_modeler/fingerprint.rb index efc324d..91a0ad9 100644 --- a/lib/cuke_modeler/fingerprint.rb +++ b/lib/cuke_modeler/fingerprint.rb @@ -10,7 +10,14 @@ module Fingerprint def fingerprint() if children.empty? # NOTE: yield the result value of the block as the argument to .hexdigest - return Digest::MD5.hexdigest(yield self) if block_given? + if block_given? + value = yield self + + # NOTE: The block returned nil, nothing to hexdigest here + return nil unless value.present? + + return Digest::MD5.hexdigest(value) + end # NOTE: no block given, .hexdigest the to_s of the model return Digest::MD5.hexdigest(to_s) @@ -25,7 +32,7 @@ def fingerprint() # NOTE: this child is a leaf node, return the fingerprint child.fingerprint end - end + end.compact # NOTE: create a .hexdigest of the combined fingerprint of all children Digest::MD5.hexdigest(children_fingerprints.join) diff --git a/testing/rspec/spec/integration/fingerprint_integration_spec.rb b/testing/rspec/spec/integration/fingerprint_integration_spec.rb index ec3ca6e..dc96ea4 100644 --- a/testing/rspec/spec/integration/fingerprint_integration_spec.rb +++ b/testing/rspec/spec/integration/fingerprint_integration_spec.rb @@ -90,7 +90,30 @@ end end + + describe 'getting a fingerprint with a block that excludes some children' do + + it 'returns the Digest::MD5 of the block return value' do + block = lambda do |m| + next unless m.is_a? CukeModeler::Step + + m.text + end + + step_values = model.steps.map(&:text) + step_fingerprints = step_values.map { |v| Digest::MD5.hexdigest(v) } + + fingerprint = model.fingerprint(&block) + + expect(fingerprint).to eq(Digest::MD5.hexdigest(step_fingerprints.join)) + end + + end + + end + end + end From b8c484f5f80ba70d79f51c08eb9c1835b265cece Mon Sep 17 00:00:00 2001 From: Alexander Jeurissen Date: Thu, 24 Dec 2020 00:58:26 +0100 Subject: [PATCH 3/8] remove unneeded model specs --- .../models/background_integration_spec.rb | 6 ------ .../spec/integration/models/cell_integration_spec.rb | 5 ----- .../integration/models/comment_integration_spec.rb | 4 ---- .../integration/models/directory_integration_spec.rb | 7 ------- .../models/doc_string_integration_spec.rb | 7 ------- .../integration/models/example_integration_spec.rb | 8 +------- .../models/feature_file_integration_spec.rb | 10 ---------- .../integration/models/feature_integration_spec.rb | 11 ----------- .../integration/models/outline_integration_spec.rb | 12 ------------ .../spec/integration/models/row_integration_spec.rb | 8 -------- .../spec/integration/models/rule_integration_spec.rb | 12 ------------ .../integration/models/scenario_integration_spec.rb | 11 ----------- .../spec/integration/models/step_integration_spec.rb | 7 ------- 13 files changed, 1 insertion(+), 107 deletions(-) diff --git a/testing/rspec/spec/integration/models/background_integration_spec.rb b/testing/rspec/spec/integration/models/background_integration_spec.rb index 5b08691..b6fcfb4 100644 --- a/testing/rspec/spec/integration/models/background_integration_spec.rb +++ b/testing/rspec/spec/integration/models/background_integration_spec.rb @@ -246,12 +246,6 @@ expect(step_names).to eq(['a step', 'another step']) end - it "models the background's fingerprint" do - children_fingerprints = background.children.map(&:fingerprint) - - expect(children_fingerprints.compact).to match_array children_fingerprints - expect(background.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) - end end context 'an empty background' do diff --git a/testing/rspec/spec/integration/models/cell_integration_spec.rb b/testing/rspec/spec/integration/models/cell_integration_spec.rb index a2d4955..698b0f6 100644 --- a/testing/rspec/spec/integration/models/cell_integration_spec.rb +++ b/testing/rspec/spec/integration/models/cell_integration_spec.rb @@ -268,11 +268,6 @@ expect(cell.source_line).to eq(5) end - - it "models the cell's fingerprint" do - expect(cell.fingerprint).to eq(Digest::MD5.hexdigest(cell.to_s)) - end - end end diff --git a/testing/rspec/spec/integration/models/comment_integration_spec.rb b/testing/rspec/spec/integration/models/comment_integration_spec.rb index 7837f20..6cbf0d4 100644 --- a/testing/rspec/spec/integration/models/comment_integration_spec.rb +++ b/testing/rspec/spec/integration/models/comment_integration_spec.rb @@ -138,10 +138,6 @@ expect(comment.source_line).to eq(1) end - it "models the comment's fingerprint" do - expect(comment.fingerprint).to eq(Digest::MD5.hexdigest(comment.to_s)) - end - it 'removes surrounding whitespace' do comment = clazz.new(' # a comment ') diff --git a/testing/rspec/spec/integration/models/directory_integration_spec.rb b/testing/rspec/spec/integration/models/directory_integration_spec.rb index 925c3c0..182f61e 100644 --- a/testing/rspec/spec/integration/models/directory_integration_spec.rb +++ b/testing/rspec/spec/integration/models/directory_integration_spec.rb @@ -67,13 +67,6 @@ expect(modeled_files).to match_array(feature_files) end - it "models the directory's fingerprint" do - children_fingerprints = directory_model.children.map(&:fingerprint) - - expect(children_fingerprints.compact).to match_array children_fingerprints - expect(directory_model.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) - end - it 'does not model non-feature files contained in the directory' do modeled_files = directory_model.feature_files.collect { |file| file.name[/test_file_\d/] } diff --git a/testing/rspec/spec/integration/models/doc_string_integration_spec.rb b/testing/rspec/spec/integration/models/doc_string_integration_spec.rb index 3e379cd..a185ef1 100644 --- a/testing/rspec/spec/integration/models/doc_string_integration_spec.rb +++ b/testing/rspec/spec/integration/models/doc_string_integration_spec.rb @@ -255,9 +255,6 @@ expect(doc_string.content).to eq('bar') end - it "models the doc_string's fingerprint" do - expect(doc_string.fingerprint).to eq(Digest::MD5.hexdigest(doc_string.to_s)) - end end context 'an empty doc_string' do @@ -276,10 +273,6 @@ expect(doc_string.content).to eq('') end - it "models the doc_string's fingerprint" do - expect(doc_string.fingerprint).to eq(Digest::MD5.hexdigest(doc_string.to_s)) - end - end it "models the doc string's source line" do diff --git a/testing/rspec/spec/integration/models/example_integration_spec.rb b/testing/rspec/spec/integration/models/example_integration_spec.rb index 7aa83d2..8c34f7a 100644 --- a/testing/rspec/spec/integration/models/example_integration_spec.rb +++ b/testing/rspec/spec/integration/models/example_integration_spec.rb @@ -153,6 +153,7 @@ expect(example.source_line).to eq(5) end + context 'a filled example' do let(:source_text) { @@ -199,13 +200,6 @@ expect(example.parameters).to eq(['param']) end - it "models the example's fingerprint" do - children_fingerprints = example.children.map(&:fingerprint) - - expect(children_fingerprints.compact).to match_array children_fingerprints - expect(example.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) - end - end context 'an empty example' do diff --git a/testing/rspec/spec/integration/models/feature_file_integration_spec.rb b/testing/rspec/spec/integration/models/feature_file_integration_spec.rb index 78b5862..d26fdb6 100644 --- a/testing/rspec/spec/integration/models/feature_file_integration_spec.rb +++ b/testing/rspec/spec/integration/models/feature_file_integration_spec.rb @@ -239,12 +239,6 @@ expect(comments).to match_array(expected_comments) end - it "models the feature_file's fingerprint" do - children_fingerprints = feature_file.children.map(&:fingerprint) - - expect(children_fingerprints.compact).to match_array children_fingerprints - expect(feature_file.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) - end context 'an empty feature file' do @@ -254,10 +248,6 @@ expect(feature_file.feature).to be_nil end - it "models the feature_file's fingerprint" do - expect(feature_file.fingerprint).to eq(Digest::MD5.hexdigest(feature_file.to_s)) - end - end end diff --git a/testing/rspec/spec/integration/models/feature_integration_spec.rb b/testing/rspec/spec/integration/models/feature_integration_spec.rb index 83e72c7..8ec470d 100644 --- a/testing/rspec/spec/integration/models/feature_integration_spec.rb +++ b/testing/rspec/spec/integration/models/feature_integration_spec.rb @@ -298,13 +298,6 @@ expect(tag_names).to eq(['@tag_1', '@tag_2']) end - it "models the feature's fingerprint" do - children_fingerprints = feature.children.map(&:fingerprint) - - expect(children_fingerprints.compact).to match_array children_fingerprints - expect(feature.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) - end - end @@ -342,10 +335,6 @@ expect(feature.tags).to eq([]) end - it "models the feature's fingerprint" do - expect(feature.fingerprint).to eq(Digest::MD5.hexdigest(feature.to_s)) - end - end end diff --git a/testing/rspec/spec/integration/models/outline_integration_spec.rb b/testing/rspec/spec/integration/models/outline_integration_spec.rb index eee5a46..ba5dd52 100644 --- a/testing/rspec/spec/integration/models/outline_integration_spec.rb +++ b/testing/rspec/spec/integration/models/outline_integration_spec.rb @@ -247,14 +247,6 @@ expect(example_names).to eq(['example 1', 'example 2']) end - - it "models the outline's fingerprint" do - children_fingerprints = outline.children.map(&:fingerprint) - - expect(children_fingerprints.compact).to match_array children_fingerprints - expect(outline.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) - end - end @@ -284,10 +276,6 @@ expect(outline.examples).to eq([]) end - it "models the outline's fingerprint" do - expect(outline.fingerprint).to eq(Digest::MD5.hexdigest(outline.to_s)) - end - end end diff --git a/testing/rspec/spec/integration/models/row_integration_spec.rb b/testing/rspec/spec/integration/models/row_integration_spec.rb index a8f9265..2d14c8f 100644 --- a/testing/rspec/spec/integration/models/row_integration_spec.rb +++ b/testing/rspec/spec/integration/models/row_integration_spec.rb @@ -254,14 +254,6 @@ expect(row.source_line).to eq(6) end - - it "models the row's fingerprint" do - children_fingerprints = row.children.map(&:fingerprint) - - expect(children_fingerprints.compact).to match_array children_fingerprints - expect(row.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) - end - end end diff --git a/testing/rspec/spec/integration/models/rule_integration_spec.rb b/testing/rspec/spec/integration/models/rule_integration_spec.rb index 3fa7ef8..e23d242 100644 --- a/testing/rspec/spec/integration/models/rule_integration_spec.rb +++ b/testing/rspec/spec/integration/models/rule_integration_spec.rb @@ -285,14 +285,6 @@ expect(outline_names).to eq(['Outline 1', 'Outline 2']) end - - it "models the rule's fingerprint" do - children_fingerprints = rule.children.map(&:fingerprint) - - expect(children_fingerprints.compact).to match_array children_fingerprints - expect(rule.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) - end - end @@ -322,10 +314,6 @@ expect(rule.outlines).to eq([]) end - it "models the rule's fingerprint" do - expect(rule.fingerprint).to eq(Digest::MD5.hexdigest(rule.to_s)) - end - end end diff --git a/testing/rspec/spec/integration/models/scenario_integration_spec.rb b/testing/rspec/spec/integration/models/scenario_integration_spec.rb index fefad3f..222d85b 100644 --- a/testing/rspec/spec/integration/models/scenario_integration_spec.rb +++ b/testing/rspec/spec/integration/models/scenario_integration_spec.rb @@ -240,13 +240,6 @@ expect(tag_names).to eq(['@tag1', '@tag2', '@tag3']) end - it "models the scenario's fingerprint" do - children_fingerprints = scenario.children.map(&:fingerprint) - - expect(children_fingerprints.compact).to match_array children_fingerprints - expect(scenario.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) - end - end context 'an empty scenario' do @@ -271,10 +264,6 @@ expect(scenario.tags).to eq([]) end - it "models the scenario's fingerprint" do - expect(scenario.fingerprint).to eq(Digest::MD5.hexdigest(scenario.to_s)) - end - end end diff --git a/testing/rspec/spec/integration/models/step_integration_spec.rb b/testing/rspec/spec/integration/models/step_integration_spec.rb index cd0e66b..99f7c8a 100644 --- a/testing/rspec/spec/integration/models/step_integration_spec.rb +++ b/testing/rspec/spec/integration/models/step_integration_spec.rb @@ -114,10 +114,6 @@ expect(step.text).to eq('a step') end - it "models the step's fingerprint" do - expect(step.fingerprint).to eq(Digest::MD5.hexdigest(step.to_s)) - end - it "models the step's source line" do source_text = "#{FEATURE_KEYWORD}: @@ -157,9 +153,6 @@ expect(table_cell_values).to eq([['value 1'], ['value 2']]) end - it "models the step's fingerprint" do - expect(step.fingerprint).to eq(Digest::MD5.hexdigest(step.to_s)) - end end context 'a step with a doc string' do From 177abd1ebf27266c824fb555a39abd73663509b4 Mon Sep 17 00:00:00 2001 From: Alexander Jeurissen Date: Thu, 24 Dec 2020 00:58:58 +0100 Subject: [PATCH 4/8] split up methods fingerprint + fingerprint_children --- lib/cuke_modeler/fingerprint.rb | 40 ++++--- .../fingerprint_integration_spec.rb | 101 +++++++++++++++++- 2 files changed, 119 insertions(+), 22 deletions(-) diff --git a/lib/cuke_modeler/fingerprint.rb b/lib/cuke_modeler/fingerprint.rb index 91a0ad9..23e406a 100644 --- a/lib/cuke_modeler/fingerprint.rb +++ b/lib/cuke_modeler/fingerprint.rb @@ -7,35 +7,41 @@ module CukeModeler module Fingerprint # NOTE: create a fingerprint for the given model using Digest::MD5 - def fingerprint() - if children.empty? - # NOTE: yield the result value of the block as the argument to .hexdigest - if block_given? - value = yield self + def fingerprint + # NOTE: yield the result value of the block as the argument to .hexdigest + if block_given? + value = yield self - # NOTE: The block returned nil, nothing to hexdigest here - return nil unless value.present? + # NOTE: The block returned nil, nothing to hexdigest here + return nil unless value.present? - return Digest::MD5.hexdigest(value) - end + return Digest::MD5.hexdigest(value) + end + + # NOTE: no block given, .hexdigest the to_s of the model + return Digest::MD5.hexdigest(to_s) + end + + # NOTE: create a fingerprint for the given model's children using Digest::MD5 + def fingerprint_children(depth = 0) + if children.empty? + fail 'dont invoke fingerprint_children on a model without children' if depth == 0 - # NOTE: no block given, .hexdigest the to_s of the model - return Digest::MD5.hexdigest(to_s) + return fingerprint { |m| yield m } if block_given? + return fingerprint end # NOTE: aggregate the fingerprints of all it's children - children_fingerprints = children.map do |child| + fingerprints = children.map do |child| if block_given? - # NOTE: this child has children of its own traverse those - child.fingerprint { |nested_child| yield nested_child } + child.fingerprint_children(depth+1) { |c| yield c } else - # NOTE: this child is a leaf node, return the fingerprint - child.fingerprint + child.fingerprint_children(depth+1) end end.compact # NOTE: create a .hexdigest of the combined fingerprint of all children - Digest::MD5.hexdigest(children_fingerprints.join) + Digest::MD5.hexdigest(fingerprints.join) end end end diff --git a/testing/rspec/spec/integration/fingerprint_integration_spec.rb b/testing/rspec/spec/integration/fingerprint_integration_spec.rb index dc96ea4..dece255 100644 --- a/testing/rspec/spec/integration/fingerprint_integration_spec.rb +++ b/testing/rspec/spec/integration/fingerprint_integration_spec.rb @@ -2,8 +2,7 @@ describe 'Fingerprint, Integration' do - describe 'unique behavior' do - + describe '.fingerprint' do describe 'An object without children' do let(:model) { CukeModeler::Step.new } @@ -37,6 +36,71 @@ describe 'An object with children' do let(:model) { CukeModeler::Scenario.new } + + before do + model.name = 'Testing the fingerprint' + + model.steps = 3.times.map do |i| + step = CukeModeler::Step.new + step.keyword = 'When' + step.text = "Step #{i}" + step + end + + model.tags = 2.times.map do |i| + tag = CukeModeler::Tag.new + tag.name = "Tag #{i}" + tag + end + end + + describe 'getting a fingerprint without args' do + + it 'returns the Digest::MD5 of the to_s representation' do + expect(model.fingerprint).to eq(Digest::MD5.hexdigest(model.to_s)) + end + + end + + + describe 'getting a fingerprint with a block' do + + it 'returns the Digest::MD5 of the block return value' do + fingerprint = model.fingerprint do |m| + m.name + end + + expect(fingerprint).to eq(Digest::MD5.hexdigest(model.name)) + end + + end + + end + + end + + describe '.fingerprint_children' do + + describe 'An object without children' do + + let(:model) { CukeModeler::Step.new } + before do + model.keyword = 'Given' + model.text = 'I am testing the fingerprint' + end + + it 'throws an error' do + expect { model.fingerprint_children }.to raise_error( + 'dont invoke fingerprint_children on a model without children' + ) + end + + end + + describe 'An object with children' do + + let(:model) { CukeModeler::Scenario.new } + before do model.name = 'Testing the fingerprint' @@ -62,7 +126,7 @@ expect(children_fingerprints.compact.count) .to eq(children_fingerprints.count) - expect(model.fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) + expect(model.fingerprint_children).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) end end @@ -84,7 +148,7 @@ children_values = [*model.steps.map(&:text), *model.tags.map(&:name)] children_fingerprints = children_values.map { |v| Digest::MD5.hexdigest(v) } - fingerprint = model.fingerprint(&block) + fingerprint = model.fingerprint_children(&block) expect(fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) end @@ -103,17 +167,44 @@ step_values = model.steps.map(&:text) step_fingerprints = step_values.map { |v| Digest::MD5.hexdigest(v) } - fingerprint = model.fingerprint(&block) + fingerprint = model.fingerprint_children(&block) expect(fingerprint).to eq(Digest::MD5.hexdigest(step_fingerprints.join)) end end + end + + end + + describe '.fingerprint differs from fingerprint_children' do + let(:model) { CukeModeler::Scenario.new } + + before do + model.name = 'Testing the fingerprint' + + model.steps = 3.times.map do |i| + step = CukeModeler::Step.new + step.keyword = 'When' + step.text = "Step #{i}" + step + end + + model.tags = 2.times.map do |i| + tag = CukeModeler::Tag.new + tag.name = "Tag #{i}" + tag + end + end + + it do + expect(model.fingerprint).not_to eq(model.fingerprint_children) end end + end From ef0386849931afe9dc85319f5f1003ff3db82a03 Mon Sep 17 00:00:00 2001 From: Alexander Jeurissen Date: Thu, 24 Dec 2020 01:26:30 +0100 Subject: [PATCH 5/8] remove fingerprint_children as it was too specific --- lib/cuke_modeler/fingerprint.rb | 25 +-- .../fingerprint_integration_spec.rb | 197 ++---------------- 2 files changed, 21 insertions(+), 201 deletions(-) diff --git a/lib/cuke_modeler/fingerprint.rb b/lib/cuke_modeler/fingerprint.rb index 23e406a..6b335de 100644 --- a/lib/cuke_modeler/fingerprint.rb +++ b/lib/cuke_modeler/fingerprint.rb @@ -6,13 +6,14 @@ module CukeModeler # A mix-in module containing methods used by models that represent an element that has a name. module Fingerprint + # NOTE: create a fingerprint for the given model using Digest::MD5 def fingerprint # NOTE: yield the result value of the block as the argument to .hexdigest if block_given? value = yield self - # NOTE: The block returned nil, nothing to hexdigest here + # NOTE: The block returned nil, nothing to digest here return nil unless value.present? return Digest::MD5.hexdigest(value) @@ -22,26 +23,6 @@ def fingerprint return Digest::MD5.hexdigest(to_s) end - # NOTE: create a fingerprint for the given model's children using Digest::MD5 - def fingerprint_children(depth = 0) - if children.empty? - fail 'dont invoke fingerprint_children on a model without children' if depth == 0 - - return fingerprint { |m| yield m } if block_given? - return fingerprint - end - - # NOTE: aggregate the fingerprints of all it's children - fingerprints = children.map do |child| - if block_given? - child.fingerprint_children(depth+1) { |c| yield c } - else - child.fingerprint_children(depth+1) - end - end.compact - - # NOTE: create a .hexdigest of the combined fingerprint of all children - Digest::MD5.hexdigest(fingerprints.join) - end end + end diff --git a/testing/rspec/spec/integration/fingerprint_integration_spec.rb b/testing/rspec/spec/integration/fingerprint_integration_spec.rb index dece255..7c5c830 100644 --- a/testing/rspec/spec/integration/fingerprint_integration_spec.rb +++ b/testing/rspec/spec/integration/fingerprint_integration_spec.rb @@ -3,208 +3,47 @@ describe 'Fingerprint, Integration' do describe '.fingerprint' do - describe 'An object without children' do - let(:model) { CukeModeler::Step.new } - before do - model.keyword = 'Given' - model.text = 'I am testing the fingerprint' - end - - describe 'getting a fingerprint without args' do - - it 'returns the Digest::MD5 of the to_s representation' do - expect(model.fingerprint).to eq(Digest::MD5.hexdigest(model.to_s)) - end - - end - - - describe 'getting a fingerprint with a block' do - - it 'returns the Digest::MD5 of the block return value' do - fingerprint = model.fingerprint do |m| - m.text - end - - expect(fingerprint).to eq(Digest::MD5.hexdigest(model.text)) - end + let(:model) { CukeModeler::Step.new } - end + before do + model.keyword = 'Given' + model.text = 'I am testing the fingerprint' end - describe 'An object with children' do - - let(:model) { CukeModeler::Scenario.new } - - before do - model.name = 'Testing the fingerprint' - - model.steps = 3.times.map do |i| - step = CukeModeler::Step.new - step.keyword = 'When' - step.text = "Step #{i}" - step - end - - model.tags = 2.times.map do |i| - tag = CukeModeler::Tag.new - tag.name = "Tag #{i}" - tag - end - end - - describe 'getting a fingerprint without args' do - - it 'returns the Digest::MD5 of the to_s representation' do - expect(model.fingerprint).to eq(Digest::MD5.hexdigest(model.to_s)) - end - - end - - - describe 'getting a fingerprint with a block' do - - it 'returns the Digest::MD5 of the block return value' do - fingerprint = model.fingerprint do |m| - m.name - end - - expect(fingerprint).to eq(Digest::MD5.hexdigest(model.name)) - end + describe 'getting a fingerprint without args' do + it 'returns the Digest::MD5 of the to_s representation' do + expect(model.fingerprint).to eq(Digest::MD5.hexdigest(model.to_s)) end end - end - - describe '.fingerprint_children' do - - describe 'An object without children' do - - let(:model) { CukeModeler::Step.new } - before do - model.keyword = 'Given' - model.text = 'I am testing the fingerprint' - end - - it 'throws an error' do - expect { model.fingerprint_children }.to raise_error( - 'dont invoke fingerprint_children on a model without children' - ) - end - - end + describe 'getting a fingerprint with a block' do - describe 'An object with children' do - - let(:model) { CukeModeler::Scenario.new } - - before do - model.name = 'Testing the fingerprint' - - model.steps = 3.times.map do |i| - step = CukeModeler::Step.new - step.keyword = 'When' - step.text = "Step #{i}" - step - end - - model.tags = 2.times.map do |i| - tag = CukeModeler::Tag.new - tag.name = "Tag #{i}" - tag - end - end - - describe 'getting a fingerprint without args' do - - it 'returns the Digest::MD5 of the to_s representation of its children' do - children_fingerprints = model.children.map(&:fingerprint) - - expect(children_fingerprints.compact.count) - .to eq(children_fingerprints.count) - - expect(model.fingerprint_children).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) - end - - end - - - describe 'getting a fingerprint with a block' do - - it 'returns the Digest::MD5 of the block return value' do - block = lambda do |m| - if m.respond_to?(:name) - m.name - elsif m.respond_to?(:text) - m.text - else - m.to_s - end - end - - children_values = [*model.steps.map(&:text), *model.tags.map(&:name)] - children_fingerprints = children_values.map { |v| Digest::MD5.hexdigest(v) } - - fingerprint = model.fingerprint_children(&block) - - expect(fingerprint).to eq(Digest::MD5.hexdigest(children_fingerprints.join)) - end - - end - - describe 'getting a fingerprint with a block that excludes some children' do - - it 'returns the Digest::MD5 of the block return value' do - block = lambda do |m| - next unless m.is_a? CukeModeler::Step - - m.text - end - - step_values = model.steps.map(&:text) - step_fingerprints = step_values.map { |v| Digest::MD5.hexdigest(v) } - - fingerprint = model.fingerprint_children(&block) - - expect(fingerprint).to eq(Digest::MD5.hexdigest(step_fingerprints.join)) + it 'returns the Digest::MD5 of the block return value' do + fingerprint = model.fingerprint do |m| + m.text end + expect(fingerprint).to eq(Digest::MD5.hexdigest(model.text)) end end - end - - describe '.fingerprint differs from fingerprint_children' do - - let(:model) { CukeModeler::Scenario.new } - - before do - model.name = 'Testing the fingerprint' + describe 'getting a fingerprint with a block that returns nil' do - model.steps = 3.times.map do |i| - step = CukeModeler::Step.new - step.keyword = 'When' - step.text = "Step #{i}" - step - end + it 'returns nil' do + fingerprint = model.fingerprint do |m| + nil + end - model.tags = 2.times.map do |i| - tag = CukeModeler::Tag.new - tag.name = "Tag #{i}" - tag + expect(fingerprint).to eq(nil) end - end - it do - expect(model.fingerprint).not_to eq(model.fingerprint_children) end end - end From b2fdbb6068221fb74ef7b197005296782aecabc4 Mon Sep 17 00:00:00 2001 From: Alexander Jeurissen Date: Thu, 24 Dec 2020 02:15:40 +0100 Subject: [PATCH 6/8] Fix specs --- testing/rspec/spec/unit/fingerprint_unit_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/rspec/spec/unit/fingerprint_unit_spec.rb b/testing/rspec/spec/unit/fingerprint_unit_spec.rb index c2afab6..76aee57 100644 --- a/testing/rspec/spec/unit/fingerprint_unit_spec.rb +++ b/testing/rspec/spec/unit/fingerprint_unit_spec.rb @@ -3,7 +3,7 @@ describe 'Fingerprint, Unit', unit_test: true do let(:nodule) { CukeModeler::Fingerprint } - let(:fingerprint_model) { Object.new.extend(nodule) } + let(:model) { Object.new.extend(nodule) } it 'has a fingerprint' do From 580cec018516cb968c84ba866ce814fdbeb9adde Mon Sep 17 00:00:00 2001 From: Alexander Jeurissen Date: Fri, 1 Jan 2021 16:39:30 +0100 Subject: [PATCH 7/8] memoize fingerprint, and add performance specs --- lib/cuke_modeler/fingerprint.rb | 21 ++++++----- .../fingerprint_integration_spec.rb | 37 +++++++++++++++++++ 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/lib/cuke_modeler/fingerprint.rb b/lib/cuke_modeler/fingerprint.rb index 6b335de..55f1af2 100644 --- a/lib/cuke_modeler/fingerprint.rb +++ b/lib/cuke_modeler/fingerprint.rb @@ -9,18 +9,21 @@ module Fingerprint # NOTE: create a fingerprint for the given model using Digest::MD5 def fingerprint - # NOTE: yield the result value of the block as the argument to .hexdigest - if block_given? - value = yield self + # NOTE: memoize fingerprint for improved performance + @fingerprint ||= begin + # NOTE: yield the result value of the block as the argument to .hexdigest + if block_given? + value = yield self - # NOTE: The block returned nil, nothing to digest here - return nil unless value.present? + # NOTE: The block returned nil, nothing to digest here + return nil unless value.present? - return Digest::MD5.hexdigest(value) - end + return Digest::MD5.hexdigest(value) + end - # NOTE: no block given, .hexdigest the to_s of the model - return Digest::MD5.hexdigest(to_s) + # NOTE: no block given, .hexdigest the to_s of the model + Digest::MD5.hexdigest(to_s) + end end end diff --git a/testing/rspec/spec/integration/fingerprint_integration_spec.rb b/testing/rspec/spec/integration/fingerprint_integration_spec.rb index 7c5c830..b36d1e8 100644 --- a/testing/rspec/spec/integration/fingerprint_integration_spec.rb +++ b/testing/rspec/spec/integration/fingerprint_integration_spec.rb @@ -1,4 +1,5 @@ require "#{File.dirname(__FILE__)}/../spec_helper" +require 'benchmark' describe 'Fingerprint, Integration' do @@ -43,6 +44,42 @@ end + describe 'comparing fingerprints' do + + let(:maximum_viable_gherkin) do + "@a_tag + #{SCENARIO_KEYWORD}: test scenario + + Scenario + description + + #{STEP_KEYWORD} a step + | value1 | + | value2 | + #{STEP_KEYWORD} another step + \"\"\" with content type + some text + \"\"\"" + end + + let(:model_one) { CukeModeler::Scenario.new(maximum_viable_gherkin) } + let(:model_two) { CukeModeler::Scenario.new(maximum_viable_gherkin) } + let(:model_three) { CukeModeler::Scenario.new } + + it 'should result in the same fingerprint' do + expect(model_one.fingerprint).to eq(model_two.fingerprint) + expect(model_one.fingerprint).not_to eq(model_three.fingerprint) + end + + it 'is more performant than ==' do + equals_time = Benchmark.realtime { model_one == model_two } + fingerprint_time = Benchmark.realtime { model_one.fingerprint == model_two.fingerprint } + + expect(equals_time > fingerprint_time).to eq(true) + end + + end + end end From c4a6a138c60e841785f678e39d477c59c50d9932 Mon Sep 17 00:00:00 2001 From: Alexander Jeurissen Date: Fri, 1 Jan 2021 16:57:11 +0100 Subject: [PATCH 8/8] recalculate when a block is given --- lib/cuke_modeler/fingerprint.rb | 20 ++++++++++--------- .../fingerprint_integration_spec.rb | 10 +++++++++- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/cuke_modeler/fingerprint.rb b/lib/cuke_modeler/fingerprint.rb index 55f1af2..01de729 100644 --- a/lib/cuke_modeler/fingerprint.rb +++ b/lib/cuke_modeler/fingerprint.rb @@ -9,18 +9,20 @@ module Fingerprint # NOTE: create a fingerprint for the given model using Digest::MD5 def fingerprint - # NOTE: memoize fingerprint for improved performance - @fingerprint ||= begin - # NOTE: yield the result value of the block as the argument to .hexdigest - if block_given? - value = yield self + # NOTE: yield the result value of the block as the argument to .hexdigest + if block_given? + value = yield self + + # NOTE: The block returned nil, nothing to digest here + return nil unless value.present? - # NOTE: The block returned nil, nothing to digest here - return nil unless value.present? + @fingerprint = Digest::MD5.hexdigest(value) - return Digest::MD5.hexdigest(value) - end + return @fingerprint + end + # NOTE: memoize fingerprint for improved performance + @fingerprint ||= begin # NOTE: no block given, .hexdigest the to_s of the model Digest::MD5.hexdigest(to_s) end diff --git a/testing/rspec/spec/integration/fingerprint_integration_spec.rb b/testing/rspec/spec/integration/fingerprint_integration_spec.rb index b36d1e8..6655ab6 100644 --- a/testing/rspec/spec/integration/fingerprint_integration_spec.rb +++ b/testing/rspec/spec/integration/fingerprint_integration_spec.rb @@ -66,11 +66,19 @@ let(:model_two) { CukeModeler::Scenario.new(maximum_viable_gherkin) } let(:model_three) { CukeModeler::Scenario.new } - it 'should result in the same fingerprint' do + it 'should result in the same fingerprint when classes exactly the same' do expect(model_one.fingerprint).to eq(model_two.fingerprint) expect(model_one.fingerprint).not_to eq(model_three.fingerprint) end + it 'should recalculate the fingerprint when using a block' do + model_one_fingerprint = model_one.fingerprint + model_one_block_fingerprint = model_one.fingerprint { |m| m.name } + + expect(model_one_fingerprint).not_to eq(model_one_block_fingerprint) + expect(model_one.fingerprint).to eq(model_one_block_fingerprint) + end + it 'is more performant than ==' do equals_time = Benchmark.realtime { model_one == model_two } fingerprint_time = Benchmark.realtime { model_one.fingerprint == model_two.fingerprint }