rng 0.1.2 → 0.3.4
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/docs.yml +63 -0
- data/.github/workflows/release.yml +8 -3
- data/.gitignore +11 -0
- data/.rubocop.yml +10 -7
- data/.rubocop_todo.yml +229 -23
- data/CHANGELOG.md +317 -0
- data/CLAUDE.md +139 -0
- data/Gemfile +11 -12
- data/README.adoc +1538 -11
- data/Rakefile +11 -3
- data/docs/Gemfile +8 -0
- data/docs/_config.yml +23 -0
- data/docs/getting-started/index.adoc +75 -0
- data/docs/guides/error-handling.adoc +137 -0
- data/docs/guides/external-references.adoc +128 -0
- data/docs/guides/index.adoc +24 -0
- data/docs/guides/parsing-rnc.adoc +141 -0
- data/docs/guides/parsing-rng-xml.adoc +81 -0
- data/docs/guides/rng-to-rnc.adoc +101 -0
- data/docs/guides/validation.adoc +85 -0
- data/docs/index.adoc +52 -0
- data/docs/reference/api.adoc +126 -0
- data/docs/reference/cli.adoc +182 -0
- data/docs/understanding/architecture.adoc +58 -0
- data/docs/understanding/rng-vs-rnc.adoc +118 -0
- data/exe/rng +5 -0
- data/lib/rng/any_name.rb +10 -8
- data/lib/rng/attribute.rb +28 -26
- data/lib/rng/choice.rb +24 -24
- data/lib/rng/cli.rb +607 -0
- data/lib/rng/data.rb +10 -10
- data/lib/rng/datatype_declaration.rb +26 -0
- data/lib/rng/define.rb +44 -41
- data/lib/rng/div.rb +36 -0
- data/lib/rng/documentation.rb +9 -0
- data/lib/rng/element.rb +39 -37
- data/lib/rng/empty.rb +7 -7
- data/lib/rng/except.rb +25 -25
- data/lib/rng/external_ref.rb +8 -8
- data/lib/rng/external_ref_resolver.rb +602 -0
- data/lib/rng/foreign_attribute.rb +26 -0
- data/lib/rng/foreign_element.rb +33 -0
- data/lib/rng/grammar.rb +14 -12
- data/lib/rng/group.rb +26 -24
- data/lib/rng/include.rb +5 -6
- data/lib/rng/include_processor.rb +461 -0
- data/lib/rng/interleave.rb +23 -23
- data/lib/rng/list.rb +22 -22
- data/lib/rng/mixed.rb +23 -23
- data/lib/rng/name.rb +6 -7
- data/lib/rng/namespace_declaration.rb +47 -0
- data/lib/rng/namespaces.rb +15 -0
- data/lib/rng/not_allowed.rb +7 -7
- data/lib/rng/ns_name.rb +9 -9
- data/lib/rng/one_or_more.rb +23 -23
- data/lib/rng/optional.rb +23 -23
- data/lib/rng/param.rb +7 -8
- data/lib/rng/parent_ref.rb +8 -8
- data/lib/rng/parse_tree_processor.rb +695 -0
- data/lib/rng/pattern.rb +7 -7
- data/lib/rng/ref.rb +8 -8
- data/lib/rng/rnc_builder.rb +927 -0
- data/lib/rng/rnc_parser.rb +605 -305
- data/lib/rng/rnc_to_rng_converter.rb +1408 -0
- data/lib/rng/schema_preamble.rb +73 -0
- data/lib/rng/schema_validator.rb +1622 -0
- data/lib/rng/start.rb +27 -25
- data/lib/rng/test_suite_parser.rb +168 -0
- data/lib/rng/text.rb +11 -8
- data/lib/rng/to_rnc.rb +4 -35
- data/lib/rng/value.rb +6 -7
- data/lib/rng/version.rb +1 -1
- data/lib/rng/zero_or_more.rb +23 -23
- data/lib/rng.rb +68 -17
- data/rng.gemspec +18 -19
- data/scripts/extract_spectest_resources.rb +96 -0
- data/spec/fixtures/compacttest.xml +2511 -0
- data/spec/fixtures/external/circular_a.rng +7 -0
- data/spec/fixtures/external/circular_b.rng +7 -0
- data/spec/fixtures/external/circular_main.rng +7 -0
- data/spec/fixtures/external/external_ref_lib.rng +7 -0
- data/spec/fixtures/external/external_ref_main.rng +7 -0
- data/spec/fixtures/external/include_lib.rng +7 -0
- data/spec/fixtures/external/include_main.rng +3 -0
- data/spec/fixtures/external/nested_chain.rng +6 -0
- data/spec/fixtures/external/nested_leaf.rng +7 -0
- data/spec/fixtures/external/nested_mid.rng +8 -0
- data/spec/fixtures/metanorma/3gpp.rnc +35 -0
- data/spec/fixtures/metanorma/3gpp.rng +105 -0
- data/spec/fixtures/metanorma/basicdoc.rnc +11 -0
- data/spec/fixtures/metanorma/bipm.rnc +148 -0
- data/spec/fixtures/metanorma/bipm.rng +376 -0
- data/spec/fixtures/metanorma/bsi.rnc +104 -0
- data/spec/fixtures/metanorma/bsi.rng +332 -0
- data/spec/fixtures/metanorma/csa.rnc +45 -0
- data/spec/fixtures/metanorma/csa.rng +131 -0
- data/spec/fixtures/metanorma/csd.rnc +43 -0
- data/spec/fixtures/metanorma/csd.rng +132 -0
- data/spec/fixtures/metanorma/gbstandard.rnc +99 -0
- data/spec/fixtures/metanorma/gbstandard.rng +316 -0
- data/spec/fixtures/metanorma/iec.rnc +49 -0
- data/spec/fixtures/metanorma/iec.rng +193 -0
- data/spec/fixtures/metanorma/ietf.rnc +275 -0
- data/spec/fixtures/metanorma/ietf.rng +925 -0
- data/spec/fixtures/metanorma/iho.rnc +58 -0
- data/spec/fixtures/metanorma/iho.rng +179 -0
- data/spec/fixtures/metanorma/isodoc.rnc +873 -0
- data/spec/fixtures/metanorma/isodoc.rng +2704 -0
- data/spec/fixtures/metanorma/isostandard-amd.rnc +43 -0
- data/spec/fixtures/metanorma/isostandard-amd.rng +108 -0
- data/spec/fixtures/metanorma/isostandard.rnc +166 -0
- data/spec/fixtures/metanorma/isostandard.rng +494 -0
- data/spec/fixtures/metanorma/itu.rnc +122 -0
- data/spec/fixtures/metanorma/itu.rng +377 -0
- data/spec/fixtures/metanorma/m3d.rnc +41 -0
- data/spec/fixtures/metanorma/m3d.rng +122 -0
- data/spec/fixtures/metanorma/mpfd.rnc +36 -0
- data/spec/fixtures/metanorma/mpfd.rng +95 -0
- data/spec/fixtures/metanorma/nist.rnc +77 -0
- data/spec/fixtures/metanorma/nist.rng +216 -0
- data/spec/fixtures/metanorma/ogc.rnc +51 -0
- data/spec/fixtures/metanorma/ogc.rng +151 -0
- data/spec/fixtures/metanorma/reqt.rnc +6 -0
- data/spec/fixtures/metanorma/rsd.rnc +36 -0
- data/spec/fixtures/metanorma/rsd.rng +95 -0
- data/spec/fixtures/metanorma/un.rnc +103 -0
- data/spec/fixtures/metanorma/un.rng +367 -0
- data/spec/fixtures/rnc/base.rnc +4 -0
- data/spec/fixtures/rnc/grammar_with_trailing.rnc +8 -0
- data/spec/fixtures/rnc/main_include_trailing.rnc +3 -0
- data/spec/fixtures/rnc/main_with_include.rnc +5 -0
- data/spec/fixtures/rnc/test_augment.rnc +10 -0
- data/spec/fixtures/rnc/test_isodoc_simple.rnc +9 -0
- data/spec/fixtures/rnc/top_level_include.rnc +8 -0
- data/spec/fixtures/spectest_external/case_10_4.7/x +3 -0
- data/spec/fixtures/spectest_external/case_10_4.7/y +7 -0
- data/spec/fixtures/spectest_external/case_11_4.7/x +3 -0
- data/spec/fixtures/spectest_external/case_12_4.7/x +3 -0
- data/spec/fixtures/spectest_external/case_13_4.7/x +3 -0
- data/spec/fixtures/spectest_external/case_13_4.7/y +3 -0
- data/spec/fixtures/spectest_external/case_14_4.7/x +7 -0
- data/spec/fixtures/spectest_external/case_15_4.7/x +7 -0
- data/spec/fixtures/spectest_external/case_16_4.7/x +5 -0
- data/spec/fixtures/spectest_external/case_17_4.7/x +5 -0
- data/spec/fixtures/spectest_external/case_18_4.7/x +7 -0
- data/spec/fixtures/spectest_external/case_19_4.7/level1.rng +9 -0
- data/spec/fixtures/spectest_external/case_19_4.7/level2.rng +7 -0
- data/spec/fixtures/spectest_external/case_1_4.5/sub1/x +3 -0
- data/spec/fixtures/spectest_external/case_1_4.5/sub3/x +3 -0
- data/spec/fixtures/spectest_external/case_1_4.5/x +3 -0
- data/spec/fixtures/spectest_external/case_20_4.6/x +3 -0
- data/spec/fixtures/spectest_external/case_2_4.5/x +3 -0
- data/spec/fixtures/spectest_external/case_3_4.6/x +3 -0
- data/spec/fixtures/spectest_external/case_4_4.6/x +3 -0
- data/spec/fixtures/spectest_external/case_5_4.6/x +1 -0
- data/spec/fixtures/spectest_external/case_6_4.6/x +5 -0
- data/spec/fixtures/spectest_external/case_7_4.6/x +1 -0
- data/spec/fixtures/spectest_external/case_7_4.6/y +1 -0
- data/spec/fixtures/spectest_external/case_8_4.7/x +7 -0
- data/spec/fixtures/spectest_external/case_9_4.7/x +7 -0
- data/spec/fixtures/spectest_external/resources.json +149 -0
- data/spec/rng/advanced_rnc_spec.rb +101 -0
- data/spec/rng/compacttest_spec.rb +197 -0
- data/spec/rng/datatype_declaration_spec.rb +28 -0
- data/spec/rng/div_spec.rb +207 -0
- data/spec/rng/external_ref_resolver_spec.rb +122 -0
- data/spec/rng/metanorma_conversion_spec.rb +159 -0
- data/spec/rng/namespace_declaration_spec.rb +60 -0
- data/spec/rng/namespace_support_spec.rb +199 -0
- data/spec/rng/rnc_parser_spec.rb +498 -22
- data/spec/rng/rnc_roundtrip_spec.rb +96 -82
- data/spec/rng/rng_generation_spec.rb +288 -0
- data/spec/rng/roundtrip_spec.rb +342 -0
- data/spec/rng/schema_preamble_spec.rb +145 -0
- data/spec/rng/schema_spec.rb +68 -64
- data/spec/rng/spectest_spec.rb +168 -90
- data/spec/rng_spec.rb +2 -2
- data/spec/spec_helper.rb +7 -42
- metadata +141 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<externalRef xmlns="http://relaxng.org/ns/structure/1.0" href="x"></externalRef>
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
{
|
|
2
|
+
"1": {
|
|
3
|
+
"section": "4.5",
|
|
4
|
+
"documentation": "",
|
|
5
|
+
"resources": [
|
|
6
|
+
"sub1/x",
|
|
7
|
+
"sub1/x",
|
|
8
|
+
"sub3/x",
|
|
9
|
+
"x"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"2": {
|
|
13
|
+
"section": "4.5",
|
|
14
|
+
"documentation": "",
|
|
15
|
+
"resources": [
|
|
16
|
+
"x"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"3": {
|
|
20
|
+
"section": "4.6",
|
|
21
|
+
"documentation": "",
|
|
22
|
+
"resources": [
|
|
23
|
+
"x"
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"4": {
|
|
27
|
+
"section": "4.6",
|
|
28
|
+
"documentation": "",
|
|
29
|
+
"resources": [
|
|
30
|
+
"x"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"5": {
|
|
34
|
+
"section": "4.6",
|
|
35
|
+
"documentation": "",
|
|
36
|
+
"resources": [
|
|
37
|
+
"x"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"6": {
|
|
41
|
+
"section": "4.6",
|
|
42
|
+
"documentation": "",
|
|
43
|
+
"resources": [
|
|
44
|
+
"x"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"7": {
|
|
48
|
+
"section": "4.6",
|
|
49
|
+
"documentation": "",
|
|
50
|
+
"resources": [
|
|
51
|
+
"x",
|
|
52
|
+
"y"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"8": {
|
|
56
|
+
"section": "4.7",
|
|
57
|
+
"documentation": "",
|
|
58
|
+
"resources": [
|
|
59
|
+
"x"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"9": {
|
|
63
|
+
"section": "4.7",
|
|
64
|
+
"documentation": "",
|
|
65
|
+
"resources": [
|
|
66
|
+
"x"
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
"10": {
|
|
70
|
+
"section": "4.7",
|
|
71
|
+
"documentation": "",
|
|
72
|
+
"resources": [
|
|
73
|
+
"x",
|
|
74
|
+
"y"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
"11": {
|
|
78
|
+
"section": "4.7",
|
|
79
|
+
"documentation": "",
|
|
80
|
+
"resources": [
|
|
81
|
+
"x"
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
"12": {
|
|
85
|
+
"section": "4.7",
|
|
86
|
+
"documentation": "",
|
|
87
|
+
"resources": [
|
|
88
|
+
"x"
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
"13": {
|
|
92
|
+
"section": "4.7",
|
|
93
|
+
"documentation": "",
|
|
94
|
+
"resources": [
|
|
95
|
+
"x",
|
|
96
|
+
"y"
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
"14": {
|
|
100
|
+
"section": "4.7",
|
|
101
|
+
"documentation": "",
|
|
102
|
+
"resources": [
|
|
103
|
+
"x"
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
"15": {
|
|
107
|
+
"section": "4.7",
|
|
108
|
+
"documentation": "",
|
|
109
|
+
"resources": [
|
|
110
|
+
"x"
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
"16": {
|
|
114
|
+
"section": "4.7",
|
|
115
|
+
"documentation": "",
|
|
116
|
+
"resources": [
|
|
117
|
+
"x"
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
"17": {
|
|
121
|
+
"section": "4.7",
|
|
122
|
+
"documentation": "",
|
|
123
|
+
"resources": [
|
|
124
|
+
"x"
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
"18": {
|
|
128
|
+
"section": "4.7",
|
|
129
|
+
"documentation": "",
|
|
130
|
+
"resources": [
|
|
131
|
+
"x"
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
"19": {
|
|
135
|
+
"section": "4.7",
|
|
136
|
+
"documentation": "",
|
|
137
|
+
"resources": [
|
|
138
|
+
"level1.rng",
|
|
139
|
+
"level2.rng"
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
"20": {
|
|
143
|
+
"section": "4.6",
|
|
144
|
+
"documentation": "",
|
|
145
|
+
"resources": [
|
|
146
|
+
"x"
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'rng'
|
|
5
|
+
|
|
6
|
+
RSpec.describe 'Advanced RNC features' do
|
|
7
|
+
describe 'Name wildcards' do
|
|
8
|
+
context 'anyName in attributes' do
|
|
9
|
+
it 'parses wildcard attribute without except' do
|
|
10
|
+
rnc = 'start = element foo { attribute * { text } }'
|
|
11
|
+
expect { Rng.parse_rnc(rnc) }.not_to raise_error
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'parses wildcard attribute with except clause' do
|
|
15
|
+
rnc = 'start = element foo { attribute * - bar { text } }'
|
|
16
|
+
expect { Rng.parse_rnc(rnc) }.not_to raise_error
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context 'anyName in elements' do
|
|
21
|
+
it 'parses wildcard element without except' do
|
|
22
|
+
rnc = 'start = element * { text }'
|
|
23
|
+
expect { Rng.parse_rnc(rnc) }.not_to raise_error
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context 'nsName wildcards' do
|
|
28
|
+
it 'parses namespace wildcard in attributes' do
|
|
29
|
+
rnc = 'start = element foo { attribute xml:* { text } }'
|
|
30
|
+
expect { Rng.parse_rnc(rnc) }.not_to raise_error
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'parses namespace wildcard with except' do
|
|
34
|
+
rnc = 'start = element foo { attribute ns:* - ns:bar { text } }'
|
|
35
|
+
expect { Rng.parse_rnc(rnc) }.not_to raise_error
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe 'Augmentation operators' do
|
|
41
|
+
it 'parses choice augmentation (|=)' do
|
|
42
|
+
rnc = <<~RNC
|
|
43
|
+
start = foo
|
|
44
|
+
foo = bar
|
|
45
|
+
foo |= baz
|
|
46
|
+
RNC
|
|
47
|
+
schema = Rng.parse_rnc(rnc)
|
|
48
|
+
|
|
49
|
+
# Should have two define elements with combine="choice"
|
|
50
|
+
defines = schema.define.select { |d| d.name == 'foo' }
|
|
51
|
+
expect(defines.length).to eq(2)
|
|
52
|
+
expect(defines[1].combine).to eq('choice')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'parses interleave augmentation (&=)' do
|
|
56
|
+
rnc = <<~RNC
|
|
57
|
+
start = foo
|
|
58
|
+
foo = bar
|
|
59
|
+
foo &= baz
|
|
60
|
+
RNC
|
|
61
|
+
schema = Rng.parse_rnc(rnc)
|
|
62
|
+
|
|
63
|
+
defines = schema.define.select { |d| d.name == 'foo' }
|
|
64
|
+
expect(defines.length).to eq(2)
|
|
65
|
+
expect(defines[1].combine).to eq('interleave')
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe 'List patterns' do
|
|
70
|
+
it 'parses simple list pattern' do
|
|
71
|
+
rnc = 'start = element foo { list { xsd:token+ } }'
|
|
72
|
+
expect { Rng.parse_rnc(rnc) }.not_to raise_error
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'parses list with text' do
|
|
76
|
+
rnc = 'start = element foo { list { text } }'
|
|
77
|
+
expect { Rng.parse_rnc(rnc) }.not_to raise_error
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe 'Parent references' do
|
|
82
|
+
it 'parses parent reference' do
|
|
83
|
+
rnc = 'start = parent start'
|
|
84
|
+
expect { Rng.parse_rnc(rnc) }.not_to raise_error
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
describe 'External references' do
|
|
89
|
+
it 'parses external reference' do
|
|
90
|
+
rnc = 'start = external "common.rng"'
|
|
91
|
+
expect { Rng.parse_rnc(rnc) }.not_to raise_error
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
describe 'notAllowed pattern' do
|
|
96
|
+
it 'parses notAllowed' do
|
|
97
|
+
rnc = 'start = element foo { notAllowed }'
|
|
98
|
+
expect { Rng.parse_rnc(rnc) }.not_to raise_error
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'rng/test_suite_parser'
|
|
5
|
+
require 'tmpdir'
|
|
6
|
+
|
|
7
|
+
RSpec.describe 'Official RELAX NG Compact Test Suite' do
|
|
8
|
+
let(:test_suite_path) do
|
|
9
|
+
File.expand_path('../fixtures/compacttest.xml', __dir__)
|
|
10
|
+
end
|
|
11
|
+
let(:test_suite) { Rng::TestSuiteParser.load(test_suite_path) }
|
|
12
|
+
|
|
13
|
+
describe 'Test Suite Loading' do
|
|
14
|
+
it 'loads the test suite successfully' do
|
|
15
|
+
expect(test_suite).to be_a(Rng::TestSuiteParser)
|
|
16
|
+
expect(test_suite.test_cases).not_to be_empty
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'provides test case categorization' do
|
|
20
|
+
expect(test_suite.test_cases).not_to be_empty
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe 'Valid RNC Parsing' do
|
|
25
|
+
let(:valid_cases) { test_suite.valid_rnc_cases }
|
|
26
|
+
|
|
27
|
+
context 'without external resources' do
|
|
28
|
+
let(:cases_without_resources) do
|
|
29
|
+
valid_cases.reject(&:has_resources?)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'has test cases to validate' do
|
|
33
|
+
expect(cases_without_resources).not_to be_empty
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
0
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe 'Valid RNC Schema Parsing (Individual Tests)' do
|
|
41
|
+
let(:test_suite_instance) { Rng::TestSuiteParser.load(test_suite_path) }
|
|
42
|
+
|
|
43
|
+
before(:all) do
|
|
44
|
+
@suite = Rng::TestSuiteParser.load(
|
|
45
|
+
File.expand_path('../fixtures/compacttest.xml', __dir__)
|
|
46
|
+
)
|
|
47
|
+
@results = {
|
|
48
|
+
passed: 0,
|
|
49
|
+
failed: 0,
|
|
50
|
+
skipped: 0,
|
|
51
|
+
errors: []
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
test_suite_parser = Rng::TestSuiteParser.load(
|
|
56
|
+
File.expand_path('../fixtures/compacttest.xml', __dir__)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
test_suite_parser.valid_rnc_cases.each_with_index do |test_case, _idx|
|
|
60
|
+
next if test_case.has_resources? # Skip resource-based tests for now
|
|
61
|
+
|
|
62
|
+
it "parses test case ##{test_case.id} (#{test_case.description})" do
|
|
63
|
+
rnc_content = test_case.compact_correct
|
|
64
|
+
|
|
65
|
+
begin
|
|
66
|
+
grammar = Rng.parse_rnc(rnc_content)
|
|
67
|
+
|
|
68
|
+
expect(grammar).to be_a(Rng::Grammar)
|
|
69
|
+
@results[:passed] += 1
|
|
70
|
+
rescue StandardError => e
|
|
71
|
+
@results[:failed] += 1
|
|
72
|
+
@results[:errors] << {
|
|
73
|
+
id: test_case.id,
|
|
74
|
+
message: e.message,
|
|
75
|
+
rnc: rnc_content[0..200]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# Re-raise to fail the test
|
|
79
|
+
raise e
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
describe 'Invalid RNC Rejection (Individual Tests)' do
|
|
86
|
+
before(:all) do
|
|
87
|
+
@suite = Rng::TestSuiteParser.load(
|
|
88
|
+
File.expand_path('../fixtures/compacttest.xml', __dir__)
|
|
89
|
+
)
|
|
90
|
+
@results = {
|
|
91
|
+
correctly_rejected: 0,
|
|
92
|
+
incorrectly_accepted: 0,
|
|
93
|
+
errors: []
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
test_suite_parser = Rng::TestSuiteParser.load(
|
|
98
|
+
File.expand_path('../fixtures/compacttest.xml', __dir__)
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
test_suite_parser.invalid_rnc_cases.each do |test_case|
|
|
102
|
+
it "rejects test case ##{test_case.id} (should fail parsing)" do
|
|
103
|
+
rnc_content = test_case.compact_incorrect
|
|
104
|
+
|
|
105
|
+
expect do
|
|
106
|
+
Rng.parse_rnc(rnc_content)
|
|
107
|
+
end.to raise_error(StandardError)
|
|
108
|
+
|
|
109
|
+
@results[:correctly_rejected] += 1
|
|
110
|
+
rescue RSpec::Expectations::ExpectationNotMetError
|
|
111
|
+
@results[:incorrectly_accepted] += 1
|
|
112
|
+
@results[:errors] << {
|
|
113
|
+
id: test_case.id,
|
|
114
|
+
rnc: rnc_content
|
|
115
|
+
}
|
|
116
|
+
raise
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
describe 'RNC ↔ RNG Round-Trip Conversion' do
|
|
122
|
+
let(:roundtrip_cases) do
|
|
123
|
+
test_suite.roundtrip_cases.reject(&:has_resources?)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
before(:all) do
|
|
127
|
+
@suite = Rng::TestSuiteParser.load(
|
|
128
|
+
File.expand_path('../fixtures/compacttest.xml', __dir__)
|
|
129
|
+
)
|
|
130
|
+
@results = {
|
|
131
|
+
passed: 0,
|
|
132
|
+
failed: 0,
|
|
133
|
+
parse_errors: 0,
|
|
134
|
+
errors: []
|
|
135
|
+
}
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
test_suite_parser = Rng::TestSuiteParser.load(
|
|
139
|
+
File.expand_path('../fixtures/compacttest.xml', __dir__)
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
test_suite_parser.roundtrip_cases
|
|
143
|
+
.reject(&:has_resources?)
|
|
144
|
+
.each do |test_case|
|
|
145
|
+
it "round-trips test case ##{test_case.id}" do
|
|
146
|
+
rnc_content = test_case.compact_correct
|
|
147
|
+
expected_rng = test_case.xml_correct
|
|
148
|
+
|
|
149
|
+
begin
|
|
150
|
+
# Parse RNC to Grammar
|
|
151
|
+
grammar = Rng.parse_rnc(rnc_content)
|
|
152
|
+
|
|
153
|
+
# Generate RNG XML
|
|
154
|
+
generated_rng = grammar.to_xml
|
|
155
|
+
|
|
156
|
+
# Parse both RNG versions to Grammar for comparison
|
|
157
|
+
# Use Grammar.from_xml to skip schema validation - test suite schemas
|
|
158
|
+
# may have non-standard root elements (e.g., bare value patterns)
|
|
159
|
+
expected_grammar = Rng::Grammar.from_xml(expected_rng)
|
|
160
|
+
generated_grammar = Rng::Grammar.from_xml(generated_rng)
|
|
161
|
+
|
|
162
|
+
# Compare using structural equivalence
|
|
163
|
+
# For now, just check both parse successfully
|
|
164
|
+
expect(generated_grammar).to be_a(Rng::Grammar)
|
|
165
|
+
expect(expected_grammar).to be_a(Rng::Grammar)
|
|
166
|
+
|
|
167
|
+
@results[:passed] += 1
|
|
168
|
+
rescue Parslet::ParseFailed, StandardError => e
|
|
169
|
+
if e.is_a?(Parslet::ParseFailed) || e.message.include?('parse')
|
|
170
|
+
@results[:parse_errors] += 1
|
|
171
|
+
else
|
|
172
|
+
@results[:failed] += 1
|
|
173
|
+
@results[:errors] << {
|
|
174
|
+
id: test_case.id,
|
|
175
|
+
message: e.message
|
|
176
|
+
}
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Don't fail the test for parse errors, only conversion errors
|
|
180
|
+
raise e unless e.is_a?(Parslet::ParseFailed) || e.message.include?('parse')
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
describe 'Resource-Based Tests' do
|
|
187
|
+
let(:resource_cases) { test_suite.resource_cases }
|
|
188
|
+
|
|
189
|
+
it 'identifies resource-based test cases' do
|
|
190
|
+
expect(resource_cases).not_to be_empty
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it 'skips resource-based tests (not yet implemented)' do
|
|
194
|
+
skip 'External resource handling not yet implemented'
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require_relative '../../lib/rng/datatype_declaration'
|
|
5
|
+
|
|
6
|
+
RSpec.describe Rng::DatatypeDeclaration do
|
|
7
|
+
describe '#initialize' do
|
|
8
|
+
it 'creates a datatype declaration with prefix and URI' do
|
|
9
|
+
decl = described_class.new(
|
|
10
|
+
prefix: 'xsd',
|
|
11
|
+
uri: 'http://www.w3.org/2001/XMLSchema-datatypes'
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
expect(decl.prefix).to eq('xsd')
|
|
15
|
+
expect(decl.uri).to eq('http://www.w3.org/2001/XMLSchema-datatypes')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'creates a datatype declaration with custom prefix' do
|
|
19
|
+
decl = described_class.new(
|
|
20
|
+
prefix: 'custom',
|
|
21
|
+
uri: 'http://example.com/datatypes'
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
expect(decl.prefix).to eq('custom')
|
|
25
|
+
expect(decl.uri).to eq('http://example.com/datatypes')
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Rng::Div do
|
|
6
|
+
describe 'Div pattern parsing and generation' do
|
|
7
|
+
let(:rng_with_div) do
|
|
8
|
+
<<~RNG
|
|
9
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
10
|
+
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
|
|
11
|
+
<start>
|
|
12
|
+
<element name="doc">
|
|
13
|
+
<text/>
|
|
14
|
+
</element>
|
|
15
|
+
</start>
|
|
16
|
+
#{' '}
|
|
17
|
+
<div>
|
|
18
|
+
<define name="section">
|
|
19
|
+
<element name="section">
|
|
20
|
+
<ref name="title"/>
|
|
21
|
+
<ref name="content"/>
|
|
22
|
+
</element>
|
|
23
|
+
</define>
|
|
24
|
+
#{' '}
|
|
25
|
+
<define name="title">
|
|
26
|
+
<element name="title">
|
|
27
|
+
<text/>
|
|
28
|
+
</element>
|
|
29
|
+
</define>
|
|
30
|
+
</div>
|
|
31
|
+
#{' '}
|
|
32
|
+
<div>
|
|
33
|
+
<define name="content">
|
|
34
|
+
<element name="content">
|
|
35
|
+
<text/>
|
|
36
|
+
</element>
|
|
37
|
+
</define>
|
|
38
|
+
</div>
|
|
39
|
+
</grammar>
|
|
40
|
+
RNG
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'parses RNG with div blocks' do
|
|
44
|
+
parsed = Rng::Grammar.from_xml(rng_with_div)
|
|
45
|
+
|
|
46
|
+
expect(parsed).to be_a(Rng::Grammar)
|
|
47
|
+
expect(parsed.div).not_to be_empty
|
|
48
|
+
expect(parsed.div.length).to eq(2)
|
|
49
|
+
|
|
50
|
+
# First div should have 2 defines
|
|
51
|
+
first_div = parsed.div[0]
|
|
52
|
+
expect(first_div).to be_a(described_class)
|
|
53
|
+
expect(first_div.define.length).to eq(2)
|
|
54
|
+
expect(first_div.define[0].name).to eq('section')
|
|
55
|
+
expect(first_div.define[1].name).to eq('title')
|
|
56
|
+
|
|
57
|
+
# Second div should have 1 define
|
|
58
|
+
second_div = parsed.div[1]
|
|
59
|
+
expect(second_div.define.length).to eq(1)
|
|
60
|
+
expect(second_div.define[0].name).to eq('content')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'correctly round-trips RNG with div blocks' do
|
|
64
|
+
parsed = Rng::Grammar.from_xml(rng_with_div)
|
|
65
|
+
regenerated = parsed.to_xml
|
|
66
|
+
|
|
67
|
+
expect(regenerated).to be_xml_equivalent_to(rng_with_div)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'generates RNC with div blocks' do
|
|
71
|
+
parsed = Rng::Grammar.from_xml(rng_with_div)
|
|
72
|
+
rnc = Rng.to_rnc(parsed)
|
|
73
|
+
|
|
74
|
+
expect(rnc).to include('div {')
|
|
75
|
+
expect(rnc).to include('section =')
|
|
76
|
+
expect(rnc).to include('title =')
|
|
77
|
+
expect(rnc).to include('content =')
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe 'Nested div patterns' do
|
|
82
|
+
let(:rng_with_nested_div) do
|
|
83
|
+
<<~RNG
|
|
84
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
85
|
+
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
|
|
86
|
+
<start>
|
|
87
|
+
<element name="doc">
|
|
88
|
+
<text/>
|
|
89
|
+
</element>
|
|
90
|
+
</start>
|
|
91
|
+
#{' '}
|
|
92
|
+
<div>
|
|
93
|
+
<define name="outer">
|
|
94
|
+
<element name="outer">
|
|
95
|
+
<text/>
|
|
96
|
+
</element>
|
|
97
|
+
</define>
|
|
98
|
+
#{' '}
|
|
99
|
+
<div>
|
|
100
|
+
<define name="inner">
|
|
101
|
+
<element name="inner">
|
|
102
|
+
<text/>
|
|
103
|
+
</element>
|
|
104
|
+
</define>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</grammar>
|
|
108
|
+
RNG
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it 'parses nested div blocks' do
|
|
112
|
+
parsed = Rng::Grammar.from_xml(rng_with_nested_div)
|
|
113
|
+
|
|
114
|
+
expect(parsed.div.length).to eq(1)
|
|
115
|
+
|
|
116
|
+
outer_div = parsed.div[0]
|
|
117
|
+
expect(outer_div.define.length).to eq(1)
|
|
118
|
+
expect(outer_div.define[0].name).to eq('outer')
|
|
119
|
+
|
|
120
|
+
# Check nested div
|
|
121
|
+
expect(outer_div.div.length).to eq(1)
|
|
122
|
+
inner_div = outer_div.div[0]
|
|
123
|
+
expect(inner_div.define.length).to eq(1)
|
|
124
|
+
expect(inner_div.define[0].name).to eq('inner')
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it 'correctly round-trips nested div blocks' do
|
|
128
|
+
parsed = Rng::Grammar.from_xml(rng_with_nested_div)
|
|
129
|
+
regenerated = parsed.to_xml
|
|
130
|
+
|
|
131
|
+
expect(regenerated).to be_xml_equivalent_to(rng_with_nested_div)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
describe 'Div with start pattern' do
|
|
136
|
+
let(:rng_with_div_start) do
|
|
137
|
+
<<~RNG
|
|
138
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
139
|
+
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
|
|
140
|
+
<div>
|
|
141
|
+
<start>
|
|
142
|
+
<element name="doc">
|
|
143
|
+
<text/>
|
|
144
|
+
</element>
|
|
145
|
+
</start>
|
|
146
|
+
#{' '}
|
|
147
|
+
<define name="helper">
|
|
148
|
+
<element name="helper">
|
|
149
|
+
<text/>
|
|
150
|
+
</element>
|
|
151
|
+
</define>
|
|
152
|
+
</div>
|
|
153
|
+
</grammar>
|
|
154
|
+
RNG
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it 'parses div with start pattern' do
|
|
158
|
+
parsed = Rng::Grammar.from_xml(rng_with_div_start)
|
|
159
|
+
|
|
160
|
+
expect(parsed.div.length).to eq(1)
|
|
161
|
+
|
|
162
|
+
div = parsed.div[0]
|
|
163
|
+
expect(div.start).not_to be_empty
|
|
164
|
+
expect(div.start[0].element.attr_name).to eq('doc')
|
|
165
|
+
expect(div.define.length).to eq(1)
|
|
166
|
+
expect(div.define[0].name).to eq('helper')
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
describe 'Div attributes' do
|
|
171
|
+
let(:rng_with_div_attrs) do
|
|
172
|
+
<<~RNG
|
|
173
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
174
|
+
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
|
|
175
|
+
<start>
|
|
176
|
+
<element name="doc">
|
|
177
|
+
<text/>
|
|
178
|
+
</element>
|
|
179
|
+
</start>
|
|
180
|
+
#{' '}
|
|
181
|
+
<div id="section-div" ns="http://example.com/ns">
|
|
182
|
+
<define name="section">
|
|
183
|
+
<element name="section">
|
|
184
|
+
<text/>
|
|
185
|
+
</element>
|
|
186
|
+
</define>
|
|
187
|
+
</div>
|
|
188
|
+
</grammar>
|
|
189
|
+
RNG
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it 'parses div with id and ns attributes' do
|
|
193
|
+
parsed = Rng::Grammar.from_xml(rng_with_div_attrs)
|
|
194
|
+
|
|
195
|
+
div = parsed.div[0]
|
|
196
|
+
expect(div.id).to eq('section-div')
|
|
197
|
+
expect(div.ns).to eq('http://example.com/ns')
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it 'correctly round-trips div with attributes' do
|
|
201
|
+
parsed = Rng::Grammar.from_xml(rng_with_div_attrs)
|
|
202
|
+
regenerated = parsed.to_xml
|
|
203
|
+
|
|
204
|
+
expect(regenerated).to be_xml_equivalent_to(rng_with_div_attrs)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|