earl-report 0.0.1
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.
- 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
|