diff options
Diffstat (limited to 'tests/auto/corelib/serialization/json')
40 files changed, 1493 insertions, 414 deletions
diff --git a/tests/auto/corelib/serialization/json/CMakeLists.txt b/tests/auto/corelib/serialization/json/CMakeLists.txt new file mode 100644 index 0000000000..c73a99a3b8 --- /dev/null +++ b/tests/auto/corelib/serialization/json/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_json Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_json LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +set(json_resource_files + "bom.json" + "test.json" + "test2.json" + "test3.json" + "simple.duplicates.json" + "test.duplicates.json" + "test3.duplicates.json" +) + +qt_internal_add_test(tst_json + SOURCES + tst_qtjson.cpp + LIBRARIES + Qt::CorePrivate + Qt::TestPrivate + TESTDATA ${json_resource_files} +) + +qt_internal_extend_target(tst_json CONDITION NOT QT_FEATURE_doubleconversion AND NOT QT_FEATURE_system_doubleconversion + DEFINES + QT_NO_DOUBLECONVERSION +) diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/10.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/10.bjson Binary files differdeleted file mode 100644 index 12b29b7aa5..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/10.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/11.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/11.bjson Binary files differdeleted file mode 100644 index cf2b612111..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/11.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/12.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/12.bjson Binary files differdeleted file mode 100644 index 9c2403350e..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/12.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/13.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/13.bjson Binary files differdeleted file mode 100644 index db6308b1fd..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/13.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/14.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/14.bjson Binary files differdeleted file mode 100644 index 347da4572c..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/14.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/15.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/15.bjson Binary files differdeleted file mode 100644 index c6c5558934..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/15.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/16.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/16.bjson Binary files differdeleted file mode 100644 index ae8b57446d..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/16.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/17.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/17.bjson Binary files differdeleted file mode 100644 index 32f0cc0e23..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/17.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/18.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/18.bjson Binary files differdeleted file mode 100644 index 50c89169eb..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/18.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/19.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/19.bjson Binary files differdeleted file mode 100644 index b922212f45..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/19.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/20.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/20.bjson Binary files differdeleted file mode 100644 index c965a0d294..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/20.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/21.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/21.bjson Binary files differdeleted file mode 100644 index 98165ee40c..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/21.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/22.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/22.bjson Binary files differdeleted file mode 100644 index 151f773a81..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/22.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/23.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/23.bjson Binary files differdeleted file mode 100644 index 6eb5269470..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/23.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/24.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/24.bjson Binary files differdeleted file mode 100644 index c55a2a3e3b..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/24.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/25.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/25.bjson Binary files differdeleted file mode 100644 index 6c619f2ae1..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/25.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/26.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/26.bjson Binary files differdeleted file mode 100644 index 3bf303215a..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/26.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/27.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/27.bjson Binary files differdeleted file mode 100644 index d2656c2287..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/27.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/28.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/28.bjson Binary files differdeleted file mode 100644 index 6797cf8c40..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/28.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/29.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/29.bjson Binary files differdeleted file mode 100644 index 0645dfc3b2..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/29.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/30.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/30.bjson Binary files differdeleted file mode 100644 index f77fe1efd0..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/30.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/31.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/31.bjson Binary files differdeleted file mode 100644 index d9840b6582..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/31.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/32.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/32.bjson Binary files differdeleted file mode 100644 index 1de4cb829f..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/32.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/33.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/33.bjson Binary files differdeleted file mode 100644 index 532a31dc08..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/33.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/34.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/34.bjson Binary files differdeleted file mode 100644 index f498558eff..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/34.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/35.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/35.bjson Binary files differdeleted file mode 100644 index 8701210755..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/35.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/36.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/36.bjson Binary files differdeleted file mode 100644 index ef5864e911..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/36.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/37.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/37.bjson Binary files differdeleted file mode 100644 index f4dd4ae12f..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/37.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/38.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/38.bjson deleted file mode 100644 index e69de29bb2..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/38.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/39.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/39.bjson Binary files differdeleted file mode 100644 index c6025aa9eb..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/39.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/40.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/40.bjson Binary files differdeleted file mode 100644 index 277096f8cb..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/40.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/invalidBinaryData/41.bjson b/tests/auto/corelib/serialization/json/invalidBinaryData/41.bjson Binary files differdeleted file mode 100644 index 0b5940ab95..0000000000 --- a/tests/auto/corelib/serialization/json/invalidBinaryData/41.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/json.pro b/tests/auto/corelib/serialization/json/json.pro deleted file mode 100644 index 8fa17c5c38..0000000000 --- a/tests/auto/corelib/serialization/json/json.pro +++ /dev/null @@ -1,12 +0,0 @@ -TARGET = tst_json -QT = core-private testlib -CONFIG += testcase - -!android:TESTDATA += bom.json test.json test.bjson test3.json test2.json - else:RESOURCES += json.qrc - -!qtConfig(doubleconversion):!qtConfig(system-doubleconversion) { - DEFINES += QT_NO_DOUBLECONVERSION -} - -SOURCES += tst_qtjson.cpp diff --git a/tests/auto/corelib/serialization/json/json.qrc b/tests/auto/corelib/serialization/json/json.qrc deleted file mode 100644 index eb122a1779..0000000000 --- a/tests/auto/corelib/serialization/json/json.qrc +++ /dev/null @@ -1,9 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource prefix="/"> - <file>bom.json</file> - <file>test2.json</file> - <file>test3.json</file> - <file>test.json</file> - <file>test.bjson</file> -</qresource> -</RCC> diff --git a/tests/auto/corelib/serialization/json/simple.duplicates.json b/tests/auto/corelib/serialization/json/simple.duplicates.json new file mode 100644 index 0000000000..6f989e8aa9 --- /dev/null +++ b/tests/auto/corelib/serialization/json/simple.duplicates.json @@ -0,0 +1 @@ +{"":{"":0},"":0} diff --git a/tests/auto/corelib/serialization/json/test.bjson b/tests/auto/corelib/serialization/json/test.bjson Binary files differdeleted file mode 100644 index 137b4dfeff..0000000000 --- a/tests/auto/corelib/serialization/json/test.bjson +++ /dev/null diff --git a/tests/auto/corelib/serialization/json/test.duplicates.json b/tests/auto/corelib/serialization/json/test.duplicates.json new file mode 100644 index 0000000000..0d5af8ef74 --- /dev/null +++ b/tests/auto/corelib/serialization/json/test.duplicates.json @@ -0,0 +1,66 @@ +[ + "JSON Test Pattern pass1", + {"a":["array with 1 element"]}, + {}, + [], + -42, + true, + false, + null, + { + "a": 1234567890, + "a": -9876.543210, + "a": 0.123456789e-12, + "a": 1.234567890E+34, + "a": 23456789012E66, + "a": 0, + "a": 1, + "a": " ", + "a": "\"", + "a": "\\", + "a": "\b\f\n\r\t", + "a": "/ & \/", + "a": "abcdefghijklmnopqrstuvwxyz", + "a": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "a": "0123456789", + "a": "digit", + "a": "`1~!@#$%^&*()_+-={':[,]}|;.</>?", + "a": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", + "a": true, + "a": false, + "a": null, + "a":[ ], + "a":{ }, + "a": "50 St. James Street", + "a": "nix", + "a": "// /* <!-- --", + "a": " ", + "a":[1,2 , 3 + +, + +4 , 5 , 6 ,7 ],"a":[1,2,3,4,5,6,7], + "a": "\"a:[\"array with 1 element\"]}", + "a": "" \u0022 %22 0x22 034 "", + "a": "A key can be any string" + }, + 0.5 ,98.6 +, +99.44 +, + +1066, +1e1, +0.1e1, +1e-1, +1e00, +2e+00, +2e-00, +"rosebud", +{"a": "bar"}, +{"a":{"a":1000,"a":{"a":"nix"}},"a":{"a":{"a":{"a":"$0.01","a":1},"a":{"a":"$4,833.99","a":483399}},"a":[{"a":"PRODUCT","a":"Silicone c","a":"Elite Hori","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":310711221747712.000000,"a":{"a":{"a":"$1.56","a":156},"a":{"a":"$29.99","a":2999},"a":14},"a":1968262863,"a":8515},{"a":"PRODUCT","a":"Nonslip Ch","a":"Specificat","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":175580930637824.000000,"a":{"a":{"a":"$0.45","a":45},"a":{"a":"$194.95","a":19495},"a":34},"a":2534935499,"a":8515},{"a":"PRODUCT","a":"Plastic Ca","a":"Descriptio","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":132488642953216.000000,"a":{"a":{"a":"$0.99","a":99},"a":{"a":"$303.68","a":30368},"a":33},"a":2305624670,"a":8515},{"a":"PRODUCT","a":"Protective","a":"Made of hi","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":108614681362432.000000,"a":{"a":{"a":"$1.70","a":170},"a":{"a":"$99.99","a":9999},"a":11},"a":2120981405,"a":8515},{"a":"PRODUCT","a":"P® 4","a":"Do more th","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":96203484168192.000000,"a":{"a":{"a":"$2.49","a":249},"a":{"a":"$79.95","a":7995},"a":16},"a":2203798762,"a":8515},{"a":"PRODUCT","a":"Case Refle","a":"NCAA iPhon","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":84727583211520.000000,"a":{"a":{"a":"$0.69","a":69},"a":{"a":"$75.52","a":7552},"a":59},"a":1114627445,"a":8515},{"a":"PRODUCT","a":"Infuse Pro","a":"Protect an","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":80831066406912.000000,"a":{"a":{"a":"$0.59","a":59},"a":{"a":"$79.00","a":7900},"a":24},"a":2557462717,"a":8515},{"a":"PRODUCT","a":"Dragonfly ","a":"d","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":70900229603328.000000,"a":{"a":{"a":"$1.05","a":105},"a":{"a":"$94.49","a":9449},"a":30},"a":2442061740,"a":8515},{"a":"PRODUCT","a":"Pho","a":"Snap on Ap","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":65194915004416.000000,"a":{"a":{"a":"$0.01","a":1},"a":{"a":"$414.99","a":41499},"a":39},"a":2004746863,"a":8515},{"a":"PRODUCT","a":"Otterbox i","a":"Your iPhon","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":61515478597632.000000,"a":{"a":{"a":"$3.28","a":328},"a":{"a":"$110.65","a":11065},"a":25},"a":2584611575,"a":8515}],"a":10,"a":2000}}, +{"a":{"a":1000,"a":{"a":"nix"}},"a":{"a":{"a":{"a":"$0.01","a":1},"a":{"a":"$4,833.99","a":483399}},"a":[{"a":"PRODUCT","a":"Silicone c","a":"Elite Hori","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":310711221747712.000000,"a":{"a":{"a":"$1.56","a":156},"a":{"a":"$29.99","a":2999},"a":14},"a":1968262863,"a":8515},{"a":"PRODUCT","a":"Nonslip Ch","a":"Specificat","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":175580930637824.000000,"a":{"a":{"a":"$0.45","a":45},"a":{"a":"$194.95","a":19495},"a":34},"a":2534935499,"a":8515},{"a":"PRODUCT","a":"Plastic Ca","a":"Descriptio","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":132488642953216.000000,"a":{"a":{"a":"$0.99","a":99},"a":{"a":"$303.68","a":30368},"a":33},"a":2305624670,"a":8515},{"a":"PRODUCT","a":"Protective","a":"Made of hi","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":108614681362432.000000,"a":{"a":{"a":"$1.70","a":170},"a":{"a":"$99.99","a":9999},"a":11},"a":2120981405,"a":8515},{"a":"PRODUCT","a":"P® 4","a":"Do more th","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":96203484168192.000000,"a":{"a":{"a":"$2.49","a":249},"a":{"a":"$79.95","a":7995},"a":16},"a":2203798762,"a":8515},{"a":"PRODUCT","a":"Case Refle","a":"NCAA iPhon","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":84727583211520.000000,"a":{"a":{"a":"$0.69","a":69},"a":{"a":"$75.52","a":7552},"a":59},"a":1114627445,"a":8515},{"a":"PRODUCT","a":"Infuse Pro","a":"Protect an","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":80831066406912.000000,"a":{"a":{"a":"$0.59","a":59},"a":{"a":"$79.00","a":7900},"a":24},"a":2557462717,"a":8515},{"a":"PRODUCT","a":"Dragonfly ","a":"d","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":70900229603328.000000,"a":{"a":{"a":"$1.05","a":105},"a":{"a":"$94.49","a":9449},"a":30},"a":2442061740,"a":8515},{"a":"PRODUCT","a":"Pho","a":"Snap on Ap","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":65194915004416.000000,"a":{"a":{"a":"$0.01","a":1},"a":{"a":"$414.99","a":41499},"a":39},"a":2004746863,"a":8515},{"a":"PRODUCT","a":"Otterbox i","a":"Your iPhon","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":61515478597632.000000,"a":{"a":{"a":"$3.28","a":328},"a":{"a":"$110.65","a":11065},"a":25},"a":2584611575,"a":8515}],"a":10,"a":2000}}, +{"a":{"a":1000,"a":{"a":"nix"}},"a":{"a":{"a":{"a":"$0.01","a":1},"a":{"a":"$4,833.99","a":483399}},"a":[{"a":"PRODUCT","a":"Silicone c","a":"Elite Hori","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":310711221747712.000000,"a":{"a":{"a":"$1.56","a":156},"a":{"a":"$29.99","a":2999},"a":14},"a":1968262863,"a":8515},{"a":"PRODUCT","a":"Nonslip Ch","a":"Specificat","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":175580930637824.000000,"a":{"a":{"a":"$0.45","a":45},"a":{"a":"$194.95","a":19495},"a":34},"a":2534935499,"a":8515},{"a":"PRODUCT","a":"Plastic Ca","a":"Descriptio","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":132488642953216.000000,"a":{"a":{"a":"$0.99","a":99},"a":{"a":"$303.68","a":30368},"a":33},"a":2305624670,"a":8515},{"a":"PRODUCT","a":"Protective","a":"Made of hi","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":108614681362432.000000,"a":{"a":{"a":"$1.70","a":170},"a":{"a":"$99.99","a":9999},"a":11},"a":2120981405,"a":8515},{"a":"PRODUCT","a":"P® 4","a":"Do more th","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":96203484168192.000000,"a":{"a":{"a":"$2.49","a":249},"a":{"a":"$79.95","a":7995},"a":16},"a":2203798762,"a":8515},{"a":"PRODUCT","a":"Case Refle","a":"NCAA iPhon","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":84727583211520.000000,"a":{"a":{"a":"$0.69","a":69},"a":{"a":"$75.52","a":7552},"a":59},"a":1114627445,"a":8515},{"a":"PRODUCT","a":"Infuse Pro","a":"Protect an","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":80831066406912.000000,"a":{"a":{"a":"$0.59","a":59},"a":{"a":"$79.00","a":7900},"a":24},"a":2557462717,"a":8515},{"a":"PRODUCT","a":"Dragonfly ","a":"d","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":70900229603328.000000,"a":{"a":{"a":"$1.05","a":105},"a":{"a":"$94.49","a":9449},"a":30},"a":2442061740,"a":8515},{"a":"PRODUCT","a":"Pho","a":"Snap on Ap","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":65194915004416.000000,"a":{"a":{"a":"$0.01","a":1},"a":{"a":"$414.99","a":41499},"a":39},"a":2004746863,"a":8515},{"a":"PRODUCT","a":"Otterbox i","a":"Your iPhon","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":61515478597632.000000,"a":{"a":{"a":"$3.28","a":328},"a":{"a":"$110.65","a":11065},"a":25},"a":2584611575,"a":8515}],"a":10,"a":2000}}, +{"a":{"a":1000,"a":{"a":"nix"}},"a":{"a":{"a":{"a":"$0.01","a":1},"a":{"a":"$4,833.99","a":483399}},"a":[{"a":"PRODUCT","a":"Silicone c","a":"Elite Hori","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":310711221747712.000000,"a":{"a":{"a":"$1.56","a":156},"a":{"a":"$29.99","a":2999},"a":14},"a":1968262863,"a":8515},{"a":"PRODUCT","a":"Nonslip Ch","a":"Specificat","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":175580930637824.000000,"a":{"a":{"a":"$0.45","a":45},"a":{"a":"$194.95","a":19495},"a":34},"a":2534935499,"a":8515},{"a":"PRODUCT","a":"Plastic Ca","a":"Descriptio","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":132488642953216.000000,"a":{"a":{"a":"$0.99","a":99},"a":{"a":"$303.68","a":30368},"a":33},"a":2305624670,"a":8515},{"a":"PRODUCT","a":"Protective","a":"Made of hi","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":108614681362432.000000,"a":{"a":{"a":"$1.70","a":170},"a":{"a":"$99.99","a":9999},"a":11},"a":2120981405,"a":8515},{"a":"PRODUCT","a":"P® 4","a":"Do more th","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":96203484168192.000000,"a":{"a":{"a":"$2.49","a":249},"a":{"a":"$79.95","a":7995},"a":16},"a":2203798762,"a":8515},{"a":"PRODUCT","a":"Case Refle","a":"NCAA iPhon","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":84727583211520.000000,"a":{"a":{"a":"$0.69","a":69},"a":{"a":"$75.52","a":7552},"a":59},"a":1114627445,"a":8515},{"a":"PRODUCT","a":"Infuse Pro","a":"Protect an","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":80831066406912.000000,"a":{"a":{"a":"$0.59","a":59},"a":{"a":"$79.00","a":7900},"a":24},"a":2557462717,"a":8515},{"a":"PRODUCT","a":"Dragonfly ","a":"d","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":70900229603328.000000,"a":{"a":{"a":"$1.05","a":105},"a":{"a":"$94.49","a":9449},"a":30},"a":2442061740,"a":8515},{"a":"PRODUCT","a":"Pho","a":"Snap on Ap","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":65194915004416.000000,"a":{"a":{"a":"$0.01","a":1},"a":{"a":"$414.99","a":41499},"a":39},"a":2004746863,"a":8515},{"a":"PRODUCT","a":"Otterbox i","a":"Your iPhon","a":"someone","a":{"a":"nix"},"a":{"a":[{"a":"nix","a":60,"a":60},{"a":"nix","a":100,"a":100},{"a":"nix","a":160,"a":160},{"a":"nix","a":400,"a":400}]},"a":61515478597632.000000,"a":{"a":{"a":"$3.28","a":328},"a":{"a":"$110.65","a":11065},"a":25},"a":2584611575,"a":8515}],"a":10,"a":2000}} +] + diff --git a/tests/auto/corelib/serialization/json/test3.duplicates.json b/tests/auto/corelib/serialization/json/test3.duplicates.json new file mode 100644 index 0000000000..c635a2523f --- /dev/null +++ b/tests/auto/corelib/serialization/json/test3.duplicates.json @@ -0,0 +1,15 @@ +{ + "a": "John", + "a": "Smith", + "a": 25, + "a": { + "a": "21 2nd Street", + "a": "New York", + "a": "NY", + "a": "10021" + }, + "a": [ + { "a": "home", "a": "212 555-1234" }, + { "a": "fax", "a": "646 555-4567" } + ] +} diff --git a/tests/auto/corelib/serialization/json/tst_qtjson.cpp b/tests/auto/corelib/serialization/json/tst_qtjson.cpp index 4651258ef3..54ef9be4f2 100644 --- a/tests/auto/corelib/serialization/json/tst_qtjson.cpp +++ b/tests/auto/corelib/serialization/json/tst_qtjson.cpp @@ -1,38 +1,20 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtTest> +// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2022 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> +#include <QMap> +#include <QVariantList> + +QT_WARNING_DISABLE_DEPRECATED #include "qjsonarray.h" #include "qjsonobject.h" #include "qjsonvalue.h" #include "qjsondocument.h" #include "qregularexpression.h" +#include "private/qnumeric_p.h" #include <limits> #define INVALID_UNICODE "\xCE\xBA\xE1" @@ -46,24 +28,33 @@ class tst_QtJson: public QObject private Q_SLOTS: void initTestCase(); + void compareCompiles(); void testValueSimple(); void testNumbers(); void testNumbers_2(); void testNumbers_3(); void testNumbers_4(); + void testNumberComparisons(); + void testObjectSimple(); + void testObjectTakeDetach(); void testObjectSmallKeys(); + void testObjectInsertCopies(); void testArraySimple(); + void testArrayInsertCopies(); void testValueObject(); void testValueArray(); void testObjectNested(); void testArrayNested(); void testArrayNestedEmpty(); void testArrayComfortOperators(); + void testArrayEquality_data(); + void testArrayEquality(); void testObjectNestedEmpty(); void testValueRef(); + void testValueRefComparison(); void testObjectIteration(); void testArrayIteration(); @@ -76,6 +67,7 @@ private Q_SLOTS: void nullObject(); void constNullObject(); + void keySorting_data(); void keySorting(); void undefinedValues(); @@ -95,30 +87,24 @@ private Q_SLOTS: void toJson(); void toJsonSillyNumericValues(); void toJsonLargeNumericValues(); + void toJsonDenormalValues(); void fromJson(); void fromJsonErrors(); - void fromBinary(); - void toAndFromBinary_data(); - void toAndFromBinary(); - void invalidBinaryData(); void parseNumbers(); void parseStrings(); void parseDuplicateKeys(); void testParser(); - void compactArray(); - void compactObject(); - - void validation(); - void assignToDocument(); void testDuplicateKeys(); void testCompaction(); void testDebugStream(); - void testCompactionError(); - void parseUnicodeEscapes(); + void parseEscapes_data(); + void parseEscapes(); + void makeEscapes_data(); + void makeEscapes(); void assignObjects(); void assignArrays(); @@ -132,6 +118,8 @@ private Q_SLOTS: void objectEquals(); void arrayEquals_data(); void arrayEquals(); + void documentEquals_data(); + void documentEquals(); void bom(); void nesting(); @@ -153,6 +141,27 @@ private Q_SLOTS: void implicitValueType(); void implicitDocumentType(); + void streamSerializationQJsonDocument_data(); + void streamSerializationQJsonDocument(); + void streamSerializationQJsonArray_data(); + void streamSerializationQJsonArray(); + void streamSerializationQJsonObject_data(); + void streamSerializationQJsonObject(); + void streamSerializationQJsonValue_data(); + void streamSerializationQJsonValue(); + void streamSerializationQJsonValueEmpty(); + void streamVariantSerialization(); + void escapeSurrogateCodePoints_data(); + void escapeSurrogateCodePoints(); + + void fromToVariantConversions_data(); + void fromToVariantConversions(); + + void testIteratorComparison(); + + void noLeakOnNameClash_data(); + void noLeakOnNameClash(); + private: QString testDataDir; }; @@ -164,6 +173,31 @@ void tst_QtJson::initTestCase() testDataDir = QCoreApplication::applicationDirPath(); } +void tst_QtJson::compareCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QJsonArray>(); + QTestPrivate::testAllComparisonOperatorsCompile<QJsonArray::iterator>(); + QTestPrivate::testAllComparisonOperatorsCompile<QJsonArray::const_iterator>(); + QTestPrivate::testAllComparisonOperatorsCompile<QJsonArray::iterator, + QJsonArray::const_iterator>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonDocument>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonObject>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonObject::iterator>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonObject::const_iterator>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonValue>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonValueConstRef>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonValueRef>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonArray, QJsonValue>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonObject, QJsonValue>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonObject, QJsonValueConstRef>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonObject, QJsonValueRef>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonValueConstRef, QJsonValue>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonValueRef, QJsonValue>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonValueRef, QJsonValueConstRef>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonObject::iterator, + QJsonObject::const_iterator>(); +} + void tst_QtJson::testValueSimple() { QJsonObject object; @@ -206,26 +240,32 @@ void tst_QtJson::testNumbers() { int numbers[] = { 0, - -1, 1, + 2, + -1, + -2, + (1<<25), (1<<26), (1<<27), (1<<28), + -(1<<25), -(1<<26), -(1<<27), -(1<<28), (1<<26) - 1, (1<<27) - 1, (1<<28) - 1, + (1<<29) - 1, -((1<<26) - 1), -((1<<27) - 1), - -((1<<28) - 1) + -((1<<28) - 1), + -((1<<29) - 1) }; int n = sizeof(numbers)/sizeof(int); QJsonArray array; for (int i = 0; i < n; ++i) - array.append((double)numbers[i]); + array.append(numbers[i]); QByteArray serialized = QJsonDocument(array).toJson(); QJsonDocument json = QJsonDocument::fromJson(serialized); @@ -234,8 +274,10 @@ void tst_QtJson::testNumbers() QCOMPARE(array.size(), array2.size()); for (int i = 0; i < array.size(); ++i) { QCOMPARE(array.at(i).type(), QJsonValue::Double); + QCOMPARE(array.at(i).toInt(), numbers[i]); QCOMPARE(array.at(i).toDouble(), (double)numbers[i]); QCOMPARE(array2.at(i).type(), QJsonValue::Double); + QCOMPARE(array2.at(i).toInt(), numbers[i]); QCOMPARE(array2.at(i).toDouble(), (double)numbers[i]); } } @@ -243,8 +285,10 @@ void tst_QtJson::testNumbers() { qint64 numbers[] = { 0, - -1, 1, + 2, + -1, + -2, (1ll<<54), (1ll<<55), (1ll<<56), @@ -254,15 +298,21 @@ void tst_QtJson::testNumbers() (1ll<<54) - 1, (1ll<<55) - 1, (1ll<<56) - 1, + (1ll<<57) - 1, + (1ll<<58) - 1, + (1ll<<59) + 1001, -((1ll<<54) - 1), -((1ll<<55) - 1), - -((1ll<<56) - 1) + -((1ll<<56) - 1), + -((1ll<<57) - 1), + -((1ll<<58) - 1), + -((1ll<<59) + 1001), }; int n = sizeof(numbers)/sizeof(qint64); QJsonArray array; for (int i = 0; i < n; ++i) - array.append((double)numbers[i]); + array.append(QJsonValue(numbers[i])); QByteArray serialized = QJsonDocument(array).toJson(); QJsonDocument json = QJsonDocument::fromJson(serialized); @@ -271,8 +321,10 @@ void tst_QtJson::testNumbers() QCOMPARE(array.size(), array2.size()); for (int i = 0; i < array.size(); ++i) { QCOMPARE(array.at(i).type(), QJsonValue::Double); + QCOMPARE(array.at(i).toInteger(), numbers[i]); QCOMPARE(array.at(i).toDouble(), (double)numbers[i]); QCOMPARE(array2.at(i).type(), QJsonValue::Double); + QCOMPARE(array2.at(i).toInteger(), numbers[i]); QCOMPARE(array2.at(i).toDouble(), (double)numbers[i]); } } @@ -345,24 +397,24 @@ void tst_QtJson::testNumbers_2() QJsonDocument jDocument2(QJsonDocument::fromJson(ba)); for (int power = 0; power <= 1075; power++) { floatValues_1[power] = jDocument2.object().value(QString::number(power)).toDouble(); -#ifdef Q_OS_QNX - if (power >= 970) - QEXPECT_FAIL("", "See QTBUG-37066", Abort); -#endif QVERIFY2(floatValues[power] == floatValues_1[power], QString("floatValues[%1] != floatValues_1[%1]").arg(power).toLatin1()); } + QT_TEST_EQUALITY_OPS(jDocument1, jDocument2, true); // The last value is below min denorm and should round to 0, everything else should contain a value QVERIFY2(floatValues_1[1075] == 0, "Value after min denorm should round to 0"); // Validate the last actual value is min denorm QVERIFY2(floatValues_1[1074] == 4.9406564584124654417656879286822e-324, QString("Min denorm value is incorrect: %1").arg(floatValues_1[1074]).toLatin1()); - // Validate that every value is half the value before it up to 1 - for (int index = 1074; index > 0; index--) { - QVERIFY2(floatValues_1[index] != 0, QString("2**- %1 should not be 0").arg(index).toLatin1()); - - QVERIFY2(floatValues_1[index - 1] == (floatValues_1[index] * 2), QString("Value should be double adjacent value at index %1").arg(index).toLatin1()); + if constexpr (std::numeric_limits<double>::has_denorm == std::denorm_present) { + // Validate that every value is half the value before it up to 1 + for (int index = 1074; index > 0; index--) { + QVERIFY2(floatValues_1[index] != 0, QString("2**- %1 should not be 0").arg(index).toLatin1()); + QVERIFY2(floatValues_1[index - 1] == (floatValues_1[index] * 2), QString("Value should be double adjacent value at index %1").arg(index).toLatin1()); + } + } else { + qInfo("Skipping denormal test as this system's double type lacks support"); } } @@ -380,6 +432,10 @@ void tst_QtJson::testNumbers_3() QJsonDocument jDocument2(QJsonDocument::fromJson(ba)); + QT_TEST_EQUALITY_OPS(jDocument1, jDocument2, true); + QT_TEST_EQUALITY_OPS(jDocument1, QJsonDocument(), false); + QT_TEST_EQUALITY_OPS(QJsonDocument(), QJsonDocument(), true); + double d1_1(jDocument2.object().value("d1").toDouble()); double d2_1(jDocument2.object().value("d2").toDouble()); QVERIFY(d1_1 != d2_1); @@ -397,7 +453,8 @@ void tst_QtJson::testNumbers_4() array << QJsonValue(-9223372036854775808.0); array << QJsonValue(+18446744073709551616.0); array << QJsonValue(-18446744073709551616.0); - const QByteArray json(QJsonDocument(array).toJson()); + QJsonDocument doc1 = QJsonDocument(array); + const QByteArray json(doc1.toJson()); const QByteArray expected = "[\n" " 1000000000000000,\n" @@ -410,6 +467,49 @@ void tst_QtJson::testNumbers_4() " -18446744073709552000\n" "]\n"; QCOMPARE(json, expected); + + QJsonArray array2; + array2 << QJsonValue(Q_INT64_C(+1000000000000000)); + array2 << QJsonValue(Q_INT64_C(-1000000000000000)); + array2 << QJsonValue(Q_INT64_C(+9007199254740992)); + array2 << QJsonValue(Q_INT64_C(-9007199254740992)); + array2 << QJsonValue(Q_INT64_C(+9223372036854775807)); + array2 << QJsonValue(Q_INT64_C(-9223372036854775807)); + QJsonDocument doc2 = QJsonDocument(array2); + const QByteArray json2(doc2.toJson()); + const QByteArray expected2 = + "[\n" + " 1000000000000000,\n" + " -1000000000000000,\n" + " 9007199254740992,\n" + " -9007199254740992,\n" + " 9223372036854775807,\n" + " -9223372036854775807\n" + "]\n"; + QCOMPARE(json2, expected2); + + QT_TEST_EQUALITY_OPS(doc1, doc2, false); +} + +void tst_QtJson::testNumberComparisons() +{ + // QJsonValues created using doubles only have double precision + QJsonValue llMinDbl(-9223372036854775807.0); + QJsonValue llMinPlus1Dbl(-9223372036854775806.0); + QCOMPARE(llMinDbl == llMinPlus1Dbl, -9223372036854775807.0 == -9223372036854775806.0); // true + + // QJsonValues created using qint64 have full qint64 precision + QJsonValue llMin(Q_INT64_C(-9223372036854775807)); + QJsonValue llMinPlus1(Q_INT64_C(-9223372036854775806)); + QCOMPARE(llMin == llMinPlus1, Q_INT64_C(-9223372036854775807) == Q_INT64_C(-9223372036854775806)); // false + + // The different storage formats should be able to compare as their C++ versions (all true) + QCOMPARE(llMin == llMinDbl, Q_INT64_C(-9223372036854775807) == -9223372036854775807.0); + QCOMPARE(llMinDbl == llMin, -9223372036854775807.0 == Q_INT64_C(-9223372036854775807)); + QCOMPARE(llMinPlus1 == llMinPlus1Dbl, Q_INT64_C(-9223372036854775806) == -9223372036854775806.0); + QCOMPARE(llMinPlus1Dbl == llMinPlus1, -9223372036854775806.0 == Q_INT64_C(-9223372036854775806)); + QCOMPARE(llMinPlus1 == llMinDbl, Q_INT64_C(-9223372036854775806) == -9223372036854775807.0); + QCOMPARE(llMinPlus1Dbl == llMin, -9223372036854775806.0 == Q_INT64_C(-9223372036854775807)); } void tst_QtJson::testObjectSimple() @@ -424,6 +524,8 @@ void tst_QtJson::testObjectSimple() object.insert("boolean", true); QCOMPARE(object.value("boolean").toBool(), true); QCOMPARE(object.value(QLatin1String("boolean")).toBool(), true); + QJsonObject object2 = object; + QJsonObject object3 = object; QStringList keys = object.keys(); QVERIFY2(keys.contains("number"), "key number not found"); @@ -435,6 +537,7 @@ void tst_QtJson::testObjectSimple() QJsonValue value(QLatin1String("foo")); object.insert("value", value); QCOMPARE(object.value("value"), value); + QT_TEST_EQUALITY_OPS(object.value("value"), value, true); int size = object.size(); object.remove("boolean"); @@ -443,12 +546,47 @@ void tst_QtJson::testObjectSimple() QJsonValue taken = object.take("value"); QCOMPARE(taken, value); + QT_TEST_EQUALITY_OPS(taken, value, true); QVERIFY2(!object.contains("value"), "key value should have been removed"); QString before = object.value("string").toString(); object.insert("string", QString::fromLatin1("foo")); QVERIFY2(object.value(QLatin1String("string")).toString() != before, "value should have been updated"); + // same tests again but with QStringView keys + object2.insert(QStringView(u"value"), value); + QCOMPARE(object2.value("value"), value); + + size = object2.size(); + object2.remove(QStringView(u"boolean")); + QCOMPARE(object2.size(), size - 1); + QVERIFY2(!object2.contains(QStringView(u"boolean")), "key boolean should have been removed"); + + taken = object2.take(QStringView(u"value")); + QCOMPARE(taken, value); + QVERIFY2(!object2.contains("value"), "key value should have been removed"); + + before = object2.value("string").toString(); + object2.insert(QStringView(u"string"), QString::fromLatin1("foo")); + QVERIFY2(object2.value(QStringView(u"string")).toString() != before, "value should have been updated"); + + // same tests again but with QLatin1String keys + object3.insert(QLatin1String("value"), value); + QCOMPARE(object3.value("value"), value); + + size = object3.size(); + object3.remove(QLatin1String("boolean")); + QCOMPARE(object3.size(), size - 1); + QVERIFY2(!object3.contains("boolean"), "key boolean should have been removed"); + + taken = object3.take(QLatin1String("value")); + QCOMPARE(taken, value); + QVERIFY2(!object3.contains("value"), "key value should have been removed"); + + before = object3.value("string").toString(); + object3.insert(QLatin1String("string"), QString::fromLatin1("foo")); + QVERIFY2(object3.value(QLatin1String("string")).toString() != before, "value should have been updated"); + size = object.size(); QJsonObject subobject; subobject.insert("number", 42); @@ -465,6 +603,24 @@ void tst_QtJson::testObjectSimple() QCOMPARE(subvalue.toObject(), subobject); } +void tst_QtJson::testObjectTakeDetach() +{ + QJsonObject object1, object2; + object1["key1"] = 1; + object1["key2"] = 2; + object2 = object1; + + object1.take("key2"); + object1.remove("key1"); + QVERIFY(!object1.contains("key1")); + QVERIFY(object2.contains("key1")); + QVERIFY(object2.value("key1").isDouble()); + + QVERIFY(!object1.contains("key2")); + QVERIFY(object2.contains("key2")); + QVERIFY(object2.value("key2").isDouble()); +} + void tst_QtJson::testObjectSmallKeys() { QJsonObject data1; @@ -479,6 +635,78 @@ void tst_QtJson::testObjectSmallKeys() QVERIFY(data1.contains(QStringLiteral("123"))); QCOMPARE(data1.value(QStringLiteral("123")).type(), QJsonValue::Double); QCOMPARE(data1.value(QStringLiteral("123")).toDouble(), (double)323); + QCOMPARE(data1.constEnd() - data1.constBegin(), 3); + QCOMPARE(data1.end() - data1.begin(), 3); +} + +void tst_QtJson::testObjectInsertCopies() +{ + { + QJsonObject obj; + obj["prop1"] = "TEST"; + QCOMPARE(obj.size(), 1); + QCOMPARE(obj.value("prop1"), "TEST"); + + obj["prop2"] = obj.value("prop1"); + QCOMPARE(obj.size(), 2); + QCOMPARE(obj.value("prop1"), "TEST"); + QCOMPARE(obj.value("prop2"), "TEST"); + } + { + // see QTBUG-83366 + QJsonObject obj; + obj["value"] = "TEST"; + QCOMPARE(obj.size(), 1); + QCOMPARE(obj.value("value"), "TEST"); + + obj["prop2"] = obj.value("value"); + QCOMPARE(obj.size(), 2); + QCOMPARE(obj.value("value"), "TEST"); + QCOMPARE(obj.value("prop2"), "TEST"); + } + { + QJsonObject obj; + obj["value"] = "TEST"; + QCOMPARE(obj.size(), 1); + QCOMPARE(obj.value("value"), "TEST"); + + // same as previous, but this is a QJsonValueRef + QJsonValueRef rv = obj["prop2"]; + rv = obj["value"]; + QCOMPARE(obj.size(), 2); + QCOMPARE(obj.value("value"), "TEST"); + QCOMPARE(obj.value("prop2"), "TEST"); + QT_TEST_EQUALITY_OPS(rv, obj["value"].toObject(), true); + } + { + QJsonObject obj; + obj["value"] = "TEST"; + QCOMPARE(obj.size(), 1); + QCOMPARE(obj.value("value"), "TEST"); + + // same as previous, but this is a QJsonValueRef + QJsonValueRef rv = obj["value"]; + obj["prop2"] = rv; + QCOMPARE(obj.size(), 2); + QCOMPARE(obj.value("value"), "TEST"); + QEXPECT_FAIL("", "QTBUG-83398: design flaw: the obj[] call invalidates the QJsonValueRef", Continue); + QCOMPARE(obj.value("prop2"), "TEST"); + } + { + QJsonObject obj; + obj["value"] = "TEST"; + QCOMPARE(obj.size(), 1); + QCOMPARE(obj.value("value"), "TEST"); + + QJsonValueRef v = obj["value"]; + QJsonObject obj2 = obj; + obj.insert("prop2", v); + QCOMPARE(obj.size(), 2); + QCOMPARE(obj.value("value"), "TEST"); + QCOMPARE(obj.value("prop2"), "TEST"); + QCOMPARE(obj2.size(), 1); + QCOMPARE(obj2.value("value"), "TEST"); + } } void tst_QtJson::testArraySimple() @@ -534,6 +762,32 @@ void tst_QtJson::testArraySimple() QCOMPARE(array.at(1), QJsonValue(QLatin1String("test"))); } +void tst_QtJson::testArrayInsertCopies() +{ + { + QJsonArray array; + array.append("TEST"); + QCOMPARE(array.size(), 1); + QCOMPARE(array.at(0), "TEST"); + + array.append(array.at(0)); + QCOMPARE(array.size(), 2); + QCOMPARE(array.at(0), "TEST"); + QCOMPARE(array.at(1), "TEST"); + } + { + QJsonArray array; + array.append("TEST"); + QCOMPARE(array.size(), 1); + QCOMPARE(array.at(0), "TEST"); + + array.prepend(array.at(0)); + QCOMPARE(array.size(), 2); + QCOMPARE(array.at(0), "TEST"); + QCOMPARE(array.at(1), "TEST"); + } +} + void tst_QtJson::testValueObject() { QJsonObject object; @@ -555,15 +809,20 @@ void tst_QtJson::testValueObject() void tst_QtJson::testValueArray() { QJsonArray array; + QJsonArray otherArray = {"wrong value"}; + QJsonValue value(array); + QCOMPARE(value.toArray(), array); + QCOMPARE(value.toArray(otherArray), array); + array.append(999.); array.append(QLatin1String("test")); array.append(true); - - QJsonValue value(array); + value = array; // if we don't modify the original JsonArray, toArray() // on the JsonValue should return the same object (non-detached). QCOMPARE(value.toArray(), array); + QCOMPARE(value.toArray(otherArray), array); // if we modify the original array, it should detach array.append(QLatin1String("test")); @@ -573,14 +832,28 @@ void tst_QtJson::testValueArray() void tst_QtJson::testObjectNested() { QJsonObject inner, outer; + QJsonObject otherObject = {{"wrong key", "wrong value"}}; + QJsonValue v = inner; + QCOMPARE(v.toObject(), inner); + QCOMPARE(v.toObject(otherObject), inner); + QT_TEST_EQUALITY_OPS(v.toObject(), inner, true); + QT_TEST_EQUALITY_OPS(v.toObject(otherObject), inner, true); + inner.insert("number", 999.); outer.insert("nested", inner); + QT_TEST_EQUALITY_OPS(outer, inner, false); // if we don't modify the original JsonObject, value() // should return the same object (non-detached). QJsonObject value = outer.value("nested").toObject(); + v = value; QCOMPARE(value, inner); QCOMPARE(value.value("number").toDouble(), 999.); + QCOMPARE(v.toObject(), inner); + QCOMPARE(v.toObject(otherObject), inner); + QT_TEST_EQUALITY_OPS(v.toObject(), inner, true); + QT_TEST_EQUALITY_OPS(v.toObject(otherObject), inner, true); + QCOMPARE(v["number"].toDouble(), 999.); // if we modify the original object, it should detach and not // affect the nested object @@ -605,6 +878,7 @@ void tst_QtJson::testObjectNested() QCOMPARE(outer.value("nested").toObject().value("nested").toObject(), twoDeep); QCOMPARE(outer.value("nested").toObject().value("nested").toObject().value("boolean").toBool(), true); + QT_TEST_EQUALITY_OPS(outer.value("nested").toObject().value("nested").toObject(), twoDeep, true); } void tst_QtJson::testArrayNested() @@ -630,6 +904,7 @@ void tst_QtJson::testArrayNested() object.insert("boolean", true); outer.append(object); QCOMPARE(outer.last().toObject(), object); + QT_TEST_EQUALITY_OPS(outer.last().toObject(), object, true); QCOMPARE(outer.last().toObject().value("boolean").toBool(), true); // two deep arrays @@ -649,14 +924,13 @@ void tst_QtJson::testArrayNestedEmpty() QJsonValue val = object.value("inner"); QJsonArray value = object.value("inner").toArray(); QVERIFY(QJsonDocument(value).isArray()); + QT_TEST_EQUALITY_OPS(QJsonDocument(), QJsonDocument(value), false); QCOMPARE(value.size(), 0); QCOMPARE(value, inner); QCOMPARE(value.size(), 0); object.insert("count", 0.); QCOMPARE(object.value("inner").toArray().size(), 0); QVERIFY(object.value("inner").toArray().isEmpty()); - QJsonDocument(object).toBinaryData(); - QCOMPARE(object.value("inner").toArray().size(), 0); } void tst_QtJson::testObjectNestedEmpty() @@ -668,21 +942,44 @@ void tst_QtJson::testObjectNestedEmpty() object.insert("inner2", inner2); QJsonObject value = object.value("inner").toObject(); QVERIFY(QJsonDocument(value).isObject()); + QT_TEST_EQUALITY_OPS(QJsonDocument(), QJsonDocument(value), false); QCOMPARE(value.size(), 0); QCOMPARE(value, inner); + QT_TEST_EQUALITY_OPS(value, inner, true); QCOMPARE(value.size(), 0); object.insert("count", 0.); QCOMPARE(object.value("inner").toObject().size(), 0); QCOMPARE(object.value("inner").type(), QJsonValue::Object); - QJsonDocument(object).toBinaryData(); - QVERIFY(object.value("inner").toObject().isEmpty()); - QVERIFY(object.value("inner2").toObject().isEmpty()); - QJsonDocument doc = QJsonDocument::fromBinaryData(QJsonDocument(object).toBinaryData()); - QVERIFY(!doc.isNull()); - QJsonObject reconstituted(doc.object()); - QCOMPARE(reconstituted.value("inner").toObject().size(), 0); - QCOMPARE(reconstituted.value("inner").type(), QJsonValue::Object); - QCOMPARE(reconstituted.value("inner2").type(), QJsonValue::Object); +} + +void tst_QtJson::testArrayEquality_data() +{ + QTest::addColumn<QJsonArray>("array1"); + QTest::addColumn<QJsonArray>("array2"); + QTest::addColumn<bool>("expectedResult"); + QTest::addRow("QJsonArray(), QJsonArray{665, 666, 667}") + << QJsonArray() << QJsonArray{665, 666, 667} << false; + QTest::addRow("QJsonArray(), QJsonArray{}") + << QJsonArray() << QJsonArray{} <<true; + QTest::addRow("QJsonArray(), QJsonArray{123, QLatin1String(\"foo\")}") + << QJsonArray() << QJsonArray{123, QLatin1String("foo")} << false; + QTest::addRow( + "QJsonArray{123,QLatin1String(\"foo\")}, QJsonArray{123,QLatin1String(\"foo\")}") + << QJsonArray{123, QLatin1String("foo")} + << QJsonArray{123, QLatin1String("foo")} + << true; +} + +void tst_QtJson::testArrayEquality() +{ + QFETCH(QJsonArray, array1); + QFETCH(QJsonArray, array2); + QFETCH(bool, expectedResult); + + QJsonValue value = QJsonValue(array1); + + QT_TEST_EQUALITY_OPS(array1, array2, expectedResult); + QT_TEST_EQUALITY_OPS(value, array2, expectedResult); } void tst_QtJson::testArrayComfortOperators() @@ -724,7 +1021,7 @@ void tst_QtJson::testValueRef() QCOMPARE(object.value(QLatin1String("null")), QJsonValue()); object[QLatin1String("null")] = 100.; QCOMPARE(object.value(QLatin1String("null")).type(), QJsonValue::Double); - QJsonValue val = qAsConst(object)[QLatin1String("null")]; + QJsonValue val = std::as_const(object)[QLatin1String("null")]; QCOMPARE(val.toDouble(), 100.); QCOMPARE(object.size(), 2); @@ -734,21 +1031,79 @@ void tst_QtJson::testValueRef() QCOMPARE(object.value(QLatin1String("key")), QJsonValue(42)); } +void tst_QtJson::testValueRefComparison() +{ + QJsonValue a0 = 42.; + QJsonValue a1 = QStringLiteral("142"); + +#define CHECK_IMPL(lhs, rhs, ineq) \ + QCOMPARE(lhs, rhs); \ + QVERIFY(!(lhs != rhs)); \ + QVERIFY(lhs != ineq); \ + QVERIFY(!(lhs == ineq)); \ + QVERIFY(ineq != rhs); \ + QVERIFY(!(ineq == rhs)); \ + /* end */ + +#define CHECK(lhs, rhs, ineq) \ + do { \ + CHECK_IMPL(lhs, rhs, ineq) \ + CHECK_IMPL(std::as_const(lhs), rhs, ineq) \ + CHECK_IMPL(lhs, std::as_const(rhs), ineq) \ + CHECK_IMPL(std::as_const(lhs), std::as_const(rhs), ineq) \ + } while (0) + + // check that the (in)equality operators aren't ambiguous in C++20: + QJsonArray a = {a0, a1}; + + static_assert(std::is_same_v<decltype(a[0]), QJsonValueRef>); + + auto r0 = a.begin()[0]; + auto r1 = a.begin()[1]; + auto c0 = std::as_const(a).begin()[0]; + // ref <> ref + CHECK(r0, r0, r1); + // cref <> ref + CHECK(c0, r0, r1); + // ref <> cref + CHECK(r0, c0, r1); + // ref <> val + CHECK(r0, a0, r1); + // cref <> val + CHECK(c0, a0, r1); + // val <> ref + CHECK(a0, r0, a1); + // val <> cref + CHECK(a0, c0, a1); + // val <> val + CHECK(a0, a0, a1); + + QT_TEST_EQUALITY_OPS(r0, r1, false); + QT_TEST_EQUALITY_OPS(r0, c0, true); + QT_TEST_EQUALITY_OPS(c0, r1, false); + QT_TEST_EQUALITY_OPS(a0, c0, true); + QT_TEST_EQUALITY_OPS(a0, r1, false); + +#undef CHECK +#undef CHECK_IMPL +} + void tst_QtJson::testObjectIteration() { QJsonObject object; for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it) - QVERIFY(false); + QFAIL("Iterator of default-initialized object should be empty"); const QString property = "kkk"; object.insert(property, 11); object.take(property); for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it) - QVERIFY(false); + QFAIL("Iterator after property add-and-remove should be empty"); - for (int i = 0; i < 10; ++i) - object[QString::number(i)] = (double)i; + // insert in weird order to confirm keys are sorted + for (int i : {0, 9, 5, 7, 8, 2, 1, 3, 6, 4}) + object[QString::number(i)] = double(i); QCOMPARE(object.size(), 10); @@ -757,37 +1112,79 @@ void tst_QtJson::testObjectIteration() for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it) { QJsonValue value = it.value(); QCOMPARE((double)it.key().toInt(), value.toDouble()); + QT_TEST_EQUALITY_OPS(it, QJsonObject::iterator(), false); } { QJsonObject object2 = object; QCOMPARE(object, object2); + QT_TEST_EQUALITY_OPS(object, object2, true); QJsonValue val = *object2.begin(); - object2.erase(object2.begin()); + auto next = object2.erase(object2.begin()); QCOMPARE(object.size(), 10); QCOMPARE(object2.size(), 9); + QVERIFY(next == object2.begin()); + QT_TEST_EQUALITY_OPS(next, object2.begin(), true); + + double d = 1; // we erased the first item + for (auto it = object2.constBegin(); it != object2.constEnd(); ++it, d += 1) { + QJsonValue value = it.value(); + QVERIFY(it.value() != val); + QCOMPARE(it.value(), d); + QCOMPARE(it.value().toDouble(), d); + QCOMPARE(it.key().toInt(), value.toDouble()); + } + } + + { + QJsonObject object2 = object; + QCOMPARE(object, object2); + QT_TEST_EQUALITY_OPS(object, object2, true); - for (QJsonObject::const_iterator it = object2.constBegin(); it != object2.constEnd(); ++it) { + QJsonValue val = *(object2.end() - 1); + auto next = object2.erase(object2.end() - 1); + QCOMPARE(object.size(), 10); + QCOMPARE(object2.size(), 9); + QVERIFY(next == object2.end()); + double d = 0; + for (auto it = object2.constBegin(); it != object2.constEnd(); ++it, d += 1) { QJsonValue value = it.value(); QVERIFY(it.value() != val); - QCOMPARE((double)it.key().toInt(), value.toDouble()); + QCOMPARE(it.value(), d); + QCOMPARE(it.value().toDouble(), d); + QCOMPARE(it.key().toInt(), value.toDouble()); } } { QJsonObject object2 = object; QCOMPARE(object, object2); + QT_TEST_EQUALITY_OPS(object, object2, true); QJsonObject::iterator it = object2.find(QString::number(5)); - object2.erase(it); + QJsonValue val = *it; + auto next = object2.erase(it); QCOMPARE(object.size(), 10); QCOMPARE(object2.size(), 9); + QCOMPARE(*next, 6); + + int i = 0; + for (auto it = object2.constBegin(); it != object2.constEnd(); ++it, ++i) { + if (i == 5) + ++i; + QJsonValue value = it.value(); + QVERIFY(it.value() != val); + QCOMPARE(it.value(), i); + QCOMPARE(it.value().toInt(), i); + QCOMPARE(it.key().toInt(), value.toDouble()); + } } { QJsonObject::Iterator it = object.begin(); it += 5; + QT_TEST_ALL_COMPARISON_OPS(it, object.begin(), Qt::strong_ordering::greater); QCOMPARE(QJsonValue(it.value()).toDouble(), 5.); it -= 3; QCOMPARE(QJsonValue(it.value()).toDouble(), 2.); @@ -802,10 +1199,14 @@ void tst_QtJson::testObjectIteration() it += 5; QCOMPARE(QJsonValue(it.value()).toDouble(), 5.); it -= 3; + QT_TEST_ALL_COMPARISON_OPS(object.constBegin(), it, Qt::strong_ordering::less); QCOMPARE(QJsonValue(it.value()).toDouble(), 2.); QJsonObject::ConstIterator it2 = it + 5; + QT_TEST_EQUALITY_OPS(it, it2, false); QCOMPARE(QJsonValue(it2.value()).toDouble(), 7.); it2 = it - 1; + QT_TEST_ALL_COMPARISON_OPS(it2, it, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(it2, it - 2, Qt::strong_ordering::greater); QCOMPARE(QJsonValue(it2.value()).toDouble(), 1.); } @@ -814,6 +1215,17 @@ void tst_QtJson::testObjectIteration() it = object.erase(it); QCOMPARE(object.size() , 0); QCOMPARE(it, object.end()); + QT_TEST_ALL_COMPARISON_OPS(it, object.end(), Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(it, object.constEnd(), Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(it, object.begin(), + Qt::strong_ordering::equal); // because object is empty + QT_TEST_ALL_COMPARISON_OPS(it, object.constBegin(), Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(QJsonObject::Iterator(), + QJsonObject::Iterator(), Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(QJsonObject::ConstIterator(), + QJsonObject::Iterator(), Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(QJsonObject::ConstIterator(), + QJsonObject::ConstIterator(), Qt::strong_ordering::equal); } void tst_QtJson::testArrayIteration() @@ -827,7 +1239,11 @@ void tst_QtJson::testArrayIteration() int i = 0; for (QJsonArray::iterator it = array.begin(); it != array.end(); ++it, ++i) { QJsonValue value = (*it); + QJsonArray::iterator it1 = it; QCOMPARE((double)i, value.toDouble()); + QT_TEST_EQUALITY_OPS(QJsonArray::iterator(), QJsonArray::iterator(), true); + QT_TEST_EQUALITY_OPS(QJsonArray::iterator(), it, false); + QT_TEST_EQUALITY_OPS(it1, it, true); } QCOMPARE(array.begin()->toDouble(), array.constBegin()->toDouble()); @@ -837,14 +1253,38 @@ void tst_QtJson::testArrayIteration() QCOMPARE(array, array2); QJsonValue val = *array2.begin(); - array2.erase(array2.begin()); + auto next = array2.erase(array2.begin()); QCOMPARE(array.size(), 10); QCOMPARE(array2.size(), 9); + QVERIFY(next == array2.begin()); i = 1; - for (QJsonArray::const_iterator it = array2.constBegin(); it != array2.constEnd(); ++it, ++i) { + for (auto it = array2.constBegin(); it != array2.constEnd(); ++it, ++i) { + QJsonValue value = (*it); + QCOMPARE(value.toInt(), i); + QCOMPARE(value.toDouble(), i); + QCOMPARE(it->toInt(), i); + QCOMPARE(it->toDouble(), i); + } + } + + { + QJsonArray array2 = array; + QCOMPARE(array, array2); + + QJsonValue val = array2.last(); + auto next = array2.erase(array2.end() - 1); + QCOMPARE(array.size(), 10); + QCOMPARE(array2.size(), 9); + QVERIFY(next == array2.end()); + + i = 0; + for (auto it = array2.constBegin(); it != array2.constEnd(); ++it, ++i) { QJsonValue value = (*it); - QCOMPARE((double)i, value.toDouble()); + QCOMPARE(value.toInt(), i); + QCOMPARE(value.toDouble(), i); + QCOMPARE(it->toInt(), i); + QCOMPARE(it->toDouble(), i); } } @@ -858,6 +1298,13 @@ void tst_QtJson::testArrayIteration() QCOMPARE(QJsonValue(*it2).toDouble(), 7.); it2 = it - 1; QCOMPARE(QJsonValue(*it2).toDouble(), 1.); + QT_TEST_EQUALITY_OPS(it, it2, false); + it = array.begin(); + QT_TEST_EQUALITY_OPS(it, array.begin(), true); + it2 = it + 5; + QT_TEST_ALL_COMPARISON_OPS(it2, it, Qt::strong_ordering::greater); + it += 5; + QT_TEST_EQUALITY_OPS(it, it2, true); } { @@ -877,6 +1324,26 @@ void tst_QtJson::testArrayIteration() it = array.erase(it); QCOMPARE(array.size() , 0); QCOMPARE(it, array.end()); + QT_TEST_EQUALITY_OPS(it, array.end(), true); + + { + int i = 0; + for (QJsonArray::const_iterator it = array.constBegin(); + it != array.constEnd(); ++it, ++i) { + QJsonArray::const_iterator it1 = it; + QT_TEST_EQUALITY_OPS(QJsonArray::const_iterator(), QJsonArray::const_iterator(), true); + QT_TEST_EQUALITY_OPS(QJsonArray::const_iterator(), it, false); + QT_TEST_EQUALITY_OPS(it1, it, true); + } + } + + { + QJsonArray::iterator nonConstIt = array.begin(); + QJsonArray::const_iterator it = array.constBegin(); + QT_TEST_EQUALITY_OPS(nonConstIt, it, true); + it+=1; + QT_TEST_ALL_COMPARISON_OPS(nonConstIt, it, Qt::strong_ordering::less); + } } void tst_QtJson::testObjectFind() @@ -890,14 +1357,12 @@ void tst_QtJson::testObjectFind() QJsonObject::iterator it = object.find(QLatin1String("1")); QCOMPARE((*it).toDouble(), 1.); it = object.find(QString("11")); - QCOMPARE((*it).type(), QJsonValue::Undefined); QCOMPARE(it, object.end()); QJsonObject::const_iterator cit = object.constFind(QLatin1String("1")); QCOMPARE((*cit).toDouble(), 1.); cit = object.constFind(QString("11")); - QCOMPARE((*it).type(), QJsonValue::Undefined); - QCOMPARE(it, object.end()); + QCOMPARE(cit, object.constEnd()); } void tst_QtJson::testDocument() @@ -972,6 +1437,8 @@ void tst_QtJson::testDocument() QCOMPARE(doc5.isObject(), false); QCOMPARE(doc5.array().size(), 1); QCOMPARE(doc5.array().at(0), QJsonValue(23)); + + QT_TEST_EQUALITY_OPS(doc2, doc3, true); } void tst_QtJson::nullValues() @@ -1062,21 +1529,55 @@ void tst_QtJson::constNullObject() QCOMPARE(nullObject["foo"], QJsonValue(QJsonValue::Undefined)); } -void tst_QtJson::keySorting() +void tst_QtJson::keySorting_data() { + QTest::addColumn<QString>("json"); + QTest::addColumn<QStringList>("sortedKeys"); + + QStringList list = {"A", "B"}; + QTest::newRow("sorted-ascii-2") << R"({ "A": false, "B": true })" << list; const char *json = "{ \"B\": true, \"A\": false }"; - QJsonDocument doc = QJsonDocument::fromJson(json); + QTest::newRow("unsorted-ascii-2") << json << list; + + list = QStringList{"A", "B", "C", "D", "E"}; + QTest::newRow("sorted-ascii-5") << R"({"A": 1, "B": 2, "C": 3, "D": 4, "E": 5})" << list; + QTest::newRow("unsorted-ascii-5") << R"({"A": 1, "C": 3, "D": 4, "B": 2, "E": 5})" << list; + QTest::newRow("inverse-sorted-ascii-5") << R"({"E": 5, "D": 4, "C": 3, "B": 2, "A": 1})" << list; + + list = QStringList{"á", "é", "í", "ó", "ú"}; + QTest::newRow("sorted-latin1") << R"({"á": 1, "é": 2, "í": 3, "ó": 4, "ú": 5})" << list; + QTest::newRow("unsorted-latin1") << R"({"á": 1, "í": 3, "ó": 4, "é": 2, "ú": 5})" << list; + QTest::newRow("inverse-sorted-latin1") << R"({"ú": 5, "ó": 4, "í": 3, "é": 2, "á": 1})" << list; + + QTest::newRow("sorted-escaped-latin1") << R"({"\u00e1": 1, "\u00e9": 2, "\u00ed": 3, "\u00f3": 4, "\u00fa": 5})" << list; + QTest::newRow("unsorted-escaped-latin1") << R"({"\u00e1": 1, "\u00ed": 3, "\u00f3": 4, "\u00e9": 2, "\u00fa": 5})" << list; + QTest::newRow("inverse-sorted-escaped-latin1") << R"({"\u00fa": 5, "\u00f3": 4, "\u00ed": 3, "\u00e9": 2, "\u00e1": 1})" << list; + + list = QStringList{"A", "α", "Я", "€", "测"}; + QTest::newRow("sorted") << R"({"A": 1, "α": 2, "Я": 3, "€": 4, "测": 5})" << list; + QTest::newRow("unsorted") << R"({"A": 1, "Я": 3, "€": 4, "α": 2, "测": 5})" << list; + QTest::newRow("inverse-sorted") << R"({"测": 5, "€": 4, "Я": 3, "α": 2, "A": 1})" << list; + + QTest::newRow("sorted-escaped") << R"({"A": 1, "\u03b1": 2, "\u042f": 3, "\u20ac": 4, "\u6d4b": 5})" << list; + QTest::newRow("unsorted-escaped") << R"({"A": 1, "\u042f": 3, "\u20ac": 4, "\u03b1": 2, "\u6d4b": 5})" << list; + QTest::newRow("inverse-sorted-escaped") << R"({"\u6d4b": 5, "\u20ac": 4, "\u042f": 3, "\u03b1": 2, "A": 1})" << list; +} + +void tst_QtJson::keySorting() +{ + QFETCH(QString, json); + QFETCH(QStringList, sortedKeys); + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8()); QCOMPARE(doc.isObject(), true); QJsonObject o = doc.object(); - QCOMPARE(o.size(), 2); + QCOMPARE(o.size(), sortedKeys.size()); + QCOMPARE(o.keys(), sortedKeys); QJsonObject::const_iterator it = o.constBegin(); - QCOMPARE(it.key(), QLatin1String("A")); - ++it; - QCOMPARE(it.key(), QLatin1String("B")); - - QCOMPARE(o.keys(), QStringList() << QLatin1String("A") << QLatin1String("B")); + QStringList::const_iterator it2 = sortedKeys.constBegin(); + for ( ; it != o.constEnd(); ++it, ++it2) + QCOMPARE(it.key(), *it2); } void tst_QtJson::undefinedValues() @@ -1084,6 +1585,8 @@ void tst_QtJson::undefinedValues() QJsonObject object; object.insert("Key", QJsonValue(QJsonValue::Undefined)); QCOMPARE(object.size(), 0); + object["Key"] = QJsonValue(QJsonValue::Undefined); + QCOMPARE(object.size(), 0); object.insert("Key", QLatin1String("Value")); QCOMPARE(object.size(), 1); @@ -1110,8 +1613,8 @@ void tst_QtJson::fromVariant_data() bool boolValue = true; int intValue = -1; uint uintValue = 1; - long long longlongValue = -2; - unsigned long long ulonglongValue = 2; + qlonglong longlongValue = -2; + qulonglong ulonglongValue = 2; float floatValue = 3.3f; double doubleValue = 4.4; QString stringValue("str"); @@ -1130,6 +1633,7 @@ void tst_QtJson::fromVariant_data() variantList.append(stringValue); variantList.append(stringList); variantList.append(QVariant::fromValue(nullptr)); + variantList.append(QVariant()); QJsonArray jsonArray_variant; jsonArray_variant.append(boolValue); jsonArray_variant.append(floatValue); @@ -1137,27 +1641,35 @@ void tst_QtJson::fromVariant_data() jsonArray_variant.append(stringValue); jsonArray_variant.append(jsonArray_string); jsonArray_variant.append(QJsonValue(QJsonValue::Null)); + jsonArray_variant.append(QJsonValue()); QVariantMap variantMap; variantMap["bool"] = boolValue; variantMap["float"] = floatValue; variantMap["string"] = stringValue; variantMap["array"] = variantList; + variantMap["null"] = QVariant::fromValue(nullptr); + variantMap["default"] = QVariant(); QVariantHash variantHash; variantHash["bool"] = boolValue; variantHash["float"] = floatValue; variantHash["string"] = stringValue; variantHash["array"] = variantList; + variantHash["null"] = QVariant::fromValue(nullptr); + variantHash["default"] = QVariant(); QJsonObject jsonObject; jsonObject["bool"] = boolValue; jsonObject["float"] = floatValue; jsonObject["string"] = stringValue; jsonObject["array"] = jsonArray_variant; + jsonObject["null"] = QJsonValue::Null; + jsonObject["default"] = QJsonValue(); + QTest::newRow("default") << QVariant() << QJsonValue(); QTest::newRow("nullptr") << QVariant::fromValue(nullptr) << QJsonValue(QJsonValue::Null); QTest::newRow("bool") << QVariant(boolValue) << QJsonValue(boolValue); QTest::newRow("int") << QVariant(intValue) << QJsonValue(intValue); - QTest::newRow("uint") << QVariant(uintValue) << QJsonValue(static_cast<double>(uintValue)); + QTest::newRow("uint") << QVariant(uintValue) << QJsonValue(static_cast<qint64>(uintValue)); QTest::newRow("longlong") << QVariant(longlongValue) << QJsonValue(longlongValue); QTest::newRow("ulonglong") << QVariant(ulonglongValue) << QJsonValue(static_cast<double>(ulonglongValue)); QTest::newRow("float") << QVariant(floatValue) << QJsonValue(floatValue); @@ -1169,13 +1681,55 @@ void tst_QtJson::fromVariant_data() QTest::newRow("variantHash") << QVariant(variantHash) << QJsonValue(jsonObject); } +// replaces QVariant() with QVariant(nullptr) +static QVariant normalizedVariant(const QVariant &v) +{ + switch (v.userType()) { + case QMetaType::UnknownType: + return QVariant::fromValue(nullptr); + case QMetaType::QVariantList: { + const QVariantList in = v.toList(); + QVariantList out; + out.reserve(in.size()); + for (const QVariant &v : in) + out << normalizedVariant(v); + return out; + } + case QMetaType::QStringList: { + const QStringList in = v.toStringList(); + QVariantList out; + out.reserve(in.size()); + for (const QString &v : in) + out << v; + return out; + } + case QMetaType::QVariantMap: { + const QVariantMap in = v.toMap(); + QVariantMap out; + for (auto it = in.begin(); it != in.end(); ++it) + out.insert(it.key(), normalizedVariant(it.value())); + return out; + } + case QMetaType::QVariantHash: { + const QVariantHash in = v.toHash(); + QVariantMap out; + for (auto it = in.begin(); it != in.end(); ++it) + out.insert(it.key(), normalizedVariant(it.value())); + return out; + } + + default: + return v; + } +} + void tst_QtJson::fromVariant() { QFETCH( QVariant, variant ); QFETCH( QJsonValue, jsonvalue ); QCOMPARE(QJsonValue::fromVariant(variant), jsonvalue); - QCOMPARE(variant.toJsonValue(), jsonvalue); + QCOMPARE(normalizedVariant(variant).toJsonValue(), jsonvalue); } void tst_QtJson::fromVariantSpecial_data() @@ -1208,7 +1762,7 @@ void tst_QtJson::toVariant() QFETCH( QVariant, variant ); QFETCH( QJsonValue, jsonvalue ); - QCOMPARE(jsonvalue.toVariant(), variant); + QCOMPARE(jsonvalue.toVariant(), normalizedVariant(variant)); } void tst_QtJson::fromVariantMap() @@ -1256,7 +1810,8 @@ void tst_QtJson::fromVariantHash() void tst_QtJson::toVariantMap() { - QCOMPARE(QMetaType::Type(QJsonValue(QJsonObject()).toVariant().type()), QMetaType::QVariantMap); // QTBUG-32524 + QCOMPARE(QMetaType::Type(QJsonValue(QJsonObject()).toVariant().typeId()), + QMetaType::QVariantMap); // QTBUG-32524 QJsonObject object; QVariantMap map = object.toVariantMap(); @@ -1276,7 +1831,7 @@ void tst_QtJson::toVariantMap() QCOMPARE(map.size(), 3); QCOMPARE(map.value("Key"), QVariant(QString("Value"))); QCOMPARE(map.value("null"), QVariant::fromValue(nullptr)); - QCOMPARE(map.value("Array").type(), QVariant::List); + QCOMPARE(map.value("Array").typeId(), QMetaType::QVariantList); QVariantList list = map.value("Array").toList(); QCOMPARE(list.size(), 4); QCOMPARE(list.at(0), QVariant(true)); @@ -1305,7 +1860,7 @@ void tst_QtJson::toVariantHash() QCOMPARE(hash.size(), 3); QCOMPARE(hash.value("Key"), QVariant(QString("Value"))); QCOMPARE(hash.value("null"), QVariant::fromValue(nullptr)); - QCOMPARE(hash.value("Array").type(), QVariant::List); + QCOMPARE(hash.value("Array").typeId(), QMetaType::QVariantList); QVariantList list = hash.value("Array").toList(); QCOMPARE(list.size(), 4); QCOMPARE(list.at(0), QVariant(true)); @@ -1316,7 +1871,8 @@ void tst_QtJson::toVariantHash() void tst_QtJson::toVariantList() { - QCOMPARE(QMetaType::Type(QJsonValue(QJsonArray()).toVariant().type()), QMetaType::QVariantList); // QTBUG-32524 + QCOMPARE(QMetaType::Type(QJsonValue(QJsonArray()).toVariant().typeId()), + QMetaType::QVariantList); // QTBUG-32524 QJsonArray array; QVariantList list = array.toVariantList(); @@ -1336,7 +1892,7 @@ void tst_QtJson::toVariantList() QCOMPARE(list.size(), 3); QCOMPARE(list[0], QVariant(QString("Value"))); QCOMPARE(list[1], QVariant::fromValue(nullptr)); - QCOMPARE(list[2].type(), QVariant::List); + QCOMPARE(list[2].typeId(), QMetaType::QVariantList); QVariantList vlist = list[2].toList(); QCOMPARE(vlist.size(), 4); QCOMPARE(vlist.at(0), QVariant(true)); @@ -1458,16 +2014,13 @@ void tst_QtJson::toJsonLargeNumericValues() QJsonArray array; array.append(QJsonValue(1.234567)); // actual precision bug in Qt 5.0.0 array.append(QJsonValue(1.7976931348623157e+308)); // JS Number.MAX_VALUE - array.append(QJsonValue(5e-324)); // JS Number.MIN_VALUE array.append(QJsonValue(std::numeric_limits<double>::min())); array.append(QJsonValue(std::numeric_limits<double>::max())); array.append(QJsonValue(std::numeric_limits<double>::epsilon())); - array.append(QJsonValue(std::numeric_limits<double>::denorm_min())); array.append(QJsonValue(0.0)); array.append(QJsonValue(-std::numeric_limits<double>::min())); array.append(QJsonValue(-std::numeric_limits<double>::max())); array.append(QJsonValue(-std::numeric_limits<double>::epsilon())); - array.append(QJsonValue(-std::numeric_limits<double>::denorm_min())); array.append(QJsonValue(-0.0)); array.append(QJsonValue(9007199254740992LL)); // JS Number max integer array.append(QJsonValue(-9007199254740992LL)); // JS Number min integer @@ -1481,27 +2034,21 @@ void tst_QtJson::toJsonLargeNumericValues() " 1.234567,\n" " 1.7976931348623157e+308,\n" #ifdef QT_NO_DOUBLECONVERSION // "shortest" double conversion is not very short then - " 4.9406564584124654e-324,\n" " 2.2250738585072014e-308,\n" " 1.7976931348623157e+308,\n" " 2.2204460492503131e-16,\n" - " 4.9406564584124654e-324,\n" " 0,\n" " -2.2250738585072014e-308,\n" " -1.7976931348623157e+308,\n" " -2.2204460492503131e-16,\n" - " -4.9406564584124654e-324,\n" #else - " 5e-324,\n" " 2.2250738585072014e-308,\n" " 1.7976931348623157e+308,\n" " 2.220446049250313e-16,\n" - " 5e-324,\n" " 0,\n" " -2.2250738585072014e-308,\n" " -1.7976931348623157e+308,\n" " -2.220446049250313e-16,\n" - " -5e-324,\n" #endif " 0,\n" " 9007199254740992,\n" @@ -1509,20 +2056,50 @@ void tst_QtJson::toJsonLargeNumericValues() " ]\n" "}\n"; -#ifdef Q_OS_QNX - QEXPECT_FAIL("", "See QTBUG-37066", Continue); -#endif QCOMPARE(json, expected); QJsonDocument doc; doc.setObject(object); json = doc.toJson(); -#ifdef Q_OS_QNX - QEXPECT_FAIL("", "See QTBUG-37066", Continue); -#endif QCOMPARE(json, expected); } +void tst_QtJson::toJsonDenormalValues() +{ + if constexpr (std::numeric_limits<double>::has_denorm == std::denorm_present) { + QJsonObject object; + QJsonArray array; + array.append(QJsonValue(5e-324)); // JS Number.MIN_VALUE + array.append(QJsonValue(std::numeric_limits<double>::denorm_min())); + array.append(QJsonValue(-std::numeric_limits<double>::denorm_min())); + object.insert("Array", array); + + QByteArray json = QJsonDocument(object).toJson(); + QByteArray expected = + "{\n" + " \"Array\": [\n" +#ifdef QT_NO_DOUBLECONVERSION // "shortest" double conversion is not very short then + " 4.9406564584124654e-324,\n" + " 4.9406564584124654e-324,\n" + " -4.9406564584124654e-324\n" +#else + " 5e-324,\n" + " 5e-324,\n" + " -5e-324\n" +#endif + " ]\n" + "}\n"; + + QCOMPARE(json, expected); + QJsonDocument doc; + doc.setObject(object); + json = doc.toJson(); + QCOMPARE(json, expected); + } else { + QSKIP("Skipping 'denorm' as this type lacks denormals on this system"); + } +} + void tst_QtJson::fromJson() { { @@ -1813,64 +2390,6 @@ void tst_QtJson::fromJsonErrors() } } -void tst_QtJson::fromBinary() -{ - QFile file(testDataDir + "/test.json"); - file.open(QFile::ReadOnly); - QByteArray testJson = file.readAll(); - - QJsonDocument doc = QJsonDocument::fromJson(testJson); - QJsonDocument outdoc = QJsonDocument::fromBinaryData(doc.toBinaryData()); - QVERIFY(!outdoc.isNull()); - QCOMPARE(doc, outdoc); - - QFile bfile(testDataDir + "/test.bjson"); - bfile.open(QFile::ReadOnly); - QByteArray binary = bfile.readAll(); - - QJsonDocument bdoc = QJsonDocument::fromBinaryData(binary); - QVERIFY(!bdoc.isNull()); - QCOMPARE(doc.toVariant(), bdoc.toVariant()); - QCOMPARE(doc, bdoc); -} - -void tst_QtJson::toAndFromBinary_data() -{ - QTest::addColumn<QString>("filename"); - QTest::newRow("test.json") << (testDataDir + "/test.json"); - QTest::newRow("test2.json") << (testDataDir + "/test2.json"); -} - -void tst_QtJson::toAndFromBinary() -{ - QFETCH(QString, filename); - QFile file(filename); - QVERIFY(file.open(QFile::ReadOnly)); - QByteArray data = file.readAll(); - - QJsonDocument doc = QJsonDocument::fromJson(data); - QVERIFY(!doc.isNull()); - QJsonDocument outdoc = QJsonDocument::fromBinaryData(doc.toBinaryData()); - QVERIFY(!outdoc.isNull()); - QCOMPARE(doc, outdoc); -} - -void tst_QtJson::invalidBinaryData() -{ - QDir dir(testDataDir + "/invalidBinaryData"); - QFileInfoList files = dir.entryInfoList(); - for (int i = 0; i < files.size(); ++i) { - if (!files.at(i).isFile()) - continue; - QFile file(files.at(i).filePath()); - file.open(QIODevice::ReadOnly); - QByteArray bytes = file.readAll(); - bytes.squeeze(); - QJsonDocument document = QJsonDocument::fromRawData(bytes.constData(), bytes.size()); - QVERIFY(document.isNull()); - } -} - void tst_QtJson::parseNumbers() { { @@ -1903,12 +2422,12 @@ void tst_QtJson::parseNumbers() QCOMPARE(val.toDouble(), (double)numbers[i].n); } } + // test number parsing + struct Numbers { + const char *str; + double n; + }; { - // test number parsing - struct Numbers { - const char *str; - double n; - }; Numbers numbers [] = { { "0", 0 }, { "1", 1 }, @@ -1924,8 +2443,6 @@ void tst_QtJson::parseNumbers() { "1.1e10", 1.1e10 }, { "1.1e308", 1.1e308 }, { "-1.1e308", -1.1e308 }, - { "1.1e-308", 1.1e-308 }, - { "-1.1e-308", -1.1e-308 }, { "1.1e+308", 1.1e+308 }, { "-1.1e+308", -1.1e+308 }, { "1.e+308", 1.e+308 }, @@ -1937,10 +2454,6 @@ void tst_QtJson::parseNumbers() json += numbers[i].str; json += " ]"; QJsonDocument doc = QJsonDocument::fromJson(json); -#ifdef Q_OS_QNX - if (0 == QString::compare(numbers[i].str, "1.1e-308")) - QEXPECT_FAIL("", "See QTBUG-37066", Abort); -#endif QVERIFY(!doc.isEmpty()); QCOMPARE(doc.isArray(), true); QCOMPARE(doc.isObject(), false); @@ -1951,6 +2464,29 @@ void tst_QtJson::parseNumbers() QCOMPARE(val.toDouble(), numbers[i].n); } } + if constexpr (std::numeric_limits<double>::has_denorm == std::denorm_present) { + Numbers numbers [] = { + { "1.1e-308", 1.1e-308 }, + { "-1.1e-308", -1.1e-308 } + }; + int size = sizeof(numbers)/sizeof(Numbers); + for (int i = 0; i < size; ++i) { + QByteArray json = "[ "; + json += numbers[i].str; + json += " ]"; + QJsonDocument doc = QJsonDocument::fromJson(json); + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), true); + QCOMPARE(doc.isObject(), false); + QJsonArray array = doc.array(); + QCOMPARE(array.size(), 1); + QJsonValue val = array.at(0); + QCOMPARE(val.type(), QJsonValue::Double); + QCOMPARE(val.toDouble(), numbers[i].n); + } + } else { + qInfo("Skipping denormal test as this system's double type lacks support"); + } } void tst_QtJson::parseStrings() @@ -2040,136 +2576,11 @@ void tst_QtJson::parseDuplicateKeys() void tst_QtJson::testParser() { QFile file(testDataDir + "/test.json"); - file.open(QFile::ReadOnly); - QByteArray testJson = file.readAll(); - - QJsonDocument doc = QJsonDocument::fromJson(testJson); - QVERIFY(!doc.isEmpty()); -} - -void tst_QtJson::compactArray() -{ - QJsonArray array; - array.append(QLatin1String("First Entry")); - array.append(QLatin1String("Second Entry")); - array.append(QLatin1String("Third Entry")); - QJsonDocument doc(array); - int s = doc.toBinaryData().size(); - array.removeAt(1); - doc.setArray(array); - QVERIFY(s > doc.toBinaryData().size()); - s = doc.toBinaryData().size(); - QCOMPARE(doc.toJson(), - QByteArray("[\n" - " \"First Entry\",\n" - " \"Third Entry\"\n" - "]\n")); - - array.removeAt(0); - doc.setArray(array); - QVERIFY(s > doc.toBinaryData().size()); - s = doc.toBinaryData().size(); - QCOMPARE(doc.toJson(), - QByteArray("[\n" - " \"Third Entry\"\n" - "]\n")); - - array.removeAt(0); - doc.setArray(array); - QVERIFY(s > doc.toBinaryData().size()); - s = doc.toBinaryData().size(); - QCOMPARE(doc.toJson(), - QByteArray("[\n" - "]\n")); - -} - -void tst_QtJson::compactObject() -{ - QJsonObject object; - object.insert(QLatin1String("Key1"), QLatin1String("First Entry")); - object.insert(QLatin1String("Key2"), QLatin1String("Second Entry")); - object.insert(QLatin1String("Key3"), QLatin1String("Third Entry")); - QJsonDocument doc(object); - int s = doc.toBinaryData().size(); - object.remove(QLatin1String("Key2")); - doc.setObject(object); - QVERIFY(s > doc.toBinaryData().size()); - s = doc.toBinaryData().size(); - QCOMPARE(doc.toJson(), - QByteArray("{\n" - " \"Key1\": \"First Entry\",\n" - " \"Key3\": \"Third Entry\"\n" - "}\n")); - - object.remove(QLatin1String("Key1")); - doc.setObject(object); - QVERIFY(s > doc.toBinaryData().size()); - s = doc.toBinaryData().size(); - QCOMPARE(doc.toJson(), - QByteArray("{\n" - " \"Key3\": \"Third Entry\"\n" - "}\n")); - - object.remove(QLatin1String("Key3")); - doc.setObject(object); - QVERIFY(s > doc.toBinaryData().size()); - s = doc.toBinaryData().size(); - QCOMPARE(doc.toJson(), - QByteArray("{\n" - "}\n")); - -} - -void tst_QtJson::validation() -{ - // this basically tests that we don't crash on corrupt data - QFile file(testDataDir + "/test.json"); QVERIFY(file.open(QFile::ReadOnly)); QByteArray testJson = file.readAll(); - QVERIFY(!testJson.isEmpty()); QJsonDocument doc = QJsonDocument::fromJson(testJson); - QVERIFY(!doc.isNull()); - - QByteArray binary = doc.toBinaryData(); - - // only test the first 1000 bytes. Testing the full file takes too long - for (int i = 0; i < 1000; ++i) { - QByteArray corrupted = binary; - corrupted[i] = char(0xff); - QJsonDocument doc = QJsonDocument::fromBinaryData(corrupted); - if (doc.isNull()) - continue; - QByteArray json = doc.toJson(); - } - - - QFile file2(testDataDir + "/test3.json"); - file2.open(QFile::ReadOnly); - testJson = file2.readAll(); - QVERIFY(!testJson.isEmpty()); - - doc = QJsonDocument::fromJson(testJson); - QVERIFY(!doc.isNull()); - - binary = doc.toBinaryData(); - - for (int i = 0; i < binary.size(); ++i) { - QByteArray corrupted = binary; - corrupted[i] = char(0xff); - QJsonDocument doc = QJsonDocument::fromBinaryData(corrupted); - if (doc.isNull()) - continue; - QByteArray json = doc.toJson(); - - corrupted = binary; - corrupted[i] = 0x00; - doc = QJsonDocument::fromBinaryData(corrupted); - if (doc.isNull()) - continue; - json = doc.toJson(); - } + QVERIFY(!doc.isEmpty()); } void tst_QtJson::assignToDocument() @@ -2222,12 +2633,12 @@ void tst_QtJson::testCompaction() QCOMPARE(obj.size(), 1); QCOMPARE(obj.value(QLatin1String("foo")).toString(), QLatin1String("bar")); - QJsonDocument doc = QJsonDocument::fromBinaryData(QJsonDocument(obj).toBinaryData()); - QVERIFY(!doc.isNull()); - QVERIFY(!doc.isEmpty()); - QCOMPARE(doc.isArray(), false); - QCOMPARE(doc.isObject(), true); - QCOMPARE(doc.object(), obj); + QJsonObject obj2; + + QT_TEST_EQUALITY_OPS(obj, obj2, false); + QT_TEST_EQUALITY_OPS(QJsonObject(), obj2, true); + obj2 = obj; + QT_TEST_EQUALITY_OPS(obj, obj2, true); } void tst_QtJson::testDebugStream() @@ -2317,48 +2728,128 @@ void tst_QtJson::testDebugStream() } } -void tst_QtJson::testCompactionError() +void tst_QtJson::parseEscapes_data() { - QJsonObject schemaObject; - schemaObject.insert("_Type", QLatin1String("_SchemaType")); - schemaObject.insert("name", QLatin1String("Address")); - schemaObject.insert("schema", QJsonObject()); - { - QJsonObject content(schemaObject); - QJsonDocument doc(content); - QVERIFY(!doc.isNull()); - QByteArray hash = QCryptographicHash::hash(doc.toBinaryData(), QCryptographicHash::Md5).toHex(); - schemaObject.insert("_Version", QString::fromLatin1(hash.constData(), hash.size())); - } + QTest::addColumn<QByteArray>("json"); + QTest::addColumn<QString>("result"); + + auto addUnicodeRow = [](char32_t u) { + char buf[32]; // more than enough + char *ptr = buf; + const QString result = QString::fromUcs4(&u, 1); + for (QChar c : result) + ptr += snprintf(ptr, std::end(buf) - ptr, "\\u%04x", c.unicode()); + QTest::addRow("U+%04X", u) << "[\"" + QByteArray(buf) + "\"]" << result; + }; - QJsonObject schema; - schema.insert("streetNumber", schema.value("number").toObject()); - schemaObject.insert("schema", schema); - { - QJsonObject content(schemaObject); - content.remove("_Uuid"); - content.remove("_Version"); - QJsonDocument doc(content); - QVERIFY(!doc.isNull()); - QByteArray hash = QCryptographicHash::hash(doc.toBinaryData(), QCryptographicHash::Md5).toHex(); - schemaObject.insert("_Version", QString::fromLatin1(hash.constData(), hash.size())); + char singleCharJson[] = R"(["\x"])"; + Q_ASSERT(singleCharJson[3] == 'x'); + auto makeSingleCharEscape = [&singleCharJson](char c) { + singleCharJson[3] = char(c); + return QByteArray(singleCharJson, std::size(singleCharJson) - 1); + }; + + QTest::addRow("quote") << makeSingleCharEscape('"') << "\""; + QTest::addRow("backslash") << makeSingleCharEscape('\\') << "\\"; + QTest::addRow("slash") << makeSingleCharEscape('/') << "/"; + QTest::addRow("backspace") << makeSingleCharEscape('b') << "\b"; + QTest::addRow("form-feed") << makeSingleCharEscape('f') << "\f"; + QTest::addRow("newline") << makeSingleCharEscape('n') << "\n"; + QTest::addRow("carriage-return") << makeSingleCharEscape('r') << "\r"; + QTest::addRow("tab") << makeSingleCharEscape('t') << "\t"; + + // we're not going to exhaustively test all Unicode possibilities + for (char16_t c = 0; c < 0x21; ++c) + addUnicodeRow(c); + addUnicodeRow(u'\u007f'); + addUnicodeRow(u'\u0080'); + addUnicodeRow(u'\u00ff'); + addUnicodeRow(u'\u0100'); + addUnicodeRow(char16_t(0xd800)); + addUnicodeRow(char16_t(0xdc00)); + addUnicodeRow(u'\ufffe'); + addUnicodeRow(u'\uffff'); + addUnicodeRow(U'\U00010000'); + addUnicodeRow(U'\U00100000'); + addUnicodeRow(U'\U0010ffff'); + + QTest::addRow("mojibake-utf8") << QByteArrayLiteral(R"(["A\u00e4\u00C4"])") + << QStringLiteral(u"A\u00e4\u00C4"); + + // characters for which, preceded by backslash, it is a valid (recognized) + // escape sequence (should match the above list) + static const char validEscapes[] = "\"\\/bfnrtu"; + for (int i = 0; i <= 0xff; ++i) { + if (i && strchr(validEscapes, i)) + continue; + QTest::addRow("invalid-uchar-0x%02x", i) << makeSingleCharEscape(i) << QString(char16_t(i)); } } -void tst_QtJson::parseUnicodeEscapes() +void tst_QtJson::parseEscapes() { - const QByteArray json = "[ \"A\\u00e4\\u00C4\" ]"; + QFETCH(QByteArray, json); + QFETCH(QString, result); QJsonDocument doc = QJsonDocument::fromJson(json); QJsonArray array = doc.array(); - QString result = QLatin1String("A"); - result += QChar(0xe4); - result += QChar(0xc4); - QCOMPARE(array.first().toString(), result); } +void tst_QtJson::makeEscapes_data() +{ + QTest::addColumn<QString>("input"); + QTest::addColumn<QByteArray>("result"); + + auto addUnicodeRow = [](char16_t c) { + char buf[32]; // more than enough + snprintf(buf, std::size(buf), "\\u%04x", c); + QTest::addRow("U+%04X", c) << QString(c) << QByteArray(buf); + }; + + + QTest::addRow("quote") << "\"" << QByteArray(R"(\")"); + QTest::addRow("backslash") << "\\" << QByteArray(R"(\\)"); + //QTest::addRow("slash") << "/" << QByteArray(R"(\/)"); // does not get escaped + QTest::addRow("backspace") << "\b" << QByteArray(R"(\b)"); + QTest::addRow("form-feed") << "\f" << QByteArray(R"(\f)"); + QTest::addRow("newline") << "\n" << QByteArray(R"(\n)"); + QTest::addRow("carriage-return") << "\r" << QByteArray(R"(\r)"); + QTest::addRow("tab") << "\t" << QByteArray(R"(\t)"); + + // control characters other than the above + for (char16_t c = 0; c < 0x20; ++c) { + if (c && strchr("\b\f\n\r\t", c)) + continue; + addUnicodeRow(c); + } + // unpaired surrogates + addUnicodeRow(char16_t(0xd800)); + addUnicodeRow(char16_t(0xdc00)); + + QString improperlyPaired; + improperlyPaired.append(char16_t(0xdc00)); + improperlyPaired.append(char16_t(0xd800)); + QTest::addRow("inverted-surrogates") << improperlyPaired << QByteArray("\\udc00\\ud800"); +} + +void tst_QtJson::makeEscapes() +{ + QFETCH(QString, input); + QFETCH(QByteArray, result); + + QJsonArray array = { input }; + QByteArray json = QJsonDocument(array).toJson(QJsonDocument::Compact); + + QVERIFY(json.startsWith("[\"")); + result.prepend("[\""); + QVERIFY(json.endsWith("\"]")); + result.append("\"]"); + + QCOMPARE(json, result); +} + void tst_QtJson::assignObjects() { const char *json = @@ -2427,57 +2918,57 @@ void tst_QtJson::testDetachBug() void tst_QtJson::valueEquals() { QCOMPARE(QJsonValue(), QJsonValue()); - QVERIFY(QJsonValue() != QJsonValue(QJsonValue::Undefined)); - QVERIFY(QJsonValue() != QJsonValue(true)); - QVERIFY(QJsonValue() != QJsonValue(1.)); - QVERIFY(QJsonValue() != QJsonValue(QJsonArray())); - QVERIFY(QJsonValue() != QJsonValue(QJsonObject())); + QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(QJsonValue::Undefined), false); + QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(true), false); + QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(1.), false); + QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(QJsonArray()), false); + QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(QJsonObject()), false); QCOMPARE(QJsonValue(true), QJsonValue(true)); - QVERIFY(QJsonValue(true) != QJsonValue(false)); - QVERIFY(QJsonValue(true) != QJsonValue(QJsonValue::Undefined)); - QVERIFY(QJsonValue(true) != QJsonValue()); - QVERIFY(QJsonValue(true) != QJsonValue(1.)); - QVERIFY(QJsonValue(true) != QJsonValue(QJsonArray())); - QVERIFY(QJsonValue(true) != QJsonValue(QJsonObject())); + QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(false), false); + QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(QJsonValue::Undefined), false); + QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(), false); + QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(1.), false); + QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(QJsonArray()), false); + QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(QJsonObject()), false); QCOMPARE(QJsonValue(1), QJsonValue(1)); - QVERIFY(QJsonValue(1) != QJsonValue(2)); + QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(2), false); QCOMPARE(QJsonValue(1), QJsonValue(1.)); - QVERIFY(QJsonValue(1) != QJsonValue(1.1)); - QVERIFY(QJsonValue(1) != QJsonValue(QJsonValue::Undefined)); - QVERIFY(QJsonValue(1) != QJsonValue()); - QVERIFY(QJsonValue(1) != QJsonValue(true)); - QVERIFY(QJsonValue(1) != QJsonValue(QJsonArray())); - QVERIFY(QJsonValue(1) != QJsonValue(QJsonObject())); + QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(1.1), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(QJsonValue::Undefined), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(true), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(QJsonArray()), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(QJsonObject()), false); QCOMPARE(QJsonValue(1.), QJsonValue(1.)); - QVERIFY(QJsonValue(1.) != QJsonValue(2.)); - QVERIFY(QJsonValue(1.) != QJsonValue(QJsonValue::Undefined)); - QVERIFY(QJsonValue(1.) != QJsonValue()); - QVERIFY(QJsonValue(1.) != QJsonValue(true)); - QVERIFY(QJsonValue(1.) != QJsonValue(QJsonArray())); - QVERIFY(QJsonValue(1.) != QJsonValue(QJsonObject())); + QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(2.), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(QJsonValue::Undefined), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(true), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(QJsonArray()), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(QJsonObject()), false); QCOMPARE(QJsonValue(QJsonArray()), QJsonValue(QJsonArray())); QJsonArray nonEmptyArray; nonEmptyArray.append(true); - QVERIFY(QJsonValue(QJsonArray()) != nonEmptyArray); - QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(QJsonValue::Undefined)); - QVERIFY(QJsonValue(QJsonArray()) != QJsonValue()); - QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(true)); - QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(1.)); - QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(QJsonObject())); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), nonEmptyArray, false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(QJsonValue::Undefined), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(true), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(1.), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(QJsonObject()), false); QCOMPARE(QJsonValue(QJsonObject()), QJsonValue(QJsonObject())); QJsonObject nonEmptyObject; nonEmptyObject.insert("Key", true); - QVERIFY(QJsonValue(QJsonObject()) != nonEmptyObject); - QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(QJsonValue::Undefined)); - QVERIFY(QJsonValue(QJsonObject()) != QJsonValue()); - QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(true)); - QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(1.)); - QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(QJsonArray())); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), nonEmptyObject, false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(QJsonValue::Undefined), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(true), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(1.), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(QJsonArray()), false); QCOMPARE(QJsonValue("foo"), QJsonValue(QLatin1String("foo"))); QCOMPARE(QJsonValue("foo"), QJsonValue(QString("foo"))); @@ -2553,6 +3044,12 @@ void tst_QtJson::objectEquals() QCOMPARE(QJsonValue(left) != QJsonValue(right), !result); QCOMPARE(QJsonValue(right) == QJsonValue(left), result); QCOMPARE(QJsonValue(right) != QJsonValue(left), !result); + + // The same, but from a QJsonDocument perspective + QCOMPARE(QJsonDocument(left) == QJsonDocument(right), result); + QCOMPARE(QJsonDocument(left) != QJsonDocument(right), !result); + QCOMPARE(QJsonDocument(right) == QJsonDocument(left), result); + QCOMPARE(QJsonDocument(right) != QJsonDocument(left), !result); } void tst_QtJson::arrayEquals_data() @@ -2606,12 +3103,65 @@ void tst_QtJson::arrayEquals() QCOMPARE(QJsonValue(left) != QJsonValue(right), !result); QCOMPARE(QJsonValue(right) == QJsonValue(left), result); QCOMPARE(QJsonValue(right) != QJsonValue(left), !result); + + // The same but from QJsonDocument perspective + QCOMPARE(QJsonDocument(left) == QJsonDocument(right), result); + QCOMPARE(QJsonDocument(left) != QJsonDocument(right), !result); + QCOMPARE(QJsonDocument(right) == QJsonDocument(left), result); + QCOMPARE(QJsonDocument(right) != QJsonDocument(left), !result); +} + +void tst_QtJson::documentEquals_data() +{ + QTest::addColumn<QJsonDocument>("left"); + QTest::addColumn<QJsonDocument>("right"); + QTest::addColumn<bool>("result"); + + QTest::newRow("two defaults") << QJsonDocument() << QJsonDocument() << true; + + QJsonDocument emptyobj(QJsonObject{}); + QJsonDocument emptyarr(QJsonArray{}); + QTest::newRow("emptyarray vs default") << emptyarr << QJsonDocument() << false; + QTest::newRow("emptyobject vs default") << emptyobj << QJsonDocument() << false; + QTest::newRow("emptyarray vs emptyobject") << emptyarr << emptyobj << false; + + QJsonDocument array1(QJsonArray{1}); + QJsonDocument array2(QJsonArray{2}); + QTest::newRow("emptyarray vs emptyarray") << emptyarr << emptyarr << true; + QTest::newRow("emptyarray vs array") << emptyarr << array1 << false; + QTest::newRow("array vs array") << array1 << array1 << true; + QTest::newRow("array vs otherarray") << array1 << array2 << false; + + QJsonDocument object1(QJsonObject{{"hello", "world"}}); + QJsonDocument object2(QJsonObject{{"hello", 2}}); + QTest::newRow("emptyobject vs emptyobject") << emptyobj << emptyobj << true; + QTest::newRow("emptyobject vs object") << emptyobj << object1 << false; + QTest::newRow("object vs object") << object1 << object1 << true; + QTest::newRow("object vs otherobject") << object1 << object2 << false; + + QTest::newRow("object vs array") << array1 << object1 << false; +} + +void tst_QtJson::documentEquals() +{ + QFETCH(QJsonDocument, left); + QFETCH(QJsonDocument, right); + QFETCH(bool, result); + + QCOMPARE(left == right, result); + QCOMPARE(right == left, result); + + // invariants checks + QCOMPARE(left, left); + QCOMPARE(right, right); + QCOMPARE(left != right, !result); + QCOMPARE(right != left, !result); } void tst_QtJson::bom() { QFile file(testDataDir + "/bom.json"); - file.open(QFile::ReadOnly); + QVERIFY(file.open(QFile::ReadOnly)); QByteArray json = file.readAll(); // Import json document into a QJsonDocument @@ -2686,6 +3236,8 @@ void tst_QtJson::longStrings() // test around 15 and 16 bit boundaries, as these are limits // in the data structures (for Latin1String in qjson_p.h) QString s(0x7ff0, 'a'); + QByteArray ba(0x7ff0, 'a'); + ba.append(0x8010 - 0x7ff0, 'c'); for (int i = 0x7ff0; i < 0x8010; i++) { s.append(QLatin1Char('c')); @@ -2702,9 +3254,21 @@ void tst_QtJson::longStrings() /* ... and a QByteArray from the QJsonDocument */ QByteArray a2 = d2.toJson(); QCOMPARE(a1, a2); + + // Test long keys + QJsonObject o1, o2; + o1[s] = 42; + o2[QLatin1String(ba.data(), i + 1)] = 42; + d1.setObject(o1); + d2.setObject(o2); + a1 = d1.toJson(); + a2 = d2.toJson(); + QCOMPARE(a1, a2); } s = QString(0xfff0, 'a'); + ba = QByteArray(0xfff0, 'a'); + ba.append(0x10010 - 0xfff0, 'c'); for (int i = 0xfff0; i < 0x10010; i++) { s.append(QLatin1Char('c')); @@ -2721,6 +3285,16 @@ void tst_QtJson::longStrings() /* ... and a QByteArray from the QJsonDocument */ QByteArray a2 = d2.toJson(); QCOMPARE(a1, a2); + + // Test long keys + QJsonObject o1, o2; + o1[s] = 42; + o2[QLatin1String(ba.data(), i + 1)] = 42; + d1.setObject(o1); + d2.setObject(o2); + a1 = d1.toJson(); + a2 = d2.toJson(); + QCOMPARE(a1, a2); } } @@ -2743,9 +3317,6 @@ void tst_QtJson::testJsonValueRefDefault() void tst_QtJson::arrayInitializerList() { -#ifndef Q_COMPILER_INITIALIZER_LISTS - QSKIP("initializer_list is enabled only with c++11 support"); -#else QVERIFY(QJsonArray{}.isEmpty()); QCOMPARE(QJsonArray{"one"}.count(), 1); QCOMPARE(QJsonArray{1}.count(), 1); @@ -2791,14 +3362,10 @@ void tst_QtJson::arrayInitializerList() QCOMPARE(QJsonValue(a43["one"]), QJsonValue(1)); } } -#endif } void tst_QtJson::objectInitializerList() { -#ifndef Q_COMPILER_INITIALIZER_LISTS - QSKIP("initializer_list is enabled only with c++11 support"); -#else QVERIFY(QJsonObject{}.isEmpty()); { // one property @@ -2838,7 +3405,6 @@ void tst_QtJson::objectInitializerList() QCOMPARE(QJsonValue(nested[0]), QJsonValue("innerValue")); QCOMPARE(QJsonValue(nested[1]), QJsonValue(2.1)); } -#endif } void tst_QtJson::unicodeKeys() @@ -2916,7 +3482,7 @@ void tst_QtJson::documentFromVariant() // As JSON arrays they should be equal. QCOMPARE(da1.array(), da2.array()); - + QT_TEST_EQUALITY_OPS(da1, da2, true); QMap <QString, QVariant> map; map["key"] = string; @@ -2932,6 +3498,7 @@ void tst_QtJson::documentFromVariant() // As JSON objects they should be equal. QCOMPARE(do1.object(), do2.object()); + QT_TEST_EQUALITY_OPS(do1, do2, true); } void tst_QtJson::parseErrorOffset_data() @@ -3011,5 +3578,420 @@ void tst_QtJson::implicitDocumentType() QCOMPARE(arrayDocument[-1].toInt(123), 123); } +void tst_QtJson::streamSerializationQJsonDocument_data() +{ + QTest::addColumn<QJsonDocument>("document"); + QTest::newRow("empty") << QJsonDocument(); + QTest::newRow("object") << QJsonDocument(QJsonObject{{"value", 42}}); +} + +void tst_QtJson::streamSerializationQJsonDocument() +{ + // Check interface only, implementation is tested through to and from + // json functions. + QByteArray buffer; + QFETCH(QJsonDocument, document); + QJsonDocument output; + QDataStream save(&buffer, QIODevice::WriteOnly); + save << document; + QDataStream load(buffer); + load >> output; + QCOMPARE(output, document); + QT_TEST_EQUALITY_OPS(output, document, true); +} + +void tst_QtJson::streamSerializationQJsonArray_data() +{ + QTest::addColumn<QJsonArray>("array"); + QTest::newRow("empty") << QJsonArray(); + QTest::newRow("values") << QJsonArray{665, 666, 667}; +} + +void tst_QtJson::streamSerializationQJsonArray() +{ + // Check interface only, implementation is tested through to and from + // json functions. + QByteArray buffer; + QFETCH(QJsonArray, array); + QJsonArray output; + QDataStream save(&buffer, QIODevice::WriteOnly); + save << array; + QDataStream load(buffer); + load >> output; + QCOMPARE(output, array); +} + +void tst_QtJson::streamSerializationQJsonObject_data() +{ + QTest::addColumn<QJsonObject>("object"); + QTest::newRow("empty") << QJsonObject(); + QTest::newRow("non-empty") << QJsonObject{{"foo", 665}, {"bar", 666}}; +} + +void tst_QtJson::streamSerializationQJsonObject() +{ + // Check interface only, implementation is tested through to and from + // json functions. + QByteArray buffer; + QFETCH(QJsonObject, object); + QJsonObject output; + QDataStream save(&buffer, QIODevice::WriteOnly); + save << object; + QDataStream load(buffer); + load >> output; + QCOMPARE(output, object); +} + +void tst_QtJson::streamSerializationQJsonValue_data() +{ + QTest::addColumn<QJsonValue>("value"); + QTest::newRow("double") << QJsonValue{665}; + QTest::newRow("bool") << QJsonValue{true}; + QTest::newRow("string") << QJsonValue{QStringLiteral("bum")}; + QTest::newRow("array") << QJsonValue{QJsonArray{12,1,5,6,7}}; + QTest::newRow("object") << QJsonValue{QJsonObject{{"foo", 665}, {"bar", 666}}}; + // test json escape sequence + QTest::newRow("array with 0xD800") << QJsonValue(QJsonArray{QString(QChar(0xD800))}); + QTest::newRow("array with 0xDF06,0xD834") << QJsonValue(QJsonArray{QString(QChar(0xDF06)).append(QChar(0xD834))}); +} + +void tst_QtJson::streamSerializationQJsonValue() +{ + QByteArray buffer; + QFETCH(QJsonValue, value); + QJsonValue output; + QDataStream save(&buffer, QIODevice::WriteOnly); + save << value; + QDataStream load(buffer); + load >> output; + QCOMPARE(output, value); +} + +void tst_QtJson::streamSerializationQJsonValueEmpty() +{ + QByteArray buffer; + { + QJsonValue undef{QJsonValue::Undefined}; + QDataStream save(&buffer, QIODevice::WriteOnly); + save << undef; + QDataStream load(buffer); + QJsonValue output; + load >> output; + QVERIFY(output.isUndefined()); + } + { + QJsonValue null{QJsonValue::Null}; + QDataStream save(&buffer, QIODevice::WriteOnly); + save << null; + QDataStream load(buffer); + QJsonValue output; + load >> output; + QVERIFY(output.isNull()); + } +} + +void tst_QtJson::streamVariantSerialization() +{ + // Check interface only, implementation is tested through to and from + // json functions. + QByteArray buffer; + { + QJsonDocument objectDoc(QJsonArray{665, 666, 667}); + QVariant output; + QVariant variant(objectDoc); + QDataStream save(&buffer, QIODevice::WriteOnly); + save << variant; + QDataStream load(buffer); + load >> output; + QCOMPARE(output.userType(), QMetaType::QJsonDocument); + QCOMPARE(output.toJsonDocument(), objectDoc); + } + { + QJsonArray array{665, 666, 667}; + QVariant output; + QVariant variant(array); + QDataStream save(&buffer, QIODevice::WriteOnly); + save << variant; + QDataStream load(buffer); + load >> output; + QCOMPARE(output.userType(), QMetaType::QJsonArray); + QCOMPARE(output.toJsonArray(), array); + } + { + QJsonObject obj{{"foo", 42}}; + QVariant output; + QVariant variant(obj); + QDataStream save(&buffer, QIODevice::WriteOnly); + save << variant; + QDataStream load(buffer); + load >> output; + QCOMPARE(output.userType(), QMetaType::QJsonObject); + QCOMPARE(output.toJsonObject(), obj); + } + { + QJsonValue value{42}; + QVariant output; + QVariant variant(value); + QDataStream save(&buffer, QIODevice::WriteOnly); + save << variant; + QDataStream load(buffer); + load >> output; + QCOMPARE(output.userType(), QMetaType::QJsonValue); + QCOMPARE(output.toJsonValue(), value); + } +} + +void tst_QtJson::escapeSurrogateCodePoints_data() +{ + QTest::addColumn<QString>("str"); + QTest::addColumn<QByteArray>("escStr"); + QTest::newRow("0xD800") << QString(QChar(0xD800)) << QByteArray("\\ud800"); + QTest::newRow("0xDF06,0xD834") << QString(QChar(0xDF06)).append(QChar(0xD834)) << QByteArray("\\udf06\\ud834"); +} + +void tst_QtJson::escapeSurrogateCodePoints() +{ + QFETCH(QString, str); + QFETCH(QByteArray, escStr); + QJsonArray array; + array.append(str); + QByteArray buffer; + QDataStream save(&buffer, QIODevice::WriteOnly); + save << array; + // verify the buffer has escaped values + QVERIFY(buffer.contains(escStr)); +} + +void tst_QtJson::fromToVariantConversions_data() +{ + QTest::addColumn<QVariant>("variant"); + QTest::addColumn<QJsonValue>("json"); + QTest::addColumn<QVariant>("jsonToVariant"); + + QByteArray utf8("\xC4\x90\xC4\x81\xC5\xA3\xC3\xA2"); // Đāţâ + QDateTime dt = QDateTime::currentDateTimeUtc(); + QUuid uuid = QUuid::createUuid(); + + constexpr qlonglong maxInt = std::numeric_limits<qlonglong>::max(); + constexpr qlonglong minInt = std::numeric_limits<qlonglong>::min(); + constexpr double maxDouble = std::numeric_limits<double>::max(); + constexpr double minDouble = std::numeric_limits<double>::min(); + + QTest::newRow("default") << QVariant() << QJsonValue(QJsonValue::Null) + << QVariant::fromValue(nullptr); + QTest::newRow("nullptr") << QVariant::fromValue(nullptr) << QJsonValue(QJsonValue::Null) + << QVariant::fromValue(nullptr); + QTest::newRow("bool") << QVariant(true) << QJsonValue(true) << QVariant(true); + QTest::newRow("int pos") << QVariant(123) << QJsonValue(123) << QVariant(qlonglong(123)); + QTest::newRow("int neg") << QVariant(-123) << QJsonValue(-123) << QVariant(qlonglong(-123)); + QTest::newRow("int big pos") << QVariant((1ll << 55) +1) << QJsonValue((1ll << 55) + 1) + << QVariant(qlonglong((1ll << 55) + 1)); + QTest::newRow("int big neg") << QVariant(-(1ll << 55) + 1) << QJsonValue(-(1ll << 55) + 1) + << QVariant(qlonglong(-(1ll << 55) + 1)); + QTest::newRow("int max") << QVariant(maxInt) << QJsonValue(maxInt) << QVariant(maxInt); + QTest::newRow("int min") << QVariant(minInt) << QJsonValue(minInt) << QVariant(minInt); + QTest::newRow("double pos") << QVariant(123.) << QJsonValue(123.) << QVariant(qlonglong(123.)); + QTest::newRow("double neg") << QVariant(-123.) << QJsonValue(-123.) + << QVariant(qlonglong(-123.)); + QTest::newRow("double big") << QVariant(maxDouble - 1000) << QJsonValue(maxDouble - 1000) + << QVariant(maxDouble - 1000); + QTest::newRow("double max") << QVariant(maxDouble) << QJsonValue(maxDouble) + << QVariant(maxDouble); + QTest::newRow("double min") << QVariant(minDouble) << QJsonValue(minDouble) + << QVariant(minDouble); + QTest::newRow("double big neg") << QVariant(1000 - maxDouble) << QJsonValue(1000 - maxDouble) + << QVariant(1000 - maxDouble); + QTest::newRow("double max neg") << QVariant(-maxDouble) << QJsonValue(-maxDouble) + << QVariant(-maxDouble); + QTest::newRow("double min neg") << QVariant(-minDouble) << QJsonValue(-minDouble) + << QVariant(-minDouble); + + QTest::newRow("string null") << QVariant(QString()) << QJsonValue(QString()) + << QVariant(QString()); + QTest::newRow("string empty") << QVariant(QString("")) << QJsonValue(QString("")) + << QVariant(QString("")); + QTest::newRow("string ascii") << QVariant(QString("Data")) << QJsonValue(QString("Data")) + << QVariant(QString("Data")); + QTest::newRow("string utf8") << QVariant(QString(utf8)) << QJsonValue(QString(utf8)) + << QVariant(QString(utf8)); + + QTest::newRow("bytearray null") << QVariant(QByteArray()) << QJsonValue(QJsonValue::Null) + << QVariant::fromValue(nullptr); + QTest::newRow("bytearray empty") << QVariant(QByteArray()) << QJsonValue(QJsonValue::Null) + << QVariant::fromValue(nullptr); + QTest::newRow("bytearray ascii") << QVariant(QByteArray("Data")) << QJsonValue(QString("Data")) + << QVariant(QString("Data")); + QTest::newRow("bytearray utf8") << QVariant(utf8) << QJsonValue(QString(utf8)) + << QVariant(QString(utf8)); + + QTest::newRow("datetime") << QVariant(dt) << QJsonValue(dt.toString(Qt::ISODateWithMs)) + << QVariant(dt.toString(Qt::ISODateWithMs)); + QTest::newRow("url") << QVariant(QUrl("http://example.com/{q}")) + << QJsonValue("http://example.com/%7Bq%7D") + << QVariant(QString("http://example.com/%7Bq%7D")); + QTest::newRow("uuid") << QVariant(QUuid(uuid)) + << QJsonValue(uuid.toString(QUuid::WithoutBraces)) + << QVariant(uuid.toString(QUuid::WithoutBraces)); + QTest::newRow("regexp") << QVariant(QRegularExpression(".")) << QJsonValue(QJsonValue::Null) + << QVariant::fromValue(nullptr); + + QTest::newRow("inf") << QVariant(qInf()) << QJsonValue(QJsonValue::Null) + << QVariant::fromValue(nullptr); + QTest::newRow("-inf") << QVariant(-qInf()) << QJsonValue(QJsonValue::Null) + << QVariant::fromValue(nullptr); + QTest::newRow("NaN") << QVariant(qQNaN()) << QJsonValue(QJsonValue::Null) + << QVariant::fromValue(nullptr); + + static_assert(std::numeric_limits<double>::digits <= 63, + "double is too big on this platform, this test would fail"); + constexpr quint64 Threshold = Q_UINT64_C(1) << 63; + const qulonglong ulongValue = qulonglong(Threshold) + 1; + const double uLongToDouble = Threshold; + QTest::newRow("ulonglong") << QVariant(ulongValue) << QJsonValue(uLongToDouble) + << QVariant(uLongToDouble); +} + +void tst_QtJson::fromToVariantConversions() +{ + QFETCH(QVariant, variant); + QFETCH(QJsonValue, json); + QFETCH(QVariant, jsonToVariant); + + QVariant variantFromJson(json); + QVariant variantFromJsonArray(QJsonArray { json }); + QVariant variantFromJsonObject(QVariantMap { { "foo", variant } }); + + QJsonObject object { QPair<QString, QJsonValue>("foo", json) }; + + // QJsonValue <> QVariant + { + QCOMPARE(QJsonValue::fromVariant(variant), json); + + // test the same for QVariant from QJsonValue/QJsonArray/QJsonObject + QCOMPARE(QJsonValue::fromVariant(variantFromJson), json); + QCOMPARE(QJsonValue::fromVariant(variantFromJsonArray), QJsonArray { json }); + QCOMPARE(QJsonValue::fromVariant(variantFromJsonObject), object); + + // QJsonValue to variant + QCOMPARE(json.toVariant(), jsonToVariant); + QCOMPARE(json.toVariant().userType(), jsonToVariant.userType()); + + // variant to QJsonValue + QCOMPARE(QVariant(json).toJsonValue(), json); + } + + // QJsonArray <> QVariantList + { + QCOMPARE(QJsonArray::fromVariantList(QVariantList { variant }), QJsonArray { json }); + + // test the same for QVariantList from QJsonValue/QJsonArray/QJsonObject + QCOMPARE(QJsonArray::fromVariantList(QVariantList { variantFromJson }), + QJsonArray { json }); + QCOMPARE(QJsonArray::fromVariantList(QVariantList { variantFromJsonArray }), + QJsonArray {{ QJsonArray { json } }}); + QCOMPARE(QJsonArray::fromVariantList(QVariantList { variantFromJsonObject }), + QJsonArray { object }); + + // QJsonArray to variant + QCOMPARE(QJsonArray { json }.toVariantList(), QVariantList { jsonToVariant }); + // variant to QJsonArray + QCOMPARE(QVariant(QJsonArray { json }).toJsonArray(), QJsonArray { json }); + } + + // QJsonObject <> QVariantMap + { + QCOMPARE(QJsonObject::fromVariantMap(QVariantMap { { "foo", variant } }), object); + + // test the same for QVariantMap from QJsonValue/QJsonArray/QJsonObject + QCOMPARE(QJsonObject::fromVariantMap(QVariantMap { { "foo", variantFromJson } }), object); + + QJsonObject nestedArray { QPair<QString, QJsonArray>("bar", QJsonArray { json }) }; + QJsonObject nestedObject { QPair<QString, QJsonObject>("bar", object) }; + QCOMPARE(QJsonObject::fromVariantMap(QVariantMap { { "bar", variantFromJsonArray } }), + nestedArray); + QCOMPARE(QJsonObject::fromVariantMap(QVariantMap { { "bar", variantFromJsonObject } }), + nestedObject); + + // QJsonObject to variant + QCOMPARE(object.toVariantMap(), QVariantMap({ { "foo", jsonToVariant } })); + // variant to QJsonObject + QCOMPARE(QVariant(object).toJsonObject(), object); + } +} + +void tst_QtJson::testIteratorComparison() +{ + QJsonObject t = QJsonObject::fromVariantHash({ + { QStringLiteral("a"), QVariant(12) }, + { QStringLiteral("b"), QVariant(13) } + }); + + QVERIFY(t.begin() == t.begin()); + QVERIFY(t.begin() <= t.begin()); + QVERIFY(t.begin() >= t.begin()); + QVERIFY(!(t.begin() != t.begin())); + QVERIFY(!(t.begin() < t.begin())); + QVERIFY(!(t.begin() > t.begin())); + + QVERIFY(!(t.begin() == t.end())); + QVERIFY(t.begin() <= t.end()); + QVERIFY(!(t.begin() >= t.end())); + QVERIFY(t.begin() != t.end()); + QVERIFY(t.begin() < t.end()); + QVERIFY(!(t.begin() > t.end())); + + QVERIFY(!(t.end() == t.begin())); + QVERIFY(!(t.end() <= t.begin())); + QVERIFY(t.end() >= t.begin()); + QVERIFY(t.end() != t.begin()); + QVERIFY(!(t.end() < t.begin())); + QVERIFY(t.end() > t.begin()); +} + +void tst_QtJson::noLeakOnNameClash_data() +{ + QTest::addColumn<QString>("fileName"); + QTest::addColumn<QByteArray>("result"); + QTest::addRow("simple") + << QStringLiteral("simple.duplicates.json") + << QByteArray(R"({"": 0})"); + QTest::addRow("test") + << QStringLiteral("test.duplicates.json") + << QByteArray(R"([ + "JSON Test Pattern pass1", {"a": ["array with 1 element"]}, {}, [], -42, true, + false, null, {"a": "A key can be any string"}, 0.5, 98.6, 99.44, 1066, 10, 1, + 0.1, 1, 2, 2, "rosebud", {"a": "bar"}, {"a": {"a": 2000}}, {"a": {"a": 2000}}, + {"a": {"a": 2000}}, {"a": {"a": 2000}} + ])"); + QTest::addRow("test3") + << QStringLiteral("test3.duplicates.json") + << QByteArray(R"({"a": [{"a": "212 555-1234"}, {"a": "646 555-4567"}]})"); +} + +void tst_QtJson::noLeakOnNameClash() +{ + QFETCH(QString, fileName); + QFETCH(QByteArray, result); + + QFile file(testDataDir + u'/' + fileName); + QVERIFY(file.open(QFile::ReadOnly)); + QByteArray testJson = file.readAll(); + QVERIFY(!testJson.isEmpty()); + + QJsonParseError error; + + // Retains the last one of each set of duplicate keys. + QJsonDocument doc = QJsonDocument::fromJson(testJson, &error); + QVERIFY2(!doc.isNull(), qPrintable(error.errorString())); + QJsonDocument expected = QJsonDocument::fromJson(result, &error); + QVERIFY2(!expected.isNull(), qPrintable(error.errorString())); + + QCOMPARE(doc, expected); + QT_TEST_EQUALITY_OPS(doc, expected, true); + + // It should not leak. + // In particular it should not forget to deref the container for the inner objects. +} + QTEST_MAIN(tst_QtJson) #include "tst_qtjson.moc" |