| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/webshare/win/fake_storage_file_statics.h" |
| |
| #include <windows.foundation.h> |
| #include <windows.storage.streams.h> |
| #include <wrl/module.h> |
| |
| #include <memory> |
| #include <tuple> |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/test/bind.h" |
| #include "base/test/fake_iasync_operation_win.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/win/scoped_hstring.h" |
| #include "chrome/browser/webshare/win/fake_random_access_stream.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ABI::Windows::Foundation::DateTime; |
| using ABI::Windows::Foundation::IAsyncAction; |
| using ABI::Windows::Foundation::IAsyncOperation; |
| using ABI::Windows::Foundation::IUriRuntimeClass; |
| using ABI::Windows::Storage::FileAccessMode; |
| using ABI::Windows::Storage::FileAttributes; |
| using ABI::Windows::Storage::IStorageFile; |
| using ABI::Windows::Storage::IStorageFolder; |
| using ABI::Windows::Storage::IStorageItem; |
| using ABI::Windows::Storage::IStreamedFileDataRequestedHandler; |
| using ABI::Windows::Storage::NameCollisionOption; |
| using ABI::Windows::Storage::StorageDeleteOption; |
| using ABI::Windows::Storage::StorageFile; |
| using ABI::Windows::Storage::StorageItemTypes; |
| using ABI::Windows::Storage::StorageStreamTransaction; |
| using ABI::Windows::Storage::FileProperties::BasicProperties; |
| using ABI::Windows::Storage::Streams::IOutputStream; |
| using ABI::Windows::Storage::Streams::IRandomAccessStream; |
| using ABI::Windows::Storage::Streams::IRandomAccessStreamReference; |
| using Microsoft::WRL::ComPtr; |
| using Microsoft::WRL::Make; |
| using Microsoft::WRL::RuntimeClass; |
| using Microsoft::WRL::RuntimeClassFlags; |
| using Microsoft::WRL::WinRtClassicComMix; |
| |
| namespace webshare { |
| namespace { |
| |
| class FakeStorageFile final |
| : public RuntimeClass<RuntimeClassFlags<WinRtClassicComMix>, |
| IStorageFile, |
| IStorageItem> { |
| public: |
| FakeStorageFile(HSTRING display_name_with_extension, |
| IStreamedFileDataRequestedHandler* data_requested, |
| IRandomAccessStreamReference* thumbnail) |
| : streamed_file_data_requested_handler_(data_requested) { |
| // ScopedHString takes ownership of the HSTRING provided to it, but taking |
| // ownership is not an expected behavior when passing an HSTRING to a system |
| // API, so we use a temporary ScopedHString to make a copy we can safely own |
| // and release ownership of the original 'back' to the caller. |
| base::win::ScopedHString holder(display_name_with_extension); |
| display_name_with_extension_ = holder.GetAsUTF8(); |
| std::ignore = holder.release(); |
| } |
| FakeStorageFile(const FakeStorageFile&) = delete; |
| FakeStorageFile& operator=(const FakeStorageFile&) = delete; |
| ~FakeStorageFile() final { |
| EXPECT_FALSE(open_async_in_progress_) |
| << "FakeStorageFile destroyed while open operation is in progress."; |
| } |
| |
| // ABI::Windows::Storage::IStorageFile |
| IFACEMETHODIMP get_FileType(HSTRING* value) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP get_ContentType(HSTRING* value) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP OpenAsync( |
| FileAccessMode access_mode, |
| IAsyncOperation<IRandomAccessStream*>** operation) final { |
| if (open_async_in_progress_) { |
| ADD_FAILURE() |
| << "OpenAsync called while an open operation is in progress."; |
| return E_ILLEGAL_METHOD_CALL; |
| } |
| |
| auto fake_iasync_operation = |
| Make<base::win::FakeIAsyncOperation<IRandomAccessStream*>>(); |
| HRESULT hr = fake_iasync_operation->QueryInterface(IID_PPV_ARGS(operation)); |
| if (FAILED(hr)) { |
| EXPECT_HRESULT_SUCCEEDED(hr); |
| return hr; |
| } |
| |
| bool success = base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&FakeStorageFile::OnOpenAsync, |
| weak_factory_.GetWeakPtr(), fake_iasync_operation)); |
| if (!success) { |
| EXPECT_TRUE(success); |
| return E_ASYNC_OPERATION_NOT_STARTED; |
| } |
| |
| open_async_in_progress_ = true; |
| return S_OK; |
| } |
| IFACEMETHODIMP OpenTransactedWriteAsync( |
| IAsyncOperation<StorageStreamTransaction*>** operation) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP CopyOverloadDefaultNameAndOptions( |
| IStorageFolder* destination_folder, |
| IAsyncOperation<StorageFile*>** operation) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP CopyOverloadDefaultOptions( |
| IStorageFolder* destination_folder, |
| HSTRING desired_new_name, |
| IAsyncOperation<StorageFile*>** operation) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP CopyOverload(IStorageFolder* destination_folder, |
| HSTRING desired_new_name, |
| NameCollisionOption option, |
| IAsyncOperation<StorageFile*>** operation) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP |
| CopyAndReplaceAsync(IStorageFile* file_to_replace, |
| IAsyncAction** operation) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP MoveOverloadDefaultNameAndOptions( |
| IStorageFolder* destination_folder, |
| IAsyncAction** operation) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP MoveOverloadDefaultOptions(IStorageFolder* destination_folder, |
| HSTRING desired_new_name, |
| IAsyncAction** operation) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP |
| MoveOverload(IStorageFolder* destination_folder, |
| HSTRING desired_new_name, |
| NameCollisionOption option, |
| IAsyncAction** operation) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP |
| MoveAndReplaceAsync(IStorageFile* file_to_replace, |
| IAsyncAction** operation) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| |
| // ABI::Windows::Storage::IStorageItem |
| IFACEMETHODIMP RenameAsyncOverloadDefaultOptions( |
| HSTRING desired_name, |
| IAsyncAction** operation) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP |
| RenameAsync(HSTRING desired_name, |
| NameCollisionOption option, |
| IAsyncAction** operation) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP DeleteAsyncOverloadDefaultOptions( |
| IAsyncAction** operation) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP |
| DeleteAsync(StorageDeleteOption option, IAsyncAction** operation) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP GetBasicPropertiesAsync( |
| IAsyncOperation<BasicProperties*>** operation) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP get_Name(HSTRING* value) final { |
| auto copy = base::win::ScopedHString::Create(display_name_with_extension_); |
| *value = copy.release(); |
| return S_OK; |
| } |
| IFACEMETHODIMP get_Path(HSTRING* value) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP |
| get_Attributes(FileAttributes* value) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP |
| get_DateCreated(DateTime* value) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| IFACEMETHODIMP |
| IsOfType(StorageItemTypes type, boolean* value) final { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| |
| private: |
| void OnOpenAsync(ComPtr<base::win::FakeIAsyncOperation<IRandomAccessStream*>> |
| fake_iasync_operation) { |
| ASSERT_TRUE(open_async_in_progress_); |
| open_async_in_progress_ = false; |
| |
| auto fake_stream = Make<FakeRandomAccessStream>(); |
| |
| ComPtr<IOutputStream> output_stream; |
| ASSERT_HRESULT_SUCCEEDED(fake_stream->GetOutputStreamAt(0, &output_stream)); |
| ComPtr<FakeRandomAccessStream> fake_output_stream = |
| static_cast<FakeRandomAccessStream*>(output_stream.Get()); |
| ASSERT_TRUE(fake_output_stream); |
| |
| fake_output_stream->OnClose( |
| base::BindLambdaForTesting([fake_stream, fake_iasync_operation]() { |
| ComPtr<IRandomAccessStream> random_access_stream; |
| ASSERT_HRESULT_SUCCEEDED(fake_stream.As(&random_access_stream)); |
| fake_iasync_operation->CompleteWithResults(random_access_stream); |
| })); |
| |
| ASSERT_HRESULT_SUCCEEDED( |
| streamed_file_data_requested_handler_->Invoke(output_stream.Get())); |
| } |
| |
| std::string display_name_with_extension_; |
| bool open_async_in_progress_ = false; |
| ComPtr<IStreamedFileDataRequestedHandler> |
| streamed_file_data_requested_handler_; |
| base::WeakPtrFactory<FakeStorageFile> weak_factory_{this}; |
| }; |
| |
| } // namespace |
| |
| FakeStorageFileStatics::FakeStorageFileStatics() = default; |
| FakeStorageFileStatics::~FakeStorageFileStatics() = default; |
| |
| IFACEMETHODIMP FakeStorageFileStatics::GetFileFromPathAsync( |
| HSTRING path, |
| IAsyncOperation<StorageFile*>** operation) { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| |
| IFACEMETHODIMP FakeStorageFileStatics::GetFileFromApplicationUriAsync( |
| IUriRuntimeClass* uri, |
| IAsyncOperation<StorageFile*>** operation) { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| |
| IFACEMETHODIMP FakeStorageFileStatics::CreateStreamedFileAsync( |
| HSTRING display_name_with_extension, |
| IStreamedFileDataRequestedHandler* data_requested, |
| IRandomAccessStreamReference* thumbnail, |
| IAsyncOperation<StorageFile*>** operation) { |
| if (!base::win::ScopedHString::ResolveCoreWinRTStringDelayload()) { |
| ADD_FAILURE() << "Attempted to use FakeStorageFileStatics in an " |
| "environment that doesn't support ScopedHStrings."; |
| return E_UNEXPECTED; |
| } |
| |
| auto fake_iasync_operation = |
| Make<base::win::FakeIAsyncOperation<StorageFile*>>(); |
| HRESULT hr = fake_iasync_operation->QueryInterface(IID_PPV_ARGS(operation)); |
| if (FAILED(hr)) { |
| EXPECT_HRESULT_SUCCEEDED(hr); |
| return hr; |
| } |
| |
| auto fake_storage_file = Make<FakeStorageFile>(display_name_with_extension, |
| data_requested, thumbnail); |
| bool success = base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindLambdaForTesting([fake_iasync_operation, fake_storage_file]() { |
| fake_iasync_operation->CompleteWithResults(fake_storage_file); |
| })); |
| if (!success) { |
| EXPECT_TRUE(success); |
| return E_ASYNC_OPERATION_NOT_STARTED; |
| } |
| |
| return S_OK; |
| } |
| |
| IFACEMETHODIMP FakeStorageFileStatics::ReplaceWithStreamedFileAsync( |
| IStorageFile* file_to_replace, |
| IStreamedFileDataRequestedHandler* data_requested, |
| IRandomAccessStreamReference* thumbnail, |
| IAsyncOperation<StorageFile*>** operation) { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| |
| IFACEMETHODIMP FakeStorageFileStatics::CreateStreamedFileFromUriAsync( |
| HSTRING display_name_with_extension, |
| IUriRuntimeClass* uri, |
| IRandomAccessStreamReference* thumbnail, |
| IAsyncOperation<StorageFile*>** operation) { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| |
| IFACEMETHODIMP FakeStorageFileStatics::ReplaceWithStreamedFileFromUriAsync( |
| IStorageFile* file_to_replace, |
| IUriRuntimeClass* uri, |
| IRandomAccessStreamReference* thumbnail, |
| IAsyncOperation<StorageFile*>** operation) { |
| NOTREACHED(); |
| return E_NOTIMPL; |
| } |
| |
| } // namespace webshare |