@@ -192,9 +192,12 @@ public void SendNOSMessage()
192192 _session ! . Next ( CreateNOSMessage ( _seqNum ++ ) . ConstructString ( ) ) ;
193193 }
194194
195- public void SendNOSMessage ( SeqNumType n )
195+ public void SendNOSMessage ( SeqNumType n , bool possDupFlag = false )
196196 {
197- _session ! . Next ( CreateNOSMessage ( n ) . ConstructString ( ) ) ;
197+ var msg = CreateNOSMessage ( n ) ;
198+ if ( possDupFlag )
199+ msg . Header . SetField ( new QuickFix . Fields . PossDupFlag ( possDupFlag ) ) ;
200+ _session ! . Next ( msg . ConstructString ( ) ) ;
198201 }
199202
200203 public void SendResendRequest ( SeqNumType begin , SeqNumType end )
@@ -212,11 +215,15 @@ public void SendResendRequest40(SeqNumType begin, SeqNumType end)
212215 }
213216
214217 private void SendTheMessage ( QuickFix . Message msg )
218+ {
219+ SendTheMessage ( msg , _seqNum ++ ) ;
220+ }
221+
222+ private void SendTheMessage ( QuickFix . Message msg , SeqNumType seqNum )
215223 {
216224 msg . Header . SetField ( new QuickFix . Fields . TargetCompID ( _sessionId . SenderCompID ) ) ;
217225 msg . Header . SetField ( new QuickFix . Fields . SenderCompID ( _sessionId . TargetCompID ) ) ;
218- msg . Header . SetField ( new QuickFix . Fields . MsgSeqNum ( _seqNum ++ ) ) ;
219-
226+ msg . Header . SetField ( new QuickFix . Fields . MsgSeqNum ( seqNum ) ) ;
220227 _session ! . Next ( msg . ConstructString ( ) ) ;
221228 }
222229
@@ -961,5 +968,100 @@ public void TestBasicResendRequest_WithMax() {
961968 Assert . That ( _session . IsResendRequested , Is . False , $ "seq num { i } ") ;
962969 }
963970 }
971+
972+ [ Test ]
973+ public void TestSequenceResetNoGapFillIsProcessed ( )
974+ {
975+ Assert . That ( _session ! . IgnorePossDupResendRequests , Is . EqualTo ( false ) ) ;
976+ Logon ( ) ;
977+ SendNOSMessage ( ) ;
978+ SendNOSMessage ( ) ;
979+ Assert . That ( _session . NextTargetMsgSeqNum , Is . EqualTo ( 4 ) ) ;
980+
981+ QuickFix . FIX42 . SequenceReset sr = new ( new QuickFix . Fields . NewSeqNo ( 150 ) ) ;
982+ SendTheMessage ( sr ) ;
983+
984+ Assert . That ( _session . NextTargetMsgSeqNum , Is . EqualTo ( 150 ) ) ;
985+ }
986+
987+ [ Test ]
988+ public void TestSequenceResetWithGapFillIsProcessed ( )
989+ {
990+ Assert . That ( _session ! . IgnorePossDupResendRequests , Is . EqualTo ( false ) ) ;
991+ Logon ( ) ;
992+ SendNOSMessage ( ) ;
993+ SendNOSMessage ( ) ;
994+ Assert . That ( _session . NextTargetMsgSeqNum , Is . EqualTo ( 4 ) ) ;
995+
996+ QuickFix . FIX42 . SequenceReset sr = new ( new QuickFix . Fields . NewSeqNo ( 150 ) ) ;
997+ sr . GapFillFlag = new QuickFix . Fields . GapFillFlag ( true ) ;
998+ SendTheMessage ( sr ) ;
999+
1000+ Assert . That ( _session . NextTargetMsgSeqNum , Is . EqualTo ( 150 ) ) ;
1001+ }
1002+
1003+ [ Test ]
1004+ public void TestSequenceResetDuringResendRequestIsProcessed ( )
1005+ {
1006+ Assert . That ( _session ! . IgnorePossDupResendRequests , Is . EqualTo ( false ) ) ;
1007+ _session . RequiresOrigSendingTime = false ; // default is true
1008+ Logon ( ) ;
1009+ SendNOSMessage ( ) ; // seq 2
1010+ SendNOSMessage ( 10 ) ; // causes ResendRequest to be sent
1011+ Assert . That ( _session . NextTargetMsgSeqNum , Is . EqualTo ( 3 ) ) ;
1012+
1013+ var resendRequest =
1014+ ( QuickFix . FIX42 . ResendRequest ) _responder . MsgLookup [ QuickFix . Fields . MsgType . RESEND_REQUEST ] . Dequeue ( ) ;
1015+ Assert . That ( resendRequest . BeginSeqNo . Value , Is . EqualTo ( 3 ) ) ;
1016+ Assert . That ( resendRequest . EndSeqNo . Value , Is . EqualTo ( 0 ) ) ;
1017+
1018+ // Resend & GapFill through seq=9
1019+ SendNOSMessage ( 3 ) ;
1020+ SendNOSMessage ( 4 ) ;
1021+ QuickFix . FIX42 . SequenceReset sr = new ( new QuickFix . Fields . NewSeqNo ( 9 ) ) ;
1022+ SendTheMessage ( sr , 5 ) ;
1023+ SendNOSMessage ( 9 ) ;
1024+
1025+ // We already processed 10, so the next one is 11
1026+ Assert . That ( _session . NextTargetMsgSeqNum , Is . EqualTo ( 11 ) ) ;
1027+ }
1028+
1029+ [ Test ]
1030+ public void TestGapFillShouldNotBeIgnoredIfPossDup ( )
1031+ {
1032+ Assert . That ( _session ! . IgnorePossDupResendRequests , Is . EqualTo ( false ) ) ;
1033+ _session . RequiresOrigSendingTime = false ; // default is true
1034+ Logon ( ) ;
1035+ SendNOSMessage ( ) ; // seq 2
1036+
1037+ QuickFix . FIX42 . Heartbeat hb = new ( ) ;
1038+ SendTheMessage ( hb , 5 ) ; // seq=5, causes ResendRequest to be sent
1039+
1040+ // Verify the ResendRequest that was just sent
1041+ var resendRequest =
1042+ ( QuickFix . FIX42 . ResendRequest ) _responder . MsgLookup [ QuickFix . Fields . MsgType . RESEND_REQUEST ] . Dequeue ( ) ;
1043+ Assert . That ( resendRequest . BeginSeqNo . Value , Is . EqualTo ( 3 ) ) ;
1044+ Assert . That ( resendRequest . EndSeqNo . Value , Is . EqualTo ( 0 ) ) ;
1045+
1046+ Assert . That ( _session . NextTargetMsgSeqNum , Is . EqualTo ( 3 ) ) ;
1047+
1048+ // Resends
1049+ SendNOSMessage ( 3 , possDupFlag : true ) ;
1050+ SendNOSMessage ( 4 , possDupFlag : true ) ;
1051+ // Now the session processes the Heartbeat/seq=5 from its queue.
1052+ Assert . That ( _session . NextTargetMsgSeqNum , Is . EqualTo ( 6 ) ) ;
1053+
1054+ // But the counterparty still needs to resend the seq=5!
1055+ // The 5 is a Heartbeat, an admin message, so it's a gapfill.
1056+ // The gapfill goes to 7, omitting ever sending a 6.
1057+ QuickFix . FIX42 . SequenceReset sr = new ( new QuickFix . Fields . NewSeqNo ( 7 ) ) ;
1058+ sr . Header . SetField ( new QuickFix . Fields . PossDupFlag ( true ) ) ;
1059+ sr . SetField ( new QuickFix . Fields . GapFillFlag ( true ) ) ;
1060+ SendTheMessage ( sr , 5 ) ;
1061+
1062+ // Even though client already processed the original Heartbeat/seq=5,
1063+ // it must not discard the GapFill 5.
1064+ Assert . That ( _session . NextTargetMsgSeqNum , Is . EqualTo ( 7 ) ) ;
1065+ }
9641066}
9651067
0 commit comments