| // 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. |
| |
| #ifdef UNSAFE_BUFFERS_BUILD |
| // TODO(crbug.com/40285824): Remove this and convert code to safer constructs. |
| #pragma allow_unsafe_buffers |
| #endif |
| |
| #include "chrome/browser/webshare/win/fake_random_access_stream.h" |
| |
| #include <wrl/event.h> |
| #include <wrl/implements.h> |
| |
| #include "base/functional/callback_helpers.h" |
| #include "base/run_loop.h" |
| #include "base/test/task_environment.h" |
| #include "chrome/browser/webshare/win/fake_buffer.h" |
| #include "testing/gtest/include/gtest/gtest-spi.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ABI::Windows::Foundation::IAsyncOperation; |
| using ABI::Windows::Foundation::IAsyncOperationCompletedHandler; |
| using ABI::Windows::Foundation::IAsyncOperationWithProgress; |
| using ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler; |
| using ABI::Windows::Foundation::IClosable; |
| using ABI::Windows::Storage::Streams::IBuffer; |
| using ABI::Windows::Storage::Streams::IInputStream; |
| using ABI::Windows::Storage::Streams::InputStreamOptions; |
| using ABI::Windows::Storage::Streams::IOutputStream; |
| using Microsoft::WRL::Callback; |
| using Microsoft::WRL::ComPtr; |
| using Microsoft::WRL::Make; |
| |
| namespace webshare { |
| |
| TEST(FakeRandomAccessStreamTest, InvalidSeek) { |
| auto stream = Make<FakeRandomAccessStream>(); |
| ASSERT_HRESULT_SUCCEEDED(stream->Seek(0)); |
| EXPECT_NONFATAL_FAILURE(ASSERT_HRESULT_FAILED(stream->Seek(1)), "Seek"); |
| ASSERT_HRESULT_SUCCEEDED(stream->Close()); |
| } |
| |
| TEST(FakeRandomAccessStreamTest, UsageAfterClose) { |
| base::test::SingleThreadTaskEnvironment task_environment; |
| |
| auto stream = Make<FakeRandomAccessStream>(); |
| ASSERT_HRESULT_SUCCEEDED(stream->Close()); |
| |
| // IRandomAccessStream APIs |
| UINT64 size; |
| EXPECT_NONFATAL_FAILURE(ASSERT_HRESULT_FAILED(stream->get_Size(&size)), |
| "closed"); |
| EXPECT_NONFATAL_FAILURE(ASSERT_HRESULT_FAILED(stream->put_Size(7)), "closed"); |
| ComPtr<IInputStream> input_stream; |
| EXPECT_NONFATAL_FAILURE( |
| ASSERT_HRESULT_FAILED(stream->GetInputStreamAt(0, &input_stream)), |
| "closed"); |
| ComPtr<IOutputStream> output_stream; |
| EXPECT_NONFATAL_FAILURE( |
| ASSERT_HRESULT_FAILED(stream->GetOutputStreamAt(0, &output_stream)), |
| "closed"); |
| UINT64 position; |
| EXPECT_NONFATAL_FAILURE( |
| ASSERT_HRESULT_FAILED(stream->get_Position(&position)), "closed"); |
| EXPECT_NONFATAL_FAILURE(ASSERT_HRESULT_FAILED(stream->Seek(0)), "closed"); |
| boolean can_read; |
| EXPECT_NONFATAL_FAILURE(ASSERT_HRESULT_FAILED(stream->get_CanRead(&can_read)), |
| "closed"); |
| boolean can_write; |
| EXPECT_NONFATAL_FAILURE( |
| ASSERT_HRESULT_FAILED(stream->get_CanRead(&can_write)), "closed"); |
| |
| // IClosable APIs |
| EXPECT_NONFATAL_FAILURE(ASSERT_HRESULT_FAILED(stream->Close()), "closed"); |
| |
| // IInputStream APIs |
| auto buffer = Make<FakeBuffer>(4); |
| ComPtr<IAsyncOperationWithProgress<IBuffer*, UINT32>> read_operation; |
| EXPECT_NONFATAL_FAILURE( |
| ASSERT_HRESULT_FAILED(stream->ReadAsync( |
| buffer.Get(), 4, InputStreamOptions::InputStreamOptions_None, |
| &read_operation)), |
| "closed"); |
| |
| // IOutputStream APIs |
| ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> write_operation; |
| EXPECT_NONFATAL_FAILURE( |
| ASSERT_HRESULT_FAILED(stream->WriteAsync(buffer.Get(), &write_operation)), |
| "closed"); |
| ComPtr<IAsyncOperation<bool>> flush_operation; |
| EXPECT_NONFATAL_FAILURE( |
| ASSERT_HRESULT_FAILED(stream->FlushAsync(&flush_operation)), "closed"); |
| |
| // Test APIs |
| // EXPECT_FATAL_FAILURE() can only reference globals and statics. |
| static ComPtr<FakeRandomAccessStream>& static_stream = stream; |
| EXPECT_FATAL_FAILURE(static_stream->OnClose(base::DoNothing()), "closed"); |
| } |
| |
| TEST(FakeRandomAccessStreamTest, CompetingAsyncCalls) { |
| base::test::SingleThreadTaskEnvironment task_environment; |
| |
| auto stream = Make<FakeRandomAccessStream>(); |
| |
| ComPtr<IAsyncOperation<bool>> flush_operation; |
| ComPtr<IAsyncOperationWithProgress<IBuffer*, UINT32>> read_operation; |
| ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> write_operation; |
| auto buffer = Make<FakeBuffer>(4); |
| |
| { |
| base::RunLoop run_loop; |
| ASSERT_HRESULT_SUCCEEDED( |
| stream->WriteAsync(buffer.Get(), &write_operation)); |
| ASSERT_HRESULT_SUCCEEDED(write_operation->put_Completed( |
| Callback<IAsyncOperationWithProgressCompletedHandler<UINT32, UINT32>>( |
| [&run_loop]( |
| IAsyncOperationWithProgress<UINT32, UINT32>* async_operation, |
| AsyncStatus async_status) { |
| run_loop.Quit(); |
| return S_OK; |
| }) |
| .Get())); |
| |
| EXPECT_NONFATAL_FAILURE( |
| ASSERT_HRESULT_FAILED(stream->FlushAsync(&flush_operation)), |
| "in progress"); |
| EXPECT_NONFATAL_FAILURE( |
| ASSERT_HRESULT_FAILED(stream->ReadAsync( |
| buffer.Get(), 4, InputStreamOptions::InputStreamOptions_None, |
| &read_operation)), |
| "in progress"); |
| EXPECT_NONFATAL_FAILURE(ASSERT_HRESULT_FAILED(stream->WriteAsync( |
| buffer.Get(), &write_operation)), |
| "in progress"); |
| run_loop.Run(); |
| } |
| |
| { |
| base::RunLoop run_loop; |
| ASSERT_HRESULT_SUCCEEDED(stream->ReadAsync( |
| buffer.Get(), 4, InputStreamOptions::InputStreamOptions_None, |
| &read_operation)); |
| ASSERT_HRESULT_SUCCEEDED(read_operation->put_Completed( |
| Callback<IAsyncOperationWithProgressCompletedHandler<IBuffer*, UINT32>>( |
| [&run_loop]( |
| IAsyncOperationWithProgress<IBuffer*, UINT32>* async_operation, |
| AsyncStatus async_status) { |
| run_loop.Quit(); |
| return S_OK; |
| }) |
| .Get())); |
| |
| EXPECT_NONFATAL_FAILURE( |
| ASSERT_HRESULT_FAILED(stream->FlushAsync(&flush_operation)), |
| "in progress"); |
| EXPECT_NONFATAL_FAILURE( |
| ASSERT_HRESULT_FAILED(stream->ReadAsync( |
| buffer.Get(), 4, InputStreamOptions::InputStreamOptions_None, |
| &read_operation)), |
| "in progress"); |
| EXPECT_NONFATAL_FAILURE(ASSERT_HRESULT_FAILED(stream->WriteAsync( |
| buffer.Get(), &write_operation)), |
| "in progress"); |
| run_loop.Run(); |
| } |
| |
| { |
| base::RunLoop run_loop; |
| ASSERT_HRESULT_SUCCEEDED(stream->FlushAsync(&flush_operation)); |
| ASSERT_HRESULT_SUCCEEDED(flush_operation->put_Completed( |
| Callback<IAsyncOperationCompletedHandler<bool>>( |
| [&run_loop](IAsyncOperation<bool>* async_operation, |
| AsyncStatus async_status) { |
| run_loop.Quit(); |
| return S_OK; |
| }) |
| .Get())); |
| |
| EXPECT_NONFATAL_FAILURE( |
| ASSERT_HRESULT_FAILED(stream->FlushAsync(&flush_operation)), |
| "in progress"); |
| EXPECT_NONFATAL_FAILURE( |
| ASSERT_HRESULT_FAILED(stream->ReadAsync( |
| buffer.Get(), 4, InputStreamOptions::InputStreamOptions_None, |
| &read_operation)), |
| "in progress"); |
| EXPECT_NONFATAL_FAILURE(ASSERT_HRESULT_FAILED(stream->WriteAsync( |
| buffer.Get(), &write_operation)), |
| "in progress"); |
| run_loop.Run(); |
| } |
| ASSERT_HRESULT_SUCCEEDED(stream->Close()); |
| } |
| |
| TEST(FakeRandomAccessStreamTest, BasicReadWrite) { |
| base::test::SingleThreadTaskEnvironment task_environment; |
| |
| // Initialize an input and output stream based on the same stream |
| ComPtr<IInputStream> input_stream; |
| ComPtr<IOutputStream> output_stream; |
| { |
| auto stream = Make<FakeRandomAccessStream>(); |
| UINT64 position; |
| ASSERT_HRESULT_SUCCEEDED(stream->get_Position(&position)); |
| ASSERT_EQ(position, 0u); |
| |
| ASSERT_HRESULT_SUCCEEDED(stream->GetInputStreamAt(0, &input_stream)); |
| ASSERT_HRESULT_SUCCEEDED(stream->GetOutputStreamAt(0, &output_stream)); |
| ASSERT_HRESULT_SUCCEEDED(stream->Close()); |
| } |
| |
| // Create a filled buffer that reads "abcd" |
| auto buffer = Make<FakeBuffer>(4); |
| ASSERT_HRESULT_SUCCEEDED(buffer->put_Length(4)); |
| byte* raw_buffer; |
| ASSERT_HRESULT_SUCCEEDED(buffer->Buffer(&raw_buffer)); |
| raw_buffer[0] = 'a'; |
| raw_buffer[1] = 'b'; |
| raw_buffer[2] = 'c'; |
| raw_buffer[3] = 'd'; |
| |
| // Write the buffer to the output stream |
| { |
| base::RunLoop run_loop; |
| ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> write_operation; |
| ASSERT_HRESULT_SUCCEEDED( |
| output_stream->WriteAsync(buffer.Get(), &write_operation)); |
| ASSERT_HRESULT_SUCCEEDED(write_operation->put_Completed( |
| Callback<IAsyncOperationWithProgressCompletedHandler<UINT32, UINT32>>( |
| [&run_loop]( |
| IAsyncOperationWithProgress<UINT32, UINT32>* async_operation, |
| AsyncStatus async_status) { |
| EXPECT_EQ(async_status, AsyncStatus::Completed); |
| UINT32 results; |
| EXPECT_HRESULT_SUCCEEDED(async_operation->GetResults(&results)); |
| EXPECT_EQ(results, 4u); |
| run_loop.Quit(); |
| return S_OK; |
| }) |
| .Get())); |
| run_loop.Run(); |
| } |
| |
| // Update the same buffer to now read "ef" |
| raw_buffer[0] = 'e'; |
| raw_buffer[1] = 'f'; |
| ASSERT_HRESULT_SUCCEEDED(buffer->put_Length(2)); |
| |
| // Write the buffer to the output stream |
| { |
| base::RunLoop run_loop; |
| ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> write_operation; |
| ASSERT_HRESULT_SUCCEEDED( |
| output_stream->WriteAsync(buffer.Get(), &write_operation)); |
| ASSERT_HRESULT_SUCCEEDED(write_operation->put_Completed( |
| Callback<IAsyncOperationWithProgressCompletedHandler<UINT32, UINT32>>( |
| [&run_loop]( |
| IAsyncOperationWithProgress<UINT32, UINT32>* async_operation, |
| AsyncStatus async_status) { |
| EXPECT_EQ(async_status, AsyncStatus::Completed); |
| UINT32 results; |
| EXPECT_HRESULT_SUCCEEDED(async_operation->GetResults(&results)); |
| EXPECT_EQ(results, 2u); |
| run_loop.Quit(); |
| return S_OK; |
| }) |
| .Get())); |
| run_loop.Run(); |
| } |
| |
| // Flush the output stream |
| { |
| base::RunLoop run_loop; |
| ComPtr<IAsyncOperation<bool>> flush_operation; |
| ASSERT_HRESULT_SUCCEEDED(output_stream->FlushAsync(&flush_operation)); |
| ASSERT_HRESULT_SUCCEEDED(flush_operation->put_Completed( |
| Callback<IAsyncOperationCompletedHandler<bool>>( |
| [&run_loop](IAsyncOperation<bool>* async_operation, |
| AsyncStatus async_status) { |
| EXPECT_EQ(async_status, AsyncStatus::Completed); |
| boolean results; |
| EXPECT_HRESULT_SUCCEEDED(async_operation->GetResults(&results)); |
| EXPECT_EQ(results, TRUE); |
| run_loop.Quit(); |
| return S_OK; |
| }) |
| .Get())); |
| run_loop.Run(); |
| ComPtr<IClosable> closable_stream; |
| ASSERT_HRESULT_SUCCEEDED(output_stream.As(&closable_stream)); |
| ASSERT_HRESULT_SUCCEEDED(closable_stream->Close()); |
| } |
| |
| // Read the input stream to the buffer |
| { |
| base::RunLoop run_loop; |
| ComPtr<IAsyncOperationWithProgress<IBuffer*, UINT32>> read_operation; |
| ASSERT_HRESULT_SUCCEEDED(input_stream->ReadAsync( |
| buffer.Get(), 4, InputStreamOptions::InputStreamOptions_None, |
| &read_operation)); |
| ASSERT_HRESULT_SUCCEEDED(read_operation->put_Completed( |
| Callback<IAsyncOperationWithProgressCompletedHandler<IBuffer*, UINT32>>( |
| [&run_loop, &buffer]( |
| IAsyncOperationWithProgress<IBuffer*, UINT32>* async_operation, |
| AsyncStatus async_status) { |
| EXPECT_EQ(async_status, AsyncStatus::Completed); |
| ComPtr<IBuffer> results; |
| EXPECT_HRESULT_SUCCEEDED(async_operation->GetResults(&results)); |
| EXPECT_EQ(results, buffer); |
| run_loop.Quit(); |
| return S_OK; |
| }) |
| .Get())); |
| run_loop.Run(); |
| } |
| |
| UINT32 length; |
| ASSERT_HRESULT_SUCCEEDED(buffer->get_Length(&length)); |
| ASSERT_EQ(length, 4u); |
| ASSERT_EQ(raw_buffer[0], 'a'); |
| ASSERT_EQ(raw_buffer[1], 'b'); |
| ASSERT_EQ(raw_buffer[2], 'c'); |
| ASSERT_EQ(raw_buffer[3], 'd'); |
| |
| // Read the remaining input stream to the buffer |
| { |
| base::RunLoop run_loop; |
| ComPtr<IAsyncOperationWithProgress<IBuffer*, UINT32>> read_operation; |
| ASSERT_HRESULT_SUCCEEDED(input_stream->ReadAsync( |
| buffer.Get(), 2, InputStreamOptions::InputStreamOptions_None, |
| &read_operation)); |
| ASSERT_HRESULT_SUCCEEDED(read_operation->put_Completed( |
| Callback<IAsyncOperationWithProgressCompletedHandler<IBuffer*, UINT32>>( |
| [&run_loop, &buffer]( |
| IAsyncOperationWithProgress<IBuffer*, UINT32>* async_operation, |
| AsyncStatus async_status) { |
| EXPECT_EQ(async_status, AsyncStatus::Completed); |
| ComPtr<IBuffer> results; |
| EXPECT_HRESULT_SUCCEEDED(async_operation->GetResults(&results)); |
| EXPECT_EQ(results, buffer); |
| run_loop.Quit(); |
| return S_OK; |
| }) |
| .Get())); |
| run_loop.Run(); |
| ComPtr<IClosable> closable_stream; |
| ASSERT_HRESULT_SUCCEEDED(input_stream.As(&closable_stream)); |
| ASSERT_HRESULT_SUCCEEDED(closable_stream->Close()); |
| } |
| |
| ASSERT_HRESULT_SUCCEEDED(buffer->get_Length(&length)); |
| ASSERT_EQ(length, 2u); |
| ASSERT_EQ(raw_buffer[0], 'e'); |
| ASSERT_EQ(raw_buffer[1], 'f'); |
| } |
| |
| } // namespace webshare |