| Index: test/BaseJsTest.h | 
| =================================================================== | 
| --- a/test/BaseJsTest.h | 
| +++ b/test/BaseJsTest.h | 
| @@ -13,19 +13,56 @@ | 
| * | 
| * You should have received a copy of the GNU General Public License | 
| * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
| */ | 
|  | 
| #ifndef MOCKS_H | 
| #define MOCKS_H | 
|  | 
| +#include <thread> | 
| + | 
| #include <AdblockPlus.h> | 
| #include <gtest/gtest.h> | 
| #include "../src/Thread.h" | 
| +#include "../src/Utils.h" | 
| + | 
| +class Sync | 
| +{ | 
| +public: | 
| +  Sync() | 
| +    : set(false) | 
| +  { | 
| +  } | 
| +  void Wait(int sec = 20) | 
| +  { | 
| +    std::unique_lock<std::mutex> lock(mutex); | 
| +    while (!set) | 
| +      cv.wait_for(lock, std::chrono::seconds(sec)); | 
| +  } | 
| +  void Set(const std::string& error_ = std::string()) | 
| +  { | 
| +    { | 
| +      std::unique_lock<std::mutex> lock(mutex); | 
| +      set = true; | 
| +      error = error_; | 
| +    } | 
| +    cv.notify_all(); | 
| +  } | 
| +  std::string GetError() | 
| +  { | 
| +    std::unique_lock<std::mutex> lock(mutex); | 
| +    return error; | 
| +  } | 
| +private: | 
| +  std::mutex mutex; | 
| +  std::condition_variable cv; | 
| +  bool set; | 
| +  std::string error; | 
| +}; | 
|  | 
| // Strictly speaking in each test there should be a special implementation of | 
| // an interface, which is merely referenced by a wrapper and the latter should | 
| // be injected into JsEngine or what ever. However the everthing a test often | 
| // actually needs is the access to pending tasks. Therefore instantiation of | 
| // implemenation of an interface, creation of shared tasks and sharing them | 
| // with tasks in test is located in this class. | 
| // | 
| @@ -97,96 +134,166 @@ | 
| public: | 
| void operator()(LogLevel logLevel, const std::string& message, | 
| const std::string& source) | 
| { | 
| throw std::runtime_error("Unexpected error: " + message); | 
| } | 
| }; | 
|  | 
| -class ThrowingFileSystem : public AdblockPlus::FileSystem | 
| +class ThrowingFileSystem : public AdblockPlus::IFileSystem, public AdblockPlus::FileSystem | 
| { | 
| public: | 
| std::shared_ptr<std::istream> Read(const std::string& path) const | 
| { | 
| throw std::runtime_error("Not implemented"); | 
| } | 
| +  void Read(const std::string& path, | 
| +            const ReadCallback& callback) const | 
| +  { | 
| +    throw std::runtime_error("Not implemented"); | 
| +  } | 
|  | 
| void Write(const std::string& path, std::istream& content) | 
| { | 
| throw std::runtime_error("Not implemented"); | 
| } | 
| +  void Write(const std::string& path, std::shared_ptr<std::istream> data, | 
| +             const Callback& callback) | 
| +  { | 
| +    throw std::runtime_error("Not implemented"); | 
| +  } | 
|  | 
| void Move(const std::string& fromPath, const std::string& toPath) | 
| { | 
| throw std::runtime_error("Not implemented"); | 
| } | 
| +  void Move(const std::string& fromPath, const std::string& toPath, | 
| +            const Callback& callback) | 
| +  { | 
| +    throw std::runtime_error("Not implemented"); | 
| +  } | 
|  | 
| void Remove(const std::string& path) | 
| { | 
| throw std::runtime_error("Not implemented"); | 
| } | 
| +  void Remove(const std::string& path, const Callback& callback) | 
| +  { | 
| +    throw std::runtime_error("Not implemented"); | 
| +  } | 
|  | 
| StatResult Stat(const std::string& path) const | 
| { | 
| throw std::runtime_error("Not implemented"); | 
| } | 
| +  void Stat(const std::string& path, | 
| +            const StatCallback& callback) const | 
| +  { | 
| +    throw std::runtime_error("Not implemented"); | 
| +  } | 
|  | 
| std::string Resolve(const std::string& path) const | 
| { | 
| throw std::runtime_error("Not implemented"); | 
| } | 
| - | 
| }; | 
|  | 
| class ThrowingWebRequest : public AdblockPlus::IWebRequest | 
| { | 
| public: | 
| void GET(const std::string& url, const AdblockPlus::HeaderList& requestHeaders, const GetCallback&) override | 
| { | 
| throw std::runtime_error("Unexpected GET: " + url); | 
| } | 
| }; | 
|  | 
| -class LazyFileSystem : public AdblockPlus::FileSystem | 
| +class LazyFileSystem : public AdblockPlus::IFileSystem, public AdblockPlus::FileSystem | 
| { | 
| public: | 
| std::shared_ptr<std::istream> Read(const std::string& path) const | 
| { | 
| std::string dummyData(""); | 
| if (path == "patterns.ini") | 
| dummyData = "# Adblock Plus preferences\n[Subscription]\nurl=~fl~"; | 
| else if (path == "prefs.json") | 
| dummyData = "{}"; | 
| return std::shared_ptr<std::istream>(new std::istringstream(dummyData)); | 
| } | 
|  | 
| +  void Read(const std::string& path, const ReadCallback& callback) const | 
| +  { | 
| +    std::thread([this, path, callback] | 
| +    { | 
| +      auto result = Read(path); | 
| +      std::string content = AdblockPlus::Utils::Slurp(*result); | 
| +      callback(std::move(content), ""); | 
| +    }).detach(); | 
| +  } | 
| + | 
| void Write(const std::string& path, std::istream& content) | 
| { | 
| } | 
|  | 
| +  void Write(const std::string& path, std::shared_ptr<std::istream> data, | 
| +             const Callback& callback) | 
| +  { | 
| +    std::thread([this, path, data, callback] | 
| +    { | 
| +      Write(path, *data); | 
| +      callback(""); | 
| +    }).detach(); | 
| +  } | 
| + | 
| void Move(const std::string& fromPath, const std::string& toPath) | 
| { | 
| } | 
|  | 
| +  void Move(const std::string& fromPath, const std::string& toPath, | 
| +            const Callback& callback) | 
| +  { | 
| +    std::thread([this, fromPath, toPath, callback] | 
| +    { | 
| +      Move(fromPath, toPath); | 
| +      callback(""); | 
| +    }).detach(); | 
| +  } | 
| + | 
| void Remove(const std::string& path) | 
| { | 
| } | 
|  | 
| +  void Remove(const std::string& path, const Callback& callback) | 
| +  { | 
| +    std::thread([this, path, callback] | 
| +    { | 
| +      Remove(path); | 
| +      callback(""); | 
| +    }).detach(); | 
| +  } | 
| + | 
| StatResult Stat(const std::string& path) const | 
| { | 
| StatResult result; | 
| if (path == "patterns.ini") | 
| { | 
| result.exists = true; | 
| result.isFile = true; | 
| } | 
| return result; | 
| } | 
|  | 
| +  void Stat(const std::string& path, const StatCallback& callback) const | 
| +  { | 
| +    std::thread([this, path, callback] | 
| +    { | 
| +      callback(Stat(path), ""); | 
| +    }).detach(); | 
| +  } | 
| + | 
| std::string Resolve(const std::string& path) const | 
| { | 
| return path; | 
| } | 
| }; | 
|  | 
| class NoopWebRequest : public AdblockPlus::IWebRequest | 
| { | 
|  |