earl-report 0.5.0 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -10
- data/VERSION +1 -1
- data/bin/earl-report +4 -2
- data/lib/earl_report.rb +198 -115
- data/lib/earl_report/views/earl_report.html.haml +8 -8
- data/spec/earl_report_spec.rb +118 -29
- data/spec/spec_helper.rb +15 -9
- data/spec/test-files/doap.ttl +11 -11
- data/spec/test-files/foaf.ttl +2 -2
- data/spec/test-files/report-complete.ttl +15 -15
- data/spec/test-files/report-no-doap.ttl +4 -4
- data/spec/test-files/report-no-foaf.ttl +14 -14
- data/spec/test-files/report-no-test.ttl +48 -0
- data/spec/test-files/results.html +30 -30
- data/spec/test-files/results.jsonld +83 -83
- data/spec/test-files/results.ttl +61 -74
- metadata +107 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5d9b1de6d1ff74153dad8f2ff10691a43066da67b118c2c3cac375d030963e9
|
4
|
+
data.tar.gz: 2717c41b45c6f878157d070e34ca459530eda435b668e83141e6acb730f0be7b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f32d6ee582e6fb93c7a87f43a88c8146ec2ca952cb6000e8459213d65260cb454a253947adefe013d8cd0026aa847fde157a0eda0949077cb72e265f06ed61b3
|
7
|
+
data.tar.gz: 6ade127c2bb38bb1bec57106a71672d091a107799e6b2e1f37e65ecc66e4608a2573455a50470be918636aa230669b88fbde660e65c9172d660619982539dd73
|
data/README.md
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
Ruby gem to consolidate multiple EARL report and generate a rollup conformance report.
|
3
3
|
|
4
4
|
[![Gem Version](https://badge.fury.io/rb/earl-report.png)](http://badge.fury.io/rb/earl-report)
|
5
|
-
[![Build Status](https://
|
6
|
-
[![Coverage Status](https://coveralls.io/repos/gkellogg/earl-report/badge.svg)](https://coveralls.io/r/gkellogg/earl-report)
|
5
|
+
[![Build Status](https://github.com/gkellogg/earl-report/workflows/CI/badge.svg?branch=develop)](https://github.com/gkellogg/earl-report/actions?query=workflow%3ACI)
|
6
|
+
[![Coverage Status](https://coveralls.io/repos/gkellogg/earl-report/badge.svg?branch=develop)](https://coveralls.io/r/gkellogg/earl-report?branch=develop)
|
7
7
|
|
8
8
|
## Description
|
9
9
|
Reads a test manifest in the
|
@@ -17,8 +17,8 @@ may be specified in an any compatible RDF serialization. The report is composed
|
|
17
17
|
in the following form:
|
18
18
|
|
19
19
|
[ a earl:Assertion;
|
20
|
-
earl:assertedBy <
|
21
|
-
earl:subject <
|
20
|
+
earl:assertedBy <https://greggkellogg.net/foaf#me>;
|
21
|
+
earl:subject <https://rubygems.org/gems/rdf-turtle>;
|
22
22
|
earl:test <http://dvcs.w3.org/hg/rdf/raw-file/default/rdf-turtle/tests-ttl/manifest.ttl#turtle-syntax-file-01>;
|
23
23
|
earl:result [
|
24
24
|
a earl:TestResult;
|
@@ -29,9 +29,9 @@ in the following form:
|
|
29
29
|
Additionally, `earl:subject` is expected to reference a [DOAP]() description
|
30
30
|
of the reported software, in the following form:
|
31
31
|
|
32
|
-
<
|
32
|
+
<https://rubygems.org/gems/rdf-turtle> a doap:Project, earl:TestSubject, earl:Software ;
|
33
33
|
doap:name "RDF::Turtle" ;
|
34
|
-
doap:developer <
|
34
|
+
doap:developer <https://greggkellogg.net/foaf#me> ;
|
35
35
|
doap:homepage <http://ruby-rdf.github.com/rdf-turtle> ;
|
36
36
|
doap:description "RDF::Turtle is an Turtle reader/writer for the RDF.rb library suite."@en ;
|
37
37
|
doap:release [
|
@@ -49,7 +49,7 @@ The `doap:developer` is expected to reference a [FOAF]() profile for the agent
|
|
49
49
|
(user or organization) responsible for the test subject. It is expected to be
|
50
50
|
of the following form:
|
51
51
|
|
52
|
-
<
|
52
|
+
<https://greggkellogg.net/foaf#me> foaf:name "Gregg Kellogg" .
|
53
53
|
|
54
54
|
If not found, the IRI identified by `doap:developer`
|
55
55
|
will be dereferenced and is presumed to provide a [FOAF]() profile of the developer.
|
@@ -96,14 +96,14 @@ Version 0.3 and prior re-constructed the test manifest used to create the body o
|
|
96
96
|
mf:result <http://example/test-00.out>;
|
97
97
|
earl:assertions ([
|
98
98
|
a earl:Assertion;
|
99
|
-
earl:assertedBy <
|
99
|
+
earl:assertedBy <https://greggkellogg.net/foaf#me>;
|
100
100
|
earl:mode earl:automatic;
|
101
101
|
earl:result [
|
102
102
|
a earl:TestResult;
|
103
103
|
dc:date "2012-11-06T19:23:29-08:00"^^xsd:dateTime;
|
104
104
|
earl:outcome earl:passed
|
105
105
|
];
|
106
|
-
earl:subject <
|
106
|
+
earl:subject <https://rubygems.org/gems/rdf-turtle>;
|
107
107
|
earl:test <http://example/manifest.ttl#testeval00>
|
108
108
|
]) .
|
109
109
|
|
@@ -132,7 +132,7 @@ Generally, creating a `json` format first is more efficient. Subsequent invocati
|
|
132
132
|
When run, `earl-report` attempts to open the file `.earl` in the current directory. This file is in [YAML][] format with entries for each option. Use the `--rc` option to automatically generate it.
|
133
133
|
|
134
134
|
## Author
|
135
|
-
* [Gregg Kellogg](
|
135
|
+
* [Gregg Kellogg](https://github.com/gkellogg) - <https://greggkellogg.net/>
|
136
136
|
|
137
137
|
## License
|
138
138
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.2
|
data/bin/earl-report
CHANGED
@@ -15,6 +15,7 @@ OPT_ARGS = [
|
|
15
15
|
["--output", "-o", GetoptLong::REQUIRED_ARGUMENT,"Output report to file"],
|
16
16
|
["--query", GetoptLong::REQUIRED_ARGUMENT,"Query, or file containing query for extracting information from Test manifest"],
|
17
17
|
["--rc", GetoptLong::NO_ARGUMENT, "Write options to run-control file"],
|
18
|
+
["--strict", GetoptLong::NO_ARGUMENT, "Fails if any skipped result is found"],
|
18
19
|
["--template", GetoptLong::OPTIONAL_ARGUMENT,"Specify or return default report template"],
|
19
20
|
["--verbose", GetoptLong::NO_ARGUMENT, "Detail on execution"],
|
20
21
|
["--help", "-?", GetoptLong::NO_ARGUMENT, "This message"]
|
@@ -65,6 +66,7 @@ opts.each do |opt, arg|
|
|
65
66
|
when '--name' then options[:name] = arg
|
66
67
|
when '--output' then options[:io] = File.open(arg, "w")
|
67
68
|
when '--rc' then options[:rc] = true
|
69
|
+
when '--strict' then options[:strict] = true
|
68
70
|
when '--template' then options[:template] = (File.open(arg, "r") unless arg.empty?)
|
69
71
|
when '--verbose' then options[:verbose] = true
|
70
72
|
when '--help' then usage
|
@@ -94,6 +96,6 @@ if options.has_key?(:template) && options[:template].nil?
|
|
94
96
|
elsif ARGV.empty?
|
95
97
|
usage
|
96
98
|
else
|
97
|
-
earl = EarlReport.new(*ARGV, options)
|
98
|
-
earl.generate(options)
|
99
|
+
earl = EarlReport.new(*ARGV, **options)
|
100
|
+
earl.generate(**options)
|
99
101
|
end
|
data/lib/earl_report.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# EARL reporting
|
2
|
-
require '
|
2
|
+
require 'json/ld'
|
3
|
+
require 'rdf/turtle'
|
4
|
+
require 'rdf/vocab'
|
3
5
|
require 'sparql'
|
4
6
|
require 'haml'
|
5
7
|
require 'open-uri'
|
@@ -11,6 +13,7 @@ class EarlReport
|
|
11
13
|
autoload :VERSION, 'earl_report/version'
|
12
14
|
|
13
15
|
attr_reader :graph
|
16
|
+
attr_reader :verbose
|
14
17
|
|
15
18
|
# Return information about each test.
|
16
19
|
# Tests all have an mf:action property.
|
@@ -152,35 +155,71 @@ class EarlReport
|
|
152
155
|
}]
|
153
156
|
}.freeze
|
154
157
|
|
158
|
+
TURTLE_PREFIXES = %(@prefix dc: <http://purl.org/dc/terms/> .
|
159
|
+
@prefix doap: <http://usefulinc.com/ns/doap#> .
|
160
|
+
@prefix earl: <http://www.w3.org/ns/earl#> .
|
161
|
+
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
|
162
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
163
|
+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
164
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
165
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
166
|
+
).gsub(/^ /, '')
|
167
|
+
|
168
|
+
TURTLE_SOFTWARE = %(
|
169
|
+
# Report Generation Software
|
170
|
+
<https://rubygems.org/gems/earl-report> a earl:Software, doap:Project;
|
171
|
+
doap:name "earl-report";
|
172
|
+
doap:shortdesc "Earl Report summary generator"@en;
|
173
|
+
doap:description "EarlReport generates HTML+RDFa rollups of multiple EARL reports"@en;
|
174
|
+
doap:homepage <https://github.com/gkellogg/earl-report>;
|
175
|
+
doap:programming-language "Ruby";
|
176
|
+
doap:license <http://unlicense.org>;
|
177
|
+
doap:release <https://github.com/gkellogg/earl-report/tree/#{VERSION}>;
|
178
|
+
doap:developer <https://greggkellogg.net/foaf#me> .
|
179
|
+
|
180
|
+
<https://github.com/gkellogg/earl-report/tree/#{VERSION}> a doap:Version;
|
181
|
+
doap:name "earl-report-#{VERSION}";
|
182
|
+
doap:created "#{File.mtime(File.expand_path('../../VERSION', __FILE__)).strftime('%Y-%m-%d')}"^^xsd:date;
|
183
|
+
doap:revision "#{VERSION}" .
|
184
|
+
).gsub(/^ /, '')
|
185
|
+
|
155
186
|
# Convenience vocabularies
|
156
187
|
class EARL < RDF::Vocabulary("http://www.w3.org/ns/earl#"); end
|
157
188
|
class MF < RDF::Vocabulary("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#"); end
|
158
189
|
|
159
190
|
##
|
160
191
|
# Load test assertions and look for referenced software and developer information
|
161
|
-
#
|
162
|
-
#
|
163
|
-
# @
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
172
|
-
#
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
192
|
+
#
|
193
|
+
# @param [Array<String>] files Assertions
|
194
|
+
# @param [String] base (nil) Base IRI for loading Manifest
|
195
|
+
# @param [String] bibRef ('Unknown reference')
|
196
|
+
# ReSpec bibliography reference for specification being tested
|
197
|
+
# @param [Boolean] json (false) File is in the JSON format of a report.
|
198
|
+
# @param [String, Array<String>] manifest (nil) Test manifest(s)
|
199
|
+
# @param [String] name ('Unknown') Name of specification
|
200
|
+
# @param [String] query (MANIFEST_QUERY)
|
201
|
+
# Query, or file containing query for extracting information from Test manifests
|
202
|
+
# @param [Boolean] strict (false) Abort on any warning
|
203
|
+
# @param [Boolean] verbose (false)
|
204
|
+
def initialize(*files,
|
205
|
+
base: nil,
|
206
|
+
bibRef: 'Unknown reference',
|
207
|
+
json: false,
|
208
|
+
manifest: nil,
|
209
|
+
name: 'Unknown',
|
210
|
+
query: MANIFEST_QUERY,
|
211
|
+
strict: false,
|
212
|
+
verbose: false,
|
213
|
+
**options)
|
214
|
+
@verbose = verbose
|
215
|
+
raise "Test Manifests must be specified with :manifest option" unless manifest || json
|
178
216
|
raise "Require at least one input file" if files.empty?
|
179
217
|
@files = files
|
180
218
|
@prefixes = {}
|
219
|
+
@warnings = 0
|
181
220
|
|
182
|
-
# If provided
|
183
|
-
if
|
221
|
+
# If provided json, it is used for generating all other output forms
|
222
|
+
if json
|
184
223
|
@json_hash = ::JSON.parse(File.read(files.first))
|
185
224
|
# Add a base_uri so relative subjects aren't dropped
|
186
225
|
JSON::LD::Reader.open(files.first, base_uri: "http://example.org/report") do |r|
|
@@ -195,25 +234,25 @@ class EarlReport
|
|
195
234
|
end
|
196
235
|
|
197
236
|
# Load manifests, possibly with base URI
|
198
|
-
status "read #{
|
237
|
+
status "read #{manifest.inspect}"
|
199
238
|
man_opts = {}
|
200
|
-
man_opts[:base_uri] = RDF::URI(
|
239
|
+
man_opts[:base_uri] = RDF::URI(base) if base
|
201
240
|
@graph = RDF::Graph.new
|
202
|
-
Array(
|
241
|
+
Array(manifest).each do |man|
|
203
242
|
g = RDF::Graph.load(man, unique_bnodes: true, **man_opts)
|
204
243
|
status " loaded #{g.count} triples from #{man}"
|
205
244
|
graph << g
|
206
245
|
end
|
207
246
|
|
208
247
|
# Hash test cases by URI
|
209
|
-
tests = SPARQL.execute(
|
248
|
+
tests = SPARQL.execute(query, graph)
|
210
249
|
.to_a
|
211
250
|
.inject({}) {|memo, soln| memo[soln[:uri]] = soln; memo}
|
212
251
|
|
213
252
|
if tests.empty?
|
214
253
|
raise "no tests found querying manifest.\n" +
|
215
254
|
"Results are found using the following query, this can be overridden using the --query option:\n" +
|
216
|
-
"#{
|
255
|
+
"#{query}"
|
217
256
|
end
|
218
257
|
|
219
258
|
# Manifests in graph
|
@@ -234,7 +273,7 @@ class EarlReport
|
|
234
273
|
status "read #{file}"
|
235
274
|
file_graph = RDF::Graph.load(file)
|
236
275
|
if file_graph.first_object(predicate: RDF::URI('http://www.w3.org/ns/earl#testSubjects'))
|
237
|
-
|
276
|
+
warn " skip #{file}, which seems to be a previous rollup earl report"
|
238
277
|
@files -= [file]
|
239
278
|
else
|
240
279
|
status " loaded #{file_graph.count} triples"
|
@@ -286,7 +325,7 @@ class EarlReport
|
|
286
325
|
subjects[solution[:uri]] = RDF::URI(file)
|
287
326
|
|
288
327
|
# Add TestSubject information to main graph
|
289
|
-
|
328
|
+
doapName = solution[:name].to_s if solution[:name]
|
290
329
|
language = solution[:language].to_s if solution[:language]
|
291
330
|
doapDesc = solution[:doapDesc] if solution[:doapDesc]
|
292
331
|
doapDesc.language ||= :en if doapDesc
|
@@ -294,7 +333,7 @@ class EarlReport
|
|
294
333
|
graph << RDF::Statement(solution[:uri], RDF.type, RDF::Vocab::DOAP.Project)
|
295
334
|
graph << RDF::Statement(solution[:uri], RDF.type, EARL.TestSubject)
|
296
335
|
graph << RDF::Statement(solution[:uri], RDF.type, EARL.Software)
|
297
|
-
graph << RDF::Statement(solution[:uri], RDF::Vocab::DOAP.name,
|
336
|
+
graph << RDF::Statement(solution[:uri], RDF::Vocab::DOAP.name, doapName)
|
298
337
|
graph << RDF::Statement(solution[:uri], RDF::Vocab::DOAP.developer, solution[:developer])
|
299
338
|
graph << RDF::Statement(solution[:uri], RDF::Vocab::DOAP.homepage, solution[:homepage]) if solution[:homepage]
|
300
339
|
graph << RDF::Statement(solution[:uri], RDF::Vocab::DOAP.description, doapDesc) if doapDesc
|
@@ -303,7 +342,12 @@ class EarlReport
|
|
303
342
|
graph << RDF::Statement(solution[:developer], RDF::Vocab::FOAF.name, devName) if devName
|
304
343
|
graph << RDF::Statement(solution[:developer], RDF::Vocab::FOAF.homepage, solution[:devHomepage]) if solution[:devHomepage]
|
305
344
|
|
306
|
-
|
345
|
+
# Make sure BNode identifiers don't leak
|
346
|
+
release ||= if !solution[:release] || solution[:release].node?
|
347
|
+
RDF::Node.new
|
348
|
+
else
|
349
|
+
solution[:release]
|
350
|
+
end
|
307
351
|
graph << RDF::Statement(solution[:uri], RDF::Vocab::DOAP.release, release)
|
308
352
|
graph << RDF::Statement(release, RDF::Vocab::DOAP.revision, (solution[:revision] || "unknown"))
|
309
353
|
end
|
@@ -317,12 +361,12 @@ class EarlReport
|
|
317
361
|
subject = solution[:subject]
|
318
362
|
unless tests[solution[:test]]
|
319
363
|
assertion_stats["Skipped"] = assertion_stats["Skipped"].to_i + 1
|
320
|
-
|
364
|
+
warn "Skipping result for #{solution[:test]} for #{subject}, which is not defined in manifests"
|
321
365
|
next
|
322
366
|
end
|
323
367
|
unless subjects[subject]
|
324
368
|
assertion_stats["Missing Subject"] = assertion_stats["Missing Subject"].to_i + 1
|
325
|
-
|
369
|
+
warn "No test result subject found for #{subject}: in #{subjects.keys.join(', ')}"
|
326
370
|
next
|
327
371
|
end
|
328
372
|
found_solutions ||= true
|
@@ -345,7 +389,7 @@ class EarlReport
|
|
345
389
|
end
|
346
390
|
|
347
391
|
# See if subject did not report results, which may indicate a formatting error in the EARL source
|
348
|
-
|
392
|
+
warn "No results found for #{subject} using #{ASSERTION_QUERY}" unless found_solutions
|
349
393
|
end
|
350
394
|
end
|
351
395
|
|
@@ -374,36 +418,16 @@ class EarlReport
|
|
374
418
|
assertion_stats.each {|stat, count| status("Assertions #{stat}: #{count}")}
|
375
419
|
|
376
420
|
# Add report wrapper to graph
|
377
|
-
ttl = %(
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
earl:generatedBy <http://rubygems.org/gems/earl-report>;
|
388
|
-
earl:assertions #{subjects.values.map {|f| f.to_ntriples}.join(",\n ")};
|
389
|
-
earl:testSubjects #{subjects.keys.map {|f| f.to_ntriples}.join(",\n ")};
|
390
|
-
mf:entries (#{man_uris.map {|f| f.to_ntriples}.join("\n ")}) .
|
391
|
-
|
392
|
-
<http://rubygems.org/gems/earl-report> a earl:Software, doap:Project;
|
393
|
-
doap:name "earl-report";
|
394
|
-
doap:shortdesc "Earl Report summary generator"@en;
|
395
|
-
doap:description "EarlReport generates HTML+RDFa rollups of multiple EARL reports"@en;
|
396
|
-
doap:homepage <https://github.com/gkellogg/earl-report>;
|
397
|
-
doap:programming-language "Ruby";
|
398
|
-
doap:license <http://unlicense.org>;
|
399
|
-
doap:release <https://github.com/gkellogg/earl-report/tree/#{VERSION}>;
|
400
|
-
doap:developer <http://greggkellogg.net/foaf#me> .
|
401
|
-
|
402
|
-
<https://github.com/gkellogg/earl-report/tree/#{VERSION}> a doap:Version;
|
403
|
-
doap:name "earl-report-#{VERSION}";
|
404
|
-
doap:created "#{File.mtime(File.expand_path('../../VERSION', __FILE__)).strftime('%Y-%m-%d')}"^^xsd:date;
|
405
|
-
doap:revision "#{VERSION}" .
|
406
|
-
).gsub(/^ /, '')
|
421
|
+
ttl = TURTLE_PREFIXES + %(
|
422
|
+
<> a earl:Software, doap:Project;
|
423
|
+
doap:name #{quoted(name)};
|
424
|
+
dc:bibliographicCitation "#{bibRef}";
|
425
|
+
earl:generatedBy <https://rubygems.org/gems/earl-report>;
|
426
|
+
earl:assertions #{subjects.values.map {|f| f.to_ntriples}.join(",\n ")};
|
427
|
+
earl:testSubjects #{subjects.keys.map {|f| f.to_ntriples}.join(",\n ")};
|
428
|
+
mf:entries (#{man_uris.map {|f| f.to_ntriples}.join("\n ")}) .
|
429
|
+
).gsub(/^ /, '') +
|
430
|
+
TURTLE_SOFTWARE
|
407
431
|
RDF::Turtle::Reader.new(ttl) {|r| graph << r}
|
408
432
|
|
409
433
|
# Each manifest is an earl:Report
|
@@ -421,6 +445,8 @@ class EarlReport
|
|
421
445
|
graph << RDF::Statement.new(u, RDF.type, EARL.TestCriterion)
|
422
446
|
graph << RDF::Statement.new(u, RDF.type, EARL.TestCase)
|
423
447
|
end
|
448
|
+
|
449
|
+
raise "Warnings issued in strict mode" if strict && @warnings > 0
|
424
450
|
end
|
425
451
|
|
426
452
|
##
|
@@ -428,48 +454,47 @@ class EarlReport
|
|
428
454
|
#
|
429
455
|
# If no `io` option is provided, the output is returned as a string
|
430
456
|
#
|
457
|
+
# @param [Symbol] format (:html)
|
458
|
+
# @param [IO] io (nil)
|
459
|
+
# `IO` to output results
|
431
460
|
# @param [Hash{Symbol => Object}] options
|
432
|
-
# @
|
433
|
-
#
|
434
|
-
# Optional `IO` to output results
|
461
|
+
# @param [String] template
|
462
|
+
# HAML template for generating report
|
435
463
|
# @return [String] serialized graph, if `io` is nil
|
436
|
-
def generate(
|
437
|
-
options = {format: :html}.merge(options)
|
438
|
-
|
439
|
-
io = options[:io]
|
464
|
+
def generate(format: :html, io: nil, template: nil, **options)
|
440
465
|
|
441
|
-
status("generate: #{
|
466
|
+
status("generate: #{format}")
|
442
467
|
##
|
443
468
|
# Retrieve Hashed information in JSON-LD format
|
444
|
-
case
|
469
|
+
case format
|
445
470
|
when :jsonld, :json
|
446
471
|
json = json_hash.to_json(JSON::LD::JSON_STATE)
|
447
472
|
io.write(json) if io
|
448
473
|
json
|
449
474
|
when :turtle, :ttl
|
450
475
|
if io
|
451
|
-
earl_turtle(
|
476
|
+
earl_turtle(io: io)
|
452
477
|
else
|
453
478
|
io = StringIO.new
|
454
|
-
earl_turtle(
|
479
|
+
earl_turtle(io: io)
|
455
480
|
io.rewind
|
456
481
|
io.read
|
457
482
|
end
|
458
483
|
when :html
|
459
|
-
|
460
|
-
when String then
|
461
|
-
when IO, StringIO then
|
484
|
+
haml = case template
|
485
|
+
when String then template
|
486
|
+
when IO, StringIO then template.read
|
462
487
|
else
|
463
488
|
File.read(File.expand_path('../earl_report/views/earl_report.html.haml', __FILE__))
|
464
489
|
end
|
465
490
|
|
466
491
|
# Generate HTML report
|
467
|
-
html = Haml::Engine.new(
|
492
|
+
html = Haml::Engine.new(haml, format: :xhtml).render(self, tests: json_hash)
|
468
493
|
io.write(html) if io
|
469
494
|
html
|
470
495
|
else
|
471
|
-
writer = RDF::Writer.for(
|
472
|
-
writer.dump(@graph, io,
|
496
|
+
writer = RDF::Writer.for(format)
|
497
|
+
writer.dump(@graph, io, standard_prefixes: true, **options)
|
473
498
|
end
|
474
499
|
end
|
475
500
|
|
@@ -487,10 +512,10 @@ class EarlReport
|
|
487
512
|
embed: '@never',
|
488
513
|
pruneBlankNodeIdentifiers: false)
|
489
514
|
# Reorder test subjects by @id
|
490
|
-
framed['testSubjects'] = framed['testSubjects'].sort_by {|t| t['@id']}
|
515
|
+
framed['testSubjects'] = Array(framed['testSubjects']).sort_by {|t| t['@id']}
|
491
516
|
|
492
517
|
# Reorder test assertions to make them consistent with subject order
|
493
|
-
framed['entries'].each do |manifest|
|
518
|
+
Array(framed['entries']).each do |manifest|
|
494
519
|
manifest['entries'].each do |test|
|
495
520
|
test['assertions'] = test['assertions'].sort_by {|a| a['subject']}
|
496
521
|
end
|
@@ -506,61 +531,119 @@ class EarlReport
|
|
506
531
|
|
507
532
|
##
|
508
533
|
# Output consoloated EARL report as Turtle
|
509
|
-
# @param [
|
510
|
-
#
|
534
|
+
# @param [IO] io ($stdout)
|
535
|
+
# `IO` to output results
|
511
536
|
# @return [String]
|
512
|
-
def earl_turtle(
|
513
|
-
|
514
|
-
|
515
|
-
top_level = graph.first_subject(predicate: EARL.generatedBy)
|
537
|
+
def earl_turtle(io: $stdout)
|
538
|
+
context = JSON::LD::Context.parse(json_hash['@context'])
|
539
|
+
io.write(TURTLE_PREFIXES + "\n")
|
516
540
|
|
517
|
-
# Write
|
518
|
-
|
519
|
-
writer << graph
|
541
|
+
# Write project header
|
542
|
+
ttl_entity(io, json_hash, context)
|
520
543
|
|
521
|
-
|
522
|
-
|
544
|
+
# Write out each manifest entry
|
545
|
+
io.puts("# Manifests")
|
546
|
+
json_hash['entries'].each do |man|
|
547
|
+
ttl_entity(io, man, context)
|
523
548
|
|
524
|
-
|
525
|
-
|
549
|
+
# Output each test entry with assertions
|
550
|
+
man['entries'].each do |entry|
|
551
|
+
ttl_entity(io, entry, context)
|
552
|
+
end
|
553
|
+
end
|
526
554
|
|
527
|
-
#
|
528
|
-
|
529
|
-
|
530
|
-
writer.send(:statement, manifest)
|
555
|
+
# Output each DOAP
|
556
|
+
json_hash['testSubjects'].each do |doap|
|
557
|
+
ttl_entity(io, doap, context)
|
531
558
|
|
532
|
-
#
|
533
|
-
|
534
|
-
|
559
|
+
# FOAF
|
560
|
+
dev = doap['developer']
|
561
|
+
dev = [dev] unless dev.is_a?(Array)
|
562
|
+
dev.each do |foaf|
|
563
|
+
ttl_entity(io, foaf, context)
|
535
564
|
end
|
536
565
|
end
|
566
|
+
|
567
|
+
io.write(TURTLE_SOFTWARE)
|
568
|
+
end
|
537
569
|
|
538
|
-
|
539
|
-
io.
|
540
|
-
|
541
|
-
|
570
|
+
def ttl_entity(io, entity, context)
|
571
|
+
io.write(ttl_value(entity) + " " + entity.map do |dk, dv|
|
572
|
+
case dk
|
573
|
+
when '@context', '@id'
|
574
|
+
nil
|
575
|
+
when '@type'
|
576
|
+
"a " + ttl_value(dv)
|
577
|
+
when 'assertions'
|
578
|
+
"earl:assertions #{dv.map {|a| ttl_assertion(a)}.join(", ")}"
|
579
|
+
when 'entries'
|
580
|
+
"mf:entries #{ttl_value({'@list' => dv}, whitespace: "\n ")}"
|
581
|
+
when 'release'
|
582
|
+
"doap:release [doap:revision #{quoted(dv['revision'])}]"
|
583
|
+
else
|
584
|
+
dv = [dv] unless dv.is_a?(Array)
|
585
|
+
dv = dv.map {|v| v.is_a?(Hash) ? v : context.expand_value(dk, v)}
|
586
|
+
"#{ttl_value(dk)} #{ttl_value(dv, whitespace: "\n ")}"
|
587
|
+
end
|
588
|
+
end.compact.join(" ;\n ") + " .\n\n")
|
589
|
+
end
|
542
590
|
|
543
|
-
|
544
|
-
|
545
|
-
|
591
|
+
def ttl_value(value, whitespace: " ")
|
592
|
+
if value.is_a?(Array)
|
593
|
+
value.map {|v| ttl_value(v)}.join(",#{whitespace}")
|
594
|
+
elsif value.is_a?(Hash)
|
595
|
+
if value.key?('@list')
|
596
|
+
"(#{value['@list'].map {|vv| ttl_value(vv)}.join(whitespace)})"
|
597
|
+
elsif value.key?('@value')
|
598
|
+
quoted(value['@value'], language: value['@language'], datatype: value['@type'])
|
599
|
+
elsif value.key?('@id')
|
600
|
+
ttl_value(value['@id'])
|
601
|
+
else
|
602
|
+
"[]"
|
546
603
|
end
|
604
|
+
elsif value.start_with?(/https?/) || value.start_with?('/')
|
605
|
+
"<#{value}>"
|
606
|
+
elsif value.include?(':')
|
607
|
+
value
|
608
|
+
elsif json_hash['@context'][value].is_a?(Hash)
|
609
|
+
json_hash['@context'][value].fetch('@id', "earl:#{value}")
|
610
|
+
elsif value.empty?
|
611
|
+
"<>"
|
612
|
+
else
|
613
|
+
"earl:#{value}"
|
547
614
|
end
|
615
|
+
end
|
548
616
|
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
617
|
+
def ttl_assertion(value)
|
618
|
+
return ttl_value(value) if value.is_a?(String)
|
619
|
+
block = [
|
620
|
+
"[",
|
621
|
+
" a earl:Assertion ;",
|
622
|
+
" earl:test #{ttl_value(value['test'])} ;",
|
623
|
+
" earl:subject #{ttl_value(value['subject'])} ;",
|
624
|
+
" earl:result [",
|
625
|
+
" a earl:TestResult ;",
|
626
|
+
" earl:outcome #{ttl_value(value['result']['outcome'])}",
|
627
|
+
" ] ;",
|
628
|
+
]
|
629
|
+
block << " earl:assertedBy #{ttl_value(value['assertedBy'])} ;" if value['assertedBy']
|
630
|
+
|
631
|
+
block.join("\n") + "\n ]"
|
553
632
|
end
|
554
633
|
|
555
|
-
def quoted(string)
|
556
|
-
(@turtle_writer ||= RDF::Turtle::Writer.new).send(:quoted, string)
|
634
|
+
def quoted(string, language: nil, datatype: nil)
|
635
|
+
str = (@turtle_writer ||= RDF::Turtle::Writer.new).send(:quoted, string)
|
636
|
+
str += "@#{language}" if language
|
637
|
+
str += "^^#{ttl_value(datatype)}" if datatype
|
638
|
+
str
|
557
639
|
end
|
558
640
|
|
559
641
|
def warn(message)
|
642
|
+
@warnings += 1
|
560
643
|
$stderr.puts message
|
561
644
|
end
|
562
645
|
|
563
646
|
def status(message)
|
564
|
-
$stderr.puts message if
|
647
|
+
$stderr.puts message if verbose
|
565
648
|
end
|
566
649
|
end
|