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 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
@@ -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