lorax 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,130 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Lorax do
4
+ def round_trip_should_succeed(doc1, doc2)
5
+ delta_set = Lorax.diff(doc1, doc2)
6
+ new_doc = delta_set.apply(doc1)
7
+
8
+ unless Lorax::Signature.new(new_doc.root).signature == Lorax::Signature.new(doc2.root).signature
9
+ errmsg = []
10
+ errmsg << "Documents are not identical after a round-trip diff and patch:"
11
+ errmsg << doc1.root.to_xml
12
+ errmsg << "-----"
13
+ errmsg << doc2.root.to_xml
14
+ errmsg << "=> patch: #{delta_set.deltas.inspect}"
15
+ errmsg << new_doc.root.to_xml
16
+ fail errmsg.join("\n")
17
+ end
18
+ end
19
+
20
+ context "inserted nodes" do
21
+ it "handles appends to matching siblings" do
22
+ doc1 = xml { root {
23
+ a1 "hello"
24
+ } }
25
+ doc2 = xml { root {
26
+ a1 "hello"
27
+ a2 "goodbye"
28
+ } }
29
+ round_trip_should_succeed doc1, doc2
30
+ end
31
+
32
+ it "inserts into matching siblings" do
33
+ doc1 = xml { root {
34
+ a1 "hello"
35
+ a3 "goodbye"
36
+ } }
37
+ doc2 = xml { root {
38
+ a1 "hello"
39
+ a2
40
+ a3 "goodbye"
41
+ } }
42
+ round_trip_should_succeed doc1, doc2
43
+ end
44
+
45
+ it "inserts under an existing sibling node" do
46
+ doc1 = xml { root {
47
+ a1 "hello"
48
+ a2
49
+ } }
50
+ doc2 = xml { root {
51
+ a1 "hello"
52
+ a2 { b1 "subnode" }
53
+ } }
54
+ round_trip_should_succeed doc1, doc2
55
+ end
56
+ end
57
+
58
+ context "deleted nodes" do
59
+ it "handles deleting nodes" do
60
+ doc1 = xml { root {
61
+ a1 "hello"
62
+ a2 "goodbye"
63
+ a3 "natch"
64
+ } }
65
+ doc2 = xml { root {
66
+ a1 "hello"
67
+ a3 "natch"
68
+ } }
69
+ round_trip_should_succeed doc1, doc2
70
+ end
71
+ end
72
+
73
+ context "modified nodes" do
74
+ it "handles modifying nodes" do
75
+ doc1 = xml { root {
76
+ a1 "hello"
77
+ a2 "goodbye"
78
+ a3 "natch"
79
+ } }
80
+ doc2 = xml { root {
81
+ a1 "hello"
82
+ a2 "good buy"
83
+ a3 "natch"
84
+ } }
85
+ round_trip_should_succeed doc1, doc2
86
+ end
87
+ end
88
+
89
+ context "mixed operations" do
90
+ it "handles mixed deletions and modifications" do
91
+ doc1 = xml { root {
92
+ a1 "hello"
93
+ a2 "goodbye"
94
+ a3 "natch"
95
+ a4 "jimmy"
96
+ } }
97
+ doc2 = xml { root {
98
+ a1 "hello"
99
+ a3 "not"
100
+ a4 "jimmy"
101
+ } }
102
+ round_trip_should_succeed doc1, doc2
103
+ end
104
+ end
105
+
106
+ context "with whitespace interleaved" do
107
+ it "handles whitespace nodes" do
108
+ doc1 = xml { root {
109
+ a1
110
+ text "\n\n"
111
+ a4
112
+ text "\n\n"
113
+ a5
114
+ } }
115
+ doc2 = xml { root {
116
+ a1
117
+ text "\n\n"
118
+ a2
119
+ text "\n\n"
120
+ a3
121
+ text "\n\n"
122
+ a4
123
+ text "\n\n"
124
+ a5
125
+ } }
126
+ round_trip_should_succeed doc1, doc2
127
+ end
128
+ end
129
+
130
+ end
@@ -0,0 +1,54 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Lorax::Match do
4
+ before do
5
+ @doc1 = xml { root }
6
+ @doc2 = xml { root }
7
+ end
8
+
9
+ describe "#new" do
10
+ it "takes two nodes as arguments" do
11
+ proc { Lorax::Match.new(@doc1.root, @doc2.root) }.should_not raise_error
12
+ end
13
+
14
+ it "takes optional options" do
15
+ proc { Lorax::Match.new(@doc1.root, @doc2.root, {:perfect => true}) }.should_not raise_error
16
+ end
17
+ end
18
+
19
+ describe "#perfect" do
20
+ it "returns true if {:perfect => true} option was passed to #new" do
21
+ Lorax::Match.new(@doc1.root, @doc2.root, {:perfect => true}).should be_perfect
22
+ end
23
+
24
+ it "returns false if {:perfect => false} option was passed to #new" do
25
+ Lorax::Match.new(@doc1.root, @doc2.root, {:perfect => false}).should_not be_perfect
26
+ end
27
+
28
+ it "returns false if no :perfect option was passed to #new" do
29
+ Lorax::Match.new(@doc1.root, @doc2.root).should_not be_perfect
30
+ end
31
+ end
32
+
33
+ describe "#pair" do
34
+ it "returns the two nodes in an array" do
35
+ Lorax::Match.new(@doc1.root, @doc2.root).pair.should == [@doc1.root, @doc2.root]
36
+ end
37
+ end
38
+
39
+ describe "#other" do
40
+ context "the node is in the pair" do
41
+ it "returns the other node" do
42
+ match = Lorax::Match.new :a, :b
43
+ match.other(:a).should == :b
44
+ match.other(:b).should == :a
45
+ end
46
+ end
47
+
48
+ context "the node is not in the pair" do
49
+ it "returns nil" do
50
+ Lorax::Match.new(:a, :b).other(:c).should be_nil
51
+ end
52
+ end
53
+ end
54
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,42 @@
1
+ require 'rubygems'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ require 'lorax'
6
+
7
+ require 'spec'
8
+ require 'spec/autorun'
9
+ require 'rr'
10
+ require 'pp'
11
+
12
+ warn "#{__FILE__}:#{__LINE__}: libxml version info: #{Nokogiri::VERSION_INFO.inspect}"
13
+
14
+ module XmlBuilderHelper
15
+ def xml(&block)
16
+ Nokogiri::XML::Builder.new(&block).doc
17
+ end
18
+
19
+ def assert_no_match_exists(match_set, node1, node2)
20
+ match_set.match(node1).should be_nil
21
+ match_set.match(node2).should be_nil
22
+ end
23
+
24
+ def assert_perfect_match_exists(match_set, node1, node2)
25
+ match = match_set.match(node1)
26
+ fail "#{node1.inspect} was not matched" if match.nil?
27
+ match.other(node1).should == node2
28
+ match.should be_perfect
29
+ end
30
+
31
+ def assert_forced_match_exists(match_set, node1, node2)
32
+ match = match_set.match(node1)
33
+ fail "#{node1.inspect} was not matched" if match.nil?
34
+ match.other(node1).should == node2
35
+ match.should_not be_perfect
36
+ end
37
+ end
38
+
39
+ Spec::Runner.configure do |config|
40
+ config.mock_with :rr
41
+ config.include XmlBuilderHelper
42
+ end
@@ -0,0 +1,50 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe Lorax::DeleteDelta do
4
+ describe ".new" do
5
+ it "takes one argument" do
6
+ proc { Lorax::DeleteDelta.new(:foo) }.should_not raise_error(ArgumentError)
7
+ proc { Lorax::DeleteDelta.new(:foo, :bar)}.should raise_error(ArgumentError)
8
+ end
9
+ end
10
+
11
+ describe "#node" do
12
+ it "returns the initalizer argument" do
13
+ Lorax::DeleteDelta.new(:foo).node.should == :foo
14
+ end
15
+ end
16
+
17
+ describe "#apply!" do
18
+ context "for an atomic node delta" do
19
+ it "should delete the node" do
20
+ doc1 = xml { root { a1 } }
21
+ doc2 = xml { root }
22
+ node = doc1.at_css("a1")
23
+ delta = Lorax::DeleteDelta.new node
24
+
25
+ delta.apply!(doc1)
26
+
27
+ doc1.at_css("a1").should be_nil
28
+ node.parent.should == nil
29
+ end
30
+ end
31
+
32
+ context "for a subtree delta" do
33
+ it "should delete the subtree" do
34
+ doc1 = xml { root { a1 { b1 ; b2 "hello" } } }
35
+ doc2 = xml { root }
36
+ node = doc1.at_css("a1")
37
+ delta = Lorax::DeleteDelta.new node
38
+
39
+ delta.apply!(doc1)
40
+
41
+ doc1.at_css("a1,b1,b2").should be_nil
42
+ node.parent.should == nil
43
+ end
44
+ end
45
+ end
46
+
47
+ describe "#descriptor" do
48
+ it "needs a spec"
49
+ end
50
+ end
@@ -0,0 +1,109 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe Lorax::InsertDelta do
4
+ describe ".new" do
5
+ it "takes three arguments" do
6
+ proc { Lorax::InsertDelta.new(:foo, :bar) }.should raise_error(ArgumentError)
7
+ proc { Lorax::InsertDelta.new(:foo, :bar, :quux) }.should_not raise_error(ArgumentError)
8
+ proc { Lorax::InsertDelta.new(:foo, :bar, :quux, :fuzz)}.should raise_error(ArgumentError)
9
+ end
10
+ end
11
+
12
+ describe "#node" do
13
+ it "returns the first argument to #new" do
14
+ Lorax::InsertDelta.new(:foo, :bar, :quux).node.should == :foo
15
+ end
16
+ end
17
+
18
+ describe "#xpath" do
19
+ it "returns the second argument to #new" do
20
+ Lorax::InsertDelta.new(:foo, :bar, :quux).xpath.should == :bar
21
+ end
22
+ end
23
+
24
+ describe "#position" do
25
+ it "returns the third argument to #new" do
26
+ Lorax::InsertDelta.new(:foo, :bar, :quux).position.should == :quux
27
+ end
28
+ end
29
+
30
+ describe "#apply!" do
31
+ context "for an atomic node delta" do
32
+ it "should insert a copy into the document" do
33
+ doc1 = xml { root }
34
+ doc2 = xml { root { a1 } }
35
+ node = doc2.at_css("a1")
36
+ delta = Lorax::InsertDelta.new node, node.parent.path, 0
37
+
38
+ delta.apply!(doc1)
39
+
40
+ doc1.at_css("a1").should_not be_nil
41
+ node.parent.should == doc2.root
42
+ end
43
+ end
44
+
45
+ context "for a subtree node delta" do
46
+ it "should insert a copy into the document" do
47
+ doc1 = xml { root }
48
+ doc2 = xml { root { a1 { b1 ; b2 "hello" } } }
49
+ node = doc2.at_css("a1")
50
+ delta = Lorax::InsertDelta.new node, node.parent.path, 0
51
+
52
+ delta.apply!(doc1)
53
+
54
+ doc1.at_css("a1").should_not be_nil
55
+ node.parent.should == doc2.root
56
+ end
57
+ end
58
+
59
+ context "sibling node insertions" do
60
+ it "should insert at the front" do
61
+ doc1 = xml { root { a2 } }
62
+ doc2 = xml { root { a1 ; a2 } }
63
+ node = doc2.at_css("a1")
64
+ delta = Lorax::InsertDelta.new node, node.parent.path, 0
65
+
66
+ delta.apply! doc1
67
+
68
+ doc1.root.children.map {|child| child.name}.should == %w[a1 a2]
69
+ end
70
+
71
+ it "should insert at the middle" do
72
+ doc1 = xml { root { a1 ; a3 } }
73
+ doc2 = xml { root { a1 ; a2 ; a3 } }
74
+ node = doc2.at_css("a2")
75
+ delta = Lorax::InsertDelta.new node, node.parent.path, 1
76
+
77
+ delta.apply! doc1
78
+
79
+ doc1.root.children.map {|child| child.name}.should == %w[a1 a2 a3]
80
+ end
81
+
82
+ it "should insert at the end" do
83
+ doc1 = xml { root { a1 } }
84
+ doc2 = xml { root { a1 ; a2 } }
85
+ node = doc2.at_css("a2")
86
+ delta = Lorax::InsertDelta.new node, node.parent.path, 1
87
+
88
+ delta.apply! doc1
89
+
90
+ doc1.root.children.map {|child| child.name}.should == %w[a1 a2]
91
+ end
92
+ end
93
+
94
+ context "delta with unresolvable xpath" do
95
+ it "should raise a Conflict exception" do
96
+ doc1 = xml { root }
97
+ doc2 = xml { root { a1 } }
98
+ node = doc2.at_css("a1")
99
+ delta = Lorax::InsertDelta.new node, "/foo/bar/quux", 0
100
+
101
+ proc { delta.apply!(doc1) }.should raise_error(Lorax::Delta::NodeNotFoundError)
102
+ end
103
+ end
104
+ end
105
+
106
+ describe "#descriptor" do
107
+ it "needs a spec"
108
+ end
109
+ end
@@ -0,0 +1,94 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe Lorax::ModifyDelta do
4
+ describe ".new" do
5
+ it "takes two arguments" do
6
+ proc { Lorax::ModifyDelta.new(:foo) }.should raise_error(ArgumentError)
7
+ proc { Lorax::ModifyDelta.new(:foo, :bar) }.should_not raise_error(ArgumentError)
8
+ proc { Lorax::ModifyDelta.new(:foo, :bar, :quux)}.should raise_error(ArgumentError)
9
+ end
10
+ end
11
+
12
+ describe "#node1" do
13
+ it "returns the first initializer parameter" do
14
+ Lorax::ModifyDelta.new(:foo, :bar).node1.should == :foo
15
+ end
16
+ end
17
+
18
+ describe "#node2" do
19
+ it "returns the first initializer parameter" do
20
+ Lorax::ModifyDelta.new(:foo, :bar).node2.should == :bar
21
+ end
22
+ end
23
+
24
+ describe "#apply!" do
25
+ context "element node" do
26
+ context "when attributes differ" do
27
+ it "should set the attributes properly" do
28
+ doc1 = xml { root { a1(:foo => :bar) } }
29
+ doc2 = xml { root { a1(:bazz => :quux, :once => :twice) } }
30
+ doc3 = doc1.dup
31
+ node1 = doc1.at_css("a1")
32
+ node2 = doc2.at_css("a1")
33
+ node3 = doc3.at_css("a1")
34
+
35
+ delta = Lorax::ModifyDelta.new(node1, node2)
36
+ delta.apply!(doc3)
37
+
38
+ node3["foo"].should be_nil
39
+ node3["bazz"].should == "quux"
40
+ node3["once"].should == "twice"
41
+ end
42
+ end
43
+ end
44
+
45
+ context "text node" do
46
+ it "should set the content properly" do
47
+ doc1 = xml { root "hello" }
48
+ doc2 = xml { root "goodbye" }
49
+ doc3 = doc1.dup
50
+
51
+ delta = Lorax::ModifyDelta.new(doc1.root.children.first, doc2.root.children.first)
52
+ delta.apply!(doc3)
53
+
54
+ doc3.root.content.should == "goodbye"
55
+ end
56
+ end
57
+
58
+ context "when positions differ" do
59
+ it "should move the node" do
60
+ doc1 = xml { root {
61
+ a1 { b1 }
62
+ a2
63
+ } }
64
+ doc2 = xml { root {
65
+ a1
66
+ a2 { b1 }
67
+ } }
68
+ delta = Lorax::ModifyDelta.new(doc1.at_css("b1"), doc2.at_css("b1"))
69
+ doc3 = doc1.dup
70
+ delta.apply!(doc3)
71
+ doc3.at_xpath("/root/a2/b1").should_not be_nil
72
+ end
73
+
74
+ it "should move the node to the correct position" do
75
+ doc1 = xml { root {
76
+ a1 { b2 }
77
+ a2 { b1 ; b3 }
78
+ } }
79
+ doc2 = xml { root {
80
+ a1
81
+ a2 { b1 ; b2 ; b3 }
82
+ } }
83
+ delta = Lorax::ModifyDelta.new(doc1.at_css("b2"), doc2.at_css("b2"))
84
+ doc3 = doc1.dup
85
+ delta.apply!(doc3)
86
+ doc3.at_xpath("/root/a2/*[2]").name.should == "b2"
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "#descriptor" do
92
+ it "needs a spec"
93
+ end
94
+ end