| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "services/device/fingerprint/fingerprint_chromeos.h" |
| |
| #include "base/functional/bind.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/run_loop.h" |
| #include "base/test/task_environment.h" |
| #include "chromeos/ash/components/dbus/biod/fake_biod_client.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace device { |
| |
| class FakeFingerprintObserver : public mojom::FingerprintObserver { |
| public: |
| explicit FakeFingerprintObserver( |
| mojo::PendingReceiver<mojom::FingerprintObserver> receiver) |
| : receiver_(this, std::move(receiver)) {} |
| |
| FakeFingerprintObserver(const FakeFingerprintObserver&) = delete; |
| FakeFingerprintObserver& operator=(const FakeFingerprintObserver&) = delete; |
| |
| ~FakeFingerprintObserver() override {} |
| |
| // mojom::FingerprintObserver |
| void OnRestarted() override { restarts_++; } |
| void OnStatusChanged(device::mojom::BiometricsManagerStatus status) override { |
| DCHECK_EQ(status, device::mojom::BiometricsManagerStatus::INITIALIZED); |
| status_changes_++; |
| } |
| |
| void OnEnrollScanDone(device::mojom::ScanResult scan_result, |
| bool is_complete, |
| int percent_complete) override { |
| enroll_scan_dones_++; |
| } |
| |
| void OnAuthScanDone( |
| const device::mojom::FingerprintMessagePtr msg, |
| const base::flat_map<std::string, std::vector<std::string>>& matches) |
| override { |
| auth_scan_dones_++; |
| last_message_ = *msg; |
| } |
| |
| void OnSessionFailed() override { session_failures_++; } |
| |
| // Test status counts. |
| int enroll_scan_dones() { return enroll_scan_dones_; } |
| int auth_scan_dones() { return auth_scan_dones_; } |
| int restarts() { return restarts_; } |
| int session_failures() { return session_failures_; } |
| |
| const device::mojom::FingerprintMessage& last_message() const { |
| return last_message_; |
| } |
| |
| private: |
| mojo::Receiver<mojom::FingerprintObserver> receiver_; |
| int enroll_scan_dones_ = 0; // Count of enroll scan done signal received. |
| int auth_scan_dones_ = 0; // Count of auth scan done signal received. |
| int restarts_ = 0; // Count of restart signal received. |
| int status_changes_ = 0; // Count of StatusChanged signal received. |
| int session_failures_ = 0; // Count of session failed signal received. |
| |
| device::mojom::FingerprintMessage |
| last_message_; // Last received FingerprintMessage. |
| }; |
| |
| class FingerprintChromeOSTest : public testing::Test { |
| public: |
| FingerprintChromeOSTest() = default; |
| |
| FingerprintChromeOSTest(const FingerprintChromeOSTest&) = delete; |
| FingerprintChromeOSTest& operator=(const FingerprintChromeOSTest&) = delete; |
| |
| ~FingerprintChromeOSTest() override = default; |
| |
| void SetUp() override { |
| ash::BiodClient::InitializeFake(); |
| fingerprint_ = base::WrapUnique(new FingerprintChromeOS()); |
| } |
| |
| void TearDown() override { |
| fingerprint_.reset(); |
| ash::BiodClient::Shutdown(); |
| } |
| |
| FingerprintChromeOS* fingerprint() { return fingerprint_.get(); } |
| |
| void GenerateRestartSignal() { fingerprint_->BiodServiceRestarted(); } |
| |
| void GenerateSessionStateSignal() { |
| fingerprint_->BiodServiceStatusChanged( |
| biod::BiometricsManagerStatus::INITIALIZED); |
| } |
| |
| void GenerateEnrollScanDoneSignal() { |
| std::string fake_fingerprint_data; |
| ash::FakeBiodClient::Get()->SendEnrollScanDone( |
| fake_fingerprint_data, biod::SCAN_RESULT_SUCCESS, true, |
| -1 /* percent_complete */); |
| } |
| |
| void GenerateAuthScanDoneSignal(const biod::FingerprintMessage& msg) { |
| std::string fake_fingerprint_data; |
| ash::FakeBiodClient::Get()->SendAuthScanDone(fake_fingerprint_data, msg); |
| } |
| |
| void GenerateSessionFailedSignal() { |
| ash::FakeBiodClient::Get()->SendSessionFailed(); |
| } |
| |
| void onStartSession(const dbus::ObjectPath& path) {} |
| |
| void SimulateRequestRunning(bool is_running) { |
| fingerprint_->is_request_running_ = is_running; |
| if (!is_running) |
| fingerprint_->StartNextRequest(); |
| } |
| |
| bool RequestDataIsReset() { |
| return fingerprint_->records_path_to_label_.empty() && |
| !fingerprint_->on_get_records_; |
| } |
| |
| void GenerateGetRecordsForUserRequest(int num_of_request) { |
| for (int i = 0; i < num_of_request; i++) { |
| fingerprint_->GetRecordsForUser( |
| "" /*user_id*/, base::BindOnce(&FingerprintChromeOSTest::OnGetRecords, |
| base::Unretained(this))); |
| } |
| } |
| |
| void OnGetRecords( |
| const base::flat_map<std::string, std::string>& fingerprints_list_mapping, |
| bool success) { |
| ++get_records_results_; |
| } |
| |
| int GetPendingRequests() { |
| return fingerprint_->get_records_pending_requests_.size(); |
| } |
| |
| bool IsRequestRunning() { return fingerprint_->is_request_running_; } |
| int get_records_results() { return get_records_results_; } |
| |
| private: |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| std::unique_ptr<FingerprintChromeOS> fingerprint_; |
| int get_records_results_ = 0; |
| }; |
| |
| TEST_F(FingerprintChromeOSTest, FingerprintObserverTest) { |
| mojo::PendingRemote<mojom::FingerprintObserver> pending_observer; |
| FakeFingerprintObserver observer( |
| pending_observer.InitWithNewPipeAndPassReceiver()); |
| fingerprint()->AddFingerprintObserver(std::move(pending_observer)); |
| |
| GenerateRestartSignal(); |
| GenerateSessionStateSignal(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.restarts(), 1); |
| |
| std::string user_id; |
| std::string label; |
| ash::FakeBiodClient::Get()->StartEnrollSession( |
| user_id, label, |
| base::BindOnce(&FingerprintChromeOSTest::onStartSession, |
| base::Unretained(this))); |
| base::RunLoop().RunUntilIdle(); |
| GenerateEnrollScanDoneSignal(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.enroll_scan_dones(), 1); |
| |
| biod::FingerprintMessage msg; |
| ash::FakeBiodClient::Get()->StartAuthSession(base::BindOnce( |
| &FingerprintChromeOSTest::onStartSession, base::Unretained(this))); |
| base::RunLoop().RunUntilIdle(); |
| msg.set_scan_result(biod::SCAN_RESULT_SUCCESS); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.auth_scan_dones(), 1); |
| |
| GenerateSessionFailedSignal(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.session_failures(), 1); |
| } |
| |
| TEST_F(FingerprintChromeOSTest, SimultaneousGetRecordsRequests) { |
| EXPECT_EQ(GetPendingRequests(), 0); |
| EXPECT_FALSE(IsRequestRunning()); |
| |
| // Single request. |
| GenerateGetRecordsForUserRequest(1); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(get_records_results(), 1); |
| EXPECT_FALSE(IsRequestRunning()); |
| EXPECT_EQ(GetPendingRequests(), 0); |
| EXPECT_TRUE(RequestDataIsReset()); |
| |
| // Multiple requests at the same time. |
| SimulateRequestRunning(true); |
| GenerateGetRecordsForUserRequest(5); |
| EXPECT_EQ(GetPendingRequests(), 5); |
| SimulateRequestRunning(false); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(get_records_results(), 6); |
| EXPECT_FALSE(IsRequestRunning()); |
| EXPECT_EQ(GetPendingRequests(), 0); |
| EXPECT_TRUE(RequestDataIsReset()); |
| } |
| |
| TEST_F(FingerprintChromeOSTest, FingerprintScanResultConvertTest) { |
| mojo::PendingRemote<mojom::FingerprintObserver> pending_observer; |
| FakeFingerprintObserver observer( |
| pending_observer.InitWithNewPipeAndPassReceiver()); |
| fingerprint()->AddFingerprintObserver(std::move(pending_observer)); |
| |
| ash::FakeBiodClient::Get()->StartAuthSession(base::BindOnce( |
| &FingerprintChromeOSTest::onStartSession, base::Unretained(this))); |
| base::RunLoop().RunUntilIdle(); |
| |
| biod::FingerprintMessage msg; |
| msg.set_scan_result(biod::SCAN_RESULT_SUCCESS); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kScanResult); |
| EXPECT_EQ(observer.last_message().get_scan_result(), |
| device::mojom::ScanResult::SUCCESS); |
| |
| msg.set_scan_result(biod::SCAN_RESULT_PARTIAL); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kScanResult); |
| EXPECT_EQ(observer.last_message().get_scan_result(), |
| device::mojom::ScanResult::PARTIAL); |
| |
| msg.set_scan_result(biod::SCAN_RESULT_INSUFFICIENT); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kScanResult); |
| EXPECT_EQ(observer.last_message().get_scan_result(), |
| device::mojom::ScanResult::INSUFFICIENT); |
| |
| msg.set_scan_result(biod::SCAN_RESULT_SENSOR_DIRTY); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kScanResult); |
| EXPECT_EQ(observer.last_message().get_scan_result(), |
| device::mojom::ScanResult::SENSOR_DIRTY); |
| |
| msg.set_scan_result(biod::SCAN_RESULT_TOO_SLOW); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kScanResult); |
| EXPECT_EQ(observer.last_message().get_scan_result(), |
| device::mojom::ScanResult::TOO_SLOW); |
| |
| msg.set_scan_result(biod::SCAN_RESULT_TOO_FAST); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kScanResult); |
| EXPECT_EQ(observer.last_message().get_scan_result(), |
| device::mojom::ScanResult::TOO_FAST); |
| |
| msg.set_scan_result(biod::SCAN_RESULT_IMMOBILE); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kScanResult); |
| EXPECT_EQ(observer.last_message().get_scan_result(), |
| device::mojom::ScanResult::IMMOBILE); |
| |
| msg.set_scan_result(biod::SCAN_RESULT_NO_MATCH); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kScanResult); |
| EXPECT_EQ(observer.last_message().get_scan_result(), |
| device::mojom::ScanResult::NO_MATCH); |
| |
| msg.set_scan_result(biod::SCAN_RESULT_MAX); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kScanResult); |
| EXPECT_EQ(observer.last_message().get_scan_result(), |
| device::mojom::ScanResult::kMaxValue); |
| } |
| |
| // Make sure that compilation fails if a new value is added and this assert is |
| // not updated. When updating this, please extend unit tests to check newly |
| // added value. |
| static_assert(device::mojom::ScanResult::kMaxValue == |
| device::mojom::ScanResult::NO_MATCH); |
| |
| TEST_F(FingerprintChromeOSTest, FingerprintErrorConvertTest) { |
| mojo::PendingRemote<mojom::FingerprintObserver> pending_observer; |
| FakeFingerprintObserver observer( |
| pending_observer.InitWithNewPipeAndPassReceiver()); |
| fingerprint()->AddFingerprintObserver(std::move(pending_observer)); |
| |
| ash::FakeBiodClient::Get()->StartAuthSession(base::BindOnce( |
| &FingerprintChromeOSTest::onStartSession, base::Unretained(this))); |
| base::RunLoop().RunUntilIdle(); |
| |
| biod::FingerprintMessage msg; |
| msg.set_error(biod::ERROR_HW_UNAVAILABLE); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kFingerprintError); |
| EXPECT_EQ(observer.last_message().get_fingerprint_error(), |
| device::mojom::FingerprintError::HW_UNAVAILABLE); |
| |
| msg.set_error(biod::ERROR_UNABLE_TO_PROCESS); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kFingerprintError); |
| EXPECT_EQ(observer.last_message().get_fingerprint_error(), |
| device::mojom::FingerprintError::UNABLE_TO_PROCESS); |
| |
| msg.set_error(biod::ERROR_TIMEOUT); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kFingerprintError); |
| EXPECT_EQ(observer.last_message().get_fingerprint_error(), |
| device::mojom::FingerprintError::TIMEOUT); |
| |
| msg.set_error(biod::ERROR_NO_SPACE); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kFingerprintError); |
| EXPECT_EQ(observer.last_message().get_fingerprint_error(), |
| device::mojom::FingerprintError::NO_SPACE); |
| |
| msg.set_error(biod::ERROR_CANCELED); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kFingerprintError); |
| EXPECT_EQ(observer.last_message().get_fingerprint_error(), |
| device::mojom::FingerprintError::CANCELED); |
| |
| msg.set_error(biod::ERROR_UNABLE_TO_REMOVE); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kFingerprintError); |
| EXPECT_EQ(observer.last_message().get_fingerprint_error(), |
| device::mojom::FingerprintError::UNABLE_TO_REMOVE); |
| |
| msg.set_error(biod::ERROR_LOCKOUT); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kFingerprintError); |
| EXPECT_EQ(observer.last_message().get_fingerprint_error(), |
| device::mojom::FingerprintError::LOCKOUT); |
| |
| msg.set_error(biod::ERROR_NO_TEMPLATES); |
| GenerateAuthScanDoneSignal(msg); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(observer.last_message().which(), |
| device::mojom::FingerprintMessage::Tag::kFingerprintError); |
| EXPECT_EQ(observer.last_message().get_fingerprint_error(), |
| device::mojom::FingerprintError::NO_TEMPLATES); |
| } |
| |
| // Make sure that compilation fails if a new value is added and this assert is |
| // not updated. When updating this, please extend unit tests to check newly |
| // added value. |
| static_assert(device::mojom::FingerprintError::kMaxValue == |
| device::mojom::FingerprintError::NO_TEMPLATES); |
| |
| } // namespace device |