// Copyright (c) 2012 The Chromium 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/basictypes.h"
#include "base/stringprintf.h"
#include "base/string_util.h"
#include "base/time.h"
#include "chrome/browser/metrics/metrics_log.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/common/metrics/proto/system_profile.pb.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_pref_service.h"
#include "googleurl/src/gurl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/size.h"
#include "webkit/plugins/webplugininfo.h"

using base::TimeDelta;

namespace {

const char kClientId[] = "bogus client ID";
const int kSessionId = 127;
const int kScreenWidth = 1024;
const int kScreenHeight = 768;
const int kScreenCount = 3;
const base::FieldTrial::NameGroupId kFieldTrialIds[] = {
  {37, 43},
  {13, 47},
  {23, 17}
};

class TestMetricsLog : public MetricsLog {
 public:
  TestMetricsLog(const std::string& client_id, int session_id)
      : MetricsLog(client_id, session_id) {
    browser::RegisterLocalState(&prefs_);

#if defined(OS_CHROMEOS)
    prefs_.SetInteger(prefs::kStabilityChildProcessCrashCount, 10);
    prefs_.SetInteger(prefs::kStabilityOtherUserCrashCount, 11);
    prefs_.SetInteger(prefs::kStabilityKernelCrashCount, 12);
    prefs_.SetInteger(prefs::kStabilitySystemUncleanShutdownCount, 13);
#endif  // OS_CHROMEOS
  }
  virtual ~TestMetricsLog() {}

  virtual PrefService* GetPrefService() OVERRIDE {
    return &prefs_;
  }

  const metrics::SystemProfileProto& system_profile() const {
    return uma_proto()->system_profile();
  }

 private:
  virtual std::string GetCurrentTimeString() OVERRIDE {
    return std::string();
  }

  virtual void GetFieldTrialIds(
      std::vector<base::FieldTrial::NameGroupId>* field_trial_ids) const
      OVERRIDE {
    ASSERT_TRUE(field_trial_ids->empty());

    for (size_t i = 0; i < arraysize(kFieldTrialIds); ++i) {
      field_trial_ids->push_back(kFieldTrialIds[i]);
    }
  }

  virtual gfx::Size GetScreenSize() const OVERRIDE {
    return gfx::Size(kScreenWidth, kScreenHeight);
  }

  virtual int GetScreenCount() const OVERRIDE {
    return kScreenCount;
  }

  TestingPrefService prefs_;

  DISALLOW_COPY_AND_ASSIGN(TestMetricsLog);
};

}  // namespace

class MetricsLogTest : public testing::Test {
 protected:
  void TestRecordEnvironment(bool proto_only) {
    TestMetricsLog log(kClientId, kSessionId);

    std::vector<webkit::WebPluginInfo> plugins;
    if (proto_only)
      log.RecordEnvironmentProto(plugins);
    else
      log.RecordEnvironment(plugins, NULL);

    const metrics::SystemProfileProto& system_profile = log.system_profile();
    ASSERT_EQ(arraysize(kFieldTrialIds),
              static_cast<size_t>(system_profile.field_trial_size()));
    for (size_t i = 0; i < arraysize(kFieldTrialIds); ++i) {
      const metrics::SystemProfileProto::FieldTrial& field_trial =
          system_profile.field_trial(i);
      EXPECT_EQ(kFieldTrialIds[i].name, field_trial.name_id());
      EXPECT_EQ(kFieldTrialIds[i].group, field_trial.group_id());
    }

    // TODO(isherman): Verify other data written into the protobuf as a result
    // of this call.
  }
};

TEST_F(MetricsLogTest, RecordEnvironment) {
  // Test that recording the environment works via both of the public methods
  // RecordEnvironment() and RecordEnvironmentProto().
  TestRecordEnvironment(false);
  TestRecordEnvironment(true);
}

#if defined(OS_CHROMEOS)
TEST_F(MetricsLogTest, ChromeOSStabilityData) {
  TestMetricsLog log(kClientId, kSessionId);

  // Expect 3 warnings about not yet being able to send the
  // Chrome OS stability stats.
  std::vector<webkit::WebPluginInfo> plugins;
  PrefService* prefs = log.GetPrefService();
  log.WriteStabilityElement(plugins, prefs);
  log.CloseLog();

  int size = log.GetEncodedLogSizeXml();
  ASSERT_GT(size, 0);

  EXPECT_EQ(0, prefs->GetInteger(prefs::kStabilityChildProcessCrashCount));
  EXPECT_EQ(0, prefs->GetInteger(prefs::kStabilityOtherUserCrashCount));
  EXPECT_EQ(0, prefs->GetInteger(prefs::kStabilityKernelCrashCount));
  EXPECT_EQ(0, prefs->GetInteger(prefs::kStabilitySystemUncleanShutdownCount));

  std::string encoded;
  // Leave room for the NUL terminator.
  bool encoding_result = log.GetEncodedLogXml(
      WriteInto(&encoded, size + 1), size);
  ASSERT_TRUE(encoding_result);

  // Check that we can find childprocesscrashcount, but not
  // any of the ChromeOS ones that we are not emitting until log
  // servers can handle them.
  EXPECT_NE(std::string::npos,
            encoded.find(" childprocesscrashcount=\"10\""));
  EXPECT_EQ(std::string::npos,
            encoded.find(" otherusercrashcount="));
  EXPECT_EQ(std::string::npos,
            encoded.find(" kernelcrashcount="));
  EXPECT_EQ(std::string::npos,
            encoded.find(" systemuncleanshutdowns="));
}
#endif  // OS_CHROMEOS