# frozen_string_literal: false

module REXMLTests
  class TestXPathBase < Test::Unit::TestCase
    include Helper::Fixture
    include REXML
    SOURCE = <<-EOF
      <a id='1'>
        <b id='2' x='y'>
          <c id='3'/>
          <c id='4'/>
        </b>
        <d id='5'>
          <c id='6' x='y'/>
          <c id='7'/>
          <c id='8'/>
          <q id='19'/>
        </d>
        <e id='9'>
          <f id='10' a='b'/>
          <f id='11' a='c'/>
          <f id='12' a='d'>
            <g id='13'/>
          </f>
          <f id='14' a='d'/>
        </e>
        <m id='15'>
          <n id='16'>
            <o id='17'>
              <p id='18'/>
            </o>
          </n>
        </m>
      </a>
      EOF
    JENI_TENNISON = <<-EOJT
    <a>
      <b>
        <c>
          <d>
            <e id='x'>
              <f/>
            </e>
          </d>
        </c>
        <c>
          <d>
            <e id='y'/>
          </d>
        </c>
      </b>
      <b>
        <c>
          <d>
            <e id='z'/>
          </d>
        </c>
      </b>
    </a>
    EOJT

    def setup
      @@doc = Document.new(SOURCE) unless defined? @@doc
      @@jeni = Document.new( JENI_TENNISON ) unless defined? @@jeni
    end

    def each_test( element, xpath )
      count = 0
      XPath::each( element, xpath ) { |child|
        count += 1
        yield child if block_given?
      }
      count
    end

    def test_descendant
      doc = Document.new("<a><b><c id='1'/></b><d><b><c id='2'/></b></d></a>")
      p = XPath.match( doc, "//c" )
      assert_equal( 2, p.size )
      p = XPath.first( @@doc, "//p" )
      assert_equal "p", p.name
      c = each_test( @@doc, "//c" ) { |child| assert_equal "c", child.name }
      assert_equal 5, c
      c = each_test( @@doc.root, "b//c" ) { |child|
          assert_equal "c", child.name
      }
      assert_equal 2, c

      doc = Document.new( "<a><z id='1'/><b><z id='11'/><z id='12'/></b><c><z id='21'/><z id='22'/><d><z id='31'/><z id='32'/></d></c></a>" )
      # //para[1] : all descendants which are the first para child of their parent
      assert_equal( 4, XPath.match( doc, "//z[1]" ).size )
      # /descendant::para[1] : the first descendant para element
      assert_equal( 1, XPath.match( doc, "/descendant::z[1]" ).size )
    end

    def test_root
      source = "<a><b/></a>"
      doc = Document.new( source )
      assert_equal doc, doc.root_node
      assert_equal "a", XPath::first( doc, "/" ).elements[1].name
    end

    def test_abbreviated_simple_child
      assert_equal "a", XPath::first(@@doc, "a").name
    end

    def test_child
      c = XPath::first( @@doc, "a/b/c" )
      assert_equal "c", c.name
      assert_equal "3", XPath::first(@@doc, "a/b/c").attributes["id"]
    end

    def test_root_child
      assert_equal "a", XPath::first(@@doc, "/a").name
      c = XPath::first( @@doc, "a/b/c" )
      assert_equal "a", XPath::first(c, "/a").name
    end

    def test_root_children
      c = XPath::first( @@doc, "a/b/c" )
      assert_equal "2", XPath::first(c, "/a/b").attributes["id"]
    end

    def test_abbreviated_step
      c = XPath::first( @@doc, "a/b/c" )
      assert_equal("c", c.name)
      assert_equal("a", XPath::first(@@doc.root, ".").name)
      assert_equal("b", XPath::first(c, "..").name)
      assert_equal("a", XPath::first(@@doc, "a/b/..").name)

      doc = File.open(fixture_path("project.xml")) do |f|
        REXML::Document.new(f)
      end
      c = each_test(doc.root, "./Description" ) { |child|
        assert_equal("Description",child.name)
      }
      assert_equal 1, c
    end

    # Things that aren't tested elsewhere
    def test_predicates
      assert_equal "12", XPath::first(@@doc, "a/e/f[3]").attributes["id"]
      assert_equal "13", XPath::first(@@doc, "a/e/f[3]/g").attributes["id"]
      assert_equal "14", XPath::first(@@doc, "a/e/f[@a='d'][2]").attributes["id"]
      assert_equal "14", XPath::first(@@doc, "a/e/f[@a='d'][@id='14']").attributes["id"]
      assert_equal "a", XPath::first( @@doc, "*[name()='a' and @id='1']" ).name
      c=each_test( @@doc, "//*[name()='f' and @a='d']") { |i|
        assert_equal "f", i.name
      }
      assert_equal 2, c
      c=each_test( @@doc, "//*[name()='m' or @a='d']") { |i|
        assert ["m","f"].include?(i.name)
      }
      assert_equal 3, c

      assert_equal "b", XPath::first( @@doc, "//b[@x]" ).name
    end

    def test_node_type
      doc = Document.new "<a><?foo bar?><!--comment-->text</a>"
      #res = XPath::first(doc.root, "text()")
      #assert_equal "text", res.to_s

      #res = XPath::first(doc, "*")
      #assert_equal "a", res.name

      assert_equal( :processing_instruction,
      XPath::first(doc.root, "processing-instruction()").node_type)
      assert_equal( :comment, XPath::first(doc.root, "comment()").node_type)
    end

    def test_functions
      # trivial text() test
      # confuse-a-function
      source = "<a>more <b id='1'/><b id='2'>dumb</b><b id='3'/><c/> text</a>"
      doc = Document.new source
      res = ""
      #XPath::each(doc.root, "text()") {|val| res << val.to_s}
      #assert_equal "more  text", res

      #res = XPath::first(doc.root, "b[last()]")
      #assert_equal '3', res.attributes['id']
      res = XPath::first(doc.root, "b[position()=2]")
      assert_equal '2', res.attributes['id']
      res = XPath::first(doc.root, "*[name()='c']")
      assert_equal "c", res.name
    end

    def no_test_ancestor
      doc = REXML::Document.new(File.new(fixture_path("testsrc.xml")))
      doc.elements.each("//item") { |el| print el.name
        if el.attributes['x']
          puts " -- "+el.attributes['x']
        else
          puts
        end
      }
      doc.elements.each("//item/ancestor::") { |el| print el.name
        if el.attributes['x']
          puts " -- "+el.attributes['x']
        else
          puts
        end
      }
    end

    # Here are some XPath tests that were originally submitted by ...
    # The code has changed some, but the logic and the source documents are the
    # same.
    # This method reads a document from a file, and then a series of xpaths,
    # also from a file.  It then checks each xpath against the source file.
    def test_more
      xmlsource   = fixture_path("testsrc.xml")
      xpathtests  = fixture_path("xp.tst")

      doc = File.open(xmlsource) {|f| REXML::Document.new(f) }
      #results = ""
      results = REXML::Document.new
      results.add_element "test-results"
      File.foreach(xpathtests) do |line|
        line.strip!
        begin
          doc.root
          #puts "#"*80
          #print "\nDoing #{line} " ; $stdout.flush
          doc.elements.each(line) do |el|
            #print "." ; $stdout.flush
            results.root << el.clone
            #results << el.to_s
          end
          #ObjectSpace.garbage_collect
          GC::start
        rescue Exception => z
          #puts "\n'#{line}' failed"
          fail("Error on line #{line}:\n#{z.message}\n"+z.backtrace[0,10].join("\n"))
          #results.root.add_element( "error", {"path"=>line}).text = z.message+"\n"+z.backtrace[0,10].join("\n")
          #results << "<error path='"+line+"'>"+z.message+"</error>"
        end
      end
    end

    def test_axe_descendant
      assert_equal "f", XPath::first( @@doc, "descendant::f").name
    end

    def test_axe_parent
      q = XPath.first( @@doc, "a/d/c/parent::*/q" )
      assert_equal 19, q.attributes["id"].to_i
    end

    def test_abbreviated_attribute
      assert_equal 'a', XPath::first( @@doc, "a[@id='1']" ).name
      c = XPath::first( @@doc, "a/b/c[@id='4']" )
      assert_equal 'c', c.name
      assert_equal '4', c.attributes['id']

      result = XPath::first( @@doc, "descendant::f[@a='c']")
      assert_equal "11", result.attributes['id']

      assert_equal "11", XPath::first(@@doc, "a/e/f[@a='c']").attributes["id"]
      assert_equal "11", XPath::first(@@doc, "a/e/*[@a='c']").attributes["id"]
    end

    def test_axe_self
      c = XPath::first( @@doc, "a/b/c" )
      assert c
      assert_equal "c", c.name
      assert_equal "c", XPath::first( c, "self::node()" ).name
    end

    def test_axe_ancestor
      doc = REXML::Document.new "
      <a>
        <b id='1'>
          <c>
            <b id='2'>
              <d/>
            </b>
          </c>
        </b>
      </a>"

      d = XPath.first( doc, "//d" )
      assert_equal "d", d.name
      b = each_test( d, "ancestor::b" ) { |el|
        assert((1..2) === el.attributes['id'].to_i,
          "Expected #{el.attributes['id']} to be either 1 or 2"
        )
      }
      assert_equal 2, b
    end

    def test_axe_child
      m = XPath.first( @@doc, "a/child::m" )
      assert_equal 15, m.attributes['id'].to_i
    end

    def test_axe_attribute
      a = XPath.first( @@doc, "a/attribute::id" )
      assert_equal "1", a.value
      a = XPath.first( @@doc, "a/e/f[@id='14']/attribute::a" )
      assert_equal "d", a.value
    end

    def test_axe_sibling
      doc = Document.new "<a><b><c/></b><e><f id='10'/><f id='11'/><f id='12'/></e></a>"
      first_f = XPath.first( doc, "a/e/f" )
      assert first_f
      assert_equal '10', first_f.attributes['id']
      next_f = XPath.first( doc, "a/e/f/following-sibling::node()" )
      assert_equal '11', next_f.attributes['id']

      b = XPath.first( doc, "a/e/preceding-sibling::node()" )
      assert_equal 'b', b.name
    end

    def test_lang
      doc = File.open(fixture_path("lang0.xml")) {|f| Document.new(f) }
      #puts IO.read( "test/lang.xml" )

      #puts XPath.match( doc, "//language/*" ).size
      c = each_test( doc, "//language/*" ) { |element|
        #puts "#{element.name}: #{element.text}"
      }
      assert_equal 4, c
    end

    def test_namespaces_1
      source = <<-EOF
        <foo xmlns:ts="this" xmlns:tt="that">
          <ts:bar>this bar</ts:bar>
          <tt:bar>that bar</tt:bar>
        </foo>
      EOF
      doc = Document.new source
      XPath.each( doc, "//bar" ) {
        fail "'bar' should match nothing in this case"
      }

      namespace = {"t"=>"this"}
      results = XPath.first( doc, "//t:bar", namespace )
      assert_equal "this bar", results.text
    end

    def test_namespaces_2
      source = <<-EOF
        <foo xmlns:ts="this" xmlns:tt="that">
          <ts:bar>this bar</ts:bar>
          <tt:bar>that bar</tt:bar>
        </foo>
      EOF
      doc = Document.new source
      res = XPath::first(doc, "//*[local_name()='bar']")
      assert res, "looking for //*[name()='bar']"
      assert_equal 'this', res.namespace
      res = XPath::first(doc.root, "*[namespace_uri()='that']")
      assert_equal 'that bar', res.text
    end

    def test_complex
      next_f = XPath.first( @@doc, "a/e/f[@id='11']/following-sibling::*" )
      assert_equal 12, next_f.attributes['id'].to_i
      prev_f = XPath.first( @@doc, "a/e/f[@id='11']/preceding-sibling::*" )
      assert_equal 10, prev_f.attributes['id'].to_i
      c = each_test( @@doc, "descendant-or-self::*[@x='y']" )
      assert_equal 2, c
    end

    def match(xpath)
      XPath.match(@@doc, xpath).collect(&:to_s)
    end

    def test_grouping
      assert_equal([],
                   match("a/d/*[name()='d' and (name()='f' or name()='q')]"))
      assert_equal(["<q id='19'/>"],
                   match("a/d/*[(name()='d' and name()='f') or name()='q']"))
    end

    def test_preceding
      d = Document.new "<a><b id='0'/><b id='2'/><b><c id='0'/><c id='1'/><c id='2'/></b><b id='1'/></a>"
      start = XPath.first( d, "/a/b[@id='1']" )
      assert_equal 'b', start.name
      c = XPath.first( start, "preceding::c" )
      assert_equal '2', c.attributes['id']

      c1, c0 = XPath.match( d, "/a/b/c[@id='2']/preceding::node()" )
      assert_equal '1', c1.attributes['id']
      assert_equal '0', c0.attributes['id']

      c2, c1, c0, b, b2, b0 = XPath.match( start, "preceding::node()" )

      assert_equal 'c', c2.name
      assert_equal 'c', c1.name
      assert_equal 'c', c0.name
      assert_equal 'b', b.name
      assert_equal 'b', b2.name
      assert_equal 'b', b0.name

      assert_equal '2', c2.attributes['id']
      assert_equal '1', c1.attributes['id']
      assert_equal '0', c0.attributes['id']
      assert b.attributes.empty?
      assert_equal '2', b2.attributes['id']
      assert_equal '0', b0.attributes['id']

      d = REXML::Document.new("<a><b/><c/><d/></a>")
      matches = REXML::XPath.match(d, "/a/d/preceding::node()")
      assert_equal("c", matches[0].name)
      assert_equal("b", matches[1].name)

      s = "<a><b><c id='1'/></b><b><b><c id='2'/><c id='3'/></b><c id='4'/></b><c id='NOMATCH'><c id='5'/></c></a>"
      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 )
    end

    def test_following
      d = Document.new "<a><b id='0'/><b/><b><c id='1'/><c id='2'/></b><b id='1'/></a>"
      start = XPath.first( d, "/a/b[@id='0']" )
      assert_equal 'b', start.name
      c = XPath.first( start, "following::c" )
      assert_equal '1', c.attributes['id']

      s = "<a><b><c><d/></c><e/></b><f><g><h/><i/></g></f><i/></a>"
      d = Document.new(s)
      c = XPath.first(d, '/a/b/c')
      assert_equal 'c', c.name
      res = XPath.match( c, 'following::*' )
      assert_equal 6, res.size
      res = XPath.match( c, 'following::i' )
      assert_equal 2, res.size
    end

    # The following three paths were provided by
    # Jeni Tennison <jeni@jenitennison.com>
    # a consultant who is also an XSL and XPath expert
    #def test_child_cubed
    #  els = @@jeni.elements.to_a("*****")
    #  assert_equal 3, els.size
    #end

    #def test_div_2
    #  results = doc.elements.to_a("/ div 2")
    #end

    #def test_nested_predicates
    #  puts @@jeni.root.elements[1].elements[1].name
    #  results = @@jeni.root.elements[1].elements[1].elements.to_a("../following-sibling::*[*[name() = name(current())]]")
    #  puts results
    #end

    def test_nested_predicates
      doc = Document.new <<-EOF
        <div>
          <div>
            <test>ab</test>
            <test>cd</test>
          </div>
          <div>
            <test>ef</test>
            <test>gh</test>
          </div>
          <div>
            <test>hi</test>
          </div>
        </div>
      EOF

      matches = XPath.match(doc, '(/div/div/test[0])').map(&:text)
      assert_equal [], matches
      matches = XPath.match(doc, '(/div/div/test[1])').map(&:text)
      assert_equal ["ab", "ef", "hi"], matches
      matches = XPath.match(doc, '(/div/div/test[2])').map(&:text)
      assert_equal ["cd", "gh"], matches
      matches = XPath.match(doc, '(/div/div/test[3])').map(&:text)
      assert_equal [], matches

      matches = XPath.match(doc, '(/div/div/test[1])[1]').map(&:text)
      assert_equal ["ab"], matches
      matches = XPath.match(doc, '(/div/div/test[1])[2]').map(&:text)
      assert_equal ["ef"], matches
      matches = XPath.match(doc, '(/div/div/test[1])[3]').map(&:text)
      assert_equal ["hi"], matches
      matches = XPath.match(doc, '(/div/div/test[2])[1]').map(&:text)
      assert_equal ["cd"], matches
      matches = XPath.match(doc, '(/div/div/test[2])[2]').map(&:text)
      assert_equal ["gh"], matches
      matches = XPath.match(doc, '(/div/div/test[2])[3]').map(&:text)
      assert_equal [], matches
    end

    # Contributed by Mike Stok
    def test_starts_with
      source = <<-EOF
        <foo>
        <a href="mailto:a@b.c">a@b.c</a>
        <a href="http://www.foo.com">http://www.foo.com</a>
        </foo>
      EOF
      doc = Document.new source
      mailtos = doc.elements.to_a("//a[starts-with(@href, 'mailto:')]")
      assert_equal 1, mailtos.size
      assert_equal "mailto:a@b.c", mailtos[0].attributes['href']

      ailtos = doc.elements.to_a("//a[starts-with(@href, 'ailto:')]")
      assert_equal 0, ailtos.size
    end

    def test_toms_text_node
      file = "<a>A<b>B</b><c>C<d>D</d>E</c>F</a>"
      doc = Document.new(file)
      assert_equal 'A', XPath.first(doc[0], 'text()').to_s
      assert_equal 'AF', XPath.match(doc[0], 'text()').collect { |n|
        n.to_s
      }.join('')
      assert_equal 'B', XPath.first(doc[0], 'b/text()').to_s
      assert_equal 'D', XPath.first(doc[0], '//d/text()').to_s
      assert_equal 'ABCDEF', XPath.match(doc[0], '//text()').collect {|n|
        n.to_s
      }.join('')
    end

    def test_string_length
      doc = Document.new <<-EOF
        <AAA>
        <Q/>
        <SSSS/>
        <BB/>
        <CCC/>
        <DDDDDDDD/>
        <EEEE/>
        </AAA>
      EOF
      assert doc, "create doc"

      set = doc.elements.to_a("//*[string-length(name()) = 3]")
      assert_equal 2, set.size, "nodes with names length = 3"

      set = doc.elements.to_a("//*[string-length(name()) < 3]")
      assert_equal 2, set.size, "nodes with names length < 3"

      set = doc.elements.to_a("//*[string-length(name()) > 3]")
      assert_equal 3, set.size, "nodes with names length > 3"
    end

    # Test provided by Mike Stok
    def test_contains
      source = <<-EOF
        <foo>
        <a href="mailto:a@b.c">a@b.c</a>
        <a href="http://www.foo.com">http://www.foo.com</a>
        </foo>
      EOF
      doc = Document.new source

      [
      #['o', 2],
      ['foo', 1], ['bar', 0]].each { |search, expected|
        set = doc.elements.to_a("//a[contains(@href, '#{search}')]")
        assert_equal expected, set.size
      }
    end

    # Mike Stok and Sean Russell
    def test_substring
      # examples from http://www.w3.org/TR/xpath#function-substring
      doc = Document.new('<test string="12345" />')

      Document.new("<a b='1'/>")
      #puts XPath.first(d, 'node()[0 + 1]')
      #d = Document.new("<a b='1'/>")
      #puts XPath.first(d, 'a[0 mod 0]')
      [ [1.5, 2.6, '234'],
        [0, 3, '12'],
        [0, '0 div 0', ''],
        [1, '0 div 0', ''],
        ['-42', '1 div 0', '12345'],
        ['-1 div 0', '1 div 0', '']
      ].each { |start, length, expected|
        set = doc.elements.to_a("//test[substring(@string, #{start}, #{length}) = '#{expected}']")
        assert_equal 1, set.size, "#{start}, #{length}, '#{expected}'"
      }
    end

    def test_translate
      source = <<-EOF
      <doc>
      <case name='w3c one' result='BAr' />        <!-- w3c -->
      <case name='w3c two' result='AAA' />        <!-- w3c -->
      <case name='alchemy' result="gold" />   <!-- mike -->
      <case name='vbxml one' result='A Space Odyssey' />
      <case name='vbxml two' result='AbCdEf' />
      </doc>
      EOF

      doc = Document.new(source)

      [ ['bar', 'abc', 'ABC', 'w3c one'],
      ['--aaa--','abc-','ABC', 'w3c two'],
      ['lead', 'dear language', 'doll groover', 'alchemy'],
      ['A Space Odissei', 'i', 'y', 'vbxml one'],
      ['abcdefg', 'aceg', 'ACE', 'vbxml two'],
      ].each { |arg1, arg2, arg3, name|
        translate = "translate('#{arg1}', '#{arg2}', '#{arg3}')"
        set = doc.elements.to_a("//case[@result = #{translate}]")
        assert_equal 1, set.size, translate
        assert_equal name, set[0].attributes['name']
      }
    end

    def test_math
      d = Document.new( '<a><b/><c/></a>' )
      assert XPath.first( d.root, 'node()[1]' )
      assert_equal 'b', XPath.first( d.root, 'node()[1]' ).name
      assert XPath.first( d.root, 'node()[0 + 1]' )
      assert_equal 'b', XPath.first( d.root, './node()[0 + 1]' ).name
      assert XPath.first( d.root, 'node()[1 + 1]' )
      assert_equal 'c', XPath.first( d.root, './node()[1 + 1]' ).name
      assert XPath.first( d.root, 'node()[4 div 2]' )
      assert_equal 'c', XPath.first( d.root, './node()[4 div 2]' ).name
      assert XPath.first( d.root, 'node()[2 - 1]' )
      assert_equal 'b', XPath.first( d.root, './node()[2 - 1]' ).name
      assert XPath.first( d.root, 'node()[5 mod 2]' )
      assert_equal 'b', XPath.first( d.root, './node()[5 mod 2]' ).name
      assert XPath.first( d.root, 'node()[8 mod 3]' )
      assert_equal 'c', XPath.first( d.root, './node()[8 mod 3]' ).name
      assert XPath.first( d.root, 'node()[1 * 2]' )
      assert_equal 'c', XPath.first( d.root, './node()[1 * 2]' ).name
      assert XPath.first( d.root, 'node()[2 + -1]' )
      assert_equal 'b', XPath.first( d.root, './node()[2 + -1]' ).name
    end

    def test_name
      assert_raise( UndefinedNamespaceException, "x should be undefined" ) {
        REXML::Document.new("<a x='foo'><b/><x:b/></a>")
      }
      d = REXML::Document.new("<a xmlns:x='foo'><b/><x:b/></a>")
      assert_equal 1, d.root.elements.to_a('*[name() = "b"]').size
      assert_equal 1, d.elements.to_a('//*[name() = "x:b"]').size
    end

    def test_local_name
      d = REXML::Document.new("<a xmlns:x='foo'><b/><x:b/></a>")
      assert_equal 2, d.root.elements.to_a('*[local_name() = "b"]').size
      assert_equal 2, d.elements.to_a('//*[local_name() = "b"]').size
    end

    def test_comparisons
      source = "<a><b id='1'/><b id='2'/><b id='3'/></a>"
      doc = REXML::Document.new(source)

      # NOTE: check that number() is required
      assert_equal 2, REXML::XPath.match(doc, "//b[number(@id) > 1]").size
      assert_equal 3, REXML::XPath.match(doc, "//b[number(@id) >= 1]").size
      assert_equal 1, REXML::XPath.match(doc, "//b[number(@id) <= 1]").size
      assert_equal 1, REXML::XPath.match(doc, "//b[number(@id) = (1 * 1)]").size
      assert_equal 1, REXML::XPath.match(doc, "//b[number(@id) = (1 mod 2)]").size
      assert_equal 1, REXML::XPath.match(doc, "//b[number(@id) = (4 div 2)]").size
    end

    # Contributed by Kouhei
    def test_substring_before
      doc = Document.new("<r><a/><b/><c/></r>")
      assert_equal("a", doc.root.elements.to_a("*[name()=substring-before('abc', 'b')]")[0].name)
      assert_equal("c", doc.root.elements.to_a("*[name()=substring-after('abc', 'b')]")[0].name)
    end

    def test_spaces
      doc = Document.new("<a>
        <b>
          <c id='a'/>
        </b>
        <c id='b'/>
        <c id='c'/>
        <c/>
      </a>")
      match = lambda do |xpath|
        REXML::XPath.match(doc, xpath).collect(&:to_s)
      end
      assert_equal(["<c id='b'/>"],
                   match.call("//*[local-name()='c' and @id='b']"))
      assert_equal(["<c id='b'/>"],
                   match.call("//*[ local-name()='c' and @id='b' ]"))
      assert_equal(["<c id='b'/>"],
                   match.call("//*[ local-name() = 'c' and @id = 'b' ]"))
      assert_equal(["<c id='b'/>", "<c id='c'/>"],
                   match.call('/a/c[@id]'))
      assert_equal(["<c id='b'/>", "<c id='c'/>"],
                   match.call('/a/c[(@id)]'))
      assert_equal(["<c id='b'/>", "<c id='c'/>"],
                   match.call('/a/c[ @id ]'))
      assert_equal(["<c id='b'/>", "<c id='c'/>"],
                   match.call('/a/c[ (@id) ]'))
      assert_equal(["<c id='b'/>", "<c id='c'/>"],
                   match.call('/a/c[( @id )]'))
      assert_equal(["<c id='b'/>", "<c id='c'/>"],
                   match.call('/a/c[ ( @id ) ]'))
      assert_equal(["<c id='b'/>", "<c id='c'/>"],
                   match.call('/a/c [ ( @id ) ] '))
      assert_equal(["<c id='b'/>", "<c id='c'/>"],
                   match.call(' / a / c [ ( @id ) ] '))
      assert_equal(["<c id='b'/>", "<c id='c'/>"],
                   match.call('/ a / child:: c [( @id )] /'))
    end

    def test_text_nodes
      #  source = "<root>
      #<child/>
      #<child>test</child>
      #</root>"
      source = "<root><child>test</child></root>"
      d = REXML::Document.new( source )
      r = REXML::XPath.match( d, %q{/root/child[text()="test"]} )
      assert_equal( 1, r.size )
      assert_equal( "child", r[0].name )
      assert_equal( "test", r[0].text )
    end

    def test_auto_string_value
      source = "<root><foo/><title>Introduction</title></root>"
      d = REXML::Document.new( source )
      #r = REXML::XPath.match( d, %q{/root[title="Introduction"]} )
      #assert_equal( 1, r.size )
      source = "<a><b/><c/><c>test</c></a>"
      d = REXML::Document.new( source )
      r = REXML::XPath.match( d, %q{/a[c='test']} )
      assert_equal( 1, r.size )
      r = REXML::XPath.match( d, %q{a[c='test']} )
      assert_equal( 1, r.size )
      r = d.elements["/a[c='test']"]
      assert_not_nil( r )
      r = d.elements["a[c='test']"]
      assert_not_nil( r )
      r = d.elements["a[c='xtest']"]
      assert_nil( r )
      r = REXML::XPath.match( d, %q{a[c='xtest']} )
      assert_equal( 0, r.size )
    end

    def test_ordering
      source = <<-XML
<a>
  <b>
    <c id='1'/>
    <c id='2'/>
  </b>
  <b>
    <d id='3'/>
    <d id='4'/>
  </b>
</a>
      XML
      d = REXML::Document.new( source )
      r = REXML::XPath.match( d, %q{/a/*/*[1]} )
      assert_equal(["1", "3"],
                   r.collect {|element| element.attribute("id").value})
    end

    def test_descendant_or_self_ordering
      source = "<a>
      <b>
        <c id='1'/>
        <c id='2'/>
      </b>
      <b>
        <d id='1'>
          <c id='3'/>
        </d>
        <d id='2'>
          <e>
            <c id='4'/>
          </e>
        </d>
      </b>
    </a>"
      d = REXML::Document.new( source )
      cs = XPath.match( d, "/descendant-or-self::c" )
      assert_equal( 4, cs.length )
      1.upto(4) {|x| assert_equal( x.to_s, cs[x-1].attributes['id'] ) }
    end

    def test_and
      d = Document.new %q{<doc><route run='*' title='HNO'
      destination='debian_production1' date='*' edition='*'
      source='debian_satellite1'/></doc>}
      assert_equal( nil, d.root.elements["route[@run='0']"] )
      assert_equal( nil, d.root.elements["route[@run='0' and @title='HNO']"] )
    end


    def test_numbers
      d = Document.new %q{<a x="0" y="*" z="4e" w="e4" v="a"/>}

      xp1 = "/a[ @x = 0 ]"
      xp2 = "/a[ @x = '0' ]"
      xp3 = "/a[ (@x + 1) = 1 ]"
      xp4 = "/a[ @y = 0 ]"
      xp5 = "/a[ (@z + 1) = 5 ]"
      xp6 = "/a[ (@w + 1) = 5 ]"
      xp7 = "/a[ (@v + 1) = 1 ]"
      xp8 = "/a[ @n = 0 ]"

      assert_equal( 1, XPath.match( d, xp1 ).length )
      assert_equal( 1, XPath.match( d, xp2 ).length )
      assert_equal( 1, XPath.match( d, xp3 ).length )
      assert_equal( 0, XPath.match( d, xp4 ).length )
      assert_equal( 0, XPath.match( d, xp5 ).length )
      assert_equal( 0, XPath.match( d, xp6 ).length )
      assert_equal( 0, XPath.match( d, xp7 ).length )
      assert_equal( 0, XPath.match( d, xp8 ).length )
    end

    def test_tobis_preceding
      doc_string = '<a>
  <b/>
  <c>
    <d/>
    <e/>
  </c>
</a>'

      doc = Document.new(doc_string)

      # e = REXML::XPath.first(doc,'/a/c/e')
      e = doc.root.get_elements('/a/c/e')[0]
      assert_equal( 1, e.get_elements('preceding-sibling::*').length )
      assert_equal( 2, XPath.match(e, 'preceding::*').length )
    end


    def test_filtering
      #doc=Document.new("<a><b><c1/><c2/></b><b><c3/><c4/></b><b><c5/><c6/></b></a>")
      #assert_equal( 3, XPath.match( doc, '/a/b/*[1]' ).length )
      #assert_equal( 2, XPath.match( doc, '/a/b/following-sibling::*[1]' ).length )
    end

    # Submitted by Alex
    def test_union
      data = %Q{<div id="the_div">
  <span id="the_span">
  <strong id="the_strong">a</strong>
  </span>
  <em id="the_em2">b</em>
</div>}
      rd = REXML::Document.new( data )
      #union = rd.get_elements("/div/span | /div/em")
      #assert_equal(2, union.length, "/div/span | /div/em" )
      union = rd.get_elements('//*[name()="em" or name()="strong"]')
      assert_equal(2, union.length, 'name() and "or" failed')
      union = rd.get_elements('//em|//strong')
      assert_equal(2, union.length,
             'Both tag types are returned by XPath union operator')
    end


    def test_union2
      src = <<-EOL
<div id="the_div">
<span id="the_span">
<strong id="the_strong">a</strong>
</span>
<em id="the_em2">b</em>
</div>
      EOL
      rd = REXML::Document.new( src )
      union = rd.get_elements('//em|//strong')
      assert_equal(2, union.length,
        'Both tag types are returned by XPath union operator')
    end


    def test_a_star_star_one
      string = <<-EOL
<a>
  <b>
    <c1/>
    <d/>
    <e/>
    <f/>
  </b>
  <b>
    <c2/>
    <d/>
    <e/>
    <f/>
  </b>
</a>
      EOL
      d = REXML::Document.new( string )
      cs = XPath.match( d, '/a/*/*[1]' )
      assert_equal(["c1", "c2"], cs.collect(&:name))
    end

    def test_sum
      d = Document.new(<<-XML)
<a>
  <b>1</b>
  <b>2</b>
  <b>3</b>
  <c>
    <d>1</d>
    <d>2</d>
  </c>
  <e att='1'/>
  <e att='2'/>
</a>
      XML

      assert_equal([6], XPath::match(d, "sum(/a/b)"))
      assert_equal([9], XPath::match(d, "sum(//b | //d)"))
      assert_equal([3], XPath::match(d, "sum(/a/e/@*)"))
    end

    def test_xpath_namespace
      d = REXML::Document.new(<<-XML)
<tag1 xmlns='ns1'>
  <tag2 xmlns='ns2'/>
  <tada>xa</tada>
  <tada xmlns=''>xb</tada>
</tag1>
      XML
      actual = []
      d.root.each_element('tada') do |element|
        actual << element.to_s
      end
      assert_equal(["<tada>xa</tada>", "<tada xmlns=''>xb</tada>"],
                   actual)
    end

    def test_ticket_39
      doc = REXML::Document.new( <<-EOL )
      <rss>
        <channel>
          <!-- removing the namespace declaration makes the test pass -->
          <convertLineBreaks xmlns="http://www.blogger.com/atom/ns#">true</convertLineBreaks>
          <item>
            <title>Item 1</title>
          </item>
          <item>
            <title>Item 2</title>
            <pubDate>Thu, 13 Oct 2005 19:59:00 +0000</pubDate>
          </item>
          <item>
            <title>Item 3</title>
          </item>
        </channel>
      </rss>
      EOL
      root_node = XPath.first(doc, "rss")
      assert_not_nil root_node
      channel_node = XPath.first(root_node, "channel")
      assert_not_nil channel_node
      items = XPath.match(channel_node, "*")
      assert_equal 4, items.size
      items = XPath.match(channel_node, "item")
      assert_equal 3, items.size  # fails
    end


    def test_ticket_42
      source = "<a></a>"
      doc = Document.new(source)
      bElem = Element.new('b')
      doc.root.add_element(bElem)
      doc.elements.each('//b[name(..) = "a"]') { |x|
        assert_equal x,bElem
      }
    end

    def test_ticket_56
      namespaces = {'h' => 'http://www.w3.org/1999/xhtml'}

      finaldoc = REXML::Document.new(File.read(fixture_path('google.2.xml')))

      column_headers = []

      REXML::XPath.each(finaldoc, '//h:form[@action="ModifyCampaign"]//h:th',
                        namespaces) do |el|
        node = REXML::XPath.first(el, 'h:a/text()', namespaces)
        column_headers << (node ? node.value : nil)
      end
      column_headers.map! { |h| h.to_s.strip.chomp }
      expected = ["", "", "Current Status", "Current Budget",
        "Clicks", "Impr.", "CTR", "Avg. CPC", "Cost", "Conv. Rate",
        "Cost/Conv."]
      assert_equal( expected, column_headers )
    end


    def test_ticket_70
      string = <<EOF

<mydoc>

    <someelement attribute="1.10">Text1, text,
text</someelement>

    <someelement attribute="1.11">Text2, text,
text</someelement>


</mydoc>

EOF
      doc = Document.new string
      assert_equal( 1, XPath.match( doc, "//someelement[contains(@attribute,'1.10')]" ).length )
    end

    def test_ticket_43
      #url = http://news.search.yahoo.com/news/rss?p=market&ei=UTF-8&fl=0&x=wrt

      sum = File.open(fixture_path("yahoo.xml")) do |f|
        Document.new(f).elements.to_a("//item").size
      end
      assert_equal( 10, sum )

      text = File.open(fixture_path("yahoo.xml")) do |f|
        Document.new(f).elements.to_a(%Q{//title[contains(text(), "'")]}).collect{|e| e.text}.join
      end
      assert_equal( "Broward labor market's a solid performer (Miami Herald)", text )
    end

    def test_ticket_57
      data = "<?xml version='1.0'?><a:x xmlns:a='1'><a:y p='p' q='q'><a:z>zzz</a:z></a:y></a:x>"

      r = Document.new(data)

      assert_equal(Text, REXML::XPath.first(r,"a:x/a:y[@p='p' and @q='q']/a:z/text()").class)
      assert_equal("zzz", REXML::XPath.first(r,"a:x/a:y[@p='p' and @q='q']/a:z/text()").to_s)
    end

    def test_ticket_59
      data = "<a>
        <c id='1'/>
        <c id='2'/>
        <b>
          <c id='3'/>
        </b>
        <c id='4'/>
        <b>
          <b>
            <c id='5'/>
          </b>
          <c id='6'/>
        </b>
        <c id='7'/>
        <b>
          <b>
            <c id='8'/>
            <b>
              <c id='9'/>
              <b>
                <c id='10'/>
              </b>
              <c id='11'/>
            </b>
          </b>
        </b>
        <c id='12'/>
      </a>"
      d = Document.new(data)
      res = d.elements.to_a( "//c" ).collect {|e| e.attributes['id'].to_i}
      assert_equal((1..12).to_a, res)
    end

    def ticket_61_fixture(doc, xpath)
      matches = []
      doc.elements.each(xpath) do |element|
        matches << element
        assert_equal('Add', element.text)
        assert_equal('ButtonText', element.attributes['class'])
      end
      assert_equal(1, matches.length)
    end

    def test_ticket_61_text
      doc = File.open(fixture_path("ticket_61.xml")) do |file|
        REXML::Document.new file
      end
      ticket_61_fixture( doc, "//div[text()='Add' and @class='ButtonText']" )
    end

    def test_ticket_61_contains
      doc = File.open(fixture_path("ticket_61.xml")) do |file|
        REXML::Document.new file
      end
      ticket_61_fixture( doc, "//div[contains(.,'Add') and @class='ButtonText']" )
    end

    def test_namespaces_0
      d = Document.new(%q{<x:a xmlns:x="y"/>})
      assert_equal( 1,  XPath.match( d, "//x:a" ).size )
      assert_equal( 1,  XPath.match( d, "//x:*" ).size )
    end

    def test_ticket_71
      doc = Document.new(%Q{<root xmlns:ns1="xyz" xmlns:ns2="123"><element ns1:attrname="foo" ns2:attrname="bar"/></root>})
      el = doc.root.elements[1]
      assert_equal( "element", el.name )
      el2 = XPath.first( doc.root, "element[@ns:attrname='foo']", { 'ns' => "xyz" } )
      assert_equal( el, el2 )
    end

    def test_ticket_78
      doc = <<-EOT
      <root>
          <element>
              <tag x='1'>123</tag>
          </element>
          <element>
              <tag x='2'>123a</tag>
          </element>
      </root>
      EOT
      seq = %w{BEGIN 123 END BEGIN 123a END}

      xmlDoc = Document.new(doc)

      ["//element[tag='123']/tag", "//element[tag='123a']/tag"].each do |query|
        assert_equal( "BEGIN", seq.shift )
        XPath.each(xmlDoc, query) { |element|
          assert_equal( seq.shift, element.text )
        }
        assert_equal( "END", seq.shift )
      end
    end

    def test_ticket_79
      source = "<a><b><c>test</c></b><b><c>3</c></b></a>"
      d = REXML::Document.new( source )
      r = REXML::XPath.match( d, %q{/a/b[c='test']} )
      assert_equal(1, r.size())
      r = REXML::XPath.match( d, %q{/a/b[c='3']} )
      assert_equal(1, r.size())
    end

    def test_or_and
      doc = "
<html>
  <head>
    <title>test</title>
  </head>
  <body>
    <p>
      A <a rel=\"sub\" href=\"/\">link</a>.
    </p>
  </body>
</html>
"

      xmldoc = REXML::Document.new(doc)
      xpath = "descendant::node()[(local-name()='link' or local-name()='a') and @rel='sub']"
      hrefs = []
      xmldoc.elements.each(xpath) do |element|
        hrefs << element.attributes["href"]
      end
      assert_equal(["/"], hrefs, "Bug #3842 [ruby-core:32447]")
    end
  end
end
