moxml 0.5.148 → 0.5.150
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/dependent-repos.json +7 -2
- data/.github/workflows/dependent-tests.yml +4 -1
- data/.github/workflows/nightly-fuzz.yml +80 -0
- data/.github/workflows/performance-release.yml +79 -0
- data/.github/workflows/performance.yml +59 -0
- data/Rakefile +23 -0
- data/benchmark/README.md +106 -0
- data/benchmark/scoreboard.json +115 -0
- data/benchmark/scoreboard.rb +556 -0
- data/bin/generate-model-coverage +21 -0
- data/docs/_pages/adapter-compatibility.adoc +3 -3
- data/docs/_pages/divergences.adoc +88 -39
- data/docs/_pages/ecosystem.adoc +62 -0
- data/docs/_pages/headed-ox-limitations.adoc +1 -1
- data/docs/_pages/headed-ox.adoc +2 -2
- data/docs/_pages/index.adoc +4 -0
- data/docs/_pages/model-coverage.adoc +57 -0
- data/docs/_pages/performance.adoc +29 -0
- data/docs/_pages/why-moxml.adoc +87 -0
- data/docs/_references/processing-model.adoc +69 -3
- data/lib/compat/opal/moxml_boot.rb +4 -0
- data/lib/moxml/adapter/base.rb +73 -0
- data/lib/moxml/adapter/leptris.rb +293 -23
- data/lib/moxml/adapter/libxml.rb +179 -25
- data/lib/moxml/adapter/nokogiri.rb +10 -0
- data/lib/moxml/adapter/oga.rb +71 -9
- data/lib/moxml/adapter/ox.rb +34 -2
- data/lib/moxml/adapter/rexml.rb +82 -17
- data/lib/moxml/c14n/data_model.rb +8 -0
- data/lib/moxml/c14n/processor.rb +17 -8
- data/lib/moxml/context.rb +33 -1
- data/lib/moxml/document.rb +18 -4
- data/lib/moxml/node.rb +75 -8
- data/lib/moxml/node_set.rb +42 -1
- data/lib/moxml/reference/normalizer.rb +63 -0
- data/lib/moxml/reference/parser.rb +417 -0
- data/lib/moxml/reference/writer.rb +121 -0
- data/lib/moxml/reference.rb +205 -0
- data/lib/moxml/relaxng.rb +62 -0
- data/lib/moxml/sax/block_handler.rb +7 -0
- data/lib/moxml/sax/handler.rb +20 -0
- data/lib/moxml/version.rb +1 -1
- data/lib/moxml/xpath/compiler.rb +17 -2
- data/lib/moxml/xpath/ordering.rb +97 -0
- data/lib/moxml/xpath.rb +29 -0
- data/lib/moxml.rb +2 -0
- data/spec/consistency/adapter_parity_spec.rb +24 -0
- data/spec/consistency/citation_hygiene_spec.rb +30 -0
- data/spec/consistency/differential_taxonomy_spec.rb +117 -0
- data/spec/consistency/divergence_register_spec.rb +9 -1
- data/spec/consistency/eol_normalization_spec.rb +2 -0
- data/spec/consistency/loud_degradation_spec.rb +2 -0
- data/spec/consistency/metamorphic_properties_spec.rb +195 -0
- data/spec/consistency/metamorphic_spec.rb +138 -0
- data/spec/consistency/model_coverage_audit_spec.rb +144 -61
- data/spec/consistency/published_docs_spec.rb +49 -0
- data/spec/consistency/reference_differential_spec.rb +186 -0
- data/spec/consistency/xpath_idioms_spec.rb +34 -5
- data/spec/consistency/xslt_spec.rb +2 -0
- data/spec/integration/shared_examples/edge_cases.rb +0 -13
- data/spec/moxml/adapter/leptris_attribute_snapshot_spec.rb +10 -7
- data/spec/moxml/adapter/leptris_spec.rb +23 -3
- data/spec/moxml/adapter/ox_spec.rb +1 -1
- data/spec/moxml/adapter/shared_examples/adapter_contract.rb +0 -8
- data/spec/moxml/attribute_resolver_spec.rb +35 -0
- data/spec/moxml/c14n/adapter_delegation_spec.rb +35 -6
- data/spec/moxml/c14n/character_encoding_spec.rb +7 -13
- data/spec/moxml/c14n/comments_pis_spec.rb +35 -8
- data/spec/moxml/c14n/subset_spec.rb +2 -12
- data/spec/moxml/c14n/w3c_examples_spec.rb +2 -5
- data/spec/moxml/doctype_spec.rb +7 -0
- data/spec/moxml/document_parse_diagnostics_spec.rb +2 -0
- data/spec/moxml/element_indent_spec.rb +34 -0
- data/spec/moxml/entity_spec.rb +2 -0
- data/spec/moxml/node_spec.rb +0 -3
- data/spec/moxml/plan_conformance_spec.rb +2 -0
- data/spec/moxml/processing_instruction_spec.rb +9 -0
- data/spec/moxml/reference_opal_spec.rb +29 -0
- data/spec/moxml/reference_spec.rb +162 -0
- data/spec/moxml/relax_ng_spec.rb +67 -0
- data/spec/moxml/sax_spec.rb +47 -74
- data/spec/moxml/text_spec.rb +2 -0
- data/spec/moxml/to_html_spec.rb +2 -0
- data/spec/moxml/xhtml_output_spec.rb +2 -0
- data/spec/moxml/xpath/axes_spec.rb +2 -2
- data/spec/moxml/xpath_capabilities_spec.rb +11 -38
- data/spec/moxml/xsd_spec.rb +38 -0
- data/spec/support/input_generator.rb +154 -0
- data/spec/support/input_taxonomy.rb +219 -0
- data/spec/support/model_coverage.rb +159 -0
- data/spec/support/xml_generator.rb +54 -0
- metadata +32 -2
data/spec/moxml/doctype_spec.rb
CHANGED
|
@@ -84,6 +84,13 @@ RSpec.describe Moxml::Doctype do
|
|
|
84
84
|
expect(doctype.external_id).to be_nil
|
|
85
85
|
expect(doctype.system_id).to be_nil
|
|
86
86
|
end
|
|
87
|
+
|
|
88
|
+
it "preserves a DOCTYPE internal subset on serialization" do
|
|
89
|
+
xml = '<!DOCTYPE r [<!ENTITY x "v">]><r>&x;</r>'
|
|
90
|
+
parsed = ctx.parse(xml)
|
|
91
|
+
|
|
92
|
+
expect(parsed.to_xml(indent: 0)).to include('<!ENTITY x "v">')
|
|
93
|
+
end
|
|
87
94
|
end
|
|
88
95
|
end
|
|
89
96
|
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# Element-level indentation is a declared capability (model, Serialization;
|
|
6
|
+
# D-09): engines that cannot honor the knob raise the named AdapterError
|
|
7
|
+
# instead of answering unindented output. indent: 0 and the no-option
|
|
8
|
+
# path stay common — consumers use them for canonical shapes.
|
|
9
|
+
RSpec.describe "element-level serialization indentation" do
|
|
10
|
+
it "honors a positive indent on engines that support it" do
|
|
11
|
+
%i[nokogiri rexml].each do |name|
|
|
12
|
+
doc = Moxml.new(name).parse("<r><a/></r>")
|
|
13
|
+
expect(doc.root.to_xml(indent: 2)).to include("\n"),
|
|
14
|
+
"#{name}: indent not honored"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "raises the named AdapterError on engines that ignore the knob" do
|
|
19
|
+
%i[oga ox headed_ox libxml leptris].each do |name|
|
|
20
|
+
doc = Moxml.new(name).parse("<r><a/></r>")
|
|
21
|
+
expect { doc.root.to_xml(indent: 2) }
|
|
22
|
+
.to raise_error(Moxml::AdapterError, /indentation/),
|
|
23
|
+
"#{name}: silent unindented answer"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "keeps indent: 0 and the no-option path common on every adapter" do
|
|
28
|
+
Moxml::Adapter::AVAILABLE_ADAPTERS.each do |name|
|
|
29
|
+
doc = Moxml.new(name).parse("<r><a/></r>")
|
|
30
|
+
expect(doc.root.to_xml(indent: 0)).to include("<a")
|
|
31
|
+
expect(doc.root.to_xml).to include("<a")
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
data/spec/moxml/entity_spec.rb
CHANGED
data/spec/moxml/node_spec.rb
CHANGED
|
@@ -156,9 +156,6 @@ RSpec.describe Moxml::Node do
|
|
|
156
156
|
end
|
|
157
157
|
|
|
158
158
|
it "locates the node via its path" do
|
|
159
|
-
# Ox translates only a limited XPath subset (no positional predicates)
|
|
160
|
-
skip "D-12: ox xpath translation does not support positional predicates" if adapter_name == :ox
|
|
161
|
-
|
|
162
159
|
expect(doc.at_xpath(title.path)).to eq(title)
|
|
163
160
|
end
|
|
164
161
|
|
|
@@ -21,6 +21,15 @@ RSpec.describe Moxml::ProcessingInstruction do
|
|
|
21
21
|
expect(pi.content).to include("type=")
|
|
22
22
|
expect(pi.content).to include("href=")
|
|
23
23
|
end
|
|
24
|
+
|
|
25
|
+
it "answers the empty string for a PI with no data on every adapter" do
|
|
26
|
+
Moxml::Adapter::AVAILABLE_ADAPTERS.each do |name|
|
|
27
|
+
parsed = Moxml.new(name).parse("<r><?pi?></r>")
|
|
28
|
+
pi = parsed.children.first.children
|
|
29
|
+
.find { |n| n.is_a?(described_class) }
|
|
30
|
+
expect(pi.content).to eq(""), "empty PI content on #{name}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
24
33
|
end
|
|
25
34
|
|
|
26
35
|
describe "creation" do
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# The engine-free lane (TODO.sota/03): the reference is pure Ruby and
|
|
6
|
+
# answers the model where no native engine exists. This spec proves it
|
|
7
|
+
# transpiles and runs under Opal.
|
|
8
|
+
RSpec.describe "Moxml::Reference under Opal", if: RUBY_ENGINE == "opal" do
|
|
9
|
+
it "parses, answers faces, and writes without a native engine" do
|
|
10
|
+
document = Moxml::Reference.parse(
|
|
11
|
+
'<r a="1"><b>t&u</b><![CDATA[c]]><?pi d?></r>',
|
|
12
|
+
)
|
|
13
|
+
expect(Moxml::Reference.element_names(document)).to eq(%w[r b])
|
|
14
|
+
expect(Moxml::Reference.child_element_names(document)).to eq([["b"], []])
|
|
15
|
+
element = document.root.children.find(&:element?)
|
|
16
|
+
expect(element.children.map(&:kind)).to eq([:text])
|
|
17
|
+
expect(element.children.first.data).to eq("t&u")
|
|
18
|
+
expect(document.root.text).to eq("t&uc")
|
|
19
|
+
expect(Moxml::Reference.processing_instructions(document))
|
|
20
|
+
.to eq([["pi", "d"]])
|
|
21
|
+
expect(Moxml::Reference.to_xml(document))
|
|
22
|
+
.to include("<r a=\"1\">", "<b>t&u</b>", "<![CDATA[c]]>")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "raises a named ParseError for malformed input" do
|
|
26
|
+
expect { Moxml::Reference.parse("<r><c></r>") }
|
|
27
|
+
.to raise_error(Moxml::ParseError)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require "support/input_taxonomy"
|
|
5
|
+
require "support/xml_generator"
|
|
6
|
+
|
|
7
|
+
# The executable reference (TODO.sota/03): a pure-Ruby reader,
|
|
8
|
+
# writer, and normalizer that answer the processing model with no
|
|
9
|
+
# engine involved. The differential suite tests adapters against it;
|
|
10
|
+
# these specs pin the reference itself.
|
|
11
|
+
RSpec.describe Moxml::Reference do
|
|
12
|
+
describe "parsing" do
|
|
13
|
+
it "reads the object model, declaration, and document parts" do
|
|
14
|
+
doc = described_class.parse(
|
|
15
|
+
'<?xml version="1.0" encoding="UTF-8"?><!--lead--><r a="1"><b>t</b></r>',
|
|
16
|
+
)
|
|
17
|
+
expect(doc.declaration[:version]).to eq("1.0")
|
|
18
|
+
expect(doc.declaration[:encoding]).to eq("UTF-8")
|
|
19
|
+
expect(described_class.element_names(doc)).to eq(%w[r b])
|
|
20
|
+
expect(doc.root.attribute_pairs).to eq([["a", "1"]])
|
|
21
|
+
expect(described_class.comments(doc)).to eq(["lead"])
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "answers bare local names for prefixed sources" do
|
|
25
|
+
doc = described_class.parse('<r xmlns:p="urn:a"><p:c p:x="1"/></r>')
|
|
26
|
+
expect(described_class.element_names(doc)).to eq(%w[r c])
|
|
27
|
+
expect(described_class.child_element_names(doc)).to eq([["c"], []])
|
|
28
|
+
expect(doc.root.children.first.attribute_pairs).to eq([["x", "1"]])
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "expands built-in and numeric references, never comments or PI data" do
|
|
32
|
+
doc = described_class.parse(
|
|
33
|
+
"<r>&AB<![CDATA[<raw>]]><!-- c --><?pi d?></r>",
|
|
34
|
+
)
|
|
35
|
+
expect(doc.root.text).to eq("&AB<raw>")
|
|
36
|
+
expect(described_class.processing_instructions(doc)).to eq([["pi", "d"]])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "preserves DTD-declared general entities lexically (uniform lane)" do
|
|
40
|
+
doc = described_class.parse(
|
|
41
|
+
'<!DOCTYPE r [<!ENTITY x "expanded">]><r>&x;</r>',
|
|
42
|
+
)
|
|
43
|
+
expect(doc.root.text).to eq("&x;")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "normalizes literal line endings but not character references" do
|
|
47
|
+
doc = described_class.parse("<r>a\r\nb\rc
d</r>")
|
|
48
|
+
expect(doc.root.text).to eq("a\nb\nc\rd")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "normalizes literal attribute whitespace" do
|
|
52
|
+
doc = described_class.parse(%(<r a="x\ty\nz"/>))
|
|
53
|
+
expect(doc.root.attribute_pairs).to eq([["a", "x y z"]])
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "drops the PI separator from PI data" do
|
|
57
|
+
doc = described_class.parse("<r><?pi data ?></r>")
|
|
58
|
+
expect(described_class.processing_instructions(doc))
|
|
59
|
+
.to eq([["pi", "data "]])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "answers the empty string for PI with no data" do
|
|
63
|
+
doc = described_class.parse("<r><?pi?></r>")
|
|
64
|
+
expect(described_class.processing_instructions(doc)).to eq([["pi", ""]])
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe "totality over the input taxonomy" do
|
|
69
|
+
it "parses every well-formed taxonomy fixture" do
|
|
70
|
+
InputTaxonomy.well_formed.each do |input|
|
|
71
|
+
expect { described_class.parse(input.xml) }
|
|
72
|
+
.not_to raise_error, "#{input.id} must parse in the reference"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "raises a named ParseError for every malformed taxonomy fixture" do
|
|
77
|
+
InputTaxonomy.malformed.each do |input|
|
|
78
|
+
expect { described_class.parse(input.xml) }
|
|
79
|
+
.to raise_error(Moxml::ParseError),
|
|
80
|
+
"#{input.id} must raise in the reference"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "parses every generated document" do
|
|
85
|
+
(1..10).each do |seed|
|
|
86
|
+
expect { described_class.parse(XmlGenerator.generate(seed)) }
|
|
87
|
+
.not_to raise_error
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
describe "writers" do
|
|
93
|
+
let(:doc) do
|
|
94
|
+
described_class.parse(
|
|
95
|
+
'<r a="<&""><b/>t<!--c--><![CDATA[x<y]]></r>',
|
|
96
|
+
)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "emits XML that re-parses to the same information set" do
|
|
100
|
+
xml = described_class.to_xml(doc)
|
|
101
|
+
again = described_class.parse(xml)
|
|
102
|
+
expect(described_class.element_names(again))
|
|
103
|
+
.to eq(described_class.element_names(doc))
|
|
104
|
+
expect(again.root.attribute_pairs).to eq(doc.root.attribute_pairs)
|
|
105
|
+
expect(again.root.text).to eq(doc.root.text)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "emits the canonical facade escaping" do
|
|
109
|
+
expect(described_class.to_xml(doc))
|
|
110
|
+
.to include('<r a="<&"">')
|
|
111
|
+
expect(described_class.to_xml(doc)).to include("<![CDATA[x<y]]>")
|
|
112
|
+
expect(described_class.to_xml(doc)).to include("<b/>")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "splits a CDATA end sequence" do
|
|
116
|
+
document = Moxml::Reference::Document.new
|
|
117
|
+
root = Moxml::Reference::Node.new(kind: :element, name: "r")
|
|
118
|
+
root.children << Moxml::Reference::Node.new(kind: :cdata, data: "a]]>b")
|
|
119
|
+
document.children << root
|
|
120
|
+
expect(described_class.to_xml(document)).to include("]]]]><![CDATA[>")
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "leaves void elements unclosed in HTML and self-closed in XHTML" do
|
|
124
|
+
html_doc = described_class.parse("<html><body>a<br/>b</body></html>")
|
|
125
|
+
expect(described_class.to_html(html_doc)).to include("<br>")
|
|
126
|
+
expect(described_class.to_xhtml(html_doc)).to include("<br/>")
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "emits raw text inside HTML script and style, escaped in XHTML" do
|
|
130
|
+
html_doc = described_class.parse("<html><script>a < b</script></html>")
|
|
131
|
+
expect(described_class.to_html(html_doc))
|
|
132
|
+
.to include("<script>a < b</script>")
|
|
133
|
+
expect(described_class.to_xhtml(html_doc))
|
|
134
|
+
.to include("<script>a < b</script>")
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "emits no XML declaration unless the source had one" do
|
|
138
|
+
expect(described_class.to_html(described_class.parse("<r/>")))
|
|
139
|
+
.to eq("<r></r>")
|
|
140
|
+
with_declaration = described_class.parse('<?xml version="1.0"?><r/>')
|
|
141
|
+
expect(described_class.to_xml(with_declaration))
|
|
142
|
+
.to start_with('<?xml version="1.0"?>')
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
describe "normalizer" do
|
|
147
|
+
it "normalizes CRLF and bare CR to LF" do
|
|
148
|
+
expect(Moxml::Reference::Normalizer.normalize_eol("a\r\nb\rc"))
|
|
149
|
+
.to eq("a\nb\nc")
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it "expands built-ins and numeric references" do
|
|
153
|
+
expect(Moxml::Reference::Normalizer.expand_entities("<A"))
|
|
154
|
+
.to eq("<A")
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "raises for an undefined reference" do
|
|
158
|
+
expect { Moxml::Reference::Normalizer.expand_entities("&nope;") }
|
|
159
|
+
.to raise_error(Moxml::ParseError)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require "tempfile"
|
|
5
|
+
|
|
6
|
+
# RELAX NG facade (TODO.sota/05): engines with an RNG validator
|
|
7
|
+
# answer valid?/validate/validate_errors; engines without one raise
|
|
8
|
+
# the named NotImplementedError at compile and answer supported?
|
|
9
|
+
# without raising.
|
|
10
|
+
RSpec.describe Moxml::RelaxNG do
|
|
11
|
+
RNG_SCHEMA = <<~RNG # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
|
|
12
|
+
<element name="r" xmlns="http://relaxng.org/ns/structure/1.0">
|
|
13
|
+
<attribute name="a"><text/></attribute>
|
|
14
|
+
</element>
|
|
15
|
+
RNG
|
|
16
|
+
|
|
17
|
+
let(:valid_doc) { Moxml.new(:leptris).parse('<r a="x"/>') }
|
|
18
|
+
let(:invalid_doc) { Moxml.new(:leptris).parse("<r/>") }
|
|
19
|
+
|
|
20
|
+
it "answers supported? without raising on every adapter" do
|
|
21
|
+
Moxml::Adapter::AVAILABLE_ADAPTERS.each do |name|
|
|
22
|
+
ctx = Moxml.new(name)
|
|
23
|
+
expect { described_class.supported?(ctx) }.not_to raise_error
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "validates against a compiled schema and reports findings" do
|
|
28
|
+
ctx = Moxml.new(:leptris)
|
|
29
|
+
skip "leptris binding has no RNG face" unless described_class.supported?(ctx)
|
|
30
|
+
|
|
31
|
+
schema = ctx.relaxng(RNG_SCHEMA)
|
|
32
|
+
expect(schema.valid?(valid_doc)).to be(true)
|
|
33
|
+
expect(schema.validate(valid_doc)).to eq([])
|
|
34
|
+
expect(schema.valid?(invalid_doc)).to be(false)
|
|
35
|
+
error = schema.validate_errors(invalid_doc).first
|
|
36
|
+
expect(error[:message]).to include("missing required attribute")
|
|
37
|
+
expect(error[:line]).to eq(1)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "compiles a schema from a file" do
|
|
41
|
+
ctx = Moxml.new(:leptris)
|
|
42
|
+
skip "leptris binding has no RNG face" unless described_class.supported?(ctx)
|
|
43
|
+
|
|
44
|
+
Tempfile.create(["schema", ".rng"]) do |file|
|
|
45
|
+
file.write(RNG_SCHEMA)
|
|
46
|
+
file.flush
|
|
47
|
+
schema = ctx.relaxng_file(file.path)
|
|
48
|
+
expect(schema.valid?(valid_doc)).to be(true)
|
|
49
|
+
expect(schema.valid?(invalid_doc)).to be(false)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "raises a named NotImplementedError on engines without an RNG validator" do
|
|
54
|
+
ctx = Moxml.new(:nokogiri)
|
|
55
|
+
expect(described_class.supported?(ctx)).to be(false)
|
|
56
|
+
expect { ctx.relaxng(RNG_SCHEMA) }
|
|
57
|
+
.to raise_error(Moxml::NotImplementedError, /RELAX NG/)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "names a malformed schema as a ValidationError" do
|
|
61
|
+
ctx = Moxml.new(:leptris)
|
|
62
|
+
skip "leptris binding has no RNG face" unless described_class.supported?(ctx)
|
|
63
|
+
|
|
64
|
+
expect { ctx.relaxng("<not-a-schema/>") }
|
|
65
|
+
.to raise_error(Moxml::ValidationError, /RELAX NG/)
|
|
66
|
+
end
|
|
67
|
+
end
|
data/spec/moxml/sax_spec.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Processing model §SAX (normative): pinned here.
|
|
4
|
+
|
|
3
5
|
require "spec_helper"
|
|
4
6
|
|
|
5
7
|
RSpec.describe Moxml::SAX do
|
|
@@ -410,50 +412,39 @@ RSpec.describe Moxml::SAX do
|
|
|
410
412
|
expect(handler.error_message).to match(/invalid|unclosed|premature|mismatch|not closed/i)
|
|
411
413
|
end
|
|
412
414
|
|
|
413
|
-
it "
|
|
414
|
-
# Ox SAX
|
|
415
|
-
#
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
context.sax_parse(xml_with_cdata) do
|
|
422
|
-
cdata { |_text| cdata_found = true }
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
# This will fail because Ox doesn't support separate CDATA events
|
|
426
|
-
expect(cdata_found).to be true
|
|
415
|
+
it "declares CDATA event subscriptions loudly (D-10)" do
|
|
416
|
+
# Ox SAX has no separate CDATA events; a subscriber gets the
|
|
417
|
+
# named error rather than an incomplete stream.
|
|
418
|
+
expect do
|
|
419
|
+
context.sax_parse("<root><![CDATA[special content]]></root>") do
|
|
420
|
+
cdata { |_text| nil }
|
|
421
|
+
end
|
|
422
|
+
end.to raise_error(Moxml::NotImplementedError, /cdata/)
|
|
427
423
|
end
|
|
428
424
|
|
|
429
|
-
it "
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
comment_found = false
|
|
436
|
-
context.sax_parse(xml_with_comment) do
|
|
437
|
-
comment { |_text| comment_found = true }
|
|
438
|
-
end
|
|
439
|
-
|
|
440
|
-
# This will fail because Ox doesn't support comment events
|
|
441
|
-
expect(comment_found).to be true
|
|
425
|
+
it "declares comment event subscriptions loudly (D-10)" do
|
|
426
|
+
expect do
|
|
427
|
+
context.sax_parse("<root><!-- comment --><data>content</data></root>") do
|
|
428
|
+
comment { |_text| nil }
|
|
429
|
+
end
|
|
430
|
+
end.to raise_error(Moxml::NotImplementedError, /comment/)
|
|
442
431
|
end
|
|
443
432
|
|
|
444
|
-
it "
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
433
|
+
it "declares PI event subscriptions loudly (D-10)" do
|
|
434
|
+
expect do
|
|
435
|
+
context.sax_parse('<?xml-stylesheet type="text/xsl" href="style.xsl"?><root/>') do
|
|
436
|
+
processing_instruction { |_target, _data| nil }
|
|
437
|
+
end
|
|
438
|
+
end.to raise_error(Moxml::NotImplementedError, /processing_instruction/)
|
|
439
|
+
end
|
|
449
440
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
end
|
|
441
|
+
it "declares unsupported families loudly for class handlers too" do
|
|
442
|
+
handler = Class.new(Moxml::SAX::Handler) do
|
|
443
|
+
def on_cdata(_text); end
|
|
444
|
+
end.new
|
|
454
445
|
|
|
455
|
-
|
|
456
|
-
|
|
446
|
+
expect { context.sax_parse("<root/>", handler) }
|
|
447
|
+
.to raise_error(Moxml::NotImplementedError, /cdata/)
|
|
457
448
|
end
|
|
458
449
|
end
|
|
459
450
|
|
|
@@ -1022,46 +1013,28 @@ RSpec.describe Moxml::SAX do
|
|
|
1022
1013
|
expect(handler.error_message).to match(/invalid|unclosed|premature|mismatch|not closed/i)
|
|
1023
1014
|
end
|
|
1024
1015
|
|
|
1025
|
-
it "
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
cdata_found = false
|
|
1032
|
-
context.sax_parse(xml_with_cdata) do
|
|
1033
|
-
cdata { |_text| cdata_found = true }
|
|
1034
|
-
end
|
|
1035
|
-
|
|
1036
|
-
expect(cdata_found).to be true
|
|
1016
|
+
it "declares CDATA event subscriptions loudly (inherited from Ox, D-10)" do
|
|
1017
|
+
expect do
|
|
1018
|
+
context.sax_parse("<root><![CDATA[special content]]></root>") do
|
|
1019
|
+
cdata { |_text| nil }
|
|
1020
|
+
end
|
|
1021
|
+
end.to raise_error(Moxml::NotImplementedError, /cdata/)
|
|
1037
1022
|
end
|
|
1038
1023
|
|
|
1039
|
-
it "
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
comment_found = false
|
|
1046
|
-
context.sax_parse(xml_with_comment) do
|
|
1047
|
-
comment { |_text| comment_found = true }
|
|
1048
|
-
end
|
|
1049
|
-
|
|
1050
|
-
expect(comment_found).to be true
|
|
1024
|
+
it "declares comment event subscriptions loudly (inherited from Ox, D-10)" do
|
|
1025
|
+
expect do
|
|
1026
|
+
context.sax_parse("<root><!-- comment --><data>content</data></root>") do
|
|
1027
|
+
comment { |_text| nil }
|
|
1028
|
+
end
|
|
1029
|
+
end.to raise_error(Moxml::NotImplementedError, /comment/)
|
|
1051
1030
|
end
|
|
1052
1031
|
|
|
1053
|
-
it "
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
pi_found = false
|
|
1060
|
-
context.sax_parse(xml_with_pi) do
|
|
1061
|
-
processing_instruction { |_target, _data| pi_found = true }
|
|
1062
|
-
end
|
|
1063
|
-
|
|
1064
|
-
expect(pi_found).to be true
|
|
1032
|
+
it "declares PI event subscriptions loudly (inherited from Ox, D-10)" do
|
|
1033
|
+
expect do
|
|
1034
|
+
context.sax_parse('<?xml-stylesheet type="text/xsl" href="style.xsl"?><root/>') do
|
|
1035
|
+
processing_instruction { |_target, _data| nil }
|
|
1036
|
+
end
|
|
1037
|
+
end.to raise_error(Moxml::NotImplementedError, /processing_instruction/)
|
|
1065
1038
|
end
|
|
1066
1039
|
end
|
|
1067
1040
|
end
|
data/spec/moxml/text_spec.rb
CHANGED
data/spec/moxml/to_html_spec.rb
CHANGED
|
@@ -217,11 +217,11 @@ RSpec.describe "XPath Axes" do
|
|
|
217
217
|
end
|
|
218
218
|
|
|
219
219
|
it "selects ancestors with ancestor::" do
|
|
220
|
-
expect(select("//c/ancestor::*")).to eq(%w[parent
|
|
220
|
+
expect(select("//c/ancestor::*")).to eq(%w[root parent])
|
|
221
221
|
end
|
|
222
222
|
|
|
223
223
|
it "includes the context node with ancestor-or-self::" do
|
|
224
|
-
expect(select("//c/ancestor-or-self::*")).to eq(%w[
|
|
224
|
+
expect(select("//c/ancestor-or-self::*")).to eq(%w[root parent c])
|
|
225
225
|
end
|
|
226
226
|
|
|
227
227
|
it "selects following siblings" do
|