mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-12-28 10:56:36 +00:00
8cb9beb321
* lisp/json.el (json-pretty-print): Force distinction between empty objects and null. (json-encode-list): Remove responsibility to print "null" as this value is not a list. (json-encode): Give higher precedence to lists so that an empty list is printed as an empty object, not as "null". * test/lisp/json-tests.el (test-json-encode): Add many tests to check the behavior of pretty-printing.
397 lines
14 KiB
EmacsLisp
397 lines
14 KiB
EmacsLisp
;;; json-tests.el --- Test suite for json.el
|
||
|
||
;; Copyright (C) 2015-2018 Free Software Foundation, Inc.
|
||
|
||
;; Author: Dmitry Gutov <dgutov@yandex.ru>
|
||
|
||
;; This program is free software; you can redistribute it and/or modify
|
||
;; it under the terms of the GNU General Public License as published by
|
||
;; the Free Software Foundation, either version 3 of the License, or
|
||
;; (at your option) any later version.
|
||
|
||
;; This program is distributed in the hope that it will be useful,
|
||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
;; GNU General Public License for more details.
|
||
|
||
;; You should have received a copy of the GNU General Public License
|
||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
||
;;; Code:
|
||
|
||
(require 'ert)
|
||
(require 'json)
|
||
|
||
(defmacro json-tests--with-temp-buffer (content &rest body)
|
||
"Create a temporary buffer with CONTENT and evaluate BODY there.
|
||
Point is moved to beginning of the buffer."
|
||
(declare (indent 1))
|
||
`(with-temp-buffer
|
||
(insert ,content)
|
||
(goto-char (point-min))
|
||
,@body))
|
||
|
||
;;; Utilities
|
||
|
||
(ert-deftest test-json-join ()
|
||
(should (equal (json-join '() ", ") ""))
|
||
(should (equal (json-join '("a" "b" "c") ", ") "a, b, c")))
|
||
|
||
(ert-deftest test-json-alist-p ()
|
||
(should (json-alist-p '()))
|
||
(should (json-alist-p '((a 1) (b 2) (c 3))))
|
||
(should (json-alist-p '((:a 1) (:b 2) (:c 3))))
|
||
(should (json-alist-p '(("a" 1) ("b" 2) ("c" 3))))
|
||
(should-not (json-alist-p '(:a :b :c)))
|
||
(should-not (json-alist-p '(:a 1 :b 2 :c 3)))
|
||
(should-not (json-alist-p '((:a 1) (:b 2) 3))))
|
||
|
||
(ert-deftest test-json-plist-p ()
|
||
(should (json-plist-p '()))
|
||
(should (json-plist-p '(:a 1 :b 2 :c 3)))
|
||
(should-not (json-plist-p '(a 1 b 2 c 3)))
|
||
(should-not (json-plist-p '("a" 1 "b" 2 "c" 3)))
|
||
(should-not (json-plist-p '(:a :b :c)))
|
||
(should-not (json-plist-p '((:a 1) (:b 2) (:c 3)))))
|
||
|
||
(ert-deftest test-json-plist-reverse ()
|
||
(should (equal (json--plist-reverse '()) '()))
|
||
(should (equal (json--plist-reverse '(:a 1)) '(:a 1)))
|
||
(should (equal (json--plist-reverse '(:a 1 :b 2 :c 3))
|
||
'(:c 3 :b 2 :a 1))))
|
||
|
||
(ert-deftest test-json-plist-to-alist ()
|
||
(should (equal (json--plist-to-alist '()) '()))
|
||
(should (equal (json--plist-to-alist '(:a 1)) '((:a . 1))))
|
||
(should (equal (json--plist-to-alist '(:a 1 :b 2 :c 3))
|
||
'((:a . 1) (:b . 2) (:c . 3)))))
|
||
|
||
(ert-deftest test-json-advance ()
|
||
(json-tests--with-temp-buffer "{ \"a\": 1 }"
|
||
(json-advance 0)
|
||
(should (= (point) (point-min)))
|
||
(json-advance 3)
|
||
(should (= (point) (+ (point-min) 3)))))
|
||
|
||
(ert-deftest test-json-peek ()
|
||
(json-tests--with-temp-buffer ""
|
||
(should (zerop (json-peek))))
|
||
(json-tests--with-temp-buffer "{ \"a\": 1 }"
|
||
(should (equal (json-peek) ?{))))
|
||
|
||
(ert-deftest test-json-pop ()
|
||
(json-tests--with-temp-buffer ""
|
||
(should-error (json-pop) :type 'json-end-of-file))
|
||
(json-tests--with-temp-buffer "{ \"a\": 1 }"
|
||
(should (equal (json-pop) ?{))
|
||
(should (= (point) (+ (point-min) 1)))))
|
||
|
||
(ert-deftest test-json-skip-whitespace ()
|
||
(json-tests--with-temp-buffer "\t\r\n\f\b { \"a\": 1 }"
|
||
(json-skip-whitespace)
|
||
(should (equal (char-after) ?\f)))
|
||
(json-tests--with-temp-buffer "\t\r\n\t { \"a\": 1 }"
|
||
(json-skip-whitespace)
|
||
(should (equal (char-after) ?{))))
|
||
|
||
;;; Paths
|
||
|
||
(ert-deftest test-json-path-to-position-with-objects ()
|
||
(let* ((json-string "{\"foo\": {\"bar\": {\"baz\": \"value\"}}}")
|
||
(matched-path (json-path-to-position 32 json-string)))
|
||
(should (equal (plist-get matched-path :path) '("foo" "bar" "baz")))
|
||
(should (equal (plist-get matched-path :match-start) 25))
|
||
(should (equal (plist-get matched-path :match-end) 32))))
|
||
|
||
(ert-deftest test-json-path-to-position-with-arrays ()
|
||
(let* ((json-string "{\"foo\": [\"bar\", [\"baz\"]]}")
|
||
(matched-path (json-path-to-position 20 json-string)))
|
||
(should (equal (plist-get matched-path :path) '("foo" 1 0)))
|
||
(should (equal (plist-get matched-path :match-start) 18))
|
||
(should (equal (plist-get matched-path :match-end) 23))))
|
||
|
||
(ert-deftest test-json-path-to-position-no-match ()
|
||
(let* ((json-string "{\"foo\": {\"bar\": \"baz\"}}")
|
||
(matched-path (json-path-to-position 5 json-string)))
|
||
(should (null matched-path))))
|
||
|
||
;;; Keywords
|
||
|
||
(ert-deftest test-json-read-keyword ()
|
||
(json-tests--with-temp-buffer "true"
|
||
(should (json-read-keyword "true")))
|
||
(json-tests--with-temp-buffer "true"
|
||
(should-error
|
||
(json-read-keyword "false") :type 'json-unknown-keyword))
|
||
(json-tests--with-temp-buffer "foo"
|
||
(should-error
|
||
(json-read-keyword "foo") :type 'json-unknown-keyword)))
|
||
|
||
(ert-deftest test-json-encode-keyword ()
|
||
(should (equal (json-encode-keyword t) "true"))
|
||
(should (equal (json-encode-keyword json-false) "false"))
|
||
(should (equal (json-encode-keyword json-null) "null")))
|
||
|
||
;;; Numbers
|
||
|
||
(ert-deftest test-json-read-number ()
|
||
(json-tests--with-temp-buffer "3"
|
||
(should (= (json-read-number) 3)))
|
||
(json-tests--with-temp-buffer "-5"
|
||
(should (= (json-read-number) -5)))
|
||
(json-tests--with-temp-buffer "123.456"
|
||
(should (= (json-read-number) 123.456)))
|
||
(json-tests--with-temp-buffer "1e3"
|
||
(should (= (json-read-number) 1e3)))
|
||
(json-tests--with-temp-buffer "2e+3"
|
||
(should (= (json-read-number) 2e3)))
|
||
(json-tests--with-temp-buffer "3E3"
|
||
(should (= (json-read-number) 3e3)))
|
||
(json-tests--with-temp-buffer "1e-7"
|
||
(should (= (json-read-number) 1e-7)))
|
||
(json-tests--with-temp-buffer "abc"
|
||
(should-error (json-read-number) :type 'json-number-format)))
|
||
|
||
(ert-deftest test-json-encode-number ()
|
||
(should (equal (json-encode-number 3) "3"))
|
||
(should (equal (json-encode-number -5) "-5"))
|
||
(should (equal (json-encode-number 123.456) "123.456")))
|
||
|
||
;; Strings
|
||
|
||
(ert-deftest test-json-read-escaped-char ()
|
||
(json-tests--with-temp-buffer "\\\""
|
||
(should (equal (json-read-escaped-char) ?\"))))
|
||
|
||
(ert-deftest test-json-read-string ()
|
||
(json-tests--with-temp-buffer "\"formfeed\f\""
|
||
(should-error (json-read-string) :type 'json-string-format))
|
||
(json-tests--with-temp-buffer "\"foo \\\"bar\\\"\""
|
||
(should (equal (json-read-string) "foo \"bar\"")))
|
||
(json-tests--with-temp-buffer "\"abcαβγ\""
|
||
(should (equal (json-read-string) "abcαβγ")))
|
||
(json-tests--with-temp-buffer "\"\\nasd\\u0444\\u044b\\u0432fgh\\t\""
|
||
(should (equal (json-read-string) "\nasdфывfgh\t")))
|
||
;; Bug#24784
|
||
(json-tests--with-temp-buffer "\"\\uD834\\uDD1E\""
|
||
(should (equal (json-read-string) "\U0001D11E")))
|
||
(json-tests--with-temp-buffer "foo"
|
||
(should-error (json-read-string) :type 'json-string-format)))
|
||
|
||
(ert-deftest test-json-encode-string ()
|
||
(should (equal (json-encode-string "foo") "\"foo\""))
|
||
(should (equal (json-encode-string "a\n\fb") "\"a\\n\\fb\""))
|
||
(should (equal (json-encode-string "\nasdфыв\u001f\u007ffgh\t")
|
||
"\"\\nasdфыв\\u001f\u007ffgh\\t\"")))
|
||
|
||
(ert-deftest test-json-encode-key ()
|
||
(should (equal (json-encode-key "foo") "\"foo\""))
|
||
(should (equal (json-encode-key 'foo) "\"foo\""))
|
||
(should (equal (json-encode-key :foo) "\"foo\""))
|
||
(should-error (json-encode-key 5) :type 'json-key-format)
|
||
(should-error (json-encode-key ["foo"]) :type 'json-key-format)
|
||
(should-error (json-encode-key '("foo")) :type 'json-key-format))
|
||
|
||
;;; Objects
|
||
|
||
(ert-deftest test-json-new-object ()
|
||
(let ((json-object-type 'alist))
|
||
(should (equal (json-new-object) '())))
|
||
(let ((json-object-type 'plist))
|
||
(should (equal (json-new-object) '())))
|
||
(let* ((json-object-type 'hash-table)
|
||
(json-object (json-new-object)))
|
||
(should (hash-table-p json-object))
|
||
(should (= (hash-table-count json-object) 0))))
|
||
|
||
(ert-deftest test-json-add-to-object ()
|
||
(let* ((json-object-type 'alist)
|
||
(json-key-type nil)
|
||
(obj (json-new-object)))
|
||
(setq obj (json-add-to-object obj "a" 1))
|
||
(setq obj (json-add-to-object obj "b" 2))
|
||
(should (equal (assq 'a obj) '(a . 1)))
|
||
(should (equal (assq 'b obj) '(b . 2))))
|
||
(let* ((json-object-type 'plist)
|
||
(json-key-type nil)
|
||
(obj (json-new-object)))
|
||
(setq obj (json-add-to-object obj "a" 1))
|
||
(setq obj (json-add-to-object obj "b" 2))
|
||
(should (= (plist-get obj :a) 1))
|
||
(should (= (plist-get obj :b) 2)))
|
||
(let* ((json-object-type 'hash-table)
|
||
(json-key-type nil)
|
||
(obj (json-new-object)))
|
||
(setq obj (json-add-to-object obj "a" 1))
|
||
(setq obj (json-add-to-object obj "b" 2))
|
||
(should (= (gethash "a" obj) 1))
|
||
(should (= (gethash "b" obj) 2))))
|
||
|
||
(ert-deftest test-json-read-object ()
|
||
(json-tests--with-temp-buffer "{ \"a\": 1, \"b\": 2 }"
|
||
(let ((json-object-type 'alist))
|
||
(should (equal (json-read-object) '((a . 1) (b . 2))))))
|
||
(json-tests--with-temp-buffer "{ \"a\": 1, \"b\": 2 }"
|
||
(let ((json-object-type 'plist))
|
||
(should (equal (json-read-object) '(:a 1 :b 2)))))
|
||
(json-tests--with-temp-buffer "{ \"a\": 1, \"b\": 2 }"
|
||
(let* ((json-object-type 'hash-table)
|
||
(hash-table (json-read-object)))
|
||
(should (= (gethash "a" hash-table) 1))
|
||
(should (= (gethash "b" hash-table) 2))))
|
||
(json-tests--with-temp-buffer "{ \"a\": 1 \"b\": 2 }"
|
||
(should-error (json-read-object) :type 'json-object-format)))
|
||
|
||
(ert-deftest test-json-encode-hash-table ()
|
||
(let ((hash-table (make-hash-table))
|
||
(json-encoding-object-sort-predicate 'string<)
|
||
(json-encoding-pretty-print nil))
|
||
(puthash :a 1 hash-table)
|
||
(puthash :b 2 hash-table)
|
||
(puthash :c 3 hash-table)
|
||
(should (equal (json-encode hash-table)
|
||
"{\"a\":1,\"b\":2,\"c\":3}"))))
|
||
|
||
(ert-deftest json-encode-simple-alist ()
|
||
(let ((json-encoding-pretty-print nil))
|
||
(should (equal (json-encode '((a . 1) (b . 2)))
|
||
"{\"a\":1,\"b\":2}"))))
|
||
|
||
(ert-deftest test-json-encode-plist ()
|
||
(let ((plist '(:a 1 :b 2))
|
||
(json-encoding-pretty-print nil))
|
||
(should (equal (json-encode plist) "{\"a\":1,\"b\":2}"))))
|
||
|
||
(ert-deftest test-json-encode-plist-with-sort-predicate ()
|
||
(let ((plist '(:c 3 :a 1 :b 2))
|
||
(json-encoding-object-sort-predicate 'string<)
|
||
(json-encoding-pretty-print nil))
|
||
(should (equal (json-encode plist) "{\"a\":1,\"b\":2,\"c\":3}"))))
|
||
|
||
(ert-deftest test-json-encode-alist-with-sort-predicate ()
|
||
(let ((alist '((:c . 3) (:a . 1) (:b . 2)))
|
||
(json-encoding-object-sort-predicate 'string<)
|
||
(json-encoding-pretty-print nil))
|
||
(should (equal (json-encode alist) "{\"a\":1,\"b\":2,\"c\":3}"))))
|
||
|
||
(ert-deftest test-json-encode-list ()
|
||
(let ((json-encoding-pretty-print nil))
|
||
(should (equal (json-encode-list '(:a 1 :b 2))
|
||
"{\"a\":1,\"b\":2}"))
|
||
(should (equal (json-encode-list '((:a . 1) (:b . 2)))
|
||
"{\"a\":1,\"b\":2}"))
|
||
(should (equal (json-encode-list '(1 2 3 4)) "[1,2,3,4]"))))
|
||
|
||
;;; Arrays
|
||
|
||
(ert-deftest test-json-read-array ()
|
||
(let ((json-array-type 'vector))
|
||
(json-tests--with-temp-buffer "[1, 2, \"a\", \"b\"]"
|
||
(should (equal (json-read-array) [1 2 "a" "b"]))))
|
||
(let ((json-array-type 'list))
|
||
(json-tests--with-temp-buffer "[1, 2, \"a\", \"b\"]"
|
||
(should (equal (json-read-array) '(1 2 "a" "b")))))
|
||
(json-tests--with-temp-buffer "[1 2]"
|
||
(should-error (json-read-array) :type 'json-error)))
|
||
|
||
(ert-deftest test-json-encode-array ()
|
||
(let ((json-encoding-pretty-print nil))
|
||
(should (equal (json-encode-array [1 2 "a" "b"])
|
||
"[1,2,\"a\",\"b\"]"))))
|
||
|
||
;;; Reader
|
||
|
||
(ert-deftest test-json-read ()
|
||
(json-tests--with-temp-buffer "{ \"a\": 1 }"
|
||
;; We don't care exactly what the return value is (that is tested
|
||
;; in `test-json-read-object'), but it should parse without error.
|
||
(should (json-read)))
|
||
(json-tests--with-temp-buffer ""
|
||
(should-error (json-read) :type 'json-end-of-file))
|
||
(json-tests--with-temp-buffer "xxx"
|
||
(let ((err (should-error (json-read) :type 'json-readtable-error)))
|
||
(should (equal (cdr err) '(?x))))))
|
||
|
||
(ert-deftest test-json-read-from-string ()
|
||
(let ((json-string "{ \"a\": 1 }"))
|
||
(json-tests--with-temp-buffer json-string
|
||
(should (equal (json-read-from-string json-string)
|
||
(json-read))))))
|
||
|
||
;;; JSON encoder
|
||
|
||
(ert-deftest test-json-encode ()
|
||
(should (equal (json-encode "foo") "\"foo\""))
|
||
(with-temp-buffer
|
||
(should-error (json-encode (current-buffer)) :type 'json-error)))
|
||
|
||
;;; Pretty-print
|
||
|
||
(defun json-tests-equal-pretty-print (original &optional expected)
|
||
"Abort current test if pretty-printing ORIGINAL does not yield EXPECTED.
|
||
|
||
Both ORIGINAL and EXPECTED should be strings. If EXPECTED is
|
||
nil, ORIGINAL should stay unchanged by pretty-printing."
|
||
(with-temp-buffer
|
||
(insert original)
|
||
(json-pretty-print-buffer)
|
||
(should (equal (buffer-string) (or expected original)))))
|
||
|
||
(ert-deftest test-json-pretty-print-string ()
|
||
(json-tests-equal-pretty-print "\"\"")
|
||
(json-tests-equal-pretty-print "\"foo\""))
|
||
|
||
(ert-deftest test-json-pretty-print-atom ()
|
||
(json-tests-equal-pretty-print "true")
|
||
(json-tests-equal-pretty-print "false")
|
||
(json-tests-equal-pretty-print "null"))
|
||
|
||
(ert-deftest test-json-pretty-print-number ()
|
||
(json-tests-equal-pretty-print "123")
|
||
(json-tests-equal-pretty-print "0.123"))
|
||
|
||
(ert-deftest test-json-pretty-print-object ()
|
||
;; empty (regression test for bug#24252)
|
||
(json-tests-equal-pretty-print
|
||
"{}"
|
||
"{\n}")
|
||
;; one pair
|
||
(json-tests-equal-pretty-print
|
||
"{\"key\":1}"
|
||
"{\n \"key\": 1\n}")
|
||
;; two pairs
|
||
(json-tests-equal-pretty-print
|
||
"{\"key1\":1,\"key2\":2}"
|
||
"{\n \"key1\": 1,\n \"key2\": 2\n}")
|
||
;; embedded object
|
||
(json-tests-equal-pretty-print
|
||
"{\"foo\":{\"key\":1}}"
|
||
"{\n \"foo\": {\n \"key\": 1\n }\n}")
|
||
;; embedded array
|
||
(json-tests-equal-pretty-print
|
||
"{\"key\":[1,2]}"
|
||
"{\n \"key\": [\n 1,\n 2\n ]\n}"))
|
||
|
||
(ert-deftest test-json-pretty-print-array ()
|
||
;; empty
|
||
(json-tests-equal-pretty-print "[]")
|
||
;; one item
|
||
(json-tests-equal-pretty-print
|
||
"[1]"
|
||
"[\n 1\n]")
|
||
;; two items
|
||
(json-tests-equal-pretty-print
|
||
"[1,2]"
|
||
"[\n 1,\n 2\n]")
|
||
;; embedded object
|
||
(json-tests-equal-pretty-print
|
||
"[{\"key\":1}]"
|
||
"[\n {\n \"key\": 1\n }\n]")
|
||
;; embedded array
|
||
(json-tests-equal-pretty-print
|
||
"[[1,2]]"
|
||
"[\n [\n 1,\n 2\n ]\n]"))
|
||
|
||
(provide 'json-tests)
|
||
;;; json-tests.el ends here
|