| // 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__ |