earl-report 0.3.6 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +39 -20
- data/VERSION +1 -1
- data/bin/earl-report +3 -1
- data/lib/earl_report.rb +282 -422
- data/lib/earl_report/views/earl_report.html.haml +80 -82
- data/spec/earl_report_spec.rb +16 -235
- data/spec/test-files/results.html +29 -35
- data/spec/test-files/results.jsonld +236 -1
- data/spec/test-files/results.ttl +76 -72
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 255dbe2a1d649a13c1f35e23d6c3dbcd2105a3e3
|
4
|
+
data.tar.gz: 37c3002143ac3de614a84664311c4dc57e888469
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad90655184326e3d6cc2a1c754d7bdc707fe41939b69da97727e1b0066b64f76cd8353341636da863bc60340d4107963f222039e52dd86ecbd2f2c09b0c77966
|
7
|
+
data.tar.gz: 47a6100932b129080d1c8182522937fa6616d30a817ee5e29b8fea4a1ede40bdcd80a0b568353bbc22548948fd6c30ee823594bcf0672301c7f3be4be00f8356
|
data/README.md
CHANGED
@@ -48,40 +48,59 @@ of the following form:
|
|
48
48
|
If not found, the IRI identified by `doap:developer`
|
49
49
|
will be dereferenced and is presumed to provide a [FOAF]() profile of the developer.
|
50
50
|
|
51
|
+
Assertions are added to each test entry based on that test being referenced from the assertion.
|
52
|
+
|
51
53
|
## Manifest query
|
52
|
-
The test manifest is used to
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
is the following:
|
58
|
-
|
59
|
-
PREFIX dc: <http://purl.org/dc/terms/>
|
54
|
+
The test manifest is used to find test entries and a manifest. The built-in
|
55
|
+
query is based on the [standard RDF WG format](). Alternative manifest formats
|
56
|
+
can be used by specifying a customized manifest query, but may require a custom
|
57
|
+
[Haml]() template for report generation. The default query is the following:
|
58
|
+
|
60
59
|
PREFIX mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#>
|
61
60
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
62
|
-
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
63
61
|
|
64
|
-
SELECT ?
|
62
|
+
SELECT ?uri ?testAction ?manUri
|
65
63
|
WHERE {
|
66
|
-
?uri
|
67
|
-
mf:name ?title;
|
68
|
-
mf:action ?testAction .
|
69
|
-
OPTIONAL { ?uri rdfs:comment ?description . }
|
70
|
-
OPTIONAL { ?uri mf:result ?testResult . }
|
64
|
+
?uri mf:action ?testAction .
|
71
65
|
OPTIONAL {
|
72
66
|
?manUri a mf:Manifest; mf:entries ?lh .
|
73
67
|
?lh rdf:first ?uri .
|
74
|
-
OPTIONAL { ?manUri mf:name ?manTitle . }
|
75
|
-
OPTIONAL { ?manUri rdfs:comment ?manDescription . }
|
76
68
|
}
|
77
69
|
}
|
78
70
|
|
79
|
-
If any result has a non-null `?lh`, it is taken as the list head and used
|
80
|
-
to maintain the list order within `earl:tests`.
|
81
|
-
|
82
71
|
## Report generation template
|
83
72
|
The report template is in [ReSpec][] form using [Haml]() to generate individual report elements.
|
84
73
|
|
74
|
+
## Changes from previous versions
|
75
|
+
Version 0.3 and prior re-constructed the test manifest used to create the body of the report, which caused information not described within the query to be lost. Starting with 0.4, all manifests and assertions are read into a single graph, and each test references a list of assertions against it using a list referenced by `earl:assertions`. Additionally, all included manifests are included in a top-level entity referenced via `mf:entries`. For example:
|
76
|
+
|
77
|
+
<> a earl:Software, doap:Project;
|
78
|
+
mf:entries (<http://example/manifest.ttl>);
|
79
|
+
earl:assertions (<spec/test-files/report-complete.ttl>) .
|
80
|
+
|
81
|
+
<http://example/manifest.ttl> a mf:Manifest, earl:Report;
|
82
|
+
mf:name "Example Test Cases";
|
83
|
+
rdfs:comment "Description for Example Test Cases";
|
84
|
+
mf:entries (<http://example/manifest.ttl#testeval00>) .
|
85
|
+
|
86
|
+
<http://example/manifest.ttl#testeval00> a earl:TestCriterion, earl:TestCase;
|
87
|
+
mf:name "subm-test-00";
|
88
|
+
rdfs:comment "Blank subject";
|
89
|
+
mf:action <http://example/test-00.ttl>;
|
90
|
+
mf:result <http://example/test-00.out>;
|
91
|
+
earl:assertions ([
|
92
|
+
a earl:Assertion;
|
93
|
+
earl:assertedBy <http://greggkellogg.net/foaf#me>;
|
94
|
+
earl:mode earl:automatic;
|
95
|
+
earl:result [
|
96
|
+
a earl:TestResult;
|
97
|
+
dc:date "2012-11-06T19:23:29-08:00"^^xsd:dateTime;
|
98
|
+
earl:outcome earl:passed
|
99
|
+
];
|
100
|
+
earl:subject <http://rubygems.org/gems/rdf-turtle>;
|
101
|
+
earl:test <http://example/manifest.ttl#testeval00>
|
102
|
+
]) .
|
103
|
+
|
85
104
|
## Usage
|
86
105
|
The `earl-report` command may be used to directly create a report from zero or more input files, which are themselves [EARL][] report.
|
87
106
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/bin/earl-report
CHANGED
@@ -9,7 +9,7 @@ require 'yaml'
|
|
9
9
|
OPT_ARGS = [
|
10
10
|
["--base", GetoptLong::REQUIRED_ARGUMENT,"Base URI to use when loading test manifest"],
|
11
11
|
["--bibRef", GetoptLong::REQUIRED_ARGUMENT,"ReSpec BibRef of specification being reported upon"],
|
12
|
-
["--format", "-f", GetoptLong::REQUIRED_ARGUMENT,"Format of output, one of 'ttl', 'json', or 'html'"],
|
12
|
+
["--format", "-f", GetoptLong::REQUIRED_ARGUMENT,"Format of output, one of 'ttl', 'json', or 'html'. May also be a different RDF format"],
|
13
13
|
["--json", GetoptLong::NO_ARGUMENT, "Input is a JSON-LD formatted result"],
|
14
14
|
["--manifest", GetoptLong::REQUIRED_ARGUMENT,"Test manifest(s)"],
|
15
15
|
["--name", GetoptLong::REQUIRED_ARGUMENT,"Name of specification"],
|
@@ -30,6 +30,8 @@ def usage
|
|
30
30
|
Options are initialized by reading optional run-control file '.earl' in the local directory,
|
31
31
|
if it exists.
|
32
32
|
|
33
|
+
Writing with a format other than ttl, json, or html will also write any loaded manifests
|
34
|
+
|
33
35
|
Usage: #{$0} [options] test-result ...
|
34
36
|
}.gsub(/^ /, '')
|
35
37
|
width = OPT_ARGS.map do |o|
|
data/lib/earl_report.rb
CHANGED
@@ -12,26 +12,19 @@ class EarlReport
|
|
12
12
|
|
13
13
|
attr_reader :graph
|
14
14
|
|
15
|
-
# Return information about each test
|
16
|
-
#
|
15
|
+
# Return information about each test.
|
16
|
+
# Tests all have an mf:action property.
|
17
|
+
# The Manifest lists all actions in list from mf:entries
|
17
18
|
MANIFEST_QUERY = %(
|
18
|
-
PREFIX dc: <http://purl.org/dc/terms/>
|
19
19
|
PREFIX mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#>
|
20
20
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
21
|
-
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
22
21
|
|
23
|
-
SELECT ?
|
22
|
+
SELECT ?uri ?testAction ?manUri
|
24
23
|
WHERE {
|
25
|
-
?uri
|
26
|
-
mf:name ?title;
|
27
|
-
mf:action ?testAction .
|
28
|
-
OPTIONAL { ?uri rdfs:comment|dc:description ?description . }
|
29
|
-
OPTIONAL { ?uri mf:result ?testResult . }
|
24
|
+
?uri mf:action ?testAction .
|
30
25
|
OPTIONAL {
|
31
26
|
?manUri a mf:Manifest; mf:entries ?lh .
|
32
27
|
?lh rdf:first ?uri .
|
33
|
-
OPTIONAL { ?manUri rdfs:label|mf:name ?manTitle . }
|
34
|
-
OPTIONAL { ?manUri rdfs:comment|dc:description ?manDescription . }
|
35
28
|
}
|
36
29
|
}
|
37
30
|
).freeze
|
@@ -39,7 +32,7 @@ class EarlReport
|
|
39
32
|
TEST_SUBJECT_QUERY = %(
|
40
33
|
PREFIX doap: <http://usefulinc.com/ns/doap#>
|
41
34
|
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
|
42
|
-
|
35
|
+
|
43
36
|
SELECT DISTINCT ?uri ?name ?doapDesc ?homepage ?language ?developer ?devName ?devType ?devHomepage
|
44
37
|
WHERE {
|
45
38
|
?uri a doap:Project; doap:name ?name; doap:developer ?developer .
|
@@ -50,6 +43,7 @@ class EarlReport
|
|
50
43
|
OPTIONAL { ?developer foaf:name ?devName .}
|
51
44
|
OPTIONAL { ?developer foaf:homepage ?devHomepage .}
|
52
45
|
}
|
46
|
+
ORDER BY ?name
|
53
47
|
).freeze
|
54
48
|
|
55
49
|
DOAP_QUERY = %(
|
@@ -68,7 +62,7 @@ class EarlReport
|
|
68
62
|
ASSERTION_QUERY = %(
|
69
63
|
PREFIX earl: <http://www.w3.org/ns/earl#>
|
70
64
|
|
71
|
-
SELECT ?
|
65
|
+
SELECT ?test ?subject ?by ?mode ?outcome
|
72
66
|
WHERE {
|
73
67
|
?a a earl:Assertion;
|
74
68
|
earl:assertedBy ?by;
|
@@ -82,40 +76,69 @@ class EarlReport
|
|
82
76
|
ORDER BY ?subject
|
83
77
|
).freeze
|
84
78
|
|
85
|
-
|
86
|
-
"@
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
79
|
+
TEST_FRAME = {
|
80
|
+
"@context" => {
|
81
|
+
"@vocab" => "http://www.w3.org/ns/earl#",
|
82
|
+
"foaf:homepage" => {"@type" => "@id"},
|
83
|
+
"dc" => "http://purl.org/dc/terms/",
|
84
|
+
"doap" => "http://usefulinc.com/ns/doap#",
|
85
|
+
"earl" => "http://www.w3.org/ns/earl#",
|
86
|
+
"mf" => "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#",
|
87
|
+
"foaf" => "http://xmlns.com/foaf/0.1/",
|
88
|
+
"rdfs" => "http://www.w3.org/2000/01/rdf-schema#",
|
89
|
+
"assertedBy" => {"@type" => "@id"},
|
90
|
+
"assertions" => {"@type" => "@id", "@container" => "@set"},
|
91
|
+
"bibRef" => {"@id" => "dc:bibliographicCitation"},
|
92
|
+
"created" => {"@id" => "doap:created", "@type" => "xsd:date"},
|
93
|
+
"description" => {"@id" => "rdfs:comment", "@language" => "en"},
|
94
|
+
"developer" => {"@id" => "doap:developer", "@type" => "@id", "@container" => "@set"},
|
95
|
+
"doapDesc" => {"@id" => "doap:description", "@language" => "en"},
|
96
|
+
"generatedBy" => {"@type" => "@id"},
|
97
|
+
"homepage" => {"@id" => "doap:homepage", "@type" => "@id"},
|
98
|
+
"language" => {"@id" => "doap:programming-language"},
|
99
|
+
"license" => {"@id" => "doap:license", "@type" => "@id"},
|
100
|
+
"mode" => {"@type" => "@id"},
|
101
|
+
"name" => {"@id" => "doap:name"},
|
102
|
+
"outcome" => {"@type" => "@id"},
|
103
|
+
"release" => {"@id" => "doap:release", "@type" => "@id"},
|
104
|
+
"revision" => {"@id" => "doap:revision"},
|
105
|
+
"shortdesc" => {"@id" => "doap:shortdesc", "@language" => "en"},
|
106
|
+
"subject" => {"@type" => "@id"},
|
107
|
+
"test" => {"@type" => "@id"},
|
108
|
+
"testAction" => {"@id" => "mf:action", "@type" => "@id"},
|
109
|
+
"testResult" => {"@id" => "mf:result", "@type" => "@id"},
|
110
|
+
"title" => {"@id" => "mf:name"},
|
111
|
+
"entries" => {"@id" => "mf:entries", "@type" => "@id", "@container" => "@list"},
|
112
|
+
"testSubjects" => {"@type" => "@id", "@container" => "@set"},
|
113
|
+
"xsd" => {"@id" => "http://www.w3.org/2001/XMLSchema#"}
|
114
|
+
},
|
115
|
+
"assertions" => {},
|
116
|
+
"bibRef" => {},
|
117
|
+
"generatedBy" => {
|
118
|
+
"@embed" => "@always",
|
119
|
+
"developer" => {},
|
120
|
+
"release" => {
|
121
|
+
"@embed" => "@always"
|
122
|
+
}
|
123
|
+
},
|
124
|
+
"testSubjects" => {
|
125
|
+
"@embed" => "@always",
|
126
|
+
"@type" => "earl:TestSubject",
|
127
|
+
"developer" => {"@embed" => "@always"},
|
128
|
+
"homepage" => {"@embed" => false}
|
129
|
+
},
|
130
|
+
"entries" => [{
|
131
|
+
"@type" => "mf:Manifest",
|
132
|
+
"entries" => [{
|
133
|
+
"@type" => "earl:TestCase",
|
134
|
+
"assertions" => {
|
135
|
+
"@type" => "earl:Assertion",
|
136
|
+
"assertedBy" => {"@embed" => false},
|
137
|
+
"result" => {"@type" => "earl:TestResult"},
|
138
|
+
"subject" => {"@embed" => false}
|
139
|
+
}
|
140
|
+
}]
|
141
|
+
}]
|
119
142
|
}.freeze
|
120
143
|
|
121
144
|
# Convenience vocabularies
|
@@ -144,8 +167,11 @@ class EarlReport
|
|
144
167
|
raise "Require at least one input file" if files.empty?
|
145
168
|
@files = files
|
146
169
|
@prefixes = {}
|
170
|
+
|
171
|
+
# If provided :json, it is used for generating all other output forms
|
147
172
|
if @options[:json]
|
148
173
|
@json_hash = ::JSON.parse(File.read(files.first))
|
174
|
+
JSON::LD::Reader.open(files.first) {|r| @graph = RDF::Graph.new << r}
|
149
175
|
return
|
150
176
|
end
|
151
177
|
|
@@ -157,14 +183,31 @@ class EarlReport
|
|
157
183
|
Array(@options[:manifest]).each do |man|
|
158
184
|
g = RDF::Graph.load(man, man_opts)
|
159
185
|
status " loaded #{g.count} triples from #{man}"
|
160
|
-
|
186
|
+
graph << g
|
187
|
+
end
|
188
|
+
|
189
|
+
# Hash test cases by URI
|
190
|
+
tests = SPARQL.execute(@options[:query], graph)
|
191
|
+
.to_a
|
192
|
+
.inject({}) {|memo, soln| memo[soln[:uri]] = soln; memo}
|
193
|
+
|
194
|
+
if tests.empty?
|
195
|
+
raise "no tests found querying manifest.\n" +
|
196
|
+
"Results are found using the following query, this can be overridden using the --query option:\n" +
|
197
|
+
"#{@options[:query]}"
|
161
198
|
end
|
162
199
|
|
163
|
-
#
|
200
|
+
# Manifests in graph
|
201
|
+
man_uris = tests.values.map {|v| v[:manUri]}.uniq.compact
|
202
|
+
test_resources = tests.values.map {|v| v[:uri]}.uniq.compact
|
203
|
+
subjects = {}
|
204
|
+
|
205
|
+
assertion_graph = RDF::Graph.new
|
206
|
+
# Read test assertion files into assertion graph
|
164
207
|
files.flatten.each do |file|
|
165
208
|
status "read #{file}"
|
166
209
|
file_graph = RDF::Graph.load(file)
|
167
|
-
if file_graph.first_object(:
|
210
|
+
if file_graph.first_object(predicate: RDF::URI('http://www.w3.org/ns/earl#testSubjects'))
|
168
211
|
status " skip #{file}, which seems to be a previous rollup earl report"
|
169
212
|
@files -= [file]
|
170
213
|
else
|
@@ -196,30 +239,163 @@ class EarlReport
|
|
196
239
|
end
|
197
240
|
|
198
241
|
# Load developers referenced from Test Subjects
|
199
|
-
solutions.
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
warn "\nNo developer identified for #{solution[:developer]}"
|
242
|
+
if !solutions.first[:developer]
|
243
|
+
warn "\nNo developer identified for #{solutions.first[:uri]}"
|
244
|
+
elsif !solutions.first[:devName]
|
245
|
+
status "read description for developer #{solutions.first[:developer].inspect}"
|
246
|
+
begin
|
247
|
+
foaf_graph = RDF::Graph.load(solutions.first[:developer])
|
248
|
+
status " loaded #{foaf_graph.count} triples"
|
249
|
+
file_graph << foaf_graph.to_a
|
250
|
+
# Reload solutions
|
251
|
+
solutions = SPARQL.execute(TEST_SUBJECT_QUERY, file_graph)
|
252
|
+
rescue
|
253
|
+
warn "\nfailed to load FOAF from #{solutions.first[:developer]}: #{$!}"
|
212
254
|
end
|
213
255
|
end
|
214
256
|
|
215
|
-
|
216
|
-
|
257
|
+
solutions.each do |solution|
|
258
|
+
# Kepp track of subjects
|
259
|
+
subjects[solution[:uri]] = RDF::URI(file)
|
260
|
+
|
261
|
+
# Add TestSubject information to main graph
|
262
|
+
name = solution[:name].to_s if solution[:name]
|
263
|
+
language = solution[:language].to_s if solution[:language]
|
264
|
+
doapDesc = solution[:doapDesc] if solution[:doapDesc]
|
265
|
+
doapDesc.language ||= :en
|
266
|
+
devName = solution[:devName].to_s if solution[:devName]
|
267
|
+
graph << RDF::Statement(solution[:uri], RDF.type, RDF::DOAP.Project)
|
268
|
+
graph << RDF::Statement(solution[:uri], RDF.type, EARL.TestSubject)
|
269
|
+
graph << RDF::Statement(solution[:uri], RDF.type, EARL.Software)
|
270
|
+
graph << RDF::Statement(solution[:uri], RDF::DOAP.name, name)
|
271
|
+
graph << RDF::Statement(solution[:uri], RDF::DOAP.developer, solution[:developer])
|
272
|
+
graph << RDF::Statement(solution[:uri], RDF::DOAP.homepage, solution[:homepage]) if solution[:homepage]
|
273
|
+
graph << RDF::Statement(solution[:uri], RDF::DOAP.description, doapDesc) if doapDesc
|
274
|
+
graph << RDF::Statement(solution[:uri], RDF::DOAP[:"programming-language"], language) if solution[:language]
|
275
|
+
graph << RDF::Statement(solution[:developer], RDF.type, solution[:devType]) if solution[:devType]
|
276
|
+
graph << RDF::Statement(solution[:developer], RDF::FOAF.name, devName) if devName
|
277
|
+
graph << RDF::Statement(solution[:developer], RDF::FOAF.homepage, solution[:devHomepage]) if solution[:devHomepage]
|
278
|
+
end
|
279
|
+
|
280
|
+
assertion_graph << file_graph
|
217
281
|
end
|
218
282
|
end
|
283
|
+
|
284
|
+
# Make sure that each assertion matches a test and add reference from test to assertion
|
285
|
+
found_solutions = {}
|
286
|
+
|
287
|
+
# Initialize test assertions with an entry for each test subject
|
288
|
+
test_assertion_lists = {}
|
289
|
+
test_assertion_lists = tests.keys.inject({}) do |memo, test|
|
290
|
+
memo.merge(test => Array.new(subjects.length))
|
291
|
+
end
|
292
|
+
|
293
|
+
status "query assertions"
|
294
|
+
SPARQL.execute(ASSERTION_QUERY, assertion_graph).each do |solution|
|
295
|
+
subject = solution[:subject]
|
296
|
+
unless tests[solution[:test]]
|
297
|
+
$stderr.puts "Skipping result for #{solution[:test]} for #{subject}, which is not defined in manifests"
|
298
|
+
next
|
299
|
+
end
|
300
|
+
unless subjects[subject]
|
301
|
+
$stderr.puts "No test result subject found for #{subject}: #{solution.inspect}"
|
302
|
+
next
|
303
|
+
end
|
304
|
+
found_solutions[subject] = true
|
305
|
+
|
306
|
+
# Add this solution at the appropriate index within that list
|
307
|
+
ndx = subjects.keys.find_index(subject)
|
308
|
+
ary = test_assertion_lists[solution[:test]] ||= []
|
309
|
+
|
310
|
+
ary[ndx] = a = RDF::Node.new
|
311
|
+
graph << RDF::Statement(a, RDF.type, EARL.Assertion)
|
312
|
+
graph << RDF::Statement(a, EARL.subject, subject)
|
313
|
+
graph << RDF::Statement(a, EARL.test, solution[:test])
|
314
|
+
graph << RDF::Statement(a, EARL.assertedBy, solution[:by])
|
315
|
+
graph << RDF::Statement(a, EARL.mode, solution[:mode]) if solution[:mode]
|
316
|
+
r = RDF::Node.new
|
317
|
+
graph << RDF::Statement(a, EARL.result, r)
|
318
|
+
graph << RDF::Statement(r, RDF.type, EARL.TestResult)
|
319
|
+
graph << RDF::Statement(r, EARL.outcome, solution[:outcome])
|
320
|
+
end
|
321
|
+
|
322
|
+
# Add ordered assertions for each test
|
323
|
+
test_assertion_lists.each do |test, ary|
|
324
|
+
# Fill any missing entries with an untested outcome
|
325
|
+
ary.each_with_index do |a, ndx|
|
326
|
+
unless a
|
327
|
+
ary[ndx] = a = RDF::Node.new
|
328
|
+
graph << RDF::Statement(a, RDF.type, EARL.Assertion)
|
329
|
+
graph << RDF::Statement(a, EARL.subject, subjects.keys[ndx])
|
330
|
+
graph << RDF::Statement(a, EARL.test, test)
|
331
|
+
r = RDF::Node.new
|
332
|
+
graph << RDF::Statement(a, EARL.result, r)
|
333
|
+
graph << RDF::Statement(r, RDF.type, EARL.TestResult)
|
334
|
+
graph << RDF::Statement(r, EARL.outcome, EARL.untested)
|
335
|
+
end
|
336
|
+
|
337
|
+
# This counts on order being preserved in default repository so we can avoid using an rdf:List
|
338
|
+
graph << RDF::Statement(test, EARL.assertions, a)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
# See if any subject did not report results, which may indicate a formatting error in the EARL source
|
343
|
+
subjects.reject {|s| found_solutions[s]}.each do |sub|
|
344
|
+
$stderr.puts "No results found for #{sub} using #{ASSERTION_QUERY}"
|
345
|
+
end
|
346
|
+
|
347
|
+
# Add report wrapper to graph
|
348
|
+
ttl = %(
|
349
|
+
@prefix dc: <http://purl.org/dc/terms/> .
|
350
|
+
@prefix doap: <http://usefulinc.com/ns/doap#> .
|
351
|
+
@prefix earl: <http://www.w3.org/ns/earl#> .
|
352
|
+
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
|
353
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
354
|
+
|
355
|
+
<> a earl:Software, doap:Project;
|
356
|
+
doap:name #{quoted(@options.fetch(:name, 'Unknown'))};
|
357
|
+
dc:bibliographicCitation "#{@options.fetch(:bibRef, 'Unknown reference')}";
|
358
|
+
earl:generatedBy <http://rubygems.org/gems/earl-report>;
|
359
|
+
earl:assertions #{subjects.values.map {|f| f.to_ntriples}.join(",\n ")};
|
360
|
+
earl:testSubjects #{subjects.keys.map {|f| f.to_ntriples}.join(",\n ")};
|
361
|
+
mf:entries (#{man_uris.map {|f| f.to_ntriples}.join("\n ")}) .
|
362
|
+
|
363
|
+
<http://rubygems.org/gems/earl-report> a earl:Software, doap:Project;
|
364
|
+
doap:name "earl-report";
|
365
|
+
doap:shortdesc "Earl Report summary generator"@en;
|
366
|
+
doap:description "EarlReport generates HTML+RDFa rollups of multiple EARL reports"@en;
|
367
|
+
doap:homepage <https://github.com/gkellogg/earl-report>;
|
368
|
+
doap:programming-language "Ruby";
|
369
|
+
doap:license <http://unlicense.org>;
|
370
|
+
doap:release <https://github.com/gkellogg/earl-report/tree/#{VERSION}>;
|
371
|
+
doap:developer <http://greggkellogg.net/foaf#me> .
|
372
|
+
|
373
|
+
<https://github.com/gkellogg/earl-report/tree/#{VERSION}> a doap:Version;
|
374
|
+
doap:name "earl-report-#{VERSION}";
|
375
|
+
doap:created "#{File.mtime(File.expand_path('../../VERSION', __FILE__)).strftime('%Y-%m-%d')}"^^xsd:date;
|
376
|
+
doap:revision "#{VERSION}" .
|
377
|
+
).gsub(/^ /, '')
|
378
|
+
RDF::Turtle::Reader.new(ttl) {|r| graph << r}
|
379
|
+
|
380
|
+
# Each manifest is an earl:Report
|
381
|
+
man_uris.each do |u|
|
382
|
+
graph << RDF::Statement.new(u, RDF.type, EARL.Report)
|
383
|
+
end
|
384
|
+
|
385
|
+
# Each subject is an earl:TestSubject
|
386
|
+
subjects.keys.each do |u|
|
387
|
+
graph << RDF::Statement.new(u, RDF.type, EARL.TestSubject)
|
388
|
+
end
|
389
|
+
|
390
|
+
# Each assertion test is a earl:TestCriterion and earl:TestCase
|
391
|
+
test_resources.each do |u|
|
392
|
+
graph << RDF::Statement.new(u, RDF.type, EARL.TestCriterion)
|
393
|
+
graph << RDF::Statement.new(u, RDF.type, EARL.TestCase)
|
394
|
+
end
|
219
395
|
end
|
220
|
-
|
396
|
+
|
221
397
|
##
|
222
|
-
# Dump the
|
398
|
+
# Dump the coalesced output graph
|
223
399
|
#
|
224
400
|
# If no `io` option is provided, the output is returned as a string
|
225
401
|
#
|
@@ -229,7 +405,7 @@ class EarlReport
|
|
229
405
|
# Optional `IO` to output results
|
230
406
|
# @return [String] serialized graph, if `io` is nil
|
231
407
|
def generate(options = {})
|
232
|
-
options = {:
|
408
|
+
options = {format: :html}.merge(options)
|
233
409
|
|
234
410
|
io = options[:io]
|
235
411
|
|
@@ -246,7 +422,7 @@ class EarlReport
|
|
246
422
|
earl_turtle(options)
|
247
423
|
else
|
248
424
|
io = StringIO.new
|
249
|
-
earl_turtle(options.merge(:
|
425
|
+
earl_turtle(options.merge(io: io))
|
250
426
|
io.rewind
|
251
427
|
io.read
|
252
428
|
end
|
@@ -259,15 +435,12 @@ class EarlReport
|
|
259
435
|
end
|
260
436
|
|
261
437
|
# Generate HTML report
|
262
|
-
html = Haml::Engine.new(template, :
|
438
|
+
html = Haml::Engine.new(template, format: :xhtml).render(self, tests: json_hash)
|
263
439
|
io.write(html) if io
|
264
440
|
html
|
265
441
|
else
|
266
|
-
|
267
|
-
|
268
|
-
else
|
269
|
-
graph.dump(options[:format])
|
270
|
-
end
|
442
|
+
writer = RDF::Writer.for(options[:format])
|
443
|
+
writer.dump(@graph, io, options.merge(standard_prefixes: true))
|
271
444
|
end
|
272
445
|
end
|
273
446
|
|
@@ -279,209 +452,16 @@ class EarlReport
|
|
279
452
|
def json_hash
|
280
453
|
@json_hash ||= begin
|
281
454
|
# Customized JSON-LD output
|
282
|
-
|
283
|
-
|
284
|
-
"@id" => "",
|
285
|
-
"@type" => %w(earl:Software doap:Project),
|
286
|
-
'name' => @options.fetch(:name, "Unknown"),
|
287
|
-
'bibRef' => @options.fetch(:bibRef, "Unknown reference"),
|
288
|
-
'generatedBy' => {
|
289
|
-
"@id" => "http://rubygems.org/gems/earl-report",
|
290
|
-
"@type" => "doap:Project",
|
291
|
-
"name" => "earl-report",
|
292
|
-
"shortdesc" => "Earl Report summary generator",
|
293
|
-
"doapDesc" => "EarlReport generates HTML+RDFa rollups of multiple EARL reports",
|
294
|
-
"homepage" => "https://github.com/gkellogg/earl-report",
|
295
|
-
"language" => "Ruby",
|
296
|
-
"license" => "http://unlicense.org",
|
297
|
-
"release" => {
|
298
|
-
"@id" => "https://github.com/gkellogg/earl-report/tree/#{VERSION}",
|
299
|
-
"@type" => "doap:Version",
|
300
|
-
"name" => "earl-report-#{VERSION}",
|
301
|
-
"created" => File.mtime(File.expand_path('../../VERSION', __FILE__)).strftime('%Y-%m-%d'),
|
302
|
-
"revision" => VERSION.to_s
|
303
|
-
},
|
304
|
-
"developer" => {
|
305
|
-
"@type" => "foaf:Person",
|
306
|
-
"@id" => "http://greggkellogg.net/foaf#me",
|
307
|
-
"foaf:name" => "Gregg Kellogg",
|
308
|
-
"foaf:homepage" => "http://greggkellogg.net/"
|
309
|
-
}
|
310
|
-
},
|
311
|
-
"assertions" => @files,
|
312
|
-
'testSubjects' => json_test_subject_info,
|
313
|
-
'entries' => json_result_info
|
314
|
-
}
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
##
|
319
|
-
# Return array of test subject information
|
320
|
-
# @return [Array]
|
321
|
-
def json_test_subject_info
|
322
|
-
# Get the set of subjects
|
323
|
-
@subject_info ||= begin
|
324
|
-
ts_info = {}
|
325
|
-
SPARQL.execute(TEST_SUBJECT_QUERY, @graph).each do |solution|
|
326
|
-
#status "solution #{solution.to_hash.inspect}"
|
327
|
-
info = ts_info[solution[:uri].to_s] ||= {}
|
328
|
-
%w(name doapDesc homepage language).each do |prop|
|
329
|
-
info[prop] = solution[prop.to_sym].to_s if solution[prop.to_sym]
|
330
|
-
end
|
331
|
-
if solution[:devName]
|
332
|
-
dev_type = solution[:devType].to_s =~ /Organization/ ? "foaf:Organization" : "foaf:Person"
|
333
|
-
dev = {'@type' => dev_type}
|
334
|
-
dev['@id'] = solution[:developer].to_s if solution[:developer].uri?
|
335
|
-
dev['foaf:name'] = solution[:devName].to_s if solution[:devName]
|
336
|
-
dev['foaf:homepage'] = solution[:devHomepage].to_s if solution[:devHomepage]
|
337
|
-
(info['developer'] ||= []) << dev
|
338
|
-
end
|
339
|
-
info['developer'] = info['developer'].uniq if info['developer']
|
455
|
+
r = JSON::LD::API.fromRDF(graph) do |expanded|
|
456
|
+
JSON::LD::API.frame(expanded, TEST_FRAME, expanded: true)
|
340
457
|
end
|
341
|
-
|
342
|
-
|
343
|
-
ts_info.keys.sort.map do |id|
|
344
|
-
info = ts_info[id]
|
345
|
-
subject = {"@id" => id, "@type" => %w(earl:TestSubject doap:Project)}
|
346
|
-
%w(name developer doapDesc homepage language).each do |prop|
|
347
|
-
subject[prop] = info[prop] if info[prop]
|
348
|
-
end
|
349
|
-
subject
|
458
|
+
unless r.is_a?(Hash) && r.has_key?('@graph') && Array(r["@graph"]).length == 1
|
459
|
+
raise "Expected JSON result to have a single entry"
|
350
460
|
end
|
461
|
+
{"@context" => r["@context"]}.merge(r["@graph"].first)
|
351
462
|
end
|
352
463
|
end
|
353
464
|
|
354
|
-
##
|
355
|
-
# Return result information for each test.
|
356
|
-
# This counts on hash maintaining insertion order
|
357
|
-
#
|
358
|
-
# @return [Array<Hash>] List of manifests
|
359
|
-
def json_result_info
|
360
|
-
manifests = []
|
361
|
-
test_cases = {}
|
362
|
-
subjects = json_test_subject_info.map {|s| s['@id']}
|
363
|
-
|
364
|
-
# Hash test cases by URI
|
365
|
-
solutions = SPARQL.execute(@options[:query], @graph)
|
366
|
-
.to_a
|
367
|
-
.inject({}) {|memo, soln| memo[soln[:uri]] = soln; memo}
|
368
|
-
|
369
|
-
if solutions.empty?
|
370
|
-
raise "no results found querying manifest.\n" +
|
371
|
-
"Results are found using the following query, this can be overridden using the --query option:\n" +
|
372
|
-
"#{@options[:query]}"
|
373
|
-
end
|
374
|
-
|
375
|
-
qst = Time.now
|
376
|
-
# If test cases are in a list, maintain order
|
377
|
-
solutions.values.select {|s| s[:manUri]}.each do |man_soln|
|
378
|
-
# Get tests for this manifest in list order
|
379
|
-
solution_list = RDF::List.new(man_soln[:lh], @graph)
|
380
|
-
|
381
|
-
# Set up basic manifest information
|
382
|
-
man_info = manifests.detect {|m| m['@id'] == man_soln[:manUri].to_s}
|
383
|
-
unless man_info
|
384
|
-
status "manifest: #{man_soln[:manUri]}"
|
385
|
-
man_info = {
|
386
|
-
'@id' => man_soln[:manUri].to_s,
|
387
|
-
"@type" => %w{earl:Report mf:Manifest},
|
388
|
-
'entries' => []
|
389
|
-
}
|
390
|
-
man_info['title'] = man_soln[:manTitle].to_s if man_soln[:manTitle]
|
391
|
-
man_info['description'] = man_soln[:manDescription].to_s if man_soln[:manDescription]
|
392
|
-
manifests << man_info
|
393
|
-
end
|
394
|
-
|
395
|
-
# Collect each TestCase
|
396
|
-
solution_list.each do |uri|
|
397
|
-
solution = solutions[uri]
|
398
|
-
|
399
|
-
unless solution
|
400
|
-
$stderr.puts "Expected to find solution for #{uri}\n"
|
401
|
-
"Results are found using the following query, this can be overridden using the --query option:\n" +
|
402
|
-
"#{@options[:query]}"
|
403
|
-
next
|
404
|
-
end
|
405
|
-
|
406
|
-
# Create entry for this test case, if it doesn't already exist
|
407
|
-
tc = man_info['entries'].detect {|t| t['@id'] == uri}
|
408
|
-
unless tc
|
409
|
-
tc = {
|
410
|
-
'@id' => uri.to_s,
|
411
|
-
'@type' => %w(earl:TestCriterion earl:TestCase),
|
412
|
-
'title' => solution[:title].to_s,
|
413
|
-
'testAction' => solution[:testAction].to_s,
|
414
|
-
'assertions' => []
|
415
|
-
}
|
416
|
-
tc['@type'] << solution[:type].to_s if solution[:type]
|
417
|
-
tc['description'] = solution[:description].to_s if solution[:description]
|
418
|
-
tc['testResult'] = solution[:testResult].to_s if solution[:testResult]
|
419
|
-
|
420
|
-
# Pre-initialize results for each subject to untested
|
421
|
-
subjects.each do |siri|
|
422
|
-
tc['assertions'] << {
|
423
|
-
'@type' => 'earl:Assertion',
|
424
|
-
'test' => uri.to_s,
|
425
|
-
'subject' => siri,
|
426
|
-
'mode' => 'earl:notAvailable',
|
427
|
-
'result' => {
|
428
|
-
'@type' => 'earl:TestResult',
|
429
|
-
'outcome' => 'earl:untested'
|
430
|
-
}
|
431
|
-
}
|
432
|
-
end
|
433
|
-
|
434
|
-
test_cases[uri.to_s] = tc
|
435
|
-
man_info['entries'] << tc
|
436
|
-
end
|
437
|
-
end
|
438
|
-
|
439
|
-
raise "No test cases found" if man_info['entries'].empty?
|
440
|
-
status "Test cases:\n #{man_info['entries'].map {|tc| tc.fetch('@id')}.join("\n ")}"
|
441
|
-
end
|
442
|
-
qnd = Time.now
|
443
|
-
status "\nassertion query: #{(qnd - qst)/1000} secs"
|
444
|
-
|
445
|
-
raise "No manifests found" if manifests.empty?
|
446
|
-
status "Manifests:\n #{manifests.map {|m| m.fetch('@id')}.join("\n ")}"
|
447
|
-
|
448
|
-
# Iterate through assertions and add to appropriate test case
|
449
|
-
found_solutions = {}
|
450
|
-
SPARQL.execute(ASSERTION_QUERY, @graph).each do |solution|
|
451
|
-
tc = test_cases[solution[:test].to_s]
|
452
|
-
unless tc
|
453
|
-
$stderr.puts "Skipping result for #{solution[:test]}, which is not defined in manifests"
|
454
|
-
next
|
455
|
-
end
|
456
|
-
unless solution[:outcome]
|
457
|
-
$stderr.puts "No result found for #{solution[:test]}: #{solution.inspect}"
|
458
|
-
next
|
459
|
-
end
|
460
|
-
unless solution[:outcome]
|
461
|
-
$stderr.puts "No test subject found for #{solution[:test]}: #{solution.inspect}"
|
462
|
-
next
|
463
|
-
end
|
464
|
-
subject = solution[:subject].to_s
|
465
|
-
found_solutions[subject] = true
|
466
|
-
result_index = subjects.index(subject)
|
467
|
-
unless result_index
|
468
|
-
$stderr.puts "No test result subject found for #{subject}: #{solution.inspect}"
|
469
|
-
next
|
470
|
-
end
|
471
|
-
ta_hash = tc['assertions'][result_index]
|
472
|
-
ta_hash['assertedBy'] = solution[:by].to_s
|
473
|
-
ta_hash['mode'] = "earl:#{solution[:mode].to_s.split('#').last || 'notAvailable'}"
|
474
|
-
ta_hash['result']['outcome'] = "earl:#{solution[:outcome].to_s.split('#').last}"
|
475
|
-
end
|
476
|
-
|
477
|
-
# See if any subject did not report results, which indicates a formatting error in the EARL source
|
478
|
-
subjects.reject {|s| found_solutions[s]}.each do |sub|
|
479
|
-
$stderr.puts "No results found for #{sub} using #{ASSERTION_QUERY}"
|
480
|
-
end
|
481
|
-
|
482
|
-
manifests.sort_by {|m| m['title'].to_s}
|
483
|
-
end
|
484
|
-
|
485
465
|
##
|
486
466
|
# Output consoloated EARL report as Turtle
|
487
467
|
# @param [Hash{Symbol => Object}] options
|
@@ -489,165 +469,45 @@ class EarlReport
|
|
489
469
|
# @return [String]
|
490
470
|
def earl_turtle(options)
|
491
471
|
io = options[:io]
|
492
|
-
# Write preamble
|
493
|
-
{
|
494
|
-
:dc => RDF::DC,
|
495
|
-
:doap => RDF::DOAP,
|
496
|
-
:earl => EARL,
|
497
|
-
:foaf => RDF::FOAF,
|
498
|
-
:mf => MF,
|
499
|
-
:owl => RDF::OWL,
|
500
|
-
:rdf => RDF,
|
501
|
-
:rdfs => RDF::RDFS,
|
502
|
-
:xhv => RDF::XHV,
|
503
|
-
:xsd => RDF::XSD
|
504
|
-
}.each do |prefix, vocab|
|
505
|
-
io.puts("@prefix #{prefix}: <#{vocab.to_uri}> .")
|
506
|
-
end
|
507
|
-
io.puts
|
508
472
|
|
509
|
-
|
510
|
-
raise "Expected EARL json to have #{field.inspect} entry" unless json_hash[field]
|
511
|
-
end
|
473
|
+
top_level = graph.first_subject(predicate: EARL.generatedBy)
|
512
474
|
|
513
|
-
# Write
|
514
|
-
|
515
|
-
|
516
|
-
#{as_resource(json_hash.fetch('@id'))} a #{Array(json_hash.fetch('@type')).join(', ')};
|
517
|
-
doap:name #{quoted(json_hash.fetch('name', 'Unknown'))};
|
518
|
-
dc:bibliographicCitation "#{json_hash.fetch('bibRef', 'Unknown reference')}";
|
519
|
-
earl:generatedBy #{as_resource json_hash.fetch('generatedBy').fetch('@id')};
|
520
|
-
earl:assertions
|
521
|
-
#{json_hash.fetch('assertions').map {|a| as_resource(a)}.join(",\n ")};
|
522
|
-
earl:testSubjects (
|
523
|
-
#{json_hash.fetch('testSubjects').map {|a| as_resource(a.fetch('@id'))}.join("\n ")});
|
524
|
-
mf:entries (\n #{man_defs}) .
|
525
|
-
}.gsub(/^ /, '')
|
526
|
-
|
527
|
-
# Write generating software information
|
528
|
-
io.puts %{
|
529
|
-
<http://rubygems.org/gems/earl-report> a earl:Software, doap:Project;
|
530
|
-
doap:name "earl-report";
|
531
|
-
doap:shortdesc "Earl Report summary generator"@en;
|
532
|
-
doap:description "EarlReport generates HTML+RDFa rollups of multiple EARL reports"@en;
|
533
|
-
doap:homepage <https://github.com/gkellogg/earl-report>;
|
534
|
-
doap:programming-language "Ruby";
|
535
|
-
doap:license <http://unlicense.org>;
|
536
|
-
doap:release <https://github.com/gkellogg/earl-report/tree/#{VERSION}>;
|
537
|
-
doap:developer <http://greggkellogg.net/foaf#me> .
|
538
|
-
|
539
|
-
}.gsub(/^ /, '')
|
540
|
-
|
541
|
-
# Output Manifest definitions
|
542
|
-
# along with test cases and assertions
|
543
|
-
test_cases = []
|
544
|
-
io.puts %(\n# Manifests)
|
545
|
-
json_hash['entries'].each do |man|
|
546
|
-
io.puts %(#{as_resource(man.fetch('@id'))} a earl:Report, mf:Manifest;)
|
547
|
-
io.puts %( dc:title #{quoted(man['title'])};) if man['title']
|
548
|
-
io.puts %( mf:name #{quoted(man['title'])};) if man['title']
|
549
|
-
io.puts %( rdfs:comment #{quoted(man['description'])};) if man['description']
|
550
|
-
|
551
|
-
# Test Cases
|
552
|
-
test_defs = man.fetch('entries').map {|defn| as_resource(defn.fetch('@id'))}.join("\n ")
|
553
|
-
io.puts %( mf:entries (\n #{test_defs}) .\n\n)
|
554
|
-
|
555
|
-
test_cases += man['entries']
|
556
|
-
end
|
475
|
+
# Write starting with the entire graph to get preamble
|
476
|
+
writer = RDF::Turtle::Writer.new(io, standard_prefixes: true)
|
477
|
+
writer << graph
|
557
478
|
|
558
|
-
|
559
|
-
|
560
|
-
json_hash.fetch('testSubjects').each do |ts_desc|
|
561
|
-
io.write(test_subject_turtle(ts_desc))
|
562
|
-
end
|
479
|
+
writer.send(:preprocess)
|
480
|
+
writer.send(:start_document)
|
563
481
|
|
564
|
-
# Write
|
565
|
-
|
566
|
-
json_hash.fetch('entries').each do |manifest|
|
567
|
-
manifest.fetch('entries').each do |test_case|
|
568
|
-
io.write(tc_turtle(test_case))
|
569
|
-
end
|
570
|
-
end
|
571
|
-
end
|
572
|
-
|
573
|
-
##
|
574
|
-
# Write out Test Subject definition for each earl:TestSubject
|
575
|
-
# @param [Hash] desc
|
576
|
-
# @return [String]
|
577
|
-
def test_subject_turtle(desc)
|
578
|
-
%w(@id @type name).each do |field|
|
579
|
-
raise "Expected test description to have #{field.inspect} entry" unless desc[field]
|
580
|
-
end
|
482
|
+
# Write top-level object referencing manifests and subjects
|
483
|
+
writer.send(:statement, top_level)
|
581
484
|
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
res += %( .\n\n)
|
485
|
+
# Write each manifest
|
486
|
+
io.puts "\n# Manifests"
|
487
|
+
RDF::List.new(graph.first_object(subject: top_level, predicate: MF[:entries]), graph).each do |manifest|
|
488
|
+
writer.send(:statement, manifest)
|
587
489
|
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
raise "Expected FOAF description to have #{field.inspect} entry" unless developer[field]
|
592
|
-
end
|
593
|
-
res += %(<#{desc.fetch('@id')}> doap:developer <#{developer.fetch('@id')}> .\n\n)
|
594
|
-
res += %(<#{developer.fetch('@id')}> a #{Array(developer.fetch('@type')).join(', ')};\n)
|
595
|
-
res += %( foaf:homepage <#{developer['foaf:homepage']}>;\n) if developer['foaf:homepage']
|
596
|
-
res += %( foaf:name #{quoted(developer.fetch('foaf:name'))} .\n\n)
|
597
|
-
else
|
598
|
-
%w(foaf:name).each do |field|
|
599
|
-
raise "Expected FOAF description to have #{field.inspect} entry" unless developer[field]
|
600
|
-
end
|
601
|
-
res += %(<#{desc.fetch('@id')}> doap:developer\n)
|
602
|
-
res += %( [ a #{developer.fetch('@type', 'foaf:Person')};\n)
|
603
|
-
res += %( foaf:homepage <#{developer['foaf:homepage']}>;\n) if developer['foaf:homepage']
|
604
|
-
res += %( foaf:name #{quoted(developer.fetch('foaf:name'))} ] .\n\n)
|
490
|
+
# Write each test case
|
491
|
+
RDF::List.new(graph.first_object(subject: manifest, predicate: MF[:entries]), graph).each do |tc|
|
492
|
+
writer.send(:statement, tc)
|
605
493
|
end
|
606
494
|
end
|
607
|
-
res + "\n"
|
608
|
-
end
|
609
|
-
|
610
|
-
##
|
611
|
-
# Write out each Test Case definition
|
612
|
-
# @prarm[Hash] desc
|
613
|
-
# @return [String]
|
614
|
-
def tc_turtle(desc)
|
615
|
-
%w(@id @type title testAction assertions).each do |field|
|
616
|
-
raise "Expected test case description to have #{field.inspect} entry" unless desc[field]
|
617
|
-
end
|
618
|
-
types = Array(desc['@type']).map do |t|
|
619
|
-
t.include?("://") ? "<#{t}>" : t
|
620
|
-
end
|
621
|
-
res = %{#{as_resource desc.fetch('@id')} a #{types.join(', ')};\n}
|
622
|
-
res += %{ dc:title #{quoted(desc.fetch('title'))};\n}
|
623
|
-
res += %{ dc:description #{quoted(desc['description'])}@en;\n} if desc['description']
|
624
|
-
res += %{ mf:result #{as_resource desc['testResult']};\n} if desc['testResult']
|
625
|
-
res += %{ mf:action #{as_resource desc.fetch('testAction')};\n}
|
626
|
-
res += %{ earl:assertions (\n}
|
627
|
-
desc['assertions'].each do |as_desc|
|
628
|
-
res += as_turtle(as_desc)
|
629
|
-
end
|
630
|
-
res += %{ ) .\n\n}
|
631
|
-
end
|
632
495
|
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
496
|
+
# Write test subjects
|
497
|
+
io.puts "\n# Test Subjects"
|
498
|
+
graph.query(subject: top_level, predicate: EARL.testSubjects).each do |s|
|
499
|
+
writer.send(:statement, s.object)
|
500
|
+
|
501
|
+
# Write each developer
|
502
|
+
graph.query(subject: s.object, predicate: RDF::DOAP.developer).each do |d|
|
503
|
+
writer.send(:statement, d.object)
|
504
|
+
end
|
640
505
|
end
|
641
|
-
res = %( [ a earl:Assertion;\n)
|
642
|
-
res += %( earl:assertedBy #{as_resource desc['assertedBy']};\n) if desc['assertedBy']
|
643
|
-
res += %( earl:test #{as_resource desc.fetch('test')};\n)
|
644
|
-
res += %( earl:subject #{as_resource desc.fetch('subject')};\n)
|
645
|
-
res += %( earl:mode #{desc['mode']};\n) if desc['mode']
|
646
|
-
res += %( earl:result [ a earl:TestResult; earl:outcome #{desc.fetch('result').fetch('outcome')} ]]\n)
|
647
|
-
end
|
648
506
|
|
649
|
-
|
650
|
-
|
507
|
+
# Write generator
|
508
|
+
io.puts "\n# Report Generation Software"
|
509
|
+
writer.send(:statement, RDF::URI("http://rubygems.org/gems/earl-report"))
|
510
|
+
writer.send(:statement, RDF::URI("https://github.com/gkellogg/earl-report/tree/#{VERSION}"))
|
651
511
|
end
|
652
512
|
|
653
513
|
def quoted(string)
|