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;
|
|
}
|