Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update XML::Node.parse and XML::Node#canonicalize for keyword args #3331

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/nokogiri/xml/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1102,7 +1102,7 @@ def fragment(tags)
# Parse +string_or_io+ as a document fragment within the context of
# *this* node. Returns a XML::NodeSet containing the nodes parsed from
# +string_or_io+.
def parse(string_or_io, options = nil)
def parse(string_or_io, options_ = nil, options: options_)
##
# When the current node is unparented and not an element node, use the
# document as the parsing context instead. Otherwise, the in-context
Expand Down Expand Up @@ -1489,7 +1489,7 @@ def write_xml_to(io, options = {})
write_to(io, options)
end

def canonicalize(mode = XML::XML_C14N_1_0, inclusive_namespaces = nil, with_comments = false)
def canonicalize(mode_ = XML::XML_C14N_1_0, inclusive_namespaces_ = nil, with_comments_ = false, mode: mode_, inclusive_namespaces: inclusive_namespaces_, with_comments: with_comments_)
c14n_root = self
document.canonicalize(mode, inclusive_namespaces, with_comments) do |node, parent|
tn = node.is_a?(XML::Node) ? node : parent
Expand Down
107 changes: 107 additions & 0 deletions test/xml/test_c14n.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ def test_3_1
assert_match(/Comment/, c14n)
c14n = doc.canonicalize(nil, nil, false)
refute_match(/Comment/, c14n)

# with keyword args
# TODO: when Document#canonicalize is updated to keyword args, these tests should pass
# c14n = doc.canonicalize(mode: nil, inclusive_namespaces: nil, with_comments: true)
# assert_match(/Comment/, c14n)
# c14n = doc.canonicalize(mode: nil, inclusive_namespaces: nil, with_comments: false)
# refute_match(/Comment/, c14n)
end

def test_exclude_block_params
Expand Down Expand Up @@ -199,13 +206,113 @@ def test_c14n_modes
end
end

def test_c14n_modes_with_keyword_args
# http://www.w3.org/TR/xml-exc-c14n/#sec-Enveloping

doc1 = Nokogiri.XML(<<~EOXML)
<n0:local xmlns:n0="http://foobar.org" xmlns:n3="ftp://example.org">
<n1:elem2 xmlns:n1="http://example.net" xml:lang="en">
<n3:stuff xmlns:n3="ftp://example.org"/>
</n1:elem2>
</n0:local>
EOXML
node1 = doc1.at_xpath("//n1:elem2", { "n1" => "http://example.net" })

doc2 = Nokogiri.XML(<<~EOXML)
<n2:pdu xmlns:n1="http://example.com"
xmlns:n2="http://foo.example"
xmlns:n4="http://foo.example"
xml:lang="fr"
xml:space="retain">
<n1:elem2 xmlns:n1="http://example.net" xml:lang="en">
<n3:stuff xmlns:n3="ftp://example.org"/>
<n4:stuff />
</n1:elem2>
</n2:pdu>
EOXML
node2 = doc2.at_xpath("//n1:elem2", { "n1" => "http://example.net" })

expected = <<~EOF.strip
<n1:elem2 xmlns:n0="http://foobar.org" xmlns:n1="http://example.net" xmlns:n3="ftp://example.org" xml:lang="en">
<n3:stuff></n3:stuff>
</n1:elem2>
EOF
c14n = node1.canonicalize
assert_equal(expected, c14n)

expected = <<~EOF.strip
<n1:elem2 xmlns:n1="http://example.net" xmlns:n2="http://foo.example" xmlns:n4="http://foo.example" xml:lang="en" xml:space="retain">
<n3:stuff xmlns:n3="ftp://example.org"></n3:stuff>
<n4:stuff></n4:stuff>
</n1:elem2>
EOF
c14n = node2.canonicalize
assert_equal(expected, c14n)
c14n = node2.canonicalize(mode: XML::XML_C14N_1_0)
assert_equal(expected, c14n)
assert_raises(RuntimeError) do
node2.canonicalize(mode: XML::XML_C14N_1_0, inclusive_namespaces: ["n2"])
end

expected = <<~EOF.strip
<n1:elem2 xmlns:n1="http://example.net" xml:lang="en">
<n3:stuff xmlns:n3="ftp://example.org"></n3:stuff>
</n1:elem2>
EOF
c14n = node1.canonicalize(mode: XML::XML_C14N_EXCLUSIVE_1_0)
assert_equal(expected, c14n)

expected = <<~EOF.strip
<n1:elem2 xmlns:n1="http://example.net" xml:lang="en">
<n3:stuff xmlns:n3="ftp://example.org"></n3:stuff>
<n4:stuff xmlns:n4="http://foo.example"></n4:stuff>
</n1:elem2>
EOF
c14n = node2.canonicalize(mode: XML::XML_C14N_EXCLUSIVE_1_0)
assert_equal(expected, c14n)

expected = <<~EOF.strip
<n1:elem2 xmlns:n1="http://example.net" xmlns:n2="http://foo.example" xml:lang="en">
<n3:stuff xmlns:n3="ftp://example.org"></n3:stuff>
<n4:stuff xmlns:n4="http://foo.example"></n4:stuff>
</n1:elem2>
EOF
c14n = node2.canonicalize(mode: XML::XML_C14N_EXCLUSIVE_1_0, inclusive_namespaces: ["n2"])
assert_equal(expected, c14n)

expected = <<~EOF.strip
<n1:elem2 xmlns:n1="http://example.net" xmlns:n2="http://foo.example" xmlns:n4="http://foo.example" xml:lang="en">
<n3:stuff xmlns:n3="ftp://example.org"></n3:stuff>
<n4:stuff></n4:stuff>
</n1:elem2>
EOF
c14n = node2.canonicalize(mode: XML::XML_C14N_EXCLUSIVE_1_0, inclusive_namespaces: ["n2", "n4"])
assert_equal(expected, c14n)

expected = <<~EOF.strip
<n1:elem2 xmlns:n1="http://example.net" xmlns:n2="http://foo.example" xmlns:n4="http://foo.example" xml:lang="en" xml:space="retain">
<n3:stuff xmlns:n3="ftp://example.org"></n3:stuff>
<n4:stuff></n4:stuff>
</n1:elem2>
EOF
c14n = node2.canonicalize(mode: XML::XML_C14N_1_1)
assert_equal(expected, c14n)
assert_raises(RuntimeError) do
node2.canonicalize(mode: XML::XML_C14N_1_1, inclusive_namespaces: ["n2"])
end
end

def test_wrong_params
xml = "<a><b></b></a>"
doc = Nokogiri.XML(xml)

assert_raises(TypeError) { doc.canonicalize(:wrong_type) }
assert_raises(TypeError) { doc.canonicalize(nil, :wrong_type) }
doc.canonicalize(nil, nil, :wrong_type)

# with keyword args
# assert_raises(TypeError) { doc.canonicalize(nil, inclusive_namespaces: :wrong_type) }
# doc.canonicalize(nil, inclusive_namespaces: nil, with_comments: :wrong_type)
end
end
end
Expand Down
30 changes: 30 additions & 0 deletions test/xml/test_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,36 @@ def test_node_context_parsing_of_malformed_html_fragment_without_recover_is_not_
end
end

def test_node_context_parsing_of_malformed_html_fragment_without_recover_is_not_corrected_keyword
skip("libxml2 2.14.0 no longer raises this error") if Nokogiri.uses_libxml?(">= 2.14.0")

doc = HTML4.parse("<html><body><div></div></body></html>")
context_node = doc.at_css("div")
assert_raises(Nokogiri::XML::SyntaxError) do
context_node.parse("<div </div>", options: ParseOptions.new)
end
end

def test_node_context_parsing_of_malformed_xml_fragment_without_recover_is_not_corrected
skip("libxml2 2.14.0 no longer raises this error") if Nokogiri.uses_libxml?(">= 2.14.0")

doc = XML.parse("<root><body><div></div></body></roo")
context_node = doc.at_css("div")
assert_raises(Nokogiri::XML::SyntaxError) do
context_node.parse("<div </div>", &:strict)
end
end

def test_node_context_parsing_of_malformed_xml_fragment_without_recover_is_not_corrected_keyword
skip("libxml2 2.14.0 no longer raises this error") if Nokogiri.uses_libxml?(">= 2.14.0")

doc = XML.parse("<root><body><div></div></body></roo")
context_node = doc.at_css("div")
assert_raises(Nokogiri::XML::SyntaxError) do
context_node.parse("<div </div>", options: ParseOptions.new)
end
end

def test_node_context_parsing_of_malformed_xml_fragment_uses_the_right_class_to_recover
doc = XML.parse("<root><body><div></div></body></root>")
context_node = doc.at_css("div")
Expand Down