Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/libwebrtc/rtc_base/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 18 kB image not shown  

Quelle  logging_unittest.cc

  Sprache: C
 

/*
 *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */


#include "rtc_base/logging.h"

#if RTC_LOG_ENABLED()

#include <stdlib.h>

#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <memory>
#include <string>
#include <utility>

#include "absl/cleanup/cleanup.h"
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "api/location.h"
#include "api/task_queue/task_queue_base.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtc_base/checks.h"
#include "rtc_base/platform_thread.h"
#include "rtc_base/thread.h"
#include "system_wrappers/include/clock.h"
#include "test/gmock.h"
#include "test/gtest.h"

namespace webrtc {

namespace {

using ::testing::HasSubstr;
using ::testing::StartsWith;

#if defined(WEBRTC_WIN)
constexpr char kFakeFilePath[] = "some\\path\\myfile.cc";
#else
constexpr char kFakeFilePath[] = "some/path/myfile.cc";
#endif

}  // namespace

class LogSinkImpl : public LogSink {
 public:
  explicit LogSinkImpl(std::string* log_data) : log_data_(log_data) {}

  template <typename P>
  explicit LogSinkImpl(P* p) {}

 private:
  void OnLogMessage(const std::string& message) override {
    OnLogMessage(absl::string_view(message));
  }
  void OnLogMessage(absl::string_view message) override {
    log_data_->append(message.begin(), message.end());
  }
  std::string* const log_data_;
};

class LogMessageForTesting : public LogMessage {
 public:
  LogMessageForTesting(const char* file,
                       int line,
                       LoggingSeverity sev,
                       LogErrorContext err_ctx = ERRCTX_NONE,
                       int err = 0)
      : LogMessage(file, line, sev, err_ctx, err) {}

  const std::string& get_extra() const { return extra_; }
#if defined(WEBRTC_ANDROID)
  const char* get_tag() const { return log_line_.tag().data(); }
#endif

  // Returns the contents of the internal log stream.
  // Note that parts of the stream won't (as is) be available until *after* the
  // dtor of the parent class has run. So, as is, this only represents a
  // partially built stream.
  std::string GetPrintStream() {
    RTC_DCHECK(!is_finished_);
    is_finished_ = true;
    FinishPrintStream();
    return print_stream_.Release();
  }

 private:
  bool is_finished_ = false;
};

// Test basic logging operation. We should get the INFO log but not the VERBOSE.
// We should restore the correct global state at the end.
TEST(LogTest, SingleStream) {
  int sev = LogMessage::GetLogToStream(nullptr);

  std::string str;
  LogSinkImpl stream(&str);
  LogMessage::AddLogToStream(&stream, LS_INFO);
  EXPECT_EQ(LS_INFO, LogMessage::GetLogToStream(&stream));

  RTC_LOG(LS_INFO) << "INFO";
  RTC_LOG(LS_VERBOSE) << "VERBOSE";
  EXPECT_THAT(str, HasSubstr("INFO"));
  EXPECT_THAT(str, Not(HasSubstr("VERBOSE")));

  int i = 1;
  long l = 2l;
  long long ll = 3ll;

  unsigned int u = 4u;
  unsigned long ul = 5ul;
  unsigned long long ull = 6ull;

  std::string s1 = "char*";
  std::string s2 = "std::string";
  std::string s3 = "absl::stringview";
  const char* null_string = nullptr;
  void* p = reinterpret_cast<void*>(0xabcd);

  // Log all supported types(except doubles/floats) as a sanity-check.
  RTC_LOG(LS_INFO) << "|" << i << "|" << l << "|" << ll << "|" << u << "|" << ul
                   << "|" << ull << "|" << s1.c_str() << "|" << s2 << "|"
                   << absl::string_view(s3) << "|" << p << "|" << null_string
                   << "|";

  // Signed integers
  EXPECT_THAT(str, HasSubstr("|1|"));
  EXPECT_THAT(str, HasSubstr("|2|"));
  EXPECT_THAT(str, HasSubstr("|3|"));

  // Unsigned integers
  EXPECT_THAT(str, HasSubstr("|4|"));
  EXPECT_THAT(str, HasSubstr("|5|"));
  EXPECT_THAT(str, HasSubstr("|6|"));

  // Strings
  EXPECT_THAT(str, HasSubstr("|char*|"));
  EXPECT_THAT(str, HasSubstr("|std::string|"));
  EXPECT_THAT(str, HasSubstr("|absl::stringview|"));

  // void*
  EXPECT_THAT(str, HasSubstr("|abcd|"));

  // null char*
  EXPECT_THAT(str, HasSubstr("|(null)|"));

  LogMessage::RemoveLogToStream(&stream);
  EXPECT_EQ(LS_NONE, LogMessage::GetLogToStream(&stream));
  EXPECT_EQ(sev, LogMessage::GetLogToStream(nullptr));
}

TEST(LogTest, LogIfLogIfConditionIsTrue) {
  std::string str;
  LogSinkImpl stream(&str);
  LogMessage::AddLogToStream(&stream, LS_INFO);

  RTC_LOG_IF(LS_INFO, true) << "Hello";
  EXPECT_THAT(str, HasSubstr("Hello"));

  LogMessage::RemoveLogToStream(&stream);
}

TEST(LogTest, LogIfDontLogIfConditionIsFalse) {
  std::string str;
  LogSinkImpl stream(&str);
  LogMessage::AddLogToStream(&stream, LS_INFO);

  RTC_LOG_IF(LS_INFO, false) << "Hello";
  EXPECT_THAT(str, Not(HasSubstr("Hello")));

  LogMessage::RemoveLogToStream(&stream);
}

TEST(LogTest, LogIfFLogIfConditionIsTrue) {
  std::string str;
  LogSinkImpl stream(&str);
  LogMessage::AddLogToStream(&stream, LS_INFO);

  RTC_LOG_IF_F(LS_INFO, true) << "Hello";
  EXPECT_THAT(str, HasSubstr(__FUNCTION__));
  EXPECT_THAT(str, HasSubstr("Hello"));

  LogMessage::RemoveLogToStream(&stream);
}

TEST(LogTest, LogIfFDontLogIfConditionIsFalse) {
  std::string str;
  LogSinkImpl stream(&str);
  LogMessage::AddLogToStream(&stream, LS_INFO);

  RTC_LOG_IF_F(LS_INFO, false) << "Not";
  EXPECT_THAT(str, Not(HasSubstr(__FUNCTION__)));
  EXPECT_THAT(str, Not(HasSubstr("Not")));

  LogMessage::RemoveLogToStream(&stream);
}

// Test using multiple log streams. The INFO stream should get the INFO message,
// the VERBOSE stream should get the INFO and the VERBOSE.
// We should restore the correct global state at the end.
TEST(LogTest, MultipleStreams) {
  int sev = LogMessage::GetLogToStream(nullptr);

  std::string str1, str2;
  LogSinkImpl stream1(&str1), stream2(&str2);
  LogMessage::AddLogToStream(&stream1, LS_INFO);
  LogMessage::AddLogToStream(&stream2, LS_VERBOSE);
  EXPECT_EQ(LS_INFO, LogMessage::GetLogToStream(&stream1));
  EXPECT_EQ(LS_VERBOSE, LogMessage::GetLogToStream(&stream2));

  RTC_LOG(LS_INFO) << "INFO";
  RTC_LOG(LS_VERBOSE) << "VERBOSE";

  EXPECT_NE(std::string::npos, str1.find("INFO"));
  EXPECT_EQ(std::string::npos, str1.find("VERBOSE"));
  EXPECT_NE(std::string::npos, str2.find("INFO"));
  EXPECT_NE(std::string::npos, str2.find("VERBOSE"));

  LogMessage::RemoveLogToStream(&stream2);
  LogMessage::RemoveLogToStream(&stream1);
  EXPECT_EQ(LS_NONE, LogMessage::GetLogToStream(&stream2));
  EXPECT_EQ(LS_NONE, LogMessage::GetLogToStream(&stream1));

  EXPECT_EQ(sev, LogMessage::GetLogToStream(nullptr));
}

class LogThread {
 public:
  void Start() {
    thread_ = PlatformThread::SpawnJoinable(
        [] { RTC_LOG(LS_VERBOSE) << "RTC_LOG"; }, "LogThread");
  }

 private:
  PlatformThread thread_;
};

// Ensure we don't crash when adding/removing streams while threads are going.
// We should restore the correct global state at the end.
TEST(LogTest, MultipleThreads) {
  int sev = LogMessage::GetLogToStream(nullptr);

  LogThread thread1, thread2, thread3;
  thread1.Start();
  thread2.Start();
  thread3.Start();

  std::string s1, s2, s3;
  LogSinkImpl stream1(&s1), stream2(&s2), stream3(&s3);
  for (int i = 0; i < 1000; ++i) {
    LogMessage::AddLogToStream(&stream1, LS_WARNING);
    LogMessage::AddLogToStream(&stream2, LS_INFO);
    LogMessage::AddLogToStream(&stream3, LS_VERBOSE);
    LogMessage::RemoveLogToStream(&stream1);
    LogMessage::RemoveLogToStream(&stream2);
    LogMessage::RemoveLogToStream(&stream3);
  }

  EXPECT_EQ(sev, LogMessage::GetLogToStream(nullptr));
}

TEST(LogTest, WallClockStartTime) {
  uint32_t time = LogMessage::WallClockStartTime();
  // Expect the time to be in a sensible range, e.g. > 2012-01-01.
  EXPECT_GT(time, 1325376000u);
}

TEST(LogTest, CheckExtraErrorField) {
  LogMessageForTesting log_msg(kFakeFilePath, 100, LS_WARNING, ERRCTX_ERRNO,
                               0xD);
  log_msg.stream() << "This gets added at dtor time";

  EXPECT_THAT(log_msg.get_extra(), StartsWith("[0x0000000D]"));
}

TEST(LogTest, CheckFilePathParsed) {
  std::string str;
  LogSinkImpl stream(&str);
  LogMessage::AddLogToStream(&stream, LS_INFO);
  EXPECT_EQ(LS_INFO, LogMessage::GetLogToStream(&stream));
#if defined(WEBRTC_ANDROID)
  const char* tag = nullptr;
#endif
  {
    LogMessageForTesting log_msg(kFakeFilePath, 100, LS_INFO);
    log_msg.stream() << "<- Does this look right?";
#if defined(WEBRTC_ANDROID)
    tag = log_msg.get_tag();
#endif
  }

#if defined(WEBRTC_ANDROID)
  EXPECT_NE(nullptr, strstr(tag, "myfile.cc"));
  EXPECT_THAT(str, HasSubstr("100"));
#else
  EXPECT_THAT(str, HasSubstr("(myfile.cc:100)"));
#endif
  LogMessage::RemoveLogToStream(&stream);
}

#if defined(WEBRTC_ANDROID)
TEST(LogTest, CheckTagAddedToStringInDefaultOnLogMessageAndroid) {
  std::string str;
  LogSinkImpl stream(&str);
  LogMessage::AddLogToStream(&stream, LS_INFO);
  EXPECT_EQ(LS_INFO, LogMessage::GetLogToStream(&stream));

  RTC_LOG_TAG(LS_INFO, "my_tag") << "INFO";
  EXPECT_THAT(str, HasSubstr("INFO"));
  EXPECT_THAT(str, HasSubstr("my_tag"));
}
#endif

// Test the time required to write 1000 80-character logs to a string.
TEST(LogTest, Perf) {
  // This test should probably be turned into a benchmark.
  Clock& clock = *Clock::GetRealTimeClock();
  std::string str;
  LogSinkImpl stream(&str);
  LogMessage::AddLogToStream(&stream, LS_VERBOSE);

  const std::string message(80'X');
  {
    LogMessageForTesting sanity_check_msg(__FILE__, __LINE__, LS_VERBOSE);
  }

  // We now know how many bytes the logging framework will tag onto every msg.
  const size_t logging_overhead = str.size();
  // Reset the stream to 0 size.
  str.clear();
  str.reserve(120000);
  static const int kRepetitions = 1000;

  Timestamp start = clock.CurrentTime();
  for (int i = 0; i < kRepetitions; ++i) {
    LogMessageForTesting(__FILE__, __LINE__, LS_VERBOSE).stream() << message;
  }
  Timestamp finish = clock.CurrentTime();

  LogMessage::RemoveLogToStream(&stream);

  EXPECT_EQ(str.size(), (message.size() + logging_overhead) * kRepetitions);
  RTC_LOG(LS_INFO) << "Total log time: " << (finish - start)
                   << " total bytes logged: " << str.size();
}

TEST(LogTest, EnumsAreSupported) {
  enum class TestEnum { kValue0 = 0, kValue1 = 1 };
  std::string str;
  LogSinkImpl stream(&str);
  LogMessage::AddLogToStream(&stream, LS_INFO);
  RTC_LOG(LS_INFO) << "[" << TestEnum::kValue0 << "]";
  EXPECT_THAT(str, HasSubstr("[0]"));
  EXPECT_THAT(str, Not(HasSubstr("[1]")));
  RTC_LOG(LS_INFO) << "[" << TestEnum::kValue1 << "]";
  EXPECT_THAT(str, HasSubstr("[1]"));
  LogMessage::RemoveLogToStream(&stream);
}

TEST(LogTest, NoopSeverityDoesNotRunStringFormatting) {
  if (!LogMessage::IsNoop(LS_VERBOSE)) {
    RTC_LOG(LS_WARNING) << "Skipping test since verbose logging is turned on.";
    return;
  }
  bool was_called = false;
  auto cb = [&was_called]() {
    was_called = true;
    return "This could be an expensive callback.";
  };
  RTC_LOG(LS_VERBOSE) << "This should not be logged: " << cb();
  EXPECT_FALSE(was_called);
}

struct StructWithStringfy {
  template <typename Sink>
  friend void AbslStringify(Sink& sink, const StructWithStringfy& /*self*/) {
    sink.Append("absl-stringify");
  }
};

TEST(LogTest, UseAbslStringForCustomTypes) {
  std::string str;
  LogSinkImpl stream(&str);
  LogMessage::AddLogToStream(&stream, LS_INFO);
  StructWithStringfy t;

  RTC_LOG(LS_INFO) << t;

  EXPECT_THAT(str, HasSubstr("absl-stringify"));

  LogMessage::RemoveLogToStream(&stream);
}

enum class TestEnumStringify { kValue0 = 0, kValue1 = 1 };

template <typename Sink>
void AbslStringify(Sink& sink, TestEnumStringify value) {
  switch (value) {
    case TestEnumStringify::kValue0:
      sink.Append("kValue0");
      break;
    case TestEnumStringify::kValue1:
      sink.Append("kValue1");
      break;
  }
}

TEST(LogTest, EnumSupportsAbslStringify) {
  std::string str;
  LogSinkImpl stream(&str);
  LogMessage::AddLogToStream(&stream, LS_INFO);
  RTC_LOG(LS_INFO) << "[" << TestEnumStringify::kValue0 << "]";
  EXPECT_THAT(str, HasSubstr("[kValue0]"));
  EXPECT_THAT(str, Not(HasSubstr("[kValue1]")));
  RTC_LOG(LS_INFO) << "[" << TestEnumStringify::kValue1 << "]";
  EXPECT_THAT(str, HasSubstr("[kValue1]"));
  LogMessage::RemoveLogToStream(&stream);
}

#if !defined(WEBRTC_CHROMIUM_BUILD)
// Logging initialization tests require isolation because they modify global
// state that can only be set once per process. We use EXPECT_EXIT to run
// them in a forked child process.

#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) && !defined(WEBRTC_IOS)
TEST(LogTest, ExplicitInitialization) {
  GTEST_FLAG_SET(death_test_style, "threadsafe");
#if defined(WEBRTC_WIN)
  _putenv_s("WEBRTC_TEST_SKIP_LOGGING_INIT""1");
#else
  setenv("WEBRTC_TEST_SKIP_LOGGING_INIT""1"1);
#endif
  EXPECT_EXIT(
      {
        LoggingConfig config;
        config.set_min_severity(LS_WARNING);
        config.set_log_prefix("TEST_PREFIX: ");
        bool success = InitializeLogging(std::move(config));
        if (!success) {
          exit(1);
        }

        if (GetLoggingConfig().min_severity() != LS_WARNING) {
          exit(2);
        }

        RTC_LOG(LS_WARNING) << "Test message";
        exit(0);
      },
      ::testing::ExitedWithCode(0), "TEST_PREFIX: .*Test message");
#if defined(WEBRTC_WIN)
  _putenv_s("WEBRTC_TEST_SKIP_LOGGING_INIT""");
#else
  unsetenv("WEBRTC_TEST_SKIP_LOGGING_INIT");
#endif
}

TEST(LogTest, ImplicitInitialization) {
  GTEST_FLAG_SET(death_test_style, "threadsafe");
  EXPECT_EXIT(
      {
        RTC_LOG(LS_WARNING) << "Trigger implicit init";
        LoggingConfig config;
        if (InitializeLogging(std::move(config))) {
          exit(1);
        }
        exit(0);
      },
      ::testing::ExitedWithCode(0), "");
}

TEST(LogTest, DoubleExplicitInitialization) {
  GTEST_FLAG_SET(death_test_style, "threadsafe");
#if defined(WEBRTC_WIN)
  _putenv_s("WEBRTC_TEST_SKIP_LOGGING_INIT""1");
#else
  setenv("WEBRTC_TEST_SKIP_LOGGING_INIT""1"1);
#endif
  EXPECT_EXIT(
      {
        LoggingConfig config1;
        if (!InitializeLogging(std::move(config1))) {
          exit(1);
        }

        LoggingConfig config2;
        if (InitializeLogging(std::move(config2))) {
          exit(2);
        }

        exit(0);
      },
      ::testing::ExitedWithCode(0), "");
#if defined(WEBRTC_WIN)
  _putenv_s("WEBRTC_TEST_SKIP_LOGGING_INIT""");
#else
  unsetenv("WEBRTC_TEST_SKIP_LOGGING_INIT");
#endif
}

TEST(LogTest, InitializeLoggingTransfersQueueName) {
  GTEST_FLAG_SET(death_test_style, "threadsafe");
#if defined(WEBRTC_WIN)
  _putenv_s("WEBRTC_TEST_SKIP_LOGGING_INIT""1");
#else
  setenv("WEBRTC_TEST_SKIP_LOGGING_INIT""1"1);
#endif
  EXPECT_EXIT(
      {
        LoggingConfig config;
        config.set_log_queue_name(true);

        struct CustomSink : public LogSink {
          void OnLogMessage(const LogLineRef& line) override {
            queue_name = std::string(line.queue_name());
          }
          void OnLogMessage(const std::string& message) override {}
          void OnLogMessage(absl::string_view message) override {}
          std::string queue_name;
        };

        auto sink = std::make_unique<CustomSink>();
        CustomSink* sink_ptr = sink.get();
        config.AddSink(std::move(sink));

        if (!InitializeLogging(std::move(config))) {
          exit(2);
        }

        std::unique_ptr<Thread> thread = Thread::Create();
        thread->SetName("TestQueue", nullptr);
        thread->Start();

        thread->BlockingCall([&]() { RTC_LOG(LS_INFO) << "Hello"; });

        if (sink_ptr->queue_name != "TestQueue") {
          exit(1);
        }
        exit(0);
      },
      ::testing::ExitedWithCode(0), "");
#if defined(WEBRTC_WIN)
  _putenv_s("WEBRTC_TEST_SKIP_LOGGING_INIT""");
#else
  unsetenv("WEBRTC_TEST_SKIP_LOGGING_INIT");
#endif
}

#endif  // GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) &&
        // !defined(WEBRTC_IOS)

TEST(LogTest, LogQueueNameFromThread) {
  std::string str;
  LogSinkImpl stream(&str);
  LogMessage::AddLogToStream(&stream, LS_INFO);
  LogMessage::SetLogQueueNames(true);

  std::unique_ptr<Thread> thread = Thread::Create();
  thread->SetName("TName", nullptr);
  thread->Start();
  thread->BlockingCall([&]() { RTC_LOG(LS_INFO) << "Hello"; });

  EXPECT_THAT(str, HasSubstr("[TName]"));

  LogMessage::SetLogQueueNames(false);  // Reset
  LogMessage::RemoveLogToStream(&stream);
}

class LogTestWithParam : public testing::TestWithParam<bool> {};

TEST_P(LogTestWithParam, LogQueueNameFromTaskQueueOverridingThread) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
  bool prev_log_threads = LogMessage::LogThreads(GetParam());
#pragma clang diagnostic pop
  bool prev_log_queue_names = LogMessage::SetLogQueueNames(true);

  std::string str;
  LogSinkImpl stream(&str);
  LogMessage::AddLogToStream(&stream, LS_INFO);
  absl::Cleanup cleanup = [&] {
    LogMessage::RemoveLogToStream(&stream);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    LogMessage::LogThreads(prev_log_threads);
#pragma clang diagnostic pop
    LogMessage::SetLogQueueNames(prev_log_queue_names);
  };

  std::unique_ptr<Thread> thread = Thread::Create();
  thread->SetName("TName", nullptr);
  thread->Start();

  class CustomTaskQueue : public TaskQueueBase {
   public:
    explicit CustomTaskQueue(absl::string_view name) : name_(name) {}
    void Delete() override {}
    void PostTaskImpl(absl::AnyInvocable<void() &&> task,
                      const PostTaskTraits& traits,
                      const Location& location) override {
      CurrentTaskQueueSetter set_current(this);
      std::move(task)();
    }
    void PostDelayedTaskImpl(absl::AnyInvocable<void() &&> task,
                             TimeDelta delay,
                             const PostDelayedTaskTraits& traits,
                             const Location& location) override {
      RTC_DCHECK_NOTREACHED();
    }
    absl::string_view queue_name() const override { return name_; }

   private:
    std::string name_;
  };

  thread->BlockingCall([&]() {
    // 1. Verify thread name is printed first.
    RTC_LOG(LS_INFO) << "Prints the name of the thread.";
    EXPECT_THAT(str, HasSubstr("TName]"));
    str.clear();

    // 2. Set custom task queue.
    CustomTaskQueue custom_tq("QName");
    custom_tq.PostTask(
        [&]() { RTC_LOG(LS_INFO) << "Prints the name of the task queue."; });
    EXPECT_THAT(str, HasSubstr("QName]"));
    EXPECT_THAT(str, Not(HasSubstr("TName]")));
    str.clear();

    // 3. Verify fallback to thread name.
    RTC_LOG(LS_INFO) << "Prints the name of the thread again.";
    EXPECT_THAT(str, HasSubstr("TName]"));
  });
}

INSTANTIATE_TEST_SUITE_P(All, LogTestWithParam, ::testing::Bool());
#endif  // !defined(WEBRTC_CHROMIUM_BUILD)

}  // namespace webrtc
#endif  // RTC_LOG_ENABLED()

Messung V0.5 in Prozent
C=95 H=98 G=96

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet am  2026-08-24) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.