lorax 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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