blob: 8079e45520be49a3c1193fb9b5e8756dab4c9fe3 [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 "server/file_watcher.h"
#include <queue>
#include <utility>
#include <glib.h>
#include "base/file_util.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) {}
virtual ~FakeFileWatcher() {
if (source_id_ != 0)
g_source_remove(source_id_);
}
virtual const std::vector<base::FilePath>& files() const {
return exposed_files_;
};
virtual void SetChangedCallback(
FileWatcher::FileWatcherCallback changed_callback) {
changed_callback_ = changed_callback;
}
virtual const base::FilePath& dir() const {
return dir_;
}
virtual const std::string& file_extension() const {
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.
case kFileChanged:
// Both kFileAdded and kFileChanged execute this part:
buf = static_cast<char*>(malloc(event.file_size));
file_util::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_;
DISALLOW_COPY_AND_ASSIGN(FakeFileWatcher);
};
} // namespace server
} // namespace p2p
#endif // P2P_SERVER_FAKE_FILE_WATCHER_H__