// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cassert>
#include <cstdint>
#include <cstring>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "base/containers/span.h"
#include "usr/include/linux/netlink.h"
// NOTE: this exists as a regression test for crbug/357433195, where before the
// associated patch that added this, this would fail an assertion in
// extract_edits.py. However after fixing the assertion the code below doesn't
// get rewritten so original.cc == expected.cc, we're just asserting that the
// script doesn't fail on it.
namespace internal {
namespace {
using std::size_t;
// No expected rewrite
bool GetAddress(const struct nlmsghdr* header,
int header_length,
bool* really_deprecated) {
if (really_deprecated) {
*really_deprecated = false;
}
// No expected rewrite
const struct nlmsghdr* msg =
reinterpret_cast<const struct nlmsghdr*>(NLMSG_DATA(header));
return true;
}
// No expected rewrite
template <typename T>
T* SafelyCastNetlinkMsgData(const struct nlmsghdr* header, int length) {
if (length <= 0 || static_cast<size_t>(length) < NLMSG_HDRLEN + sizeof(T)) {
return nullptr;
}
// no expected rewrite
return reinterpret_cast<const T*>(NLMSG_DATA(header));
}
} // namespace
} // namespace internal
// While fixing the above, the updating sometimes caused the rewrite to occur in
// the middle of the MACRO rather than inside the macro. This catches that case.
// Expected rewrite:
// dict.data()
#define CAST_FUN reinterpret_cast<std::size_t**>(dict.data());
#define CAST_FUN_2(x) reinterpret_cast<std::size_t**>(x);
// Expected rewrite:
// base::span<int*> dict
std::size_t check(base::span<int*> dict, std::size_t N) {
if (dict) {
for (int i = 0; i < N; ++i) {
int* ptr = dict[i];
// No expected rewrite
std::size_t** new_ptr = CAST_FUN;
// Expected rewrite:
// dict.data()
std::size_t** new_ptr_2 = CAST_FUN_2(dict.data());
if (**new_ptr > 10) {
return **new_ptr;
} else {
return **new_ptr_2;
}
}
}
return 10;
}
void checkMacroInFile() {
int array[2][1] = {{int(2)}, {int(3)}};
// Expected rewrite:
// base::span<int*>
base::span<int*> front = reinterpret_cast<int**>(array);
check(front, 2);
}
// Finally after fixing the initial multiple neighbors there was a new one and
// this one catches that. Again this used to crash and showcases the situations
// where the assertion would fail.
struct Entry {
explicit Entry(std::string n) : name(n) {}
std::string name;
std::unordered_map<std::string, int> metrics;
};
class Recorder {
public:
// No expected rewrite.
bool EntryHasMetricNoRewrites(const Entry* entry, std::string metric_name) {
const int* val = nullptr;
if (entry->metrics.find(metric_name) != entry->metrics.end()) {
val = &entry->metrics.find(metric_name)->second;
}
return val != nullptr;
}
// No expected rewrite
bool EntryHasMetricRhsRewrite(const Entry* const* entries,
size_t len,
std::string metric_name) {
// Can't use entries like a buffer to avoid the rewrite.
const int* val = nullptr;
if (!entries || metric_name == "hello" || len > 3) {
return false;
}
return true;
}
// Expected rewrite:
// base::span<const Entry* const>
bool EntryHasMetricFullRewrite(base::span<const Entry* const> entries,
size_t len,
std::string metric_name) {
const int* val = nullptr;
for (size_t i = 0; i < len; ++i) {
const Entry* entry = entries[i];
if (entry->metrics.find(metric_name) != entry->metrics.end()) {
val = &entry->metrics.find(metric_name)->second;
}
}
return val != nullptr;
}
// No expected rewrite.
std::vector<const Entry*> GetEntriesByName(std::string name) const {
std::vector<const Entry*> result;
for (const auto& entry : entries_) {
if (entry->name == name) {
result.push_back(entry.get());
}
}
return result;
}
std::vector<std::unique_ptr<Entry>> entries_;
};
// No expected rewrite.
#define EXPECT_HAS_UKM_NO_REWRITE(name) \
assert(test_recorder_->EntryHasMetricNoRewrites(entry, name));
// Expected rewrite:
// test_entries.data()
#define EXPECT_HAS_UKM_RHS_REWRITE(name) \
assert(test_recorder_->EntryHasMetricRhsRewrite(test_entries.data(), \
entries.size(), name));
// No expected rewrite.
#define EXPECT_HAS_UKM_FULL_REWRITE(name) \
assert(test_recorder_->EntryHasMetricFullRewrite(test_entries, \
entries.size(), name));
void MediaMetricsProviderTestTestUkm() {
Recorder recorder_;
Recorder* test_recorder_ = &recorder_;
test_recorder_->entries_.push_back(std::make_unique<Entry>("foo"));
// Test when neither lhs nor rhs gets rewritten this is the common case in the
// code base.
{
const auto& entries = test_recorder_->GetEntriesByName("foo");
assert(1u == entries.size());
// No expected rewrite.
for (const Entry* entry : entries) {
// This macro references |entry| and thus would need to rewrite it as
// .data() if we changed the for loop to a base::span (which we don't).
EXPECT_HAS_UKM_NO_REWRITE("bar");
}
}
{
const auto& entries = test_recorder_->GetEntriesByName("foo");
assert(1u == entries.size());
// No expected rewrite.
for (const Entry* entry : entries) {
// This macro references |entry| and thus would need to rewrite it as
// .data() if we changed the for loop to a base::span (which we don't).
EXPECT_HAS_UKM_NO_REWRITE("bar");
}
}
// Test how we handle macros when the function (lhs) doesn't get rewritten but
// the rhs does.
{
const auto& entries = test_recorder_->GetEntriesByName("foo");
// Expected rewrite:
// base::span<const Entry*> test_entries = entries;
base::span<const Entry* const> test_entries = entries;
const_cast<const Entry**>(test_entries)[0] = nullptr;
EXPECT_HAS_UKM_RHS_REWRITE("bar");
}
{
const auto& entries = test_recorder_->GetEntriesByName("foo");
// Expected rewrite:
// base::span<const Entry*> test_entries = entries;
base::span<const Entry* const> test_entries = entries;
const_cast<const Entry**>(test_entries)[0] = nullptr;
EXPECT_HAS_UKM_RHS_REWRITE("bar");
}
// Test how we handle macros when the function and the rhs gets rewritten.
{
const auto& entries = test_recorder_->GetEntriesByName("foo");
// Expected rewrite:
// base::span<const Entry* const> test_entries = entries;
base::span<const Entry* const> test_entries = entries;
EXPECT_HAS_UKM_FULL_REWRITE("bar");
}
{
const auto& entries = test_recorder_->GetEntriesByName("foo");
// Expected rewrite:
// base::span<const Entry* const> test_entries = entries;
base::span<const Entry* const> test_entries = entries;
EXPECT_HAS_UKM_FULL_REWRITE("bar");
}
}