| // Copyright 2021 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <utility> |
| |
| #include <base/test/task_environment.h> |
| #include <brillo/dbus/dbus_object_test_helpers.h> |
| #include <brillo/message_loops/base_message_loop.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <dbus/bus.h> |
| #include <dbus/mock_bus.h> |
| #include <dbus/mock_exported_object.h> |
| #include <dbus/mock_object_proxy.h> |
| #include <gmock/gmock.h> |
| #include <hps/daemon/dbus_adaptor.h> |
| #include <hps/hps.h> |
| |
| using brillo::dbus_utils::AsyncEventSequencer; |
| using testing::_; |
| using testing::InSequence; |
| using testing::NiceMock; |
| using testing::Return; |
| using testing::StrictMock; |
| |
| namespace hps { |
| |
| class MockHps : public HPS { |
| public: |
| MOCK_METHOD(void, |
| Init, |
| (uint32_t, |
| const base::FilePath&, |
| const base::FilePath&, |
| const base::FilePath&), |
| (override)); |
| MOCK_METHOD(void, Boot, (), (override)); |
| MOCK_METHOD(bool, ShutDown, (), (override)); |
| MOCK_METHOD(bool, IsRunning, (), (override)); |
| MOCK_METHOD(bool, Enable, (uint8_t), (override)); |
| MOCK_METHOD(bool, Disable, (uint8_t), (override)); |
| MOCK_METHOD(FeatureResult, Result, (int), (override)); |
| MOCK_METHOD(DevInterface*, Device, (), (override)); |
| MOCK_METHOD(bool, Download, (HpsBank, const base::FilePath&), (override)); |
| MOCK_METHOD(void, SetDownloadObserver, (DownloadObserver), (override)); |
| }; |
| |
| class HpsDaemonTest : public testing::Test { |
| public: |
| HpsDaemonTest() { |
| dbus::Bus::Options options; |
| mock_bus_ = base::MakeRefCounted<NiceMock<dbus::MockBus>>(options); |
| dbus::ObjectPath path(::hps::kHpsServicePath); |
| |
| mock_object_proxy_ = base::MakeRefCounted<NiceMock<dbus::MockObjectProxy>>( |
| mock_bus_.get(), kHpsServicePath, path); |
| |
| mock_exported_object_ = |
| base::MakeRefCounted<StrictMock<dbus::MockExportedObject>>( |
| mock_bus_.get(), path); |
| |
| ON_CALL(*mock_bus_, GetExportedObject(path)) |
| .WillByDefault(Return(mock_exported_object_.get())); |
| |
| ON_CALL(*mock_bus_, GetDBusTaskRunner()) |
| .WillByDefault( |
| Return(task_environment_.GetMainThreadTaskRunner().get())); |
| |
| EXPECT_CALL(*mock_exported_object_, ExportMethod(_, _, _, _)) |
| .Times(testing::AnyNumber()); |
| |
| auto hps = std::make_unique<StrictMock<MockHps>>(); |
| mock_hps_ = hps.get(); |
| EXPECT_CALL(*mock_hps_, ShutDown()).WillOnce(Return(true)); |
| hps_daemon_.reset( |
| new DBusAdaptor(mock_bus_, std::move(hps), |
| static_cast<uint32_t>(kPollTime.InMilliseconds()))); |
| |
| feature_config_.set_allocated_basic_filter_config( |
| new FeatureConfig_BasicFilterConfig()); |
| |
| brillo_loop_.SetAsCurrent(); |
| } |
| |
| protected: |
| base::test::SingleThreadTaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| brillo::BaseMessageLoop brillo_loop_{ |
| task_environment_.GetMainThreadTaskRunner().get()}; |
| |
| scoped_refptr<dbus::MockBus> mock_bus_; |
| scoped_refptr<dbus::MockObjectProxy> mock_object_proxy_; |
| scoped_refptr<dbus::MockExportedObject> mock_exported_object_; |
| StrictMock<MockHps>* mock_hps_; |
| std::unique_ptr<DBusAdaptor> hps_daemon_; |
| FeatureConfig feature_config_; |
| static constexpr base::TimeDelta kPollTime = base::Milliseconds(500); |
| }; |
| |
| // Failing to enable or disable a feature at the hardware level should trigger a |
| // crash. |
| TEST_F(HpsDaemonTest, EnableFeatureFailed) { |
| EXPECT_DEATH( |
| { |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(false)); |
| brillo::ErrorPtr error; |
| hps_daemon_->EnableHpsSense(&error, feature_config_); |
| }, |
| ".*Failed to enable feature.*"); |
| } |
| |
| TEST_F(HpsDaemonTest, DisableFeatureFailed) { |
| EXPECT_DEATH( |
| { |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Disable(0)).WillOnce(Return(false)); |
| brillo::ErrorPtr error; |
| EXPECT_TRUE(hps_daemon_->EnableHpsSense(&error, feature_config_)); |
| hps_daemon_->DisableHpsSense(&error); |
| }, |
| ".*Failed to disable feature.*"); |
| } |
| |
| TEST_F(HpsDaemonTest, EnableFeatureReady) { |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| brillo::ErrorPtr error; |
| bool result = hps_daemon_->EnableHpsSense(&error, feature_config_); |
| EXPECT_TRUE(result); |
| } |
| |
| TEST_F(HpsDaemonTest, DisableFeatureNotEnabled) { |
| brillo::ErrorPtr error; |
| bool result = hps_daemon_->DisableHpsSense(&error); |
| EXPECT_FALSE(result); |
| } |
| |
| // With another feature still enabled the device is not shutdown |
| TEST_F(HpsDaemonTest, DisableFeatureReady) { |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Enable(1)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Disable(0)).WillOnce(Return(true)); |
| brillo::ErrorPtr error; |
| ASSERT_TRUE(hps_daemon_->EnableHpsSense(&error, feature_config_)); |
| ASSERT_TRUE(hps_daemon_->EnableHpsNotify(&error, feature_config_)); |
| bool result = hps_daemon_->DisableHpsSense(&error); |
| EXPECT_TRUE(result); |
| } |
| |
| // When the last feature is disabled the device is shutdown |
| TEST_F(HpsDaemonTest, DisableFeatureReadyLast) { |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Disable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, ShutDown()).WillOnce(Return(true)); |
| brillo::ErrorPtr error; |
| bool result = hps_daemon_->EnableHpsSense(&error, feature_config_); |
| EXPECT_TRUE(result); |
| result = hps_daemon_->DisableHpsSense(&error); |
| EXPECT_TRUE(result); |
| } |
| |
| // When the last feature is disabled, the device is shutdown. If a feature gets |
| // enabled again, we boot the device. |
| TEST_F(HpsDaemonTest, DisableThenEnableFeature) { |
| brillo::ErrorPtr error; |
| { |
| InSequence sequence; |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Disable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, ShutDown()).WillOnce(Return(true)); |
| bool result = hps_daemon_->EnableHpsSense(&error, feature_config_); |
| EXPECT_TRUE(result); |
| result = hps_daemon_->DisableHpsSense(&error); |
| EXPECT_TRUE(result); |
| } |
| { |
| InSequence sequence; |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| bool result = hps_daemon_->EnableHpsSense(&error, feature_config_); |
| EXPECT_TRUE(result); |
| } |
| } |
| |
| TEST_F(HpsDaemonTest, GetFeatureResultNotEnabled) { |
| brillo::ErrorPtr error; |
| HpsResultProto result; |
| |
| bool call_result = hps_daemon_->GetResultHpsSense(&error, &result); |
| EXPECT_FALSE(call_result); |
| EXPECT_EQ("hpsd: Feature not enabled.", error->GetMessage()); |
| } |
| |
| TEST_F(HpsDaemonTest, TestPollTimer) { |
| FeatureResult feature_result{.inference_result = 0, .valid = true}; |
| { |
| InSequence sequence; |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(0)).WillOnce(Return(feature_result)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(0)).WillOnce(Return(feature_result)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Disable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, ShutDown()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(0)).Times(0); |
| } |
| |
| brillo::ErrorPtr error; |
| bool result = hps_daemon_->EnableHpsSense(&error, feature_config_); |
| EXPECT_TRUE(result); |
| |
| // Advance timer far enough so that the poll timer should fire twice. |
| task_environment_.FastForwardBy(kPollTime * 2); |
| |
| // Disable the feature, time should no longer fire. |
| result = hps_daemon_->DisableHpsSense(&error); |
| EXPECT_TRUE(result); |
| |
| // Poll task should no longer fire if we advance the timer. |
| task_environment_.FastForwardBy(kPollTime * 2); |
| EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u); |
| } |
| |
| TEST_F(HpsDaemonTest, TestPollTimerMultipleFeatures) { |
| FeatureResult feature_result{.inference_result = 0, .valid = true}; |
| { |
| InSequence sequence; |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Enable(1)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(0)).WillOnce(Return(feature_result)); |
| EXPECT_CALL(*mock_hps_, Result(1)).WillOnce(Return(feature_result)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Disable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(0)).Times(0); |
| EXPECT_CALL(*mock_hps_, Result(1)).WillOnce(Return(feature_result)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Disable(1)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, ShutDown()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(1)).Times(0); |
| } |
| |
| brillo::ErrorPtr error; |
| |
| // Enable features 0 & 1 |
| bool result = hps_daemon_->EnableHpsSense(&error, feature_config_); |
| EXPECT_TRUE(result); |
| result = hps_daemon_->EnableHpsNotify(&error, feature_config_); |
| EXPECT_TRUE(result); |
| |
| // Advance timer far enough so that the poll timer should fire. |
| task_environment_.FastForwardBy(kPollTime); |
| |
| // Disable the feature, timer should no longer fire for feature 0. |
| result = hps_daemon_->DisableHpsSense(&error); |
| EXPECT_TRUE(result); |
| |
| // Advance timer far enough so that the poll timer should fire. |
| task_environment_.FastForwardBy(kPollTime); |
| |
| // Disable the feature, timer should no longer fire for feature 1. |
| result = hps_daemon_->DisableHpsNotify(&error); |
| EXPECT_TRUE(result); |
| |
| // Advance time to ensure no more features are firing. |
| task_environment_.FastForwardBy(kPollTime); |
| EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u); |
| } |
| |
| // TODO(slangley): Work out how to check that the signal was fired, on first |
| // inspection it doesn't come via the mocks we have. |
| TEST_F(HpsDaemonTest, DISABLED_TestSignals) { |
| // This result indicates a positive inference from HPS. |
| FeatureResult valid_feature_result{.inference_result = 100, .valid = true}; |
| FeatureResult invalid_feature_result{.inference_result = 100, .valid = false}; |
| { |
| InSequence sequence; |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Enable(1)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(0)).WillOnce(Return(valid_feature_result)); |
| EXPECT_CALL(*mock_hps_, Result(1)).WillOnce(Return(valid_feature_result)); |
| EXPECT_CALL(*mock_hps_, Result(0)).WillOnce(Return(invalid_feature_result)); |
| EXPECT_CALL(*mock_hps_, Result(1)).WillOnce(Return(invalid_feature_result)); |
| EXPECT_CALL(*mock_hps_, Disable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Disable(1)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, ShutDown()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(0)).Times(0); |
| EXPECT_CALL(*mock_hps_, Result(1)).Times(0); |
| } |
| |
| brillo::ErrorPtr error; |
| |
| // Enable features 0 & 1 |
| bool result = hps_daemon_->EnableHpsSense(&error, feature_config_); |
| EXPECT_TRUE(result); |
| result = hps_daemon_->EnableHpsNotify(&error, feature_config_); |
| EXPECT_TRUE(result); |
| |
| // Advance timer far enough so that the poll timer should fire. |
| task_environment_.FastForwardBy(kPollTime); |
| |
| // Advance timer far enough so that the poll timer should fire again. |
| task_environment_.FastForwardBy(kPollTime); |
| |
| // Disable the feature, timer should no longer fire for feature 0. |
| result = hps_daemon_->DisableHpsSense(&error); |
| EXPECT_TRUE(result); |
| |
| // Disable the feature, timer should no longer fire for feature 1. |
| result = hps_daemon_->DisableHpsNotify(&error); |
| EXPECT_TRUE(result); |
| |
| // Advance time to ensure no more features are firing. |
| task_environment_.FastForwardBy(kPollTime); |
| } |
| |
| TEST_F(HpsDaemonTest, TestSuspendAndResume) { |
| FeatureResult feature_result{.inference_result = 0, .valid = true}; |
| { |
| InSequence sequence; |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(0)).WillOnce(Return(feature_result)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(false)); |
| EXPECT_CALL(*mock_hps_, ShutDown()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(0)).WillOnce(Return(feature_result)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Disable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, ShutDown()).WillOnce(Return(true)); |
| } |
| |
| brillo::ErrorPtr error; |
| bool result = hps_daemon_->EnableHpsSense(&error, feature_config_); |
| EXPECT_TRUE(result); |
| |
| // Advance timer far enough so that the poll timer should fire twice. On the |
| // second invocation, HPS pretends that it has rebooted (IsRunning() == |
| // false), so we reinitialize the enabled features before resuming polling. |
| task_environment_.FastForwardBy(kPollTime * 2); |
| |
| // Disable the feature, time should no longer fire. |
| result = hps_daemon_->DisableHpsSense(&error); |
| EXPECT_TRUE(result); |
| |
| // Poll task should no longer fire if we advance the timer. |
| task_environment_.FastForwardBy(kPollTime * 2); |
| EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u); |
| } |
| |
| TEST_F(HpsDaemonTest, DisableFeatureAfterResume) { |
| { |
| InSequence sequence; |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(false)); |
| EXPECT_CALL(*mock_hps_, ShutDown()).WillOnce(Return(true)); |
| } |
| |
| brillo::ErrorPtr error; |
| bool result = hps_daemon_->EnableHpsSense(&error, feature_config_); |
| EXPECT_TRUE(result); |
| |
| result = hps_daemon_->DisableHpsSense(&error); |
| EXPECT_TRUE(result); |
| |
| // Poll task should no longer fire if we advance the timer. |
| task_environment_.FastForwardBy(kPollTime * 2); |
| EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u); |
| } |
| |
| TEST_F(HpsDaemonTest, AverageFilter) { |
| feature_config_.set_allocated_average_filter_config( |
| new FeatureConfig_AverageFilterConfig()); |
| feature_config_.mutable_average_filter_config()->set_average_window_size(2); |
| |
| FeatureResult feature_result1{.inference_result = 100, .valid = true}; |
| FeatureResult feature_result2{.inference_result = -100, .valid = true}; |
| { |
| InSequence sequence; |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(0)).WillOnce(Return(feature_result1)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(0)).WillOnce(Return(feature_result2)); |
| } |
| |
| brillo::ErrorPtr error; |
| bool result = hps_daemon_->EnableHpsSense(&error, feature_config_); |
| EXPECT_TRUE(result); |
| |
| // Advance timer far enough so that the poll timer should fire twice. |
| task_environment_.FastForwardBy(kPollTime * 2); |
| |
| HpsResultProto value; |
| result = hps_daemon_->GetResultHpsSense(&error, &value); |
| EXPECT_TRUE(result); |
| EXPECT_EQ(value.value(), HpsResult::POSITIVE); |
| EXPECT_EQ(value.inference_result(), 0); |
| EXPECT_FALSE(value.inference_result_valid()); |
| } |
| |
| TEST_F(HpsDaemonTest, ReportRawResults) { |
| feature_config_.set_allocated_average_filter_config( |
| new FeatureConfig_AverageFilterConfig()); |
| feature_config_.mutable_average_filter_config()->set_average_window_size(2); |
| feature_config_.set_report_raw_results(true); |
| |
| FeatureResult feature_result{.inference_result = 100, .valid = true}; |
| { |
| InSequence sequence; |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(0)).WillOnce(Return(feature_result)); |
| } |
| |
| brillo::ErrorPtr error; |
| bool result = hps_daemon_->EnableHpsSense(&error, feature_config_); |
| EXPECT_TRUE(result); |
| task_environment_.FastForwardBy(kPollTime); |
| |
| HpsResultProto value; |
| result = hps_daemon_->GetResultHpsSense(&error, &value); |
| EXPECT_TRUE(result); |
| EXPECT_EQ(value.inference_result(), feature_result.inference_result); |
| EXPECT_TRUE(value.inference_result_valid()); |
| } |
| |
| TEST_F(HpsDaemonTest, ResetFilterOnResume) { |
| feature_config_.set_allocated_average_filter_config( |
| new FeatureConfig_AverageFilterConfig()); |
| feature_config_.mutable_average_filter_config()->set_average_window_size(2); |
| |
| FeatureResult feature_result1{.inference_result = 100, .valid = true}; |
| FeatureResult feature_result2{.inference_result = -100, .valid = true}; |
| { |
| InSequence sequence; |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(0)).WillOnce(Return(feature_result1)); |
| EXPECT_CALL(*mock_hps_, IsRunning()).WillOnce(Return(false)); |
| EXPECT_CALL(*mock_hps_, ShutDown()).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Boot()); |
| EXPECT_CALL(*mock_hps_, Enable(0)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_hps_, Result(0)).WillOnce(Return(feature_result2)); |
| } |
| |
| brillo::ErrorPtr error; |
| bool result = hps_daemon_->EnableHpsSense(&error, feature_config_); |
| EXPECT_TRUE(result); |
| |
| // Advance timer far enough so that the poll timer should fire twice. Since |
| // HPS resets before the second measurement, the filter also gets reset and |
| // the overall result is negative. |
| task_environment_.FastForwardBy(kPollTime * 2); |
| |
| HpsResultProto value; |
| result = hps_daemon_->GetResultHpsSense(&error, &value); |
| EXPECT_TRUE(result); |
| EXPECT_EQ(value.value(), HpsResult::NEGATIVE); |
| } |
| |
| } // namespace hps |