@@ -214,6 +214,7 @@ Status CommandHandler::ProcessInvokeRequest(System::PacketBufferHandle && payloa
214
214
SetGroupRequest (true );
215
215
}
216
216
217
+ // When updating this code, please remember to make corresponding changes to TestOnlyInvokeCommandRequestWithFaultsInjected.
217
218
VerifyOrReturnError (invokeRequestMessage.GetSuppressResponse (&mSuppressResponse ) == CHIP_NO_ERROR, Status::InvalidAction);
218
219
VerifyOrReturnError (invokeRequestMessage.GetTimedRequest (&mTimedRequest ) == CHIP_NO_ERROR, Status::InvalidAction);
219
220
VerifyOrReturnError (invokeRequestMessage.GetInvokeRequests (&invokeRequests) == CHIP_NO_ERROR, Status::InvalidAction);
@@ -911,6 +912,135 @@ void CommandHandler::MoveToState(const State aTargetState)
911
912
ChipLogDetail (DataManagement, " Command handler moving to [%10.10s]" , GetStateStr ());
912
913
}
913
914
915
+ #if CHIP_WITH_NLFAULTINJECTION
916
+
917
+ namespace {
918
+
919
+ CHIP_ERROR TestOnlyExtractCommandPathFromNextInvokeRequest (TLV::TLVReader & invokeRequestsReader,
920
+ ConcreteCommandPath & concretePath)
921
+ {
922
+ ReturnErrorOnFailure (invokeRequestsReader.Next (TLV::AnonymousTag ()));
923
+ CommandDataIB::Parser commandData;
924
+ ReturnErrorOnFailure (commandData.Init (invokeRequestsReader));
925
+ CommandPathIB::Parser commandPath;
926
+ ReturnErrorOnFailure (commandData.GetPath (&commandPath));
927
+ return commandPath.GetConcreteCommandPath (concretePath);
928
+ }
929
+
930
+ [[maybe_unused]] const char * GetFaultInjectionTypeStr (CommandHandler::NlFaultInjectionType faultType)
931
+ {
932
+ switch (faultType)
933
+ {
934
+ case CommandHandler::NlFaultInjectionType::SeparateResponseMessages:
935
+ return " Each response will be sent in a separate InvokeResponseMessage. The order of responses will be the same as the "
936
+ " original request." ;
937
+ case CommandHandler::NlFaultInjectionType::SeparateResponseMessagesAndInvertedResponseOrder:
938
+ return " Each response will be sent in a separate InvokeResponseMessage. The order of responses will be reversed from the "
939
+ " original request." ;
940
+ case CommandHandler::NlFaultInjectionType::SkipSecondResponse:
941
+ return " Single InvokeResponseMessages. Dropping response to second request" ;
942
+ }
943
+ VerifyOrDieWithMsg (false , DataManagement, " TH Failure: Unexpected fault type" );
944
+ }
945
+
946
+ } // anonymous namespace
947
+
948
+ // This method intentionally duplicates code from other sections. While code consolidation
949
+ // is generally preferred, here we prioritize generating a clear crash message to aid in
950
+ // troubleshooting test failures.
951
+ void CommandHandler::TestOnlyInvokeCommandRequestWithFaultsInjected (Messaging::ExchangeContext * ec,
952
+ System::PacketBufferHandle && payload, bool isTimedInvoke,
953
+ NlFaultInjectionType faultType)
954
+ {
955
+ VerifyOrDieWithMsg (ec != nullptr , DataManagement, " TH Failure: Incoming exchange context should not be null" );
956
+ VerifyOrDieWithMsg (mState == State::Idle, DataManagement, " TH Failure: state should be Idle, issue with TH" );
957
+
958
+ ChipLogProgress (DataManagement, " Response to InvokeRequestMessage overridden by fault injection" );
959
+ ChipLogProgress (DataManagement, " Injecting the following response:%s" , GetFaultInjectionTypeStr (faultType));
960
+
961
+ mResponseSender .SetExchangeContext (ec);
962
+ Handle workHandle (this );
963
+ mResponseSender .WillSendMessage ();
964
+ VerifyOrDieWithMsg (!mResponseSender .IsForGroup (), DataManagement, " DUT Failure: Unexpected Group Command" );
965
+
966
+ System::PacketBufferTLVReader reader;
967
+ InvokeRequestMessage::Parser invokeRequestMessage;
968
+ InvokeRequests::Parser invokeRequests;
969
+ reader.Init (std::move (payload));
970
+ VerifyOrDieWithMsg (invokeRequestMessage.Init (reader) == CHIP_NO_ERROR, DataManagement,
971
+ " TH Failure: Failed 'invokeRequestMessage.Init(reader)'" );
972
+ #if CHIP_CONFIG_IM_PRETTY_PRINT
973
+ invokeRequestMessage.PrettyPrint ();
974
+ #endif
975
+
976
+ VerifyOrDieWithMsg (invokeRequestMessage.GetSuppressResponse (&mSuppressResponse ) == CHIP_NO_ERROR, DataManagement,
977
+ " DUT Failure: Mandatory SuppressResponse field missing" );
978
+ VerifyOrDieWithMsg (invokeRequestMessage.GetTimedRequest (&mTimedRequest ) == CHIP_NO_ERROR, DataManagement,
979
+ " DUT Failure: Mandatory TimedRequest field missing" );
980
+ VerifyOrDieWithMsg (invokeRequestMessage.GetInvokeRequests (&invokeRequests) == CHIP_NO_ERROR, DataManagement,
981
+ " DUT Failure: Mandatory InvokeRequests field missing" );
982
+ VerifyOrDieWithMsg (mTimedRequest == isTimedInvoke, DataManagement,
983
+ " DUT Failure: TimedRequest value in message mismatches action" );
984
+
985
+ {
986
+ InvokeRequestMessage::Parser validationInvokeRequestMessage = invokeRequestMessage;
987
+ VerifyOrDieWithMsg (ValidateInvokeRequestMessageAndBuildRegistry (validationInvokeRequestMessage) == CHIP_NO_ERROR,
988
+ DataManagement, " DUT Failure: InvokeRequestMessage contents were invalid" );
989
+ }
990
+
991
+ TLV::TLVReader invokeRequestsReader;
992
+ invokeRequests.GetReader (&invokeRequestsReader);
993
+
994
+ size_t commandCount = 0 ;
995
+ VerifyOrDieWithMsg (TLV::Utilities::Count (invokeRequestsReader, commandCount, false /* recurse */ ) == CHIP_NO_ERROR,
996
+ DataManagement,
997
+ " TH Failure: Failed to get the length of InvokeRequests after InvokeRequestMessage validation" );
998
+
999
+ // The command count check (specifically for a count of 2) is tied to IDM_1_3. This may need adjustment for
1000
+ // compatibility with future test plans.
1001
+ VerifyOrDieWithMsg (commandCount == 2 , DataManagement, " DUT failure: We were strictly expecting exactly 2 InvokeRequests" );
1002
+ mReserveSpaceForMoreChunkMessages = true ;
1003
+
1004
+ {
1005
+ // Response path is the same as request path since we are replying with a failure message.
1006
+ ConcreteCommandPath concreteResponsePath1;
1007
+ ConcreteCommandPath concreteResponsePath2;
1008
+ VerifyOrDieWithMsg (
1009
+ TestOnlyExtractCommandPathFromNextInvokeRequest (invokeRequestsReader, concreteResponsePath1) == CHIP_NO_ERROR,
1010
+ DataManagement, " DUT Failure: Issues encountered while extracting the ConcreteCommandPath from the first request" );
1011
+ VerifyOrDieWithMsg (
1012
+ TestOnlyExtractCommandPathFromNextInvokeRequest (invokeRequestsReader, concreteResponsePath2) == CHIP_NO_ERROR,
1013
+ DataManagement, " DUT Failure: Issues encountered while extracting the ConcreteCommandPath from the second request" );
1014
+
1015
+ if (faultType == NlFaultInjectionType::SeparateResponseMessagesAndInvertedResponseOrder)
1016
+ {
1017
+ ConcreteCommandPath temp (concreteResponsePath1);
1018
+ concreteResponsePath1 = concreteResponsePath2;
1019
+ concreteResponsePath2 = temp;
1020
+ }
1021
+
1022
+ VerifyOrDieWithMsg (FallibleAddStatus (concreteResponsePath1, Status::Failure) == CHIP_NO_ERROR, DataManagement,
1023
+ " TH Failure: Error adding the first InvokeResponse" );
1024
+ if (faultType == NlFaultInjectionType::SeparateResponseMessages ||
1025
+ faultType == NlFaultInjectionType::SeparateResponseMessagesAndInvertedResponseOrder)
1026
+ {
1027
+ VerifyOrDieWithMsg (FinalizeInvokeResponseMessageAndPrepareNext () == CHIP_NO_ERROR, DataManagement,
1028
+ " TH Failure: Failed to create second InvokeResponseMessage" );
1029
+ }
1030
+ if (faultType != NlFaultInjectionType::SkipSecondResponse)
1031
+ {
1032
+ VerifyOrDieWithMsg (FallibleAddStatus (concreteResponsePath2, Status::Failure) == CHIP_NO_ERROR, DataManagement,
1033
+ " TH Failure: Error adding the second InvokeResponse" );
1034
+ }
1035
+ }
1036
+
1037
+ VerifyOrDieWithMsg (invokeRequestsReader.Next () == CHIP_END_OF_TLV, DataManagement,
1038
+ " DUT Failure: Unexpected TLV ending of InvokeRequests" );
1039
+ VerifyOrDieWithMsg (invokeRequestMessage.ExitContainer () == CHIP_NO_ERROR, DataManagement,
1040
+ " DUT Failure: InvokeRequestMessage TLV is not properly terminated" );
1041
+ }
1042
+ #endif // CHIP_WITH_NLFAULTINJECTION
1043
+
914
1044
} // namespace app
915
1045
} // namespace chip
916
1046
0 commit comments