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.DebugInfoResultProto; 22 import com.google.android.icing.proto.DebugInfoVerbosity; 23 import com.google.android.icing.proto.DeleteByNamespaceResultProto; 24 import com.google.android.icing.proto.DeleteByQueryResultProto; 25 import com.google.android.icing.proto.DeleteBySchemaTypeResultProto; 26 import com.google.android.icing.proto.DeleteResultProto; 27 import com.google.android.icing.proto.DocumentProto; 28 import com.google.android.icing.proto.GetAllNamespacesResultProto; 29 import com.google.android.icing.proto.GetOptimizeInfoResultProto; 30 import com.google.android.icing.proto.GetResultProto; 31 import com.google.android.icing.proto.GetResultSpecProto; 32 import com.google.android.icing.proto.GetSchemaResultProto; 33 import com.google.android.icing.proto.GetSchemaTypeResultProto; 34 import com.google.android.icing.proto.IcingSearchEngineOptions; 35 import com.google.android.icing.proto.InitializeResultProto; 36 import com.google.android.icing.proto.LogSeverity; 37 import com.google.android.icing.proto.OptimizeResultProto; 38 import com.google.android.icing.proto.PersistToDiskResultProto; 39 import com.google.android.icing.proto.PersistType; 40 import com.google.android.icing.proto.PropertyConfigProto; 41 import com.google.android.icing.proto.PropertyProto; 42 import com.google.android.icing.proto.PutResultProto; 43 import com.google.android.icing.proto.ReportUsageResultProto; 44 import com.google.android.icing.proto.ResetResultProto; 45 import com.google.android.icing.proto.ResultSpecProto; 46 import com.google.android.icing.proto.SchemaProto; 47 import com.google.android.icing.proto.SchemaTypeConfigProto; 48 import com.google.android.icing.proto.ScoringSpecProto; 49 import com.google.android.icing.proto.SearchResultProto; 50 import com.google.android.icing.proto.SearchSpecProto; 51 import com.google.android.icing.proto.SetSchemaResultProto; 52 import com.google.android.icing.proto.SnippetMatchProto; 53 import com.google.android.icing.proto.SnippetProto; 54 import com.google.android.icing.proto.StatusProto; 55 import com.google.android.icing.proto.StorageInfoResultProto; 56 import com.google.android.icing.proto.StringIndexingConfig; 57 import com.google.android.icing.proto.StringIndexingConfig.TokenizerType; 58 import com.google.android.icing.proto.SuggestionResponse; 59 import com.google.android.icing.proto.SuggestionScoringSpecProto; 60 import com.google.android.icing.proto.SuggestionSpecProto; 61 import com.google.android.icing.proto.TermMatchType; 62 import com.google.android.icing.proto.TermMatchType.Code; 63 import com.google.android.icing.proto.UsageReport; 64 import java.io.File; 65 import java.util.HashMap; 66 import java.util.Map; 67 import org.junit.After; 68 import org.junit.Before; 69 import org.junit.Rule; 70 import org.junit.Test; 71 import org.junit.rules.TemporaryFolder; 72 import org.junit.runner.RunWith; 73 import org.junit.runners.JUnit4; 74 75 /** 76 * This test is not intended to fully test the functionality of each API. But rather to test the JNI 77 * wrapper and Java interfaces of Icing library {@link IcingSearchEngine}. 78 */ 79 @RunWith(JUnit4.class) 80 public final class IcingSearchEngineTest { 81 @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); 82 83 private static final String EMAIL_TYPE = "Email"; 84 85 private File tempDir; 86 87 private IcingSearchEngine icingSearchEngine; 88 createEmailTypeConfig()89 private static SchemaTypeConfigProto createEmailTypeConfig() { 90 return SchemaTypeConfigProto.newBuilder() 91 .setSchemaType(EMAIL_TYPE) 92 .addProperties( 93 PropertyConfigProto.newBuilder() 94 .setPropertyName("subject") 95 .setDataType(PropertyConfigProto.DataType.Code.STRING) 96 .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) 97 .setStringIndexingConfig( 98 StringIndexingConfig.newBuilder() 99 .setTokenizerType(TokenizerType.Code.PLAIN) 100 .setTermMatchType(TermMatchType.Code.PREFIX))) 101 .addProperties( 102 PropertyConfigProto.newBuilder() 103 .setPropertyName("body") 104 .setDataType(PropertyConfigProto.DataType.Code.STRING) 105 .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) 106 .setStringIndexingConfig( 107 StringIndexingConfig.newBuilder() 108 .setTokenizerType(TokenizerType.Code.PLAIN) 109 .setTermMatchType(TermMatchType.Code.PREFIX))) 110 .build(); 111 } 112 createEmailDocument(String namespace, String uri)113 private static DocumentProto createEmailDocument(String namespace, String uri) { 114 return DocumentProto.newBuilder() 115 .setNamespace(namespace) 116 .setUri(uri) 117 .setSchema(EMAIL_TYPE) 118 .setCreationTimestampMs(1) // Arbitrary non-zero number so Icing doesn't override it 119 .build(); 120 } 121 122 @Before setUp()123 public void setUp() throws Exception { 124 tempDir = temporaryFolder.newFolder(); 125 IcingSearchEngineOptions options = 126 IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build(); 127 icingSearchEngine = new IcingSearchEngine(options); 128 } 129 130 @After tearDown()131 public void tearDown() throws Exception { 132 icingSearchEngine.close(); 133 } 134 135 @Test testInitialize()136 public void testInitialize() throws Exception { 137 InitializeResultProto initializeResultProto = icingSearchEngine.initialize(); 138 assertStatusOk(initializeResultProto.getStatus()); 139 } 140 141 @Test testSetAndGetSchema()142 public void testSetAndGetSchema() throws Exception { 143 assertStatusOk(icingSearchEngine.initialize().getStatus()); 144 145 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 146 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 147 SetSchemaResultProto setSchemaResultProto = 148 icingSearchEngine.setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false); 149 assertStatusOk(setSchemaResultProto.getStatus()); 150 151 GetSchemaResultProto getSchemaResultProto = icingSearchEngine.getSchema(); 152 assertStatusOk(getSchemaResultProto.getStatus()); 153 assertThat(getSchemaResultProto.getSchema()).isEqualTo(schema); 154 155 GetSchemaTypeResultProto getSchemaTypeResultProto = 156 icingSearchEngine.getSchemaType(emailTypeConfig.getSchemaType()); 157 assertStatusOk(getSchemaTypeResultProto.getStatus()); 158 assertThat(getSchemaTypeResultProto.getSchemaTypeConfig()).isEqualTo(emailTypeConfig); 159 } 160 161 @Test testPutAndGetDocuments()162 public void testPutAndGetDocuments() throws Exception { 163 assertStatusOk(icingSearchEngine.initialize().getStatus()); 164 165 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 166 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 167 assertThat( 168 icingSearchEngine 169 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false) 170 .getStatus() 171 .getCode()) 172 .isEqualTo(StatusProto.Code.OK); 173 174 DocumentProto emailDocument = createEmailDocument("namespace", "uri"); 175 PutResultProto putResultProto = icingSearchEngine.put(emailDocument); 176 assertStatusOk(putResultProto.getStatus()); 177 178 GetResultProto getResultProto = 179 icingSearchEngine.get("namespace", "uri", GetResultSpecProto.getDefaultInstance()); 180 assertStatusOk(getResultProto.getStatus()); 181 assertThat(getResultProto.getDocument()).isEqualTo(emailDocument); 182 } 183 184 @Test testSearch()185 public void testSearch() throws Exception { 186 assertStatusOk(icingSearchEngine.initialize().getStatus()); 187 188 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 189 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 190 assertThat( 191 icingSearchEngine 192 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false) 193 .getStatus() 194 .getCode()) 195 .isEqualTo(StatusProto.Code.OK); 196 197 DocumentProto emailDocument = 198 createEmailDocument("namespace", "uri").toBuilder() 199 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo")) 200 .build(); 201 assertStatusOk(icingSearchEngine.put(emailDocument).getStatus()); 202 203 SearchSpecProto searchSpec = 204 SearchSpecProto.newBuilder() 205 .setQuery("foo") 206 .setTermMatchType(TermMatchType.Code.PREFIX) 207 .build(); 208 209 SearchResultProto searchResultProto = 210 icingSearchEngine.search( 211 searchSpec, 212 ScoringSpecProto.getDefaultInstance(), 213 ResultSpecProto.getDefaultInstance()); 214 assertStatusOk(searchResultProto.getStatus()); 215 assertThat(searchResultProto.getResultsCount()).isEqualTo(1); 216 assertThat(searchResultProto.getResults(0).getDocument()).isEqualTo(emailDocument); 217 218 assertThat(searchResultProto.getQueryStats().hasNativeToJavaStartTimestampMs()).isTrue(); 219 assertThat(searchResultProto.getQueryStats().hasNativeToJavaJniLatencyMs()).isTrue(); 220 assertThat(searchResultProto.getQueryStats().hasJavaToNativeJniLatencyMs()).isTrue(); 221 assertThat(searchResultProto.getQueryStats().getNativeToJavaStartTimestampMs()) 222 .isGreaterThan(0); 223 assertThat(searchResultProto.getQueryStats().getNativeToJavaJniLatencyMs()).isAtLeast(0); 224 assertThat(searchResultProto.getQueryStats().getJavaToNativeJniLatencyMs()).isAtLeast(0); 225 } 226 227 @Test testGetNextPage()228 public void testGetNextPage() throws Exception { 229 assertStatusOk(icingSearchEngine.initialize().getStatus()); 230 231 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 232 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 233 assertThat( 234 icingSearchEngine 235 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false) 236 .getStatus() 237 .getCode()) 238 .isEqualTo(StatusProto.Code.OK); 239 240 Map<String, DocumentProto> documents = new HashMap<>(); 241 for (int i = 0; i < 10; i++) { 242 DocumentProto emailDocument = 243 createEmailDocument("namespace", "uri:" + i).toBuilder() 244 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo")) 245 .build(); 246 documents.put("uri:" + i, emailDocument); 247 assertWithMessage(icingSearchEngine.put(emailDocument).getStatus().getMessage()) 248 .that(icingSearchEngine.put(emailDocument).getStatus().getCode()) 249 .isEqualTo(StatusProto.Code.OK); 250 } 251 252 SearchSpecProto searchSpec = 253 SearchSpecProto.newBuilder() 254 .setQuery("foo") 255 .setTermMatchType(TermMatchType.Code.PREFIX) 256 .build(); 257 ResultSpecProto resultSpecProto = ResultSpecProto.newBuilder().setNumPerPage(1).build(); 258 259 SearchResultProto searchResultProto = 260 icingSearchEngine.search( 261 searchSpec, ScoringSpecProto.getDefaultInstance(), resultSpecProto); 262 assertStatusOk(searchResultProto.getStatus()); 263 assertThat(searchResultProto.getResultsCount()).isEqualTo(1); 264 DocumentProto resultDocument = searchResultProto.getResults(0).getDocument(); 265 assertThat(resultDocument).isEqualTo(documents.remove(resultDocument.getUri())); 266 267 assertThat(searchResultProto.getQueryStats().hasNativeToJavaStartTimestampMs()).isTrue(); 268 assertThat(searchResultProto.getQueryStats().hasNativeToJavaJniLatencyMs()).isTrue(); 269 assertThat(searchResultProto.getQueryStats().hasJavaToNativeJniLatencyMs()).isTrue(); 270 assertThat(searchResultProto.getQueryStats().getNativeToJavaStartTimestampMs()) 271 .isGreaterThan(0); 272 assertThat(searchResultProto.getQueryStats().getNativeToJavaJniLatencyMs()).isAtLeast(0); 273 assertThat(searchResultProto.getQueryStats().getJavaToNativeJniLatencyMs()).isAtLeast(0); 274 275 // fetch rest pages 276 for (int i = 1; i < 5; i++) { 277 searchResultProto = icingSearchEngine.getNextPage(searchResultProto.getNextPageToken()); 278 assertWithMessage(searchResultProto.getStatus().getMessage()) 279 .that(searchResultProto.getStatus().getCode()) 280 .isEqualTo(StatusProto.Code.OK); 281 assertThat(searchResultProto.getResultsCount()).isEqualTo(1); 282 resultDocument = searchResultProto.getResults(0).getDocument(); 283 assertThat(resultDocument).isEqualTo(documents.remove(resultDocument.getUri())); 284 } 285 286 // invalidate rest result 287 icingSearchEngine.invalidateNextPageToken(searchResultProto.getNextPageToken()); 288 289 searchResultProto = icingSearchEngine.getNextPage(searchResultProto.getNextPageToken()); 290 assertStatusOk(searchResultProto.getStatus()); 291 assertThat(searchResultProto.getResultsCount()).isEqualTo(0); 292 } 293 294 @Test testDelete()295 public void testDelete() throws Exception { 296 assertStatusOk(icingSearchEngine.initialize().getStatus()); 297 298 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 299 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 300 assertThat( 301 icingSearchEngine 302 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false) 303 .getStatus() 304 .getCode()) 305 .isEqualTo(StatusProto.Code.OK); 306 307 DocumentProto emailDocument = createEmailDocument("namespace", "uri"); 308 assertStatusOk(icingSearchEngine.put(emailDocument).getStatus()); 309 310 DeleteResultProto deleteResultProto = icingSearchEngine.delete("namespace", "uri"); 311 assertStatusOk(deleteResultProto.getStatus()); 312 313 GetResultProto getResultProto = 314 icingSearchEngine.get("namespace", "uri", GetResultSpecProto.getDefaultInstance()); 315 assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND); 316 } 317 318 @Test testDeleteByNamespace()319 public void testDeleteByNamespace() throws Exception { 320 assertStatusOk(icingSearchEngine.initialize().getStatus()); 321 322 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 323 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 324 assertThat( 325 icingSearchEngine 326 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false) 327 .getStatus() 328 .getCode()) 329 .isEqualTo(StatusProto.Code.OK); 330 331 DocumentProto emailDocument = createEmailDocument("namespace", "uri"); 332 assertStatusOk(icingSearchEngine.put(emailDocument).getStatus()); 333 334 DeleteByNamespaceResultProto deleteByNamespaceResultProto = 335 icingSearchEngine.deleteByNamespace("namespace"); 336 assertStatusOk(deleteByNamespaceResultProto.getStatus()); 337 338 GetResultProto getResultProto = 339 icingSearchEngine.get("namespace", "uri", GetResultSpecProto.getDefaultInstance()); 340 assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND); 341 } 342 343 @Test testDeleteBySchemaType()344 public void testDeleteBySchemaType() throws Exception { 345 assertStatusOk(icingSearchEngine.initialize().getStatus()); 346 347 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 348 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 349 assertThat( 350 icingSearchEngine 351 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false) 352 .getStatus() 353 .getCode()) 354 .isEqualTo(StatusProto.Code.OK); 355 356 DocumentProto emailDocument = createEmailDocument("namespace", "uri"); 357 assertStatusOk(icingSearchEngine.put(emailDocument).getStatus()); 358 359 DeleteBySchemaTypeResultProto deleteBySchemaTypeResultProto = 360 icingSearchEngine.deleteBySchemaType(EMAIL_TYPE); 361 assertStatusOk(deleteBySchemaTypeResultProto.getStatus()); 362 363 GetResultProto getResultProto = 364 icingSearchEngine.get("namespace", "uri", GetResultSpecProto.getDefaultInstance()); 365 assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND); 366 } 367 368 @Test testDeleteByQuery()369 public void testDeleteByQuery() throws Exception { 370 assertStatusOk(icingSearchEngine.initialize().getStatus()); 371 372 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 373 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 374 assertThat( 375 icingSearchEngine 376 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false) 377 .getStatus() 378 .getCode()) 379 .isEqualTo(StatusProto.Code.OK); 380 381 DocumentProto emailDocument1 = 382 createEmailDocument("namespace", "uri1").toBuilder() 383 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo")) 384 .build(); 385 386 assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus()); 387 DocumentProto emailDocument2 = 388 createEmailDocument("namespace", "uri2").toBuilder() 389 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("bar")) 390 .build(); 391 392 assertStatusOk(icingSearchEngine.put(emailDocument2).getStatus()); 393 394 SearchSpecProto searchSpec = 395 SearchSpecProto.newBuilder() 396 .setQuery("foo") 397 .setTermMatchType(TermMatchType.Code.PREFIX) 398 .build(); 399 400 SearchResultProto searchResultProto = 401 icingSearchEngine.search( 402 searchSpec, 403 ScoringSpecProto.getDefaultInstance(), 404 ResultSpecProto.getDefaultInstance()); 405 assertStatusOk(searchResultProto.getStatus()); 406 assertThat(searchResultProto.getResultsCount()).isEqualTo(1); 407 assertThat(searchResultProto.getResults(0).getDocument()).isEqualTo(emailDocument1); 408 409 DeleteByQueryResultProto deleteResultProto = icingSearchEngine.deleteByQuery(searchSpec); 410 assertStatusOk(deleteResultProto.getStatus()); 411 // By default, the deleteByQuery API does not return the summary about deleted documents, unless 412 // the returnDeletedDocumentInfo parameter is set to true. 413 assertThat(deleteResultProto.getDeletedDocumentsList()).isEmpty(); 414 415 GetResultProto getResultProto = 416 icingSearchEngine.get("namespace", "uri1", GetResultSpecProto.getDefaultInstance()); 417 assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND); 418 getResultProto = 419 icingSearchEngine.get("namespace", "uri2", GetResultSpecProto.getDefaultInstance()); 420 assertStatusOk(getResultProto.getStatus()); 421 } 422 423 @Test testDeleteByQueryWithDeletedDocumentInfo()424 public void testDeleteByQueryWithDeletedDocumentInfo() throws Exception { 425 assertStatusOk(icingSearchEngine.initialize().getStatus()); 426 427 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 428 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 429 assertThat( 430 icingSearchEngine 431 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false) 432 .getStatus() 433 .getCode()) 434 .isEqualTo(StatusProto.Code.OK); 435 436 DocumentProto emailDocument1 = 437 createEmailDocument("namespace", "uri1").toBuilder() 438 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo")) 439 .build(); 440 441 assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus()); 442 DocumentProto emailDocument2 = 443 createEmailDocument("namespace", "uri2").toBuilder() 444 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("bar")) 445 .build(); 446 447 assertStatusOk(icingSearchEngine.put(emailDocument2).getStatus()); 448 449 SearchSpecProto searchSpec = 450 SearchSpecProto.newBuilder() 451 .setQuery("foo") 452 .setTermMatchType(TermMatchType.Code.PREFIX) 453 .build(); 454 455 DeleteByQueryResultProto deleteResultProto = 456 icingSearchEngine.deleteByQuery(searchSpec, /*returnDeletedDocumentInfo=*/ true); 457 assertStatusOk(deleteResultProto.getStatus()); 458 DeleteByQueryResultProto.DocumentGroupInfo info = 459 DeleteByQueryResultProto.DocumentGroupInfo.newBuilder() 460 .setNamespace("namespace") 461 .setSchema("Email") 462 .addUris("uri1") 463 .build(); 464 assertThat(deleteResultProto.getDeletedDocumentsList()).containsExactly(info); 465 466 GetResultProto getResultProto = 467 icingSearchEngine.get("namespace", "uri1", GetResultSpecProto.getDefaultInstance()); 468 assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND); 469 getResultProto = 470 icingSearchEngine.get("namespace", "uri2", GetResultSpecProto.getDefaultInstance()); 471 assertStatusOk(getResultProto.getStatus()); 472 } 473 474 @Test testPersistToDisk()475 public void testPersistToDisk() throws Exception { 476 assertStatusOk(icingSearchEngine.initialize().getStatus()); 477 478 PersistToDiskResultProto persistToDiskResultProto = 479 icingSearchEngine.persistToDisk(PersistType.Code.LITE); 480 assertStatusOk(persistToDiskResultProto.getStatus()); 481 } 482 483 @Test testOptimize()484 public void testOptimize() throws Exception { 485 assertStatusOk(icingSearchEngine.initialize().getStatus()); 486 487 OptimizeResultProto optimizeResultProto = icingSearchEngine.optimize(); 488 assertStatusOk(optimizeResultProto.getStatus()); 489 } 490 491 @Test testGetOptimizeInfo()492 public void testGetOptimizeInfo() throws Exception { 493 assertStatusOk(icingSearchEngine.initialize().getStatus()); 494 495 GetOptimizeInfoResultProto getOptimizeInfoResultProto = icingSearchEngine.getOptimizeInfo(); 496 assertStatusOk(getOptimizeInfoResultProto.getStatus()); 497 assertThat(getOptimizeInfoResultProto.getOptimizableDocs()).isEqualTo(0); 498 assertThat(getOptimizeInfoResultProto.getEstimatedOptimizableBytes()).isEqualTo(0); 499 } 500 501 @Test testGetStorageInfo()502 public void testGetStorageInfo() throws Exception { 503 assertStatusOk(icingSearchEngine.initialize().getStatus()); 504 505 StorageInfoResultProto storageInfoResultProto = icingSearchEngine.getStorageInfo(); 506 assertStatusOk(storageInfoResultProto.getStatus()); 507 } 508 509 @Test testGetDebugInfo()510 public void testGetDebugInfo() throws Exception { 511 assertStatusOk(icingSearchEngine.initialize().getStatus()); 512 513 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 514 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 515 assertThat( 516 icingSearchEngine 517 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false) 518 .getStatus() 519 .getCode()) 520 .isEqualTo(StatusProto.Code.OK); 521 522 DocumentProto emailDocument = createEmailDocument("namespace", "uri"); 523 assertStatusOk(icingSearchEngine.put(emailDocument).getStatus()); 524 525 DebugInfoResultProto debugInfoResultProtoBasic = 526 icingSearchEngine.getDebugInfo(DebugInfoVerbosity.Code.BASIC); 527 assertStatusOk(debugInfoResultProtoBasic.getStatus()); 528 assertThat(debugInfoResultProtoBasic.getDebugInfo().getDocumentInfo().getCorpusInfoList()) 529 .isEmpty(); // because verbosity=BASIC 530 531 DebugInfoResultProto debugInfoResultProtoDetailed = 532 icingSearchEngine.getDebugInfo(DebugInfoVerbosity.Code.DETAILED); 533 assertStatusOk(debugInfoResultProtoDetailed.getStatus()); 534 assertThat(debugInfoResultProtoDetailed.getDebugInfo().getDocumentInfo().getCorpusInfoList()) 535 .hasSize(1); // because verbosity=DETAILED 536 } 537 538 @Test testGetAllNamespaces()539 public void testGetAllNamespaces() throws Exception { 540 assertStatusOk(icingSearchEngine.initialize().getStatus()); 541 542 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 543 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 544 assertThat( 545 icingSearchEngine 546 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false) 547 .getStatus() 548 .getCode()) 549 .isEqualTo(StatusProto.Code.OK); 550 551 DocumentProto emailDocument = createEmailDocument("namespace", "uri"); 552 assertStatusOk(icingSearchEngine.put(emailDocument).getStatus()); 553 554 GetAllNamespacesResultProto getAllNamespacesResultProto = icingSearchEngine.getAllNamespaces(); 555 assertStatusOk(getAllNamespacesResultProto.getStatus()); 556 assertThat(getAllNamespacesResultProto.getNamespacesList()).containsExactly("namespace"); 557 } 558 559 @Test testReset()560 public void testReset() throws Exception { 561 assertStatusOk(icingSearchEngine.initialize().getStatus()); 562 563 ResetResultProto resetResultProto = icingSearchEngine.reset(); 564 assertStatusOk(resetResultProto.getStatus()); 565 } 566 567 @Test testReportUsage()568 public void testReportUsage() throws Exception { 569 assertStatusOk(icingSearchEngine.initialize().getStatus()); 570 571 // Set schema and put a document. 572 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 573 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 574 assertThat( 575 icingSearchEngine 576 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false) 577 .getStatus() 578 .getCode()) 579 .isEqualTo(StatusProto.Code.OK); 580 581 DocumentProto emailDocument = createEmailDocument("namespace", "uri"); 582 PutResultProto putResultProto = icingSearchEngine.put(emailDocument); 583 assertStatusOk(putResultProto.getStatus()); 584 585 // Report usage 586 UsageReport usageReport = 587 UsageReport.newBuilder() 588 .setDocumentNamespace("namespace") 589 .setDocumentUri("uri") 590 .setUsageTimestampMs(1) 591 .setUsageType(UsageReport.UsageType.USAGE_TYPE1) 592 .build(); 593 ReportUsageResultProto reportUsageResultProto = icingSearchEngine.reportUsage(usageReport); 594 assertStatusOk(reportUsageResultProto.getStatus()); 595 } 596 597 @Test testCJKTSnippets()598 public void testCJKTSnippets() throws Exception { 599 assertStatusOk(icingSearchEngine.initialize().getStatus()); 600 601 SchemaProto schema = SchemaProto.newBuilder().addTypes(createEmailTypeConfig()).build(); 602 assertStatusOk( 603 icingSearchEngine.setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false).getStatus()); 604 605 // String: "天是蓝的" 606 // ^ ^^ ^ 607 // UTF16 idx: 0 1 2 3 608 // Breaks into segments: "天", "是", "蓝", "的" 609 // "The sky is blue" 610 String chinese = "天是蓝的"; 611 assertThat(chinese.length()).isEqualTo(4); 612 DocumentProto emailDocument1 = 613 createEmailDocument("namespace", "uri1").toBuilder() 614 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues(chinese)) 615 .build(); 616 assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus()); 617 618 // Search and request snippet matching but no windowing. 619 SearchSpecProto searchSpec = 620 SearchSpecProto.newBuilder() 621 .setQuery("是") 622 .setTermMatchType(TermMatchType.Code.PREFIX) 623 .build(); 624 ResultSpecProto resultSpecProto = 625 ResultSpecProto.newBuilder() 626 .setSnippetSpec( 627 ResultSpecProto.SnippetSpecProto.newBuilder() 628 .setNumToSnippet(Integer.MAX_VALUE) 629 .setNumMatchesPerProperty(Integer.MAX_VALUE)) 630 .build(); 631 632 // Search and make sure that we got a single successful results 633 SearchResultProto searchResultProto = 634 icingSearchEngine.search( 635 searchSpec, ScoringSpecProto.getDefaultInstance(), resultSpecProto); 636 assertStatusOk(searchResultProto.getStatus()); 637 assertThat(searchResultProto.getResultsCount()).isEqualTo(1); 638 639 // Ensure that one and only one property was matched and it was "subject" 640 SnippetProto snippetProto = searchResultProto.getResults(0).getSnippet(); 641 assertThat(snippetProto.getEntriesList()).hasSize(1); 642 SnippetProto.EntryProto entryProto = snippetProto.getEntries(0); 643 assertThat(entryProto.getPropertyName()).isEqualTo("subject"); 644 645 // Get the content for "subject" and see what the match is. 646 DocumentProto resultDocument = searchResultProto.getResults(0).getDocument(); 647 assertThat(resultDocument.getPropertiesList()).hasSize(1); 648 PropertyProto subjectProperty = resultDocument.getProperties(0); 649 assertThat(subjectProperty.getName()).isEqualTo("subject"); 650 assertThat(subjectProperty.getStringValuesList()).hasSize(1); 651 String content = subjectProperty.getStringValues(0); 652 653 // Ensure that there is one and only one match within "subject" 654 assertThat(entryProto.getSnippetMatchesList()).hasSize(1); 655 SnippetMatchProto matchProto = entryProto.getSnippetMatches(0); 656 657 int matchStart = matchProto.getExactMatchUtf16Position(); 658 int matchEnd = matchStart + matchProto.getExactMatchUtf16Length(); 659 assertThat(matchStart).isEqualTo(1); 660 assertThat(matchEnd).isEqualTo(2); 661 String match = content.substring(matchStart, matchEnd); 662 assertThat(match).isEqualTo("是"); 663 } 664 665 @Test testUtf16MultiByteSnippets()666 public void testUtf16MultiByteSnippets() throws Exception { 667 assertStatusOk(icingSearchEngine.initialize().getStatus()); 668 669 SchemaProto schema = SchemaProto.newBuilder().addTypes(createEmailTypeConfig()).build(); 670 assertStatusOk( 671 icingSearchEngine.setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false).getStatus()); 672 673 // String: " " 674 // ^ ^ ^ 675 // UTF16 idx: 0 5 10 676 // Breaks into segments: "", "", "" 677 String text = " "; 678 assertThat(text.length()).isEqualTo(12); 679 DocumentProto emailDocument1 = 680 createEmailDocument("namespace", "uri1").toBuilder() 681 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues(text)) 682 .build(); 683 assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus()); 684 685 // Search and request snippet matching but no windowing. 686 SearchSpecProto searchSpec = 687 SearchSpecProto.newBuilder() 688 .setQuery("") 689 .setTermMatchType(TermMatchType.Code.PREFIX) 690 .build(); 691 ResultSpecProto resultSpecProto = 692 ResultSpecProto.newBuilder() 693 .setSnippetSpec( 694 ResultSpecProto.SnippetSpecProto.newBuilder() 695 .setNumToSnippet(Integer.MAX_VALUE) 696 .setNumMatchesPerProperty(Integer.MAX_VALUE)) 697 .build(); 698 699 // Search and make sure that we got a single successful results 700 SearchResultProto searchResultProto = 701 icingSearchEngine.search( 702 searchSpec, ScoringSpecProto.getDefaultInstance(), resultSpecProto); 703 assertStatusOk(searchResultProto.getStatus()); 704 assertThat(searchResultProto.getResultsCount()).isEqualTo(1); 705 706 // Ensure that one and only one property was matched and it was "subject" 707 SnippetProto snippetProto = searchResultProto.getResults(0).getSnippet(); 708 assertThat(snippetProto.getEntriesList()).hasSize(1); 709 SnippetProto.EntryProto entryProto = snippetProto.getEntries(0); 710 assertThat(entryProto.getPropertyName()).isEqualTo("subject"); 711 712 // Get the content for "subject" and see what the match is. 713 DocumentProto resultDocument = searchResultProto.getResults(0).getDocument(); 714 assertThat(resultDocument.getPropertiesList()).hasSize(1); 715 PropertyProto subjectProperty = resultDocument.getProperties(0); 716 assertThat(subjectProperty.getName()).isEqualTo("subject"); 717 assertThat(subjectProperty.getStringValuesList()).hasSize(1); 718 String content = subjectProperty.getStringValues(0); 719 720 // Ensure that there is one and only one match within "subject" 721 assertThat(entryProto.getSnippetMatchesList()).hasSize(1); 722 SnippetMatchProto matchProto = entryProto.getSnippetMatches(0); 723 724 int matchStart = matchProto.getExactMatchUtf16Position(); 725 int matchEnd = matchStart + matchProto.getExactMatchUtf16Length(); 726 assertThat(matchStart).isEqualTo(5); 727 assertThat(matchEnd).isEqualTo(9); 728 String match = content.substring(matchStart, matchEnd); 729 assertThat(match).isEqualTo(""); 730 } 731 732 @Test testSearchSuggestions()733 public void testSearchSuggestions() { 734 assertStatusOk(icingSearchEngine.initialize().getStatus()); 735 736 SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig(); 737 SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build(); 738 assertThat( 739 icingSearchEngine 740 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false) 741 .getStatus() 742 .getCode()) 743 .isEqualTo(StatusProto.Code.OK); 744 745 DocumentProto emailDocument1 = 746 createEmailDocument("namespace", "uri1").toBuilder() 747 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("fo")) 748 .build(); 749 DocumentProto emailDocument2 = 750 createEmailDocument("namespace", "uri2").toBuilder() 751 .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo")) 752 .build(); 753 assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus()); 754 assertStatusOk(icingSearchEngine.put(emailDocument2).getStatus()); 755 756 SuggestionSpecProto suggestionSpec = 757 SuggestionSpecProto.newBuilder() 758 .setPrefix("f") 759 .setNumToReturn(10) 760 .setScoringSpec( 761 SuggestionScoringSpecProto.newBuilder() 762 .setScoringMatchType(Code.EXACT_ONLY) 763 .build()) 764 .build(); 765 766 SuggestionResponse response = icingSearchEngine.searchSuggestions(suggestionSpec); 767 assertStatusOk(response.getStatus()); 768 assertThat(response.getSuggestionsList()).hasSize(2); 769 assertThat(response.getSuggestions(0).getQuery()).isEqualTo("foo"); 770 assertThat(response.getSuggestions(1).getQuery()).isEqualTo("fo"); 771 } 772 773 @Test testLogging()774 public void testLogging() throws Exception { 775 // Set to INFO 776 assertThat(IcingSearchEngine.setLoggingLevel(LogSeverity.Code.INFO)).isTrue(); 777 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.INFO)).isTrue(); 778 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.DBG)).isFalse(); 779 780 // Set to WARNING 781 assertThat(IcingSearchEngine.setLoggingLevel(LogSeverity.Code.WARNING)).isTrue(); 782 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.WARNING)).isTrue(); 783 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.INFO)).isFalse(); 784 785 // Set to DEBUG 786 assertThat(IcingSearchEngine.setLoggingLevel(LogSeverity.Code.DBG)).isTrue(); 787 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.DBG)).isTrue(); 788 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.VERBOSE)).isFalse(); 789 790 // Set to VERBOSE 791 assertThat(IcingSearchEngine.setLoggingLevel(LogSeverity.Code.VERBOSE, (short) 1)).isTrue(); 792 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.VERBOSE, (short) 1)).isTrue(); 793 assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.VERBOSE, (short) 2)).isFalse(); 794 795 assertThat(IcingSearchEngine.getLoggingTag()).isNotEmpty(); 796 } 797 assertStatusOk(StatusProto status)798 private static void assertStatusOk(StatusProto status) { 799 assertWithMessage(status.getMessage()).that(status.getCode()).isEqualTo(StatusProto.Code.OK); 800 } 801 } 802