Files
openthread/tests/unit/test_msg_backed_array.cpp
T
Abtin Keshavarzian 62a2d794d4 [common] add MessageBackedArray class (#12341)
This commit adds `MessageBackedArray`, which implements a dynamic
array backed by a `Message` for data storage.

The new array class supports:
- Pushing new elements to the end.
- Reading and writing elements at specific indices.
- Searching in the array to find matching entries.
- Iterating over array elements.
- Clearing the array and freeing the underlying message.

Unit tests are added in `tests/unit/test_msg_backed_array.cpp` to
verify its functionality.
2026-02-09 11:36:53 -08:00

221 lines
7.3 KiB
C++

/*
* Copyright (c) 2026, 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 <stdarg.h>
#include <string.h>
#include "test_platform.h"
#include <openthread/config.h>
#include "common/clearable.hpp"
#include "common/equatable.hpp"
#include "common/msg_backed_array.hpp"
#include "instance/instance.hpp"
#include "test_util.hpp"
namespace ot {
struct Entry : public Clearable<Entry>, public Equatable<Entry>
{
static constexpr uint8_t kStringSize = 32;
Entry(void) { Clear(); }
Entry(bool aFlag, uint32_t aValue, const char *aString)
{
Clear();
mFlag = aFlag;
mValue = aValue;
SuccessOrQuit(StringCopy(mString, aString));
}
bool Matches(bool aFlag) const { return mFlag == aFlag; }
bool Matches(uint16_t aValue) const { return mValue == aValue; }
bool Matches(bool aFlag, uint16_t aValue) const { return Matches(aFlag) && Matches(aValue); }
bool Matches(const char *aString) const { return StringMatch(mString, aString); }
bool mFlag;
uint32_t mValue;
char mString[kStringSize];
};
void TestMsgBackedArray(void)
{
static constexpr uint16_t kMaxSize = 4;
using EntryArray = MessageBackedArray<Entry, kMaxSize>;
Instance *instance = testInitInstance();
EntryArray array(*instance);
EntryArray::IndexedEntry entry;
Entry entry0(false, 0x1234, "Entry A");
Entry entry1(false, 0x5678, "Second Entry");
Entry entry2(true, 0xfedc, "");
Entry entry3(true, 0x1234, "4");
Entry entry4(true, 0x9876, "Replace");
// Initial state when array is empty
VerifyOrQuit(array.GetLength() == 0);
VerifyOrQuit(array.ReadAt(0, entry) == kErrorNotFound);
VerifyOrQuit(array.WriteAt(0, entry0) == kErrorInvalidArgs);
VerifyOrQuit(array.FindMatching(entry, true) == kErrorNotFound);
entry.InitForIteration();
VerifyOrQuit(array.ReadNext(entry) == kErrorNotFound);
// Array with one entry
SuccessOrQuit(array.Push(entry0));
VerifyOrQuit(array.GetLength() == 1);
SuccessOrQuit(array.ReadAt(0, entry));
VerifyOrQuit(entry == entry0);
VerifyOrQuit(array.ReadAt(1, entry) == kErrorNotFound);
VerifyOrQuit(array.FindMatching(entry, true) == kErrorNotFound);
SuccessOrQuit(array.FindMatching(entry, false));
VerifyOrQuit(entry.GetIndex() == 0);
VerifyOrQuit(entry == entry0);
SuccessOrQuit(array.FindMatching(entry, "Entry A"));
VerifyOrQuit(entry.GetIndex() == 0);
VerifyOrQuit(entry == entry0);
entry.InitForIteration();
SuccessOrQuit(array.ReadNext(entry));
VerifyOrQuit(entry.GetIndex() == 0);
VerifyOrQuit(entry == entry0);
VerifyOrQuit(array.ReadNext(entry) == kErrorNotFound);
// Array with four entries.
SuccessOrQuit(array.Push(entry1));
SuccessOrQuit(array.Push(entry2));
SuccessOrQuit(array.Push(entry3));
VerifyOrQuit(array.GetLength() == 4);
SuccessOrQuit(array.ReadAt(0, entry));
VerifyOrQuit(entry == entry0);
SuccessOrQuit(array.ReadAt(1, entry));
VerifyOrQuit(entry == entry1);
SuccessOrQuit(array.ReadAt(2, entry));
VerifyOrQuit(entry == entry2);
SuccessOrQuit(array.ReadAt(3, entry));
VerifyOrQuit(entry == entry3);
VerifyOrQuit(array.ReadAt(4, entry) == kErrorNotFound);
SuccessOrQuit(array.FindMatching(entry, true));
VerifyOrQuit(entry.GetIndex() == 2);
VerifyOrQuit(entry == entry2);
SuccessOrQuit(array.FindMatching(entry, false));
VerifyOrQuit(entry.GetIndex() == 0);
VerifyOrQuit(entry == entry0);
SuccessOrQuit(array.FindMatching(entry, true, 0x1234));
VerifyOrQuit(entry.GetIndex() == 3);
VerifyOrQuit(entry == entry3);
entry.InitForIteration();
SuccessOrQuit(array.ReadNext(entry));
VerifyOrQuit(entry.GetIndex() == 0);
VerifyOrQuit(entry == entry0);
SuccessOrQuit(array.ReadNext(entry));
VerifyOrQuit(entry.GetIndex() == 1);
VerifyOrQuit(entry == entry1);
SuccessOrQuit(array.ReadNext(entry));
VerifyOrQuit(entry.GetIndex() == 2);
VerifyOrQuit(entry == entry2);
SuccessOrQuit(array.ReadNext(entry));
VerifyOrQuit(entry.GetIndex() == 3);
VerifyOrQuit(entry == entry3);
VerifyOrQuit(array.ReadNext(entry) == kErrorNotFound);
// Overwrite entry at index 1
SuccessOrQuit(array.WriteAt(1, entry4));
VerifyOrQuit(array.GetLength() == 4);
SuccessOrQuit(array.ReadAt(0, entry));
VerifyOrQuit(entry == entry0);
SuccessOrQuit(array.ReadAt(1, entry));
VerifyOrQuit(entry == entry4);
SuccessOrQuit(array.ReadAt(2, entry));
VerifyOrQuit(entry == entry2);
SuccessOrQuit(array.ReadAt(3, entry));
VerifyOrQuit(entry == entry3);
VerifyOrQuit(array.ReadAt(4, entry) == kErrorNotFound);
// Overwrite last entry
SuccessOrQuit(array.WriteAt(3, entry1));
VerifyOrQuit(array.GetLength() == 4);
SuccessOrQuit(array.ReadAt(0, entry));
VerifyOrQuit(entry == entry0);
SuccessOrQuit(array.ReadAt(1, entry));
VerifyOrQuit(entry == entry4);
SuccessOrQuit(array.ReadAt(2, entry));
VerifyOrQuit(entry == entry2);
SuccessOrQuit(array.ReadAt(3, entry));
VerifyOrQuit(entry == entry1);
VerifyOrQuit(array.ReadAt(4, entry) == kErrorNotFound);
// Overwrite out of bound
VerifyOrQuit(array.WriteAt(4, entry4) == kErrorInvalidArgs);
// Array at its max size
VerifyOrQuit(array.Push(entry4) == kErrorNoBufs);
// Clearing array
array.Clear();
VerifyOrQuit(array.GetLength() == 0);
VerifyOrQuit(array.ReadAt(0, entry) == kErrorNotFound);
VerifyOrQuit(array.WriteAt(0, entry0) == kErrorInvalidArgs);
testFreeInstance(instance);
}
} // namespace ot
int main(void)
{
ot::TestMsgBackedArray();
printf("All tests passed\n");
return 0;
}