| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/nearby_sharing/incoming_frames_reader.h" |
| |
| #include <vector> |
| |
| #include "base/run_loop.h" |
| #include "base/test/bind.h" |
| #include "base/time/time.h" |
| #include "chrome/services/sharing/public/proto/wire_format.pb.h" |
| #include "chromeos/ash/components/nearby/common/connections_manager/fake_nearby_connection.h" |
| #include "chromeos/ash/services/nearby/public/cpp/mock_nearby_process_manager.h" |
| #include "chromeos/ash/services/nearby/public/cpp/mock_nearby_sharing_decoder.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| |
| constexpr base::TimeDelta kTimeout = base::Milliseconds(1000); |
| |
| std::vector<uint8_t> GetIntroductionFrame() { |
| sharing::nearby::Frame frame = sharing::nearby::Frame(); |
| sharing::nearby::V1Frame* v1frame = frame.mutable_v1(); |
| v1frame->set_type(sharing::nearby::V1Frame_FrameType_INTRODUCTION); |
| v1frame->mutable_introduction(); |
| |
| std::vector<uint8_t> data; |
| data.resize(frame.ByteSizeLong()); |
| EXPECT_TRUE(frame.SerializeToArray(&data[0], frame.ByteSizeLong())); |
| |
| return data; |
| } |
| |
| std::vector<uint8_t> GetCancelFrame() { |
| sharing::nearby::Frame frame = sharing::nearby::Frame(); |
| sharing::nearby::V1Frame* v1frame = frame.mutable_v1(); |
| v1frame->set_type(sharing::nearby::V1Frame_FrameType_CANCEL); |
| |
| std::vector<uint8_t> data; |
| data.resize(frame.ByteSizeLong()); |
| EXPECT_TRUE(frame.SerializeToArray(&data[0], frame.ByteSizeLong())); |
| |
| return data; |
| } |
| |
| void ExpectIntroductionFrame( |
| const std::optional<sharing::mojom::V1FramePtr>& frame) { |
| ASSERT_TRUE(frame); |
| EXPECT_TRUE((*frame)->is_introduction()); |
| } |
| |
| void ExpectCancelFrame(const std::optional<sharing::mojom::V1FramePtr>& frame) { |
| ASSERT_TRUE(frame); |
| EXPECT_TRUE((*frame)->is_cancel_frame()); |
| } |
| |
| } // namespace |
| |
| class IncomingFramesReaderTest : public testing::Test { |
| public: |
| IncomingFramesReaderTest() |
| : frames_reader_(&mock_process_manager_, &mock_nearby_connection_) {} |
| |
| ~IncomingFramesReaderTest() override = default; |
| |
| void SetUp() override { |
| EXPECT_CALL(mock_process_manager_, GetNearbyProcessReference) |
| .WillRepeatedly([&](ash::nearby::NearbyProcessManager:: |
| NearbyProcessStoppedCallback) { |
| auto mock_reference_ptr = |
| std::make_unique<ash::nearby::MockNearbyProcessManager:: |
| MockNearbyProcessReference>(); |
| |
| EXPECT_CALL(*(mock_reference_ptr.get()), GetNearbySharingDecoder) |
| .WillRepeatedly( |
| testing::ReturnRef(mock_decoder_.shared_remote())); |
| |
| return mock_reference_ptr; |
| }); |
| } |
| |
| FakeNearbyConnection& connection() { return mock_nearby_connection_; } |
| |
| testing::StrictMock<ash::nearby::MockNearbySharingDecoder>& decoder() { |
| return mock_decoder_; |
| } |
| |
| IncomingFramesReader& frames_reader() { return frames_reader_; } |
| |
| private: |
| content::BrowserTaskEnvironment task_environment_; |
| FakeNearbyConnection mock_nearby_connection_; |
| testing::StrictMock<ash::nearby::MockNearbyProcessManager> |
| mock_process_manager_; |
| testing::StrictMock<ash::nearby::MockNearbySharingDecoder> mock_decoder_; |
| IncomingFramesReader frames_reader_; |
| }; |
| |
| TEST_F(IncomingFramesReaderTest, ReadTimedOut) { |
| EXPECT_CALL(decoder(), DecodeFrame(testing::_, testing::_)).Times(0); |
| |
| base::RunLoop run_loop; |
| frames_reader().ReadFrame( |
| sharing::mojom::V1Frame::Tag::kIntroduction, |
| base::BindLambdaForTesting( |
| [&](std::optional<sharing::mojom::V1FramePtr> frame) { |
| EXPECT_FALSE(frame); |
| run_loop.Quit(); |
| }), |
| kTimeout); |
| run_loop.Run(); |
| |
| // Ensure that the OnDataReadFromConnection callback is not run since the read |
| // timed out. |
| EXPECT_FALSE(connection().has_read_callback_been_run()); |
| // Ensure that the IncomingFramesReader does not close the connection. |
| EXPECT_FALSE(connection().IsClosed()); |
| } |
| |
| TEST_F(IncomingFramesReaderTest, ReadAnyFrameSuccessful) { |
| std::vector<uint8_t> introduction_frame = GetIntroductionFrame(); |
| connection().AppendReadableData(introduction_frame); |
| |
| EXPECT_CALL(decoder(), |
| DecodeFrame(testing::Eq(introduction_frame), testing::_)) |
| .WillOnce(testing::Invoke( |
| [&](const std::vector<uint8_t>& data, |
| ash::nearby::MockNearbySharingDecoder::DecodeFrameCallback |
| callback) { |
| sharing::mojom::V1FramePtr mojo_v1frame = |
| sharing::mojom::V1Frame::NewIntroduction( |
| sharing::mojom::IntroductionFrame::New()); |
| |
| sharing::mojom::FramePtr mojo_frame = |
| sharing::mojom::Frame::NewV1(std::move(mojo_v1frame)); |
| std::move(callback).Run(std::move(mojo_frame)); |
| })); |
| |
| base::RunLoop run_loop; |
| frames_reader().ReadFrame(base::BindLambdaForTesting( |
| [&](std::optional<sharing::mojom::V1FramePtr> frame) { |
| ExpectIntroductionFrame(frame); |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| } |
| |
| TEST_F(IncomingFramesReaderTest, ReadSuccessful) { |
| std::vector<uint8_t> introduction_frame = GetIntroductionFrame(); |
| connection().AppendReadableData(introduction_frame); |
| |
| EXPECT_CALL(decoder(), |
| DecodeFrame(testing::Eq(introduction_frame), testing::_)) |
| .WillOnce(testing::Invoke( |
| [&](const std::vector<uint8_t>& data, |
| ash::nearby::MockNearbySharingDecoder::DecodeFrameCallback |
| callback) { |
| sharing::mojom::V1FramePtr mojo_v1frame = |
| sharing::mojom::V1Frame::NewIntroduction( |
| sharing::mojom::IntroductionFrame::New()); |
| |
| sharing::mojom::FramePtr mojo_frame = |
| sharing::mojom::Frame::NewV1(std::move(mojo_v1frame)); |
| std::move(callback).Run(std::move(mojo_frame)); |
| })); |
| |
| base::RunLoop run_loop; |
| frames_reader().ReadFrame( |
| sharing::mojom::V1Frame::Tag::kIntroduction, |
| base::BindLambdaForTesting( |
| [&](std::optional<sharing::mojom::V1FramePtr> frame) { |
| ExpectIntroductionFrame(frame); |
| run_loop.Quit(); |
| }), |
| kTimeout); |
| run_loop.Run(); |
| } |
| |
| TEST_F(IncomingFramesReaderTest, ReadSuccessful_JumbledFramesOrdering) { |
| std::vector<uint8_t> cancel_frame = GetCancelFrame(); |
| connection().AppendReadableData(cancel_frame); |
| |
| std::vector<uint8_t> introduction_frame = GetIntroductionFrame(); |
| connection().AppendReadableData(introduction_frame); |
| |
| EXPECT_CALL(decoder(), DecodeFrame(testing::_, testing::_)) |
| .WillOnce(testing::Invoke( |
| [&](const std::vector<uint8_t>& data, |
| ash::nearby::MockNearbySharingDecoder::DecodeFrameCallback |
| callback) { |
| EXPECT_EQ(cancel_frame, data); |
| sharing::mojom::V1FramePtr mojo_v1frame = |
| sharing::mojom::V1Frame::NewCancelFrame( |
| sharing::mojom::CancelFrame::New()); |
| |
| sharing::mojom::FramePtr mojo_frame = |
| sharing::mojom::Frame::NewV1(std::move(mojo_v1frame)); |
| std::move(callback).Run(std::move(mojo_frame)); |
| })) |
| .WillOnce(testing::Invoke( |
| [&](const std::vector<uint8_t>& data, |
| ash::nearby::MockNearbySharingDecoder::DecodeFrameCallback |
| callback) { |
| EXPECT_EQ(introduction_frame, data); |
| sharing::mojom::V1FramePtr mojo_v1frame = |
| sharing::mojom::V1Frame::NewIntroduction( |
| sharing::mojom::IntroductionFrame::New()); |
| |
| sharing::mojom::FramePtr mojo_frame = |
| sharing::mojom::Frame::NewV1(std::move(mojo_v1frame)); |
| std::move(callback).Run(std::move(mojo_frame)); |
| })); |
| |
| base::RunLoop run_loop_introduction; |
| frames_reader().ReadFrame( |
| sharing::mojom::V1Frame::Tag::kIntroduction, |
| base::BindLambdaForTesting( |
| [&](std::optional<sharing::mojom::V1FramePtr> frame) { |
| ExpectIntroductionFrame(frame); |
| run_loop_introduction.Quit(); |
| }), |
| kTimeout); |
| run_loop_introduction.Run(); |
| } |
| |
| TEST_F(IncomingFramesReaderTest, JumbledFramesOrdering_ReadFromCache) { |
| std::vector<uint8_t> cancel_frame = GetCancelFrame(); |
| connection().AppendReadableData(cancel_frame); |
| |
| std::vector<uint8_t> introduction_frame = GetIntroductionFrame(); |
| connection().AppendReadableData(introduction_frame); |
| |
| EXPECT_CALL(decoder(), DecodeFrame(testing::_, testing::_)) |
| .WillOnce(testing::Invoke( |
| [&](const std::vector<uint8_t>& data, |
| ash::nearby::MockNearbySharingDecoder::DecodeFrameCallback |
| callback) { |
| EXPECT_EQ(cancel_frame, data); |
| sharing::mojom::V1FramePtr mojo_v1frame = |
| sharing::mojom::V1Frame::NewCancelFrame( |
| sharing::mojom::CancelFrame::New()); |
| |
| sharing::mojom::FramePtr mojo_frame = |
| sharing::mojom::Frame::NewV1(std::move(mojo_v1frame)); |
| std::move(callback).Run(std::move(mojo_frame)); |
| })) |
| .WillOnce(testing::Invoke( |
| [&](const std::vector<uint8_t>& data, |
| ash::nearby::MockNearbySharingDecoder::DecodeFrameCallback |
| callback) { |
| EXPECT_EQ(introduction_frame, data); |
| sharing::mojom::V1FramePtr mojo_v1frame = |
| sharing::mojom::V1Frame::NewIntroduction( |
| sharing::mojom::IntroductionFrame::New()); |
| |
| sharing::mojom::FramePtr mojo_frame = |
| sharing::mojom::Frame::NewV1(std::move(mojo_v1frame)); |
| std::move(callback).Run(std::move(mojo_frame)); |
| })); |
| |
| base::RunLoop run_loop_introduction; |
| frames_reader().ReadFrame( |
| sharing::mojom::V1Frame::Tag::kIntroduction, |
| base::BindLambdaForTesting( |
| [&](std::optional<sharing::mojom::V1FramePtr> frame) { |
| ExpectIntroductionFrame(frame); |
| run_loop_introduction.Quit(); |
| }), |
| kTimeout); |
| run_loop_introduction.Run(); |
| |
| // Reading any frame should return CancelFrame. |
| base::RunLoop run_loop_cancel; |
| frames_reader().ReadFrame(base::BindLambdaForTesting( |
| [&](std::optional<sharing::mojom::V1FramePtr> frame) { |
| ExpectCancelFrame(frame); |
| run_loop_cancel.Quit(); |
| })); |
| run_loop_cancel.Run(); |
| } |
| |
| TEST_F(IncomingFramesReaderTest, ReadAfterConnectionClosed) { |
| EXPECT_CALL(decoder(), DecodeFrame(testing::_, testing::_)).Times(0); |
| |
| base::RunLoop run_loop_before_close; |
| frames_reader().ReadFrame( |
| sharing::mojom::V1Frame::Tag::kIntroduction, |
| base::BindLambdaForTesting( |
| [&](std::optional<sharing::mojom::V1FramePtr> frame) { |
| EXPECT_FALSE(frame); |
| run_loop_before_close.Quit(); |
| }), |
| kTimeout); |
| |
| connection().Close(); |
| run_loop_before_close.Run(); |
| } |