// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/arc/tracing/arc_tracing_model.h"

#include <set>
#include <string>

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/trace_event/common/trace_event_common.h"
#include "chrome/browser/ash/arc/tracing/arc_tracing_event.h"
#include "chrome/browser/ash/arc/tracing/arc_tracing_event_matcher.h"
#include "chrome/browser/ash/arc/tracing/arc_tracing_graphics_model.h"
#include "chrome/browser/ash/arc/tracing/present_frames_tracer.h"
#include "chrome/common/chrome_paths.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/zlib/google/compression_utils.h"

namespace arc {

namespace {

using GraphicsEvents = ArcTracingGraphicsModel::BufferEvents;
using GraphicsEventType = ArcTracingGraphicsModel::EventType;

constexpr char kAcquireBufferQuery[] =
    "android:onMessageReceived/android:handleMessageInvalidate/"
    "android:latchBuffer/android:updateTexImage/android:acquireBuffer";

constexpr char kAttachSurfaceQueury[] =
    "toplevel:OnLibevent/exo:Surface::Attach";

constexpr char kTestEvent[] =
    R"({"pid":4640,"tid":4641,"id":"1234","ts":14241877057,"ph":"X","cat":"exo",
        "name":"Surface::Attach",
        "args":{"buffer_id":"0x7f9f5110690","app_id":"org.chromium.arc.6"},
        "dur":10,"tdur":9,"tts":1216670360})";
constexpr char kTestEvent2[] =
    R"({"args":{},"cat":"exo","id2":{"local":"0x2df1a3130a10"},
        "name":"BufferInUse","ph":"F","pid":17518,"tid":17518,
        "ts":7654346859.0,"tts":93686437})";

constexpr char kAppId[] = "app_id";
constexpr char kAppIdValue[] = "org.chromium.arc.6";
constexpr char kBufferId[] = "buffer_id";
constexpr char kBufferIdBad[] = "_buffer_id";
constexpr char kBufferIdValue[] = "0x7f9f5110690";
constexpr char kBufferIdValueBad[] = "_0x7f9f5110690";
constexpr char kBufferInUse[] = "BufferInUse";
constexpr char kDefault[] = "default";
constexpr char kExo[] = "exo";
constexpr char kExoBad[] = "_exo";
constexpr char kPhaseF = 'F';
constexpr char kPhaseP = 'P';
constexpr char kPhaseX = 'X';
constexpr char kSurfaceAttach[] = "Surface::Attach";
constexpr char kSurfaceAttachBad[] = "_Surface::Attach";

// Validates that events have increasing timestamp, and all events have allowed
// transitions from the previous state.
bool ValidateCpuEvents(const CpuEvents& cpu_events) {
  if (cpu_events.empty()) {
    return false;
  }

  CpuEvents cpu_events_reconstructed;
  for (const auto& cpu_event : cpu_events) {
    if (!AddCpuEvent(&cpu_events_reconstructed, cpu_event.timestamp,
                     cpu_event.type, cpu_event.tid)) {
      return false;
    }
  }

  return cpu_events_reconstructed == cpu_events;
}

// Validates that events have increasing timestamp, have only allowed types and
// each type is found at least once.
bool ValidateGrahpicsEvents(const GraphicsEvents& events,
                            const std::set<GraphicsEventType>& allowed_types) {
  bool ok = true;
  uint64_t previous_timestamp = 0;
  std::set<GraphicsEventType> used_types;

  for (const auto& event : events) {
    if (event.timestamp < previous_timestamp) {
      LOG(ERROR) << "Timestamp sequence broken: " << event.timestamp << " vs "
                 << previous_timestamp << ".";
      ok = false;
    }
    previous_timestamp = event.timestamp;
    if (!allowed_types.count(event.type)) {
      LOG(ERROR) << "Unexpected event type " << event.type << ".";
      ok = false;
    }
    used_types.insert(event.type);
  }

  for (const auto& allowed_type : allowed_types) {
    if (!used_types.count(allowed_type)) {
      LOG(ERROR) << "Required event type " << allowed_type
                 << " << is not found.";
      ok = false;
    }
  }

  return ok;
}

std::unique_ptr<ArcTracingGraphicsModel> LoadGraphicsModel(
    const std::string& name) {
  base::FilePath base_path;
  base::PathService::Get(chrome::DIR_TEST_DATA, &base_path);
  const base::FilePath tracing_path =
      base_path.Append("arc_overview_tracing").Append(name);
  std::string json_data;
  base::ReadFileToString(tracing_path, &json_data);
  DCHECK(!json_data.empty());
  std::unique_ptr<ArcTracingGraphicsModel> model =
      std::make_unique<ArcTracingGraphicsModel>();
  if (!model->LoadFromJson(json_data)) {
    return nullptr;
  }
  return model;
}

// Ensures |model1| is equal to |model2|.
void EnsureGraphicsModelsEqual(const ArcTracingGraphicsModel& model1,
                               const ArcTracingGraphicsModel& model2) {
  EXPECT_EQ(model1.chrome_top_level(), model2.chrome_top_level());
  EXPECT_EQ(model1.view_buffers(), model2.view_buffers());
  EXPECT_EQ(model1.system_model(), model2.system_model());
  EXPECT_EQ(model1.duration(), model2.duration());
  EXPECT_EQ(model1.app_title(), model2.app_title());
  EXPECT_EQ(model1.app_icon_png(), model2.app_icon_png());
  EXPECT_EQ(model1.timestamp(), model2.timestamp());
  EXPECT_EQ(model1.platform(), model2.platform());
}

}  // namespace

class ArcTracingModelTest : public testing::Test {
 public:
  ArcTracingModelTest() = default;

  ArcTracingModelTest(const ArcTracingModelTest&) = delete;
  ArcTracingModelTest& operator=(const ArcTracingModelTest&) = delete;

  ~ArcTracingModelTest() override = default;
};

// TopLevel test is performed on the data collected from the real test device.
// It tests that model can be built from the provided data and queries work.
TEST_F(ArcTracingModelTest, TopLevel) {
  base::FilePath base_path;
  base::PathService::Get(chrome::DIR_TEST_DATA, &base_path);
  const base::FilePath tracing_path =
      base_path.Append("arc_overview_tracing").Append("trace.dat.gz");

  std::string tracing_data_compressed;
  ASSERT_TRUE(base::ReadFileToString(tracing_path, &tracing_data_compressed));

  std::string tracing_data;
  ASSERT_TRUE(
      compression::GzipUncompress(tracing_data_compressed, &tracing_data));

  ArcTracingModel model;
  ASSERT_TRUE(model.Build(tracing_data));

  // 4 CPU cores.
  EXPECT_EQ(4U, model.system_model().all_cpu_events().size());
  for (const auto& cpu_events : model.system_model().all_cpu_events()) {
    EXPECT_TRUE(ValidateCpuEvents(cpu_events));
  }

  // Perform several well-known queries.
  EXPECT_FALSE(model.Select(kAcquireBufferQuery).empty());
  EXPECT_FALSE(model.Select(kAttachSurfaceQueury).empty());

  std::stringstream ss;
  model.Dump(ss);
  EXPECT_FALSE(ss.str().empty());

  // Continue in this test to avoid heavy calculations for building base model.
  // Make sure we can create graphics model.
  ArcTracingGraphicsModel graphics_model;
  PresentFramesTracer present_frames;
  present_frames.AddCommit(base::TimeTicks::FromUptimeMillis(42));
  ASSERT_TRUE(graphics_model.Build(model, present_frames));

  EXPECT_EQ(1U, graphics_model.chrome_top_level().buffer_events().size());
  for (const auto& chrome_top_level_band :
       graphics_model.chrome_top_level().buffer_events()) {
    EXPECT_TRUE(ValidateGrahpicsEvents(chrome_top_level_band, {}));
  }
  EXPECT_FALSE(graphics_model.view_buffers().empty());
  for (const auto& view : graphics_model.view_buffers()) {
    // At least one buffer.
    EXPECT_GT(view.first.task_id, 0);
    EXPECT_NE(std::string(), view.first.activity);
    EXPECT_FALSE(view.second.buffer_events().empty());
    for (const auto& buffer : view.second.buffer_events()) {
      EXPECT_TRUE(ValidateGrahpicsEvents(
          buffer, {GraphicsEventType::kExoSurfaceCommit}));
    }
  }

  // Note, CPU events in |graphics_model| are normalized by timestamp. So they
  // are not equal and we cannot do direct comparison. Also first VSYNC event
  // trimming may drop some events.
  ASSERT_LE(graphics_model.system_model().all_cpu_events().size(),
            model.system_model().all_cpu_events().size());
  EXPECT_EQ(graphics_model.system_model().thread_map(),
            model.system_model().thread_map());
  for (size_t i = 0; i < graphics_model.system_model().all_cpu_events().size();
       ++i) {
    EXPECT_LE(graphics_model.system_model().all_cpu_events()[i].size(),
              model.system_model().all_cpu_events()[i].size());
  }

  EXPECT_GT(graphics_model.duration(), 0U);

  // Serialize and restore;
  const std::string graphics_model_data = graphics_model.SerializeToJson();
  EXPECT_FALSE(graphics_model_data.empty());

  ArcTracingGraphicsModel graphics_model_loaded;
  EXPECT_TRUE(graphics_model_loaded.LoadFromJson(graphics_model_data));

  // Models should match.
  EnsureGraphicsModelsEqual(graphics_model, graphics_model_loaded);
}

TEST_F(ArcTracingModelTest, FrameDisplayed) {
  std::string tracing_data = R"({"traceEvents":[
{"args":{},"cat":"benchmark,viz,disabled-by-default-display.framedisplayed","name":"Display::FrameDisplayed","ph":"I","pid":18869,"s":"t","tid":18912,"ts":4966534893},
{"args":{},"cat":"benchmark,viz,disabled-by-default-display.framedisplayed","name":"Display::FrameDisplayed","ph":"I","pid":18869,"s":"t","tid":18912,"ts":4966551562},
{"args":{},"cat":"benchmark,viz,disabled-by-default-display.framedisplayed","name":"Display::FrameDisplayed","ph":"I","pid":18869,"s":"t","tid":18912,"ts":4966568229}
],"systemTraceEvents":""})";

  ArcTracingModel model;
  ASSERT_TRUE(model.Build(tracing_data));

  ArcTracingGraphicsModel graphics_model;
  ASSERT_TRUE(graphics_model.Build(model, {}));

  EXPECT_TRUE(ValidateGrahpicsEvents(
      graphics_model.chrome_top_level().buffer_events()[0],
      {GraphicsEventType::kChromeOSSwapDone}));
}

// Validates basic system event timestamp processing
TEST_F(ArcTracingModelTest, SystemTraceEventTimestampParsing) {
  {
    std::string tracing_data =
        "{\"traceEvents\":[],\"systemTraceEvents\":\""
        // clang-format off
        "  surfaceflinger-9772  [000] ...0 80156.539255: tracing_mark_write: B|15|acquireBuffer\n"
        // clang-format on
        "\"}";

    ArcTracingModel model;
    ASSERT_TRUE(model.Build(tracing_data));

    const ArcTracingModel::TracingEventPtrs events =
        model.Select("android:acquireBuffer");
    ASSERT_EQ(1u, events.size());
    EXPECT_EQ(15, events[0]->GetPid());
    EXPECT_EQ("android", events[0]->GetCategory());
    EXPECT_EQ("acquireBuffer", events[0]->GetName());
    EXPECT_EQ('X', events[0]->GetPhase());
    EXPECT_EQ(80156539255UL, events[0]->GetTimestamp());
    EXPECT_EQ(0U, events[0]->GetDuration());
  }

  {
    // Too few digits after the timestamp decimal point should be an error
    std::string bad_tracing_data_1 =
        "{\"traceEvents\":[],\"systemTraceEvents\":\""
        // clang-format off
        "  surfaceflinger-9772  [000] ...0 999.12345: tracing_mark_write: B|15|acquireBuffer\n"
        // clang-format on
        "\"}";
    ArcTracingModel model;
    EXPECT_FALSE(model.Build(bad_tracing_data_1));
  }

  {
    // Too many digits after the timestamp decimal point should be an error
    std::string bad_tracing_data_2 =
        "{\"traceEvents\":[],\"systemTraceEvents\":\""
        // clang-format off
        "  surfaceflinger-9772  [000] ...0 999.1234567: tracing_mark_write: B|15|acquireBuffer\n"
        // clang-format on
        "\"}";
    ArcTracingModel model;
    EXPECT_FALSE(model.Build(bad_tracing_data_2));
  }
}

namespace {
bool TestSystemTraceEventExtraFlags(const char* flag_field) {
  std::string tracing_data =
      base::StrCat({"{\"traceEvents\":[],\"systemTraceEvents\":\""
                    // clang-format off
      "  surfaceflinger-9772  [000] ", flag_field, " 987.123456: tracing_mark_write: B|51|acquireBuffer\n"
                    // clang-format on
                    "\"}"});
  ArcTracingModel model;
  if (!model.Build(tracing_data)) {
    return false;
  }

  const ArcTracingModel::TracingEventPtrs events =
      model.Select("android:acquireBuffer");
  CHECK_EQ(1u, events.size());
  EXPECT_EQ(51, events[0]->GetPid());
  EXPECT_EQ("android", events[0]->GetCategory());
  EXPECT_EQ("acquireBuffer", events[0]->GetName());
  EXPECT_EQ('X', events[0]->GetPhase());
  EXPECT_EQ(987123456UL, events[0]->GetTimestamp());
  EXPECT_EQ(0U, events[0]->GetDuration());

  return true;
}
}  // namespace

TEST_F(ArcTracingModelTest, SystemTraceEventMoreFlags) {
  // Kernel v5.15 has 5 flags after the CPU#, whereas v5.10 has only 4. Verify
  // we don't fail for 5 flags, or more in case future kernels get them.

  // Empty flags field - two spaces between CPU# and timestamp fields.
  EXPECT_FALSE(TestSystemTraceEventExtraFlags(""));

  EXPECT_FALSE(TestSystemTraceEventExtraFlags("0"));
  EXPECT_FALSE(TestSystemTraceEventExtraFlags(".0"));
  EXPECT_FALSE(TestSystemTraceEventExtraFlags("..0"));
  EXPECT_TRUE(TestSystemTraceEventExtraFlags("...0"));
  EXPECT_TRUE(TestSystemTraceEventExtraFlags("....0"));
  EXPECT_TRUE(TestSystemTraceEventExtraFlags(".....0"));
  EXPECT_TRUE(TestSystemTraceEventExtraFlags("......0"));
}

TEST_F(ArcTracingModelTest, SystemTraceEventCpuEventProcessing) {
  std::string tracing_data =
      "{\"traceEvents\":[],\"systemTraceEvents\":\""
      // clang-format off
      "          <idle>-0     [000] d..0 123.000001: cpu_idle: state=0 cpu_id=0\n"
      "          <idle>-0     [000] dn.0 123.000002: cpu_idle: state=4294967295 cpu_id=0\n"
      "          <idle>-0     [000] dnh3 123.000003: sched_wakeup: comm=foo pid=15821 prio=115 target_cpu=000\n"
      "          <idle>-0     [000] d..3 123.000004: sched_switch: prev_comm=bar prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=baz next_pid=15821 next_prio=115\n"
      // clang-format on
      "\"}";

  ArcTracingModel model;
  ASSERT_TRUE(model.Build(tracing_data));

  const AllCpuEvents& cpu_events = model.system_model().all_cpu_events();
  ASSERT_EQ(1u, cpu_events.size());
  CpuEvents cpu0_events = cpu_events[0];
  ASSERT_EQ(4u, cpu0_events.size());

  EXPECT_EQ(123000001UL, cpu0_events[0].timestamp);
  EXPECT_EQ(ArcCpuEvent::Type::kIdleIn, cpu0_events[0].type);
  EXPECT_EQ(0u, cpu0_events[0].tid);

  EXPECT_EQ(123000002UL, cpu0_events[1].timestamp);
  EXPECT_EQ(ArcCpuEvent::Type::kIdleOut, cpu0_events[1].type);
  EXPECT_EQ(0u, cpu0_events[1].tid);

  EXPECT_EQ(123000003UL, cpu0_events[2].timestamp);
  EXPECT_EQ(ArcCpuEvent::Type::kWakeUp, cpu0_events[2].type);
  EXPECT_EQ(15821u, cpu0_events[2].tid);

  EXPECT_EQ(123000004UL, cpu0_events[3].timestamp);
  EXPECT_EQ(ArcCpuEvent::Type::kActive, cpu0_events[3].type);
  EXPECT_EQ(15821u, cpu0_events[3].tid);
}

TEST_F(ArcTracingModelTest, Event) {
  const ArcTracingEvent event(std::move(
      base::JSONReader::Read(kTestEvent, base::JSON_PARSE_CHROMIUM_EXTENSIONS)
          ->GetDict()));

  EXPECT_EQ(4640, event.GetPid());
  EXPECT_EQ(4641, event.GetTid());
  EXPECT_EQ("1234", event.GetId());
  EXPECT_EQ(kExo, event.GetCategory());
  EXPECT_EQ(kSurfaceAttach, event.GetName());
  EXPECT_EQ(kPhaseX, event.GetPhase());
  EXPECT_EQ(14241877057UL, event.GetTimestamp());
  EXPECT_EQ(10U, event.GetDuration());
  EXPECT_EQ(14241877067UL, event.GetEndTimestamp());
  EXPECT_NE(nullptr, event.GetDictionary());
  EXPECT_EQ(kBufferIdValue, event.GetArgAsString(kBufferId, std::string()));
  EXPECT_EQ(kDefault, event.GetArgAsString(kBufferIdBad, kDefault));
}

TEST_F(ArcTracingModelTest, EventId2) {
  const ArcTracingEvent event(std::move(
      base::JSONReader::Read(kTestEvent2, base::JSON_PARSE_CHROMIUM_EXTENSIONS)
          ->GetDict()));

  EXPECT_EQ(17518, event.GetPid());
  EXPECT_EQ(17518, event.GetTid());
  EXPECT_EQ("0x2df1a3130a10", event.GetId());
  EXPECT_EQ(kExo, event.GetCategory());
  EXPECT_EQ(kBufferInUse, event.GetName());
  EXPECT_EQ(kPhaseF, event.GetPhase());
  EXPECT_EQ(7654346859UL, event.GetTimestamp());
  EXPECT_EQ(0U, event.GetDuration());
  EXPECT_EQ(7654346859UL, event.GetEndTimestamp());
  EXPECT_NE(nullptr, event.GetDictionary());
}

TEST_F(ArcTracingModelTest, EventClassification) {
  const ArcTracingEvent event(std::move(
      base::JSONReader::Read(kTestEvent, base::JSON_PARSE_CHROMIUM_EXTENSIONS)
          ->GetDict()));

  ArcTracingEvent event_before(std::move(
      base::JSONReader::Read(kTestEvent, base::JSON_PARSE_CHROMIUM_EXTENSIONS)
          ->GetDict()));
  event_before.SetTimestamp(event.GetTimestamp() - event.GetDuration());
  EXPECT_EQ(ArcTracingEvent::Position::kBefore,
            event.ClassifyPositionOf(event_before));

  ArcTracingEvent event_after(std::move(
      base::JSONReader::Read(kTestEvent, base::JSON_PARSE_CHROMIUM_EXTENSIONS)
          ->GetDict()));
  event_after.SetTimestamp(event.GetTimestamp() + event.GetDuration());
  EXPECT_EQ(ArcTracingEvent::Position::kAfter,
            event.ClassifyPositionOf(event_after));

  ArcTracingEvent event_inside(std::move(
      base::JSONReader::Read(kTestEvent, base::JSON_PARSE_CHROMIUM_EXTENSIONS)
          ->GetDict()));
  event_inside.SetTimestamp(event.GetTimestamp() + 1);
  event_inside.SetDuration(event.GetDuration() - 2);
  EXPECT_EQ(ArcTracingEvent::Position::kInside,
            event.ClassifyPositionOf(event_inside));
  EXPECT_EQ(ArcTracingEvent::Position::kInside,
            event.ClassifyPositionOf(event));

  ArcTracingEvent event_overlap(std::move(
      base::JSONReader::Read(kTestEvent, base::JSON_PARSE_CHROMIUM_EXTENSIONS)
          ->GetDict()));
  event_overlap.SetTimestamp(event.GetTimestamp() + 1);
  EXPECT_EQ(ArcTracingEvent::Position::kOverlap,
            event.ClassifyPositionOf(event_overlap));
  event_overlap.SetTimestamp(event.GetTimestamp());
  event_overlap.SetDuration(event.GetDuration() + 1);
  EXPECT_EQ(ArcTracingEvent::Position::kOverlap,
            event.ClassifyPositionOf(event_overlap));
  event_overlap.SetTimestamp(event.GetTimestamp() - 1);
  event_overlap.SetDuration(event.GetDuration() + 2);
  EXPECT_EQ(ArcTracingEvent::Position::kOverlap,
            event.ClassifyPositionOf(event_overlap));
}

TEST_F(ArcTracingModelTest, EventAppendChild) {
  ArcTracingEvent event(std::move(
      base::JSONReader::Read(kTestEvent, base::JSON_PARSE_CHROMIUM_EXTENSIONS)
          ->GetDict()));

  // Impossible to append the even that is bigger than target.
  std::unique_ptr<ArcTracingEvent> event_overlap =
      std::make_unique<ArcTracingEvent>(
          std::move(base::JSONReader::Read(kTestEvent,
                                           base::JSON_PARSE_CHROMIUM_EXTENSIONS)
                        ->GetDict()));
  event_overlap->SetTimestamp(event.GetTimestamp() + 1);
  EXPECT_FALSE(event.AppendChild(std::move(event_overlap)));

  std::unique_ptr<ArcTracingEvent> event1 = std::make_unique<ArcTracingEvent>(
      std::move(base::JSONReader::Read(kTestEvent,
                                       base::JSON_PARSE_CHROMIUM_EXTENSIONS)
                    ->GetDict()));
  event1->SetTimestamp(event.GetTimestamp() + 4);
  event1->SetDuration(2);
  EXPECT_TRUE(event.AppendChild(std::move(event1)));
  EXPECT_EQ(1U, event.children().size());

  // Impossible to append the event that is before last child.
  std::unique_ptr<ArcTracingEvent> event2 = std::make_unique<ArcTracingEvent>(
      std::move(base::JSONReader::Read(kTestEvent,
                                       base::JSON_PARSE_CHROMIUM_EXTENSIONS)
                    ->GetDict()));
  event2->SetTimestamp(event.GetTimestamp());
  event2->SetDuration(2);
  EXPECT_FALSE(event.AppendChild(std::move(event2)));
  EXPECT_EQ(1U, event.children().size());

  // Append child to child
  std::unique_ptr<ArcTracingEvent> event3 = std::make_unique<ArcTracingEvent>(
      std::move(base::JSONReader::Read(kTestEvent,
                                       base::JSON_PARSE_CHROMIUM_EXTENSIONS)
                    ->GetDict()));
  event3->SetTimestamp(event.GetTimestamp() + 5);
  event3->SetDuration(1);
  EXPECT_TRUE(event.AppendChild(std::move(event3)));
  ASSERT_EQ(1U, event.children().size());
  EXPECT_EQ(1U, event.children()[0].get()->children().size());

  // Append next immediate child.
  std::unique_ptr<ArcTracingEvent> event4 = std::make_unique<ArcTracingEvent>(
      std::move(base::JSONReader::Read(kTestEvent,
                                       base::JSON_PARSE_CHROMIUM_EXTENSIONS)
                    ->GetDict()));
  event4->SetTimestamp(event.GetTimestamp() + 6);
  event4->SetDuration(2);
  EXPECT_TRUE(event.AppendChild(std::move(event4)));
  EXPECT_EQ(2U, event.children().size());
}

TEST_F(ArcTracingModelTest, EventMatcher) {
  const ArcTracingEvent event(std::move(
      base::JSONReader::Read(kTestEvent, base::JSON_PARSE_CHROMIUM_EXTENSIONS)
          ->GetDict()));
  // Nothing is specified. It matches any event.
  EXPECT_TRUE(ArcTracingEventMatcher().Match(event));

  // Phase
  EXPECT_TRUE(ArcTracingEventMatcher().SetPhase(kPhaseX).Match(event));
  EXPECT_FALSE(ArcTracingEventMatcher().SetPhase(kPhaseP).Match(event));

  // Category
  EXPECT_TRUE(ArcTracingEventMatcher().SetCategory(kExo).Match(event));
  EXPECT_FALSE(ArcTracingEventMatcher().SetCategory(kExoBad).Match(event));

  // Name
  EXPECT_TRUE(ArcTracingEventMatcher().SetName(kSurfaceAttach).Match(event));
  EXPECT_FALSE(
      ArcTracingEventMatcher().SetName(kSurfaceAttachBad).Match(event));

  // Arguments
  EXPECT_TRUE(ArcTracingEventMatcher()
                  .AddArgument(kBufferId, kBufferIdValue)
                  .Match(event));
  EXPECT_TRUE(ArcTracingEventMatcher()
                  .AddArgument(kBufferId, kBufferIdValue)
                  .AddArgument(kAppId, kAppIdValue)
                  .Match(event));
  EXPECT_FALSE(ArcTracingEventMatcher()
                   .AddArgument(kBufferIdBad, kBufferIdValue)
                   .Match(event));
  EXPECT_FALSE(ArcTracingEventMatcher()
                   .AddArgument(kBufferId, kBufferIdValueBad)
                   .Match(event));

  // String query
  EXPECT_TRUE(
      ArcTracingEventMatcher("exo:Surface::Attach(buffer_id=0x7f9f5110690)")
          .Match(event));
  EXPECT_FALSE(
      ArcTracingEventMatcher("_exo:_Surface::Attach(buffer_id=_0x7f9f5110690)")
          .Match(event));
}

TEST_F(ArcTracingModelTest, TimeMinMax) {
  // Model contains events with timestamps 100000..100004 inclusively.
  base::FilePath base_path;
  base::PathService::Get(chrome::DIR_TEST_DATA, &base_path);
  const base::FilePath tracing_path =
      base_path.Append("arc_overview_tracing").Append("trace_time.dat");

  std::string tracing_data;
  ASSERT_TRUE(base::ReadFileToString(tracing_path, &tracing_data));

  ArcTracingModel model_without_time_filter;
  EXPECT_TRUE(model_without_time_filter.Build(tracing_data));
  EXPECT_EQ(5U, model_without_time_filter.GetRoots().size());

  ArcTracingModel model_with_time_filter;
  model_with_time_filter.SetMinMaxTime(100001L, 100003L);
  EXPECT_TRUE(model_with_time_filter.Build(tracing_data));
  ASSERT_EQ(2U, model_with_time_filter.GetRoots().size());
  EXPECT_EQ(100001UL, model_with_time_filter.GetRoots()[0]->GetTimestamp());
  EXPECT_EQ(100002UL, model_with_time_filter.GetRoots()[1]->GetTimestamp());

  ArcTracingModel model_with_empty_time_filter;
  model_with_empty_time_filter.SetMinMaxTime(99999L, 100000L);
  EXPECT_TRUE(model_with_empty_time_filter.Build(tracing_data));
  EXPECT_EQ(0U, model_with_empty_time_filter.GetRoots().size());
}

TEST_F(ArcTracingModelTest, GraphicsModelLoadSerialize) {
  std::unique_ptr<ArcTracingGraphicsModel> model =
      LoadGraphicsModel("gm_good.json");
  ASSERT_TRUE(model);
  EXPECT_EQ("CrOS 12642.0.0 (Official Build) dev-channel eve",
            model->platform());
  EXPECT_EQ("Play Store", model->app_title());
  EXPECT_FALSE(model->app_icon_png().empty());
  EXPECT_EQ(base::Time::FromMillisecondsSinceUnixEpoch(1572898642036L),
            model->timestamp());
  EXPECT_EQ(1000U, model->duration());

  ArcTracingGraphicsModel test_model;
  EXPECT_TRUE(test_model.LoadFromJson(model->SerializeToJson()));
  EnsureGraphicsModelsEqual(*model, test_model);

  EXPECT_TRUE(LoadGraphicsModel("gm_good.json"));
  EXPECT_FALSE(LoadGraphicsModel("gm_bad_no_view_buffers.json"));
  EXPECT_FALSE(LoadGraphicsModel("gm_bad_no_view_desc.json"));
  EXPECT_FALSE(LoadGraphicsModel("gm_bad_wrong_timestamp.json"));
}

TEST_F(ArcTracingModelTest, AsynchronousSystemEvents) {
  base::FilePath base_path;
  base::PathService::Get(chrome::DIR_TEST_DATA, &base_path);
  const base::FilePath tracing_path = base_path.Append("arc_overview_tracing")
                                          .Append("trace_async_events.json");
  std::string tracing_data;
  base::ReadFileToString(tracing_path, &tracing_data);
  DCHECK(!tracing_data.empty());

  ArcTracingModel model;
  ASSERT_TRUE(model.Build(tracing_data));

  const ArcTracingModel::TracingEventPtrs group1 = model.GetGroupEvents("1");
  const ArcTracingModel::TracingEventPtrs group2 = model.GetGroupEvents("2");

  constexpr char kAsync1[] = "async1";
  constexpr char kAsync2[] = "async2";

  ASSERT_EQ(2U, group1.size());
  EXPECT_EQ(kAsync1, group1[0]->GetName());
  EXPECT_EQ(kAsync1, group1[1]->GetName());
  EXPECT_EQ("1", group1[0]->GetId());
  EXPECT_EQ(group1[0]->GetId(), group1[1]->GetId());
  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_BEGIN, group1[0]->GetPhase());
  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_END, group1[1]->GetPhase());
  EXPECT_EQ(1100000UL, group1[0]->GetTimestamp());
  EXPECT_EQ(1300000UL, group1[1]->GetTimestamp());

  ASSERT_EQ(2U, group2.size());
  EXPECT_EQ(kAsync2, group2[0]->GetName());
  EXPECT_EQ(kAsync2, group2[1]->GetName());
  EXPECT_EQ("2", group2[0]->GetId());
  EXPECT_EQ(group2[0]->GetId(), group2[1]->GetId());
  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_BEGIN, group2[0]->GetPhase());
  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_END, group2[1]->GetPhase());
  EXPECT_EQ(1200000UL, group2[0]->GetTimestamp());
  EXPECT_EQ(1400000UL, group2[1]->GetTimestamp());
}

}  // namespace arc
