@@ -99,13 +99,19 @@ class MockConnection implements Connection {
99
99
* Used to make sure a write was actually sent out on the network before the
100
100
* test runner continues.
101
101
*/
102
- writeSendBarriers : Array < Deferred < api . Write > > = [ ] ;
102
+ writeSendBarriers : Array < Deferred < api . WriteRequest > > = [ ] ;
103
103
104
104
/**
105
105
* The set of mutations sent out before there was a corresponding
106
106
* writeSendBarrier.
107
107
*/
108
- earlyWrites : api . Write [ ] = [ ] ;
108
+ earlyWrites : api . WriteRequest [ ] = [ ] ;
109
+
110
+ /** The total number of requests sent to the watch stream. */
111
+ watchStreamRequestCount = 0 ;
112
+
113
+ /** The total number of requests sent to the write stream. */
114
+ writeStreamRequestCount = 0 ;
109
115
110
116
nextWriteStreamToken = 0 ;
111
117
@@ -120,15 +126,22 @@ class MockConnection implements Connection {
120
126
/** A Deferred that is resolved once watch opens. */
121
127
watchOpen = new Deferred < void > ( ) ;
122
128
129
+ reset ( ) : void {
130
+ this . watchStreamRequestCount = 0 ;
131
+ this . writeStreamRequestCount = 0 ;
132
+ this . earlyWrites = [ ] ;
133
+ this . activeTargets = [ ] ;
134
+ }
135
+
123
136
invoke ( rpcName : string , request : any ) : Promise < any > {
124
137
throw new Error ( 'Not implemented!' ) ;
125
138
}
126
139
127
- waitForWriteSend ( ) : Promise < api . Write > {
140
+ waitForWriteRequest ( ) : Promise < api . WriteRequest > {
128
141
if ( this . earlyWrites . length > 0 ) {
129
142
return Promise . resolve ( this . earlyWrites . shift ( ) ) as AnyDuringMigration ;
130
143
}
131
- const barrier = new Deferred < api . Write > ( ) ;
144
+ const barrier = new Deferred < WriteRequest > ( ) ;
132
145
this . writeSendBarriers . push ( barrier ) ;
133
146
return barrier . promise ;
134
147
}
@@ -176,6 +189,7 @@ class MockConnection implements Connection {
176
189
let firstCall = true ;
177
190
const writeStream = new StreamBridge < WriteRequest , api . WriteResponse > ( {
178
191
sendFn : ( request : WriteRequest ) => {
192
+ ++ this . writeStreamRequestCount ;
179
193
if ( firstCall ) {
180
194
assert (
181
195
! ! request . database ,
@@ -195,27 +209,19 @@ class MockConnection implements Connection {
195
209
'streamToken must be set on all writes'
196
210
) ;
197
211
assert ( ! ! request . writes , 'writes must be set on all writes' ) ;
198
- assert (
199
- request . writes ! . length > 0 ,
200
- 'there must be non-zero mutations'
201
- ) ;
202
- if ( request . writes ! . length !== 1 ) {
203
- // TODO(dimond): support batching?
204
- fail ( 'Unexpected batched mutation found!' ) ;
205
- }
206
212
207
213
const barrier = this . writeSendBarriers . shift ( ) ;
208
214
if ( ! barrier ) {
209
215
// The test runner hasn't set up the barrier yet, so we queue
210
216
// up this mutation to provide to the barrier promise when it
211
217
// arrives.
212
- this . earlyWrites . push ( request . writes ! [ 0 ] ) ;
218
+ this . earlyWrites . push ( request ) ;
213
219
} else {
214
220
// The test runner is waiting on a write invocation, now that we
215
221
// have it we can resolve the write send barrier. If we add
216
222
// (automatic) batching support we need to make sure the number of
217
- // batches matches the number of calls to waitForWriteSend .
218
- barrier . resolve ( request . writes ! [ 0 ] ) ;
223
+ // batches matches the number of calls to waitForWriteRequest .
224
+ barrier . resolve ( request ) ;
219
225
}
220
226
} ,
221
227
closeFn : ( ) => {
@@ -240,6 +246,7 @@ class MockConnection implements Connection {
240
246
api . ListenResponse
241
247
> ( {
242
248
sendFn : ( request : api . ListenRequest ) => {
249
+ ++ this . watchStreamRequestCount ;
243
250
if ( request . addTarget ) {
244
251
const targetId = request . addTarget . targetId ! ;
245
252
this . activeTargets [ targetId ] = request . addTarget ;
@@ -407,6 +414,7 @@ abstract class TestRunner {
407
414
protected abstract destroyPersistence ( ) : Promise < void > ;
408
415
409
416
async start ( ) : Promise < void > {
417
+ this . connection . reset ( ) ;
410
418
await this . persistence . start ( ) ;
411
419
await this . localStore . start ( ) ;
412
420
await this . remoteStore . start ( ) ;
@@ -458,6 +466,10 @@ abstract class TestRunner {
458
466
return this . doWriteAck ( step . writeAck ! ) ;
459
467
} else if ( 'failWrite' in step ) {
460
468
return this . doFailWrite ( step . failWrite ! ) ;
469
+ } else if ( 'enableNetwork' in step ) {
470
+ return step . enableNetwork !
471
+ ? this . doEnableNetwork ( )
472
+ : this . doDisableNetwork ( ) ;
461
473
} else if ( 'restart' in step ) {
462
474
assert ( step . restart ! , 'Restart cannot be false' ) ;
463
475
return this . doRestart ( ) ;
@@ -517,9 +529,9 @@ abstract class TestRunner {
517
529
private doMutations ( mutations : Mutation [ ] ) : Promise < void > {
518
530
const userCallback = new Deferred < void > ( ) ;
519
531
this . outstandingWrites . push ( { mutations, userCallback } ) ;
520
- return this . queue . schedule ( ( ) =>
521
- this . syncEngine . write ( mutations , userCallback )
522
- ) ;
532
+ return this . queue . schedule ( ( ) => {
533
+ return this . syncEngine . write ( mutations , userCallback ) ;
534
+ } ) ;
523
535
}
524
536
525
537
private doWatchAck (
@@ -693,19 +705,24 @@ abstract class TestRunner {
693
705
}
694
706
695
707
/** Validates that a write was sent and matches the expected write. */
696
- private validateNextWriteSent ( mutations : Mutation [ ] ) : Promise < void > {
708
+ private validateNextWriteRequest ( mutations : Mutation [ ] ) : Promise < void > {
697
709
// Make sure this write was sent on the wire and it matches the expected
698
710
// write.
699
- return this . connection . waitForWriteSend ( ) . then ( write => {
700
- assert ( mutations . length === 1 , "We don't support multiple mutations." ) ;
701
- expect ( write ) . to . deep . equal ( this . serializer . toMutation ( mutations [ 0 ] ) ) ;
711
+ return this . connection . waitForWriteRequest ( ) . then ( request => {
712
+ const writes = request . writes ! ;
713
+ expect ( writes . length ) . to . equal ( mutations . length ) ;
714
+ for ( let i = 0 ; i < writes . length ; ++ i ) {
715
+ expect ( writes [ i ] ) . to . deep . equal (
716
+ this . serializer . toMutation ( mutations [ i ] )
717
+ ) ;
718
+ }
702
719
} ) ;
703
720
}
704
721
705
722
private doWriteAck ( writeAck : SpecWriteAck ) : Promise < void > {
706
723
const updateTime = this . serializer . toVersion ( version ( writeAck . version ) ) ;
707
724
const nextWrite = this . outstandingWrites . shift ( ) ! ;
708
- return this . validateNextWriteSent ( nextWrite . mutations ) . then ( ( ) => {
725
+ return this . validateNextWriteRequest ( nextWrite . mutations ) . then ( ( ) => {
709
726
this . connection . ackWrite ( updateTime , [ { updateTime } ] ) ;
710
727
if ( writeAck . expectUserCallback ) {
711
728
return nextWrite . userCallback . promise ;
@@ -722,7 +739,7 @@ abstract class TestRunner {
722
739
specError . message
723
740
) ;
724
741
const nextWrite = this . outstandingWrites . shift ( ) ! ;
725
- return this . validateNextWriteSent ( nextWrite . mutations ) . then ( ( ) => {
742
+ return this . validateNextWriteRequest ( nextWrite . mutations ) . then ( ( ) => {
726
743
// If this is not a permanent error, the write is expected to be sent
727
744
// again.
728
745
if ( ! isPermanentError ( error . code ) ) {
@@ -745,6 +762,17 @@ abstract class TestRunner {
745
762
} ) ;
746
763
}
747
764
765
+ private async doDisableNetwork ( ) : Promise < void > {
766
+ // Make sure to execute all writes that are currently queued. This allows us
767
+ // to assert on the total number of requests sent before shutdown.
768
+ await this . remoteStore . fillWritePipeline ( ) ;
769
+ await this . remoteStore . disableNetwork ( ) ;
770
+ }
771
+
772
+ private async doEnableNetwork ( ) : Promise < void > {
773
+ await this . remoteStore . enableNetwork ( ) ;
774
+ }
775
+
748
776
private async doRestart ( ) : Promise < void > {
749
777
// Reinitialize everything, except the persistence.
750
778
// No local store to shutdown.
@@ -793,6 +821,16 @@ abstract class TestRunner {
793
821
expectation . numOutstandingWrites
794
822
) ;
795
823
}
824
+ if ( 'writeStreamRequestCount' in expectation ) {
825
+ expect ( this . connection . writeStreamRequestCount ) . to . deep . equal (
826
+ expectation . writeStreamRequestCount
827
+ ) ;
828
+ }
829
+ if ( 'watchStreamRequestCount' in expectation ) {
830
+ expect ( this . connection . watchStreamRequestCount ) . to . deep . equal (
831
+ expectation . watchStreamRequestCount
832
+ ) ;
833
+ }
796
834
if ( 'limboDocs' in expectation ) {
797
835
this . expectedLimboDocs = expectation . limboDocs ! . map ( key ) ;
798
836
}
@@ -1052,6 +1090,9 @@ export interface SpecStep {
1052
1090
/** Fail a write */
1053
1091
failWrite ?: SpecWriteFailure ;
1054
1092
1093
+ /** Enable or disable RemoteStore's network connection. */
1094
+ enableNetwork ?: boolean ;
1095
+
1055
1096
/** Change to a new active user (specified by uid or null for anonymous). */
1056
1097
changeUser ?: string | null ;
1057
1098
@@ -1193,6 +1234,10 @@ export interface SpecExpectation {
1193
1234
export interface StateExpectation {
1194
1235
/** Number of outstanding writes in the datastore queue. */
1195
1236
numOutstandingWrites ?: number ;
1237
+ /** Number of requests sent to the write stream. */
1238
+ writeStreamRequestCount ?: number ;
1239
+ /** Number of requests sent to the watch stream. */
1240
+ watchStreamRequestCount ?: number ;
1196
1241
/** Current documents in limbo. Verified in each step until overwritten. */
1197
1242
limboDocs ?: string [ ] ;
1198
1243
/**
0 commit comments