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