the-experimenters-rdf-rdfxml 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,79 @@
1
+ require 'rspec/matchers'
2
+
3
+ RSpec::Matchers.define :have_xpath do |xpath, value, namespaces = {}|
4
+ match do |actual|
5
+ @doc = Nokogiri::XML.parse(actual)
6
+ @doc.should be_a(Nokogiri::XML::Document)
7
+ @doc.root.should be_a(Nokogiri::XML::Element)
8
+ @namespaces = @doc.namespaces.
9
+ merge(namespaces).
10
+ merge("xhtml" => "http://www.w3.org/1999/xhtml", "xml" => "http://www.w3.org/XML/1998/namespace")
11
+ @result = @doc.root.at_xpath(xpath, @namespaces) rescue false
12
+ case value
13
+ when false
14
+ @result.should be_nil
15
+ when Array
16
+ @result.to_s.split(" ").should include(*value)
17
+ when Regexp
18
+ @result.to_s.should =~ value
19
+ else
20
+ @result.to_s.should == value
21
+ end
22
+ end
23
+
24
+ failure_message_for_should do |actual|
25
+ msg = "expected to that #{xpath.inspect} would be #{value.inspect} in:\n" + actual.to_s
26
+ msg += "was: #{@result}"
27
+ end
28
+ end
29
+
30
+ def normalize(graph)
31
+ case graph
32
+ when RDF::Graph then graph
33
+ when IO, StringIO
34
+ RDF::Graph.new.load(graph, :base_uri => @info.about)
35
+ else
36
+ # Figure out which parser to use
37
+ g = RDF::Graph.new
38
+ reader_class = RDF::Reader.for(detect_format(graph))
39
+ reader_class.new(graph, :base_uri => @info.about).each {|s| g << s}
40
+ g
41
+ end
42
+ end
43
+
44
+ Info = Struct.new(:about, :information, :trace, :compare, :inputDocument, :outputDocument)
45
+
46
+ RSpec::Matchers.define :be_equivalent_graph do |expected, info|
47
+ match do |actual|
48
+ @info = if info.respond_to?(:about)
49
+ info
50
+ elsif info.is_a?(Hash)
51
+ identifier = info[:identifier] || expected.is_a?(RDF::Graph) ? expected.context : info[:about]
52
+ trace = info[:trace]
53
+ trace = trace.join("\n") if trace.is_a?(Array)
54
+ Info.new(identifier, info[:information] || "", trace, info[:compare])
55
+ else
56
+ Info.new(expected.is_a?(RDF::Graph) ? expected.context : info, info.to_s)
57
+ end
58
+ @expected = normalize(expected)
59
+ @actual = normalize(actual)
60
+ @actual.isomorphic_with?(@expected)
61
+ end
62
+
63
+ failure_message_for_should do |actual|
64
+ info = @info.respond_to?(:information) ? @info.information : @info.inspect
65
+ if @expected.is_a?(RDF::Graph) && @actual.size != @expected.size
66
+ "Graph entry count differs:\nexpected: #{@expected.size}\nactual: #{@actual.size}"
67
+ elsif @expected.is_a?(Array) && @actual.size != @expected.length
68
+ "Graph entry count differs:\nexpected: #{@expected.length}\nactual: #{@actual.size}"
69
+ else
70
+ "Graph differs"
71
+ end +
72
+ "\n#{info + "\n" unless info.empty?}" +
73
+ (@info.inputDocument ? "Input file: #{@info.inputDocument}\n" : "") +
74
+ (@info.outputDocument ? "Output file: #{@info.outputDocument}\n" : "") +
75
+ "Unsorted Expected:\n#{@expected.dump(:ntriples)}" +
76
+ "Unsorted Results:\n#{@actual.dump(:ntriples)}" +
77
+ (@info.trace ? "\nDebug:\n#{@info.trace}" : "")
78
+ end
79
+ end
@@ -0,0 +1,69 @@
1
+ # Spira class for manipulating test-manifest style test suites.
2
+ # Used for SWAP tests
3
+ require 'spira'
4
+ require 'rdf/n3'
5
+ require 'open-uri'
6
+
7
+ module Fixtures
8
+ module TestCase
9
+ class Test < RDF::Vocabulary("http://www.w3.org/2000/10/rdf-tests/rdfcore/testSchema#"); end
10
+
11
+ class Entry
12
+ attr_accessor :debug
13
+ attr_accessor :compare
14
+ include Spira::Resource
15
+
16
+ property :description, :predicate => Test.description, :type => XSD.string
17
+ property :status, :predicate => Test.status, :type => XSD.string
18
+ property :warning, :predicate => Test.warning, :type => XSD.string
19
+ property :approval, :predicate => Test.approval
20
+ property :issue, :predicate => Test.issue
21
+ property :document, :predicate => Test.document
22
+ property :discussion, :predicate => Test.discussion
23
+ property :inputDocument, :predicate => Test.inputDocument
24
+ property :outputDocument, :predicate => Test.outputDocument
25
+
26
+ def name
27
+ subject.to_s.split("#").last
28
+ end
29
+
30
+ def input
31
+ Kernel.open(self.inputDocument)
32
+ end
33
+
34
+ def output
35
+ self.outputDocument ? Kernel.open(self.outputDocument) : ""
36
+ end
37
+
38
+ def information; self.description; end
39
+
40
+ def inspect
41
+ "[#{self.class.to_s} " + %w(
42
+ subject
43
+ description
44
+ inputDocument
45
+ outputDocument
46
+ ).map {|a| v = self.send(a); "#{a}='#{v}'" if v}.compact.join(", ") +
47
+ "]"
48
+ end
49
+ end
50
+
51
+ class PositiveParserTest < Entry
52
+ default_source :entries
53
+ type Test.PositiveParserTest
54
+ end
55
+
56
+ class NegativeParserTest < Entry
57
+ default_source :entries
58
+ type Test.NegativeParserTest
59
+ end
60
+
61
+ class MiscellaneousTest < Entry
62
+ default_source :entries
63
+ type Test.MiscellaneousTest
64
+ end
65
+
66
+ repo = RDF::Repository.load("http://www.w3.org/2000/10/rdf-tests/rdfcore/Manifest.rdf")
67
+ Spira.add_repository! :entries, repo
68
+ end
69
+ end
@@ -0,0 +1,361 @@
1
+ # coding: utf-8
2
+ $:.unshift "."
3
+ require File.join(File.dirname(__FILE__), 'spec_helper')
4
+ require 'rdf/spec/reader'
5
+
6
+ # w3c test suite: http://www.w3.org/TR/rdf-testcases/
7
+
8
+ describe "RDF::RDFXML::Reader" do
9
+ before :each do
10
+ @reader = RDF::RDFXML::Reader
11
+ end
12
+
13
+ it_should_behave_like RDF_Reader
14
+
15
+ context "discovery" do
16
+ {
17
+ "rdf" => RDF::Reader.for(:rdf),
18
+ "rdfxml" => RDF::Reader.for(:rdfxml),
19
+ "etc/foaf.xml" => RDF::Reader.for("etc/foaf.xml"),
20
+ "etc/foaf.rdf" => RDF::Reader.for("etc/foaf.rdf"),
21
+ "foaf.xml" => RDF::Reader.for(:file_name => "foaf.xml"),
22
+ "foaf.rdf" => RDF::Reader.for(:file_name => "foaf.rdf"),
23
+ ".xml" => RDF::Reader.for(:file_extension => "xml"),
24
+ ".rdf" => RDF::Reader.for(:file_extension => "rdf"),
25
+ "application/xml" => RDF::Reader.for(:content_type => "application/xml"),
26
+ "application/rdf+xml" => RDF::Reader.for(:content_type => "application/rdf+xml"),
27
+ }.each_pair do |label, format|
28
+ it "should discover '#{label}'" do
29
+ format.should == @reader
30
+ end
31
+ end
32
+ end
33
+
34
+ context :interface do
35
+ before(:each) do
36
+ @sampledoc = <<-EOF;
37
+ <?xml version="1.0" ?>
38
+ <GenericXML xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:ex="http://example.org/">
39
+ <rdf:RDF>
40
+ <rdf:Description rdf:about="http://example.org/one">
41
+ <ex:name>Foo</ex:name>
42
+ </rdf:Description>
43
+ </rdf:RDF>
44
+ <blablabla />
45
+ <rdf:RDF>
46
+ <rdf:Description rdf:about="http://example.org/two">
47
+ <ex:name>Bar</ex:name>
48
+ </rdf:Description>
49
+ </rdf:RDF>
50
+ </GenericXML>
51
+ EOF
52
+ end
53
+
54
+ it "should yield reader" do
55
+ inner = mock("inner")
56
+ inner.should_receive(:called).with(@reader)
57
+ @reader.new(@sampledoc) do |reader|
58
+ inner.called(reader.class)
59
+ end
60
+ end
61
+
62
+ it "should return reader" do
63
+ @reader.new(@sampledoc).should be_a(@reader)
64
+ end
65
+
66
+ it "should yield statements" do
67
+ inner = mock("inner")
68
+ inner.should_receive(:called).with(RDF::Statement).twice
69
+ @reader.new(@sampledoc).each_statement do |statement|
70
+ inner.called(statement.class)
71
+ end
72
+ end
73
+
74
+ it "should yield triples" do
75
+ inner = mock("inner")
76
+ inner.should_receive(:called).with(RDF::URI, RDF::URI, RDF::Literal).twice
77
+ @reader.new(@sampledoc).each_triple do |subject, predicate, object|
78
+ inner.called(subject.class, predicate.class, object.class)
79
+ end
80
+ end
81
+ end
82
+
83
+ context "simple parsing" do
84
+ it "should recognise and create single triple for empty non-RDF root" do
85
+ sampledoc = %(<?xml version="1.0" ?>
86
+ <NotRDF />)
87
+ graph = parse(sampledoc, :base_uri => "http://example.com", :validate => true)
88
+ graph.size.should == 1
89
+ statement = graph.statements.first
90
+ statement.subject.class.should == RDF::Node
91
+ statement.predicate.should == RDF.type
92
+ statement.object.should == RDF::XML.NotRDF
93
+ end
94
+
95
+ it "should parse on XML documents with multiple RDF nodes" do
96
+ sampledoc = <<-EOF;
97
+ <?xml version="1.0" ?>
98
+ <GenericXML xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:ex="http://example.org/">
99
+ <rdf:RDF>
100
+ <rdf:Description rdf:about="http://example.org/one">
101
+ <ex:name>Foo</ex:name>
102
+ </rdf:Description>
103
+ </rdf:RDF>
104
+ <blablabla />
105
+ <rdf:RDF>
106
+ <rdf:Description rdf:about="http://example.org/two">
107
+ <ex:name>Bar</ex:name>
108
+ </rdf:Description>
109
+ </rdf:RDF>
110
+ </GenericXML>
111
+ EOF
112
+ graph = parse(sampledoc, :base_uri => "http://example.com", :validate => true)
113
+ objects = graph.statements.map {|s| s.object.value}.sort
114
+ objects.should == ["Bar", "Foo"]
115
+ end
116
+
117
+ it "should be able to parse a simple single-triple document" do
118
+ sampledoc = <<-EOF;
119
+ <?xml version="1.0" ?>
120
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
121
+ xmlns:ex="http://www.example.org/" xml:lang="en" xml:base="http://www.example.org/foo">
122
+ <ex:Thing rdf:about="http://example.org/joe" ex:name="bar">
123
+ <ex:belongsTo rdf:resource="http://tommorris.org/" />
124
+ <ex:sampleText rdf:datatype="http://www.w3.org/2001/XMLSchema#string">foo</ex:sampleText>
125
+ <ex:hadADodgyRelationshipWith>
126
+ <rdf:Description>
127
+ <ex:name>Tom</ex:name>
128
+ <ex:hadADodgyRelationshipWith>
129
+ <rdf:Description>
130
+ <ex:name>Rob</ex:name>
131
+ <ex:hadADodgyRelationshipWith>
132
+ <rdf:Description>
133
+ <ex:name>Mary</ex:name>
134
+ </rdf:Description>
135
+ </ex:hadADodgyRelationshipWith>
136
+ </rdf:Description>
137
+ </ex:hadADodgyRelationshipWith>
138
+ </rdf:Description>
139
+ </ex:hadADodgyRelationshipWith>
140
+ </ex:Thing>
141
+ </rdf:RDF>
142
+ EOF
143
+
144
+ graph = parse(sampledoc, :base_uri => "http://example.com", :validate => true)
145
+ #puts @debug
146
+ graph.size.should == 10
147
+ # print graph.dump(:ntriples
148
+ # TODO: add datatype parsing
149
+ # TODO: make sure the BNode forging is done correctly - an internal element->nodeID mapping
150
+ # TODO: proper test
151
+ end
152
+
153
+ it "should be able to handle Bags/Alts etc." do
154
+ sampledoc = <<-EOF;
155
+ <?xml version="1.0" ?>
156
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:eg="http://example.org/">
157
+ <rdf:Bag>
158
+ <rdf:li rdf:resource="http://tommorris.org/" />
159
+ <rdf:li rdf:resource="http://twitter.com/tommorris" />
160
+ </rdf:Bag>
161
+ </rdf:RDF>
162
+ EOF
163
+ graph = parse(sampledoc, :base_uri => "http://example.com", :validate => true)
164
+ graph.predicates.map(&:to_s).should include("http://www.w3.org/1999/02/22-rdf-syntax-ns#_1", "http://www.w3.org/1999/02/22-rdf-syntax-ns#_2")
165
+ end
166
+ end
167
+
168
+ context :exceptions do
169
+ it "should raise an error if rdf:aboutEach is used, as per the negative parser test rdfms-abouteach-error001 (rdf:aboutEach attribute)" do
170
+ sampledoc = <<-EOF;
171
+ <?xml version="1.0" ?>
172
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
173
+ xmlns:eg="http://example.org/">
174
+
175
+ <rdf:Bag rdf:ID="node">
176
+ <rdf:li rdf:resource="http://example.org/node2"/>
177
+ </rdf:Bag>
178
+
179
+ <rdf:Description rdf:aboutEach="#node">
180
+ <dc:rights xmlns:dc="http://purl.org/dc/elements/1.1/">me</dc:rights>
181
+
182
+ </rdf:Description>
183
+
184
+ </rdf:RDF>
185
+ EOF
186
+
187
+ lambda do
188
+ graph = parse(sampledoc, :base_uri => "http://example.com", :validate => true)
189
+ end.should raise_error(RDF::ReaderError, /Obsolete attribute .*aboutEach/)
190
+ end
191
+
192
+ it "should raise an error if rdf:aboutEachPrefix is used, as per the negative parser test rdfms-abouteach-error002 (rdf:aboutEachPrefix attribute)" do
193
+ sampledoc = <<-EOF;
194
+ <?xml version="1.0" ?>
195
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
196
+ xmlns:eg="http://example.org/">
197
+
198
+ <rdf:Description rdf:about="http://example.org/node">
199
+ <eg:property>foo</eg:property>
200
+ </rdf:Description>
201
+
202
+ <rdf:Description rdf:aboutEachPrefix="http://example.org/">
203
+ <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">me</dc:creator>
204
+
205
+ </rdf:Description>
206
+
207
+ </rdf:RDF>
208
+ EOF
209
+
210
+ lambda do
211
+ graph = parse(sampledoc, :base_uri => "http://example.com", :validate => true)
212
+ end.should raise_error(RDF::ReaderError, /Obsolete attribute .*aboutEachPrefix/)
213
+ end
214
+
215
+ it "should fail if given a non-ID as an ID (as per rdfcore-rdfms-rdf-id-error001)" do
216
+ sampledoc = <<-EOF;
217
+ <?xml version="1.0"?>
218
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
219
+ <rdf:Description rdf:ID='333-555-666' />
220
+ </rdf:RDF>
221
+ EOF
222
+
223
+ lambda do
224
+ graph = parse(sampledoc, :base_uri => "http://example.com", :validate => true)
225
+ end.should raise_error(RDF::ReaderError, /ID addtribute '.*' must be a NCName/)
226
+ end
227
+
228
+ it "should make sure that the value of rdf:ID attributes match the XML Name production (child-element version)" do
229
+ sampledoc = <<-EOF;
230
+ <?xml version="1.0" ?>
231
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
232
+ xmlns:eg="http://example.org/">
233
+ <rdf:Description>
234
+ <eg:prop rdf:ID="q:name" />
235
+ </rdf:Description>
236
+ </rdf:RDF>
237
+ EOF
238
+
239
+ lambda do
240
+ graph = parse(sampledoc, :base_uri => "http://example.com", :validate => true)
241
+ end.should raise_error(RDF::ReaderError, /ID addtribute '.*' must be a NCName/)
242
+ end
243
+
244
+ it "should make sure that the value of rdf:ID attributes match the XML Name production (data attribute version)" do
245
+ sampledoc = <<-EOF;
246
+ <?xml version="1.0" ?>
247
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
248
+ xmlns:eg="http://example.org/">
249
+ <rdf:Description rdf:ID="a/b" eg:prop="val" />
250
+ </rdf:RDF>
251
+ EOF
252
+
253
+ lambda do
254
+ graph = parse(sampledoc, :base_uri => "http://example.com", :validate => true)
255
+ end.should raise_error(RDF::ReaderError, "ID addtribute 'a/b' must be a NCName")
256
+ end
257
+
258
+ it "should detect bad bagIDs" do
259
+ sampledoc = <<-EOF;
260
+ <?xml version="1.0" ?>
261
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
262
+ <rdf:Description rdf:bagID='333-555-666' />
263
+ </rdf:RDF>
264
+ EOF
265
+
266
+ lambda do
267
+ graph = parse(sampledoc, :base_uri => "http://example.com", :validate => true)
268
+ puts @debug
269
+ end.should raise_error(RDF::ReaderError, /Obsolete attribute .*bagID/)
270
+ end
271
+ end
272
+
273
+ context :reification do
274
+ it "should be able to reify according to §2.17 of RDF/XML Syntax Specification" do
275
+ sampledoc = <<-EOF;
276
+ <?xml version="1.0"?>
277
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
278
+ xmlns:ex="http://example.org/stuff/1.0/"
279
+ xml:base="http://example.org/triples/">
280
+ <rdf:Description rdf:about="http://example.org/">
281
+ <ex:prop rdf:ID="triple1">blah</ex:prop>
282
+ </rdf:Description>
283
+ </rdf:RDF>
284
+ EOF
285
+
286
+ triples = <<-EOF
287
+ <http://example.org/> <http://example.org/stuff/1.0/prop> \"blah\" .
288
+ <http://example.org/triples/#triple1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> .
289
+ <http://example.org/triples/#triple1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> <http://example.org/> .
290
+ <http://example.org/triples/#triple1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> <http://example.org/stuff/1.0/prop> .
291
+ <http://example.org/triples/#triple1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#object> \"blah\" .
292
+ EOF
293
+
294
+ graph = parse(sampledoc, :base_uri => "http://example.com", :validate => true)
295
+ graph.should be_equivalent_graph(triples, :about => "http://example.com/", :trace => @debug)
296
+ end
297
+ end
298
+
299
+ # W3C Test suite from http://www.w3.org/2000/10/rdf-tests/rdfcore/
300
+ describe "w3c rdfcore tests" do
301
+ require 'rdf_test'
302
+
303
+ # Positive parser tests should raise errors.
304
+ describe "positive parser tests" do
305
+ Fixtures::TestCase::PositiveParserTest.each do |t|
306
+ next unless t.status == "APPROVED"
307
+ #next unless t.about =~ /rdfms-rdf-names-use/
308
+ #next unless t.name =~ /11/
309
+ #puts t.inspect
310
+ specify "#{t.name}: " + (t.description || "#{t.inputDocument} against #{t.outputDocument}") do
311
+ begin
312
+ graph = RDF::Graph.new << @reader.new(t.input,
313
+ :base_uri => t.inputDocument,
314
+ :validate => false,
315
+ :debug => t.debug)
316
+
317
+ # Parse result graph
318
+ #puts "parse #{self.outputDocument} as #{RDF::Reader.for(self.outputDocument)}"
319
+ format = detect_format(t.output)
320
+ output_graph = RDF::Graph.load(t.outputDocument, :format => format, :base_uri => t.inputDocument)
321
+ puts "result: #{CGI.escapeHTML(graph.dump(:ntriples))}" if ::RDF::N3::debug?
322
+ graph.should be_equivalent_graph(output_graph, t)
323
+ rescue RSpec::Expectations::ExpectationNotMetError => e
324
+ if t.inputDocument =~ %r(xml-literal|xml-canon)
325
+ pending("XMLLiteral canonicalization not implemented yet")
326
+ else
327
+ raise
328
+ end
329
+ end
330
+ end
331
+ end
332
+ end
333
+
334
+ # Negative parser tests should raise errors.
335
+ describe "negative parser tests" do
336
+ Fixtures::TestCase::NegativeParserTest.each do |t|
337
+ next unless t.status == "APPROVED"
338
+ #next unless t.about =~ /rdfms-empty-property-elements/
339
+ #next unless t.name =~ /1/
340
+ #puts t.inspect
341
+ specify "test #{t.name}: " + (t.description || t.inputDocument) do
342
+ lambda do
343
+ RDF::Graph.new << @reader.new(t.input,
344
+ :base_uri => t.inputDocument,
345
+ :validate => true)
346
+ end.should raise_error(RDF::ReaderError)
347
+ end
348
+ end
349
+ end
350
+ end
351
+
352
+ def parse(input, options)
353
+ @debug = []
354
+ graph = RDF::Graph.new
355
+ @reader.new(input, options.merge(:debug => @debug)).each do |statement|
356
+ graph << statement
357
+ end
358
+ graph
359
+ end
360
+ end
361
+