Create test utils and shims for porting AppSearch CTS tests.

The shims adapt the framework API to ListenableFuture so that the
jetpack tests can be reused.

Bug: 170997047
Bug: 162450968
Bug: 175661706
Test: CtsAppSearchTestCases
Change-Id: I649a94b784fb74af137788e3a08106296dcb57fb
This commit is contained in:
Alexander Dorokhine
2020-12-15 21:50:49 -08:00
parent 81b11c39ad
commit ab03e08bfb
11 changed files with 423 additions and 739 deletions

View File

@@ -0,0 +1,24 @@
// Copyright (C) 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.
java_library {
name: "AppSearchTestUtils",
srcs: ["java/**/*.java"],
libs: [
"androidx.test.ext.junit",
"framework",
"framework-appsearch",
"guava",
"truth-prebuilt",
],
}

View File

@@ -0,0 +1,145 @@
/*
* 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.testing;
import android.annotation.NonNull;
import android.app.appsearch.AppSearchBatchResult;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSession;
import android.app.appsearch.BatchResultCallback;
import android.app.appsearch.GenericDocument;
import android.app.appsearch.GetByUriRequest;
import android.app.appsearch.PutDocumentsRequest;
import android.app.appsearch.RemoveByUriRequest;
import android.app.appsearch.SearchResults;
import android.app.appsearch.SearchSpec;
import android.app.appsearch.SetSchemaRequest;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* This test class adapts the AppSearch Framework API to ListenableFuture, so it can be tested via
* a consistent interface.
* @hide
*/
public class AppSearchSessionShim {
private final AppSearchSession mAppSearchSession;
private final ExecutorService mExecutor;
@NonNull
public static ListenableFuture<AppSearchResult<AppSearchSessionShim>> createSearchSession(
@NonNull AppSearchManager.SearchContext searchContext) {
Context context = ApplicationProvider.getApplicationContext();
AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
SettableFuture<AppSearchResult<AppSearchSession>> future = SettableFuture.create();
ExecutorService executor = Executors.newCachedThreadPool();
appSearchManager.createSearchSession(searchContext, executor, future::set);
return Futures.transform(future, (instance) -> {
if (!instance.isSuccess()) {
return AppSearchResult.newFailedResult(
instance.getResultCode(), instance.getErrorMessage());
}
AppSearchSession searchSession = instance.getResultValue();
AppSearchSessionShim shim = new AppSearchSessionShim(searchSession, executor);
return AppSearchResult.newSuccessfulResult(shim);
}, executor);
}
private AppSearchSessionShim(
@NonNull AppSearchSession session, @NonNull ExecutorService executor) {
mAppSearchSession = Preconditions.checkNotNull(session);
mExecutor = Preconditions.checkNotNull(executor);
}
@NonNull
public ListenableFuture<AppSearchResult<Void>> setSchema(@NonNull SetSchemaRequest request) {
SettableFuture<AppSearchResult<Void>> future = SettableFuture.create();
mAppSearchSession.setSchema(request, mExecutor, future::set);
return future;
}
@NonNull
public ListenableFuture<AppSearchBatchResult<String, Void>> putDocuments(
@NonNull PutDocumentsRequest request) {
SettableFuture<AppSearchBatchResult<String, Void>> future = SettableFuture.create();
mAppSearchSession.putDocuments(
request, mExecutor, new BatchResultCallbackAdapter<>(future));
return future;
}
@NonNull
public ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByUri(
@NonNull GetByUriRequest request) {
SettableFuture<AppSearchBatchResult<String, GenericDocument>> future =
SettableFuture.create();
mAppSearchSession.getByUri(request, mExecutor, new BatchResultCallbackAdapter<>(future));
return future;
}
@NonNull
public SearchResultsShim query(
@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
SearchResults searchResults =
mAppSearchSession.query(queryExpression, searchSpec, mExecutor);
return new SearchResultsShim(searchResults);
}
@NonNull
public ListenableFuture<AppSearchBatchResult<String, Void>> removeByUri(
@NonNull RemoveByUriRequest request) {
SettableFuture<AppSearchBatchResult<String, Void>> future = SettableFuture.create();
mAppSearchSession.removeByUri(request, mExecutor, new BatchResultCallbackAdapter<>(future));
return future;
}
@NonNull
public ListenableFuture<AppSearchResult<Void>> removeByQuery(
@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
SettableFuture<AppSearchResult<Void>> future = SettableFuture.create();
mAppSearchSession.removeByQuery(queryExpression, searchSpec, mExecutor, future::set);
return future;
}
private static final class BatchResultCallbackAdapter<K, V>
implements BatchResultCallback<K, V> {
private final SettableFuture<AppSearchBatchResult<K, V>> mFuture;
BatchResultCallbackAdapter(SettableFuture<AppSearchBatchResult<K, V>> future) {
mFuture = future;
}
@Override
public void onResult(AppSearchBatchResult<K, V> result) {
mFuture.set(result);
}
@Override
public void onSystemError(Throwable t) {
mFuture.setException(t);
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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.testing;
import android.annotation.NonNull;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.GlobalSearchSession;
import android.app.appsearch.SearchResults;
import android.app.appsearch.SearchSpec;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* This test class adapts the AppSearch Framework API to ListenableFuture, so it can be tested via
* a consistent interface.
* @hide
*/
public class GlobalSearchSessionShim {
private final GlobalSearchSession mGlobalSearchSession;
private final ExecutorService mExecutor;
@NonNull
public static ListenableFuture<AppSearchResult<GlobalSearchSessionShim>>
createGlobalSearchSession() {
Context context = ApplicationProvider.getApplicationContext();
AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
SettableFuture<AppSearchResult<GlobalSearchSession>> future = SettableFuture.create();
ExecutorService executor = Executors.newCachedThreadPool();
appSearchManager.createGlobalSearchSession(executor, future::set);
return Futures.transform(future, (instance) -> {
if (!instance.isSuccess()) {
return AppSearchResult.newFailedResult(
instance.getResultCode(), instance.getErrorMessage());
}
GlobalSearchSession searchSession = instance.getResultValue();
GlobalSearchSessionShim shim = new GlobalSearchSessionShim(searchSession, executor);
return AppSearchResult.newSuccessfulResult(shim);
}, executor);
}
private GlobalSearchSessionShim(
@NonNull GlobalSearchSession session, @NonNull ExecutorService executor) {
mGlobalSearchSession = Preconditions.checkNotNull(session);
mExecutor = Preconditions.checkNotNull(executor);
}
@NonNull
public SearchResultsShim query(
@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
SearchResults searchResults =
mGlobalSearchSession.query(queryExpression, searchSpec, mExecutor);
return new SearchResultsShim(searchResults);
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.testing;
import android.annotation.NonNull;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.SearchResult;
import android.app.appsearch.SearchResults;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import java.io.Closeable;
import java.util.List;
/**
* This test class adapts the AppSearch Framework API to ListenableFuture, so it can be tested via
* a consistent interface.
* @hide
*/
public class SearchResultsShim implements Closeable {
private final SearchResults mSearchResults;
SearchResultsShim(@NonNull SearchResults searchResults) {
mSearchResults = Preconditions.checkNotNull(searchResults);
}
@NonNull
public ListenableFuture<AppSearchResult<List<SearchResult>>> getNextPage() {
SettableFuture<AppSearchResult<List<SearchResult>>> future = SettableFuture.create();
mSearchResults.getNextPage(future::set);
return future;
}
@Override
public void close() {
mSearchResults.close();
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.testing;
import static com.google.common.truth.Truth.assertThat;
import android.app.appsearch.AppSearchBatchResult;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.GenericDocument;
import android.app.appsearch.GetByUriRequest;
import android.app.appsearch.SearchResult;
import android.app.appsearch.SetSchemaRequest;
import android.content.Context;
import com.google.common.collect.ImmutableList;
import junit.framework.AssertionFailedError;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
public class AppSearchTestUtils {
// List of databases that may be used in tests. Keeping them in a centralized location helps
// #cleanup know which databases to clear.
public static final String DEFAULT_DATABASE = AppSearchManager.DEFAULT_DATABASE_NAME;
public static final String DB_1 = "testDb1";
public static final String DB_2 = "testDb2";
public static void cleanup(Context context) throws Exception {
List<String> databases = ImmutableList.of(DEFAULT_DATABASE, DB_1, DB_2);
for (String database : databases) {
AppSearchSessionShim session = checkIsResultSuccess(
AppSearchSessionShim.createSearchSession(
new AppSearchManager.SearchContext.Builder()
.setDatabaseName(database).build()));
checkIsResultSuccess(session.setSchema(
new SetSchemaRequest.Builder().setForceOverride(true).build()));
}
}
public static <V> V checkIsResultSuccess(Future<AppSearchResult<V>> future) throws Exception {
AppSearchResult<V> result = future.get();
if (!result.isSuccess()) {
throw new AssertionFailedError("AppSearchResult not successful: " + result);
}
return result.getResultValue();
}
public static <K, V> AppSearchBatchResult<K, V> checkIsBatchResultSuccess(
Future<AppSearchBatchResult<K, V>> future) throws Exception {
AppSearchBatchResult<K, V> result = future.get();
if (!result.isSuccess()) {
throw new AssertionFailedError("AppSearchBatchResult not successful: " + result);
}
return result;
}
public static List<GenericDocument> doGet(
AppSearchSessionShim session, String namespace, String... uris) throws Exception {
AppSearchBatchResult<String, GenericDocument> result = checkIsBatchResultSuccess(
session.getByUri(
new GetByUriRequest.Builder()
.setNamespace(namespace).addUri(uris).build()));
assertThat(result.getSuccesses()).hasSize(uris.length);
assertThat(result.getFailures()).isEmpty();
List<GenericDocument> list = new ArrayList<>(uris.length);
for (String uri : uris) {
list.add(result.getSuccesses().get(uri));
}
return list;
}
public static List<GenericDocument> convertSearchResultsToDocuments(
SearchResultsShim searchResults) throws Exception {
List<SearchResult> results = checkIsResultSuccess(searchResults.getNextPage());
List<GenericDocument> documents = new ArrayList<>();
while (results.size() > 0) {
for (SearchResult result : results) {
documents.add(result.getDocument());
}
results = checkIsResultSuccess(searchResults.getNextPage());
}
return documents;
}
}

View File

@@ -0,0 +1,20 @@
/*
* 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.
*/
/**
* @hide
*/
package com.android.server.appsearch.testing;

View File

@@ -1,79 +0,0 @@
/*
* 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());
}
}

View File

@@ -1,202 +0,0 @@
/*
* 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.AppSearchSchema;
import android.app.appsearch.AppSearchSchema.PropertyConfig;
import android.app.appsearch.exceptions.IllegalSchemaException;
import org.junit.Test;
public class AppSearchSchemaCtsTest {
@Test
public void testInvalidEnums() {
PropertyConfig.Builder builder = new PropertyConfig.Builder("test");
expectThrows(IllegalArgumentException.class, () -> builder.setDataType(99));
expectThrows(IllegalArgumentException.class, () -> builder.setCardinality(99));
}
@Test
public void testMissingFields() {
PropertyConfig.Builder builder = new PropertyConfig.Builder("test");
IllegalSchemaException e = expectThrows(IllegalSchemaException.class, builder::build);
assertThat(e).hasMessageThat().contains("Missing field: dataType");
builder.setDataType(PropertyConfig.DATA_TYPE_DOCUMENT);
e = expectThrows(IllegalSchemaException.class, builder::build);
assertThat(e).hasMessageThat().contains("Missing field: schemaType");
builder.setSchemaType("TestType");
e = expectThrows(IllegalSchemaException.class, builder::build);
assertThat(e).hasMessageThat().contains("Missing field: cardinality");
builder.setCardinality(PropertyConfig.CARDINALITY_REPEATED);
builder.build();
}
@Test
public void testDuplicateProperties() {
AppSearchSchema.Builder builder =
new AppSearchSchema.Builder("Email")
.addProperty(
new PropertyConfig.Builder("subject")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build());
IllegalSchemaException e =
expectThrows(
IllegalSchemaException.class,
() ->
builder.addProperty(
new PropertyConfig.Builder("subject")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(
PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(
PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build()));
assertThat(e).hasMessageThat().contains("Property defined more than once: subject");
}
@Test
public void testEquals_identical() {
AppSearchSchema schema1 =
new AppSearchSchema.Builder("Email")
.addProperty(
new PropertyConfig.Builder("subject")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build())
.build();
AppSearchSchema schema2 =
new AppSearchSchema.Builder("Email")
.addProperty(
new PropertyConfig.Builder("subject")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build())
.build();
assertThat(schema1).isEqualTo(schema2);
assertThat(schema1.hashCode()).isEqualTo(schema2.hashCode());
}
@Test
public void testEquals_differentOrder() {
AppSearchSchema schema1 =
new AppSearchSchema.Builder("Email")
.addProperty(
new PropertyConfig.Builder("subject")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build())
.build();
AppSearchSchema schema2 =
new AppSearchSchema.Builder("Email")
.addProperty(
new PropertyConfig.Builder("subject")
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.build())
.build();
assertThat(schema1).isEqualTo(schema2);
assertThat(schema1.hashCode()).isEqualTo(schema2.hashCode());
}
@Test
public void testEquals_failure() {
AppSearchSchema schema1 =
new AppSearchSchema.Builder("Email")
.addProperty(
new PropertyConfig.Builder("subject")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build())
.build();
AppSearchSchema schema2 =
new AppSearchSchema.Builder("Email")
.addProperty(
new PropertyConfig.Builder("subject")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(
PropertyConfig
.INDEXING_TYPE_EXACT_TERMS) // Different
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build())
.build();
assertThat(schema1).isNotEqualTo(schema2);
assertThat(schema1.hashCode()).isNotEqualTo(schema2.hashCode());
}
@Test
public void testEquals_failure_differentOrder() {
AppSearchSchema schema1 =
new AppSearchSchema.Builder("Email")
.addProperty(
new PropertyConfig.Builder("subject")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build())
.addProperty(
new PropertyConfig.Builder("body")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build())
.build();
// Order of 'body' and 'subject' has been switched
AppSearchSchema schema2 =
new AppSearchSchema.Builder("Email")
.addProperty(
new PropertyConfig.Builder("body")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build())
.addProperty(
new PropertyConfig.Builder("subject")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build())
.build();
assertThat(schema1).isNotEqualTo(schema2);
assertThat(schema1.hashCode()).isNotEqualTo(schema2.hashCode());
}
}

View File

@@ -1,286 +0,0 @@
/*
* 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.GenericDocument;
import org.junit.Test;
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")
.setCreationTimestampMillis(12345L)
.build();
private static final GenericDocument sDocumentProperties2 =
new GenericDocument.Builder<>("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
.setCreationTimestampMillis(6789L)
.build();
@Test
public void testDocumentEquals_identical() {
GenericDocument document1 =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setTtlMillis(1L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.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)
.build();
GenericDocument document2 =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setTtlMillis(1L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.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)
.build();
assertThat(document1).isEqualTo(document2);
assertThat(document1.hashCode()).isEqualTo(document2.hashCode());
}
@Test
public void testDocumentEquals_differentOrder() {
GenericDocument document1 =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyDocument(
"documentKey1", sDocumentProperties1, sDocumentProperties2)
.setPropertyString(
"stringKey1", "test-value1", "test-value2", "test-value3")
.build();
// Create second document with same parameter but different order.
GenericDocument document2 =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyDocument(
"documentKey1", sDocumentProperties1, sDocumentProperties2)
.setPropertyString(
"stringKey1", "test-value1", "test-value2", "test-value3")
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.build();
assertThat(document1).isEqualTo(document2);
assertThat(document1.hashCode()).isEqualTo(document2.hashCode());
}
@Test
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")
.setCreationTimestampMillis(5L)
.setPropertyLong("longKey1", 1L, 2L, 4L) // Different
.build();
assertThat(document1).isNotEqualTo(document2);
assertThat(document1.hashCode()).isNotEqualTo(document2.hashCode());
}
@Test
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")
.setCreationTimestampMillis(5L)
.setPropertyBoolean("booleanKey1", true, true, false) // Different
.build();
assertThat(document1).isNotEqualTo(document2);
assertThat(document1.hashCode()).isNotEqualTo(document2.hashCode());
}
@Test
public void testDocumentGetSingleValue() {
GenericDocument document =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setScore(1)
.setTtlMillis(1L)
.setPropertyLong("longKey1", 1L)
.setPropertyDouble("doubleKey1", 1.0)
.setPropertyBoolean("booleanKey1", true)
.setPropertyString("stringKey1", "test-value1")
.setPropertyBytes("byteKey1", sByteArray1)
.setPropertyDocument("documentKey1", sDocumentProperties1)
.build();
assertThat(document.getUri()).isEqualTo("uri1");
assertThat(document.getTtlMillis()).isEqualTo(1L);
assertThat(document.getSchemaType()).isEqualTo("schemaType1");
assertThat(document.getCreationTimestampMillis()).isEqualTo(5);
assertThat(document.getScore()).isEqualTo(1);
assertThat(document.getPropertyLong("longKey1")).isEqualTo(1L);
assertThat(document.getPropertyDouble("doubleKey1")).isEqualTo(1.0);
assertThat(document.getPropertyBoolean("booleanKey1")).isTrue();
assertThat(document.getPropertyString("stringKey1")).isEqualTo("test-value1");
assertThat(document.getPropertyBytes("byteKey1"))
.asList()
.containsExactly((byte) 1, (byte) 2, (byte) 3);
assertThat(document.getPropertyDocument("documentKey1")).isEqualTo(sDocumentProperties1);
}
@Test
public void testDocumentGetArrayValues() {
GenericDocument document =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.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)
.build();
assertThat(document.getUri()).isEqualTo("uri1");
assertThat(document.getSchemaType()).isEqualTo("schemaType1");
assertThat(document.getPropertyLongArray("longKey1")).asList().containsExactly(1L, 2L, 3L);
assertThat(document.getPropertyDoubleArray("doubleKey1"))
.usingExactEquality()
.containsExactly(1.0, 2.0, 3.0);
assertThat(document.getPropertyBooleanArray("booleanKey1"))
.asList()
.containsExactly(true, false, true);
assertThat(document.getPropertyStringArray("stringKey1"))
.asList()
.containsExactly("test-value1", "test-value2", "test-value3");
assertThat(document.getPropertyBytesArray("byteKey1"))
.asList()
.containsExactly(sByteArray1, sByteArray2);
assertThat(document.getPropertyDocumentArray("documentKey1"))
.asList()
.containsExactly(sDocumentProperties1, sDocumentProperties2);
}
@Test
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)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString("stringKey1", "String1", "String2", "String3")
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyDocument(
"documentKey1", sDocumentProperties1, sDocumentProperties2)
.build();
String exceptedString =
"{ key: 'creationTimestampMillis' value: 5 } "
+ "{ key: 'namespace' value: } "
+ "{ key: 'properties' value: "
+ "{ key: 'booleanKey1' value: [ 'true' 'false' 'true' ] } "
+ "{ key: 'byteKey1' value: "
+ "{ key: 'byteArray' value: [ '1' '2' '3' ] } "
+ "{ key: 'byteArray' value: [ '4' '5' '6' '7' ] } } "
+ "{ key: 'documentKey1' value: [ '"
+ "{ key: 'creationTimestampMillis' value: 12345 } "
+ "{ key: 'namespace' value: } "
+ "{ key: 'properties' value: } "
+ "{ key: 'schemaType' value: sDocumentPropertiesSchemaType1 } "
+ "{ key: 'score' value: 0 } "
+ "{ key: 'ttlMillis' value: 0 } "
+ "{ key: 'uri' value: sDocumentProperties1 } ' '"
+ "{ key: 'creationTimestampMillis' value: 6789 } "
+ "{ key: 'namespace' value: } "
+ "{ key: 'properties' value: } "
+ "{ key: 'schemaType' value: sDocumentPropertiesSchemaType2 } "
+ "{ key: 'score' value: 0 } "
+ "{ key: 'ttlMillis' value: 0 } "
+ "{ key: 'uri' value: sDocumentProperties2 } ' ] } "
+ "{ key: 'doubleKey1' value: [ '1.0' '2.0' '3.0' ] } "
+ "{ key: 'longKey1' value: [ '1' '2' '3' ] } "
+ "{ key: 'stringKey1' value: [ 'String1' 'String2' 'String3' ] } } "
+ "{ key: 'schemaType' value: schemaType1 } "
+ "{ key: 'score' value: 0 } "
+ "{ key: 'ttlMillis' value: 0 } "
+ "{ key: 'uri' value: uri1 } ";
assertThat(document.toString()).isEqualTo(exceptedString);
}
@Test
public void testDocumentGetValues_differentTypes() {
GenericDocument document =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setScore(1)
.setPropertyLong("longKey1", 1L)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString(
"stringKey1", "test-value1", "test-value2", "test-value3")
.build();
// Get a value for a key that doesn't exist
assertThat(document.getPropertyDouble("doubleKey1")).isEqualTo(0.0);
assertThat(document.getPropertyDoubleArray("doubleKey1")).isNull();
// Get a value with a single element as an array and as a single value
assertThat(document.getPropertyLong("longKey1")).isEqualTo(1L);
assertThat(document.getPropertyLongArray("longKey1")).asList().containsExactly(1L);
// Get a value with multiple elements as an array and as a single value
assertThat(document.getPropertyString("stringKey1")).isEqualTo("test-value1");
assertThat(document.getPropertyStringArray("stringKey1"))
.asList()
.containsExactly("test-value1", "test-value2", "test-value3");
// Get a value of the wrong type
assertThat(document.getPropertyDouble("longKey1")).isEqualTo(0.0);
assertThat(document.getPropertyDoubleArray("longKey1")).isNull();
}
@Test
public void testDocumentInvalid() {
GenericDocument.Builder<?> builder = new GenericDocument.Builder<>("uri1", "schemaType1");
expectThrows(
IllegalArgumentException.class,
() -> builder.setPropertyBoolean("test", new boolean[] {}));
}
}

View File

@@ -1,67 +0,0 @@
/*
* 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)
.setResultCountPerPage(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.getResultCountPerPage()).isEqualTo(42);
assertThat(searchSpec.getOrder()).isEqualTo(SearchSpec.ORDER_ASCENDING);
assertThat(searchSpec.getRankingStrategy())
.isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE);
}
}

View File

@@ -1,105 +0,0 @@
/*
* 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.customer;
import static com.google.common.truth.Truth.assertThat;
import android.annotation.NonNull;
import android.app.appsearch.GenericDocument;
import org.junit.Test;
/**
* Tests that {@link GenericDocument} and {@link GenericDocument.Builder} are extendable by
* developers.
*
* <p>This class is intentionally in a different package than {@link GenericDocument} to make sure
* there are no package-private methods required for external developers to add custom types.
*/
public class CustomerDocumentTest {
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 final GenericDocument DOCUMENT_PROPERTIES2 =
new GenericDocument.Builder<>("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
.build();
@Test
public void testBuildCustomerDocument() {
CustomerDocument customerDocument =
new CustomerDocument.Builder("uri1")
.setScore(1)
.setCreationTimestampMillis(0)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString(
"stringKey1", "test-value1", "test-value2", "test-value3")
.setPropertyBytes("byteKey1", BYTE_ARRAY1, BYTE_ARRAY2)
.setPropertyDocument(
"documentKey1", DOCUMENT_PROPERTIES1, DOCUMENT_PROPERTIES2)
.build();
assertThat(customerDocument.getUri()).isEqualTo("uri1");
assertThat(customerDocument.getSchemaType()).isEqualTo("customerDocument");
assertThat(customerDocument.getScore()).isEqualTo(1);
assertThat(customerDocument.getCreationTimestampMillis()).isEqualTo(0L);
assertThat(customerDocument.getPropertyLongArray("longKey1"))
.asList()
.containsExactly(1L, 2L, 3L);
assertThat(customerDocument.getPropertyDoubleArray("doubleKey1"))
.usingExactEquality()
.containsExactly(1.0, 2.0, 3.0);
assertThat(customerDocument.getPropertyBooleanArray("booleanKey1"))
.asList()
.containsExactly(true, false, true);
assertThat(customerDocument.getPropertyStringArray("stringKey1"))
.asList()
.containsExactly("test-value1", "test-value2", "test-value3");
assertThat(customerDocument.getPropertyBytesArray("byteKey1"))
.asList()
.containsExactly(BYTE_ARRAY1, BYTE_ARRAY2);
assertThat(customerDocument.getPropertyDocumentArray("documentKey1"))
.asList()
.containsExactly(DOCUMENT_PROPERTIES1, DOCUMENT_PROPERTIES2);
}
/**
* An example document type for test purposes, defined outside of {@link GenericDocument} (the
* way an external developer would define it).
*/
private static class CustomerDocument extends GenericDocument {
private CustomerDocument(GenericDocument document) {
super(document);
}
public static class Builder extends GenericDocument.Builder<CustomerDocument.Builder> {
private Builder(@NonNull String uri) {
super(uri, "customerDocument");
}
@Override
@NonNull
public CustomerDocument build() {
return new CustomerDocument(super.build());
}
}
}
}