xref: /aosp_15_r20/external/icing/icing/schema/schema-store_test.cc (revision 8b6cd535a057e39b3b86660c4aa06c99747c2136)
1 // Copyright (C) 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "icing/schema/schema-store.h"
16 
17 #include <cstdint>
18 #include <memory>
19 #include <optional>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
24 #include "icing/text_classifier/lib3/utils/base/status.h"
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
27 #include "icing/absl_ports/str_cat.h"
28 #include "icing/document-builder.h"
29 #include "icing/feature-flags.h"
30 #include "icing/file/file-backed-proto.h"
31 #include "icing/file/filesystem.h"
32 #include "icing/file/mock-filesystem.h"
33 #include "icing/file/version-util.h"
34 #include "icing/portable/equals-proto.h"
35 #include "icing/proto/debug.pb.h"
36 #include "icing/proto/document.pb.h"
37 #include "icing/proto/logging.pb.h"
38 #include "icing/proto/schema.pb.h"
39 #include "icing/proto/storage.pb.h"
40 #include "icing/schema-builder.h"
41 #include "icing/schema/section.h"
42 #include "icing/store/document-filter-data.h"
43 #include "icing/testing/common-matchers.h"
44 #include "icing/testing/fake-clock.h"
45 #include "icing/testing/test-feature-flags.h"
46 #include "icing/testing/tmp-directory.h"
47 #include "icing/util/crc32.h"
48 
49 namespace icing {
50 namespace lib {
51 
52 namespace {
53 
54 using ::icing::lib::portable_equals_proto::EqualsProto;
55 using ::testing::ElementsAre;
56 using ::testing::Eq;
57 using ::testing::Ge;
58 using ::testing::Gt;
59 using ::testing::HasSubstr;
60 using ::testing::Not;
61 using ::testing::Pair;
62 using ::testing::Pointee;
63 using ::testing::Return;
64 using ::testing::SizeIs;
65 using ::testing::UnorderedElementsAre;
66 
67 constexpr int64_t kDefaultTimestamp = 12345678;
68 
69 class SchemaStoreTest : public ::testing::Test {
70  protected:
SetUp()71   void SetUp() override {
72     feature_flags_ = std::make_unique<FeatureFlags>(GetTestFeatureFlags());
73     test_dir_ = GetTestTempDir() + "/icing";
74     schema_store_dir_ = test_dir_ + "/schema_store";
75     filesystem_.CreateDirectoryRecursively(schema_store_dir_.c_str());
76 
77     schema_ = SchemaBuilder()
78                   .AddType(SchemaTypeConfigBuilder()
79                                .SetType("email")
80                                .AddProperty(
81                                    // Add an indexed property so we generate
82                                    // section metadata on it
83                                    PropertyConfigBuilder()
84                                        .SetName("subject")
85                                        .SetDataTypeString(TERM_MATCH_EXACT,
86                                                           TOKENIZER_PLAIN)
87                                        .SetCardinality(CARDINALITY_OPTIONAL))
88                                .AddProperty(
89                                    PropertyConfigBuilder()
90                                        .SetName("timestamp")
91                                        .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
92                                        .SetCardinality(CARDINALITY_OPTIONAL)
93                                        .SetScorableType(SCORABLE_TYPE_ENABLED)))
94                   .Build();
95   }
96 
TearDown()97   void TearDown() override {
98     // Check that the schema store directory is the *only* directory in the
99     // schema_store_dir_. IOW, ensure that all temporary directories have been
100     // properly cleaned up.
101     std::vector<std::string> sub_dirs;
102     ASSERT_TRUE(filesystem_.ListDirectory(test_dir_.c_str(), &sub_dirs));
103     ASSERT_THAT(sub_dirs, ElementsAre("schema_store"));
104 
105     // Finally, clean everything up.
106     ASSERT_TRUE(filesystem_.DeleteDirectoryRecursively(test_dir_.c_str()));
107   }
108 
109   std::unique_ptr<FeatureFlags> feature_flags_;
110   Filesystem filesystem_;
111   std::string test_dir_;
112   std::string schema_store_dir_;
113   SchemaProto schema_;
114   FakeClock fake_clock_;
115 };
116 
TEST_F(SchemaStoreTest,CreationWithFilesystemNullPointerShouldFail)117 TEST_F(SchemaStoreTest, CreationWithFilesystemNullPointerShouldFail) {
118   EXPECT_THAT(SchemaStore::Create(/*filesystem=*/nullptr, schema_store_dir_,
119                                   &fake_clock_, feature_flags_.get()),
120               StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
121 }
122 
TEST_F(SchemaStoreTest,CreationWithClockNullPointerShouldFail)123 TEST_F(SchemaStoreTest, CreationWithClockNullPointerShouldFail) {
124   EXPECT_THAT(SchemaStore::Create(&filesystem_, schema_store_dir_,
125                                   /*clock=*/nullptr, feature_flags_.get()),
126               StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
127 }
128 
TEST_F(SchemaStoreTest,CreationWithFeatureFlagsNullPointerShouldFail)129 TEST_F(SchemaStoreTest, CreationWithFeatureFlagsNullPointerShouldFail) {
130   EXPECT_THAT(SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
131                                   /*feature_flags=*/nullptr),
132               StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
133 }
134 
TEST_F(SchemaStoreTest,SchemaStoreMoveConstructible)135 TEST_F(SchemaStoreTest, SchemaStoreMoveConstructible) {
136   // Create an instance of SchemaStore.
137   SchemaProto schema =
138       SchemaBuilder()
139           .AddType(SchemaTypeConfigBuilder().SetType("type_a").AddProperty(
140               PropertyConfigBuilder()
141                   .SetName("prop1")
142                   .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
143                   .SetCardinality(CARDINALITY_OPTIONAL)))
144           .Build();
145 
146   ICING_ASSERT_OK_AND_ASSIGN(
147       std::unique_ptr<SchemaStore> schema_store,
148       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
149                           feature_flags_.get()));
150 
151   ICING_ASSERT_OK(schema_store->SetSchema(
152       schema, /*ignore_errors_and_delete_documents=*/false,
153       /*allow_circular_schema_definitions=*/false));
154   ICING_ASSERT_OK_AND_ASSIGN(Crc32 expected_checksum,
155                              schema_store->UpdateChecksum());
156 
157   // Move construct an instance of SchemaStore
158   SchemaStore move_constructed_schema_store(std::move(*schema_store));
159   EXPECT_THAT(move_constructed_schema_store.GetSchema(),
160               IsOkAndHolds(Pointee(EqualsProto(schema))));
161   EXPECT_THAT(move_constructed_schema_store.UpdateChecksum(),
162               IsOkAndHolds(Eq(expected_checksum)));
163   SectionMetadata expected_metadata(/*id_in=*/0, TYPE_STRING, TOKENIZER_PLAIN,
164                                     TERM_MATCH_EXACT, NUMERIC_MATCH_UNKNOWN,
165                                     EMBEDDING_INDEXING_UNKNOWN,
166                                     QUANTIZATION_TYPE_NONE, "prop1");
167   EXPECT_THAT(move_constructed_schema_store.GetSectionMetadata("type_a"),
168               IsOkAndHolds(Pointee(ElementsAre(expected_metadata))));
169 }
170 
TEST_F(SchemaStoreTest,SchemaStoreMoveAssignment)171 TEST_F(SchemaStoreTest, SchemaStoreMoveAssignment) {
172   // Create an instance of SchemaStore.
173   SchemaProto schema1 =
174       SchemaBuilder()
175           .AddType(SchemaTypeConfigBuilder().SetType("type_a").AddProperty(
176               PropertyConfigBuilder()
177                   .SetName("prop1")
178                   .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
179                   .SetCardinality(CARDINALITY_OPTIONAL)))
180           .Build();
181 
182   ICING_ASSERT_OK_AND_ASSIGN(
183       std::unique_ptr<SchemaStore> schema_store,
184       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
185                           feature_flags_.get()));
186 
187   ICING_ASSERT_OK(schema_store->SetSchema(
188       schema1, /*ignore_errors_and_delete_documents=*/false,
189       /*allow_circular_schema_definitions=*/false));
190   ICING_ASSERT_OK_AND_ASSIGN(Crc32 expected_checksum,
191                              schema_store->UpdateChecksum());
192 
193   // Construct another instance of SchemaStore
194   SchemaProto schema2 =
195       SchemaBuilder()
196           .AddType(SchemaTypeConfigBuilder().SetType("type_b").AddProperty(
197               PropertyConfigBuilder()
198                   .SetName("prop2")
199                   .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
200                   .SetCardinality(CARDINALITY_OPTIONAL)))
201           .Build();
202 
203   ICING_ASSERT_OK_AND_ASSIGN(
204       std::unique_ptr<SchemaStore> move_assigned_schema_store,
205       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
206                           feature_flags_.get()));
207   ICING_ASSERT_OK(schema_store->SetSchema(
208       schema2, /*ignore_errors_and_delete_documents=*/false,
209       /*allow_circular_schema_definitions=*/false));
210 
211   // Move assign the first instance into the second one.
212   *move_assigned_schema_store = std::move(*schema_store);
213   EXPECT_THAT(move_assigned_schema_store->GetSchema(),
214               IsOkAndHolds(Pointee(EqualsProto(schema1))));
215   EXPECT_THAT(move_assigned_schema_store->UpdateChecksum(),
216               IsOkAndHolds(Eq(expected_checksum)));
217   SectionMetadata expected_metadata(/*id_in=*/0, TYPE_STRING, TOKENIZER_PLAIN,
218                                     TERM_MATCH_EXACT, NUMERIC_MATCH_UNKNOWN,
219                                     EMBEDDING_INDEXING_UNKNOWN,
220                                     QUANTIZATION_TYPE_NONE, "prop1");
221   EXPECT_THAT(move_assigned_schema_store->GetSectionMetadata("type_a"),
222               IsOkAndHolds(Pointee(ElementsAre(expected_metadata))));
223 }
224 
TEST_F(SchemaStoreTest,CorruptSchemaError)225 TEST_F(SchemaStoreTest, CorruptSchemaError) {
226   {
227     ICING_ASSERT_OK_AND_ASSIGN(
228         std::unique_ptr<SchemaStore> schema_store,
229         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
230                             feature_flags_.get()));
231 
232     // Set it for the first time
233     SchemaStore::SetSchemaResult result;
234     result.success = true;
235     result.schema_types_new_by_name.insert(schema_.types(0).schema_type());
236     EXPECT_THAT(schema_store->SetSchema(
237                     schema_, /*ignore_errors_and_delete_documents=*/false,
238                     /*allow_circular_schema_definitions=*/false),
239                 IsOkAndHolds(EqualsSetSchemaResult(result)));
240     ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
241                                schema_store->GetSchema());
242     EXPECT_THAT(*actual_schema, EqualsProto(schema_));
243   }
244 
245   // "Corrupt" the  ground truth schema by adding new data to it. This will mess
246   // up the checksum of the schema store
247 
248   SchemaProto corrupt_schema =
249       SchemaBuilder()
250           .AddType(SchemaTypeConfigBuilder().SetType("corrupted"))
251           .Build();
252 
253   const std::string schema_file =
254       absl_ports::StrCat(schema_store_dir_, "/schema.pb");
255   const std::string serialized_schema = corrupt_schema.SerializeAsString();
256 
257   filesystem_.Write(schema_file.c_str(), serialized_schema.data(),
258                     serialized_schema.size());
259 
260   // If ground truth was corrupted, we won't know what to do
261   EXPECT_THAT(SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
262                                   feature_flags_.get()),
263               StatusIs(libtextclassifier3::StatusCode::INTERNAL));
264 }
265 
TEST_F(SchemaStoreTest,RecoverCorruptDerivedFileOk)266 TEST_F(SchemaStoreTest, RecoverCorruptDerivedFileOk) {
267   {
268     ICING_ASSERT_OK_AND_ASSIGN(
269         std::unique_ptr<SchemaStore> schema_store,
270         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
271                             feature_flags_.get()));
272 
273     // Set it for the first time
274     SchemaStore::SetSchemaResult result;
275     result.success = true;
276     result.schema_types_new_by_name.insert(schema_.types(0).schema_type());
277     EXPECT_THAT(schema_store->SetSchema(
278                     schema_, /*ignore_errors_and_delete_documents=*/false,
279                     /*allow_circular_schema_definitions=*/false),
280                 IsOkAndHolds(EqualsSetSchemaResult(result)));
281     ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
282                                schema_store->GetSchema());
283     EXPECT_THAT(*actual_schema, EqualsProto(schema_));
284 
285     EXPECT_THAT(schema_store->GetSchemaTypeId("email"), IsOkAndHolds(0));
286 
287     // Scorable property manager working as expected.
288     EXPECT_THAT(schema_store->GetOrderedScorablePropertyInfo(
289                     /*schema_type_id=*/0),
290                 IsOkAndHolds(Pointee(ElementsAre(
291                     EqualsScorablePropertyInfo("timestamp", TYPE_INT64)))));
292     EXPECT_THAT(schema_store->GetScorablePropertyIndex(
293                     /*schema_type_id=*/0,
294                     /*property_path=*/"timestamp"),
295                 IsOkAndHolds(0));
296   }
297 
298   // "Corrupt" the derived SchemaTypeIds by deleting the entire directory. This
299   // will mess up the initialization of schema store, causing everything to be
300   // regenerated from ground truth
301 
302   const std::string schema_type_mapper_dir =
303       absl_ports::StrCat(schema_store_dir_, "/schema_type_mapper");
304   filesystem_.DeleteDirectoryRecursively(schema_type_mapper_dir.c_str());
305 
306   InitializeStatsProto initialize_stats;
307   fake_clock_.SetTimerElapsedMilliseconds(123);
308   ICING_ASSERT_OK_AND_ASSIGN(
309       std::unique_ptr<SchemaStore> schema_store,
310       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
311                           feature_flags_.get(),
312                           /*enable_schema_database=*/false, &initialize_stats));
313   EXPECT_THAT(initialize_stats.schema_store_recovery_cause(),
314               Eq(InitializeStatsProto::IO_ERROR));
315   EXPECT_THAT(initialize_stats.schema_store_recovery_latency_ms(), Eq(123));
316 
317   // Everything looks fine, ground truth and derived data
318   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
319                              schema_store->GetSchema());
320   EXPECT_THAT(*actual_schema, EqualsProto(schema_));
321   EXPECT_THAT(schema_store->GetSchemaTypeId("email"), IsOkAndHolds(0));
322 
323   // Scorable property manager working as expected.
324   EXPECT_THAT(schema_store->GetOrderedScorablePropertyInfo(
325                   /*schema_type_id=*/0),
326               IsOkAndHolds(Pointee(ElementsAre(
327                   EqualsScorablePropertyInfo("timestamp", TYPE_INT64)))));
328   EXPECT_THAT(schema_store->GetScorablePropertyIndex(
329                   /*schema_type_id=*/0,
330                   /*property_path=*/"timestamp"),
331               IsOkAndHolds(0));
332 }
333 
TEST_F(SchemaStoreTest,RecoverDiscardDerivedFilesOk)334 TEST_F(SchemaStoreTest, RecoverDiscardDerivedFilesOk) {
335   {
336     ICING_ASSERT_OK_AND_ASSIGN(
337         std::unique_ptr<SchemaStore> schema_store,
338         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
339                             feature_flags_.get()));
340 
341     // Set it for the first time
342     SchemaStore::SetSchemaResult result;
343     result.success = true;
344     result.schema_types_new_by_name.insert(schema_.types(0).schema_type());
345     EXPECT_THAT(schema_store->SetSchema(
346                     schema_, /*ignore_errors_and_delete_documents=*/false,
347                     /*allow_circular_schema_definitions=*/false),
348                 IsOkAndHolds(EqualsSetSchemaResult(result)));
349     ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
350                                schema_store->GetSchema());
351     EXPECT_THAT(*actual_schema, EqualsProto(schema_));
352 
353     EXPECT_THAT(schema_store->GetSchemaTypeId("email"), IsOkAndHolds(0));
354 
355     // Scorable property manager working as expected.
356     EXPECT_THAT(schema_store->GetOrderedScorablePropertyInfo(
357                     /*schema_type_id=*/0),
358                 IsOkAndHolds(Pointee(ElementsAre(
359                     EqualsScorablePropertyInfo("timestamp", TYPE_INT64)))));
360     EXPECT_THAT(schema_store->GetScorablePropertyIndex(
361                     /*schema_type_id=*/0,
362                     /*property_path=*/"timestamp"),
363                 IsOkAndHolds(0));
364   }
365 
366   ICING_ASSERT_OK(
367       SchemaStore::DiscardDerivedFiles(&filesystem_, schema_store_dir_));
368 
369   InitializeStatsProto initialize_stats;
370   fake_clock_.SetTimerElapsedMilliseconds(123);
371   ICING_ASSERT_OK_AND_ASSIGN(
372       std::unique_ptr<SchemaStore> schema_store,
373       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
374                           feature_flags_.get(),
375                           /*enable_schema_database=*/false, &initialize_stats));
376   EXPECT_THAT(initialize_stats.schema_store_recovery_cause(),
377               Eq(InitializeStatsProto::IO_ERROR));
378   EXPECT_THAT(initialize_stats.schema_store_recovery_latency_ms(), Eq(123));
379 
380   // Everything looks fine, ground truth and derived data
381   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
382                              schema_store->GetSchema());
383   EXPECT_THAT(*actual_schema, EqualsProto(schema_));
384   EXPECT_THAT(schema_store->GetSchemaTypeId("email"), IsOkAndHolds(0));
385 
386   // Scorable property manager working as expected.
387   EXPECT_THAT(schema_store->GetOrderedScorablePropertyInfo(
388                   /*schema_type_id=*/0),
389               IsOkAndHolds(Pointee(ElementsAre(
390                   EqualsScorablePropertyInfo("timestamp", TYPE_INT64)))));
391   EXPECT_THAT(schema_store->GetScorablePropertyIndex(
392                   /*schema_type_id=*/0,
393                   /*property_path=*/"timestamp"),
394               IsOkAndHolds(0));
395 }
396 
TEST_F(SchemaStoreTest,RecoverBadChecksumOk)397 TEST_F(SchemaStoreTest, RecoverBadChecksumOk) {
398   {
399     ICING_ASSERT_OK_AND_ASSIGN(
400         std::unique_ptr<SchemaStore> schema_store,
401         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
402                             feature_flags_.get()));
403 
404     // Set it for the first time
405     SchemaStore::SetSchemaResult result;
406     result.success = true;
407     result.schema_types_new_by_name.insert(schema_.types(0).schema_type());
408     EXPECT_THAT(schema_store->SetSchema(
409                     schema_, /*ignore_errors_and_delete_documents=*/false,
410                     /*allow_circular_schema_definitions=*/false),
411                 IsOkAndHolds(EqualsSetSchemaResult(result)));
412     ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
413                                schema_store->GetSchema());
414     EXPECT_THAT(*actual_schema, EqualsProto(schema_));
415 
416     EXPECT_THAT(schema_store->GetSchemaTypeId("email"), IsOkAndHolds(0));
417 
418     // Scorable property manager working as expected.
419     EXPECT_THAT(schema_store->GetOrderedScorablePropertyInfo(
420                     /*schema_type_id=*/0),
421                 IsOkAndHolds(Pointee(ElementsAre(
422                     EqualsScorablePropertyInfo("timestamp", TYPE_INT64)))));
423     EXPECT_THAT(schema_store->GetScorablePropertyIndex(
424                     /*schema_type_id=*/0,
425                     /*property_path=*/"timestamp"),
426                 IsOkAndHolds(0));
427   }
428 
429   // Change the SchemaStore's header combined checksum so that it won't match
430   // the recalculated checksum on initialization. This will force a regeneration
431   // of derived files from ground truth.
432   const std::string header_file =
433       absl_ports::StrCat(schema_store_dir_, "/schema_store_header");
434   SchemaStore::LegacyHeader header;
435   header.magic = SchemaStore::Header::kMagic;
436   header.checksum = 10;  // Arbitrary garbage checksum
437   filesystem_.DeleteFile(header_file.c_str());
438   filesystem_.Write(header_file.c_str(), &header, sizeof(header));
439 
440   ICING_ASSERT_OK_AND_ASSIGN(
441       std::unique_ptr<SchemaStore> schema_store,
442       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
443                           feature_flags_.get()));
444 
445   // Everything looks fine, ground truth and derived data
446   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
447                              schema_store->GetSchema());
448   EXPECT_THAT(*actual_schema, EqualsProto(schema_));
449   EXPECT_THAT(schema_store->GetSchemaTypeId("email"), IsOkAndHolds(0));
450 
451   // Scorable property manager working as expected.
452   EXPECT_THAT(schema_store->GetOrderedScorablePropertyInfo(
453                   /*schema_type_id=*/0),
454               IsOkAndHolds(Pointee(ElementsAre(
455                   EqualsScorablePropertyInfo("timestamp", TYPE_INT64)))));
456   EXPECT_THAT(schema_store->GetScorablePropertyIndex(
457                   /*schema_type_id=*/0,
458                   /*property_path=*/"timestamp"),
459               IsOkAndHolds(0));
460 }
461 
TEST_F(SchemaStoreTest,CreateNoPreviousSchemaOk)462 TEST_F(SchemaStoreTest, CreateNoPreviousSchemaOk) {
463   ICING_ASSERT_OK_AND_ASSIGN(
464       std::unique_ptr<SchemaStore> store,
465       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
466                           feature_flags_.get()));
467 
468   // The apis to retrieve information about the schema should fail gracefully.
469   EXPECT_THAT(store->GetSchema(),
470               StatusIs(libtextclassifier3::StatusCode::NOT_FOUND));
471   EXPECT_THAT(store->GetSchemaTypeConfig("foo"),
472               StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
473   EXPECT_THAT(store->GetSchemaTypeId("foo"),
474               StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
475   EXPECT_THAT(store->GetSectionMetadata(/*schema_type_id=*/0, /*section_id=*/0),
476               StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
477   EXPECT_THAT(store->GetJoinablePropertyMetadata(/*schema_type_id=*/0,
478                                                  /*property_path=*/"A"),
479               StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
480 
481   // The apis to extract content from a document should fail gracefully.
482   DocumentProto doc;
483   PropertyProto* prop = doc.add_properties();
484   prop->set_name("name");
485   prop->add_string_values("foo bar baz");
486 
487   EXPECT_THAT(store->ExtractSections(doc),
488               StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
489   EXPECT_THAT(store->ExtractJoinableProperties(doc),
490               StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
491 
492   // The apis to persist and checksum data should succeed.
493   EXPECT_THAT(store->UpdateChecksum(), IsOkAndHolds(Crc32()));
494   EXPECT_THAT(store->PersistToDisk(), IsOk());
495 }
496 
TEST_F(SchemaStoreTest,CreateWithPreviousSchemaOk)497 TEST_F(SchemaStoreTest, CreateWithPreviousSchemaOk) {
498   ICING_ASSERT_OK_AND_ASSIGN(
499       std::unique_ptr<SchemaStore> schema_store,
500       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
501                           feature_flags_.get()));
502 
503   SchemaStore::SetSchemaResult result;
504   result.success = true;
505   result.schema_types_new_by_name.insert(schema_.types(0).schema_type());
506   EXPECT_THAT(schema_store->SetSchema(
507                   schema_, /*ignore_errors_and_delete_documents=*/false,
508                   /*allow_circular_schema_definitions=*/false),
509               IsOkAndHolds(EqualsSetSchemaResult(result)));
510 
511   schema_store.reset();
512   EXPECT_THAT(SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
513                                   feature_flags_.get()),
514               IsOk());
515 }
516 
TEST_F(SchemaStoreTest,MultipleCreateOk)517 TEST_F(SchemaStoreTest, MultipleCreateOk) {
518   DocumentProto document;
519   document.set_schema("email");
520   auto subject_property = document.add_properties();
521   subject_property->set_name("subject");
522   subject_property->add_string_values("subject_content");
523   auto timestamp_property = document.add_properties();
524   timestamp_property->set_name("timestamp");
525   timestamp_property->add_int64_values(kDefaultTimestamp);
526 
527   ICING_ASSERT_OK_AND_ASSIGN(
528       std::unique_ptr<SchemaStore> schema_store,
529       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
530                           feature_flags_.get()));
531 
532   SchemaStore::SetSchemaResult result;
533   result.success = true;
534   result.schema_types_new_by_name.insert(schema_.types(0).schema_type());
535   EXPECT_THAT(schema_store->SetSchema(
536                   schema_, /*ignore_errors_and_delete_documents=*/false,
537                   /*allow_circular_schema_definitions=*/false),
538               IsOkAndHolds(EqualsSetSchemaResult(result)));
539 
540   // Verify that our in-memory structures are ok
541   EXPECT_THAT(schema_store->GetSchemaTypeConfig("email"),
542               IsOkAndHolds(Pointee(EqualsProto(schema_.types(0)))));
543   ICING_ASSERT_OK_AND_ASSIGN(SectionGroup section_group,
544                              schema_store->ExtractSections(document));
545   EXPECT_THAT(section_group.string_sections[0].content,
546               ElementsAre("subject_content"));
547   EXPECT_THAT(section_group.integer_sections[0].content,
548               ElementsAre(kDefaultTimestamp));
549 
550   // Scorable property manager working as expected.
551   EXPECT_THAT(schema_store->GetOrderedScorablePropertyInfo(
552                   /*schema_type_id=*/0),
553               IsOkAndHolds(Pointee(ElementsAre(
554                   EqualsScorablePropertyInfo("timestamp", TYPE_INT64)))));
555   EXPECT_THAT(schema_store->GetScorablePropertyIndex(
556                   /*schema_type_id=*/0,
557                   /*property_path=*/"timestamp"),
558               IsOkAndHolds(0));
559 
560   // Verify that our persisted data is ok
561   EXPECT_THAT(schema_store->GetSchemaTypeId("email"), IsOkAndHolds(0));
562 
563   schema_store.reset();
564   ICING_ASSERT_OK_AND_ASSIGN(
565       schema_store, SchemaStore::Create(&filesystem_, schema_store_dir_,
566                                         &fake_clock_, feature_flags_.get()));
567 
568   // Verify that our in-memory structures are ok
569   EXPECT_THAT(schema_store->GetSchemaTypeConfig("email"),
570               IsOkAndHolds(Pointee(EqualsProto(schema_.types(0)))));
571 
572   ICING_ASSERT_OK_AND_ASSIGN(section_group,
573                              schema_store->ExtractSections(document));
574   EXPECT_THAT(section_group.string_sections[0].content,
575               ElementsAre("subject_content"));
576   EXPECT_THAT(section_group.integer_sections[0].content,
577               ElementsAre(kDefaultTimestamp));
578 
579   // Scorable property manager working as expected.
580   EXPECT_THAT(schema_store->GetOrderedScorablePropertyInfo(
581                   /*schema_type_id=*/0),
582               IsOkAndHolds(Pointee(ElementsAre(
583                   EqualsScorablePropertyInfo("timestamp", TYPE_INT64)))));
584   EXPECT_THAT(schema_store->GetScorablePropertyIndex(
585                   /*schema_type_id=*/0,
586                   /*property_path=*/"timestamp"),
587               IsOkAndHolds(0));
588 
589   // Verify that our persisted data is ok
590   EXPECT_THAT(schema_store->GetSchemaTypeId("email"), IsOkAndHolds(0));
591 }
592 
TEST_F(SchemaStoreTest,SetNewSchemaOk)593 TEST_F(SchemaStoreTest, SetNewSchemaOk) {
594   ICING_ASSERT_OK_AND_ASSIGN(
595       std::unique_ptr<SchemaStore> schema_store,
596       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
597                           feature_flags_.get()));
598 
599   // Set it for the first time
600   SchemaStore::SetSchemaResult result;
601   result.success = true;
602   result.schema_types_new_by_name.insert(schema_.types(0).schema_type());
603   EXPECT_THAT(schema_store->SetSchema(
604                   schema_, /*ignore_errors_and_delete_documents=*/false,
605                   /*allow_circular_schema_definitions=*/false),
606               IsOkAndHolds(EqualsSetSchemaResult(result)));
607   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
608                              schema_store->GetSchema());
609   EXPECT_THAT(*actual_schema, EqualsProto(schema_));
610 }
611 
TEST_F(SchemaStoreTest,SetNewSchemaInDifferentDatabaseOk)612 TEST_F(SchemaStoreTest, SetNewSchemaInDifferentDatabaseOk) {
613   ICING_ASSERT_OK_AND_ASSIGN(
614       std::unique_ptr<SchemaStore> schema_store,
615       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
616                           feature_flags_.get(),
617                           /*enable_schema_database=*/true,
618                           /*initialize_stats=*/nullptr));
619 
620   SchemaProto db1_schema =
621       SchemaBuilder()
622           .AddType(
623               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
624           .AddType(SchemaTypeConfigBuilder()
625                        .SetType("db1_message")
626                        .SetDatabase("db1"))
627           .Build();
628   SchemaStore::SetSchemaResult result;
629   result.success = true;
630   result.schema_types_new_by_name.insert("db1_email");
631   result.schema_types_new_by_name.insert("db1_message");
632   EXPECT_THAT(schema_store->SetSchema(
633                   db1_schema, /*ignore_errors_and_delete_documents=*/false,
634                   /*allow_circular_schema_definitions=*/false),
635               IsOkAndHolds(EqualsSetSchemaResult(result)));
636   EXPECT_THAT(schema_store->GetSchema(),
637               IsOkAndHolds(Pointee(EqualsProto(db1_schema))));
638   EXPECT_THAT(schema_store->GetSchema("db1"),
639               IsOkAndHolds(EqualsProto(db1_schema)));
640 
641   // Set a schema in a different database
642   SchemaProto db2_schema =
643       SchemaBuilder()
644           .AddType(
645               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
646           .AddType(SchemaTypeConfigBuilder()
647                        .SetType("db2_message")
648                        .SetDatabase("db2"))
649           .Build();
650   result = SchemaStore::SetSchemaResult();
651   result.success = true;
652   result.schema_types_new_by_name.insert("db2_email");
653   result.schema_types_new_by_name.insert("db2_message");
654   EXPECT_THAT(schema_store->SetSchema(
655                   db2_schema, /*ignore_errors_and_delete_documents=*/false,
656                   /*allow_circular_schema_definitions=*/false),
657               IsOkAndHolds(EqualsSetSchemaResult(result)));
658 
659   // Check the full schema. Databases that are updated last are appended to the
660   // schema proto
661   SchemaProto expected_full_schema =
662       SchemaBuilder()
663           .AddType(
664               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
665           .AddType(SchemaTypeConfigBuilder()
666                        .SetType("db1_message")
667                        .SetDatabase("db1"))
668           .AddType(
669               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
670           .AddType(SchemaTypeConfigBuilder()
671                        .SetType("db2_message")
672                        .SetDatabase("db2"))
673           .Build();
674   EXPECT_THAT(schema_store->GetSchema(),
675               IsOkAndHolds(Pointee(EqualsProto(expected_full_schema))));
676   EXPECT_THAT(schema_store->GetSchema("db1"),
677               IsOkAndHolds(EqualsProto(db1_schema)));
678   EXPECT_THAT(schema_store->GetSchema("db2"),
679               IsOkAndHolds(EqualsProto(db2_schema)));
680 }
681 
TEST_F(SchemaStoreTest,SetSameSchemaOk)682 TEST_F(SchemaStoreTest, SetSameSchemaOk) {
683   ICING_ASSERT_OK_AND_ASSIGN(
684       std::unique_ptr<SchemaStore> schema_store,
685       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
686                           feature_flags_.get()));
687 
688   // Set it for the first time
689   SchemaStore::SetSchemaResult result;
690   result.success = true;
691   result.schema_types_new_by_name.insert(schema_.types(0).schema_type());
692   EXPECT_THAT(schema_store->SetSchema(
693                   schema_, /*ignore_errors_and_delete_documents=*/false,
694                   /*allow_circular_schema_definitions=*/false),
695               IsOkAndHolds(EqualsSetSchemaResult(result)));
696   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
697                              schema_store->GetSchema());
698   EXPECT_THAT(*actual_schema, EqualsProto(schema_));
699 
700   // And one more for fun
701   result = SchemaStore::SetSchemaResult();
702   result.success = true;
703   EXPECT_THAT(schema_store->SetSchema(
704                   schema_, /*ignore_errors_and_delete_documents=*/false,
705                   /*allow_circular_schema_definitions=*/false),
706               IsOkAndHolds(EqualsSetSchemaResult(result)));
707   ICING_ASSERT_OK_AND_ASSIGN(actual_schema, schema_store->GetSchema());
708   EXPECT_THAT(*actual_schema, EqualsProto(schema_));
709 }
710 
TEST_F(SchemaStoreTest,SetSameDatabaseSchemaOk)711 TEST_F(SchemaStoreTest, SetSameDatabaseSchemaOk) {
712   ICING_ASSERT_OK_AND_ASSIGN(
713       std::unique_ptr<SchemaStore> schema_store,
714       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
715                           feature_flags_.get(),
716                           /*enable_schema_database=*/true,
717                           /*initialize_stats=*/nullptr));
718 
719   // Set schema for the first time
720   SchemaProto db1_schema =
721       SchemaBuilder()
722           .AddType(
723               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
724           .AddType(SchemaTypeConfigBuilder()
725                        .SetType("db1_message")
726                        .SetDatabase("db1"))
727           .Build();
728   SchemaProto db2_schema =
729       SchemaBuilder()
730           .AddType(
731               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
732           .AddType(SchemaTypeConfigBuilder()
733                        .SetType("db2_message")
734                        .SetDatabase("db2"))
735           .Build();
736   SchemaProto expected_full_schema =
737       SchemaBuilder()
738           .AddType(
739               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
740           .AddType(SchemaTypeConfigBuilder()
741                        .SetType("db1_message")
742                        .SetDatabase("db1"))
743           .AddType(
744               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
745           .AddType(SchemaTypeConfigBuilder()
746                        .SetType("db2_message")
747                        .SetDatabase("db2"))
748           .Build();
749   SchemaStore::SetSchemaResult result;
750   result.success = true;
751   result.schema_types_new_by_name.insert("db1_email");
752   result.schema_types_new_by_name.insert("db1_message");
753   EXPECT_THAT(schema_store->SetSchema(
754                   db1_schema, /*ignore_errors_and_delete_documents=*/false,
755                   /*allow_circular_schema_definitions=*/false),
756               IsOkAndHolds(EqualsSetSchemaResult(result)));
757   result = SchemaStore::SetSchemaResult();
758   result.success = true;
759   result.schema_types_new_by_name.insert("db2_email");
760   result.schema_types_new_by_name.insert("db2_message");
761   EXPECT_THAT(schema_store->SetSchema(
762                   db2_schema, /*ignore_errors_and_delete_documents=*/false,
763                   /*allow_circular_schema_definitions=*/false),
764               IsOkAndHolds(EqualsSetSchemaResult(result)));
765   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_full_schema,
766                              schema_store->GetSchema());
767   EXPECT_THAT(*actual_full_schema, EqualsProto(expected_full_schema));
768 
769   // Reset db1 with the same SchemaProto. The schema should be exactly the same.
770   result = SchemaStore::SetSchemaResult();
771   result.success = true;
772   EXPECT_THAT(
773       schema_store->SetSchema(db1_schema,
774                               /*ignore_errors_and_delete_documents=*/false,
775                               /*allow_circular_schema_definitions=*/false),
776       IsOkAndHolds(EqualsSetSchemaResult(result)));
777 
778   // Check the schema, this should not have changed
779   EXPECT_THAT(schema_store->GetSchema(),
780               IsOkAndHolds(Pointee(EqualsProto(expected_full_schema))));
781   EXPECT_THAT(schema_store->GetSchema("db1"),
782               IsOkAndHolds(EqualsProto(db1_schema)));
783   EXPECT_THAT(schema_store->GetSchema("db2"),
784               IsOkAndHolds(EqualsProto(db2_schema)));
785 }
786 
TEST_F(SchemaStoreTest,SetDatabaseReorderedTypesPreservesSchemaTypeIds)787 TEST_F(SchemaStoreTest, SetDatabaseReorderedTypesPreservesSchemaTypeIds) {
788   ICING_ASSERT_OK_AND_ASSIGN(
789       std::unique_ptr<SchemaStore> schema_store,
790       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
791                           feature_flags_.get(),
792                           /*enable_schema_database=*/true,
793                           /*initialize_stats=*/nullptr));
794 
795   // Set schema for the first time
796   SchemaProto db1_schema =
797       SchemaBuilder()
798           .AddType(
799               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
800           .AddType(SchemaTypeConfigBuilder()
801                        .SetType("db1_message")
802                        .SetDatabase("db1"))
803           .Build();
804   SchemaProto db2_schema =
805       SchemaBuilder()
806           .AddType(
807               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
808           .AddType(SchemaTypeConfigBuilder()
809                        .SetType("db2_message")
810                        .SetDatabase("db2"))
811           .Build();
812   SchemaProto db3_schema =
813       SchemaBuilder()
814           .AddType(
815               SchemaTypeConfigBuilder().SetType("db3_email").SetDatabase("db3"))
816           .AddType(SchemaTypeConfigBuilder()
817                        .SetType("db3_message")
818                        .SetDatabase("db3"))
819           .Build();
820   SchemaProto expected_full_schema =
821       SchemaBuilder()
822           .AddType(
823               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
824           .AddType(SchemaTypeConfigBuilder()
825                        .SetType("db1_message")
826                        .SetDatabase("db1"))
827           .AddType(
828               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
829           .AddType(SchemaTypeConfigBuilder()
830                        .SetType("db2_message")
831                        .SetDatabase("db2"))
832           .AddType(
833               SchemaTypeConfigBuilder().SetType("db3_email").SetDatabase("db3"))
834           .AddType(SchemaTypeConfigBuilder()
835                        .SetType("db3_message")
836                        .SetDatabase("db3"))
837           .Build();
838 
839   // Set schema for db1
840   SchemaStore::SetSchemaResult result;
841   result.success = true;
842   result.schema_types_new_by_name.insert("db1_email");
843   result.schema_types_new_by_name.insert("db1_message");
844   EXPECT_THAT(schema_store->SetSchema(
845                   db1_schema, /*ignore_errors_and_delete_documents=*/false,
846                   /*allow_circular_schema_definitions=*/false),
847               IsOkAndHolds(EqualsSetSchemaResult(result)));
848   // Set schema for db2
849   result = SchemaStore::SetSchemaResult();
850   result.success = true;
851   result.schema_types_new_by_name.insert("db2_email");
852   result.schema_types_new_by_name.insert("db2_message");
853   EXPECT_THAT(schema_store->SetSchema(
854                   db2_schema, /*ignore_errors_and_delete_documents=*/false,
855                   /*allow_circular_schema_definitions=*/false),
856               IsOkAndHolds(EqualsSetSchemaResult(result)));
857   // Set schema for db3
858   result = SchemaStore::SetSchemaResult();
859   result.success = true;
860   result.schema_types_new_by_name.insert("db3_email");
861   result.schema_types_new_by_name.insert("db3_message");
862   EXPECT_THAT(schema_store->SetSchema(
863                   db3_schema, /*ignore_errors_and_delete_documents=*/false,
864                   /*allow_circular_schema_definitions=*/false),
865               IsOkAndHolds(EqualsSetSchemaResult(result)));
866   // Verify schema.
867   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_full_schema,
868                              schema_store->GetSchema());
869   EXPECT_THAT(*actual_full_schema, EqualsProto(expected_full_schema));
870 
871   // Reset db2 with the types reordered. The expected full schema will be
872   // different, but the SchemaTypeIds for db1 and db3 should not change.
873   db2_schema =
874       SchemaBuilder()
875           .AddType(SchemaTypeConfigBuilder()
876                        .SetType("db2_message")
877                        .SetDatabase("db2"))
878           .AddType(
879               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
880           .Build();
881   expected_full_schema =
882       SchemaBuilder()
883           .AddType(
884               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
885           .AddType(SchemaTypeConfigBuilder()
886                        .SetType("db1_message")
887                        .SetDatabase("db1"))
888           .AddType(SchemaTypeConfigBuilder()
889                        .SetType("db2_message")
890                        .SetDatabase("db2"))
891           .AddType(
892               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
893           .AddType(
894               SchemaTypeConfigBuilder().SetType("db3_email").SetDatabase("db3"))
895           .AddType(SchemaTypeConfigBuilder()
896                        .SetType("db3_message")
897                        .SetDatabase("db3"))
898           .Build();
899   result = SchemaStore::SetSchemaResult();
900   result.success = true;
901   // Only db2's schema type ids should change.
902   result.old_schema_type_ids_changed.insert(2);
903   result.old_schema_type_ids_changed.insert(3);
904   EXPECT_THAT(
905       schema_store->SetSchema(db2_schema,
906                               /*ignore_errors_and_delete_documents=*/false,
907                               /*allow_circular_schema_definitions=*/false),
908       IsOkAndHolds(EqualsSetSchemaResult(result)));
909 
910   // Check the schema
911   EXPECT_THAT(schema_store->GetSchema(),
912               IsOkAndHolds(Pointee(EqualsProto(expected_full_schema))));
913   EXPECT_THAT(schema_store->GetSchema("db1"),
914               IsOkAndHolds(EqualsProto(db1_schema)));
915   EXPECT_THAT(schema_store->GetSchema("db2"),
916               IsOkAndHolds(EqualsProto(db2_schema)));
917   EXPECT_THAT(schema_store->GetSchema("db3"),
918               IsOkAndHolds(EqualsProto(db3_schema)));
919 }
920 
TEST_F(SchemaStoreTest,SetDatabaseAddedTypesPreservesSchemaTypeIds)921 TEST_F(SchemaStoreTest, SetDatabaseAddedTypesPreservesSchemaTypeIds) {
922   ICING_ASSERT_OK_AND_ASSIGN(
923       std::unique_ptr<SchemaStore> schema_store,
924       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
925                           feature_flags_.get(),
926                           /*enable_schema_database=*/true,
927                           /*initialize_stats=*/nullptr));
928 
929   // Set schema for the first time
930   SchemaProto db1_schema =
931       SchemaBuilder()
932           .AddType(
933               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
934           .AddType(SchemaTypeConfigBuilder()
935                        .SetType("db1_message")
936                        .SetDatabase("db1"))
937           .Build();
938   SchemaProto db2_schema =
939       SchemaBuilder()
940           .AddType(
941               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
942           .AddType(SchemaTypeConfigBuilder()
943                        .SetType("db2_message")
944                        .SetDatabase("db2"))
945           .Build();
946   SchemaProto db3_schema =
947       SchemaBuilder()
948           .AddType(
949               SchemaTypeConfigBuilder().SetType("db3_email").SetDatabase("db3"))
950           .AddType(SchemaTypeConfigBuilder()
951                        .SetType("db3_message")
952                        .SetDatabase("db3"))
953           .Build();
954   SchemaProto expected_full_schema =
955       SchemaBuilder()
956           .AddType(
957               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
958           .AddType(SchemaTypeConfigBuilder()
959                        .SetType("db1_message")
960                        .SetDatabase("db1"))
961           .AddType(
962               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
963           .AddType(SchemaTypeConfigBuilder()
964                        .SetType("db2_message")
965                        .SetDatabase("db2"))
966           .AddType(
967               SchemaTypeConfigBuilder().SetType("db3_email").SetDatabase("db3"))
968           .AddType(SchemaTypeConfigBuilder()
969                        .SetType("db3_message")
970                        .SetDatabase("db3"))
971           .Build();
972 
973   // Set schema for db1
974   SchemaStore::SetSchemaResult result;
975   result.success = true;
976   result.schema_types_new_by_name.insert("db1_email");
977   result.schema_types_new_by_name.insert("db1_message");
978   EXPECT_THAT(schema_store->SetSchema(
979                   db1_schema, /*ignore_errors_and_delete_documents=*/false,
980                   /*allow_circular_schema_definitions=*/false),
981               IsOkAndHolds(EqualsSetSchemaResult(result)));
982   // Set schema for db2
983   result = SchemaStore::SetSchemaResult();
984   result.success = true;
985   result.schema_types_new_by_name.insert("db2_email");
986   result.schema_types_new_by_name.insert("db2_message");
987   EXPECT_THAT(schema_store->SetSchema(
988                   db2_schema, /*ignore_errors_and_delete_documents=*/false,
989                   /*allow_circular_schema_definitions=*/false),
990               IsOkAndHolds(EqualsSetSchemaResult(result)));
991   // Set schema for db3
992   result = SchemaStore::SetSchemaResult();
993   result.success = true;
994   result.schema_types_new_by_name.insert("db3_email");
995   result.schema_types_new_by_name.insert("db3_message");
996   EXPECT_THAT(schema_store->SetSchema(
997                   db3_schema, /*ignore_errors_and_delete_documents=*/false,
998                   /*allow_circular_schema_definitions=*/false),
999               IsOkAndHolds(EqualsSetSchemaResult(result)));
1000   // Verify schema.
1001   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_full_schema,
1002                              schema_store->GetSchema());
1003   EXPECT_THAT(*actual_full_schema, EqualsProto(expected_full_schema));
1004 
1005   // Reset db2 and add a type. The added type should be appended to the end of
1006   // the SchemaProto, and SchemaTypeIds for db1 and db3 should not change.
1007   //
1008   // Whether or not the SchemaTypeIds for db2 change depends on the order in the
1009   // new db2 SchemaProto (in this case, existing type's order and ids do not
1010   // change)
1011   db2_schema =
1012       SchemaBuilder()
1013           .AddType(
1014               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
1015           .AddType(SchemaTypeConfigBuilder()
1016                        .SetType("db2_message")
1017                        .SetDatabase("db2"))
1018           .AddType(SchemaTypeConfigBuilder()
1019                        .SetType("db2_recipient")
1020                        .SetDatabase("db2"))
1021           .Build();
1022   expected_full_schema =
1023       SchemaBuilder()
1024           .AddType(SchemaTypeConfigBuilder()  // db1 types
1025                        .SetType("db1_email")
1026                        .SetDatabase("db1"))
1027           .AddType(SchemaTypeConfigBuilder()
1028                        .SetType("db1_message")
1029                        .SetDatabase("db1"))
1030           .AddType(SchemaTypeConfigBuilder()  // db2 types
1031                        .SetType("db2_email")
1032                        .SetDatabase("db2"))
1033           .AddType(SchemaTypeConfigBuilder()
1034                        .SetType("db2_message")
1035                        .SetDatabase("db2"))
1036           .AddType(SchemaTypeConfigBuilder()  // db3 types
1037                        .SetType("db3_email")
1038                        .SetDatabase("db3"))
1039           .AddType(SchemaTypeConfigBuilder()
1040                        .SetType("db3_message")
1041                        .SetDatabase("db3"))
1042           .AddType(SchemaTypeConfigBuilder()  // Additional db2 type is appended
1043                                               // at the end
1044                        .SetType("db2_recipient")
1045                        .SetDatabase("db2"))
1046           .Build();
1047   result = SchemaStore::SetSchemaResult();
1048   result.success = true;
1049   result.schema_types_new_by_name.insert("db2_recipient");
1050   EXPECT_THAT(
1051       schema_store->SetSchema(db2_schema,
1052                               /*ignore_errors_and_delete_documents=*/false,
1053                               /*allow_circular_schema_definitions=*/false),
1054       IsOkAndHolds(EqualsSetSchemaResult(result)));
1055 
1056   // Check the schema
1057   EXPECT_THAT(schema_store->GetSchema(),
1058               IsOkAndHolds(Pointee(EqualsProto(expected_full_schema))));
1059   EXPECT_THAT(schema_store->GetSchema("db1"),
1060               IsOkAndHolds(EqualsProto(db1_schema)));
1061   EXPECT_THAT(schema_store->GetSchema("db2"),
1062               IsOkAndHolds(EqualsProto(db2_schema)));
1063   EXPECT_THAT(schema_store->GetSchema("db3"),
1064               IsOkAndHolds(EqualsProto(db3_schema)));
1065 }
1066 
TEST_F(SchemaStoreTest,SetDatabaseDeletedTypesOk)1067 TEST_F(SchemaStoreTest, SetDatabaseDeletedTypesOk) {
1068   ICING_ASSERT_OK_AND_ASSIGN(
1069       std::unique_ptr<SchemaStore> schema_store,
1070       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1071                           feature_flags_.get(),
1072                           /*enable_schema_database=*/true,
1073                           /*initialize_stats=*/nullptr));
1074 
1075   // Set schema for the first time
1076   SchemaProto db1_schema =
1077       SchemaBuilder()
1078           .AddType(
1079               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
1080           .AddType(SchemaTypeConfigBuilder()
1081                        .SetType("db1_message")
1082                        .SetDatabase("db1"))
1083           .Build();
1084   SchemaProto db2_schema =
1085       SchemaBuilder()
1086           .AddType(
1087               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
1088           .AddType(SchemaTypeConfigBuilder()
1089                        .SetType("db2_message")
1090                        .SetDatabase("db2"))
1091           .Build();
1092   SchemaProto db3_schema =
1093       SchemaBuilder()
1094           .AddType(
1095               SchemaTypeConfigBuilder().SetType("db3_email").SetDatabase("db3"))
1096           .AddType(SchemaTypeConfigBuilder()
1097                        .SetType("db3_message")
1098                        .SetDatabase("db3"))
1099           .Build();
1100 
1101   // Set schema for db1
1102   SchemaStore::SetSchemaResult result;
1103   result.success = true;
1104   result.schema_types_new_by_name.insert("db1_email");
1105   result.schema_types_new_by_name.insert("db1_message");
1106   EXPECT_THAT(schema_store->SetSchema(
1107                   db1_schema, /*ignore_errors_and_delete_documents=*/false,
1108                   /*allow_circular_schema_definitions=*/false),
1109               IsOkAndHolds(EqualsSetSchemaResult(result)));
1110   // Set schema for db2
1111   result = SchemaStore::SetSchemaResult();
1112   result.success = true;
1113   result.schema_types_new_by_name.insert("db2_email");
1114   result.schema_types_new_by_name.insert("db2_message");
1115   EXPECT_THAT(schema_store->SetSchema(
1116                   db2_schema, /*ignore_errors_and_delete_documents=*/false,
1117                   /*allow_circular_schema_definitions=*/false),
1118               IsOkAndHolds(EqualsSetSchemaResult(result)));
1119   // Set schema for db3
1120   result = SchemaStore::SetSchemaResult();
1121   result.success = true;
1122   result.schema_types_new_by_name.insert("db3_email");
1123   result.schema_types_new_by_name.insert("db3_message");
1124   EXPECT_THAT(schema_store->SetSchema(
1125                   db3_schema, /*ignore_errors_and_delete_documents=*/false,
1126                   /*allow_circular_schema_definitions=*/false),
1127               IsOkAndHolds(EqualsSetSchemaResult(result)));
1128   // Set schema again for db2 and add a type. The added type should be appended
1129   // to the end of the SchemaProto.
1130   db2_schema =
1131       SchemaBuilder()
1132           .AddType(
1133               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
1134           .AddType(SchemaTypeConfigBuilder()
1135                        .SetType("db2_message")
1136                        .SetDatabase("db2"))
1137           .AddType(SchemaTypeConfigBuilder()
1138                        .SetType("db2_recipient")
1139                        .SetDatabase("db2"))
1140           .Build();
1141   result = SchemaStore::SetSchemaResult();
1142   result.success = true;
1143   result.schema_types_new_by_name.insert("db2_recipient");
1144   EXPECT_THAT(schema_store->SetSchema(
1145                   db2_schema, /*ignore_errors_and_delete_documents=*/false,
1146                   /*allow_circular_schema_definitions=*/false),
1147               IsOkAndHolds(EqualsSetSchemaResult(result)));
1148   SchemaProto expected_full_schema =
1149       SchemaBuilder()
1150           .AddType(SchemaTypeConfigBuilder()
1151                        .SetType("db1_email")  // SchemaTypeId 0
1152                        .SetDatabase("db1"))
1153           .AddType(SchemaTypeConfigBuilder()
1154                        .SetType("db1_message")  // SchemaTypeId 1
1155                        .SetDatabase("db1"))
1156           .AddType(SchemaTypeConfigBuilder()
1157                        .SetType("db2_email")  // SchemaTypeId 2
1158                        .SetDatabase("db2"))
1159           .AddType(SchemaTypeConfigBuilder()
1160                        .SetType("db2_message")  // SchemaTypeId 3
1161                        .SetDatabase("db2"))
1162           .AddType(SchemaTypeConfigBuilder()
1163                        .SetType("db3_email")  // SchemaTypeId 4
1164                        .SetDatabase("db3"))
1165           .AddType(SchemaTypeConfigBuilder()
1166                        .SetType("db3_message")  // SchemaTypeId 5
1167                        .SetDatabase("db3"))
1168           .AddType(SchemaTypeConfigBuilder()
1169                        .SetType("db2_recipient")  // SchemaTypeId 6
1170                        .SetDatabase("db2"))
1171           .Build();
1172   // Verify schema.
1173   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_full_schema,
1174                              schema_store->GetSchema());
1175   EXPECT_THAT(*actual_full_schema, EqualsProto(expected_full_schema));
1176 
1177   // Reset db2 and delete some types. All types that were originally added after
1178   // db2 should have their type ids changed.
1179   db2_schema = SchemaBuilder()
1180                    .AddType(SchemaTypeConfigBuilder()
1181                                 .SetType("db2_message")
1182                                 .SetDatabase("db2"))
1183                    .Build();
1184   expected_full_schema =
1185       SchemaBuilder()
1186           .AddType(SchemaTypeConfigBuilder()
1187                        .SetType("db1_email")  // SchemaTypeId 0
1188                        .SetDatabase("db1"))
1189           .AddType(SchemaTypeConfigBuilder()
1190                        .SetType("db1_message")  // SchemaTypeId 1
1191                        .SetDatabase("db1"))
1192           .AddType(SchemaTypeConfigBuilder()
1193                        .SetType("db2_message")  // SchemaTypeId 2
1194                        .SetDatabase("db2"))
1195           .AddType(SchemaTypeConfigBuilder()
1196                        .SetType("db3_email")  // SchemaTypeId 3
1197                        .SetDatabase("db3"))
1198           .AddType(SchemaTypeConfigBuilder()
1199                        .SetType("db3_message")  // SchemaTypeId 4
1200                        .SetDatabase("db3"))
1201           .Build();
1202   result = SchemaStore::SetSchemaResult();
1203   result.success = true;
1204   result.schema_types_deleted_by_name.insert("db2_email");
1205   result.schema_types_deleted_by_name.insert("db2_recipient");
1206   result.schema_types_deleted_by_id.insert(2);   // db2_email
1207   result.schema_types_deleted_by_id.insert(6);   // db2_recipient
1208   result.old_schema_type_ids_changed.insert(3);  // db2_message
1209   result.old_schema_type_ids_changed.insert(4);  // db3_email
1210   result.old_schema_type_ids_changed.insert(5);  // db3_message
1211   EXPECT_THAT(
1212       schema_store->SetSchema(db2_schema,
1213                               /*ignore_errors_and_delete_documents=*/true,
1214                               /*allow_circular_schema_definitions=*/false),
1215       IsOkAndHolds(EqualsSetSchemaResult(result)));
1216 
1217   // Check the schema
1218   EXPECT_THAT(schema_store->GetSchema(),
1219               IsOkAndHolds(Pointee(EqualsProto(expected_full_schema))));
1220   EXPECT_THAT(schema_store->GetSchema("db1"),
1221               IsOkAndHolds(EqualsProto(db1_schema)));
1222   EXPECT_THAT(schema_store->GetSchema("db2"),
1223               IsOkAndHolds(EqualsProto(db2_schema)));
1224   EXPECT_THAT(schema_store->GetSchema("db3"),
1225               IsOkAndHolds(EqualsProto(db3_schema)));
1226 }
1227 
TEST_F(SchemaStoreTest,SetEmptySchemaInDifferentDatabaseOk)1228 TEST_F(SchemaStoreTest, SetEmptySchemaInDifferentDatabaseOk) {
1229   ICING_ASSERT_OK_AND_ASSIGN(
1230       std::unique_ptr<SchemaStore> schema_store,
1231       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1232                           feature_flags_.get(),
1233                           /*enable_schema_database=*/true,
1234                           /*initialize_stats=*/nullptr));
1235 
1236   SchemaProto db1_schema =
1237       SchemaBuilder()
1238           .AddType(
1239               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
1240           .AddType(SchemaTypeConfigBuilder()
1241                        .SetType("db1_message")
1242                        .SetDatabase("db1"))
1243           .Build();
1244   SchemaStore::SetSchemaResult result;
1245   result.success = true;
1246   result.schema_types_new_by_name.insert("db1_email");
1247   result.schema_types_new_by_name.insert("db1_message");
1248   EXPECT_THAT(schema_store->SetSchema(
1249                   db1_schema, /*ignore_errors_and_delete_documents=*/false,
1250                   /*allow_circular_schema_definitions=*/false),
1251               IsOkAndHolds(EqualsSetSchemaResult(result)));
1252   EXPECT_THAT(schema_store->GetSchema(),
1253               IsOkAndHolds(Pointee(EqualsProto(db1_schema))));
1254   EXPECT_THAT(schema_store->GetSchema("db1"),
1255               IsOkAndHolds(EqualsProto(db1_schema)));
1256 
1257   // Set an empty schema in a different database
1258   SchemaProto db2_schema;
1259   result = SchemaStore::SetSchemaResult();
1260   result.success = true;
1261   EXPECT_THAT(schema_store->SetSchema(
1262                   db2_schema, /*ignore_errors_and_delete_documents=*/false,
1263                   /*allow_circular_schema_definitions=*/false),
1264               IsOkAndHolds(EqualsSetSchemaResult(result)));
1265 
1266   // Check the schema, this should not have changed
1267   EXPECT_THAT(schema_store->GetSchema(),
1268               IsOkAndHolds(Pointee(EqualsProto(db1_schema))));
1269   EXPECT_THAT(schema_store->GetSchema("db1"),
1270               IsOkAndHolds(EqualsProto(db1_schema)));
1271 
1272   // GetSchema for an empty database should return NotFoundError
1273   EXPECT_THAT(schema_store->GetSchema("db2"),
1274               StatusIs(libtextclassifier3::StatusCode::NOT_FOUND));
1275 }
1276 
TEST_F(SchemaStoreTest,SetIncompatibleSchemaOk)1277 TEST_F(SchemaStoreTest, SetIncompatibleSchemaOk) {
1278   ICING_ASSERT_OK_AND_ASSIGN(
1279       std::unique_ptr<SchemaStore> schema_store,
1280       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1281                           feature_flags_.get()));
1282 
1283   // Set it for the first time
1284   SchemaStore::SetSchemaResult result;
1285   result.success = true;
1286   result.schema_types_new_by_name.insert(schema_.types(0).schema_type());
1287   EXPECT_THAT(schema_store->SetSchema(
1288                   schema_, /*ignore_errors_and_delete_documents=*/false,
1289                   /*allow_circular_schema_definitions=*/false),
1290               IsOkAndHolds(EqualsSetSchemaResult(result)));
1291   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
1292                              schema_store->GetSchema());
1293   EXPECT_THAT(*actual_schema, EqualsProto(schema_));
1294 
1295   // Make the schema incompatible by removing a type.
1296   schema_.clear_types();
1297 
1298   // Set the incompatible schema
1299   result = SchemaStore::SetSchemaResult();
1300   result.success = false;
1301   result.schema_types_deleted_by_name.emplace("email");
1302   result.schema_types_deleted_by_id.emplace(0);
1303   EXPECT_THAT(schema_store->SetSchema(
1304                   schema_, /*ignore_errors_and_delete_documents=*/false,
1305                   /*allow_circular_schema_definitions=*/false),
1306               IsOkAndHolds(EqualsSetSchemaResult(result)));
1307 }
1308 
TEST_F(SchemaStoreTest,SetIncompatibleInDifferentDatabaseOk)1309 TEST_F(SchemaStoreTest, SetIncompatibleInDifferentDatabaseOk) {
1310   ICING_ASSERT_OK_AND_ASSIGN(
1311       std::unique_ptr<SchemaStore> schema_store,
1312       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1313                           feature_flags_.get(),
1314                           /*enable_schema_database=*/true,
1315                           /*initialize_stats=*/nullptr));
1316 
1317   // Set schema for the first time
1318   SchemaProto db1_schema =
1319       SchemaBuilder()
1320           .AddType(
1321               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
1322           .AddType(SchemaTypeConfigBuilder()
1323                        .SetType("db1_message")
1324                        .SetDatabase("db1"))
1325           .Build();
1326   SchemaProto db2_schema =
1327       SchemaBuilder()
1328           .AddType(
1329               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
1330           .AddType(SchemaTypeConfigBuilder()
1331                        .SetType("db2_message")
1332                        .SetDatabase("db2"))
1333           .Build();
1334   SchemaProto expected_full_schema =
1335       SchemaBuilder()
1336           .AddType(
1337               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
1338           .AddType(SchemaTypeConfigBuilder()
1339                        .SetType("db1_message")
1340                        .SetDatabase("db1"))
1341           .AddType(
1342               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
1343           .AddType(SchemaTypeConfigBuilder()
1344                        .SetType("db2_message")
1345                        .SetDatabase("db2"))
1346           .Build();
1347   SchemaStore::SetSchemaResult result;
1348   result.success = true;
1349   result.schema_types_new_by_name.insert("db1_email");
1350   result.schema_types_new_by_name.insert("db1_message");
1351   EXPECT_THAT(schema_store->SetSchema(
1352                   db1_schema, /*ignore_errors_and_delete_documents=*/false,
1353                   /*allow_circular_schema_definitions=*/false),
1354               IsOkAndHolds(EqualsSetSchemaResult(result)));
1355   result = SchemaStore::SetSchemaResult();
1356   result.success = true;
1357   result.schema_types_new_by_name.insert("db2_email");
1358   result.schema_types_new_by_name.insert("db2_message");
1359   EXPECT_THAT(schema_store->SetSchema(
1360                   db2_schema, /*ignore_errors_and_delete_documents=*/false,
1361                   /*allow_circular_schema_definitions=*/false),
1362               IsOkAndHolds(EqualsSetSchemaResult(result)));
1363   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_full_schema,
1364                              schema_store->GetSchema());
1365   EXPECT_THAT(*actual_full_schema, EqualsProto(expected_full_schema));
1366 
1367   // Make db2 incompatible by changing a type name
1368   SchemaProto db2_schema_incompatible =
1369       SchemaBuilder()
1370           .AddType(
1371               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
1372           .AddType(SchemaTypeConfigBuilder()
1373                        .SetType("db2_recipient")
1374                        .SetDatabase("db2"))
1375           .Build();
1376   result = SchemaStore::SetSchemaResult();
1377   result.success = false;
1378   result.schema_types_deleted_by_name.insert("db2_message");
1379   result.schema_types_new_by_name.insert("db2_recipient");
1380   result.schema_types_deleted_by_id.insert(3);  // db2_message
1381   EXPECT_THAT(
1382       schema_store->SetSchema(db2_schema_incompatible,
1383                               /*ignore_errors_and_delete_documents=*/false,
1384                               /*allow_circular_schema_definitions=*/false),
1385       IsOkAndHolds(EqualsSetSchemaResult(result)));
1386 
1387   // Check the schema, this should not have changed
1388   EXPECT_THAT(schema_store->GetSchema(),
1389               IsOkAndHolds(Pointee(EqualsProto(expected_full_schema))));
1390   EXPECT_THAT(schema_store->GetSchema("db1"),
1391               IsOkAndHolds(EqualsProto(db1_schema)));
1392   EXPECT_THAT(schema_store->GetSchema("db2"),
1393               IsOkAndHolds(EqualsProto(db2_schema)));
1394 }
1395 
TEST_F(SchemaStoreTest,SetInvalidInDifferentDatabaseFails)1396 TEST_F(SchemaStoreTest, SetInvalidInDifferentDatabaseFails) {
1397   ICING_ASSERT_OK_AND_ASSIGN(
1398       std::unique_ptr<SchemaStore> schema_store,
1399       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1400                           feature_flags_.get(),
1401                           /*enable_schema_database=*/true,
1402                           /*initialize_stats=*/nullptr));
1403 
1404   // Set schema for the first time
1405   SchemaProto db1_schema =
1406       SchemaBuilder()
1407           .AddType(
1408               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
1409           .AddType(SchemaTypeConfigBuilder()
1410                        .SetType("db1_message")
1411                        .SetDatabase("db1"))
1412           .Build();
1413   SchemaProto db2_schema =
1414       SchemaBuilder()
1415           .AddType(
1416               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
1417           .AddType(SchemaTypeConfigBuilder()
1418                        .SetType("db2_message")
1419                        .SetDatabase("db2"))
1420           .Build();
1421   SchemaProto expected_full_schema =
1422       SchemaBuilder()
1423           .AddType(
1424               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
1425           .AddType(SchemaTypeConfigBuilder()
1426                        .SetType("db1_message")
1427                        .SetDatabase("db1"))
1428           .AddType(
1429               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
1430           .AddType(SchemaTypeConfigBuilder()
1431                        .SetType("db2_message")
1432                        .SetDatabase("db2"))
1433           .Build();
1434   SchemaStore::SetSchemaResult result;
1435   result.success = true;
1436   result.schema_types_new_by_name.insert("db1_email");
1437   result.schema_types_new_by_name.insert("db1_message");
1438   EXPECT_THAT(schema_store->SetSchema(
1439                   db1_schema, /*ignore_errors_and_delete_documents=*/false,
1440                   /*allow_circular_schema_definitions=*/false),
1441               IsOkAndHolds(EqualsSetSchemaResult(result)));
1442   result = SchemaStore::SetSchemaResult();
1443   result.success = true;
1444   result.schema_types_new_by_name.insert("db2_email");
1445   result.schema_types_new_by_name.insert("db2_message");
1446   EXPECT_THAT(schema_store->SetSchema(
1447                   db2_schema, /*ignore_errors_and_delete_documents=*/false,
1448                   /*allow_circular_schema_definitions=*/false),
1449               IsOkAndHolds(EqualsSetSchemaResult(result)));
1450   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_full_schema,
1451                              schema_store->GetSchema());
1452   EXPECT_THAT(*actual_full_schema, EqualsProto(expected_full_schema));
1453 
1454   // Make db2 invalid by duplicating a property name
1455   PropertyConfigProto prop =
1456       PropertyConfigBuilder()
1457           .SetName("prop0")
1458           .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
1459           .SetCardinality(CARDINALITY_OPTIONAL)
1460           .Build();
1461   SchemaProto db2_schema_incompatible = SchemaBuilder()
1462                                             .AddType(SchemaTypeConfigBuilder()
1463                                                          .SetType("db2_email")
1464                                                          .SetDatabase("db2")
1465                                                          .AddProperty(prop)
1466                                                          .AddProperty(prop))
1467                                             .Build();
1468   EXPECT_THAT(
1469       schema_store->SetSchema(db2_schema_incompatible,
1470                               /*ignore_errors_and_delete_documents=*/false,
1471                               /*allow_circular_schema_definitions=*/false),
1472       StatusIs(libtextclassifier3::StatusCode::ALREADY_EXISTS));
1473 
1474   // Check the schema, this should not have changed
1475   EXPECT_THAT(schema_store->GetSchema(),
1476               IsOkAndHolds(Pointee(EqualsProto(expected_full_schema))));
1477   EXPECT_THAT(schema_store->GetSchema("db1"),
1478               IsOkAndHolds(EqualsProto(db1_schema)));
1479   EXPECT_THAT(schema_store->GetSchema("db2"),
1480               IsOkAndHolds(EqualsProto(db2_schema)));
1481 }
1482 
TEST_F(SchemaStoreTest,SetSchemaWithMultipleDbFails)1483 TEST_F(SchemaStoreTest, SetSchemaWithMultipleDbFails) {
1484   ICING_ASSERT_OK_AND_ASSIGN(
1485       std::unique_ptr<SchemaStore> schema_store,
1486       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1487                           feature_flags_.get(),
1488                           /*enable_schema_database=*/true,
1489                           /*initialize_stats=*/nullptr));
1490 
1491   // Set schema for the first time
1492   SchemaProto combined_schema =
1493       SchemaBuilder()
1494           .AddType(
1495               SchemaTypeConfigBuilder().SetType("db2_email").SetDatabase("db2"))
1496           .AddType(SchemaTypeConfigBuilder()
1497                        .SetType("db2_message")
1498                        .SetDatabase("db2"))
1499           .AddType(
1500               SchemaTypeConfigBuilder().SetType("db1_email").SetDatabase("db1"))
1501           .AddType(SchemaTypeConfigBuilder()
1502                        .SetType("db1_message")
1503                        .SetDatabase("db1"))
1504           .Build();
1505   EXPECT_THAT(schema_store->SetSchema(
1506                   combined_schema, /*ignore_errors_and_delete_documents=*/false,
1507                   /*allow_circular_schema_definitions=*/false),
1508               StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT));
1509 }
1510 
TEST_F(SchemaStoreTest,SetSchemaWithDuplicateTypeNameAcrossDifferentDbFails)1511 TEST_F(SchemaStoreTest, SetSchemaWithDuplicateTypeNameAcrossDifferentDbFails) {
1512   ICING_ASSERT_OK_AND_ASSIGN(
1513       std::unique_ptr<SchemaStore> schema_store,
1514       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1515                           feature_flags_.get(),
1516                           /*enable_schema_database=*/true,
1517                           /*initialize_stats=*/nullptr));
1518 
1519   // Set schema for the first time
1520   SchemaProto db1_schema =
1521       SchemaBuilder()
1522           .AddType(
1523               SchemaTypeConfigBuilder().SetType("email").SetDatabase("db1"))
1524           .AddType(SchemaTypeConfigBuilder()
1525                        .SetType("db1_message")
1526                        .SetDatabase("db1"))
1527           .Build();
1528   SchemaStore::SetSchemaResult result;
1529   result.success = true;
1530   result.schema_types_new_by_name.insert("email");
1531   result.schema_types_new_by_name.insert("db1_message");
1532   EXPECT_THAT(schema_store->SetSchema(
1533                   db1_schema, /*ignore_errors_and_delete_documents=*/false,
1534                   /*allow_circular_schema_definitions=*/false),
1535               IsOkAndHolds(EqualsSetSchemaResult(result)));
1536   EXPECT_THAT(schema_store->GetSchema(),
1537               IsOkAndHolds(Pointee(EqualsProto(db1_schema))));
1538   EXPECT_THAT(schema_store->GetSchema("db1"),
1539               IsOkAndHolds(EqualsProto(db1_schema)));
1540 
1541   // Set schema in db2 with the same type name
1542   SchemaProto db2_schema =
1543       SchemaBuilder()
1544           .AddType(
1545               SchemaTypeConfigBuilder().SetType("email").SetDatabase("db2"))
1546           .AddType(SchemaTypeConfigBuilder()
1547                        .SetType("db2_message")
1548                        .SetDatabase("db2"))
1549           .Build();
1550   EXPECT_THAT(schema_store->SetSchema(
1551                   db2_schema, /*ignore_errors_and_delete_documents=*/false,
1552                   /*allow_circular_schema_definitions=*/false),
1553               StatusIs(libtextclassifier3::StatusCode::ALREADY_EXISTS));
1554 
1555   // Check schema, this should not have changed
1556   EXPECT_THAT(schema_store->GetSchema(),
1557               IsOkAndHolds(Pointee(EqualsProto(db1_schema))));
1558   EXPECT_THAT(schema_store->GetSchema("db1"),
1559               IsOkAndHolds(EqualsProto(db1_schema)));
1560 }
1561 
TEST_F(SchemaStoreTest,SetSchemaWithAddedTypeOk)1562 TEST_F(SchemaStoreTest, SetSchemaWithAddedTypeOk) {
1563   ICING_ASSERT_OK_AND_ASSIGN(
1564       std::unique_ptr<SchemaStore> schema_store,
1565       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1566                           feature_flags_.get()));
1567 
1568   SchemaProto schema = SchemaBuilder()
1569                            .AddType(SchemaTypeConfigBuilder().SetType("email"))
1570                            .Build();
1571 
1572   // Set it for the first time
1573   SchemaStore::SetSchemaResult result;
1574   result.success = true;
1575   result.schema_types_new_by_name.insert("email");
1576   EXPECT_THAT(schema_store->SetSchema(
1577                   schema, /*ignore_errors_and_delete_documents=*/false,
1578                   /*allow_circular_schema_definitions=*/false),
1579               IsOkAndHolds(EqualsSetSchemaResult(result)));
1580   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
1581                              schema_store->GetSchema());
1582   EXPECT_THAT(*actual_schema, EqualsProto(schema));
1583 
1584   // Add a type, shouldn't affect the index or cached SchemaTypeIds
1585   schema = SchemaBuilder(schema)
1586                .AddType(SchemaTypeConfigBuilder().SetType("new_type"))
1587                .Build();
1588 
1589   // Set the compatible schema
1590   result = SchemaStore::SetSchemaResult();
1591   result.success = true;
1592   result.schema_types_new_by_name.insert("new_type");
1593   EXPECT_THAT(schema_store->SetSchema(
1594                   schema, /*ignore_errors_and_delete_documents=*/false,
1595                   /*allow_circular_schema_definitions=*/false),
1596               IsOkAndHolds(EqualsSetSchemaResult(result)));
1597   ICING_ASSERT_OK_AND_ASSIGN(actual_schema, schema_store->GetSchema());
1598   EXPECT_THAT(*actual_schema, EqualsProto(schema));
1599 }
1600 
TEST_F(SchemaStoreTest,SetSchemaWithDeletedTypeOk)1601 TEST_F(SchemaStoreTest, SetSchemaWithDeletedTypeOk) {
1602   ICING_ASSERT_OK_AND_ASSIGN(
1603       std::unique_ptr<SchemaStore> schema_store,
1604       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1605                           feature_flags_.get()));
1606 
1607   SchemaProto schema =
1608       SchemaBuilder()
1609           .AddType(SchemaTypeConfigBuilder().SetType("email"))
1610           .AddType(SchemaTypeConfigBuilder().SetType("message"))
1611           .Build();
1612 
1613   // Set it for the first time
1614   SchemaStore::SetSchemaResult result;
1615   result.success = true;
1616   result.schema_types_new_by_name.insert("email");
1617   result.schema_types_new_by_name.insert("message");
1618   EXPECT_THAT(schema_store->SetSchema(
1619                   schema, /*ignore_errors_and_delete_documents=*/false,
1620                   /*allow_circular_schema_definitions=*/false),
1621               IsOkAndHolds(EqualsSetSchemaResult(result)));
1622   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
1623                              schema_store->GetSchema());
1624   EXPECT_THAT(*actual_schema, EqualsProto(schema));
1625 
1626   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId old_email_schema_type_id,
1627                              schema_store->GetSchemaTypeId("email"));
1628   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId old_message_schema_type_id,
1629                              schema_store->GetSchemaTypeId("message"));
1630 
1631   // Remove "email" type, this also changes previous SchemaTypeIds
1632   schema = SchemaBuilder()
1633                .AddType(SchemaTypeConfigBuilder().SetType("message"))
1634                .Build();
1635 
1636   SchemaStore::SetSchemaResult incompatible_result;
1637   incompatible_result.success = false;
1638   incompatible_result.old_schema_type_ids_changed.emplace(
1639       old_message_schema_type_id);
1640   incompatible_result.schema_types_deleted_by_name.emplace("email");
1641   incompatible_result.schema_types_deleted_by_id.emplace(
1642       old_email_schema_type_id);
1643 
1644   // Can't set the incompatible schema
1645   EXPECT_THAT(schema_store->SetSchema(
1646                   schema, /*ignore_errors_and_delete_documents=*/false,
1647                   /*allow_circular_schema_definitions=*/false),
1648               IsOkAndHolds(EqualsSetSchemaResult(incompatible_result)));
1649 
1650   SchemaStore::SetSchemaResult force_result;
1651   force_result.success = true;
1652   force_result.old_schema_type_ids_changed.emplace(old_message_schema_type_id);
1653   force_result.schema_types_deleted_by_name.emplace("email");
1654   force_result.schema_types_deleted_by_id.emplace(old_email_schema_type_id);
1655 
1656   // Force set the incompatible schema
1657   EXPECT_THAT(schema_store->SetSchema(
1658                   schema, /*ignore_errors_and_delete_documents=*/true,
1659                   /*allow_circular_schema_definitions=*/false),
1660               IsOkAndHolds(EqualsSetSchemaResult(force_result)));
1661   ICING_ASSERT_OK_AND_ASSIGN(actual_schema, schema_store->GetSchema());
1662   EXPECT_THAT(*actual_schema, EqualsProto(schema));
1663 }
1664 
TEST_F(SchemaStoreTest,SetSchemaWithReorderedTypesOk)1665 TEST_F(SchemaStoreTest, SetSchemaWithReorderedTypesOk) {
1666   ICING_ASSERT_OK_AND_ASSIGN(
1667       std::unique_ptr<SchemaStore> schema_store,
1668       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1669                           feature_flags_.get()));
1670 
1671   SchemaProto schema =
1672       SchemaBuilder()
1673           .AddType(SchemaTypeConfigBuilder().SetType("email"))
1674           .AddType(SchemaTypeConfigBuilder().SetType("message"))
1675           .Build();
1676 
1677   // Set it for the first time
1678   SchemaStore::SetSchemaResult result;
1679   result.success = true;
1680   result.schema_types_new_by_name.insert("email");
1681   result.schema_types_new_by_name.insert("message");
1682   EXPECT_THAT(schema_store->SetSchema(
1683                   schema, /*ignore_errors_and_delete_documents=*/false,
1684                   /*allow_circular_schema_definitions=*/false),
1685               IsOkAndHolds(EqualsSetSchemaResult(result)));
1686   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
1687                              schema_store->GetSchema());
1688   EXPECT_THAT(*actual_schema, EqualsProto(schema));
1689 
1690   // Reorder the types
1691   schema = SchemaBuilder()
1692                .AddType(SchemaTypeConfigBuilder().SetType("message"))
1693                .AddType(SchemaTypeConfigBuilder().SetType("email"))
1694                .Build();
1695 
1696   // Since we assign SchemaTypeIds based on order in the SchemaProto, this will
1697   // cause SchemaTypeIds to change
1698   result = SchemaStore::SetSchemaResult();
1699   result.success = true;
1700   result.old_schema_type_ids_changed.emplace(0);  // Old SchemaTypeId of "email"
1701   result.old_schema_type_ids_changed.emplace(
1702       1);  // Old SchemaTypeId of "message"
1703 
1704   // Set the compatible schema
1705   EXPECT_THAT(schema_store->SetSchema(
1706                   schema, /*ignore_errors_and_delete_documents=*/false,
1707                   /*allow_circular_schema_definitions=*/false),
1708               IsOkAndHolds(EqualsSetSchemaResult(result)));
1709   ICING_ASSERT_OK_AND_ASSIGN(actual_schema, schema_store->GetSchema());
1710   EXPECT_THAT(*actual_schema, EqualsProto(schema));
1711 }
1712 
TEST_F(SchemaStoreTest,IndexedPropertyChangeRequiresReindexingOk)1713 TEST_F(SchemaStoreTest, IndexedPropertyChangeRequiresReindexingOk) {
1714   ICING_ASSERT_OK_AND_ASSIGN(
1715       std::unique_ptr<SchemaStore> schema_store,
1716       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1717                           feature_flags_.get()));
1718 
1719   SchemaProto schema =
1720       SchemaBuilder()
1721           .AddType(SchemaTypeConfigBuilder().SetType("email").AddProperty(
1722               // Add an unindexed property
1723               PropertyConfigBuilder()
1724                   .SetName("subject")
1725                   .SetDataType(TYPE_STRING)
1726                   .SetCardinality(CARDINALITY_OPTIONAL)))
1727           .Build();
1728 
1729   // Set it for the first time
1730   SchemaStore::SetSchemaResult result;
1731   result.success = true;
1732   result.schema_types_new_by_name.insert("email");
1733   EXPECT_THAT(schema_store->SetSchema(
1734                   schema, /*ignore_errors_and_delete_documents=*/false,
1735                   /*allow_circular_schema_definitions=*/false),
1736               IsOkAndHolds(EqualsSetSchemaResult(result)));
1737   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
1738                              schema_store->GetSchema());
1739   EXPECT_THAT(*actual_schema, EqualsProto(schema));
1740 
1741   // Make a previously unindexed property indexed
1742   schema = SchemaBuilder()
1743                .AddType(SchemaTypeConfigBuilder().SetType("email").AddProperty(
1744                    PropertyConfigBuilder()
1745                        .SetName("subject")
1746                        .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
1747                        .SetCardinality(CARDINALITY_OPTIONAL)))
1748                .Build();
1749 
1750   // Set the compatible schema
1751   result = SchemaStore::SetSchemaResult();
1752   result.success = true;
1753   result.schema_types_index_incompatible_by_name.insert("email");
1754   EXPECT_THAT(schema_store->SetSchema(
1755                   schema, /*ignore_errors_and_delete_documents=*/false,
1756                   /*allow_circular_schema_definitions=*/false),
1757               IsOkAndHolds(EqualsSetSchemaResult(result)));
1758   ICING_ASSERT_OK_AND_ASSIGN(actual_schema, schema_store->GetSchema());
1759   EXPECT_THAT(*actual_schema, EqualsProto(schema));
1760 }
1761 
TEST_F(SchemaStoreTest,IndexNestedDocumentsChangeRequiresReindexingOk)1762 TEST_F(SchemaStoreTest, IndexNestedDocumentsChangeRequiresReindexingOk) {
1763   ICING_ASSERT_OK_AND_ASSIGN(
1764       std::unique_ptr<SchemaStore> schema_store,
1765       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1766                           feature_flags_.get()));
1767 
1768   // Make two schemas. One that sets index_nested_properties to false and one
1769   // that sets it to true.
1770   SchemaTypeConfigProto email_type_config =
1771       SchemaTypeConfigBuilder()
1772           .SetType("email")
1773           .AddProperty(PropertyConfigBuilder()
1774                            .SetName("subject")
1775                            .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
1776                            .SetCardinality(CARDINALITY_OPTIONAL))
1777           .Build();
1778   SchemaProto no_nested_index_schema =
1779       SchemaBuilder()
1780           .AddType(email_type_config)
1781           .AddType(SchemaTypeConfigBuilder().SetType("person").AddProperty(
1782               PropertyConfigBuilder()
1783                   .SetName("emails")
1784                   .SetDataTypeDocument("email",
1785                                        /*index_nested_properties=*/false)
1786                   .SetCardinality(CARDINALITY_REPEATED)))
1787           .Build();
1788 
1789   SchemaProto nested_index_schema =
1790       SchemaBuilder()
1791           .AddType(email_type_config)
1792           .AddType(SchemaTypeConfigBuilder().SetType("person").AddProperty(
1793               PropertyConfigBuilder()
1794                   .SetName("emails")
1795                   .SetDataTypeDocument("email",
1796                                        /*index_nested_properties=*/true)
1797                   .SetCardinality(CARDINALITY_REPEATED)))
1798           .Build();
1799 
1800   // Set schema with index_nested_properties=false to start.
1801   SchemaStore::SetSchemaResult result;
1802   result.success = true;
1803   result.schema_types_new_by_name.insert("email");
1804   result.schema_types_new_by_name.insert("person");
1805   EXPECT_THAT(
1806       schema_store->SetSchema(no_nested_index_schema,
1807                               /*ignore_errors_and_delete_documents=*/false,
1808                               /*allow_circular_schema_definitions=*/false),
1809       IsOkAndHolds(EqualsSetSchemaResult(result)));
1810   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
1811                              schema_store->GetSchema());
1812   EXPECT_THAT(*actual_schema, EqualsProto(no_nested_index_schema));
1813 
1814   // Set schema with index_nested_properties=true and confirm that the change to
1815   // 'person' is index incompatible.
1816   result = SchemaStore::SetSchemaResult();
1817   result.success = true;
1818   result.schema_types_index_incompatible_by_name.insert("person");
1819   EXPECT_THAT(
1820       schema_store->SetSchema(nested_index_schema,
1821                               /*ignore_errors_and_delete_documents=*/false,
1822                               /*allow_circular_schema_definitions=*/false),
1823       IsOkAndHolds(EqualsSetSchemaResult(result)));
1824   ICING_ASSERT_OK_AND_ASSIGN(actual_schema, schema_store->GetSchema());
1825   EXPECT_THAT(*actual_schema, EqualsProto(nested_index_schema));
1826 
1827   // Set schema with index_nested_properties=false and confirm that the change
1828   // to 'person' is index incompatible.
1829   result = SchemaStore::SetSchemaResult();
1830   result.success = true;
1831   result.schema_types_index_incompatible_by_name.insert("person");
1832   EXPECT_THAT(
1833       schema_store->SetSchema(no_nested_index_schema,
1834                               /*ignore_errors_and_delete_documents=*/false,
1835                               /*allow_circular_schema_definitions=*/false),
1836       IsOkAndHolds(EqualsSetSchemaResult(result)));
1837   ICING_ASSERT_OK_AND_ASSIGN(actual_schema, schema_store->GetSchema());
1838   EXPECT_THAT(*actual_schema, EqualsProto(no_nested_index_schema));
1839 }
1840 
TEST_F(SchemaStoreTest,SetSchemaWithIncompatibleTypesOk)1841 TEST_F(SchemaStoreTest, SetSchemaWithIncompatibleTypesOk) {
1842   ICING_ASSERT_OK_AND_ASSIGN(
1843       std::unique_ptr<SchemaStore> schema_store,
1844       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1845                           feature_flags_.get()));
1846 
1847   SchemaProto schema =
1848       SchemaBuilder()
1849           .AddType(SchemaTypeConfigBuilder().SetType("email").AddProperty(
1850               // Add a STRING property
1851               PropertyConfigBuilder()
1852                   .SetName("subject")
1853                   .SetDataType(TYPE_STRING)
1854                   .SetCardinality(CARDINALITY_OPTIONAL)))
1855           .Build();
1856 
1857   // Set it for the first time
1858   SchemaStore::SetSchemaResult result;
1859   result.success = true;
1860   result.schema_types_new_by_name.insert("email");
1861   EXPECT_THAT(schema_store->SetSchema(
1862                   schema, /*ignore_errors_and_delete_documents=*/false,
1863                   /*allow_circular_schema_definitions=*/false),
1864               IsOkAndHolds(EqualsSetSchemaResult(result)));
1865   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
1866                              schema_store->GetSchema());
1867   EXPECT_THAT(*actual_schema, EqualsProto(schema));
1868 
1869   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId old_email_schema_type_id,
1870                              schema_store->GetSchemaTypeId("email"));
1871 
1872   // Make a previously STRING property into DOUBLE
1873   schema = SchemaBuilder()
1874                .AddType(SchemaTypeConfigBuilder().SetType("email").AddProperty(
1875                    // Add a STRING property
1876                    PropertyConfigBuilder()
1877                        .SetName("subject")
1878                        .SetDataType(TYPE_DOUBLE)
1879                        .SetCardinality(CARDINALITY_OPTIONAL)))
1880                .Build();
1881 
1882   SchemaStore::SetSchemaResult incompatible_result;
1883   incompatible_result.success = false;
1884   incompatible_result.schema_types_incompatible_by_name.emplace("email");
1885   incompatible_result.schema_types_incompatible_by_id.emplace(
1886       old_email_schema_type_id);
1887 
1888   // Can't set the incompatible schema
1889   EXPECT_THAT(schema_store->SetSchema(
1890                   schema, /*ignore_errors_and_delete_documents=*/false,
1891                   /*allow_circular_schema_definitions=*/false),
1892               IsOkAndHolds(EqualsSetSchemaResult(incompatible_result)));
1893 
1894   SchemaStore::SetSchemaResult force_result;
1895   force_result.success = true;
1896   force_result.schema_types_incompatible_by_name.emplace("email");
1897   force_result.schema_types_incompatible_by_id.emplace(
1898       old_email_schema_type_id);
1899 
1900   // Force set the incompatible schema
1901   EXPECT_THAT(schema_store->SetSchema(
1902                   schema, /*ignore_errors_and_delete_documents=*/true,
1903                   /*allow_circular_schema_definitions=*/false),
1904               IsOkAndHolds(EqualsSetSchemaResult(force_result)));
1905   ICING_ASSERT_OK_AND_ASSIGN(actual_schema, schema_store->GetSchema());
1906   EXPECT_THAT(*actual_schema, EqualsProto(schema));
1907 }
1908 
TEST_F(SchemaStoreTest,SetSchemaWithIncompatibleNestedTypesOk)1909 TEST_F(SchemaStoreTest, SetSchemaWithIncompatibleNestedTypesOk) {
1910   ICING_ASSERT_OK_AND_ASSIGN(
1911       std::unique_ptr<SchemaStore> schema_store,
1912       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1913                           feature_flags_.get()));
1914 
1915   // 1. Create a ContactPoint type with a repeated property and set that schema
1916   SchemaTypeConfigBuilder contact_point_repeated_label =
1917       SchemaTypeConfigBuilder()
1918           .SetType("ContactPoint")
1919           .AddProperty(
1920               PropertyConfigBuilder()
1921                   .SetName("label")
1922                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
1923                   .SetCardinality(CARDINALITY_REPEATED));
1924   SchemaProto old_schema =
1925       SchemaBuilder().AddType(contact_point_repeated_label).Build();
1926   ICING_EXPECT_OK(schema_store->SetSchema(
1927       old_schema, /*ignore_errors_and_delete_documents=*/false,
1928       /*allow_circular_schema_definitions=*/false));
1929   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId old_contact_point_type_id,
1930                              schema_store->GetSchemaTypeId("ContactPoint"));
1931 
1932   // 2. Create a type that references the ContactPoint type and make a backwards
1933   // incompatible change to ContactPoint
1934   SchemaTypeConfigBuilder contact_point_optional_label =
1935       SchemaTypeConfigBuilder()
1936           .SetType("ContactPoint")
1937           .AddProperty(
1938               PropertyConfigBuilder()
1939                   .SetName("label")
1940                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
1941                   .SetCardinality(CARDINALITY_OPTIONAL));
1942   SchemaTypeConfigBuilder person =
1943       SchemaTypeConfigBuilder().SetType("Person").AddProperty(
1944           PropertyConfigBuilder()
1945               .SetName("contactPoints")
1946               .SetDataTypeDocument("ContactPoint",
1947                                    /*index_nested_properties=*/true)
1948               .SetCardinality(CARDINALITY_REPEATED));
1949   SchemaProto new_schema = SchemaBuilder()
1950                                .AddType(contact_point_optional_label)
1951                                .AddType(person)
1952                                .Build();
1953 
1954   // 3. SetSchema should fail with ignore_errors_and_delete_documents=false and
1955   // the old schema should remain
1956   SchemaStore::SetSchemaResult expected_result;
1957   expected_result.success = false;
1958   expected_result.schema_types_incompatible_by_name.insert("ContactPoint");
1959   expected_result.schema_types_incompatible_by_id.insert(
1960       old_contact_point_type_id);
1961   expected_result.schema_types_new_by_name.insert("Person");
1962   EXPECT_THAT(
1963       schema_store->SetSchema(new_schema,
1964                               /*ignore_errors_and_delete_documents=*/false,
1965                               /*allow_circular_schema_definitions=*/false),
1966       IsOkAndHolds(EqualsSetSchemaResult(expected_result)));
1967   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
1968                              schema_store->GetSchema());
1969   EXPECT_THAT(*actual_schema, EqualsProto(old_schema));
1970 
1971   // 4. SetSchema should succeed with ignore_errors_and_delete_documents=true
1972   // and the new schema should be set
1973   expected_result.success = true;
1974   EXPECT_THAT(
1975       schema_store->SetSchema(new_schema,
1976                               /*ignore_errors_and_delete_documents=*/true,
1977                               /*allow_circular_schema_definitions=*/false),
1978       IsOkAndHolds(EqualsSetSchemaResult(expected_result)));
1979   ICING_ASSERT_OK_AND_ASSIGN(actual_schema, schema_store->GetSchema());
1980   EXPECT_THAT(*actual_schema, EqualsProto(new_schema));
1981 }
1982 
TEST_F(SchemaStoreTest,SetSchemaWithIndexIncompatibleNestedTypesOk)1983 TEST_F(SchemaStoreTest, SetSchemaWithIndexIncompatibleNestedTypesOk) {
1984   ICING_ASSERT_OK_AND_ASSIGN(
1985       std::unique_ptr<SchemaStore> schema_store,
1986       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
1987                           feature_flags_.get()));
1988 
1989   // 1. Create a ContactPoint type with label that matches prefix and set that
1990   // schema
1991   SchemaTypeConfigBuilder contact_point_prefix_label =
1992       SchemaTypeConfigBuilder()
1993           .SetType("ContactPoint")
1994           .AddProperty(
1995               PropertyConfigBuilder()
1996                   .SetName("label")
1997                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
1998                   .SetCardinality(CARDINALITY_REPEATED));
1999   SchemaProto old_schema =
2000       SchemaBuilder().AddType(contact_point_prefix_label).Build();
2001   ICING_EXPECT_OK(schema_store->SetSchema(
2002       old_schema, /*ignore_errors_and_delete_documents=*/false,
2003       /*allow_circular_schema_definitions=*/false));
2004 
2005   // 2. Create a type that references the ContactPoint type and make a index
2006   // backwards incompatible change to ContactPoint
2007   SchemaTypeConfigBuilder contact_point_exact_label =
2008       SchemaTypeConfigBuilder()
2009           .SetType("ContactPoint")
2010           .AddProperty(PropertyConfigBuilder()
2011                            .SetName("label")
2012                            .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
2013                            .SetCardinality(CARDINALITY_REPEATED));
2014   SchemaTypeConfigBuilder person =
2015       SchemaTypeConfigBuilder().SetType("Person").AddProperty(
2016           PropertyConfigBuilder()
2017               .SetName("contactPoints")
2018               .SetDataTypeDocument("ContactPoint",
2019                                    /*index_nested_properties=*/true)
2020               .SetCardinality(CARDINALITY_REPEATED));
2021   SchemaProto new_schema = SchemaBuilder()
2022                                .AddType(contact_point_exact_label)
2023                                .AddType(person)
2024                                .Build();
2025 
2026   // SetSchema should succeed, and only ContactPoint should be in
2027   // schema_types_index_incompatible_by_name.
2028   SchemaStore::SetSchemaResult expected_result;
2029   expected_result.success = true;
2030   expected_result.schema_types_index_incompatible_by_name.insert(
2031       "ContactPoint");
2032   expected_result.schema_types_new_by_name.insert("Person");
2033   EXPECT_THAT(
2034       schema_store->SetSchema(new_schema,
2035                               /*ignore_errors_and_delete_documents=*/false,
2036                               /*allow_circular_schema_definitions=*/false),
2037       IsOkAndHolds(EqualsSetSchemaResult(expected_result)));
2038   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
2039                              schema_store->GetSchema());
2040   EXPECT_THAT(*actual_schema, EqualsProto(new_schema));
2041 }
2042 
TEST_F(SchemaStoreTest,SetSchemaWithCompatibleNestedTypesOk)2043 TEST_F(SchemaStoreTest, SetSchemaWithCompatibleNestedTypesOk) {
2044   ICING_ASSERT_OK_AND_ASSIGN(
2045       std::unique_ptr<SchemaStore> schema_store,
2046       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2047                           feature_flags_.get()));
2048 
2049   // 1. Create a ContactPoint type with a optional property and set that schema
2050   SchemaTypeConfigBuilder contact_point_optional_label =
2051       SchemaTypeConfigBuilder()
2052           .SetType("ContactPoint")
2053           .AddProperty(
2054               PropertyConfigBuilder()
2055                   .SetName("label")
2056                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
2057                   .SetCardinality(CARDINALITY_OPTIONAL));
2058   SchemaProto old_schema =
2059       SchemaBuilder().AddType(contact_point_optional_label).Build();
2060   ICING_EXPECT_OK(schema_store->SetSchema(
2061       old_schema, /*ignore_errors_and_delete_documents=*/false,
2062       /*allow_circular_schema_definitions=*/false));
2063 
2064   // 2. Create a type that references the ContactPoint type and make a backwards
2065   // compatible change to ContactPoint
2066   SchemaTypeConfigBuilder contact_point_repeated_label =
2067       SchemaTypeConfigBuilder()
2068           .SetType("ContactPoint")
2069           .AddProperty(
2070               PropertyConfigBuilder()
2071                   .SetName("label")
2072                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
2073                   .SetCardinality(CARDINALITY_REPEATED));
2074   SchemaTypeConfigBuilder person =
2075       SchemaTypeConfigBuilder().SetType("Person").AddProperty(
2076           PropertyConfigBuilder()
2077               .SetName("contactPoints")
2078               .SetDataTypeDocument("ContactPoint",
2079                                    /*index_nested_properties=*/true)
2080               .SetCardinality(CARDINALITY_REPEATED));
2081   SchemaProto new_schema = SchemaBuilder()
2082                                .AddType(contact_point_repeated_label)
2083                                .AddType(person)
2084                                .Build();
2085 
2086   // 3. SetSchema should succeed, and only ContactPoint should be in
2087   // schema_types_changed_fully_compatible_by_name.
2088   SchemaStore::SetSchemaResult expected_result;
2089   expected_result.success = true;
2090   expected_result.schema_types_changed_fully_compatible_by_name.insert(
2091       "ContactPoint");
2092   expected_result.schema_types_new_by_name.insert("Person");
2093   EXPECT_THAT(schema_store->SetSchema(
2094                   new_schema, /*ignore_errors_and_delete_documents=*/false,
2095                   /*allow_circular_schema_definitions=*/false),
2096               IsOkAndHolds(EqualsSetSchemaResult(expected_result)));
2097   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
2098                              schema_store->GetSchema());
2099   EXPECT_THAT(*actual_schema, EqualsProto(new_schema));
2100 }
2101 
TEST_F(SchemaStoreTest,SetSchemaWithAddedIndexableNestedTypeOk)2102 TEST_F(SchemaStoreTest, SetSchemaWithAddedIndexableNestedTypeOk) {
2103   ICING_ASSERT_OK_AND_ASSIGN(
2104       std::unique_ptr<SchemaStore> schema_store,
2105       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2106                           feature_flags_.get()));
2107 
2108   // 1. Create a ContactPoint type with a optional property, and a type that
2109   //    references the ContactPoint type.
2110   SchemaTypeConfigBuilder contact_point =
2111       SchemaTypeConfigBuilder()
2112           .SetType("ContactPoint")
2113           .AddProperty(
2114               PropertyConfigBuilder()
2115                   .SetName("label")
2116                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
2117                   .SetCardinality(CARDINALITY_REPEATED));
2118   SchemaTypeConfigBuilder person =
2119       SchemaTypeConfigBuilder().SetType("Person").AddProperty(
2120           PropertyConfigBuilder()
2121               .SetName("contactPoints")
2122               .SetDataTypeDocument("ContactPoint",
2123                                    /*index_nested_properties=*/true)
2124               .SetCardinality(CARDINALITY_REPEATED));
2125   SchemaProto old_schema =
2126       SchemaBuilder().AddType(contact_point).AddType(person).Build();
2127   ICING_EXPECT_OK(schema_store->SetSchema(
2128       old_schema, /*ignore_errors_and_delete_documents=*/false,
2129       /*allow_circular_schema_definitions=*/false));
2130 
2131   // 2. Add another nested document property to "Person" that has type
2132   //    "ContactPoint"
2133   SchemaTypeConfigBuilder new_person =
2134       SchemaTypeConfigBuilder()
2135           .SetType("Person")
2136           .AddProperty(
2137               PropertyConfigBuilder()
2138                   .SetName("contactPoints")
2139                   .SetDataTypeDocument("ContactPoint",
2140                                        /*index_nested_properties=*/true)
2141                   .SetCardinality(CARDINALITY_REPEATED))
2142           .AddProperty(
2143               PropertyConfigBuilder()
2144                   .SetName("anotherContactPoint")
2145                   .SetDataTypeDocument("ContactPoint",
2146                                        /*index_nested_properties=*/true)
2147                   .SetCardinality(CARDINALITY_REPEATED));
2148   SchemaProto new_schema =
2149       SchemaBuilder().AddType(contact_point).AddType(new_person).Build();
2150 
2151   // 3. Set to new schema. "Person" should be index-incompatible since we need
2152   //    to index an additional property: 'anotherContactPoint.label'.
2153   // - "Person" is also considered join-incompatible since the added nested
2154   //   document property could also contain a joinable property.
2155   SchemaStore::SetSchemaResult expected_result;
2156   expected_result.success = true;
2157   expected_result.schema_types_index_incompatible_by_name.insert("Person");
2158   expected_result.schema_types_join_incompatible_by_name.insert("Person");
2159 
2160   EXPECT_THAT(schema_store->SetSchema(
2161                   new_schema, /*ignore_errors_and_delete_documents=*/false,
2162                   /*allow_circular_schema_definitions=*/false),
2163               IsOkAndHolds(EqualsSetSchemaResult(expected_result)));
2164   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
2165                              schema_store->GetSchema());
2166   EXPECT_THAT(*actual_schema, EqualsProto(new_schema));
2167 }
2168 
TEST_F(SchemaStoreTest,SetSchemaWithAddedJoinableNestedTypeOk)2169 TEST_F(SchemaStoreTest, SetSchemaWithAddedJoinableNestedTypeOk) {
2170   ICING_ASSERT_OK_AND_ASSIGN(
2171       std::unique_ptr<SchemaStore> schema_store,
2172       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2173                           feature_flags_.get()));
2174 
2175   // 1. Create a ContactPoint type with a optional property, and a type that
2176   //    references the ContactPoint type.
2177   SchemaTypeConfigBuilder contact_point =
2178       SchemaTypeConfigBuilder()
2179           .SetType("ContactPoint")
2180           .AddProperty(
2181               PropertyConfigBuilder()
2182                   .SetName("label")
2183                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
2184                   .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID,
2185                                DELETE_PROPAGATION_TYPE_NONE)
2186                   .SetCardinality(CARDINALITY_REQUIRED));
2187   SchemaTypeConfigBuilder person =
2188       SchemaTypeConfigBuilder().SetType("Person").AddProperty(
2189           PropertyConfigBuilder()
2190               .SetName("contactPoints")
2191               .SetDataTypeDocument("ContactPoint",
2192                                    /*index_nested_properties=*/true)
2193               .SetCardinality(CARDINALITY_OPTIONAL));
2194   SchemaProto old_schema =
2195       SchemaBuilder().AddType(contact_point).AddType(person).Build();
2196   ICING_EXPECT_OK(schema_store->SetSchema(
2197       old_schema, /*ignore_errors_and_delete_documents=*/false,
2198       /*allow_circular_schema_definitions=*/false));
2199 
2200   // 2. Add another nested document property to "Person" that has type
2201   //    "ContactPoint", but make it non-indexable
2202   SchemaTypeConfigBuilder new_person =
2203       SchemaTypeConfigBuilder()
2204           .SetType("Person")
2205           .AddProperty(
2206               PropertyConfigBuilder()
2207                   .SetName("contactPoints")
2208                   .SetDataTypeDocument("ContactPoint",
2209                                        /*index_nested_properties=*/true)
2210                   .SetCardinality(CARDINALITY_OPTIONAL))
2211           .AddProperty(
2212               PropertyConfigBuilder()
2213                   .SetName("anotherContactPoint")
2214                   .SetDataTypeDocument("ContactPoint",
2215                                        /*index_nested_properties=*/false)
2216                   .SetCardinality(CARDINALITY_OPTIONAL));
2217   SchemaProto new_schema =
2218       SchemaBuilder().AddType(contact_point).AddType(new_person).Build();
2219 
2220   // 3. Set to new schema. "Person" should be join-incompatible but
2221   //    index-compatible.
2222   SchemaStore::SetSchemaResult expected_result;
2223   expected_result.success = true;
2224   expected_result.schema_types_join_incompatible_by_name.insert("Person");
2225 
2226   EXPECT_THAT(schema_store->SetSchema(
2227                   new_schema, /*ignore_errors_and_delete_documents=*/false,
2228                   /*allow_circular_schema_definitions=*/false),
2229               IsOkAndHolds(EqualsSetSchemaResult(expected_result)));
2230   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
2231                              schema_store->GetSchema());
2232   EXPECT_THAT(*actual_schema, EqualsProto(new_schema));
2233 }
2234 
TEST_F(SchemaStoreTest,SetSchemaByUpdatingScorablePropertyOk)2235 TEST_F(SchemaStoreTest, SetSchemaByUpdatingScorablePropertyOk) {
2236   ICING_ASSERT_OK_AND_ASSIGN(
2237       std::unique_ptr<SchemaStore> schema_store,
2238       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2239                           feature_flags_.get()));
2240 
2241   SchemaProto old_schema =
2242       SchemaBuilder()
2243           .AddType(SchemaTypeConfigBuilder().SetType("email").AddProperty(
2244               PropertyConfigBuilder()
2245                   .SetName("title")
2246                   .SetDataType(TYPE_STRING)
2247                   .SetCardinality(CARDINALITY_REQUIRED)))
2248           .Build();
2249   SchemaProto new_schema =
2250       SchemaBuilder()
2251           .AddType(SchemaTypeConfigBuilder()
2252                        .SetType("email")
2253                        .AddProperty(PropertyConfigBuilder()
2254                                         .SetName("title")
2255                                         .SetDataType(TYPE_STRING)
2256                                         .SetCardinality(CARDINALITY_REQUIRED))
2257                        .AddProperty(PropertyConfigBuilder()
2258                                         .SetName("score")
2259                                         .SetDataType(TYPE_DOUBLE)
2260                                         .SetScorableType(SCORABLE_TYPE_ENABLED)
2261                                         .SetCardinality(CARDINALITY_OPTIONAL)))
2262           .Build();
2263 
2264   // Set old schema
2265   SchemaStore::SetSchemaResult expected_result;
2266   expected_result.success = true;
2267   expected_result.schema_types_new_by_name.insert("email");
2268   EXPECT_THAT(schema_store->SetSchema(
2269                   old_schema, /*ignore_errors_and_delete_documents=*/false,
2270                   /*allow_circular_schema_definitions=*/false),
2271               IsOkAndHolds(EqualsSetSchemaResult(expected_result)));
2272   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
2273                              schema_store->GetSchema());
2274   EXPECT_THAT(*actual_schema, EqualsProto(old_schema));
2275 
2276   // Set new schema.
2277   // The new schema adds "score" as scorable_type ENABLED from type "email".
2278   SchemaStore::SetSchemaResult new_expected_result;
2279   new_expected_result.success = true;
2280   new_expected_result.schema_types_scorable_property_inconsistent_by_id.insert(
2281       0);
2282   new_expected_result.schema_types_changed_fully_compatible_by_name.insert(
2283       "email");
2284   EXPECT_THAT(schema_store->SetSchema(
2285                   new_schema, /*ignore_errors_and_delete_documents=*/false,
2286                   /*allow_circular_schema_definitions=*/false),
2287               IsOkAndHolds(EqualsSetSchemaResult(new_expected_result)));
2288   ICING_ASSERT_OK_AND_ASSIGN(actual_schema, schema_store->GetSchema());
2289   EXPECT_THAT(*actual_schema, EqualsProto(new_schema));
2290 }
2291 
TEST_F(SchemaStoreTest,SetSchemaWithReorderedSchemeTypesAndUpdatedScorablePropertyOk)2292 TEST_F(SchemaStoreTest,
2293        SetSchemaWithReorderedSchemeTypesAndUpdatedScorablePropertyOk) {
2294   ICING_ASSERT_OK_AND_ASSIGN(
2295       std::unique_ptr<SchemaStore> schema_store,
2296       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2297                           feature_flags_.get()));
2298 
2299   SchemaProto old_schema =
2300       SchemaBuilder()
2301           .AddType(SchemaTypeConfigBuilder().SetType("message"))
2302           .AddType(SchemaTypeConfigBuilder()
2303                        .SetType("email")
2304                        .AddProperty(PropertyConfigBuilder()
2305                                         .SetName("title")
2306                                         .SetDataType(TYPE_STRING)
2307                                         .SetCardinality(CARDINALITY_REQUIRED))
2308                        .AddProperty(PropertyConfigBuilder()
2309                                         .SetName("score")
2310                                         .SetDataType(TYPE_DOUBLE)
2311                                         .SetScorableType(SCORABLE_TYPE_DISABLED)
2312                                         .SetCardinality(CARDINALITY_OPTIONAL)))
2313           .Build();
2314   // The new schema updates "score" as scorable_type ENABLED from type "email",
2315   // and it also reorders the schema types of "email" and "message".
2316   SchemaProto new_schema =
2317       SchemaBuilder()
2318           .AddType(SchemaTypeConfigBuilder()
2319                        .SetType("email")
2320                        .AddProperty(PropertyConfigBuilder()
2321                                         .SetName("title")
2322                                         .SetDataType(TYPE_STRING)
2323                                         .SetCardinality(CARDINALITY_REQUIRED))
2324                        .AddProperty(PropertyConfigBuilder()
2325                                         .SetName("score")
2326                                         .SetDataType(TYPE_DOUBLE)
2327                                         .SetScorableType(SCORABLE_TYPE_ENABLED)
2328                                         .SetCardinality(CARDINALITY_OPTIONAL)))
2329           .AddType(SchemaTypeConfigBuilder().SetType("message"))
2330           .Build();
2331 
2332   // Set old schema
2333   SchemaStore::SetSchemaResult expected_result;
2334   expected_result.success = true;
2335   expected_result.schema_types_new_by_name.insert("email");
2336   expected_result.schema_types_new_by_name.insert("message");
2337   EXPECT_THAT(schema_store->SetSchema(
2338                   old_schema, /*ignore_errors_and_delete_documents=*/false,
2339                   /*allow_circular_schema_definitions=*/false),
2340               IsOkAndHolds(EqualsSetSchemaResult(expected_result)));
2341   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
2342                              schema_store->GetSchema());
2343   EXPECT_THAT(*actual_schema, EqualsProto(old_schema));
2344 
2345   // Set new schema.
2346   SchemaStore::SetSchemaResult new_expected_result;
2347   new_expected_result.success = true;
2348   // Schema type id of "email" is updated to 0.
2349   SchemaTypeId email_schema_type_id = 0;
2350   new_expected_result.schema_types_scorable_property_inconsistent_by_id.insert(
2351       email_schema_type_id);
2352   new_expected_result.schema_types_changed_fully_compatible_by_name.insert(
2353       "email");
2354   new_expected_result.old_schema_type_ids_changed.insert(0);
2355   new_expected_result.old_schema_type_ids_changed.insert(1);
2356   EXPECT_THAT(schema_store->SetSchema(
2357                   new_schema, /*ignore_errors_and_delete_documents=*/false,
2358                   /*allow_circular_schema_definitions=*/false),
2359               IsOkAndHolds(EqualsSetSchemaResult(new_expected_result)));
2360   ICING_ASSERT_OK_AND_ASSIGN(actual_schema, schema_store->GetSchema());
2361   EXPECT_THAT(*actual_schema, EqualsProto(new_schema));
2362 }
2363 
TEST_F(SchemaStoreTest,GetSchemaTypeId)2364 TEST_F(SchemaStoreTest, GetSchemaTypeId) {
2365   ICING_ASSERT_OK_AND_ASSIGN(
2366       std::unique_ptr<SchemaStore> schema_store,
2367       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2368                           feature_flags_.get()));
2369 
2370   schema_.clear_types();
2371 
2372   // Add a few schema types
2373   const std::string first_type = "first";
2374   auto type = schema_.add_types();
2375   type->set_schema_type(first_type);
2376 
2377   const std::string second_type = "second";
2378   type = schema_.add_types();
2379   type->set_schema_type(second_type);
2380 
2381   // Set it for the first time
2382   SchemaStore::SetSchemaResult result;
2383   result.success = true;
2384   result.schema_types_new_by_name.insert(first_type);
2385   result.schema_types_new_by_name.insert(second_type);
2386   EXPECT_THAT(schema_store->SetSchema(
2387                   schema_, /*ignore_errors_and_delete_documents=*/false,
2388                   /*allow_circular_schema_definitions=*/false),
2389               IsOkAndHolds(EqualsSetSchemaResult(result)));
2390 
2391   EXPECT_THAT(schema_store->GetSchemaTypeId(first_type), IsOkAndHolds(0));
2392   EXPECT_THAT(schema_store->GetSchemaTypeId(second_type), IsOkAndHolds(1));
2393 }
2394 
TEST_F(SchemaStoreTest,UpdateChecksumDefaultOnEmptySchemaStore)2395 TEST_F(SchemaStoreTest, UpdateChecksumDefaultOnEmptySchemaStore) {
2396   ICING_ASSERT_OK_AND_ASSIGN(
2397       std::unique_ptr<SchemaStore> schema_store,
2398       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2399                           feature_flags_.get()));
2400 
2401   EXPECT_THAT(schema_store->GetChecksum(), IsOkAndHolds(Crc32()));
2402   EXPECT_THAT(schema_store->UpdateChecksum(), IsOkAndHolds(Crc32()));
2403   EXPECT_THAT(schema_store->GetChecksum(), IsOkAndHolds(Crc32()));
2404 }
2405 
TEST_F(SchemaStoreTest,UpdateChecksumSameBetweenCalls)2406 TEST_F(SchemaStoreTest, UpdateChecksumSameBetweenCalls) {
2407   ICING_ASSERT_OK_AND_ASSIGN(
2408       std::unique_ptr<SchemaStore> schema_store,
2409       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2410                           feature_flags_.get()));
2411 
2412   SchemaProto foo_schema =
2413       SchemaBuilder().AddType(SchemaTypeConfigBuilder().SetType("foo")).Build();
2414 
2415   ICING_EXPECT_OK(schema_store->SetSchema(
2416       foo_schema, /*ignore_errors_and_delete_documents=*/false,
2417       /*allow_circular_schema_definitions=*/false));
2418 
2419   ICING_ASSERT_OK_AND_ASSIGN(Crc32 checksum, schema_store->GetChecksum());
2420   EXPECT_THAT(schema_store->UpdateChecksum(), IsOkAndHolds(checksum));
2421   EXPECT_THAT(schema_store->GetChecksum(), IsOkAndHolds(checksum));
2422 
2423   // Calling it again doesn't change the checksum
2424   EXPECT_THAT(schema_store->UpdateChecksum(), IsOkAndHolds(checksum));
2425   EXPECT_THAT(schema_store->GetChecksum(), IsOkAndHolds(checksum));
2426 }
2427 
TEST_F(SchemaStoreTest,UpdateChecksumSameAcrossInstances)2428 TEST_F(SchemaStoreTest, UpdateChecksumSameAcrossInstances) {
2429   ICING_ASSERT_OK_AND_ASSIGN(
2430       std::unique_ptr<SchemaStore> schema_store,
2431       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2432                           feature_flags_.get()));
2433 
2434   SchemaProto foo_schema =
2435       SchemaBuilder().AddType(SchemaTypeConfigBuilder().SetType("foo")).Build();
2436 
2437   ICING_EXPECT_OK(schema_store->SetSchema(
2438       foo_schema, /*ignore_errors_and_delete_documents=*/false,
2439       /*allow_circular_schema_definitions=*/false));
2440 
2441   ICING_ASSERT_OK_AND_ASSIGN(Crc32 checksum, schema_store->GetChecksum());
2442   EXPECT_THAT(schema_store->UpdateChecksum(), IsOkAndHolds(checksum));
2443   EXPECT_THAT(schema_store->GetChecksum(), IsOkAndHolds(checksum));
2444 
2445   // Destroy the previous instance and recreate SchemaStore
2446   schema_store.reset();
2447 
2448   ICING_ASSERT_OK_AND_ASSIGN(
2449       schema_store, SchemaStore::Create(&filesystem_, schema_store_dir_,
2450                                         &fake_clock_, feature_flags_.get()));
2451   EXPECT_THAT(schema_store->GetChecksum(), IsOkAndHolds(checksum));
2452   EXPECT_THAT(schema_store->UpdateChecksum(), IsOkAndHolds(checksum));
2453   EXPECT_THAT(schema_store->GetChecksum(), IsOkAndHolds(checksum));
2454 }
2455 
TEST_F(SchemaStoreTest,UpdateChecksumChangesOnModification)2456 TEST_F(SchemaStoreTest, UpdateChecksumChangesOnModification) {
2457   ICING_ASSERT_OK_AND_ASSIGN(
2458       std::unique_ptr<SchemaStore> schema_store,
2459       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2460                           feature_flags_.get()));
2461 
2462   SchemaProto foo_schema =
2463       SchemaBuilder().AddType(SchemaTypeConfigBuilder().SetType("foo")).Build();
2464 
2465   ICING_EXPECT_OK(schema_store->SetSchema(
2466       foo_schema, /*ignore_errors_and_delete_documents=*/false,
2467       /*allow_circular_schema_definitions=*/false));
2468 
2469   ICING_ASSERT_OK_AND_ASSIGN(Crc32 checksum, schema_store->GetChecksum());
2470   EXPECT_THAT(schema_store->UpdateChecksum(), IsOkAndHolds(checksum));
2471   EXPECT_THAT(schema_store->GetChecksum(), IsOkAndHolds(checksum));
2472 
2473   // Modifying the SchemaStore changes the checksum
2474   SchemaProto foo_bar_schema =
2475       SchemaBuilder()
2476           .AddType(SchemaTypeConfigBuilder().SetType("foo"))
2477           .AddType(SchemaTypeConfigBuilder().SetType("bar"))
2478           .Build();
2479 
2480   ICING_EXPECT_OK(schema_store->SetSchema(
2481       foo_bar_schema, /*ignore_errors_and_delete_documents=*/false,
2482       /*allow_circular_schema_definitions=*/false));
2483 
2484   ICING_ASSERT_OK_AND_ASSIGN(Crc32 updated_checksum,
2485                              schema_store->GetChecksum());
2486   EXPECT_THAT(updated_checksum, Not(Eq(checksum)));
2487   EXPECT_THAT(schema_store->UpdateChecksum(), IsOkAndHolds(updated_checksum));
2488   EXPECT_THAT(schema_store->GetChecksum(), IsOkAndHolds(updated_checksum));
2489 }
2490 
TEST_F(SchemaStoreTest,PersistToDiskFineForEmptySchemaStore)2491 TEST_F(SchemaStoreTest, PersistToDiskFineForEmptySchemaStore) {
2492   ICING_ASSERT_OK_AND_ASSIGN(
2493       std::unique_ptr<SchemaStore> schema_store,
2494       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2495                           feature_flags_.get()));
2496 
2497   // Persisting is fine and shouldn't affect anything
2498   ICING_EXPECT_OK(schema_store->PersistToDisk());
2499 }
2500 
TEST_F(SchemaStoreTest,UpdateChecksumAvoidsRecovery)2501 TEST_F(SchemaStoreTest, UpdateChecksumAvoidsRecovery) {
2502   ICING_ASSERT_OK_AND_ASSIGN(
2503       std::unique_ptr<SchemaStore> schema_store,
2504       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2505                           feature_flags_.get()));
2506 
2507   SchemaProto schema =
2508       SchemaBuilder().AddType(SchemaTypeConfigBuilder().SetType("foo")).Build();
2509 
2510   ICING_EXPECT_OK(schema_store->SetSchema(
2511       schema, /*ignore_errors_and_delete_documents=*/false,
2512       /*allow_circular_schema_definitions=*/false));
2513 
2514   // UpdateChecksum should update the schema store checksum. Therefore, we
2515   // should not need a recovery on reinitialization.
2516   ICING_ASSERT_OK_AND_ASSIGN(Crc32 crc, schema_store->GetChecksum());
2517   EXPECT_THAT(schema_store->UpdateChecksum(), IsOkAndHolds(crc));
2518   EXPECT_THAT(schema_store->GetChecksum(), IsOkAndHolds(crc));
2519 
2520   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
2521                              schema_store->GetSchema());
2522   EXPECT_THAT(*actual_schema, EqualsProto(schema));
2523 
2524   // And we get the same schema back on reinitialization
2525   InitializeStatsProto initialize_stats;
2526   ICING_ASSERT_OK_AND_ASSIGN(
2527       std::unique_ptr<SchemaStore> schema_store_two,
2528       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2529                           feature_flags_.get(),
2530                           /*enable_schema_database=*/false, &initialize_stats));
2531   EXPECT_THAT(initialize_stats.schema_store_recovery_cause(),
2532               Eq(InitializeStatsProto::NONE));
2533   ICING_ASSERT_OK_AND_ASSIGN(actual_schema, schema_store_two->GetSchema());
2534   EXPECT_THAT(*actual_schema, EqualsProto(schema));
2535 
2536   // The checksum should be the same.
2537   EXPECT_THAT(schema_store_two->GetChecksum(), IsOkAndHolds(crc));
2538   EXPECT_THAT(schema_store_two->UpdateChecksum(), IsOkAndHolds(crc));
2539   EXPECT_THAT(schema_store_two->GetChecksum(), IsOkAndHolds(crc));
2540 }
2541 
TEST_F(SchemaStoreTest,PersistToDiskPreservesAcrossInstances)2542 TEST_F(SchemaStoreTest, PersistToDiskPreservesAcrossInstances) {
2543   ICING_ASSERT_OK_AND_ASSIGN(
2544       std::unique_ptr<SchemaStore> schema_store,
2545       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2546                           feature_flags_.get()));
2547 
2548   SchemaProto schema =
2549       SchemaBuilder().AddType(SchemaTypeConfigBuilder().SetType("foo")).Build();
2550 
2551   ICING_EXPECT_OK(schema_store->SetSchema(
2552       schema, /*ignore_errors_and_delete_documents=*/false,
2553       /*allow_circular_schema_definitions=*/false));
2554 
2555   // Persisting shouldn't change anything
2556   ICING_EXPECT_OK(schema_store->PersistToDisk());
2557 
2558   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
2559                              schema_store->GetSchema());
2560   EXPECT_THAT(*actual_schema, EqualsProto(schema));
2561 
2562   // Modify the schema so that something different is persisted next time
2563   schema = SchemaBuilder(schema)
2564                .AddType(SchemaTypeConfigBuilder().SetType("bar"))
2565                .Build();
2566   ICING_EXPECT_OK(schema_store->SetSchema(
2567       schema, /*ignore_errors_and_delete_documents=*/false,
2568       /*allow_circular_schema_definitions=*/false));
2569 
2570   // Should also persist on destruction
2571   schema_store.reset();
2572 
2573   // And we get the same schema back on reinitialization
2574   InitializeStatsProto initialize_stats;
2575   ICING_ASSERT_OK_AND_ASSIGN(
2576       schema_store,
2577       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2578                           feature_flags_.get(),
2579                           /*enable_schema_database=*/false, &initialize_stats));
2580   EXPECT_THAT(initialize_stats.schema_store_recovery_cause(),
2581               Eq(InitializeStatsProto::NONE));
2582   ICING_ASSERT_OK_AND_ASSIGN(actual_schema, schema_store->GetSchema());
2583   EXPECT_THAT(*actual_schema, EqualsProto(schema));
2584 }
2585 
TEST_F(SchemaStoreTest,SchemaStoreStorageInfoProto)2586 TEST_F(SchemaStoreTest, SchemaStoreStorageInfoProto) {
2587   ICING_ASSERT_OK_AND_ASSIGN(
2588       std::unique_ptr<SchemaStore> schema_store,
2589       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2590                           feature_flags_.get()));
2591 
2592   // Create a schema with two types: one simple type and one type that uses all
2593   // 64 sections.
2594   PropertyConfigProto prop =
2595       PropertyConfigBuilder()
2596           .SetName("subject")
2597           .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
2598           .SetCardinality(CARDINALITY_OPTIONAL)
2599           .Build();
2600   SchemaTypeConfigBuilder full_sections_type_builder =
2601       SchemaTypeConfigBuilder().SetType("fullSectionsType");
2602   for (int i = 0; i < 64; ++i) {
2603     full_sections_type_builder.AddProperty(
2604         PropertyConfigBuilder(prop).SetName("prop" + std::to_string(i)));
2605   }
2606   SchemaProto schema =
2607       SchemaBuilder()
2608           .AddType(SchemaTypeConfigBuilder().SetType("email").AddProperty(
2609               PropertyConfigBuilder(prop)))
2610           .AddType(full_sections_type_builder)
2611           .Build();
2612 
2613   SchemaStore::SetSchemaResult result;
2614   result.success = true;
2615   result.schema_types_new_by_name.insert("email");
2616   result.schema_types_new_by_name.insert("fullSectionsType");
2617   EXPECT_THAT(schema_store->SetSchema(
2618                   schema, /*ignore_errors_and_delete_documents=*/false,
2619                   /*allow_circular_schema_definitions=*/false),
2620               IsOkAndHolds(EqualsSetSchemaResult(result)));
2621 
2622   SchemaStoreStorageInfoProto storage_info = schema_store->GetStorageInfo();
2623   EXPECT_THAT(storage_info.schema_store_size(), Ge(0));
2624   EXPECT_THAT(storage_info.num_schema_types(), Eq(2));
2625   EXPECT_THAT(storage_info.num_total_sections(), Eq(65));
2626   EXPECT_THAT(storage_info.num_schema_types_sections_exhausted(), Eq(1));
2627 }
2628 
TEST_F(SchemaStoreTest,GetDebugInfo)2629 TEST_F(SchemaStoreTest, GetDebugInfo) {
2630   ICING_ASSERT_OK_AND_ASSIGN(
2631       std::unique_ptr<SchemaStore> schema_store,
2632       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2633                           feature_flags_.get()));
2634 
2635   // Set schema
2636   ASSERT_THAT(
2637       schema_store->SetSchema(schema_,
2638                               /*ignore_errors_and_delete_documents=*/false,
2639                               /*allow_circular_schema_definitions=*/false),
2640       IsOkAndHolds(EqualsSetSchemaResult(SchemaStore::SetSchemaResult{
2641           .success = true,
2642           .schema_types_new_by_name = {schema_.types(0).schema_type()}})));
2643 
2644   // Check debug info
2645   ICING_ASSERT_OK_AND_ASSIGN(SchemaDebugInfoProto out,
2646                              schema_store->GetDebugInfo());
2647   EXPECT_THAT(out.schema(), EqualsProto(schema_));
2648   EXPECT_THAT(out.crc(), Gt(0));
2649 }
2650 
TEST_F(SchemaStoreTest,GetDebugInfoForEmptySchemaStore)2651 TEST_F(SchemaStoreTest, GetDebugInfoForEmptySchemaStore) {
2652   ICING_ASSERT_OK_AND_ASSIGN(
2653       std::unique_ptr<SchemaStore> schema_store,
2654       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2655                           feature_flags_.get()));
2656 
2657   // Check debug info before setting a schema
2658   ICING_ASSERT_OK_AND_ASSIGN(SchemaDebugInfoProto out,
2659                              schema_store->GetDebugInfo());
2660   SchemaDebugInfoProto expected_out;
2661   expected_out.set_crc(0);
2662   EXPECT_THAT(out, EqualsProto(expected_out));
2663 }
2664 
TEST_F(SchemaStoreTest,InitializeRegenerateDerivedFilesFailure)2665 TEST_F(SchemaStoreTest, InitializeRegenerateDerivedFilesFailure) {
2666   // This test covers the first point that RegenerateDerivedFiles could fail.
2667   // This should simply result in SetSchema::Create returning an INTERNAL error.
2668 
2669   {
2670     ICING_ASSERT_OK_AND_ASSIGN(
2671         std::unique_ptr<SchemaStore> schema_store,
2672         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2673                             feature_flags_.get()));
2674     SchemaProto schema = SchemaBuilder()
2675                              .AddType(SchemaTypeConfigBuilder().SetType("Type"))
2676                              .Build();
2677     ICING_ASSERT_OK(schema_store->SetSchema(
2678         std::move(schema), /*ignore_errors_and_delete_documents=*/false,
2679         /*allow_circular_schema_definitions=*/false));
2680   }
2681 
2682   auto mock_filesystem = std::make_unique<MockFilesystem>();
2683   ON_CALL(*mock_filesystem,
2684           CreateDirectoryRecursively(HasSubstr("key_mapper_dir")))
2685       .WillByDefault(Return(false));
2686   {
2687     EXPECT_THAT(SchemaStore::Create(mock_filesystem.get(), schema_store_dir_,
2688                                     &fake_clock_, feature_flags_.get()),
2689                 StatusIs(libtextclassifier3::StatusCode::INTERNAL));
2690   }
2691 }
2692 
TEST_F(SchemaStoreTest,SetSchemaRegenerateDerivedFilesFailure)2693 TEST_F(SchemaStoreTest, SetSchemaRegenerateDerivedFilesFailure) {
2694   // This test covers the second point that RegenerateDerivedFiles could fail.
2695   // If handled correctly, the schema store and section manager should still be
2696   // in the original, valid state.
2697   SchemaTypeConfigProto type =
2698       SchemaTypeConfigBuilder()
2699           .SetType("Type")
2700           .AddProperty(PropertyConfigBuilder()
2701                            .SetName("intProp1")
2702                            .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
2703                            .SetCardinality(CARDINALITY_OPTIONAL))
2704           .AddProperty(PropertyConfigBuilder()
2705                            .SetName("stringProp1")
2706                            .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
2707                            .SetCardinality(CARDINALITY_OPTIONAL))
2708           .Build();
2709   {
2710     ICING_ASSERT_OK_AND_ASSIGN(
2711         std::unique_ptr<SchemaStore> schema_store,
2712         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2713                             feature_flags_.get()));
2714     SchemaProto schema = SchemaBuilder().AddType(type).Build();
2715     ICING_ASSERT_OK(schema_store->SetSchema(
2716         std::move(schema), /*ignore_errors_and_delete_documents=*/false,
2717         /*allow_circular_schema_definitions=*/false));
2718   }
2719 
2720   {
2721     auto mock_filesystem = std::make_unique<MockFilesystem>();
2722     ICING_ASSERT_OK_AND_ASSIGN(
2723         std::unique_ptr<SchemaStore> schema_store,
2724         SchemaStore::Create(mock_filesystem.get(), schema_store_dir_,
2725                             &fake_clock_, feature_flags_.get()));
2726 
2727     ON_CALL(*mock_filesystem,
2728             CreateDirectoryRecursively(HasSubstr("key_mapper_dir")))
2729         .WillByDefault(Return(false));
2730     SchemaProto schema =
2731         SchemaBuilder()
2732             .AddType(type)
2733             .AddType(SchemaTypeConfigBuilder().SetType("Type2"))
2734             .Build();
2735     EXPECT_THAT(
2736         schema_store->SetSchema(std::move(schema),
2737                                 /*ignore_errors_and_delete_documents=*/false,
2738                                 /*allow_circular_schema_definitions=*/false),
2739         StatusIs(libtextclassifier3::StatusCode::INTERNAL));
2740     DocumentProto document =
2741         DocumentBuilder()
2742             .SetSchema("Type")
2743             .AddInt64Property("intProp1", 1, 2, 3)
2744             .AddStringProperty("stringProp1", "foo bar baz")
2745             .Build();
2746     SectionMetadata expected_int_prop1_metadata(
2747         /*id_in=*/0, TYPE_INT64, TOKENIZER_NONE, TERM_MATCH_UNKNOWN,
2748         NUMERIC_MATCH_RANGE, EMBEDDING_INDEXING_UNKNOWN, QUANTIZATION_TYPE_NONE,
2749         "intProp1");
2750     SectionMetadata expected_string_prop1_metadata(
2751         /*id_in=*/1, TYPE_STRING, TOKENIZER_PLAIN, TERM_MATCH_EXACT,
2752         NUMERIC_MATCH_UNKNOWN, EMBEDDING_INDEXING_UNKNOWN,
2753         QUANTIZATION_TYPE_NONE, "stringProp1");
2754     ICING_ASSERT_OK_AND_ASSIGN(SectionGroup section_group,
2755                                schema_store->ExtractSections(document));
2756     ASSERT_THAT(section_group.string_sections, SizeIs(1));
2757     EXPECT_THAT(section_group.string_sections.at(0).metadata,
2758                 Eq(expected_string_prop1_metadata));
2759     EXPECT_THAT(section_group.string_sections.at(0).content,
2760                 ElementsAre("foo bar baz"));
2761     ASSERT_THAT(section_group.integer_sections, SizeIs(1));
2762     EXPECT_THAT(section_group.integer_sections.at(0).metadata,
2763                 Eq(expected_int_prop1_metadata));
2764     EXPECT_THAT(section_group.integer_sections.at(0).content,
2765                 ElementsAre(1, 2, 3));
2766   }
2767 }
2768 
TEST_F(SchemaStoreTest,CanCheckForPropertiesDefinedInSchema)2769 TEST_F(SchemaStoreTest, CanCheckForPropertiesDefinedInSchema) {
2770   ICING_ASSERT_OK_AND_ASSIGN(
2771       std::unique_ptr<SchemaStore> schema_store,
2772       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2773                           feature_flags_.get()));
2774 
2775   // Set it for the first time
2776   SchemaStore::SetSchemaResult result;
2777   result.success = true;
2778   result.schema_types_new_by_name.insert(schema_.types(0).schema_type());
2779 
2780   // Don't use schema_ defined in the test suite, as we want to make sure that
2781   // the test is written correctly without referring to what the suite has
2782   // defined.
2783   SchemaProto schema =
2784       SchemaBuilder()
2785           .AddType(
2786               SchemaTypeConfigBuilder()
2787                   .SetType("email")
2788                   .AddProperty(
2789                       // Add an indexed property so we generate
2790                       // section metadata on it
2791                       PropertyConfigBuilder()
2792                           .SetName("subject")
2793                           .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
2794                           .SetCardinality(CARDINALITY_OPTIONAL))
2795                   .AddProperty(PropertyConfigBuilder()
2796                                    .SetName("timestamp")
2797                                    .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
2798                                    .SetCardinality(CARDINALITY_OPTIONAL)))
2799           .Build();
2800 
2801   EXPECT_THAT(schema_store->SetSchema(
2802                   schema, /*ignore_errors_and_delete_documents=*/false,
2803                   /*allow_circular_schema_definitions=*/false),
2804               IsOkAndHolds(EqualsSetSchemaResult(result)));
2805   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId schema_id,
2806                              schema_store->GetSchemaTypeId("email"));
2807   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(schema_id, "subject"));
2808   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(schema_id, "timestamp"));
2809   EXPECT_FALSE(schema_store->IsPropertyDefinedInSchema(schema_id, "foobar"));
2810 }
2811 
TEST_F(SchemaStoreTest,GetSchemaTypeIdsWithChildren)2812 TEST_F(SchemaStoreTest, GetSchemaTypeIdsWithChildren) {
2813   ICING_ASSERT_OK_AND_ASSIGN(
2814       std::unique_ptr<SchemaStore> schema_store,
2815       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2816                           feature_flags_.get()));
2817 
2818   // Create a schema with the following inheritance relation:
2819   //       A
2820   //     /   \
2821   //    B     E
2822   //   /  \
2823   //  C    D
2824   //       |
2825   //       F
2826   SchemaTypeConfigProto type_a = SchemaTypeConfigBuilder().SetType("A").Build();
2827   SchemaTypeConfigProto type_b =
2828       SchemaTypeConfigBuilder().SetType("B").AddParentType("A").Build();
2829   SchemaTypeConfigProto type_c =
2830       SchemaTypeConfigBuilder().SetType("C").AddParentType("B").Build();
2831   SchemaTypeConfigProto type_d =
2832       SchemaTypeConfigBuilder().SetType("D").AddParentType("B").Build();
2833   SchemaTypeConfigProto type_e =
2834       SchemaTypeConfigBuilder().SetType("E").AddParentType("A").Build();
2835   SchemaTypeConfigProto type_f =
2836       SchemaTypeConfigBuilder().SetType("F").AddParentType("D").Build();
2837   SchemaProto schema = SchemaBuilder()
2838                            .AddType(type_a)
2839                            .AddType(type_b)
2840                            .AddType(type_c)
2841                            .AddType(type_d)
2842                            .AddType(type_e)
2843                            .AddType(type_f)
2844                            .Build();
2845   ICING_ASSERT_OK(schema_store->SetSchema(
2846       schema, /*ignore_errors_and_delete_documents=*/false,
2847       /*allow_circular_schema_definitions=*/false));
2848 
2849   // Get schema type id for each type.
2850   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId type_a_id,
2851                              schema_store->GetSchemaTypeId("A"));
2852   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId type_b_id,
2853                              schema_store->GetSchemaTypeId("B"));
2854   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId type_c_id,
2855                              schema_store->GetSchemaTypeId("C"));
2856   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId type_d_id,
2857                              schema_store->GetSchemaTypeId("D"));
2858   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId type_e_id,
2859                              schema_store->GetSchemaTypeId("E"));
2860   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId type_f_id,
2861                              schema_store->GetSchemaTypeId("F"));
2862 
2863   // Check the results from GetSchemaTypeIdsWithChildren
2864   EXPECT_THAT(
2865       schema_store->GetSchemaTypeIdsWithChildren("A"),
2866       IsOkAndHolds(Pointee(UnorderedElementsAre(
2867           type_a_id, type_b_id, type_c_id, type_d_id, type_e_id, type_f_id))));
2868   EXPECT_THAT(schema_store->GetSchemaTypeIdsWithChildren("B"),
2869               IsOkAndHolds(Pointee(UnorderedElementsAre(
2870                   type_b_id, type_c_id, type_d_id, type_f_id))));
2871   EXPECT_THAT(schema_store->GetSchemaTypeIdsWithChildren("C"),
2872               IsOkAndHolds(Pointee(UnorderedElementsAre(type_c_id))));
2873   EXPECT_THAT(
2874       schema_store->GetSchemaTypeIdsWithChildren("D"),
2875       IsOkAndHolds(Pointee(UnorderedElementsAre(type_d_id, type_f_id))));
2876   EXPECT_THAT(schema_store->GetSchemaTypeIdsWithChildren("E"),
2877               IsOkAndHolds(Pointee(UnorderedElementsAre(type_e_id))));
2878   EXPECT_THAT(schema_store->GetSchemaTypeIdsWithChildren("F"),
2879               IsOkAndHolds(Pointee(UnorderedElementsAre(type_f_id))));
2880 }
2881 
TEST_F(SchemaStoreTest,DiamondGetSchemaTypeIdsWithChildren)2882 TEST_F(SchemaStoreTest, DiamondGetSchemaTypeIdsWithChildren) {
2883   ICING_ASSERT_OK_AND_ASSIGN(
2884       std::unique_ptr<SchemaStore> schema_store,
2885       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2886                           feature_flags_.get()));
2887 
2888   // Create a schema with the following inheritance relation:
2889   //       A
2890   //     /   \
2891   //    B     E
2892   //   /  \  /
2893   //  C    D
2894   //   \  /
2895   //     F
2896   SchemaTypeConfigProto type_a = SchemaTypeConfigBuilder().SetType("A").Build();
2897   SchemaTypeConfigProto type_b =
2898       SchemaTypeConfigBuilder().SetType("B").AddParentType("A").Build();
2899   SchemaTypeConfigProto type_c =
2900       SchemaTypeConfigBuilder().SetType("C").AddParentType("B").Build();
2901   SchemaTypeConfigProto type_d = SchemaTypeConfigBuilder()
2902                                      .SetType("D")
2903                                      .AddParentType("B")
2904                                      .AddParentType("E")
2905                                      .Build();
2906   SchemaTypeConfigProto type_e =
2907       SchemaTypeConfigBuilder().SetType("E").AddParentType("A").Build();
2908   SchemaTypeConfigProto type_f = SchemaTypeConfigBuilder()
2909                                      .SetType("F")
2910                                      .AddParentType("C")
2911                                      .AddParentType("D")
2912                                      .Build();
2913   SchemaProto schema = SchemaBuilder()
2914                            .AddType(type_a)
2915                            .AddType(type_b)
2916                            .AddType(type_c)
2917                            .AddType(type_d)
2918                            .AddType(type_e)
2919                            .AddType(type_f)
2920                            .Build();
2921   ICING_ASSERT_OK(schema_store->SetSchema(
2922       schema, /*ignore_errors_and_delete_documents=*/false,
2923       /*allow_circular_schema_definitions=*/false));
2924 
2925   // Get schema type id for each type.
2926   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId type_a_id,
2927                              schema_store->GetSchemaTypeId("A"));
2928   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId type_b_id,
2929                              schema_store->GetSchemaTypeId("B"));
2930   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId type_c_id,
2931                              schema_store->GetSchemaTypeId("C"));
2932   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId type_d_id,
2933                              schema_store->GetSchemaTypeId("D"));
2934   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId type_e_id,
2935                              schema_store->GetSchemaTypeId("E"));
2936   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId type_f_id,
2937                              schema_store->GetSchemaTypeId("F"));
2938 
2939   // Check the results from GetSchemaTypeIdsWithChildren
2940   EXPECT_THAT(
2941       schema_store->GetSchemaTypeIdsWithChildren("A"),
2942       IsOkAndHolds(Pointee(UnorderedElementsAre(
2943           type_a_id, type_b_id, type_c_id, type_d_id, type_e_id, type_f_id))));
2944   EXPECT_THAT(schema_store->GetSchemaTypeIdsWithChildren("B"),
2945               IsOkAndHolds(Pointee(UnorderedElementsAre(
2946                   type_b_id, type_c_id, type_d_id, type_f_id))));
2947   EXPECT_THAT(
2948       schema_store->GetSchemaTypeIdsWithChildren("C"),
2949       IsOkAndHolds(Pointee(UnorderedElementsAre(type_c_id, type_f_id))));
2950   EXPECT_THAT(
2951       schema_store->GetSchemaTypeIdsWithChildren("D"),
2952       IsOkAndHolds(Pointee(UnorderedElementsAre(type_d_id, type_f_id))));
2953   EXPECT_THAT(schema_store->GetSchemaTypeIdsWithChildren("E"),
2954               IsOkAndHolds(Pointee(
2955                   UnorderedElementsAre(type_e_id, type_d_id, type_f_id))));
2956   EXPECT_THAT(schema_store->GetSchemaTypeIdsWithChildren("F"),
2957               IsOkAndHolds(Pointee(UnorderedElementsAre(type_f_id))));
2958 }
2959 
TEST_F(SchemaStoreTest,IndexableFieldsAreDefined)2960 TEST_F(SchemaStoreTest, IndexableFieldsAreDefined) {
2961   SchemaTypeConfigProto email_type =
2962       SchemaTypeConfigBuilder()
2963           .SetType("Email")
2964           .AddProperty(
2965               PropertyConfigBuilder()
2966                   .SetName("subject")
2967                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
2968                   .SetCardinality(CARDINALITY_REQUIRED))
2969           .AddProperty(
2970               PropertyConfigBuilder()
2971                   .SetName("senderQualifiedId")
2972                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
2973                   .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID,
2974                                DELETE_PROPAGATION_TYPE_PROPAGATE_FROM)
2975                   .SetCardinality(CARDINALITY_REQUIRED))
2976           .AddProperty(PropertyConfigBuilder()
2977                            .SetName("recipients")
2978                            .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
2979                            .SetCardinality(CARDINALITY_REPEATED))
2980           .AddProperty(PropertyConfigBuilder()
2981                            .SetName("recipientIds")
2982                            .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
2983                            .SetCardinality(CARDINALITY_REPEATED))
2984           .AddProperty(PropertyConfigBuilder()
2985                            .SetName("timestamp")
2986                            .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
2987                            .SetCardinality(CARDINALITY_REQUIRED))
2988           .Build();
2989 
2990   SchemaProto schema = SchemaBuilder().AddType(email_type).Build();
2991   ICING_ASSERT_OK_AND_ASSIGN(
2992       std::unique_ptr<SchemaStore> schema_store,
2993       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
2994                           feature_flags_.get()));
2995   ICING_ASSERT_OK(schema_store->SetSchema(
2996       schema, /*ignore_errors_and_delete_documents=*/false,
2997       /*allow_circular_schema_definitions=*/true));
2998   constexpr SchemaTypeId kTypeEmailSchemaId = 0;
2999 
3000   // Indexables.
3001   EXPECT_TRUE(
3002       schema_store->IsPropertyDefinedInSchema(kTypeEmailSchemaId, "subject"));
3003   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeEmailSchemaId,
3004                                                       "senderQualifiedId"));
3005   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeEmailSchemaId,
3006                                                       "recipients"));
3007   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeEmailSchemaId,
3008                                                       "recipientIds"));
3009   EXPECT_TRUE(
3010       schema_store->IsPropertyDefinedInSchema(kTypeEmailSchemaId, "timestamp"));
3011 }
3012 
TEST_F(SchemaStoreTest,JoinableFieldsAreDefined)3013 TEST_F(SchemaStoreTest, JoinableFieldsAreDefined) {
3014   SchemaTypeConfigProto email_type =
3015       SchemaTypeConfigBuilder()
3016           .SetType("Email")
3017           .AddProperty(PropertyConfigBuilder()
3018                            .SetName("tagQualifiedId")
3019                            .SetDataType(TYPE_STRING)
3020                            .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID,
3021                                         DELETE_PROPAGATION_TYPE_PROPAGATE_FROM)
3022                            .SetCardinality(CARDINALITY_REQUIRED))
3023           .AddProperty(
3024               PropertyConfigBuilder()
3025                   .SetName("senderQualifiedId")
3026                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
3027                   .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID,
3028                                DELETE_PROPAGATION_TYPE_PROPAGATE_FROM)
3029                   .SetCardinality(CARDINALITY_REQUIRED))
3030           .Build();
3031 
3032   SchemaProto schema = SchemaBuilder().AddType(email_type).Build();
3033   ICING_ASSERT_OK_AND_ASSIGN(
3034       std::unique_ptr<SchemaStore> schema_store,
3035       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3036                           feature_flags_.get()));
3037   ICING_ASSERT_OK(schema_store->SetSchema(
3038       schema, /*ignore_errors_and_delete_documents=*/false,
3039       /*allow_circular_schema_definitions=*/true));
3040   constexpr SchemaTypeId kTypeEmailSchemaId = 0;
3041 
3042   // Joinables.
3043   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeEmailSchemaId,
3044                                                       "tagQualifiedId"));
3045   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeEmailSchemaId,
3046                                                       "senderQualifiedId"));
3047 }
3048 
TEST_F(SchemaStoreTest,NonIndexableFieldsAreDefined)3049 TEST_F(SchemaStoreTest, NonIndexableFieldsAreDefined) {
3050   SchemaTypeConfigProto email_type =
3051       SchemaTypeConfigBuilder()
3052           .SetType("Email")
3053           .AddProperty(
3054               PropertyConfigBuilder()
3055                   .SetName("text")
3056                   .SetDataTypeString(TERM_MATCH_UNKNOWN, TOKENIZER_NONE)
3057                   .SetCardinality(CARDINALITY_OPTIONAL))
3058           .AddProperty(PropertyConfigBuilder()
3059                            .SetName("attachment")
3060                            .SetDataType(TYPE_BYTES)
3061                            .SetCardinality(CARDINALITY_REQUIRED))
3062           .AddProperty(PropertyConfigBuilder()
3063                            .SetName("nonindexableInteger")
3064                            .SetDataType(TYPE_INT64)
3065                            .SetCardinality(CARDINALITY_REQUIRED))
3066           .Build();
3067 
3068   SchemaProto schema = SchemaBuilder().AddType(email_type).Build();
3069   ICING_ASSERT_OK_AND_ASSIGN(
3070       std::unique_ptr<SchemaStore> schema_store,
3071       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3072                           feature_flags_.get()));
3073   ICING_ASSERT_OK(schema_store->SetSchema(
3074       schema, /*ignore_errors_and_delete_documents=*/false,
3075       /*allow_circular_schema_definitions=*/true));
3076   constexpr SchemaTypeId kTypeEmailSchemaId = 0;
3077 
3078   // Non-indexables.
3079   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeEmailSchemaId,
3080                                                       "attachment"));
3081   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeEmailSchemaId,
3082                                                       "nonindexableInteger"));
3083   EXPECT_TRUE(
3084       schema_store->IsPropertyDefinedInSchema(kTypeEmailSchemaId, "text"));
3085 }
3086 
TEST_F(SchemaStoreTest,NonExistentFieldsAreUndefined)3087 TEST_F(SchemaStoreTest, NonExistentFieldsAreUndefined) {
3088   SchemaTypeConfigProto email_type =
3089       SchemaTypeConfigBuilder()
3090           .SetType("Email")
3091           .AddProperty(
3092               PropertyConfigBuilder()
3093                   .SetName("subject")
3094                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
3095                   .SetCardinality(CARDINALITY_REQUIRED))
3096           .AddProperty(
3097               PropertyConfigBuilder()
3098                   .SetName("senderQualifiedId")
3099                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
3100                   .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID,
3101                                DELETE_PROPAGATION_TYPE_PROPAGATE_FROM)
3102                   .SetCardinality(CARDINALITY_REQUIRED))
3103           .AddProperty(PropertyConfigBuilder()
3104                            .SetName("timestamp")
3105                            .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
3106                            .SetCardinality(CARDINALITY_REQUIRED))
3107           .AddProperty(PropertyConfigBuilder()
3108                            .SetName("nonindexableInteger")
3109                            .SetDataType(TYPE_INT64)
3110                            .SetCardinality(CARDINALITY_REQUIRED))
3111           .Build();
3112 
3113   SchemaProto schema = SchemaBuilder().AddType(email_type).Build();
3114   ICING_ASSERT_OK_AND_ASSIGN(
3115       std::unique_ptr<SchemaStore> schema_store,
3116       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3117                           feature_flags_.get()));
3118   ICING_ASSERT_OK(schema_store->SetSchema(
3119       schema, /*ignore_errors_and_delete_documents=*/false,
3120       /*allow_circular_schema_definitions=*/true));
3121   constexpr SchemaTypeId kTypeEmailSchemaId = 0;
3122 
3123   // Non-existents.
3124   EXPECT_FALSE(
3125       schema_store->IsPropertyDefinedInSchema(kTypeEmailSchemaId, "foobar"));
3126   EXPECT_FALSE(schema_store->IsPropertyDefinedInSchema(kTypeEmailSchemaId,
3127                                                        "timestamp.foo"));
3128   EXPECT_FALSE(
3129       schema_store->IsPropertyDefinedInSchema(kTypeEmailSchemaId, "time"));
3130 }
3131 
TEST_F(SchemaStoreTest,NestedIndexableFieldsAreDefined)3132 TEST_F(SchemaStoreTest, NestedIndexableFieldsAreDefined) {
3133   SchemaTypeConfigProto email_type =
3134       SchemaTypeConfigBuilder()
3135           .SetType("Email")
3136           .AddProperty(PropertyConfigBuilder()
3137                            .SetName("tagQualifiedId")
3138                            .SetDataType(TYPE_STRING)
3139                            .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID,
3140                                         DELETE_PROPAGATION_TYPE_PROPAGATE_FROM)
3141                            .SetCardinality(CARDINALITY_REQUIRED))
3142           .AddProperty(
3143               PropertyConfigBuilder()
3144                   .SetName("subject")
3145                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
3146                   .SetCardinality(CARDINALITY_REQUIRED))
3147           .AddProperty(
3148               PropertyConfigBuilder()
3149                   .SetName("text")
3150                   .SetDataTypeString(TERM_MATCH_UNKNOWN, TOKENIZER_NONE)
3151                   .SetCardinality(CARDINALITY_OPTIONAL))
3152           .AddProperty(PropertyConfigBuilder()
3153                            .SetName("timestamp")
3154                            .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
3155                            .SetCardinality(CARDINALITY_REQUIRED))
3156           .Build();
3157 
3158   SchemaTypeConfigProto conversation_type =
3159       SchemaTypeConfigBuilder()
3160           .SetType("Conversation")
3161           .AddProperty(PropertyConfigBuilder()
3162                            .SetName("emails")
3163                            .SetDataTypeDocument(
3164                                "Email", /*index_nested_properties=*/true)
3165                            .SetCardinality(CARDINALITY_OPTIONAL))
3166           .AddProperty(
3167               PropertyConfigBuilder()
3168                   .SetName("nestedNonIndexable")
3169                   .SetDataTypeDocument("Email",
3170                                        /*index_nested_properties=*/false)
3171                   .SetCardinality(CARDINALITY_OPTIONAL))
3172           .Build();
3173   SchemaProto schema =
3174       SchemaBuilder().AddType(email_type).AddType(conversation_type).Build();
3175   ICING_ASSERT_OK_AND_ASSIGN(
3176       std::unique_ptr<SchemaStore> schema_store,
3177       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3178                           feature_flags_.get()));
3179   ICING_ASSERT_OK(schema_store->SetSchema(
3180       schema, /*ignore_errors_and_delete_documents=*/false,
3181       /*allow_circular_schema_definitions=*/true));
3182   constexpr SchemaTypeId kTypeConversationSchemaId = 1;
3183 
3184   // Indexables.
3185   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeConversationSchemaId,
3186                                                       "emails.subject"));
3187   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeConversationSchemaId,
3188                                                       "emails.timestamp"));
3189 }
3190 
TEST_F(SchemaStoreTest,NestedJoinableFieldsAreDefined)3191 TEST_F(SchemaStoreTest, NestedJoinableFieldsAreDefined) {
3192   SchemaTypeConfigProto email_type =
3193       SchemaTypeConfigBuilder()
3194           .SetType("Email")
3195           .AddProperty(PropertyConfigBuilder()
3196                            .SetName("tagQualifiedId")
3197                            .SetDataType(TYPE_STRING)
3198                            .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID,
3199                                         DELETE_PROPAGATION_TYPE_PROPAGATE_FROM)
3200                            .SetCardinality(CARDINALITY_REQUIRED))
3201           .AddProperty(
3202               PropertyConfigBuilder()
3203                   .SetName("subject")
3204                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
3205                   .SetCardinality(CARDINALITY_REQUIRED))
3206           .AddProperty(
3207               PropertyConfigBuilder()
3208                   .SetName("text")
3209                   .SetDataTypeString(TERM_MATCH_UNKNOWN, TOKENIZER_NONE)
3210                   .SetCardinality(CARDINALITY_OPTIONAL))
3211           .AddProperty(PropertyConfigBuilder()
3212                            .SetName("timestamp")
3213                            .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
3214                            .SetCardinality(CARDINALITY_REQUIRED))
3215           .Build();
3216 
3217   SchemaTypeConfigProto conversation_type =
3218       SchemaTypeConfigBuilder()
3219           .SetType("Conversation")
3220           .AddProperty(PropertyConfigBuilder()
3221                            .SetName("emails")
3222                            .SetDataTypeDocument(
3223                                "Email", /*index_nested_properties=*/true)
3224                            .SetCardinality(CARDINALITY_OPTIONAL))
3225           .AddProperty(
3226               PropertyConfigBuilder()
3227                   .SetName("nestedNonIndexable")
3228                   .SetDataTypeDocument("Email",
3229                                        /*index_nested_properties=*/false)
3230                   .SetCardinality(CARDINALITY_OPTIONAL))
3231           .Build();
3232   SchemaProto schema =
3233       SchemaBuilder().AddType(email_type).AddType(conversation_type).Build();
3234   ICING_ASSERT_OK_AND_ASSIGN(
3235       std::unique_ptr<SchemaStore> schema_store,
3236       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3237                           feature_flags_.get()));
3238   ICING_ASSERT_OK(schema_store->SetSchema(
3239       schema, /*ignore_errors_and_delete_documents=*/false,
3240       /*allow_circular_schema_definitions=*/true));
3241   constexpr SchemaTypeId kTypeConversationSchemaId = 1;
3242 
3243   // Joinables.
3244   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeConversationSchemaId,
3245                                                       "emails.tagQualifiedId"));
3246   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(
3247       kTypeConversationSchemaId, "nestedNonIndexable.tagQualifiedId"));
3248 }
3249 
TEST_F(SchemaStoreTest,NestedNonIndexableFieldsAreDefined)3250 TEST_F(SchemaStoreTest, NestedNonIndexableFieldsAreDefined) {
3251   SchemaTypeConfigProto email_type =
3252       SchemaTypeConfigBuilder()
3253           .SetType("Email")
3254           .AddProperty(PropertyConfigBuilder()
3255                            .SetName("tagQualifiedId")
3256                            .SetDataType(TYPE_STRING)
3257                            .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID,
3258                                         DELETE_PROPAGATION_TYPE_PROPAGATE_FROM)
3259                            .SetCardinality(CARDINALITY_REQUIRED))
3260           .AddProperty(
3261               PropertyConfigBuilder()
3262                   .SetName("subject")
3263                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
3264                   .SetCardinality(CARDINALITY_REQUIRED))
3265           .AddProperty(
3266               PropertyConfigBuilder()
3267                   .SetName("text")
3268                   .SetDataTypeString(TERM_MATCH_UNKNOWN, TOKENIZER_NONE)
3269                   .SetCardinality(CARDINALITY_OPTIONAL))
3270           .AddProperty(PropertyConfigBuilder()
3271                            .SetName("timestamp")
3272                            .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
3273                            .SetCardinality(CARDINALITY_REQUIRED))
3274           .Build();
3275 
3276   SchemaTypeConfigProto conversation_type =
3277       SchemaTypeConfigBuilder()
3278           .SetType("Conversation")
3279           .AddProperty(PropertyConfigBuilder()
3280                            .SetName("emails")
3281                            .SetDataTypeDocument(
3282                                "Email", /*index_nested_properties=*/true)
3283                            .SetCardinality(CARDINALITY_OPTIONAL))
3284           .AddProperty(
3285               PropertyConfigBuilder()
3286                   .SetName("nestedNonIndexable")
3287                   .SetDataTypeDocument("Email",
3288                                        /*index_nested_properties=*/false)
3289                   .SetCardinality(CARDINALITY_OPTIONAL))
3290           .Build();
3291   SchemaProto schema =
3292       SchemaBuilder().AddType(email_type).AddType(conversation_type).Build();
3293   ICING_ASSERT_OK_AND_ASSIGN(
3294       std::unique_ptr<SchemaStore> schema_store,
3295       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3296                           feature_flags_.get()));
3297   ICING_ASSERT_OK(schema_store->SetSchema(
3298       schema, /*ignore_errors_and_delete_documents=*/false,
3299       /*allow_circular_schema_definitions=*/true));
3300   constexpr SchemaTypeId kTypeConversationSchemaId = 1;
3301 
3302   // Non-indexables.
3303   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeConversationSchemaId,
3304                                                       "emails.text"));
3305   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(
3306       kTypeConversationSchemaId, "nestedNonIndexable.subject"));
3307   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(
3308       kTypeConversationSchemaId, "nestedNonIndexable.text"));
3309   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(
3310       kTypeConversationSchemaId, "nestedNonIndexable.timestamp"));
3311 }
3312 
TEST_F(SchemaStoreTest,NestedNonExistentFieldsAreUndefined)3313 TEST_F(SchemaStoreTest, NestedNonExistentFieldsAreUndefined) {
3314   SchemaTypeConfigProto email_type =
3315       SchemaTypeConfigBuilder()
3316           .SetType("Email")
3317           .AddProperty(PropertyConfigBuilder()
3318                            .SetName("tagQualifiedId")
3319                            .SetDataType(TYPE_STRING)
3320                            .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID,
3321                                         DELETE_PROPAGATION_TYPE_PROPAGATE_FROM)
3322                            .SetCardinality(CARDINALITY_REQUIRED))
3323           .AddProperty(
3324               PropertyConfigBuilder()
3325                   .SetName("subject")
3326                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
3327                   .SetCardinality(CARDINALITY_REQUIRED))
3328           .AddProperty(
3329               PropertyConfigBuilder()
3330                   .SetName("text")
3331                   .SetDataTypeString(TERM_MATCH_UNKNOWN, TOKENIZER_NONE)
3332                   .SetCardinality(CARDINALITY_OPTIONAL))
3333           .AddProperty(PropertyConfigBuilder()
3334                            .SetName("timestamp")
3335                            .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
3336                            .SetCardinality(CARDINALITY_REQUIRED))
3337           .Build();
3338 
3339   SchemaTypeConfigProto conversation_type =
3340       SchemaTypeConfigBuilder()
3341           .SetType("Conversation")
3342           .AddProperty(PropertyConfigBuilder()
3343                            .SetName("emails")
3344                            .SetDataTypeDocument(
3345                                "Email", /*index_nested_properties=*/true)
3346                            .SetCardinality(CARDINALITY_OPTIONAL))
3347           .AddProperty(
3348               PropertyConfigBuilder()
3349                   .SetName("nestedNonIndexable")
3350                   .SetDataTypeDocument("Email",
3351                                        /*index_nested_properties=*/false)
3352                   .SetCardinality(CARDINALITY_OPTIONAL))
3353           .Build();
3354   SchemaProto schema =
3355       SchemaBuilder().AddType(email_type).AddType(conversation_type).Build();
3356   ICING_ASSERT_OK_AND_ASSIGN(
3357       std::unique_ptr<SchemaStore> schema_store,
3358       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3359                           feature_flags_.get()));
3360   ICING_ASSERT_OK(schema_store->SetSchema(
3361       schema, /*ignore_errors_and_delete_documents=*/false,
3362       /*allow_circular_schema_definitions=*/true));
3363   constexpr SchemaTypeId kTypeConversationSchemaId = 1;
3364 
3365   // Non-existents.
3366   EXPECT_FALSE(schema_store->IsPropertyDefinedInSchema(
3367       kTypeConversationSchemaId, "emails.foobar"));
3368   EXPECT_FALSE(schema_store->IsPropertyDefinedInSchema(
3369       kTypeConversationSchemaId, "nestedNonIndexable.foobar"));
3370   EXPECT_FALSE(schema_store->IsPropertyDefinedInSchema(
3371       kTypeConversationSchemaId, "emails.timestamp.foo"));
3372   EXPECT_FALSE(schema_store->IsPropertyDefinedInSchema(
3373       kTypeConversationSchemaId, "emails.time"));
3374 }
3375 
TEST_F(SchemaStoreTest,IntermediateDocumentPropertiesAreDefined)3376 TEST_F(SchemaStoreTest, IntermediateDocumentPropertiesAreDefined) {
3377   SchemaTypeConfigProto email_type =
3378       SchemaTypeConfigBuilder()
3379           .SetType("Email")
3380           .AddProperty(PropertyConfigBuilder()
3381                            .SetName("tagQualifiedId")
3382                            .SetDataType(TYPE_STRING)
3383                            .SetJoinable(JOINABLE_VALUE_TYPE_QUALIFIED_ID,
3384                                         DELETE_PROPAGATION_TYPE_PROPAGATE_FROM)
3385                            .SetCardinality(CARDINALITY_REQUIRED))
3386           .AddProperty(
3387               PropertyConfigBuilder()
3388                   .SetName("subject")
3389                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
3390                   .SetCardinality(CARDINALITY_REQUIRED))
3391           .AddProperty(
3392               PropertyConfigBuilder()
3393                   .SetName("text")
3394                   .SetDataTypeString(TERM_MATCH_UNKNOWN, TOKENIZER_NONE)
3395                   .SetCardinality(CARDINALITY_OPTIONAL))
3396           .AddProperty(PropertyConfigBuilder()
3397                            .SetName("timestamp")
3398                            .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
3399                            .SetCardinality(CARDINALITY_REQUIRED))
3400           .Build();
3401 
3402   SchemaTypeConfigProto conversation_type =
3403       SchemaTypeConfigBuilder()
3404           .SetType("Conversation")
3405           .AddProperty(PropertyConfigBuilder()
3406                            .SetName("emails")
3407                            .SetDataTypeDocument(
3408                                "Email", /*index_nested_properties=*/true)
3409                            .SetCardinality(CARDINALITY_OPTIONAL))
3410           .AddProperty(
3411               PropertyConfigBuilder()
3412                   .SetName("nestedNonIndexable")
3413                   .SetDataTypeDocument("Email",
3414                                        /*index_nested_properties=*/false)
3415                   .SetCardinality(CARDINALITY_OPTIONAL))
3416           .Build();
3417   SchemaProto schema =
3418       SchemaBuilder().AddType(email_type).AddType(conversation_type).Build();
3419   ICING_ASSERT_OK_AND_ASSIGN(
3420       std::unique_ptr<SchemaStore> schema_store,
3421       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3422                           feature_flags_.get()));
3423   ICING_ASSERT_OK(schema_store->SetSchema(
3424       schema, /*ignore_errors_and_delete_documents=*/false,
3425       /*allow_circular_schema_definitions=*/true));
3426   constexpr SchemaTypeId kTypeConversationSchemaId = 1;
3427 
3428   // Intermediate documents props.
3429   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeConversationSchemaId,
3430                                                       "emails"));
3431   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeConversationSchemaId,
3432                                                       "nestedNonIndexable"));
3433 }
3434 
TEST_F(SchemaStoreTest,CyclePathsAreDefined)3435 TEST_F(SchemaStoreTest, CyclePathsAreDefined) {
3436   SchemaTypeConfigProto type_a =
3437       SchemaTypeConfigBuilder()
3438           .SetType("A")
3439           .AddProperty(
3440               PropertyConfigBuilder()
3441                   .SetName("subject")
3442                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
3443                   .SetCardinality(CARDINALITY_REQUIRED))
3444           .AddProperty(
3445               PropertyConfigBuilder()
3446                   .SetName("b")
3447                   .SetDataTypeDocument("B", /*index_nested_properties=*/true)
3448                   .SetCardinality(CARDINALITY_OPTIONAL))
3449           .Build();
3450 
3451   SchemaTypeConfigProto type_b =
3452       SchemaTypeConfigBuilder()
3453           .SetType("B")
3454           .AddProperty(
3455               PropertyConfigBuilder()
3456                   .SetName("body")
3457                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
3458                   .SetCardinality(CARDINALITY_REQUIRED))
3459           .AddProperty(
3460               PropertyConfigBuilder()
3461                   .SetName("a")
3462                   .SetDataTypeDocument("A", /*index_nested_properties=*/false)
3463                   .SetCardinality(CARDINALITY_OPTIONAL))
3464           .Build();
3465   SchemaProto schema = SchemaBuilder().AddType(type_a).AddType(type_b).Build();
3466   ICING_ASSERT_OK_AND_ASSIGN(
3467       std::unique_ptr<SchemaStore> schema_store,
3468       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3469                           feature_flags_.get()));
3470   ICING_ASSERT_OK(schema_store->SetSchema(
3471       schema, /*ignore_errors_and_delete_documents=*/false,
3472       /*allow_circular_schema_definitions=*/true));
3473   constexpr SchemaTypeId kTypeASchemaId = 0;
3474   constexpr SchemaTypeId kTypeBSchemaId = 1;
3475 
3476   // A's top-level properties
3477   EXPECT_TRUE(
3478       schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "subject"));
3479   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "b"));
3480 
3481   // A's nested properties in B
3482   EXPECT_TRUE(
3483       schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "b.body"));
3484   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "b.a"));
3485 
3486   // A's nested properties in B's nested property in A
3487   EXPECT_TRUE(
3488       schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "b.a.subject"));
3489   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "b.a.b"));
3490 
3491   // B's top-level properties
3492   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "body"));
3493   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "a"));
3494 
3495   // B's nested properties in A
3496   EXPECT_TRUE(
3497       schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "a.subject"));
3498   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "a.b"));
3499 
3500   // B's nested properties in A's nested property in B
3501   EXPECT_TRUE(
3502       schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "a.b.body"));
3503   EXPECT_TRUE(schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "a.b.a"));
3504 }
3505 
TEST_F(SchemaStoreTest,WrongTypeCyclePathsAreUndefined)3506 TEST_F(SchemaStoreTest, WrongTypeCyclePathsAreUndefined) {
3507   SchemaTypeConfigProto type_a =
3508       SchemaTypeConfigBuilder()
3509           .SetType("A")
3510           .AddProperty(
3511               PropertyConfigBuilder()
3512                   .SetName("subject")
3513                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
3514                   .SetCardinality(CARDINALITY_REQUIRED))
3515           .AddProperty(
3516               PropertyConfigBuilder()
3517                   .SetName("b")
3518                   .SetDataTypeDocument("B", /*index_nested_properties=*/true)
3519                   .SetCardinality(CARDINALITY_OPTIONAL))
3520           .Build();
3521 
3522   SchemaTypeConfigProto type_b =
3523       SchemaTypeConfigBuilder()
3524           .SetType("B")
3525           .AddProperty(
3526               PropertyConfigBuilder()
3527                   .SetName("body")
3528                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
3529                   .SetCardinality(CARDINALITY_REQUIRED))
3530           .AddProperty(
3531               PropertyConfigBuilder()
3532                   .SetName("a")
3533                   .SetDataTypeDocument("A", /*index_nested_properties=*/false)
3534                   .SetCardinality(CARDINALITY_OPTIONAL))
3535           .Build();
3536   SchemaProto schema = SchemaBuilder().AddType(type_a).AddType(type_b).Build();
3537   ICING_ASSERT_OK_AND_ASSIGN(
3538       std::unique_ptr<SchemaStore> schema_store,
3539       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3540                           feature_flags_.get()));
3541   ICING_ASSERT_OK(schema_store->SetSchema(
3542       schema, /*ignore_errors_and_delete_documents=*/false,
3543       /*allow_circular_schema_definitions=*/true));
3544   constexpr SchemaTypeId kTypeASchemaId = 0;
3545   constexpr SchemaTypeId kTypeBSchemaId = 1;
3546 
3547   // The same paths as above, but we check the wrong types instead.
3548   // A's top-level properties
3549   EXPECT_FALSE(
3550       schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "subject"));
3551   EXPECT_FALSE(schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "b"));
3552 
3553   // A's nested properties in B
3554   EXPECT_FALSE(
3555       schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "b.body"));
3556   EXPECT_FALSE(schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "b.a"));
3557 
3558   // A's nested properties in B's nested property in A
3559   EXPECT_FALSE(
3560       schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "b.a.subject"));
3561   EXPECT_FALSE(
3562       schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "b.a.b"));
3563 
3564   // B's top-level properties
3565   EXPECT_FALSE(schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "body"));
3566   EXPECT_FALSE(schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "a"));
3567 
3568   // B's nested properties in A
3569   EXPECT_FALSE(
3570       schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "a.subject"));
3571   EXPECT_FALSE(schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "a.b"));
3572 
3573   // B's nested properties in A's nested property in B
3574   EXPECT_FALSE(
3575       schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "a.b.body"));
3576   EXPECT_FALSE(
3577       schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "a.b.a"));
3578 }
3579 
TEST_F(SchemaStoreTest,CyclePathsNonexistentPropertiesAreUndefined)3580 TEST_F(SchemaStoreTest, CyclePathsNonexistentPropertiesAreUndefined) {
3581   SchemaTypeConfigProto type_a =
3582       SchemaTypeConfigBuilder()
3583           .SetType("A")
3584           .AddProperty(
3585               PropertyConfigBuilder()
3586                   .SetName("subject")
3587                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
3588                   .SetCardinality(CARDINALITY_REQUIRED))
3589           .AddProperty(
3590               PropertyConfigBuilder()
3591                   .SetName("b")
3592                   .SetDataTypeDocument("B", /*index_nested_properties=*/true)
3593                   .SetCardinality(CARDINALITY_OPTIONAL))
3594           .Build();
3595 
3596   SchemaTypeConfigProto type_b =
3597       SchemaTypeConfigBuilder()
3598           .SetType("B")
3599           .AddProperty(
3600               PropertyConfigBuilder()
3601                   .SetName("body")
3602                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
3603                   .SetCardinality(CARDINALITY_REQUIRED))
3604           .AddProperty(
3605               PropertyConfigBuilder()
3606                   .SetName("a")
3607                   .SetDataTypeDocument("A", /*index_nested_properties=*/false)
3608                   .SetCardinality(CARDINALITY_OPTIONAL))
3609           .Build();
3610   SchemaProto schema = SchemaBuilder().AddType(type_a).AddType(type_b).Build();
3611   ICING_ASSERT_OK_AND_ASSIGN(
3612       std::unique_ptr<SchemaStore> schema_store,
3613       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3614                           feature_flags_.get()));
3615   ICING_ASSERT_OK(schema_store->SetSchema(
3616       schema, /*ignore_errors_and_delete_documents=*/false,
3617       /*allow_circular_schema_definitions=*/true));
3618   constexpr SchemaTypeId kTypeASchemaId = 0;
3619   constexpr SchemaTypeId kTypeBSchemaId = 1;
3620 
3621   // Undefined paths in A
3622   EXPECT_FALSE(
3623       schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "b.subject"));
3624   EXPECT_FALSE(
3625       schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "b.a.body"));
3626   EXPECT_FALSE(
3627       schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "b.a.a"));
3628   EXPECT_FALSE(
3629       schema_store->IsPropertyDefinedInSchema(kTypeASchemaId, "b.a.subject.b"));
3630 
3631   // Undefined paths in B
3632   EXPECT_FALSE(
3633       schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "a.body"));
3634   EXPECT_FALSE(
3635       schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "a.b.subject"));
3636   EXPECT_FALSE(
3637       schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "a.b.b"));
3638   EXPECT_FALSE(
3639       schema_store->IsPropertyDefinedInSchema(kTypeBSchemaId, "a.b.body.a"));
3640 }
3641 
TEST_F(SchemaStoreTest,LoadsOverlaySchemaOnInit)3642 TEST_F(SchemaStoreTest, LoadsOverlaySchemaOnInit) {
3643   // Create a schema that is rollback incompatible and will trigger us to create
3644   // an overlay schema.
3645   PropertyConfigBuilder indexed_string_property_builder =
3646       PropertyConfigBuilder()
3647           .SetCardinality(CARDINALITY_OPTIONAL)
3648           .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN);
3649   SchemaTypeConfigProto type_a =
3650       SchemaTypeConfigBuilder()
3651           .SetType("type_a")
3652           .AddProperty(indexed_string_property_builder.SetName("prop0"))
3653           .AddProperty(
3654               PropertyConfigBuilder()
3655                   .SetName("propRfc")
3656                   .SetCardinality(CARDINALITY_OPTIONAL)
3657                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_RFC822))
3658           .Build();
3659   SchemaTypeConfigProto type_b =
3660       SchemaTypeConfigBuilder()
3661           .SetType("type_b")
3662           .AddProperty(indexed_string_property_builder.SetName("prop0"))
3663           .Build();
3664   SchemaProto schema = SchemaBuilder().AddType(type_a).AddType(type_b).Build();
3665 
3666   {
3667     // Create an instance of the schema store and set the schema.
3668     ICING_ASSERT_OK_AND_ASSIGN(
3669         std::unique_ptr<SchemaStore> schema_store,
3670         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3671                             feature_flags_.get()));
3672     ICING_ASSERT_OK(schema_store->SetSchema(
3673         schema, /*ignore_errors_and_delete_documents=*/false,
3674         /*allow_circular_schema_definitions=*/false));
3675 
3676     EXPECT_THAT(schema_store->GetSchema(),
3677                 IsOkAndHolds(Pointee(EqualsProto(schema))));
3678   }
3679 
3680   {
3681     // Create a new of the schema store and check that the same schema is
3682     // present.
3683     ICING_ASSERT_OK_AND_ASSIGN(
3684         std::unique_ptr<SchemaStore> schema_store,
3685         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3686                             feature_flags_.get()));
3687     EXPECT_THAT(schema_store->GetSchema(),
3688                 IsOkAndHolds(Pointee(EqualsProto(schema))));
3689 
3690     // The overlay should exist
3691     std::string overlay_schema_path = schema_store_dir_ + "/overlay_schema.pb";
3692     ASSERT_TRUE(filesystem_.FileExists(overlay_schema_path.c_str()));
3693 
3694     // The base schema should hold a compatible schema
3695     SchemaTypeConfigProto modified_type_a =
3696         SchemaTypeConfigBuilder()
3697             .SetType("type_a")
3698             .AddProperty(indexed_string_property_builder.SetName("prop0"))
3699             .AddProperty(PropertyConfigBuilder()
3700                              .SetName("propRfc")
3701                              .SetCardinality(CARDINALITY_OPTIONAL)
3702                              .SetDataType(TYPE_STRING))
3703             .Build();
3704     SchemaProto expected_base_schema =
3705         SchemaBuilder().AddType(modified_type_a).AddType(type_b).Build();
3706     std::string base_schema_path = schema_store_dir_ + "/schema.pb";
3707     auto base_schema_file_ = std::make_unique<FileBackedProto<SchemaProto>>(
3708         filesystem_, base_schema_path);
3709     ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* base_schema,
3710                                base_schema_file_->Read());
3711     EXPECT_THAT(*base_schema, EqualsProto(expected_base_schema));
3712   }
3713 }
3714 
TEST_F(SchemaStoreTest,LoadsBaseSchemaWithNoOverlayOnInit)3715 TEST_F(SchemaStoreTest, LoadsBaseSchemaWithNoOverlayOnInit) {
3716   // Create a normal schema that won't require an overlay.
3717   PropertyConfigBuilder indexed_string_property_builder =
3718       PropertyConfigBuilder()
3719           .SetCardinality(CARDINALITY_OPTIONAL)
3720           .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN);
3721   SchemaTypeConfigProto type_a =
3722       SchemaTypeConfigBuilder()
3723           .SetType("type_a")
3724           .AddProperty(indexed_string_property_builder.SetName("prop0"))
3725           .AddProperty(
3726               PropertyConfigBuilder()
3727                   .SetName("propRfc")
3728                   .SetCardinality(CARDINALITY_OPTIONAL)
3729                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN))
3730           .Build();
3731   SchemaTypeConfigProto type_b =
3732       SchemaTypeConfigBuilder()
3733           .SetType("type_b")
3734           .AddProperty(indexed_string_property_builder.SetName("prop0"))
3735           .Build();
3736   SchemaProto schema = SchemaBuilder().AddType(type_a).AddType(type_b).Build();
3737 
3738   {
3739     // Create an instance of the schema store and set the schema.
3740     ICING_ASSERT_OK_AND_ASSIGN(
3741         std::unique_ptr<SchemaStore> schema_store,
3742         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3743                             feature_flags_.get()));
3744     ICING_ASSERT_OK(schema_store->SetSchema(
3745         schema, /*ignore_errors_and_delete_documents=*/false,
3746         /*allow_circular_schema_definitions=*/false));
3747 
3748     EXPECT_THAT(schema_store->GetSchema(),
3749                 IsOkAndHolds(Pointee(EqualsProto(schema))));
3750   }
3751 
3752   {
3753     // Create a new instance of the schema store and check that the same schema
3754     // is present.
3755     ICING_ASSERT_OK_AND_ASSIGN(
3756         std::unique_ptr<SchemaStore> schema_store,
3757         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3758                             feature_flags_.get()));
3759     EXPECT_THAT(schema_store->GetSchema(),
3760                 IsOkAndHolds(Pointee(EqualsProto(schema))));
3761 
3762     // Additionally, the overlay should not exist
3763     std::string overlay_schema_path = schema_store_dir_ + "/overlay_schema.pb";
3764     ASSERT_FALSE(filesystem_.FileExists(overlay_schema_path.c_str()));
3765   }
3766 }
3767 
TEST_F(SchemaStoreTest,LoadSchemaBackupSchemaMissing)3768 TEST_F(SchemaStoreTest, LoadSchemaBackupSchemaMissing) {
3769   // Create a schema that is rollback incompatible and will trigger us to create
3770   // a backup schema.
3771   PropertyConfigBuilder indexed_string_property_builder =
3772       PropertyConfigBuilder()
3773           .SetCardinality(CARDINALITY_OPTIONAL)
3774           .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN);
3775   SchemaTypeConfigProto type_a =
3776       SchemaTypeConfigBuilder()
3777           .SetType("type_a")
3778           .AddProperty(indexed_string_property_builder.SetName("prop0"))
3779           .AddProperty(
3780               PropertyConfigBuilder()
3781                   .SetName("propRfc")
3782                   .SetCardinality(CARDINALITY_OPTIONAL)
3783                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_RFC822))
3784           .Build();
3785   SchemaTypeConfigProto type_b =
3786       SchemaTypeConfigBuilder()
3787           .SetType("type_b")
3788           .AddProperty(indexed_string_property_builder.SetName("prop0"))
3789           .Build();
3790   SchemaProto schema = SchemaBuilder().AddType(type_a).AddType(type_b).Build();
3791 
3792   {
3793     // Create an instance of the schema store and set the schema.
3794     ICING_ASSERT_OK_AND_ASSIGN(
3795         std::unique_ptr<SchemaStore> schema_store,
3796         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3797                             feature_flags_.get()));
3798     ICING_ASSERT_OK(schema_store->SetSchema(
3799         schema, /*ignore_errors_and_delete_documents=*/false,
3800         /*allow_circular_schema_definitions=*/false));
3801 
3802     EXPECT_THAT(schema_store->GetSchema(),
3803                 IsOkAndHolds(Pointee(EqualsProto(schema))));
3804   }
3805 
3806   // Delete the backup schema.
3807   std::string backup_schema_path = schema_store_dir_ + "/schema.pb";
3808   ASSERT_TRUE(filesystem_.DeleteFile(backup_schema_path.c_str()));
3809 
3810   {
3811     // Create a new instance of the schema store and check that it fails because
3812     // the backup schema is not available.
3813     EXPECT_THAT(SchemaStore::Create(&filesystem_, schema_store_dir_,
3814                                     &fake_clock_, feature_flags_.get()),
3815                 StatusIs(libtextclassifier3::StatusCode::INTERNAL));
3816   }
3817 }
3818 
TEST_F(SchemaStoreTest,LoadSchemaOverlaySchemaMissing)3819 TEST_F(SchemaStoreTest, LoadSchemaOverlaySchemaMissing) {
3820   // Create a schema that is rollback incompatible and will trigger us to create
3821   // a backup schema.
3822   PropertyConfigBuilder indexed_string_property_builder =
3823       PropertyConfigBuilder()
3824           .SetCardinality(CARDINALITY_OPTIONAL)
3825           .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN);
3826   SchemaTypeConfigProto type_a =
3827       SchemaTypeConfigBuilder()
3828           .SetType("type_a")
3829           .AddProperty(indexed_string_property_builder.SetName("prop0"))
3830           .AddProperty(
3831               PropertyConfigBuilder()
3832                   .SetName("propRfc")
3833                   .SetCardinality(CARDINALITY_OPTIONAL)
3834                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_RFC822))
3835           .Build();
3836   SchemaTypeConfigProto type_b =
3837       SchemaTypeConfigBuilder()
3838           .SetType("type_b")
3839           .AddProperty(indexed_string_property_builder.SetName("prop0"))
3840           .Build();
3841   SchemaProto schema = SchemaBuilder().AddType(type_a).AddType(type_b).Build();
3842 
3843   {
3844     // Create an instance of the schema store and set the schema.
3845     ICING_ASSERT_OK_AND_ASSIGN(
3846         std::unique_ptr<SchemaStore> schema_store,
3847         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3848                             feature_flags_.get()));
3849     ICING_ASSERT_OK(schema_store->SetSchema(
3850         schema, /*ignore_errors_and_delete_documents=*/false,
3851         /*allow_circular_schema_definitions=*/false));
3852 
3853     EXPECT_THAT(schema_store->GetSchema(),
3854                 IsOkAndHolds(Pointee(EqualsProto(schema))));
3855   }
3856 
3857   // Delete the overlay schema.
3858   std::string overlay_schema_path = schema_store_dir_ + "/overlay_schema.pb";
3859   ASSERT_TRUE(filesystem_.DeleteFile(overlay_schema_path.c_str()));
3860 
3861   {
3862     // Create a new instance of the schema store and check that it fails because
3863     // the overlay schema is not available when we expected it to be.
3864     EXPECT_THAT(SchemaStore::Create(&filesystem_, schema_store_dir_,
3865                                     &fake_clock_, feature_flags_.get()),
3866                 StatusIs(libtextclassifier3::StatusCode::INTERNAL));
3867   }
3868 }
3869 
TEST_F(SchemaStoreTest,LoadSchemaHeaderMissing)3870 TEST_F(SchemaStoreTest, LoadSchemaHeaderMissing) {
3871   // Create a schema that is rollback incompatible and will trigger us to create
3872   // a backup schema.
3873   PropertyConfigBuilder indexed_string_property_builder =
3874       PropertyConfigBuilder()
3875           .SetCardinality(CARDINALITY_OPTIONAL)
3876           .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN);
3877   SchemaTypeConfigProto type_a =
3878       SchemaTypeConfigBuilder()
3879           .SetType("type_a")
3880           .AddProperty(indexed_string_property_builder.SetName("prop0"))
3881           .AddProperty(
3882               PropertyConfigBuilder()
3883                   .SetName("propRfc")
3884                   .SetCardinality(CARDINALITY_OPTIONAL)
3885                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_RFC822))
3886           .Build();
3887   SchemaTypeConfigProto type_b =
3888       SchemaTypeConfigBuilder()
3889           .SetType("type_b")
3890           .AddProperty(indexed_string_property_builder.SetName("prop0"))
3891           .Build();
3892   SchemaProto schema = SchemaBuilder().AddType(type_a).AddType(type_b).Build();
3893 
3894   {
3895     // Create an instance of the schema store and set the schema.
3896     ICING_ASSERT_OK_AND_ASSIGN(
3897         std::unique_ptr<SchemaStore> schema_store,
3898         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3899                             feature_flags_.get()));
3900     ICING_ASSERT_OK(schema_store->SetSchema(
3901         schema, /*ignore_errors_and_delete_documents=*/false,
3902         /*allow_circular_schema_definitions=*/false));
3903 
3904     EXPECT_THAT(schema_store->GetSchema(),
3905                 IsOkAndHolds(Pointee(EqualsProto(schema))));
3906   }
3907 
3908   // Delete the overlay schema.
3909   std::string schema_header_path = schema_store_dir_ + "/schema_store_header";
3910   ASSERT_TRUE(filesystem_.DeleteFile(schema_header_path.c_str()));
3911 
3912   {
3913     // Create a new of the schema store and check that the same schema is
3914     // present.
3915     EXPECT_THAT(SchemaStore::Create(&filesystem_, schema_store_dir_,
3916                                     &fake_clock_, feature_flags_.get()),
3917                 StatusIs(libtextclassifier3::StatusCode::INTERNAL));
3918   }
3919 }
3920 
TEST_F(SchemaStoreTest,LoadSchemaNoOverlayHeaderMissing)3921 TEST_F(SchemaStoreTest, LoadSchemaNoOverlayHeaderMissing) {
3922   // Create a normal schema that won't require a backup.
3923   PropertyConfigBuilder indexed_string_property_builder =
3924       PropertyConfigBuilder()
3925           .SetCardinality(CARDINALITY_OPTIONAL)
3926           .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN);
3927   SchemaTypeConfigProto type_a =
3928       SchemaTypeConfigBuilder()
3929           .SetType("type_a")
3930           .AddProperty(indexed_string_property_builder.SetName("prop0"))
3931           .AddProperty(
3932               PropertyConfigBuilder()
3933                   .SetName("propRfc")
3934                   .SetCardinality(CARDINALITY_OPTIONAL)
3935                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN))
3936           .Build();
3937   SchemaTypeConfigProto type_b =
3938       SchemaTypeConfigBuilder()
3939           .SetType("type_b")
3940           .AddProperty(indexed_string_property_builder.SetName("prop0"))
3941           .Build();
3942   SchemaProto schema = SchemaBuilder().AddType(type_a).AddType(type_b).Build();
3943 
3944   {
3945     // Create an instance of the schema store and set the schema.
3946     ICING_ASSERT_OK_AND_ASSIGN(
3947         std::unique_ptr<SchemaStore> schema_store,
3948         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3949                             feature_flags_.get()));
3950     ICING_ASSERT_OK(schema_store->SetSchema(
3951         schema, /*ignore_errors_and_delete_documents=*/false,
3952         /*allow_circular_schema_definitions=*/false));
3953 
3954     EXPECT_THAT(schema_store->GetSchema(),
3955                 IsOkAndHolds(Pointee(EqualsProto(schema))));
3956   }
3957 
3958   // Delete the schema header.
3959   std::string schema_header_path = schema_store_dir_ + "/schema_store_header";
3960   ASSERT_TRUE(filesystem_.DeleteFile(schema_header_path.c_str()));
3961 
3962   {
3963     // Create a new instance of the schema store and check that it fails because
3964     // the schema header (which is now a part of the ground truth) is not
3965     // available.
3966     EXPECT_THAT(SchemaStore::Create(&filesystem_, schema_store_dir_,
3967                                     &fake_clock_, feature_flags_.get()),
3968                 StatusIs(libtextclassifier3::StatusCode::INTERNAL));
3969   }
3970 }
3971 
TEST_F(SchemaStoreTest,MigrateSchemaCompatibleNoChange)3972 TEST_F(SchemaStoreTest, MigrateSchemaCompatibleNoChange) {
3973   // Create a schema that is rollback incompatible and will trigger us to create
3974   // a backup schema.
3975   SchemaTypeConfigProto type_a =
3976       SchemaTypeConfigBuilder()
3977           .SetType("type_a")
3978           .AddProperty(
3979               PropertyConfigBuilder()
3980                   .SetName("propRfc")
3981                   .SetCardinality(CARDINALITY_OPTIONAL)
3982                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_RFC822))
3983           .Build();
3984   SchemaProto schema = SchemaBuilder().AddType(type_a).Build();
3985 
3986   {
3987     // Create an instance of the schema store and set the schema.
3988     ICING_ASSERT_OK_AND_ASSIGN(
3989         std::unique_ptr<SchemaStore> schema_store,
3990         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
3991                             feature_flags_.get()));
3992     ICING_ASSERT_OK(schema_store->SetSchema(
3993         schema, /*ignore_errors_and_delete_documents=*/false,
3994         /*allow_circular_schema_definitions=*/false));
3995 
3996     EXPECT_THAT(schema_store->GetSchema(),
3997                 IsOkAndHolds(Pointee(EqualsProto(schema))));
3998   }
3999 
4000   ICING_EXPECT_OK(SchemaStore::MigrateSchema(
4001       &filesystem_, schema_store_dir_, version_util::StateChange::kCompatible,
4002       version_util::kVersion, /*perform_schema_database_migration=*/false));
4003 
4004   {
4005     // Create a new of the schema store and check that the same schema is
4006     // present.
4007     ICING_ASSERT_OK_AND_ASSIGN(
4008         std::unique_ptr<SchemaStore> schema_store,
4009         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4010                             feature_flags_.get()));
4011     EXPECT_THAT(schema_store->GetSchema(),
4012                 IsOkAndHolds(Pointee(EqualsProto(schema))));
4013   }
4014 }
4015 
TEST_F(SchemaStoreTest,MigrateSchemaUpgradeNoChange)4016 TEST_F(SchemaStoreTest, MigrateSchemaUpgradeNoChange) {
4017   // Create a schema that is rollback incompatible and will trigger us to create
4018   // a backup schema.
4019   SchemaTypeConfigProto type_a =
4020       SchemaTypeConfigBuilder()
4021           .SetType("type_a")
4022           .AddProperty(
4023               PropertyConfigBuilder()
4024                   .SetName("propRfc")
4025                   .SetCardinality(CARDINALITY_OPTIONAL)
4026                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_RFC822))
4027           .Build();
4028   SchemaProto schema = SchemaBuilder().AddType(type_a).Build();
4029 
4030   {
4031     // Create an instance of the schema store and set the schema.
4032     ICING_ASSERT_OK_AND_ASSIGN(
4033         std::unique_ptr<SchemaStore> schema_store,
4034         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4035                             feature_flags_.get()));
4036     ICING_ASSERT_OK(schema_store->SetSchema(
4037         schema, /*ignore_errors_and_delete_documents=*/false,
4038         /*allow_circular_schema_definitions=*/false));
4039 
4040     EXPECT_THAT(schema_store->GetSchema(),
4041                 IsOkAndHolds(Pointee(EqualsProto(schema))));
4042   }
4043 
4044   ICING_EXPECT_OK(SchemaStore::MigrateSchema(
4045       &filesystem_, schema_store_dir_, version_util::StateChange::kUpgrade,
4046       version_util::kVersion + 1, /*perform_schema_database_migration=*/false));
4047 
4048   {
4049     // Create a new of the schema store and check that the same schema is
4050     // present.
4051     ICING_ASSERT_OK_AND_ASSIGN(
4052         std::unique_ptr<SchemaStore> schema_store,
4053         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4054                             feature_flags_.get()));
4055     EXPECT_THAT(schema_store->GetSchema(),
4056                 IsOkAndHolds(Pointee(EqualsProto(schema))));
4057   }
4058 }
4059 
TEST_F(SchemaStoreTest,MigrateSchemaVersionZeroUpgradeNoChange)4060 TEST_F(SchemaStoreTest, MigrateSchemaVersionZeroUpgradeNoChange) {
4061   // Because we are upgrading from version zero, the schema must be compatible
4062   // with version zero.
4063   SchemaTypeConfigProto type_a =
4064       SchemaTypeConfigBuilder()
4065           .SetType("type_a")
4066           .AddProperty(
4067               PropertyConfigBuilder()
4068                   .SetName("propRfc")
4069                   .SetCardinality(CARDINALITY_OPTIONAL)
4070                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN))
4071           .Build();
4072   SchemaProto schema = SchemaBuilder().AddType(type_a).Build();
4073 
4074   {
4075     // Create an instance of the schema store and set the schema.
4076     ICING_ASSERT_OK_AND_ASSIGN(
4077         std::unique_ptr<SchemaStore> schema_store,
4078         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4079                             feature_flags_.get()));
4080     ICING_ASSERT_OK(schema_store->SetSchema(
4081         schema, /*ignore_errors_and_delete_documents=*/false,
4082         /*allow_circular_schema_definitions=*/false));
4083 
4084     EXPECT_THAT(schema_store->GetSchema(),
4085                 IsOkAndHolds(Pointee(EqualsProto(schema))));
4086   }
4087 
4088   ICING_EXPECT_OK(SchemaStore::MigrateSchema(
4089       &filesystem_, schema_store_dir_,
4090       version_util::StateChange::kVersionZeroUpgrade,
4091       version_util::kVersion + 1, /*perform_schema_database_migration=*/false));
4092 
4093   {
4094     // Create a new of the schema store and check that the same schema is
4095     // present.
4096     ICING_ASSERT_OK_AND_ASSIGN(
4097         std::unique_ptr<SchemaStore> schema_store,
4098         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4099                             feature_flags_.get()));
4100     EXPECT_THAT(schema_store->GetSchema(),
4101                 IsOkAndHolds(Pointee(EqualsProto(schema))));
4102   }
4103 }
4104 
TEST_F(SchemaStoreTest,MigrateSchemaRollbackDiscardsIncompatibleOverlaySchema)4105 TEST_F(SchemaStoreTest,
4106        MigrateSchemaRollbackDiscardsIncompatibleOverlaySchema) {
4107   // Because we are upgrading from version zero, the schema must be compatible
4108   // with version zero.
4109   SchemaTypeConfigProto type_a =
4110       SchemaTypeConfigBuilder()
4111           .SetType("type_a")
4112           .AddProperty(
4113               PropertyConfigBuilder()
4114                   .SetName("propRfc")
4115                   .SetCardinality(CARDINALITY_OPTIONAL)
4116                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_RFC822))
4117           .Build();
4118   SchemaProto schema = SchemaBuilder().AddType(type_a).Build();
4119 
4120   {
4121     // Create an instance of the schema store and set the schema.
4122     ICING_ASSERT_OK_AND_ASSIGN(
4123         std::unique_ptr<SchemaStore> schema_store,
4124         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4125                             feature_flags_.get()));
4126     ICING_ASSERT_OK(schema_store->SetSchema(
4127         schema, /*ignore_errors_and_delete_documents=*/false,
4128         /*allow_circular_schema_definitions=*/false));
4129 
4130     EXPECT_THAT(schema_store->GetSchema(),
4131                 IsOkAndHolds(Pointee(EqualsProto(schema))));
4132   }
4133 
4134   // Rollback to a version before kVersionOne. The schema header will declare
4135   // that the overlay is compatible with any version starting with kVersionOne.
4136   // So kVersionOne - 1 is incompatible and will throw out the schema.
4137   ICING_EXPECT_OK(SchemaStore::MigrateSchema(
4138       &filesystem_, schema_store_dir_, version_util::StateChange::kRollBack,
4139       version_util::kVersionOne - 1,
4140       /*perform_schema_database_migration=*/false));
4141 
4142   {
4143     // Create a new of the schema store and check that we fell back to the
4144     // base schema.
4145     ICING_ASSERT_OK_AND_ASSIGN(
4146         std::unique_ptr<SchemaStore> schema_store,
4147         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4148                             feature_flags_.get()));
4149 
4150     SchemaTypeConfigProto other_type_a =
4151         SchemaTypeConfigBuilder()
4152             .SetType("type_a")
4153             .AddProperty(PropertyConfigBuilder()
4154                              .SetName("propRfc")
4155                              .SetCardinality(CARDINALITY_OPTIONAL)
4156                              .SetDataType(TYPE_STRING))
4157             .Build();
4158     SchemaProto base_schema = SchemaBuilder().AddType(other_type_a).Build();
4159     EXPECT_THAT(schema_store->GetSchema(),
4160                 IsOkAndHolds(Pointee(EqualsProto(base_schema))));
4161   }
4162 }
4163 
TEST_F(SchemaStoreTest,MigrateSchemaRollbackKeepsCompatibleOverlaySchema)4164 TEST_F(SchemaStoreTest, MigrateSchemaRollbackKeepsCompatibleOverlaySchema) {
4165   // Because we are upgrading from version zero, the schema must be compatible
4166   // with version zero.
4167   SchemaTypeConfigProto type_a =
4168       SchemaTypeConfigBuilder()
4169           .SetType("type_a")
4170           .AddProperty(
4171               PropertyConfigBuilder()
4172                   .SetName("propRfc")
4173                   .SetCardinality(CARDINALITY_OPTIONAL)
4174                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_RFC822))
4175           .Build();
4176   SchemaProto schema = SchemaBuilder().AddType(type_a).Build();
4177 
4178   {
4179     // Create an instance of the schema store and set the schema.
4180     ICING_ASSERT_OK_AND_ASSIGN(
4181         std::unique_ptr<SchemaStore> schema_store,
4182         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4183                             feature_flags_.get()));
4184     ICING_ASSERT_OK(schema_store->SetSchema(
4185         schema, /*ignore_errors_and_delete_documents=*/false,
4186         /*allow_circular_schema_definitions=*/false));
4187 
4188     EXPECT_THAT(schema_store->GetSchema(),
4189                 IsOkAndHolds(Pointee(EqualsProto(schema))));
4190   }
4191 
4192   // Rollback to kVersion. The schema header will declare that the overlay is
4193   // compatible with any version starting with kVersion. So we will be
4194   // compatible and retain the overlay schema.
4195   ICING_EXPECT_OK(SchemaStore::MigrateSchema(
4196       &filesystem_, schema_store_dir_, version_util::StateChange::kRollBack,
4197       version_util::kVersion, /*perform_schema_database_migration=*/false));
4198 
4199   {
4200     // Create a new of the schema store and check that the same schema is
4201     // present.
4202     ICING_ASSERT_OK_AND_ASSIGN(
4203         std::unique_ptr<SchemaStore> schema_store,
4204         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4205                             feature_flags_.get()));
4206     EXPECT_THAT(schema_store->GetSchema(),
4207                 IsOkAndHolds(Pointee(EqualsProto(schema))));
4208   }
4209 }
4210 
TEST_F(SchemaStoreTest,MigrateSchemaRollforwardRetainsBaseSchema)4211 TEST_F(SchemaStoreTest, MigrateSchemaRollforwardRetainsBaseSchema) {
4212   SchemaTypeConfigProto type_a =
4213       SchemaTypeConfigBuilder()
4214           .SetType("type_a")
4215           .AddProperty(
4216               PropertyConfigBuilder()
4217                   .SetName("propRfc")
4218                   .SetCardinality(CARDINALITY_OPTIONAL)
4219                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_RFC822))
4220           .Build();
4221   SchemaProto schema = SchemaBuilder().AddType(type_a).Build();
4222   {
4223     // Create an instance of the schema store and set the schema.
4224     ICING_ASSERT_OK_AND_ASSIGN(
4225         std::unique_ptr<SchemaStore> schema_store,
4226         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4227                             feature_flags_.get()));
4228     ICING_ASSERT_OK(schema_store->SetSchema(
4229         schema, /*ignore_errors_and_delete_documents=*/false,
4230         /*allow_circular_schema_definitions=*/false));
4231 
4232     EXPECT_THAT(schema_store->GetSchema(),
4233                 IsOkAndHolds(Pointee(EqualsProto(schema))));
4234   }
4235 
4236   // Rollback to a version before kVersionOne. The schema header will declare
4237   // that the overlay is compatible with any version starting with kVersionOne.
4238   // So kVersionOne - 1 is incompatible and will throw out the schema.
4239   ICING_EXPECT_OK(SchemaStore::MigrateSchema(
4240       &filesystem_, schema_store_dir_, version_util::StateChange::kRollBack,
4241       version_util::kVersionOne - 1,
4242       /*perform_schema_database_migration=*/false));
4243 
4244   SchemaTypeConfigProto other_type_a =
4245       SchemaTypeConfigBuilder()
4246           .SetType("type_a")
4247           .AddProperty(PropertyConfigBuilder()
4248                            .SetName("propRfc")
4249                            .SetCardinality(CARDINALITY_OPTIONAL)
4250                            .SetDataType(TYPE_STRING))
4251           .Build();
4252   SchemaProto base_schema = SchemaBuilder().AddType(other_type_a).Build();
4253 
4254   {
4255     // Create a new of the schema store and check that we fell back to the
4256     // base schema.
4257     ICING_ASSERT_OK_AND_ASSIGN(
4258         std::unique_ptr<SchemaStore> schema_store,
4259         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4260                             feature_flags_.get()));
4261 
4262     EXPECT_THAT(schema_store->GetSchema(),
4263                 IsOkAndHolds(Pointee(EqualsProto(base_schema))));
4264   }
4265 
4266   // Now rollforward to a new version. This should accept whatever schema is
4267   // present (currently base schema)
4268   ICING_EXPECT_OK(SchemaStore::MigrateSchema(
4269       &filesystem_, schema_store_dir_, version_util::StateChange::kRollForward,
4270       version_util::kVersion, /*perform_schema_database_migration=*/false));
4271   {
4272     // Create a new of the schema store and check that we fell back to the
4273     // base schema.
4274     ICING_ASSERT_OK_AND_ASSIGN(
4275         std::unique_ptr<SchemaStore> schema_store,
4276         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4277                             feature_flags_.get()));
4278 
4279     EXPECT_THAT(schema_store->GetSchema(),
4280                 IsOkAndHolds(Pointee(EqualsProto(base_schema))));
4281   }
4282 }
4283 
TEST_F(SchemaStoreTest,MigrateSchemaRollforwardRetainsOverlaySchema)4284 TEST_F(SchemaStoreTest, MigrateSchemaRollforwardRetainsOverlaySchema) {
4285   SchemaTypeConfigProto type_a =
4286       SchemaTypeConfigBuilder()
4287           .SetType("type_a")
4288           .AddProperty(
4289               PropertyConfigBuilder()
4290                   .SetName("propRfc")
4291                   .SetCardinality(CARDINALITY_OPTIONAL)
4292                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_RFC822))
4293           .Build();
4294   SchemaProto schema = SchemaBuilder().AddType(type_a).Build();
4295   {
4296     // Create an instance of the schema store and set the schema.
4297     ICING_ASSERT_OK_AND_ASSIGN(
4298         std::unique_ptr<SchemaStore> schema_store,
4299         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4300                             feature_flags_.get()));
4301     ICING_ASSERT_OK(schema_store->SetSchema(
4302         schema, /*ignore_errors_and_delete_documents=*/false,
4303         /*allow_circular_schema_definitions=*/false));
4304 
4305     EXPECT_THAT(schema_store->GetSchema(),
4306                 IsOkAndHolds(Pointee(EqualsProto(schema))));
4307   }
4308 
4309   // Rollback to kVersion. The schema header will declare that the overlay is
4310   // compatible with any version starting with kVersion. So we will be
4311   // compatible and retain the overlay schema.
4312   ICING_EXPECT_OK(SchemaStore::MigrateSchema(
4313       &filesystem_, schema_store_dir_, version_util::StateChange::kRollBack,
4314       version_util::kVersion, /*perform_schema_database_migration=*/false));
4315 
4316   {
4317     // Create a new of the schema store and check that the same schema is
4318     // present.
4319     ICING_ASSERT_OK_AND_ASSIGN(
4320         std::unique_ptr<SchemaStore> schema_store,
4321         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4322                             feature_flags_.get()));
4323 
4324     EXPECT_THAT(schema_store->GetSchema(),
4325                 IsOkAndHolds(Pointee(EqualsProto(schema))));
4326   }
4327 
4328   // Now rollforward to a new version. This should accept whatever schema is
4329   // present (currently overlay schema)
4330   ICING_EXPECT_OK(SchemaStore::MigrateSchema(
4331       &filesystem_, schema_store_dir_, version_util::StateChange::kRollForward,
4332       version_util::kVersion, /*perform_schema_database_migration=*/false));
4333   {
4334     // Create a new of the schema store and check that the same schema is
4335     // present.
4336     ICING_ASSERT_OK_AND_ASSIGN(
4337         std::unique_ptr<SchemaStore> schema_store,
4338         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4339                             feature_flags_.get()));
4340 
4341     EXPECT_THAT(schema_store->GetSchema(),
4342                 IsOkAndHolds(Pointee(EqualsProto(schema))));
4343   }
4344 }
4345 
TEST_F(SchemaStoreTest,MigrateSchemaVersionZeroRollforwardDiscardsOverlaySchema)4346 TEST_F(SchemaStoreTest,
4347        MigrateSchemaVersionZeroRollforwardDiscardsOverlaySchema) {
4348   SchemaTypeConfigProto type_a =
4349       SchemaTypeConfigBuilder()
4350           .SetType("type_a")
4351           .AddProperty(
4352               PropertyConfigBuilder()
4353                   .SetName("propRfc")
4354                   .SetCardinality(CARDINALITY_OPTIONAL)
4355                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_RFC822))
4356           .Build();
4357   SchemaProto schema = SchemaBuilder().AddType(type_a).Build();
4358   {
4359     // Create an instance of the schema store and set the schema.
4360     ICING_ASSERT_OK_AND_ASSIGN(
4361         std::unique_ptr<SchemaStore> schema_store,
4362         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4363                             feature_flags_.get()));
4364     ICING_ASSERT_OK(schema_store->SetSchema(
4365         schema, /*ignore_errors_and_delete_documents=*/false,
4366         /*allow_circular_schema_definitions=*/false));
4367 
4368     EXPECT_THAT(schema_store->GetSchema(),
4369                 IsOkAndHolds(Pointee(EqualsProto(schema))));
4370   }
4371 
4372   // A VersionZeroRollforward will always discard the overlay schema because it
4373   // could be stale.
4374   ICING_EXPECT_OK(SchemaStore::MigrateSchema(
4375       &filesystem_, schema_store_dir_,
4376       version_util::StateChange::kVersionZeroRollForward,
4377       version_util::kVersion, /*perform_schema_database_migration=*/false));
4378 
4379   SchemaTypeConfigProto other_type_a =
4380       SchemaTypeConfigBuilder()
4381           .SetType("type_a")
4382           .AddProperty(PropertyConfigBuilder()
4383                            .SetName("propRfc")
4384                            .SetCardinality(CARDINALITY_OPTIONAL)
4385                            .SetDataType(TYPE_STRING))
4386           .Build();
4387   SchemaProto base_schema = SchemaBuilder().AddType(other_type_a).Build();
4388 
4389   {
4390     // Create a new of the schema store and check that we fell back to the
4391     // base schema.
4392     ICING_ASSERT_OK_AND_ASSIGN(
4393         std::unique_ptr<SchemaStore> schema_store,
4394         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4395                             feature_flags_.get()));
4396 
4397     EXPECT_THAT(schema_store->GetSchema(),
4398                 IsOkAndHolds(Pointee(EqualsProto(base_schema))));
4399   }
4400 }
4401 
TEST_F(SchemaStoreTest,MigrateSchemaVersionUndeterminedDiscardsOverlaySchema)4402 TEST_F(SchemaStoreTest, MigrateSchemaVersionUndeterminedDiscardsOverlaySchema) {
4403   SchemaTypeConfigProto type_a =
4404       SchemaTypeConfigBuilder()
4405           .SetType("type_a")
4406           .AddProperty(
4407               PropertyConfigBuilder()
4408                   .SetName("propRfc")
4409                   .SetCardinality(CARDINALITY_OPTIONAL)
4410                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_RFC822))
4411           .Build();
4412   SchemaProto schema = SchemaBuilder().AddType(type_a).Build();
4413   {
4414     // Create an instance of the schema store and set the schema.
4415     ICING_ASSERT_OK_AND_ASSIGN(
4416         std::unique_ptr<SchemaStore> schema_store,
4417         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4418                             feature_flags_.get()));
4419     ICING_ASSERT_OK(schema_store->SetSchema(
4420         schema, /*ignore_errors_and_delete_documents=*/false,
4421         /*allow_circular_schema_definitions=*/false));
4422 
4423     EXPECT_THAT(schema_store->GetSchema(),
4424                 IsOkAndHolds(Pointee(EqualsProto(schema))));
4425   }
4426 
4427   // An Undetermined will always discard the overlay schema because it doesn't
4428   // know which state we're in and so it fallback to the base schema because
4429   // it should always be valid.
4430   ICING_EXPECT_OK(SchemaStore::MigrateSchema(
4431       &filesystem_, schema_store_dir_, version_util::StateChange::kUndetermined,
4432       version_util::kVersion, /*perform_schema_database_migration=*/false));
4433 
4434   SchemaTypeConfigProto other_type_a =
4435       SchemaTypeConfigBuilder()
4436           .SetType("type_a")
4437           .AddProperty(PropertyConfigBuilder()
4438                            .SetName("propRfc")
4439                            .SetCardinality(CARDINALITY_OPTIONAL)
4440                            .SetDataType(TYPE_STRING))
4441           .Build();
4442   SchemaProto base_schema = SchemaBuilder().AddType(other_type_a).Build();
4443 
4444   {
4445     // Create a new of the schema store and check that we fell back to the
4446     // base schema.
4447     ICING_ASSERT_OK_AND_ASSIGN(
4448         std::unique_ptr<SchemaStore> schema_store,
4449         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4450                             feature_flags_.get()));
4451 
4452     EXPECT_THAT(schema_store->GetSchema(),
4453                 IsOkAndHolds(Pointee(EqualsProto(base_schema))));
4454   }
4455 }
4456 
TEST_F(SchemaStoreTest,GetTypeWithBlobProperties)4457 TEST_F(SchemaStoreTest, GetTypeWithBlobProperties) {
4458   SchemaTypeConfigProto type_a =
4459       SchemaTypeConfigBuilder()
4460           .SetType("A")
4461           .AddProperty(PropertyConfigBuilder()
4462                            .SetName("blob")
4463                            .SetDataType(TYPE_BLOB_HANDLE)
4464                            .SetCardinality(CARDINALITY_OPTIONAL))
4465           .AddProperty(
4466               PropertyConfigBuilder()
4467                   .SetName("nonBlob")
4468                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
4469                   .SetCardinality(CARDINALITY_OPTIONAL))
4470           .Build();
4471 
4472   SchemaTypeConfigProto type_b =
4473       SchemaTypeConfigBuilder()
4474           .SetType("B")
4475           .AddProperty(
4476               PropertyConfigBuilder()
4477                   .SetName("typeA")
4478                   .SetDataTypeDocument("A", /*index_nested_properties=*/false)
4479                   .SetCardinality(CARDINALITY_OPTIONAL))
4480           .AddProperty(
4481               PropertyConfigBuilder()
4482                   .SetName("nonBlob")
4483                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
4484                   .SetCardinality(CARDINALITY_OPTIONAL))
4485           .Build();
4486 
4487   SchemaTypeConfigProto type_c =
4488       SchemaTypeConfigBuilder()
4489           .SetType("C")
4490           .AddProperty(
4491               PropertyConfigBuilder()
4492                   .SetName("nonBlob")
4493                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
4494                   .SetCardinality(CARDINALITY_OPTIONAL))
4495           .Build();
4496 
4497   // type_a contains blob property.
4498   // type_b contains nested type_a, which contains blob property.
4499   // type_c contains no blob property.
4500   SchemaProto schema =
4501       SchemaBuilder().AddType(type_a).AddType(type_b).AddType(type_c).Build();
4502 
4503   {
4504     // Create an instance of the schema store and set the schema.
4505     ICING_ASSERT_OK_AND_ASSIGN(
4506         std::unique_ptr<SchemaStore> schema_store,
4507         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4508                             feature_flags_.get()));
4509     ICING_ASSERT_OK(schema_store->SetSchema(
4510         schema, /*ignore_errors_and_delete_documents=*/false,
4511         /*allow_circular_schema_definitions=*/false));
4512 
4513     EXPECT_THAT(schema_store->ConstructBlobPropertyMap(),
4514                 IsOkAndHolds(UnorderedElementsAre(
4515                     Pair("A", UnorderedElementsAre("blob")),
4516                     Pair("B", UnorderedElementsAre("typeA.blob")))));
4517   }
4518 }
4519 
TEST_F(SchemaStoreTest,GetTypeWithMultiLevelBlobProperties)4520 TEST_F(SchemaStoreTest, GetTypeWithMultiLevelBlobProperties) {
4521   SchemaTypeConfigProto type_a =
4522       SchemaTypeConfigBuilder()
4523           .SetType("A")
4524           .AddProperty(PropertyConfigBuilder()
4525                            .SetName("blob")
4526                            .SetDataType(TYPE_BLOB_HANDLE)
4527                            .SetCardinality(CARDINALITY_OPTIONAL))
4528           .AddProperty(
4529               PropertyConfigBuilder()
4530                   .SetName("nonBlob")
4531                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
4532                   .SetCardinality(CARDINALITY_OPTIONAL))
4533           .Build();
4534 
4535   SchemaTypeConfigProto type_b =
4536       SchemaTypeConfigBuilder()
4537           .SetType("B")
4538           .AddProperty(
4539               PropertyConfigBuilder()
4540                   .SetName("typeA")
4541                   .SetDataTypeDocument("A", /*index_nested_properties=*/false)
4542                   .SetCardinality(CARDINALITY_OPTIONAL))
4543           .AddProperty(
4544               PropertyConfigBuilder()
4545                   .SetName("nonBlob")
4546                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
4547                   .SetCardinality(CARDINALITY_OPTIONAL))
4548           .Build();
4549 
4550   SchemaTypeConfigProto type_c =
4551       SchemaTypeConfigBuilder()
4552           .SetType("C")
4553           .AddProperty(
4554               PropertyConfigBuilder()
4555                   .SetName("typeB")
4556                   .SetDataTypeDocument("B", /*index_nested_properties=*/false)
4557                   .SetCardinality(CARDINALITY_OPTIONAL))
4558           .AddProperty(PropertyConfigBuilder()
4559                            .SetName("blob")
4560                            .SetDataType(TYPE_BLOB_HANDLE)
4561                            .SetCardinality(CARDINALITY_OPTIONAL))
4562           .AddProperty(
4563               PropertyConfigBuilder()
4564                   .SetName("nonBlob")
4565                   .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
4566                   .SetCardinality(CARDINALITY_OPTIONAL))
4567           .Build();
4568 
4569   // type_a contains blob property.
4570   // type_b contains nested type_a, which contains blob property.
4571   // type_c contains blob property and nested type_b, which contains blob
4572   // property.
4573   SchemaProto schema =
4574       SchemaBuilder().AddType(type_a).AddType(type_b).AddType(type_c).Build();
4575   {
4576     // Create an instance of the schema store and set the schema.
4577     ICING_ASSERT_OK_AND_ASSIGN(
4578         std::unique_ptr<SchemaStore> schema_store,
4579         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4580                             feature_flags_.get()));
4581     ICING_ASSERT_OK(schema_store->SetSchema(
4582         schema, /*ignore_errors_and_delete_documents=*/false,
4583         /*allow_circular_schema_definitions=*/false));
4584 
4585     EXPECT_THAT(
4586         schema_store->ConstructBlobPropertyMap(),
4587         IsOkAndHolds(UnorderedElementsAre(
4588             Pair("A", UnorderedElementsAre("blob")),
4589             Pair("B", UnorderedElementsAre("typeA.blob")),
4590             Pair("C", UnorderedElementsAre("blob", "typeB.typeA.blob")))));
4591   }
4592 }
4593 
TEST_F(SchemaStoreTest,GetScorablePropertyIndex_SchemaNotSet)4594 TEST_F(SchemaStoreTest, GetScorablePropertyIndex_SchemaNotSet) {
4595   ICING_ASSERT_OK_AND_ASSIGN(
4596       std::unique_ptr<SchemaStore> schema_store,
4597       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4598                           feature_flags_.get()));
4599 
4600   EXPECT_THAT(schema_store->GetScorablePropertyIndex(
4601                   /*schema_type_id=*/0,
4602                   /*property_path=*/"timestamp"),
4603               StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
4604 }
4605 
TEST_F(SchemaStoreTest,GetScorablePropertyIndex_InvalidSchemaTypeId)4606 TEST_F(SchemaStoreTest, GetScorablePropertyIndex_InvalidSchemaTypeId) {
4607   ICING_ASSERT_OK_AND_ASSIGN(
4608       std::unique_ptr<SchemaStore> schema_store,
4609       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4610                           feature_flags_.get()));
4611 
4612   // Set schema
4613   ICING_ASSERT_OK(schema_store->SetSchema(
4614       schema_, /*ignore_errors_and_delete_documents=*/false,
4615       /*allow_circular_schema_definitions=*/false));
4616 
4617   // non-existing schema type id
4618   EXPECT_THAT(schema_store->GetScorablePropertyIndex(
4619                   /*schema_type_id=*/100,
4620                   /*property_path=*/"timestamp"),
4621               StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT));
4622 }
4623 
TEST_F(SchemaStoreTest,GetScorablePropertyIndex_InvalidPropertyName)4624 TEST_F(SchemaStoreTest, GetScorablePropertyIndex_InvalidPropertyName) {
4625   ICING_ASSERT_OK_AND_ASSIGN(
4626       std::unique_ptr<SchemaStore> schema_store,
4627       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4628                           feature_flags_.get()));
4629 
4630   SchemaProto schema =
4631       SchemaBuilder()
4632           .AddType(
4633               SchemaTypeConfigBuilder()
4634                   .SetType("email")
4635                   .AddProperty(
4636                       PropertyConfigBuilder()
4637                           .SetName("subject")
4638                           .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
4639                           .SetCardinality(CARDINALITY_OPTIONAL))
4640                   .AddProperty(
4641                       PropertyConfigBuilder()
4642                           .SetName("scoreDouble")
4643                           .SetDataType(PropertyConfigProto::DataType::DOUBLE)
4644                           .SetCardinality(CARDINALITY_OPTIONAL)
4645                           .SetScorableType(SCORABLE_TYPE_ENABLED))
4646                   .AddProperty(PropertyConfigBuilder()
4647                                    .SetName("timestamp")
4648                                    .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
4649                                    .SetCardinality(CARDINALITY_OPTIONAL)
4650                                    .SetScorableType(SCORABLE_TYPE_ENABLED)))
4651           .Build();
4652 
4653   // Set schema
4654   ICING_ASSERT_OK(schema_store->SetSchema(
4655       schema, /*ignore_errors_and_delete_documents=*/false,
4656       /*allow_circular_schema_definitions=*/false));
4657 
4658   // non-scorable property
4659   EXPECT_THAT(schema_store->GetScorablePropertyIndex(
4660                   /*schema_type_id=*/0,
4661                   /*property_path=*/"subject"),
4662               IsOkAndHolds(Eq(std::nullopt)));
4663   // non-existing property
4664   EXPECT_THAT(schema_store->GetScorablePropertyIndex(
4665                   /*schema_type_id=*/0,
4666                   /*property_path=*/"non_existing"),
4667               IsOkAndHolds(Eq(std::nullopt)));
4668 }
4669 
TEST_F(SchemaStoreTest,GetScorablePropertyIndex_Ok)4670 TEST_F(SchemaStoreTest, GetScorablePropertyIndex_Ok) {
4671   ICING_ASSERT_OK_AND_ASSIGN(
4672       std::unique_ptr<SchemaStore> schema_store,
4673       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4674                           feature_flags_.get()));
4675 
4676   // Set schema
4677   ICING_ASSERT_OK(schema_store->SetSchema(
4678       schema_, /*ignore_errors_and_delete_documents=*/false,
4679       /*allow_circular_schema_definitions=*/false));
4680 
4681   EXPECT_THAT(schema_store->GetScorablePropertyIndex(
4682                   /*schema_type_id=*/0,
4683                   /*property_path=*/"timestamp"),
4684               IsOkAndHolds(0));
4685 }
4686 
TEST_F(SchemaStoreTest,GetOrderedScorablePropertyPaths_SchemaNotSet)4687 TEST_F(SchemaStoreTest, GetOrderedScorablePropertyPaths_SchemaNotSet) {
4688   ICING_ASSERT_OK_AND_ASSIGN(
4689       std::unique_ptr<SchemaStore> schema_store,
4690       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4691                           feature_flags_.get()));
4692 
4693   EXPECT_THAT(schema_store->GetOrderedScorablePropertyInfo(
4694                   /*schema_type_id=*/0),
4695               StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
4696 }
4697 
TEST_F(SchemaStoreTest,GetOrderedScorablePropertyPaths_InvalidSchemaTypeId)4698 TEST_F(SchemaStoreTest, GetOrderedScorablePropertyPaths_InvalidSchemaTypeId) {
4699   ICING_ASSERT_OK_AND_ASSIGN(
4700       std::unique_ptr<SchemaStore> schema_store,
4701       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4702                           feature_flags_.get()));
4703 
4704   // Set schema
4705   ICING_ASSERT_OK(schema_store->SetSchema(
4706       schema_, /*ignore_errors_and_delete_documents=*/false,
4707       /*allow_circular_schema_definitions=*/false));
4708 
4709   EXPECT_THAT(schema_store->GetOrderedScorablePropertyInfo(
4710                   /*schema_type_id=*/100),
4711               StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT));
4712 }
4713 
TEST_F(SchemaStoreTest,GetOrderedScorablePropertyPaths_Ok)4714 TEST_F(SchemaStoreTest, GetOrderedScorablePropertyPaths_Ok) {
4715   ICING_ASSERT_OK_AND_ASSIGN(
4716       std::unique_ptr<SchemaStore> schema_store,
4717       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4718                           feature_flags_.get()));
4719 
4720   SchemaProto schema =
4721       SchemaBuilder()
4722           .AddType(
4723               SchemaTypeConfigBuilder()
4724                   .SetType("email")
4725                   .AddProperty(
4726                       PropertyConfigBuilder()
4727                           .SetName("subject")
4728                           .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
4729                           .SetCardinality(CARDINALITY_OPTIONAL))
4730                   .AddProperty(
4731                       PropertyConfigBuilder()
4732                           .SetName("scoreDouble")
4733                           .SetDataType(PropertyConfigProto::DataType::DOUBLE)
4734                           .SetCardinality(CARDINALITY_OPTIONAL)
4735                           .SetScorableType(SCORABLE_TYPE_ENABLED))
4736                   .AddProperty(PropertyConfigBuilder()
4737                                    .SetName("timestamp")
4738                                    .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
4739                                    .SetCardinality(CARDINALITY_OPTIONAL)
4740                                    .SetScorableType(SCORABLE_TYPE_ENABLED)))
4741           .AddType(SchemaTypeConfigBuilder().SetType("message").AddProperty(
4742               PropertyConfigBuilder()
4743                   .SetName("subject")
4744                   .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
4745                   .SetCardinality(CARDINALITY_OPTIONAL)))
4746           .Build();
4747 
4748   // Set schema
4749   ICING_ASSERT_OK(schema_store->SetSchema(
4750       schema, /*ignore_errors_and_delete_documents=*/false,
4751       /*allow_circular_schema_definitions=*/false));
4752   EXPECT_THAT(schema_store->GetSchemaTypeId("email"), IsOkAndHolds(0));
4753   EXPECT_THAT(schema_store->GetSchemaTypeId("message"), IsOkAndHolds(1));
4754 
4755   // no scorable properties under the schema, 'message'.
4756   EXPECT_THAT(schema_store->GetOrderedScorablePropertyInfo(
4757                   /*schema_type_id=*/1),
4758               IsOkAndHolds(Pointee(ElementsAre())));
4759 
4760   EXPECT_THAT(schema_store->GetOrderedScorablePropertyInfo(
4761                   /*schema_type_id=*/0),
4762               IsOkAndHolds(Pointee(ElementsAre(
4763                   EqualsScorablePropertyInfo("scoreDouble", TYPE_DOUBLE),
4764                   EqualsScorablePropertyInfo("timestamp", TYPE_INT64)))));
4765 }
4766 
TEST_F(SchemaStoreTest,ScorablePropertyManagerUpdatesUponSchemaChange)4767 TEST_F(SchemaStoreTest, ScorablePropertyManagerUpdatesUponSchemaChange) {
4768   ICING_ASSERT_OK_AND_ASSIGN(
4769       std::unique_ptr<SchemaStore> schema_store,
4770       SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4771                           feature_flags_.get()));
4772 
4773   // Sets the initial schema
4774   ICING_ASSERT_OK(schema_store->SetSchema(
4775       schema_, /*ignore_errors_and_delete_documents=*/false,
4776       /*allow_circular_schema_definitions=*/false));
4777 
4778   EXPECT_THAT(schema_store->GetScorablePropertyIndex(
4779                   /*schema_type_id=*/0,
4780                   /*property_path=*/"timestamp"),
4781               IsOkAndHolds(0));
4782   EXPECT_THAT(schema_store->GetOrderedScorablePropertyInfo(
4783                   /*schema_type_id=*/0),
4784               IsOkAndHolds(Pointee(ElementsAre(
4785                   EqualsScorablePropertyInfo("timestamp", TYPE_INT64)))));
4786 
4787   // The new schema drops the type 'email', and adds a new type 'message'.
4788   SchemaProto new_schema =
4789       SchemaBuilder()
4790           .AddType(
4791               SchemaTypeConfigBuilder()
4792                   .SetType("message")
4793                   .AddProperty(
4794                       // Add an indexed property so we generate
4795                       // section metadata on it
4796                       PropertyConfigBuilder()
4797                           .SetName("content")
4798                           .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
4799                           .SetCardinality(CARDINALITY_OPTIONAL))
4800                   .AddProperty(PropertyConfigBuilder()
4801                                    .SetName("scoreInt")
4802                                    .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
4803                                    .SetCardinality(CARDINALITY_OPTIONAL)
4804                                    .SetScorableType(SCORABLE_TYPE_ENABLED))
4805                   .AddProperty(
4806                       PropertyConfigBuilder()
4807                           .SetName("scoreDouble")
4808                           .SetDataType(PropertyConfigProto::DataType::DOUBLE)
4809                           .SetCardinality(CARDINALITY_OPTIONAL)
4810                           .SetScorableType(SCORABLE_TYPE_ENABLED)))
4811           .Build();
4812 
4813   // Force updates the schema.
4814   ICING_ASSERT_OK(schema_store->SetSchema(
4815       new_schema, /*ignore_errors_and_delete_documents=*/true,
4816       /*allow_circular_schema_definitions=*/false));
4817 
4818   // "timestamp" is no longer a valid property name.
4819   EXPECT_THAT(schema_store->GetScorablePropertyIndex(
4820                   /*schema_type_id=*/0,
4821                   /*property_path=*/"timestamp"),
4822               IsOkAndHolds(Eq(std::nullopt)));
4823 
4824   // ok cases for the new schema.
4825   EXPECT_THAT(schema_store->GetScorablePropertyIndex(
4826                   /*schema_type_id=*/0,
4827                   /*property_path=*/"scoreInt"),
4828               IsOkAndHolds(1));
4829   EXPECT_THAT(schema_store->GetScorablePropertyIndex(
4830                   /*schema_type_id=*/0,
4831                   /*property_path=*/"scoreDouble"),
4832               IsOkAndHolds(0));
4833   EXPECT_THAT(schema_store->GetOrderedScorablePropertyInfo(
4834                   /*schema_type_id=*/0),
4835               IsOkAndHolds(Pointee(ElementsAre(
4836                   EqualsScorablePropertyInfo("scoreDouble", TYPE_DOUBLE),
4837                   EqualsScorablePropertyInfo("scoreInt", TYPE_INT64)))));
4838 }
4839 
4840 class SchemaStoreTestWithParam
4841     : public SchemaStoreTest,
4842       public testing::WithParamInterface<version_util::StateChange> {};
4843 
TEST_P(SchemaStoreTestWithParam,MigrateSchemaWithDatabaseMigration)4844 TEST_P(SchemaStoreTestWithParam, MigrateSchemaWithDatabaseMigration) {
4845   SchemaProto schema_no_database =
4846       SchemaBuilder()
4847           .AddType(SchemaTypeConfigBuilder()
4848                        .SetType("db1/email")
4849                        .AddProperty(PropertyConfigBuilder()
4850                                         .SetName("db1Subject")
4851                                         .SetDataTypeString(TERM_MATCH_EXACT,
4852                                                            TOKENIZER_RFC822)
4853                                         .SetCardinality(CARDINALITY_OPTIONAL)))
4854           .AddType(SchemaTypeConfigBuilder()
4855                        .SetType("db2/email")
4856                        .AddProperty(PropertyConfigBuilder()
4857                                         .SetName("db2Subject")
4858                                         .SetDataTypeString(TERM_MATCH_EXACT,
4859                                                            TOKENIZER_PLAIN)
4860                                         .SetCardinality(CARDINALITY_OPTIONAL)))
4861           .Build();
4862 
4863   {
4864     // Create an instance of the schema store and set the schema.
4865     ICING_ASSERT_OK_AND_ASSIGN(
4866         std::unique_ptr<SchemaStore> schema_store,
4867         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4868                             feature_flags_.get()));
4869     ICING_ASSERT_OK(schema_store->SetSchema(
4870         schema_no_database, /*ignore_errors_and_delete_documents=*/false,
4871         /*allow_circular_schema_definitions=*/false));
4872 
4873     EXPECT_THAT(schema_store->GetSchema(),
4874                 IsOkAndHolds(Pointee(EqualsProto(schema_no_database))));
4875   }
4876 
4877   SchemaTypeConfigProto db1_email_rfc =
4878       SchemaTypeConfigBuilder()
4879           .SetType("db1/email")
4880           .SetDatabase("db1")
4881           .AddProperty(
4882               PropertyConfigBuilder()
4883                   .SetName("db1Subject")
4884                   .SetCardinality(CARDINALITY_OPTIONAL)
4885                   .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_RFC822))
4886           .Build();
4887   SchemaTypeConfigProto db1_email_no_rfc =
4888       SchemaTypeConfigBuilder()
4889           .SetType("db1/email")
4890           .SetDatabase("db1")
4891           .AddProperty(PropertyConfigBuilder()
4892                            .SetName("db1Subject")
4893                            .SetCardinality(CARDINALITY_OPTIONAL)
4894                            .SetDataType(TYPE_STRING))
4895           .Build();
4896   SchemaTypeConfigProto db2_email =
4897       SchemaTypeConfigBuilder()
4898           .SetType("db2/email")
4899           .SetDatabase("db2")
4900           .AddProperty(PropertyConfigBuilder()
4901                            .SetName("db2Subject")
4902                            .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
4903                            .SetCardinality(CARDINALITY_OPTIONAL))
4904           .Build();
4905   SchemaProto full_schema_with_database_rfc =
4906       SchemaBuilder().AddType(db1_email_rfc).AddType(db2_email).Build();
4907   SchemaProto full_schema_with_database_no_rfc =
4908       SchemaBuilder().AddType(db1_email_no_rfc).AddType(db2_email).Build();
4909   SchemaProto db1_schema_rfc = SchemaBuilder().AddType(db1_email_rfc).Build();
4910   SchemaProto db1_schema_no_rfc =
4911       SchemaBuilder().AddType(db1_email_no_rfc).Build();
4912   SchemaProto db2_schema = SchemaBuilder().AddType(db2_email).Build();
4913 
4914   ICING_EXPECT_OK(SchemaStore::MigrateSchema(
4915       &filesystem_, schema_store_dir_, /*version_state_change=*/GetParam(),
4916       version_util::kVersion, /*perform_schema_database_migration=*/true));
4917   {
4918     // Create a new instance of the schema store and check that the database
4919     // field is populated.
4920     ICING_ASSERT_OK_AND_ASSIGN(
4921         std::unique_ptr<SchemaStore> schema_store,
4922         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
4923                             feature_flags_.get()));
4924 
4925     if (GetParam() == version_util::StateChange::kVersionZeroRollForward ||
4926         GetParam() == version_util::StateChange::kUndetermined) {
4927       // For these cases, the overlay schema is discarded and we fall back to
4928       // the backup schema (no rfc version).
4929       EXPECT_THAT(
4930           schema_store->GetSchema(),
4931           IsOkAndHolds(Pointee(EqualsProto(full_schema_with_database_no_rfc))));
4932       EXPECT_THAT(schema_store->GetSchema("db1"),
4933                   IsOkAndHolds(EqualsProto(db1_schema_no_rfc)));
4934       EXPECT_THAT(schema_store->GetSchema("db2"),
4935                   IsOkAndHolds(EqualsProto(db2_schema)));
4936     } else {
4937       EXPECT_THAT(
4938           schema_store->GetSchema(),
4939           IsOkAndHolds(Pointee(EqualsProto(full_schema_with_database_rfc))));
4940       EXPECT_THAT(schema_store->GetSchema("db1"),
4941                   IsOkAndHolds(EqualsProto(db1_schema_rfc)));
4942       EXPECT_THAT(schema_store->GetSchema("db2"),
4943                   IsOkAndHolds(EqualsProto(db2_schema)));
4944 
4945       DocumentProto db1_email_doc =
4946           DocumentBuilder()
4947               .SetKey("namespace", "uri1")
4948               .SetSchema("db1/email")
4949               .AddStringProperty("db1Subject", "db1_subject")
4950               .Build();
4951       DocumentProto db2_email_doc =
4952           DocumentBuilder()
4953               .SetKey("namespace", "uri3")
4954               .SetSchema("db2/email")
4955               .AddStringProperty("db2Subject", "db2_subject")
4956               .Build();
4957 
4958       // Verify that our in-memory structures are ok
4959       ICING_ASSERT_OK_AND_ASSIGN(SectionGroup section_group,
4960                                  schema_store->ExtractSections(db1_email_doc));
4961       EXPECT_THAT(section_group.string_sections[0].content,
4962                   ElementsAre("db1_subject"));
4963       ICING_ASSERT_OK_AND_ASSIGN(section_group,
4964                                  schema_store->ExtractSections(db2_email_doc));
4965       EXPECT_THAT(section_group.string_sections[0].content,
4966                   ElementsAre("db2_subject"));
4967 
4968       // Verify that our persisted data are ok
4969       EXPECT_THAT(schema_store->GetSchemaTypeId("db1/email"), IsOkAndHolds(0));
4970       EXPECT_THAT(schema_store->GetSchemaTypeId("db2/email"), IsOkAndHolds(1));
4971     }
4972   }
4973 }
4974 
TEST_P(SchemaStoreTestWithParam,MigrateSchemaWithDatabaseMigration_noDbPrefix)4975 TEST_P(SchemaStoreTestWithParam,
4976        MigrateSchemaWithDatabaseMigration_noDbPrefix) {
4977   SchemaTypeConfigProto email_rfc =
4978       SchemaTypeConfigBuilder()
4979           .SetType("email")
4980           .AddProperty(
4981               PropertyConfigBuilder()
4982                   .SetName("subject")
4983                   .SetCardinality(CARDINALITY_OPTIONAL)
4984                   .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_RFC822))
4985           .Build();
4986   SchemaTypeConfigProto email_no_rfc =
4987       SchemaTypeConfigBuilder()
4988           .SetType("email")
4989           .AddProperty(PropertyConfigBuilder()
4990                            .SetName("subject")
4991                            .SetCardinality(CARDINALITY_OPTIONAL)
4992                            .SetDataType(TYPE_STRING))
4993           .Build();
4994   SchemaTypeConfigProto message =
4995       SchemaTypeConfigBuilder()
4996           .SetType("message")
4997           .AddProperty(PropertyConfigBuilder()
4998                            .SetName("content")
4999                            .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
5000                            .SetCardinality(CARDINALITY_OPTIONAL))
5001           .Build();
5002 
5003   SchemaProto original_schema =
5004       SchemaBuilder().AddType(email_rfc).AddType(message).Build();
5005 
5006   {
5007     // Create an instance of the schema store and set the schema.
5008     ICING_ASSERT_OK_AND_ASSIGN(
5009         std::unique_ptr<SchemaStore> schema_store,
5010         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
5011                             feature_flags_.get()));
5012     ICING_ASSERT_OK(schema_store->SetSchema(
5013         original_schema, /*ignore_errors_and_delete_documents=*/false,
5014         /*allow_circular_schema_definitions=*/false));
5015 
5016     EXPECT_THAT(schema_store->GetSchema(),
5017                 IsOkAndHolds(Pointee(EqualsProto(original_schema))));
5018   }
5019 
5020   SchemaProto backup_schema =
5021       SchemaBuilder().AddType(email_no_rfc).AddType(message).Build();
5022 
5023   ICING_EXPECT_OK(SchemaStore::MigrateSchema(
5024       &filesystem_, schema_store_dir_, /*version_state_change=*/GetParam(),
5025       version_util::kVersion, /*perform_schema_database_migration=*/true));
5026 
5027   {
5028     // Create a new instance of the schema store and check that the database
5029     // field is populated.
5030     ICING_ASSERT_OK_AND_ASSIGN(
5031         std::unique_ptr<SchemaStore> schema_store,
5032         SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_,
5033                             feature_flags_.get()));
5034 
5035     if (GetParam() == version_util::StateChange::kVersionZeroRollForward ||
5036         GetParam() == version_util::StateChange::kUndetermined) {
5037       // For these cases, the overlay schema is discarded and we fall back to
5038       // the backup schema (no rfc version).
5039       EXPECT_THAT(schema_store->GetSchema(),
5040                   IsOkAndHolds(Pointee(EqualsProto(backup_schema))));
5041       EXPECT_THAT(schema_store->GetSchema("db"),
5042                   StatusIs(libtextclassifier3::StatusCode::NOT_FOUND));
5043     } else {
5044       EXPECT_THAT(schema_store->GetSchema(),
5045                   IsOkAndHolds(Pointee(EqualsProto(original_schema))));
5046       EXPECT_THAT(schema_store->GetSchema("db"),
5047                   StatusIs(libtextclassifier3::StatusCode::NOT_FOUND));
5048 
5049       DocumentProto email_doc = DocumentBuilder()
5050                                     .SetKey("namespace", "uri1")
5051                                     .SetSchema("email")
5052                                     .AddStringProperty("subject", "subject")
5053                                     .Build();
5054       DocumentProto message_doc = DocumentBuilder()
5055                                       .SetKey("namespace", "uri2")
5056                                       .SetSchema("message")
5057                                       .AddStringProperty("content", "content")
5058                                       .Build();
5059 
5060       // Verify that our in-memory structures are ok
5061       ICING_ASSERT_OK_AND_ASSIGN(SectionGroup section_group,
5062                                  schema_store->ExtractSections(email_doc));
5063       EXPECT_THAT(section_group.string_sections[0].content,
5064                   ElementsAre("subject"));
5065       ICING_ASSERT_OK_AND_ASSIGN(section_group,
5066                                  schema_store->ExtractSections(message_doc));
5067       EXPECT_THAT(section_group.string_sections[0].content,
5068                   ElementsAre("content"));
5069 
5070       // Verify that our persisted data are ok
5071       EXPECT_THAT(schema_store->GetSchemaTypeId("email"), IsOkAndHolds(0));
5072       EXPECT_THAT(schema_store->GetSchemaTypeId("message"), IsOkAndHolds(1));
5073     }
5074   }
5075 }
5076 
5077 INSTANTIATE_TEST_SUITE_P(
5078     SchemaStoreTestWithParam, SchemaStoreTestWithParam,
5079     testing::Values(
5080         /*version_state_change=*/
5081         version_util::StateChange::kUndetermined,
5082         version_util::StateChange::kCompatible,
5083         version_util::StateChange::kRollForward,
5084         version_util::StateChange::kRollBack,
5085         version_util::StateChange::kUpgrade,
5086         version_util::StateChange::kVersionZeroUpgrade,
5087         version_util::StateChange::kVersionZeroRollForward));
5088 
5089 }  // namespace
5090 
5091 }  // namespace lib
5092 }  // namespace icing
5093