blob: e9983368d57d65367a0d5242d98f0ec847717eb3 [file] [log] [blame]
/*
*
* 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 "lib/support/CHIPMem.h"
#include <app-common/zap-generated/cluster-objects.h>
#include <app/clusters/power-source-server/power-source-server.h>
#include <app/util/af.h>
#include <lib/core/ErrorStr.h>
#include <lib/core/TLV.h>
#include <lib/core/TLVDebug.h>
#include <lib/core/TLVUtilities.h>
#include <lib/support/CHIPCounter.h>
#include <lib/support/UnitTestContext.h>
#include <lib/support/UnitTestRegistration.h>
#include <messaging/ExchangeContext.h>
#include <messaging/Flags.h>
#include <nlunit-test.h>
#include <protocols/interaction_model/Constants.h>
#include <type_traits>
#include <vector>
namespace {
chip::EndpointId numEndpoints = 0;
}
extern uint16_t emberAfGetClusterServerEndpointIndex(chip::EndpointId endpoint, chip::ClusterId cluster,
uint16_t fixedClusterServerEndpointCount)
{
// Very simple mapping here, we're just going to return the endpoint that matches the given endpoint index because the test
// uses the endpoints in order.
if (endpoint >= numEndpoints)
{
return kEmberInvalidEndpointIndex;
}
return endpoint;
}
namespace chip {
namespace app {
class TestPowerSourceCluster
{
public:
static void TestEndpointList(nlTestSuite * apSuite, void * apContext);
};
std::vector<EndpointId> ReadEndpointsThroughAttributeReader(nlTestSuite * apSuite, EndpointId endpoint)
{
Clusters::PowerSourceAttrAccess & attrAccess = Clusters::TestOnlyGetPowerSourceAttrAccess();
CHIP_ERROR err = CHIP_NO_ERROR;
// Write TLV through the attribute access interface into the buffer
// Buffer setup
constexpr size_t buflen = 128;
uint8_t buf[buflen];
memset(buf, 0, buflen);
// Create the builders
TLV::TLVWriter tlvWriter;
tlvWriter.Init(buf);
AttributeReportIBs::Builder builder;
builder.Init(&tlvWriter);
ConcreteAttributePath path(endpoint, Clusters::PowerSource::Id, Clusters::PowerSource::Attributes::EndpointList::Id);
ConcreteReadAttributePath readPath(path);
chip::DataVersion dataVersion(0);
AttributeValueEncoder aEncoder(builder, 0, path, dataVersion);
err = attrAccess.Read(readPath, aEncoder);
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
// Read out from the buffer. This comes back as a nested struct
// AttributeReportIBs is a list of
// AttributeReportIB structs containing
// AttributeDataIB struct, which holds DataVersion (tag 0), AttributePathIB (tag 1) and Data (tag 2)
TLV::TLVReader reader;
reader.Init(buf);
TLV::TLVReader attrReportsReader;
TLV::TLVReader attrReportReader;
TLV::TLVReader attrDataReader;
reader.Next();
reader.OpenContainer(attrReportsReader);
attrReportsReader.Next();
attrReportsReader.OpenContainer(attrReportReader);
attrReportReader.Next();
attrReportReader.OpenContainer(attrDataReader);
// We're now in the attribute data IB, skip to the desired tag, we want TagNum = 2
attrDataReader.Next();
for (int i = 0; i < 3 && !(IsContextTag(attrDataReader.GetTag()) && TagNumFromTag(attrDataReader.GetTag()) == 2); ++i)
{
attrDataReader.Next();
}
NL_TEST_ASSERT(apSuite, IsContextTag(attrDataReader.GetTag()));
NL_TEST_ASSERT(apSuite, TagNumFromTag(attrDataReader.GetTag()) == 2);
// OK, we should be in the right spot now, let's decode the list.
Clusters::PowerSource::Attributes::EndpointList::TypeInfo::DecodableType list;
err = list.Decode(attrDataReader);
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
std::vector<EndpointId> ret;
auto iter = list.begin();
while (iter.Next())
{
ret.push_back(iter.GetValue());
}
return ret;
}
void TestPowerSourceCluster::TestEndpointList(nlTestSuite * apSuite, void * apContext)
{
CHIP_ERROR err = CHIP_NO_ERROR;
Clusters::PowerSourceServer & powerSourceServer = Clusters::PowerSourceServer::Instance();
// test that when we read everything we get an empty list as nothing has been set up yet
for (EndpointId i = 0; i < 11; ++i)
{
std::vector<EndpointId> vec = ReadEndpointsThroughAttributeReader(apSuite, i);
NL_TEST_ASSERT(apSuite, vec.size() == 0);
}
if (powerSourceServer.GetNumSupportedEndpointLists() < 2 ||
powerSourceServer.GetNumSupportedEndpointLists() > std::numeric_limits<uint16_t>::max())
{
// Test assumes at least two endpoints. This runs on linux, not worthwhile to run on platforms with fewer endpoints.
return;
}
// *****************
// Test setting, getting and reading through the attribute access interface
// *****************
EndpointId list0[5] = { 1, 2, 3, 4, 5 };
EndpointId list1[10] = { 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
EndpointId listRest[1] = { 2 };
// we checked earlier that this fit
// This test just uses endpoints in order, so we want to set endpoints from
// 0 to numEndpoints - 1, and use this for overflow checking
numEndpoints = static_cast<EndpointId>(powerSourceServer.GetNumSupportedEndpointLists());
// Endpoint 0 - list of 5
err = powerSourceServer.SetEndpointList(0, Span<EndpointId>(list0));
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
const Span<EndpointId> * readBack = powerSourceServer.GetEndpointList(0);
NL_TEST_EXIT_ON_FAILED_ASSERT(apSuite, readBack != nullptr);
NL_TEST_ASSERT(apSuite, readBack->size() == 5);
for (size_t i = 0; i < readBack->size(); ++i)
{
NL_TEST_ASSERT(apSuite, readBack->data()[i] == list0[i]);
}
// Endpoint 1 - list of 10
err = powerSourceServer.SetEndpointList(1, Span<EndpointId>(list1));
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
readBack = powerSourceServer.GetEndpointList(1);
NL_TEST_EXIT_ON_FAILED_ASSERT(apSuite, readBack != nullptr);
NL_TEST_ASSERT(apSuite, readBack->size() == 10);
for (size_t i = 0; i < readBack->size(); ++i)
{
NL_TEST_ASSERT(apSuite, readBack->data()[i] == list1[i]);
}
// Remaining endpoints - list of 1
for (EndpointId ep = 2; ep < numEndpoints; ++ep)
{
err = powerSourceServer.SetEndpointList(ep, Span<EndpointId>(listRest));
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
readBack = powerSourceServer.GetEndpointList(ep);
NL_TEST_EXIT_ON_FAILED_ASSERT(apSuite, readBack != nullptr);
NL_TEST_ASSERT(apSuite, readBack->size() == 1);
if (readBack->size() == 1)
{
NL_TEST_ASSERT(apSuite, readBack->data()[0] == listRest[0]);
}
}
// *****************
// Check for out of memory error when setting too many endpoints
// *****************
// pick a random endpoint number for the power cluster - it doesn't matter, we don't have space anyway.
err = powerSourceServer.SetEndpointList(55, Span<EndpointId>(listRest));
NL_TEST_ASSERT(apSuite, err == CHIP_ERROR_NO_MEMORY);
// *****************
// Recheck getting and reading after OOM
// *****************
// EP0
readBack = powerSourceServer.GetEndpointList(0);
NL_TEST_EXIT_ON_FAILED_ASSERT(apSuite, readBack != nullptr);
NL_TEST_ASSERT(apSuite, readBack->size() == 5);
for (size_t i = 0; i < readBack->size(); ++i)
{
NL_TEST_ASSERT(apSuite, readBack->data()[i] == list0[i]);
}
// EP1
readBack = powerSourceServer.GetEndpointList(1);
NL_TEST_EXIT_ON_FAILED_ASSERT(apSuite, readBack != nullptr);
NL_TEST_ASSERT(apSuite, readBack->size() == 10);
for (size_t i = 0; i < readBack->size(); ++i)
{
NL_TEST_ASSERT(apSuite, readBack->data()[i] == list1[i]);
}
// Remaining endpoints
for (EndpointId ep = 2; ep < numEndpoints; ++ep)
{
readBack = powerSourceServer.GetEndpointList(ep);
NL_TEST_EXIT_ON_FAILED_ASSERT(apSuite, readBack != nullptr);
NL_TEST_ASSERT(apSuite, readBack->size() == 1);
if (readBack->size() == 1)
{
NL_TEST_ASSERT(apSuite, readBack->data()[0] == listRest[0]);
}
}
// *****************
// Test overwriting
// *****************
// Overwrite a list
err = powerSourceServer.SetEndpointList(1, Span<EndpointId>(listRest));
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
readBack = powerSourceServer.GetEndpointList(1);
NL_TEST_ASSERT(apSuite, readBack->size() == 1);
if (readBack->size() == 1)
{
NL_TEST_ASSERT(apSuite, readBack->data()[0] == listRest[0]);
}
// Ensure only the overwritten list was changed, using read interface
for (EndpointId ep = 0; ep < numEndpoints + 1; ++ep)
{
std::vector<EndpointId> vec = ReadEndpointsThroughAttributeReader(apSuite, ep);
if (ep == 0)
{
NL_TEST_ASSERT(apSuite, vec.size() == 5);
for (size_t j = 0; j < vec.size(); ++j)
{
NL_TEST_ASSERT(apSuite, vec[j] == list0[j]);
}
}
else if (ep == numEndpoints)
{
NL_TEST_ASSERT(apSuite, vec.size() == 0);
}
else
{
NL_TEST_ASSERT(apSuite, vec.size() == 1);
if (vec.size() == 1)
{
NL_TEST_ASSERT(apSuite, vec[0] == listRest[0]);
}
}
}
// *****************
// Test removal
// *****************
for (EndpointId ep = 0; ep < numEndpoints; ++ep)
{
err = powerSourceServer.SetEndpointList(ep, Span<EndpointId>());
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
readBack = powerSourceServer.GetEndpointList(ep);
NL_TEST_ASSERT(apSuite, readBack == nullptr);
}
// Check through the read interface
for (EndpointId ep = 0; ep < numEndpoints + 1; ++ep)
{
std::vector<EndpointId> vec = ReadEndpointsThroughAttributeReader(apSuite, ep);
NL_TEST_ASSERT(apSuite, vec.size() == 0);
}
}
} // namespace app
} // namespace chip
namespace {
/**
* Test Suite. It lists all the test functions.
*/
// clang-format off
const nlTest sTests[] =
{
NL_TEST_DEF("TestEndpointList", chip::app::TestPowerSourceCluster::TestEndpointList),
NL_TEST_SENTINEL()
};
// clang-format on
/**
* Set up the test suite.
*/
int TestPowerSourceClusterContext_Setup(void * inContext)
{
CHIP_ERROR error = chip::Platform::MemoryInit();
if (error != CHIP_NO_ERROR)
return FAILURE;
return SUCCESS;
}
/**
* Tear down the test suite.
*/
int TestPowerSourceClusterContext_Teardown(void * inContext)
{
chip::app::Clusters::PowerSourceServer::Instance().Shutdown();
chip::Platform::MemoryShutdown();
return SUCCESS;
}
// clang-format off
nlTestSuite sSuite =
{
"TestPowerSourceCluster",
&sTests[0],
TestPowerSourceClusterContext_Setup,
TestPowerSourceClusterContext_Teardown
};
// clang-format on
} // namespace
int TestPowerSource()
{
nlTestRunner(&sSuite, nullptr);
return nlTestRunnerStats(&sSuite);
}
CHIP_REGISTER_TEST_SUITE(TestPowerSource)