blob: f36ed4bf97b8146e9414c303ea99936bb877254f [file] [log] [blame]
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.appsearch.external.localstorage;
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.expectThrows;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument;
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SearchSpec;
import android.app.appsearch.exceptions.AppSearchException;
import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter;
import com.android.server.appsearch.proto.DocumentProto;
import com.android.server.appsearch.proto.GetOptimizeInfoResultProto;
import com.android.server.appsearch.proto.PropertyConfigProto;
import com.android.server.appsearch.proto.PropertyProto;
import com.android.server.appsearch.proto.SchemaProto;
import com.android.server.appsearch.proto.SchemaTypeConfigProto;
import com.android.server.appsearch.proto.SearchSpecProto;
import com.android.server.appsearch.proto.StringIndexingConfig;
import com.android.server.appsearch.proto.TermMatchType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class AppSearchImplTest {
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
private AppSearchImpl mAppSearchImpl;
private SchemaTypeConfigProto mVisibilitySchemaProto;
@Before
public void setUp() throws Exception {
mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder());
AppSearchSchema visibilitySchema = VisibilityStore.SCHEMA;
// We need to rewrite the schema type to follow AppSearchImpl's prefixing scheme.
AppSearchSchema.Builder rewrittenVisibilitySchema =
new AppSearchSchema.Builder(
AppSearchImpl.createPrefix(
VisibilityStore.PACKAGE_NAME, VisibilityStore.DATABASE_NAME)
+ VisibilityStore.SCHEMA_TYPE);
List<AppSearchSchema.PropertyConfig> visibilityProperties =
visibilitySchema.getProperties();
for (AppSearchSchema.PropertyConfig property : visibilityProperties) {
rewrittenVisibilitySchema.addProperty(property);
}
mVisibilitySchemaProto =
SchemaToProtoConverter.toSchemaTypeConfigProto(rewrittenVisibilitySchema.build());
}
/**
* Ensure that we can rewrite an incoming schema type by adding the database as a prefix. While
* also keeping any other existing schema types that may already be part of Icing's persisted
* schema.
*/
@Test
public void testRewriteSchema_addType() throws Exception {
SchemaProto.Builder existingSchemaBuilder =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$existingDatabase/Foo")
.build());
// Create a copy so we can modify it.
List<SchemaTypeConfigProto> existingTypes =
new ArrayList<>(existingSchemaBuilder.getTypesList());
SchemaProto newSchema =
SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build())
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("TestType")
.addProperties(
PropertyConfigProto.newBuilder()
.setPropertyName("subject")
.setDataType(
PropertyConfigProto.DataType.Code
.STRING)
.setCardinality(
PropertyConfigProto.Cardinality.Code
.OPTIONAL)
.setStringIndexingConfig(
StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig
.TokenizerType
.Code.PLAIN)
.setTermMatchType(
TermMatchType.Code
.PREFIX)
.build())
.build())
.addProperties(
PropertyConfigProto.newBuilder()
.setPropertyName("link")
.setDataType(
PropertyConfigProto.DataType.Code
.DOCUMENT)
.setCardinality(
PropertyConfigProto.Cardinality.Code
.OPTIONAL)
.setSchemaType("RefType")
.build())
.build())
.build();
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
mAppSearchImpl.rewriteSchema(
AppSearchImpl.createPrefix("package", "newDatabase"),
existingSchemaBuilder,
newSchema);
// We rewrote all the new types that were added. And nothing was removed.
assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes)
.containsExactly("package$newDatabase/Foo", "package$newDatabase/TestType");
assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty();
SchemaProto expectedSchema =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$newDatabase/Foo")
.build())
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$newDatabase/TestType")
.addProperties(
PropertyConfigProto.newBuilder()
.setPropertyName("subject")
.setDataType(
PropertyConfigProto.DataType.Code
.STRING)
.setCardinality(
PropertyConfigProto.Cardinality.Code
.OPTIONAL)
.setStringIndexingConfig(
StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig
.TokenizerType
.Code.PLAIN)
.setTermMatchType(
TermMatchType.Code
.PREFIX)
.build())
.build())
.addProperties(
PropertyConfigProto.newBuilder()
.setPropertyName("link")
.setDataType(
PropertyConfigProto.DataType.Code
.DOCUMENT)
.setCardinality(
PropertyConfigProto.Cardinality.Code
.OPTIONAL)
.setSchemaType(
"package$newDatabase/RefType")
.build())
.build())
.build();
existingTypes.addAll(expectedSchema.getTypesList());
assertThat(existingSchemaBuilder.getTypesList()).containsExactlyElementsIn(existingTypes);
}
/**
* Ensure that we track all types that were rewritten in the input schema. Even if they were not
* technically "added" to the existing schema.
*/
@Test
public void testRewriteSchema_rewriteType() throws Exception {
SchemaProto.Builder existingSchemaBuilder =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$existingDatabase/Foo")
.build());
SchemaProto newSchema =
SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build())
.build();
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
mAppSearchImpl.rewriteSchema(
AppSearchImpl.createPrefix("package", "existingDatabase"),
existingSchemaBuilder,
newSchema);
// Nothing was removed, but the method did rewrite the type name.
assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes)
.containsExactly("package$existingDatabase/Foo");
assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty();
// Same schema since nothing was added.
SchemaProto expectedSchema = existingSchemaBuilder.build();
assertThat(existingSchemaBuilder.getTypesList())
.containsExactlyElementsIn(expectedSchema.getTypesList());
}
/**
* Ensure that we track which types from the existing schema are deleted when a new schema is
* set.
*/
@Test
public void testRewriteSchema_deleteType() throws Exception {
SchemaProto.Builder existingSchemaBuilder =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$existingDatabase/Foo")
.build());
SchemaProto newSchema =
SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Bar").build())
.build();
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
mAppSearchImpl.rewriteSchema(
AppSearchImpl.createPrefix("package", "existingDatabase"),
existingSchemaBuilder,
newSchema);
// Bar type was rewritten, but Foo ended up being deleted since it wasn't included in the
// new schema.
assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes)
.containsExactly("package$existingDatabase/Bar");
assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes)
.containsExactly("package$existingDatabase/Foo");
// Same schema since nothing was added.
SchemaProto expectedSchema =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$existingDatabase/Bar")
.build())
.build();
assertThat(existingSchemaBuilder.getTypesList())
.containsExactlyElementsIn(expectedSchema.getTypesList());
}
@Test
public void testAddDocumentTypePrefix() {
DocumentProto insideDocument =
DocumentProto.newBuilder()
.setUri("inside-uri")
.setSchema("type")
.setNamespace("namespace")
.build();
DocumentProto documentProto =
DocumentProto.newBuilder()
.setUri("uri")
.setSchema("type")
.setNamespace("namespace")
.addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
.build();
DocumentProto expectedInsideDocument =
DocumentProto.newBuilder()
.setUri("inside-uri")
.setSchema("package$databaseName/type")
.setNamespace("package$databaseName/namespace")
.build();
DocumentProto expectedDocumentProto =
DocumentProto.newBuilder()
.setUri("uri")
.setSchema("package$databaseName/type")
.setNamespace("package$databaseName/namespace")
.addProperties(
PropertyProto.newBuilder()
.addDocumentValues(expectedInsideDocument))
.build();
DocumentProto.Builder actualDocument = documentProto.toBuilder();
mAppSearchImpl.addPrefixToDocument(
actualDocument, AppSearchImpl.createPrefix("package", "databaseName"));
assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto);
}
@Test
public void testRemoveDocumentTypePrefixes() throws Exception {
DocumentProto insideDocument =
DocumentProto.newBuilder()
.setUri("inside-uri")
.setSchema("package$databaseName1/type")
.setNamespace("package$databaseName2/namespace")
.build();
DocumentProto documentProto =
DocumentProto.newBuilder()
.setUri("uri")
.setSchema("package$databaseName2/type")
.setNamespace("package$databaseName3/namespace")
.addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
.build();
DocumentProto expectedInsideDocument =
DocumentProto.newBuilder()
.setUri("inside-uri")
.setSchema("type")
.setNamespace("namespace")
.build();
DocumentProto expectedDocumentProto =
DocumentProto.newBuilder()
.setUri("uri")
.setSchema("type")
.setNamespace("namespace")
.addProperties(
PropertyProto.newBuilder()
.addDocumentValues(expectedInsideDocument))
.build();
DocumentProto.Builder actualDocument = documentProto.toBuilder();
mAppSearchImpl.removePrefixesFromDocument(actualDocument);
assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto);
}
@Test
public void testOptimize() throws Exception {
// Insert schema
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
mAppSearchImpl.setSchema(
"package",
"database",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
// Insert enough documents.
for (int i = 0;
i
< AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
+ AppSearchImpl.CHECK_OPTIMIZE_INTERVAL;
i++) {
GenericDocument document =
new GenericDocument.Builder<>("uri" + i, "type")
.setNamespace("namespace")
.build();
mAppSearchImpl.putDocument("package", "database", document);
}
// Check optimize() will release 0 docs since there is no deletion.
GetOptimizeInfoResultProto optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked();
assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(0);
// delete 999 documents , we will reach the threshold to trigger optimize() in next
// deletion.
for (int i = 0; i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - 1; i++) {
mAppSearchImpl.remove("package", "database", "namespace", "uri" + i);
}
// optimize() still not be triggered since we are in the interval to call getOptimizeInfo()
optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked();
assertThat(optimizeInfo.getOptimizableDocs())
.isEqualTo(AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - 1);
// Keep delete docs, will reach the interval this time and trigger optimize().
for (int i = AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT;
i
< AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
+ AppSearchImpl.CHECK_OPTIMIZE_INTERVAL;
i++) {
mAppSearchImpl.remove("package", "database", "namespace", "uri" + i);
}
// Verify optimize() is triggered
optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked();
assertThat(optimizeInfo.getOptimizableDocs())
.isLessThan(AppSearchImpl.CHECK_OPTIMIZE_INTERVAL);
}
@Test
public void testRewriteSearchSpec_oneInstance() throws Exception {
SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery("");
// Insert schema
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
mAppSearchImpl.setSchema(
"package",
"database",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
// Insert document
GenericDocument document =
new GenericDocument.Builder<>("uri", "type").setNamespace("namespace").build();
mAppSearchImpl.putDocument("package", "database", document);
// Rewrite SearchSpec
mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
searchSpecProto,
Collections.singleton(AppSearchImpl.createPrefix("package", "database")));
assertThat(searchSpecProto.getSchemaTypeFiltersList())
.containsExactly("package$database/type");
assertThat(searchSpecProto.getNamespaceFiltersList())
.containsExactly("package$database/namespace");
}
@Test
public void testRewriteSearchSpec_twoInstances() throws Exception {
SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery("");
// Insert schema
List<AppSearchSchema> schemas =
ImmutableList.of(
new AppSearchSchema.Builder("typeA").build(),
new AppSearchSchema.Builder("typeB").build());
mAppSearchImpl.setSchema(
"package",
"database1",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
mAppSearchImpl.setSchema(
"package",
"database2",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
// Insert documents
GenericDocument document1 =
new GenericDocument.Builder<>("uri", "typeA").setNamespace("namespace").build();
mAppSearchImpl.putDocument("package", "database1", document1);
GenericDocument document2 =
new GenericDocument.Builder<>("uri", "typeB").setNamespace("namespace").build();
mAppSearchImpl.putDocument("package", "database2", document2);
// Rewrite SearchSpec
mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
searchSpecProto,
ImmutableSet.of(
AppSearchImpl.createPrefix("package", "database1"),
AppSearchImpl.createPrefix("package", "database2")));
assertThat(searchSpecProto.getSchemaTypeFiltersList())
.containsExactly(
"package$database1/typeA",
"package$database1/typeB",
"package$database2/typeA",
"package$database2/typeB");
assertThat(searchSpecProto.getNamespaceFiltersList())
.containsExactly("package$database1/namespace", "package$database2/namespace");
}
@Test
public void testQueryEmptyDatabase() throws Exception {
SearchSpec searchSpec =
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
SearchResultPage searchResultPage =
mAppSearchImpl.query("package", "EmptyDatabase", "", searchSpec);
assertThat(searchResultPage.getResults()).isEmpty();
}
@Test
public void testGlobalQueryEmptyDatabase() throws Exception {
SearchSpec searchSpec =
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
SearchResultPage searchResultPage = mAppSearchImpl.globalQuery("", searchSpec);
assertThat(searchResultPage.getResults()).isEmpty();
}
@Test
public void testRemoveEmptyDatabase_noExceptionThrown() throws Exception {
SearchSpec searchSpec =
new SearchSpec.Builder()
.addSchemaType("FakeType")
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.build();
mAppSearchImpl.removeByQuery("package", "EmptyDatabase", "", searchSpec);
searchSpec =
new SearchSpec.Builder()
.addNamespace("FakeNamespace")
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.build();
mAppSearchImpl.removeByQuery("package", "EmptyDatabase", "", searchSpec);
searchSpec = new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
mAppSearchImpl.removeByQuery("package", "EmptyDatabase", "", searchSpec);
}
@Test
public void testSetSchema() throws Exception {
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("Email").build());
// Set schema Email to AppSearch database1
mAppSearchImpl.setSchema(
"package",
"database1",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
// Create expected schemaType proto.
SchemaProto expectedProto =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$database1/Email"))
.build();
List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
expectedTypes.add(mVisibilitySchemaProto);
expectedTypes.addAll(expectedProto.getTypesList());
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
.containsExactlyElementsIn(expectedTypes);
}
@Test
public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception {
String prefix = AppSearchImpl.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
/*forceOverride=*/ false);
// "schema1" is platform hidden now
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
.isFalse();
// Add a new schema, and include the already-existing "schema1"
mAppSearchImpl.setSchema(
"package",
"database",
ImmutableList.of(
new AppSearchSchema.Builder("schema1").build(),
new AppSearchSchema.Builder("schema2").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
/*forceOverride=*/ false);
// Check that "schema1" is still platform hidden, but "schema2" is the default platform
// visible.
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
.isFalse();
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaPlatformSurfaceable(prefix, prefix + "schema2"))
.isTrue();
}
@Test
public void testRemoveSchema() throws Exception {
List<AppSearchSchema> schemas =
ImmutableList.of(
new AppSearchSchema.Builder("Email").build(),
new AppSearchSchema.Builder("Document").build());
// Set schema Email and Document to AppSearch database1
mAppSearchImpl.setSchema(
"package",
"database1",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
// Create expected schemaType proto.
SchemaProto expectedProto =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$database1/Email"))
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$database1/Document"))
.build();
// Check both schema Email and Document saved correctly.
List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
expectedTypes.add(mVisibilitySchemaProto);
expectedTypes.addAll(expectedProto.getTypesList());
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
.containsExactlyElementsIn(expectedTypes);
final List<AppSearchSchema> finalSchemas =
Collections.singletonList(new AppSearchSchema.Builder("Email").build());
// Check the incompatible error has been thrown.
AppSearchException e =
expectThrows(
AppSearchException.class,
() ->
mAppSearchImpl.setSchema(
"package",
"database1",
finalSchemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false));
assertThat(e).hasMessageThat().contains("Schema is incompatible");
assertThat(e).hasMessageThat().contains("Deleted types: [package$database1/Document]");
// ForceOverride to delete.
mAppSearchImpl.setSchema(
"package",
"database1",
finalSchemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ true);
// Check Document schema is removed.
expectedProto =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$database1/Email"))
.build();
expectedTypes = new ArrayList<>();
expectedTypes.add(mVisibilitySchemaProto);
expectedTypes.addAll(expectedProto.getTypesList());
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
.containsExactlyElementsIn(expectedTypes);
}
@Test
public void testRemoveSchema_differentDataBase() throws Exception {
// Create schemas
List<AppSearchSchema> schemas =
ImmutableList.of(
new AppSearchSchema.Builder("Email").build(),
new AppSearchSchema.Builder("Document").build());
// Set schema Email and Document to AppSearch database1 and 2
mAppSearchImpl.setSchema(
"package",
"database1",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
mAppSearchImpl.setSchema(
"package",
"database2",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
// Create expected schemaType proto.
SchemaProto expectedProto =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$database1/Email"))
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$database1/Document"))
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$database2/Email"))
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$database2/Document"))
.build();
// Check Email and Document is saved in database 1 and 2 correctly.
List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
expectedTypes.add(mVisibilitySchemaProto);
expectedTypes.addAll(expectedProto.getTypesList());
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
.containsExactlyElementsIn(expectedTypes);
// Save only Email to database1 this time.
schemas = Collections.singletonList(new AppSearchSchema.Builder("Email").build());
mAppSearchImpl.setSchema(
"package",
"database1",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ true);
// Create expected schemaType list, database 1 should only contain Email but database 2
// remains in same.
expectedProto =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$database1/Email"))
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$database2/Email"))
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$database2/Document"))
.build();
// Check nothing changed in database2.
expectedTypes = new ArrayList<>();
expectedTypes.add(mVisibilitySchemaProto);
expectedTypes.addAll(expectedProto.getTypesList());
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
.containsExactlyElementsIn(expectedTypes);
}
@Test
public void testRemoveSchema_removedFromVisibilityStore() throws Exception {
String prefix = AppSearchImpl.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
/*forceOverride=*/ false);
// "schema1" is platform hidden now
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
.isFalse();
// Remove "schema1" by force overriding
mAppSearchImpl.setSchema(
"package",
"database",
Collections.emptyList(),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ true);
// Check that "schema1" is no longer considered platform hidden
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
.isTrue();
// Add "schema1" back, it gets default visibility settings which means it's not platform
// hidden.
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
.isTrue();
}
@Test
public void testSetSchema_defaultPlatformVisible() throws Exception {
String prefix = AppSearchImpl.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaPlatformSurfaceable(prefix, prefix + "Schema"))
.isTrue();
}
@Test
public void testSetSchema_platformHidden() throws Exception {
String prefix = AppSearchImpl.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"),
/*forceOverride=*/ false);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaPlatformSurfaceable(prefix, prefix + "Schema"))
.isFalse();
}
@Test
public void testHasSchemaType() throws Exception {
// Nothing exists yet
assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isFalse();
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isTrue();
assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "UnknownSchema"))
.isFalse();
}
@Test
public void testGetDatabases() throws Exception {
// No client databases exist yet, but the VisibilityStore's does
assertThat(mAppSearchImpl.getPrefixesLocked())
.containsExactly(
AppSearchImpl.createPrefix(
VisibilityStore.PACKAGE_NAME, VisibilityStore.DATABASE_NAME));
// Has database1
mAppSearchImpl.setSchema(
"package",
"database1",
Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.getPrefixesLocked())
.containsExactly(
AppSearchImpl.createPrefix(
VisibilityStore.PACKAGE_NAME, VisibilityStore.DATABASE_NAME),
AppSearchImpl.createPrefix("package", "database1"));
// Has both databases
mAppSearchImpl.setSchema(
"package",
"database2",
Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.getPrefixesLocked())
.containsExactly(
AppSearchImpl.createPrefix(
VisibilityStore.PACKAGE_NAME, VisibilityStore.DATABASE_NAME),
AppSearchImpl.createPrefix("package", "database1"),
AppSearchImpl.createPrefix("package", "database2"));
}
}