/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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 "gtest/gtest.h"
#include "chre/util/non_copyable.h"
#include "chre/util/optional.h"
using chre::Optional;
class DestructorTestingObject {
public:
~DestructorTestingObject() {
if (valueToFlipWhenDestruct != nullptr) {
*valueToFlipWhenDestruct = !*valueToFlipWhenDestruct;
}
}
void setValueToFlipWhenDestruct(bool *value) {
valueToFlipWhenDestruct = value;
}
private:
bool *valueToFlipWhenDestruct = nullptr;
};
TEST(Optional, ShouldDestructContainedObject) {
bool destructed = false;
{
Optional<DestructorTestingObject> object(DestructorTestingObject{});
object.value().setValueToFlipWhenDestruct(&destructed);
}
EXPECT_TRUE(destructed);
}
TEST(Optional, NoValueByDefault) {
Optional<int> myInt;
EXPECT_FALSE(myInt.has_value());
}
TEST(Optional, NonDefaultValueByDefault) {
Optional<int> myInt(0x1337);
EXPECT_TRUE(myInt.has_value());
EXPECT_EQ(*myInt, 0x1337);
}
TEST(Optional, NonDefaultMovedValueByDefault) {
Optional<int> myInt(std::move(0x1337));
EXPECT_TRUE(myInt.has_value());
EXPECT_EQ(*myInt, 0x1337);
}
TEST(Optional, CopyConstruct) {
Optional<int> myInt(0x1337);
Optional<int> myNewInt(myInt);
EXPECT_TRUE(myNewInt.has_value());
EXPECT_EQ(*myNewInt, 0x1337);
}
TEST(Optional, CopyConstructConst) {
const Optional<int> myInt(0x1337);
Optional<int> myNewInt(myInt);
EXPECT_TRUE(myNewInt.has_value());
EXPECT_EQ(*myNewInt, 0x1337);
}
TEST(Optional, CopyAssignAndRead) {
Optional<int> myInt;
EXPECT_FALSE(myInt.has_value());
myInt = 0x1337;
EXPECT_EQ(*myInt, 0x1337);
EXPECT_TRUE(myInt.has_value());
myInt.reset();
EXPECT_FALSE(myInt.has_value());
}
TEST(Optional, MoveAssignAndRead) {
Optional<int> myInt;
EXPECT_FALSE(myInt.has_value());
myInt = std::move(0xcafe);
EXPECT_TRUE(myInt.has_value());
EXPECT_EQ(*myInt, 0xcafe);
}
TEST(Optional, OptionalMoveAssignAndRead) {
Optional<int> myInt(0x1337);
Optional<int> myMovedInt;
EXPECT_FALSE(myMovedInt.has_value());
myMovedInt = std::move(myInt);
EXPECT_TRUE(myInt.has_value());
EXPECT_TRUE(myMovedInt.has_value());
EXPECT_EQ(*myMovedInt, 0x1337);
}
TEST(Optional, OptionalCopyAssignAndRead) {
Optional<int> myInt(0x1337);
Optional<int> myCopiedInt;
EXPECT_FALSE(myCopiedInt.has_value());
myCopiedInt = myInt;
EXPECT_TRUE(myInt.has_value());
EXPECT_TRUE(myCopiedInt.has_value());
EXPECT_EQ(*myInt, 0x1337);
EXPECT_EQ(*myCopiedInt, 0x1337);
}
static constexpr int kInvalidValue = -1;
class MovableButNonCopyable : public chre::NonCopyable {
public:
MovableButNonCopyable() = default;
MovableButNonCopyable(int value) : mValue(value) {}
MovableButNonCopyable(MovableButNonCopyable&& other) {
mValue = other.mValue;
other.mValue = kInvalidValue;
}
MovableButNonCopyable& operator=(MovableButNonCopyable&& other) {
assert(mMagic == kConstructedMagic);
mValue = other.mValue;
other.mValue = kInvalidValue;
return *this;
}
~MovableButNonCopyable() {
mMagic = kUninitializedMagic;
mValue = kUninitializedMagic;
}
int getValue() const {
return mValue;
}
private:
static constexpr int kConstructedMagic = 0xfeedc0fe;
static constexpr int kUninitializedMagic = 0xdeadbeef;
int mMagic = kConstructedMagic;
int mValue = kInvalidValue;
};
TEST(Optional, UninitializedAssignment) {
constexpr int kValue1 = 0xd00d;
constexpr int kValue2 = 0xcafe;
MovableButNonCopyable transferee1(kValue1);
MovableButNonCopyable transferee2(kValue2);
Optional<MovableButNonCopyable> container;
EXPECT_FALSE(container.has_value());
container = std::move(transferee1);
EXPECT_TRUE(container.has_value());
EXPECT_EQ(container->getValue(), kValue1);
EXPECT_EQ(transferee1.getValue(), kInvalidValue);
container.reset();
EXPECT_FALSE(container.has_value());
container = std::move(transferee2);
EXPECT_TRUE(container.has_value());
EXPECT_EQ(container->getValue(), kValue2);
EXPECT_EQ(transferee2.getValue(), kInvalidValue);
}
// TODO: should add some tests to cover the possible assignment outcomes between
// two Optional instances (e.g. assign one w/o value to one w/value, etc)