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

#include <string>

#include "base/logging.h"

#include "compat/string.h"
#include "compat/test.h"
#include "file_utils.h"
#include "perf_stat_parser.h"
#include "scoped_temp_path.h"

namespace quipper {

namespace {

const char kInvalidInput[] =
    "PerfDataProto\n"
    "Attr: Even Count BuildID\n"
    "1.234 1234.5 time seconds\n";

const char kSmallInput[] =
    "/uncore/reads/: 711983 1002113142 1002111143\n"
    "/uncore/writes/: 140867 1002113864 1002113864\n"
    "    \n";  // Test parsing an empty line

// From a Peppy running:
// 'perf stat -v -a -e cycles -e L1-dcache-loads -e bus-cycles -e r02c4 --'
// ' sleep 2'
const char kFullInput[] =
    "cycles: 19062079 4002390292 4002381587\n"
    "L1-dcache-loads: 2081375 4002517554 4002511235\n"
    "bus-cycles: 2259169 4002527446 4002523976\n"
    "r02c4: 201584 4002518485 4002518485\n"
    "\n"
    " Performance counter stats for 'system wide':\n"
    "\n"
    "          19062079      cycles                    [100.00%]\n"
    "           2081375      L1-dcache-loads           [100.00%]\n"
    "           2259169      bus-cycles                [100.00%]\n"
    "            201584      r02c4   \n"
    "\n"
    "       2.001402976 seconds time elapsed\n"
    "\n";

}  // namespace

TEST(PerfStatParserTest, InvalidStringReturnsFalse) {
  PerfStatProto proto;
  ASSERT_FALSE(ParsePerfStatOutputToProto(kInvalidInput, &proto));
}

TEST(PerfStatParserTest, ValidInputParsesCorrectly) {
  // Test string input
  PerfStatProto proto;
  ASSERT_TRUE(ParsePerfStatOutputToProto(kSmallInput, &proto));

  ASSERT_EQ(proto.line_size(), 2);

  const auto& line1 = proto.line(0);
  EXPECT_EQ("/uncore/reads/", line1.event_name());
  EXPECT_EQ(711983, line1.count());
  EXPECT_FALSE(line1.has_time_ms());

  const auto& line2 = proto.line(1);
  EXPECT_EQ("/uncore/writes/", line2.event_name());
  EXPECT_EQ(140867, line2.count());
  EXPECT_FALSE(line2.has_time_ms());

  // Test file input
  ScopedTempFile input;
  ASSERT_FALSE(input.path().empty());
  ASSERT_TRUE(BufferToFile(input.path(), string(kSmallInput)));
  PerfStatProto proto2;
  ASSERT_TRUE(ParsePerfStatFileToProto(input.path(), &proto2));

  ASSERT_EQ(proto2.line_size(), 2);

  const auto& line3 = proto2.line(0);
  EXPECT_EQ("/uncore/reads/", line3.event_name());
  EXPECT_EQ(711983, line3.count());
  EXPECT_FALSE(line3.has_time_ms());

  const auto& line4 = proto2.line(1);
  EXPECT_EQ("/uncore/writes/", line4.event_name());
  EXPECT_EQ(140867, line4.count());
  EXPECT_FALSE(line4.has_time_ms());
}

TEST(PerfStatParserTest, ValidFullStringParsesCorrectly) {
  PerfStatProto proto;
  ASSERT_TRUE(ParsePerfStatOutputToProto(kFullInput, &proto));

  ASSERT_EQ(proto.line_size(), 4);

  const auto& line1 = proto.line(0);
  EXPECT_EQ("cycles", line1.event_name());
  EXPECT_EQ(19062079, line1.count());
  EXPECT_EQ(2001, line1.time_ms());

  const auto& line2 = proto.line(1);
  EXPECT_EQ("L1-dcache-loads", line2.event_name());
  EXPECT_EQ(2081375, line2.count());
  EXPECT_EQ(2001, line2.time_ms());

  const auto& line3 = proto.line(2);
  EXPECT_EQ("bus-cycles", line3.event_name());
  EXPECT_EQ(2259169, line3.count());
  EXPECT_EQ(2001, line3.time_ms());

  const auto& line4 = proto.line(3);
  EXPECT_EQ("r02c4", line4.event_name());
  EXPECT_EQ(201584, line4.count());
  EXPECT_EQ(2001, line4.time_ms());
}

TEST(PerfStatParserTest, NonexistentFileReturnsFalse) {
  PerfStatProto proto;
  ASSERT_FALSE(ParsePerfStatFileToProto("/dev/null/nope/nope.txt", &proto));
}

TEST(PerfStatParserTest, ParseTime) {
  uint64_t out;
  EXPECT_TRUE(SecondsStringToMillisecondsUint64("123.456", &out));
  EXPECT_EQ(123456, out);
  EXPECT_TRUE(SecondsStringToMillisecondsUint64("2.0014", &out));
  EXPECT_EQ(2001, out);
  EXPECT_TRUE(SecondsStringToMillisecondsUint64("0.0027", &out));
  EXPECT_EQ(3, out);
  EXPECT_FALSE(SecondsStringToMillisecondsUint64("-10.0027", &out));
  EXPECT_FALSE(SecondsStringToMillisecondsUint64("string", &out));
  EXPECT_FALSE(SecondsStringToMillisecondsUint64("string.string", &out));
  EXPECT_FALSE(SecondsStringToMillisecondsUint64("23.string", &out));
  EXPECT_FALSE(SecondsStringToMillisecondsUint64("string.23456", &out));
  EXPECT_FALSE(SecondsStringToMillisecondsUint64("123.234.456", &out));
}

}  // namespace quipper