commit 4eca19f3eeac488bbef020e8bf88df3ef7a30a58
parent 9070d20b9a7cdfb99ed6fbeb6902341aa2184d7a
Author: cfillion <cfillion@users.noreply.github.com>
Date: Tue, 19 Jan 2016 01:16:29 -0500
implement full remote repository uninstallation
Diffstat:
19 files changed, 365 insertions(+), 119 deletions(-)
diff --git a/src/database.cpp b/src/database.cpp
@@ -114,6 +114,12 @@ void Statement::bind(const int index, const string &text)
throw m_db->lastError();
}
+void Statement::bind(const int index, const int integer)
+{
+ if(sqlite3_bind_int(m_stmt, index, integer))
+ throw m_db->lastError();
+}
+
void Statement::bind(const int index, const uint64_t integer)
{
if(sqlite3_bind_int64(m_stmt, index, (sqlite3_int64)integer))
diff --git a/src/database.hpp b/src/database.hpp
@@ -56,6 +56,7 @@ public:
typedef std::function<bool (void)> ExecCallback;
void bind(const int index, const std::string &text);
+ void bind(const int index, const int integer);
void bind(const int index, const uint64_t integer);
void exec();
void exec(const ExecCallback &);
diff --git a/src/manager.cpp b/src/manager.cpp
@@ -184,6 +184,7 @@ void Manager::apply()
}
for(const Remote &remote : m_uninstall) {
+ m_reapack->uninstall(remote);
list->remove(remote);
}
diff --git a/src/path.cpp b/src/path.cpp
@@ -81,6 +81,11 @@ void Path::clear()
m_parts.clear();
}
+void Path::removeLast()
+{
+ m_parts.pop_back();
+}
+
string Path::basename() const
{
if(m_parts.empty())
diff --git a/src/path.hpp b/src/path.hpp
@@ -27,6 +27,7 @@ public:
void prepend(const std::string &part);
void append(const std::string &part);
+ void removeLast();
void clear();
bool empty() const { return m_parts.empty(); }
diff --git a/src/progress.cpp b/src/progress.cpp
@@ -38,8 +38,10 @@ void Progress::setTransaction(Transaction *t)
m_total = 0;
m_currentName.clear();
- if(!m_transaction)
+ if(!m_transaction) {
+ hide();
return;
+ }
SetWindowText(m_label, AUTO_STR("Initializing..."));
@@ -73,6 +75,9 @@ void Progress::addDownload(Download *dl)
updateProgress();
dl->onStart([=] {
+ if(!isVisible())
+ show();
+
m_currentName = make_autostring(dl->name());
updateProgress();
});
diff --git a/src/reapack.cpp b/src/reapack.cpp
@@ -104,6 +104,7 @@ void ReaPack::synchronizeAll()
return;
}
+ // do nothing is a transation is already running
Transaction *t = createTransaction();
if(!t)
@@ -124,6 +125,19 @@ void ReaPack::synchronize(const Remote &remote)
m_transaction->synchronize(remote);
}
+void ReaPack::uninstall(const Remote &remote)
+{
+ if(remote.isProtected())
+ return;
+
+ if(!m_transaction) {
+ if(!createTransaction())
+ return;
+ }
+
+ m_transaction->uninstall(remote);
+}
+
void ReaPack::importRemote()
{
static const char *title = "ReaPack: Import remote repository";
@@ -209,7 +223,6 @@ Transaction *ReaPack::createTransaction()
}
m_progress->setTransaction(m_transaction);
- m_progress->show();
m_transaction->onFinish([=] {
if(m_transaction->isCancelled())
@@ -217,13 +230,12 @@ Transaction *ReaPack::createTransaction()
m_progress->disable();
- if(m_transaction->packages().empty() && m_transaction->errors().empty())
+ if(m_transaction->taskCount() == 0 && m_transaction->errors().empty())
ShowMessageBox("Nothing to do!", "ReaPack", 0);
else
Dialog::Show<Report>(m_instance, m_mainWindow, m_transaction);
m_progress->enable();
- m_progress->hide();
});
m_transaction->onDestroy([=] {
diff --git a/src/reapack.hpp b/src/reapack.hpp
@@ -49,6 +49,7 @@ public:
void synchronizeAll();
void synchronize(const Remote &);
+ void uninstall(const Remote &);
void importRemote();
void manageRemotes();
diff --git a/src/registry.cpp b/src/registry.cpp
@@ -21,6 +21,7 @@
#include "index.hpp"
#include "package.hpp"
#include "path.hpp"
+#include "remote.hpp"
#include <reaper_plugin_functions.h>
@@ -31,6 +32,7 @@ Registry::Registry(const Path &path)
{
migrate();
+ // entry queries
m_insertEntry = m_db.prepare(
"INSERT OR REPLACE INTO entries "
"VALUES(NULL, ?, ?, ?, ?);"
@@ -42,6 +44,10 @@ Registry::Registry(const Path &path)
"LIMIT 1"
);
+ m_allEntries = m_db.prepare("SELECT id, version FROM entries WHERE remote = ?");
+ m_forgetEntry = m_db.prepare("DELETE FROM entries WHERE id = ?");
+
+ // file queries
m_getFiles = m_db.prepare("SELECT path FROM files WHERE entry = ?");
m_insertFile = m_db.prepare("INSERT INTO files VALUES(NULL, ?, ?)");
m_clearFiles = m_db.prepare(
@@ -49,6 +55,7 @@ Registry::Registry(const Path &path)
" SELECT id FROM entries WHERE remote = ? AND category = ? AND package = ?"
")"
);
+ m_forgetFiles = m_db.prepare("DELETE FROM files WHERE entry = ?");
// lock the database
m_db.begin();
@@ -144,6 +151,22 @@ Registry::Entry Registry::query(Package *pkg) const
return {id, status, version};
}
+vector<Registry::Entry> Registry::queryAll(const Remote &remote) const
+{
+ vector<Registry::Entry> list;
+
+ m_allEntries->bind(1, remote.name());
+ m_allEntries->exec([&] {
+ const int id = m_allEntries->intColumn(0);
+ const uint64_t version = m_allEntries->uint64Column(1);
+
+ list.push_back({id, Unknown, version});
+ return true;
+ });
+
+ return list;
+}
+
set<Path> Registry::getFiles(const Entry &qr) const
{
if(!qr.id) // skip processing for new packages
@@ -160,6 +183,15 @@ set<Path> Registry::getFiles(const Entry &qr) const
return list;
}
+void Registry::forget(const Entry &qr)
+{
+ m_forgetFiles->bind(1, qr.id);
+ m_forgetFiles->exec();
+
+ m_forgetEntry->bind(1, qr.id);
+ m_forgetEntry->exec();
+}
+
void Registry::commit()
{
m_db.commit();
diff --git a/src/registry.hpp b/src/registry.hpp
@@ -26,6 +26,7 @@
class Package;
class Path;
+class Remote;
class Version;
class Registry {
@@ -33,9 +34,10 @@ public:
Registry(const Path &path = Path());
enum Status {
- UpToDate,
- UpdateAvailable,
+ Unknown,
Uninstalled,
+ UpdateAvailable,
+ UpToDate,
};
struct Entry {
@@ -45,8 +47,10 @@ public:
};
Entry query(Package *) const;
+ std::vector<Entry> queryAll(const Remote &) const;
std::set<Path> getFiles(const Entry &) const;
void push(Version *);
+ void forget(const Entry &);
void commit();
bool addToREAPER(Version *ver, const Path &root);
@@ -57,9 +61,13 @@ private:
Database m_db;
Statement *m_insertEntry;
Statement *m_findEntry;
+ Statement *m_allEntries;
+ Statement *m_forgetEntry;
+
Statement *m_getFiles;
Statement *m_insertFile;
Statement *m_clearFiles;
+ Statement *m_forgetFiles;
};
#endif
diff --git a/src/report.cpp b/src/report.cpp
@@ -23,7 +23,6 @@
#include "transaction.hpp"
#include <boost/range/adaptor/reversed.hpp>
-#include <sstream>
using namespace std;
@@ -37,29 +36,32 @@ Report::Report(Transaction *transaction)
void Report::onInit()
{
- const size_t newPacks = m_transaction->newPackages().size();
+ const size_t newPackages = m_transaction->newPackages().size();
const size_t updates = m_transaction->updates().size();
+ const size_t removals = m_transaction->removals().size();
const size_t errors = m_transaction->errors().size();
- ostringstream text;
-
- text
- << newPacks << " new packages, "
- << updates << " updates and "
+ m_stream
+ << newPackages << " new packages, "
+ << updates << " updates, "
+ << removals << " removed files and "
<< errors << " errors"
<< NL
;
if(errors)
- formatErrors(text);
+ printErrors();
- if(newPacks)
- formatNewPackages(text);
+ if(newPackages)
+ printNewPackages();
if(updates)
- formatUpdates(text);
+ printUpdates();
+
+ if(removals)
+ printRemovals();
- const auto_string &str = make_autostring(text.str());
+ const auto_string &str = make_autostring(m_stream.str());
SetDlgItemText(handle(), IDC_REPORT, str.c_str());
}
@@ -73,19 +75,19 @@ void Report::onCommand(WPARAM wParam, LPARAM)
}
}
-void Report::formatNewPackages(ostringstream &text)
+void Report::printNewPackages()
{
- text << NL << SEP << " New packages: " << SEP << NL;
+ printHeader("New packages");
for(const Transaction::PackageEntry &entry : m_transaction->newPackages()) {
Version *ver = entry.first;
- text << NL << ver->fullName() << NL;
+ m_stream << NL << ver->fullName() << NL;
}
}
-void Report::formatUpdates(ostringstream &text)
+void Report::printUpdates()
{
- text << NL << SEP << " Updates: " << SEP << NL;
+ printHeader("Updates");
for(const Transaction::PackageEntry &entry : m_transaction->updates()) {
Package *pkg = entry.first->package();
@@ -96,27 +98,40 @@ void Report::formatUpdates(ostringstream &text)
if(ver->code() <= regEntry.version)
break;
- text << NL << ver->fullName() << NL;
+ m_stream << NL << ver->fullName() << NL;
if(!ver->changelog().empty())
- formatChangelog(ver->changelog(), text);
+ printChangelog(ver->changelog());
}
}
}
-void Report::formatChangelog(const string &changelog, ostringstream &output)
+void Report::printChangelog(const string &changelog)
{
istringstream input(changelog);
string line;
while(getline(input, line, '\n'))
- output << " " << line.substr(line.find_first_not_of('\x20')) << NL;
+ m_stream << "\x20\x20" << line.substr(line.find_first_not_of('\x20')) << NL;
}
-void Report::formatErrors(ostringstream &text)
+void Report::printErrors()
{
- text << NL << SEP << " Errors: " << SEP << NL;
+ printHeader("Errors");
for(const Transaction::Error &err : m_transaction->errors())
- text << NL << err.title << ":" << NL << err.message << NL;
+ m_stream << NL << err.title << ':' << NL << err.message << NL;
+}
+
+void Report::printRemovals()
+{
+ printHeader("Removed files");
+
+ for(const Path &path : m_transaction->removals())
+ m_stream << NL << path.join();
+}
+
+void Report::printHeader(const char *title)
+{
+ m_stream << NL << SEP << ' ' << title << ": " << SEP << NL;
}
diff --git a/src/report.hpp b/src/report.hpp
@@ -20,6 +20,8 @@
#include "dialog.hpp"
+#include <sstream>
+
class Transaction;
class Report : public Dialog {
@@ -31,12 +33,15 @@ protected:
void onCommand(WPARAM, LPARAM) override;
private:
- void formatNewPackages(std::ostringstream &);
- void formatUpdates(std::ostringstream &);
- void formatErrors(std::ostringstream &);
- void formatChangelog(const std::string &, std::ostringstream &);
+ void printNewPackages();
+ void printUpdates();
+ void printErrors();
+ void printRemovals();
+ void printChangelog(const std::string &);
+ void printHeader(const char *);
Transaction *m_transaction;
+ std::ostringstream m_stream;
};
#endif
diff --git a/src/resource.rc b/src/resource.rc
@@ -18,7 +18,8 @@ STYLE DIALOG_STYLE
FONT DIALOG_FONT
CAPTION "ReaPack: Transaction Report"
BEGIN
- LTEXT "Synchronization complete!", IDC_LABEL, 5, 5, 250, 10
+ LTEXT "All done! Description of the changes:",
+ IDC_LABEL, 5, 5, 250, 10
EDITTEXT IDC_REPORT, 6, 18, 248, 195, WS_VSCROLL | ES_MULTILINE |
ES_READONLY | NOT WS_TABSTOP
DEFPUSHBUTTON "OK", IDOK, 105, 220, 50, 14
diff --git a/src/task.cpp b/src/task.cpp
@@ -32,7 +32,51 @@ Task::Task(Transaction *transaction)
{
}
-void Task::install(Version *ver, const set<Path> &oldFiles)
+void Task::rollback()
+{
+ m_isCancelled = true;
+
+ // it's the transaction queue's job to abort the running downloads, not ours
+
+ doRollback();
+}
+
+void Task::commit()
+{
+ if(m_isCancelled)
+ return;
+
+ doCommit();
+
+ m_onCommit();
+}
+
+int Task::removeFile(const Path &path) const
+{
+ const string &fullPath = m_transaction->prefixPath(path).join();
+
+#ifdef _WIN32
+ return _wremove(make_autostring(fullPath).c_str());
+#else
+ return remove(fullPath.c_str());
+#endif
+}
+
+int Task::renameFile(const Path &from, const Path &to) const
+{
+ const string &fullFrom = m_transaction->prefixPath(from).join();
+ const string &fullTo = m_transaction->prefixPath(to).join();
+
+#ifdef _WIN32
+ return _wrename(make_autostring(fullFrom).c_str(),
+ make_autostring(fullTo).c_str());
+#else
+ return rename(fullFrom.c_str(), fullTo.c_str());
+#endif
+}
+
+InstallTask::InstallTask(Version *ver, const set<Path> &oldFiles, Transaction *t)
+ : Task(t), m_oldFiles(move(oldFiles))
{
const auto &sources = ver->sources();
@@ -41,62 +85,48 @@ void Task::install(Version *ver, const set<Path> &oldFiles)
Source *src = it->second;
Download *dl = new Download(src->fullName(), src->url());
- dl->onFinish(bind(&Task::saveSource, this, dl, src));
+ dl->onFinish(bind(&InstallTask::saveSource, this, dl, src));
- m_transaction->downloadQueue()->push(dl);
+ transaction()->downloadQueue()->push(dl);
// skip duplicate files
do { it++; } while(it != sources.end() && path == it->first);
}
-
- m_oldFiles = move(oldFiles);
}
-void Task::saveSource(Download *dl, Source *src)
+void InstallTask::saveSource(Download *dl, Source *src)
{
- if(m_isCancelled)
+ if(isCancelled())
return;
- const Path targetPath = src->targetPath();
- Path tmpPath = targetPath;
+ const Path &targetPath = src->targetPath();
+ Path tmpPath(targetPath);
tmpPath[tmpPath.size() - 1] += ".new";
- m_files.push_back({tmpPath, targetPath});
+ m_newFiles.push_back({tmpPath, targetPath});
const auto old = m_oldFiles.find(targetPath);
if(old != m_oldFiles.end())
m_oldFiles.erase(old);
- const Path path = m_transaction->prefixPath(tmpPath);
+ const Path &path = transaction()->prefixPath(tmpPath);
- if(!m_transaction->saveFile(dl, path)) {
- cancel();
+ if(!transaction()->saveFile(dl, path)) {
+ rollback();
return;
}
}
-void Task::cancel()
-{
- m_isCancelled = true;
-
- // it's the transaction queue's job to abort the running downloads, not ours
-
- rollback();
-}
-
-void Task::commit()
+void InstallTask::doCommit()
{
- if(m_isCancelled)
- return;
-
for(const Path &path : m_oldFiles)
removeFile(path);
- for(const PathPair &paths : m_files) {
+ for(const PathPair &paths : m_newFiles) {
removeFile(paths.second);
if(renameFile(paths.first, paths.second)) {
- m_transaction->addError(strerror(errno), paths.first.join());
+ transaction()->addError(strerror(errno), paths.first.join());
// it's a bit late to rollback here as some files might already have been
// overwritten. at least we can delete the temporary files
@@ -104,38 +134,43 @@ void Task::commit()
return;
}
}
-
- m_onCommit();
}
-void Task::rollback()
+void InstallTask::doRollback()
{
- for(const PathPair &paths : m_files)
+ for(const PathPair &paths : m_newFiles)
removeFile(paths.first);
- m_files.clear();
+ m_newFiles.clear();
}
-int Task::removeFile(const Path &path) const
+RemoveTask::RemoveTask(const std::set<Path> &files, Transaction *t)
+ : Task(t), m_files(move(files))
{
- const string &fullPath = m_transaction->prefixPath(path).join();
+}
-#ifdef _WIN32
- return _wremove(make_autostring(fullPath).c_str());
-#else
- return remove(fullPath.c_str());
-#endif
+void RemoveTask::doCommit()
+{
+ for(const Path &path : m_files)
+ remove(path);
}
-int Task::renameFile(const Path &from, const Path &to) const
+void RemoveTask::remove(const Path &file)
{
- const string &fullFrom = m_transaction->prefixPath(from).join();
- const string &fullTo = m_transaction->prefixPath(to).join();
+ if(removeFile(file)) {
+ transaction()->addError(strerror(errno), file.join());
+ return;
+ }
+ else
+ m_removedFiles.push_back(file);
-#ifdef _WIN32
- return _wrename(make_autostring(fullFrom).c_str(),
- make_autostring(fullTo).c_str());
-#else
- return rename(fullFrom.c_str(), fullTo.c_str());
-#endif
+ Path dir = file;
+
+ // remove empty directories, but not top-level ones that were created by REAPER
+ while(dir.size() > 2) {
+ dir.removeLast();
+
+ if(removeFile(dir))
+ break;
+ }
}
diff --git a/src/task.hpp b/src/task.hpp
@@ -34,30 +34,63 @@ public:
typedef Signal::slot_type Callback;
Task(Transaction *parent);
+ virtual ~Task() {}
void onCommit(const Callback &callback) { m_onCommit.connect(callback); }
+ bool isCancelled() const { return m_isCancelled; }
- void install(Version *ver, const std::set<Path> &oldFiles);
void commit();
- void cancel();
+ void rollback();
-private:
+protected:
int removeFile(const Path &) const;
int renameFile(const Path &, const Path &) const;
- typedef std::pair<Path, Path> PathPair;
+ Transaction *transaction() const { return m_transaction; }
- void finish();
- void rollback();
+ virtual void doCommit() = 0;
+ virtual void doRollback() = 0;
- void saveSource(Download *, Source *);
+private:
Transaction *m_transaction;
bool m_isCancelled;
- std::vector<PathPair> m_files;
- std::set<Path> m_oldFiles;
Signal m_onCommit;
};
+class InstallTask : public Task {
+public:
+ InstallTask(Version *ver, const std::set<Path> &oldFiles, Transaction *);
+
+protected:
+ void doCommit() override;
+ void doRollback() override;
+
+private:
+ typedef std::pair<Path, Path> PathPair;
+
+ void saveSource(Download *, Source *);
+
+ std::vector<PathPair> m_newFiles;
+ std::set<Path> m_oldFiles;
+};
+
+class RemoveTask : public Task {
+public:
+ RemoveTask(const std::set<Path> &files, Transaction *);
+
+ const std::vector<Path> &removedFiles() const { return m_removedFiles; }
+
+protected:
+ void doCommit() override;
+ void doRollback() override {}
+
+private:
+ void remove(const Path &);
+
+ std::set<Path> m_files;
+ std::vector<Path> m_removedFiles;
+};
+
#endif
diff --git a/src/transaction.cpp b/src/transaction.cpp
@@ -122,33 +122,54 @@ void Transaction::install()
for(const PackageEntry &entry : m_packages) {
Version *ver = entry.first;
const Registry::Entry regEntry = entry.second;
+ const set<Path> ¤tFiles = m_registry->getFiles(regEntry);
- Task *task = new Task(this);
+ InstallTask *task = new InstallTask(ver, currentFiles, this);
- try {
- task->install(ver, m_registry->getFiles(regEntry));
- task->onCommit([=] {
- if(regEntry.status == Registry::UpdateAvailable)
- m_updates.push_back(entry);
- else
- m_new.push_back(entry);
+ task->onCommit([=] {
+ if(regEntry.status == Registry::UpdateAvailable)
+ m_updates.push_back(entry);
+ else
+ m_new.push_back(entry);
- m_registry->push(ver);
+ m_registry->push(ver);
- if(!m_registry->addToREAPER(ver, m_root)) {
- addError(
- "Cannot register the package in REAPER. "
- "Are you using REAPER v5.12 or more recent?", ver->fullName()
- );
- }
- });
+ if(!m_registry->addToREAPER(ver, m_root)) {
+ addError(
+ "Cannot register the package in REAPER. "
+ "Are you using REAPER v5.12 or more recent?", ver->fullName()
+ );
+ }
+ });
- m_tasks.push_back(task);
- }
- catch(const reapack_error &e) {
- addError(e.what(), ver->fullName());
- delete task;
- }
+ addTask(task);
+ }
+}
+
+void Transaction::uninstall(const Remote &remote)
+{
+ const vector<Registry::Entry> &entries = m_registry->queryAll(remote);
+
+ if(entries.empty()) {
+ cancel();
+ return;
+ }
+
+ for(const auto &entry : entries) {
+ const set<Path> &files = m_registry->getFiles(entry);
+
+ RemoveTask *task = new RemoveTask(files, this);
+
+ task->onCommit([=] {
+ const vector<Path> &removedFiles = task->removedFiles();
+
+ m_registry->forget(entry);
+
+ m_removals.insert(m_removals.end(),
+ removedFiles.begin(), removedFiles.end());
+ });
+
+ addTask(task);
}
}
@@ -157,9 +178,12 @@ void Transaction::cancel()
m_isCancelled = true;
for(Task *task : m_tasks)
- task->cancel();
+ task->rollback();
- m_queue.abort();
+ if(m_queue.idle())
+ finish();
+ else
+ m_queue.abort();
}
bool Transaction::saveFile(Download *dl, const Path &path)
@@ -234,3 +258,11 @@ void Transaction::registerFiles(const set<Path> &list)
m_files.insert(list.begin(), list.end());
}
+
+void Transaction::addTask(Task *task)
+{
+ m_tasks.push_back(task);
+
+ if(m_queue.idle())
+ finish();
+}
diff --git a/src/transaction.hpp b/src/transaction.hpp
@@ -25,8 +25,10 @@
#include <boost/signals2.hpp>
#include <set>
+class InstallTask;
class Remote;
class RemoteIndex;
+class RemoveTask;
class Task;
class Transaction {
@@ -51,14 +53,16 @@ public:
void onDestroy(const Callback &callback) { m_onDestroy.connect(callback); }
void synchronize(const Remote &);
+ void uninstall(const Remote &);
void install();
void cancel();
bool isCancelled() const { return m_isCancelled; }
DownloadQueue *downloadQueue() { return &m_queue; }
- const PackageEntryList &packages() const { return m_packages; }
+ size_t taskCount() const { return m_tasks.size(); }
const PackageEntryList &newPackages() const { return m_new; }
const PackageEntryList &updates() const { return m_updates; }
+ const std::vector<Path> &removals() const { return m_removals; }
const ErrorList &errors() const { return m_errors; }
private:
@@ -70,6 +74,8 @@ private:
};
friend Task;
+ friend InstallTask;
+ friend RemoveTask;
void updateAll();
void finish();
@@ -80,6 +86,7 @@ private:
Path prefixPath(const Path &) const;
bool allFilesExists(const std::set<Path> &) const;
void registerFiles(const std::set<Path> &);
+ void addTask(Task *);
Registry *m_registry;
@@ -93,6 +100,7 @@ private:
PackageEntryList m_packages;
PackageEntryList m_new;
PackageEntryList m_updates;
+ std::vector<Path> m_removals;
ErrorList m_errors;
std::vector<Task *> m_tasks;
diff --git a/test/path.cpp b/test/path.cpp
@@ -150,3 +150,16 @@ TEST_CASE("absolute path (unix)", M) {
REQUIRE(a.join() == "/usr/bin/zsh");
}
#endif
+
+TEST_CASE("remove last component of path", M) {
+ Path a;
+ a.append("a");
+ a.append("b");
+
+ CHECK(a.size() == 2);
+
+ a.removeLast();
+
+ REQUIRE(a.size() == 1);
+ REQUIRE(a[0] == "a");
+}
diff --git a/test/registry.cpp b/test/registry.cpp
@@ -6,13 +6,14 @@
#include <index.hpp>
#include <package.hpp>
+#include <remote.hpp>
using namespace std;
static const char *M = "[registry]";
#define MAKE_PACKAGE \
- RemoteIndex ri("Hello"); \
+ RemoteIndex ri("Remote Name"); \
Category cat("Hello", &ri); \
Package pkg(Package::ScriptType, "Hello", &cat); \
Version *ver = new Version("1.0", &pkg); \
@@ -74,3 +75,34 @@ TEST_CASE("get file list", M) {
REQUIRE(files == ver->files());
}
+
+TEST_CASE("query all packages", M) {
+ MAKE_PACKAGE
+
+ const Remote remote("Remote Name", "irrelevent_url");
+
+ Registry reg;
+ REQUIRE(reg.queryAll(remote).empty());
+
+ reg.push(ver);
+
+ const vector<Registry::Entry> entries = reg.queryAll(remote);
+ REQUIRE(entries.size() == 1);
+ REQUIRE(entries[0].id == 1);
+ REQUIRE(entries[0].status == Registry::Unknown);
+ REQUIRE(entries[0].version == ver->code());
+}
+
+TEST_CASE("forget registry entry", M) {
+ MAKE_PACKAGE
+
+ Registry reg;
+ reg.push(ver);
+
+ reg.forget(reg.query(&pkg));
+
+ const Registry::Entry afterForget = reg.query(&pkg);
+ REQUIRE(afterForget.id == 0);
+ REQUIRE(afterForget.status == Registry::Uninstalled);
+ REQUIRE(afterForget.version == 0);
+}