@@ -277,8 +277,7 @@ describeSpec('Existence Filters:', [], () => {
277277 */
278278 specTest (
279279 'Full re-query is skipped when bloom filter can identify documents deleted' ,
280- // TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
281- [ 'no-ios' ] ,
280+ [ ] ,
282281 ( ) => {
283282 const query1 = query ( 'collection' ) ;
284283 const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
@@ -310,8 +309,7 @@ describeSpec('Existence Filters:', [], () => {
310309
311310 specTest (
312311 'Full re-query is triggered when bloom filter can not identify documents deleted' ,
313- // TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
314- [ 'no-ios' ] ,
312+ [ ] ,
315313 ( ) => {
316314 const query1 = query ( 'collection' ) ;
317315 const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
@@ -343,8 +341,7 @@ describeSpec('Existence Filters:', [], () => {
343341
344342 specTest (
345343 'Bloom filter can process special characters in document name' ,
346- // TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
347- [ 'no-ios' ] ,
344+ [ ] ,
348345 ( ) => {
349346 const query1 = query ( 'collection' ) ;
350347 const docA = doc ( 'collection/ÀÒ∑' , 1000 , { v : 1 } ) ;
@@ -372,8 +369,7 @@ describeSpec('Existence Filters:', [], () => {
372369
373370 specTest (
374371 'Bloom filter fills in default values for undefined padding and hashCount' ,
375- // TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
376- [ 'no-ios' ] ,
372+ [ ] ,
377373 ( ) => {
378374 const query1 = query ( 'collection' ) ;
379375 const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
@@ -411,7 +407,6 @@ describeSpec('Existence Filters:', [], () => {
411407 // Skip this test on Android and iOS because those platforms get the raw
412408 // bytes of the bloom filter and, therefore, are not subject to base64
413409 // decoding errors.
414- [ 'no-ios' , 'no-android' ] ,
415410 ( ) => {
416411 const query1 = query ( 'collection' ) ;
417412 const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
@@ -445,8 +440,7 @@ describeSpec('Existence Filters:', [], () => {
445440
446441 specTest (
447442 'Full re-query is triggered when bloom filter hashCount is invalid' ,
448- // TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
449- [ 'no-ios' ] ,
443+ [ ] ,
450444 ( ) => {
451445 const query1 = query ( 'collection' ) ;
452446 const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
@@ -478,187 +472,162 @@ describeSpec('Existence Filters:', [], () => {
478472 }
479473 ) ;
480474
481- specTest (
482- 'Full re-query is triggered when bloom filter is empty' ,
483- // TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
484- [ 'no-ios' ] ,
485- ( ) => {
486- const query1 = query ( 'collection' ) ;
487- const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
488- const docB = doc ( 'collection/b' , 1000 , { v : 1 } ) ;
489-
490- //Generate an empty bloom filter.
491- const bloomFilterProto = generateBloomFilterProto ( {
492- contains : [ ] ,
493- notContains : [ ] ,
494- bitCount : 0 ,
495- hashCount : 0
496- } ) ;
497-
498- return (
499- spec ( )
500- . userListens ( query1 )
501- . watchAcksFull ( query1 , 1000 , docA , docB )
502- . expectEvents ( query1 , { added : [ docA , docB ] } )
503- // DocB is deleted in the next sync.
504- . watchFilters ( [ query1 ] , [ docA . key ] , bloomFilterProto )
505- . watchSnapshots ( 2000 )
506- // Re-run query is triggered.
507- . expectEvents ( query1 , { fromCache : true } )
508- . expectActiveTargets ( {
509- query : query1 ,
510- resumeToken : '' ,
511- targetPurpose : TargetPurpose . ExistenceFilterMismatch
512- } )
513- ) ;
514- }
515- ) ;
475+ specTest ( 'Full re-query is triggered when bloom filter is empty' , [ ] , ( ) => {
476+ const query1 = query ( 'collection' ) ;
477+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
478+ const docB = doc ( 'collection/b' , 1000 , { v : 1 } ) ;
516479
517- specTest (
518- 'Same documents can have different bloom filters' ,
519- // TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
520- [ 'no-ios' ] ,
521- ( ) => {
522- const query1 = query ( 'collection' , filter ( 'v' , '<=' , 2 ) ) ;
523- const query2 = query ( 'collection' , filter ( 'v' , '>=' , 2 ) ) ;
480+ //Generate an empty bloom filter.
481+ const bloomFilterProto = generateBloomFilterProto ( {
482+ contains : [ ] ,
483+ notContains : [ ] ,
484+ bitCount : 0 ,
485+ hashCount : 0
486+ } ) ;
524487
525- const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
526- const docB = doc ( 'collection/b' , 1000 , { v : 2 } ) ;
527- const docC = doc ( 'collection/c' , 1000 , { v : 3 } ) ;
488+ return (
489+ spec ( )
490+ . userListens ( query1 )
491+ . watchAcksFull ( query1 , 1000 , docA , docB )
492+ . expectEvents ( query1 , { added : [ docA , docB ] } )
493+ // DocB is deleted in the next sync.
494+ . watchFilters ( [ query1 ] , [ docA . key ] , bloomFilterProto )
495+ . watchSnapshots ( 2000 )
496+ // Re-run query is triggered.
497+ . expectEvents ( query1 , { fromCache : true } )
498+ . expectActiveTargets ( {
499+ query : query1 ,
500+ resumeToken : '' ,
501+ targetPurpose : TargetPurpose . ExistenceFilterMismatch
502+ } )
503+ ) ;
504+ } ) ;
528505
529- const bloomFilterProto1 = generateBloomFilterProto ( {
530- contains : [ docB ] ,
531- notContains : [ docA , docC ] ,
532- bitCount : 5 ,
533- hashCount : 2
534- } ) ;
535- const bloomFilterProto2 = generateBloomFilterProto ( {
536- contains : [ docB ] ,
537- notContains : [ docA , docC ] ,
538- bitCount : 4 ,
539- hashCount : 1
540- } ) ;
541- return (
542- spec ( )
543- . userListens ( query1 )
544- . watchAcksFull ( query1 , 1000 , docA , docB )
545- . expectEvents ( query1 , { added : [ docA , docB ] } )
546- . userListens ( query2 )
547- . expectEvents ( query2 , { added : [ docB ] , fromCache : true } )
548- . watchAcksFull ( query2 , 1001 , docB , docC )
549- . expectEvents ( query2 , { added : [ docC ] } )
506+ specTest ( 'Same documents can have different bloom filters' , [ ] , ( ) => {
507+ const query1 = query ( 'collection' , filter ( 'v' , '<=' , 2 ) ) ;
508+ const query2 = query ( 'collection' , filter ( 'v' , '>=' , 2 ) ) ;
550509
551- // DocA is deleted in the next sync for query1.
552- . watchFilters ( [ query1 ] , [ docB . key ] , bloomFilterProto1 )
553- . watchSnapshots ( 2000 )
554- // BloomFilter identify docA is deleted, skip full query.
555- . expectEvents ( query1 , { fromCache : true } )
556- . expectLimboDocs ( docA . key ) // DocA is now in limbo.
557-
558- // DocC is deleted in the next sync for query2.
559- . watchFilters ( [ query2 ] , [ docB . key ] , bloomFilterProto2 )
560- . watchSnapshots ( 3000 )
561- // BloomFilter identify docC is deleted, skip full query.
562- . expectEvents ( query2 , { fromCache : true } )
563- . expectLimboDocs ( docA . key , docC . key ) // DocC is now in limbo.
564- ) ;
565- }
566- ) ;
510+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
511+ const docB = doc ( 'collection/b' , 1000 , { v : 2 } ) ;
512+ const docC = doc ( 'collection/c' , 1000 , { v : 3 } ) ;
513+
514+ const bloomFilterProto1 = generateBloomFilterProto ( {
515+ contains : [ docB ] ,
516+ notContains : [ docA , docC ] ,
517+ bitCount : 5 ,
518+ hashCount : 2
519+ } ) ;
520+ const bloomFilterProto2 = generateBloomFilterProto ( {
521+ contains : [ docB ] ,
522+ notContains : [ docA , docC ] ,
523+ bitCount : 4 ,
524+ hashCount : 1
525+ } ) ;
526+ return (
527+ spec ( )
528+ . userListens ( query1 )
529+ . watchAcksFull ( query1 , 1000 , docA , docB )
530+ . expectEvents ( query1 , { added : [ docA , docB ] } )
531+ . userListens ( query2 )
532+ . expectEvents ( query2 , { added : [ docB ] , fromCache : true } )
533+ . watchAcksFull ( query2 , 1001 , docB , docC )
534+ . expectEvents ( query2 , { added : [ docC ] } )
567535
568- specTest (
569- 'Bloom filter is handled at global snapshot' ,
570- // TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
571- [ 'no-ios' ] ,
572- ( ) => {
573- const query1 = query ( 'collection' ) ;
574- const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
575- const docB = doc ( 'collection/b' , 2000 , { v : 2 } ) ;
576- const docC = doc ( 'collection/c' , 3000 , { v : 3 } ) ;
536+ // DocA is deleted in the next sync for query1.
537+ . watchFilters ( [ query1 ] , [ docB . key ] , bloomFilterProto1 )
538+ . watchSnapshots ( 2000 )
539+ // BloomFilter identify docA is deleted, skip full query.
540+ . expectEvents ( query1 , { fromCache : true } )
541+ . expectLimboDocs ( docA . key ) // DocA is now in limbo.
542+
543+ // DocC is deleted in the next sync for query2.
544+ . watchFilters ( [ query2 ] , [ docB . key ] , bloomFilterProto2 )
545+ . watchSnapshots ( 3000 )
546+ // BloomFilter identify docC is deleted, skip full query.
547+ . expectEvents ( query2 , { fromCache : true } )
548+ . expectLimboDocs ( docA . key , docC . key ) // DocC is now in limbo.
549+ ) ;
550+ } ) ;
577551
578- const bloomFilterProto = generateBloomFilterProto ( {
579- contains : [ docA ] ,
580- notContains : [ docB ]
581- } ) ;
552+ specTest ( 'Bloom filter is handled at global snapshot' , [ ] , ( ) => {
553+ const query1 = query ( 'collection' ) ;
554+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
555+ const docB = doc ( 'collection/b' , 2000 , { v : 2 } ) ;
556+ const docC = doc ( 'collection/c' , 3000 , { v : 3 } ) ;
582557
583- return (
584- spec ( )
585- . userListens ( query1 )
586- . watchAcksFull ( query1 , 1000 , docA , docB )
587- . expectEvents ( query1 , { added : [ docA , docB ] } )
588- // Send a mismatching existence filter with one document, but don't
589- // send a new global snapshot. We should not see an event until we
590- // receive the snapshot.
591- . watchFilters ( [ query1 ] , [ docA . key ] , bloomFilterProto )
592- . watchSends ( { affects : [ query1 ] } , docC )
593- . watchSnapshots ( 2000 )
594- . expectEvents ( query1 , { added : [ docC ] , fromCache : true } )
595- // Re-run of the query1 is skipped, docB is in limbo.
596- . expectLimboDocs ( docB . key )
597- ) ;
598- }
599- ) ;
558+ const bloomFilterProto = generateBloomFilterProto ( {
559+ contains : [ docA ] ,
560+ notContains : [ docB ]
561+ } ) ;
600562
601- specTest (
602- 'Bloom filter limbo resolution is denied' ,
603- // TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
604- [ 'no-ios' ] ,
605- ( ) => {
606- const query1 = query ( 'collection' ) ;
607- const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
608- const docB = doc ( 'collection/b' , 1000 , { v : 1 } ) ;
609- const bloomFilterProto = generateBloomFilterProto ( {
610- contains : [ docA ] ,
611- notContains : [ docB ]
612- } ) ;
613- return spec ( )
563+ return (
564+ spec ( )
614565 . userListens ( query1 )
615566 . watchAcksFull ( query1 , 1000 , docA , docB )
616567 . expectEvents ( query1 , { added : [ docA , docB ] } )
568+ // Send a mismatching existence filter with one document, but don't
569+ // send a new global snapshot. We should not see an event until we
570+ // receive the snapshot.
617571 . watchFilters ( [ query1 ] , [ docA . key ] , bloomFilterProto )
572+ . watchSends ( { affects : [ query1 ] } , docC )
618573 . watchSnapshots ( 2000 )
619- . expectEvents ( query1 , { fromCache : true } )
620- . expectLimboDocs ( docB . key ) // DocB is now in limbo.
621- . watchRemoves (
622- newQueryForPath ( docB . key . path ) ,
623- new RpcError ( Code . PERMISSION_DENIED , 'no' )
624- )
625- . expectLimboDocs ( ) // DocB is no longer in limbo.
626- . expectEvents ( query1 , {
627- removed : [ docB ]
628- } ) ;
629- }
630- ) ;
631-
632- specTest (
633- 'Bloom filter with large size works as expected' ,
634- // TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
635- [ 'no-ios' ] ,
636- ( ) => {
637- const query1 = query ( 'collection' ) ;
638- const docs = [ ] ;
639- for ( let i = 0 ; i < 100 ; i ++ ) {
640- docs . push ( doc ( `collection/doc${ i } ` , 1000 , { v : 1 } ) ) ;
641- }
642- const docKeys = docs . map ( item => item . key ) ;
574+ . expectEvents ( query1 , { added : [ docC ] , fromCache : true } )
575+ // Re-run of the query1 is skipped, docB is in limbo.
576+ . expectLimboDocs ( docB . key )
577+ ) ;
578+ } ) ;
643579
644- const bloomFilterProto = generateBloomFilterProto ( {
645- contains : docs . slice ( 0 , 50 ) ,
646- notContains : docs . slice ( 50 ) ,
647- bitCount : 1000 ,
648- hashCount : 16
580+ specTest ( 'Bloom filter limbo resolution is denied' , [ ] , ( ) => {
581+ const query1 = query ( 'collection' ) ;
582+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
583+ const docB = doc ( 'collection/b' , 1000 , { v : 1 } ) ;
584+ const bloomFilterProto = generateBloomFilterProto ( {
585+ contains : [ docA ] ,
586+ notContains : [ docB ]
587+ } ) ;
588+ return spec ( )
589+ . userListens ( query1 )
590+ . watchAcksFull ( query1 , 1000 , docA , docB )
591+ . expectEvents ( query1 , { added : [ docA , docB ] } )
592+ . watchFilters ( [ query1 ] , [ docA . key ] , bloomFilterProto )
593+ . watchSnapshots ( 2000 )
594+ . expectEvents ( query1 , { fromCache : true } )
595+ . expectLimboDocs ( docB . key ) // DocB is now in limbo.
596+ . watchRemoves (
597+ newQueryForPath ( docB . key . path ) ,
598+ new RpcError ( Code . PERMISSION_DENIED , 'no' )
599+ )
600+ . expectLimboDocs ( ) // DocB is no longer in limbo.
601+ . expectEvents ( query1 , {
602+ removed : [ docB ]
649603 } ) ;
650- return (
651- spec ( )
652- . userListens ( query1 )
653- . watchAcksFull ( query1 , 1000 , ...docs )
654- . expectEvents ( query1 , { added : docs } )
655- // Doc0 to doc49 are deleted in the next sync.
656- . watchFilters ( [ query1 ] , docKeys . slice ( 0 , 50 ) , bloomFilterProto )
657- . watchSnapshots ( 2000 )
658- // BloomFilter correctly identifies docs that deleted, skip full query.
659- . expectEvents ( query1 , { fromCache : true } )
660- . expectLimboDocs ( ...docKeys . slice ( 50 ) )
661- ) ;
604+ } ) ;
605+
606+ specTest ( 'Bloom filter with large size works as expected' , [ ] , ( ) => {
607+ const query1 = query ( 'collection' ) ;
608+ const docs = [ ] ;
609+ for ( let i = 0 ; i < 100 ; i ++ ) {
610+ docs . push ( doc ( `collection/doc${ i } ` , 1000 , { v : 1 } ) ) ;
662611 }
663- ) ;
612+ const docKeys = docs . map ( item => item . key ) ;
613+
614+ const bloomFilterProto = generateBloomFilterProto ( {
615+ contains : docs . slice ( 0 , 50 ) ,
616+ notContains : docs . slice ( 50 ) ,
617+ bitCount : 1000 ,
618+ hashCount : 16
619+ } ) ;
620+ return (
621+ spec ( )
622+ . userListens ( query1 )
623+ . watchAcksFull ( query1 , 1000 , ...docs )
624+ . expectEvents ( query1 , { added : docs } )
625+ // Doc0 to doc49 are deleted in the next sync.
626+ . watchFilters ( [ query1 ] , docKeys . slice ( 0 , 50 ) , bloomFilterProto )
627+ . watchSnapshots ( 2000 )
628+ // BloomFilter correctly identifies docs that deleted, skip full query.
629+ . expectEvents ( query1 , { fromCache : true } )
630+ . expectLimboDocs ( ...docKeys . slice ( 50 ) )
631+ ) ;
632+ } ) ;
664633} ) ;
0 commit comments