Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a way to encapsulate a StatusIB in a CHIP_ERROR. #13475

Merged
merged 1 commit into from
Jan 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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