910e62b5创建于 1月15日历史提交
// Copyright 2025 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/auto_spanification_helper.h"

#include <array>
#include <cstdint>

#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace base {
namespace {

TEST(AutoSpanificationHelperTest, SpanificationCArrayBeginAndEnd) {
  int array[] = {1, 2, 3};
  int count = 0;
  for (base::span<int> it = SpanificationArrayBegin(array);
       it != SpanificationArrayEnd(array); PreIncrementSpan(it)) {
    ++count;
  }
  EXPECT_EQ(count, 3);
}

TEST(AutoSpanificationHelperTest, SpanificationCArrayCBeginAndCEnd) {
  const int array[] = {1, 2, 3};
  int count = 0;
  for (base::span<const int> it = SpanificationArrayCBegin(array);
       it != SpanificationArrayCEnd(array); PreIncrementSpan(it)) {
    ++count;
  }
  EXPECT_EQ(count, 3);
}

TEST(AutoSpanificationHelperTest, SpanificationStdArrayBeginAndEnd) {
  std::array<int, 3> array = {1, 2, 3};
  int count = 0;
  for (base::span<int> it = SpanificationArrayBegin(array);
       it != SpanificationArrayEnd(array); PreIncrementSpan(it)) {
    ++count;
  }
  EXPECT_EQ(count, 3);
}

TEST(AutoSpanificationHelperTest, SpanificationStdArrayCBeginAndCEnd) {
  std::array<int, 3> array = {1, 2, 3};
  int count = 0;
  for (base::span<const int> it = SpanificationArrayCBegin(array);
       it != SpanificationArrayCEnd(array); PreIncrementSpan(it)) {
    ++count;
  }
  EXPECT_EQ(count, 3);
}

TEST(AutoSpanificationHelperTest, SpanificationSizeofForStdArray) {
  std::array<char, 7> char_array;
  EXPECT_EQ(SpanificationSizeofForStdArray(char_array), 7);

  std::array<uint16_t, 3> uint16_array;
  EXPECT_EQ(SpanificationSizeofForStdArray(uint16_array), sizeof(uint16_t) * 3);
}

TEST(AutoSpanificationIncrementTest, PreIncrementSpan) {
  std::vector<int> data = {1, 2, 3, 4, 5};
  span<int> s(data);

  span<int> result = PreIncrementSpan(s);
  EXPECT_THAT(s, testing::ElementsAre(2, 3, 4, 5));

  EXPECT_EQ(result.data(), s.data());
  EXPECT_EQ(result.size(), s.size());
}

TEST(AutoSpanificationIncrementTest, PreIncrementSingleElementSpan) {
  std::vector<int> single_element_data = {42};
  span<int> s(single_element_data);

  span<int> result = PreIncrementSpan(s);

  EXPECT_TRUE(s.empty());
  EXPECT_EQ(s.size(), 0u);

  EXPECT_TRUE(result.empty());
  EXPECT_EQ(result.size(), 0u);
}

TEST(AutoSpanificationIncrementTest, PreIncrementEmptySpan) {
  std::vector<int> empty_data;
  span<int> s(empty_data);

  // An iterator that is at the end is expressed as an empty span and it shall
  // not be incremented. Expect a CHECK failure when trying to pre-increment an
  // empty span.
  ASSERT_DEATH_IF_SUPPORTED({ PreIncrementSpan(s); }, "");
}

TEST(AutoSpanificationIncrementTest, PreIncrementConstSpan) {
  const std::vector<int> data = {1, 2, 3, 4, 5};
  span<const int> s(data);

  span<const int> result = PreIncrementSpan(s);

  EXPECT_THAT(s, testing::ElementsAre(2, 3, 4, 5));

  EXPECT_EQ(result.data(), s.data());
  EXPECT_EQ(result.size(), s.size());
}

TEST(AutoSpanificationIncrementTest, PostIncrementSpan) {
  std::vector<int> data = {1, 2, 3, 4, 5};
  span<int> s(data);

  span<int> result = PostIncrementSpan(s);

  EXPECT_THAT(result, testing::ElementsAre(1, 2, 3, 4, 5));
  EXPECT_THAT(s, testing::ElementsAre(2, 3, 4, 5));
}

TEST(AutoSpanificationIncrementTest, PostIncrementSingleElementSpan) {
  std::vector<int> single_element_data = {42};
  span<int> s(single_element_data);

  span<int> result = PostIncrementSpan(s);

  EXPECT_EQ(result.size(), 1u);
  EXPECT_EQ(result[0], 42);

  EXPECT_TRUE(s.empty());
  EXPECT_EQ(s.size(), 0u);
}

TEST(AutoSpanificationIncrementTest, PostIncrementEmptySpan) {
  std::vector<int> empty_data;
  span<int> s(empty_data);

  // An iterator that is at the end is expressed as an empty span and it shall
  // not be incremented. Expect a CHECK failure when trying to post-increment an
  // empty span.
  ASSERT_DEATH_IF_SUPPORTED({ PostIncrementSpan(s); }, "");
}

TEST(AutoSpanificationIncrementTest, PostIncrementConstSpan) {
  const std::vector<int> data = {1, 2, 3, 4, 5};
  span<const int> s(data);

  span<const int> result = PostIncrementSpan(s);

  EXPECT_THAT(result, testing::ElementsAre(1, 2, 3, 4, 5));

  EXPECT_EQ(s.size(), 4u);
  EXPECT_THAT(s, testing::ElementsAre(2, 3, 4, 5));
}

}  // namespace
}  // namespace base

namespace base::internal::spanification {

namespace {

// Minimized mock of SkBitmap class defined in
// //third_party/skia/include/core/SkBitmap.h
class SkBitmap {
 public:
  uint32_t* getAddr32(int x, int y) const { return &row_[x]; }
  int width() const { return static_cast<int>(row_.size()); }

  mutable std::array<uint32_t, 128> row_{};
};

// The main purpose of the following test cases is to exercise C++ compilation
// on the macro usage rather than testing their behaviors.

TEST(AutoSpanificationHelperTest, SkBitmapGetAddr32Pointer) {
  SkBitmap sk_bitmap;
  const int x = 123;
  base::span<uint32_t> span = UNSAFE_SKBITMAP_GETADDR32(&sk_bitmap, x, 0);
  EXPECT_EQ(span.data(), &sk_bitmap.row_[x]);
  EXPECT_EQ(span.size(), sk_bitmap.row_.size() - x);
}

TEST(AutoSpanificationHelperTest, SkBitmapGetAddr32Reference) {
  SkBitmap sk_bitmap;
  const int x = 123;
  base::span<uint32_t> span = UNSAFE_SKBITMAP_GETADDR32(sk_bitmap, x, 0);
  EXPECT_EQ(span.data(), &sk_bitmap.row_[x]);
  EXPECT_EQ(span.size(), sk_bitmap.row_.size() - x);
}

TEST(AutoSpanificationHelperTest, SkBitmapGetAddr32SmartPtr) {
  std::unique_ptr<SkBitmap> sk_bitmap = std::make_unique<SkBitmap>();
  const int x = 123;
  base::span<uint32_t> span = UNSAFE_SKBITMAP_GETADDR32(sk_bitmap, x, 0);
  EXPECT_EQ(span.data(), &sk_bitmap->row_[x]);
  EXPECT_EQ(span.size(), sk_bitmap->row_.size() - x);
}

// Minimized mock of CRYPTO_BUFFER_data and CRYPTO_BUFFER_len defined in
// //third_party/boringssl/src/include/openssl/pool.h
struct CRYPTO_BUFFER {
  base::raw_ptr<uint8_t> data = nullptr;
  size_t len = 0;
};
const uint8_t* CRYPTO_BUFFER_data(const CRYPTO_BUFFER* buf) {
  return buf->data.get();
}
size_t CRYPTO_BUFFER_len(const CRYPTO_BUFFER* buf) {
  return buf->len;
}

TEST(AutoSpanificationHelperTest, CryptoBufferData) {
  std::array<uint8_t, 128> array;
  CRYPTO_BUFFER buffer = {array.data(), array.size()};

  base::span<const uint8_t> span = UNSAFE_CRYPTO_BUFFER_DATA(&buffer);
  EXPECT_EQ(span.data(), array.data());
  EXPECT_EQ(span.size(), array.size());
}

// Minimized mock of hb_buffer_get_glyph_infos and
// hb_buffer_get_glyph_positions defined in
// //third_party/harfbuzz-ng/src/src/hb-buffer.h
struct hb_glyph_info_t {};
struct hb_glyph_position_t {};
struct hb_buffer_t {
  base::raw_ptr<hb_glyph_info_t> info = nullptr;
  base::raw_ptr<hb_glyph_position_t> pos = nullptr;
  unsigned int len = 0;
};
hb_glyph_info_t* hb_buffer_get_glyph_infos(hb_buffer_t* buffer,
                                           unsigned int* length) {
  if (length) {
    *length = buffer->len;
  }
  return buffer->info.get();
}
hb_glyph_position_t* hb_buffer_get_glyph_positions(hb_buffer_t* buffer,
                                                   unsigned int* length) {
  if (length) {
    *length = buffer->len;
  }
  return buffer->pos.get();
}

TEST(AutoSpanificationHelperTest, HbBufferGetGlyphInfos) {
  std::array<hb_glyph_info_t, 128> info_array;
  hb_buffer_t buffer;
  unsigned int length = 0;
  base::span<hb_glyph_info_t> infos;

  buffer = {.info = info_array.data(), .len = info_array.size()};
  infos = UNSAFE_HB_BUFFER_GET_GLYPH_INFOS(&buffer, &length);
  EXPECT_EQ(infos.data(), info_array.data());
  EXPECT_EQ(infos.size(), info_array.size());
  EXPECT_EQ(length, info_array.size());

  infos = UNSAFE_HB_BUFFER_GET_GLYPH_INFOS(&buffer, nullptr);
  EXPECT_EQ(infos.data(), info_array.data());
  EXPECT_EQ(infos.size(), info_array.size());
}

TEST(AutoSpanificationHelperTest, HbBufferGetGlyphPositions) {
  std::array<hb_glyph_position_t, 128> pos_array;
  hb_buffer_t buffer;
  unsigned int length = 0;
  base::span<hb_glyph_position_t> positions;

  buffer = {.pos = pos_array.data(), .len = pos_array.size()};
  positions = UNSAFE_HB_BUFFER_GET_GLYPH_POSITIONS(&buffer, &length);
  EXPECT_EQ(positions.data(), pos_array.data());
  EXPECT_EQ(positions.size(), pos_array.size());
  EXPECT_EQ(length, pos_array.size());

  buffer = {.pos = pos_array.data(), .len = pos_array.size()};
  positions = UNSAFE_HB_BUFFER_GET_GLYPH_POSITIONS(&buffer, /*length=*/nullptr);
  EXPECT_EQ(positions.data(), pos_array.data());
  EXPECT_EQ(positions.size(), pos_array.size());

  buffer = {.pos = nullptr,
            .len = pos_array.size()};  // pos == nullptr, len != 0
  positions = UNSAFE_HB_BUFFER_GET_GLYPH_POSITIONS(&buffer, &length);
  EXPECT_EQ(positions.data(), nullptr);
  EXPECT_EQ(positions.size(), 0);  // The span's size is 0
  EXPECT_NE(length, 0);            // even when `length` is non-zero.
}

// Minimized mock of g_get_system_data_dirs
// https://web.mit.edu/barnowl/share/gtk-doc/html/glib/glib-Miscellaneous-Utility-Functions.html#g-get-system-data-dirs
using gchar = char;
constexpr auto kGlibSystemDataDirs =
    std::to_array<const gchar* const>({"foo", "bar", "baz", nullptr});
const gchar* const* g_get_system_data_dirs() {
  return kGlibSystemDataDirs.data();
}

TEST(AutoSpanificationHelperTest, GGetSystemDataDirs) {
  base::span<const gchar* const> dirs = UNSAFE_G_GET_SYSTEM_DATA_DIRS();
  EXPECT_EQ(dirs.data(), kGlibSystemDataDirs.data());
  EXPECT_EQ(dirs.size(), kGlibSystemDataDirs.size());
}

}  // namespace

}  // namespace base::internal::spanification