diff --git a/NEWS.md b/NEWS.md
index 51a45cab..7f95d829 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -386,7 +386,7 @@
* Patch by NAITOH Jun.
- * Improved parse performance when an attribute has many `<`s.
+ * Improved parse performance when an attribute has many `>`s.
* GH-126
diff --git a/benchmark/parse_cdata.yaml b/benchmark/parse_cdata.yaml
new file mode 100644
index 00000000..cde04306
--- /dev/null
+++ b/benchmark/parse_cdata.yaml
@@ -0,0 +1,50 @@
+loop_count: 100
+contexts:
+ - gems:
+ rexml: 3.2.6
+ require: false
+ prelude: require 'rexml'
+ - name: master
+ prelude: |
+ $LOAD_PATH.unshift(File.expand_path("lib"))
+ require 'rexml'
+ - name: 3.2.6(YJIT)
+ gems:
+ rexml: 3.2.6
+ require: false
+ prelude: |
+ require 'rexml'
+ RubyVM::YJIT.enable
+ - name: master(YJIT)
+ prelude: |
+ $LOAD_PATH.unshift(File.expand_path("lib"))
+ require 'rexml'
+ RubyVM::YJIT.enable
+
+prelude: |
+ require 'rexml/document'
+ require 'rexml/parsers/sax2parser'
+ require 'rexml/parsers/pullparser'
+ require 'rexml/parsers/streamparser'
+ require 'rexml/streamlistener'
+
+ def build_xml(size)
+ xml = "\n" +
+ "Test\n" +
+ "\n"
+ end
+ xml = build_xml(100000)
+
+ class Listener
+ include REXML::StreamListener
+ end
+
+benchmark:
+ 'dom' : REXML::Document.new(xml)
+ 'sax' : REXML::Parsers::SAX2Parser.new(xml).parse
+ 'pull' : |
+ parser = REXML::Parsers::PullParser.new(xml)
+ while parser.has_next?
+ parser.pull
+ end
+ 'stream' : REXML::Parsers::StreamParser.new(xml, Listener.new).parse
diff --git a/benchmark/parse_comment.yaml b/benchmark/parse_comment.yaml
new file mode 100644
index 00000000..a0a3a771
--- /dev/null
+++ b/benchmark/parse_comment.yaml
@@ -0,0 +1,36 @@
+loop_count: 100
+contexts:
+ - gems:
+ rexml: 3.2.6
+ require: false
+ prelude: require 'rexml'
+ - name: master
+ prelude: |
+ $LOAD_PATH.unshift(File.expand_path("lib"))
+ require 'rexml'
+ - name: 3.2.6(YJIT)
+ gems:
+ rexml: 3.2.6
+ require: false
+ prelude: |
+ require 'rexml'
+ RubyVM::YJIT.enable
+ - name: master(YJIT)
+ prelude: |
+ $LOAD_PATH.unshift(File.expand_path("lib"))
+ require 'rexml'
+ RubyVM::YJIT.enable
+
+prelude: |
+ require 'rexml/document'
+
+ SIZE = 100000
+
+ top_level_xml = "\n"
+ in_doctype_xml = "]>"
+ after_doctype_xml = ""
+
+benchmark:
+ 'top_level' : REXML::Document.new(top_level_xml)
+ 'in_doctype' : REXML::Document.new(in_doctype_xml)
+ 'after_doctype' : REXML::Document.new(after_doctype_xml)
diff --git a/benchmark/xpath.yaml b/benchmark/xpath.yaml
new file mode 100644
index 00000000..d6e970eb
--- /dev/null
+++ b/benchmark/xpath.yaml
@@ -0,0 +1,32 @@
+loop_count: 100
+contexts:
+ - gems:
+ rexml: 3.2.6
+ require: false
+ prelude: require 'rexml'
+ - name: master
+ prelude: |
+ $LOAD_PATH.unshift(File.expand_path("lib"))
+ require 'rexml'
+ - name: 3.2.6(YJIT)
+ gems:
+ rexml: 3.2.6
+ require: false
+ prelude: |
+ require 'rexml'
+ RubyVM::YJIT.enable
+ - name: master(YJIT)
+ prelude: |
+ $LOAD_PATH.unshift(File.expand_path("lib"))
+ require 'rexml'
+ RubyVM::YJIT.enable
+
+prelude: |
+ require 'rexml/document'
+
+ DEPTH = 100
+ xml = '' * DEPTH + '' * DEPTH
+ doc = REXML::Document.new(xml)
+
+benchmark:
+ "REXML::XPath.match(REXML::Document.new(xml), 'a//a')" : REXML::XPath.match(doc, "a//a")
diff --git a/lib/rexml/attribute.rb b/lib/rexml/attribute.rb
index fe48745c..7a190225 100644
--- a/lib/rexml/attribute.rb
+++ b/lib/rexml/attribute.rb
@@ -206,6 +206,10 @@ def xpath
path += "/@#{self.expanded_name}"
return path
end
+
+ def document
+ @element&.document
+ end
end
end
#vim:ts=2 sw=2 noexpandtab:
diff --git a/lib/rexml/document.rb b/lib/rexml/document.rb
index d1747dd4..1c678bef 100644
--- a/lib/rexml/document.rb
+++ b/lib/rexml/document.rb
@@ -309,8 +309,8 @@ def stand_alone?
end
# :call-seq:
- # doc.write(output=$stdout, indent=-1, transtive=false, ie_hack=false, encoding=nil)
- # doc.write(options={:output => $stdout, :indent => -1, :transtive => false, :ie_hack => false, :encoding => nil})
+ # doc.write(output=$stdout, indent=-1, transitive=false, ie_hack=false, encoding=nil)
+ # doc.write(options={:output => $stdout, :indent => -1, :transitive => false, :ie_hack => false, :encoding => nil})
#
# Write the XML tree out, optionally with indent. This writes out the
# entire XML document, including XML declarations, doctype declarations,
@@ -448,6 +448,20 @@ def document
end
private
+
+ attr_accessor :namespaces_cache
+
+ # New document level cache is created and available in this block.
+ # This API is thread unsafe. Users can't change this document in this block.
+ def enable_cache
+ @namespaces_cache = {}
+ begin
+ yield
+ ensure
+ @namespaces_cache = nil
+ end
+ end
+
def build( source )
Parsers::TreeParser.new( source, self ).parse
end
diff --git a/lib/rexml/element.rb b/lib/rexml/element.rb
index 4e3a60b9..b62b6cc2 100644
--- a/lib/rexml/element.rb
+++ b/lib/rexml/element.rb
@@ -589,10 +589,12 @@ def prefixes
# d.elements['//c'].namespaces # => {"x"=>"1", "y"=>"2", "z"=>"3"}
#
def namespaces
- namespaces = {}
- namespaces = parent.namespaces if parent
- namespaces = namespaces.merge( attributes.namespaces )
- return namespaces
+ namespaces_cache = document&.__send__(:namespaces_cache)
+ if namespaces_cache
+ namespaces_cache[self] ||= calculate_namespaces
+ else
+ calculate_namespaces
+ end
end
# :call-seq:
@@ -619,17 +621,9 @@ def namespace(prefix=nil)
if prefix.nil?
prefix = prefix()
end
- if prefix == ''
- prefix = "xmlns"
- else
- prefix = "xmlns:#{prefix}" unless prefix[0,5] == 'xmlns'
- end
- ns = nil
- target = self
- while ns.nil? and target
- ns = target.attributes[prefix]
- target = target.parent
- end
+ prefix = (prefix == '') ? 'xmlns' : prefix.delete_prefix("xmlns:")
+ ns = namespaces[prefix]
+
ns = '' if ns.nil? and prefix == 'xmlns'
return ns
end
@@ -1516,8 +1510,15 @@ def write(output=$stdout, indent=-1, transitive=false, ie_hack=false)
formatter.write( self, output )
end
-
private
+ def calculate_namespaces
+ if parent
+ parent.namespaces.merge(attributes.namespaces)
+ else
+ attributes.namespaces
+ end
+ end
+
def __to_xpath_helper node
rv = node.expanded_name.clone
if node.parent
diff --git a/lib/rexml/parsers/baseparser.rb b/lib/rexml/parsers/baseparser.rb
index 44aacfa2..750b1697 100644
--- a/lib/rexml/parsers/baseparser.rb
+++ b/lib/rexml/parsers/baseparser.rb
@@ -277,14 +277,7 @@ def pull_event
return process_instruction
elsif @source.match?("/um, true)
- if md.nil?
- raise REXML::ParseException.new("Unclosed comment", @source)
- end
- if /--|-\z/.match?(md[1])
- raise REXML::ParseException.new("Malformed comment", @source)
- end
- return [ :comment, md[1] ]
+ return [ :comment, process_comment ]
elsif @source.match?("DOCTYPE", true)
base_error_message = "Malformed DOCTYPE"
unless @source.match?(/\s+/um, true)
@@ -417,20 +410,17 @@ def pull_event
raise REXML::ParseException.new(message, @source)
end
return [:notationdecl, name, *id]
- elsif md = @source.match(/--(.*?)-->/um, true)
- case md[1]
- when /--/, /-\z/
- raise REXML::ParseException.new("Malformed comment", @source)
- end
- return [ :comment, md[1] ] if md
+ elsif @source.match?("--", true)
+ return [ :comment, process_comment ]
+ else
+ raise REXML::ParseException.new("Malformed node: Started with '/um, true)
@document_status = :after_doctype
return [ :end_doctype ]
- end
- if @document_status == :in_doctype
+ else
raise ParseException.new("Malformed DOCTYPE: invalid declaration", @source)
end
end
@@ -460,23 +450,19 @@ def pull_event
end
return [ :end_element, last_tag ]
elsif @source.match?("!", true)
- md = @source.match(/([^>]*>)/um)
#STDERR.puts "SOURCE BUFFER = #{source.buffer}, #{source.buffer.size}"
- raise REXML::ParseException.new("Malformed node", @source) unless md
- if md[0][0] == ?-
- md = @source.match(/--(.*?)-->/um, true)
-
- if md.nil? || /--|-\z/.match?(md[1])
- raise REXML::ParseException.new("Malformed comment", @source)
+ if @source.match?("--", true)
+ return [ :comment, process_comment ]
+ elsif @source.match?("[CDATA[", true)
+ text = @source.read_until("]]>")
+ if text.chomp!("]]>")
+ return [ :cdata, text ]
+ else
+ raise REXML::ParseException.new("Malformed CDATA: Missing end ']]>'", @source)
end
-
- return [ :comment, md[1] ]
else
- md = @source.match(/\[CDATA\[(.*?)\]\]>/um, true)
- return [ :cdata, md[1] ] if md
+ raise REXML::ParseException.new("Malformed node: Started with '")
+ unless text.chomp!("-->")
+ raise REXML::ParseException.new("Unclosed comment: Missing end '-->'", @source)
+ end
+
+ if text.include? "--" or text.end_with?("-")
+ raise REXML::ParseException.new("Malformed comment", @source)
+ end
+ text
+ end
+
def process_instruction
name = parse_name("Malformed XML: Invalid processing instruction node")
if @source.match?(/\s+/um, true)
diff --git a/lib/rexml/rexml.rb b/lib/rexml/rexml.rb
index a653f028..bf3c0d32 100644
--- a/lib/rexml/rexml.rb
+++ b/lib/rexml/rexml.rb
@@ -31,7 +31,7 @@
module REXML
COPYRIGHT = "Copyright © 2001-2008 Sean Russell "
DATE = "2008/019"
- VERSION = "3.4.1"
+ VERSION = "3.4.2"
REVISION = ""
Copyright = COPYRIGHT
diff --git a/lib/rexml/source.rb b/lib/rexml/source.rb
index 5ba5ab12..3ec1141e 100644
--- a/lib/rexml/source.rb
+++ b/lib/rexml/source.rb
@@ -67,7 +67,7 @@ class Source
module Private
SCANNER_RESET_SIZE = 100000
PRE_DEFINED_TERM_PATTERNS = {}
- pre_defined_terms = ["'", '"', "<"]
+ pre_defined_terms = ["'", '"', "<", "]]>"]
if StringScanner::Version < "3.1.1"
pre_defined_terms.each do |term|
PRE_DEFINED_TERM_PATTERNS[term] = /#{Regexp.escape(term)}/
diff --git a/lib/rexml/xpath.rb b/lib/rexml/xpath.rb
index a0921bd8..666d764f 100644
--- a/lib/rexml/xpath.rb
+++ b/lib/rexml/xpath.rb
@@ -35,7 +35,6 @@ def XPath::first(element, path=nil, namespaces=nil, variables={}, options={})
parser.namespaces = namespaces
parser.variables = variables
path = "*" unless path
- element = [element] unless element.kind_of? Array
parser.parse(path, element).flatten[0]
end
@@ -64,7 +63,6 @@ def XPath::each(element, path=nil, namespaces=nil, variables={}, options={}, &bl
parser.namespaces = namespaces
parser.variables = variables
path = "*" unless path
- element = [element] unless element.kind_of? Array
parser.parse(path, element).each( &block )
end
@@ -74,7 +72,6 @@ def XPath::match(element, path=nil, namespaces=nil, variables={}, options={})
parser.namespaces = namespaces
parser.variables = variables
path = "*" unless path
- element = [element] unless element.kind_of? Array
parser.parse(path,element)
end
end
diff --git a/lib/rexml/xpath_parser.rb b/lib/rexml/xpath_parser.rb
index 5eb1e5a9..70ae8919 100644
--- a/lib/rexml/xpath_parser.rb
+++ b/lib/rexml/xpath_parser.rb
@@ -76,19 +76,27 @@ def variables=( vars={} )
@variables = vars
end
- def parse path, nodeset
+ def parse path, node
path_stack = @parser.parse( path )
- match( path_stack, nodeset )
+ if node.is_a?(Array)
+ Kernel.warn("REXML::XPath.each, REXML::XPath.first, REXML::XPath.match dropped support for nodeset...", uplevel: 1)
+ return [] if node.empty?
+ node = node.first
+ end
+
+ node.document.__send__(:enable_cache) do
+ match( path_stack, node )
+ end
end
- def get_first path, nodeset
+ def get_first path, node
path_stack = @parser.parse( path )
- first( path_stack, nodeset )
+ first( path_stack, node )
end
- def predicate path, nodeset
+ def predicate path, node
path_stack = @parser.parse( path )
- match( path_stack, nodeset )
+ match( path_stack, node )
end
def []=( variable_name, value )
@@ -136,15 +144,12 @@ def first( path_stack, node )
end
- def match(path_stack, nodeset)
- nodeset = nodeset.collect.with_index do |node, i|
- position = i + 1
- XPathNode.new(node, position: position)
- end
+ def match(path_stack, node)
+ nodeset = [XPathNode.new(node, position: 1)]
result = expr(path_stack, nodeset)
case result
when Array # nodeset
- unnode(result)
+ unnode(result).uniq
else
[result]
end
@@ -492,14 +497,10 @@ def node_test(path_stack, nodesets, any_type: :element)
if strict?
raw_node.name == name and raw_node.namespace == ""
else
- # FIXME: This DOUBLES the time XPath searches take
- ns = get_namespace(raw_node, prefix)
- raw_node.name == name and raw_node.namespace == ns
+ raw_node.name == name and raw_node.namespace == get_namespace(raw_node, prefix)
end
else
- # FIXME: This DOUBLES the time XPath searches take
- ns = get_namespace(raw_node, prefix)
- raw_node.name == name and raw_node.namespace == ns
+ raw_node.name == name and raw_node.namespace == get_namespace(raw_node, prefix)
end
when :attribute
if prefix.nil?
@@ -507,9 +508,7 @@ def node_test(path_stack, nodesets, any_type: :element)
elsif prefix.empty?
raw_node.name == name and raw_node.namespace == ""
else
- # FIXME: This DOUBLES the time XPath searches take
- ns = get_namespace(raw_node.element, prefix)
- raw_node.name == name and raw_node.namespace == ns
+ raw_node.name == name and raw_node.namespace == get_namespace(raw_node.element, prefix)
end
else
false
@@ -671,7 +670,7 @@ def sort(array_of_nodes, order)
if order == :forward
index
else
- -index
+ index.map(&:-@)
end
end
ordered.collect do |_index, node|
diff --git a/test/parse/test_cdata.rb b/test/parse/test_cdata.rb
index b5f1a3bc..c742d6a1 100644
--- a/test/parse/test_cdata.rb
+++ b/test/parse/test_cdata.rb
@@ -7,10 +7,28 @@ module REXMLTests
class TestParseCData < Test::Unit::TestCase
include Test::Unit::CoreAssertions
+ def parse(xml)
+ REXML::Document.new(xml)
+ end
+
def test_linear_performance_gt
seq = [10000, 50000, 100000, 150000, 200000]
assert_linear_performance(seq, rehearsal: 10) do |n|
- REXML::Document.new('" * n + ' ]]>')
+ parse('" * n + ' ]]>')
+ end
+ end
+
+ class TestInvalid < self
+ def test_unclosed_cdata
+ exception = assert_raise(REXML::ParseException) do
+ parse("")
+ end
+ assert_equal(<<~DETAIL, exception.to_s)
+ Malformed CDATA: Missing end ']]>'
+ Line: 1
+ Position: 25
+ Last 80 unconsumed characters:
+ DETAIL
end
end
end
diff --git a/test/parse/test_comment.rb b/test/parse/test_comment.rb
index 4475dca7..6339835d 100644
--- a/test/parse/test_comment.rb
+++ b/test/parse/test_comment.rb
@@ -17,7 +17,7 @@ def test_toplevel_unclosed_comment
parse("'
Line: 1
Position: 4
Last 80 unconsumed characters:
@@ -48,6 +48,31 @@ def test_toplevel_malformed_comment_end
DETAIL
end
+ def test_doctype_malformed_node
+ exception = assert_raise(REXML::ParseException) do
+ parse("'
+ Line: 1
+ Position: 19
+ Last 80 unconsumed characters:
+ DETAIL
+ end
+
def test_doctype_malformed_comment_inner
exception = assert_raise(REXML::ParseException) do
parse("")
@@ -72,16 +97,28 @@ def test_doctype_malformed_comment_end
DETAIL
end
- def test_after_doctype_malformed_comment_short
+ def test_after_doctype_malformed_node
exception = assert_raise(REXML::ParseException) do
- parse("")
+ parse("")
+ end
+ assert_equal(<<~DETAIL, exception.to_s)
+ Unclosed comment: Missing end '-->'
Line: 1
Position: 8
Last 80 unconsumed characters:
- -->
DETAIL
end
diff --git a/test/test_core.rb b/test/test_core.rb
index 34fe9e07..651056f2 100644
--- a/test/test_core.rb
+++ b/test/test_core.rb
@@ -653,18 +653,23 @@ def test_namespace
assert_equal "Some text", out
end
-
def test_add_namespace
e = Element.new 'a'
+ assert_equal("", e.namespace)
+ assert_nil(e.namespace('foo'))
e.add_namespace 'someuri'
e.add_namespace 'foo', 'otheruri'
e.add_namespace 'xmlns:bar', 'thirduri'
- assert_equal 'someuri', e.attributes['xmlns']
- assert_equal 'otheruri', e.attributes['xmlns:foo']
- assert_equal 'thirduri', e.attributes['xmlns:bar']
+ assert_equal("someuri", e.namespace)
+ assert_equal("otheruri", e.namespace('foo'))
+ assert_equal("otheruri", e.namespace('xmlns:foo'))
+ assert_equal("thirduri", e.namespace('bar'))
+ assert_equal("thirduri", e.namespace('xmlns:bar'))
+ assert_equal('someuri', e.attributes['xmlns'])
+ assert_equal('otheruri', e.attributes['xmlns:foo'])
+ assert_equal('thirduri', e.attributes['xmlns:bar'])
end
-
def test_big_documentation
d = File.open(fixture_path("documentation.xml")) {|f| Document.new f }
assert_equal "Sean Russell", d.elements["documentation/head/author"].text.tr("\n\t", " ").squeeze(" ")
@@ -764,9 +769,15 @@ def test_attributes_each
def test_delete_namespace
doc = Document.new ""
+ assert_equal("1", doc.root.namespace)
+ assert_equal("2", doc.root.namespace('x'))
+ assert_equal("2", doc.root.namespace('xmlns:x'))
doc.root.delete_namespace
doc.root.delete_namespace 'x'
- assert_equal "", doc.to_s
+ assert_equal("", doc.to_s)
+ assert_equal("", doc.root.namespace)
+ assert_nil(doc.root.namespace('x'))
+ assert_nil(doc.root.namespace('xmlns:x'))
end
def test_each_element_with_attribute
diff --git a/test/test_jaxen.rb b/test/test_jaxen.rb
index 6038e88e..548120d6 100644
--- a/test/test_jaxen.rb
+++ b/test/test_jaxen.rb
@@ -56,7 +56,9 @@ def process_test_case(name)
# processes a tests/document/context node
def process_context(doc, context)
- test_context = XPath.match(doc, context.attributes["select"])
+ matched = XPath.match(doc, context.attributes["select"])
+ assert_equal(1, matched.size)
+ test_context = matched.first
namespaces = context.namespaces
namespaces.delete("var")
namespaces = nil if namespaces.empty?
@@ -101,10 +103,14 @@ def process_nominal_test(context, variables, namespaces, test)
assert_equal(Integer(expected, 10),
matched.size,
user_message(context, xpath, matched))
+ else
+ assert_operator(matched.size, :>, 0, user_message(context, xpath, matched))
end
XPath.each(test, "valueOf") do |value_of|
- process_value_of(matched, variables, namespaces, value_of)
+ matched.each do |subcontext|
+ process_value_of(subcontext, variables, namespaces, value_of)
+ end
end
end
@@ -118,10 +124,8 @@ def process_exceptional_test(context, variables, namespaces, test)
def user_message(context, xpath, matched)
message = ""
- context.each_with_index do |node, i|
- message << "Node#{i}:\n"
- message << "#{node}\n"
- end
+ message << "Node:\n"
+ message << "#{context}\n"
message << "XPath: <#{xpath}>\n"
message << "Matched <#{matched}>"
message
diff --git a/test/xpath/test_base.rb b/test/xpath/test_base.rb
index 1dacd69d..764171ab 100644
--- a/test/xpath/test_base.rb
+++ b/test/xpath/test_base.rb
@@ -411,9 +411,107 @@ def test_preceding
s = ""
d = REXML::Document.new(s)
- c = REXML::XPath.match( d, "//c[@id = '5']")
- cs = REXML::XPath.match( c, "preceding::c" )
- assert_equal( 4, cs.length )
+ c = REXML::XPath.match(d, "//c[@id = '5']")
+ assert_equal(1, c.length)
+ cs = REXML::XPath.match(c.first, "preceding::c")
+ assert_equal(4, cs.length)
+ end
+
+ def test_preceding_multiple
+ source = <<-XML
+
+
+
+ XML
+ doc = REXML::Document.new(source)
+ matches = REXML::XPath.match(doc, "a/d/preceding::*")
+ assert_equal(["d", "c", "b"], matches.map(&:name))
+ end
+
+ def test_following_multiple
+ source = <<-XML
+
+
+
+ XML
+ doc = REXML::Document.new(source)
+ matches = REXML::XPath.match(doc, "a/d/following::*")
+ assert_equal(["d", "e", "f"], matches.map(&:name))
+ end
+
+ def test_following_sibling_across_multiple_nodes
+ source = <<-XML
+
+
+
+
+
+
+
+
+ XML
+ doc = REXML::Document.new(source)
+ matches = REXML::XPath.match(doc, "a/b/x/following-sibling::*")
+ assert_equal(["c", "d", "e"], matches.map(&:name))
+ end
+
+ def test_following_sibling_within_single_node
+ source = <<-XML
+
+
+
+
+
+ XML
+ doc = REXML::Document.new(source)
+ matches = REXML::XPath.match(doc, "a/b/x/following-sibling::*")
+ assert_equal(["c", "d", "x", "e"], matches.map(&:name))
+ end
+
+ def test_following_sibling_predicates
+ source = <<-XML
+
+ XML
+ doc = REXML::Document.new(source)
+ # Finds a node flowing
+ matches = REXML::XPath.match(doc, "//a/following-sibling::*[1]")
+ assert_equal(["w", "x", "y", "z"], matches.map(&:name))
+ end
+
+ def test_preceding_sibling_across_multiple_nodes
+ source = <<-XML
+
+
+
+
+
+
+
+
+ XML
+ doc = REXML::Document.new(source)
+ matches = REXML::XPath.match(doc, "a/b/x/preceding-sibling::*")
+ assert_equal(["e", "d", "c"], matches.map(&:name))
+ end
+
+ def test_preceding_sibling_within_single_node
+ source = <<-XML
+
+
+
+
+
+ XML
+ doc = REXML::Document.new(source)
+ matches = REXML::XPath.match(doc, "a/b/x/preceding-sibling::*")
+ assert_equal(["e", "x", "d", "c"], matches.map(&:name))
end
def test_following
@@ -1095,6 +1193,16 @@ def test_namespaces_0
assert_equal( 1, XPath.match( d, "//x:*" ).size )
end
+ def test_namespaces_cache
+ doc = Document.new("")
+ assert_equal("", XPath.first(doc, "//b[namespace-uri()='1']").to_s)
+ assert_nil(XPath.first(doc, "//b[namespace-uri()='']"))
+
+ doc.root.delete_namespace
+ assert_nil(XPath.first(doc, "//b[namespace-uri()='1']"))
+ assert_equal("", XPath.first(doc, "//b[namespace-uri()='']").to_s)
+ end
+
def test_ticket_71
doc = Document.new(%Q{})
el = doc.root.elements[1]
@@ -1158,5 +1266,15 @@ def test_or_and
end
assert_equal(["/"], hrefs, "Bug #3842 [ruby-core:32447]")
end
+
+ def test_match_with_deprecated_usage
+ verbose, $VERBOSE = $VERBOSE, nil
+ doc = Document.new("")
+ assert_equal(['b'], XPath.match([doc, doc], '//b').map(&:name))
+ assert_equal(['b'], XPath.match([doc], '//b').map(&:name))
+ assert_equal([], XPath.match([], '//b').map(&:name))
+ ensure
+ $VERBOSE = verbose
+ end
end
end