// Copyright (c) 2011 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.

// This is a mock of the http cache and related testing classes. To be fair, it
// is not really a mock http cache given that it uses the real implementation of
// the http cache, but it has fake implementations of all required components,
// so it is useful for unit tests at the http layer.

#ifndef NET_HTTP_MOCK_HTTP_CACHE_H_
#define NET_HTTP_MOCK_HTTP_CACHE_H_

#include "base/containers/hash_tables.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
#include "net/http/http_transaction_test_util.h"

//-----------------------------------------------------------------------------
// Mock disk cache (a very basic memory cache implementation).

class MockDiskEntry : public disk_cache::Entry,
                      public base::RefCounted<MockDiskEntry> {
 public:
  explicit MockDiskEntry(const std::string& key);

  bool is_doomed() const { return doomed_; }

  virtual void Doom() OVERRIDE;
  virtual void Close() OVERRIDE;
  virtual std::string GetKey() const OVERRIDE;
  virtual base::Time GetLastUsed() const OVERRIDE;
  virtual base::Time GetLastModified() const OVERRIDE;
  virtual int32 GetDataSize(int index) const OVERRIDE;
  virtual int ReadData(int index, int offset, net::IOBuffer* buf, int buf_len,
                       const net::CompletionCallback& callback) OVERRIDE;
  virtual int WriteData(int index, int offset, net::IOBuffer* buf, int buf_len,
                        const net::CompletionCallback& callback,
                        bool truncate) OVERRIDE;
  virtual int ReadSparseData(int64 offset, net::IOBuffer* buf, int buf_len,
                             const net::CompletionCallback& callback) OVERRIDE;
  virtual int WriteSparseData(
      int64 offset, net::IOBuffer* buf, int buf_len,
      const net::CompletionCallback& callback) OVERRIDE;
  virtual int GetAvailableRange(
      int64 offset, int len, int64* start,
      const net::CompletionCallback& callback) OVERRIDE;
  virtual bool CouldBeSparse() const OVERRIDE;
  virtual void CancelSparseIO() OVERRIDE;
  virtual int ReadyForSparseIO(
      const net::CompletionCallback& completion_callback) OVERRIDE;

  // Fail most subsequent requests.
  void set_fail_requests() { fail_requests_ = true; }

  void set_fail_sparse_requests() { fail_sparse_requests_ = true; }

  // If |value| is true, don't deliver any completion callbacks until called
  // again with |value| set to false.  Caution: remember to enable callbacks
  // again or all subsequent tests will fail.
  static void IgnoreCallbacks(bool value);

 private:
  friend class base::RefCounted<MockDiskEntry>;
  struct CallbackInfo;

  virtual ~MockDiskEntry();

  // Unlike the callbacks for MockHttpTransaction, we want this one to run even
  // if the consumer called Close on the MockDiskEntry.  We achieve that by
  // leveraging the fact that this class is reference counted.
  void CallbackLater(const net::CompletionCallback& callback, int result);

  void RunCallback(const net::CompletionCallback& callback, int result);

  // When |store| is true, stores the callback to be delivered later; otherwise
  // delivers any callback previously stored.
  static void StoreAndDeliverCallbacks(bool store, MockDiskEntry* entry,
                                       const net::CompletionCallback& callback,
                                       int result);

  static const int kNumCacheEntryDataIndices = 3;

  std::string key_;
  std::vector<char> data_[kNumCacheEntryDataIndices];
  int test_mode_;
  bool doomed_;
  bool sparse_;
  bool fail_requests_;
  bool fail_sparse_requests_;
  bool busy_;
  bool delayed_;
  static bool cancel_;
  static bool ignore_callbacks_;
};

class MockDiskCache : public disk_cache::Backend {
 public:
  MockDiskCache();
  virtual ~MockDiskCache();

  virtual net::CacheType GetCacheType() const OVERRIDE;
  virtual int32 GetEntryCount() const OVERRIDE;
  virtual int OpenEntry(const std::string& key, disk_cache::Entry** entry,
                        const net::CompletionCallback& callback) OVERRIDE;
  virtual int CreateEntry(const std::string& key, disk_cache::Entry** entry,
                          const net::CompletionCallback& callback) OVERRIDE;
  virtual int DoomEntry(const std::string& key,
                        const net::CompletionCallback& callback) OVERRIDE;
  virtual int DoomAllEntries(const net::CompletionCallback& callback) OVERRIDE;
  virtual int DoomEntriesBetween(
      base::Time initial_time,
      base::Time end_time,
      const net::CompletionCallback& callback) OVERRIDE;
  virtual int DoomEntriesSince(
      base::Time initial_time,
      const net::CompletionCallback& callback) OVERRIDE;
  virtual int OpenNextEntry(void** iter, disk_cache::Entry** next_entry,
                            const net::CompletionCallback& callback) OVERRIDE;
  virtual void EndEnumeration(void** iter) OVERRIDE;
  virtual void GetStats(
      std::vector<std::pair<std::string, std::string> >* stats) OVERRIDE;
  virtual void OnExternalCacheHit(const std::string& key) OVERRIDE;

  // Returns number of times a cache entry was successfully opened.
  int open_count() const { return open_count_; }

  // Returns number of times a cache entry was successfully created.
  int create_count() const { return create_count_; }

  // Fail any subsequent CreateEntry and OpenEntry.
  void set_fail_requests() { fail_requests_ = true; }

  // Return entries that fail some of their requests.
  void set_soft_failures(bool value) { soft_failures_ = value; }

  // Makes sure that CreateEntry is not called twice for a given key.
  void set_double_create_check(bool value) { double_create_check_ = value; }

  // Makes all requests for data ranges to fail as not implemented.
  void set_fail_sparse_requests() { fail_sparse_requests_ = true; }

  void ReleaseAll();

 private:
  typedef base::hash_map<std::string, MockDiskEntry*> EntryMap;

  void CallbackLater(const net::CompletionCallback& callback, int result);

  EntryMap entries_;
  int open_count_;
  int create_count_;
  bool fail_requests_;
  bool soft_failures_;
  bool double_create_check_;
  bool fail_sparse_requests_;
};

class MockBackendFactory : public net::HttpCache::BackendFactory {
 public:
  virtual int CreateBackend(net::NetLog* net_log,
                            scoped_ptr<disk_cache::Backend>* backend,
                            const net::CompletionCallback& callback) OVERRIDE;
};

class MockHttpCache {
 public:
  MockHttpCache();
  explicit MockHttpCache(net::HttpCache::BackendFactory* disk_cache_factory);

  net::HttpCache* http_cache() { return &http_cache_; }

  MockNetworkLayer* network_layer() {
    return static_cast<MockNetworkLayer*>(http_cache_.network_layer());
  }
  MockDiskCache* disk_cache();

  // Wrapper around http_cache()->CreateTransaction(net::DEFAULT_PRIORITY...)
  int CreateTransaction(scoped_ptr<net::HttpTransaction>* trans);

  // Helper function for reading response info from the disk cache.
  static bool ReadResponseInfo(disk_cache::Entry* disk_entry,
                               net::HttpResponseInfo* response_info,
                               bool* response_truncated);

  // Helper function for writing response info into the disk cache.
  static bool WriteResponseInfo(disk_cache::Entry* disk_entry,
                                const net::HttpResponseInfo* response_info,
                                bool skip_transient_headers,
                                bool response_truncated);

  // Helper function to synchronously open a backend entry.
  bool OpenBackendEntry(const std::string& key, disk_cache::Entry** entry);

  // Helper function to synchronously create a backend entry.
  bool CreateBackendEntry(const std::string& key, disk_cache::Entry** entry,
                          net::NetLog* net_log);

  // Returns the test mode after considering the global override.
  static int GetTestMode(int test_mode);

  // Overrides the test mode for a given operation. Remember to reset it after
  // the test! (by setting test_mode to zero).
  static void SetTestMode(int test_mode);

 private:
  net::HttpCache http_cache_;
};

// This version of the disk cache doesn't invoke CreateEntry callbacks.
class MockDiskCacheNoCB : public MockDiskCache {
  virtual int CreateEntry(const std::string& key, disk_cache::Entry** entry,
                          const net::CompletionCallback& callback) OVERRIDE;
};

class MockBackendNoCbFactory : public net::HttpCache::BackendFactory {
 public:
  virtual int CreateBackend(net::NetLog* net_log,
                            scoped_ptr<disk_cache::Backend>* backend,
                            const net::CompletionCallback& callback) OVERRIDE;
};

// This backend factory allows us to control the backend instantiation.
class MockBlockingBackendFactory : public net::HttpCache::BackendFactory {
 public:
  MockBlockingBackendFactory();
  virtual ~MockBlockingBackendFactory();

  virtual int CreateBackend(net::NetLog* net_log,
                            scoped_ptr<disk_cache::Backend>* backend,
                            const net::CompletionCallback& callback) OVERRIDE;

  // Completes the backend creation. Any blocked call will be notified via the
  // provided callback.
  void FinishCreation();

  scoped_ptr<disk_cache::Backend>* backend() { return backend_; }
  void set_fail(bool fail) { fail_ = fail; }

  const net::CompletionCallback& callback() { return callback_; }

 private:
  int Result() { return fail_ ? net::ERR_FAILED : net::OK; }

  scoped_ptr<disk_cache::Backend>* backend_;
  net::CompletionCallback callback_;
  bool block_;
  bool fail_;
};

#endif  // NET_HTTP_MOCK_HTTP_CACHE_H_