blob: 6c856259516647e95e31e574179e65a9aae33f3e [file] [edit]
/*
*
* Copyright (c) 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.
*/
#include <pw_unit_test/framework.h>
#include <controller/CHIPDeviceController.h>
#include <controller/SetUpCodePairer.h>
#include <controller/tests/SetUpCodePairerTestAccess.h>
#include <inet/IPAddress.h>
#include <lib/core/CHIPError.h>
#include <transport/raw/PeerAddress.h>
#include <memory>
using namespace chip;
using namespace chip::Controller;
using PairerAccess = chip::Testing::SetUpCodePairerTestAccess;
namespace {
// DeviceCommissioner is too large to embed in a test fixture (it exceeds
// the pw_unit_test light backend's static memory pool). Heap-allocate it
// along with the SetUpCodePairer and test accessor that depend on it.
class TestSetUpCodePairer : public ::testing::Test
{
public:
static void SetUpTestSuite() { ASSERT_EQ(Platform::MemoryInit(), CHIP_NO_ERROR); }
static void TearDownTestSuite() { Platform::MemoryShutdown(); }
void SetUp() override
{
mCommissioner = std::make_unique<DeviceCommissioner>();
mPairer = std::make_unique<SetUpCodePairer>(mCommissioner.get());
mAccess = std::make_unique<PairerAccess>(mPairer.get());
}
void TearDown() override
{
mAccess.reset();
mPairer.reset();
mCommissioner.reset();
}
protected:
PairerAccess & Access() { return *mAccess; }
private:
std::unique_ptr<DeviceCommissioner> mCommissioner;
std::unique_ptr<SetUpCodePairer> mPairer;
std::unique_ptr<PairerAccess> mAccess;
};
// When the discovery timeout fires while a PASE attempt is in progress,
// DNS-SD should be stopped (it runs indefinitely) but other transports
// (BLE, Wi-Fi PAF, NFC) should be left alone since they self-terminate.
TEST_F(TestSetUpCodePairer, TimeoutDuringPASE_StopsDNSSD_PreservesOtherTransports)
{
Access().SetRemoteId(1);
Access().SetWaitingForPASE(true);
Access().SetWaitingForDiscovery(PairerAccess::kIPTransport, true);
Access().SetWaitingForDiscovery(PairerAccess::kBLETransport, true);
Access().FireTimeoutCallback();
// DNS-SD must be stopped to prevent DiscoveryInProgress() from being stuck true.
EXPECT_FALSE(Access().GetWaitingForDiscovery(PairerAccess::kIPTransport));
// BLE must be preserved — it may still discover a commissionee.
EXPECT_TRUE(Access().GetWaitingForDiscovery(PairerAccess::kBLETransport));
// PASE state must not be disturbed.
EXPECT_TRUE(Access().GetWaitingForPASE());
}
// When the discovery timeout fires with no PASE in progress,
// all transports should be stopped and failure reported.
TEST_F(TestSetUpCodePairer, TimeoutNoPASE_StopsAllTransports)
{
Access().SetRemoteId(1);
Access().SetWaitingForPASE(false);
Access().SetWaitingForDiscovery(PairerAccess::kIPTransport, true);
Access().SetWaitingForDiscovery(PairerAccess::kBLETransport, true);
Access().FireTimeoutCallback();
EXPECT_FALSE(Access().GetWaitingForDiscovery(PairerAccess::kIPTransport));
EXPECT_FALSE(Access().GetWaitingForDiscovery(PairerAccess::kBLETransport));
// StopPairingIfTransportsExhausted should have reported failure and cleared mRemoteId.
EXPECT_EQ(Access().GetRemoteId(), kUndefinedNodeId);
}
// When a PASE attempt fails (OnPairingComplete with error) and DNS-SD
// is still running, DNS-SD should NOT be stopped. Keeping DNS-SD alive
// keeps DiscoveryInProgress() true, which suppresses premature failure
// through OnStatusUpdate, TryNextRendezvousParameters, and
// StopPairingIfTransportsExhausted.
TEST_F(TestSetUpCodePairer, PASEFailure_DNSSDStaysAlive)
{
Access().SetRemoteId(1);
Access().SetWaitingForDiscovery(PairerAccess::kIPTransport, true);
Access().SetWaitingForDiscovery(PairerAccess::kBLETransport, true);
// Simulate a UDP PASE attempt: set mCurrentPASEParameters and enter
// the PASE-waiting state (as ConnectToDiscoveredDevice would).
SetUpCodePairerParameters udpParams;
udpParams.SetPeerAddress(Transport::PeerAddress::UDP(Inet::IPAddress::Any, 5540));
Access().SetCurrentPASEParameters(udpParams);
Access().ExpectPASEEstablishment();
// PASE fails.
Access().CallOnPairingComplete(CHIP_ERROR_TIMEOUT);
// DNS-SD must still be running — the 30s timeout is its natural upper bound.
EXPECT_TRUE(Access().GetWaitingForDiscovery(PairerAccess::kIPTransport));
// BLE must still be running.
EXPECT_TRUE(Access().GetWaitingForDiscovery(PairerAccess::kBLETransport));
// The pairer should still be active (not yet reported failure).
EXPECT_NE(Access().GetRemoteId(), kUndefinedNodeId);
// The error should be saved for later.
EXPECT_EQ(Access().GetLastPASEError(), CHIP_ERROR_TIMEOUT);
// mCurrentPASEParameters should have been cleared.
EXPECT_FALSE(Access().HasCurrentPASEParameters());
}
// When a PASE attempt fails synchronously in ConnectToDiscoveredDevice,
// mCurrentPASEParameters must be cleared so a later non-UDP PASE
// completion cannot incorrectly use stale parameters for ReconfirmRecord.
TEST_F(TestSetUpCodePairer, SyncPASEFailure_ClearsCurrentPASEParameters)
{
Access().SetRemoteId(1);
Access().SetWaitingForDiscovery(PairerAccess::kIPTransport, true);
// Set up stale UDP parameters (as if ConnectToDiscoveredDevice had set
// them before calling PairDevice, which then failed synchronously).
SetUpCodePairerParameters udpParams;
udpParams.SetPeerAddress(Transport::PeerAddress::UDP(Inet::IPAddress::Any, 5540));
Access().SetCurrentPASEParameters(udpParams);
// Now simulate a subsequent non-UDP PASE completion (e.g., BLE PASE
// succeeds or fails). ExpectPASEEstablishment + OnPairingComplete
// with success exercises ResetDiscoveryState which clears everything.
// Instead, verify directly that after a PASE failure the stale
// parameters don't persist: simulate a PASE completion with error.
Access().ExpectPASEEstablishment();
Access().CallOnPairingComplete(CHIP_ERROR_CONNECTION_ABORTED);
// mCurrentPASEParameters must be cleared — stale UDP params must not
// survive to confuse a later ReconfirmRecord check.
EXPECT_FALSE(Access().HasCurrentPASEParameters());
}
} // namespace