Merge "Sync framework from jetpack."
This commit is contained in:
committed by
Android (Google) Code Review
commit
6c850e577a
@@ -72,7 +72,7 @@ public final class AppSearchSchema {
|
||||
|
||||
/** Returns the name of this schema type, e.g. Email. */
|
||||
@NonNull
|
||||
public String getSchemaTypeName() {
|
||||
public String getSchemaType() {
|
||||
return mBundle.getString(SCHEMA_TYPE_FIELD, "");
|
||||
}
|
||||
|
||||
|
||||
@@ -79,9 +79,8 @@ public class GenericDocument {
|
||||
* The maximum number of indexed properties a document can have.
|
||||
*
|
||||
* <p>Indexed properties are properties where the
|
||||
* {@link android.app.appsearch.annotation.AppSearchDocument.Property#indexingType} constant is
|
||||
* anything other than {@link
|
||||
* android.app.appsearch.AppSearchSchema.PropertyConfig.IndexingType#INDEXING_TYPE_NONE}.
|
||||
* {@link AppSearchSchema.PropertyConfig#getIndexingType()} constant is anything other than
|
||||
* {@link AppSearchSchema.PropertyConfig.IndexingType#INDEXING_TYPE_NONE}.
|
||||
*/
|
||||
public static int getMaxIndexedProperties() {
|
||||
return MAX_INDEXED_PROPERTIES;
|
||||
|
||||
@@ -154,12 +154,12 @@ public final class SearchSpec {
|
||||
* <p>If empty, the query will search over all schema types.
|
||||
*/
|
||||
@NonNull
|
||||
public List<String> getSchemas() {
|
||||
List<String> schemas = mBundle.getStringArrayList(SCHEMA_TYPE_FIELD);
|
||||
if (schemas == null) {
|
||||
public List<String> getSchemaTypes() {
|
||||
List<String> schemaTypes = mBundle.getStringArrayList(SCHEMA_TYPE_FIELD);
|
||||
if (schemaTypes == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.unmodifiableList(schemas);
|
||||
return Collections.unmodifiableList(schemaTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,10 +241,10 @@ public final class SearchSpec {
|
||||
* <p>If unset, the query will search over all schema types.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder addSchema(@NonNull String... schemaTypes) {
|
||||
public Builder addSchemaType(@NonNull String... schemaTypes) {
|
||||
Preconditions.checkNotNull(schemaTypes);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
return addSchema(Arrays.asList(schemaTypes));
|
||||
return addSchemaType(Arrays.asList(schemaTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,7 +254,7 @@ public final class SearchSpec {
|
||||
* <p>If unset, the query will search over all schema types.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder addSchema(@NonNull Collection<String> schemaTypes) {
|
||||
public Builder addSchemaType(@NonNull Collection<String> schemaTypes) {
|
||||
Preconditions.checkNotNull(schemaTypes);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
mSchemaTypes.addAll(schemaTypes);
|
||||
@@ -341,8 +341,7 @@ public final class SearchSpec {
|
||||
|
||||
/**
|
||||
* Sets {@code snippetCountPerProperty}. Only the first {@code snippetCountPerProperty}
|
||||
* snippets for a every property of {@link GenericDocument} will contain snippet
|
||||
* information.
|
||||
* snippets for each property of {@link GenericDocument} will contain snippet information.
|
||||
*
|
||||
* <p>If set to 0, snippeting is disabled and {@link SearchResult#getMatches}
|
||||
* will return {@code null} for that result.
|
||||
|
||||
@@ -30,11 +30,12 @@ import android.app.appsearch.GenericDocument;
|
||||
import android.app.appsearch.SearchResultPage;
|
||||
import android.app.appsearch.SearchSpec;
|
||||
import android.app.appsearch.exceptions.AppSearchException;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
|
||||
import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter;
|
||||
import com.android.server.appsearch.external.localstorage.converter.SearchResultToProtoConverter;
|
||||
import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter;
|
||||
import android.util.ArraySet;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import com.google.android.icing.IcingSearchEngine;
|
||||
import com.google.android.icing.proto.DeleteResultProto;
|
||||
@@ -62,7 +63,6 @@ import com.google.android.icing.proto.StatusProto;
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -104,7 +104,9 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
@WorkerThread
|
||||
public final class AppSearchImpl {
|
||||
private static final String TAG = "AppSearchImpl";
|
||||
private static final char DATABASE_DELIMITER = '/';
|
||||
|
||||
@VisibleForTesting
|
||||
static final char DATABASE_DELIMITER = '/';
|
||||
|
||||
@VisibleForTesting
|
||||
static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000;
|
||||
@@ -114,17 +116,28 @@ public final class AppSearchImpl {
|
||||
static final int CHECK_OPTIMIZE_INTERVAL = 100;
|
||||
|
||||
private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
|
||||
|
||||
@GuardedBy("mReadWriteLock")
|
||||
private final IcingSearchEngine mIcingSearchEngine;
|
||||
|
||||
@GuardedBy("mReadWriteLock")
|
||||
private final VisibilityStore mVisibilityStore;
|
||||
|
||||
// The map contains schemaTypes and namespaces for all database. All values in the map have
|
||||
// been already added database name prefix.
|
||||
// the database name prefix.
|
||||
// TODO(b/172360376): Check if this can be replaced with an ArrayMap
|
||||
@GuardedBy("mReadWriteLock")
|
||||
private final Map<String, Set<String>> mSchemaMap = new HashMap<>();
|
||||
|
||||
// TODO(b/172360376): Check if this can be replaced with an ArrayMap
|
||||
@GuardedBy("mReadWriteLock")
|
||||
private final Map<String, Set<String>> mNamespaceMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The counter to check when to call {@link #checkForOptimize(boolean)}. The interval is
|
||||
* {@link #CHECK_OPTIMIZE_INTERVAL}.
|
||||
*/
|
||||
@GuardedBy("mReadWriteLock")
|
||||
private int mOptimizeIntervalCount = 0;
|
||||
|
||||
/**
|
||||
@@ -134,12 +147,15 @@ public final class AppSearchImpl {
|
||||
@NonNull
|
||||
public static AppSearchImpl create(@NonNull File icingDir) throws AppSearchException {
|
||||
Preconditions.checkNotNull(icingDir);
|
||||
return new AppSearchImpl(icingDir);
|
||||
AppSearchImpl appSearchImpl = new AppSearchImpl(icingDir);
|
||||
appSearchImpl.initializeVisibilityStore();
|
||||
return appSearchImpl;
|
||||
}
|
||||
|
||||
private AppSearchImpl(@NonNull File icingDir) throws AppSearchException {
|
||||
boolean isReset = false;
|
||||
mReadWriteLock.writeLock().lock();
|
||||
|
||||
try {
|
||||
// We synchronize here because we don't want to call IcingSearchEngine.initialize() more
|
||||
// than once. It's unnecessary and can be a costly operation.
|
||||
@@ -156,27 +172,45 @@ public final class AppSearchImpl {
|
||||
getAllNamespacesResultProto = mIcingSearchEngine.getAllNamespaces();
|
||||
checkSuccess(getAllNamespacesResultProto.getStatus());
|
||||
} catch (AppSearchException e) {
|
||||
Log.w(TAG, "Error initializing, resetting IcingSearchEngine.", e);
|
||||
// Some error. Reset and see if it fixes it.
|
||||
reset();
|
||||
isReset = true;
|
||||
}
|
||||
|
||||
// Populate schema map
|
||||
for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) {
|
||||
String qualifiedSchemaType = schema.getSchemaType();
|
||||
addToMap(mSchemaMap, getDatabaseName(qualifiedSchemaType), qualifiedSchemaType);
|
||||
}
|
||||
|
||||
// Populate namespace map
|
||||
for (String qualifiedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
|
||||
addToMap(mNamespaceMap, getDatabaseName(qualifiedNamespace), qualifiedNamespace);
|
||||
addToMap(mNamespaceMap, getDatabaseName(qualifiedNamespace),
|
||||
qualifiedNamespace);
|
||||
}
|
||||
|
||||
// TODO(b/155939114): It's possible to optimize after init, which would reduce the time
|
||||
// to when we're able to serve queries. Consider moving this optimize call out.
|
||||
if (!isReset) {
|
||||
checkForOptimize(/* force= */ true);
|
||||
}
|
||||
|
||||
mVisibilityStore = new VisibilityStore(this);
|
||||
} finally {
|
||||
mReadWriteLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the visibility store in AppSearchImpl.
|
||||
*
|
||||
* @throws AppSearchException on IcingSearchEngine error.
|
||||
*/
|
||||
void initializeVisibilityStore() throws AppSearchException {
|
||||
mVisibilityStore.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the AppSearch schema for this app.
|
||||
*
|
||||
@@ -190,26 +224,24 @@ public final class AppSearchImpl {
|
||||
*/
|
||||
public void setSchema(@NonNull String databaseName, @NonNull Set<AppSearchSchema> schemas,
|
||||
boolean forceOverride) throws AppSearchException {
|
||||
SchemaProto schemaProto = getSchemaProto();
|
||||
|
||||
SchemaProto.Builder existingSchemaBuilder = schemaProto.toBuilder();
|
||||
|
||||
SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder();
|
||||
for (AppSearchSchema schema : schemas) {
|
||||
SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema);
|
||||
newSchemaBuilder.addTypes(schemaTypeProto);
|
||||
}
|
||||
|
||||
// Combine the existing schema (which may have types from other databases) with this
|
||||
// database's new schema. Modifies the existingSchemaBuilder.
|
||||
Set<String> newTypeNames = rewriteSchema(databaseName, existingSchemaBuilder,
|
||||
newSchemaBuilder.build());
|
||||
|
||||
SetSchemaResultProto setSchemaResultProto;
|
||||
mReadWriteLock.writeLock().lock();
|
||||
try {
|
||||
SchemaProto.Builder existingSchemaBuilder = getSchemaProto().toBuilder();
|
||||
|
||||
SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder();
|
||||
for (AppSearchSchema schema : schemas) {
|
||||
SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema);
|
||||
newSchemaBuilder.addTypes(schemaTypeProto);
|
||||
}
|
||||
|
||||
// Combine the existing schema (which may have types from other databases) with this
|
||||
// database's new schema. Modifies the existingSchemaBuilder.
|
||||
RewrittenSchemaResults rewrittenSchemaResults = rewriteSchema(databaseName,
|
||||
existingSchemaBuilder,
|
||||
newSchemaBuilder.build());
|
||||
|
||||
// Apply schema
|
||||
setSchemaResultProto =
|
||||
SetSchemaResultProto setSchemaResultProto =
|
||||
mIcingSearchEngine.setSchema(existingSchemaBuilder.build(), forceOverride);
|
||||
|
||||
// Determine whether it succeeded.
|
||||
@@ -231,7 +263,9 @@ public final class AppSearchImpl {
|
||||
}
|
||||
|
||||
// Update derived data structures.
|
||||
mSchemaMap.put(databaseName, newTypeNames);
|
||||
mSchemaMap.put(databaseName, rewrittenSchemaResults.mRewrittenQualifiedTypes);
|
||||
mVisibilityStore.updateSchemas(databaseName,
|
||||
rewrittenSchemaResults.mDeletedQualifiedTypes);
|
||||
|
||||
// Determine whether to schedule an immediate optimize.
|
||||
if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
|
||||
@@ -247,6 +281,40 @@ public final class AppSearchImpl {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the visibility settings for this app.
|
||||
*
|
||||
* <p>This method belongs to the mutate group
|
||||
*
|
||||
* @param databaseName The name of the database where the
|
||||
* visibility settings will apply.
|
||||
* @param schemasHiddenFromPlatformSurfaces Schemas that should be hidden from platform
|
||||
* surfaces
|
||||
* @throws AppSearchException on IcingSearchEngine error
|
||||
*/
|
||||
public void setVisibility(@NonNull String databaseName,
|
||||
@NonNull Set<String> schemasHiddenFromPlatformSurfaces)
|
||||
throws AppSearchException {
|
||||
mReadWriteLock.writeLock().lock();
|
||||
try {
|
||||
String databasePrefix = getDatabasePrefix(databaseName);
|
||||
Set<String> qualifiedSchemasHiddenFromPlatformSurface =
|
||||
new ArraySet<>(schemasHiddenFromPlatformSurfaces.size());
|
||||
for (String schema : schemasHiddenFromPlatformSurfaces) {
|
||||
Set<String> existingSchemas = mSchemaMap.get(databaseName);
|
||||
if (existingSchemas == null || !existingSchemas.contains(databasePrefix + schema)) {
|
||||
throw new AppSearchException(AppSearchResult.RESULT_NOT_FOUND,
|
||||
"Unknown schema(s): " + schemasHiddenFromPlatformSurfaces
|
||||
+ " provided during setVisibility.");
|
||||
}
|
||||
qualifiedSchemasHiddenFromPlatformSurface.add(databasePrefix + schema);
|
||||
}
|
||||
mVisibilityStore.setVisibility(databaseName, qualifiedSchemasHiddenFromPlatformSurface);
|
||||
} finally {
|
||||
mReadWriteLock.writeLock().lock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a document to the AppSearch index.
|
||||
*
|
||||
@@ -341,6 +409,9 @@ public final class AppSearchImpl {
|
||||
public SearchResultPage globalQuery(
|
||||
@NonNull String queryExpression,
|
||||
@NonNull SearchSpec searchSpec) throws AppSearchException {
|
||||
// TODO(b/169883602): Check if the platform is querying us at a higher level. At this
|
||||
// point, we should add all platform-surfaceable schemas assuming the querier has been
|
||||
// verified.
|
||||
return doQuery(mNamespaceMap.keySet(), queryExpression, searchSpec);
|
||||
}
|
||||
|
||||
@@ -358,8 +429,9 @@ public final class AppSearchImpl {
|
||||
SearchResultProto searchResultProto;
|
||||
mReadWriteLock.readLock().lock();
|
||||
try {
|
||||
// rewriteSearchSpecForDatabases will return false if none of the databases have
|
||||
// documents, so we can return an empty SearchResult and skip sending request to Icing.
|
||||
// rewriteSearchSpecForDatabases will return false if none of the databases that the
|
||||
// client is trying to search on exist, so we can return an empty SearchResult and skip
|
||||
// sending request to Icing.
|
||||
// We use the mNamespaceMap.keySet here because it's the smaller set of valid databases
|
||||
// that could exist.
|
||||
if (!rewriteSearchSpecForDatabases(searchSpecBuilder, databases)) {
|
||||
@@ -475,8 +547,7 @@ public final class AppSearchImpl {
|
||||
*
|
||||
* @throws AppSearchException on IcingSearchEngine error.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void reset() throws AppSearchException {
|
||||
private void reset() throws AppSearchException {
|
||||
ResetResultProto resetResultProto;
|
||||
mReadWriteLock.writeLock().lock();
|
||||
try {
|
||||
@@ -484,12 +555,27 @@ public final class AppSearchImpl {
|
||||
mOptimizeIntervalCount = 0;
|
||||
mSchemaMap.clear();
|
||||
mNamespaceMap.clear();
|
||||
|
||||
// Must be called after everything else since VisibilityStore may repopulate
|
||||
// IcingSearchEngine with an initial schema.
|
||||
mVisibilityStore.handleReset();
|
||||
} finally {
|
||||
mReadWriteLock.writeLock().unlock();
|
||||
}
|
||||
checkSuccess(resetResultProto.getStatus());
|
||||
}
|
||||
|
||||
/** Wrapper around schema changes */
|
||||
@VisibleForTesting
|
||||
static class RewrittenSchemaResults {
|
||||
// Any database-qualified types that used to exist in the schema, but are deleted in the
|
||||
// new one.
|
||||
final Set<String> mDeletedQualifiedTypes = new ArraySet<>();
|
||||
|
||||
// Database-qualified types that were part of the new schema.
|
||||
final Set<String> mRewrittenQualifiedTypes = new ArraySet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites all types mentioned in the given {@code newSchema} to prepend {@code prefix}.
|
||||
* Rewritten types will be added to the {@code existingSchema}.
|
||||
@@ -499,10 +585,11 @@ public final class AppSearchImpl {
|
||||
* instances. Will be mutated to contain the properly rewritten schema
|
||||
* types from {@code newSchema}.
|
||||
* @param newSchema Schema with types to add to the {@code existingSchema}.
|
||||
* @return a Set contains all remaining qualified schema type names in given database.
|
||||
* @return a RewrittenSchemaResults contains all qualified schema type names in the given
|
||||
* database as well as a set of schema types that were deleted from the database.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
Set<String> rewriteSchema(@NonNull String databaseName,
|
||||
RewrittenSchemaResults rewriteSchema(@NonNull String databaseName,
|
||||
@NonNull SchemaProto.Builder existingSchema,
|
||||
@NonNull SchemaProto newSchema) throws AppSearchException {
|
||||
String prefix = getDatabasePrefix(databaseName);
|
||||
@@ -533,7 +620,9 @@ public final class AppSearchImpl {
|
||||
newTypesToProto.put(newSchemaType, typeConfigBuilder.build());
|
||||
}
|
||||
|
||||
Set<String> newSchemaTypesName = newTypesToProto.keySet();
|
||||
// newTypesToProto is modified below, so we need a copy first
|
||||
RewrittenSchemaResults rewrittenSchemaResults = new RewrittenSchemaResults();
|
||||
rewrittenSchemaResults.mRewrittenQualifiedTypes.addAll(newTypesToProto.keySet());
|
||||
|
||||
// Combine the existing schema (which may have types from other databases) with this
|
||||
// database's new schema. Modifies the existingSchemaBuilder.
|
||||
@@ -548,13 +637,14 @@ public final class AppSearchImpl {
|
||||
// All types existing before but not in newSchema should be removed.
|
||||
existingSchema.removeTypes(i);
|
||||
--i;
|
||||
rewrittenSchemaResults.mDeletedQualifiedTypes.add(schemaType);
|
||||
}
|
||||
}
|
||||
// We've been removing existing types from newTypesToProto, so everything that remains is
|
||||
// new.
|
||||
existingSchema.addAllTypes(newTypesToProto.values());
|
||||
|
||||
return newSchemaTypesName;
|
||||
return rewrittenSchemaResults;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -601,21 +691,11 @@ public final class AppSearchImpl {
|
||||
* @param documentBuilder The document to mutate
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void removeDatabasesFromDocument(@NonNull DocumentProto.Builder documentBuilder) {
|
||||
int delimiterIndex;
|
||||
if ((delimiterIndex = documentBuilder.getSchema().indexOf(DATABASE_DELIMITER)) != -1) {
|
||||
// Rewrite the type name to remove the prefix.
|
||||
// Add 1 to include the char size of the DATABASE_DELIMITER
|
||||
String newSchema = documentBuilder.getSchema().substring(delimiterIndex + 1);
|
||||
documentBuilder.setSchema(newSchema);
|
||||
}
|
||||
|
||||
if ((delimiterIndex = documentBuilder.getNamespace().indexOf(DATABASE_DELIMITER)) != -1) {
|
||||
// Rewrite the namespace to remove the prefix.
|
||||
// Add 1 to include the char size of the DATABASE_DELIMITER
|
||||
String newNamespace = documentBuilder.getNamespace().substring(delimiterIndex + 1);
|
||||
documentBuilder.setNamespace(newNamespace);
|
||||
}
|
||||
void removeDatabasesFromDocument(@NonNull DocumentProto.Builder documentBuilder)
|
||||
throws AppSearchException {
|
||||
// Rewrite the type name and namespace to remove the prefix.
|
||||
documentBuilder.setSchema(removeDatabasePrefix(documentBuilder.getSchema()));
|
||||
documentBuilder.setNamespace(removeDatabasePrefix(documentBuilder.getNamespace()));
|
||||
|
||||
// Recurse into derived documents
|
||||
for (int propertyIdx = 0;
|
||||
@@ -652,7 +732,7 @@ public final class AppSearchImpl {
|
||||
@NonNull SearchSpecProto.Builder searchSpecBuilder,
|
||||
@NonNull Set<String> databaseNames) {
|
||||
// Create a copy since retainAll() modifies the original set.
|
||||
Set<String> existingDatabases = new HashSet<>(mNamespaceMap.keySet());
|
||||
Set<String> existingDatabases = new ArraySet<>(mNamespaceMap.keySet());
|
||||
existingDatabases.retainAll(databaseNames);
|
||||
|
||||
if (existingDatabases.isEmpty()) {
|
||||
@@ -670,17 +750,17 @@ public final class AppSearchImpl {
|
||||
// Rewrite filters to include a database prefix.
|
||||
for (String databaseName : existingDatabases) {
|
||||
Set<String> existingSchemaTypes = mSchemaMap.get(databaseName);
|
||||
String databaseNamePrefix = getDatabasePrefix(databaseName);
|
||||
if (schemaTypeFilters.isEmpty()) {
|
||||
// Include all schema types
|
||||
searchSpecBuilder.addAllSchemaTypeFilters(existingSchemaTypes);
|
||||
} else {
|
||||
// Qualify the given schema types
|
||||
for (String schemaType : schemaTypeFilters) {
|
||||
String qualifiedType = getDatabasePrefix(databaseName) + schemaType;
|
||||
for (int i = 0; i < schemaTypeFilters.size(); i++) {
|
||||
String qualifiedType = databaseNamePrefix + schemaTypeFilters.get(i);
|
||||
if (existingSchemaTypes.contains(qualifiedType)) {
|
||||
searchSpecBuilder.addSchemaTypeFilters(qualifiedType);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,8 +770,8 @@ public final class AppSearchImpl {
|
||||
searchSpecBuilder.addAllNamespaceFilters(existingNamespaces);
|
||||
} else {
|
||||
// Qualify the given namespaces.
|
||||
for (String namespace : namespaceFilters) {
|
||||
String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace;
|
||||
for (int i = 0; i < namespaceFilters.size(); i++) {
|
||||
String qualifiedNamespace = databaseNamePrefix + namespaceFilters.get(i);
|
||||
if (existingNamespaces.contains(qualifiedNamespace)) {
|
||||
searchSpecBuilder.addNamespaceFilters(qualifiedNamespace);
|
||||
}
|
||||
@@ -711,12 +791,44 @@ public final class AppSearchImpl {
|
||||
return schemaProto.getSchema();
|
||||
}
|
||||
|
||||
/** Returns true if {@code databaseName} has a {@code schemaType} */
|
||||
@GuardedBy("mReadWriteLock")
|
||||
boolean hasSchemaType(@NonNull String databaseName, @NonNull String schemaType) {
|
||||
Preconditions.checkNotNull(databaseName);
|
||||
Preconditions.checkNotNull(schemaType);
|
||||
|
||||
Set<String> schemaTypes = mSchemaMap.get(databaseName);
|
||||
if (schemaTypes == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return schemaTypes.contains(getDatabasePrefix(databaseName) + schemaType);
|
||||
}
|
||||
|
||||
/** Returns a set of all databases AppSearchImpl knows about. */
|
||||
@NonNull
|
||||
private String getDatabasePrefix(@NonNull String databaseName) {
|
||||
Set<String> getDatabases() {
|
||||
return mSchemaMap.keySet();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String getDatabasePrefix(@NonNull String databaseName) {
|
||||
// TODO(b/170370381): Reconsider the way we separate database names for security reasons.
|
||||
return databaseName + DATABASE_DELIMITER;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String removeDatabasePrefix(@NonNull String prefixedString)
|
||||
throws AppSearchException {
|
||||
int delimiterIndex;
|
||||
if ((delimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER)) != -1) {
|
||||
// Add 1 to include the char size of the DATABASE_DELIMITER
|
||||
return prefixedString.substring(delimiterIndex + 1);
|
||||
}
|
||||
throw new AppSearchException(AppSearchResult.RESULT_UNKNOWN_ERROR,
|
||||
"The prefixed value doesn't contains a valid database name.");
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private String getDatabaseName(@NonNull String prefixedValue) throws AppSearchException {
|
||||
int delimiterIndex = prefixedValue.indexOf(DATABASE_DELIMITER);
|
||||
@@ -731,7 +843,7 @@ public final class AppSearchImpl {
|
||||
private void addToMap(Map<String, Set<String>> map, String databaseName, String prefixedValue) {
|
||||
Set<String> values = map.get(databaseName);
|
||||
if (values == null) {
|
||||
values = new HashSet<>();
|
||||
values = new ArraySet<>();
|
||||
map.put(databaseName, values);
|
||||
}
|
||||
values.add(prefixedValue);
|
||||
@@ -805,7 +917,7 @@ public final class AppSearchImpl {
|
||||
|
||||
/** Remove the rewritten schema types from any result documents. */
|
||||
private SearchResultPage rewriteSearchResultProto(
|
||||
@NonNull SearchResultProto searchResultProto) {
|
||||
@NonNull SearchResultProto searchResultProto) throws AppSearchException {
|
||||
SearchResultProto.Builder resultsBuilder = searchResultProto.toBuilder();
|
||||
for (int i = 0; i < searchResultProto.getResultsCount(); i++) {
|
||||
if (searchResultProto.getResults(i).hasDocument()) {
|
||||
@@ -825,6 +937,11 @@ public final class AppSearchImpl {
|
||||
return mIcingSearchEngine.getOptimizeInfo();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
VisibilityStore getVisibilityStore() {
|
||||
return mVisibilityStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an erroneous status code to an AppSearchException. Callers should ensure that
|
||||
* the status code is not OK or WARNING_DATA_LOSS.
|
||||
|
||||
250
apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
vendored
Normal file
250
apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* 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 android.annotation.NonNull;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import android.app.appsearch.AppSearchResult;
|
||||
import android.app.appsearch.AppSearchSchema;
|
||||
import android.app.appsearch.GenericDocument;
|
||||
import android.app.appsearch.exceptions.AppSearchException;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Manages any visibility settings for all the databases that AppSearchImpl knows about. Persists
|
||||
* the visibility settings and reloads them on initialization.
|
||||
*
|
||||
* <p>The VisibilityStore creates a document for each database. This document holds the visibility
|
||||
* settings that apply to that database. The VisibilityStore also creates a schema for these
|
||||
* documents and has its own database so that its data doesn't interfere with any clients' data.
|
||||
* It persists the document and schema through AppSearchImpl.
|
||||
*
|
||||
* <p>These visibility settings are used to ensure AppSearch queries respect the clients'
|
||||
* settings on who their data is visible to.
|
||||
*
|
||||
* <p>This class doesn't handle any locking itself. Its callers should handle the locking at a
|
||||
* higher level.
|
||||
*
|
||||
* <p>NOTE: This class holds an instance of AppSearchImpl and AppSearchImpl holds an instance of
|
||||
* this class. Take care to not cause any circular dependencies.
|
||||
*/
|
||||
class VisibilityStore {
|
||||
// Schema type for documents that hold AppSearch's metadata, e.g. visibility settings
|
||||
@VisibleForTesting
|
||||
static final String SCHEMA_TYPE = "Visibility";
|
||||
// Property that holds the list of platform-hidden schemas, as part of the visibility
|
||||
// settings.
|
||||
@VisibleForTesting
|
||||
static final String PLATFORM_HIDDEN_PROPERTY = "platformHidden";
|
||||
// Database name to prefix all visibility schemas and documents with. Special-cased to
|
||||
// minimize the chance of collision with a client-supplied database.
|
||||
@VisibleForTesting
|
||||
static final String DATABASE_NAME = "$$__AppSearch__Database";
|
||||
// Namespace of documents that contain visibility settings
|
||||
private static final String NAMESPACE = "namespace";
|
||||
private final AppSearchImpl mAppSearchImpl;
|
||||
|
||||
// The map contains schemas that are platform-hidden for each database. All schemas in the map
|
||||
// have a database name prefix.
|
||||
private final Map<String, Set<String>> mPlatformHiddenMap = new ArrayMap<>();
|
||||
|
||||
/**
|
||||
* Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()}
|
||||
* before using the object.
|
||||
*
|
||||
* @param appSearchImpl AppSearchImpl instance
|
||||
*/
|
||||
VisibilityStore(@NonNull AppSearchImpl appSearchImpl) {
|
||||
mAppSearchImpl = appSearchImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes schemas and member variables to track visibility settings.
|
||||
*
|
||||
* <p>This is kept separate from the constructor because this will call methods on
|
||||
* AppSearchImpl. Some may even then recursively call back into VisibilityStore (for example,
|
||||
* {@link AppSearchImpl#setSchema} will call {@link #updateSchemas}. We need to have both
|
||||
* AppSearchImpl and VisibilityStore fully initialized for this call flow to work.
|
||||
*
|
||||
* @throws AppSearchException AppSearchException on AppSearchImpl error.
|
||||
*/
|
||||
public void initialize() throws AppSearchException {
|
||||
if (!mAppSearchImpl.hasSchemaType(DATABASE_NAME, SCHEMA_TYPE)) {
|
||||
// Schema type doesn't exist yet. Add it.
|
||||
mAppSearchImpl.setSchema(DATABASE_NAME,
|
||||
Collections.singleton(new AppSearchSchema.Builder(SCHEMA_TYPE)
|
||||
.addProperty(new AppSearchSchema.PropertyConfig.Builder(
|
||||
PLATFORM_HIDDEN_PROPERTY)
|
||||
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
|
||||
.setCardinality(
|
||||
AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
|
||||
.build())
|
||||
.build()),
|
||||
/*forceOverride=*/ false);
|
||||
}
|
||||
|
||||
// Populate visibility settings map
|
||||
for (String database : mAppSearchImpl.getDatabases()) {
|
||||
if (database.equals(DATABASE_NAME)) {
|
||||
// Our own database. Skip
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Note: We use the other clients' database names as uris
|
||||
GenericDocument document = mAppSearchImpl.getDocument(
|
||||
DATABASE_NAME, NAMESPACE, /*uri=*/ database);
|
||||
|
||||
String[] schemas = document.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY);
|
||||
mPlatformHiddenMap.put(database, new ArraySet<>(Arrays.asList(schemas)));
|
||||
} catch (AppSearchException e) {
|
||||
if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) {
|
||||
// TODO(b/172068212): This indicates some desync error. We were expecting a
|
||||
// document, but didn't find one. Should probably reset AppSearch instead of
|
||||
// ignoring it.
|
||||
continue;
|
||||
}
|
||||
// Otherwise, this is some other error we should pass up.
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update visibility settings for the {@code databaseName}.
|
||||
*
|
||||
* @param schemasToRemove Database-prefixed schemas that should be removed
|
||||
*/
|
||||
public void updateSchemas(@NonNull String databaseName,
|
||||
@NonNull Set<String> schemasToRemove) throws AppSearchException {
|
||||
Preconditions.checkNotNull(databaseName);
|
||||
Preconditions.checkNotNull(schemasToRemove);
|
||||
|
||||
GenericDocument visibilityDocument;
|
||||
try {
|
||||
visibilityDocument = mAppSearchImpl.getDocument(
|
||||
DATABASE_NAME, NAMESPACE, /*uri=*/ databaseName);
|
||||
} catch (AppSearchException e) {
|
||||
if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) {
|
||||
// This might be the first time we're seeing visibility changes for a database.
|
||||
// Create a new visibility document.
|
||||
mAppSearchImpl.putDocument(DATABASE_NAME, new GenericDocument.Builder(
|
||||
/*uri=*/ databaseName, SCHEMA_TYPE)
|
||||
.setNamespace(NAMESPACE).build());
|
||||
|
||||
// Since we know there was nothing that existed before, we don't need to remove
|
||||
// anything either. Return early.
|
||||
return;
|
||||
}
|
||||
// Otherwise, this is some real error we should pass up.
|
||||
throw e;
|
||||
}
|
||||
|
||||
String[] hiddenSchemas =
|
||||
visibilityDocument.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY);
|
||||
if (hiddenSchemas == null) {
|
||||
// Nothing to remove.
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new set so we can remove from it.
|
||||
Set<String> remainingSchemas = new ArraySet<>(Arrays.asList(hiddenSchemas));
|
||||
boolean changed = remainingSchemas.removeAll(schemasToRemove);
|
||||
if (!changed) {
|
||||
// Nothing was actually removed. Can return early.
|
||||
return;
|
||||
}
|
||||
|
||||
// Update our persisted document
|
||||
// TODO(b/171882200): Switch to a .toBuilder API when it's available.
|
||||
GenericDocument.Builder newVisibilityDocument = new GenericDocument.Builder(
|
||||
/*uri=*/ databaseName, SCHEMA_TYPE)
|
||||
.setNamespace(NAMESPACE);
|
||||
if (!remainingSchemas.isEmpty()) {
|
||||
newVisibilityDocument.setPropertyString(PLATFORM_HIDDEN_PROPERTY,
|
||||
remainingSchemas.toArray(new String[0]));
|
||||
}
|
||||
mAppSearchImpl.putDocument(DATABASE_NAME, newVisibilityDocument.build());
|
||||
|
||||
// Update derived data structures
|
||||
mPlatformHiddenMap.put(databaseName, remainingSchemas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets visibility settings for {@code databaseName}. Any previous visibility settings will be
|
||||
* overwritten.
|
||||
*
|
||||
* @param databaseName Database name that owns the {@code platformHiddenSchemas}.
|
||||
* @param platformHiddenSchemas Set of database-qualified schemas that should be hidden from
|
||||
* the platform.
|
||||
* @throws AppSearchException on AppSearchImpl error.
|
||||
*/
|
||||
public void setVisibility(@NonNull String databaseName,
|
||||
@NonNull Set<String> platformHiddenSchemas) throws AppSearchException {
|
||||
Preconditions.checkNotNull(databaseName);
|
||||
Preconditions.checkNotNull(platformHiddenSchemas);
|
||||
|
||||
// Persist the document
|
||||
GenericDocument.Builder visibilityDocument = new GenericDocument.Builder(
|
||||
/*uri=*/ databaseName, SCHEMA_TYPE)
|
||||
.setNamespace(NAMESPACE);
|
||||
if (!platformHiddenSchemas.isEmpty()) {
|
||||
visibilityDocument.setPropertyString(PLATFORM_HIDDEN_PROPERTY,
|
||||
platformHiddenSchemas.toArray(new String[0]));
|
||||
}
|
||||
mAppSearchImpl.putDocument(DATABASE_NAME, visibilityDocument.build());
|
||||
|
||||
// Update derived data structures.
|
||||
mPlatformHiddenMap.put(databaseName, platformHiddenSchemas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of database-qualified schemas in {@code databaseName} that are hidden from
|
||||
* the platform.
|
||||
*
|
||||
* @param databaseName Database name to retrieve schemas for
|
||||
* @return Set of database-qualified schemas that are hidden from the platform. Empty set if
|
||||
* none exist.
|
||||
*/
|
||||
@NonNull
|
||||
public Set<String> getPlatformHiddenSchemas(@NonNull String databaseName) {
|
||||
Preconditions.checkNotNull(databaseName);
|
||||
Set<String> platformHiddenSchemas = mPlatformHiddenMap.get(databaseName);
|
||||
if (platformHiddenSchemas == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return platformHiddenSchemas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an {@link AppSearchImpl#reset()} by clearing any cached state and resetting to a
|
||||
* first-initialized state.
|
||||
*
|
||||
* @throws AppSearchException on AppSearchImpl error.
|
||||
*/
|
||||
public void handleReset() throws AppSearchException {
|
||||
mPlatformHiddenMap.clear();
|
||||
initialize();
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,9 @@ import android.annotation.NonNull;
|
||||
import android.app.appsearch.AppSearchSchema;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import com.google.android.icing.proto.IndexingConfig;
|
||||
import com.google.android.icing.proto.PropertyConfigProto;
|
||||
import com.google.android.icing.proto.SchemaTypeConfigProto;
|
||||
import com.google.android.icing.proto.StringIndexingConfig;
|
||||
import com.google.android.icing.proto.TermMatchType;
|
||||
|
||||
import java.util.List;
|
||||
@@ -44,7 +44,7 @@ public final class SchemaToProtoConverter {
|
||||
public static SchemaTypeConfigProto convert(@NonNull AppSearchSchema schema) {
|
||||
Preconditions.checkNotNull(schema);
|
||||
SchemaTypeConfigProto.Builder protoBuilder =
|
||||
SchemaTypeConfigProto.newBuilder().setSchemaType(schema.getSchemaTypeName());
|
||||
SchemaTypeConfigProto.newBuilder().setSchemaType(schema.getSchemaType());
|
||||
List<AppSearchSchema.PropertyConfig> properties = schema.getProperties();
|
||||
for (int i = 0; i < properties.size(); i++) {
|
||||
PropertyConfigProto propertyProto = convertProperty(properties.get(i));
|
||||
@@ -59,7 +59,7 @@ public final class SchemaToProtoConverter {
|
||||
Preconditions.checkNotNull(property);
|
||||
PropertyConfigProto.Builder propertyConfigProto = PropertyConfigProto.newBuilder()
|
||||
.setPropertyName(property.getName());
|
||||
IndexingConfig.Builder indexingConfig = IndexingConfig.newBuilder();
|
||||
StringIndexingConfig.Builder indexingConfig = StringIndexingConfig.newBuilder();
|
||||
|
||||
// Set dataType
|
||||
@AppSearchSchema.PropertyConfig.DataType int dataType = property.getDataType();
|
||||
@@ -106,15 +106,15 @@ public final class SchemaToProtoConverter {
|
||||
// Set tokenizerType
|
||||
@AppSearchSchema.PropertyConfig.TokenizerType int tokenizerType =
|
||||
property.getTokenizerType();
|
||||
IndexingConfig.TokenizerType.Code tokenizerTypeProto =
|
||||
IndexingConfig.TokenizerType.Code.forNumber(tokenizerType);
|
||||
StringIndexingConfig.TokenizerType.Code tokenizerTypeProto =
|
||||
StringIndexingConfig.TokenizerType.Code.forNumber(tokenizerType);
|
||||
if (tokenizerTypeProto == null) {
|
||||
throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType);
|
||||
}
|
||||
indexingConfig.setTokenizerType(tokenizerTypeProto);
|
||||
|
||||
// Build!
|
||||
propertyConfigProto.setIndexingConfig(indexingConfig);
|
||||
propertyConfigProto.setStringIndexingConfig(indexingConfig);
|
||||
return propertyConfigProto.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ import java.util.ArrayList;
|
||||
public class SearchResultToProtoConverter {
|
||||
private SearchResultToProtoConverter() {}
|
||||
|
||||
|
||||
/** Translate a {@link SearchResultProto} into {@link SearchResultPage}. */
|
||||
@NonNull
|
||||
public static SearchResultPage convertToSearchResultPage(
|
||||
|
||||
@@ -39,7 +39,7 @@ public final class SearchSpecToProtoConverter {
|
||||
public static SearchSpecProto toSearchSpecProto(@NonNull SearchSpec spec) {
|
||||
Preconditions.checkNotNull(spec);
|
||||
SearchSpecProto.Builder protoBuilder = SearchSpecProto.newBuilder()
|
||||
.addAllSchemaTypeFilters(spec.getSchemas())
|
||||
.addAllSchemaTypeFilters(spec.getSchemaTypes())
|
||||
.addAllNamespaceFilters(spec.getNamespaces());
|
||||
|
||||
@SearchSpec.TermMatch int termMatchCode = spec.getTermMatch();
|
||||
|
||||
1
apex/appsearch/synced_jetpack_changeid.txt
Normal file
1
apex/appsearch/synced_jetpack_changeid.txt
Normal file
@@ -0,0 +1 @@
|
||||
I9ba99ecc4f9a7eb177e678d22d083750efce81b5
|
||||
@@ -18,54 +18,17 @@ package android.app.appsearch;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.testng.Assert.expectThrows;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class SearchSpecTest {
|
||||
@Test
|
||||
public void buildSearchSpecWithoutTermMatchType() {
|
||||
expectThrows(RuntimeException.class, () -> new SearchSpec.Builder()
|
||||
.addSchema("testSchemaType")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildSearchSpec() {
|
||||
SearchSpec searchSpec = new SearchSpec.Builder()
|
||||
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
|
||||
.addNamespace("namespace1", "namespace2")
|
||||
.addSchema("schemaTypes1", "schemaTypes2")
|
||||
.setSnippetCount(5)
|
||||
.setSnippetCountPerProperty(10)
|
||||
.setMaxSnippetSize(15)
|
||||
.setNumPerPage(42)
|
||||
.setOrder(SearchSpec.ORDER_ASCENDING)
|
||||
.setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
|
||||
.build();
|
||||
|
||||
assertThat(searchSpec.getTermMatch()).isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
|
||||
assertThat(searchSpec.getNamespaces())
|
||||
.containsExactly("namespace1", "namespace2").inOrder();
|
||||
assertThat(searchSpec.getSchemas())
|
||||
.containsExactly("schemaTypes1", "schemaTypes2").inOrder();
|
||||
assertThat(searchSpec.getSnippetCount()).isEqualTo(5);
|
||||
assertThat(searchSpec.getSnippetCountPerProperty()).isEqualTo(10);
|
||||
assertThat(searchSpec.getMaxSnippetSize()).isEqualTo(15);
|
||||
assertThat(searchSpec.getNumPerPage()).isEqualTo(42);
|
||||
assertThat(searchSpec.getOrder()).isEqualTo(SearchSpec.ORDER_ASCENDING);
|
||||
assertThat(searchSpec.getRankingStrategy())
|
||||
.isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBundle() {
|
||||
SearchSpec searchSpec = new SearchSpec.Builder()
|
||||
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
|
||||
.addNamespace("namespace1", "namespace2")
|
||||
.addSchema("schemaTypes1", "schemaTypes2")
|
||||
.addSchemaType("schemaTypes1", "schemaTypes2")
|
||||
.setSnippetCount(5)
|
||||
.setSnippetCountPerProperty(10)
|
||||
.setMaxSnippetSize(15)
|
||||
|
||||
82
core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchResultCtsTest.java
vendored
Normal file
82
core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchResultCtsTest.java
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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 android.app.appsearch.cts;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.appsearch.AppSearchResult;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class AppSearchResultCtsTest {
|
||||
|
||||
@Test
|
||||
public void testResultEquals_identical() {
|
||||
AppSearchResult<String> result1 = AppSearchResult.newSuccessfulResult("String");
|
||||
AppSearchResult<String> result2 = AppSearchResult.newSuccessfulResult("String");
|
||||
|
||||
assertThat(result1).isEqualTo(result2);
|
||||
assertThat(result1.hashCode()).isEqualTo(result2.hashCode());
|
||||
|
||||
AppSearchResult<String> result3 =
|
||||
AppSearchResult.newFailedResult(AppSearchResult.RESULT_INTERNAL_ERROR,
|
||||
"errorMessage");
|
||||
AppSearchResult<String> result4 =
|
||||
AppSearchResult.newFailedResult(AppSearchResult.RESULT_INTERNAL_ERROR,
|
||||
"errorMessage");
|
||||
|
||||
assertThat(result3).isEqualTo(result4);
|
||||
assertThat(result3.hashCode()).isEqualTo(result4.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResultEquals_failure() {
|
||||
AppSearchResult<String> result1 = AppSearchResult.newSuccessfulResult("String");
|
||||
AppSearchResult<String> result2 = AppSearchResult.newSuccessfulResult("Wrong");
|
||||
AppSearchResult<String> resultNull = AppSearchResult.newSuccessfulResult(/*value=*/null);
|
||||
|
||||
assertThat(result1).isNotEqualTo(result2);
|
||||
assertThat(result1.hashCode()).isNotEqualTo(result2.hashCode());
|
||||
assertThat(result1).isNotEqualTo(resultNull);
|
||||
assertThat(result1.hashCode()).isNotEqualTo(resultNull.hashCode());
|
||||
|
||||
AppSearchResult<String> result3 =
|
||||
AppSearchResult.newFailedResult(AppSearchResult.RESULT_INTERNAL_ERROR,
|
||||
"errorMessage");
|
||||
AppSearchResult<String> result4 =
|
||||
AppSearchResult.newFailedResult(AppSearchResult.RESULT_IO_ERROR,
|
||||
"errorMessage");
|
||||
|
||||
assertThat(result3).isNotEqualTo(result4);
|
||||
assertThat(result3.hashCode()).isNotEqualTo(result4.hashCode());
|
||||
|
||||
|
||||
AppSearchResult<String> result5 =
|
||||
AppSearchResult.newFailedResult(AppSearchResult.RESULT_INTERNAL_ERROR,
|
||||
"Wrong");
|
||||
|
||||
assertThat(result3).isNotEqualTo(result5);
|
||||
assertThat(result3.hashCode()).isNotEqualTo(result5.hashCode());
|
||||
|
||||
AppSearchResult<String> result6 =
|
||||
AppSearchResult.newFailedResult(AppSearchResult.RESULT_INTERNAL_ERROR,
|
||||
/*errorMessage=*/null);
|
||||
|
||||
assertThat(result3).isNotEqualTo(result6);
|
||||
assertThat(result3.hashCode()).isNotEqualTo(result6.hashCode());
|
||||
}
|
||||
}
|
||||
@@ -14,19 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.appsearch;
|
||||
package android.app.appsearch.cts;
|
||||
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.testng.Assert.expectThrows;
|
||||
|
||||
import android.app.appsearch.AppSearchSchema;
|
||||
import android.app.appsearch.AppSearchSchema.PropertyConfig;
|
||||
import android.app.appsearch.exceptions.IllegalSchemaException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class AppSearchSchemaTest {
|
||||
public class AppSearchSchemaCtsTest {
|
||||
@Test
|
||||
public void testInvalidEnums() {
|
||||
PropertyConfig.Builder builder = new PropertyConfig.Builder("test");
|
||||
@@ -14,29 +14,31 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.appsearch;
|
||||
package android.app.appsearch.cts;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.testng.Assert.expectThrows;
|
||||
|
||||
import android.app.appsearch.GenericDocument;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class GenericDocumentTest {
|
||||
public class GenericDocumentCtsTest {
|
||||
private static final byte[] sByteArray1 = new byte[]{(byte) 1, (byte) 2, (byte) 3};
|
||||
private static final byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6, (byte) 7};
|
||||
private static final GenericDocument sDocumentProperties1 = new GenericDocument
|
||||
.Builder("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
|
||||
.Builder<>("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
|
||||
.setCreationTimestampMillis(12345L)
|
||||
.build();
|
||||
private static final GenericDocument sDocumentProperties2 = new GenericDocument
|
||||
.Builder("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
|
||||
.Builder<>("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
|
||||
.setCreationTimestampMillis(6789L)
|
||||
.build();
|
||||
|
||||
@Test
|
||||
public void testDocumentEquals_Identical() {
|
||||
GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
|
||||
public void testDocumentEquals_identical() {
|
||||
GenericDocument document1 = new GenericDocument.Builder<>("uri1", "schemaType1")
|
||||
.setCreationTimestampMillis(5L)
|
||||
.setTtlMillis(1L)
|
||||
.setPropertyLong("longKey1", 1L, 2L, 3L)
|
||||
@@ -46,7 +48,7 @@ public class GenericDocumentTest {
|
||||
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
|
||||
.setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
|
||||
.build();
|
||||
GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
|
||||
GenericDocument document2 = new GenericDocument.Builder<>("uri1", "schemaType1")
|
||||
.setCreationTimestampMillis(5L)
|
||||
.setTtlMillis(1L)
|
||||
.setPropertyLong("longKey1", 1L, 2L, 3L)
|
||||
@@ -61,8 +63,8 @@ public class GenericDocumentTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDocumentEquals_DifferentOrder() {
|
||||
GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
|
||||
public void testDocumentEquals_differentOrder() {
|
||||
GenericDocument document1 = new GenericDocument.Builder<>("uri1", "schemaType1")
|
||||
.setCreationTimestampMillis(5L)
|
||||
.setPropertyLong("longKey1", 1L, 2L, 3L)
|
||||
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
|
||||
@@ -73,7 +75,7 @@ public class GenericDocumentTest {
|
||||
.build();
|
||||
|
||||
// Create second document with same parameter but different order.
|
||||
GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
|
||||
GenericDocument document2 = new GenericDocument.Builder<>("uri1", "schemaType1")
|
||||
.setCreationTimestampMillis(5L)
|
||||
.setPropertyBoolean("booleanKey1", true, false, true)
|
||||
.setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
|
||||
@@ -87,14 +89,14 @@ public class GenericDocumentTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDocumentEquals_Failure() {
|
||||
GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
|
||||
public void testDocumentEquals_failure() {
|
||||
GenericDocument document1 = new GenericDocument.Builder<>("uri1", "schemaType1")
|
||||
.setCreationTimestampMillis(5L)
|
||||
.setPropertyLong("longKey1", 1L, 2L, 3L)
|
||||
.build();
|
||||
|
||||
// Create second document with same order but different value.
|
||||
GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
|
||||
GenericDocument document2 = new GenericDocument.Builder<>("uri1", "schemaType1")
|
||||
.setCreationTimestampMillis(5L)
|
||||
.setPropertyLong("longKey1", 1L, 2L, 4L) // Different
|
||||
.build();
|
||||
@@ -103,14 +105,14 @@ public class GenericDocumentTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDocumentEquals_Failure_RepeatedFieldOrder() {
|
||||
GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
|
||||
public void testDocumentEquals_repeatedFieldOrder_failure() {
|
||||
GenericDocument document1 = new GenericDocument.Builder<>("uri1", "schemaType1")
|
||||
.setCreationTimestampMillis(5L)
|
||||
.setPropertyBoolean("booleanKey1", true, false, true)
|
||||
.build();
|
||||
|
||||
// Create second document with same order but different value.
|
||||
GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
|
||||
GenericDocument document2 = new GenericDocument.Builder<>("uri1", "schemaType1")
|
||||
.setCreationTimestampMillis(5L)
|
||||
.setPropertyBoolean("booleanKey1", true, true, false) // Different
|
||||
.build();
|
||||
@@ -120,7 +122,7 @@ public class GenericDocumentTest {
|
||||
|
||||
@Test
|
||||
public void testDocumentGetSingleValue() {
|
||||
GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
|
||||
GenericDocument document = new GenericDocument.Builder<>("uri1", "schemaType1")
|
||||
.setCreationTimestampMillis(5L)
|
||||
.setScore(1)
|
||||
.setTtlMillis(1L)
|
||||
@@ -147,7 +149,7 @@ public class GenericDocumentTest {
|
||||
|
||||
@Test
|
||||
public void testDocumentGetArrayValues() {
|
||||
GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
|
||||
GenericDocument document = new GenericDocument.Builder<>("uri1", "schemaType1")
|
||||
.setCreationTimestampMillis(5L)
|
||||
.setPropertyLong("longKey1", 1L, 2L, 3L)
|
||||
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
|
||||
@@ -173,8 +175,8 @@ public class GenericDocumentTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDocument_ToString() throws Exception {
|
||||
GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
|
||||
public void testDocument_toString() {
|
||||
GenericDocument document = new GenericDocument.Builder<>("uri1", "schemaType1")
|
||||
.setCreationTimestampMillis(5L)
|
||||
.setPropertyLong("longKey1", 1L, 2L, 3L)
|
||||
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
|
||||
@@ -216,8 +218,8 @@ public class GenericDocumentTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDocumentGetValues_DifferentTypes() {
|
||||
GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
|
||||
public void testDocumentGetValues_differentTypes() {
|
||||
GenericDocument document = new GenericDocument.Builder<>("uri1", "schemaType1")
|
||||
.setScore(1)
|
||||
.setPropertyLong("longKey1", 1L)
|
||||
.setPropertyBoolean("booleanKey1", true, false, true)
|
||||
@@ -244,7 +246,7 @@ public class GenericDocumentTest {
|
||||
|
||||
@Test
|
||||
public void testDocumentInvalid() {
|
||||
GenericDocument.Builder builder = new GenericDocument.Builder("uri1", "schemaType1");
|
||||
GenericDocument.Builder<?> builder = new GenericDocument.Builder<>("uri1", "schemaType1");
|
||||
expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> builder.setPropertyBoolean("test", new boolean[]{}));
|
||||
63
core/tests/coretests/src/android/app/appsearch/external/app/cts/SearchSpecCtsTest.java
vendored
Normal file
63
core/tests/coretests/src/android/app/appsearch/external/app/cts/SearchSpecCtsTest.java
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 android.app.appsearch.cts;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.testng.Assert.expectThrows;
|
||||
|
||||
import android.app.appsearch.SearchSpec;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class SearchSpecCtsTest {
|
||||
@Test
|
||||
public void buildSearchSpecWithoutTermMatchType() {
|
||||
RuntimeException e = expectThrows(RuntimeException.class, () -> new SearchSpec.Builder()
|
||||
.addSchemaType("testSchemaType")
|
||||
.build());
|
||||
assertThat(e).hasMessageThat().contains("Missing termMatchType field");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildSearchSpec() {
|
||||
SearchSpec searchSpec = new SearchSpec.Builder()
|
||||
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
|
||||
.addNamespace("namespace1", "namespace2")
|
||||
.addSchemaType("schemaTypes1", "schemaTypes2")
|
||||
.setSnippetCount(5)
|
||||
.setSnippetCountPerProperty(10)
|
||||
.setMaxSnippetSize(15)
|
||||
.setNumPerPage(42)
|
||||
.setOrder(SearchSpec.ORDER_ASCENDING)
|
||||
.setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
|
||||
.build();
|
||||
|
||||
assertThat(searchSpec.getTermMatch()).isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
|
||||
assertThat(searchSpec.getNamespaces())
|
||||
.containsExactly("namespace1", "namespace2").inOrder();
|
||||
assertThat(searchSpec.getSchemaTypes())
|
||||
.containsExactly("schemaTypes1", "schemaTypes2").inOrder();
|
||||
assertThat(searchSpec.getSnippetCount()).isEqualTo(5);
|
||||
assertThat(searchSpec.getSnippetCountPerProperty()).isEqualTo(10);
|
||||
assertThat(searchSpec.getMaxSnippetSize()).isEqualTo(15);
|
||||
assertThat(searchSpec.getNumPerPage()).isEqualTo(42);
|
||||
assertThat(searchSpec.getOrder()).isEqualTo(SearchSpec.ORDER_ASCENDING);
|
||||
assertThat(searchSpec.getRankingStrategy())
|
||||
.isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE);
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.appsearch.customer;
|
||||
package android.app.appsearch.cts.customer;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.appsearch.GenericDocument;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
@@ -32,13 +32,13 @@ import org.junit.Test;
|
||||
*/
|
||||
public class CustomerDocumentTest {
|
||||
|
||||
private static byte[] sByteArray1 = new byte[]{(byte) 1, (byte) 2, (byte) 3};
|
||||
private static byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6};
|
||||
private static GenericDocument sDocumentProperties1 = new GenericDocument
|
||||
.Builder("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
|
||||
private static final byte[] BYTE_ARRAY1 = new byte[]{(byte) 1, (byte) 2, (byte) 3};
|
||||
private static final byte[] BYTE_ARRAY2 = new byte[]{(byte) 4, (byte) 5, (byte) 6};
|
||||
private static final GenericDocument DOCUMENT_PROPERTIES1 = new GenericDocument
|
||||
.Builder<>("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
|
||||
.build();
|
||||
private static GenericDocument sDocumentProperties2 = new GenericDocument
|
||||
.Builder("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
|
||||
private static final GenericDocument DOCUMENT_PROPERTIES2 = new GenericDocument
|
||||
.Builder<>("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
|
||||
.build();
|
||||
|
||||
@Test
|
||||
@@ -50,8 +50,8 @@ public class CustomerDocumentTest {
|
||||
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
|
||||
.setPropertyBoolean("booleanKey1", true, false, true)
|
||||
.setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
|
||||
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
|
||||
.setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
|
||||
.setPropertyBytes("byteKey1", BYTE_ARRAY1, BYTE_ARRAY2)
|
||||
.setPropertyDocument("documentKey1", DOCUMENT_PROPERTIES1, DOCUMENT_PROPERTIES2)
|
||||
.build();
|
||||
|
||||
assertThat(customerDocument.getUri()).isEqualTo("uri1");
|
||||
@@ -67,9 +67,9 @@ public class CustomerDocumentTest {
|
||||
assertThat(customerDocument.getPropertyStringArray("stringKey1")).asList()
|
||||
.containsExactly("test-value1", "test-value2", "test-value3");
|
||||
assertThat(customerDocument.getPropertyBytesArray("byteKey1")).asList()
|
||||
.containsExactly(sByteArray1, sByteArray2);
|
||||
.containsExactly(BYTE_ARRAY1, BYTE_ARRAY2);
|
||||
assertThat(customerDocument.getPropertyDocumentArray("documentKey1")).asList()
|
||||
.containsExactly(sDocumentProperties1, sDocumentProperties2);
|
||||
.containsExactly(DOCUMENT_PROPERTIES1, DOCUMENT_PROPERTIES2);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -25,15 +25,16 @@ 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.IndexingConfig;
|
||||
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.ImmutableSet;
|
||||
|
||||
@@ -42,18 +43,33 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
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 visibilityAppSearchSchema =
|
||||
new AppSearchSchema.Builder(
|
||||
VisibilityStore.DATABASE_NAME + AppSearchImpl.DATABASE_DELIMITER
|
||||
+ VisibilityStore.SCHEMA_TYPE)
|
||||
.addProperty(new AppSearchSchema.PropertyConfig.Builder(
|
||||
VisibilityStore.PLATFORM_HIDDEN_PROPERTY)
|
||||
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
|
||||
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
|
||||
.build())
|
||||
.build();
|
||||
mVisibilitySchemaProto = SchemaToProtoConverter.convert(visibilityAppSearchSchema);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,8 +78,14 @@ public class AppSearchImplTest {
|
||||
* schema.
|
||||
*/
|
||||
@Test
|
||||
public void testRewriteSchema() throws Exception {
|
||||
SchemaProto.Builder existingSchemaBuilder = mAppSearchImpl.getSchemaProto().toBuilder();
|
||||
public void testRewriteSchema_addType() throws Exception {
|
||||
SchemaProto.Builder existingSchemaBuilder = SchemaProto.newBuilder()
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder()
|
||||
.setSchemaType("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()
|
||||
@@ -74,12 +96,11 @@ public class AppSearchImplTest {
|
||||
.setPropertyName("subject")
|
||||
.setDataType(PropertyConfigProto.DataType.Code.STRING)
|
||||
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
|
||||
.setIndexingConfig(
|
||||
IndexingConfig.newBuilder()
|
||||
.setTokenizerType(
|
||||
IndexingConfig.TokenizerType.Code.PLAIN)
|
||||
.setTermMatchType(TermMatchType.Code.PREFIX)
|
||||
.build()
|
||||
.setStringIndexingConfig(StringIndexingConfig.newBuilder()
|
||||
.setTokenizerType(
|
||||
StringIndexingConfig.TokenizerType.Code.PLAIN)
|
||||
.setTermMatchType(TermMatchType.Code.PREFIX)
|
||||
.build()
|
||||
).build()
|
||||
).addProperties(PropertyConfigProto.newBuilder()
|
||||
.setPropertyName("link")
|
||||
@@ -90,34 +111,103 @@ public class AppSearchImplTest {
|
||||
).build()
|
||||
).build();
|
||||
|
||||
Set<String> newTypes = mAppSearchImpl.rewriteSchema("databaseName", existingSchemaBuilder,
|
||||
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
|
||||
"newDatabase", existingSchemaBuilder,
|
||||
newSchema);
|
||||
assertThat(newTypes).containsExactly("databaseName/Foo", "databaseName/TestType");
|
||||
|
||||
// We rewrote all the new types that were added. And nothing was removed.
|
||||
assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
|
||||
.containsExactly("newDatabase/Foo", "newDatabase/TestType");
|
||||
assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes).isEmpty();
|
||||
|
||||
SchemaProto expectedSchema = SchemaProto.newBuilder()
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder()
|
||||
.setSchemaType("databaseName/Foo").build())
|
||||
.setSchemaType("newDatabase/Foo").build())
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder()
|
||||
.setSchemaType("databaseName/TestType")
|
||||
.setSchemaType("newDatabase/TestType")
|
||||
.addProperties(PropertyConfigProto.newBuilder()
|
||||
.setPropertyName("subject")
|
||||
.setDataType(PropertyConfigProto.DataType.Code.STRING)
|
||||
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
|
||||
.setIndexingConfig(
|
||||
IndexingConfig.newBuilder()
|
||||
.setTokenizerType(
|
||||
IndexingConfig.TokenizerType.Code.PLAIN)
|
||||
.setTermMatchType(TermMatchType.Code.PREFIX)
|
||||
.build()
|
||||
.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("databaseName/RefType")
|
||||
.setSchemaType("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("existingDatabase/Foo").build());
|
||||
|
||||
SchemaProto newSchema = SchemaProto.newBuilder()
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder()
|
||||
.setSchemaType("Foo").build())
|
||||
.build();
|
||||
|
||||
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
|
||||
"existingDatabase", existingSchemaBuilder, newSchema);
|
||||
|
||||
// Nothing was removed, but the method did rewrite the type name.
|
||||
assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
|
||||
.containsExactly("existingDatabase/Foo");
|
||||
assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes).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("existingDatabase/Foo").build());
|
||||
|
||||
SchemaProto newSchema = SchemaProto.newBuilder()
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder()
|
||||
.setSchemaType("Bar").build())
|
||||
.build();
|
||||
|
||||
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
|
||||
"existingDatabase", existingSchemaBuilder, newSchema);
|
||||
|
||||
// Bar type was rewritten, but Foo ended up being deleted since it wasn't included in the
|
||||
// new schema.
|
||||
assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
|
||||
.containsExactly("existingDatabase/Bar");
|
||||
assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes)
|
||||
.containsExactly("existingDatabase/Foo");
|
||||
|
||||
// Same schema since nothing was added.
|
||||
SchemaProto expectedSchema = SchemaProto.newBuilder()
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder()
|
||||
.setSchemaType("existingDatabase/Bar").build())
|
||||
.build();
|
||||
|
||||
assertThat(existingSchemaBuilder.getTypesList())
|
||||
.containsExactlyElementsIn(expectedSchema.getTypesList());
|
||||
}
|
||||
@@ -154,7 +244,7 @@ public class AppSearchImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveDocumentTypePrefixes() {
|
||||
public void testRemoveDocumentTypePrefixes() throws Exception {
|
||||
DocumentProto insideDocument = DocumentProto.newBuilder()
|
||||
.setUri("inside-uri")
|
||||
.setSchema("databaseName1/type")
|
||||
@@ -230,7 +320,7 @@ public class AppSearchImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRewriteSearchSpec_OneInstance() throws Exception {
|
||||
public void testRewriteSearchSpec_oneInstance() throws Exception {
|
||||
SearchSpecProto.Builder searchSpecProto =
|
||||
SearchSpecProto.newBuilder().setQuery("");
|
||||
|
||||
@@ -252,7 +342,7 @@ public class AppSearchImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRewriteSearchSpec_TwoInstances() throws Exception {
|
||||
public void testRewriteSearchSpec_twoInstances() throws Exception {
|
||||
SearchSpecProto.Builder searchSpecProto =
|
||||
SearchSpecProto.newBuilder().setQuery("");
|
||||
|
||||
@@ -302,9 +392,9 @@ public class AppSearchImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveEmptyDatabase_NoExceptionThrown() throws Exception {
|
||||
public void testRemoveEmptyDatabase_noExceptionThrown() throws Exception {
|
||||
SearchSpec searchSpec =
|
||||
new SearchSpec.Builder().addSchema("FakeType").setTermMatch(
|
||||
new SearchSpec.Builder().addSchemaType("FakeType").setTermMatch(
|
||||
TermMatchType.Code.PREFIX_VALUE).build();
|
||||
mAppSearchImpl.removeByQuery("EmptyDatabase",
|
||||
"", searchSpec);
|
||||
@@ -326,12 +416,37 @@ public class AppSearchImplTest {
|
||||
// Set schema Email to AppSearch database1
|
||||
mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false);
|
||||
|
||||
// Create excepted schemaType proto.
|
||||
SchemaProto exceptedProto = SchemaProto.newBuilder()
|
||||
// Create expected schemaType proto.
|
||||
SchemaProto expectedProto = SchemaProto.newBuilder()
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
|
||||
.build();
|
||||
|
||||
List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
|
||||
expectedTypes.add(mVisibilitySchemaProto);
|
||||
expectedTypes.addAll(expectedProto.getTypesList());
|
||||
assertThat(mAppSearchImpl.getSchemaProto().getTypesList())
|
||||
.containsExactlyElementsIn(exceptedProto.getTypesList());
|
||||
.containsExactlyElementsIn(expectedTypes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception {
|
||||
mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
|
||||
"schema1").build()), /*forceOverride=*/false);
|
||||
mAppSearchImpl.setVisibility("database", Set.of("schema1"));
|
||||
|
||||
// "schema1" is platform hidden now
|
||||
assertThat(mAppSearchImpl.getVisibilityStore().getPlatformHiddenSchemas(
|
||||
"database")).containsExactly("database/schema1");
|
||||
|
||||
// Add a new schema, and include the already-existing "schema1"
|
||||
mAppSearchImpl.setSchema("database", Set.of(new AppSearchSchema.Builder(
|
||||
"schema1").build(), new AppSearchSchema.Builder(
|
||||
"schema2").build()), /*forceOverride=*/false);
|
||||
|
||||
// Check that "schema1" is still platform hidden, but "schema2" is the default platform
|
||||
// visible.
|
||||
assertThat(mAppSearchImpl.getVisibilityStore().getPlatformHiddenSchemas(
|
||||
"database")).containsExactly("database/schema1");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -342,15 +457,18 @@ public class AppSearchImplTest {
|
||||
// Set schema Email and Document to AppSearch database1
|
||||
mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false);
|
||||
|
||||
// Create excepted schemaType proto.
|
||||
SchemaProto exceptedProto = SchemaProto.newBuilder()
|
||||
// Create expected schemaType proto.
|
||||
SchemaProto expectedProto = SchemaProto.newBuilder()
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("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.getSchemaProto().getTypesList())
|
||||
.containsExactlyElementsIn(exceptedProto.getTypesList());
|
||||
.containsExactlyElementsIn(expectedTypes);
|
||||
|
||||
final Set<AppSearchSchema> finalSchemas = Collections.singleton(new AppSearchSchema.Builder(
|
||||
"Email").build());
|
||||
@@ -364,11 +482,15 @@ public class AppSearchImplTest {
|
||||
mAppSearchImpl.setSchema("database1", finalSchemas, /*forceOverride=*/true);
|
||||
|
||||
// Check Document schema is removed.
|
||||
exceptedProto = SchemaProto.newBuilder()
|
||||
expectedProto = SchemaProto.newBuilder()
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
|
||||
.build();
|
||||
|
||||
expectedTypes = new ArrayList<>();
|
||||
expectedTypes.add(mVisibilitySchemaProto);
|
||||
expectedTypes.addAll(expectedProto.getTypesList());
|
||||
assertThat(mAppSearchImpl.getSchemaProto().getTypesList())
|
||||
.containsExactlyElementsIn(exceptedProto.getTypesList());
|
||||
.containsExactlyElementsIn(expectedTypes);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -382,8 +504,8 @@ public class AppSearchImplTest {
|
||||
mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false);
|
||||
mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/false);
|
||||
|
||||
// Create excepted schemaType proto.
|
||||
SchemaProto exceptedProto = SchemaProto.newBuilder()
|
||||
// Create expected schemaType proto.
|
||||
SchemaProto expectedProto = SchemaProto.newBuilder()
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Document"))
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
|
||||
@@ -391,23 +513,114 @@ public class AppSearchImplTest {
|
||||
.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.getSchemaProto().getTypesList())
|
||||
.containsExactlyElementsIn(exceptedProto.getTypesList());
|
||||
.containsExactlyElementsIn(expectedTypes);
|
||||
|
||||
// Save only Email to database1 this time.
|
||||
schemas = Collections.singleton(new AppSearchSchema.Builder("Email").build());
|
||||
mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/true);
|
||||
|
||||
// Create excepted schemaType list, database 1 should only contain Email but database 2
|
||||
// Create expected schemaType list, database 1 should only contain Email but database 2
|
||||
// remains in same.
|
||||
exceptedProto = SchemaProto.newBuilder()
|
||||
expectedProto = SchemaProto.newBuilder()
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
|
||||
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Document"))
|
||||
.build();
|
||||
|
||||
// Check nothing changed in database2.
|
||||
expectedTypes = new ArrayList<>();
|
||||
expectedTypes.add(mVisibilitySchemaProto);
|
||||
expectedTypes.addAll(expectedProto.getTypesList());
|
||||
assertThat(mAppSearchImpl.getSchemaProto().getTypesList())
|
||||
.containsExactlyElementsIn(exceptedProto.getTypesList());
|
||||
.containsExactlyElementsIn(expectedTypes);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRemoveSchema_removedFromVisibilityStore() throws Exception {
|
||||
mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
|
||||
"schema1").build()), /*forceOverride=*/false);
|
||||
mAppSearchImpl.setVisibility("database", Set.of("schema1"));
|
||||
|
||||
// "schema1" is platform hidden now
|
||||
assertThat(mAppSearchImpl.getVisibilityStore().getPlatformHiddenSchemas(
|
||||
"database")).containsExactly("database/schema1");
|
||||
|
||||
// Remove "schema1" by force overriding
|
||||
mAppSearchImpl.setSchema("database", Collections.emptySet(), /*forceOverride=*/true);
|
||||
|
||||
// Check that "schema1" is no longer considered platform hidden
|
||||
assertThat(
|
||||
mAppSearchImpl.getVisibilityStore().getPlatformHiddenSchemas("database")).isEmpty();
|
||||
|
||||
// Add "schema1" back, it gets default visibility settings which means it's not platform
|
||||
// hidden.
|
||||
mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
|
||||
"schema1").build()), /*forceOverride=*/false);
|
||||
assertThat(
|
||||
mAppSearchImpl.getVisibilityStore().getPlatformHiddenSchemas("database")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetVisibility_defaultPlatformVisible() throws Exception {
|
||||
mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
|
||||
"Schema").build()), /*forceOverride=*/false);
|
||||
assertThat(
|
||||
mAppSearchImpl.getVisibilityStore().getPlatformHiddenSchemas("database")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetVisibility_platformHidden() throws Exception {
|
||||
mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
|
||||
"Schema").build()), /*forceOverride=*/false);
|
||||
mAppSearchImpl.setVisibility("database", Set.of("Schema"));
|
||||
assertThat(mAppSearchImpl.getVisibilityStore().getPlatformHiddenSchemas(
|
||||
"database")).containsExactly("database/Schema");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetVisibility_unknownSchema() throws Exception {
|
||||
mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
|
||||
"Schema").build()), /*forceOverride=*/false);
|
||||
|
||||
// We'll throw an exception if a client tries to set visibility on a schema we don't know
|
||||
// about.
|
||||
AppSearchException e = expectThrows(AppSearchException.class,
|
||||
() -> mAppSearchImpl.setVisibility("database", Set.of("UnknownSchema")));
|
||||
assertThat(e).hasMessageThat().contains("Unknown schema(s)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasSchemaType() throws Exception {
|
||||
// Nothing exists yet
|
||||
assertThat(mAppSearchImpl.hasSchemaType("database", "Schema")).isFalse();
|
||||
|
||||
mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
|
||||
"Schema").build()), /*forceOverride=*/false);
|
||||
assertThat(mAppSearchImpl.hasSchemaType("database", "Schema")).isTrue();
|
||||
|
||||
assertThat(mAppSearchImpl.hasSchemaType("database", "UnknownSchema")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDatabases() throws Exception {
|
||||
// No client databases exist yet, but the VisibilityStore's does
|
||||
assertThat(mAppSearchImpl.getDatabases()).containsExactly(VisibilityStore.DATABASE_NAME);
|
||||
|
||||
// Has database1
|
||||
mAppSearchImpl.setSchema("database1", Collections.singleton(new AppSearchSchema.Builder(
|
||||
"schema").build()), /*forceOverride=*/false);
|
||||
assertThat(mAppSearchImpl.getDatabases()).containsExactly(
|
||||
VisibilityStore.DATABASE_NAME, "database1");
|
||||
|
||||
// Has both databases
|
||||
mAppSearchImpl.setSchema("database2", Collections.singleton(new AppSearchSchema.Builder(
|
||||
"schema").build()), /*forceOverride=*/false);
|
||||
assertThat(mAppSearchImpl.getDatabases()).containsExactly(
|
||||
VisibilityStore.DATABASE_NAME, "database1", "database2");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public class VisibilityStoreTest {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
|
||||
private AppSearchImpl mAppSearchImpl;
|
||||
private VisibilityStore mVisibilityStore;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder());
|
||||
mVisibilityStore = mAppSearchImpl.getVisibilityStore();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetVisibility() throws Exception {
|
||||
mVisibilityStore.setVisibility(
|
||||
"database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema2"));
|
||||
assertThat(mVisibilityStore.getPlatformHiddenSchemas("database"))
|
||||
.containsExactly("schema1", "schema2");
|
||||
|
||||
// New .setVisibility() call completely overrides previous visibility settings. So
|
||||
// "schema1" isn't preserved.
|
||||
mVisibilityStore.setVisibility(
|
||||
"database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema3"));
|
||||
assertThat(mVisibilityStore.getPlatformHiddenSchemas("database"))
|
||||
.containsExactly("schema1", "schema3");
|
||||
|
||||
mVisibilityStore.setVisibility(
|
||||
"database", /*platformHiddenSchemas=*/ Collections.emptySet());
|
||||
assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveSchemas() throws Exception {
|
||||
mVisibilityStore.setVisibility(
|
||||
"database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema2"));
|
||||
|
||||
// Removed just schema1
|
||||
mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema1"));
|
||||
assertThat(mVisibilityStore.getPlatformHiddenSchemas("database"))
|
||||
.containsExactly("schema2");
|
||||
|
||||
// Removed everything now
|
||||
mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema2"));
|
||||
assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,9 +20,9 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.appsearch.AppSearchSchema;
|
||||
|
||||
import com.android.server.appsearch.proto.IndexingConfig;
|
||||
import com.android.server.appsearch.proto.PropertyConfigProto;
|
||||
import com.android.server.appsearch.proto.SchemaTypeConfigProto;
|
||||
import com.android.server.appsearch.proto.StringIndexingConfig;
|
||||
import com.android.server.appsearch.proto.TermMatchType;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -51,18 +51,20 @@ public class SchemaToProtoConverterTest {
|
||||
.setPropertyName("subject")
|
||||
.setDataType(PropertyConfigProto.DataType.Code.STRING)
|
||||
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
|
||||
.setIndexingConfig(
|
||||
com.android.server.appsearch.proto.IndexingConfig.newBuilder()
|
||||
.setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN)
|
||||
.setStringIndexingConfig(
|
||||
StringIndexingConfig.newBuilder()
|
||||
.setTokenizerType(
|
||||
StringIndexingConfig.TokenizerType.Code.PLAIN)
|
||||
.setTermMatchType(TermMatchType.Code.PREFIX)
|
||||
)
|
||||
).addProperties(PropertyConfigProto.newBuilder()
|
||||
.setPropertyName("body")
|
||||
.setDataType(PropertyConfigProto.DataType.Code.STRING)
|
||||
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
|
||||
.setIndexingConfig(
|
||||
com.android.server.appsearch.proto.IndexingConfig.newBuilder()
|
||||
.setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN)
|
||||
.setStringIndexingConfig(
|
||||
StringIndexingConfig.newBuilder()
|
||||
.setTokenizerType(
|
||||
StringIndexingConfig.TokenizerType.Code.PLAIN)
|
||||
.setTermMatchType(TermMatchType.Code.PREFIX)
|
||||
)
|
||||
).build();
|
||||
@@ -93,18 +95,20 @@ public class SchemaToProtoConverterTest {
|
||||
.setPropertyName("artist")
|
||||
.setDataType(PropertyConfigProto.DataType.Code.STRING)
|
||||
.setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
|
||||
.setIndexingConfig(
|
||||
com.android.server.appsearch.proto.IndexingConfig.newBuilder()
|
||||
.setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN)
|
||||
.setStringIndexingConfig(
|
||||
StringIndexingConfig.newBuilder()
|
||||
.setTokenizerType(
|
||||
StringIndexingConfig.TokenizerType.Code.PLAIN)
|
||||
.setTermMatchType(TermMatchType.Code.PREFIX)
|
||||
)
|
||||
).addProperties(PropertyConfigProto.newBuilder()
|
||||
.setPropertyName("pubDate")
|
||||
.setDataType(PropertyConfigProto.DataType.Code.INT64)
|
||||
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
|
||||
.setIndexingConfig(
|
||||
com.android.server.appsearch.proto.IndexingConfig.newBuilder()
|
||||
.setTokenizerType(IndexingConfig.TokenizerType.Code.NONE)
|
||||
.setStringIndexingConfig(
|
||||
StringIndexingConfig.newBuilder()
|
||||
.setTokenizerType(
|
||||
StringIndexingConfig.TokenizerType.Code.NONE)
|
||||
.setTermMatchType(TermMatchType.Code.UNKNOWN)
|
||||
)
|
||||
).build();
|
||||
|
||||
Reference in New Issue
Block a user