/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * 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 <cstdint>
#include <string>

#include "perfetto/base/status.h"
#include "src/base/test/status_matchers.h"
#include "src/trace_redaction/collect_timeline_events.h"
#include "src/trace_redaction/scrub_process_stats.h"
#include "src/trace_redaction/trace_redaction_framework.h"
#include "src/trace_redaction/trace_redaction_integration_fixture.h"
#include "src/trace_redaction/trace_redactor.h"
#include "test/gtest_and_gmock.h"

#include "protos/perfetto/trace/ps/process_stats.pbzero.h"
#include "protos/perfetto/trace/trace.pbzero.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"

namespace perfetto::trace_redaction {

class ScrubProcessStatsTest : public testing::Test,
                              protected TraceRedactionIntegrationFixure {
 protected:
  void SetUp() override {
    trace_redactor_.emplace_collect<CollectTimelineEvents>();

    auto* scrub = trace_redactor_.emplace_transform<ScrubProcessStats>();
    scrub->emplace_filter<ConnectedToPackage>();

    // Package "com.Unity.com.unity.multiplayer.samples.coop";
    context_.package_uid = 10252;
  }

  // Gets pids from all process_stats messages in the trace (bytes).
  base::FlatSet<int32_t> GetAllPids(const std::string& bytes) const {
    base::FlatSet<int32_t> pids;

    protos::pbzero::Trace::Decoder decoder(bytes);

    for (auto packet = decoder.packet(); packet; ++packet) {
      protos::pbzero::TracePacket::Decoder trace_packet(packet->as_bytes());

      if (!trace_packet.has_process_stats()) {
        continue;
      }

      protos::pbzero::ProcessStats::Decoder process_stats(
          trace_packet.process_stats());

      for (auto process = process_stats.processes(); process; ++process) {
        protos::pbzero::ProcessStats::Process::Decoder p(process->as_bytes());
        PERFETTO_DCHECK(p.has_pid());
        pids.insert(p.pid());
      }
    }

    return pids;
  }

  Context context_;
  TraceRedactor trace_redactor_;
};

// This test is a canary for changes to the test data. If the test data was to
// change, every test in this file would fail.
//
//  SELECT DISTINCT pid
//  FROM process
//  WHERE upid IN (
//    SELECT DISTINCT upid
//    FROM counter
//      JOIN process_counter_track ON counter.track_id=process_counter_track.id
//    WHERE name!='oom_score_adj'
//  )
//  ORDER BY pid
//
//  NOTE: WHERE name!='oom_score_adj' is used because there are two sources for
//  oom_score_adj values and we only want process stats here.
TEST_F(ScrubProcessStatsTest, VerifyTraceStats) {
  base::FlatSet<int32_t> expected = {
      1,     578,   581,   696,   697,   698,   699,   700,   701,   704,
      709,   710,   718,   728,   749,   750,   751,   752,   756,   760,
      761,   762,   873,   874,   892,   1046,  1047,  1073,  1074,  1091,
      1092,  1093,  1101,  1103,  1104,  1105,  1106,  1107,  1110,  1111,
      1112,  1113,  1115,  1116,  1118,  1119,  1120,  1121,  1123,  1124,
      1125,  1126,  1127,  1129,  1130,  1131,  1133,  1140,  1145,  1146,
      1147,  1151,  1159,  1163,  1164,  1165,  1166,  1167,  1168,  1175,
      1177,  1205,  1206,  1235,  1237,  1238,  1248,  1251,  1254,  1255,
      1295,  1296,  1298,  1300,  1301,  1303,  1304,  1312,  1317,  1325,
      1339,  1340,  1363,  1374,  1379,  1383,  1388,  1392,  1408,  1409,
      1410,  1413,  1422,  1426,  1427,  1428,  1429,  1433,  1436,  1448,
      1450,  1451,  1744,  1774,  1781,  1814,  2262,  2268,  2286,  2392,
      2456,  2502,  2510,  2518,  2528,  2569,  3171,  3195,  3262,  3286,
      3310,  3338,  3442,  3955,  4386,  4759,  5935,  6034,  6062,  6167,
      6547,  6573,  6720,  6721,  6725,  6944,  6984,  7105,  7207,  7557,
      7636,  7786,  7874,  7958,  7960,  7967,  15449, 15685, 15697, 16453,
      19683, 21124, 21839, 23150, 23307, 23876, 24317, 25017, 25126, 25450,
      25474, 27271, 30604, 32289,
  };

  auto original = LoadOriginal();
  ASSERT_OK(original) << original.status().c_message();

  auto actual = GetAllPids(*original);

  for (auto pid : expected) {
    ASSERT_TRUE(actual.count(pid))
        << "pid " << pid << " was not found in the trace";
  }

  for (auto pid : actual) {
    ASSERT_TRUE(expected.count(pid))
        << "pid " << pid << " was found in the trace";
  }
}

// Package name: "com.Unity.com.unity.multiplayer.samples.coop"
// Package pid: 7105
TEST_F(ScrubProcessStatsTest, OnlyKeepsStatsForPackage) {
  auto result = Redact(trace_redactor_, &context_);
  ASSERT_OK(result) << result.c_message();

  auto redacted = LoadRedacted();
  ASSERT_OK(redacted) << redacted.status().c_message();

  auto actual = GetAllPids(*redacted);
  ASSERT_EQ(actual.size(), 1u);
  ASSERT_TRUE(actual.count(7105));
}

}  // namespace perfetto::trace_redaction
