From 1c040aa351dd183b514e371939a381b5105543fe Mon Sep 17 00:00:00 2001 From: Bernard Lambeau Date: Sun, 21 Mar 2021 10:28:20 +0100 Subject: [PATCH] Add Predicate#assert!, with the help of minitest. --- CHANGELOG.md | 5 +++ lib/predicate.rb | 5 +++ lib/predicate/asserter.rb | 12 ++++++ lib/predicate/nodes/eq.rb | 6 +++ lib/predicate/nodes/expr.rb | 4 ++ lib/predicate/nodes/neq.rb | 6 +++ spec/predicate/test_assert!.rb | 67 ++++++++++++++++++++++++++++++++++ 7 files changed, 105 insertions(+) create mode 100644 lib/predicate/asserter.rb create mode 100644 spec/predicate/test_assert!.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 20d000f..7599b59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ * BREAKING: removed support for ruby < 2.7 and upgraded sexpr to 1.1.x +* Added Predicate#assert!, with the help of minitest. + + Implementation is fuly functional, yet error messages will + be improved in the future by a better usage of minitest itself. + # 2.7.1 - 2022-04-21 * Add shadow support for Exists tree nodes, that are used by diff --git a/lib/predicate.rb b/lib/predicate.rb index 8a7b54a..0436bdd 100644 --- a/lib/predicate.rb +++ b/lib/predicate.rb @@ -6,6 +6,7 @@ require_relative 'predicate/grammar' require_relative 'predicate/processors' require_relative 'predicate/dsl' +require_relative 'predicate/asserter' class Predicate class Error < StandardError; end @@ -125,6 +126,10 @@ def call(tuple) expr.evaluate(tuple) end + def assert!(tuple = {}) + expr.assert!(tuple) + end + # Splits this predicate, say P, as too predicates P1 & P2 # such that `P <=> P1 & P2` and P2 makes no reference to # any attribute in `attr_list`. diff --git a/lib/predicate/asserter.rb b/lib/predicate/asserter.rb new file mode 100644 index 0000000..65ffbdd --- /dev/null +++ b/lib/predicate/asserter.rb @@ -0,0 +1,12 @@ +require 'minitest' +class Predicate + class Asserter + include Minitest::Assertions + + def initialize + @assertions = 0 + end + attr_accessor :assertions + + end # class Asserter +end # class Predicate \ No newline at end of file diff --git a/lib/predicate/nodes/eq.rb b/lib/predicate/nodes/eq.rb index cf9a13f..e9209c5 100644 --- a/lib/predicate/nodes/eq.rb +++ b/lib/predicate/nodes/eq.rb @@ -46,6 +46,12 @@ def evaluate(tuple) left.evaluate(tuple) == right.evaluate(tuple) end + def assert!(tuple, asserter = Asserter.new) + l, r = left.evaluate(tuple), right.evaluate(tuple) + asserter.assert_equal(l, r) + l + end + def to_hash if left.identifier? && right.literal? && !right.has_placeholder? { left.name => right.value } diff --git a/lib/predicate/nodes/expr.rb b/lib/predicate/nodes/expr.rb index 1d24d24..c84ed0e 100644 --- a/lib/predicate/nodes/expr.rb +++ b/lib/predicate/nodes/expr.rb @@ -50,6 +50,10 @@ def |(other) sexpr([:or, self, other]) end + def assert!(tuple, asserter = Asserter.new) + asserter.assert(evaluate(tuple)) + end + def and_split(attr_list) # If we have no reference to attr_list, then we are P2, else we are P1 (free_variables & attr_list).empty? ? [ tautology, self ] : [ self, tautology ] diff --git a/lib/predicate/nodes/neq.rb b/lib/predicate/nodes/neq.rb index 9987f50..54b833d 100644 --- a/lib/predicate/nodes/neq.rb +++ b/lib/predicate/nodes/neq.rb @@ -10,5 +10,11 @@ def evaluate(tuple) left.evaluate(tuple) != right.evaluate(tuple) end + def assert!(tuple, asserter = Asserter.new) + l, r = left.evaluate(tuple), right.evaluate(tuple) + asserter.refute_equal(l, r) + l + end + end end diff --git a/spec/predicate/test_assert!.rb b/spec/predicate/test_assert!.rb new file mode 100644 index 0000000..854df93 --- /dev/null +++ b/spec/predicate/test_assert!.rb @@ -0,0 +1,67 @@ +require 'spec_helper' +class Predicate + describe Predicate, "assert!" do + + describe "eq" do + let(:predicate) { + Predicate.eq(:x, 2) + } + + subject{ + predicate.assert!(input) + } + + context "when ok" do + let(:input){ + { x: 2 } + } + it 'works and returns 2' do + expect(subject).to eql(2) + end + end + + context "when ko" do + let(:input){ + { x: 3 } + } + it 'works and throws' do + expect{ + subject + }.to raise_error(Minitest::Assertion, /Expected: 3\s+Actual: 2/) + end + end + end + + describe "neq" do + let(:predicate) { + Predicate.neq(:x, 2) + } + + subject{ + predicate.assert!(input) + } + + context "when ok" do + let(:input){ + { x: 3 } + } + it 'works and returns 3' do + expect(subject).to eql(3) + end + end + + context "when ko" do + let(:input){ + { x: 2 } + } + it 'works and throws' do + expect{ + subject + }.to raise_error(Minitest::Assertion, /Expected 2 to not be equal to 2./) + end + end + end + + end +end +