earl-report 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +99 -0
- data/VERSION +1 -0
- data/bin/earl-report +68 -0
- data/lib/earl_report.rb +424 -0
- data/spec/earl_report_spec.rb +373 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/test-files/doap.ttl +33 -0
- data/spec/test-files/foaf.ttl +4 -0
- data/spec/test-files/manifest.ttl +26 -0
- data/spec/test-files/report-complete.ttl +48 -0
- data/spec/test-files/report-no-doap.ttl +23 -0
- data/spec/test-files/report-no-foaf.ttl +45 -0
- data/spec/test-files/results.html +229 -0
- data/spec/test-files/results.jsonld +130 -0
- data/spec/test-files/results.ttl +45 -0
- data/spec/test-files/test-00.out +1 -0
- data/spec/test-files/test-00.ttl +2 -0
- metadata +125 -0
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# earl-report
|
2
|
+
============
|
3
|
+
|
4
|
+
Ruby gem to consolidate multiple EARL report and generate a rollup conformance report.
|
5
|
+
|
6
|
+
## Description
|
7
|
+
|
8
|
+
Reads a test manifest in the
|
9
|
+
[standard RDF WG format](http://www.w3.org/2011/rdf-wg/wiki/Turtle_Test_Suite)
|
10
|
+
and generates a rollup report in RDFa+HTML.
|
11
|
+
|
12
|
+
## Test Specifications
|
13
|
+
The test manifest is presumed to be of the following form:
|
14
|
+
|
15
|
+
### Manifest Header
|
16
|
+
|
17
|
+
The manifest header looks like:
|
18
|
+
|
19
|
+
<> rdf:type mf:Manifest ;
|
20
|
+
rdfs:comment "Turtle tests" ;
|
21
|
+
mf:entries
|
22
|
+
(
|
23
|
+
....
|
24
|
+
) .
|
25
|
+
|
26
|
+
where .... is a list of links to test descriptions, one per line.
|
27
|
+
|
28
|
+
### Test description
|
29
|
+
|
30
|
+
This is an example of a synatx test:
|
31
|
+
|
32
|
+
<#turtle-syntax-file-01> rdf:type rdft:TestTurtlePositiveSyntax ;
|
33
|
+
mf:name "turtle-syntax-file-01" ;
|
34
|
+
rdfs:comment "Further description of the test" ;
|
35
|
+
mf:action <turtle-syntax-file-01.ttl> ;
|
36
|
+
mf:result <turtle-eval-struct-01.nt> .
|
37
|
+
|
38
|
+
## Individual EARL reports
|
39
|
+
|
40
|
+
Results for individual implementations should be specified in Turtle form, but
|
41
|
+
may be specified in an any compatible RDF serialization (JSON-LD is presumed to
|
42
|
+
be a cached rollup report). The report is composed of `Assertion` declarations
|
43
|
+
in the following form:
|
44
|
+
|
45
|
+
[ a earl:Assertion;
|
46
|
+
earl:assertedBy <http://greggkellogg.net/foaf#me>;
|
47
|
+
earl:subject <http://rubygems.org/gems/rdf-turtle>;
|
48
|
+
earl:test <http://dvcs.w3.org/hg/rdf/raw-file/e80b58a1a711/rdf-turtle/tests-ttl/manifest.ttl#turtle-syntax-file-01>;
|
49
|
+
earl:result [
|
50
|
+
a earl:TestResult;
|
51
|
+
earl:outcome earl:passed;
|
52
|
+
dc:date "2012-11-17T15:19:11-05:00"^^xsd:dateTime];
|
53
|
+
earl:mode earl:automatic ] .
|
54
|
+
|
55
|
+
Additionally, `earl:subject` is expected to reference a [DOAP]() description
|
56
|
+
of the reported software, in the following form:
|
57
|
+
|
58
|
+
<http://rubygems.org/gems/rdf-turtle> a doap:Project, earl:TestSubject, earl:Software ;
|
59
|
+
doap:name "RDF::Turtle" ;
|
60
|
+
doap:developer <http://greggkellogg.net/foaf#me> ;
|
61
|
+
doap:homepage <http://ruby-rdf.github.com/rdf-turtle> ;
|
62
|
+
doap:description "RDF::Turtle is an Turtle reader/writer for the RDF.rb library suite."@en ;
|
63
|
+
doap:programming-language "Ruby" .
|
64
|
+
|
65
|
+
The [DOAP]() description may be included in the [EARL]() report. If not found,
|
66
|
+
the IRI identified by `earl:subject` will be dereferenced and is presumed to
|
67
|
+
provide a [DOAP]() specification of the test subject.
|
68
|
+
|
69
|
+
The `doap:developer` is expected to reference a [FOAF]() profile for the agent
|
70
|
+
(user or organization) responsible for the test subject. It is expected to be
|
71
|
+
of the following form:
|
72
|
+
|
73
|
+
<http://greggkellogg.net/foaf#me> foaf:name "Gregg Kellogg" .
|
74
|
+
|
75
|
+
If not found, the IRI identified by `doap:developer`
|
76
|
+
will be dereferenced and is presumed to provide a [FOAF]() profile of the developer.
|
77
|
+
|
78
|
+
## Usage
|
79
|
+
|
80
|
+
The `earl` command may be used to directly create a report from zero or more input files, which are themselves [EARL][] report.
|
81
|
+
|
82
|
+
gem install earl-report
|
83
|
+
|
84
|
+
earl \
|
85
|
+
--output FILE # Location for generated report
|
86
|
+
--tempate [FILE] # Location of report template file; returns default if not specified
|
87
|
+
--bibRef # The default ReSpec-formatted bibliographic reference for the report
|
88
|
+
--name # The name of the software being reported upon
|
89
|
+
manifest # one or more test manifests used to define test descriptions
|
90
|
+
report* # one or more EARL report in most RDF formats
|
91
|
+
|
92
|
+
## Report generation template
|
93
|
+
|
94
|
+
The report template is in ReSpec form using [Haml]() to generate individual elements.
|
95
|
+
|
96
|
+
[DOAP]: https://github.com/edumbill/doap/wiki
|
97
|
+
[EARL]: http://www.w3.org/TR/EARL10-Schema/
|
98
|
+
[FOAF]: http://xmlns.com/foaf/spec/
|
99
|
+
[Haml]: http://haml.info/
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/bin/earl-report
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", 'lib')))
|
4
|
+
|
5
|
+
require 'earl_report'
|
6
|
+
require 'getoptlong'
|
7
|
+
|
8
|
+
def run(input, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
OPT_ARGS = [
|
12
|
+
["--bibRef", GetoptLong::REQUIRED_ARGUMENT,"ReSpec BibRef of specification being reported upon"],
|
13
|
+
["--format", "-f", GetoptLong::REQUIRED_ARGUMENT,"Format of output, one of 'ttl', 'json', or 'html'"],
|
14
|
+
["--json", GetoptLong::NO_ARGUMENT, "Input is a JSON-LD formatted result"],
|
15
|
+
["--name", GetoptLong::REQUIRED_ARGUMENT,"Name of specification"],
|
16
|
+
["--output", "-o", GetoptLong::REQUIRED_ARGUMENT,"Output report to file"],
|
17
|
+
["--template", GetoptLong::OPTIONAL_ARGUMENT,"Specify or return default report template"],
|
18
|
+
["--verbose", GetoptLong::NO_ARGUMENT, "Detail on execution"],
|
19
|
+
["--help", "-?", GetoptLong::NO_ARGUMENT, "This message"]
|
20
|
+
]
|
21
|
+
def usage
|
22
|
+
STDERR.puts %{
|
23
|
+
Generate EARL report for mutliple test results against a test manifest.
|
24
|
+
|
25
|
+
Usage: #{$0} [options] test-manifest test-result ...
|
26
|
+
}.gsub(/^ /, '')
|
27
|
+
width = OPT_ARGS.map do |o|
|
28
|
+
l = o.first.length
|
29
|
+
l += o[1].length + 2 if o[1].is_a?(String)
|
30
|
+
l
|
31
|
+
end.max
|
32
|
+
OPT_ARGS.each do |o|
|
33
|
+
s = " %-*s " % [width, (o[1].is_a?(String) ? "#{o[0,2].join(', ')}" : o[0])]
|
34
|
+
s += o.last
|
35
|
+
STDERR.puts s
|
36
|
+
end
|
37
|
+
exit(1)
|
38
|
+
end
|
39
|
+
|
40
|
+
opts = GetoptLong.new(*OPT_ARGS.map {|o| o[0..-2]})
|
41
|
+
|
42
|
+
options = {
|
43
|
+
:format => :html,
|
44
|
+
:io => STDOUT}
|
45
|
+
|
46
|
+
opts.each do |opt, arg|
|
47
|
+
case opt
|
48
|
+
when '--bibRef' then options[:bibRef] = arg
|
49
|
+
when '--format' then options[:format] = arg.to_sym
|
50
|
+
when '--name' then options[:name] = arg
|
51
|
+
when '--output' then options[:io] = File.open(arg, "w")
|
52
|
+
when '--template' then options[:template] = arg
|
53
|
+
when '--verbose' then options[:verbose] = true
|
54
|
+
when '--help' then usage
|
55
|
+
else
|
56
|
+
options[opt.to_sym] = arg
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# If requesting template, just return it
|
61
|
+
if options.has_key?(:template) && options[:template].empty?
|
62
|
+
File.open(File.expand_path("../../lib/earl_report/views/earl_report.html.haml", __FILE__)) do |f|
|
63
|
+
options[:io].write(f.read)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
earl = EarlReport.new(*ARGV, options)
|
67
|
+
earl.generate(options.merge(:source_files => ARGV))
|
68
|
+
end
|
data/lib/earl_report.rb
ADDED
@@ -0,0 +1,424 @@
|
|
1
|
+
# EARL reporting
|
2
|
+
require 'linkeddata'
|
3
|
+
require 'sparql'
|
4
|
+
require 'haml'
|
5
|
+
|
6
|
+
##
|
7
|
+
# EARL reporting class.
|
8
|
+
# Instantiate a new class using one or more input graphs
|
9
|
+
class EarlReport
|
10
|
+
attr_reader :graph
|
11
|
+
TEST_SUBJECT_QUERY = %(
|
12
|
+
PREFIX doap: <http://usefulinc.com/ns/doap#>
|
13
|
+
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
|
14
|
+
|
15
|
+
SELECT DISTINCT ?uri ?name ?developer ?dev_name ?dev_type ?doap_desc ?homepage ?language
|
16
|
+
WHERE {
|
17
|
+
?uri a doap:Project; doap:name ?name .
|
18
|
+
OPTIONAL { ?uri doap:developer ?developer .}
|
19
|
+
OPTIONAL { ?uri doap:homepage ?homepage . }
|
20
|
+
OPTIONAL { ?uri doap:description ?doap_desc . }
|
21
|
+
OPTIONAL { ?uri doap:programming-language ?language . }
|
22
|
+
OPTIONAL { ?developer foaf:name ?dev_name .}
|
23
|
+
OPTIONAL { ?developer a ?dev_type . }
|
24
|
+
}
|
25
|
+
).freeze
|
26
|
+
|
27
|
+
DOAP_QUERY = %(
|
28
|
+
PREFIX earl: <http://www.w3.org/ns/earl#>
|
29
|
+
PREFIX doap: <http://usefulinc.com/ns/doap#>
|
30
|
+
|
31
|
+
SELECT DISTINCT ?subject ?name
|
32
|
+
WHERE {
|
33
|
+
[ a earl:Assertion; earl:subject ?subject ] .
|
34
|
+
OPTIONAL {
|
35
|
+
?subject a doap:Project; doap:name ?name
|
36
|
+
}
|
37
|
+
}
|
38
|
+
)
|
39
|
+
|
40
|
+
ASSERTION_QUERY = %(
|
41
|
+
PREFIX earl: <http://www.w3.org/ns/earl#>
|
42
|
+
|
43
|
+
SELECT ?by ?mode ?outcome ?subject ?test
|
44
|
+
WHERE {
|
45
|
+
[ a earl:Assertion;
|
46
|
+
earl:assertedBy ?by;
|
47
|
+
earl:mode ?mode;
|
48
|
+
earl:result [earl:outcome ?outcome];
|
49
|
+
earl:subject ?subject;
|
50
|
+
earl:test ?test ] .
|
51
|
+
}
|
52
|
+
).freeze
|
53
|
+
|
54
|
+
# Convenience vocabularies
|
55
|
+
class EARL < RDF::Vocabulary("http://www.w3.org/ns/earl#"); end
|
56
|
+
class MF < RDF::Vocabulary("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#"); end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Load test assertions and look for referenced software and developer information
|
60
|
+
# @param [Array<String>] *files Assertions
|
61
|
+
# @param [Hash{Symbol => Object}] options
|
62
|
+
# @option options [Boolean] :verbose (true)
|
63
|
+
def initialize(*files)
|
64
|
+
@options = files.last.is_a?(Hash) ? files.pop.dup : {}
|
65
|
+
@graph = RDF::Graph.new
|
66
|
+
@prefixes = {}
|
67
|
+
files.flatten.each do |file|
|
68
|
+
status "read #{file}"
|
69
|
+
file_graph = case file
|
70
|
+
when /\.jsonld/
|
71
|
+
@json_hash = ::JSON.parse(File.read(file))
|
72
|
+
return
|
73
|
+
else RDF::Graph.load(file)
|
74
|
+
end
|
75
|
+
status " loaded #{file_graph.count} triples"
|
76
|
+
@graph << file_graph
|
77
|
+
end
|
78
|
+
|
79
|
+
# Find or load DOAP descriptions for all subjects
|
80
|
+
SPARQL.execute(DOAP_QUERY, @graph).each do |solution|
|
81
|
+
subject = solution[:subject]
|
82
|
+
|
83
|
+
# Load DOAP definitions
|
84
|
+
unless solution[:name] # not loaded
|
85
|
+
status "read doap description for #{subject}"
|
86
|
+
begin
|
87
|
+
doap_graph = RDF::Graph.load(subject)
|
88
|
+
status " loaded #{doap_graph.count} triples"
|
89
|
+
@graph << doap_graph.to_a
|
90
|
+
rescue
|
91
|
+
status " failed"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Load developers referenced from Test Subjects
|
97
|
+
SPARQL.execute(TEST_SUBJECT_QUERY, @graph).each do |solution|
|
98
|
+
# Load DOAP definitions
|
99
|
+
if solution[:developer] && !solution[:dev_name] # not loaded
|
100
|
+
status "read description for #{solution[:developer].inspect}"
|
101
|
+
begin
|
102
|
+
foaf_graph = RDF::Graph.load(solution[:developer])
|
103
|
+
status " loaded #{foaf_graph.count} triples"
|
104
|
+
@graph << foaf_graph.to_a
|
105
|
+
rescue
|
106
|
+
status " failed"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Dump the collesced output graph
|
114
|
+
#
|
115
|
+
# If no `io` option is provided, the output is returned as a string
|
116
|
+
#
|
117
|
+
# @param [Hash{Symbol => Object}] options
|
118
|
+
# @option options [Symbol] format (:html)
|
119
|
+
# @option options [String] :bibRef
|
120
|
+
# ReSpec bibliography reference for specification being tested
|
121
|
+
# @option options [Array<String>] :source_files
|
122
|
+
# Used for referencing the files used to generate this report
|
123
|
+
# @option options[IO] :io
|
124
|
+
# Optional `IO` to output results
|
125
|
+
# @return [String] serialized graph, if `io` is nil
|
126
|
+
def generate(options = {})
|
127
|
+
options = {
|
128
|
+
format: :html,
|
129
|
+
bibRef: "[[TURTLE]]",
|
130
|
+
name: "Turtle Test Results",
|
131
|
+
}.merge(options)
|
132
|
+
|
133
|
+
io = options[:io]
|
134
|
+
|
135
|
+
status("generate: #{options[:format]}")
|
136
|
+
##
|
137
|
+
# Retrieve Hashed information in JSON-LD format
|
138
|
+
hash = json_hash(options)
|
139
|
+
case options[:format]
|
140
|
+
when :jsonld, :json
|
141
|
+
json = hash.to_json(JSON::LD::JSON_STATE)
|
142
|
+
io.write(json) if io
|
143
|
+
json
|
144
|
+
when :turtle, :ttl
|
145
|
+
if io
|
146
|
+
earl_turtle(options.merge(:json_hash => hash))
|
147
|
+
else
|
148
|
+
io = StringIO.new
|
149
|
+
earl_turtle(:json_hash => hash, :io => io)
|
150
|
+
io.rewind
|
151
|
+
io.read
|
152
|
+
end
|
153
|
+
when :html
|
154
|
+
template = options[:template] ||
|
155
|
+
File.read(File.expand_path('../earl_report/views/earl_report.html.haml', __FILE__))
|
156
|
+
|
157
|
+
# Generate HTML report
|
158
|
+
html = Haml::Engine.new(template, :format => :xhtml)
|
159
|
+
.render(self, :tests => hash, :source_files => options.fetch(:source_files, []))
|
160
|
+
io.write(html) if io
|
161
|
+
html
|
162
|
+
else
|
163
|
+
if io
|
164
|
+
RDF::Writer.for(options[:format]).new(io) {|w| w << graph}
|
165
|
+
else
|
166
|
+
graph.dump(options[:format])
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
##
|
174
|
+
# Return hashed EARL report in JSON-LD form
|
175
|
+
# @return [Hash]
|
176
|
+
def json_hash(options)
|
177
|
+
@json_hash ||= begin
|
178
|
+
# Customized JSON-LD output
|
179
|
+
{
|
180
|
+
"@context" => {
|
181
|
+
dc: "http://purl.org/dc/terms/",
|
182
|
+
doap: "http://usefulinc.com/ns/doap#",
|
183
|
+
earl: "http://www.w3.org/ns/earl#",
|
184
|
+
mf: "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#",
|
185
|
+
foaf: "http://xmlns.com/foaf/0.1/",
|
186
|
+
rdfs: "http://www.w3.org/2000/01/rdf-schema#",
|
187
|
+
assertedBy: {"@id" => "earl:assertedBy", "@type" => "@id"},
|
188
|
+
bibRef: {"@id" => "dc: bibliographicCitation"},
|
189
|
+
description: {"@id" => "dc:description"},
|
190
|
+
developer: {"@id" => "doap:developer", "@type" => "@id"},
|
191
|
+
homepage: {"@id" => "doap:homepage", "@type" => "@id"},
|
192
|
+
doap_desc: {"@id" => "doap:description"},
|
193
|
+
language: {"@id" => "doap:programming-language"},
|
194
|
+
testAction: {"@id" => "mf:action", "@type" => "@id"},
|
195
|
+
testResult: {"@id" => "mf:result", "@type" => "@id"},
|
196
|
+
label: {"@id" => "rdfs:label"},
|
197
|
+
mode: {"@id" => "earl:mode", "@type" => "@id"},
|
198
|
+
name: {"@id" => "doap:name"},
|
199
|
+
outcome: {"@id" => "earl:outcome", "@type" => "@id"},
|
200
|
+
result: {"@id" => "earl:result"},
|
201
|
+
subject: {"@id" => "earl:subject", "@type" => "@id"},
|
202
|
+
test: {"@id" => "earl:test", "@type" => "@id"},
|
203
|
+
title: {"@id" => "dc:title"}
|
204
|
+
},
|
205
|
+
"@id" => "",
|
206
|
+
"@type" => %w(earl:Software doap:Project),
|
207
|
+
'name' => options[:name],
|
208
|
+
'bibRef' => options[:bibRef],
|
209
|
+
'testSubjects' => json_test_subject_info,
|
210
|
+
'tests' => json_result_info
|
211
|
+
}
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
##
|
216
|
+
# Return array of test subject information
|
217
|
+
# @return [Array]
|
218
|
+
def json_test_subject_info
|
219
|
+
# Get the set of subjects
|
220
|
+
ts_info = {}
|
221
|
+
SPARQL.execute(TEST_SUBJECT_QUERY, @graph).each do |solution|
|
222
|
+
status "solution #{solution.to_hash.inspect}"
|
223
|
+
info = ts_info[solution[:uri].to_s] ||= {}
|
224
|
+
%w(name doap_desc homepage language).each do |prop|
|
225
|
+
info[prop] = solution[prop.to_sym].to_s if solution[prop.to_sym]
|
226
|
+
end
|
227
|
+
if solution[:dev_name]
|
228
|
+
dev_type = solution[:dev_type].to_s =~ /Organization/ ? "foaf:Organization" : "foaf:Person"
|
229
|
+
info['developer'] = Hash.ordered
|
230
|
+
info['developer']['@id'] = solution[:developer].to_s if solution[:developer].uri?
|
231
|
+
info['developer']['@type'] = dev_type
|
232
|
+
info['developer']['foaf:name'] = solution[:dev_name].to_s if solution[:dev_name]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Map ids and values to array entries
|
237
|
+
ts_info.keys.map do |id|
|
238
|
+
info = ts_info[id]
|
239
|
+
subject = Hash.ordered
|
240
|
+
subject["@id"] = id
|
241
|
+
subject["@type"] = %w(earl:TestSubject doap:Project)
|
242
|
+
%w(name developer doap_desc homepage language).each do |prop|
|
243
|
+
subject[prop] = info[prop] if info[prop]
|
244
|
+
end
|
245
|
+
subject
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
##
|
250
|
+
# Return result information for each test
|
251
|
+
#
|
252
|
+
# @return [Array]
|
253
|
+
def json_result_info
|
254
|
+
test_cases = {}
|
255
|
+
|
256
|
+
@graph.query(:predicate => MF['entries']) do |stmt|
|
257
|
+
# Iterate through the test manifest and write out a TestCase
|
258
|
+
# for each test
|
259
|
+
RDF::List.new(stmt.object, @graph).map do |tc|
|
260
|
+
tc_hash = {}
|
261
|
+
tc_hash['@id'] = tc.to_s
|
262
|
+
tc_hash['@type'] = %w(earl:TestCriterion earl:TestCase)
|
263
|
+
|
264
|
+
# Extract important properties
|
265
|
+
@graph.query(:subject => tc).each do |tc_stmt|
|
266
|
+
case tc_stmt.predicate.to_s
|
267
|
+
when MF['name'].to_s
|
268
|
+
tc_hash['title'] = tc_stmt.object.to_s
|
269
|
+
when RDF::RDFS.comment.to_s
|
270
|
+
tc_hash['description'] = tc_stmt.object.to_s
|
271
|
+
when MF.action.to_s
|
272
|
+
tc_hash['testAction'] = tc_stmt.object.to_s
|
273
|
+
when MF.result.to_s
|
274
|
+
tc_hash['testResult'] = tc_stmt.object.to_s
|
275
|
+
else
|
276
|
+
#STDERR.puts "TC soln: #{tc_stmt.inspect}"
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
test_cases[tc.to_s] = tc_hash
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
raise "No test cases found" if test_cases.empty?
|
285
|
+
|
286
|
+
status "Test cases:\n #{test_cases.keys.join("\n ")}"
|
287
|
+
# Iterate through assertions and add to appropriate test case
|
288
|
+
SPARQL.execute(ASSERTION_QUERY, @graph).each do |solution|
|
289
|
+
tc = test_cases[solution[:test].to_s]
|
290
|
+
STDERR.puts "No test case found for #{solution[:test]}" unless tc
|
291
|
+
tc ||= {}
|
292
|
+
subject = solution[:subject].to_s
|
293
|
+
ta_hash = {}
|
294
|
+
ta_hash['@type'] = 'earl:Assertion'
|
295
|
+
ta_hash['assertedBy'] = solution[:by].to_s
|
296
|
+
ta_hash['test'] = solution[:test].to_s
|
297
|
+
ta_hash['mode'] = "earl:#{solution[:mode].to_s.split('#').last || 'automatic'}"
|
298
|
+
ta_hash['subject'] = subject
|
299
|
+
ta_hash['result'] = {
|
300
|
+
'@type' => 'earl:TestResult',
|
301
|
+
"outcome" => (solution[:outcome] == EARL.passed ? 'earl:passed' : 'earl:failed')
|
302
|
+
}
|
303
|
+
tc[subject] = ta_hash
|
304
|
+
end
|
305
|
+
|
306
|
+
test_cases.values
|
307
|
+
end
|
308
|
+
|
309
|
+
##
|
310
|
+
# Output consoloated EARL report as Turtle
|
311
|
+
# @param [IO, StringIO] io
|
312
|
+
# @return [String]
|
313
|
+
def earl_turtle(options)
|
314
|
+
io = options[:io]
|
315
|
+
json_hash = options[:json_hash]
|
316
|
+
# Write preamble
|
317
|
+
{
|
318
|
+
:dc => RDF::DC,
|
319
|
+
:doap => RDF::DOAP,
|
320
|
+
:earl => EARL,
|
321
|
+
:foaf => RDF::FOAF,
|
322
|
+
:mf => MF,
|
323
|
+
:owl => RDF::OWL,
|
324
|
+
:rdf => RDF,
|
325
|
+
:rdfs => RDF::RDFS,
|
326
|
+
:xhv => RDF::XHV,
|
327
|
+
:xsd => RDF::XSD
|
328
|
+
}.each do |prefix, vocab|
|
329
|
+
io.puts("@prefix #{prefix}: <#{vocab.to_uri}> .")
|
330
|
+
end
|
331
|
+
io.puts
|
332
|
+
|
333
|
+
# Write earl:Software for the report
|
334
|
+
io.puts %(<#{json_hash['@id']}> a earl:Software, doap:Project;)
|
335
|
+
io.puts %( doap:homepage <#{json_hash['homepage']}>;)
|
336
|
+
io.puts %( doap:name "#{json_hash['name']}".)
|
337
|
+
|
338
|
+
# Test Cases
|
339
|
+
# also collect each assertion definition
|
340
|
+
test_cases = {}
|
341
|
+
assertions = []
|
342
|
+
|
343
|
+
# Tests
|
344
|
+
json_hash['tests'].each do |test_case|
|
345
|
+
tc_desc = test_cases[test_case['test']] ||= test_case.dup
|
346
|
+
test_case.keys.select {|k| k =~ /^http:/}.each do |ts_uri|
|
347
|
+
tc_desc[ts_uri] = test_case[ts_uri]['@id']
|
348
|
+
assertions << test_case[ts_uri]
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# Write out each earl:TestSubject
|
353
|
+
io.puts %(#\n# Subject Definitions\n#)
|
354
|
+
json_hash['testSubjects'].each do |ts_desc|
|
355
|
+
io.write(test_subject_turtle(ts_desc))
|
356
|
+
end
|
357
|
+
|
358
|
+
# Write out each earl:TestCase
|
359
|
+
io.puts %(#\n# Test Case Definitions\n#)
|
360
|
+
test_cases.keys.sort.each do |num|
|
361
|
+
io.write(tc_turtle(test_cases[num]))
|
362
|
+
end
|
363
|
+
|
364
|
+
# Write out each earl:Assertion
|
365
|
+
io.puts %(#\n# Assertions\n#)
|
366
|
+
assertions.sort_by {|a| a['@id']}.each do |as_desc|
|
367
|
+
io.write(as_turtle(as_desc))
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
##
|
372
|
+
# Write out Test Subject definition for each earl:TestSubject
|
373
|
+
# @param [Hash] desc
|
374
|
+
# @return [String]
|
375
|
+
def test_subject_turtle(desc)
|
376
|
+
developer = desc['developer']
|
377
|
+
res = %(<#{desc['@id']}> a #{desc['@type'].join(', ')};\n)
|
378
|
+
res += %( doap:name "#{desc['name']}";\n)
|
379
|
+
res += %( doap:description """#{desc['doap_desc']}""";\n) if desc['doap_desc']
|
380
|
+
res += %( doap:programming-language "#{desc['language']}";\n) if desc['language']
|
381
|
+
if developer && developer['@id']
|
382
|
+
res += %( doap:developer <#{developer['@id']}> .\n\n)
|
383
|
+
res += %(<#{developer['@id']}> a #{[developer['@type']].flatten.join(', ')};\n)
|
384
|
+
res += %( foaf:name "#{developer['foaf:name']}" .\n)
|
385
|
+
elsif developer
|
386
|
+
res += %( doap:developer [ a #{developer['@type'] || "foaf:Person"}; foaf:name "#{developer['foaf:name']}"] .\n)
|
387
|
+
else
|
388
|
+
res += %( .\n)
|
389
|
+
end
|
390
|
+
res + "\n"
|
391
|
+
end
|
392
|
+
|
393
|
+
##
|
394
|
+
# Write out each Test Case definition
|
395
|
+
# @prarm[Hash] desc
|
396
|
+
# @return [String]
|
397
|
+
def tc_turtle(desc)
|
398
|
+
res = %(<#{desc['@id']}> a #{[desc['@type']].flatten.join(', ')};\n)
|
399
|
+
res += %( dc:title "#{desc['title']}";\n)
|
400
|
+
res += %( dc:description """#{desc['description']}""";\n)
|
401
|
+
res += %( mf:action <#{desc['testAction']}>;\n)
|
402
|
+
res += %( mf:result <#{desc['testResult']}>;\n)
|
403
|
+
res + "\n"
|
404
|
+
end
|
405
|
+
|
406
|
+
##
|
407
|
+
# Write out each Assertion definition
|
408
|
+
# @prarm[Hash] desc
|
409
|
+
# @return [String]
|
410
|
+
def as_turtle(desc)
|
411
|
+
res = %([ a earl:Assertion;\n)
|
412
|
+
res += %( earl:assertedBy <#{desc['assertedBy']}>;\n)
|
413
|
+
res += %( earl:test <#{desc['test']}>;\n)
|
414
|
+
res += %( earl:subject <#{desc['subject']}>;\n)
|
415
|
+
res += %( earl:mode #{desc['mode']};\n)
|
416
|
+
res += %( earl:result [ a earl:TestResult; earl:outcome #{desc['result']['outcome']}] ] .\n)
|
417
|
+
res += %(\n)
|
418
|
+
res
|
419
|
+
end
|
420
|
+
|
421
|
+
def status(message)
|
422
|
+
puts message if @options[:verbose]
|
423
|
+
end
|
424
|
+
end
|