1 // Copyright (C) 2019 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package com.google.android.icing; 16 17 import static com.google.common.truth.Truth.assertThat; 18 import static com.google.common.truth.Truth.assertWithMessage; 19 20 import com.google.android.icing.IcingSearchEngine; 21 import com.google.android.icing.proto.BatchGetResultProto; 22 import com.google.android.icing.proto.BatchPutResultProto; 23 import com.google.android.icing.proto.BlobProto; 24 import com.google.android.icing.proto.DebugInfoResultProto; 25 import com.google.android.icing.proto.DebugInfoVerbosity; 26 import com.google.android.icing.proto.DeleteByNamespaceResultProto; 27 import com.google.android.icing.proto.DeleteByQueryResultProto; 28 import com.google.android.icing.proto.DeleteBySchemaTypeResultProto; 29 import com.google.android.icing.proto.DeleteResultProto; 30 import com.google.android.icing.proto.DocumentProto; 31 import com.google.android.icing.proto.GetAllNamespacesResultProto; 32 import com.google.android.icing.proto.GetOptimizeInfoResultProto; 33 import com.google.android.icing.proto.GetResultProto; 34 import com.google.android.icing.proto.GetResultSpecProto; 35 import com.google.android.icing.proto.GetSchemaResultProto; 36 import com.google.android.icing.proto.GetSchemaTypeResultProto; 37 import com.google.android.icing.proto.IcingSearchEngineOptions; 38 import com.google.android.icing.proto.InitializeResultProto; 39 import com.google.android.icing.proto.LogSeverity; 40 import com.google.android.icing.proto.OptimizeResultProto; 41 import com.google.android.icing.proto.PersistToDiskResultProto; 42 import com.google.android.icing.proto.PersistType; 43 import com.google.android.icing.proto.PropertyConfigProto; 44 import com.google.android.icing.proto.PropertyProto; 45 import com.google.android.icing.proto.PutDocumentRequest; 46 import com.google.android.icing.proto.PutResultProto; 47 import com.google.android.icing.proto.ReportUsageResultProto; 48 import com.google.android.icing.proto.ResetResultProto; 49 import com.google.android.icing.proto.ResultSpecProto; 50 import com.google.android.icing.proto.SchemaProto; 51 import com.google.android.icing.proto.SchemaTypeConfigProto; 52 import com.google.android.icing.proto.ScoringSpecProto; 53 import com.google.android.icing.proto.SearchResultProto; 54 import com.google.android.icing.proto.SearchSpecProto; 55 import com.google.android.icing.proto.SetSchemaRequestProto; 56 import com.google.android.icing.proto.SetSchemaResultProto; 57 import com.google.android.icing.proto.SnippetMatchProto; 58 import com.google.android.icing.proto.SnippetProto; 59 import com.google.android.icing.proto.StatusProto; 60 import com.google.android.icing.proto.StorageInfoResultProto; 61 import com.google.android.icing.proto.StringIndexingConfig; 62 import com.google.android.icing.proto.StringIndexingConfig.TokenizerType; 63 import com.google.android.icing.proto.SuggestionResponse; 64 import com.google.android.icing.proto.SuggestionScoringSpecProto; 65 import com.google.android.icing.proto.SuggestionSpecProto; 66 import com.google.android.icing.proto.TermMatchType; 67 import com.google.android.icing.proto.TermMatchType.Code; 68 import com.google.android.icing.proto.UsageReport; 69 import com.google.protobuf.ByteString; 70 import java.io.File; 71 import java.io.FileDescriptor; 72 import java.io.FileInputStream; 73 import java.io.FileOutputStream; 74 import java.lang.reflect.Field; 75 import java.security.MessageDigest; 76 import java.security.NoSuchAlgorithmException; 77 import java.util.HashMap; 78 import java.util.Map; 79 import java.util.Random; 80 import org.junit.After; 81 import org.junit.Before; 82 import org.junit.Ignore; 83 import org.junit.Rule; 84 import org.junit.Test; 85 import org.junit.rules.TemporaryFolder; 86 import org.junit.runner.RunWith; 87 import org.junit.runners.JUnit4; 88 89 /** 90 * This test is not intended to fully test the functionality of each API. But rather to test the JNI 91 * wrapper and Java interfaces of Icing library {@link IcingSearchEngine}. 92 */ 93 @RunWith(JUnit4.class) 94 public final class IcingSearchEngineTest { 95 @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); 96 97 private static final String SCHEMA_DATABASE_DELIMITER = "/"; 98 private static final String EMAIL_TYPE = "Email"; 99 100 private File tempDir; 101 102 private IcingSearchEngine icingSearchEngine; 103 createEmailTypeConfig()104 private static SchemaTypeConfigProto createEmailTypeConfig() { 105 return SchemaTypeConfigProto.newBuilder() 106 .setSchemaType(EMAIL_TYPE) 107 .addProperties( 108 PropertyConfigProto.newBuilder() 109 .setPropertyName("subject") 110 .setDataType(PropertyConfigProto.DataType.Code.STRING) 111 .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) 112 .setStringIndexingConfig( 113 StringIndexingConfig.newBuilder() 114 .setTokenizerType(TokenizerType.Code.PLAIN) 115 .setTermMatchType(TermMatchType.Code.PREFIX))) 116 .addProperties( 117 PropertyConfigProto.newBuilder() 118 .setPropertyName("body") 119 .setDataType(PropertyConfigProto.DataType.Code.STRING) 120 .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) 121 .setStringIndexingConfig( 122 StringIndexingConfig.newBuilder() 123 .setTokenizerType(TokenizerType.Code.PLAIN) 124 .setTermMatchType(TermMatchType.Code.PREFIX))) 125 .build(); 126 } 127 createEmailTypeConfigWithDatabase(String database)128 private static SchemaTypeConfigProto createEmailTypeConfigWithDatabase(String database) { 129 return SchemaTypeConfigProto.newBuilder() 130 .setSchemaType(database + SCHEMA_DATABASE_DELIMITER + EMAIL_TYPE) 131 .setDatabase(database) 132 .addProperties( 133 PropertyConfigProto.newBuilder() 134 .setPropertyName("subject") 135 .setDataType(PropertyConfigProto.DataType.Code.STRING) 136 .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) 137 .setStringIndexingConfig( 138 StringIndexingConfig.newBuilder() 139 .setTokenizerType(TokenizerType.Code.PLAIN) 140 .setTermMatchType(TermMatchType.Code.PREFIX))) 141 .addProperties( 142 PropertyConfigProto.newBuilder() 143 .setPropertyName("body") 144 .setDataType(PropertyConfigProto.DataType.Code.STRING) 145 .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) 146 .setStringIndexingConfig( 147 StringIndexingConfig.newBuilder() 148 .setTokenizerType(TokenizerType.Code.PLAIN) 149 .setTermMatchType(TermMatchType.Code.PREFIX))) 150 .build(); 151 } 152 createEmailDocument(String namespace, String uri)153 private static DocumentProto createEmailDocument(String namespace, String uri) { 154 return DocumentProto.newBuilder() 155 .setNamespace(namespace) 156 .setUri(uri) 157 .setSchema(EMAIL_TYPE) 158 .setCreationTimestampMs(1) // Arbitrary non-zero number so Icing doesn't override it 159 .build(); 160 } 161 162 /** Generate an array contains random bytes for the given length. */ generateRandomBytes(int length)163 private static byte[] generateRandomBytes(int length) { 164 byte[] bytes = new byte[length]; 165 Random rd = new Random(); // creating Random object 166 rd.nextBytes(bytes); 167 return bytes; 168 } 169 170 /** Calculate the sha-256 digest for the given data. */ calculateDigest(byte[] data)171 private static byte[] calculateDigest(byte[] data) throws NoSuchAlgorithmException { 172 MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); 173 messageDigest.update(data); 174 return messageDigest.digest(); 175 } 176 177 @Before setUp()178 public void setUp() throws Exception { 179 tempDir = temporaryFolder.newFolder(); 180 IcingSearchEngineOptions options = 181 IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build(); 182 icingSearchEngine = new IcingSearchEngine(options); 183 } 184 185 @After tearDown()186 public void tearDown() throws Exception { 187 icingSearchEngine.close(); 188 } 189 190 @Test testInitialize()191 public void testInitialize() throws Exception { 192 InitializeResultProto initializeResultProto = icingSearchEngine.initialize(); 193 assertStatusOk(initializeResultProto.getStatus()); 194 } 195 196 @Test testSetAndGetSchema()197 public void testSetAndGetSchema() throws Exception { 198 assertStatusOk(icingSearchEngine.initialize().getStatus()); 199 200 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 201 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 202 SetSchemaResultProto setSchemaResultProto = 203 icingSearchEngine.setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false); 204 assertStatusOk(setSchemaResultProto.getStatus()); 205 206 GetSchemaResultProto getSchemaResultProto = icingSearchEngine.getSchema(); 207 assertStatusOk(getSchemaResultProto.getStatus()); 208 assertThat(getSchemaResultProto.getSchema()).isEqualTo(schema); 209 210 GetSchemaTypeResultProto getSchemaTypeResultProto = 211 icingSearchEngine.getSchemaType(emailTypeConfig.getSchemaType()); 212 assertStatusOk(getSchemaTypeResultProto.getStatus()); 213 assertThat(getSchemaTypeResultProto.getSchemaTypeConfig()).isEqualTo(emailTypeConfig); 214 } 215 216 // TODO: b/383379132 - Re-enable this test once the JNI API is pre-registered and dropped back 217 // into g3. 218 @Ignore 219 @Test setAndGetSchemaWithDatabase_ok()220 public void setAndGetSchemaWithDatabase_ok() throws Exception { 221 IcingSearchEngineOptions options = 222 IcingSearchEngineOptions.newBuilder() 223 .setBaseDir(tempDir.getCanonicalPath()) 224 .setEnableSchemaDatabase(true) 225 .build(); 226 IcingSearchEngine icingSearchEngine = new IcingSearchEngine(options); 227 assertStatusOk(icingSearchEngine.initialize().getStatus()); 228 229 String db1 = "db1"; 230 String db2 = "db2"; 231 SchemaProto db1Schema = 232 SchemaProto.newBuilder().addTypes(createEmailTypeConfigWithDatabase(db1)).build(); 233 SchemaProto db2Schema = 234 SchemaProto.newBuilder().addTypes(createEmailTypeConfigWithDatabase(db2)).build(); 235 236 SetSchemaRequestProto requestProto1 = 237 SetSchemaRequestProto.newBuilder() 238 .setSchema(db1Schema) 239 .setDatabase(db1) 240 .setIgnoreErrorsAndDeleteDocuments(false) 241 .build(); 242 SetSchemaResultProto setSchemaResultProto = 243 icingSearchEngine.setSchemaWithRequestProto(requestProto1); 244 assertStatusOk(setSchemaResultProto.getStatus()); 245 246 SetSchemaRequestProto requestProto2 = 247 SetSchemaRequestProto.newBuilder() 248 .setSchema(db2Schema) 249 .setDatabase(db2) 250 .setIgnoreErrorsAndDeleteDocuments(false) 251 .build(); 252 setSchemaResultProto = icingSearchEngine.setSchemaWithRequestProto(requestProto2); 253 assertStatusOk(setSchemaResultProto.getStatus()); 254 255 // Get schema for individual databases. 256 GetSchemaResultProto getSchemaResultProto = icingSearchEngine.getSchemaForDatabase(db1); 257 assertStatusOk(getSchemaResultProto.getStatus()); 258 assertThat(getSchemaResultProto.getSchema()).isEqualTo(db1Schema); 259 260 getSchemaResultProto = icingSearchEngine.getSchemaForDatabase(db2); 261 assertStatusOk(getSchemaResultProto.getStatus()); 262 assertThat(getSchemaResultProto.getSchema()).isEqualTo(db2Schema); 263 264 // The getSchema() API should still return the full schema. 265 SchemaProto fullSchema = 266 SchemaProto.newBuilder() 267 .addTypes(createEmailTypeConfigWithDatabase(db1)) 268 .addTypes(createEmailTypeConfigWithDatabase(db2)) 269 .build(); 270 getSchemaResultProto = icingSearchEngine.getSchema(); 271 assertStatusOk(getSchemaResultProto.getStatus()); 272 assertThat(getSchemaResultProto.getSchema()).isEqualTo(fullSchema); 273 } 274 275 @Test testPutAndGetDocuments()276 public void testPutAndGetDocuments() throws Exception { 277 assertStatusOk(icingSearchEngine.initialize().getStatus()); 278 279 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 280 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 281 assertThat( 282 icingSearchEngine 283 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 284 .getStatus() 285 .getCode()) 286 .isEqualTo(StatusProto.Code.OK); 287 288 DocumentProto emailDocument = createEmailDocument("namespace", "uri"); 289 PutResultProto putResultProto = icingSearchEngine.put(emailDocument); 290 assertStatusOk(putResultProto.getStatus()); 291 292 GetResultProto getResultProto = 293 icingSearchEngine.get("namespace", "uri", GetResultSpecProto.getDefaultInstance()); 294 assertStatusOk(getResultProto.getStatus()); 295 assertThat(getResultProto.getDocument()).isEqualTo(emailDocument); 296 } 297 298 @Test testBatchPutAndGetDocuments()299 public void testBatchPutAndGetDocuments() throws Exception { 300 assertStatusOk(icingSearchEngine.initialize().getStatus()); 301 302 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 303 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 304 assertThat( 305 icingSearchEngine 306 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 307 .getStatus() 308 .getCode()) 309 .isEqualTo(StatusProto.Code.OK); 310 311 DocumentProto emailDocument1 = createEmailDocument("namespace", "uri1"); 312 DocumentProto emailDocument2 = createEmailDocument("namespace", "uri2"); 313 PutDocumentRequest putDocumentRequest = 314 PutDocumentRequest.newBuilder() 315 .addDocuments(emailDocument1) 316 .addDocuments(emailDocument2) 317 .build(); 318 BatchPutResultProto batchPutResultProto = icingSearchEngine.batchPut(putDocumentRequest); 319 320 assertStatusOk(batchPutResultProto.getStatus()); 321 assertThat(batchPutResultProto.getPutResultProtos(0).getUri()).isEqualTo("uri1"); 322 assertStatusOk(batchPutResultProto.getPutResultProtos(0).getStatus()); 323 assertThat(batchPutResultProto.getPutResultProtos(1).getUri()).isEqualTo("uri2"); 324 assertStatusOk(batchPutResultProto.getPutResultProtos(1).getStatus()); 325 326 // PersistToDiskResultProto should not be set if persist_type is not set in the 327 // PutDocumentRequest. 328 assertThat(batchPutResultProto.getPersistToDiskResultProto().getStatus().getCode()) 329 .isEqualTo(StatusProto.Code.UNKNOWN); 330 331 GetResultSpecProto getResultSpecProto = 332 GetResultSpecProto.newBuilder() 333 .setNamespaceRequested("namespace") 334 .addIds("uri1") 335 .addIds("uri2") 336 .build(); 337 BatchGetResultProto batchGetResultProto = icingSearchEngine.batchGet(getResultSpecProto); 338 339 assertStatusOk(batchGetResultProto.getStatus()); 340 // Check doc1 341 DocumentProto document = batchGetResultProto.getGetResultProtos(0).getDocument(); 342 assertStatusOk(batchGetResultProto.getGetResultProtos(0).getStatus()); 343 assertThat(document).isEqualTo(emailDocument1); 344 // Check doc2 345 document = batchGetResultProto.getGetResultProtos(1).getDocument(); 346 assertStatusOk(batchGetResultProto.getGetResultProtos(1).getStatus()); 347 assertThat(document).isEqualTo(emailDocument2); 348 } 349 350 @Test testBatchGetWithEmptyResult()351 public void testBatchGetWithEmptyResult() throws Exception { 352 assertStatusOk(icingSearchEngine.initialize().getStatus()); 353 354 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 355 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 356 assertThat( 357 icingSearchEngine 358 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 359 .getStatus() 360 .getCode()) 361 .isEqualTo(StatusProto.Code.OK); 362 363 DocumentProto emailDocument1 = createEmailDocument("namespace", "uri1"); 364 DocumentProto emailDocument2 = createEmailDocument("namespace", "uri2"); 365 PutDocumentRequest putDocumentRequest = 366 PutDocumentRequest.newBuilder() 367 .addDocuments(emailDocument1) 368 .addDocuments(emailDocument2) 369 .build(); 370 BatchPutResultProto batchPutResultProto = icingSearchEngine.batchPut(putDocumentRequest); 371 assertStatusOk(batchPutResultProto.getStatus()); 372 373 // no ids. 374 GetResultSpecProto getResultSpecProto = 375 GetResultSpecProto.newBuilder().setNamespaceRequested("namespace").build(); 376 BatchGetResultProto batchGetResultProto = icingSearchEngine.batchGet(getResultSpecProto); 377 378 // Check no doc returned if no ids are specified. 379 assertStatusOk(batchGetResultProto.getStatus()); 380 assertThat(batchGetResultProto.getGetResultProtosList()).isEmpty(); 381 382 // empty namespace. 383 getResultSpecProto = GetResultSpecProto.newBuilder().addIds("uri1").build(); 384 batchGetResultProto = icingSearchEngine.batchGet(getResultSpecProto); 385 assertStatusOk(batchGetResultProto.getStatus()); 386 assertThat(batchGetResultProto.getGetResultProtosList()).hasSize(1); 387 assertThat(batchGetResultProto.getGetResultProtos(0).getStatus().getCode()) 388 .isEqualTo(StatusProto.Code.NOT_FOUND); 389 390 // different namespace. 391 getResultSpecProto = 392 GetResultSpecProto.newBuilder() 393 .setNamespaceRequested("otherNameSpace") 394 .addIds("uri1") 395 .addIds("uri2") 396 .build(); 397 batchGetResultProto = icingSearchEngine.batchGet(getResultSpecProto); 398 399 // Check not found returned if namespace is different. 400 assertStatusOk(batchGetResultProto.getStatus()); 401 assertThat(batchGetResultProto.getGetResultProtosList()).hasSize(2); 402 assertThat(batchGetResultProto.getGetResultProtos(0).getStatus().getCode()) 403 .isEqualTo(StatusProto.Code.NOT_FOUND); 404 assertThat(batchGetResultProto.getGetResultProtos(1).getStatus().getCode()) 405 .isEqualTo(StatusProto.Code.NOT_FOUND); 406 } 407 408 @Test testBatchPutWithDuplicatedDocuments()409 public void testBatchPutWithDuplicatedDocuments() throws Exception { 410 assertStatusOk(icingSearchEngine.initialize().getStatus()); 411 412 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 413 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 414 assertThat( 415 icingSearchEngine 416 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 417 .getStatus() 418 .getCode()) 419 .isEqualTo(StatusProto.Code.OK); 420 421 // Two docs with same uri. 422 DocumentProto emailDocument1 = createEmailDocument("namespace", "uri"); 423 DocumentProto emailDocument2 = createEmailDocument("namespace", "uri"); 424 PutDocumentRequest putDocumentRequest = 425 PutDocumentRequest.newBuilder() 426 .addDocuments(emailDocument1) 427 .addDocuments(emailDocument2) 428 .build(); 429 BatchPutResultProto batchPutResultProto = icingSearchEngine.batchPut(putDocumentRequest); 430 431 // We should still get two putResults back. That's intended behavior. 432 assertThat(batchPutResultProto.getPutResultProtosList()).hasSize(2); 433 assertThat(batchPutResultProto.getPutResultProtos(0).getUri()).isEqualTo("uri"); 434 assertStatusOk(batchPutResultProto.getPutResultProtos(0).getStatus()); 435 assertThat(batchPutResultProto.getPutResultProtos(0).getWasReplacement()).isFalse(); 436 assertThat(batchPutResultProto.getPutResultProtos(1).getUri()).isEqualTo("uri"); 437 assertStatusOk(batchPutResultProto.getPutResultProtos(1).getStatus()); 438 assertThat(batchPutResultProto.getPutResultProtos(1).getWasReplacement()).isTrue(); 439 440 // PersistToDiskResultProto should not be set if persist_type is not set in the 441 // PutDocumentRequest. 442 assertThat(batchPutResultProto.getPersistToDiskResultProto().getStatus().getCode()) 443 .isEqualTo(StatusProto.Code.UNKNOWN); 444 } 445 446 @Test testBatchPutWithEmptyRequest()447 public void testBatchPutWithEmptyRequest() throws Exception { 448 assertStatusOk(icingSearchEngine.initialize().getStatus()); 449 450 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 451 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 452 assertThat( 453 icingSearchEngine 454 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 455 .getStatus() 456 .getCode()) 457 .isEqualTo(StatusProto.Code.OK); 458 459 PutDocumentRequest putDocumentRequest = PutDocumentRequest.getDefaultInstance(); 460 BatchPutResultProto batchPutResultProto = icingSearchEngine.batchPut(putDocumentRequest); 461 462 BatchPutResultProto expected = 463 BatchPutResultProto.newBuilder() 464 .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK)) 465 .build(); 466 assertThat(batchPutResultProto).isEqualTo(expected); 467 468 // PersistToDiskResultProto should not be set if persist_type is not set in the 469 // PutDocumentRequest. 470 assertThat(batchPutResultProto.getPersistToDiskResultProto().getStatus().getCode()) 471 .isEqualTo(StatusProto.Code.UNKNOWN); 472 } 473 474 @Test testBatchPutAndGetDocumentsWithError()475 public void testBatchPutAndGetDocumentsWithError() throws Exception { 476 assertStatusOk(icingSearchEngine.initialize().getStatus()); 477 478 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 479 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 480 assertThat( 481 icingSearchEngine 482 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 483 .getStatus() 484 .getCode()) 485 .isEqualTo(StatusProto.Code.OK); 486 // Document 1 has no namespace. 487 DocumentProto emailDocument1 = DocumentProto.newBuilder().setUri("uri1").build(); 488 DocumentProto emailDocument2 = createEmailDocument("namespace", "uri2"); 489 PutDocumentRequest putDocumentRequest = 490 PutDocumentRequest.newBuilder() 491 .addDocuments(emailDocument1) 492 .addDocuments(emailDocument2) 493 .build(); 494 BatchPutResultProto batchPutResultProto = icingSearchEngine.batchPut(putDocumentRequest); 495 496 PutResultProto putResult1 = batchPutResultProto.getPutResultProtos(0); 497 // result0 error as namespace is missing. 498 assertThat(putResult1.getUri()).isEqualTo("uri1"); 499 assertWithMessage(putResult1.getStatus().getMessage()) 500 .that(putResult1.getStatus().getCode()) 501 .isEqualTo(StatusProto.Code.INVALID_ARGUMENT); 502 // result1 is ok. 503 assertThat(batchPutResultProto.getPutResultProtos(1).getUri()).isEqualTo("uri2"); 504 assertStatusOk(batchPutResultProto.getPutResultProtos(1).getStatus()); 505 506 // PersistToDiskResultProto should not be set if persist_type is not set in the 507 // PutDocumentRequest. 508 assertThat(batchPutResultProto.getPersistToDiskResultProto().getStatus().getCode()) 509 .isEqualTo(StatusProto.Code.UNKNOWN); 510 511 // Check document 1 512 GetResultProto getResultProto = 513 icingSearchEngine.get("namespace", "uri1", GetResultSpecProto.getDefaultInstance()); 514 assertWithMessage(getResultProto.getStatus().getMessage()) 515 .that(getResultProto.getStatus().getCode()) 516 .isEqualTo(StatusProto.Code.NOT_FOUND); 517 // check document 2 518 getResultProto = 519 icingSearchEngine.get("namespace", "uri2", GetResultSpecProto.getDefaultInstance()); 520 assertStatusOk(getResultProto.getStatus()); 521 assertThat(getResultProto.getDocument()).isEqualTo(emailDocument2); 522 } 523 524 @Test testBatchPutWithPersistToDisk()525 public void testBatchPutWithPersistToDisk() throws Exception { 526 assertStatusOk(icingSearchEngine.initialize().getStatus()); 527 528 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 529 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 530 assertThat( 531 icingSearchEngine 532 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 533 .getStatus() 534 .getCode()) 535 .isEqualTo(StatusProto.Code.OK); 536 537 DocumentProto emailDocument1 = createEmailDocument("namespace", "uri1"); 538 DocumentProto emailDocument2 = createEmailDocument("namespace", "uri2"); 539 PutDocumentRequest putDocumentRequest = 540 PutDocumentRequest.newBuilder() 541 .addDocuments(emailDocument1) 542 .addDocuments(emailDocument2) 543 .setPersistType(PersistType.Code.FULL) 544 .build(); 545 BatchPutResultProto batchPutResultProto = icingSearchEngine.batchPut(putDocumentRequest); 546 547 assertThat(batchPutResultProto.getPutResultProtos(0).getUri()).isEqualTo("uri1"); 548 assertStatusOk(batchPutResultProto.getPutResultProtos(0).getStatus()); 549 assertThat(batchPutResultProto.getPutResultProtos(1).getUri()).isEqualTo("uri2"); 550 assertStatusOk(batchPutResultProto.getPutResultProtos(1).getStatus()); 551 552 // PersistToDisk should be called if persist_type is set in the PutDocumentRequest. 553 assertStatusOk(batchPutResultProto.getPersistToDiskResultProto().getStatus()); 554 } 555 556 @Test testSearch()557 public void testSearch() throws Exception { 558 assertStatusOk(icingSearchEngine.initialize().getStatus()); 559 560 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 561 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 562 assertThat( 563 icingSearchEngine 564 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 565 .getStatus() 566 .getCode()) 567 .isEqualTo(StatusProto.Code.OK); 568 569 DocumentProto emailDocument = 570 createEmailDocument("namespace", "uri").toBuilder() 571 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo")) 572 .build(); 573 assertStatusOk(icingSearchEngine.put(emailDocument).getStatus()); 574 575 SearchSpecProto searchSpec = 576 SearchSpecProto.newBuilder() 577 .setQuery("foo") 578 .setTermMatchType(TermMatchType.Code.PREFIX) 579 .build(); 580 581 SearchResultProto searchResultProto = 582 icingSearchEngine.search( 583 searchSpec, 584 ScoringSpecProto.getDefaultInstance(), 585 ResultSpecProto.getDefaultInstance()); 586 assertStatusOk(searchResultProto.getStatus()); 587 assertThat(searchResultProto.getResultsCount()).isEqualTo(1); 588 assertThat(searchResultProto.getResults(0).getDocument()).isEqualTo(emailDocument); 589 590 assertThat(searchResultProto.getQueryStats().hasNativeToJavaStartTimestampMs()).isTrue(); 591 assertThat(searchResultProto.getQueryStats().hasNativeToJavaJniLatencyMs()).isTrue(); 592 assertThat(searchResultProto.getQueryStats().hasJavaToNativeJniLatencyMs()).isTrue(); 593 assertThat(searchResultProto.getQueryStats().getNativeToJavaStartTimestampMs()) 594 .isGreaterThan(0); 595 assertThat(searchResultProto.getQueryStats().getNativeToJavaJniLatencyMs()).isAtLeast(0); 596 assertThat(searchResultProto.getQueryStats().getJavaToNativeJniLatencyMs()).isAtLeast(0); 597 } 598 599 @Test testGetNextPage()600 public void testGetNextPage() throws Exception { 601 assertStatusOk(icingSearchEngine.initialize().getStatus()); 602 603 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 604 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 605 assertThat( 606 icingSearchEngine 607 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 608 .getStatus() 609 .getCode()) 610 .isEqualTo(StatusProto.Code.OK); 611 612 Map<String, DocumentProto> documents = new HashMap<>(); 613 for (int i = 0; i < 10; i++) { 614 DocumentProto emailDocument = 615 createEmailDocument("namespace", "uri:" + i).toBuilder() 616 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo")) 617 .build(); 618 documents.put("uri:" + i, emailDocument); 619 assertWithMessage(icingSearchEngine.put(emailDocument).getStatus().getMessage()) 620 .that(icingSearchEngine.put(emailDocument).getStatus().getCode()) 621 .isEqualTo(StatusProto.Code.OK); 622 } 623 624 SearchSpecProto searchSpec = 625 SearchSpecProto.newBuilder() 626 .setQuery("foo") 627 .setTermMatchType(TermMatchType.Code.PREFIX) 628 .build(); 629 ResultSpecProto resultSpecProto = ResultSpecProto.newBuilder().setNumPerPage(1).build(); 630 631 SearchResultProto searchResultProto = 632 icingSearchEngine.search( 633 searchSpec, ScoringSpecProto.getDefaultInstance(), resultSpecProto); 634 assertStatusOk(searchResultProto.getStatus()); 635 assertThat(searchResultProto.getResultsCount()).isEqualTo(1); 636 DocumentProto resultDocument = searchResultProto.getResults(0).getDocument(); 637 assertThat(resultDocument).isEqualTo(documents.remove(resultDocument.getUri())); 638 639 assertThat(searchResultProto.getQueryStats().hasNativeToJavaStartTimestampMs()).isTrue(); 640 assertThat(searchResultProto.getQueryStats().hasNativeToJavaJniLatencyMs()).isTrue(); 641 assertThat(searchResultProto.getQueryStats().hasJavaToNativeJniLatencyMs()).isTrue(); 642 assertThat(searchResultProto.getQueryStats().getNativeToJavaStartTimestampMs()) 643 .isGreaterThan(0); 644 assertThat(searchResultProto.getQueryStats().getNativeToJavaJniLatencyMs()).isAtLeast(0); 645 assertThat(searchResultProto.getQueryStats().getJavaToNativeJniLatencyMs()).isAtLeast(0); 646 647 // fetch rest pages 648 for (int i = 1; i < 5; i++) { 649 searchResultProto = icingSearchEngine.getNextPage(searchResultProto.getNextPageToken()); 650 assertWithMessage(searchResultProto.getStatus().getMessage()) 651 .that(searchResultProto.getStatus().getCode()) 652 .isEqualTo(StatusProto.Code.OK); 653 assertThat(searchResultProto.getResultsCount()).isEqualTo(1); 654 resultDocument = searchResultProto.getResults(0).getDocument(); 655 assertThat(resultDocument).isEqualTo(documents.remove(resultDocument.getUri())); 656 } 657 658 // invalidate rest result 659 icingSearchEngine.invalidateNextPageToken(searchResultProto.getNextPageToken()); 660 661 searchResultProto = icingSearchEngine.getNextPage(searchResultProto.getNextPageToken()); 662 assertStatusOk(searchResultProto.getStatus()); 663 assertThat(searchResultProto.getResultsCount()).isEqualTo(0); 664 } 665 666 @Ignore // b/350530146 667 @Test writeAndReadBlob_blobContentMatches()668 public void writeAndReadBlob_blobContentMatches() throws Exception { 669 // 1 Arrange: set up IcingSearchEngine with and blob data 670 File tempDir = temporaryFolder.newFolder(); 671 IcingSearchEngineOptions options = 672 IcingSearchEngineOptions.newBuilder() 673 .setBaseDir(tempDir.getCanonicalPath()) 674 .setEnableBlobStore(true) 675 .build(); 676 IcingSearchEngine icing = new IcingSearchEngine(options); 677 assertStatusOk(icing.initialize().getStatus()); 678 679 byte[] data = generateRandomBytes(100); // 10 Bytes 680 byte[] digest = calculateDigest(data); 681 PropertyProto.BlobHandleProto blobHandle = 682 PropertyProto.BlobHandleProto.newBuilder() 683 .setDigest(ByteString.copyFrom(digest)) 684 .setNamespace("namespace") 685 .build(); 686 687 // 2 Act: write the blob and read it back. 688 BlobProto openWriteBlobProto = icing.openWriteBlob(blobHandle); 689 assertStatusOk(openWriteBlobProto.getStatus()); 690 Field field = FileDescriptor.class.getDeclaredField("fd"); 691 field.setAccessible(true); // Make the field accessible 692 693 // Create a new FileDescriptor object 694 FileDescriptor writeFd = new FileDescriptor(); 695 696 // Set the file descriptor value using reflection 697 field.setInt(writeFd, openWriteBlobProto.getFileDescriptor()); 698 699 try (FileOutputStream outputStream = new FileOutputStream(writeFd)) { 700 outputStream.write(data); 701 } 702 703 // Commit and read the blob. 704 BlobProto commitBlobProto = icing.commitBlob(blobHandle); 705 assertStatusOk(commitBlobProto.getStatus()); 706 707 BlobProto openReadBlobProto = icing.openReadBlob(blobHandle); 708 assertStatusOk(openReadBlobProto.getStatus()); 709 710 FileDescriptor readFd = new FileDescriptor(); 711 field.setInt(readFd, openReadBlobProto.getFileDescriptor()); 712 byte[] output = new byte[data.length]; 713 try (FileInputStream inputStream = new FileInputStream(readFd)) { 714 inputStream.read(output); 715 } 716 717 // 3 Assert: the blob content matches. 718 assertThat(output).isEqualTo(data); 719 } 720 721 @Ignore // b/350530146 722 @Test removeBlob()723 public void removeBlob() throws Exception { 724 // 1 Arrange: set up IcingSearchEngine with and blob data 725 File tempDir = temporaryFolder.newFolder(); 726 IcingSearchEngineOptions options = 727 IcingSearchEngineOptions.newBuilder() 728 .setBaseDir(tempDir.getCanonicalPath()) 729 .setEnableBlobStore(true) 730 .build(); 731 IcingSearchEngine icing = new IcingSearchEngine(options); 732 assertStatusOk(icing.initialize().getStatus()); 733 734 byte[] data = generateRandomBytes(100); // 10 Bytes 735 byte[] digest = calculateDigest(data); 736 PropertyProto.BlobHandleProto blobHandle = 737 PropertyProto.BlobHandleProto.newBuilder() 738 .setNamespace("ns") 739 .setDigest(ByteString.copyFrom(digest)) 740 .build(); 741 742 // 2 Act: write the blob and read it back. 743 BlobProto openWriteBlobProto = icing.openWriteBlob(blobHandle); 744 assertStatusOk(openWriteBlobProto.getStatus()); 745 Field field = FileDescriptor.class.getDeclaredField("fd"); 746 field.setAccessible(true); // Make the field accessible 747 748 // Create a new FileDescriptor object 749 FileDescriptor writeFd = new FileDescriptor(); 750 751 // Set the file descriptor value using reflection 752 field.setInt(writeFd, openWriteBlobProto.getFileDescriptor()); 753 754 try (FileOutputStream outputStream = new FileOutputStream(writeFd)) { 755 outputStream.write(data); 756 } 757 758 // Remove the blob. 759 BlobProto removeBlobProto = icing.removeBlob(blobHandle); 760 assertStatusOk(removeBlobProto.getStatus()); 761 762 // Commit will not found. 763 BlobProto commitBlobProto = icing.commitBlob(blobHandle); 764 assertThat(commitBlobProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND); 765 } 766 767 @Test testDelete()768 public void testDelete() throws Exception { 769 assertStatusOk(icingSearchEngine.initialize().getStatus()); 770 771 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 772 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 773 assertThat( 774 icingSearchEngine 775 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 776 .getStatus() 777 .getCode()) 778 .isEqualTo(StatusProto.Code.OK); 779 780 DocumentProto emailDocument = createEmailDocument("namespace", "uri"); 781 assertStatusOk(icingSearchEngine.put(emailDocument).getStatus()); 782 783 DeleteResultProto deleteResultProto = icingSearchEngine.delete("namespace", "uri"); 784 assertStatusOk(deleteResultProto.getStatus()); 785 786 GetResultProto getResultProto = 787 icingSearchEngine.get("namespace", "uri", GetResultSpecProto.getDefaultInstance()); 788 assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND); 789 } 790 791 @Test testDeleteByNamespace()792 public void testDeleteByNamespace() throws Exception { 793 assertStatusOk(icingSearchEngine.initialize().getStatus()); 794 795 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 796 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 797 assertThat( 798 icingSearchEngine 799 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 800 .getStatus() 801 .getCode()) 802 .isEqualTo(StatusProto.Code.OK); 803 804 DocumentProto emailDocument = createEmailDocument("namespace", "uri"); 805 assertStatusOk(icingSearchEngine.put(emailDocument).getStatus()); 806 807 DeleteByNamespaceResultProto deleteByNamespaceResultProto = 808 icingSearchEngine.deleteByNamespace("namespace"); 809 assertStatusOk(deleteByNamespaceResultProto.getStatus()); 810 811 GetResultProto getResultProto = 812 icingSearchEngine.get("namespace", "uri", GetResultSpecProto.getDefaultInstance()); 813 assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND); 814 } 815 816 @Test testDeleteBySchemaType()817 public void testDeleteBySchemaType() throws Exception { 818 assertStatusOk(icingSearchEngine.initialize().getStatus()); 819 820 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 821 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 822 assertThat( 823 icingSearchEngine 824 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 825 .getStatus() 826 .getCode()) 827 .isEqualTo(StatusProto.Code.OK); 828 829 DocumentProto emailDocument = createEmailDocument("namespace", "uri"); 830 assertStatusOk(icingSearchEngine.put(emailDocument).getStatus()); 831 832 DeleteBySchemaTypeResultProto deleteBySchemaTypeResultProto = 833 icingSearchEngine.deleteBySchemaType(EMAIL_TYPE); 834 assertStatusOk(deleteBySchemaTypeResultProto.getStatus()); 835 836 GetResultProto getResultProto = 837 icingSearchEngine.get("namespace", "uri", GetResultSpecProto.getDefaultInstance()); 838 assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND); 839 } 840 841 @Test testDeleteByQuery()842 public void testDeleteByQuery() throws Exception { 843 assertStatusOk(icingSearchEngine.initialize().getStatus()); 844 845 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 846 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 847 assertThat( 848 icingSearchEngine 849 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 850 .getStatus() 851 .getCode()) 852 .isEqualTo(StatusProto.Code.OK); 853 854 DocumentProto emailDocument1 = 855 createEmailDocument("namespace", "uri1").toBuilder() 856 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo")) 857 .build(); 858 859 assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus()); 860 DocumentProto emailDocument2 = 861 createEmailDocument("namespace", "uri2").toBuilder() 862 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("bar")) 863 .build(); 864 865 assertStatusOk(icingSearchEngine.put(emailDocument2).getStatus()); 866 867 SearchSpecProto searchSpec = 868 SearchSpecProto.newBuilder() 869 .setQuery("foo") 870 .setTermMatchType(TermMatchType.Code.PREFIX) 871 .build(); 872 873 SearchResultProto searchResultProto = 874 icingSearchEngine.search( 875 searchSpec, 876 ScoringSpecProto.getDefaultInstance(), 877 ResultSpecProto.getDefaultInstance()); 878 assertStatusOk(searchResultProto.getStatus()); 879 assertThat(searchResultProto.getResultsCount()).isEqualTo(1); 880 assertThat(searchResultProto.getResults(0).getDocument()).isEqualTo(emailDocument1); 881 882 DeleteByQueryResultProto deleteResultProto = icingSearchEngine.deleteByQuery(searchSpec); 883 assertStatusOk(deleteResultProto.getStatus()); 884 // By default, the deleteByQuery API does not return the summary about deleted documents, unless 885 // the returnDeletedDocumentInfo parameter is set to true. 886 assertThat(deleteResultProto.getDeletedDocumentsList()).isEmpty(); 887 888 GetResultProto getResultProto = 889 icingSearchEngine.get("namespace", "uri1", GetResultSpecProto.getDefaultInstance()); 890 assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND); 891 getResultProto = 892 icingSearchEngine.get("namespace", "uri2", GetResultSpecProto.getDefaultInstance()); 893 assertStatusOk(getResultProto.getStatus()); 894 } 895 896 @Test testDeleteByQueryWithDeletedDocumentInfo()897 public void testDeleteByQueryWithDeletedDocumentInfo() throws Exception { 898 assertStatusOk(icingSearchEngine.initialize().getStatus()); 899 900 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 901 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 902 assertThat( 903 icingSearchEngine 904 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 905 .getStatus() 906 .getCode()) 907 .isEqualTo(StatusProto.Code.OK); 908 909 DocumentProto emailDocument1 = 910 createEmailDocument("namespace", "uri1").toBuilder() 911 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo")) 912 .build(); 913 914 assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus()); 915 DocumentProto emailDocument2 = 916 createEmailDocument("namespace", "uri2").toBuilder() 917 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("bar")) 918 .build(); 919 920 assertStatusOk(icingSearchEngine.put(emailDocument2).getStatus()); 921 922 SearchSpecProto searchSpec = 923 SearchSpecProto.newBuilder() 924 .setQuery("foo") 925 .setTermMatchType(TermMatchType.Code.PREFIX) 926 .build(); 927 928 DeleteByQueryResultProto deleteResultProto = 929 icingSearchEngine.deleteByQuery(searchSpec, /* returnDeletedDocumentInfo= */ true); 930 assertStatusOk(deleteResultProto.getStatus()); 931 DeleteByQueryResultProto.DocumentGroupInfo info = 932 DeleteByQueryResultProto.DocumentGroupInfo.newBuilder() 933 .setNamespace("namespace") 934 .setSchema("Email") 935 .addUris("uri1") 936 .build(); 937 assertThat(deleteResultProto.getDeletedDocumentsList()).containsExactly(info); 938 939 GetResultProto getResultProto = 940 icingSearchEngine.get("namespace", "uri1", GetResultSpecProto.getDefaultInstance()); 941 assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND); 942 getResultProto = 943 icingSearchEngine.get("namespace", "uri2", GetResultSpecProto.getDefaultInstance()); 944 assertStatusOk(getResultProto.getStatus()); 945 } 946 947 @Test testPersistToDisk()948 public void testPersistToDisk() throws Exception { 949 assertStatusOk(icingSearchEngine.initialize().getStatus()); 950 951 PersistToDiskResultProto persistToDiskResultProto = 952 icingSearchEngine.persistToDisk(PersistType.Code.LITE); 953 assertStatusOk(persistToDiskResultProto.getStatus()); 954 } 955 956 @Test testOptimize()957 public void testOptimize() throws Exception { 958 assertStatusOk(icingSearchEngine.initialize().getStatus()); 959 960 OptimizeResultProto optimizeResultProto = icingSearchEngine.optimize(); 961 assertStatusOk(optimizeResultProto.getStatus()); 962 } 963 964 @Test testGetOptimizeInfo()965 public void testGetOptimizeInfo() throws Exception { 966 assertStatusOk(icingSearchEngine.initialize().getStatus()); 967 968 GetOptimizeInfoResultProto getOptimizeInfoResultProto = icingSearchEngine.getOptimizeInfo(); 969 assertStatusOk(getOptimizeInfoResultProto.getStatus()); 970 assertThat(getOptimizeInfoResultProto.getOptimizableDocs()).isEqualTo(0); 971 assertThat(getOptimizeInfoResultProto.getEstimatedOptimizableBytes()).isEqualTo(0); 972 } 973 974 @Test testGetStorageInfo()975 public void testGetStorageInfo() throws Exception { 976 assertStatusOk(icingSearchEngine.initialize().getStatus()); 977 978 StorageInfoResultProto storageInfoResultProto = icingSearchEngine.getStorageInfo(); 979 assertStatusOk(storageInfoResultProto.getStatus()); 980 } 981 982 @Test testGetDebugInfo()983 public void testGetDebugInfo() throws Exception { 984 assertStatusOk(icingSearchEngine.initialize().getStatus()); 985 986 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 987 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 988 assertThat( 989 icingSearchEngine 990 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 991 .getStatus() 992 .getCode()) 993 .isEqualTo(StatusProto.Code.OK); 994 995 DocumentProto emailDocument = createEmailDocument("namespace", "uri"); 996 assertStatusOk(icingSearchEngine.put(emailDocument).getStatus()); 997 998 DebugInfoResultProto debugInfoResultProtoBasic = 999 icingSearchEngine.getDebugInfo(DebugInfoVerbosity.Code.BASIC); 1000 assertStatusOk(debugInfoResultProtoBasic.getStatus()); 1001 assertThat(debugInfoResultProtoBasic.getDebugInfo().getDocumentInfo().getCorpusInfoList()) 1002 .isEmpty(); // because verbosity=BASIC 1003 1004 DebugInfoResultProto debugInfoResultProtoDetailed = 1005 icingSearchEngine.getDebugInfo(DebugInfoVerbosity.Code.DETAILED); 1006 assertStatusOk(debugInfoResultProtoDetailed.getStatus()); 1007 assertThat(debugInfoResultProtoDetailed.getDebugInfo().getDocumentInfo().getCorpusInfoList()) 1008 .hasSize(1); // because verbosity=DETAILED 1009 } 1010 1011 @Test testGetAllNamespaces()1012 public void testGetAllNamespaces() throws Exception { 1013 assertStatusOk(icingSearchEngine.initialize().getStatus()); 1014 1015 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 1016 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 1017 assertThat( 1018 icingSearchEngine 1019 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 1020 .getStatus() 1021 .getCode()) 1022 .isEqualTo(StatusProto.Code.OK); 1023 1024 DocumentProto emailDocument = createEmailDocument("namespace", "uri"); 1025 assertStatusOk(icingSearchEngine.put(emailDocument).getStatus()); 1026 1027 GetAllNamespacesResultProto getAllNamespacesResultProto = icingSearchEngine.getAllNamespaces(); 1028 assertStatusOk(getAllNamespacesResultProto.getStatus()); 1029 assertThat(getAllNamespacesResultProto.getNamespacesList()).containsExactly("namespace"); 1030 } 1031 1032 @Test testReset()1033 public void testReset() throws Exception { 1034 assertStatusOk(icingSearchEngine.initialize().getStatus()); 1035 1036 ResetResultProto resetResultProto = icingSearchEngine.reset(); 1037 assertStatusOk(resetResultProto.getStatus()); 1038 } 1039 1040 @Test testReportUsage()1041 public void testReportUsage() throws Exception { 1042 assertStatusOk(icingSearchEngine.initialize().getStatus()); 1043 1044 // Set schema and put a document. 1045 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 1046 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 1047 assertThat( 1048 icingSearchEngine 1049 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 1050 .getStatus() 1051 .getCode()) 1052 .isEqualTo(StatusProto.Code.OK); 1053 1054 DocumentProto emailDocument = createEmailDocument("namespace", "uri"); 1055 PutResultProto putResultProto = icingSearchEngine.put(emailDocument); 1056 assertStatusOk(putResultProto.getStatus()); 1057 1058 // Report usage 1059 UsageReport usageReport = 1060 UsageReport.newBuilder() 1061 .setDocumentNamespace("namespace") 1062 .setDocumentUri("uri") 1063 .setUsageTimestampMs(1) 1064 .setUsageType(UsageReport.UsageType.USAGE_TYPE1) 1065 .build(); 1066 ReportUsageResultProto reportUsageResultProto = icingSearchEngine.reportUsage(usageReport); 1067 assertStatusOk(reportUsageResultProto.getStatus()); 1068 } 1069 1070 @Test testCJKTSnippets()1071 public void testCJKTSnippets() throws Exception { 1072 assertStatusOk(icingSearchEngine.initialize().getStatus()); 1073 1074 SchemaProto schema = SchemaProto.newBuilder().addTypes(createEmailTypeConfig()).build(); 1075 assertStatusOk( 1076 icingSearchEngine 1077 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 1078 .getStatus()); 1079 1080 // String: "天是蓝的" 1081 // ^ ^^ ^ 1082 // UTF16 idx: 0 1 2 3 1083 // Breaks into segments: "天", "是", "蓝", "的" 1084 // "The sky is blue" 1085 String chinese = "天是蓝的"; 1086 assertThat(chinese.length()).isEqualTo(4); 1087 DocumentProto emailDocument1 = 1088 createEmailDocument("namespace", "uri1").toBuilder() 1089 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues(chinese)) 1090 .build(); 1091 assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus()); 1092 1093 // Search and request snippet matching but no windowing. 1094 SearchSpecProto searchSpec = 1095 SearchSpecProto.newBuilder() 1096 .setQuery("是") 1097 .setTermMatchType(TermMatchType.Code.PREFIX) 1098 .build(); 1099 ResultSpecProto resultSpecProto = 1100 ResultSpecProto.newBuilder() 1101 .setSnippetSpec( 1102 ResultSpecProto.SnippetSpecProto.newBuilder() 1103 .setNumToSnippet(Integer.MAX_VALUE) 1104 .setNumMatchesPerProperty(Integer.MAX_VALUE)) 1105 .build(); 1106 1107 // Search and make sure that we got a single successful results 1108 SearchResultProto searchResultProto = 1109 icingSearchEngine.search( 1110 searchSpec, ScoringSpecProto.getDefaultInstance(), resultSpecProto); 1111 assertStatusOk(searchResultProto.getStatus()); 1112 assertThat(searchResultProto.getResultsCount()).isEqualTo(1); 1113 1114 // Ensure that one and only one property was matched and it was "subject" 1115 SnippetProto snippetProto = searchResultProto.getResults(0).getSnippet(); 1116 assertThat(snippetProto.getEntriesList()).hasSize(1); 1117 SnippetProto.EntryProto entryProto = snippetProto.getEntries(0); 1118 assertThat(entryProto.getPropertyName()).isEqualTo("subject"); 1119 1120 // Get the content for "subject" and see what the match is. 1121 DocumentProto resultDocument = searchResultProto.getResults(0).getDocument(); 1122 assertThat(resultDocument.getPropertiesList()).hasSize(1); 1123 PropertyProto subjectProperty = resultDocument.getProperties(0); 1124 assertThat(subjectProperty.getName()).isEqualTo("subject"); 1125 assertThat(subjectProperty.getStringValuesList()).hasSize(1); 1126 String content = subjectProperty.getStringValues(0); 1127 1128 // Ensure that there is one and only one match within "subject" 1129 assertThat(entryProto.getSnippetMatchesList()).hasSize(1); 1130 SnippetMatchProto matchProto = entryProto.getSnippetMatches(0); 1131 1132 int matchStart = matchProto.getExactMatchUtf16Position(); 1133 int matchEnd = matchStart + matchProto.getExactMatchUtf16Length(); 1134 assertThat(matchStart).isEqualTo(1); 1135 assertThat(matchEnd).isEqualTo(2); 1136 String match = content.substring(matchStart, matchEnd); 1137 assertThat(match).isEqualTo("是"); 1138 } 1139 1140 @Test testUtf16MultiByteSnippets()1141 public void testUtf16MultiByteSnippets() throws Exception { 1142 assertStatusOk(icingSearchEngine.initialize().getStatus()); 1143 1144 SchemaProto schema = SchemaProto.newBuilder().addTypes(createEmailTypeConfig()).build(); 1145 assertStatusOk( 1146 icingSearchEngine 1147 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 1148 .getStatus()); 1149 1150 // String: " " 1151 // ^ ^ ^ 1152 // UTF16 idx: 0 5 10 1153 // Breaks into segments: "", "", "" 1154 String text = " "; 1155 assertThat(text.length()).isEqualTo(12); 1156 DocumentProto emailDocument1 = 1157 createEmailDocument("namespace", "uri1").toBuilder() 1158 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues(text)) 1159 .build(); 1160 assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus()); 1161 1162 // Search and request snippet matching but no windowing. 1163 SearchSpecProto searchSpec = 1164 SearchSpecProto.newBuilder() 1165 .setQuery("") 1166 .setTermMatchType(TermMatchType.Code.PREFIX) 1167 .build(); 1168 ResultSpecProto resultSpecProto = 1169 ResultSpecProto.newBuilder() 1170 .setSnippetSpec( 1171 ResultSpecProto.SnippetSpecProto.newBuilder() 1172 .setNumToSnippet(Integer.MAX_VALUE) 1173 .setNumMatchesPerProperty(Integer.MAX_VALUE)) 1174 .build(); 1175 1176 // Search and make sure that we got a single successful results 1177 SearchResultProto searchResultProto = 1178 icingSearchEngine.search( 1179 searchSpec, ScoringSpecProto.getDefaultInstance(), resultSpecProto); 1180 assertStatusOk(searchResultProto.getStatus()); 1181 assertThat(searchResultProto.getResultsCount()).isEqualTo(1); 1182 1183 // Ensure that one and only one property was matched and it was "subject" 1184 SnippetProto snippetProto = searchResultProto.getResults(0).getSnippet(); 1185 assertThat(snippetProto.getEntriesList()).hasSize(1); 1186 SnippetProto.EntryProto entryProto = snippetProto.getEntries(0); 1187 assertThat(entryProto.getPropertyName()).isEqualTo("subject"); 1188 1189 // Get the content for "subject" and see what the match is. 1190 DocumentProto resultDocument = searchResultProto.getResults(0).getDocument(); 1191 assertThat(resultDocument.getPropertiesList()).hasSize(1); 1192 PropertyProto subjectProperty = resultDocument.getProperties(0); 1193 assertThat(subjectProperty.getName()).isEqualTo("subject"); 1194 assertThat(subjectProperty.getStringValuesList()).hasSize(1); 1195 String content = subjectProperty.getStringValues(0); 1196 1197 // Ensure that there is one and only one match within "subject" 1198 assertThat(entryProto.getSnippetMatchesList()).hasSize(1); 1199 SnippetMatchProto matchProto = entryProto.getSnippetMatches(0); 1200 1201 int matchStart = matchProto.getExactMatchUtf16Position(); 1202 int matchEnd = matchStart + matchProto.getExactMatchUtf16Length(); 1203 assertThat(matchStart).isEqualTo(5); 1204 assertThat(matchEnd).isEqualTo(9); 1205 String match = content.substring(matchStart, matchEnd); 1206 assertThat(match).isEqualTo(""); 1207 } 1208 1209 @Test testSearchSuggestions()1210 public void testSearchSuggestions() { 1211 assertStatusOk(icingSearchEngine.initialize().getStatus()); 1212 1213 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 1214 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 1215 assertThat( 1216 icingSearchEngine 1217 .setSchema(schema, /* ignoreErrorsAndDeleteDocuments= */ false) 1218 .getStatus() 1219 .getCode()) 1220 .isEqualTo(StatusProto.Code.OK); 1221 1222 DocumentProto emailDocument1 = 1223 createEmailDocument("namespace", "uri1").toBuilder() 1224 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("fo")) 1225 .build(); 1226 DocumentProto emailDocument2 = 1227 createEmailDocument("namespace", "uri2").toBuilder() 1228 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo")) 1229 .build(); 1230 assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus()); 1231 assertStatusOk(icingSearchEngine.put(emailDocument2).getStatus()); 1232 1233 SuggestionSpecProto suggestionSpec = 1234 SuggestionSpecProto.newBuilder() 1235 .setPrefix("f") 1236 .setNumToReturn(10) 1237 .setScoringSpec( 1238 SuggestionScoringSpecProto.newBuilder() 1239 .setScoringMatchType(Code.EXACT_ONLY) 1240 .build()) 1241 .build(); 1242 1243 SuggestionResponse response = icingSearchEngine.searchSuggestions(suggestionSpec); 1244 assertStatusOk(response.getStatus()); 1245 assertThat(response.getSuggestionsList()).hasSize(2); 1246 assertThat(response.getSuggestions(0).getQuery()).isEqualTo("foo"); 1247 assertThat(response.getSuggestions(1).getQuery()).isEqualTo("fo"); 1248 } 1249 1250 @Test testLogging()1251 public void testLogging() throws Exception { 1252 // Set to INFO 1253 assertThat(IcingSearchEngine.setLoggingLevel(LogSeverity.Code.INFO)).isTrue(); 1254 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.INFO)).isTrue(); 1255 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.DBG)).isFalse(); 1256 1257 // Set to WARNING 1258 assertThat(IcingSearchEngine.setLoggingLevel(LogSeverity.Code.WARNING)).isTrue(); 1259 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.WARNING)).isTrue(); 1260 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.INFO)).isFalse(); 1261 1262 // Set to DEBUG 1263 assertThat(IcingSearchEngine.setLoggingLevel(LogSeverity.Code.DBG)).isTrue(); 1264 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.DBG)).isTrue(); 1265 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.VERBOSE)).isFalse(); 1266 1267 // Set to VERBOSE 1268 assertThat(IcingSearchEngine.setLoggingLevel(LogSeverity.Code.VERBOSE, (short) 1)).isTrue(); 1269 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.VERBOSE, (short) 1)).isTrue(); 1270 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.VERBOSE, (short) 2)).isFalse(); 1271 1272 assertThat(IcingSearchEngine.getLoggingTag()).isNotEmpty(); 1273 } 1274 assertStatusOk(StatusProto status)1275 private static void assertStatusOk(StatusProto status) { 1276 assertWithMessage(status.getMessage()).that(status.getCode()).isEqualTo(StatusProto.Code.OK); 1277 } 1278 } 1279