chromium/chrome/browser/webshare/win/fake_storage_file_statics.cc

// 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/webshare/win/fake_storage_file_statics.h"

#include <windows.foundation.h>
#include <windows.storage.streams.h>
#include <wrl/module.h>

#include <memory>
#include <string>
#include <tuple>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/fake_iasync_operation_win.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_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP get_ContentType(HSTRING* value) final {
    NOTREACHED_IN_MIGRATION();
    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::SingleThreadTaskRunner::GetCurrentDefault()->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_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP CopyOverloadDefaultNameAndOptions(
      IStorageFolder* destination_folder,
      IAsyncOperation<StorageFile*>** operation) final {
    NOTREACHED_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP CopyOverloadDefaultOptions(
      IStorageFolder* destination_folder,
      HSTRING desired_new_name,
      IAsyncOperation<StorageFile*>** operation) final {
    NOTREACHED_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP CopyOverload(IStorageFolder* destination_folder,
                              HSTRING desired_new_name,
                              NameCollisionOption option,
                              IAsyncOperation<StorageFile*>** operation) final {
    NOTREACHED_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP
  CopyAndReplaceAsync(IStorageFile* file_to_replace,
                      IAsyncAction** operation) final {
    NOTREACHED_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP MoveOverloadDefaultNameAndOptions(
      IStorageFolder* destination_folder,
      IAsyncAction** operation) final {
    NOTREACHED_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP MoveOverloadDefaultOptions(IStorageFolder* destination_folder,
                                            HSTRING desired_new_name,
                                            IAsyncAction** operation) final {
    NOTREACHED_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP
  MoveOverload(IStorageFolder* destination_folder,
               HSTRING desired_new_name,
               NameCollisionOption option,
               IAsyncAction** operation) final {
    NOTREACHED_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP
  MoveAndReplaceAsync(IStorageFile* file_to_replace,
                      IAsyncAction** operation) final {
    NOTREACHED_IN_MIGRATION();
    return E_NOTIMPL;
  }

  // ABI::Windows::Storage::IStorageItem
  IFACEMETHODIMP RenameAsyncOverloadDefaultOptions(
      HSTRING desired_name,
      IAsyncAction** operation) final {
    NOTREACHED_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP
  RenameAsync(HSTRING desired_name,
              NameCollisionOption option,
              IAsyncAction** operation) final {
    NOTREACHED_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP DeleteAsyncOverloadDefaultOptions(
      IAsyncAction** operation) final {
    NOTREACHED_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP
  DeleteAsync(StorageDeleteOption option, IAsyncAction** operation) final {
    NOTREACHED_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP GetBasicPropertiesAsync(
      IAsyncOperation<BasicProperties*>** operation) final {
    NOTREACHED_IN_MIGRATION();
    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_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP
  get_Attributes(FileAttributes* value) final {
    NOTREACHED_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP
  get_DateCreated(DateTime* value) final {
    NOTREACHED_IN_MIGRATION();
    return E_NOTIMPL;
  }
  IFACEMETHODIMP
  IsOfType(StorageItemTypes type, boolean* value) final {
    NOTREACHED_IN_MIGRATION();
    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_IN_MIGRATION();
  return E_NOTIMPL;
}

IFACEMETHODIMP FakeStorageFileStatics::GetFileFromApplicationUriAsync(
    IUriRuntimeClass* uri,
    IAsyncOperation<StorageFile*>** operation) {
  NOTREACHED_IN_MIGRATION();
  return E_NOTIMPL;
}

IFACEMETHODIMP FakeStorageFileStatics::CreateStreamedFileAsync(
    HSTRING display_name_with_extension,
    IStreamedFileDataRequestedHandler* data_requested,
    IRandomAccessStreamReference* thumbnail,
    IAsyncOperation<StorageFile*>** operation) {
  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::SingleThreadTaskRunner::GetCurrentDefault()->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_IN_MIGRATION();
  return E_NOTIMPL;
}

IFACEMETHODIMP FakeStorageFileStatics::CreateStreamedFileFromUriAsync(
    HSTRING display_name_with_extension,
    IUriRuntimeClass* uri,
    IRandomAccessStreamReference* thumbnail,
    IAsyncOperation<StorageFile*>** operation) {
  NOTREACHED_IN_MIGRATION();
  return E_NOTIMPL;
}

IFACEMETHODIMP FakeStorageFileStatics::ReplaceWithStreamedFileFromUriAsync(
    IStorageFile* file_to_replace,
    IUriRuntimeClass* uri,
    IRandomAccessStreamReference* thumbnail,
    IAsyncOperation<StorageFile*>** operation) {
  NOTREACHED_IN_MIGRATION();
  return E_NOTIMPL;
}

}  // namespace webshare