From e970e63146fb5d8de511104eba7aef5319e8653b Mon Sep 17 00:00:00 2001 From: David Robillard Date: Mon, 27 Mar 2023 15:42:27 -0400 Subject: Add pretty-printing test suite The earlier "test" was just hitting the code without actually checking the output. This new suite is a set of pretty-printed documents which serd must reproduce exactly to pass. This should make it easy to add cases in the future, since each case is just a document, as it should look. --- test/good/manifest.ttl | 7 -- test/good/test-pretty.nt | 46 ------- test/good/test-pretty.ttl | 44 ------- test/meson.build | 47 +++++-- test/pretty/README.md | 5 + test/pretty/abbreviation.ttl | 57 +++++++++ test/pretty/anonymous-in-list-object.ttl | 10 ++ test/pretty/anonymous-object.ttl | 10 ++ test/pretty/anonymous-subject-and-object.ttl | 4 + test/pretty/anonymous-subject.ttl | 4 + test/pretty/datatypes.ttl | 15 +++ test/pretty/empty-anonymous-object.ttl | 4 + test/pretty/empty-list-object.ttl | 4 + test/pretty/empty-list-subject-and-object.ttl | 4 + test/pretty/empty-list-subject.ttl | 4 + test/pretty/ext-named-blank.ttl | 11 ++ test/pretty/graph-abbreviation.trig | 65 ++++++++++ test/pretty/langtags.ttl | 5 + test/pretty/list-in-object.ttl | 10 ++ test/pretty/list-object.ttl | 8 ++ test/pretty/local-name-escapes.ttl | 4 + test/pretty/long-string-escapes.ttl | 4 + test/pretty/long-string-quotes.ttl | 5 + test/pretty/manifest.ttl | 171 ++++++++++++++++++++++++++ test/pretty/many-objects.ttl | 9 ++ test/pretty/named-graph.trig | 8 ++ test/pretty/nested-list-object.ttl | 12 ++ test/pretty/short-string-escapes.ttl | 4 + test/pretty/uri-escapes.ttl | 2 + test/run_suite.py | 125 +++++++++++++++++++ test/serd_test_util/__init__.py | 19 +++ 31 files changed, 618 insertions(+), 109 deletions(-) delete mode 100644 test/good/test-pretty.nt delete mode 100644 test/good/test-pretty.ttl create mode 100644 test/pretty/README.md create mode 100644 test/pretty/abbreviation.ttl create mode 100644 test/pretty/anonymous-in-list-object.ttl create mode 100644 test/pretty/anonymous-object.ttl create mode 100644 test/pretty/anonymous-subject-and-object.ttl create mode 100644 test/pretty/anonymous-subject.ttl create mode 100644 test/pretty/datatypes.ttl create mode 100644 test/pretty/empty-anonymous-object.ttl create mode 100644 test/pretty/empty-list-object.ttl create mode 100644 test/pretty/empty-list-subject-and-object.ttl create mode 100644 test/pretty/empty-list-subject.ttl create mode 100644 test/pretty/ext-named-blank.ttl create mode 100644 test/pretty/graph-abbreviation.trig create mode 100644 test/pretty/langtags.ttl create mode 100644 test/pretty/list-in-object.ttl create mode 100644 test/pretty/list-object.ttl create mode 100644 test/pretty/local-name-escapes.ttl create mode 100644 test/pretty/long-string-escapes.ttl create mode 100644 test/pretty/long-string-quotes.ttl create mode 100644 test/pretty/manifest.ttl create mode 100644 test/pretty/many-objects.ttl create mode 100644 test/pretty/named-graph.trig create mode 100644 test/pretty/nested-list-object.ttl create mode 100644 test/pretty/short-string-escapes.ttl create mode 100644 test/pretty/uri-escapes.ttl create mode 100755 test/run_suite.py (limited to 'test') diff --git a/test/good/manifest.ttl b/test/good/manifest.ttl index 25359fdc..aeb90340 100644 --- a/test/good/manifest.ttl +++ b/test/good/manifest.ttl @@ -44,7 +44,6 @@ <#test-num> <#test-out-of-range-unicode> <#test-prefix> - <#test-pretty> <#test-quote-escapes> <#test-rel> <#test-semi-dot> @@ -276,12 +275,6 @@ mf:action ; mf:result . -<#test-pretty> - rdf:type rdft:TestTurtleEval ; - mf:name "test-pretty" ; - mf:action ; - mf:result . - <#test-quote-escapes> rdf:type rdft:TestTurtleEval ; mf:name "test-quote-escapes" ; diff --git a/test/good/test-pretty.nt b/test/good/test-pretty.nt deleted file mode 100644 index 9251563a..00000000 --- a/test/good/test-pretty.nt +++ /dev/null @@ -1,46 +0,0 @@ - . -_:b1 . - . -_:b2 _:b3 . -_:b4 "apple" . -_:b4 _:b5 . -_:b5 "banana" . -_:b5 _:b6 . -_:b6 "pear" . -_:b6 . -_:b4 . -_:b7 _:b8 . -_:b8 . -_:b8 _:b9 . -_:b9 . -_:b9 . -_:b7 _:b10 . -_:b10 _:b11 . -_:b11 . -_:b11 _:b12 . -_:b12 . -_:b12 . -_:b10 . -_:b7 . -_:b13 _:b14 . -_:b14 "apple" . -_:b14 _:b15 . -_:b15 "banana" . -_:b15 _:b16 . -_:b16 "pear" . -_:b16 . -_:b17 . -_:b17 . -_:b17 . -_:b18 _:b19 . -_:b19 . -_:b19 . -_:b18 _:b20 . -_:b20 . -_:b21 _:b22 . -_:b22 _:b23 . -_:b23 . -_:b22 _:b24 . -_:b24 _:b25 . -_:b25 . -_:b24 . diff --git a/test/good/test-pretty.ttl b/test/good/test-pretty.ttl deleted file mode 100644 index 4eb7204f..00000000 --- a/test/good/test-pretty.ttl +++ /dev/null @@ -1,44 +0,0 @@ -@prefix : . - -() :isA :List . - -[] :isA :Blank . - -() :sameAs () . - -[] :sameAs [] . - -( - "apple" - "banana" - "pear" -) a :List . - -( - (:a :b) - (:c :d) -) a :List . - -[] - :list ( - "apple" - "banana" - "pear" - ) . - -[] - :a :b , :c , :d . - -[] - :a [ - :b :c ; - :d :e ; - ] , [ - :f :g - ] . - -[] - :list ( - [ a :Apple ] - [ a :Banana ] - ) . \ No newline at end of file diff --git a/test/meson.build b/test/meson.build index 847d5bef..6a4bbc55 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,6 +1,7 @@ # Copyright 2020-2023 David Robillard # SPDX-License-Identifier: 0BSD OR ISC +run_suite = find_program('run_suite.py') run_test_suite = files('run_test_suite.py') wrapper = meson.get_external_property('exe_wrapper', '') @@ -64,13 +65,13 @@ endforeach # System Tests # ################ -if not get_option('tools').disabled() - if wrapper != '' - script_args = ['--wrapper', wrapper, '--serdi', serdi.full_path()] - else - script_args = ['--serdi', serdi.full_path()] - endif +common_script_args = [] +if wrapper != '' + common_script_args += ['--wrapper', wrapper] +endif +if not get_option('tools').disabled() + script_args = common_script_args + ['--serdi', serdi] serd_ttl = files('../serd.ttl')[0] test('serd.ttl', serdi, args: [serd_ttl], suite: 'data') @@ -158,18 +159,41 @@ if not get_option('tools').disabled() test('write_error', files('test_write_error.py'), args: script_args + [serd_ttl], suite: 'io_errors') +endif - # RDF test suites +########################### +# Data-Driven Test Suites # +########################### + +ns_serdtest = 'http://drobilla.net/sw/serd/test/' +ns_w3 = 'http://www.w3.org/2013/' + +test_suites = { + 'pretty': [ + files('pretty/manifest.ttl'), ns_serdtest + 'pretty/' + ], +} + +if not get_option('tools').disabled() + script_args = common_script_args + ['--serdi', serdi] + + foreach name, args : test_suites + test( + name, run_suite, + args: script_args + args, + suite: ['rdf'], + timeout: 240, + ) + endforeach ## Serd-specific test suites serd_suites = ['good', 'bad'] - serd_base = 'http://drobilla.net/sw/serd/test/' ### Run all suites with no special arguments foreach name : serd_suites manifest = files(name / 'manifest.ttl') - base_uri = serd_base + name + '/' + base_uri = ns_serdtest + name + '/' test(name, run_test_suite, args: script_args + [manifest, base_uri], suite: ['rdf', 'serd'], @@ -178,7 +202,7 @@ if not get_option('tools').disabled() ### The lax suite is special because it is run twice... lax_manifest = files('lax/manifest.ttl') - lax_base_uri = serd_base + 'lax/' + lax_base_uri = ns_serdtest + 'lax/' ### ... once with strict parsing to test the hard errors test('lax.strict', run_test_suite, @@ -197,11 +221,10 @@ if not get_option('tools').disabled() ## Standard W3C test suites w3c_suites = ['Turtle', 'NTriples', 'NQuads', 'TriG'] - w3c_base = 'http://www.w3.org/2013/' foreach syntax : w3c_suites manifest = files(syntax + 'Tests' / 'manifest.ttl') - base_uri = w3c_base + syntax + 'Tests/' + base_uri = ns_w3 + syntax + 'Tests/' args = ['--syntax', syntax, manifest, base_uri] if syntax == 'TriG' diff --git a/test/pretty/README.md b/test/pretty/README.md new file mode 100644 index 00000000..361d9491 --- /dev/null +++ b/test/pretty/README.md @@ -0,0 +1,5 @@ +Pretty Test Suite +================= + +This suite contains single-file tests that can be read and rewritten in the +same syntax, exactly reproducing the input byte-for-byte. diff --git a/test/pretty/abbreviation.ttl b/test/pretty/abbreviation.ttl new file mode 100644 index 00000000..bf46f6d5 --- /dev/null +++ b/test/pretty/abbreviation.ttl @@ -0,0 +1,57 @@ +@prefix eg: . + +eg:s1 + eg:b eg:c , + eg:d , + eg:e ; + eg:f eg:g , + eg:h ; + eg:i eg:j ; + eg:k eg:l . + +eg:s2 + a eg:Thing ; + eg:p1 eg:o1 , + [ + a eg:SubThing ; + eg:p2 eg:o2 + ] , [ + a eg:OtherSubThing ; + eg:p3 eg:o3 + ] ; + eg:p4 eg:o4 . + +eg:s3 + eg:resource eg:Blank . + +eg:s4 + eg:anon [] . + +eg:s5 + eg:blank [ + eg:nestedEmptyBlank [] ; + eg:nestedNonEmptyBlanks [ + eg:value 1 + ] , [ + eg:value 2 + ] + ] ; + eg:listOfNumbers ( + 3 + 4 + ) . + +eg:s6 + eg:listOfNumbers ( + 5 + 6 + ) . + +eg:s7 + eg:listOfResources ( + [ + eg:value 7 + ] [ + eg:value 8 + ] + ) . diff --git a/test/pretty/anonymous-in-list-object.ttl b/test/pretty/anonymous-in-list-object.ttl new file mode 100644 index 00000000..4f135e29 --- /dev/null +++ b/test/pretty/anonymous-in-list-object.ttl @@ -0,0 +1,10 @@ +@prefix eg: . + +eg:s + eg:p ( + [ + a eg:Spy + ] [ + a eg:Ninja + ] + ) . diff --git a/test/pretty/anonymous-object.ttl b/test/pretty/anonymous-object.ttl new file mode 100644 index 00000000..f592fed1 --- /dev/null +++ b/test/pretty/anonymous-object.ttl @@ -0,0 +1,10 @@ +@prefix eg: . + +eg:s + eg:p1 [ + eg:p2 eg:o2 ; + eg:p3 eg:o3 + ] , [ + eg:p4 eg:o4 ; + eg:p5 eg:o5 + ] . diff --git a/test/pretty/anonymous-subject-and-object.ttl b/test/pretty/anonymous-subject-and-object.ttl new file mode 100644 index 00000000..8904cca6 --- /dev/null +++ b/test/pretty/anonymous-subject-and-object.ttl @@ -0,0 +1,4 @@ +@prefix eg: . + +[] + eg:p [] . diff --git a/test/pretty/anonymous-subject.ttl b/test/pretty/anonymous-subject.ttl new file mode 100644 index 00000000..e0e467e2 --- /dev/null +++ b/test/pretty/anonymous-subject.ttl @@ -0,0 +1,4 @@ +@prefix eg: . + +[] + eg:p eg:o . diff --git a/test/pretty/datatypes.ttl b/test/pretty/datatypes.ttl new file mode 100644 index 00000000..721dfe4d --- /dev/null +++ b/test/pretty/datatypes.ttl @@ -0,0 +1,15 @@ +@prefix eg: . +@prefix xsd: . + +eg:s + eg:p -1.2 , + "-3."^^xsd:decimal , + -4 , + 0 , + 1 , + 2.3 , + "4."^^xsd:decimal , + false , + true , + "x"^^eg:datatype , + "y"^^ . diff --git a/test/pretty/empty-anonymous-object.ttl b/test/pretty/empty-anonymous-object.ttl new file mode 100644 index 00000000..f3b8d7f7 --- /dev/null +++ b/test/pretty/empty-anonymous-object.ttl @@ -0,0 +1,4 @@ +@prefix eg: . + +eg:s + eg:p [] . diff --git a/test/pretty/empty-list-object.ttl b/test/pretty/empty-list-object.ttl new file mode 100644 index 00000000..b4045ec0 --- /dev/null +++ b/test/pretty/empty-list-object.ttl @@ -0,0 +1,4 @@ +@prefix eg: . + +eg:s + eg:list () . diff --git a/test/pretty/empty-list-subject-and-object.ttl b/test/pretty/empty-list-subject-and-object.ttl new file mode 100644 index 00000000..3b84980e --- /dev/null +++ b/test/pretty/empty-list-subject-and-object.ttl @@ -0,0 +1,4 @@ +@prefix eg: . + +() + eg:list () . diff --git a/test/pretty/empty-list-subject.ttl b/test/pretty/empty-list-subject.ttl new file mode 100644 index 00000000..0610eb07 --- /dev/null +++ b/test/pretty/empty-list-subject.ttl @@ -0,0 +1,4 @@ +@prefix eg: . + +() + a eg:ExampleList . diff --git a/test/pretty/ext-named-blank.ttl b/test/pretty/ext-named-blank.ttl new file mode 100644 index 00000000..807d6313 --- /dev/null +++ b/test/pretty/ext-named-blank.ttl @@ -0,0 +1,11 @@ +@prefix eg: . + +eg:s + eg:p1 [ + == ; + eg:name "object" + ] ; + eg:p2 [ + == eg:o ; + eg:name "o" + ] . diff --git a/test/pretty/graph-abbreviation.trig b/test/pretty/graph-abbreviation.trig new file mode 100644 index 00000000..8ec75f6e --- /dev/null +++ b/test/pretty/graph-abbreviation.trig @@ -0,0 +1,65 @@ +@prefix : . +@prefix rdf: . + +:graph { + :a + :b :c , + :d , + :e ; + :f :g , + :h ; + :i :j ; + :k :l . + + :s + a :Thing ; + :p1 :o1 , + [ + a :SubThing ; + :p2 :o2 + ] , [ + a :OtherSubThing ; + :p3 :o3 + ] ; + :p4 :o4 . + + [] + :isA :Blank . + + [] + :sameAs [] . + + [] + :blank [ + :nestedEmptyBlank [] ; + :nestedNonEmptyBlanks [ + rdf:value 1 + ] , [ + rdf:value 2 + ] + ] ; + :lists ( + 3 + 4 + ) . + + [] + :lists ( + 5 + 6 + ) . + + [] + :lists ( + [ + rdf:value 7 + ] [ + rdf:value 8 + ] + ) . +} + +:separateGraph { + :m + a :OtherThing . +} diff --git a/test/pretty/langtags.ttl b/test/pretty/langtags.ttl new file mode 100644 index 00000000..29d7e2d4 --- /dev/null +++ b/test/pretty/langtags.ttl @@ -0,0 +1,5 @@ +@prefix eg: . + +eg:s + eg:p "eh?"@en-ca , + "hä?"@de . diff --git a/test/pretty/list-in-object.ttl b/test/pretty/list-in-object.ttl new file mode 100644 index 00000000..ce887157 --- /dev/null +++ b/test/pretty/list-in-object.ttl @@ -0,0 +1,10 @@ +@prefix eg: . + +eg:s + eg:p [ + eg:list ( + "apple" + "banana" + "cherry" + ) + ] . diff --git a/test/pretty/list-object.ttl b/test/pretty/list-object.ttl new file mode 100644 index 00000000..735d9df6 --- /dev/null +++ b/test/pretty/list-object.ttl @@ -0,0 +1,8 @@ +@prefix eg: . + +eg:s + eg:list ( + "apple" + "banana" + "cherry" + ) . diff --git a/test/pretty/local-name-escapes.ttl b/test/pretty/local-name-escapes.ttl new file mode 100644 index 00000000..91c3905d --- /dev/null +++ b/test/pretty/local-name-escapes.ttl @@ -0,0 +1,4 @@ +@prefix eg: . + +eg:s + eg:p eg:local\'\!\#\$\%\&\(\)\*\+\,\/\;\=\?\@\~ . diff --git a/test/pretty/long-string-escapes.ttl b/test/pretty/long-string-escapes.ttl new file mode 100644 index 00000000..435e3d1a --- /dev/null +++ b/test/pretty/long-string-escapes.ttl @@ -0,0 +1,4 @@ +@prefix eg: . + +eg:s + eg:p """\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b \u000B \u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"'\\\u007F""" . diff --git a/test/pretty/long-string-quotes.ttl b/test/pretty/long-string-quotes.ttl new file mode 100644 index 00000000..aceaecda --- /dev/null +++ b/test/pretty/long-string-quotes.ttl @@ -0,0 +1,5 @@ +@prefix eg: . + +eg:s + eg:p1 """Unescaped "quotes" within""" ; + eg:p2 """Trickier ""\"case""\"""" . diff --git a/test/pretty/manifest.ttl b/test/pretty/manifest.ttl new file mode 100644 index 00000000..a78aa943 --- /dev/null +++ b/test/pretty/manifest.ttl @@ -0,0 +1,171 @@ +@prefix mf: . +@prefix rdfs: . +@prefix rdft: . +@prefix serd: . + +<> + a mf:Manifest ; + rdfs:comment "Serd pretty-printing test suite" ; + mf:entries ( + <#abbreviation> + <#anonymous-in-list-object> + <#anonymous-object> + <#anonymous-subject> + <#anonymous-subject-and-object> + <#datatypes> + <#empty-anonymous-object> + <#empty-list-object> + <#empty-list-subject> + <#empty-list-subject-and-object> + <#ext-named-blank> + <#graph-abbreviation> + <#langtags> + <#list-in-object> + <#list-object> + <#local-name-escapes> + <#long-string-escapes> + <#long-string-quotes> + <#many-objects> + <#named-graph> + <#nested-list-object> + <#short-string-escapes> + <#uri-escapes> + ) . + +<#abbreviation> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "abbreviation" ; + mf:result . + +<#anonymous-in-list-object> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "anonymous-in-list-object" ; + mf:result . + +<#anonymous-object> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "anonymous-object" ; + mf:result . + +<#anonymous-subject> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "anonymous-subject" ; + mf:result . + +<#anonymous-subject-and-object> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "anonymous-subject-and-object" ; + mf:result . + +<#datatypes> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "datatypes" ; + mf:result . + +<#empty-anonymous-object> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "empty-anonymous-object" ; + mf:result . + +<#empty-list-object> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "empty-list-object" ; + mf:result . + +<#empty-list-subject> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "empty-list-subject" ; + mf:result . + +<#empty-list-subject-and-object> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "empty-list-subject-and-object" ; + mf:result . + +<#ext-named-blank> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "ext-named-blank" ; + mf:result . + +<#graph-abbreviation> + a rdft:TestTrigEval ; + mf:action ; + mf:name "graph-abbreviation" ; + mf:result . + +<#langtags> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "langtags" ; + mf:result . + +<#list-in-object> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "list-in-object" ; + mf:result . + +<#list-object> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "list-object" ; + mf:result . + +<#local-name-escapes> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "local-name-escapes" ; + mf:result . + +<#long-string-escapes> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "long-string-escapes" ; + mf:result . + +<#long-string-quotes> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "long-string-quotes" ; + mf:result . + +<#many-objects> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "many-objects" ; + mf:result . + +<#named-graph> + a rdft:TestTrigEval ; + mf:action ; + mf:name "named-graph" ; + mf:result . + +<#nested-list-object> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "nested-list-object" ; + mf:result . + +<#short-string-escapes> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "short-string-escapes" ; + mf:result . + +<#uri-escapes> + a rdft:TestTurtleEval ; + mf:action ; + mf:name "uri-escapes" ; + mf:result . diff --git a/test/pretty/many-objects.ttl b/test/pretty/many-objects.ttl new file mode 100644 index 00000000..534741dd --- /dev/null +++ b/test/pretty/many-objects.ttl @@ -0,0 +1,9 @@ +@prefix eg: . + +eg:s + eg:p1 "apple" , + "banana" , + "cherry" ; + eg:p2 "asparagus" , + "beet" , + "carrot" . diff --git a/test/pretty/named-graph.trig b/test/pretty/named-graph.trig new file mode 100644 index 00000000..5cd12f3b --- /dev/null +++ b/test/pretty/named-graph.trig @@ -0,0 +1,8 @@ +@prefix eg: . + +eg:g { + eg:s + eg:p [ + a eg:Object + ] . +} diff --git a/test/pretty/nested-list-object.ttl b/test/pretty/nested-list-object.ttl new file mode 100644 index 00000000..d2177ba3 --- /dev/null +++ b/test/pretty/nested-list-object.ttl @@ -0,0 +1,12 @@ +@prefix eg: . + +eg:s + eg:list ( + ( + eg:l1e1 + eg:l1e2 + ) ( + eg:l2e1 + eg:l2e2 + ) + ) . diff --git a/test/pretty/short-string-escapes.ttl b/test/pretty/short-string-escapes.ttl new file mode 100644 index 00000000..0665e814 --- /dev/null +++ b/test/pretty/short-string-escapes.ttl @@ -0,0 +1,4 @@ +@prefix eg: . + +eg:s + eg:p "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u000B\f\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\\\u007F" . diff --git a/test/pretty/uri-escapes.ttl b/test/pretty/uri-escapes.ttl new file mode 100644 index 00000000..09ced38a --- /dev/null +++ b/test/pretty/uri-escapes.ttl @@ -0,0 +1,2 @@ + + . diff --git a/test/run_suite.py b/test/run_suite.py new file mode 100755 index 00000000..423397b5 --- /dev/null +++ b/test/run_suite.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 + +# Copyright 2022-2023 David Robillard +# SPDX-License-Identifier: ISC + +"""Run a "simple" one-pass RDF-based test suite for serd.""" + +import argparse +import os +import shlex +import subprocess +import sys +import tempfile + +import serd_test_util as util + +NS_MF = "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#" +NS_RDFT = "http://www.w3.org/ns/rdftest#" + +DEVNULL = subprocess.DEVNULL +PIPE = subprocess.PIPE + +TEST_TYPES = [ + NS_RDFT + "TestNQuadsPositiveSyntax", + NS_RDFT + "TestNTriplesPositiveSyntax", + NS_RDFT + "TestTrigEval", + NS_RDFT + "TestTrigNegativeEval", + NS_RDFT + "TestTrigNegativeSyntax", + NS_RDFT + "TestTrigPositiveSyntax", + NS_RDFT + "TestTurtleEval", + NS_RDFT + "TestTurtleNegativeEval", + NS_RDFT + "TestTurtleNegativeSyntax", + NS_RDFT + "TestTurtlePositiveSyntax", +] + + +def run_eval_test(base_uri, command, in_path, good_path, out_path): + """Run a positive eval test and return whether the output matches.""" + + syntax = util.syntax_from_path(out_path) + command = command + ["-o", syntax, in_path, base_uri] + + proc = subprocess.run( + command, check=True, encoding="utf-8", stderr=DEVNULL, stdout=PIPE + ) + + out = [l + "\n" for l in proc.stdout.split("\n")][:-1] + with open(good_path, "r", encoding="utf-8") as good: + return util.lines_equal(list(good), out, good_path, out_path) + + +def run_entry(entry, base_uri, command_prefix, out_dir, suite_dir): + """Run a single test entry from the manifest.""" + + in_path = util.file_path(suite_dir, entry[NS_MF + "action"][0]) + base = base_uri + os.path.basename(in_path) + + good_path = in_path + if NS_MF + "result" in entry: + good_path = util.file_path(suite_dir, entry[NS_MF + "result"][0]) + + out_path = os.path.join(out_dir, os.path.basename(good_path)) + return run_eval_test(base, command_prefix, in_path, good_path, out_path) + + +def run_suite(args, command, out_dir): + """Run all tests in the manifest.""" + + # Load manifest model + top = os.path.dirname(args.manifest) + model, instances = util.load_rdf(args.manifest, args.base_uri, command) + + # Run all the listed tests that have known types + command = command + args.arg + results = util.Results() + for klass, instances in instances.items(): + check = klass in [NS_RDFT + "TestTrigEval", NS_RDFT + "TestTurtleEval"] + if klass == NS_MF + "Manifest": + continue + + if klass not in TEST_TYPES: + raise RuntimeError("Unknown manifest entry type: " + klass) + + for instance in instances: + try: + entry = model[instance] + if check and NS_MF + "result" not in entry: + raise RuntimeError("Eval test missing result: " + instance) + + results.check( + run_entry(entry, args.base_uri, command, out_dir, top) + ) + + except subprocess.CalledProcessError as exception: + if exception.stderr is not None: + sys.stderr.write(exception.stderr) + + results.check(False, str(exception) + "\n") + + return util.print_result_summary(results) + + +def main(): + """Run the command line tool.""" + + parser = argparse.ArgumentParser( + usage="%(prog)s [OPTION]... MANIFEST BASE_URI -- [ARG]...", + description=__doc__, + ) + + parser.add_argument("--serdi", default="serdi", help="path to serdi") + parser.add_argument("--wrapper", default="", help="executable wrapper") + parser.add_argument("manifest", help="test suite manifest.ttl file") + parser.add_argument("base_uri", help="base URI for tests") + parser.add_argument("arg", nargs=argparse.REMAINDER, help="serdi argument") + + args = parser.parse_args(sys.argv[1:]) + command = shlex.split(args.wrapper) + [args.serdi] + + with tempfile.TemporaryDirectory() as temp: + return run_suite(args, command, temp) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/test/serd_test_util/__init__.py b/test/serd_test_util/__init__.py index 844c454c..7482762d 100644 --- a/test/serd_test_util/__init__.py +++ b/test/serd_test_util/__init__.py @@ -71,6 +71,25 @@ def uri_path(uri): return path if not drive else path[1:] +def file_path(suite_dir, uri): + """Return a relative path to a file in a test suite.""" + + return os.path.relpath(os.path.join(suite_dir, os.path.basename(uri))) + + +def syntax_from_path(path): + """Return the serd syntax name corresponding to a file path.""" + + extensions = { + ".ttl": "turtle", + ".nt": "ntriples", + ".trig": "trig", + ".nq": "nquads", + } + + return extensions[os.path.splitext(path)[1]] + + def earl_assertion(test, passed, asserter): """Return a Turtle description of an assertion for the test report.""" -- cgit v1.2.1