Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions testing/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ source_set("testing") {
testonly = true

sources = [
"debugger_detection.cc",
"debugger_detection.h",
"run_all_unittests.cc",
"test_timeout_listener.cc",
"test_timeout_listener.h",
Expand Down
71 changes: 71 additions & 0 deletions testing/debugger_detection.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2013 The Flutter 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 "flutter/testing/debugger_detection.h"

#include "flutter/fml/build_config.h"
#include "flutter/fml/logging.h"

#if OS_MACOSX

#include <assert.h>
#include <stdbool.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>

#endif // OS_MACOSX

#if OS_WIN

#include <windows.h>

#endif // OS_WIN

namespace flutter {
namespace testing {

DebuggerStatus GetDebuggerStatus() {
#if OS_MACOSX
// From Technical Q&A QA1361 Detecting the Debugger
// https://developer.apple.com/library/archive/qa/qa1361/_index.html
int management_info_base[4];
struct kinfo_proc info;
size_t size;

// Initialize the flags so that, if sysctl fails for some bizarre
// reason, we get a predictable result.
info.kp_proc.p_flag = 0;

// Initialize management_info_base, which tells sysctl the info we want, in
// this case we're looking for information about a specific process ID.
management_info_base[0] = CTL_KERN;
management_info_base[1] = KERN_PROC;
management_info_base[2] = KERN_PROC_PID;
management_info_base[3] = getpid();

// Call sysctl.

size = sizeof(info);
auto status =
::sysctl(management_info_base,
sizeof(management_info_base) / sizeof(*management_info_base),
&info, &size, NULL, 0);
FML_CHECK(status == 0);

// We're being debugged if the P_TRACED flag is set.
return ((info.kp_proc.p_flag & P_TRACED) != 0) ? DebuggerStatus::kAttached
: DebuggerStatus::kDontKnow;

#elif OS_WIN
return ::IsDebuggerPresent() ? DebuggerStatus::kAttached
: DebuggerStatus::kDontKnow;

#else
return DebuggerStatus::kDontKnow;
#endif
} // namespace testing

} // namespace testing
} // namespace flutter
23 changes: 23 additions & 0 deletions testing/debugger_detection.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_TESTING_DEBUGGER_DETECTION_H_
#define FLUTTER_TESTING_DEBUGGER_DETECTION_H_

#include "flutter/fml/macros.h"

namespace flutter {
namespace testing {

enum class DebuggerStatus {
kDontKnow,
kAttached,
};

DebuggerStatus GetDebuggerStatus();

} // namespace testing
} // namespace flutter

#endif // FLUTTER_TESTING_DEBUGGER_DETECTION_H_
42 changes: 41 additions & 1 deletion testing/run_all_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,38 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <iostream>
#include <optional>
#include <string>

#include "flutter/fml/build_config.h"
#include "flutter/fml/command_line.h"
#include "flutter/testing/debugger_detection.h"
#include "flutter/testing/test_timeout_listener.h"
#include "gtest/gtest.h"

#ifdef OS_IOS
#include <asl.h>
#endif // OS_IOS

std::optional<fml::TimeDelta> GetTestTimeoutFromArgs(int argc, char** argv) {
const auto command_line = fml::CommandLineFromArgcArgv(argc, argv);

std::string timeout_seconds;
if (!command_line.GetOptionValue("timeout", &timeout_seconds)) {
// No timeout specified. Default to 30s.
return fml::TimeDelta::FromSeconds(30u);
}

const auto seconds = std::stoi(timeout_seconds);

if (seconds < 1) {
return std::nullopt;
}

return fml::TimeDelta::FromSeconds(seconds);
}

int main(int argc, char** argv) {
#ifdef OS_IOS
asl_log_descriptor(NULL, NULL, ASL_LEVEL_NOTICE, STDOUT_FILENO,
Expand All @@ -19,7 +43,23 @@ int main(int argc, char** argv) {
#endif // OS_IOS

::testing::InitGoogleTest(&argc, argv);
auto timeout_listener = new flutter::testing::TestTimeoutListener();

// Check if the user has specified a timeout.
const auto timeout = GetTestTimeoutFromArgs(argc, argv);
if (!timeout.has_value()) {
FML_LOG(INFO) << "Timeouts disabled via a command line flag.";
return RUN_ALL_TESTS();
}

// Check if the user is debugging the process.
if (flutter::testing::GetDebuggerStatus() ==
flutter::testing::DebuggerStatus::kAttached) {
FML_LOG(INFO) << "Debugger is attached. Suspending test timeouts.";
return RUN_ALL_TESTS();
}

auto timeout_listener =
new flutter::testing::TestTimeoutListener(timeout.value());
auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
listeners.Append(timeout_listener);
auto result = RUN_ALL_TESTS();
Expand Down
5 changes: 4 additions & 1 deletion testing/test_timeout_listener.cc
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ TestTimeoutListener::TestTimeoutListener(fml::TimeDelta timeout)
: timeout_(timeout),
listener_thread_("test_timeout_listener"),
listener_thread_runner_(listener_thread_.GetTaskRunner()),
pending_tests_(PendingTests::Create(listener_thread_runner_, timeout_)) {}
pending_tests_(PendingTests::Create(listener_thread_runner_, timeout_)) {
FML_LOG(INFO) << "Test timeout of " << timeout_.ToSeconds()
<< " seconds per test case will be enforced.";
}

TestTimeoutListener::~TestTimeoutListener() {
listener_thread_runner_->PostTask(
Expand Down
3 changes: 1 addition & 2 deletions testing/test_timeout_listener.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ class PendingTests;

class TestTimeoutListener : public ::testing::EmptyTestEventListener {
public:
TestTimeoutListener(
fml::TimeDelta timeout = fml::TimeDelta::FromSeconds(30u));
TestTimeoutListener(fml::TimeDelta timeout);

~TestTimeoutListener();

Expand Down