/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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 <folly/logging/Init.h>

#include <folly/logging/LogConfigParser.h>
#include <folly/logging/LoggerDB.h>
#include <folly/logging/test/ConfigHelpers.h>
#include <folly/portability/GFlags.h>
#include <folly/portability/GTest.h>
#include <folly/test/TestUtils.h>

using folly::initLogging;
using folly::LoggerDB;
using folly::parseLogConfig;

namespace {
// A counter to help confirm that our getBaseLoggingConfigCalled() was invoked
// rather than the default implementation that folly exports as a weak symbol.
unsigned int getBaseLoggingConfigCalled;
} // namespace

namespace folly {

const char* getBaseLoggingConfig() {
  ++getBaseLoggingConfigCalled;
  return "folly=INFO; default:stream=stdout";
}

} // namespace folly

TEST(Init, checkConfig) {
  // Before we call initLogging(), the LoggerDB will have the default
  // configuration provided by initializeLoggerDB().
  auto initialConfig = folly::LoggerDB::get().getConfig();
  EXPECT_EQ(0, getBaseLoggingConfigCalled);
  EXPECT_EQ(
      parseLogConfig(".:=INFO:default; "
                     "default=stream:stream=stderr,async=false"),
      LoggerDB::get().getConfig());

  // Call initLogging()
  // Make sure it merges the supplied config argument with our custom
  // base configuration.
  initLogging(".=ERROR,folly.logging=DBG7");
  EXPECT_EQ(1, getBaseLoggingConfigCalled);
  EXPECT_EQ(
      parseLogConfig(".:=ERROR:default,folly=INFO:,folly.logging=DBG7:; "
                     "default=stream:stream=stdout,async=false"),
      LoggerDB::get().getConfig());

  // Test calling initLogging() with bad configuration strings, and
  // configured such that it should throw an exception on error rather than
  // exiting.
  //
  // Note that it is okay to call initLogging() multiple times (we already
  // called it successfully once above), but this isn't really something to
  // expect most callers to want to do.
  EXPECT_THROW_RE(
      initLogging(".=BOGUSLEVEL"),
      folly::LogConfigParseError,
      R"(invalid log level "BOGUSLEVEL")");
  EXPECT_THROW_RE(
      initLogging(".=ERR:undefined_handler"),
      std::invalid_argument,
      R"(unknown log handler "undefined_handler")");
}

// We use our custom main() to ensure that folly::initLogging() has
// not been called yet when we start running the tests.
int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  gflags::ParseCommandLineFlags(&argc, &argv, /* remove_flags = */ true);

  return RUN_ALL_TESTS();
}
