// Copyright 2025 gRPC authors.
//
// 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 "tools/codegen/core/gen_stats/gen_stats_utils.h"

#include <string>

#include "test/core/test_util/test_config.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/string_view.h"

namespace grpc_core {
namespace {

TEST(GenStatsUtilsTest, StatsGenerationMatchesExpectedOutput) {
  constexpr absl::string_view kTestStatsDataYaml = R"(
    - scope: global
      metrics:
      - counter: client_calls_created
        doc: Number of client side calls created by this process
      - histogram: call_initial_size
        max: 65536
        buckets: 26
        doc: Initial size of the grpc_call arena created at call start
      - counter: client_channels_created
        doc: Number of client channels created
  
    - scope: http2_global
      metrics:
      - histogram: http2_send_message_size
        max: 16777216
        buckets: 20
        doc: Size of messages received by HTTP2 transport
      - counter: http2_settings_writes
        doc: Number of settings frames sent
  
    - scope: http2
      global_scope: http2_global
      metrics:
      - histogram: http2_write_target_size
        doc: Number of bytes targeted for http2 writes
        max: 16777216
        buckets: 50
        scope_counter_bits: 8
        scope_buckets: 8
      - counter: http2_writes_begun
        doc: Number of HTTP2 writes initiated
  
  )";

  static constexpr absl::string_view kExpectedHdrOutput =
      R"(// Copyright 2025 gRPC authors.
// 
// 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.

// Automatically generated by tools/codegen/core/gen_stats_utils.cc

#ifndef GRPC_SRC_CORE_TELEMETRY_STATS_DATA_H
#define GRPC_SRC_CORE_TELEMETRY_STATS_DATA_H

#include <grpc/support/port_platform.h>
#include <atomic>
#include <memory>
#include <stdint.h>
#include "src/core/telemetry/histogram_view.h"
#include "test/absl/strings/string_view.h"
#include "src/core/util/per_cpu.h"
#include "src/core/util/no_destruct.h"

namespace grpc_core {
class GlobalStatsCollector;
class Http2GlobalStatsCollector;
class Http2StatsCollector;
class HistogramCollector_65536_26_64;
class Histogram_65536_26_64 {
 public:
  static int BucketFor(int value);
  const uint64_t* buckets() const { return buckets_; }
  size_t bucket_count() const { return 26; }
  void Increment(int value) {
    ++buckets_[Histogram_65536_26_64::BucketFor(value)];
  }
  friend Histogram_65536_26_64 operator-(const Histogram_65536_26_64& left, const Histogram_65536_26_64& right);
 private:
  friend class HistogramCollector_65536_26_64;
  uint64_t buckets_[26]{};
};
class HistogramCollector_65536_26_64 {
 public:
  void Increment(int value) {
    buckets_[Histogram_65536_26_64::BucketFor(value)]
        .fetch_add(1, std::memory_order_relaxed);
  }
  void Collect(Histogram_65536_26_64* result) const;
 private:
  std::atomic<uint64_t> buckets_[26]{};
};
class Histogram_16777216_8_8 {
 public:
  static int BucketFor(int value);
  const uint8_t* buckets() const { return buckets_; }
  size_t bucket_count() const { return 8; }
  void Increment(int value) {
    auto& bucket = buckets_[Histogram_16777216_8_8::BucketFor(value)];
    if (GPR_UNLIKELY(bucket == std::numeric_limits<uint8_t>::max())) {
      for (size_t i=0; i<8; ++i) {
        buckets_[i] /= 2;
      }
    }
    ++bucket;
  }
 private:
  uint8_t buckets_[8]{};
};
class HistogramCollector_16777216_20_64;
class Histogram_16777216_20_64 {
 public:
  static int BucketFor(int value);
  const uint64_t* buckets() const { return buckets_; }
  size_t bucket_count() const { return 20; }
  void Increment(int value) {
    ++buckets_[Histogram_16777216_20_64::BucketFor(value)];
  }
  friend Histogram_16777216_20_64 operator-(const Histogram_16777216_20_64& left, const Histogram_16777216_20_64& right);
 private:
  friend class HistogramCollector_16777216_20_64;
  uint64_t buckets_[20]{};
};
class HistogramCollector_16777216_20_64 {
 public:
  void Increment(int value) {
    buckets_[Histogram_16777216_20_64::BucketFor(value)]
        .fetch_add(1, std::memory_order_relaxed);
  }
  void Collect(Histogram_16777216_20_64* result) const;
 private:
  std::atomic<uint64_t> buckets_[20]{};
};
class HistogramCollector_16777216_50_64;
class Histogram_16777216_50_64 {
 public:
  static int BucketFor(int value);
  const uint64_t* buckets() const { return buckets_; }
  size_t bucket_count() const { return 50; }
  void Increment(int value) {
    ++buckets_[Histogram_16777216_50_64::BucketFor(value)];
  }
  friend Histogram_16777216_50_64 operator-(const Histogram_16777216_50_64& left, const Histogram_16777216_50_64& right);
 private:
  friend class HistogramCollector_16777216_50_64;
  uint64_t buckets_[50]{};
};
class HistogramCollector_16777216_50_64 {
 public:
  void Increment(int value) {
    buckets_[Histogram_16777216_50_64::BucketFor(value)]
        .fetch_add(1, std::memory_order_relaxed);
  }
  void Collect(Histogram_16777216_50_64* result) const;
 private:
  std::atomic<uint64_t> buckets_[50]{};
};
struct GlobalStats {
  enum class Counter {
    kClientCallsCreated,
    kClientChannelsCreated,
    COUNT
  };
  enum class Histogram {
    kCallInitialSize,
    COUNT
  };
  GlobalStats();
  static const absl::string_view counter_name[static_cast<int>(Counter::COUNT)];
  static const absl::string_view histogram_name[static_cast<int>(Histogram::COUNT)];
  static const absl::string_view counter_doc[static_cast<int>(Counter::COUNT)];
  static const absl::string_view histogram_doc[static_cast<int>(Histogram::COUNT)];
  union {
    struct {
    uint64_t client_calls_created;
    uint64_t client_channels_created;
    };
    uint64_t counters[static_cast<int>(Counter::COUNT)];
  };
  Histogram_65536_26_64 call_initial_size;
  HistogramView histogram(Histogram which) const;
  std::unique_ptr<GlobalStats> Diff(const GlobalStats& other) const;
};
class GlobalStatsCollector {
 public:
  std::unique_ptr<GlobalStats> Collect() const;
  void IncrementClientCallsCreated() { data_.this_cpu().client_calls_created.fetch_add(1, std::memory_order_relaxed); }
  void IncrementClientChannelsCreated() { data_.this_cpu().client_channels_created.fetch_add(1, std::memory_order_relaxed); }
  void IncrementCallInitialSize(int value) { data_.this_cpu().call_initial_size.Increment(value); }
 private:
  friend class Http2GlobalStatsCollector;
  friend class Http2StatsCollector;
  struct Data {
    std::atomic<uint64_t> client_calls_created{0};
    std::atomic<uint64_t> client_channels_created{0};
    HistogramCollector_65536_26_64 call_initial_size;
  };
  PerCpu<Data> data_{PerCpuOptions().SetCpusPerShard(4).SetMaxShards(32)};
};
inline GlobalStatsCollector& global_stats() {
  return *NoDestructSingleton<GlobalStatsCollector>::Get();
}
struct Http2GlobalStats {
  enum class Counter {
    kHttp2SettingsWrites,
    kHttp2WritesBegun,
    COUNT
  };
  enum class Histogram {
    kHttp2SendMessageSize,
    kHttp2WriteTargetSize,
    COUNT
  };
  Http2GlobalStats();
  static const absl::string_view counter_name[static_cast<int>(Counter::COUNT)];
  static const absl::string_view histogram_name[static_cast<int>(Histogram::COUNT)];
  static const absl::string_view counter_doc[static_cast<int>(Counter::COUNT)];
  static const absl::string_view histogram_doc[static_cast<int>(Histogram::COUNT)];
  union {
    struct {
    uint64_t http2_settings_writes;
    uint64_t http2_writes_begun;
    };
    uint64_t counters[static_cast<int>(Counter::COUNT)];
  };
  Histogram_16777216_20_64 http2_send_message_size;
  Histogram_16777216_50_64 http2_write_target_size;
  HistogramView histogram(Histogram which) const;
  std::unique_ptr<Http2GlobalStats> Diff(const Http2GlobalStats& other) const;
};
class Http2GlobalStatsCollector {
 public:
  std::unique_ptr<Http2GlobalStats> Collect() const;
  void IncrementHttp2SettingsWrites() { data_.this_cpu().http2_settings_writes.fetch_add(1, std::memory_order_relaxed); }
 private:
  void IncrementHttp2WritesBegun() { data_.this_cpu().http2_writes_begun.fetch_add(1, std::memory_order_relaxed); }
 public:
  void IncrementHttp2SendMessageSize(int value) { data_.this_cpu().http2_send_message_size.Increment(value); }
 private:
  void IncrementHttp2WriteTargetSize(int value) { data_.this_cpu().http2_write_target_size.Increment(value); }
  friend class GlobalStatsCollector;
  friend class Http2StatsCollector;
  struct Data {
    std::atomic<uint64_t> http2_settings_writes{0};
    std::atomic<uint64_t> http2_writes_begun{0};
    HistogramCollector_16777216_20_64 http2_send_message_size;
    HistogramCollector_16777216_50_64 http2_write_target_size;
  };
  PerCpu<Data> data_{PerCpuOptions().SetCpusPerShard(4).SetMaxShards(32)};
};
inline Http2GlobalStatsCollector& http2_global_stats() {
  return *NoDestructSingleton<Http2GlobalStatsCollector>::Get();
}
struct Http2Stats {
  enum class Counter {
    kHttp2WritesBegun,
    COUNT
  };
  enum class Histogram {
    kHttp2WriteTargetSize,
    COUNT
  };
  Http2Stats();
  static const absl::string_view counter_name[static_cast<int>(Counter::COUNT)];
  static const absl::string_view histogram_name[static_cast<int>(Histogram::COUNT)];
  static const absl::string_view counter_doc[static_cast<int>(Counter::COUNT)];
  static const absl::string_view histogram_doc[static_cast<int>(Histogram::COUNT)];
  union {
    struct {
    uint64_t http2_writes_begun;
    };
    uint64_t counters[static_cast<int>(Counter::COUNT)];
  };
  Histogram_16777216_8_8 http2_write_target_size;
};
class Http2StatsCollector {
 public:
  const Http2Stats& View() const { return data_; };
  void IncrementHttp2SettingsWrites() { http2_global_stats().IncrementHttp2SettingsWrites(); }
  void IncrementHttp2WritesBegun() { ++data_.http2_writes_begun; http2_global_stats().IncrementHttp2WritesBegun(); }
  void IncrementHttp2SendMessageSize(int value) { http2_global_stats().IncrementHttp2SendMessageSize(value); }
  void IncrementHttp2WriteTargetSize(int value) { data_.http2_write_target_size.Increment(value); http2_global_stats().IncrementHttp2WriteTargetSize(value); }
 private:
  Http2Stats data_;
};
}

#endif // GRPC_SRC_CORE_TELEMETRY_STATS_DATA_H
)";

  static constexpr absl::string_view kExpectedSrcOutput =
      R"(// Copyright 2025 gRPC authors.
// 
// 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.

// Automatically generated by tools/codegen/core/gen_stats_utils.cc

#include <grpc/support/port_platform.h>

#include "src/core/telemetry/stats_data.h"
#include <stdint.h>

namespace grpc_core {
namespace { union DblUint { double dbl; uint64_t uint; }; }
void HistogramCollector_65536_26_64::Collect(Histogram_65536_26_64* result) const {
  for (int i=0; i<26; i++) {
    result->buckets_[i] += buckets_[i].load(std::memory_order_relaxed);
  }
}
Histogram_65536_26_64 operator-(const Histogram_65536_26_64& left, const Histogram_65536_26_64& right) {
  Histogram_65536_26_64 result;
  for (int i=0; i<26; i++) {
    result.buckets_[i] = left.buckets_[i] - right.buckets_[i];
  }
  return result;
}
void HistogramCollector_16777216_20_64::Collect(Histogram_16777216_20_64* result) const {
  for (int i=0; i<20; i++) {
    result->buckets_[i] += buckets_[i].load(std::memory_order_relaxed);
  }
}
Histogram_16777216_20_64 operator-(const Histogram_16777216_20_64& left, const Histogram_16777216_20_64& right) {
  Histogram_16777216_20_64 result;
  for (int i=0; i<20; i++) {
    result.buckets_[i] = left.buckets_[i] - right.buckets_[i];
  }
  return result;
}
void HistogramCollector_16777216_50_64::Collect(Histogram_16777216_50_64* result) const {
  for (int i=0; i<50; i++) {
    result->buckets_[i] += buckets_[i].load(std::memory_order_relaxed);
  }
}
Histogram_16777216_50_64 operator-(const Histogram_16777216_50_64& left, const Histogram_16777216_50_64& right) {
  Histogram_16777216_50_64 result;
  for (int i=0; i<50; i++) {
    result.buckets_[i] = left.buckets_[i] - right.buckets_[i];
  }
  return result;
}
namespace {
const int kStatsTable0[27] = {0,1,2,4,7,11,17,26,40,61,92,139,210,317,478,721,1087,1638,2468,3719,5604,8443,12721,19166,28875,43502,65536};
const uint8_t kStatsTable1[29] = {3,3,4,5,6,6,7,8,9,10,11,11,12,13,14,15,16,16,17,18,19,20,21,21,22,23,24,25,26};
const int kStatsTable2[9] = {0,1,11,119,1275,13656,146259,1566467,16777216};
const uint8_t kStatsTable3[11] = {2,2,3,4,4,5,5,6,6,7,8};
const int kStatsTable4[21] = {0,1,3,8,19,45,106,250,588,1383,3252,7646,17976,42262,99359,233593,549177,1291113,3035402,7136218,16777216};
const uint8_t kStatsTable5[23] = {2,3,3,4,5,6,7,8,8,9,10,11,12,12,13,14,15,16,16,17,18,19,20};
const int kStatsTable6[51] = {0,1,2,3,5,7,10,14,20,28,39,54,75,104,144,200,277,383,530,733,1014,1402,1939,2681,3706,5123,7082,9790,13533,18707,25859,35746,49412,68303,94416,130512,180408,249380,344720,476509,658682,910501,1258592,1739760,2404882,3324285,4595181,6351949,8780340,12137120,16777216};
const uint8_t kStatsTable7[88] = {4,4,5,5,6,6,7,7,8,8,9,9,10,11,11,12,12,13,13,14,14,15,15,16,16,17,18,18,18,19,20,20,21,21,22,22,23,23,24,24,25,25,26,27,27,28,28,29,29,30,30,31,31,32,32,33,33,34,35,35,36,36,37,37,38,38,39,39,40,40,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,49,50,50};
}  // namespace
int Histogram_65536_26_64::BucketFor(int value) {if (value < 3) {
if (value < 0) {
return 0;
} else {
return value;
}
} else {
if (value < 49153) {
DblUint val;
val.dbl = value;
const int bucket = kStatsTable1[((val.uint - 4613937818241073152ull) >> 51)];
return bucket - (value < kStatsTable0[bucket]);
} else {
return 25;
}
}}
int Histogram_16777216_8_8::BucketFor(int value) {if (value < 2) {
if (value < 0) {
return 0;
} else {
return value;
}
} else {
if (value < 2097153) {
DblUint val;
val.dbl = value;
const int bucket = kStatsTable3[((val.uint - 4611686018427387904ull) >> 53)];
return bucket - (value < kStatsTable2[bucket]);
} else {
return 7;
}
}}
int Histogram_16777216_20_64::BucketFor(int value) {if (value < 2) {
if (value < 0) {
return 0;
} else {
return value;
}
} else {
if (value < 8388609) {
DblUint val;
val.dbl = value;
const int bucket = kStatsTable5[((val.uint - 4611686018427387904ull) >> 52)];
return bucket - (value < kStatsTable4[bucket]);
} else {
return 19;
}
}}
int Histogram_16777216_50_64::BucketFor(int value) {if (value < 4) {
if (value < 0) {
return 0;
} else {
return value;
}
} else {
if (value < 14680065) {
DblUint val;
val.dbl = value;
const int bucket = kStatsTable7[((val.uint - 4616189618054758400ull) >> 50)];
return bucket - (value < kStatsTable6[bucket]);
} else {
return 49;
}
}}
const absl::string_view GlobalStats::counter_name[static_cast<int>(Counter::COUNT)] = {
  "client_calls_created",
  "client_channels_created",
};
const absl::string_view GlobalStats::counter_doc[static_cast<int>(Counter::COUNT)] = {
  "Number of client side calls created by this process",
  "Number of client channels created",
};
const absl::string_view GlobalStats::histogram_name[static_cast<int>(Histogram::COUNT)] = {
  "call_initial_size",
};
const absl::string_view GlobalStats::histogram_doc[static_cast<int>(Histogram::COUNT)] = {
  "Initial size of the grpc_call arena created at call start",
};
GlobalStats::GlobalStats() : client_calls_created{0},client_channels_created{0} {}
HistogramView GlobalStats::histogram(Histogram which) const {
  switch (which) {
    default: GPR_UNREACHABLE_CODE(return HistogramView());
    case Histogram::kCallInitialSize:
      return HistogramView{&Histogram_65536_26_64::BucketFor, kStatsTable0, 26, call_initial_size.buckets()};
  }
}
std::unique_ptr<GlobalStats> GlobalStatsCollector::Collect() const {
  auto result = std::make_unique<GlobalStats>();
  for (const auto& data : data_) {
    result->client_calls_created += data.client_calls_created.load(std::memory_order_relaxed);
    result->client_channels_created += data.client_channels_created.load(std::memory_order_relaxed);
    data.call_initial_size.Collect(&result->call_initial_size);
  }
  return result;
}
std::unique_ptr<GlobalStats> GlobalStats::Diff(const GlobalStats& other) const {
  auto result = std::make_unique<GlobalStats>();
  result->client_calls_created = client_calls_created - other.client_calls_created;
  result->client_channels_created = client_channels_created - other.client_channels_created;
  result->call_initial_size = call_initial_size - other.call_initial_size;
  return result;
}
const absl::string_view Http2GlobalStats::counter_name[static_cast<int>(Counter::COUNT)] = {
  "http2_settings_writes",
  "http2_writes_begun",
};
const absl::string_view Http2GlobalStats::counter_doc[static_cast<int>(Counter::COUNT)] = {
  "Number of settings frames sent",
  "Number of HTTP2 writes initiated",
};
const absl::string_view Http2GlobalStats::histogram_name[static_cast<int>(Histogram::COUNT)] = {
  "http2_send_message_size",
  "http2_write_target_size",
};
const absl::string_view Http2GlobalStats::histogram_doc[static_cast<int>(Histogram::COUNT)] = {
  "Size of messages received by HTTP2 transport",
  "Number of bytes targeted for http2 writes",
};
Http2GlobalStats::Http2GlobalStats() : http2_settings_writes{0},http2_writes_begun{0} {}
HistogramView Http2GlobalStats::histogram(Histogram which) const {
  switch (which) {
    default: GPR_UNREACHABLE_CODE(return HistogramView());
    case Histogram::kHttp2SendMessageSize:
      return HistogramView{&Histogram_16777216_20_64::BucketFor, kStatsTable4, 20, http2_send_message_size.buckets()};
    case Histogram::kHttp2WriteTargetSize:
      return HistogramView{&Histogram_16777216_50_64::BucketFor, kStatsTable6, 50, http2_write_target_size.buckets()};
  }
}
std::unique_ptr<Http2GlobalStats> Http2GlobalStatsCollector::Collect() const {
  auto result = std::make_unique<Http2GlobalStats>();
  for (const auto& data : data_) {
    result->http2_settings_writes += data.http2_settings_writes.load(std::memory_order_relaxed);
    result->http2_writes_begun += data.http2_writes_begun.load(std::memory_order_relaxed);
    data.http2_send_message_size.Collect(&result->http2_send_message_size);
    data.http2_write_target_size.Collect(&result->http2_write_target_size);
  }
  return result;
}
std::unique_ptr<Http2GlobalStats> Http2GlobalStats::Diff(const Http2GlobalStats& other) const {
  auto result = std::make_unique<Http2GlobalStats>();
  result->http2_settings_writes = http2_settings_writes - other.http2_settings_writes;
  result->http2_writes_begun = http2_writes_begun - other.http2_writes_begun;
  result->http2_send_message_size = http2_send_message_size - other.http2_send_message_size;
  result->http2_write_target_size = http2_write_target_size - other.http2_write_target_size;
  return result;
}
const absl::string_view Http2Stats::counter_name[static_cast<int>(Counter::COUNT)] = {
  "http2_writes_begun",
};
const absl::string_view Http2Stats::counter_doc[static_cast<int>(Counter::COUNT)] = {
  "Number of HTTP2 writes initiated",
};
const absl::string_view Http2Stats::histogram_name[static_cast<int>(Histogram::COUNT)] = {
  "http2_write_target_size",
};
const absl::string_view Http2Stats::histogram_doc[static_cast<int>(Histogram::COUNT)] = {
  "Number of bytes targeted for http2 writes",
};
Http2Stats::Http2Stats() : http2_writes_begun{0} {}
}  // namespace grpc_core
)";

  auto attrs = ParseStatsFromYamlString(std::string(kTestStatsDataYaml));
  EXPECT_OK(attrs);
  StatsDataGenerator generator(*attrs);
  std::string hdr_output;
  std::string src_output;
  generator.GenStatsDataSrc(src_output);
  generator.GenStatsDataHdr("test/", hdr_output);
  EXPECT_EQ(hdr_output, kExpectedHdrOutput);
  EXPECT_EQ(src_output, kExpectedSrcOutput);
}

}  // namespace
}  // namespace grpc_core

int main(int argc, char** argv) {
  grpc::testing::TestEnvironment env(&argc, argv);
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
