guile-jsonld/test-modules/online.scm

online.scm

1
;;;; Copyright (C) 2019, 2020 Julien Lepiller <julien@lepiller.eu>
2
;;;; 
3
;;;; This library is free software; you can redistribute it and/or
4
;;;; modify it under the terms of the GNU Lesser General Public
5
;;;; License as published by the Free Software Foundation; either
6
;;;; version 3 of the License, or (at your option) any later version.
7
;;;; 
8
;;;; This library is distributed in the hope that it will be useful,
9
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
10
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11
;;;; Lesser General Public License for more details.
12
;;;; 
13
;;;; You should have received a copy of the GNU Lesser General Public
14
;;;; License along with this library; if not, write to the Free Software
15
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
;;;; 
17
18
(define-module (test-modules online)
19
  #:use-module (ice-9 match)
20
  #:use-module (srfi srfi-1)
21
  #:use-module (srfi srfi-9)
22
  #:use-module (test-modules download)
23
  #:use-module (test-modules result)
24
  #:use-module (test-modules testsuite)
25
  #:use-module (jsonld download)
26
  #:use-module (jsonld json)
27
  #:use-module (jsonld)
28
  #:use-module (jsonld options)
29
  #:use-module (jsonld iri)
30
  #:use-module (rdf rdf)
31
  #:use-module (nquads fromrdf)
32
  #:export (run-test-suite
33
            run-test-suites
34
            get-test-doc))
35
36
;; The JsonLD specification comes with a test suite.  It consists in on JsonLD
37
;; document per API function.  The test suite is designed for the latest
38
;; version of the API, but can be used for older versions.  Tests for newer
39
;; versions or specific to older versions are clearly marked, so we can
40
;; select them.
41
;; Each JsonLD document is a manifest listing a huge list of tests that require
42
;; us to download a few other JsonLD documents, run a function on them and compare
43
;; the result.  This is what this file does.
44
45
(define (download-document url)
46
  (match (car (reverse (string-split url #\.)))
47
    ("nq" (download-nq url))
48
    (_ (json-document-document (download-json url)))))
49
50
(define (good-result? result expected)
51
  (if (rdf-dataset? result)
52
      (begin
53
        (with-output-to-file "test-result.nq"
54
          (lambda _
55
            (format #t "~a~%" (rdf->nquads result))))
56
        (pk 'result result)
57
        (pk 'expected expected)
58
        (rdf-dataset-isomorphic? result expected))
59
      (same-json? result expected)))
60
61
(define (execute-test test)
62
  "Execute one test described by a Json object @var{test}.  Return a Json object
63
as the output of the test, or throws an exception if something went wrong."
64
  (let* ((document (test-case-document test))
65
         (input (assoc-ref document "input"))
66
         (context (assoc-ref document "context"))
67
         (type (array->list (assoc-ref document "@type")))
68
         (options (assoc-ref document "option"))
69
         (requires (assoc-ref document "requires"))
70
         (spec-version (assoc-ref options "specVersion"))
71
         (document-loader (get-test-document-loader options)))
72
    (cond
73
      ((equal? spec-version "json-ld-1.0")
74
       (throw 'unsupported-spec 1.0))
75
      ((member "jld:CompactTest" type)
76
       (compact (string-append jsonld-test-url input)
77
                (string-append jsonld-test-url context)
78
                #:options
79
                (apply
80
                  new-jsonld-options
81
                  #:ordered? #t
82
                  #:document-loader document-loader
83
                  `(,@(if (assoc-ref options "base")
84
                          `(#:base ,(assoc-ref options "base"))
85
                          '())
86
                    ,@(if (assoc-ref options "processingMode")
87
                          `(#:processing-mode ,(assoc-ref options "processingMode"))
88
                          '())
89
                    ,@(if (assoc-ref options "extractAllScripts")
90
                          `(#:extract-all-scripts?
91
                            ,(assoc-ref options "extractAllScripts"))
92
                          '())
93
                    ,@(if (json-has-key? options "compactArrays")
94
                          `(#:compact-arrays? ,(assoc-ref options "compactArrays"))
95
                          '())))))
96
      ((member "jld:ExpandTest" type)
97
       (expand (string-append jsonld-test-url input)
98
               #:options
99
               (apply
100
                 new-jsonld-options
101
                 #:ordered? #t
102
                 #:document-loader document-loader
103
                 `(,@(if (assoc-ref options "base")
104
                         `(#:base ,(assoc-ref options "base"))
105
                         '())
106
                   ,@(if (assoc-ref options "extractAllScripts")
107
                         `(#:extract-all-scripts?
108
                           ,(assoc-ref options "extractAllScripts"))
109
                         '())
110
                   ,@(if (assoc-ref options "processingMode")
111
                         `(#:processing-mode ,(assoc-ref options "processingMode"))
112
                         '())
113
                   ,@(if (assoc-ref options "expandContext")
114
                         `(#:expand-context ,(string-append jsonld-test-url
115
                                                            (assoc-ref options "expandContext")))
116
                         '())))))
117
      ((member "jld:FlattenTest" type)
118
       (flatten (string-append jsonld-test-url input)
119
                #:context
120
                (if context
121
                  (string-append jsonld-test-url context)
122
                  #f)
123
                #:options
124
                (apply
125
                  new-jsonld-options
126
                  #:ordered? #t
127
                  #:document-loader document-loader
128
                  `(,@(if (assoc-ref options "base")
129
                          `(#:base ,(assoc-ref options "base"))
130
                          '())
131
                    ,@(if (assoc-ref options "processingMode")
132
                          `(#:processing-mode ,(assoc-ref options "processingMode"))
133
                          '())
134
                    ,@(if (assoc-ref options "extractAllScripts")
135
                          `(#:extract-all-scripts?
136
                            ,(assoc-ref options "extractAllScripts"))
137
                          '())
138
                    ,@(if (json-has-key? options "compactArrays")
139
                          `(#:compact-arrays? ,(assoc-ref options "compactArrays"))
140
                          '())))))
141
      ((member "jld:ToRDFTest" type)
142
       (when (equal? requires "GeneralizedRdf")
143
         (throw 'cannot-run-test "Expected file is not in a valid nquads format"))
144
       (jsonld->rdf (string-append jsonld-test-url input)
145
                    #:options
146
                    (apply
147
                      new-jsonld-options
148
                      #:ordered? #t
149
                      #:document-loader document-loader
150
                      `(,@(if (assoc-ref options "base")
151
                              `(#:base ,(assoc-ref options "base"))
152
                              '())
153
                        ,@(if (assoc-ref options "processingMode")
154
                              `(#:processing-mode ,(assoc-ref options "processingMode"))
155
                              '())
156
                        ,@(if (assoc-ref options "produceGeneralizedRdf")
157
                              `(#:produce-generalized-rdf? #t)
158
                              `(#:produce-generalized-rdf? #f))
159
                        ,@(if (assoc-ref options "expandContext")
160
                              `(#:expand-context ,(string-append
161
                                                    jsonld-test-url
162
                                                    (assoc-ref options "expandContext")))
163
                              '())
164
                        ,@(if (assoc-ref options "rdfDirection")
165
                              `(#:rdf-direction
166
                                ,(assoc-ref options "rdfDirection"))
167
                              '())))))
168
      (else (throw 'unrecognized-test type)))))
169
170
(define (run-test test)
171
  "Run one test described by the Json object @var{test}, whose @var{id} is
172
an identifier.  @var{id} is used as the test number for the test driver.  Return
173
is undefined.  As a side-effect, outputs one line on the standard output to
174
report the test status."
175
  (let* ((document (test-case-document test))
176
         (description (or (assoc-ref document "purpose") (test-case-name test)))
177
         (description (string-append (test-case-id test) ": " description))
178
         (expect (assoc-ref document "expect"))
179
         (expect-error (assoc-ref document "expectErrorCode"))
180
         (type (array->list (assoc-ref document "@type")))
181
         (option (assoc-ref document "option"))
182
         (result
183
           (catch #t
184
             (lambda ()
185
               (execute-test test))
186
             (lambda (key . value)
187
               (cons key value)))))
188
    (if (member "jld:NegativeEvaluationTest" type)
189
      (match result
190
        (((? symbol? key) . value)
191
         (cond
192
           ((equal? key 'unrecognized-test)
193
            (update-test-case test
194
              #:result 'skip
195
              #:reason "unrecognized test type"))
196
           ((equal? key 'unsupported-spec)
197
            (update-test-case test
198
              #:result 'skip
199
              #:reason "unsupported specification version"))
200
           ((equal? key 'unsupported-version-test)
201
            (update-test-case test
202
              #:result 'skip
203
              #:reason "unsupported JsonLD version"))
204
           ((equal? key 'cannot-run-test)
205
            (update-test-case test
206
              #:result 'skip
207
              #:reason (format #f "impossible to run test: ~a" value)))
208
           ((equal? (jsonld-error->string key) expect-error)
209
            (update-test-case test #:result 'pass))
210
           (else
211
             (update-test-case test
212
               #:result 'fail
213
               #:reason (format #f "Expected ~a but got ~a: ~a"
214
                                expect-error key value)))))
215
        (_ (begin
216
             (update-test-case test
217
               #:result 'fail
218
               #:reason (format #f "Expected ~a but got success"
219
                                expect-error)))))
220
      (match result
221
        (((? symbol? key) . value)
222
         (cond
223
           ((equal? key 'unrecognized-test)
224
            (update-test-case test
225
              #:result 'skip
226
              #:reason "unrecognized test type"))
227
           ((equal? key 'unsupported-spec)
228
            (update-test-case test
229
              #:result 'skip
230
              #:reason "unsupported specification version"))
231
           ((equal? key 'unsupported-version-test)
232
            (update-test-case test
233
              #:result 'skip
234
              #:reason "unsupported JsonLD version"))
235
           ((equal? key 'cannot-run-test)
236
            (update-test-case test
237
              #:result 'skip
238
              #:reason (format #f "impossible to run test: ~a" value)))
239
           (else
240
            (update-test-case test
241
              #:result 'fail
242
              #:reason (format #f "Expected success but got ~a: ~a"
243
                               key value)))))
244
        (_ (if (member "jld:PositiveSyntaxTest" type)
245
               (update-test-case test #:result 'pass)
246
               (let ((expected (download-document
247
                                 (string-append jsonld-test-url expect))))
248
                 (if (good-result? result expected)
249
                     (update-test-case test #:result 'pass)
250
                     (update-test-case test
251
                       #:result 'fail
252
                       #:reason (format #f "Expected ~a but got ~a"
253
                                        expected result))))))))))
254
255
(define (run-tests tests expected-failures driver)
256
  "Run all the tests of the @var{tests} test suite, using identifiers starting
257
from @var{id}.  Return is undefined."
258
  (fold
259
    (lambda (test results)
260
      (let* ((result (run-test test))
261
             (result
262
               (if (assoc-ref expected-failures (test-case-id test))
263
                   (update-test-case result
264
                     #:result (cond
265
                                ((equal? 'skip (test-case-result result))
266
                                 'skip)
267
                                ((equal? 'fail (test-case-result result))
268
                                 'xfail)
269
                                ((equal? 'pass (test-case-result result))
270
                                 'xpass))
271
                     #:reason (assoc-ref expected-failures (test-case-id test)))
272
                   result)))
273
        ((test-driver-print driver) result)
274
        (cons result results)))
275
    '()
276
    tests))
277
278
(define (get-test-doc url)
279
  "Get a test suite object from the manifest at @var{url}."
280
  (assoc-ref (json-document-document (download-json url)) "sequence"))
281
282
(define* (get-test-plan url #:key (num 1))
283
  (define document (get-test-doc url))
284
285
  (cdr
286
    (fold
287
      (lambda (test result)
288
        (let* ((num (car result))
289
               (result (cdr result))
290
               (name (assoc-ref test "name"))
291
               (@id (assoc-ref test "@id"))
292
               (name (string-append @id ": " name))
293
               (context (string-join
294
                          (reverse (cdr (reverse (string-split url #\.))))
295
                          "."))
296
               (description (or (assoc-ref test "purpose") name))
297
               (description (string-append @id ": " description)))
298
          (cons (+ 1 num)
299
                (cons (make-test-case (string-append context @id)
300
                                      num name description test #f #f)
301
                      result))))
302
      `(,num . ())
303
      (array->list document))))
304
305
(define (run-test-suite manifest expected-failures driver)
306
  "Run a test suite described by @var{manifest}."
307
  (let* ((plan (reverse (get-test-plan manifest))))
308
    ((test-driver-init driver) plan)
309
    ((test-driver-finalize driver) (run-tests plan expected-failures driver))))
310
311
(define (run-test-suites manifests expected-failures driver)
312
  "Run multiple test suites described by @var{manifests}."
313
  (let* ((plan
314
           (fold
315
             (lambda (manifest plan)
316
               (append plan (reverse (get-test-plan
317
                                       manifest #:num (+ (length plan) 1)))))
318
             '()
319
             manifests)))
320
    ((test-driver-init driver) plan)
321
    ((test-driver-finalize driver) (run-tests plan expected-failures driver))))
322