blob: ca5824256960a8e8a4ad7e641fd0605519481181 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/containers/adapters.h"
#include <array>
#include <ranges>
#include <utility>
#include <vector>
#include "base/containers/span.h"
#include "base/containers/to_vector.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class UnsizedVector {
public:
UnsizedVector() = default;
UnsizedVector(std::initializer_list<int> il) : v_(il) {}
// Sentinel iterator type that wraps `end()` to ensure this type doesn't
// satisfy the `sized_range` concept.
template <typename Iterator>
class Sentinel {
public:
Sentinel() = default;
explicit Sentinel(Iterator it) : wrapped_it_(it) {}
bool operator==(Iterator it) const { return wrapped_it_ == it; }
private:
Iterator wrapped_it_;
};
auto begin() const { return v_.begin(); }
auto end() const { return Sentinel(v_.end()); }
auto rbegin() const { return v_.rbegin(); }
auto rend() const { return Sentinel(v_.rend()); }
auto begin() { return v_.begin(); }
auto end() { return Sentinel(v_.end()); }
auto rbegin() { return v_.rbegin(); }
auto rend() { return Sentinel(v_.rend()); }
auto operator[](size_t pos) const { return v_[pos]; }
auto operator[](size_t pos) { return v_[pos]; }
private:
std::vector<int> v_;
};
[[maybe_unused]] void StaticAsserts() {
{
// Named local variables are more readable than std::declval<T>().
std::vector<int> v;
static_assert(std::ranges::range<decltype(base::Reversed(v))>);
static_assert(std::ranges::sized_range<decltype(base::Reversed(v))>);
// `base::Reversed()` takes a const ref to the vector, which is, by
// definition, a borrowed range.
static_assert(std::ranges::borrowed_range<decltype(base::Reversed(v))>);
auto make_vector = [] { return std::vector<int>(); };
static_assert(std::ranges::range<decltype(base::Reversed(make_vector()))>);
static_assert(
std::ranges::sized_range<decltype(base::Reversed(make_vector()))>);
static_assert(
!std::ranges::borrowed_range<decltype(base::Reversed(make_vector()))>);
}
{
base::span<int> s;
static_assert(std::ranges::range<decltype(base::Reversed(s))>);
static_assert(std::ranges::sized_range<decltype(base::Reversed(s))>);
static_assert(std::ranges::borrowed_range<decltype(base::Reversed(s))>);
auto rvalue_span = [] { return base::span<int>(); };
static_assert(std::ranges::range<decltype(base::Reversed(rvalue_span()))>);
static_assert(
std::ranges::sized_range<decltype(base::Reversed(rvalue_span()))>);
static_assert(
std::ranges::borrowed_range<decltype(base::Reversed(rvalue_span()))>);
}
{
// A named local variable is more readable than std::declval<T>().
UnsizedVector v;
static_assert(std::ranges::range<decltype(v)>);
static_assert(!std::ranges::sized_range<decltype(v)>);
static_assert(std::ranges::range<decltype(base::Reversed(v))>);
static_assert(!std::ranges::sized_range<decltype(base::Reversed(v))>);
// `base::Reversed()` takes a const ref to the vector, which is, by
// definition, a borrowed range.
static_assert(std::ranges::borrowed_range<decltype(base::Reversed(v))>);
auto make_vector = [] { return UnsizedVector(); };
static_assert(std::ranges::range<decltype(base::Reversed(make_vector()))>);
static_assert(
!std::ranges::sized_range<decltype(base::Reversed(make_vector()))>);
static_assert(!std::ranges::borrowed_range<decltype(make_vector())>);
}
}
TEST(AdaptersTest, Reversed) {
std::vector<int> v = {3, 2, 1};
int j = 0;
for (int& i : base::Reversed(v)) {
EXPECT_EQ(++j, i);
i += 100;
}
EXPECT_EQ(103, v[0]);
EXPECT_EQ(102, v[1]);
EXPECT_EQ(101, v[2]);
}
TEST(AdaptersTest, ReversedUnsized) {
UnsizedVector v = {3, 2, 1};
int j = 0;
for (int& i : base::Reversed(v)) {
EXPECT_EQ(++j, i);
i += 100;
}
EXPECT_EQ(103, v[0]);
EXPECT_EQ(102, v[1]);
EXPECT_EQ(101, v[2]);
}
TEST(AdaptersTest, ReversedArray) {
std::array<int, 3> v = {3, 2, 1};
int j = 0;
for (int& i : base::Reversed(v)) {
EXPECT_EQ(++j, i);
i += 100;
}
EXPECT_EQ(103, v[0]);
EXPECT_EQ(102, v[1]);
EXPECT_EQ(101, v[2]);
}
TEST(AdaptersTest, ReversedConst) {
std::vector<int> v = {3, 2, 1};
const std::vector<int>& cv = v;
int j = 0;
for (int i : base::Reversed(cv)) {
EXPECT_EQ(++j, i);
}
}
TEST(AdaptersTest, RangeAsRvalues) {
std::vector<std::unique_ptr<int>> v;
v.push_back(std::make_unique<int>(1));
v.push_back(std::make_unique<int>(2));
v.push_back(std::make_unique<int>(3));
auto v2 = base::ToVector(base::RangeAsRvalues(std::move(v)));
EXPECT_EQ(1, *v2[0]);
EXPECT_EQ(2, *v2[1]);
EXPECT_EQ(3, *v2[2]);
// The old vector should be consumed. The standard guarantees that a
// moved-from std::unique_ptr will be null.
EXPECT_EQ(nullptr, v[0]); // NOLINT(bugprone-use-after-move)
EXPECT_EQ(nullptr, v[1]); // NOLINT(bugprone-use-after-move)
EXPECT_EQ(nullptr, v[2]); // NOLINT(bugprone-use-after-move)
}
} // namespace