blob: efd3c6dbba99b5f5d90dbe331ee01d9880068e8d [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "faced/enrollment_session.h"
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <absl/random/random.h>
#include <absl/status/status.h>
#include <base/functional/bind.h>
#include <base/run_loop.h>
#include <base/test/bind.h>
#include <base/test/task_environment.h>
#include <brillo/cryptohome.h>
#include <gmock/gmock-nice-strict.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <mojo/public/cpp/bindings/receiver.h>
#include <mojo/public/cpp/bindings/remote.h>
#include "faced/mock_face_enrollment_session_delegate.h"
#include "faced/mojom/faceauth.mojom.h"
#include "faced/testing/face_service.h"
#include "faced/testing/status.h"
#include "faced/util/queueing_stream.h"
namespace faced {
namespace {
constexpr char kUserName[] = "someone@example.com";
using ::testing::_;
using ::testing::Invoke;
using ::testing::StrictMock;
using ::chromeos::faceauth::mojom::EnrollmentCompleteMessagePtr;
using ::chromeos::faceauth::mojom::EnrollmentSessionConfig;
using ::chromeos::faceauth::mojom::FaceEnrollmentSession;
using ::chromeos::faceauth::mojom::FaceEnrollmentSessionDelegate;
using ::chromeos::faceauth::mojom::FaceOperationStatus;
using ::chromeos::faceauth::mojom::SessionError;
using ::brillo::cryptohome::home::SanitizeUserName;
std::string SampleUserHash() {
return *SanitizeUserName(::brillo::cryptohome::home::Username(kUserName));
}
absl::BitGen bitgen;
} // namespace
TEST(TestEnrollmentSession, TestSessionComplete) {
// Create a mock session delegate, that expects a completion event to be
// triggered.
StrictMock<MockFaceEnrollmentSessionDelegate> mock_delegate;
EXPECT_CALL(mock_delegate, OnEnrollmentComplete(_)).Times(1);
FACE_ASSERT_OK_AND_ASSIGN(std::unique_ptr<FakeFaceServiceManager> service_mgr,
FakeFaceServiceManager::Create());
EXPECT_CALL(*(service_mgr->mock_service()), StartEnrollment)
.WillOnce(GrpcReplyOk(StartEnrollmentSuccessResponse()));
EXPECT_CALL(*(service_mgr->mock_service()), ProcessFrameForEnrollment)
.WillOnce(GrpcReplyOk(EnrollmentCompleteResponse()));
EXPECT_CALL(*(service_mgr->mock_service()), CompleteEnrollment)
.WillOnce(GrpcReplyOk(CompleteEnrollmentSuccessResponse(TestUserData())));
FACE_ASSERT_OK_AND_ASSIGN(
Lease<brillo::AsyncGrpcClient<faceauth::eora::FaceService>> client,
service_mgr->LeaseClient());
// Create an enrollment session.
mojo::Receiver<FaceEnrollmentSessionDelegate> delegate(&mock_delegate);
mojo::Remote<FaceEnrollmentSession> session_remote;
QueueingStream<EnrollmentSession::InputFrame> stream(/*max_queue_size=*/3);
FACE_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<EnrollmentSession> session,
EnrollmentSession::Create(
bitgen, session_remote.BindNewPipeAndPassReceiver(),
delegate.BindNewPipeAndPassRemote(),
EnrollmentSessionConfig::New(SampleUserHash(),
/*accessibility=*/false),
std::move(client), stream.GetReader()));
// Set up a loop to run until the client disconnects.
base::RunLoop run_loop;
// Add a frame to the input stream.
stream.Write(std::make_unique<Frame>());
// Start the session and run the loop until the service is disconnected.
session->Start(base::BindLambdaForTesting([]() {}),
base::BindLambdaForTesting([&](absl::Status status) {
EXPECT_TRUE(status.ok());
run_loop.Quit();
}));
run_loop.Run();
// On destruction, `mock_delegate` will ensure OnEnrollmentComplete
// was called.
}
TEST(TestEnrollmentSession, TestStartSessionError) {
// Create a mock session delegate, that expects no events to be triggered.
StrictMock<MockFaceEnrollmentSessionDelegate> mock_delegate;
FACE_ASSERT_OK_AND_ASSIGN(std::unique_ptr<FakeFaceServiceManager> service_mgr,
FakeFaceServiceManager::Create());
EXPECT_CALL(*(service_mgr->mock_service()), StartEnrollment)
.WillOnce(GrpcReplyOk(StartEnrollmentFailureResponse()));
FACE_ASSERT_OK_AND_ASSIGN(
Lease<brillo::AsyncGrpcClient<faceauth::eora::FaceService>> client,
service_mgr->LeaseClient());
// Create an enrollment session.
mojo::Receiver<FaceEnrollmentSessionDelegate> delegate(&mock_delegate);
mojo::Remote<FaceEnrollmentSession> session_remote;
QueueingStream<EnrollmentSession::InputFrame> stream(/*max_queue_size=*/3);
FACE_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<EnrollmentSession> session,
EnrollmentSession::Create(bitgen,
session_remote.BindNewPipeAndPassReceiver(),
delegate.BindNewPipeAndPassRemote(),
EnrollmentSessionConfig::New(
SampleUserHash(), /*accessibility=*/false),
std::move(client), stream.GetReader()));
// Set up a loop to run until the client disconnects.
base::RunLoop run_loop;
// Start the session and run the loop until the service is disconnected.
session->Start(
base::BindLambdaForTesting([]() {
EXPECT_FALSE(true); // The start callback should not be invoked.
}),
base::BindLambdaForTesting([&](absl::Status status) {
EXPECT_FALSE(status.ok());
run_loop.Quit();
}));
run_loop.Run();
// On destruction, `mock_delegate` will ensure OnEnrollmentError
// was called.
}
TEST(TestEnrollmentSession, TestSessionStreamError) {
// Create a mock session delegate, that expects an error event to be
// triggered.
StrictMock<MockFaceEnrollmentSessionDelegate> mock_delegate;
EXPECT_CALL(mock_delegate, OnEnrollmentError(_))
.WillOnce(Invoke([&](SessionError error) {
EXPECT_EQ(error, SessionError::UNKNOWN);
}));
FACE_ASSERT_OK_AND_ASSIGN(std::unique_ptr<FakeFaceServiceManager> service_mgr,
FakeFaceServiceManager::Create());
EXPECT_CALL(*(service_mgr->mock_service()), StartEnrollment)
.WillOnce(GrpcReplyOk(StartEnrollmentSuccessResponse()));
EXPECT_CALL(*(service_mgr->mock_service()), AbortEnrollment)
.WillOnce(GrpcReplyOk(AbortEnrollmentSuccessResponse()));
FACE_ASSERT_OK_AND_ASSIGN(
Lease<brillo::AsyncGrpcClient<faceauth::eora::FaceService>> client,
service_mgr->LeaseClient());
// Create an enrollment session.
mojo::Receiver<FaceEnrollmentSessionDelegate> delegate(&mock_delegate);
mojo::Remote<FaceEnrollmentSession> session_remote;
QueueingStream<EnrollmentSession::InputFrame> stream(/*max_queue_size=*/3);
FACE_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<EnrollmentSession> session,
EnrollmentSession::Create(bitgen,
session_remote.BindNewPipeAndPassReceiver(),
delegate.BindNewPipeAndPassRemote(),
EnrollmentSessionConfig::New(
SampleUserHash(), /*accessibility=*/false),
std::move(client), stream.GetReader()));
// Set up a loop to run until the client disconnects.
base::RunLoop run_loop;
// Add an error to the input stream.
stream.Write(absl::InternalError("test frame capture failure"));
// Start the session, and run the loop until the service is disconnected.
session->Start(base::BindLambdaForTesting([]() {}),
base::BindLambdaForTesting([&](absl::Status status) {
EXPECT_FALSE(status.ok());
run_loop.Quit();
}));
run_loop.Run();
// On destruction, `mock_delegate` will ensure OnEnrollmentError
// was called.
}
TEST(TestEnrollmentSession, TestSessionProcessingError) {
// Create a mock session delegate, that expects an error event to be
// triggered.
StrictMock<MockFaceEnrollmentSessionDelegate> mock_delegate;
EXPECT_CALL(mock_delegate, OnEnrollmentError(_))
.WillOnce(Invoke([&](SessionError error) {
EXPECT_EQ(error, SessionError::UNKNOWN);
}));
FACE_ASSERT_OK_AND_ASSIGN(std::unique_ptr<FakeFaceServiceManager> service_mgr,
FakeFaceServiceManager::Create());
EXPECT_CALL(*(service_mgr->mock_service()), StartEnrollment)
.WillOnce(GrpcReplyOk(StartEnrollmentSuccessResponse()));
EXPECT_CALL(*(service_mgr->mock_service()), ProcessFrameForEnrollment)
.WillOnce(GrpcReplyOk(EnrollmentProcessingErrorResponse()));
EXPECT_CALL(*(service_mgr->mock_service()), AbortEnrollment)
.WillOnce(GrpcReplyOk(AbortEnrollmentSuccessResponse()));
FACE_ASSERT_OK_AND_ASSIGN(
Lease<brillo::AsyncGrpcClient<faceauth::eora::FaceService>> client,
service_mgr->LeaseClient());
// Create an enrollment session.
mojo::Receiver<FaceEnrollmentSessionDelegate> delegate(&mock_delegate);
mojo::Remote<FaceEnrollmentSession> session_remote;
QueueingStream<EnrollmentSession::InputFrame> stream(/*max_queue_size=*/3);
FACE_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<EnrollmentSession> session,
EnrollmentSession::Create(bitgen,
session_remote.BindNewPipeAndPassReceiver(),
delegate.BindNewPipeAndPassRemote(),
EnrollmentSessionConfig::New(
SampleUserHash(), /*accessibility=*/false),
std::move(client), stream.GetReader()));
// Set up a loop to run until the client disconnects.
base::RunLoop run_loop;
// Add an empty frame to the input stream.
stream.Write(std::make_unique<Frame>());
// Start the session, and run the loop until the service is disconnected.
session->Start(base::BindLambdaForTesting([]() {}),
base::BindLambdaForTesting([&](absl::Status status) {
EXPECT_FALSE(status.ok());
run_loop.Quit();
}));
run_loop.Run();
// On destruction, `mock_delegate` will ensure OnEnrollmentError
// was called.
}
TEST(TestEnrollmentSession, TestSessionCancelled) {
// Create a mock session delegate, that expects an cancelled event to be
// triggered.
StrictMock<MockFaceEnrollmentSessionDelegate> mock_delegate;
EXPECT_CALL(mock_delegate, OnEnrollmentCancelled()).Times(1);
FACE_ASSERT_OK_AND_ASSIGN(std::unique_ptr<FakeFaceServiceManager> service_mgr,
FakeFaceServiceManager::Create());
EXPECT_CALL(*(service_mgr->mock_service()), StartEnrollment)
.WillOnce(GrpcReplyOk(StartEnrollmentSuccessResponse()));
EXPECT_CALL(*(service_mgr->mock_service()), AbortEnrollment)
.WillOnce(GrpcReplyOk(AbortEnrollmentSuccessResponse()));
FACE_ASSERT_OK_AND_ASSIGN(
Lease<brillo::AsyncGrpcClient<faceauth::eora::FaceService>> client,
service_mgr->LeaseClient());
// Create an enrollment session.
mojo::Receiver<FaceEnrollmentSessionDelegate> delegate(&mock_delegate);
mojo::Remote<FaceEnrollmentSession> session_remote;
QueueingStream<EnrollmentSession::InputFrame> stream(/*max_queue_size=*/3);
FACE_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<EnrollmentSession> session,
EnrollmentSession::Create(bitgen,
session_remote.BindNewPipeAndPassReceiver(),
delegate.BindNewPipeAndPassRemote(),
EnrollmentSessionConfig::New(
SampleUserHash(), /*accessibility=*/false),
std::move(client), stream.GetReader()));
// Set up a loop to run until the client disconnects.
base::RunLoop run_loop;
// Start the session and run the loop until the service
// is disconnected.
session->Start(base::BindLambdaForTesting([&]() {
// Disconnect session remote to cancel.
session_remote.reset();
}),
base::BindLambdaForTesting([&](absl::Status status) {
EXPECT_FALSE(status.ok());
run_loop.Quit();
}));
run_loop.Run();
// On destruction, `mock_delegate` will ensure OnEnrollmentError
// was called.
}
} // namespace faced