diff --git a/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp b/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp index caf2eb73eda..ef4589fce24 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp @@ -1535,6 +1535,96 @@ TEST_F(ECSqlStatementTestFixture, IsNull) } //--------------------------------------------------------------------------------------- +//@bsimethod +//--------------------------------------------------------------------------------------- +TEST_F(ECSqlStatementTestFixture, UseOfWrongPropertyTags_ForAllVersions) + { + ASSERT_EQ(DbResult::BE_SQLITE_OK, SetupECDb("UseOfWrongPropertyTags.ecdb")); + + unsigned int ecXmlMajorVersion; + unsigned int ecXmlMinorVersion; + EXPECT_EQ(ECObjectsStatus::Success, ECSchema::ParseECVersion(ecXmlMajorVersion, ecXmlMinorVersion, ECVersion::Latest)); + + //Below schema uses ECProperty tag for Struct property which is wrong. It should use ECStructProperty tag. + Utf8CP xmlSchema = R"xml( + + + + + + + + + )xml"; + + //Below schema uses ECProperty tag for Struct property which is wrong. It should use ECStructProperty tag. + //Declaring separate xmlSchema for version 2.0 since, previous versions had different xml schema format. + Utf8CP xmlSchemaFor_V2_0 = R"xml( + + + + + + + + )xml"; + + //Below schema uses ECProperty tag for Struct property which is wrong. It should use ECStructProperty tag. + //Declaring separate xmlSchema for version 3.0 since, previous versions had different xml schema format. + Utf8CP xmlSchemaFor_V3_0 = R"xml( + + + + + + + + + )xml"; + + for (const auto& [testCaseNumber, majorVersion, minorVersion, xmlSchemaVar, deserializationStatus, importStatus] : std::vector> + { + { 1, 2U, 0U, xmlSchemaFor_V2_0, true , true }, // Older versions on encountering wrong property tags default to a safe type in this case, "string" + { 2, 3U, 0U, xmlSchemaFor_V3_0, true , true }, // Older versions on encountering wrong property tags default to a safe type in this case, "string" + { 3, 3U, 1U, xmlSchema, true , true }, // Older versions on encountering wrong property tags default to a safe type in this case, "string" + { 4, ecXmlMajorVersion, ecXmlMinorVersion, xmlSchema, false , false }, // Current version should always log an error on encountering wrong property tags with no deserialization and import + { 5, ecXmlMajorVersion, ecXmlMinorVersion + 1U, xmlSchema, true , false }, // Future versions should deserialize successfully, default to a safe type but, import should fail + }) + { + ECSchemaPtr schema; + const auto context = ECSchemaReadContext::CreateContext(); + + // Schema should always be deserialized successfully irrespective of the ECXml version + ASSERT_EQ(deserializationStatus, SchemaReadStatus::Success == ECSchema::ReadFromXmlString(schema, Utf8PrintfString(xmlSchemaVar, majorVersion, minorVersion).c_str(), *context)) << "Test case number " << testCaseNumber << " failed at deserializing."; + ASSERT_EQ(deserializationStatus, schema.IsValid()) << "Test case number: " << testCaseNumber << " failed due to invalid schemas."; + + // Checking if the wrong property tags are defaulted to string type + if (deserializationStatus) + { + // Fetching the class pointer + auto classP = schema->GetClassCP("UseOfWrongPropertyTags"); + ASSERT_TRUE(classP) << "Test case failed." << testCaseNumber << "failed to fetch class pointer."; + // Fetching the property pointer + auto propP = classP->GetPropertyByIndex(0); + ASSERT_TRUE(propP) << "Test case failed." << testCaseNumber << "failed to fetch property pointer."; + // Fetching property as primitive property + auto primProp = propP->GetAsPrimitiveProperty(); + ASSERT_TRUE(primProp) << "Test case failed." << testCaseNumber << "failed to fetch the primitive property"; + // Checking for equality of primivite property type to string + auto propType = primProp->GetType(); + EXPECT_EQ(PRIMITIVETYPE_String, propType) << "Test case failed." << testCaseNumber << "did not default to string type."; + } + + + // Schema import should fail when ECXml version of the schema is greater than the current version that the ECDb supports + EXPECT_EQ(importStatus, m_ecdb.Schemas().ImportSchemas(context->GetCache().GetSchemas()) == SchemaImportResult::OK) << "Test case number " << testCaseNumber << " failed."; + if (importStatus) + EXPECT_NE(nullptr, m_ecdb.Schemas().GetSchema("TestSchema")) << "Test case number " << testCaseNumber << " failed."; + m_ecdb.AbandonChanges(); + + } + } +//--------------------------------------------------------------------------------------- // @bsimethod //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECSqlStatementTestFixture, pragma_ecdb_version) diff --git a/iModelCore/ecobjects/src/ECProperty.cpp b/iModelCore/ecobjects/src/ECProperty.cpp index fd43b2c6519..014448faf47 100644 --- a/iModelCore/ecobjects/src/ECProperty.cpp +++ b/iModelCore/ecobjects/src/ECProperty.cpp @@ -44,6 +44,13 @@ SchemaReadStatus ReadTypeNameWithPruning(pugi::xml_node node, ECPropertyP prop, // must come after prune check to make sure we don't default to string properties that should be pruned if (setTypeStatus == ECObjectsStatus::ParseError && ignoreParseErrors) { + // Unknown type encounter in the schema for the current version returning ERROR status + if (prop->GetClass().GetSchema().GetOriginalECXmlVersionAsString() == ECSchema::GetECVersionString(ECVersion::Latest)) + { + LOG.errorv("Invalid ECSchemaXML: The Property '%s.%s' has a type name '%s' that can not be resolved to a known type.", + prop->GetClass().GetFullName(), prop->GetName().c_str(), typeName.c_str()); + return SchemaReadStatus::InvalidECSchemaXml; + } LOG.warningv ("Defaulting the type of ECProperty '%s' to '%s' in reaction to non-fatal parse error.", prop->GetName().c_str(), prop->GetTypeName().c_str()); return SchemaReadStatus::Success; } diff --git a/iModelCore/ecobjects/test/NonPublished/SchemaComparerTests.cpp b/iModelCore/ecobjects/test/NonPublished/SchemaComparerTests.cpp index 1c81cb74c24..e4fa3b9d791 100644 --- a/iModelCore/ecobjects/test/NonPublished/SchemaComparerTests.cpp +++ b/iModelCore/ecobjects/test/NonPublished/SchemaComparerTests.cpp @@ -1121,6 +1121,54 @@ TEST_F(SchemaCompareTest, CompareECClassIdentical) //---------------------------------------------------------------------------------------- // @bsimethod //---------------+---------------+---------------+---------------+---------------+-------- +TEST_F(SchemaComparerXmlTests, CompareSchemasWithWrongPropertyTags) + { + //Test created for 3.1 version of schema specifically + //to observe defaulting behavior for and when the property tags are wrong + bvector firstSchemasXml { + R"schema( + + + + + + + + + + )schema" + }; + + LoadSchemasIntoContext(m_firstContext, firstSchemasXml); + + bvector secondSchemasXml { + R"schema( + + + + + + + + + + )schema" + }; + + LoadSchemasIntoContext(m_secondContext, secondSchemasXml); + + SchemaComparer comparer; + SchemaDiff changes; + comparer.Compare(changes, m_firstContext->GetCache().GetSchemas(), m_secondContext->GetCache().GetSchemas()); + + ASSERT_EQ(1, changes.Changes().Count()); + + // We default to string when the property tag is wrong therefore, property change should be observed. + ASSERT_TRUE(changes.Changes()[0].Classes()[0].Properties()[0].IsChanged()); + } +//---------------------------------------------------------------------------------------- +// @bsimethod +//---------------+---------------+---------------+---------------+---------------+-------- TEST_F(SchemaCompareTest, CompareECSchemaClassPropertyDescriptionAgainstNull) { CreateFirstSchema();