Increase unit test code coverage of `controller/` by 2.3% (#39941)
* Add unit tests for `BtpEngine` sequence number handling
- Test that the initial value of the last received sequence number is zero.
- Test that the initial value of the newest unacknowledged sent sequence number is zero.
- Test that sending a packet updates the unacknowledged sequence number correctly and that receiving an acknowledgment updates the state as expected.
* Restyle
* Check newest unacked sent sequence number
* Combine test cases for initial sequence numbers
* Restyle
* Add a friend accessor class
* Add an accessor class
* Add unit Test for
* Fix typos; Improve comments
* Fix typos; Restyle
* Restyle
* Remove s
* Add as source file
* Remove redundant `chip::`s
* Restyle
* Update AutoCommissionerTestAccess.h
* Add comments
diff --git a/src/controller/AutoCommissioner.h b/src/controller/AutoCommissioner.h
index 038be19..7f95123 100644
--- a/src/controller/AutoCommissioner.h
+++ b/src/controller/AutoCommissioner.h
@@ -26,12 +26,22 @@
#include <protocols/secure_channel/RendezvousParameters.h>
namespace chip {
+
+namespace Test {
+
+class AutoCommissionerTestAccess;
+
+} // namespace Test
+
namespace Controller {
class DeviceCommissioner;
class AutoCommissioner : public CommissioningDelegate
{
+
+ friend class chip::Test::AutoCommissionerTestAccess;
+
public:
AutoCommissioner();
~AutoCommissioner() override;
@@ -137,6 +147,7 @@
app::Clusters::TimeSynchronization::Structs::DSTOffsetStruct::Type mDstOffsetsBuf[kMaxSupportedDstStructs];
static constexpr size_t kMaxDefaultNtpSize = 128;
+
char mDefaultNtp[kMaxDefaultNtpSize];
uint8_t mICDSymmetricKey[Crypto::kAES_CCM128_Key_Length];
diff --git a/src/controller/tests/AutoCommissionerTestAccess.h b/src/controller/tests/AutoCommissionerTestAccess.h
new file mode 100644
index 0000000..193324f
--- /dev/null
+++ b/src/controller/tests/AutoCommissionerTestAccess.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * Copyright (c) 2020-2025 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.
+ */
+#pragma once
+
+#include <controller/AutoCommissioner.h>
+
+#include <lib/core/StringBuilderAdapters.h>
+#include <lib/support/CHIPMemString.h>
+
+#include <app/AttributePathParams.h>
+#include <controller/CommissioneeDeviceProxy.h>
+#include <controller/CommissioningDelegate.h>
+#include <credentials/DeviceAttestationConstructor.h>
+#include <crypto/CHIPCryptoPAL.h>
+#include <lib/support/ScopedBuffer.h>
+#include <protocols/secure_channel/RendezvousParameters.h>
+
+namespace chip {
+
+namespace Test {
+// Provides access to private/protected members of AutoCommissioner class for testing
+class AutoCommissionerTestAccess
+{
+public:
+ AutoCommissionerTestAccess() = delete;
+ AutoCommissionerTestAccess(Controller::AutoCommissioner * commissioner) : mCommissioner(commissioner) {}
+
+ Controller::CommissioningStage AccessGetNextCommissioningStageInternal(Controller::CommissioningStage currentStage,
+ CHIP_ERROR & lastErr)
+ {
+ return mCommissioner->GetNextCommissioningStageInternal(currentStage, lastErr);
+ }
+ void SetBreadcrumb(uint64_t value) { mCommissioner->mDeviceCommissioningInfo.general.breadcrumb = value; }
+ void SetUTCRequirements(bool requiresUTC) { mCommissioner->mDeviceCommissioningInfo.requiresUTC = requiresUTC; }
+
+private:
+ Controller::AutoCommissioner * mCommissioner = nullptr;
+};
+
+} // namespace Test
+} // namespace chip
diff --git a/src/controller/tests/BUILD.gn b/src/controller/tests/BUILD.gn
index 2c1f6dc..00a7203 100644
--- a/src/controller/tests/BUILD.gn
+++ b/src/controller/tests/BUILD.gn
@@ -62,6 +62,8 @@
cflags = [ "-Wconversion" ]
+ sources = [ "AutoCommissionerTestAccess.h" ]
+
public_deps = [
"${chip_root}/src/app/common:cluster-objects",
"${chip_root}/src/app/tests:helpers",
diff --git a/src/controller/tests/TestAutoCommissioner.cpp b/src/controller/tests/TestAutoCommissioner.cpp
index ab65e7d..dbc0fca 100644
--- a/src/controller/tests/TestAutoCommissioner.cpp
+++ b/src/controller/tests/TestAutoCommissioner.cpp
@@ -19,7 +19,8 @@
#include <pw_unit_test/framework.h>
#include <controller/AutoCommissioner.h>
-
+#include <controller/CommissioningDelegate.h>
+#include <controller/tests/AutoCommissionerTestAccess.h>
#include <lib/core/StringBuilderAdapters.h>
#include <lib/support/CHIPMemString.h>
@@ -29,6 +30,7 @@
using namespace chip;
using namespace chip::Dnssd;
using namespace chip::Controller;
+using namespace chip::Test;
namespace {
@@ -219,4 +221,122 @@
ASSERT_EQ(pathParams[0].mAttributeId, attributeId);
}
+// kStages are enumerators from enum type name CommissioningStage
+struct StageTransition
+{
+ CommissioningStage currentStage;
+ CommissioningStage nextStage;
+};
+
+const std::vector<StageTransition> kStagePairs = {
+ // Only linear transitions are tested here;
+ // Branching cases (like kReadCommissioningInfo, kConfigureTCAcknowledgments, etc.) are tested separately
+ { kSecurePairing, kReadCommissioningInfo },
+ { kArmFailsafe, kConfigRegulatory },
+ { kConfigRegulatory, kConfigureTCAcknowledgments },
+ { kConfigureDefaultNTP, kSendPAICertificateRequest },
+ { kSendPAICertificateRequest, kSendDACCertificateRequest },
+ { kSendDACCertificateRequest, kSendAttestationRequest },
+ { kSendAttestationRequest, kAttestationVerification },
+ { kAttestationVerification, kAttestationRevocationCheck },
+ { kJFValidateNOC, kSendVIDVerificationRequest },
+ { kSendVIDVerificationRequest, kSendOpCertSigningRequest },
+ { kSendOpCertSigningRequest, kValidateCSR },
+ { kValidateCSR, kGenerateNOCChain },
+ { kGenerateNOCChain, kSendTrustedRootCert },
+ { kSendTrustedRootCert, kSendNOC },
+ { kICDGetRegistrationInfo, kICDRegistration },
+ { kScanNetworks, kNeedsNetworkCreds },
+ { kWiFiNetworkSetup, kFailsafeBeforeWiFiEnable },
+ { kThreadNetworkSetup, kFailsafeBeforeThreadEnable },
+ { kFailsafeBeforeWiFiEnable, kWiFiNetworkEnable },
+ { kFailsafeBeforeThreadEnable, kThreadNetworkEnable },
+ { kEvictPreviousCaseSessions, kFindOperationalForStayActive },
+ { kFindOperationalForStayActive, kICDSendStayActive },
+ { kICDSendStayActive, kFindOperationalForCommissioningComplete },
+ { kFindOperationalForCommissioningComplete, kSendComplete },
+ { kSendComplete, kCleanup },
+ { kCleanup, kError },
+ { kError, kError },
+ { static_cast<CommissioningStage>(250), kError }, // triggers default case in switch statement
+
+};
+
+// Test each case pair for the next commissioning stage
+TEST_F(AutoCommissionerTest, NextCommissioningStage)
+{
+ // Accessor class used due to private/protected members.
+ AutoCommissionerTestAccess privateConfigCommissioner(&mCommissioner);
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ for (const auto & stagePair : kStagePairs)
+ {
+ CommissioningStage nextStage =
+ privateConfigCommissioner.AccessGetNextCommissioningStageInternal(stagePair.currentStage, err);
+ EXPECT_EQ(nextStage, stagePair.nextStage);
+ }
+}
+
+// if commissioning is manually stopped, the next stage should be kCleanup
+TEST_F(AutoCommissionerTest, NextStageStopCommissioning)
+{
+ AutoCommissionerTestAccess privateConfigCommissioner(&mCommissioner);
+ mCommissioner.StopCommissioning();
+
+ CHIP_ERROR err = CHIP_ERROR_INTERNAL;
+ CommissioningStage stage = privateConfigCommissioner.AccessGetNextCommissioningStageInternal(kSecurePairing, err);
+ EXPECT_EQ(stage, kCleanup);
+}
+
+// if commissioning failed, then the next stage should be cleanup
+TEST_F(AutoCommissionerTest, NextCommissioningStageAfterError)
+{
+ AutoCommissionerTestAccess privateConfigCommissioner(&mCommissioner);
+
+ CHIP_ERROR err = CHIP_ERROR_INTERNAL;
+ CommissioningStage stage = privateConfigCommissioner.AccessGetNextCommissioningStageInternal(kSecurePairing, err);
+ EXPECT_EQ(stage, kCleanup);
+}
+
+// Verifies that the commissioner proceeds to ConfigureTCAcknowledgments under the correct conditions.
+TEST_F(AutoCommissionerTest, NextStageReadCommissioningInfo)
+{
+ AutoCommissionerTestAccess privateConfigCommissioner(&mCommissioner);
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ privateConfigCommissioner.SetBreadcrumb(0);
+ CommissioningStage nextStage = privateConfigCommissioner.AccessGetNextCommissioningStageInternal(kReadCommissioningInfo, err);
+
+ EXPECT_EQ(nextStage, kArmFailsafe);
+
+ // if breadcrumb > 0, the stage changes to kSendNOC; subsequent stages progress accordingly.
+ privateConfigCommissioner.SetBreadcrumb(1);
+
+ CommissioningStage nextStageReadCommissioningInfo =
+ privateConfigCommissioner.AccessGetNextCommissioningStageInternal(kReadCommissioningInfo, err);
+ CommissioningStage nextStageSendNOC = privateConfigCommissioner.AccessGetNextCommissioningStageInternal(kSendNOC, err);
+
+ EXPECT_EQ(nextStageReadCommissioningInfo, nextStageSendNOC);
+}
+
+// Ensures TCAcknowledgment stage is triggered only under expected commissioning conditions.
+TEST_F(AutoCommissionerTest, NextStageConfigureTCAcknowledgments)
+{
+ AutoCommissionerTestAccess privateConfigCommissioner(&mCommissioner);
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ privateConfigCommissioner.SetUTCRequirements(true);
+
+ CommissioningStage nextStage =
+ privateConfigCommissioner.AccessGetNextCommissioningStageInternal(kConfigureTCAcknowledgments, err);
+
+ EXPECT_EQ(nextStage, kConfigureUTCTime);
+
+ privateConfigCommissioner.SetUTCRequirements(false);
+
+ nextStage = privateConfigCommissioner.AccessGetNextCommissioningStageInternal(kConfigureTCAcknowledgments, err);
+
+ EXPECT_EQ(nextStage, kSendPAICertificateRequest);
+}
+
} // namespace