reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

commit 0008d74cc7b5066dbb954459b0c4dabddf9e21e6
parent f79b8e1f0fbe1b0c0019989f68069107e8880f7a
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Wed, 20 Apr 2016 22:51:47 -0400

Merge branch 'prereleases'

Diffstat:
Msrc/browser.cpp | 15+++++++++++----
Msrc/browser.hpp | 2+-
Msrc/config.cpp | 11++++++++---
Msrc/config.hpp | 16++++++++++------
Msrc/manager.cpp | 27+++++++++++++++++++++------
Msrc/manager.hpp | 1+
Msrc/package.cpp | 12+++++++-----
Msrc/package.hpp | 2+-
Msrc/reapack.cpp | 37++++++++++++++++++++++++-------------
Msrc/reapack.hpp | 4++++
Msrc/transaction.cpp | 15+++++++++------
Msrc/transaction.hpp | 5+++--
Msrc/version.cpp | 125++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/version.hpp | 30+++++++++++++++++++-----------
Mtest/index_v1.cpp | 4++--
Mtest/package.cpp | 24++++++++++++++++++++++++
Mtest/version.cpp | 260+++++++++++++++++++++++++++++++++++++++----------------------------------------
17 files changed, 345 insertions(+), 245 deletions(-)

diff --git a/src/browser.cpp b/src/browser.cpp @@ -461,9 +461,12 @@ void Browser::transferActions() } auto Browser::makeEntry(const Package *pkg, const Registry::Entry &regEntry) - -> Entry + const -> Entry { - const Version *latest = pkg->lastVersion(); + const auto &instOpts = *m_reapack->config()->install(); + const bool includePre = instOpts.bleedingEdge || + (regEntry && !regEntry.version.isStable()); + const Version *latest = pkg->lastVersion(includePre); const Version *current = nullptr; int flags = 0; @@ -471,13 +474,17 @@ auto Browser::makeEntry(const Package *pkg, const Registry::Entry &regEntry) if(regEntry) { flags |= InstalledFlag; - if(regEntry.version < *latest) + if(latest && regEntry.version < *latest) flags |= OutOfDateFlag; current = pkg->findVersion(regEntry.version); } - else + else { + if(!latest) // show prerelases if no stable version is available + latest = pkg->lastVersion(true); + flags |= UninstalledFlag; + } return {flags, regEntry, pkg, latest, current}; } diff --git a/src/browser.hpp b/src/browser.hpp @@ -89,7 +89,7 @@ private: Uninstalled, }; - static Entry makeEntry(const Package *, const Registry::Entry &); + Entry makeEntry(const Package *, const Registry::Entry &) const; void populate(); void transferActions(); diff --git a/src/config.cpp b/src/config.cpp @@ -33,6 +33,7 @@ static const char *VERSION_KEY = "version"; static const char *INSTALL_GRP = "install"; static const char *AUTOINSTALL_KEY = "autoinstall"; +static const char *PRERELEASES_KEY = "prereleases"; static const char *BROWSER_GRP = "browser"; static const char *TYPEFILTER_KEY = "typefilter"; @@ -50,7 +51,7 @@ static string ArrayKey(const string &key, const unsigned int i) static const int BUFFER_SIZE = 2083; Config::Config() - : m_isFirstRun(false), m_version(0), m_autoInstall(false), m_browser(), + : m_isFirstRun(false), m_version(0), m_install(), m_browser(), m_remotesIniSize(0) { } @@ -90,7 +91,10 @@ void Config::read(const Path &path) { m_path = path.join(); - m_autoInstall = getUInt(INSTALL_GRP, AUTOINSTALL_KEY) > 0; + m_install = { + getUInt(INSTALL_GRP, AUTOINSTALL_KEY) > 0, + getUInt(INSTALL_GRP, PRERELEASES_KEY) > 0, + }; m_browser = { getUInt(BROWSER_GRP, TYPEFILTER_KEY), @@ -105,7 +109,8 @@ void Config::write() { setUInt(GENERAL_GRP, VERSION_KEY, m_version); - setUInt(INSTALL_GRP, AUTOINSTALL_KEY, m_autoInstall); + setUInt(INSTALL_GRP, AUTOINSTALL_KEY, m_install.autoInstall); + setUInt(INSTALL_GRP, PRERELEASES_KEY, m_install.bleedingEdge); setUInt(BROWSER_GRP, TYPEFILTER_KEY, m_browser.typeFilter); diff --git a/src/config.hpp b/src/config.hpp @@ -24,7 +24,12 @@ class Path; -struct BrowserConfig { +struct InstallOpts { + bool autoInstall; + bool bleedingEdge; +}; + +struct BrowserOpts { unsigned int typeFilter; }; @@ -36,10 +41,9 @@ public: void write(); bool isFirstRun() const { return m_isFirstRun; } - bool autoInstall() const { return m_autoInstall; } - void setAutoInstall(const bool enable) { m_autoInstall = enable; } RemoteList *remotes() { return &m_remotes; } - BrowserConfig *browser() { return &m_browser; } + InstallOpts *install() { return &m_install; } + BrowserOpts *browser() { return &m_browser; } private: std::string getString(const char *, const std::string &) const; @@ -55,8 +59,8 @@ private: std::string m_path; bool m_isFirstRun; unsigned int m_version; - bool m_autoInstall; - BrowserConfig m_browser; + InstallOpts m_install; + BrowserOpts m_browser; void readRemotes(); void restoreSelfRemote(); diff --git a/src/manager.cpp b/src/manager.cpp @@ -30,7 +30,7 @@ using namespace std; enum { ACTION_ENABLE = 80, ACTION_DISABLE, ACTION_UNINSTALL, ACTION_ABOUT, - ACTION_AUTOINSTALL, ACTION_SELECT, ACTION_UNSELECT }; + ACTION_AUTOINSTALL, ACTION_BLEEDINGEDGE, ACTION_SELECT, ACTION_UNSELECT }; Manager::Manager(ReaPack *reapack) : Dialog(IDD_CONFIG_DIALOG), @@ -77,7 +77,11 @@ void Manager::onCommand(const int id, int) uninstall(); break; case ACTION_AUTOINSTALL: - m_autoInstall = !m_autoInstall.value_or(m_config->autoInstall()); + m_autoInstall = !m_autoInstall.value_or(m_config->install()->autoInstall); + enable(m_apply); + break; + case ACTION_BLEEDINGEDGE: + m_bleedingEdge = !m_bleedingEdge.value_or(m_config->install()->bleedingEdge); enable(m_apply); break; case ACTION_SELECT: @@ -247,10 +251,14 @@ void Manager::options() Menu menu; - const UINT index = menu.addAction( + UINT index = menu.addAction( AUTO_STR("&Install new packages automatically"), ACTION_AUTOINSTALL); + if(m_autoInstall.value_or(m_config->install()->autoInstall)) + menu.check(index); - if(m_autoInstall.value_or(m_config->autoInstall())) + index = menu.addAction( + AUTO_STR("Enable &pre-releases (bleeding edge mode)"), ACTION_BLEEDINGEDGE); + if(m_bleedingEdge.value_or(m_config->install()->bleedingEdge)) menu.check(index); menu.show(rect.left, rect.bottom - 1, handle()); @@ -278,7 +286,10 @@ bool Manager::confirm() const void Manager::apply() { if(m_autoInstall) - m_config->setAutoInstall(m_autoInstall.value()); + m_config->install()->autoInstall = m_autoInstall.value(); + + if(m_bleedingEdge) + m_config->install()->bleedingEdge = m_bleedingEdge.value(); for(const auto &pair : m_enableOverrides) { const Remote &remote = pair.first; @@ -294,7 +305,11 @@ void Manager::apply() m_reapack->uninstall(remote); m_config->write(); - m_reapack->runTasks(); + + if(m_reapack->isRunning()) + m_reapack->runTasks(); + else + m_reapack->refreshBrowser(); } void Manager::reset() diff --git a/src/manager.hpp b/src/manager.hpp @@ -63,6 +63,7 @@ private: std::map<Remote, bool> m_enableOverrides; std::set<Remote> m_uninstall; boost::optional<bool> m_autoInstall; + boost::optional<bool> m_bleedingEdge; }; #endif diff --git a/src/package.cpp b/src/package.cpp @@ -21,7 +21,7 @@ #include "index.hpp" #include <algorithm> -#include <sstream> +#include <boost/range/adaptor/reversed.hpp> using namespace std; @@ -99,12 +99,14 @@ const Version *Package::version(const size_t index) const return *it; } -const Version *Package::lastVersion() const +const Version *Package::lastVersion(const bool prerelease) const { - if(m_versions.empty()) - return nullptr; + for(const Version *ver : m_versions | boost::adaptors::reversed) { + if(ver->isStable() || prerelease) + return ver; + } - return *m_versions.rbegin(); + return nullptr; } const Version *Package::findVersion(const Version &ver) const diff --git a/src/package.hpp b/src/package.hpp @@ -51,7 +51,7 @@ public: void addVersion(const Version *ver); const VersionSet &versions() const { return m_versions; } const Version *version(size_t index) const; - const Version *lastVersion() const; + const Version *lastVersion(bool prerelease = true) const; const Version *findVersion(const Version &) const; private: diff --git a/src/reapack.cpp b/src/reapack.cpp @@ -147,7 +147,7 @@ void ReaPack::synchronizeAll() return; for(const Remote &remote : remotes) - t->synchronize(remote, m_config->autoInstall()); + t->synchronize(remote, *m_config->install()); t->runTasks(); } @@ -159,13 +159,12 @@ void ReaPack::setRemoteEnabled(const Remote &original, const bool enable) const auto apply = [=] { m_config->remotes()->add(remote); - - if(m_manager) - m_manager->refresh(); + refreshManager(); }; if(!hitchhikeTransaction()) { apply(); + refreshBrowser(); return; } @@ -196,6 +195,7 @@ void ReaPack::uninstall(const Remote &remote) if(!hitchhikeTransaction()) { apply(); + refreshBrowser(); return; } @@ -280,11 +280,8 @@ void ReaPack::import(const Remote &remote, HWND parent) remotes->add(remote); m_config->write(); - if(m_manager) - m_manager->refresh(); - - if(m_browser) - m_browser->refresh(); + refreshManager(); + refreshBrowser(); const string msg = remote.name() + " has been successfully imported into your repository list."; @@ -332,8 +329,11 @@ void ReaPack::about(const Remote &remote, HWND parent) if(ret == About::InstallResult) { enable(remote); - if(m_transaction) // transaction is created by enable() - m_transaction->synchronize(remote, true); + if(m_transaction) { // transaction is created by enable() + InstallOpts opts = *m_config->install(); + opts.autoInstall = true; + m_transaction->synchronize(remote, opts); + } runTasks(); } @@ -526,8 +526,7 @@ Transaction *ReaPack::createTransaction() m_transaction = nullptr; // refresh only once all onFinish slots were ran - if(m_browser) - m_browser->refresh(); + refreshBrowser(); }); return m_transaction; @@ -547,6 +546,18 @@ void ReaPack::runTasks() m_transaction->runTasks(); } +void ReaPack::refreshManager() +{ + if(m_manager) + m_manager->refresh(); +} + +void ReaPack::refreshBrowser() +{ + if(m_browser) + m_browser->refresh(); +} + void ReaPack::registerSelf() { // hard-coding galore! diff --git a/src/reapack.hpp b/src/reapack.hpp @@ -69,6 +69,7 @@ public: void install(const Version *); void uninstall(const Remote &); void uninstall(const Registry::Entry &); + void importRemote(); void import(const Remote &, HWND = nullptr); void manageRemotes(); @@ -76,6 +77,9 @@ public: void about(const std::string &, HWND parent); void about(const Remote &, HWND parent); void browsePackages(); + void refreshManager(); + void refreshBrowser(); + void fetchIndex(const Remote &remote, const IndexCallback &, HWND parent, bool stale = false); void fetchIndexes(const std::vector<Remote> &, diff --git a/src/transaction.cpp b/src/transaction.cpp @@ -17,6 +17,7 @@ #include "transaction.hpp" +#include "config.hpp" #include "encoding.hpp" #include "errors.hpp" #include "filesystem.hpp" @@ -64,7 +65,7 @@ Transaction::~Transaction() delete m_registry; } -void Transaction::synchronize(const Remote &remote, const bool autoInstall) +void Transaction::synchronize(const Remote &remote, const InstallOpts &opts) { // show the report dialog or "nothing to do" even if no task are ran m_receipt.setEnabled(true); @@ -82,20 +83,22 @@ void Transaction::synchronize(const Remote &remote, const bool autoInstall) } for(const Package *pkg : ri->packages()) - synchronize(pkg, autoInstall); + synchronize(pkg, opts); }); } -void Transaction::synchronize(const Package *pkg, const bool autoInstall) +void Transaction::synchronize(const Package *pkg, const InstallOpts &opts) { const auto &regEntry = m_registry->getEntry(pkg); - if(!regEntry && !autoInstall) + if(!regEntry && !opts.autoInstall) return; - const Version *latest = pkg->lastVersion(); + const bool includePre = opts.bleedingEdge || + (regEntry && !regEntry.version.isStable()); + const Version *latest = pkg->lastVersion(includePre); - if(regEntry.version == *latest) { + if(latest && regEntry.version == *latest) { if(allFilesExists(latest->files())) return; // latest version is really installed, nothing to do here! } diff --git a/src/transaction.hpp b/src/transaction.hpp @@ -32,6 +32,7 @@ class Index; class Path; class Remote; class Task; +struct InstallOpts; typedef std::shared_ptr<const Index> IndexPtr; @@ -56,7 +57,7 @@ public: void onFinish(const VoidSignal::slot_type &slot) { m_onFinish.connect(slot); } void setCleanupHandler(const CleanupHandler &cb) { m_cleanupHandler = cb; } - void synchronize(const Remote &, bool autoInstall); + void synchronize(const Remote &, const InstallOpts &); void install(const Version *); void uninstall(const Remote &); void uninstall(const Registry::Entry &); @@ -78,7 +79,7 @@ private: void fetchIndex(const Remote &, const IndexCallback &cb); void saveIndex(Download *, const std::string &remoteName); - void synchronize(const Package *, bool autoInstall); + void synchronize(const Package *, const InstallOpts &); void install(const Version *, const Registry::Entry &); void installTicket(const InstallTicket &); void addTask(Task *); diff --git a/src/version.cpp b/src/version.cpp @@ -21,26 +21,26 @@ #include "package.hpp" #include "source.hpp" -#include <algorithm> -#include <cmath> +#include <boost/lexical_cast.hpp> +#include <cctype> #include <regex> using namespace std; Version::Version() - : m_code(0), m_time(), m_package(nullptr), m_mainSource(nullptr) + : m_stable(false), m_time(), m_package(nullptr), m_mainSource(nullptr) { } Version::Version(const string &str, const Package *pkg) - : m_code(0), m_time(), m_package(pkg), m_mainSource(nullptr) + : m_time(), m_package(pkg), m_mainSource(nullptr) { parse(str); } Version::Version(const Version &o, const Package *pkg) - : m_name(o.name()), m_code(o.code()), - m_author(o.author()), m_changelog(o.changelog()), m_time(o.time()), + : m_name(o.m_name), m_segments(o.m_segments), m_stable(o.m_stable), + m_author(o.m_author), m_changelog(o.m_changelog), m_time(o.m_time), m_package(pkg), m_mainSource(nullptr) { } @@ -53,30 +53,39 @@ Version::~Version() void Version::parse(const string &str) { - static const regex pattern("(\\d+)"); - - auto begin = sregex_iterator(str.begin(), str.end(), pattern); - auto end = sregex_iterator(); - - // set the major version by default - // even if there are less than 4 numeric components in the string - const size_t size = max((size_t)4, (size_t)distance(begin, end)); + static const regex pattern("\\d+|[a-zA-Z]+"); + + const auto &begin = sregex_iterator(str.begin(), str.end(), pattern); + const sregex_iterator end; + + size_t numeric = 0, alpha = 0; + vector<Segment> segments; + + for(sregex_iterator it = begin; it != end; it++) { + const string match = it->str(0); + const char first = tolower(match[0]); + + if(first >= 'a' || first >= 'z') { + segments.push_back(match); + alpha++; + } + else { + try { + segments.push_back(boost::lexical_cast<Numeric>(match)); + numeric++; + } + catch(const boost::bad_lexical_cast &) { + throw reapack_error("version segment overflow"); + } + } + } - if(begin == end || size > 4L) + if(!numeric) throw reapack_error("invalid version name"); - size_t index = 0; - - for(sregex_iterator it = begin; it != end; it++, index++) { - const string match = it->str(1); - - if(match.size() > 4) - throw reapack_error("version component overflow"); - - m_code += stoi(match) * (Code)pow(10000, size - index - 1); - } - m_name = str; + swap(m_segments, segments); + m_stable = alpha < 1; } bool Version::tryParse(const string &str) @@ -183,32 +192,44 @@ const Source *Version::source(const size_t index) const return it->second; } -bool Version::operator<(const Version &o) const -{ - return m_code < o.code(); -} - -bool Version::operator<=(const Version &o) const -{ - return !(m_code > o.code()); -} - -bool Version::operator>(const Version &o) const -{ - return m_code > o.code(); -} - -bool Version::operator>=(const Version &o) const -{ - return !(m_code < o.code()); -} - -bool Version::operator==(const Version &o) const +auto Version::segment(const size_t index) const -> Segment { - return m_code == o.code(); -} + if(index < size()) + return m_segments[index]; + else + return 0; +} + +int Version::compare(const Version &o) const +{ + const size_t biggest = max(size(), o.size()); + + for(size_t i = 0; i < biggest; i++) { + const Segment &lseg = segment(i); + const Numeric *lnum = boost::get<Numeric>(&lseg); + const string *lstr = boost::get<string>(&lseg); + + const Segment &rseg = o.segment(i); + const Numeric *rnum = boost::get<Numeric>(&rseg); + const string *rstr = boost::get<string>(&rseg); + + if(lnum && rnum) { + if(*lnum < *rnum) + return -1; + else if(*lnum > *rnum) + return 1; + } + else if(lstr && rstr) { + if(*lstr < *rstr) + return -1; + else if(*lstr > *rstr) + return 1; + } + else if(lnum && rstr) + return 1; + else if(lstr && rnum) + return -1; + } -bool Version::operator!=(const Version &o) const -{ - return !(*this == o); + return 0; } diff --git a/src/version.hpp b/src/version.hpp @@ -18,6 +18,7 @@ #ifndef REAPACK_VERSION_HPP #define REAPACK_VERSION_HPP +#include <boost/variant.hpp> #include <cstdint> #include <ctime> #include <map> @@ -31,7 +32,6 @@ class Path; class Version { public: - typedef uint64_t Code; typedef std::vector<Source *> SourceList; typedef std::multimap<Path, const Source *> SourceMap; @@ -45,7 +45,8 @@ public: const std::string &name() const { return m_name; } std::string fullName() const; - uint64_t code() const { return m_code; } + size_t size() const { return m_segments.size(); } + bool isStable() const { return m_stable; } const Package *package() const { return m_package; } @@ -67,16 +68,23 @@ public: const std::set<Path> &files() const { return m_files; } - bool operator<(const Version &) const; - bool operator<=(const Version &) const; - bool operator>(const Version &) const; - bool operator>=(const Version &) const; - bool operator==(const Version &) const; - bool operator!=(const Version &) const; + int compare(const Version &) const; + bool operator<(const Version &o) const { return compare(o) < 0; } + bool operator<=(const Version &o) const { return compare(o) <= 0; } + bool operator>(const Version &o) const { return compare(o) > 0; } + bool operator>=(const Version &o) const { return compare(o) >= 0; } + bool operator==(const Version &o) const { return compare(o) == 0; } + bool operator!=(const Version &o) const { return compare(o) != 0; } private: + typedef uint16_t Numeric; + typedef boost::variant<Numeric, std::string> Segment; + + Segment segment(size_t i) const; + std::string m_name; - Code m_code; + std::vector<Segment> m_segments; + bool m_stable; std::string m_author; std::string m_changelog; @@ -89,7 +97,7 @@ private: std::set<Path> m_files; }; -class VersionCompare { +class VersionPtrCompare { public: bool operator()(const Version *l, const Version *r) const { @@ -97,6 +105,6 @@ public: } }; -typedef std::set<const Version *, VersionCompare> VersionSet; +typedef std::set<const Version *, VersionPtrCompare> VersionSet; #endif diff --git a/test/index_v1.cpp b/test/index_v1.cpp @@ -74,7 +74,7 @@ TEST_CASE("read version author", M) { IndexPtr ri = Index::load("author"); CHECK(ri->packages().size() == 1); - REQUIRE(ri->category(0)->package(0)->lastVersion()->author() + REQUIRE(ri->category(0)->package(0)->version(0)->author() == "Watanabe Saki"); } @@ -84,7 +84,7 @@ TEST_CASE("read version time", M) { IndexPtr ri = Index::load("time"); CHECK(ri->packages().size() == 1); - const tm &time = ri->category(0)->package(0)->lastVersion()->time(); + const tm &time = ri->category(0)->package(0)->version(0)->time(); REQUIRE(time.tm_year == 2016 - 1900); REQUIRE(time.tm_mon == 2 - 1); REQUIRE(time.tm_mday == 12); diff --git a/test/package.cpp b/test/package.cpp @@ -94,6 +94,30 @@ TEST_CASE("package versions are sorted", M) { REQUIRE(pack.lastVersion() == final); } +TEST_CASE("get latest stable version", M) { + Index ri("Remote Name"); + Category cat("Category Name", &ri); + Package pack(Package::ScriptType, "a", &cat); + + Version *alpha = new Version("2.0-alpha", &pack); + alpha->addSource(new Source({}, "google.com", alpha)); + pack.addVersion(alpha); + + SECTION("only prereleases are available") + REQUIRE(pack.lastVersion(false) == nullptr); + + SECTION("an older stable release is available") { + Version *final = new Version("1.0", &pack); + final->addSource(new Source({}, "google.com", final)); + pack.addVersion(final); + + REQUIRE(pack.lastVersion(false) == final); + } + + REQUIRE(pack.lastVersion() == alpha); + REQUIRE(pack.lastVersion(true) == alpha); +} + TEST_CASE("drop empty version", M) { Package pack(Package::ScriptType, "a"); pack.addVersion(new Version("1", &pack)); diff --git a/test/version.cpp b/test/version.cpp @@ -25,52 +25,67 @@ static Source *mksource(Source::Platform p, Version *parent) static const char *M = "[version]"; -TEST_CASE("invalid", M) { - try { - Version ver("hello"); - FAIL(); +TEST_CASE("construct null version", M) { + const Version ver; + + REQUIRE(ver.size() == 0); + REQUIRE_FALSE(ver.isStable()); + REQUIRE(ver.displayTime().empty()); + REQUIRE(ver.package() == nullptr); + REQUIRE(ver.mainSource() == nullptr); +} + +TEST_CASE("parse version", M) { + Version ver; + + SECTION("valid") { + ver.parse("1.0.1"); + REQUIRE(ver.name() == "1.0.1"); + REQUIRE(ver.size() == 3); } - catch(const reapack_error &e) { - REQUIRE(string(e.what()) == "invalid version name"); + + SECTION("prerelease set/unset") { + ver.parse("1.0beta"); + REQUIRE_FALSE(ver.isStable()); + ver.parse("1.0"); + REQUIRE(ver.isStable()); } -} -TEST_CASE("major minor patch version", M) { - Version ver("1.2.3"); - REQUIRE(ver.name() == "1.2.3"); - REQUIRE(ver.code() == UINT64_C(1000200030000)); -} + SECTION("invalid") { + try { ver.parse("hello"); FAIL(); } + catch(const reapack_error &) {} -TEST_CASE("major minor version", M) { - Version ver("1.2"); - REQUIRE(ver.name() == "1.2"); - REQUIRE(ver.code() == UINT64_C(1000200000000)); + REQUIRE(ver.name().empty()); + REQUIRE(ver.size() == 0); + } } -TEST_CASE("major version", M) { - Version ver("1"); - REQUIRE(ver.name() == "1"); - REQUIRE(ver.code() == UINT64_C(1000000000000)); -} +TEST_CASE("parse version failsafe", M) { + Version ver; -TEST_CASE("version with string suffix", M) { - Version ver("1.2pre3"); - REQUIRE(ver.name() == "1.2pre3"); - REQUIRE(ver.code() == UINT64_C(1000200030000)); -} + SECTION("valid") { + REQUIRE(ver.tryParse("1.0")); -TEST_CASE("version with 4 components", M) { - Version ver("1.2.3.4"); - REQUIRE(ver.name() == "1.2.3.4"); - REQUIRE(ver.code() == UINT64_C(1000200030004)); - REQUIRE(ver < Version("1.2.4")); + REQUIRE(ver.name() == "1.0"); + REQUIRE(ver.size() == 2); + } + + SECTION("invalid") { + REQUIRE_FALSE(ver.tryParse("hello")); + + REQUIRE(ver.name().empty()); + REQUIRE(ver.size() == 0); + } } -TEST_CASE("version with repeated digits", M) { - Version ver("1.1.1"); - REQUIRE(ver.name() == "1.1.1"); - REQUIRE(ver.code() == UINT64_C(1000100010000)); - REQUIRE(ver < Version("1.1.2")); +TEST_CASE("construct invalid version", M) { + try { + Version ver("hello"); + FAIL(); + } + catch(const reapack_error &e) { + REQUIRE(string(e.what()) == "invalid version name"); + } } TEST_CASE("decimal version", M) { @@ -79,29 +94,88 @@ TEST_CASE("decimal version", M) { REQUIRE(ver < Version("5.50")); } -TEST_CASE("4 digits version component", M) { - Version ver("0.2015.12.25"); - REQUIRE(ver.name() == "0.2015.12.25"); - REQUIRE(ver.code() == UINT64_C(201500120025)); +TEST_CASE("5 version segments", M) { + REQUIRE(Version("1.1.1.1.0") < Version("1.1.1.1.1")); + REQUIRE(Version("1.1.1.1.1") == Version("1.1.1.1.1")); + REQUIRE(Version("1.1.1.1.1") < Version("1.1.1.1.2")); + REQUIRE(Version("1.1.1.1.1") < Version("1.1.1.2.0")); } -TEST_CASE("5 digits version component", M) { +TEST_CASE("version segment overflow", M) { try { - Version ver("12345.1"); + Version ver("9999999999999999999999"); FAIL(); } catch(const reapack_error &e) { - REQUIRE(string(e.what()) == "version component overflow"); + REQUIRE(string(e.what()) == "version segment overflow"); } } -TEST_CASE("version with 5 components", M) { - try { - Version ver("1.2.3.4.5"); - FAIL(); +TEST_CASE("compare versions", M) { + SECTION("equality") { + REQUIRE(Version("1.0").compare(Version("1.0")) == 0); + + REQUIRE(Version("1.0") == Version("1.0")); + REQUIRE_FALSE(Version("1.0") == Version("1.1")); } - catch(const reapack_error &e) { - REQUIRE(string(e.what()) == "invalid version name"); + + SECTION("inequality") { + REQUIRE_FALSE(Version("1.0") != Version("1.0")); + REQUIRE(Version("1.0") != Version("1.1")); + } + + SECTION("less than") { + REQUIRE(Version("1.0").compare(Version("1.1")) == -1); + + REQUIRE(Version("1.0") < Version("1.1")); + REQUIRE_FALSE(Version("1.0") < Version("1.0")); + REQUIRE_FALSE(Version("1.1") < Version("1.0")); + } + + SECTION("less than or equal") { + REQUIRE(Version("1.0") <= Version("1.1")); + REQUIRE(Version("1.0") <= Version("1.0")); + REQUIRE_FALSE(Version("1.1") <= Version("1.0")); + } + + SECTION("greater than") { + REQUIRE(Version("1.1").compare(Version("1.0")) == 1); + + REQUIRE_FALSE(Version("1.0") > Version("1.1")); + REQUIRE_FALSE(Version("1.0") > Version("1.0")); + REQUIRE(Version("1.1") > Version("1.0")); + } + + SECTION("greater than or equal") { + REQUIRE_FALSE(Version("1.0") >= Version("1.1")); + REQUIRE(Version("1.0") >= Version("1.0")); + REQUIRE(Version("1.1") >= Version("1.0")); + } +} + +TEST_CASE("compare versions with more or less segments", M) { + REQUIRE(Version("1") == Version("1.0.0.0")); + REQUIRE(Version("1") != Version("1.0.0.1")); + + REQUIRE(Version("1.0.0.0") == Version("1")); + REQUIRE(Version("1.0.0.1") != Version("1")); +} + +TEST_CASE("prerelease versions", M) { + SECTION("detect") { + REQUIRE(Version("1.0").isStable()); + REQUIRE_FALSE(Version("1.0b").isStable()); + REQUIRE_FALSE(Version("1.0-beta").isStable()); + REQUIRE_FALSE(Version("1.0-beta1").isStable()); + } + + SECTION("compare") { + REQUIRE(Version("0.9") < Version("1.0a")); + REQUIRE(Version("1.0a.2") < Version("1.0b.1")); + REQUIRE(Version("1.0-beta1") < Version("1.0")); + + REQUIRE(Version("1.0b") < Version("1.0.1")); + REQUIRE(Version("1.0.1") > Version("1.0b")); } } @@ -236,62 +310,18 @@ TEST_CASE("version date", M) { } } -TEST_CASE("construct null version", M) { - const Version ver; - - REQUIRE(ver.code() == 0); - REQUIRE(ver.displayTime().empty()); - REQUIRE(ver.package() == nullptr); - REQUIRE(ver.mainSource() == nullptr); -} - -TEST_CASE("parse version", M) { - Version ver; - - SECTION("valid") { - ver.parse("1.0"); - REQUIRE(ver.name() == "1.0"); - REQUIRE(ver.code() == UINT64_C(1000000000000)); - } - - SECTION("invalid") { - try { ver.parse("hello"); FAIL(); } - catch(const reapack_error &) {} - - REQUIRE(ver.name().empty()); - REQUIRE(ver.code() == 0); - } -} - -TEST_CASE("parse version failsafe", M) { - Version ver; - - SECTION("valid") { - REQUIRE(ver.tryParse("1.0")); - - REQUIRE(ver.name() == "1.0"); - REQUIRE(ver.code() == UINT64_C(1000000000000)); - } - - SECTION("invalid") { - REQUIRE_FALSE(ver.tryParse("hello")); - - REQUIRE(ver.name().empty()); - REQUIRE(ver.code() == 0); - } -} - TEST_CASE("copy version constructor", M) { const Package pkg(Package::UnknownType, "Hello"); - Version original("1.1", &pkg); + Version original("1.1test", &pkg); original.setAuthor("John Doe"); original.setChangelog("Initial release"); original.setTime("2016-02-12T01:16:40Z"); const Version copy1(original); - REQUIRE(copy1.name() == "1.1"); - REQUIRE(copy1.code() == original.code()); + REQUIRE(copy1.name() == "1.1test"); + REQUIRE(copy1.size() == original.size()); + REQUIRE(copy1.isStable() == original.isStable()); REQUIRE(copy1.author() == original.author()); REQUIRE(copy1.changelog() == original.changelog()); REQUIRE(copy1.displayTime() == original.displayTime()); @@ -303,42 +333,6 @@ TEST_CASE("copy version constructor", M) { REQUIRE(copy2.package() == &pkg); } -TEST_CASE("version operators", M) { - SECTION("equality") { - REQUIRE(Version("1.0") == Version("1.0")); - REQUIRE_FALSE(Version("1.0") == Version("1.1")); - } - - SECTION("inequality") { - REQUIRE_FALSE(Version("1.0") != Version("1.0")); - REQUIRE(Version("1.0") != Version("1.1")); - } - - SECTION("less than") { - REQUIRE(Version("1.0") < Version("1.1")); - REQUIRE_FALSE(Version("1.0") < Version("1.0")); - REQUIRE_FALSE(Version("1.1") < Version("1.0")); - } - - SECTION("less than or equal") { - REQUIRE(Version("1.0") <= Version("1.1")); - REQUIRE(Version("1.0") <= Version("1.0")); - REQUIRE_FALSE(Version("1.1") <= Version("1.0")); - } - - SECTION("greater than") { - REQUIRE_FALSE(Version("1.0") > Version("1.1")); - REQUIRE_FALSE(Version("1.0") > Version("1.0")); - REQUIRE(Version("1.1") > Version("1.0")); - } - - SECTION("greater than or equal") { - REQUIRE_FALSE(Version("1.0") >= Version("1.1")); - REQUIRE(Version("1.0") >= Version("1.0")); - REQUIRE(Version("1.1") >= Version("1.0")); - } -} - #ifdef __APPLE__ TEST_CASE("drop windows sources on os x", M) { MAKE_VERSION