blob: 5409bfe8e7aac2c44b55dc984715d2ae11af77f1 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <lib/cxxabi-dynamic-init/cxxabi-dynamic-init.h>
#include <lib/fit/defer.h>
#include <zxtest/zxtest.h>
using cxxabi_dynamic_init::Abort;
using cxxabi_dynamic_init::Acquire;
using cxxabi_dynamic_init::Release;
// Provide a fake implementation of ConstructorsCalled so that the tests can simulate pre and post
// constructor context.
bool gCtorsCalled = false;
namespace cxxabi_dynamic_init::internal {
bool ConstructorsCalled() { return gCtorsCalled; }
} // namespace cxxabi_dynamic_init::internal
// Verify behavior of acquire when variable is already initialized.
TEST(StaticInitGuardTest, AcquireAlreadyInitialized) {
uint64_t guard = 1;
// Failed to acquire because object is already initialized.
ASSERT_EQ(0, Acquire(&guard));
// Guard is unchanged.
ASSERT_EQ(1, guard);
}
// Verify happy case of acquire then release.
TEST(StaticInitGuardTest, AcquireRelease) {
uint64_t guard = 0;
ASSERT_EQ(1, Acquire(&guard));
Release(&guard);
// Guard shows initialized.
ASSERT_EQ(1, 0x000000ff & guard);
}
// Verify that an aborted initialization can be retried.
TEST(StaticInitGuardTest, AcquireAbort) {
uint64_t guard = 0;
ASSERT_EQ(1, Acquire(&guard));
Abort(&guard);
// Guard shows uninitialized.
ASSERT_EQ(0, guard);
// Try again.
ASSERT_EQ(1, Acquire(&guard));
Release(&guard);
// Guard shows initialized.
ASSERT_EQ(1, 0x000000ff & guard);
}
// Skip the following tests if run in the host environment, which does not have ASSERT_DEATH, or if
// ZX_DEBUG_ASSERTs aren't implemented.
#if defined(__Fuchsia__) && ZX_DEBUG_ASSERT_IMPLEMENTED
// Verify that attempting to acquire an already acquired guard results in a debug assert.
TEST(StaticInitGuardTest, DoubleAcquireMaybeDeath) {
uint64_t guard = 0;
ASSERT_EQ(1, Acquire(&guard));
ASSERT_DEATH([&]() { Acquire(&guard); });
}
// Verify that attempting to acquire after global ctors have been called results in a debug assert.
TEST(StaticInitGuardTest, AcquireAfterGlobalCtorsMaybeDeath) {
gCtorsCalled = true;
auto cleanup = fit::defer([]() { gCtorsCalled = false; });
uint64_t guard = 0;
ASSERT_DEATH([&]() { Acquire(&guard); });
}
// Verify that attempting to release after global ctors have been called results in a debug assert.
TEST(StaticInitGuardTest, ReleaseAfterGlobalCtorsMaybeDeath) {
uint64_t guard = 0;
ASSERT_EQ(1, Acquire(&guard));
gCtorsCalled = true;
auto cleanup = fit::defer([]() { gCtorsCalled = false; });
ASSERT_DEATH([&]() { Release(&guard); });
}
// Verify that attempting to abort after global ctors have been called results in a debug assert.
TEST(StaticInitGuardTest, AbortAfterGlobalCtorsMaybeDeath) {
uint64_t guard = 0;
ASSERT_EQ(1, Acquire(&guard));
gCtorsCalled = true;
auto cleanup = fit::defer([]() { gCtorsCalled = false; });
ASSERT_DEATH([&]() { Abort(&guard); });
}
#endif