webservd: add fake request, mock response for testing request handlers

Change-Id: I26ab8bf01038b4ae9f022bc1001bd7455cb1f986
diff --git a/libwebserv/mock_response.h b/libwebserv/mock_response.h
new file mode 100644
index 0000000..d0f50ff
--- /dev/null
+++ b/libwebserv/mock_response.h
@@ -0,0 +1,58 @@
+// Copyright 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef WEBSERVER_LIBWEBSERV_MOCK_RESPONSE_H_
+#define WEBSERVER_LIBWEBSERV_MOCK_RESPONSE_H_
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include <libwebserv/response.h>
+
+namespace libwebserv {
+
+// Mock Response implementation for testing.
+class MockResponse : public Response {
+ public:
+  MockResponse() = default;
+
+  MOCK_METHOD2(AddHeader, void(const std::string&, const std::string&));
+  MOCK_METHOD1(AddHeaders,
+               void(const std::vector<std::pair<std::string, std::string>>&));
+
+  // Workaround for mocking with move-only StreamPtr.
+  MOCK_METHOD3(MockReply, void(int, brillo::Stream*, const std::string&));
+
+  MOCK_METHOD3(ReplyWithText,
+               void(int, const std::string&, const std::string&));
+  MOCK_METHOD2(ReplyWithJson, void(int, const base::Value*));
+  MOCK_METHOD2(ReplyWithJson,
+               void(int, const std::map<std::string, std::string>&));
+  MOCK_METHOD2(Redirect, void(int, const std::string&));
+  MOCK_METHOD2(ReplyWithError, void(int, const std::string&));
+  MOCK_METHOD0(ReplyWithErrorNotFound, void());
+
+ private:
+  void Reply(int status_code,
+             brillo::StreamPtr data_stream,
+             const std::string &mime_type) override {
+    return MockReply(status_code, data_stream.get(), mime_type);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(MockResponse);
+};
+
+}  // namespace libwebserv
+
+#endif  // WEBSERVER_LIBWEBSERV_MOCK_RESPONSE_H_
diff --git a/libwebserv/protocol_handler.cc b/libwebserv/protocol_handler.cc
index df8c255..73b61db 100644
--- a/libwebserv/protocol_handler.cc
+++ b/libwebserv/protocol_handler.cc
@@ -24,7 +24,7 @@
 #include "dbus_bindings/org.chromium.WebServer.RequestHandler.h"
 #include "libwebserv/request.h"
 #include "libwebserv/request_handler_callback.h"
-#include "libwebserv/response.h"
+#include "libwebserv/response_impl.h"
 #include "libwebserv/server.h"
 #include "webservd/dbus-proxies.h"
 
@@ -220,7 +220,7 @@
   }
   handler_iter->second.handler->HandleRequest(
       std::move(request),
-      std::unique_ptr<Response>{new Response{this, request_id}});
+      std::unique_ptr<Response>{new ResponseImpl{this, request_id}});
   return true;
 }
 
diff --git a/libwebserv/protocol_handler.h b/libwebserv/protocol_handler.h
index ab4a749..ab5e0ac 100644
--- a/libwebserv/protocol_handler.h
+++ b/libwebserv/protocol_handler.h
@@ -127,7 +127,7 @@
  private:
   friend class FileInfo;
   friend class Server;
-  friend class Response;
+  friend class ResponseImpl;
 
   using ProtocolHandlerProxy = org::chromium::WebServer::ProtocolHandlerProxy;
 
diff --git a/libwebserv/request.cc b/libwebserv/request.cc
index f0bdd79..959b294 100644
--- a/libwebserv/request.cc
+++ b/libwebserv/request.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <libwebserv/request.h>
+#include <libwebserv/request_impl.h>
 
 #include <base/callback.h>
 #include <brillo/http/http_utils.h>
@@ -45,16 +45,13 @@
                         error_callback);
 }
 
-Request::Request(ProtocolHandler* handler,
-                 const std::string& url,
-                 const std::string& method)
-    : handler_{handler}, url_{url}, method_{method} {
+RequestImpl::RequestImpl(ProtocolHandler* handler,
+                         const std::string& url,
+                         const std::string& method)
+    : Request{url, method}, handler_{handler} {
 }
 
-Request::~Request() {
-}
-
-brillo::StreamPtr Request::GetDataStream() {
+brillo::StreamPtr RequestImpl::GetDataStream() {
   return brillo::FileStream::FromFileDescriptor(
       raw_data_fd_.GetPlatformFile(), false, nullptr);
 }
diff --git a/libwebserv/request.h b/libwebserv/request.h
index 63bea8e..f1f7043 100644
--- a/libwebserv/request.h
+++ b/libwebserv/request.h
@@ -69,9 +69,11 @@
 };
 
 // A class that represents the HTTP request data.
-class LIBWEBSERV_EXPORT Request final {
+class LIBWEBSERV_EXPORT Request {
  public:
-  ~Request();
+  Request(const std::string& url, const std::string& method)
+    : url_{url}, method_{method} {}
+  virtual ~Request() = default;
 
   // Gets the request body data stream. Note that the stream is available
   // only for requests that provided data and if this data is not already
@@ -81,7 +83,7 @@
   // The stream returned is valid for as long as the Request object itself is
   // alive. Accessing the stream after the Request object is destroyed will lead
   // to an undefined behavior (will likely just crash).
-  brillo::StreamPtr GetDataStream();
+  virtual brillo::StreamPtr GetDataStream() = 0;
 
   // Returns the request path (e.g. "/path/document").
   const std::string& GetPath() const { return url_; }
@@ -131,25 +133,13 @@
   // An empty string is returned if the header does not exist in the request.
   std::string GetFirstHeader(const std::string& name) const;
 
- private:
-  friend class Server;
-
-  LIBWEBSERV_PRIVATE Request(ProtocolHandler* handler,
-                             const std::string& url,
-                             const std::string& method);
-
-  ProtocolHandler* handler_{nullptr};
+ protected:
   std::string url_;
   std::string method_;
-  base::File raw_data_fd_;
-  bool last_posted_data_was_file_{false};
-
   std::multimap<std::string, std::string> post_data_;
   std::multimap<std::string, std::string> get_data_;
   std::multimap<std::string, std::unique_ptr<FileInfo>> file_info_;
   std::multimap<std::string, std::string> headers_;
-
-  DISALLOW_COPY_AND_ASSIGN(Request);
 };
 
 }  // namespace libwebserv
diff --git a/libwebserv/request_fake.h b/libwebserv/request_fake.h
new file mode 100644
index 0000000..4fd4f20
--- /dev/null
+++ b/libwebserv/request_fake.h
@@ -0,0 +1,73 @@
+// Copyright 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef WEBSERVER_LIBWEBSERV_REQUEST_FAKE_H_
+#define WEBSERVER_LIBWEBSERV_REQUEST_FAKE_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/callback_forward.h>
+#include <base/files/file.h>
+#include <base/macros.h>
+#include <base/memory/ref_counted.h>
+#include <brillo/errors/error.h>
+#include <brillo/streams/stream.h>
+#include <libwebserv/export.h>
+#include <libwebserv/request.h>
+
+namespace libwebserv {
+
+// Implementation of Request that allows custom data for testing.
+class RequestFake : public Request {
+ public:
+  RequestFake(const std::string& url, const std::string& method)
+    : Request(url, method) {}
+
+  void SetDataStream(brillo::StreamPtr data_stream) {
+    data_stream_ = std::move(data_stream);
+  }
+
+  void SetFormDataPost(std::multimap<std::string, std::string> post_data) {
+    post_data_ = std::move(post_data);
+  }
+
+  void SetFormDataGet(std::multimap<std::string, std::string> get_data) {
+    get_data_ = std::move(get_data);
+  }
+
+  void SetFileInfo(
+      std::multimap<std::string, std::unique_ptr<FileInfo>> file_info) {
+    file_info_ = std::move(file_info);
+  }
+
+  void SetHeaders(std::multimap<std::string, std::string> headers) {
+    headers_ = std::move(headers);
+  }
+
+  // Overrides from Request.
+  brillo::StreamPtr GetDataStream() override { return std::move(data_stream_); }
+
+ private:
+  brillo::StreamPtr data_stream_;
+
+  DISALLOW_COPY_AND_ASSIGN(RequestFake);
+};
+
+}  // namespace libwebserv
+
+#endif  // WEBSERVER_LIBWEBSERV_REQUEST_FAKE_H_
diff --git a/libwebserv/request_impl.h b/libwebserv/request_impl.h
new file mode 100644
index 0000000..4d99873
--- /dev/null
+++ b/libwebserv/request_impl.h
@@ -0,0 +1,48 @@
+// Copyright 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef WEBSERVER_LIBWEBSERV_REQUEST_IMPL_H_
+#define WEBSERVER_LIBWEBSERV_REQUEST_IMPL_H_
+
+#include <string>
+
+#include <base/files/file.h>
+#include <base/macros.h>
+#include <brillo/streams/stream.h>
+#include <libwebserv/request.h>
+
+namespace libwebserv {
+
+// Implementation of the Request interface.
+class RequestImpl final : public Request {
+ public:
+  // Overrides from Request.
+  brillo::StreamPtr GetDataStream() override;
+
+ private:
+  friend class Server;
+
+  LIBWEBSERV_PRIVATE RequestImpl(ProtocolHandler* handler,
+                                 const std::string& url,
+                                 const std::string& method);
+  ProtocolHandler* handler_{nullptr};
+  base::File raw_data_fd_;
+  bool last_posted_data_was_file_{false};
+
+  DISALLOW_COPY_AND_ASSIGN(RequestImpl);
+};
+
+}  // namespace libwebserv
+
+#endif  // WEBSERVER_LIBWEBSERV_REQUEST_IMPL_H_
diff --git a/libwebserv/response.cc b/libwebserv/response.cc
index 59dc51f..bedc10c 100644
--- a/libwebserv/response.cc
+++ b/libwebserv/response.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <libwebserv/response.h>
+#include <libwebserv/response_impl.h>
 
 #include <algorithm>
 
@@ -27,30 +27,31 @@
 
 namespace libwebserv {
 
-Response::Response(ProtocolHandler* handler, const std::string& request_id)
+ResponseImpl::ResponseImpl(ProtocolHandler* handler,
+                           const std::string& request_id)
     : handler_{handler}, request_id_{request_id} {
 }
 
-Response::~Response() {
+ResponseImpl::~ResponseImpl() {
   if (!reply_sent_) {
     ReplyWithError(brillo::http::status_code::InternalServerError,
                    "Internal server error");
   }
 }
 
-void Response::AddHeader(const std::string& header_name,
-                         const std::string& value) {
+void ResponseImpl::AddHeader(const std::string& header_name,
+                             const std::string& value) {
   headers_.emplace(header_name, value);
 }
 
-void Response::AddHeaders(
+void ResponseImpl::AddHeaders(
     const std::vector<std::pair<std::string, std::string>>& headers) {
   headers_.insert(headers.begin(), headers.end());
 }
 
-void Response::Reply(int status_code,
-                     brillo::StreamPtr data_stream,
-                     const std::string& mime_type) {
+void ResponseImpl::Reply(int status_code,
+                         brillo::StreamPtr data_stream,
+                         const std::string& mime_type) {
   CHECK(data_stream);
   status_code_ = status_code;
   data_stream_ = std::move(data_stream);
@@ -58,14 +59,14 @@
   SendResponse();
 }
 
-void Response::ReplyWithText(int status_code,
-                             const std::string& text,
-                             const std::string& mime_type) {
+void ResponseImpl::ReplyWithText(int status_code,
+                                 const std::string& text,
+                                 const std::string& mime_type) {
   Reply(status_code, brillo::MemoryStream::OpenCopyOf(text, nullptr),
         mime_type);
 }
 
-void Response::ReplyWithJson(int status_code, const base::Value* json) {
+void ResponseImpl::ReplyWithJson(int status_code, const base::Value* json) {
   std::string text;
   base::JSONWriter::WriteWithOptions(
       *json, base::JSONWriter::OPTIONS_PRETTY_PRINT, &text);
@@ -76,8 +77,8 @@
   ReplyWithText(status_code, text, mime_type);
 }
 
-void Response::ReplyWithJson(int status_code,
-                             const std::map<std::string, std::string>& json) {
+void ResponseImpl::ReplyWithJson(
+    int status_code, const std::map<std::string, std::string>& json) {
   base::DictionaryValue json_value;
   for (const auto& pair : json) {
     json_value.SetString(pair.first, pair.second);
@@ -85,22 +86,23 @@
   ReplyWithJson(status_code, &json_value);
 }
 
-void Response::Redirect(int status_code, const std::string& redirect_url) {
+void ResponseImpl::Redirect(int status_code, const std::string& redirect_url) {
   AddHeader(brillo::http::response_header::kLocation, redirect_url);
   ReplyWithError(status_code, "");
 }
 
-void Response::ReplyWithError(int status_code, const std::string& error_text) {
+void ResponseImpl::ReplyWithError(int status_code,
+                                  const std::string& error_text) {
   status_code_ = status_code;
   data_stream_ = brillo::MemoryStream::OpenCopyOf(error_text, nullptr);
   SendResponse();
 }
 
-void Response::ReplyWithErrorNotFound() {
+void ResponseImpl::ReplyWithErrorNotFound() {
   ReplyWithError(brillo::http::status_code::NotFound, "Not Found");
 }
 
-void Response::SendResponse() {
+void ResponseImpl::SendResponse() {
   CHECK(!reply_sent_) << "Response already sent";
   reply_sent_ = true;
   handler_->CompleteRequest(request_id_, status_code_, headers_,
diff --git a/libwebserv/response.h b/libwebserv/response.h
index fdd9cad..70e4f47 100644
--- a/libwebserv/response.h
+++ b/libwebserv/response.h
@@ -35,63 +35,48 @@
 
 // Response class is a proxy for HTTP response used by the request handler
 // to provide response HTTP headers and data.
-class LIBWEBSERV_EXPORT Response final {
+class LIBWEBSERV_EXPORT Response {
  public:
-  ~Response();
+  virtual ~Response() = default;
 
   // Adds a single HTTP response header to the response.
-  void AddHeader(const std::string& header_name, const std::string& value);
+  virtual void AddHeader(const std::string& header_name,
+                         const std::string& value) = 0;
 
   // Adds number of HTTP response headers to the response.
-  void AddHeaders(
-      const std::vector<std::pair<std::string, std::string>>& headers);
+  virtual void AddHeaders(
+      const std::vector<std::pair<std::string, std::string>>& headers) = 0;
 
   // Generic reply method for sending arbitrary binary data response.
-  void Reply(int status_code,
-             brillo::StreamPtr data_stream,
-             const std::string& mime_type);
+  virtual void Reply(int status_code,
+                     brillo::StreamPtr data_stream,
+                     const std::string& mime_type) = 0;
 
   // Reply with text body.
-  void ReplyWithText(int status_code,
-                     const std::string& text,
-                     const std::string& mime_type);
+  virtual void ReplyWithText(int status_code,
+                             const std::string& text,
+                             const std::string& mime_type) = 0;
 
   // Reply with JSON object. The content type will be "application/json".
-  void ReplyWithJson(int status_code, const base::Value* json);
+  virtual void ReplyWithJson(int status_code, const base::Value* json) = 0;
 
   // Special form for JSON response for simple objects that have a flat
   // list of key-value pairs of string type.
-  void ReplyWithJson(int status_code,
-                     const std::map<std::string, std::string>& json);
+  virtual void ReplyWithJson(
+      int status_code, const std::map<std::string, std::string>& json) = 0;
 
   // Issue a redirect response, so the client browser loads a page at
   // the URL specified in |redirect_url|. If this is not an external URL,
   // it must be an absolute path starting at the root "/...".
-  void Redirect(int status_code, const std::string& redirect_url);
+  virtual void Redirect(int status_code, const std::string& redirect_url) = 0;
 
   // Send a plain text response (with no Content-Type header).
   // Usually used with error responses. |error_text| must be plain text.
-  void ReplyWithError(int status_code, const std::string& error_text);
+  virtual void ReplyWithError(int status_code,
+                              const std::string& error_text) = 0;
 
   // Send "404 Not Found" response.
-  void ReplyWithErrorNotFound();
-
- private:
-  friend class ProtocolHandler;
-
-  LIBWEBSERV_PRIVATE Response(ProtocolHandler* handler,
-                              const std::string& request_id);
-
-  LIBWEBSERV_PRIVATE void SendResponse();
-
-  ProtocolHandler* handler_{nullptr};
-  std::string request_id_;
-  int status_code_{0};
-  brillo::StreamPtr data_stream_;
-  std::multimap<std::string, std::string> headers_;
-  bool reply_sent_{false};
-
-  DISALLOW_COPY_AND_ASSIGN(Response);
+  virtual void ReplyWithErrorNotFound() = 0;
 };
 
 }  // namespace libwebserv
diff --git a/libwebserv/response_impl.h b/libwebserv/response_impl.h
new file mode 100644
index 0000000..81b4faa
--- /dev/null
+++ b/libwebserv/response_impl.h
@@ -0,0 +1,71 @@
+// Copyright 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef WEBSERVER_LIBWEBSERV_RESPONSE_IMPL_H_
+#define WEBSERVER_LIBWEBSERV_RESPONSE_IMPL_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <brillo/streams/stream.h>
+#include <libwebserv/response.h>
+
+namespace libwebserv {
+
+// Implementation of the Response interface.
+class ResponseImpl final : public Response {
+ public:
+  ~ResponseImpl() override;
+
+  // Overrides from Response.
+  void AddHeader(const std::string& header_name,
+                 const std::string& value) override;
+  void AddHeaders(
+      const std::vector<std::pair<std::string, std::string>>& headers) override;
+  void Reply(int status_code,
+             brillo::StreamPtr data_stream,
+             const std::string& mime_type) override;
+  void ReplyWithText(int status_code,
+                     const std::string& text,
+                     const std::string& mime_type) override;
+  void ReplyWithJson(int status_code, const base::Value* json) override;
+  void ReplyWithJson(int status_code,
+                     const std::map<std::string, std::string>& json) override;
+  void Redirect(int status_code, const std::string& redirect_url) override;
+  void ReplyWithError(int status_code, const std::string& error_text) override;
+  void ReplyWithErrorNotFound() override;
+
+ private:
+  friend class ProtocolHandler;
+
+  LIBWEBSERV_PRIVATE ResponseImpl(ProtocolHandler* handler,
+                                  const std::string& request_id);
+
+  LIBWEBSERV_PRIVATE void SendResponse();
+
+  ProtocolHandler* handler_{nullptr};
+  std::string request_id_;
+  int status_code_{0};
+  brillo::StreamPtr data_stream_;
+  std::multimap<std::string, std::string> headers_;
+  bool reply_sent_{false};
+
+  DISALLOW_COPY_AND_ASSIGN(ResponseImpl);
+};
+
+}  // namespace libwebserv
+
+#endif  // WEBSERVER_LIBWEBSERV_RESPONSE_IMPL_H_
diff --git a/libwebserv/server.cc b/libwebserv/server.cc
index 080fe03..7ca3c55 100644
--- a/libwebserv/server.cc
+++ b/libwebserv/server.cc
@@ -18,7 +18,7 @@
 #include <vector>
 
 #include <libwebserv/protocol_handler.h>
-#include <libwebserv/request.h>
+#include <libwebserv/request_impl.h>
 
 #include "dbus_bindings/org.chromium.WebServer.RequestHandler.h"
 #include "webservd/dbus-proxies.h"
@@ -68,7 +68,8 @@
                                protocol_handler_id.c_str());
     return false;
   }
-  std::unique_ptr<Request> request{new Request{protocol_handler, url, method}};
+  std::unique_ptr<RequestImpl> request{
+    new RequestImpl{protocol_handler, url, method}};
   // Convert request data into format required by the Request object.
   for (const auto& tuple : in_params) {
     if (std::get<0>(tuple))