linkage 0.0.6 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/.gitignore +10 -0
  2. data/Gemfile +15 -13
  3. data/Gemfile.lock +67 -37
  4. data/Guardfile +0 -2
  5. data/Rakefile +122 -25
  6. data/lib/linkage/comparator.rb +172 -0
  7. data/lib/linkage/comparators/binary.rb +12 -0
  8. data/lib/linkage/comparators/compare.rb +46 -0
  9. data/lib/linkage/comparators/within.rb +32 -0
  10. data/lib/linkage/configuration.rb +285 -153
  11. data/lib/linkage/data.rb +32 -7
  12. data/lib/linkage/dataset.rb +107 -32
  13. data/lib/linkage/decollation.rb +93 -0
  14. data/lib/linkage/expectation.rb +21 -0
  15. data/lib/linkage/expectations/exhaustive.rb +63 -0
  16. data/lib/linkage/expectations/simple.rb +168 -0
  17. data/lib/linkage/field.rb +30 -4
  18. data/lib/linkage/field_set.rb +6 -3
  19. data/lib/linkage/function.rb +50 -3
  20. data/lib/linkage/functions/binary.rb +30 -0
  21. data/lib/linkage/functions/cast.rb +54 -0
  22. data/lib/linkage/functions/length.rb +29 -0
  23. data/lib/linkage/functions/strftime.rb +12 -11
  24. data/lib/linkage/functions/trim.rb +8 -0
  25. data/lib/linkage/group.rb +20 -0
  26. data/lib/linkage/import_buffer.rb +5 -16
  27. data/lib/linkage/meta_object.rb +139 -0
  28. data/lib/linkage/result_set.rb +74 -17
  29. data/lib/linkage/runner/single_threaded.rb +125 -10
  30. data/lib/linkage/version.rb +3 -0
  31. data/lib/linkage.rb +11 -0
  32. data/linkage.gemspec +16 -121
  33. data/test/config.yml +5 -0
  34. data/test/helper.rb +73 -8
  35. data/test/integration/test_collation.rb +45 -0
  36. data/test/integration/test_configuration.rb +268 -0
  37. data/test/integration/test_cross_linkage.rb +4 -17
  38. data/test/integration/test_dataset.rb +45 -2
  39. data/test/integration/test_dual_linkage.rb +40 -24
  40. data/test/integration/test_functions.rb +22 -0
  41. data/test/integration/test_result_set.rb +85 -0
  42. data/test/integration/test_scoring.rb +84 -0
  43. data/test/integration/test_self_linkage.rb +5 -0
  44. data/test/integration/test_within_comparator.rb +100 -0
  45. data/test/unit/comparators/test_compare.rb +105 -0
  46. data/test/unit/comparators/test_within.rb +57 -0
  47. data/test/unit/expectations/test_exhaustive.rb +111 -0
  48. data/test/unit/expectations/test_simple.rb +303 -0
  49. data/test/unit/functions/test_binary.rb +54 -0
  50. data/test/unit/functions/test_cast.rb +98 -0
  51. data/test/unit/functions/test_length.rb +52 -0
  52. data/test/unit/functions/test_strftime.rb +17 -13
  53. data/test/unit/functions/test_trim.rb +11 -4
  54. data/test/unit/test_comparator.rb +124 -0
  55. data/test/unit/test_configuration.rb +137 -175
  56. data/test/unit/test_data.rb +44 -0
  57. data/test/unit/test_dataset.rb +73 -21
  58. data/test/unit/test_decollation.rb +201 -0
  59. data/test/unit/test_field.rb +38 -14
  60. data/test/unit/test_field_set.rb +12 -8
  61. data/test/unit/test_function.rb +83 -16
  62. data/test/unit/test_group.rb +28 -0
  63. data/test/unit/test_import_buffer.rb +13 -27
  64. data/test/unit/test_meta_object.rb +208 -0
  65. data/test/unit/test_result_set.rb +221 -3
  66. metadata +82 -190
@@ -0,0 +1,100 @@
1
+ require 'helper'
2
+
3
+ module IntegrationTests
4
+ class TestWithinComparator < Test::Unit::TestCase
5
+ test "within comparator with no simple expectations" do
6
+ database_for('sqlite') do |db|
7
+ db.create_table(:foo) { primary_key(:id); Integer(:num) }
8
+ db.create_table(:bar) { primary_key(:id); Integer(:num) }
9
+ db[:foo].import([:id, :num], (1..10).collect { |i| [i, i] })
10
+ db[:bar].import([:id, :num], (1..10).collect { |i| [i, i] })
11
+ end
12
+
13
+ db_opts = database_options_for('sqlite')
14
+ dataset_1 = Linkage::Dataset.new(db_opts, "foo")
15
+ dataset_2 = Linkage::Dataset.new(db_opts, "bar")
16
+ conf = dataset_1.link_with(dataset_2) do
17
+ lhs[:num].must be_within(5).of(rhs[:num])
18
+ save_results_in(db_opts)
19
+ end
20
+
21
+ runner = Linkage::SingleThreadedRunner.new(conf)
22
+ runner.execute
23
+
24
+ database_for('sqlite') do |db|
25
+ assert_equal db[:scores].count, 100
26
+ db[:scores].order(:record_1_id, :record_2_id).each do |score|
27
+ if (score[:record_2_id] - score[:record_1_id]).abs <= 5
28
+ assert_equal 1, score[:score], score.inspect
29
+ else
30
+ assert_equal 0, score[:score], score.inspect
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ test "within comparator with simple expectations" do
37
+ database_for('sqlite') do |db|
38
+ db.create_table(:foo) { primary_key(:id); Integer(:num); String(:parity) }
39
+ db.create_table(:bar) { primary_key(:id); Integer(:num); String(:parity) }
40
+ db[:foo].import([:id, :num, :parity], (1..10).collect { |i| [i, i, i % 2 == 0 ? "even" : "odd"] })
41
+ db[:bar].import([:id, :num, :parity], (1..10).collect { |i| [i, i, i % 2 == 0 ? "even" : "odd"] })
42
+ end
43
+
44
+ db_opts = database_options_for('sqlite')
45
+ dataset_1 = Linkage::Dataset.new(db_opts, "foo")
46
+ dataset_2 = Linkage::Dataset.new(db_opts, "bar")
47
+ conf = dataset_1.link_with(dataset_2) do
48
+ lhs[:parity].must == rhs[:parity]
49
+ lhs[:num].must be_within(5).of(rhs[:num])
50
+ save_results_in(db_opts)
51
+ end
52
+
53
+ runner = Linkage::SingleThreadedRunner.new(conf)
54
+ runner.execute
55
+
56
+ database_for('sqlite') do |db|
57
+ assert_equal db[:scores].count, 50
58
+ db[:scores].order(:record_1_id, :record_2_id).each do |score|
59
+ if (score[:record_2_id] - score[:record_1_id]).abs <= 5
60
+ assert_equal 1, score[:score]
61
+ else
62
+ assert_equal 0, score[:score]
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ test "within comparator with simple expectations and functions" do
69
+ database_for('sqlite') do |db|
70
+ db.create_table(:foo) { primary_key(:id); Integer(:num); String(:parity) }
71
+ db.create_table(:bar) { primary_key(:id); Integer(:num); String(:parity) }
72
+ db[:foo].import([:id, :num, :parity], (1..10).collect { |i| [i, i, i % 2 == 0 ? "even" : "odd"] })
73
+ db[:bar].import([:id, :num, :parity], (1..10).collect { |i| [i, i, i % 2 == 0 ? "even" : "odd"] })
74
+ end
75
+
76
+ db_opts = database_options_for('sqlite')
77
+ dataset_1 = Linkage::Dataset.new(db_opts, "foo")
78
+ dataset_2 = Linkage::Dataset.new(db_opts, "bar")
79
+ conf = dataset_1.link_with(dataset_2) do
80
+ lhs[:parity].must == rhs[:parity]
81
+ cast(lhs[:num], 'integer').must be_within(5).of(cast(rhs[:num], 'integer'))
82
+ save_results_in(db_opts)
83
+ end
84
+
85
+ runner = Linkage::SingleThreadedRunner.new(conf)
86
+ runner.execute
87
+
88
+ database_for('sqlite') do |db|
89
+ assert_equal db[:scores].count, 50
90
+ db[:scores].order(:record_1_id, :record_2_id).each do |score|
91
+ if (score[:record_2_id] - score[:record_1_id]).abs <= 5
92
+ assert_equal 1, score[:score]
93
+ else
94
+ assert_equal 0, score[:score]
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,105 @@
1
+ require 'helper'
2
+
3
+ class UnitTests::TestCompare < Test::Unit::TestCase
4
+ def self.const_missing(name)
5
+ if Linkage::Comparators.const_defined?(name)
6
+ Linkage::Comparators.const_get(name)
7
+ else
8
+ super
9
+ end
10
+ end
11
+
12
+ test "subclass of Binary" do
13
+ assert_equal Binary, Compare.superclass
14
+ end
15
+
16
+ test "valid parameters" do
17
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
18
+ meta_object_2 = stub('meta object', :object => '>', :ruby_type => { :type => String }, :static? => true, :raw? => true)
19
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
20
+ assert_nothing_raised do
21
+ Compare.new(meta_object_1, meta_object_2, meta_object_3)
22
+ end
23
+ end
24
+
25
+ test "score for not equal to" do
26
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
27
+ meta_object_2 = stub('meta object', :object => '!=', :ruby_type => { :type => String }, :static? => true, :raw? => true)
28
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
29
+ comp = Compare.new(meta_object_1, meta_object_2, meta_object_3)
30
+ assert_equal 1, comp.score({:foo => 10}, {:bar => 5})
31
+ assert_equal 0, comp.score({:foo => 5}, {:bar => 5})
32
+ assert_equal 1, comp.score({:foo => 0}, {:bar => 5})
33
+ end
34
+
35
+ test "score for greater than" do
36
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
37
+ meta_object_2 = stub('meta object', :object => '>', :ruby_type => { :type => String }, :static? => true, :raw? => true)
38
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
39
+ comp = Compare.new(meta_object_1, meta_object_2, meta_object_3)
40
+ assert_equal 1, comp.score({:foo => 10}, {:bar => 5})
41
+ assert_equal 0, comp.score({:foo => 5}, {:bar => 5})
42
+ assert_equal 0, comp.score({:foo => 0}, {:bar => 5})
43
+ end
44
+
45
+ test "score for greater than or equal to" do
46
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
47
+ meta_object_2 = stub('meta object', :object => '>=', :ruby_type => { :type => String }, :static? => true, :raw? => true)
48
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
49
+ comp = Compare.new(meta_object_1, meta_object_2, meta_object_3)
50
+ assert_equal 1, comp.score({:foo => 10}, {:bar => 5})
51
+ assert_equal 1, comp.score({:foo => 5}, {:bar => 5})
52
+ assert_equal 0, comp.score({:foo => 0}, {:bar => 5})
53
+ end
54
+
55
+ test "score for less than or equal to" do
56
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
57
+ meta_object_2 = stub('meta object', :object => '<=', :ruby_type => { :type => String }, :static? => true, :raw? => true)
58
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
59
+ comp = Compare.new(meta_object_1, meta_object_2, meta_object_3)
60
+ assert_equal 0, comp.score({:foo => 10}, {:bar => 5})
61
+ assert_equal 1, comp.score({:foo => 5}, {:bar => 5})
62
+ assert_equal 1, comp.score({:foo => 0}, {:bar => 5})
63
+ end
64
+
65
+ test "score for less than" do
66
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
67
+ meta_object_2 = stub('meta object', :object => '<', :ruby_type => { :type => String }, :static? => true, :raw? => true)
68
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
69
+ comp = Compare.new(meta_object_1, meta_object_2, meta_object_3)
70
+ assert_equal 0, comp.score({:foo => 10}, {:bar => 5})
71
+ assert_equal 0, comp.score({:foo => 5}, {:bar => 5})
72
+ assert_equal 1, comp.score({:foo => 0}, {:bar => 5})
73
+ end
74
+
75
+ test "registers itself" do
76
+ assert_equal Compare, Linkage::Comparator['compare']
77
+ end
78
+
79
+ test "requires argument from each side" do
80
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
81
+ meta_object_2 = stub('meta object', :object => '>=', :ruby_type => { :type => String }, :static? => true, :raw? => true)
82
+ meta_object_3 = stub('meta object', :name => :bar, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
83
+ assert_raises do
84
+ Compare.new(meta_object_1, meta_object_2, meta_object_3)
85
+ end
86
+ end
87
+
88
+ test "requires that 3rd argument has the same type as the first argument" do
89
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
90
+ meta_object_2 = stub('meta object', :object => '>=', :ruby_type => { :type => String }, :static? => true, :raw? => true)
91
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Date }, :static? => false)
92
+ assert_raises do
93
+ Compare.new(meta_object_1, meta_object_2, meta_object_3)
94
+ end
95
+ end
96
+
97
+ test "requires raw operator to be >, >=, <=, <, or !=" do
98
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
99
+ meta_object_2 = stub('meta object', :object => 'foo', :ruby_type => { :type => String }, :static? => true, :raw? => true)
100
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
101
+ assert_raises do
102
+ Compare.new(meta_object_1, meta_object_2, meta_object_3)
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,57 @@
1
+ require 'helper'
2
+
3
+ class UnitTests::TestWithin < Test::Unit::TestCase
4
+ def self.const_missing(name)
5
+ if Linkage::Comparators.const_defined?(name)
6
+ Linkage::Comparators.const_get(name)
7
+ else
8
+ super
9
+ end
10
+ end
11
+
12
+ test "subclass of Binary" do
13
+ assert_equal Linkage::Comparators::Binary, Within.superclass
14
+ end
15
+
16
+ test "valid parameters" do
17
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
18
+ meta_object_2 = stub('meta object', :object => 123, :ruby_type => { :type => Fixnum }, :static? => true, :object => 123)
19
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
20
+ assert_nothing_raised do
21
+ Within.new(meta_object_1, meta_object_2, meta_object_3)
22
+ end
23
+ end
24
+
25
+ test "score" do
26
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
27
+ meta_object_2 = stub('meta object', :object => 123, :ruby_type => { :type => Fixnum }, :static? => true, :object => 123)
28
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
29
+ comp = Within.new(meta_object_1, meta_object_2, meta_object_3)
30
+ assert_equal 1, comp.score({:foo => 123}, {:bar => 124})
31
+ assert_equal 1, comp.score({:foo => 124}, {:bar => 123})
32
+ assert_equal 1, comp.score({:foo => 0}, {:bar => 123})
33
+ assert_equal 0, comp.score({:foo => 0}, {:bar => 124})
34
+ end
35
+
36
+ test "registers itself" do
37
+ assert_equal Within, Linkage::Comparator['within']
38
+ end
39
+
40
+ test "requires argument from each side" do
41
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
42
+ meta_object_2 = stub('meta object', :object => 123, :ruby_type => { :type => Fixnum }, :static? => true, :object => 123)
43
+ meta_object_3 = stub('meta object', :name => :bar, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
44
+ assert_raises do
45
+ Within.new(meta_object_1, meta_object_2, meta_object_3)
46
+ end
47
+ end
48
+
49
+ test "requires that 3rd argument has the same type as the first argument" do
50
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
51
+ meta_object_2 = stub('meta object', :object => 123, :ruby_type => { :type => Fixnum }, :static? => true, :object => 123)
52
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Date }, :static? => false)
53
+ assert_raises do
54
+ Within.new(meta_object_1, meta_object_2, meta_object_3)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,111 @@
1
+ require 'helper'
2
+
3
+ class UnitTests::TestExhaustive < Test::Unit::TestCase
4
+ test "initialize with comparator, threshold, and mode" do
5
+ comparator = stub('comparator')
6
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
7
+ assert_equal comparator, exp.comparator
8
+ assert_equal 100, exp.threshold
9
+ assert_equal :min, exp.mode
10
+ end
11
+
12
+ test "kind is :self when comparator has same args on both sides" do
13
+ meta_object_1 = stub('meta object 1')
14
+ meta_object_2 = stub('meta object 2')
15
+ meta_object_1.expects(:objects_equal?).with(meta_object_2).returns(true)
16
+ comparator = stub('comparator', {
17
+ :lhs_args => [meta_object_1], :rhs_args => [meta_object_2]
18
+ })
19
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
20
+ assert_equal :self, exp.kind
21
+ end
22
+
23
+ test "kind is :cross when comparator has args with same dataset but different number of args" do
24
+ meta_object_1 = stub('meta object 1')
25
+ meta_object_2 = stub('meta object 2')
26
+ meta_object_3 = stub('meta object 3')
27
+ meta_object_1.expects(:datasets_equal?).with(meta_object_2).returns(true)
28
+ comparator = stub('comparator', {
29
+ :lhs_args => [meta_object_1], :rhs_args => [meta_object_2, meta_object_3]
30
+ })
31
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
32
+ assert_equal :cross, exp.kind
33
+ end
34
+
35
+ test "kind is :cross when comparator has args with same dataset but different objects" do
36
+ meta_object_1 = stub('meta object 1')
37
+ meta_object_2 = stub('meta object 2')
38
+ meta_object_1.expects(:objects_equal?).with(meta_object_2).returns(false)
39
+ meta_object_1.expects(:datasets_equal?).with(meta_object_2).returns(true)
40
+ comparator = stub('comparator', {
41
+ :lhs_args => [meta_object_1], :rhs_args => [meta_object_2]
42
+ })
43
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
44
+ assert_equal :cross, exp.kind
45
+ end
46
+
47
+ test "kind is :dual when comparator has args with different datasets and different number of args" do
48
+ meta_object_1 = stub('meta object 1')
49
+ meta_object_2 = stub('meta object 2')
50
+ meta_object_3 = stub('meta object 3')
51
+ meta_object_1.expects(:datasets_equal?).with(meta_object_2).returns(false)
52
+ comparator = stub('comparator', {
53
+ :lhs_args => [meta_object_1], :rhs_args => [meta_object_2, meta_object_3]
54
+ })
55
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
56
+ assert_equal :dual, exp.kind
57
+ end
58
+
59
+ test "kind is :dual when comparator has args with different datasets" do
60
+ meta_object_1 = stub('meta object 1')
61
+ meta_object_2 = stub('meta object 2')
62
+ meta_object_1.expects(:objects_equal?).with(meta_object_2).returns(false)
63
+ meta_object_1.expects(:datasets_equal?).with(meta_object_2).returns(false)
64
+ comparator = stub('comparator', {
65
+ :lhs_args => [meta_object_1], :rhs_args => [meta_object_2]
66
+ })
67
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
68
+ assert_equal :dual, exp.kind
69
+ end
70
+
71
+ test "apply_to lhs dataset" do
72
+ dataset = stub('dataset')
73
+ new_dataset = stub('new dataset')
74
+ meta_object_1 = stub('meta object 1', :name => :foo, :to_expr => :foo)
75
+ meta_object_2 = stub('meta object 2', :name => :bar, :to_expr => :bar)
76
+ comparator = stub('comparator', :lhs_args => [meta_object_1], :rhs_args => [meta_object_2])
77
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
78
+ dataset.expects(:select_more).with(:foo.as(:foo)).returns(new_dataset)
79
+ assert_equal new_dataset, exp.apply_to(dataset, :lhs)
80
+ end
81
+
82
+ test "apply_to rhs dataset" do
83
+ dataset = stub('dataset')
84
+ new_dataset = stub('new dataset')
85
+ meta_object_1 = stub('meta object 1', :name => :foo, :to_expr => :foo)
86
+ meta_object_2 = stub('meta object 2', :name => :bar, :to_expr => :bar)
87
+ comparator = stub('comparator', :lhs_args => [meta_object_1], :rhs_args => [meta_object_2])
88
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
89
+ dataset.expects(:select_more).with(:bar.as(:bar)).returns(new_dataset)
90
+ assert_equal new_dataset, exp.apply_to(dataset, :rhs)
91
+ end
92
+
93
+ test "satisfied? with :equal mode" do
94
+ meta_object_1 = stub('meta object 1')
95
+ meta_object_2 = stub('meta object 2')
96
+ comparator = stub('comparator', :lhs_args => [meta_object_1], :rhs_args => [meta_object_2])
97
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 50, :equal)
98
+ assert exp.satisfied?(50)
99
+ assert !exp.satisfied?(123)
100
+ end
101
+
102
+ test "satisfied? with :min mode" do
103
+ meta_object_1 = stub('meta object 1')
104
+ meta_object_2 = stub('meta object 2')
105
+ comparator = stub('comparator', :lhs_args => [meta_object_1], :rhs_args => [meta_object_2])
106
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 50, :min)
107
+ assert exp.satisfied?(50)
108
+ assert exp.satisfied?(55)
109
+ assert !exp.satisfied?(45)
110
+ end
111
+ end
@@ -0,0 +1,303 @@
1
+ require 'helper'
2
+
3
+ class UnitTests::TestSimple < Test::Unit::TestCase
4
+ test "initialize with invalid operator" do
5
+ assert_raises(ArgumentError) do
6
+ Linkage::Expectations::Simple.new(stub(), stub(), :foo)
7
+ end
8
+ end
9
+
10
+ test "creating filter expectation from a dynamic object and a static object" do
11
+ field = stub('meta field', :static? => false, :side => :lhs)
12
+ object = stub('meta object', :static? => true)
13
+ exp = Linkage::Expectations::Simple.create(field, object, :==)
14
+ assert_kind_of Linkage::Expectations::Filter, exp
15
+ assert_equal :lhs, exp.side
16
+ end
17
+
18
+ test "creating expectation from two static objects raises error" do
19
+ object_1 = stub('meta object 1', :static? => true)
20
+ object_2 = stub('meta object 2', :static? => true)
21
+ assert_raises(ArgumentError) do
22
+ Linkage::Expectations::Simple.create(object_1, object_2, :==)
23
+ end
24
+ end
25
+
26
+ test "creating filter expectation from two dynamic objects with the same side" do
27
+ field_1 = stub('meta field 1', :static? => false, :side => :lhs)
28
+ field_2 = stub('meta field 2', :static? => false, :side => :lhs)
29
+ field_1.expects(:datasets_equal?).with(field_2).returns(true)
30
+ exp = Linkage::Expectations::Simple.create(field_1, field_2, :==)
31
+ assert_kind_of Linkage::Expectations::Filter, exp
32
+ assert_equal :lhs, exp.side
33
+ end
34
+
35
+ test "creating filter expectation from two dynamic objects with the same side but different datasets raises error" do
36
+ field_1 = stub('meta field 1', :static? => false, :side => :lhs)
37
+ field_2 = stub('meta field 2', :static? => false, :side => :lhs)
38
+ field_1.expects(:datasets_equal?).with(field_2).returns(false)
39
+ assert_raises(ArgumentError) do
40
+ Linkage::Expectations::Simple.create(field_1, field_2, :==)
41
+ end
42
+ end
43
+
44
+ test "creating self expectation from two dynamic objects with different sides" do
45
+ object_1 = stub('meta object 1', :static? => false, :side => :lhs)
46
+ object_2 = stub('meta object 2', :static? => false, :side => :rhs)
47
+ object_1.expects(:objects_equal?).with(object_2).returns(true)
48
+ exp = Linkage::Expectations::Simple.create(object_1, object_2, :==)
49
+ assert_kind_of Linkage::Expectations::Self, exp
50
+ end
51
+
52
+ test "creating cross expectation from two dynamic objects with different sides but same dataset" do
53
+ object_1 = stub('meta object 1', :static? => false, :side => :lhs)
54
+ object_2 = stub('meta object 2', :static? => false, :side => :rhs)
55
+ object_1.expects(:objects_equal?).with(object_2).returns(false)
56
+ object_1.expects(:datasets_equal?).with(object_2).returns(true)
57
+ exp = Linkage::Expectations::Simple.create(object_1, object_2, :==)
58
+ assert_kind_of Linkage::Expectations::Cross, exp
59
+ end
60
+
61
+ test "creating dual expectation from two dynamic objects with different sides and datasets" do
62
+ object_1 = stub('meta object 1', :static? => false, :side => :lhs)
63
+ object_2 = stub('meta object 2', :static? => false, :side => :rhs)
64
+ object_1.expects(:objects_equal?).with(object_2).returns(false)
65
+ object_1.expects(:datasets_equal?).with(object_2).returns(false)
66
+ exp = Linkage::Expectations::Simple.create(object_1, object_2, :==)
67
+ assert_kind_of Linkage::Expectations::Dual, exp
68
+ end
69
+
70
+ test "apply_to with filter expectation (== operator)" do
71
+ dataset = stub('dataset')
72
+ meta_field_1 = stub('meta field 1', :static? => false, :side => :lhs)
73
+ meta_field_2 = stub('meta field 2', :static? => false, :side => :lhs)
74
+ meta_field_1.stubs(:datasets_equal?).with(meta_field_2).returns(true)
75
+
76
+ exp = Linkage::Expectations::Simple.create(meta_field_1, meta_field_2, :==)
77
+ meta_field_1.expects(:to_expr).returns(:foo)
78
+ meta_field_2.expects(:to_expr).returns(:bar)
79
+ dataset.expects(:filter).with({:foo => :bar}).returns(dataset)
80
+ assert_equal dataset, exp.apply_to(dataset, :lhs)
81
+
82
+ dataset.expects(:filter).never
83
+ assert_equal dataset, exp.apply_to(dataset, :rhs)
84
+ end
85
+
86
+ test "apply_to with filter expectation (!= operator)" do
87
+ dataset = stub('dataset')
88
+ meta_field_1 = stub('meta field 1', :static? => false, :side => :lhs)
89
+ meta_field_2 = stub('meta field 2', :static? => false, :side => :lhs)
90
+ meta_field_1.stubs(:datasets_equal?).with(meta_field_2).returns(true)
91
+
92
+ exp = Linkage::Expectations::Simple.create(meta_field_1, meta_field_2, :'!=')
93
+ meta_field_1.expects(:to_expr).returns(:foo)
94
+ meta_field_2.expects(:to_expr).returns(:bar)
95
+ dataset.expects(:filter).with(~{:foo => :bar}).returns(dataset)
96
+ assert_equal dataset, exp.apply_to(dataset, :lhs)
97
+
98
+ dataset.expects(:filter).never
99
+ assert_equal dataset, exp.apply_to(dataset, :rhs)
100
+ end
101
+
102
+ test "apply_to with filter expectation (> operator)" do
103
+ dataset = stub('dataset')
104
+ meta_field_1 = stub('meta field 1', :static? => false, :side => :lhs)
105
+ meta_field_2 = stub('meta field 2', :static? => false, :side => :lhs)
106
+ meta_field_1.stubs(:datasets_equal?).with(meta_field_2).returns(true)
107
+ exp = Linkage::Expectations::Simple.create(meta_field_1, meta_field_2, :>)
108
+
109
+ identifier_1 = Sequel::SQL::Identifier.new(:foo)
110
+ meta_field_1.expects(:to_identifier).returns(identifier_1)
111
+ identifier_2 = Sequel::SQL::Identifier.new(:bar)
112
+ meta_field_2.expects(:to_identifier).returns(identifier_2)
113
+ expected = Sequel::SQL::BooleanExpression.new(:>, identifier_1, identifier_2)
114
+
115
+ dataset.expects(:filter).with(expected).returns(dataset)
116
+ assert_equal dataset, exp.apply_to(dataset, :lhs)
117
+
118
+ dataset.expects(:filter).never
119
+ assert_equal dataset, exp.apply_to(dataset, :rhs)
120
+ end
121
+
122
+ test "apply_to with self expectation" do
123
+ dataset = stub('dataset')
124
+ object_1 = stub('meta object 1', {
125
+ :static? => false, :side => :lhs, :dataset => dataset,
126
+ :to_expr => :foo
127
+ })
128
+ object_2 = stub('meta object 2', {
129
+ :static? => false, :side => :rhs, :dataset => dataset,
130
+ :to_expr => :foo
131
+ })
132
+ object_1.expects(:objects_equal?).with(object_2).returns(true)
133
+ exp = Linkage::Expectations::Simple.create(object_1, object_2, :==)
134
+
135
+ merged_field = stub('merged field', :name => :foo)
136
+ object_1.expects(:merge).with(object_2).returns(merged_field)
137
+ dataset.expects(:group_match_more).with({:meta_object => object_1, :alias => :foo}).returns(dataset)
138
+ assert_equal dataset, exp.apply_to(dataset, :lhs)
139
+
140
+ dataset.expects(:group_match_more).with({:meta_object => object_2, :alias => :foo}).returns(dataset)
141
+ assert_equal dataset, exp.apply_to(dataset, :rhs)
142
+ end
143
+
144
+ test "apply_to with cross expectation" do
145
+ dataset = stub('dataset')
146
+ object_1 = stub('meta object 1', {
147
+ :static? => false, :side => :lhs, :dataset => dataset,
148
+ :to_expr => :foo
149
+ })
150
+ object_2 = stub('meta object 2', {
151
+ :static? => false, :side => :rhs, :dataset => dataset,
152
+ :to_expr => :bar
153
+ })
154
+ object_1.stubs(:objects_equal?).with(object_2).returns(false)
155
+ object_1.stubs(:datasets_equal?).with(object_2).returns(true)
156
+ exp = Linkage::Expectations::Simple.create(object_1, object_2, :==)
157
+
158
+ merged_field = stub('merged field', :name => :foo_bar)
159
+ object_1.expects(:merge).with(object_2).returns(merged_field)
160
+ dataset.expects(:group_match_more).with({:meta_object => object_1, :alias => :foo_bar}).returns(dataset)
161
+ assert_equal dataset, exp.apply_to(dataset, :lhs)
162
+
163
+ dataset.expects(:group_match_more).with({:meta_object => object_2, :alias => :foo_bar}).returns(dataset)
164
+ assert_equal dataset, exp.apply_to(dataset, :rhs)
165
+ end
166
+
167
+ test "apply_to with dual expectation" do
168
+ dataset_1 = stub('dataset 1')
169
+ object_1 = stub('meta object 1', {
170
+ :static? => false, :side => :lhs, :dataset => dataset_1,
171
+ :to_expr => :foo
172
+ })
173
+ dataset_2 = stub('dataset 2')
174
+ object_2 = stub('meta object 2', {
175
+ :static? => false, :side => :rhs, :dataset => dataset_2,
176
+ :to_expr => :foo
177
+ })
178
+ object_1.stubs(:objects_equal?).with(object_2).returns(false)
179
+ object_1.stubs(:datasets_equal?).with(object_2).returns(false)
180
+ exp = Linkage::Expectations::Simple.create(object_1, object_2, :==)
181
+
182
+ merged_field = stub('merged field', :name => :foo)
183
+ object_1.expects(:merge).with(object_2).returns(merged_field)
184
+ dataset_1.expects(:group_match_more).with({:meta_object => object_1, :alias => :foo}).returns(dataset_1)
185
+ assert_equal dataset_1, exp.apply_to(dataset_1, :lhs)
186
+
187
+ dataset_2.expects(:group_match_more).with({:meta_object => object_2, :alias => :foo}).returns(dataset_2)
188
+ assert_equal dataset_2, exp.apply_to(dataset_2, :rhs)
189
+ end
190
+
191
+ test "exactly! wraps each side inside the binary function" do
192
+ dataset_1 = stub('dataset 1')
193
+ field_1 = stub_field('field 1', :dataset => dataset_1)
194
+ meta_object_1 = stub('meta object 1', :static? => false, :side => :lhs,
195
+ :object => field_1, :dataset => dataset_1)
196
+
197
+ dataset_2 = stub('dataset 2')
198
+ field_2 = stub_field('field 2', :dataset => dataset_2)
199
+ meta_object_2 = stub('meta object 2', :static? => false, :side => :rhs,
200
+ :object => field_2, :dataset => dataset_2)
201
+ meta_object_1.stubs(:objects_equal?).with(meta_object_2).returns(true)
202
+
203
+ exp = Linkage::Expectations::Simple.create(meta_object_1, meta_object_2, :==)
204
+
205
+ klass = Linkage::Functions::Binary
206
+ func_1 = stub('binary function 1')
207
+ klass.expects(:new).with(field_1, :dataset => dataset_1).returns(func_1)
208
+ func_2 = stub('binary function 2')
209
+ klass.expects(:new).with(field_2, :dataset => dataset_2).returns(func_2)
210
+
211
+ new_meta_object_1 = stub('new meta object 1')
212
+ Linkage::MetaObject.expects(:new).with(func_1, :lhs).returns(new_meta_object_1)
213
+ new_meta_object_2 = stub('new meta object 2')
214
+ Linkage::MetaObject.expects(:new).with(func_2, :rhs).returns(new_meta_object_2)
215
+
216
+ exp.exactly!
217
+ # LAZY: assume the expectation set its meta objects appropriately
218
+ end
219
+
220
+ test "#same_except_side with two filter expectations" do
221
+ field = stub_field('foo')
222
+ meta_object_1 = stub('meta field 1', {
223
+ :static? => false, :side => :lhs, :object => field
224
+ })
225
+ meta_object_2 = stub('meta object', :static? => true)
226
+ meta_object_3 = stub('meta field 2', {
227
+ :static? => false, :side => :rhs, :object => field
228
+ })
229
+
230
+ exp_1 = Linkage::Expectations::Simple.create(meta_object_1, meta_object_2, :==)
231
+ exp_2 = Linkage::Expectations::Simple.create(meta_object_3, meta_object_2, :==)
232
+
233
+ meta_object_1.expects(:objects_equal?).with(meta_object_3).returns(true)
234
+ meta_object_2.expects(:objects_equal?).with(meta_object_2).returns(true)
235
+ assert exp_1.same_except_side?(exp_2)
236
+ end
237
+
238
+ test "#decollation_needed? is false when comparing non-string objects" do
239
+ dataset_1 = stub('dataset 1')
240
+ object_1 = stub('meta object 1', {
241
+ :static? => false, :side => :lhs, :dataset => dataset_1,
242
+ })
243
+ dataset_2 = stub('dataset 2')
244
+ object_2 = stub('meta object 2', {
245
+ :static? => false, :side => :rhs, :dataset => dataset_2,
246
+ })
247
+ merged_field = stub('merged field', :ruby_type => {:type => Fixnum})
248
+ object_1.stubs(:merge).with(object_2).returns(merged_field)
249
+ exp = Linkage::Expectations::Dual.new(object_1, object_2, :==)
250
+ assert !exp.decollation_needed?
251
+ end
252
+
253
+ test "#decollation_needed? is false when comparing two dynamic string objects with the same database type and same collation" do
254
+ dataset_1 = stub('dataset 1')
255
+ object_1 = stub('meta object 1', {
256
+ :static? => false, :side => :lhs, :dataset => dataset_1,
257
+ :collation => 'foo', :database_type => :mysql
258
+ })
259
+ dataset_2 = stub('dataset 2')
260
+ object_2 = stub('meta object 2', {
261
+ :static? => false, :side => :rhs, :dataset => dataset_2,
262
+ :collation => 'foo', :database_type => :mysql
263
+ })
264
+ merged_field = stub('merged field', :ruby_type => {:type => String})
265
+ object_1.stubs(:merge).with(object_2).returns(merged_field)
266
+ exp = Linkage::Expectations::Dual.new(object_1, object_2, :==)
267
+ assert !exp.decollation_needed?
268
+ end
269
+
270
+ test "#decollation_needed? is true when comparing two dynamic string objects with the same database type but different collations" do
271
+ dataset_1 = stub('dataset 1')
272
+ object_1 = stub('meta object 1', {
273
+ :static? => false, :side => :lhs, :dataset => dataset_1,
274
+ :collation => 'foo', :database_type => :mysql
275
+ })
276
+ dataset_2 = stub('dataset 2')
277
+ object_2 = stub('meta object 2', {
278
+ :static? => false, :side => :rhs, :dataset => dataset_2,
279
+ :collation => 'bar', :database_type => :mysql
280
+ })
281
+ merged_field = stub('merged field', :ruby_type => {:type => String})
282
+ object_1.stubs(:merge).with(object_2).returns(merged_field)
283
+ exp = Linkage::Expectations::Dual.new(object_1, object_2, :==)
284
+ assert exp.decollation_needed?
285
+ end
286
+
287
+ test "#decollation_needed? is true when comparing two dynamic string objects with same collation but different database types" do
288
+ dataset_1 = stub('dataset 1')
289
+ object_1 = stub('meta object 1', {
290
+ :static? => false, :side => :lhs, :dataset => dataset_1,
291
+ :collation => 'foo', :database_type => :foo
292
+ })
293
+ dataset_2 = stub('dataset 2')
294
+ object_2 = stub('meta object 2', {
295
+ :static? => false, :side => :rhs, :dataset => dataset_2,
296
+ :collation => 'foo', :database_type => :bar
297
+ })
298
+ merged_field = stub('merged field', :ruby_type => {:type => String})
299
+ object_1.stubs(:merge).with(object_2).returns(merged_field)
300
+ exp = Linkage::Expectations::Dual.new(object_1, object_2, :==)
301
+ assert exp.decollation_needed?
302
+ end
303
+ end