chromium/third_party/distributed_point_functions/code/dpf/internal/maybe_deref_span_test.cc

// Copyright 2023 Google LLC
//
// 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 "dpf/internal/maybe_deref_span.h"

#include <type_traits>
#include <vector>

#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace distributed_point_functions {
namespace dpf_internal {
namespace {

using T = int;

TEST(MaybeDerefSpanTest, TestExplicitMutableDirectSpan) {
  std::vector<T> x = {1, 2};
  absl::Span<T> span(x);
  MaybeDerefSpan<T> span2(span);

  EXPECT_EQ(span2.size(), x.size());
  EXPECT_EQ(span2[0], x[0]);
  EXPECT_EQ(span2[1], x[1]);
  EXPECT_EQ(&span2[0], &x[0]);
  EXPECT_EQ(&span2[1], &x[1]);

  span2[0] = 3;
  EXPECT_EQ(span2[0], x[0]);
  EXPECT_EQ(x[0], 3);
}

TEST(MaybeDerefSpanTest, TestExplicitMutableSpan) {
  const std::vector<T> x = {1, 2};
  absl::Span<const T> span(x);
  MaybeDerefSpan<const T> span2(span);

  EXPECT_EQ(span2.size(), x.size());
  EXPECT_EQ(span2[0], x[0]);
  EXPECT_EQ(span2[1], x[1]);
  EXPECT_EQ(&span2[0], &x[0]);
  EXPECT_EQ(&span2[1], &x[1]);
}

TEST(MaybeDerefSpanTest, TestExplicitMutablePointerSpan) {
  std::vector<T> x = {1, 2};
  std::vector<T*> x2 = {&x[0], &x[1]};
  absl::Span<T*> span(x2);
  MaybeDerefSpan<T> span2(span);

  EXPECT_EQ(span2.size(), x.size());
  EXPECT_EQ(span2[0], x[0]);
  EXPECT_EQ(span2[1], x[1]);
  EXPECT_EQ(&span2[0], &x[0]);
  EXPECT_EQ(&span2[1], &x[1]);

  span2[0] = 3;
  EXPECT_EQ(span2[0], x[0]);
  EXPECT_EQ(x[0], 3);
}

TEST(MaybeDerefSpanTest, TestExplicitMutablePointerConstSpan) {
  std::vector<T> x = {1, 2};
  const std::vector<T*> x2 = {&x[0], &x[1]};
  absl::Span<T* const> span(x2);
  MaybeDerefSpan<T> span2(span);

  EXPECT_EQ(span2.size(), x.size());
  EXPECT_EQ(span2[0], x[0]);
  EXPECT_EQ(span2[1], x[1]);
  EXPECT_EQ(&span2[0], &x[0]);
  EXPECT_EQ(&span2[1], &x[1]);
}

TEST(MaybeDerefSpanTest, TestExplicitConstPointerConstSpan) {
  const std::vector<T> x = {1, 2};
  const std::vector<const T*> x2 = {&x[0], &x[1]};
  absl::Span<const T* const> span(x2);
  MaybeDerefSpan<const T> span2(span);

  EXPECT_EQ(span2.size(), x.size());
  EXPECT_EQ(span2[0], x[0]);
  EXPECT_EQ(span2[1], x[1]);
  EXPECT_EQ(&span2[0], &x[0]);
  EXPECT_EQ(&span2[1], &x[1]);
}

TEST(MaybeDerefSpanTest, TestMutableSpanToConstSpan) {
  std::vector<T> x = {1, 2};
  absl::Span<T> span(x);
  MaybeDerefSpan<T> span2(span);
  MaybeDerefSpan<const T> span3(span2);

  EXPECT_EQ(span3.size(), x.size());
  EXPECT_EQ(span3[0], x[0]);
  EXPECT_EQ(span3[1], x[1]);
  EXPECT_EQ(&span3[0], &x[0]);
  EXPECT_EQ(&span3[1], &x[1]);
}

TEST(MaybeDerefSpanTest, TestImplicitConstSpan) {
  const std::vector<T> x = {1, 2};
  MaybeDerefSpan<const T> span2(x);

  EXPECT_EQ(span2.size(), x.size());
  EXPECT_EQ(span2[0], x[0]);
  EXPECT_EQ(span2[1], x[1]);
  EXPECT_EQ(&span2[0], &x[0]);
  EXPECT_EQ(&span2[1], &x[1]);
}

TEST(MaybeDerefSpanTest, TestImplicitPointerConstSpan) {
  const std::vector<T> x = {1, 2};
  const std::vector<const T*> x2 = {&x[0], &x[1]};
  MaybeDerefSpan<const T> span2(x2);

  EXPECT_EQ(span2.size(), x.size());
  EXPECT_EQ(span2[0], x[0]);
  EXPECT_EQ(span2[1], x[1]);
  EXPECT_EQ(&span2[0], &x[0]);
  EXPECT_EQ(&span2[1], &x[1]);
}

void TestEq(MaybeDerefSpan<const T> span, const std::vector<T>& vector) {
  EXPECT_EQ(span.size(), vector.size());
  EXPECT_EQ(span[0], vector[0]);
  EXPECT_EQ(span[1], vector[1]);
  EXPECT_EQ(&span[0], &vector[0]);
  EXPECT_EQ(&span[1], &vector[1]);
}

TEST(MaybeDerefSpanTest, TestFunctionCallMutableVector) {
  std::vector<T> x = {1, 2};

  TestEq(x, x);
}

TEST(MaybeDerefSpanTest, TestFunctionCallMutablePointerVector) {
  std::vector<T> x = {1, 2};
  std::vector<T*> x2 = {&x[0], &x[1]};

  TestEq(x2, x);
}

TEST(MaybeDerefSpanTest, TestFunctionCallConstVector) {
  const std::vector<T> x = {1, 2};

  TestEq(x, x);
}

TEST(MaybeDerefSpanTest, TestFunctionCallMutablePointerConstVector) {
  std::vector<T> x = {1, 2};
  const std::vector<T*> x2 = {&x[0], &x[1]};

  TestEq(x2, x);
}

TEST(MaybeDerefSpanTest, TestFunctionCallConstPointerConstVector) {
  const std::vector<T> x = {1, 2};
  const std::vector<const T*> x2 = {&x[0], &x[1]};

  TestEq(x2, x);
}

// Taken from https://en.cppreference.com/w/cpp/types/is_convertible.
template <class From, class To>
auto test_implicitly_convertible(int)
    -> decltype(void(std::declval<void (&)(To)>()(std::declval<From>())),
                std::true_type{});
template <class, class>
auto test_implicitly_convertible(...) -> std::false_type;

// Test that vectors are convertible only to const spans.
static_assert(
    decltype(test_implicitly_convertible<std::vector<T>, MaybeDerefSpan<T>>(
        0))::value == false);
static_assert(decltype(test_implicitly_convertible<
                       std::vector<T>, MaybeDerefSpan<const T>>(0))::value ==
              true);

}  // namespace
}  // namespace dpf_internal
}  // namespace distributed_point_functions