skia2/tests/SkTOptionalTest.cpp
John Stiles e2c4e7e62d Add comparison operators to skstd::optional.
This involves a significant number of overloads, as optionals can be
compared against other optionals, or nullopt, or a value; either side
can mix and match as desired.

Change-Id: I26b8420e417300d32569fc76a55bcfd8e01eb322
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/420576
Auto-Submit: John Stiles <johnstiles@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
2021-06-22 21:11:15 +00:00

287 lines
9.2 KiB
C++

/*
* Copyright 2021 Google LLC.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/private/SkTArray.h"
#include "include/private/SkTOptional.h"
#include "tests/Test.h"
DEF_TEST(SkTOptionalEmpty, r) {
skstd::optional<int> o;
REPORTER_ASSERT(r, !o);
REPORTER_ASSERT(r, !o.has_value());
}
DEF_TEST(SkTOptionalNulloptCtor, r) {
skstd::optional<int> o(skstd::nullopt);
REPORTER_ASSERT(r, !o);
REPORTER_ASSERT(r, !o.has_value());
}
DEF_TEST(SkTOptionalValue, r) {
skstd::optional<const char*> o("test");
REPORTER_ASSERT(r, o);
REPORTER_ASSERT(r, o.has_value());
REPORTER_ASSERT(r, !strcmp(*o, "test"));
REPORTER_ASSERT(r, !strcmp(o.value(), "test"));
o.reset();
REPORTER_ASSERT(r, !o);
REPORTER_ASSERT(r, !o.has_value());
}
DEF_TEST(SkTOptionalNulloptAssignment, r) {
skstd::optional<const char*> o("test");
REPORTER_ASSERT(r, o);
REPORTER_ASSERT(r, o.has_value());
o = skstd::nullopt;
REPORTER_ASSERT(r, !o);
REPORTER_ASSERT(r, !o.has_value());
}
DEF_TEST(SkTOptionalNulloptReturn, r) {
auto fn = []() -> skstd::optional<float> { return skstd::nullopt; };
skstd::optional<float> o = fn();
REPORTER_ASSERT(r, !o);
REPORTER_ASSERT(r, !o.has_value());
}
DEF_TEST(SkTOptionalComparisons, r) {
int v[] = { 1, 2, 3, 4, 5 };
skstd::optional<int> o[] = {1, 2, skstd::nullopt, 4, 5};
skstd::optional<int> five = 5;
skstd::optional<int> six = 6;
for (int index = 0; index < (int)SK_ARRAY_COUNT(v); ++index) {
REPORTER_ASSERT(r, v[index] < six);
REPORTER_ASSERT(r, o[index] < six);
REPORTER_ASSERT(r, six > v[index]);
REPORTER_ASSERT(r, six > o[index]);
REPORTER_ASSERT(r, v[index] < 6);
REPORTER_ASSERT(r, o[index] < 6);
REPORTER_ASSERT(r, 6 > v[index]);
REPORTER_ASSERT(r, 6 > o[index]);
REPORTER_ASSERT(r, !(six < v[index]));
REPORTER_ASSERT(r, !(six < o[index]));
REPORTER_ASSERT(r, !(v[index] > six));
REPORTER_ASSERT(r, !(o[index] > six));
REPORTER_ASSERT(r, !(6 < v[index]));
REPORTER_ASSERT(r, !(6 < o[index]));
REPORTER_ASSERT(r, !(v[index] > 6));
REPORTER_ASSERT(r, !(o[index] > 6));
REPORTER_ASSERT(r, v[index] <= five);
REPORTER_ASSERT(r, o[index] <= five);
REPORTER_ASSERT(r, five >= v[index]);
REPORTER_ASSERT(r, five >= o[index]);
REPORTER_ASSERT(r, v[index] <= 5);
REPORTER_ASSERT(r, o[index] <= 5);
REPORTER_ASSERT(r, 5 >= v[index]);
REPORTER_ASSERT(r, 5 >= o[index]);
REPORTER_ASSERT(r, skstd::nullopt <= o[index]);
REPORTER_ASSERT(r, !(skstd::nullopt > o[index]));
REPORTER_ASSERT(r, o[index] >= skstd::nullopt);
REPORTER_ASSERT(r, !(o[index] < skstd::nullopt));
if (o[index].has_value()) {
REPORTER_ASSERT(r, o[index] != skstd::nullopt);
REPORTER_ASSERT(r, skstd::nullopt != o[index]);
REPORTER_ASSERT(r, o[index] == o[index]);
REPORTER_ASSERT(r, o[index] != six);
REPORTER_ASSERT(r, o[index] == v[index]);
REPORTER_ASSERT(r, v[index] == o[index]);
REPORTER_ASSERT(r, o[index] > 0);
REPORTER_ASSERT(r, o[index] >= 1);
REPORTER_ASSERT(r, o[index] <= 5);
REPORTER_ASSERT(r, o[index] < 6);
REPORTER_ASSERT(r, 0 < o[index]);
REPORTER_ASSERT(r, 1 <= o[index]);
REPORTER_ASSERT(r, 5 >= o[index]);
REPORTER_ASSERT(r, 6 > o[index]);
} else {
REPORTER_ASSERT(r, o[index] == skstd::nullopt);
REPORTER_ASSERT(r, skstd::nullopt == o[index]);
REPORTER_ASSERT(r, o[index] == o[index]);
REPORTER_ASSERT(r, o[index] != five);
REPORTER_ASSERT(r, o[index] != v[index]);
REPORTER_ASSERT(r, v[index] != o[index]);
REPORTER_ASSERT(r, o[index] < 0);
REPORTER_ASSERT(r, o[index] <= 0);
REPORTER_ASSERT(r, 0 > o[index]);
REPORTER_ASSERT(r, 0 >= o[index]);
REPORTER_ASSERT(r, !(o[index] > 0));
REPORTER_ASSERT(r, !(o[index] >= 0));
REPORTER_ASSERT(r, !(0 < o[index]));
REPORTER_ASSERT(r, !(0 <= o[index]));
}
}
}
class SkTOptionalTestPayload {
public:
enum State {
kConstructed,
kCopyConstructed,
kCopyAssigned,
kMoveConstructed,
kMoveAssigned,
kMovedFrom
};
SkTOptionalTestPayload(int payload)
: fState(kConstructed)
, fPayload(payload) {}
SkTOptionalTestPayload(const SkTOptionalTestPayload& other)
: fState(kCopyConstructed)
, fPayload(other.fPayload) {}
SkTOptionalTestPayload(SkTOptionalTestPayload&& other)
: fState(kMoveConstructed)
, fPayload(other.fPayload) {
other.fState = kMovedFrom;
}
SkTOptionalTestPayload& operator=(const SkTOptionalTestPayload& other) {
fState = kCopyAssigned;
fPayload = other.fPayload;
return *this;
}
SkTOptionalTestPayload& operator=(SkTOptionalTestPayload&& other) {
fState = kMoveAssigned;
fPayload = other.fPayload;
other.fState = kMovedFrom;
return *this;
}
State fState;
int fPayload;
};
DEF_TEST(SkTOptionalConstruction, r) {
skstd::optional<SkTOptionalTestPayload> o(1);
REPORTER_ASSERT(r, o);
REPORTER_ASSERT(r, o->fState == SkTOptionalTestPayload::kConstructed);
REPORTER_ASSERT(r, o->fPayload == 1);
skstd::optional<SkTOptionalTestPayload> copy(o);
REPORTER_ASSERT(r, copy);
REPORTER_ASSERT(r, copy->fState == SkTOptionalTestPayload::kCopyConstructed);
REPORTER_ASSERT(r, copy->fPayload == 1);
REPORTER_ASSERT(r, o->fState == SkTOptionalTestPayload::kConstructed);
skstd::optional<SkTOptionalTestPayload> move(std::move(o));
REPORTER_ASSERT(r, move);
REPORTER_ASSERT(r, move->fState == SkTOptionalTestPayload::kMoveConstructed);
REPORTER_ASSERT(r, move->fPayload == 1);
// NOLINTNEXTLINE(bugprone-use-after-move)
REPORTER_ASSERT(r, o->fState == SkTOptionalTestPayload::kMovedFrom);
}
DEF_TEST(SkTOptionalMoveAssignment, r) {
skstd::optional<SkTOptionalTestPayload> o;
REPORTER_ASSERT(r, !o);
// assign to an empty optional from an empty optional
o = skstd::optional<SkTOptionalTestPayload>();
REPORTER_ASSERT(r, !o);
// assign to an empty optional from a full optional
skstd::optional<SkTOptionalTestPayload> full(1);
o = std::move(full);
REPORTER_ASSERT(r, o);
REPORTER_ASSERT(r, o->fState == SkTOptionalTestPayload::kMoveConstructed);
REPORTER_ASSERT(r, o->fPayload == 1);
// NOLINTNEXTLINE(bugprone-use-after-move)
REPORTER_ASSERT(r, full->fState == SkTOptionalTestPayload::kMovedFrom);
// assign to a full optional from a full optional
full = skstd::optional<SkTOptionalTestPayload>(2);
o = std::move(full);
REPORTER_ASSERT(r, o);
REPORTER_ASSERT(r, o->fState == SkTOptionalTestPayload::kMoveAssigned);
REPORTER_ASSERT(r, o->fPayload == 2);
// NOLINTNEXTLINE(bugprone-use-after-move)
REPORTER_ASSERT(r, full->fState == SkTOptionalTestPayload::kMovedFrom);
// assign to a full optional from an empty optional
o = skstd::optional<SkTOptionalTestPayload>();
REPORTER_ASSERT(r, !o);
}
DEF_TEST(SkTOptionalCopyAssignment, r) {
skstd::optional<SkTOptionalTestPayload> o;
REPORTER_ASSERT(r, !o);
skstd::optional<SkTOptionalTestPayload> empty;
skstd::optional<SkTOptionalTestPayload> full(1);
// assign to an empty optional from an empty optional
o = empty;
REPORTER_ASSERT(r, !o);
// assign to an empty optional from a full optional
o = full;
REPORTER_ASSERT(r, o);
REPORTER_ASSERT(r, o->fState == SkTOptionalTestPayload::kCopyConstructed);
REPORTER_ASSERT(r, o->fPayload == 1);
// assign to a full optional from a full optional
o = full;
REPORTER_ASSERT(r, o);
REPORTER_ASSERT(r, o->fState == SkTOptionalTestPayload::kCopyAssigned);
REPORTER_ASSERT(r, o->fPayload == 1);
// assign to a full optional from an empty optional
o = empty;
REPORTER_ASSERT(r, !o);
}
DEF_TEST(SkTOptionalNoDefaultConstructor, r) {
class NoDefaultConstructor {
public:
NoDefaultConstructor(int value)
: fValue(value) {}
int fValue;
};
skstd::optional<NoDefaultConstructor> o1;
REPORTER_ASSERT(r, !o1);
skstd::optional<NoDefaultConstructor> o2(5);
REPORTER_ASSERT(r, o2);
REPORTER_ASSERT(r, o2->fValue == 5);
o1 = std::move(o2);
REPORTER_ASSERT(r, o1);
REPORTER_ASSERT(r, o1->fValue == 5);
}
DEF_TEST(SkTOptionalSelfAssignment, r) {
skstd::optional<SkString> empty;
skstd::optional<SkString>& emptyRef = empty;
empty = emptyRef;
REPORTER_ASSERT(r, !empty);
empty = std::move(emptyRef);
REPORTER_ASSERT(r, !empty);
skstd::optional<SkString> full("full");
skstd::optional<SkString>& fullRef = full;
full = fullRef;
REPORTER_ASSERT(r, full);
REPORTER_ASSERT(r, *full == SkString("full"));
full = std::move(fullRef);
REPORTER_ASSERT(r, full);
REPORTER_ASSERT(r, *full == SkString("full"));
}