/*
 * Copyright (C) 2020 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.
 */

#define LOG_TAG "global_state_test_tag"

#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android/log.h>

#include <gtest/gtest.h>

TEST(liblog_global_state, libbase_logs_with_libbase_SetLogger) {
  using namespace android::base;
  bool message_seen = false;
  LogSeverity expected_severity = WARNING;
  std::string expected_file = Basename(__FILE__);
  unsigned int expected_line;
  std::string expected_message = "libbase test message";

  auto LoggerFunction = [&](LogId log_id, LogSeverity severity, const char* tag, const char* file,
                            unsigned int line, const char* message) {
    message_seen = true;
    EXPECT_EQ(DEFAULT, log_id);
    EXPECT_EQ(expected_severity, severity);
    EXPECT_STREQ(LOG_TAG, tag);
    EXPECT_EQ(expected_file, file);
    EXPECT_EQ(expected_line, line);
    EXPECT_EQ(expected_message, message);
  };

  SetLogger(LoggerFunction);

  expected_line = __LINE__ + 1;
  LOG(expected_severity) << expected_message;
  EXPECT_TRUE(message_seen);
}

TEST(liblog_global_state, libbase_logs_with_liblog_set_logger) {
  using namespace android::base;
  // These must be static since they're used by the liblog logger function, which only accepts
  // lambdas without captures.  The items used by the libbase logger are explicitly not static, to
  // ensure that lambdas with captures do work there.
  static bool message_seen = false;
  static std::string expected_file = Basename(__FILE__);
  static unsigned int expected_line;
  static std::string expected_message = "libbase test message";

  auto liblog_logger_function = [](const struct __android_log_message* log_message) {
    message_seen = true;
    EXPECT_EQ(sizeof(__android_log_message), log_message->struct_size);
    EXPECT_EQ(LOG_ID_DEFAULT, log_message->buffer_id);
    EXPECT_EQ(ANDROID_LOG_WARN, log_message->priority);
    EXPECT_STREQ(LOG_TAG, log_message->tag);
    EXPECT_EQ(expected_file, log_message->file);
    EXPECT_EQ(expected_line, log_message->line);
    EXPECT_EQ(expected_message, log_message->message);
  };

  __android_log_set_logger(liblog_logger_function);

  expected_line = __LINE__ + 1;
  LOG(WARNING) << expected_message;
  EXPECT_TRUE(message_seen);
}

TEST(liblog_global_state, liblog_logs_with_libbase_SetLogger) {
  using namespace android::base;
  bool message_seen = false;
  std::string expected_message = "libbase test message";

  auto LoggerFunction = [&](LogId log_id, LogSeverity severity, const char* tag, const char* file,
                            unsigned int line, const char* message) {
    message_seen = true;
    EXPECT_EQ(MAIN, log_id);
    EXPECT_EQ(WARNING, severity);
    EXPECT_STREQ(LOG_TAG, tag);
    EXPECT_EQ(nullptr, file);
    EXPECT_EQ(0U, line);
    EXPECT_EQ(expected_message, message);
  };

  SetLogger(LoggerFunction);

  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, LOG_TAG, expected_message.c_str());
  EXPECT_TRUE(message_seen);
  message_seen = false;
}

TEST(liblog_global_state, liblog_logs_with_liblog_set_logger) {
  using namespace android::base;
  // These must be static since they're used by the liblog logger function, which only accepts
  // lambdas without captures.  The items used by the libbase logger are explicitly not static, to
  // ensure that lambdas with captures do work there.
  static bool message_seen = false;
  static int expected_buffer_id = LOG_ID_MAIN;
  static int expected_priority = ANDROID_LOG_WARN;
  static std::string expected_message = "libbase test message";

  auto liblog_logger_function = [](const struct __android_log_message* log_message) {
    message_seen = true;
    EXPECT_EQ(sizeof(__android_log_message), log_message->struct_size);
    EXPECT_EQ(expected_buffer_id, log_message->buffer_id);
    EXPECT_EQ(expected_priority, log_message->priority);
    EXPECT_STREQ(LOG_TAG, log_message->tag);
    EXPECT_STREQ(nullptr, log_message->file);
    EXPECT_EQ(0U, log_message->line);
    EXPECT_EQ(expected_message, log_message->message);
  };

  __android_log_set_logger(liblog_logger_function);

  __android_log_buf_write(expected_buffer_id, expected_priority, LOG_TAG, expected_message.c_str());
  EXPECT_TRUE(message_seen);
}

TEST(liblog_global_state, SetAborter_with_liblog) {
  using namespace android::base;

  std::string expected_message = "libbase test message";
  static bool message_seen = false;
  auto aborter_function = [&](const char* message) {
    message_seen = true;
    EXPECT_EQ(expected_message, message);
  };

  SetAborter(aborter_function);
  LOG(FATAL) << expected_message;
  EXPECT_TRUE(message_seen);
  message_seen = false;

  static std::string expected_message_static = "libbase test message";
  auto liblog_aborter_function = [](const char* message) {
    message_seen = true;
    EXPECT_EQ(expected_message_static, message);
  };
  __android_log_set_aborter(liblog_aborter_function);
  LOG(FATAL) << expected_message_static;
  EXPECT_TRUE(message_seen);
  message_seen = false;
}

TEST(liblog_global_state, is_loggable_both_default) {
  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
}

TEST(liblog_global_state, is_loggable_minimum_log_priority_only) {
  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));

  EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));

  EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));

  EXPECT_EQ(android::base::WARNING, android::base::SetMinimumLogSeverity(android::base::DEBUG));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));

  EXPECT_EQ(android::base::DEBUG, android::base::SetMinimumLogSeverity(android::base::WARNING));
  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
}

TEST(liblog_global_state, is_loggable_tag_log_priority_only) {
#ifdef __ANDROID__
  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));

  auto log_tag_property = std::string("log.tag.") + LOG_TAG;
  android::base::SetProperty(log_tag_property, "d");
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));

  android::base::SetProperty(log_tag_property, "w");
  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));

  android::base::SetProperty(log_tag_property, "");
#else
  GTEST_SKIP() << "No log tag properties on host";
#endif
}

TEST(liblog_global_state, is_loggable_both_set) {
#ifdef __ANDROID__
  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));

  // When both a tag and a minimum priority are set, we use the lower value of the two.

  // tag = warning, minimum_priority = debug, expect 'debug'
  auto log_tag_property = std::string("log.tag.") + LOG_TAG;
  android::base::SetProperty(log_tag_property, "w");
  EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));

  // tag = warning, minimum_priority = warning, expect 'warning'
  EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));

  // tag = debug, minimum_priority = warning, expect 'debug'
  android::base::SetProperty(log_tag_property, "d");
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));

  // tag = debug, minimum_priority = debug, expect 'debug'
  EXPECT_EQ(ANDROID_LOG_WARN, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));

  android::base::SetProperty(log_tag_property, "");
#else
  GTEST_SKIP() << "No log tag properties on host";
#endif
}
