Skip to content

Commit

Permalink
Add a way to encapsulate a StatusIB in a CHIP_ERROR. (#13475)
Browse files Browse the repository at this point in the history
  • Loading branch information
bzbarsky-apple authored and pull[bot] committed May 20, 2022
1 parent ed6b831 commit 1087936
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 7 deletions.
41 changes: 41 additions & 0 deletions src/app/MessageDef/StatusIB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

using namespace chip;
using namespace chip::TLV;
using namespace chip::Protocols::InteractionModel;

namespace chip {
namespace app {
Expand Down Expand Up @@ -142,5 +143,45 @@ StatusIB::Builder & StatusIB::Builder::EncodeStatusIB(const StatusIB & aStatusIB
return *this;
}

CHIP_ERROR StatusIB::ToChipError() const
{
if (mStatus == Status::Success)
{
return CHIP_NO_ERROR;
}

if (mClusterStatus.HasValue())
{
return ChipError(ChipError::SdkPart::kIMClusterStatus, mClusterStatus.Value());
}

return ChipError(ChipError::SdkPart::kIMGlobalStatus, to_underlying(mStatus));
}

void StatusIB::InitFromChipError(CHIP_ERROR aError)
{
if (aError.IsPart(ChipError::SdkPart::kIMClusterStatus))
{
mStatus = Status::Failure;
mClusterStatus = MakeOptional(aError.GetSdkCode());
return;
}

mClusterStatus = NullOptional;
if (aError == CHIP_NO_ERROR)
{
mStatus = Status::Success;
return;
}

if (aError.IsPart(ChipError::SdkPart::kIMGlobalStatus))
{
mStatus = static_cast<Status>(aError.GetSdkCode());
return;
}

mStatus = Status::Failure;
}

}; // namespace app
}; // namespace chip
15 changes: 15 additions & 0 deletions src/app/MessageDef/StatusIB.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,21 @@ struct StatusIB
StatusIB::Builder & EncodeStatusIB(const StatusIB & aStatusIB);
};

/**
* Encapsulate a StatusIB in a CHIP_ERROR. This can be done for any
* StatusIB, but will treat all success codes (including cluster-specific
* ones) as CHIP_NO_ERROR. The resulting CHIP_ERROR will either be
* CHIP_NO_ERROR or test true for IsIMStatus().
*/
CHIP_ERROR ToChipError() const;

/**
* Extract a CHIP_ERROR into this StatusIB. If IsIMStatus is false for the
* error, this might do a best-effort attempt to come up with a
* corresponding StatusIB, defaulting to a generic Status::Failure.
*/
void InitFromChipError(CHIP_ERROR aError);

Protocols::InteractionModel::Status mStatus = Protocols::InteractionModel::Status::Success;
Optional<ClusterStatus> mClusterStatus = Optional<ClusterStatus>::Missing();

Expand Down
1 change: 1 addition & 0 deletions src/app/tests/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ chip_test_suite("tests") {
"TestNumericAttributeTraits.cpp",
"TestReadInteraction.cpp",
"TestReportingEngine.cpp",
"TestStatusIB.cpp",
"TestStatusResponseMessage.cpp",
"TestTimedHandler.cpp",
"TestWriteInteraction.cpp",
Expand Down
137 changes: 137 additions & 0 deletions src/app/tests/TestStatusIB.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* Copyright (c) 2022 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/AppBuildConfig.h>
#include <app/MessageDef/StatusIB.h>
#include <lib/core/CHIPError.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/UnitTestRegistration.h>

#include <nlunit-test.h>

namespace {

using namespace chip;
using namespace chip::app;
using namespace chip::Protocols::InteractionModel;

// Macro so failures will blame the right line.
#define VERIFY_ROUNDTRIP(err, status) \
do \
{ \
StatusIB newStatus; \
newStatus.InitFromChipError(err); \
NL_TEST_ASSERT(aSuite, newStatus.mStatus == status.mStatus); \
NL_TEST_ASSERT(aSuite, newStatus.mClusterStatus == status.mClusterStatus); \
} while (0);

void TestStatusIBToFromChipError(nlTestSuite * aSuite, void * aContext)
{
StatusIB status;

status.mStatus = Status::Success;
CHIP_ERROR err = status.ToChipError();
NL_TEST_ASSERT(aSuite, err == CHIP_NO_ERROR);
VERIFY_ROUNDTRIP(err, status);

status.mStatus = Status::Failure;
err = status.ToChipError();
NL_TEST_ASSERT(aSuite, err != CHIP_NO_ERROR);
VERIFY_ROUNDTRIP(err, status);

status.mStatus = Status::InvalidAction;
err = status.ToChipError();
NL_TEST_ASSERT(aSuite, err != CHIP_NO_ERROR);
VERIFY_ROUNDTRIP(err, status);

status.mClusterStatus = MakeOptional(static_cast<ClusterStatus>(5));

status.mStatus = Status::Success;
err = status.ToChipError();
NL_TEST_ASSERT(aSuite, err == CHIP_NO_ERROR);

status.mStatus = Status::Failure;
err = status.ToChipError();
NL_TEST_ASSERT(aSuite, err != CHIP_NO_ERROR);
VERIFY_ROUNDTRIP(err, status);

status.mStatus = Status::InvalidAction;
err = status.ToChipError();
NL_TEST_ASSERT(aSuite, err != CHIP_NO_ERROR);
{
StatusIB newStatus;
newStatus.InitFromChipError(err);
NL_TEST_ASSERT(aSuite, newStatus.mStatus == Status::Failure);
NL_TEST_ASSERT(aSuite, newStatus.mClusterStatus == status.mClusterStatus);
}

err = CHIP_ERROR_NO_MEMORY;
{
StatusIB newStatus;
newStatus.InitFromChipError(err);
NL_TEST_ASSERT(aSuite, newStatus.mStatus == Status::Failure);
NL_TEST_ASSERT(aSuite, !newStatus.mClusterStatus.HasValue());
}
}

// clang-format off
const nlTest sTests[] =
{
NL_TEST_DEF("StatusIBToFromChipError", TestStatusIBToFromChipError),
NL_TEST_SENTINEL()
};
// clang-format on
} // namespace

/**
* Set up the test suite.
*/
static int TestSetup(void * inContext)
{
CHIP_ERROR error = chip::Platform::MemoryInit();
if (error != CHIP_NO_ERROR)
return FAILURE;
return SUCCESS;
}

/**
* Tear down the test suite.
*/
static int TestTeardown(void * inContext)
{
chip::Platform::MemoryShutdown();
return SUCCESS;
}

int TestStatusIB()
{
// clang-format off
nlTestSuite theSuite =
{
"StatusIB",
&sTests[0],
TestSetup,
TestTeardown,
};
// clang-format on

nlTestRunner(&theSuite, nullptr);

return (nlTestRunnerStats(&theSuite));
}

CHIP_REGISTER_TEST_SUITE(TestStatusIB)
33 changes: 26 additions & 7 deletions src/lib/core/CHIPError.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,20 +91,22 @@ class ChipError
kLwIP = 0x3, ///< Encapsulated LwIP errors.
kOpenThread = 0x4, ///< Encapsulated OpenThread errors.
kPlatform = 0x5, ///< Platform-defined encapsulation.
kLastRange = kPlatform
kLastRange = kPlatform,
};

/**
* Secondary classification of CHIP SDK errors (Range::kSDK).
*/
enum class SdkPart : uint8_t
{
kCore = 0, ///< SDK core errors.
kInet = 1, ///< Inet layer errors; see <inet/InetError.h>.
kDevice = 2, ///< Device layer errors; see <platform/CHIPDeviceError.h>.
kASN1 = 3, ///< ASN1 errors; see <asn1/ASN1Error.h>.
kBLE = 4, ///< BLE layer errors; see <ble/BleError.h>.
kApplication = 7, ///< Application-defined errors; see CHIP_APPLICATION_ERROR
kCore = 0, ///< SDK core errors.
kInet = 1, ///< Inet layer errors; see <inet/InetError.h>.
kDevice = 2, ///< Device layer errors; see <platform/CHIPDeviceError.h>.
kASN1 = 3, ///< ASN1 errors; see <asn1/ASN1Error.h>.
kBLE = 4, ///< BLE layer errors; see <ble/BleError.h>.
kIMGlobalStatus = 5, ///< Interaction Model global status code.
kIMClusterStatus = 6, ///< Interaction Model cluster-specific status code.
kApplication = 7, ///< Application-defined errors; see CHIP_APPLICATION_ERROR
};

ChipError() = default;
Expand Down Expand Up @@ -263,6 +265,23 @@ class ChipError
MakeField(kSdkPartStart, static_cast<StorageType>(part)));
}

/**
* Get the SDK code for an SDK error.
*/
constexpr uint8_t GetSdkCode() const { return static_cast<uint8_t>(GetField(kSdkCodeStart, kSdkCodeLength, mError)); }

/**
* Test whether @a error is an SDK error representing an Interaction Model
* status. If it is, it can be converted to/from an interaction model
* StatusIB struct.
*/
constexpr bool IsIMStatus() const
{
// Open question: should CHIP_NO_ERROR be treated as an IM status for
// purposes of this test?
return IsPart(SdkPart::kIMGlobalStatus) || IsPart(SdkPart::kIMClusterStatus);
}

#if CHIP_CONFIG_ERROR_SOURCE

/**
Expand Down

0 comments on commit 1087936

Please sign in to comment.