Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/miloyip/rapidjson.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorMilo Yip <miloyip@gmail.com>2021-07-09 05:57:40 +0300
committerGitHub <noreply@github.com>2021-07-09 05:57:40 +0300
commit48fbd8cd202ca54031fe799db2ad44ffa8e77c13 (patch)
treeedbe4bef64b2191a8bed455b02ad31e7a4a57ed8 /test
parentb557259f8813bed2d79e83bd2f92eb673579a82c (diff)
parent8d16abd980122b4d4b24aad345efb95373c11c18 (diff)
Merge pull request #1848 from smhdfdl/id-and-ref
Fix issue 1843 - support Id keyword
Diffstat (limited to 'test')
-rw-r--r--test/unittest/CMakeLists.txt1
-rw-r--r--test/unittest/pointertest.cpp49
-rw-r--r--test/unittest/schematest.cpp379
-rw-r--r--test/unittest/uritest.cpp718
4 files changed, 1139 insertions, 8 deletions
diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt
index 0a8f2a31..565ed982 100644
--- a/test/unittest/CMakeLists.txt
+++ b/test/unittest/CMakeLists.txt
@@ -27,6 +27,7 @@ set(UNITTEST_SOURCES
stringbuffertest.cpp
strtodtest.cpp
unittest.cpp
+ uritest.cpp
valuetest.cpp
writertest.cpp)
diff --git a/test/unittest/pointertest.cpp b/test/unittest/pointertest.cpp
index 50c678e1..342086dd 100644
--- a/test/unittest/pointertest.cpp
+++ b/test/unittest/pointertest.cpp
@@ -650,6 +650,52 @@ TEST(Pointer, Create) {
}
}
+static const char kJsonIds[] = "{\n"
+ " \"id\": \"/root/\","
+ " \"foo\":[\"bar\", \"baz\", {\"id\": \"inarray\", \"child\": 1}],\n"
+ " \"int\" : 2,\n"
+ " \"str\" : \"val\",\n"
+ " \"obj\": {\"id\": \"inobj\", \"child\": 3},\n"
+ " \"jbo\": {\"id\": true, \"child\": 4}\n"
+ "}";
+
+
+TEST(Pointer, GetUri) {
+ CrtAllocator allocator;
+ Document d;
+ d.Parse(kJsonIds);
+ Pointer::UriType doc("http://doc");
+ Pointer::UriType root("http://doc/root/");
+ Pointer::UriType empty = Pointer::UriType();
+
+ EXPECT_TRUE(Pointer("").GetUri(d, doc) == doc);
+ EXPECT_TRUE(Pointer("/foo").GetUri(d, doc) == root);
+ EXPECT_TRUE(Pointer("/foo/0").GetUri(d, doc) == root);
+ EXPECT_TRUE(Pointer("/foo/2").GetUri(d, doc) == root);
+ EXPECT_TRUE(Pointer("/foo/2/child").GetUri(d, doc) == Pointer::UriType("http://doc/root/inarray"));
+ EXPECT_TRUE(Pointer("/int").GetUri(d, doc) == root);
+ EXPECT_TRUE(Pointer("/str").GetUri(d, doc) == root);
+ EXPECT_TRUE(Pointer("/obj").GetUri(d, doc) == root);
+ EXPECT_TRUE(Pointer("/obj/child").GetUri(d, doc) == Pointer::UriType("http://doc/root/inobj"));
+ EXPECT_TRUE(Pointer("/jbo").GetUri(d, doc) == root);
+ EXPECT_TRUE(Pointer("/jbo/child").GetUri(d, doc) == root); // id not string
+
+ size_t unresolvedTokenIndex;
+ EXPECT_TRUE(Pointer("/abc").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // Out of boundary
+ EXPECT_EQ(0u, unresolvedTokenIndex);
+ EXPECT_TRUE(Pointer("/foo/3").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // Out of boundary
+ EXPECT_EQ(1u, unresolvedTokenIndex);
+ EXPECT_TRUE(Pointer("/foo/a").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // "/foo" is an array, cannot query by "a"
+ EXPECT_EQ(1u, unresolvedTokenIndex);
+ EXPECT_TRUE(Pointer("/foo/0/0").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2u, unresolvedTokenIndex);
+ EXPECT_TRUE(Pointer("/foo/0/a").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2u, unresolvedTokenIndex);
+
+ Pointer::Token tokens[] = { { "foo ...", 3, kPointerInvalidIndex } };
+ EXPECT_TRUE(Pointer(tokens, 1).GetUri(d, doc) == root);
+}
+
TEST(Pointer, Get) {
Document d;
d.Parse(kJson);
@@ -666,7 +712,8 @@ TEST(Pointer, Get) {
EXPECT_EQ(&d["k\"l"], Pointer("/k\"l").Get(d));
EXPECT_EQ(&d[" "], Pointer("/ ").Get(d));
EXPECT_EQ(&d["m~n"], Pointer("/m~0n").Get(d));
- EXPECT_TRUE(Pointer("/abc").Get(d) == 0);
+
+ EXPECT_TRUE(Pointer("/abc").Get(d) == 0); // Out of boundary
size_t unresolvedTokenIndex;
EXPECT_TRUE(Pointer("/foo/2").Get(d, &unresolvedTokenIndex) == 0); // Out of boundary
EXPECT_EQ(1u, unresolvedTokenIndex);
diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp
index f381b4e9..1b25e2f4 100644
--- a/test/unittest/schematest.cpp
+++ b/test/unittest/schematest.cpp
@@ -13,6 +13,7 @@
// specific language governing permissions and limitations under the License.
#define RAPIDJSON_SCHEMA_VERBOSE 0
+#define RAPIDJSON_HAS_STDSTRING 1
#include "unittest.h"
#include "rapidjson/schema.h"
@@ -1811,6 +1812,189 @@ TEST(SchemaValidator, EscapedPointer) {
"}}");
}
+TEST(SchemaValidator, SchemaPointer) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"swagger\": \"2.0\","
+ " \"paths\": {"
+ " \"/some/path\": {"
+ " \"post\": {"
+ " \"parameters\": ["
+ " {"
+ " \"in\": \"body\","
+ " \"name\": \"body\","
+ " \"schema\": {"
+ " \"properties\": {"
+ " \"a\": {"
+ " \"$ref\": \"#/definitions/Prop_a\""
+ " },"
+ " \"b\": {"
+ " \"type\": \"integer\""
+ " }"
+ " },"
+ " \"type\": \"object\""
+ " }"
+ " }"
+ " ],"
+ " \"responses\": {"
+ " \"200\": {"
+ " \"schema\": {"
+ " \"$ref\": \"#/definitions/Resp_200\""
+ " }"
+ " }"
+ " }"
+ " }"
+ " }"
+ " },"
+ " \"definitions\": {"
+ " \"Prop_a\": {"
+ " \"properties\": {"
+ " \"c\": {"
+ " \"enum\": ["
+ " \"C1\","
+ " \"C2\","
+ " \"C3\""
+ " ],"
+ " \"type\": \"string\""
+ " },"
+ " \"d\": {"
+ " \"$ref\": \"#/definitions/Prop_d\""
+ " },"
+ " \"s\": {"
+ " \"type\": \"string\""
+ " }"
+ " },"
+ " \"required\": [\"c\"],"
+ " \"type\": \"object\""
+ " },"
+ " \"Prop_d\": {"
+ " \"properties\": {"
+ " \"a\": {"
+ " \"$ref\": \"#/definitions/Prop_a\""
+ " },"
+ " \"c\": {"
+ " \"$ref\": \"#/definitions/Prop_a/properties/c\""
+ " }"
+ " },"
+ " \"type\": \"object\""
+ " },"
+ " \"Resp_200\": {"
+ " \"properties\": {"
+ " \"e\": {"
+ " \"type\": \"string\""
+ " },"
+ " \"f\": {"
+ " \"type\": \"boolean\""
+ " },"
+ " \"cyclic_source\": {"
+ " \"$ref\": \"#/definitions/Resp_200/properties/cyclic_target\""
+ " },"
+ " \"cyclic_target\": {"
+ " \"$ref\": \"#/definitions/Resp_200/properties/cyclic_source\""
+ " }"
+ " },"
+ " \"type\": \"object\""
+ " }"
+ " }"
+ "}");
+ SchemaDocument s1(sd, NULL, 0, NULL, NULL, Pointer("#/paths/~1some~1path/post/parameters/0/schema"));
+ VALIDATE(s1,
+ "{"
+ " \"a\": {"
+ " \"c\": \"C1\","
+ " \"d\": {"
+ " \"a\": {"
+ " \"c\": \"C2\""
+ " },"
+ " \"c\": \"C3\""
+ " }"
+ " },"
+ " \"b\": 123"
+ "}",
+ true);
+ INVALIDATE(s1,
+ "{"
+ " \"a\": {"
+ " \"c\": \"C1\","
+ " \"d\": {"
+ " \"a\": {"
+ " \"c\": \"C2\""
+ " },"
+ " \"c\": \"C3\""
+ " }"
+ " },"
+ " \"b\": \"should be an int\""
+ "}",
+ "#/paths/~1some~1path/post/parameters/0/schema/properties/b", "type", "#/b",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\":\"#/b\","
+ " \"schemaRef\":\"#/paths/~1some~1path/post/parameters/0/schema/properties/b\","
+ " \"expected\": [\"integer\"], \"actual\":\"string\""
+ "}}");
+ INVALIDATE(s1,
+ "{"
+ " \"a\": {"
+ " \"c\": \"C1\","
+ " \"d\": {"
+ " \"a\": {"
+ " \"c\": \"should be within enum\""
+ " },"
+ " \"c\": \"C3\""
+ " }"
+ " },"
+ " \"b\": 123"
+ "}",
+ "#/definitions/Prop_a/properties/c", "enum", "#/a/d/a/c",
+ "{ \"enum\": {"
+ " \"errorCode\": 19,"
+ " \"instanceRef\":\"#/a/d/a/c\","
+ " \"schemaRef\":\"#/definitions/Prop_a/properties/c\""
+ "}}");
+ INVALIDATE(s1,
+ "{"
+ " \"a\": {"
+ " \"c\": \"C1\","
+ " \"d\": {"
+ " \"a\": {"
+ " \"s\": \"required 'c' is missing\""
+ " }"
+ " }"
+ " },"
+ " \"b\": 123"
+ "}",
+ "#/definitions/Prop_a", "required", "#/a/d/a",
+ "{ \"required\": {"
+ " \"errorCode\": 15,"
+ " \"missing\":[\"c\"],"
+ " \"instanceRef\":\"#/a/d/a\","
+ " \"schemaRef\":\"#/definitions/Prop_a\""
+ "}}");
+ SchemaDocument s2(sd, NULL, 0, NULL, NULL, Pointer("#/paths/~1some~1path/post/responses/200/schema"));
+ VALIDATE(s2,
+ "{ \"e\": \"some string\", \"f\": false }",
+ true);
+ INVALIDATE(s2,
+ "{ \"e\": true, \"f\": false }",
+ "#/definitions/Resp_200/properties/e", "type", "#/e",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\":\"#/e\","
+ " \"schemaRef\":\"#/definitions/Resp_200/properties/e\","
+ " \"expected\": [\"string\"], \"actual\":\"boolean\""
+ "}}");
+ INVALIDATE(s2,
+ "{ \"e\": \"some string\", \"f\": 123 }",
+ "#/definitions/Resp_200/properties/f", "type", "#/f",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\":\"#/f\","
+ " \"schemaRef\":\"#/definitions/Resp_200/properties/f\","
+ " \"expected\": [\"boolean\"], \"actual\":\"integer\""
+ "}}");
+}
+
template <typename Allocator>
static char* ReadFile(const char* filename, Allocator& allocator) {
const char *paths[] = {
@@ -1952,7 +2136,7 @@ public:
virtual const SchemaDocumentType* GetRemoteDocument(const char* uri, SizeType length) {
for (size_t i = 0; i < kCount; i++)
- if (typename SchemaDocumentType::URIType(uri, length) == sd_[i]->GetURI())
+ if (typename SchemaDocumentType::SValue(uri, length) == sd_[i]->GetURI())
return sd_[i];
return 0;
}
@@ -2032,7 +2216,7 @@ TEST(SchemaValidator, TestSuite) {
ADD_FAILURE();
}
else {
- //printf("json test suite file %s parsed ok\n", filename);
+ //printf("\njson test suite file %s parsed ok\n", filename);
GenericDocument<UTF8<>, MemoryPoolAllocator<>, MemoryPoolAllocator<> > d(&documentAllocator, 1024, &documentStackAllocator);
d.Parse(json);
if (d.HasParseError()) {
@@ -2042,12 +2226,14 @@ TEST(SchemaValidator, TestSuite) {
else {
for (Value::ConstValueIterator schemaItr = d.Begin(); schemaItr != d.End(); ++schemaItr) {
{
+ const char* description1 = (*schemaItr)["description"].GetString();
+ //printf("\ncompiling schema for json test %s \n", description1);
SchemaDocumentType schema((*schemaItr)["schema"], filenames[i], static_cast<SizeType>(strlen(filenames[i])), &provider, &schemaAllocator);
GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > validator(schema, &validatorAllocator);
- const char* description1 = (*schemaItr)["description"].GetString();
const Value& tests = (*schemaItr)["tests"];
for (Value::ConstValueIterator testItr = tests.Begin(); testItr != tests.End(); ++testItr) {
const char* description2 = (*testItr)["description"].GetString();
+ //printf("running json test %s \n", description2);
if (!onlyRunDescription || strcmp(description2, onlyRunDescription) == 0) {
const Value& data = (*testItr)["data"];
bool expected = (*testItr)["valid"].GetBool();
@@ -2075,8 +2261,8 @@ TEST(SchemaValidator, TestSuite) {
jsonAllocator.Clear();
}
printf("%d / %d passed (%2d%%)\n", passCount, testCount, passCount * 100 / testCount);
-// if (passCount != testCount)
-// ADD_FAILURE();
+ if (passCount != testCount)
+ ADD_FAILURE();
}
TEST(SchemaValidatingReader, Simple) {
@@ -2114,7 +2300,7 @@ TEST(SchemaValidatingReader, Invalid) {
Document e;
e.Parse(
"{ \"maxLength\": {"
-" \"errorCode\": 6,"
+ " \"errorCode\": 6,"
" \"instanceRef\": \"#\", \"schemaRef\": \"#\","
" \"expected\": 3, \"actual\": \"ABCD\""
"}}");
@@ -2244,6 +2430,185 @@ TEST(SchemaValidator, Ref_remote) {
kValidateDefaultFlags, SchemaValidatorType, PointerType);
}
+// Merge with id where $ref is full URI
+TEST(SchemaValidator, Ref_remote_change_resolution_scope_uri) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+ Document sd;
+ sd.Parse("{\"id\": \"http://ignore/blah#/ref\", \"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"http://localhost:1234/subSchemas.json#/integer\"}}}");
+ SchemaDocumentType s(sd, 0, 0, &provider);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt\","
+ " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// Merge with id where $ref is a relative path
+TEST(SchemaValidator, Ref_remote_change_resolution_scope_relative_path) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+ Document sd;
+ sd.Parse("{\"id\": \"http://localhost:1234/\", \"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"subSchemas.json#/integer\"}}}");
+ SchemaDocumentType s(sd, 0, 0, &provider);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt\","
+ " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// Merge with id where $ref is an absolute path
+TEST(SchemaValidator, Ref_remote_change_resolution_scope_absolute_path) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+ Document sd;
+ sd.Parse("{\"id\": \"http://localhost:1234/xxxx\", \"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#/integer\"}}}");
+ SchemaDocumentType s(sd, 0, 0, &provider);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt\","
+ " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// Merge with id where $ref is an absolute path, and the document has a base URI
+TEST(SchemaValidator, Ref_remote_change_resolution_scope_absolute_path_document) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+ Document sd;
+ sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#/integer\"}}}");
+ SchemaDocumentType s(sd, "http://localhost:1234/xxxx", 26, &provider);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt\","
+ " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// $ref is a non-JSON pointer fragment and there a matching id
+TEST(SchemaValidator, Ref_internal_id_1) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ Document sd;
+ sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myStr\": {\"type\": \"string\", \"id\": \"#myStrId\"}, \"myInt2\": {\"type\": \"integer\", \"id\": \"#myId\"}}}");
+ SchemaDocumentType s(sd);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"myInt1\": null}", "/properties/myInt2", "type", "/myInt1",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt1\","
+ " \"schemaRef\": \"#/properties/myInt2\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// $ref is a non-JSON pointer fragment and there are two matching ids so we take the first
+TEST(SchemaValidator, Ref_internal_id_2) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ Document sd;
+ sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myInt2\": {\"type\": \"integer\", \"id\": \"#myId\"}, \"myStr\": {\"type\": \"string\", \"id\": \"#myId\"}}}");
+ SchemaDocumentType s(sd);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"myInt1\": null}", "/properties/myInt2", "type", "/myInt1",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt1\","
+ " \"schemaRef\": \"#/properties/myInt2\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// $ref is a non-JSON pointer fragment and there is a matching id within array
+TEST(SchemaValidator, Ref_internal_id_in_array) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ Document sd;
+ sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myInt2\": {\"anyOf\": [{\"type\": \"string\", \"id\": \"#myStrId\"}, {\"type\": \"integer\", \"id\": \"#myId\"}]}}}");
+ SchemaDocumentType s(sd);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"myInt1\": null}", "/properties/myInt2/anyOf/1", "type", "/myInt1",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt1\","
+ " \"schemaRef\": \"#/properties/myInt2/anyOf/1\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// $ref is a non-JSON pointer fragment and there is a matching id, and the schema is embedded in the document
+TEST(SchemaValidator, Ref_internal_id_and_schema_pointer) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ Document sd;
+ sd.Parse("{ \"schema\": {\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myInt2\": {\"anyOf\": [{\"type\": \"integer\", \"id\": \"#myId\"}]}}}}");
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ SchemaDocumentType s(sd, 0, 0, 0, 0, PointerType("/schema"));
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ INVALIDATE_(s, "{\"myInt1\": null}", "/schema/properties/myInt2/anyOf/0", "type", "/myInt1",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt1\","
+ " \"schemaRef\": \"#/schema/properties/myInt2/anyOf/0\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// Test that $refs are correctly resolved when intermediate multiple ids are present
+// Includes $ref to a part of the document with a different in-scope id, which also contains $ref..
+TEST(SchemaValidator, Ref_internal_multiple_ids) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ //RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+ CrtAllocator allocator;
+ char* schema = ReadFile("unittestschema/idandref.json", allocator);
+ Document sd;
+ sd.Parse(schema);
+ ASSERT_FALSE(sd.HasParseError());
+ SchemaDocumentType s(sd, "http://xyz", 10/*, &provider*/);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"PA1\": \"s\", \"PA2\": \"t\", \"PA3\": \"r\", \"PX1\": 1, \"PX2Y\": 2, \"PX3Z\": 3, \"PX4\": 4, \"PX5\": 5, \"PX6\": 6, \"PX7W\": 7, \"PX8N\": { \"NX\": 8}}", "#", "errors", "#",
+ "{ \"type\": ["
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PA1\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PA2\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PA3\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX1\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX2Y\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX3Z\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX4\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX5\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX6\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX7W\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX8N/NX\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}"
+ "]}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidatorType, PointerType);
+ CrtAllocator::Free(schema);
+}
+
TEST(SchemaValidator, Ref_remote_issue1210) {
class SchemaDocumentProvider : public IRemoteSchemaDocumentProvider {
SchemaDocument** collection;
@@ -2260,7 +2625,7 @@ TEST(SchemaValidator, Ref_remote_issue1210) {
SchemaDocumentProvider(SchemaDocument** collection) : collection(collection) { }
virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeType length) {
int i = 0;
- while (collection[i] && SchemaDocument::URIType(uri, length) != collection[i]->GetURI()) ++i;
+ while (collection[i] && SchemaDocument::SValue(uri, length) != collection[i]->GetURI()) ++i;
return collection[i];
}
};
diff --git a/test/unittest/uritest.cpp b/test/unittest/uritest.cpp
new file mode 100644
index 00000000..5506aa1e
--- /dev/null
+++ b/test/unittest/uritest.cpp
@@ -0,0 +1,718 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// (C) Copyright IBM Corporation 2021
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#define RAPIDJSON_SCHEMA_VERBOSE 0
+#define RAPIDJSON_HAS_STDSTRING 1
+
+#include "unittest.h"
+#include "rapidjson/document.h"
+#include "rapidjson/uri.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(variadic-macros)
+#elif defined(_MSC_VER)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4822) // local class member function does not have a body
+#endif
+
+using namespace rapidjson;
+
+TEST(Uri, DefaultConstructor) {
+ typedef GenericUri<Value> UriType;
+ UriType u;
+ EXPECT_TRUE(u.GetSchemeString() == 0);
+ EXPECT_TRUE(u.GetAuthString() == 0);
+ EXPECT_TRUE(u.GetPathString() == 0);
+ EXPECT_TRUE(u.GetBaseString() == 0);
+ EXPECT_TRUE(u.GetQueryString() == 0);
+ EXPECT_TRUE(u.GetFragString() == 0);
+ EXPECT_TRUE(u.GetString() == 0);
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(u.GetPathStringLength() == 0);
+ EXPECT_TRUE(u.GetBaseStringLength() == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+ EXPECT_TRUE(u.GetStringLength() == 0);
+}
+
+
+TEST(Uri, Parse) {
+ typedef GenericUri<Value, MemoryPoolAllocator<> > UriType;
+ MemoryPoolAllocator<CrtAllocator> allocator;
+ Value v;
+ Value w;
+
+ v.SetString("http://auth/path/xxx?query#frag", allocator);
+ UriType u = UriType(v, &allocator);
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0);
+ EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), "/path/xxx") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth/path/xxx?query") == 0);
+ EXPECT_TRUE(StrCmp(u.GetQueryString(), "?query") == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag") == 0);
+ u.Get(w, allocator);
+ EXPECT_TRUE(*w.GetString() == *v.GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ typedef std::basic_string<Value::Ch> String;
+ String str = "http://auth/path/xxx?query#frag";
+ const UriType uri = UriType(str);
+ EXPECT_TRUE(UriType::GetScheme(uri) == "http:");
+ EXPECT_TRUE(UriType::GetAuth(uri) == "//auth");
+ EXPECT_TRUE(UriType::GetPath(uri) == "/path/xxx");
+ EXPECT_TRUE(UriType::GetBase(uri) == "http://auth/path/xxx?query");
+ EXPECT_TRUE(UriType::GetQuery(uri) == "?query");
+ EXPECT_TRUE(UriType::GetFrag(uri) == "#frag");
+ EXPECT_TRUE(UriType::Get(uri) == str);
+#endif
+
+ v.SetString("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", allocator);
+ u = UriType(v);
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), "urn:") == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+ u.Get(w, allocator);
+ EXPECT_TRUE(*w.GetString() == *v.GetString());
+
+ v.SetString("", allocator);
+ u = UriType(v);
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(u.GetPathStringLength() == 0);
+ EXPECT_TRUE(u.GetBaseStringLength() == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ v.SetString("http://auth/", allocator);
+ u = UriType(v);
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0);
+ EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), "/") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth/") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ u = UriType("/path/sub");
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), "/path/sub") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "/path/sub") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ // absolute path gets normalized
+ u = UriType("/path/../sub/");
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), "/sub/") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "/sub/") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ // relative path does not
+ u = UriType("path/../sub");
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), "path/../sub") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "path/../sub") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ u = UriType("http://auth#frag/stuff");
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0);
+ EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0);
+ EXPECT_TRUE(u.GetPathStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0);
+ EXPECT_TRUE(StrCmp(u.GetString(), "http://auth#frag/stuff") == 0);
+
+ const Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'};
+ SizeType len = internal::StrLen<Value::Ch>(c);
+ u = UriType(c, len);
+ EXPECT_TRUE(StrCmp(u.GetString(), "#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetStringLength() == len);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "") == 0);
+ EXPECT_TRUE(u.GetBaseStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == len);
+
+ u = UriType(c);
+ EXPECT_TRUE(StrCmp(u.GetString(), "#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetStringLength() == len);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "") == 0);
+ EXPECT_TRUE(u.GetBaseStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == len);
+
+ // Incomplete auth treated as path
+ str = "http:/";
+ const UriType u2 = UriType(str);
+ EXPECT_TRUE(StrCmp(u2.GetSchemeString(), "http:") == 0);
+ EXPECT_TRUE(u2.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u2.GetPathString(), "/") == 0);
+ EXPECT_TRUE(StrCmp(u2.GetBaseString(), "http:/") == 0);
+}
+
+TEST(Uri, Parse_UTF16) {
+ typedef GenericValue<UTF16<> > Value16;
+ typedef GenericUri<Value16, MemoryPoolAllocator<> > UriType;
+ MemoryPoolAllocator<CrtAllocator> allocator;
+ Value16 v;
+ Value16 w;
+
+ v.SetString(L"http://auth/path/xxx?query#frag", allocator);
+ UriType u = UriType(v, &allocator);
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0);
+ EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), L"/path/xxx") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth/path/xxx?query") == 0);
+ EXPECT_TRUE(StrCmp(u.GetQueryString(), L"?query") == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag") == 0);
+ u.Get(w, allocator);
+ EXPECT_TRUE(*w.GetString() == *v.GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ typedef std::basic_string<Value16::Ch> String;
+ String str = L"http://auth/path/xxx?query#frag";
+ const UriType uri = UriType(str);
+ EXPECT_TRUE(UriType::GetScheme(uri) == L"http:");
+ EXPECT_TRUE(UriType::GetAuth(uri) == L"//auth");
+ EXPECT_TRUE(UriType::GetPath(uri) == L"/path/xxx");
+ EXPECT_TRUE(UriType::GetBase(uri) == L"http://auth/path/xxx?query");
+ EXPECT_TRUE(UriType::GetQuery(uri) == L"?query");
+ EXPECT_TRUE(UriType::GetFrag(uri) == L"#frag");
+ EXPECT_TRUE(UriType::Get(uri) == str);
+#endif
+
+ v.SetString(L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", allocator);
+ u = UriType(v);
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"urn:") == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), L"uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+ u.Get(w, allocator);
+ EXPECT_TRUE(*w.GetString() == *v.GetString());
+
+ v.SetString(L"", allocator);
+ u = UriType(v);
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(u.GetPathStringLength() == 0);
+ EXPECT_TRUE(u.GetBaseStringLength() == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ v.SetString(L"http://auth/", allocator);
+ u = UriType(v);
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0);
+ EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), L"/") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth/") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ u = UriType(L"/path/sub");
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), L"/path/sub") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"/path/sub") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ // absolute path gets normalized
+ u = UriType(L"/path/../sub/");
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), L"/sub/") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"/sub/") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ // relative path does not
+ u = UriType(L"path/../sub");
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), L"path/../sub") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"path/../sub") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ u = UriType(L"http://auth#frag/stuff");
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0);
+ EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0);
+ EXPECT_TRUE(u.GetPathStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0);
+ EXPECT_TRUE(StrCmp(u.GetString(), L"http://auth#frag/stuff") == 0);
+
+ const Value16::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'};
+ SizeType len = internal::StrLen<Value16::Ch>(c);
+ u = UriType(c, len);
+ EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetStringLength() == len);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"") == 0);
+ EXPECT_TRUE(u.GetBaseStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == len);
+
+ u = UriType(c);
+ EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetStringLength() == len);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"") == 0);
+ EXPECT_TRUE(u.GetBaseStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == len);
+
+ // Incomplete auth treated as path
+ u = UriType(L"http:/");
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), L"/") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http:/") == 0);
+}
+
+TEST(Uri, CopyConstructor) {
+ typedef GenericUri<Value> UriType;
+ CrtAllocator allocator;
+
+ UriType u("http://auth/path/xxx?query#frag", &allocator);
+ UriType u2(u);
+ EXPECT_TRUE(u == u2);
+ EXPECT_NE(&u.GetAllocator(), &u2.GetAllocator());
+}
+
+TEST(Uri, Assignment) {
+ typedef GenericUri<Value> UriType;
+ CrtAllocator allocator;
+
+ UriType u("http://auth/path/xxx?query#frag", &allocator);
+ UriType u2;
+ u2 = u;
+ EXPECT_TRUE(u == u2);
+ EXPECT_NE(&u.GetAllocator(), &u2.GetAllocator());
+}
+
+TEST(Uri, Resolve) {
+ typedef GenericUri<Value> UriType;
+ CrtAllocator allocator;
+
+ // ref is full uri
+ UriType base = UriType("http://auth/path/#frag");
+ UriType ref = UriType("http://newauth/newpath#newfrag");
+ UriType res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0);
+
+ base = UriType("/path/#frag", &allocator);
+ ref = UriType("http://newauth/newpath#newfrag", &allocator);
+ res = ref.Resolve(base, &allocator);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0);
+
+ // ref is alternate uri
+ base = UriType("http://auth/path/#frag");
+ ref = UriType("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
+
+ // ref is absolute path
+ base = UriType("http://auth/path/#");
+ ref = UriType("/newpath#newfrag");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/newpath#newfrag") == 0);
+
+ // ref is relative path
+ base = UriType("http://auth/path/file.json#frag");
+ ref = UriType("newfile.json#");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/path/newfile.json#") == 0);
+
+ base = UriType("http://auth/path/file.json#frag/stuff");
+ ref = UriType("newfile.json#newfrag/newstuff");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/path/newfile.json#newfrag/newstuff") == 0);
+
+ base = UriType("file.json", &allocator);
+ ref = UriType("newfile.json", &base.GetAllocator());
+ res = ref.Resolve(base, &ref.GetAllocator());
+ EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0);
+
+ base = UriType("file.json", &allocator);
+ ref = UriType("./newfile.json", &allocator);
+ res = ref.Resolve(base, &allocator);
+ EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0);
+
+ base = UriType("file.json");
+ ref = UriType("parent/../newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0);
+
+ base = UriType("file.json");
+ ref = UriType("parent/./newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "parent/newfile.json") == 0);
+
+ base = UriType("file.json");
+ ref = UriType("../../parent/.././newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0);
+
+ // This adds a joining slash so resolved length is base length + ref length + 1
+ base = UriType("http://auth");
+ ref = UriType("newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/newfile.json") == 0);
+
+ // ref is fragment
+ base = UriType("#frag/stuff");
+ ref = UriType("#newfrag/newstuff");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "#newfrag/newstuff") == 0);
+
+ // test ref fragment always wins
+ base = UriType("/path#frag");
+ ref = UriType("");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "/path") == 0);
+
+ // Examples from RFC3896
+ base = UriType("http://a/b/c/d;p?q");
+ ref = UriType("g:h");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "g:h") == 0);
+ ref = UriType("g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g") == 0);
+ ref = UriType("./g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g") == 0);
+ ref = UriType("g/");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g/") == 0);
+ ref = UriType("/g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
+ ref = UriType("//g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://g") == 0);
+ ref = UriType("?y");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?y") == 0);
+ ref = UriType("g?y");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g?y") == 0);
+ ref = UriType("#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?q#s") == 0);
+ ref = UriType("g#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s") == 0);
+ ref = UriType("g?y#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g?y#s") == 0);
+ ref = UriType(";x");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/;x") == 0);
+ ref = UriType("g;x");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g;x") == 0);
+ ref = UriType("g;x?y#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g;x?y#s") == 0);
+ ref = UriType("");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?q") == 0);
+ ref = UriType(".");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/") == 0);
+ ref = UriType("./");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/") == 0);
+ ref = UriType("..");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/") == 0);
+ ref = UriType("../");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/") == 0);
+ ref = UriType("../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/g") == 0);
+ ref = UriType("../..");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/") == 0);
+ ref = UriType("../../");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/") == 0);
+ ref = UriType("../../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
+ ref = UriType("../../../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
+ ref = UriType("../../../../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
+ ref = UriType("/./g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
+ ref = UriType("/../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
+ ref = UriType("g.");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g.") == 0);
+ ref = UriType(".g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/.g") == 0);
+ ref = UriType("g..");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g..") == 0);
+ ref = UriType("..g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/..g") == 0);
+ ref = UriType("g#s/../x");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s/../x") == 0);
+}
+
+TEST(Uri, Resolve_UTF16) {
+ typedef GenericValue<UTF16<> > Value16;
+ typedef GenericUri<Value16> UriType;
+ CrtAllocator allocator;
+
+ // ref is full uri
+ UriType base = UriType(L"http://auth/path/#frag");
+ UriType ref = UriType(L"http://newauth/newpath#newfrag");
+ UriType res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://newauth/newpath#newfrag") == 0);
+
+ base = UriType(L"/path/#frag");
+ ref = UriType(L"http://newauth/newpath#newfrag");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://newauth/newpath#newfrag") == 0);
+
+ // ref is alternate uri
+ base = UriType(L"http://auth/path/#frag");
+ ref = UriType(L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
+
+ // ref is absolute path
+ base = UriType(L"http://auth/path/#");
+ ref = UriType(L"/newpath#newfrag");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/newpath#newfrag") == 0);
+
+ // ref is relative path
+ base = UriType(L"http://auth/path/file.json#frag");
+ ref = UriType(L"newfile.json#");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/path/newfile.json#") == 0);
+
+ base = UriType(L"http://auth/path/file.json#frag/stuff");
+ ref = UriType(L"newfile.json#newfrag/newstuff");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/path/newfile.json#newfrag/newstuff") == 0);
+
+ base = UriType(L"file.json", &allocator);
+ ref = UriType(L"newfile.json", &base.GetAllocator());
+ res = ref.Resolve(base, &ref.GetAllocator());
+ EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0);
+
+ base = UriType(L"file.json", &allocator);
+ ref = UriType(L"./newfile.json", &allocator);
+ res = ref.Resolve(base, &allocator);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0);
+
+ base = UriType(L"file.json");
+ ref = UriType(L"parent/../newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0);
+
+ base = UriType(L"file.json");
+ ref = UriType(L"parent/./newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"parent/newfile.json") == 0);
+
+ base = UriType(L"file.json");
+ ref = UriType(L"../../parent/.././newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0);
+
+ // This adds a joining slash so resolved length is base length + ref length + 1
+ base = UriType(L"http://auth");
+ ref = UriType(L"newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/newfile.json") == 0);
+
+ // ref is fragment
+ base = UriType(L"#frag/stuff");
+ ref = UriType(L"#newfrag/newstuff");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"#newfrag/newstuff") == 0);
+
+ // test ref fragment always wins
+ base = UriType(L"/path#frag");
+ ref = UriType(L"");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"/path") == 0);
+
+ // Examples from RFC3896
+ base = UriType(L"http://a/b/c/d;p?q");
+ ref = UriType(L"g:h");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"g:h") == 0);
+ ref = UriType(L"g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g") == 0);
+ ref = UriType(L"./g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g") == 0);
+ ref = UriType(L"g/");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g/") == 0);
+ ref = UriType(L"/g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
+ ref = UriType(L"//g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://g") == 0);
+ ref = UriType(L"?y");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?y") == 0);
+ ref = UriType(L"g?y");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g?y") == 0);
+ ref = UriType(L"#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?q#s") == 0);
+ ref = UriType(L"g#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g#s") == 0);
+ ref = UriType(L"g?y#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g?y#s") == 0);
+ ref = UriType(L";x");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/;x") == 0);
+ ref = UriType(L"g;x");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g;x") == 0);
+ ref = UriType(L"g;x?y#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g;x?y#s") == 0);
+ ref = UriType(L"");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?q") == 0);
+ ref = UriType(L".");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/") == 0);
+ ref = UriType(L"./");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/") == 0);
+ ref = UriType(L"..");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/") == 0);
+ ref = UriType(L"../");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/") == 0);
+ ref = UriType(L"../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/g") == 0);
+ ref = UriType(L"../..");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/") == 0);
+ ref = UriType(L"../../");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/") == 0);
+ ref = UriType(L"../../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
+ ref = UriType(L"../../../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
+ ref = UriType(L"../../../../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
+ ref = UriType(L"/./g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
+ ref = UriType(L"/../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
+ ref = UriType(L"g.");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g.") == 0);
+ ref = UriType(L".g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/.g") == 0);
+ ref = UriType(L"g..");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g..") == 0);
+ ref = UriType(L"..g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/..g") == 0);
+ ref = UriType(L"g#s/../x");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g#s/../x") == 0);
+}
+
+TEST(Uri, Equals) {
+ typedef GenericUri<Value> UriType;
+
+ UriType a = UriType("http://a/a#a");
+ UriType b = UriType("http://a/a#b");
+ UriType c = a;
+
+ EXPECT_TRUE(a == a);
+ EXPECT_TRUE(a == c);
+ EXPECT_TRUE(a != b);
+}
+
+TEST(Uri, Match) {
+ typedef GenericUri<Value> UriType;
+
+ UriType a = UriType("http://a/a#a");
+ UriType b = UriType("http://a/a#b");
+ UriType c = a;
+ UriType d;
+
+ EXPECT_TRUE(a.Match(a));
+ EXPECT_TRUE(a.Match(c));
+ EXPECT_FALSE(a.Match(b));
+ EXPECT_FALSE(a.Match(b, true));
+ EXPECT_TRUE(a.Match(b, false)); // Base Uri same
+ EXPECT_FALSE(a.Match(d));
+ EXPECT_FALSE(d.Match(a));
+}
+
+TEST(Uri, Issue1899) {
+ typedef GenericUri<Value, MemoryPoolAllocator<> > UriType;
+
+ UriType base = UriType("http://auth/path/#frag");
+ UriType ref = UriType("http://newauth/newpath#newfrag");
+ UriType res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0);
+}
+
+#if defined(_MSC_VER) || defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif