blob: 91b2c7d60e71e5437797d1795b456c831892eda5 [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef P2P_SERVER_FAKE_FILE_WATCHER_H_
#define P2P_SERVER_FAKE_FILE_WATCHER_H_
#include <glib.h>
#include <queue>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <base/compiler_specific.h>
#include <base/files/file_util.h>
#include "p2p/server/file_watcher.h"
namespace p2p {
namespace server {
// A FileWatcher that doesn't really do anything.
class FakeFileWatcher : public FileWatcher {
public:
FakeFileWatcher(const base::FilePath& dir, const std::string& file_extension)
: dir_(dir), file_extension_(file_extension), source_id_(0) {}
FakeFileWatcher(const FakeFileWatcher&) = delete;
FakeFileWatcher& operator=(const FakeFileWatcher&) = delete;
~FakeFileWatcher() override {
if (source_id_ != 0)
g_source_remove(source_id_);
}
const std::vector<base::FilePath>& files() const override {
return exposed_files_;
}
void SetChangedCallback(
FileWatcher::FileWatcherCallback changed_callback) override {
changed_callback_ = changed_callback;
}
const base::FilePath& dir() const override { return dir_; }
const std::string& file_extension() const override { return file_extension_; }
// Fake methods.
// Add, Remove or Touch a file in the watched directory. Since those
// functions are intended to be called by a test, the file extension is not
// checked. This will make the watched directory to emit a call to the
// provided callback with the appropriate arguments in each case from the main
// loop.
bool AddFile(const base::FilePath& filename, size_t file_size) {
if (files_.find(filename) != files_.end())
return false;
files_.insert(filename);
// Schedule the action.
if (pending_actions_.empty())
source_id_ = g_idle_add(OnFileChanged, this);
pending_actions_.push((FileEvent){.filename = filename,
.event_type = kFileAdded,
.file_size = file_size});
return true;
}
bool RemoveFile(const base::FilePath& filename) {
if (files_.find(filename) == files_.end())
return false;
files_.erase(filename);
// Schedule the action.
if (pending_actions_.empty())
source_id_ = g_idle_add(OnFileChanged, this);
pending_actions_.push((FileEvent){
.filename = filename, .event_type = kFileRemoved, .file_size = 0});
return true;
}
bool TouchFile(const base::FilePath& filename, size_t file_size) {
if (files_.find(filename) != files_.end())
return false;
// Schedule the action.
if (pending_actions_.empty())
source_id_ = g_idle_add(OnFileChanged, this);
pending_actions_.push((FileEvent){.filename = filename,
.event_type = kFileChanged,
.file_size = file_size});
return true;
}
private:
static gboolean OnFileChanged(gpointer user_data) {
FakeFileWatcher* watcher = reinterpret_cast<FakeFileWatcher*>(user_data);
const FileEvent& event = watcher->pending_actions_.front();
char* buf = NULL;
switch (event.event_type) {
case kFileAdded:
watcher->exposed_files_.push_back(event.filename);
// Create the file on disk to allow the consumer get its file size.
FALLTHROUGH;
case kFileChanged:
// Both kFileAdded and kFileChanged execute this part:
buf = static_cast<char*>(malloc(event.file_size));
base::WriteFile(event.filename, buf, event.file_size);
free(buf);
break;
case kFileRemoved:
watcher->exposed_files_.erase(find(watcher->exposed_files_.begin(),
watcher->exposed_files_.end(),
event.filename));
unlink(event.filename.value().c_str());
break;
}
// Dispatch the callback. This could add more events to the queue, so we
// keep the processed event until the callback returns and check empty()
// later on this function.
if (!watcher->changed_callback_.is_null())
watcher->changed_callback_.Run(event.filename, event.event_type);
watcher->pending_actions_.pop();
return !watcher->pending_actions_.empty();
}
base::FilePath dir_;
std::string file_extension_;
FileWatcher::FileWatcherCallback changed_callback_;
// The list of files in the watched directory once the Add/Remove event was
// processed. This is what a call to files() will return.
std::vector<base::FilePath> exposed_files_;
// The set of files added by the test, used to ensure proper call arguments
// (i.e. fail when the test attempts to add twice the same file).
std::set<base::FilePath> files_;
// The list of pending actions (Add/Remove/Touch) to be processed.
struct FileEvent {
base::FilePath filename;
EventType event_type;
size_t file_size;
};
std::queue<FileEvent> pending_actions_;
// The source_id used to dispatch file events.
guint source_id_;
};
} // namespace server
} // namespace p2p
#endif // P2P_SERVER_FAKE_FILE_WATCHER_H_