1 /* 2 * Copyright 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app.appsearch.testutil; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static com.google.common.truth.Truth.assertWithMessage; 21 22 import android.annotation.NonNull; 23 import android.app.appsearch.AppSearchBatchResult; 24 import android.app.appsearch.AppSearchSessionShim; 25 import android.app.appsearch.GenericDocument; 26 import android.app.appsearch.GetByDocumentIdRequest; 27 import android.app.appsearch.SearchResult; 28 import android.app.appsearch.SearchResultsShim; 29 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 30 31 import com.android.server.appsearch.external.localstorage.visibilitystore.CallerAccess; 32 import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityChecker; 33 import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore; 34 35 import org.junit.rules.RuleChain; 36 37 import java.security.MessageDigest; 38 import java.security.NoSuchAlgorithmException; 39 import java.util.ArrayList; 40 import java.util.List; 41 import java.util.Set; 42 import java.util.concurrent.Future; 43 import java.util.concurrent.ThreadLocalRandom; 44 45 /** 46 * Class with helper functions for testing for AppSearch. 47 * 48 * @hide 49 */ 50 public class AppSearchTestUtils { 51 /** Checks batch result. */ 52 @NonNull checkIsBatchResultSuccess( @onNull Future<AppSearchBatchResult<K, V>> future)53 public static <K, V> AppSearchBatchResult<K, V> checkIsBatchResultSuccess( 54 @NonNull Future<AppSearchBatchResult<K, V>> future) throws Exception { 55 AppSearchBatchResult<K, V> result = future.get(); 56 assertWithMessage("AppSearchBatchResult not successful: " + result) 57 .that(result.isSuccess()) 58 .isTrue(); 59 return result; 60 } 61 62 /** Gets documents from ids. */ 63 @NonNull doGet( @onNull AppSearchSessionShim session, @NonNull String namespace, @NonNull String... ids)64 public static List<GenericDocument> doGet( 65 @NonNull AppSearchSessionShim session, 66 @NonNull String namespace, 67 @NonNull String... ids) 68 throws Exception { 69 AppSearchBatchResult<String, GenericDocument> result = 70 checkIsBatchResultSuccess( 71 session.getByDocumentIdAsync( 72 new GetByDocumentIdRequest.Builder(namespace).addIds(ids).build())); 73 assertThat(result.getSuccesses()).hasSize(ids.length); 74 assertThat(result.getFailures()).isEmpty(); 75 List<GenericDocument> list = new ArrayList<>(ids.length); 76 for (String id : ids) { 77 list.add(result.getSuccesses().get(id)); 78 } 79 return list; 80 } 81 82 /** Gets documents from {@link GetByDocumentIdRequest}. */ 83 @NonNull doGet( @onNull AppSearchSessionShim session, @NonNull GetByDocumentIdRequest request)84 public static List<GenericDocument> doGet( 85 @NonNull AppSearchSessionShim session, @NonNull GetByDocumentIdRequest request) 86 throws Exception { 87 AppSearchBatchResult<String, GenericDocument> result = 88 checkIsBatchResultSuccess(session.getByDocumentIdAsync(request)); 89 Set<String> ids = request.getIds(); 90 assertThat(result.getSuccesses()).hasSize(ids.size()); 91 assertThat(result.getFailures()).isEmpty(); 92 List<GenericDocument> list = new ArrayList<>(ids.size()); 93 for (String id : ids) { 94 list.add(result.getSuccesses().get(id)); 95 } 96 return list; 97 } 98 99 /** Extracts documents from {@link SearchResultsShim}. */ 100 @NonNull convertSearchResultsToDocuments( @onNull SearchResultsShim searchResults)101 public static List<GenericDocument> convertSearchResultsToDocuments( 102 @NonNull SearchResultsShim searchResults) throws Exception { 103 List<SearchResult> results = retrieveAllSearchResults(searchResults); 104 List<GenericDocument> documents = new ArrayList<>(results.size()); 105 for (SearchResult result : results) { 106 documents.add(result.getGenericDocument()); 107 } 108 return documents; 109 } 110 111 /** Extracts all {@link SearchResult} from {@link SearchResultsShim}. */ 112 @NonNull retrieveAllSearchResults( @onNull SearchResultsShim searchResults)113 public static List<SearchResult> retrieveAllSearchResults( 114 @NonNull SearchResultsShim searchResults) throws Exception { 115 List<SearchResult> page = searchResults.getNextPageAsync().get(); 116 List<SearchResult> results = new ArrayList<>(); 117 while (!page.isEmpty()) { 118 results.addAll(page); 119 page = searchResults.getNextPageAsync().get(); 120 } 121 return results; 122 } 123 124 /** 125 * Creates a mock {@link VisibilityChecker} where schema is searchable if prefixedSchema is one 126 * of the provided set of visiblePrefixedSchemas and caller does not have system access. 127 * 128 * @param visiblePrefixedSchemas Schema types that are accessible to any caller. 129 * @return Mocked {@link VisibilityChecker} instance. 130 */ 131 @NonNull createMockVisibilityChecker( @onNull Set<String> visiblePrefixedSchemas)132 public static VisibilityChecker createMockVisibilityChecker( 133 @NonNull Set<String> visiblePrefixedSchemas) { 134 return new VisibilityChecker() { 135 @Override 136 public boolean isSchemaSearchableByCaller( 137 @NonNull CallerAccess callerAccess, 138 @NonNull String packageName, 139 @NonNull String prefixedSchema, 140 @NonNull VisibilityStore visibilityStore) { 141 return visiblePrefixedSchemas.contains(prefixedSchema); 142 } 143 144 @Override 145 public boolean doesCallerHaveSystemAccess(@NonNull String s) { 146 return false; 147 } 148 }; 149 } 150 151 /** 152 * Creates a mock {@link VisibilityChecker}, where it can be configured if schema is searchable 153 * by caller and caller does not have system access. 154 * 155 * @param isSchemaSearchableByCaller Schema visibility for caller. 156 * @return Mocked {@link VisibilityChecker} instance. 157 */ 158 @NonNull 159 public static VisibilityChecker createMockVisibilityChecker( 160 boolean isSchemaSearchableByCaller) { 161 return new VisibilityChecker() { 162 @Override 163 public boolean isSchemaSearchableByCaller( 164 @NonNull CallerAccess callerAccess, 165 @NonNull String packageName, 166 @NonNull String prefixedSchema, 167 @NonNull VisibilityStore visibilityStore) { 168 return isSchemaSearchableByCaller; 169 } 170 171 @Override 172 public boolean doesCallerHaveSystemAccess(@NonNull String s) { 173 return false; 174 } 175 }; 176 } 177 178 /** Generate an array contains random bytes for the given length. */ 179 @NonNull 180 public static byte[] generateRandomBytes(int length) { 181 byte[] bytes = new byte[length]; 182 ThreadLocalRandom.current().nextBytes(bytes); 183 return bytes; 184 } 185 186 /** Calculate the sha-256 digest for the given data. */ 187 @NonNull 188 public static byte[] calculateDigest(@NonNull byte[] data) throws NoSuchAlgorithmException { 189 MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); 190 messageDigest.update(data); 191 return messageDigest.digest(); 192 } 193 194 /** Returns a rule chain of common test rules for tests. */ 195 @NonNull 196 public static RuleChain createCommonTestRules() { 197 // We don't have another common test rule yet, but we can add one later with .around() here 198 return RuleChain.outerRule(DeviceFlagsValueProvider.createCheckFlagsRule()); 199 } 200 201 private AppSearchTestUtils() {} 202 } 203