/** * @file tests/unit/test_constructor_contracts.cpp * @brief Tests for explicit conversions, ownership transfers, and member initialization. */ // test includes #include "../tests_common.h" // standard includes #include #include #include #include #include #include #include // local includes #include "src/crypto.h" #include "src/move_by_copy.h" #include "src/platform/common.h" #include "src/sync.h" #include "src/task_pool.h" #include "src/thread_safe.h" #include "src/utility.h" #ifdef _WIN32 #include "src/platform/windows/display.h" #endif namespace { using callback_t = std::function; ///< Callable used by task and cleanup wrappers. using int_ptr_t = util::uniq_ptr>; ///< Integer owned by a Sunshine unique pointer. using shared_ptr_t = safe::shared_t::ptr_t; ///< Reference to shared integer storage. using move_wrapper_t = move_by_copy_util::MoveByCopy>; ///< Copy-shaped owner of a move-only integer. static_assert(std::is_constructible_v); static_assert(!std::is_convertible_v); static_assert(std::is_convertible_v); static_assert(std::is_convertible_v, int_ptr_t>); static_assert(!std::is_convertible_v>); static_assert(!std::is_convertible_v, util::wrap_ptr>); static_assert(!std::is_convertible_v>); static_assert(!std::is_convertible_v>); static_assert(!std::is_convertible_v>); static_assert(!std::is_convertible_v>>); static_assert(!std::is_convertible_v); static_assert(!std::is_convertible_v>); static_assert(!std::is_convertible_v); static_assert(!std::is_convertible_v); static_assert(!std::is_convertible_v); static_assert(!std::is_convertible_v); /** * @brief Polymorphic resource used to check ownership-preserving pointer conversions. */ struct base_t { virtual ~base_t() = default; int value {42}; ///< Value retained when ownership is moved to a base pointer. }; /** * @brief Derived resource that can be owned through its base class. */ struct derived_t: base_t {}; using base_ptr_t = util::uniq_ptr>; ///< Base-class owner with a virtual destructor. using derived_ptr_t = util::uniq_ptr>; ///< Compatible derived-class owner. static_assert(std::is_convertible_v); static_assert(std::is_convertible_v>, base_ptr_t>); } // namespace TEST(ConstructorContracts, EmptyUniquePointersHaveNoOwnedResource) { int_ptr_t default_pointer; int_ptr_t null_pointer = nullptr; EXPECT_EQ(default_pointer.get(), nullptr); EXPECT_EQ(null_pointer.get(), nullptr); } TEST(ConstructorContracts, ExplicitRawPointerConstructionTakesOwnership) { auto original = std::make_unique(7); const auto address = original.get(); int_ptr_t pointer {original.release()}; EXPECT_EQ(pointer.get(), address); EXPECT_EQ(*pointer, 7); } TEST(ConstructorContracts, CompatibleUniquePointerMovesPreserveOwnership) { std::unique_ptr> original {std::make_unique()}; const auto address = original.get(); derived_ptr_t derived = std::move(original); base_ptr_t base = std::move(derived); EXPECT_EQ(original.get(), nullptr); EXPECT_EQ(derived.get(), nullptr); EXPECT_EQ(base.get(), address); EXPECT_EQ(base->value, 42); } TEST(ConstructorContracts, CompatibleStandardPointerMovesCanTargetBaseOwners) { std::unique_ptr> original {std::make_unique()}; const auto address = original.get(); base_ptr_t base = std::move(original); EXPECT_EQ(original.get(), nullptr); EXPECT_EQ(base.get(), address); } TEST(ConstructorContracts, WrapPointersKeepBorrowedAndOwnedValuesAccessible) { int borrowed_value = 7; util::wrap_ptr borrowed {&borrowed_value}; util::wrap_ptr owned {std::make_unique(42)}; EXPECT_EQ(*borrowed, 7); EXPECT_EQ(*owned, 42); } TEST(ConstructorContracts, MovingDisabledFailGuardsKeepsCleanupDisabled) { int calls = 0; { auto original = util::fail_guard([&calls]() { ++calls; }); original.disable(); auto moved = std::move(original); EXPECT_FALSE(moved.failure); } EXPECT_EQ(calls, 0); } TEST(ConstructorContracts, ExplicitTaskConstructionRunsTheSuppliedCallable) { int calls = 0; task_pool_util::_Impl task {callback_t {[&calls]() { ++calls; }}}; task.run(); EXPECT_EQ(calls, 1); } TEST(ConstructorContracts, ExplicitSynchronizedConstructionKeepsTheInitialValue) { sync_util::sync_t value {42}; EXPECT_EQ(value.raw, 42); } TEST(ConstructorContracts, ExplicitMoveWrapperConversionTransfersTheValue) { auto original = std::make_unique(42); auto wrapper = move_by_copy_util::cmove(original); auto value = static_cast>(wrapper); EXPECT_EQ(original.get(), nullptr); ASSERT_NE(value.get(), nullptr); EXPECT_EQ(*value, 42); } TEST(ConstructorContracts, SharedPointersSupportExplicitBooleanChecks) { shared_ptr_t pointer; EXPECT_FALSE(static_cast(pointer)); } TEST(ConstructorContracts, ExplicitCipherConstructionKeepsEncryptionWorking) { const crypto::aes_t key {16}; crypto::aes_t iv {16}; constexpr std::string_view plaintext = "0123456789abcdef"; crypto::cipher::ecb_t ecb {key, false}; crypto::cipher::cbc_t cbc {key, false}; crypto::cipher::gcm_t gcm {key, false}; std::vector encrypted; std::vector decrypted; std::array cipher {}; std::array tag {}; ASSERT_EQ(ecb.encrypt(plaintext, encrypted), 0); const std::string ciphertext {encrypted.begin(), encrypted.end()}; ASSERT_EQ(ecb.decrypt(ciphertext, decrypted), 0); EXPECT_EQ((std::string {decrypted.begin(), decrypted.end()}), plaintext); EXPECT_EQ(cbc.encrypt(plaintext, cipher.data(), &iv), plaintext.size()); EXPECT_EQ(gcm.encrypt(plaintext, tag.data(), cipher.data(), &iv), plaintext.size()); } #ifdef _WIN32 TEST(ConstructorContracts, CursorViewportsStartEmptyWithTheFullDepthRange) { platf::dxgi::gpu_cursor_t cursor; EXPECT_FLOAT_EQ(cursor.cursor_view.TopLeftX, 0.0f); EXPECT_FLOAT_EQ(cursor.cursor_view.TopLeftY, 0.0f); EXPECT_FLOAT_EQ(cursor.cursor_view.Width, 0.0f); EXPECT_FLOAT_EQ(cursor.cursor_view.Height, 0.0f); EXPECT_FLOAT_EQ(cursor.cursor_view.MinDepth, 0.0f); EXPECT_FLOAT_EQ(cursor.cursor_view.MaxDepth, 1.0f); } #endif