diff options
Diffstat (limited to 'icing/schema/schema-util_test.cc')
-rw-r--r-- | icing/schema/schema-util_test.cc | 646 |
1 files changed, 626 insertions, 20 deletions
diff --git a/icing/schema/schema-util_test.cc b/icing/schema/schema-util_test.cc index 44d8def..2d1e683 100644 --- a/icing/schema/schema-util_test.cc +++ b/icing/schema/schema-util_test.cc @@ -30,9 +30,13 @@ namespace icing { namespace lib { namespace { +using portable_equals_proto::EqualsProto; using ::testing::Eq; using ::testing::HasSubstr; using ::testing::IsEmpty; +using ::testing::Pair; +using ::testing::Pointee; +using ::testing::UnorderedElementsAre; // Properties/fields in a schema type constexpr char kEmailType[] = "EmailMessage"; @@ -118,12 +122,32 @@ TEST(SchemaUtilTest, DependentGraphAlphabeticalOrder) { ICING_ASSERT_OK_AND_ASSIGN(SchemaUtil::DependentMap d_map, SchemaUtil::Validate(schema)); EXPECT_THAT(d_map, testing::SizeIs(5)); - EXPECT_THAT(d_map["F"], - testing::UnorderedElementsAre("A", "B", "C", "D", "E")); - EXPECT_THAT(d_map["E"], testing::UnorderedElementsAre("A", "B", "C", "D")); - EXPECT_THAT(d_map["D"], testing::UnorderedElementsAre("A", "B")); - EXPECT_THAT(d_map["C"], testing::UnorderedElementsAre("A", "B")); - EXPECT_THAT(d_map["B"], testing::UnorderedElementsAre("A")); + EXPECT_THAT( + d_map["F"], + UnorderedElementsAre(Pair("A", IsEmpty()), Pair("B", IsEmpty()), + Pair("C", IsEmpty()), Pair("D", IsEmpty()), + Pair("E", UnorderedElementsAre(Pointee( + EqualsProto(type_e.properties(0))))))); + EXPECT_THAT(d_map["E"], + UnorderedElementsAre( + Pair("A", IsEmpty()), Pair("B", IsEmpty()), + Pair("C", UnorderedElementsAre( + Pointee(EqualsProto(type_c.properties(0))))), + Pair("D", UnorderedElementsAre( + Pointee(EqualsProto(type_d.properties(0))))))); + EXPECT_THAT( + d_map["D"], + UnorderedElementsAre(Pair("A", IsEmpty()), + Pair("B", UnorderedElementsAre(Pointee( + EqualsProto(type_b.properties(1))))))); + EXPECT_THAT( + d_map["C"], + UnorderedElementsAre(Pair("A", IsEmpty()), + Pair("B", UnorderedElementsAre(Pointee( + EqualsProto(type_b.properties(0))))))); + EXPECT_THAT(d_map["B"], UnorderedElementsAre(Pair( + "A", UnorderedElementsAre(Pointee( + EqualsProto(type_a.properties(0))))))); } TEST(SchemaUtilTest, DependentGraphReverseAlphabeticalOrder) { @@ -206,12 +230,32 @@ TEST(SchemaUtilTest, DependentGraphReverseAlphabeticalOrder) { ICING_ASSERT_OK_AND_ASSIGN(SchemaUtil::DependentMap d_map, SchemaUtil::Validate(schema)); EXPECT_THAT(d_map, testing::SizeIs(5)); - EXPECT_THAT(d_map["F"], - testing::UnorderedElementsAre("A", "B", "C", "D", "E")); - EXPECT_THAT(d_map["E"], testing::UnorderedElementsAre("A", "B", "C", "D")); - EXPECT_THAT(d_map["D"], testing::UnorderedElementsAre("A", "B")); - EXPECT_THAT(d_map["C"], testing::UnorderedElementsAre("A", "B")); - EXPECT_THAT(d_map["B"], testing::UnorderedElementsAre("A")); + EXPECT_THAT( + d_map["F"], + UnorderedElementsAre(Pair("A", IsEmpty()), Pair("B", IsEmpty()), + Pair("C", IsEmpty()), Pair("D", IsEmpty()), + Pair("E", UnorderedElementsAre(Pointee( + EqualsProto(type_e.properties(0))))))); + EXPECT_THAT(d_map["E"], + UnorderedElementsAre( + Pair("A", IsEmpty()), Pair("B", IsEmpty()), + Pair("C", UnorderedElementsAre( + Pointee(EqualsProto(type_c.properties(0))))), + Pair("D", UnorderedElementsAre( + Pointee(EqualsProto(type_d.properties(0))))))); + EXPECT_THAT( + d_map["D"], + UnorderedElementsAre(Pair("A", IsEmpty()), + Pair("B", UnorderedElementsAre(Pointee( + EqualsProto(type_b.properties(1))))))); + EXPECT_THAT( + d_map["C"], + UnorderedElementsAre(Pair("A", IsEmpty()), + Pair("B", UnorderedElementsAre(Pointee( + EqualsProto(type_b.properties(0))))))); + EXPECT_THAT(d_map["B"], UnorderedElementsAre(Pair( + "A", UnorderedElementsAre(Pointee( + EqualsProto(type_a.properties(0))))))); } TEST(SchemaUtilTest, DependentGraphMixedOrder) { @@ -293,12 +337,32 @@ TEST(SchemaUtilTest, DependentGraphMixedOrder) { ICING_ASSERT_OK_AND_ASSIGN(SchemaUtil::DependentMap d_map, SchemaUtil::Validate(schema)); EXPECT_THAT(d_map, testing::SizeIs(5)); - EXPECT_THAT(d_map["F"], - testing::UnorderedElementsAre("A", "B", "C", "D", "E")); - EXPECT_THAT(d_map["E"], testing::UnorderedElementsAre("A", "B", "C", "D")); - EXPECT_THAT(d_map["D"], testing::UnorderedElementsAre("A", "B")); - EXPECT_THAT(d_map["C"], testing::UnorderedElementsAre("A", "B")); - EXPECT_THAT(d_map["B"], testing::UnorderedElementsAre("A")); + EXPECT_THAT( + d_map["F"], + UnorderedElementsAre(Pair("A", IsEmpty()), Pair("B", IsEmpty()), + Pair("C", IsEmpty()), Pair("D", IsEmpty()), + Pair("E", UnorderedElementsAre(Pointee( + EqualsProto(type_e.properties(0))))))); + EXPECT_THAT(d_map["E"], + UnorderedElementsAre( + Pair("A", IsEmpty()), Pair("B", IsEmpty()), + Pair("C", UnorderedElementsAre( + Pointee(EqualsProto(type_c.properties(0))))), + Pair("D", UnorderedElementsAre( + Pointee(EqualsProto(type_d.properties(0))))))); + EXPECT_THAT( + d_map["D"], + UnorderedElementsAre(Pair("A", IsEmpty()), + Pair("B", UnorderedElementsAre(Pointee( + EqualsProto(type_b.properties(1))))))); + EXPECT_THAT( + d_map["C"], + UnorderedElementsAre(Pair("A", IsEmpty()), + Pair("B", UnorderedElementsAre(Pointee( + EqualsProto(type_b.properties(0))))))); + EXPECT_THAT(d_map["B"], UnorderedElementsAre(Pair( + "A", UnorderedElementsAre(Pointee( + EqualsProto(type_a.properties(0))))))); } TEST(SchemaUtilTest, TopLevelCycle) { @@ -888,7 +952,8 @@ TEST(SchemaUtilTest, DifferentSchemaTypeIsIncompatible) { SchemaUtil::SchemaDelta schema_delta; schema_delta.schema_types_incompatible.emplace(kEmailType); // kEmailType depends on kMessageType - SchemaUtil::DependentMap dependents_map = {{kMessageType, {kEmailType}}}; + SchemaUtil::DependentMap dependents_map = { + {kMessageType, {{kEmailType, {}}}}}; SchemaUtil::SchemaDelta actual = SchemaUtil::ComputeCompatibilityDelta( old_schema, new_schema, dependents_map); EXPECT_THAT(actual, Eq(schema_delta)); @@ -1403,7 +1468,7 @@ TEST(SchemaUtilTest, IndexNestedDocumentsIndexIncompatible) { // unaffected. SchemaUtil::SchemaDelta schema_delta; schema_delta.schema_types_index_incompatible.emplace(kPersonType); - SchemaUtil::DependentMap dependents_map = {{kEmailType, {kPersonType}}}; + SchemaUtil::DependentMap dependents_map = {{kEmailType, {{kPersonType, {}}}}}; SchemaUtil::SchemaDelta actual = SchemaUtil::ComputeCompatibilityDelta( no_nested_index_schema, nested_index_schema, dependents_map); EXPECT_THAT(actual, Eq(schema_delta)); @@ -1466,6 +1531,547 @@ TEST(SchemaUtilTest, ValidateStringIndexingConfigShouldHaveTokenizer) { EXPECT_THAT(SchemaUtil::Validate(schema), IsOk()); } +TEST(SchemaUtilTest, + ValidateJoinablePropertyTypeQualifiedIdShouldHaveStringDataType) { + SchemaProto schema = + SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("MyType").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_INT64) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_REQUIRED))) + .Build(); + + // Error if data type is not STRING for qualified id joinable value type. + EXPECT_THAT(SchemaUtil::Validate(schema), + StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT)); + + // Passes once we set STRING as the data type. + schema = SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("MyType").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_REQUIRED))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), IsOk()); +} + +TEST(SchemaUtilTest, ValidateJoinablePropertyShouldNotHaveRepeatedCardinality) { + SchemaProto schema = + SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("MyType").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_REPEATED))) + .Build(); + + // Error if using REPEATED cardinality for joinable property. + EXPECT_THAT(SchemaUtil::Validate(schema), + StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT)); + + // Passes once we use OPTIONAL cardinality with joinable property. + schema = SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("MyType").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), IsOk()); + + // Passes once we use REQUIRED cardinality with joinable property. + schema = SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("MyType").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_REQUIRED))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), IsOk()); + + // Passes once we use REPEATED cardinality with non-joinable property. + schema = SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("MyType").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_NONE, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_REPEATED))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), IsOk()); +} + +TEST(SchemaUtilTest, + ValidateJoinablePropertyWithDeletePropagationShouldHaveTypeQualifiedId) { + SchemaProto schema = + SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("MyType").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_NONE, + /*propagate_delete=*/true) + .SetCardinality(CARDINALITY_REQUIRED))) + .Build(); + + // Error if enabling delete propagation with non qualified id joinable value + // type. + EXPECT_THAT(SchemaUtil::Validate(schema), + StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT)); + + // Passes once we set qualified id joinable value type with delete propagation + // enabled. + schema = SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("MyType").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/true) + .SetCardinality(CARDINALITY_REQUIRED))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), IsOk()); + + // Passes once we disable delete propagation. + schema = SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("MyType").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_NONE, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_REQUIRED))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), IsOk()); +} + +TEST(SchemaUtilTest, + ValidateNestedJoinablePropertyShouldNotHaveNestedRepeatedCardinality) { + // Dependency and nested document property cardinality: + // "C" --(REPEATED)--> "B" --(OPTIONAL)--> "A" + // where "A" contains joinable property. This should not be allowed. + SchemaProto schema = + SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("A").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder().SetType("B").AddProperty( + PropertyConfigBuilder() + .SetName("a") + .SetDataTypeDocument("A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder().SetType("C").AddProperty( + PropertyConfigBuilder() + .SetName("b") + .SetDataTypeDocument("B", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_REPEATED))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), + StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT)); + + // Passes once we use non-REPEATED cardinality for "C.b", i.e. the dependency + // and nested document property cardinality becomes: + // "C" --(OPTIONAL)--> "B" --(OPTIONAL)--> "A" + schema = SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("A").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder().SetType("B").AddProperty( + PropertyConfigBuilder() + .SetName("a") + .SetDataTypeDocument("A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder().SetType("C").AddProperty( + PropertyConfigBuilder() + .SetName("b") + .SetDataTypeDocument("B", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), IsOk()); +} + +TEST( + SchemaUtilTest, + ValidateNestedJoinablePropertyShouldAllowRepeatedCardinalityIfNoJoinableProperty) { + // Dependency and nested document property cardinality: + // "C" --(OPTIONAL)--> "B" --(REPEATED)--> "A" + // where only "B" contains joinable property. This should be allowed. + SchemaProto schema = + SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("A").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_NONE, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder() + .SetType("B") + .AddProperty(PropertyConfigBuilder() + .SetName("a") + .SetDataTypeDocument( + "A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_REPEATED)) + .AddProperty( + PropertyConfigBuilder() + .SetName("Bar") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder().SetType("C").AddProperty( + PropertyConfigBuilder() + .SetName("b") + .SetDataTypeDocument("B", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .Build(); + + // Passes since nested schema type with REPEATED cardinality doesn't have + // joinable property. + EXPECT_THAT(SchemaUtil::Validate(schema), IsOk()); +} + +TEST(SchemaUtilTest, + ValidateNestedJoinablePropertyMultiplePropertiesWithSameSchema) { + // Dependency and nested document property cardinality: + // --(a1: OPTIONAL)-- + // / \ + // B -- --> A + // \ / + // --(a2: REPEATED)-- + // where "A" contains joinable property. This should not be allowed. + SchemaProto schema = + SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("A").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder() + .SetType("B") + .AddProperty(PropertyConfigBuilder() + .SetName("a1") + .SetDataTypeDocument( + "A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL)) + .AddProperty(PropertyConfigBuilder() + .SetName("a2") + .SetDataTypeDocument( + "A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_REPEATED))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), + StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT)); + + // Passes once we use non-REPEATED cardinality for "B.a2", i.e. the dependency + // and nested document property cardinality becomes: + // --(a1: OPTIONAL)-- + // / \ + // B -- --> A + // \ / + // --(a2: OPTIONAL)-- + schema = + SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("A").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder() + .SetType("B") + .AddProperty(PropertyConfigBuilder() + .SetName("a1") + .SetDataTypeDocument( + "A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL)) + .AddProperty(PropertyConfigBuilder() + .SetName("a2") + .SetDataTypeDocument( + "A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), IsOk()); +} + +TEST(SchemaUtilTest, ValidateNestedJoinablePropertyDiamondRelationship) { + // Dependency and nested document property cardinality: + // B + // / \ + // (OPTIONAL) (OPTIONAL) + // / \ + // D --- --> A + // \ / + // (OPTIONAL) (OPTIONAL) + // \ / + // C + // where "A" contains joinable property. This should be allowed. + SchemaProto schema = + SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("A").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder().SetType("B").AddProperty( + PropertyConfigBuilder() + .SetName("a") + .SetDataTypeDocument("A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder().SetType("C").AddProperty( + PropertyConfigBuilder() + .SetName("a") + .SetDataTypeDocument("A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder() + .SetType("D") + .AddProperty(PropertyConfigBuilder() + .SetName("b") + .SetDataTypeDocument( + "B", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL)) + .AddProperty(PropertyConfigBuilder() + .SetName("c") + .SetDataTypeDocument( + "C", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), IsOk()); + + // Fails once we change any of edge to REPEATED cardinality. + // B + // / \ + // (REPEATED) (OPTIONAL) + // / \ + // D --- --> A + // \ / + // (OPTIONAL) (OPTIONAL) + // \ / + // C + schema = + SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("A").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder().SetType("B").AddProperty( + PropertyConfigBuilder() + .SetName("a") + .SetDataTypeDocument("A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder().SetType("C").AddProperty( + PropertyConfigBuilder() + .SetName("a") + .SetDataTypeDocument("A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder() + .SetType("D") + .AddProperty(PropertyConfigBuilder() + .SetName("b") + .SetDataTypeDocument( + "B", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_REPEATED)) + .AddProperty(PropertyConfigBuilder() + .SetName("c") + .SetDataTypeDocument( + "C", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), + StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT)); + + // B + // / \ + // (OPTIONAL) (REPEATED) + // / \ + // D --- --> A + // \ / + // (OPTIONAL) (OPTIONAL) + // \ / + // C + schema = + SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("A").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder().SetType("B").AddProperty( + PropertyConfigBuilder() + .SetName("a") + .SetDataTypeDocument("A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_REPEATED))) + .AddType(SchemaTypeConfigBuilder().SetType("C").AddProperty( + PropertyConfigBuilder() + .SetName("a") + .SetDataTypeDocument("A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder() + .SetType("D") + .AddProperty(PropertyConfigBuilder() + .SetName("b") + .SetDataTypeDocument( + "B", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL)) + .AddProperty(PropertyConfigBuilder() + .SetName("c") + .SetDataTypeDocument( + "C", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), + StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT)); + + // B + // / \ + // (OPTIONAL) (OPTIONAL) + // / \ + // D --- --> A + // \ / + // (REPEATED) (OPTIONAL) + // \ / + // C + schema = + SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("A").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder().SetType("B").AddProperty( + PropertyConfigBuilder() + .SetName("a") + .SetDataTypeDocument("A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder().SetType("C").AddProperty( + PropertyConfigBuilder() + .SetName("a") + .SetDataTypeDocument("A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder() + .SetType("D") + .AddProperty(PropertyConfigBuilder() + .SetName("b") + .SetDataTypeDocument( + "B", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL)) + .AddProperty(PropertyConfigBuilder() + .SetName("c") + .SetDataTypeDocument( + "C", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_REPEATED))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), + StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT)); + + // B + // / \ + // (OPTIONAL) (OPTIONAL) + // / \ + // D --- --> A + // \ / + // (OPTIONAL) (REPEATED) + // \ / + // C + schema = + SchemaBuilder() + .AddType(SchemaTypeConfigBuilder().SetType("A").AddProperty( + PropertyConfigBuilder() + .SetName("Foo") + .SetDataType(TYPE_STRING) + .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID, + /*propagate_delete=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder().SetType("B").AddProperty( + PropertyConfigBuilder() + .SetName("a") + .SetDataTypeDocument("A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .AddType(SchemaTypeConfigBuilder().SetType("C").AddProperty( + PropertyConfigBuilder() + .SetName("a") + .SetDataTypeDocument("A", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_REPEATED))) + .AddType(SchemaTypeConfigBuilder() + .SetType("D") + .AddProperty(PropertyConfigBuilder() + .SetName("b") + .SetDataTypeDocument( + "B", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL)) + .AddProperty(PropertyConfigBuilder() + .SetName("c") + .SetDataTypeDocument( + "C", + /*index_nested_properties=*/false) + .SetCardinality(CARDINALITY_OPTIONAL))) + .Build(); + EXPECT_THAT(SchemaUtil::Validate(schema), + StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT)); +} + TEST(SchemaUtilTest, MultipleReferencesToSameNestedSchemaOk) { SchemaProto schema = SchemaBuilder() |