text: /*
   *
   *    Copyright (c) 2021 Project CHIP Authors
   *    All rights reserved.
   *
   *    Licensed under the Apache License, Version 2.0 (the "License");
   *    you may not use this file except in compliance with the License.
   *    You may obtain a copy of the License at
   *
   *        http://www.apache.org/licenses/LICENSE-2.0
   *
   *    Unless required by applicable law or agreed to in writing, software
   *    distributed under the License is distributed on an "AS IS" BASIS,
   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *    See the License for the specific language governing permissions and
   *    limitations under the License.
   */
  
  #include "app-common/zap-generated/ids/Attributes.h"
  #include "lib/core/CHIPTLVTags.h"
  #include "protocols/interaction_model/Constants.h"
  #include "system/SystemPacketBuffer.h"
  #include "system/TLVPacketBufferBackingStore.h"
  #include <app-common/zap-generated/cluster-objects.h>
  #include <app/BufferedReadCallback.h>
  #include <app/data-model/DecodableList.h>
  #include <app/data-model/Decode.h>
  #include <app/tests/AppTestContext.h>
  #include <lib/support/UnitTestRegistration.h>
  #include <nlunit-test.h>
  #include <vector>
  
  using TestContext = chip::Test::AppContext;
  using namespace chip::app;
  using namespace chip;
  
  namespace {
  
  nlTestSuite * gSuite = nullptr;
  
  struct ValidationInstruction
  {
      enum ProcessingType
      {
          kValidChunk,
          kDiscardedChunk
      };
  
      enum ValidationType
      {
          kSimpleAttributeA,
          kSimpleAttributeB,
          kListAttributeC_Empty,
          kListAttributeC_NotEmpty,
          kListAttributeC_NotEmpty_Chunked,
          kListAttributeC_Error,
          kListAttributeD_Empty,
          kListAttributeD_NotEmpty,
          kListAttributeD_NotEmpty_Chunked,
          kListAttributeD_Error
      };
  
      ValidationType mValidationType;
      ProcessingType mProcessingType = kValidChunk;
  };
  
  using InstructionListType = std::vector<ValidationInstruction>;
  
  class DataSeriesValidator : public BufferedReadCallback::Callback
  {
  public:
      DataSeriesValidator(std::vector<ValidationInstruction> validationInstructionList)
      {
          mInstructionList = validationInstructionList;
      }
  
      //
      // BufferedReadCallback::Callback
      //
  
      void OnReportBegin() override;
      void OnReportEnd() override;
      void OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus) override;
      void OnDone() override {}
  
      std::vector<ValidationInstruction> mInstructionList;
      uint32_t mCurrentInstruction = 0;
  };
  
  void DataSeriesValidator::OnReportBegin()
  {
      mCurrentInstruction = 0;
  }
  
  void DataSeriesValidator::OnReportEnd() {}
  
  void DataSeriesValidator::OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
                                            const StatusIB & aStatus)
  {
      uint32_t expectedListLength;
  
      while ((mCurrentInstruction < mInstructionList.size()) &&
             (mInstructionList[mCurrentInstruction].mProcessingType == ValidationInstruction::kDiscardedChunk))
      {
          mCurrentInstruction++;
      }
  
      if (mCurrentInstruction >= mInstructionList.size())
      {
          return;
      }
  
      switch (mInstructionList[mCurrentInstruction].mValidationType)
      {
      case ValidationInstruction::kSimpleAttributeA: {
          ChipLogProgress(DataManagement, "\t\t -- Validating A");
  
          Clusters::TestCluster::Attributes::Int8u::TypeInfo::Type value;
          NL_TEST_ASSERT(gSuite,
                         aPath.mEndpointId == 0 && aPath.mClusterId == Clusters::TestCluster::Id &&
                             aPath.mAttributeId == Clusters::TestCluster::Attributes::Int8u::Id &&
                             aPath.mListOp == ConcreteDataAttributePath::ListOperation::NotList);
          NL_TEST_ASSERT(gSuite, DataModel::Decode(*apData, value) == CHIP_NO_ERROR);
          NL_TEST_ASSERT(gSuite, value == mCurrentInstruction);
          break;
      }
  
      case ValidationInstruction::kSimpleAttributeB: {
          ChipLogProgress(DataManagement, "\t\t -- Validating B");
  
          Clusters::TestCluster::Attributes::Int32u::TypeInfo::Type value;
          NL_TEST_ASSERT(gSuite,
                         aPath.mEndpointId == 0 && aPath.mClusterId == Clusters::TestCluster::Id &&
                             aPath.mAttributeId == Clusters::TestCluster::Attributes::Int32u::Id &&
                             aPath.mListOp == ConcreteDataAttributePath::ListOperation::NotList);
          NL_TEST_ASSERT(gSuite, DataModel::Decode(*apData, value) == CHIP_NO_ERROR);
          NL_TEST_ASSERT(gSuite, value == mCurrentInstruction);
          break;
      }
  
      case ValidationInstruction::kListAttributeC_Empty: {
          ChipLogProgress(DataManagement, "\t\t -- Validating C[]");
  
          Clusters::TestCluster::Attributes::ListStructOctetString::TypeInfo::DecodableType value;
          size_t len;
  
          NL_TEST_ASSERT(gSuite,
                         aPath.mEndpointId == 0 && aPath.mClusterId == Clusters::TestCluster::Id &&
                             aPath.mAttributeId == Clusters::TestCluster::Attributes::ListStructOctetString::Id &&
                             aPath.mListOp == ConcreteDataAttributePath::ListOperation::ReplaceAll);
          NL_TEST_ASSERT(gSuite, DataModel::Decode(*apData, value) == CHIP_NO_ERROR);
          NL_TEST_ASSERT(gSuite, value.ComputeSize(&len) == CHIP_NO_ERROR);
          NL_TEST_ASSERT(gSuite, len == 0);
  
          break;
      }
  
      case ValidationInstruction::kListAttributeC_NotEmpty_Chunked:
      case ValidationInstruction::kListAttributeC_NotEmpty: {
          if (mInstructionList[mCurrentInstruction].mValidationType == ValidationInstruction::kListAttributeC_NotEmpty_Chunked)
          {
              expectedListLength = 512;
          }
          else
          {
              expectedListLength = 2;
          }
  
          ChipLogProgress(DataManagement, "\t\t -- Validating C[%" PRIu32 "]", expectedListLength);
  
          Clusters::TestCluster::Attributes::ListStructOctetString::TypeInfo::DecodableType value;
          size_t len;
  
          NL_TEST_ASSERT(gSuite,
                         aPath.mEndpointId == 0 && aPath.mClusterId == Clusters::TestCluster::Id &&
                             aPath.mAttributeId == Clusters::TestCluster::Attributes::ListStructOctetString::Id &&
                             aPath.mListOp == ConcreteDataAttributePath::ListOperation::ReplaceAll);
          NL_TEST_ASSERT(gSuite, DataModel::Decode(*apData, value) == CHIP_NO_ERROR);
          NL_TEST_ASSERT(gSuite, value.ComputeSize(&len) == CHIP_NO_ERROR);
          NL_TEST_ASSERT(gSuite, len == expectedListLength);
  
          auto iter = value.begin();
  
          uint32_t index = 0;
          while (iter.Next() && index < expectedListLength)
          {
              auto & iterValue = iter.GetValue();
              NL_TEST_ASSERT(gSuite, iterValue.fabricIndex == (index));
              index++;
          }
  
          NL_TEST_ASSERT(gSuite, iter.GetStatus() == CHIP_NO_ERROR);
          break;
      }
  
      case ValidationInstruction::kListAttributeD_Empty: {
          ChipLogProgress(DataManagement, "\t\t -- Validating D[]");
  
          Clusters::TestCluster::Attributes::ListInt8u::TypeInfo::DecodableType value;
          size_t len;
  
          NL_TEST_ASSERT(gSuite,
                         aPath.mEndpointId == 0 && aPath.mClusterId == Clusters::TestCluster::Id &&
                             aPath.mAttributeId == Clusters::TestCluster::Attributes::ListInt8u::Id &&
                             aPath.mListOp == ConcreteDataAttributePath::ListOperation::ReplaceAll);
          NL_TEST_ASSERT(gSuite, DataModel::Decode(*apData, value) == CHIP_NO_ERROR);
          NL_TEST_ASSERT(gSuite, value.ComputeSize(&len) == CHIP_NO_ERROR);
          NL_TEST_ASSERT(gSuite, len == 0);
  
          break;
      }
  
      case ValidationInstruction::kListAttributeD_NotEmpty:
      case ValidationInstruction::kListAttributeD_NotEmpty_Chunked: {
          if (mInstructionList[mCurrentInstruction].mValidationType == ValidationInstruction::kListAttributeD_NotEmpty_Chunked)
          {
              expectedListLength = 512;
          }
          else
          {
              expectedListLength = 2;
          }
  
          ChipLogProgress(DataManagement, "\t\t -- Validating D[%" PRIu32 "]", expectedListLength);
  
          Clusters::TestCluster::Attributes::ListInt8u::TypeInfo::DecodableType value;
          size_t len;
  
          NL_TEST_ASSERT(gSuite,
                         aPath.mEndpointId == 0 && aPath.mClusterId == Clusters::TestCluster::Id &&
                             aPath.mAttributeId == Clusters::TestCluster::Attributes::ListInt8u::Id &&
                             aPath.mListOp == ConcreteDataAttributePath::ListOperation::ReplaceAll);
          NL_TEST_ASSERT(gSuite, DataModel::Decode(*apData, value) == CHIP_NO_ERROR);
          NL_TEST_ASSERT(gSuite, value.ComputeSize(&len) == CHIP_NO_ERROR);
          NL_TEST_ASSERT(gSuite, len == expectedListLength);
  
          auto iter = value.begin();
  
          uint8_t index = 0;
          while (iter.Next() && index < expectedListLength)
          {
              auto & iterValue = iter.GetValue();
              NL_TEST_ASSERT(gSuite, iterValue == (index));
              index++;
          }
  
          NL_TEST_ASSERT(gSuite, iter.GetStatus() == CHIP_NO_ERROR);
          break;
      }
  
      case ValidationInstruction::kListAttributeC_Error: {
          ChipLogProgress(DataManagement, "\t\t -- Validating C|e");
  
          NL_TEST_ASSERT(gSuite,
                         aPath.mEndpointId == 0 && aPath.mClusterId == Clusters::TestCluster::Id &&
                             aPath.mAttributeId == Clusters::TestCluster::Attributes::ListStructOctetString::Id &&
                             aPath.mListOp == ConcreteDataAttributePath::ListOperation::ReplaceAll);
          NL_TEST_ASSERT(gSuite, aStatus.mStatus == Protocols::InteractionModel::Status::Failure);
          break;
      }
  
      case ValidationInstruction::kListAttributeD_Error: {
          ChipLogProgress(DataManagement, "\t\t -- Validating D|e");
  
          NL_TEST_ASSERT(gSuite,
                         aPath.mEndpointId == 0 && aPath.mClusterId == Clusters::TestCluster::Id &&
                             aPath.mAttributeId == Clusters::TestCluster::Attributes::ListInt8u::Id &&
                             aPath.mListOp == ConcreteDataAttributePath::ListOperation::ReplaceAll);
          NL_TEST_ASSERT(gSuite, aStatus.mStatus == Protocols::InteractionModel::Status::Failure);
  
          break;
      }
  
      default:
          break;
      }
  
      mCurrentInstruction++;
  }
  
  class DataSeriesGenerator
  {
  public:
      DataSeriesGenerator(BufferedReadCallback & readCallback, std::vector<ValidationInstruction> instructionList) :
          mReadCallback(readCallback), mInstructionList(instructionList)
      {}
  
      void Generate();
  
  private:
      BufferedReadCallback & mReadCallback;
      std::vector<ValidationInstruction> mInstructionList;
  };
  
  void DataSeriesGenerator::Generate()
  {
      System::PacketBufferHandle handle;
      System::PacketBufferTLVWriter writer;
      ConcreteDataAttributePath path(0, Clusters::TestCluster::Id, 0);
      System::PacketBufferTLVReader reader;
      ReadClient::Callback * callback = &mReadCallback;
      StatusIB status;
      bool hasData;
  
      callback->OnReportBegin();
  
      uint8_t index = 0;
      for (auto & instruction : mInstructionList)
      {
          handle = System::PacketBufferHandle::New(1000);
          writer.Init(std::move(handle), true);
          status  = StatusIB();
          hasData = true;
  
          switch (instruction.mValidationType)
          {
          case ValidationInstruction::kSimpleAttributeA: {
              ChipLogProgress(DataManagement, "\t -- Generating A");
  
              Clusters::TestCluster::Attributes::Int8u::TypeInfo::Type value = index;
              path.mAttributeId                                              = Clusters::TestCluster::Attributes::Int8u::Id;
              path.mListOp                                                   = ConcreteDataAttributePath::ListOperation::NotList;
              NL_TEST_ASSERT(gSuite, DataModel::Encode(writer, TLV::AnonymousTag(), value) == CHIP_NO_ERROR);
              break;
          }
  
          case ValidationInstruction::kSimpleAttributeB: {
              ChipLogProgress(DataManagement, "\t -- Generating B");
  
              Clusters::TestCluster::Attributes::Int32u::TypeInfo::Type value = index;
              path.mAttributeId                                               = Clusters::TestCluster::Attributes::Int32u::Id;
              path.mListOp                                                    = ConcreteDataAttributePath::ListOperation::NotList;
              NL_TEST_ASSERT(gSuite, DataModel::Encode(writer, TLV::AnonymousTag(), value) == CHIP_NO_ERROR);
              break;
          }
  
          case ValidationInstruction::kListAttributeC_Empty: {
              ChipLogProgress(DataManagement, "\t -- Generating C[]");
  
              Clusters::TestCluster::Attributes::ListStructOctetString::TypeInfo::Type value;
              path.mAttributeId = Clusters::TestCluster::Attributes::ListStructOctetString::Id;
              path.mListOp      = ConcreteDataAttributePath::ListOperation::ReplaceAll;
              NL_TEST_ASSERT(gSuite, DataModel::Encode(writer, TLV::AnonymousTag(), value) == CHIP_NO_ERROR);
              break;
          }
  
          case ValidationInstruction::kListAttributeC_NotEmpty: {
              ChipLogProgress(DataManagement, "\t -- Generating C[2]");
  
              Clusters::TestCluster::Structs::TestListStructOctet::Type listData[2];
              Clusters::TestCluster::Attributes::ListStructOctetString::TypeInfo::Type value(listData);
  
              uint8_t index2 = 0;
              for (auto & item : listData)
              {
                  item.fabricIndex = index2;
                  index2++;
              }
  
              path.mAttributeId = Clusters::TestCluster::Attributes::ListStructOctetString::Id;
              path.mListOp      = ConcreteDataAttributePath::ListOperation::ReplaceAll;
  
              NL_TEST_ASSERT(gSuite, DataModel::Encode(writer, TLV::AnonymousTag(), value) == CHIP_NO_ERROR);
              break;
          }
  
          case ValidationInstruction::kListAttributeD_Empty: {
              ChipLogProgress(DataManagement, "\t -- Generating D[]");
  
              Clusters::TestCluster::Attributes::ListInt8u::TypeInfo::Type value;
              path.mAttributeId = Clusters::TestCluster::Attributes::ListInt8u::Id;
              path.mListOp      = ConcreteDataAttributePath::ListOperation::ReplaceAll;
              NL_TEST_ASSERT(gSuite, DataModel::Encode(writer, TLV::AnonymousTag(), value) == CHIP_NO_ERROR);
              break;
          }
  
          case ValidationInstruction::kListAttributeD_NotEmpty: {
              ChipLogProgress(DataManagement, "\t -- Generating D[2]");
  
              uint8_t listData[2];
              Clusters::TestCluster::Attributes::ListInt8u::TypeInfo::Type value(listData);
  
              uint8_t index2 = 0;
              for (auto & item : listData)
              {
                  item = index2;
                  index2++;
              }
  
              path.mAttributeId = Clusters::TestCluster::Attributes::ListInt8u::Id;
              path.mListOp      = ConcreteDataAttributePath::ListOperation::ReplaceAll;
  
              NL_TEST_ASSERT(gSuite, DataModel::Encode(writer, TLV::AnonymousTag(), value) == CHIP_NO_ERROR);
              break;
          }
  
          case ValidationInstruction::kListAttributeC_Error: {
              ChipLogProgress(DataManagement, "\t -- Generating C|e");
  
              path.mAttributeId = Clusters::TestCluster::Attributes::ListStructOctetString::Id;
              path.mListOp      = ConcreteDataAttributePath::ListOperation::ReplaceAll;
              status.mStatus    = Protocols::InteractionModel::Status::Failure;
              hasData           = false;
              callback->OnAttributeData(path, &reader, status);
              break;
          }
  
          case ValidationInstruction::kListAttributeD_Error: {
              ChipLogProgress(DataManagement, "\t -- Generating D|e");
  
              path.mAttributeId = Clusters::TestCluster::Attributes::ListInt8u::Id;
              path.mListOp      = ConcreteDataAttributePath::ListOperation::ReplaceAll;
              status.mStatus    = Protocols::InteractionModel::Status::Failure;
              hasData           = false;
              callback->OnAttributeData(path, &reader, status);
              break;
          }
  
          case ValidationInstruction::kListAttributeC_NotEmpty_Chunked: {
              hasData = false;
              Clusters::TestCluster::Attributes::ListStructOctetString::TypeInfo::Type value;
  
              {
                  ChipLogProgress(DataManagement, "\t -- Generating C[]");
  
                  path.mAttributeId = Clusters::TestCluster::Attributes::ListStructOctetString::Id;
                  path.mListOp      = ConcreteDataAttributePath::ListOperation::ReplaceAll;
                  NL_TEST_ASSERT(gSuite, DataModel::Encode(writer, TLV::AnonymousTag(), value) == CHIP_NO_ERROR);
  
                  writer.Finalize(&handle);
                  reader.Init(std::move(handle));
                  NL_TEST_ASSERT(gSuite, reader.Next() == CHIP_NO_ERROR);
                  callback->OnAttributeData(path, &reader, status);
              }
  
              ChipLogProgress(DataManagement, "\t -- Generating C0..C512");
  
              for (int i = 0; i < 512; i++)
              {
                  Clusters::TestCluster::Structs::TestListStructOctet::Type listItem;
  
                  handle = System::PacketBufferHandle::New(1000);
                  writer.Init(std::move(handle), true);
                  status = StatusIB();
  
                  path.mAttributeId = Clusters::TestCluster::Attributes::ListStructOctetString::Id;
                  path.mListOp      = ConcreteDataAttributePath::ListOperation::AppendItem;
  
                  listItem.fabricIndex = (uint64_t) i;
  
                  NL_TEST_ASSERT(gSuite, DataModel::Encode(writer, TLV::AnonymousTag(), listItem) == CHIP_NO_ERROR);
  
                  writer.Finalize(&handle);
                  reader.Init(std::move(handle));
                  NL_TEST_ASSERT(gSuite, reader.Next() == CHIP_NO_ERROR);
                  callback->OnAttributeData(path, &reader, status);
              }
  
              break;
          }
  
          case ValidationInstruction::kListAttributeD_NotEmpty_Chunked: {
              hasData = false;
              Clusters::TestCluster::Attributes::ListInt8u::TypeInfo::Type value;
  
              {
                  ChipLogProgress(DataManagement, "\t -- Generating D[]");
  
                  path.mAttributeId = Clusters::TestCluster::Attributes::ListInt8u::Id;
                  path.mListOp      = ConcreteDataAttributePath::ListOperation::ReplaceAll;
                  NL_TEST_ASSERT(gSuite, DataModel::Encode(writer, TLV::AnonymousTag(), value) == CHIP_NO_ERROR);
  
                  writer.Finalize(&handle);
                  reader.Init(std::move(handle));
                  NL_TEST_ASSERT(gSuite, reader.Next() == CHIP_NO_ERROR);
                  callback->OnAttributeData(path, &reader, status);
              }
  
              ChipLogProgress(DataManagement, "\t -- Generating D0..D512");
  
              for (int i = 0; i < 512; i++)
              {
                  handle = System::PacketBufferHandle::New(1000);
                  writer.Init(std::move(handle), true);
                  status = StatusIB();
  
                  path.mAttributeId = Clusters::TestCluster::Attributes::ListInt8u::Id;
                  path.mListOp      = ConcreteDataAttributePath::ListOperation::AppendItem;
  
                  NL_TEST_ASSERT(gSuite, DataModel::Encode(writer, TLV::AnonymousTag(), (uint8_t)(i)) == CHIP_NO_ERROR);
  
                  writer.Finalize(&handle);
                  reader.Init(std::move(handle));
                  NL_TEST_ASSERT(gSuite, reader.Next() == CHIP_NO_ERROR);
                  callback->OnAttributeData(path, &reader, status);
              }
  
              break;
          }
  
          default:
              break;
          }
  
          if (hasData)
          {
              writer.Finalize(&handle);
              reader.Init(std::move(handle));
              NL_TEST_ASSERT(gSuite, reader.Next() == CHIP_NO_ERROR);
              callback->OnAttributeData(path, &reader, status);
          }
  
          index++;
      }
  
      callback->OnReportEnd();
  }
  
  void RunAndValidateSequence(std::vector<ValidationInstruction> instructionList)
  {
      DataSeriesValidator validator(instructionList);
      BufferedReadCallback bufferedCallback(validator);
      DataSeriesGenerator generator(bufferedCallback, instructionList);
      generator.Generate();
  
      NL_TEST_ASSERT(gSuite, validator.mCurrentInstruction == instructionList.size());
  }
  
  void TestBufferedSequences(nlTestSuite * apSuite, void * apContext)
  {
      ChipLogProgress(DataManagement, "Validating various sequences of attribute data IBs...");
  
      ChipLogProgress(DataManagement, "A --> A");
      RunAndValidateSequence({ { ValidationInstruction::kSimpleAttributeA } });
  
      ChipLogProgress(DataManagement, "A A --> A A");
      RunAndValidateSequence({ { ValidationInstruction::kSimpleAttributeA }, { ValidationInstruction::kSimpleAttributeA } });
  
      ChipLogProgress(DataManagement, "A B --> A B");
      RunAndValidateSequence({ { ValidationInstruction::kSimpleAttributeA }, { ValidationInstruction::kSimpleAttributeB } });
  
      ChipLogProgress(DataManagement, "A C[] --> A C[]");
      RunAndValidateSequence({ { ValidationInstruction::kSimpleAttributeA }, { ValidationInstruction::kListAttributeC_Empty } });
  
      ChipLogProgress(DataManagement, "C[] C[] --> C[]");
      RunAndValidateSequence({ { ValidationInstruction::kListAttributeC_Empty, ValidationInstruction::kDiscardedChunk },
                               { ValidationInstruction::kListAttributeC_Empty } });
  
      ChipLogProgress(DataManagement, "C[2] C[] --> C[]");
      RunAndValidateSequence({ { ValidationInstruction::kListAttributeC_NotEmpty, ValidationInstruction::kDiscardedChunk },
                               { ValidationInstruction::kListAttributeC_Empty } });
  
      ChipLogProgress(DataManagement, "C[] C[2] --> C[2]");
      RunAndValidateSequence({ { ValidationInstruction::kListAttributeC_Empty, ValidationInstruction::kDiscardedChunk },
                               { ValidationInstruction::kListAttributeC_NotEmpty } });
  
      ChipLogProgress(DataManagement, "C[] A C[2] --> C[] A C[2]");
      RunAndValidateSequence({ { ValidationInstruction::kListAttributeC_Empty },
                               { ValidationInstruction::kSimpleAttributeA },
                               { ValidationInstruction::kListAttributeC_NotEmpty } });
  
      ChipLogProgress(DataManagement, "C[] C[2] A --> C[] C[2] A");
      RunAndValidateSequence({ { ValidationInstruction::kListAttributeC_Empty },
                               { ValidationInstruction::kSimpleAttributeA },
                               { ValidationInstruction::kListAttributeC_NotEmpty } });
  
      ChipLogProgress(DataManagement, "C[] D[] --> C[] D[]");
      RunAndValidateSequence({ { ValidationInstruction::kListAttributeC_Empty }, { ValidationInstruction::kListAttributeD_Empty } });
  
      ChipLogProgress(DataManagement, "C[2] D[] --> C[2] D[]");
      RunAndValidateSequence(
          { { ValidationInstruction::kListAttributeC_NotEmpty }, { ValidationInstruction::kListAttributeD_Empty } });
  
      ChipLogProgress(DataManagement, "C[2] C|e --> C|e");
      RunAndValidateSequence({ { ValidationInstruction::kListAttributeC_NotEmpty, ValidationInstruction::kDiscardedChunk },
                               { ValidationInstruction::kListAttributeC_Error } });
  
      ChipLogProgress(DataManagement, "A C|e --> A C|e");
      RunAndValidateSequence({ { ValidationInstruction::kSimpleAttributeA }, { ValidationInstruction::kListAttributeC_Error } });
  
      ChipLogProgress(DataManagement, "C|e C[2] --> C|e C[2]");
      RunAndValidateSequence(
          { { ValidationInstruction::kListAttributeC_Error }, { ValidationInstruction::kListAttributeC_NotEmpty } });
  
      ChipLogProgress(DataManagement, "C[] C0 C1 --> C[2]");
      RunAndValidateSequence({ { ValidationInstruction::kListAttributeC_NotEmpty_Chunked } });
  
      ChipLogProgress(DataManagement, "C[] C0 C1 C[] --> C[]");
      RunAndValidateSequence({
          { ValidationInstruction::kListAttributeC_NotEmpty_Chunked, ValidationInstruction::kDiscardedChunk },
          { ValidationInstruction::kListAttributeC_Empty },
      });
  
      ChipLogProgress(DataManagement, "C[] C0 C1 D[] D0 D1 --> C[2] D[2]");
      RunAndValidateSequence({
          { ValidationInstruction::kListAttributeC_NotEmpty_Chunked },
          { ValidationInstruction::kListAttributeD_NotEmpty_Chunked },
      });
  }
  
  // clang-format off
  const nlTest sTests[] =
  {
      NL_TEST_DEF("TestBufferedSequences", TestBufferedSequences),
      NL_TEST_SENTINEL()
  };
  
  nlTestSuite theSuite =
  {
      "TestBufferedReadCallback",
      &sTests[0],
      TestContext::Initialize,
      TestContext::Finalize
  };
  
  }
  // clang-format on
  
  int TestBufferedReadCallback()
  {
      TestContext gContext;
      gSuite = &theSuite;
      nlTestRunner(&theSuite, &gContext);
      return (nlTestRunnerStats(&theSuite));
  }
  
  CHIP_REGISTER_TEST_SUITE(TestBufferedReadCallback)
  