1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145

content / public / test / test_file_error_injector.h [blame]

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_PUBLIC_TEST_TEST_FILE_ERROR_INJECTOR_H_
#define CONTENT_PUBLIC_TEST_TEST_FILE_ERROR_INJECTOR_H_

#include <stddef.h>

#include <memory>
#include <string>

#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "components/download/public/common/download_interrupt_reasons.h"
#include "url/gurl.h"

namespace content {

class DownloadFileWithErrorFactory;
class DownloadManager;
class DownloadManagerImpl;

// Test helper for injecting errors into download file operations.  All errors
// for a download must be injected before it starts.  This class needs to be
// |RefCountedThreadSafe| because the implementation is referenced by other
// classes that live past the time when the user is nominally done with it.
//
// Once created, an error injected via InjectError() will cause any
// DownloadFiles created to fail with that error. Call ClearError() to stop
// injecting errors.
//
// Example:
//
// FileErrorInfo a = { ... };
//
// scoped_refptr<TestFileErrorInjector> injector =
//     TestFileErrorInjector::Create(download_manager);
//
// injector->InjectError(a);
//
// download_manager->DownloadUrl(url1, ...); // Will be interrupted due to |a|.
// download_manager->DownloadUrl(url2, ...); // Will be interrupted due to |a|.
//
// injector->ClearError();
//
// download_manager->DownloadUrl(url3, ...); // Won't be interrupted due to |a|.
class TestFileErrorInjector
    : public base::RefCountedThreadSafe<TestFileErrorInjector> {
 public:
  enum FileOperationCode {
    FILE_OPERATION_INITIALIZE,
    FILE_OPERATION_WRITE,
    FILE_OPERATION_STREAM_COMPLETE,
    FILE_OPERATION_RENAME_UNIQUIFY,
    FILE_OPERATION_RENAME_ANNOTATE,
  };

  // Structure that encapsulates the information needed to inject a file error.
  struct FileErrorInfo {
    FileErrorInfo();
    FileErrorInfo(FileOperationCode code,
                  int operation_instance,
                  download::DownloadInterruptReason error);
    FileOperationCode code;  // Operation to affect.
    int operation_instance;  // 0-based count of operation calls, for each code.
    download::DownloadInterruptReason error;  // Error to inject.
    int64_t stream_offset = -1;     // Offset of the error stream.
    int64_t stream_bytes_written = -1;  // Bytes written to the error stream.
    // If > 0, only write operations covering this offset will generate errors.
    // Otherwise, all file writes will generate errors.
    int64_t data_write_offset = -1;
  };

  // Creates an instance.  May only be called once.
  // Lives until all callbacks (in the implementation) are complete and the
  // creator goes out of scope.
  // TODO(rdsmith): Allow multiple calls for different download managers.
  static scoped_refptr<TestFileErrorInjector> Create(
      DownloadManager* download_manager);

  TestFileErrorInjector(const TestFileErrorInjector&) = delete;
  TestFileErrorInjector& operator=(const TestFileErrorInjector&) = delete;

  // Injects the errors such that new download files will be affected.
  // The download system must already be initialized before calling this.
  // Multiple calls are allowed, but only useful if the errors have changed.
  // Replaces the injected error list.
  bool InjectError(const FileErrorInfo& error_to_inject);

  // Clears all errors.
  // Only affects files created after the next call to InjectErrors().
  void ClearError();

  // Tells how many files are currently open.
  size_t CurrentFileCount() const;

  // Tells how many files have ever been open (since construction or the
  // last call to |ClearTotalFileCount()|).
  size_t TotalFileCount() const;

  // Resets the total file count. Doesn't affect what's returned by
  // CurrentFileCount().
  void ClearTotalFileCount();

  static std::string DebugString(FileOperationCode code);

 private:
  friend class base::RefCountedThreadSafe<TestFileErrorInjector>;

  explicit TestFileErrorInjector(DownloadManager* download_manager);

  virtual ~TestFileErrorInjector();

  // Callbacks from the download file, to record lifetimes.
  // These may be called on any thread.
  void RecordDownloadFileConstruction();
  void RecordDownloadFileDestruction();

  // These run on the UI thread.
  void DownloadFileCreated();
  void DestroyingDownloadFile();

  // All the data is used on the UI thread.
  // Keep track of active DownloadFiles.
  size_t active_file_count_ = 0;

  // Keep track of found DownloadFiles.
  size_t total_file_count_ = 0;

  // The factory we created. May outlive this class.
  // This dangling raw_ptr occurred in:
  // browser_tests: DownloadTest.DownloadHistoryCheck
  // https://ci.chromium.org/ui/p/chromium/builders/try/linux-chromeos-rel/1540091/test-results?q=ExactID%3Aninja%3A%2F%2Fchrome%2Ftest%3Abrowser_tests%2FDownloadTest.DownloadHistoryCheck+VHash%3A282db19e8ac0a6be
  raw_ptr<DownloadFileWithErrorFactory, FlakyDanglingUntriaged>
      created_factory_ = nullptr;

  // The download manager we set the factory on.
  raw_ptr<DownloadManagerImpl, FlakyDanglingUntriaged> download_manager_ =
      nullptr;
};

}  // namespace content

#endif  // CONTENT_PUBLIC_TEST_TEST_FILE_ERROR_INJECTOR_H_