blob: 9a8104f86ce4d39f96b5cf0587060b5f7f6d64d5 [file] [log] [blame]
/*
*
* Copyright (c) 2024 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 <array>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
#include <lib/core/StringBuilderAdapters.h>
#include <pw_unit_test/framework.h>
#include <app/data-model/Nullable.h>
#include <lib/support/Span.h>
using namespace chip;
using namespace chip::app::DataModel;
namespace {
// Counts calls to constructor and destructor, to determine if the right
// semantics applied in cases where destruction is expected.
struct CtorDtorCounter
{
CtorDtorCounter(int i) : m(i) { ++created; }
~CtorDtorCounter() { ++destroyed; }
CtorDtorCounter(const CtorDtorCounter & o) : m(o.m) { ++created; }
CtorDtorCounter & operator=(const CtorDtorCounter &) = default;
CtorDtorCounter(CtorDtorCounter && o) : m(o.m) { ++created; }
CtorDtorCounter & operator=(CtorDtorCounter &&) = default;
bool operator==(const CtorDtorCounter & o) const { return m == o.m; }
bool operator!=(const CtorDtorCounter & o) const { return m != o.m; }
int m;
static void ResetCounter()
{
created = 0;
destroyed = 0;
}
static int created;
static int destroyed;
};
struct MovableCtorDtorCounter : public CtorDtorCounter
{
public:
MovableCtorDtorCounter(int i) : CtorDtorCounter(i) {}
MovableCtorDtorCounter(const MovableCtorDtorCounter & o) = delete;
MovableCtorDtorCounter & operator=(const MovableCtorDtorCounter &) = delete;
MovableCtorDtorCounter(MovableCtorDtorCounter && o) = default;
MovableCtorDtorCounter & operator=(MovableCtorDtorCounter &&) = default;
using CtorDtorCounter::operator==;
using CtorDtorCounter::operator!=;
};
int CtorDtorCounter::created = 0;
int CtorDtorCounter::destroyed = 0;
} // namespace
TEST(TestNullable, TestBasic)
{
// Set up our test CtorDtorCounter objects, which will mess with counts, before we reset the
// counts.
CtorDtorCounter c100(100), c101(101), c102(102);
CtorDtorCounter::ResetCounter();
{
auto testNullable = MakeNullable<CtorDtorCounter>(100);
EXPECT_TRUE(CtorDtorCounter::created == 1 && CtorDtorCounter::destroyed == 0);
EXPECT_TRUE(!testNullable.IsNull() && testNullable.Value().m == 100);
EXPECT_EQ(testNullable, c100);
EXPECT_NE(testNullable, c101);
EXPECT_NE(testNullable, c102);
testNullable.SetNull();
EXPECT_TRUE(CtorDtorCounter::created == 1 && CtorDtorCounter::destroyed == 1);
EXPECT_TRUE(testNullable.IsNull());
EXPECT_NE(testNullable, c100);
EXPECT_NE(testNullable, c101);
EXPECT_NE(testNullable, c102);
testNullable.SetNonNull(CtorDtorCounter(101));
EXPECT_TRUE(CtorDtorCounter::created == 3 && CtorDtorCounter::destroyed == 2);
EXPECT_TRUE(!testNullable.IsNull() && testNullable.Value().m == 101);
EXPECT_NE(testNullable, c100);
EXPECT_EQ(testNullable, c101);
EXPECT_NE(testNullable, c102);
testNullable.SetNonNull(102);
EXPECT_TRUE(CtorDtorCounter::created == 4 && CtorDtorCounter::destroyed == 3);
EXPECT_TRUE(!testNullable.IsNull() && testNullable.Value().m == 102);
EXPECT_NE(testNullable, c100);
EXPECT_NE(testNullable, c101);
EXPECT_EQ(testNullable, c102);
}
// Our test CtorDtorCounter objects are still in scope here.
EXPECT_TRUE(CtorDtorCounter::created == 4 && CtorDtorCounter::destroyed == 4);
}
TEST(TestNullable, TestMake)
{
CtorDtorCounter::ResetCounter();
{
auto testNullable = MakeNullable<CtorDtorCounter>(200);
EXPECT_TRUE(CtorDtorCounter::created == 1 && CtorDtorCounter::destroyed == 0);
EXPECT_TRUE(!testNullable.IsNull() && testNullable.Value().m == 200);
}
EXPECT_TRUE(CtorDtorCounter::created == 1 && CtorDtorCounter::destroyed == 1);
}
TEST(TestNullable, TestCopy)
{
CtorDtorCounter::ResetCounter();
{
auto testSrc = MakeNullable<CtorDtorCounter>(300);
EXPECT_TRUE(CtorDtorCounter::created == 1 && CtorDtorCounter::destroyed == 0);
EXPECT_TRUE(!testSrc.IsNull() && testSrc.Value().m == 300);
{
Nullable<CtorDtorCounter> testDst(testSrc);
EXPECT_TRUE(CtorDtorCounter::created == 2 && CtorDtorCounter::destroyed == 0);
EXPECT_TRUE(!testDst.IsNull() && testDst.Value().m == 300);
}
EXPECT_TRUE(CtorDtorCounter::created == 2 && CtorDtorCounter::destroyed == 1);
{
Nullable<CtorDtorCounter> testDst;
EXPECT_TRUE(CtorDtorCounter::created == 2 && CtorDtorCounter::destroyed == 1);
EXPECT_TRUE(testDst.IsNull());
testDst = testSrc;
EXPECT_TRUE(CtorDtorCounter::created == 3 && CtorDtorCounter::destroyed == 1);
EXPECT_TRUE(!testDst.IsNull() && testDst.Value().m == 300);
}
EXPECT_TRUE(CtorDtorCounter::created == 3 && CtorDtorCounter::destroyed == 2);
}
EXPECT_TRUE(CtorDtorCounter::created == 3 && CtorDtorCounter::destroyed == 3);
}
TEST(TestNullable, TestMove)
{
CtorDtorCounter::ResetCounter();
{
auto testSrc = MakeNullable<MovableCtorDtorCounter>(400); // construct
Nullable<MovableCtorDtorCounter> testDst(std::move(testSrc)); // move construct
EXPECT_TRUE(CtorDtorCounter::created == 2 && CtorDtorCounter::destroyed == 0);
EXPECT_TRUE(!testDst.IsNull() && testDst.Value().m == 400);
// destroy both testsSrc and testDst
}
EXPECT_TRUE(CtorDtorCounter::created == 2 && CtorDtorCounter::destroyed == 2);
CtorDtorCounter::ResetCounter();
{
Nullable<MovableCtorDtorCounter> testDst; // no object construction
EXPECT_TRUE(CtorDtorCounter::created == 0 && CtorDtorCounter::destroyed == 0);
EXPECT_TRUE(testDst.IsNull());
auto testSrc = MakeNullable<MovableCtorDtorCounter>(401); // construct object
testDst = std::move(testSrc); // construct a copy
EXPECT_TRUE(CtorDtorCounter::created == 2 && CtorDtorCounter::destroyed == 0);
EXPECT_TRUE(!testDst.IsNull() && testDst.Value().m == 401);
}
EXPECT_TRUE(CtorDtorCounter::created == 2 && CtorDtorCounter::destroyed == 2);
}
TEST(TestNullable, TestUpdate)
{
using SmallArray = std::array<uint8_t, 3>;
// Arrays
{
auto nullable1 = MakeNullable<SmallArray>({ 1, 2, 3 });
auto nullable2 = MakeNullable<SmallArray>({ 1, 2, 3 });
EXPECT_FALSE(nullable1.IsNull());
EXPECT_FALSE(nullable2.IsNull());
EXPECT_EQ(nullable1, nullable2);
// No-op on change to same.
EXPECT_FALSE(nullable1.Update(nullable2));
EXPECT_EQ(nullable1, nullable2);
nullable1.Value()[0] = 100;
EXPECT_NE(nullable1, nullable2);
EXPECT_TRUE(nullable2.Update(nullable1));
EXPECT_EQ(nullable1, nullable2);
}
// Structs
{
struct SomeObject
{
uint8_t a;
uint8_t b;
bool operator==(const SomeObject & other) const { return (a == other.a) && (b == other.b); }
};
auto nullable1 = MakeNullable<SomeObject>({ 1, 2 });
auto nullable2 = MakeNullable<SomeObject>({ 1, 2 });
EXPECT_FALSE(nullable1.IsNull());
EXPECT_FALSE(nullable2.IsNull());
EXPECT_EQ(nullable1, nullable2);
// No-op on change to same.
EXPECT_FALSE(nullable1.Update(nullable2));
EXPECT_EQ(nullable1, nullable2);
nullable1.Value().a = 100;
EXPECT_NE(nullable1, nullable2);
EXPECT_TRUE(nullable2.Update(nullable1));
EXPECT_EQ(nullable1, nullable2);
}
// Scalar cases
{
auto nullable1 = MakeNullable(static_cast<uint8_t>(1));
EXPECT_FALSE(nullable1.IsNull());
// Non-null to non-null same value
EXPECT_FALSE(nullable1.Update(nullable1));
EXPECT_FALSE(nullable1.IsNull());
// Non-null to null
EXPECT_TRUE(nullable1.Update(NullNullable));
EXPECT_TRUE(nullable1.IsNull());
// Null to null
EXPECT_FALSE(nullable1.Update(NullNullable));
EXPECT_TRUE(nullable1.IsNull());
// Null to non-null
EXPECT_TRUE(nullable1.Update(MakeNullable(static_cast<uint8_t>(1))));
EXPECT_FALSE(nullable1.IsNull());
EXPECT_EQ(nullable1.Value(), 1);
// Non-null to non-null different value
EXPECT_TRUE(nullable1.Update(MakeNullable(static_cast<uint8_t>(2))));
EXPECT_FALSE(nullable1.IsNull());
EXPECT_EQ(nullable1.Value(), 2);
// Non-null to extent of range --> changes to "invalid" value in range.
EXPECT_TRUE(nullable1.Update(MakeNullable(static_cast<uint8_t>(255))));
EXPECT_FALSE(nullable1.IsNull());
EXPECT_EQ(nullable1.Value(), 255);
}
}