1# frozen_string_literal: false
2require_relative 'rexml_test_utils'
3
4require "rexml/document"
5require "rexml/xpath"
6
7# Harness to test REXML's capabilities against the test suite from Jaxen
8# ryan.a.cox@gmail.com
9
10module REXMLTests
11  class JaxenTester < Test::Unit::TestCase
12    include REXMLTestUtils
13    include REXML
14
15    def test_axis ; process_test_case("axis") ; end
16    def test_basic ; process_test_case("basic") ; end
17    def test_basicupdate ; process_test_case("basicupdate") ; end
18    def test_contents ; process_test_case("contents") ; end
19    def test_defaultNamespace ; process_test_case("defaultNamespace") ; end
20    def test_fibo ; process_test_case("fibo") ; end
21    def test_id ; process_test_case("id") ; end
22    def test_jaxen24 ; process_test_case("jaxen24") ; end
23    def test_lang ; process_test_case("lang") ; end
24    # document() function for XSLT isn't supported
25    def _test_message ; process_test_case("message") ; end
26    def test_moreover ; process_test_case("moreover") ; end
27    def test_much_ado ; process_test_case("much_ado") ; end
28    def test_namespaces ; process_test_case("namespaces") ; end
29    def test_nitf ; process_test_case("nitf") ; end
30    # Exception should be considered
31    def _test_numbers ; process_test_case("numbers") ; end
32    def test_pi ; process_test_case("pi") ; end
33    def test_pi2 ; process_test_case("pi2") ; end
34    def test_simple ; process_test_case("simple") ; end
35    # TODO: namespace node is needed
36    def _test_testNamespaces ; process_test_case("testNamespaces") ; end
37    # document() function for XSLT isn't supported
38    def _test_text ; process_test_case("text") ; end
39    def test_underscore ; process_test_case("underscore") ; end
40    def _test_web ; process_test_case("web") ; end
41    def test_web2 ; process_test_case("web2") ; end
42
43    private
44    def process_test_case(name)
45      xml_path = "#{name}.xml"
46      doc = File.open(fixture_path(xml_path)) do |file|
47        Document.new(file)
48      end
49      test_doc = File.open(fixture_path("test/tests.xml")) do |file|
50        Document.new(file)
51      end
52      XPath.each(test_doc,
53                 "/tests/document[@url='xml/#{xml_path}']/context") do |context|
54        process_context(doc, context)
55      end
56    end
57
58    # processes a tests/document/context node
59    def process_context(doc, context)
60      test_context = XPath.match(doc, context.attributes["select"])
61      namespaces = context.namespaces
62      namespaces.delete("var")
63      namespaces = nil if namespaces.empty?
64      variables = {}
65      var_namespace = "http://jaxen.org/test-harness/var"
66      XPath.each(context,
67                 "@*[namespace-uri() = '#{var_namespace}']") do |attribute|
68        variables[attribute.name] = attribute.value
69      end
70      XPath.each(context, "valueOf") do |value|
71        process_value_of(test_context, variables, namespaces, value)
72      end
73      XPath.each(context,
74                 "test[not(@exception) or (@exception != 'true')]") do |test|
75        process_nominal_test(test_context, variables, namespaces, test)
76      end
77      XPath.each(context,
78                 "test[@exception = 'true']") do |test|
79        process_exceptional_test(test_context, variables, namespaces, test)
80      end
81    end
82
83    # processes a tests/document/context/valueOf or tests/document/context/test/valueOf node
84    def process_value_of(context, variables, namespaces, value_of)
85      expected = value_of.text
86      xpath = value_of.attributes["select"]
87      matched = XPath.match(context, xpath, namespaces, variables, strict: true)
88
89      message = user_message(context, xpath, matched)
90      assert_equal(expected || "",
91                   REXML::Functions.string(matched),
92                   message)
93    end
94
95    # processes a tests/document/context/test node ( where @exception is false or doesn't exist )
96    def process_nominal_test(context, variables, namespaces, test)
97      xpath = test.attributes["select"]
98      matched = XPath.match(context, xpath, namespaces, variables, strict: true)
99      # might be a test with no count attribute, but nested valueOf elements
100      expected = test.attributes["count"]
101      if expected
102        assert_equal(Integer(expected, 10),
103                     matched.size,
104                     user_message(context, xpath, matched))
105      end
106
107      XPath.each(test, "valueOf") do |value_of|
108        process_value_of(matched, variables, namespaces, value_of)
109      end
110    end
111
112    # processes a tests/document/context/test node ( where @exception is true )
113    def process_exceptional_test(context, variables, namespaces, test)
114      xpath = test.attributes["select"]
115      assert_raise(REXML::ParseException) do
116        XPath.match(context, xpath, namespaces, variables, strict: true)
117      end
118    end
119
120    def user_message(context, xpath, matched)
121      message = ""
122      context.each_with_index do |node, i|
123        message << "Node#{i}:\n"
124        message << "#{node}\n"
125      end
126      message << "XPath: <#{xpath}>\n"
127      message << "Matched <#{matched}>"
128      message
129    end
130  end
131end
132