Compare commits

...

8 Commits

Author SHA1 Message Date
JstinJasiri
d00e5b0e07
Merge 2f7f4e67cbd58af7541b43d1c9448cfa56c440ee into d72f9c8aea6817cdf1ca0ac10887f328de7f3da2 2026-03-31 11:33:35 -04:00
Dillon Sharlet
d72f9c8aea Add --gtest_shard_index and --gtest_total_shards command line arguments.
This uses the environment variables as the default value for the command line argument, so this is not a breaking change to most reasonable existing use cases. This *is* a breaking change if a process expects changes to the sharding environment variables to be visible after parsing the command line arguments.

The motivation for this is that some environments do not support environment variables, which prevents the usage of test sharding. These environments also run on simulators and other slow hardware, where sharding is especially important. I've tried a few other things, like hacking together a fake implementation of `getenv`/`setenv`, but this is a pretty unreliable approach on the platform in question.

This also seems like an improvement in consistency to me. Currently, other test selection mechanisms (e.g. filtering, shuffling, repeat), `use arguments (or at least have command line arguments as an option), while as far as I can tell, sharding is the only mechanism which can *only* be specified with environment variables.

PiperOrigin-RevId: 892324928
Change-Id: I5cf814e46e16072e7c160e54c426b02300fe712b
2026-03-31 07:22:05 -07:00
Abseil Team
5fddfab2d2 Define DieInCRTDebugElse12() inside #ifdef _DEBUG
DieInCRTDebugElse12() is only used inside a #ifdef _DEBUG block. Put
its definition inside a #ifdef _DEBUG block, too, otherwise we get a
"defined but not used [-Wunused-function]" warning under MinGW GCC.

PiperOrigin-RevId: 891795658
Change-Id: Ic9109eebc354dd20b2a5430794f491c9b451931f
2026-03-30 10:47:55 -07:00
Abseil Team
2461743991 Automated Code Change
PiperOrigin-RevId: 890778982
Change-Id: Idef51b737a250560d0b71130e6e87b6249d8c47a
2026-03-27 21:30:37 -07:00
Abseil Team
015950a936 Add an implmenetation of Notification for MinGW
Add an alternative implementation of the Notification class for MinGW
using a Windows manual-reset event object.

GCC version < 13 in MinGW, if configured with the win32 thread model
(with the --enable-threads=win32 configure option), does not implement
std::mutex and std::condition_variable in the <mutex> and
<condition_variable> headers. So the current implementation of the
Notification class, which uses std::mutex and std::condition_variable,
has compilation errors. Windows has a synchronization object called a
manual-reset event object that behaves just like the Notification class.
So we can easily implement the Notification class using a Windows
manual-reset event object.

This GCC issue is fixed in GCC 13. See
https://gcc.gnu.org/gcc-13/changes.html#windows. This alternative
implementation of Notification should be removed when GoogleTest
requires GCC version >= 13 in MinGW.

Fixes https://github.com/google/googletest/issues/4031 and
https://github.com/google/googletest/issues/4464.

PiperOrigin-RevId: 888752598
Change-Id: I69e01b2e7953e1fe72d87b6d0804c7a8a8d0e740
2026-03-24 10:53:29 -07:00
Abseil Team
f38004c441 Fix typo in monomorphic matcher documentation.
PiperOrigin-RevId: 886900324
Change-Id: I96832e8162b4631783cf0f2badf160d18d3256f8
2026-03-20 11:42:04 -07:00
Krzysztof Kosiński
94be250af7 Add documentation for two-argument floating point matchers.
PiperOrigin-RevId: 881536386
Change-Id: Idd38bded4dda5a0d0c1c5fd7847fc8134e7c2ece
2026-03-10 11:25:48 -07:00
JstinJasiri
2f7f4e67cb
Create jasirijstin 2025-12-18 22:29:14 -06:00
11 changed files with 182 additions and 86 deletions

View File

@ -3591,7 +3591,7 @@ and supports the following operations:
bool matched = matcher.MatchAndExplain(value, maybe_os);
// where `value` is of type `T` and
// `maybe_os` is of type `std::ostream*`, where it can be null if the caller
// is not interested in there textual explanation.
// is not interested in the textual explanation.
matcher.DescribeTo(os);
matcher.DescribeNegationTo(os);

View File

@ -256,6 +256,14 @@ Matcher | Description
`Le()` | `x <= y`
`Lt()` | `x < y`
`Ne()` | `x != y`
`FloatEq()` | `x` approximately equals `y`
`DoubleEq()` | `x` approximately equals `y`
`NanSensitiveFloatEq()` | Same as `FloatEq()`, but treats two NaNs as equal
`NanSensitiveDoubleEq()` | Same as `DoubleEq()`, but treats two NaNs as equal
`FloatNear(max_abs_error)` | `x` is within `max_abs_error` of `y`
`DoubleNear(max_abs_error)` | `x` is within `max_abs_error` of `y`
`NanSensitiveFloatNear(max_abs_error)` | Same as `FloatNear(max_abs_error)`, but treats two NaNs as near
`NanSensitiveDoubleNear(max_abs_error)` | Same as `DoubleNear(max_abs_error)`, but treats two NaNs as near
You can use the following selectors to pick a subset of the arguments (or
reorder them) to participate in the matching:

View File

@ -1310,7 +1310,7 @@ TEST(AssignTest, Int) {
TEST(AssignTest, String) {
::std::string x;
Action<void(void)> a = Assign(&x, "Hello, world");
Action<void()> a = Assign(&x, "Hello, world");
a.Perform(std::make_tuple());
EXPECT_EQ("Hello, world", x);
}
@ -1662,14 +1662,14 @@ class SetErrnoAndReturnTest : public testing::Test {
};
TEST_F(SetErrnoAndReturnTest, Int) {
Action<int(void)> a = SetErrnoAndReturn(ENOTTY, -5);
Action<int()> a = SetErrnoAndReturn(ENOTTY, -5);
EXPECT_EQ(-5, a.Perform(std::make_tuple()));
EXPECT_EQ(ENOTTY, errno);
}
TEST_F(SetErrnoAndReturnTest, Ptr) {
int x;
Action<int*(void)> a = SetErrnoAndReturn(ENOTTY, &x);
Action<int*()> a = SetErrnoAndReturn(ENOTTY, &x);
EXPECT_EQ(&x, a.Perform(std::make_tuple()));
EXPECT_EQ(ENOTTY, errno);
}

View File

@ -137,6 +137,10 @@ GTEST_DECLARE_int32_(repeat);
// only torn down once, for the last.
GTEST_DECLARE_bool_(recreate_environments_when_repeating);
// Together these flags determine which tests are run if the test is sharded.
GTEST_DECLARE_int32_(shard_index);
GTEST_DECLARE_int32_(total_shards);
// This flag controls whether Google Test includes Google Test internal
// stack frames in failure stack traces.
GTEST_DECLARE_bool_(show_internal_stack_frames);

View File

@ -1236,9 +1236,6 @@ class GTEST_API_ [[nodiscard]] AutoHandle {
// Nothing to do here.
#else
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
/* class A needs to have dll-interface to be used by clients of class B */)
// Allows a controller thread to pause execution of newly created
// threads until notified. Instances of this class must be created
// and destroyed in the controller thread.
@ -1246,6 +1243,39 @@ GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
// This class is only for testing Google Test's own constructs. Do not
// use it in user tests, either directly or indirectly.
// TODO(b/203539622): Replace unconditionally with absl::Notification.
#ifdef GTEST_OS_WINDOWS_MINGW
// GCC version < 13 with the win32 thread model does not provide std::mutex and
// std::condition_variable in the <mutex> and <condition_variable> headers. So
// we implement the Notification class using a Windows manual-reset event. See
// https://gcc.gnu.org/gcc-13/changes.html#windows.
class GTEST_API_ [[nodiscard]] Notification {
public:
Notification();
Notification(const Notification&) = delete;
Notification& operator=(const Notification&) = delete;
~Notification();
// Notifies all threads created with this notification to start. Must
// be called from the controller thread.
void Notify();
// Blocks until the controller thread notifies. Must be called from a test
// thread.
void WaitForNotification();
private:
// Assume that Win32 HANDLE type is equivalent to void*. Doing so allows us to
// avoid including <windows.h> in this header file. Including <windows.h> is
// undesirable because it defines a lot of symbols and macros that tend to
// conflict with client code. This assumption is verified by
// WindowsTypesTest.HANDLEIsVoidStar.
typedef void* Handle;
Handle event_;
};
#else
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
/* class A needs to have dll-interface to be used by clients of class B */)
class GTEST_API_ [[nodiscard]] Notification {
public:
Notification() : notified_(false) {}
@ -1273,6 +1303,7 @@ class GTEST_API_ [[nodiscard]] Notification {
bool notified_;
};
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
#endif // GTEST_OS_WINDOWS_MINGW
#endif // GTEST_HAS_NOTIFICATION_
// On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD

View File

@ -246,15 +246,12 @@ GTEST_API_ std::string WideStringToUtf8(const wchar_t* str, int num_chars);
// be created, prints an error and exits.
void WriteToShardStatusFileIfNeeded();
// Checks whether sharding is enabled by examining the relevant
// environment variable values. If the variables are present,
// but inconsistent (e.g., shard_index >= total_shards), prints
// an error and exits. If in_subprocess_for_death_test, sharding is
// Checks whether sharding is enabled by examining the relevant flag values.
// If the flags are set, but inconsistent (e.g., shard_index >= total_shards),
// prints an error and exits. If in_subprocess_for_death_test, sharding is
// disabled because it must only be applied to the original test
// process. Otherwise, we could filter out death tests we intended to execute.
GTEST_API_ bool ShouldShard(const char* total_shards_str,
const char* shard_index_str,
bool in_subprocess_for_death_test);
GTEST_API_ bool ShouldShard(bool in_subprocess_for_death_test);
// Parses the environment variable var as a 32-bit integer. If it is unset,
// returns default_val. If it is not a 32-bit integer, prints an error and

View File

@ -303,6 +303,22 @@ bool AutoHandle::IsCloseable() const {
return handle_ != nullptr && handle_ != INVALID_HANDLE_VALUE;
}
#if !GTEST_HAS_NOTIFICATION_ && defined(GTEST_OS_WINDOWS_MINGW)
Notification::Notification() {
// Create a manual-reset event object.
event_ = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);
GTEST_CHECK_(event_ != nullptr);
}
Notification::~Notification() { ::CloseHandle(event_); }
void Notification::Notify() { GTEST_CHECK_(::SetEvent(event_)); }
void Notification::WaitForNotification() {
GTEST_CHECK_(::WaitForSingleObject(event_, INFINITE) == WAIT_OBJECT_0);
}
#endif // !GTEST_HAS_NOTIFICATION_ && defined(GTEST_OS_WINDOWS_MINGW)
Mutex::Mutex()
: owner_thread_id_(0),
type_(kDynamic),

View File

@ -407,6 +407,18 @@ GTEST_DEFINE_bool_(
"if exceptions are enabled or exit the program with a non-zero code "
"otherwise. For use with an external test framework.");
GTEST_DEFINE_int32_(
shard_index,
testing::internal::Int32FromEnvOrDie(testing::kTestShardIndex, -1),
"The zero-based index of the shard to run. A value of -1 "
"(the default) indicates that sharding is disabled.");
GTEST_DEFINE_int32_(
total_shards,
testing::internal::Int32FromEnvOrDie(testing::kTestTotalShards, -1),
"The total number of shards to use when running tests in parallel. "
"A value of -1 (the default) indicates that sharding is disabled.");
#if GTEST_USE_OWN_FLAGFILE_FLAG_
GTEST_DEFINE_string_(
flagfile, testing::internal::StringFromGTestEnv("flagfile", ""),
@ -3475,11 +3487,11 @@ void PrettyUnitTestResultPrinter::OnTestIterationStart(
filter);
}
if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) {
const int32_t shard_index = Int32FromEnvOrDie(kTestShardIndex, -1);
ColoredPrintf(GTestColor::kYellow, "Note: This is test shard %d of %s.\n",
if (internal::ShouldShard(false)) {
const int32_t shard_index = GTEST_FLAG_GET(shard_index);
ColoredPrintf(GTestColor::kYellow, "Note: This is test shard %d of %d.\n",
static_cast<int>(shard_index) + 1,
internal::posix::GetEnv(kTestTotalShards));
GTEST_FLAG_GET(total_shards));
}
if (GTEST_FLAG_GET(shuffle)) {
@ -5983,8 +5995,7 @@ bool UnitTestImpl::RunAllTests() {
#endif // defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_)
#endif // GTEST_HAS_DEATH_TEST
const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex,
in_subprocess_for_death_test);
const bool should_shard = ShouldShard(in_subprocess_for_death_test);
// Compares the full test names with the filter to decide which
// tests to run.
@ -6196,45 +6207,44 @@ void WriteToShardStatusFileIfNeeded() {
}
#endif // GTEST_HAS_FILE_SYSTEM
// Checks whether sharding is enabled by examining the relevant
// environment variable values. If the variables are present,
// but inconsistent (i.e., shard_index >= total_shards), prints
// an error and exits. If in_subprocess_for_death_test, sharding is
// disabled because it must only be applied to the original test
// process. Otherwise, we could filter out death tests we intended to execute.
bool ShouldShard(const char* total_shards_env, const char* shard_index_env,
bool in_subprocess_for_death_test) {
// Checks whether sharding is enabled by examining the relevant command line
// arguments. If the arguments are present, but inconsistent
// (i.e., shard_index >= total_shards), prints an error and exits.
// If in_subprocess_for_death_test, sharding is disabled because it must only
// be applied to the original test process. Otherwise, we could filter out death
// tests we intended to execute.
bool ShouldShard(bool in_subprocess_for_death_test) {
if (in_subprocess_for_death_test) {
return false;
}
const int32_t total_shards = Int32FromEnvOrDie(total_shards_env, -1);
const int32_t shard_index = Int32FromEnvOrDie(shard_index_env, -1);
const int32_t total_shards = GTEST_FLAG_GET(total_shards);
const int32_t shard_index = GTEST_FLAG_GET(shard_index);
if (total_shards == -1 && shard_index == -1) {
return false;
} else if (total_shards == -1 && shard_index != -1) {
const Message msg = Message() << "Invalid environment variables: you have "
<< kTestShardIndex << " = " << shard_index
<< ", but have left " << kTestTotalShards
<< " unset.\n";
const Message msg = Message()
<< "Invalid sharding: you have " << kTestShardIndex
<< " = " << shard_index << ", but have left "
<< kTestTotalShards << " unset.\n";
ColoredPrintf(GTestColor::kRed, "%s", msg.GetString().c_str());
fflush(stdout);
exit(EXIT_FAILURE);
} else if (total_shards != -1 && shard_index == -1) {
const Message msg = Message()
<< "Invalid environment variables: you have "
<< kTestTotalShards << " = " << total_shards
<< ", but have left " << kTestShardIndex << " unset.\n";
<< "Invalid sharding: you have " << kTestTotalShards
<< " = " << total_shards << ", but have left "
<< kTestShardIndex << " unset.\n";
ColoredPrintf(GTestColor::kRed, "%s", msg.GetString().c_str());
fflush(stdout);
exit(EXIT_FAILURE);
} else if (shard_index < 0 || shard_index >= total_shards) {
const Message msg =
Message() << "Invalid environment variables: we require 0 <= "
<< kTestShardIndex << " < " << kTestTotalShards
<< ", but you have " << kTestShardIndex << "=" << shard_index
<< ", " << kTestTotalShards << "=" << total_shards << ".\n";
Message() << "Invalid sharding: we require 0 <= " << kTestShardIndex
<< " < " << kTestTotalShards << ", but you have "
<< kTestShardIndex << "=" << shard_index << ", "
<< kTestTotalShards << "=" << total_shards << ".\n";
ColoredPrintf(GTestColor::kRed, "%s", msg.GetString().c_str());
fflush(stdout);
exit(EXIT_FAILURE);
@ -6277,11 +6287,10 @@ bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) {
// . Returns the number of tests that should run.
int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) {
const int32_t total_shards = shard_tests == HONOR_SHARDING_PROTOCOL
? Int32FromEnvOrDie(kTestTotalShards, -1)
? GTEST_FLAG_GET(total_shards)
: -1;
const int32_t shard_index = shard_tests == HONOR_SHARDING_PROTOCOL
? Int32FromEnvOrDie(kTestShardIndex, -1)
: -1;
const int32_t shard_index =
shard_tests == HONOR_SHARDING_PROTOCOL ? GTEST_FLAG_GET(shard_index) : -1;
const PositiveAndNegativeUnitTestFilter gtest_flag_filter(
GTEST_FLAG_GET(filter));
@ -6810,6 +6819,8 @@ static bool ParseGoogleTestFlag(const char* const arg) {
GTEST_INTERNAL_PARSE_FLAG(print_utf8);
GTEST_INTERNAL_PARSE_FLAG(random_seed);
GTEST_INTERNAL_PARSE_FLAG(repeat);
GTEST_INTERNAL_PARSE_FLAG(shard_index);
GTEST_INTERNAL_PARSE_FLAG(total_shards);
GTEST_INTERNAL_PARSE_FLAG(recreate_environments_when_repeating);
GTEST_INTERNAL_PARSE_FLAG(shuffle);
GTEST_INTERNAL_PARSE_FLAG(stack_trace_depth);

View File

@ -208,6 +208,7 @@ int DieInDebugElse12(int* sideeffect) {
#ifdef GTEST_OS_WINDOWS
// Death in dbg due to Windows CRT assertion failure, not opt.
#ifdef _DEBUG
int DieInCRTDebugElse12(int* sideeffect) {
if (sideeffect) *sideeffect = 12;
@ -222,6 +223,7 @@ int DieInCRTDebugElse12(int* sideeffect) {
return 12;
}
#endif // _DEBUG
#endif // GTEST_OS_WINDOWS

View File

@ -1873,36 +1873,31 @@ TEST(ShouldRunTestOnShardTest, IsPartitionWhenThereIsOneShard) {
class ShouldShardTest : public testing::Test {
protected:
void SetUp() override {
index_var_ = GTEST_FLAG_PREFIX_UPPER_ "INDEX";
total_var_ = GTEST_FLAG_PREFIX_UPPER_ "TOTAL";
}
void SetUp() override {}
void TearDown() override {
SetEnv(index_var_, "");
SetEnv(total_var_, "");
GTEST_FLAG_SET(shard_index, -1);
GTEST_FLAG_SET(total_shards, -1);
}
const char* index_var_;
const char* total_var_;
};
// Tests that sharding is disabled if neither of the environment variables
// are set.
TEST_F(ShouldShardTest, ReturnsFalseWhenNeitherEnvVarIsSet) {
SetEnv(index_var_, "");
SetEnv(total_var_, "");
GTEST_FLAG_SET(shard_index, -1);
GTEST_FLAG_SET(total_shards, -1);
EXPECT_FALSE(ShouldShard(total_var_, index_var_, false));
EXPECT_FALSE(ShouldShard(total_var_, index_var_, true));
EXPECT_FALSE(ShouldShard(false));
EXPECT_FALSE(ShouldShard(true));
}
// Tests that sharding is not enabled if total_shards == 1.
TEST_F(ShouldShardTest, ReturnsFalseWhenTotalShardIsOne) {
SetEnv(index_var_, "0");
SetEnv(total_var_, "1");
EXPECT_FALSE(ShouldShard(total_var_, index_var_, false));
EXPECT_FALSE(ShouldShard(total_var_, index_var_, true));
GTEST_FLAG_SET(shard_index, 0);
GTEST_FLAG_SET(total_shards, 1);
EXPECT_FALSE(ShouldShard(false));
EXPECT_FALSE(ShouldShard(true));
}
// Tests that sharding is enabled if total_shards > 1 and
@ -1910,20 +1905,20 @@ TEST_F(ShouldShardTest, ReturnsFalseWhenTotalShardIsOne) {
// Environment variables are not supported on Windows CE.
#ifndef GTEST_OS_WINDOWS_MOBILE
TEST_F(ShouldShardTest, WorksWhenShardEnvVarsAreValid) {
SetEnv(index_var_, "4");
SetEnv(total_var_, "22");
EXPECT_TRUE(ShouldShard(total_var_, index_var_, false));
EXPECT_FALSE(ShouldShard(total_var_, index_var_, true));
GTEST_FLAG_SET(shard_index, 4);
GTEST_FLAG_SET(total_shards, 22);
EXPECT_TRUE(ShouldShard(false));
EXPECT_FALSE(ShouldShard(true));
SetEnv(index_var_, "8");
SetEnv(total_var_, "9");
EXPECT_TRUE(ShouldShard(total_var_, index_var_, false));
EXPECT_FALSE(ShouldShard(total_var_, index_var_, true));
GTEST_FLAG_SET(shard_index, 8);
GTEST_FLAG_SET(total_shards, 9);
EXPECT_TRUE(ShouldShard(false));
EXPECT_FALSE(ShouldShard(true));
SetEnv(index_var_, "0");
SetEnv(total_var_, "9");
EXPECT_TRUE(ShouldShard(total_var_, index_var_, false));
EXPECT_FALSE(ShouldShard(total_var_, index_var_, true));
GTEST_FLAG_SET(shard_index, 0);
GTEST_FLAG_SET(total_shards, 9);
EXPECT_TRUE(ShouldShard(false));
EXPECT_FALSE(ShouldShard(true));
}
#endif // !GTEST_OS_WINDOWS_MOBILE
@ -1932,21 +1927,21 @@ TEST_F(ShouldShardTest, WorksWhenShardEnvVarsAreValid) {
typedef ShouldShardTest ShouldShardDeathTest;
TEST_F(ShouldShardDeathTest, AbortsWhenShardingEnvVarsAreInvalid) {
SetEnv(index_var_, "4");
SetEnv(total_var_, "4");
EXPECT_DEATH_IF_SUPPORTED(ShouldShard(total_var_, index_var_, false), ".*");
GTEST_FLAG_SET(shard_index, 4);
GTEST_FLAG_SET(total_shards, 4);
EXPECT_DEATH_IF_SUPPORTED(ShouldShard(false), ".*");
SetEnv(index_var_, "4");
SetEnv(total_var_, "-2");
EXPECT_DEATH_IF_SUPPORTED(ShouldShard(total_var_, index_var_, false), ".*");
GTEST_FLAG_SET(shard_index, 4);
GTEST_FLAG_SET(total_shards, -2);
EXPECT_DEATH_IF_SUPPORTED(ShouldShard(false), ".*");
SetEnv(index_var_, "5");
SetEnv(total_var_, "");
EXPECT_DEATH_IF_SUPPORTED(ShouldShard(total_var_, index_var_, false), ".*");
GTEST_FLAG_SET(shard_index, 5);
GTEST_FLAG_SET(total_shards, 5);
EXPECT_DEATH_IF_SUPPORTED(ShouldShard(false), ".*");
SetEnv(index_var_, "");
SetEnv(total_var_, "5");
EXPECT_DEATH_IF_SUPPORTED(ShouldShard(total_var_, index_var_, false), ".*");
GTEST_FLAG_SET(shard_index, -1);
GTEST_FLAG_SET(total_shards, 5);
EXPECT_DEATH_IF_SUPPORTED(ShouldShard(false), ".*");
}
// Tests that ShouldRunTestOnShard is a partition when 5

32
jasirijstin Normal file
View File

@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.20)
project(SecureScanPro LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(CTest)
enable_testing()
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
)
set(gtest_force_shared_crt ON CACHE BOOL"" FORCE)
FetchContent_MakeAvailable(googletest)
add_executable(securescan_tests
tests/tests_baseline.cpp
tests/test_detector.cpp
tests/test_pipeline_intergration.cpp
)
target_link_libraries(securescan_tests PRIVATE GTest::gtest_main)
target_include_directories(securescan_tests PRIVATE ${CMAKE_SOURCE_DIR}/src)
include(GoogleTest)
gtest_discover_tests(securescan_tests)
cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failure