mirror of
https://github.com/espressif/openthread.git
synced 2026-07-09 05:40:27 +00:00
7ec658e47a
This commit adds two inline template functions to get the length of an array and a pointer to the end of the array. These functions replace similar macros that were used with OT core modules. The template functions are safer to use since they would perform type checking at compile time. The functions validate that the input variable is indeed an array and unlike the macros they will not work with a pointer variable.
404 lines
14 KiB
C++
404 lines
14 KiB
C++
/*
|
|
* Copyright (c) 2018, The OpenThread Authors.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "test_platform.h"
|
|
|
|
#include <openthread/config.h>
|
|
|
|
#include "test_util.h"
|
|
#include "common/array.hpp"
|
|
#include "common/code_utils.hpp"
|
|
#include "common/instance.hpp"
|
|
#include "thread/child_table.hpp"
|
|
|
|
namespace ot {
|
|
|
|
static ot::Instance *sInstance;
|
|
|
|
enum
|
|
{
|
|
kMaxChildren = OPENTHREAD_CONFIG_MLE_MAX_CHILDREN,
|
|
};
|
|
|
|
struct TestChild
|
|
{
|
|
Child::State mState;
|
|
uint16_t mRloc16;
|
|
otExtAddress mExtAddress;
|
|
};
|
|
|
|
const Child::StateFilter kAllFilters[] = {
|
|
Child::kInStateValid,
|
|
Child::kInStateValidOrRestoring,
|
|
Child::kInStateChildIdRequest,
|
|
Child::kInStateValidOrAttaching,
|
|
Child::kInStateAnyExceptInvalid,
|
|
};
|
|
|
|
// Checks whether a `Child` matches the `TestChild` struct.
|
|
static bool ChildMatches(const Child &aChild, const TestChild &aTestChild)
|
|
{
|
|
return (aChild.GetState() == aTestChild.mState) && (aChild.GetRloc16() == aTestChild.mRloc16) &&
|
|
(aChild.GetExtAddress() == static_cast<const Mac::ExtAddress &>(aTestChild.mExtAddress));
|
|
}
|
|
|
|
// Checks whether a `Child::State` matches a `Child::StateFilter`.
|
|
static bool StateMatchesFilter(Child::State aState, Child::StateFilter aFilter)
|
|
{
|
|
bool rval = false;
|
|
Child child;
|
|
|
|
child.SetState(aState);
|
|
|
|
switch (aFilter)
|
|
{
|
|
case Child::kInStateAnyExceptInvalid:
|
|
rval = (aState != Child::kStateInvalid);
|
|
break;
|
|
|
|
case Child::kInStateValid:
|
|
rval = (aState == Child::kStateValid);
|
|
break;
|
|
|
|
case Child::kInStateValidOrRestoring:
|
|
rval = child.IsStateValidOrRestoring();
|
|
break;
|
|
|
|
case Child::kInStateChildIdRequest:
|
|
rval = (aState == Child::kStateChildIdRequest);
|
|
break;
|
|
|
|
case Child::kInStateValidOrAttaching:
|
|
rval = child.IsStateValidOrAttaching();
|
|
break;
|
|
|
|
case Child::kInStateInvalid:
|
|
rval = child.IsStateInvalid();
|
|
break;
|
|
|
|
case Child::kInStateAnyExceptValidOrRestoring:
|
|
rval = !child.IsStateValidOrRestoring();
|
|
break;
|
|
|
|
case Child::kInStateAny:
|
|
rval = true;
|
|
break;
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
// Verifies that `ChildTable` contains a given list of `TestChild` entries.
|
|
void VerifyChildTableContent(ChildTable &aTable, uint16_t aChildListLength, const TestChild *aChildList)
|
|
{
|
|
printf("Test ChildTable with %d entries", aChildListLength);
|
|
|
|
for (Child::StateFilter filter : kAllFilters)
|
|
{
|
|
// Verify that we can find all children from given list by rloc or extended address.
|
|
|
|
for (uint16_t listIndex = 0; listIndex < aChildListLength; listIndex++)
|
|
{
|
|
Child * child;
|
|
Mac::Address address;
|
|
|
|
if (!StateMatchesFilter(aChildList[listIndex].mState, filter))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
child = aTable.FindChild(aChildList[listIndex].mRloc16, filter);
|
|
VerifyOrQuit(child != nullptr, "FindChild(rloc) failed");
|
|
VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(rloc) returned incorrect child");
|
|
|
|
child = aTable.FindChild(static_cast<const Mac::ExtAddress &>(aChildList[listIndex].mExtAddress), filter);
|
|
VerifyOrQuit(child != nullptr, "FindChild(ExtAddress) failed");
|
|
VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(ExtAddress) returned incorrect child");
|
|
|
|
address.SetShort(aChildList[listIndex].mRloc16);
|
|
child = aTable.FindChild(address, filter);
|
|
VerifyOrQuit(child != nullptr, "FindChild(address) failed");
|
|
VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(address) returned incorrect child");
|
|
|
|
address.SetExtended(static_cast<const Mac::ExtAddress &>(aChildList[listIndex].mExtAddress));
|
|
child = aTable.FindChild(address, filter);
|
|
VerifyOrQuit(child != nullptr, "FindChild(address) failed");
|
|
VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(address) returned incorrect child");
|
|
}
|
|
|
|
// Verify `ChildTable::Iterator` behavior.
|
|
|
|
{
|
|
ChildTable::Iterator iter(*sInstance, filter);
|
|
bool childObserved[kMaxChildren];
|
|
uint16_t numChildren = 0;
|
|
|
|
memset(childObserved, 0, sizeof(childObserved));
|
|
|
|
// Use the iterator and verify that each returned `Child` entry is in the expected list.
|
|
|
|
for (; !iter.IsDone(); iter++)
|
|
{
|
|
Child * child = iter.GetChild();
|
|
Child & childRef = *iter;
|
|
bool didFind = false;
|
|
uint16_t childIndex;
|
|
|
|
VerifyOrQuit(child != nullptr, "iter.GetChild() failed");
|
|
VerifyOrQuit(&childRef == child, "iter.operator*() failed");
|
|
VerifyOrQuit(iter->GetRloc16() == child->GetRloc16(), "iter.operator->() failed");
|
|
|
|
childIndex = aTable.GetChildIndex(*child);
|
|
VerifyOrQuit(childIndex < aTable.GetMaxChildrenAllowed(), "Child Index is out of bound");
|
|
VerifyOrQuit(aTable.GetChildAtIndex(childIndex) == child, "GetChildAtIndex() failed");
|
|
|
|
for (uint16_t index = 0; index < aChildListLength; index++)
|
|
{
|
|
if (ChildMatches(*iter.GetChild(), aChildList[index]))
|
|
{
|
|
childObserved[index] = true;
|
|
numChildren++;
|
|
didFind = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(didFind, "ChildTable::Iterator returned an entry not in the expected list");
|
|
}
|
|
|
|
// Verify that when iterator is done, it points to `nullptr`.
|
|
|
|
VerifyOrQuit(iter.GetChild() == nullptr);
|
|
|
|
iter++;
|
|
VerifyOrQuit(iter.IsDone(), "iterator Advance() (after iterator is done) failed");
|
|
VerifyOrQuit(iter.GetChild() == nullptr);
|
|
|
|
// Verify that the number of children matches the number of entries we get from iterator.
|
|
|
|
VerifyOrQuit(aTable.GetNumChildren(filter) == numChildren);
|
|
VerifyOrQuit(aTable.HasChildren(filter) == (numChildren != 0));
|
|
|
|
// Verify that there is no missing or extra entry between the expected list
|
|
// and what was observed/returned by the iterator.
|
|
|
|
for (uint16_t index = 0; index < aChildListLength; index++)
|
|
{
|
|
if (StateMatchesFilter(aChildList[index].mState, filter))
|
|
{
|
|
VerifyOrQuit(childObserved[index], "iterator failed to return an expected entry");
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(!childObserved[index], "iterator returned an extra unexpected entry");
|
|
}
|
|
}
|
|
|
|
// Verify the behavior of range-based `for` iteration.
|
|
|
|
iter.Reset();
|
|
|
|
for (Child &child : aTable.Iterate(filter))
|
|
{
|
|
VerifyOrQuit(&child == iter.GetChild(), "range-based for loop Iterate() failed");
|
|
iter++;
|
|
}
|
|
|
|
VerifyOrQuit(iter.IsDone(), "range-based for loop Iterate() did not return all entries");
|
|
}
|
|
}
|
|
|
|
printf(" -- PASS\n");
|
|
}
|
|
|
|
void TestChildTable(void)
|
|
{
|
|
const TestChild testChildList[] = {
|
|
{
|
|
Child::kStateValid,
|
|
0x8001,
|
|
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x16}},
|
|
},
|
|
{
|
|
Child::kStateParentRequest,
|
|
0x8002,
|
|
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x17}},
|
|
},
|
|
{
|
|
Child::kStateValid,
|
|
0x8003,
|
|
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x18}},
|
|
},
|
|
{
|
|
Child::kStateValid,
|
|
0x8004,
|
|
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x19}},
|
|
},
|
|
{
|
|
Child::kStateRestored,
|
|
0x8005,
|
|
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x20}},
|
|
},
|
|
{
|
|
Child::kStateValid,
|
|
0x8006,
|
|
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x21}},
|
|
},
|
|
{
|
|
Child::kStateChildIdRequest,
|
|
0x8007,
|
|
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x22}},
|
|
},
|
|
{
|
|
Child::kStateChildUpdateRequest,
|
|
0x8008,
|
|
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x23}},
|
|
},
|
|
{
|
|
Child::kStateParentResponse,
|
|
0x8009,
|
|
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x24}},
|
|
},
|
|
{
|
|
Child::kStateRestored,
|
|
0x800a,
|
|
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x25}},
|
|
},
|
|
};
|
|
|
|
const uint16_t testListLength = GetArrayLength(testChildList);
|
|
|
|
uint16_t testNumAllowedChildren = 2;
|
|
|
|
ChildTable *table;
|
|
Error error;
|
|
|
|
sInstance = testInitInstance();
|
|
VerifyOrQuit(sInstance != nullptr);
|
|
|
|
table = &sInstance->Get<ChildTable>();
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
printf("Checking initial state after child table is constructed");
|
|
|
|
VerifyOrQuit(table->GetMaxChildrenAllowed() == table->GetMaxChildren(),
|
|
"GetMaxChildrenAllowed() initial value is incorrect ");
|
|
|
|
for (Child::StateFilter filter : kAllFilters)
|
|
{
|
|
VerifyOrQuit(table->HasChildren(filter) == false);
|
|
VerifyOrQuit(table->GetNumChildren(filter) == 0);
|
|
}
|
|
|
|
printf(" -- PASS\n");
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
VerifyChildTableContent(*table, 0, testChildList);
|
|
|
|
VerifyOrQuit(table->GetMaxChildrenAllowed() >= testListLength,
|
|
"Default child table size is too small for the unit test");
|
|
|
|
// Add the child entries from test list one by one and verify the table content
|
|
for (uint16_t i = 0; i < testListLength; i++)
|
|
{
|
|
Child *child;
|
|
|
|
child = table->GetNewChild();
|
|
VerifyOrQuit(child != nullptr, "GetNewChild() failed");
|
|
|
|
child->SetState(testChildList[i].mState);
|
|
child->SetRloc16(testChildList[i].mRloc16);
|
|
child->SetExtAddress((static_cast<const Mac::ExtAddress &>(testChildList[i].mExtAddress)));
|
|
|
|
VerifyChildTableContent(*table, i + 1, testChildList);
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Verify Child Table clear
|
|
|
|
table->Clear();
|
|
|
|
VerifyChildTableContent(*table, 0, testChildList);
|
|
|
|
// Add the child entries from test list in reverse order and verify the table content
|
|
for (uint16_t i = testListLength; i > 0; i--)
|
|
{
|
|
Child *child;
|
|
|
|
child = table->GetNewChild();
|
|
VerifyOrQuit(child != nullptr, "GetNewChild() failed");
|
|
|
|
child->SetState(testChildList[i - 1].mState);
|
|
child->SetRloc16(testChildList[i - 1].mRloc16);
|
|
child->SetExtAddress((static_cast<const Mac::ExtAddress &>(testChildList[i - 1].mExtAddress)));
|
|
|
|
VerifyChildTableContent(*table, testListLength - i + 1, &testChildList[i - 1]);
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
printf("Test Get/SetMaxChildrenAllowed");
|
|
|
|
error = table->SetMaxChildrenAllowed(kMaxChildren - 1);
|
|
VerifyOrQuit(error == kErrorInvalidState, "SetMaxChildrenAllowed() should fail when table is not empty");
|
|
|
|
table->Clear();
|
|
error = table->SetMaxChildrenAllowed(kMaxChildren + 1);
|
|
VerifyOrQuit(error == kErrorInvalidArgs, "SetMaxChildrenAllowed() did not fail with an invalid arg");
|
|
|
|
error = table->SetMaxChildrenAllowed(0);
|
|
VerifyOrQuit(error == kErrorInvalidArgs, "SetMaxChildrenAllowed() did not fail with an invalid arg");
|
|
|
|
error = table->SetMaxChildrenAllowed(testNumAllowedChildren);
|
|
VerifyOrQuit(error == kErrorNone, "SetMaxChildrenAllowed() failed");
|
|
VerifyOrQuit(table->GetMaxChildrenAllowed() == testNumAllowedChildren);
|
|
|
|
for (uint16_t num = 0; num < testNumAllowedChildren; num++)
|
|
{
|
|
Child *child = table->GetNewChild();
|
|
|
|
VerifyOrQuit(child != nullptr, "GetNewChild() failed");
|
|
child->SetState(Child::kStateValid);
|
|
}
|
|
|
|
VerifyOrQuit(table->GetNewChild() == nullptr, "GetNewChild() did not fail when table was full");
|
|
|
|
printf(" -- PASS\n");
|
|
|
|
testFreeInstance(sInstance);
|
|
}
|
|
|
|
} // namespace ot
|
|
|
|
int main(void)
|
|
{
|
|
ot::TestChildTable();
|
|
printf("\nAll tests passed.\n");
|
|
return 0;
|
|
}
|