| // Copyright 2023 The Fuchsia 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 LIB_DRIVER_TESTING_CPP_DRIVER_LIFECYCLE_H_ |
| #define LIB_DRIVER_TESTING_CPP_DRIVER_LIFECYCLE_H_ |
| |
| #include <lib/driver/component/cpp/driver_base.h> |
| #include <lib/driver/runtime/testing/cpp/dispatcher.h> |
| #include <lib/driver/symbols/symbols.h> |
| |
| // This is the exported driver lifecycle symbol that the driver framework looks for. |
| // NOLINTNEXTLINE(bugprone-reserved-identifier) |
| extern "C" const DriverLifecycle __fuchsia_driver_lifecycle__; |
| |
| namespace fdf_testing { |
| |
| using OpaqueDriverPtr = void*; |
| |
| // The |DriverUnderTest| is a templated class so we pull out the non-template specifics into this |
| // base class so we don't have to put the implementation in the header. |
| class DriverUnderTestBase { |
| public: |
| explicit DriverUnderTestBase( |
| fdf_dispatcher_t* driver_dispatcher = nullptr, |
| const DriverLifecycle& driver_lifecycle_symbol = __fuchsia_driver_lifecycle__); |
| ~DriverUnderTestBase(); |
| |
| zx::result<std::shared_ptr<libsync::Completion>> Start(fdf::DriverStartArgs start_args); |
| zx::result<std::shared_ptr<libsync::Completion>> StartWithErrorHandler( |
| fdf::DriverStartArgs start_args, fit::callback<void(zx_status_t error)> error_handler); |
| |
| std::shared_ptr<libsync::Completion> PrepareStop(); |
| std::shared_ptr<libsync::Completion> PrepareStopWithErrorHandler( |
| fit::callback<void(zx_status_t error)> error_handler); |
| zx::result<> Stop(); |
| |
| protected: |
| async::synchronization_checker& checker() { return checker_; } |
| std::optional<OpaqueDriverPtr>& driver() { return driver_; } |
| |
| private: |
| fdf_dispatcher_t* driver_dispatcher_; |
| const DriverLifecycle& driver_lifecycle_symbol_; |
| async::synchronization_checker checker_; |
| std::optional<OpaqueDriverPtr> driver_ __TA_GUARDED(checker_); |
| std::shared_ptr<libsync::Completion> prepare_stop_completer_ __TA_GUARDED(checker_); |
| }; |
| |
| // This is a RAII wrapper over a driver under test. On destruction, it will stop the driver. |
| // # Thread safety |
| // |
| // This class is thread-unsafe. Instances must be managed and used from a synchronized dispatcher. |
| // See |
| // https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/thread-safe-async#synchronized-dispatcher |
| // |
| template <typename Driver> |
| class DriverUnderTest : public DriverUnderTestBase { |
| public: |
| Driver* operator->() { |
| std::lock_guard guard(checker()); |
| ZX_ASSERT_MSG(driver().has_value(), "Driver does not exist."); |
| return static_cast<Driver*>(driver().value()); |
| } |
| |
| Driver* operator*() { |
| std::lock_guard guard(checker()); |
| ZX_ASSERT_MSG(driver().has_value(), "Driver does not exist."); |
| return static_cast<Driver*>(driver().value()); |
| } |
| }; |
| |
| // Start a driver using the DriverLifecycle symbol's start hook. This is optional to use, the test |
| // can also construct and start the driver on its own if it wants to do so. |
| // |
| // This MUST be called from the main test thread. |
| zx::result<OpaqueDriverPtr> StartDriver( |
| fdf::DriverStartArgs start_args, fdf::TestSynchronizedDispatcher& driver_dispatcher, |
| const DriverLifecycle& driver_lifecycle_symbol = __fuchsia_driver_lifecycle__); |
| |
| // Templated version of |StartDriver| which will return the requested driver type. |
| template <typename Driver> |
| zx::result<Driver*> StartDriver( |
| fdf::DriverStartArgs start_args, fdf::TestSynchronizedDispatcher& driver_dispatcher, |
| const DriverLifecycle& driver_lifecycle_symbol = __fuchsia_driver_lifecycle__) { |
| zx::result result = |
| StartDriver(std::move(start_args), driver_dispatcher, driver_lifecycle_symbol); |
| if (result.is_error()) { |
| return result.take_error(); |
| } |
| |
| return zx::ok(static_cast<Driver*>(result.value())); |
| } |
| |
| // Initiates the teardown of the driver and the driver dispatcher. Teardown consist of using the |
| // prepare_stop hook, waiting for the completion, and calling the stop lifecycle hook. |
| // |
| // This MUST be called from the main test thread. |
| zx::result<> TeardownDriver( |
| OpaqueDriverPtr driver, fdf::TestSynchronizedDispatcher& driver_dispatcher, |
| const DriverLifecycle& driver_lifecycle_symbol = __fuchsia_driver_lifecycle__); |
| |
| } // namespace fdf_testing |
| |
| #endif // LIB_DRIVER_TESTING_CPP_DRIVER_LIFECYCLE_H_ |