Compare commits

...
10 Commits
Author SHA1 Message Date
markoandGitHub a3dff8d19a Release 1.1.0 (#7)
* Add SEMVER_ENABLE_TESTING cmake option to control building of unit tests. (#6)

* Bump version
2019-03-27 10:47:58 +01:00
Marko Zivanovic 6215a7f4f9 Merge branch 'release-1.0.0' 2017-01-05 21:24:24 +01:00
Marko Zivanovic 49515b81f4 Bump version to 1.0.0 2017-01-05 21:23:27 +01:00
markoandGitHub f24208aa9f Implement methods to modify version object in various ways (#2)
* Initial implementation of non-mutable setting and resetting version components.

* Document new functionality in source code and update README.
2017-01-05 21:18:51 +01:00
Marko Živanović 0ca0088ee2 Create README.md 2015-12-05 09:38:32 +01:00
Marko Zivanovic aecbc92030 Clean-up code to satisfy GCC's pedantic mode requirements. Add fix for Clang not working with GCC 5 C++ library (https://llvm.org/bugs/show_bug.cgi?id=23529) 2015-11-25 15:22:13 +01:00
Marko Zivanovic d1759caf1c Remove unneccessary namespace usage. 2015-10-13 21:11:52 +02:00
Marko Zivanovic 398ca786c4 Comments improvements; minor header files cleanup. 2015-10-13 21:05:24 +02:00
Marko Zivanovic 522e8b484b Add copy c'tors and assignment operators; Change numeric prerelease identifier comparison to use long longs instead of ints. 2015-10-13 12:10:43 +02:00
Marko Zivanovic bb377082f2 Fix bug with generating strings for empty prerelease or build versions/ 2015-10-13 11:08:39 +02:00
15 changed files with 973 additions and 246 deletions
+15 -9
View File
@@ -1,11 +1,13 @@
cmake_minimum_required(VERSION 2.8)
project(semver)
set(VERSION_MAJOR "0")
set(VERSION_MAJOR "1")
set(VERSION_MINOR "1")
set(VERSION_PATCH "0")
set(VERSION_RELEASE "alpha")
set(VERSION_RELEASE "")
set(VERSION_BUILD "")
option(SEMVER_ENABLE_TESTING "Adds tests subdirectory and enables testing" OFF)
# Build full version string, including optional components
string(COMPARE NOTEQUAL VERSION_RELEASE "" HAVE_RELEASE)
string(COMPARE NOTEQUAL VERSION_BUILD "" HAVE_BUILD)
@@ -14,12 +16,12 @@ if(HAVE_RELEASE)
set(VERSION_FULL "${VERSION_FULL}-${VERSION_RELEASE}")
endif()
if(HAVE_BUILD)
set(VERSION_FULL "${VERSION_FULL}-${VERSION_BUILD}")
set(VERSION_FULL "${VERSION_FULL}+${VERSION_BUILD}")
endif()
if(UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Wextra -pedantic")
add_definitions(-std=c++11)
add_definitions(-std=c++14)
endif()
if(WIN32)
@@ -32,9 +34,13 @@ if(MSVC)
endif()
add_subdirectory(src)
add_subdirectory(test)
enable_testing()
add_test(NAME semver200_parser_tests COMMAND semver200_parser_tests)
add_test(NAME semver200_comparator_tests COMMAND semver200_comparator_tests)
add_test(NAME semver200_version_tests COMMAND semver200_version_tests)
if (SEMVER_ENABLE_TESTING)
add_subdirectory(test)
enable_testing()
add_test(NAME semver200_parser_tests COMMAND semver200_parser_tests)
add_test(NAME semver200_comparator_tests COMMAND semver200_comparator_tests)
add_test(NAME semver200_version_tests COMMAND semver200_version_tests)
add_test(NAME semver200_modifier_tests COMMAND semver200_modifier_tests)
endif()
+96
View File
@@ -0,0 +1,96 @@
# About
This project is MIT-licensed, C++14 implementation of [semantic versioning](http://semver.org) parser and comparator with support for modifying parsed version strings. Semantic versioning 2.0.0 specification is supported out-of-the-box and the code should be flexible-enough to support future revisions or other similar versioning schemes.
# Usage
Parsing and comparing two version strings:
```c++
#include "semver200.h"
void main(int, char**) {
version::Semver200_version v1("1.0.0");
version::Semver200_version v2("2.0.0");
if (v2 > v1) {
std::cout << v2 << " is indeed greater than " << v1 << std::endl;
} else {
std::cout << "This thing is broken, what a waste of time!" << std::endl;
}
}
```
Accessing individual version components:
```c++
#include "semver200.h"
void main(int, char**) {
version::Semver200_version v("1.2.3-alpha.1+build.no.123");
std::cout << "Major: " << v.major() << std::endl;
std::cout << "Minor: " << v.minor() << std::endl;
std::cout << "Patch: " << v.patch() << std::endl;
std::cout << "Pre-release: " << v.prerelease() << std::endl;
std::cout << "Build: " << v.build() << std::endl;
}
```
should generate following output:
```
Major: 1
Minor: 2
Patch: 3
Pre-release: alpha.1
Build: build.no.123
```
Parsed version object supports a few modification methods. All modification methods are non-destructive i.e. they return new objects with modified properties and original objects are never changed. You can:
- set major, minor, patch, pre-release or build version to desired value while keeping other fields unchanged;
- reset major, minor, patch, pre-release or build version to desired value by resetting lower-priority fields to zero/empty values (Priority is major > minor > patch > pre-release > build);
- increase/decrease major, minor or patch version by desired increment (can be negative); this is reset-type operation which will zero/empty lower priority fields.
A few examples of version modifications:
```c++
#include "semver200.h"
void main(int, char**) {
version::Semver200_version v("1.2.3-alpha.1+build.no.123");
std::cout << "Next major version: " << v.inc_major() << std::endl;
std::cout << "Next but one major: " << v.inc_major(2) << std::endl;
std::cout << "Next minor version: " << v.inc_minor() << std::endl;
std::cout << "Previous minor version: " << v.inc_minor(-1) << std::endl;
std::cout << "Next patch version: " << v.inc_patch() << std::endl;
std::cout << "Change pre-release: " << v.set_prerelease("beta.3") << std::endl;
std::cout << "Change build: " << v.set_build("170105") << std::endl;
std::cout << "Set major to 3, minor to 1: " << v.set_major(3).set_minor(1) << std::endl;
std::cout << "Reset major to 3, minor to 1: " << v.reset_major(3).reset_minor(1) << std::endl;
}
```
should generate following output:
```
Next major version: 2.0.0
Next but one major: 3.0.0
Next minor version: 1.3.0
Previous minor version: 1.1.0
Next patch version: 1.2.4
Change pre-release: 1.2.3-beta.3+build.no.123
Change build: 1.2.3-alpha.1+170105
Set major to 3, minor to 1: 3.1.3-alpha.1+build.no.123
Reset major to 3, minor to 1: 3.1.0
```
# Build
The code is written in C++14, so, fairly recent compiler is required to build it. Following compilers were tested:
- Microsoft Visual Studio 2015
- GCC 5.1.1
- Clang 3.7.0
Library itself does not have any external dependencies. Unit tests that verify the library work as expected, on the other hand, depend on the [Boost.Test](http://www.boost.org/doc/libs/1_59_0/libs/test/doc/html/index.html) library. Unit tests are disabled by default. To build tests run cmake with -DSEMVER_ENABLE_TESTING=ON option.
The code comes with CMake project files. In order to build it you should:
- create, if it doesnt already exist, directory `build` in the project directory;
- invoke `cmake ..` in build directory;
- once CMake is done, use your toolset (Visual Studio, nmake, make, …) to build the library;
- remember to link in the library you have built and to include `./include` directory to your build.
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore
+42 -2
View File
@@ -38,11 +38,51 @@ namespace version {
int compare(const Version_data&, const Version_data&) const;
};
/// Implementation of various version modification methods.
/**
All methods are non-destructive, i.e. they return a new object with modified properties;
original object is never modified.
*/
struct Semver200_modifier {
/// Set major version to specified value leaving all other components unchanged..
Version_data set_major(const Version_data&, const int) const;
/// Set minor version to specified value leaving all other components unchanged.
Version_data set_minor(const Version_data&, const int) const;
/// Set patch version to specified value leaving all other components unchanged.
Version_data set_patch(const Version_data&, const int) const;
/// Set pre-release version to specified value leaving all other components unchanged.
Version_data set_prerelease(const Version_data&, const Prerelease_identifiers&) const;
/// Set build version to specified value leaving all other components unchanged.
Version_data set_build(const Version_data&, const Build_identifiers&) const;
/// Set major version to specified value resetting all lower-priority components to zero/empty values.
Version_data reset_major(const Version_data&, const int) const;
/// Set minor version to specified value resetting all lower-priority components to zero/empty values.
Version_data reset_minor(const Version_data&, const int) const;
/// Set patch version to specified value resetting all lower-priority components to zero/empty values.
Version_data reset_patch(const Version_data&, const int) const;
/// Set pre-release version to specified value resetting all lower-priority components to zero/empty values.
Version_data reset_prerelease(const Version_data&, const Prerelease_identifiers&) const;
/// Set build version to specified value.
Version_data reset_build(const Version_data&, const Build_identifiers&) const;
};
/// Concrete version class that binds all semver 2.0.0 functionality together.
class Semver200_version : public Basic_version<Semver200_parser, Semver200_comparator> {
class Semver200_version : public Basic_version<Semver200_parser, Semver200_comparator, Semver200_modifier> {
public:
Semver200_version()
: Basic_version{ Semver200_parser(), Semver200_comparator(), Semver200_modifier() } {}
Semver200_version(const std::string& v)
: Basic_version{ v, Semver200_parser(), Semver200_comparator() } {}
: Basic_version{ v, Semver200_parser(), Semver200_comparator(), Semver200_modifier() } {}
};
}
+123 -46
View File
@@ -30,17 +30,22 @@ SOFTWARE.
namespace version {
/// Parse_error is thrown on all parsing and validation errors.
/// Any error in parsing or validation of version string will result in Parse_error exception being thrown.
class Parse_error : public std::runtime_error {
using std::runtime_error::runtime_error;
};
/// Any error in manipulating version data will result in Modification_error exception being thrown.
class Modification_error : public std::runtime_error {
using std::runtime_error::runtime_error;
};
/// Type of prerelease identifier: alphanumeric or numeric.
/**
Type of identifier affects comparison: alphanumeric identifiers are compared as ASCII strings, while
numeric identifiers are compared as numbers.
*/
enum class Identifier_type {
enum class Id_type {
alnum, ///< Identifier is alphanumerical
num ///< Identifier is numeric
};
@@ -51,22 +56,26 @@ namespace version {
These identifiers can be either numerical or alphanumerical.
This structure describes one such identifier.
*/
using Prerelease_identifier = std::pair<std::string, Identifier_type>;
using Prerelease_identifier = std::pair<std::string, Id_type>;
/// Container for all prerelease identifiers for a given version string.
using Prerelease_identifiers = std::vector<Prerelease_identifier>;
/// Build identifier is just a string and has no meaning for the purpose of version precedence.
/// Build identifier is arbitrary string with no special meaning with regards to version precedence.
using Build_identifier = std::string;
/// Container for all build identifiers for a given version string.
/// Container for all build identifiers of a given version string.
using Build_identifiers = std::vector<Build_identifier>;
/// Description of version broken into parts, as per semantic versioning specification.
struct Version_data {
int major; ///< Major version, change only on incompatible API modifications
int minor; ///< Minor version, change on compatible API modifications
int patch; ///< Patch version, change only on bugfixes
Version_data(const int M, const int m, const int p, const Prerelease_identifiers& pr, const Build_identifiers& b)
: major{ M }, minor{ m }, patch{ p }, prerelease_ids{ pr }, build_ids{ b } {}
int major; ///< Major version, change only on incompatible API modifications.
int minor; ///< Minor version, change on backwards-compatible API modifications.
int patch; ///< Patch version, change only on bugfixes.
/// Optional series of prerelease identifiers.
Prerelease_identifiers prerelease_ids;
@@ -75,66 +84,134 @@ namespace version {
Build_identifiers build_ids;
};
/// Forward declaration required for operators' template declarations.
template<typename P, typename C>
// Forward declaration required for operators' template declarations.
template<typename Parser, typename Comparator, typename Modifier>
class Basic_version;
/// Compare if left version object is less than the right.
template<typename P, typename C>
bool operator<(const Basic_version<P, C>&, const Basic_version<P, C>&);
/// Test if left-hand version operand is of lower precedence than the right-hand version.
template<typename Parser, typename Comparator, typename Modifier>
bool operator<(const Basic_version<Parser, Comparator, Modifier>&,
const Basic_version<Parser, Comparator, Modifier>&);
/// Compare if two version objects are equal.
template<typename P, typename C>
bool operator==(const Basic_version<P, C>& l, const Basic_version<P, C>& r);
/// Test if left-hand version operand if of equal precedence as the right-hand version.
template<typename Parser, typename Comparator, typename Modifier>
bool operator==(const Basic_version<Parser, Comparator, Modifier>&,
const Basic_version<Parser, Comparator, Modifier>&);
/// Output version object to stream using standard semver format (X.Y.Z-PR+B).
template<typename P, typename C>
std::ostream& operator<<(std::ostream& os, const Basic_version<P, C>& v);
template<typename Parser, typename Comparator, typename Modifier>
std::ostream& operator<<(std::ostream&,
const Basic_version<Parser, Comparator, Modifier>&);
/// Compare if two version objects are different.
template<typename P, typename C>
bool operator!=(const version::Basic_version<P, C>& l, const version::Basic_version<P, C>& r);
/// Test if left-hand version and right-hand version are of different precedence.
template<typename Parser, typename Comparator, typename Modifier>
bool operator!=(const Basic_version<Parser, Comparator, Modifier>&,
const Basic_version<Parser, Comparator, Modifier>&);
/// Compare if left version object is greater than the right.
template<typename P, typename C>
bool operator>(const version::Basic_version<P, C>& l, const version::Basic_version<P, C>& r);
/// Test if left-hand version operand is of higher precedence than the right-hand version.
template<typename Parser, typename Comparator, typename Modifier>
bool operator>(const Basic_version<Parser, Comparator, Modifier>&,
const Basic_version<Parser, Comparator, Modifier>&);
/// Compare if left version object is greater than or equal the right.
template<typename P, typename C>
bool operator>=(const version::Basic_version<P, C>& l, const version::Basic_version<P, C>& r);
/// Test if left-hand version operand is of higher or equal precedence as the right-hand version.
template<typename Parser, typename Comparator, typename Modifier>
bool operator>=(const Basic_version<Parser, Comparator, Modifier>&,
const Basic_version<Parser, Comparator, Modifier>&);
/// Compare if left version object is less than or equal the right.
template<typename P, typename C>
bool operator<=(const version::Basic_version<P, C>& l, const version::Basic_version<P, C>& r);
/// Test if left-hand version operand is of lower or equal precedence as the right-hand version.
template<typename Parser, typename Comparator, typename Modifier>
bool operator<=(const Basic_version<Parser, Comparator, Modifier>&,
const Basic_version<Parser, Comparator, Modifier>&);
/// Generic version description and comparison class.
/// Base class for various version parsing, precedence ordering and data manipulation schemes.
/**
Basic_version class describes general version object without prescribing parsing,
validation and comparison rules. These rules are implemented by supplied Parser and
Comparator objects.
validation, comparison and modification rules. These rules are implemented by supplied Parser, Comparator
and Modifier objects.
*/
template<typename Parser, typename Comparator>
template<typename Parser, typename Comparator, typename Modifier>
class Basic_version {
public:
/// Construct Basic_version object using P to parse version string and C for comparison.
Basic_version(const std::string& v, Parser p, Comparator c)
: parser_(p), comparator_(c), ver_(parser_.parse(v)) {};
/// Construct Basic_version object using Parser object to parse default ("0.0.0") version string, Comparator for comparison and Modifier for modification.
Basic_version(Parser, Comparator, Modifier);
const int major() const;
const int minor() const;
const int patch() const;
const std::string prerelease() const;
const std::string build() const;
/// Construct Basic_version object using Parser to parse supplied version string, Comparator for comparison and Modifier for modification.
Basic_version(const std::string&, Parser, Comparator, Modifier);
friend bool operator< <>(const Basic_version& l, const Basic_version& r);
friend bool operator== <>(const Basic_version& l, const Basic_version& r);
friend std::ostream& operator<< <>(std::ostream& os, const Basic_version& v);
/// Construct Basic_version object using supplied Version_data, Parser, Comparator and Modifier objects.
Basic_version(const Version_data&, Parser, Comparator, Modifier);
/// Construct Basic_version by copying data from another one.
Basic_version(const Basic_version&);
/// Copy version data from another Basic_version to this one.
Basic_version& operator=(const Basic_version&);
int major() const; ///< Get major version.
int minor() const; ///< Get minor version.
int patch() const; ///< Get patch version.
const std::string prerelease() const; ///< Get prerelease version string.
const std::string build() const; ///< Get build version string.
/// Return a copy of version with major component set to specified value.
Basic_version set_major(const int) const;
/// Return a copy of version with the minor component set to specified value.
Basic_version set_minor(const int) const;
/// Return a copy of version with the patch component set to specified value.
Basic_version set_patch(const int) const;
/// Return a copy of version with the pre-release component set to specified value.
Basic_version set_prerelease(const std::string&) const;
/// Return a copy of version with the build component set to specified value.
Basic_version set_build(const std::string&) const;
/// Return a copy of version with the major component reset to specified value.
/**
Exact implementation of reset is delegated to Modifier object.
*/
Basic_version reset_major(const int) const;
/// Return a copy of version with the minor component reset to specified value.
/**
Exact implementation of reset is delegated to Modifier object.
*/
Basic_version reset_minor(const int) const;
/// Return a copy of version with the patch component reset to specified value.
/**
Exact implementation of reset is delegated to Modifier object.
*/
Basic_version reset_patch(const int) const;
/// Return a copy of version with the pre-release component reset to specified value.
/**
Exact implementation of reset is delegated to Modifier object.
*/
Basic_version reset_prerelease(const std::string&) const;
/// Return a copy of version with the build component reset to specified value.
/**
Exact implementation of reset is delegated to Modifier object.
*/
Basic_version reset_build(const std::string&) const;
Basic_version inc_major(const int = 1) const;
Basic_version inc_minor(const int = 1) const;
Basic_version inc_patch(const int = 1) const;
friend bool operator< <>(const Basic_version&, const Basic_version&);
friend bool operator== <>(const Basic_version&, const Basic_version&);
friend std::ostream& operator<< <>(std::ostream&s, const Basic_version&);
private:
Parser parser_;
Comparator comparator_;
const Version_data ver_;
Modifier modifier_;
Version_data ver_;
};
}
+128 -33
View File
@@ -24,6 +24,7 @@ SOFTWARE.
#pragma once
#include <sstream>
#include "version.h"
namespace version {
@@ -33,57 +34,151 @@ namespace version {
/// Utility function to splice all vector elements to output stream, using designated separator
/// between elements and function object for getting values from vector elements.
template<typename T, typename F>
std::ostream& splice(std::ostream& os, const std::vector<T>& v, const std::string& sep, F read) {
for (auto it = v.cbegin(); it < v.cend() - 1; ++it) {
os << read(*it) << sep;
std::ostream& splice(std::ostream& os, const std::vector<T>& vec, const std::string& sep, F read) {
if (!vec.empty()) {
for (auto it = vec.cbegin(); it < vec.cend() - 1; ++it) {
os << read(*it) << sep;
}
os << read(*vec.crbegin());
}
os << read(*v.crbegin());
return os;
}
}
template<typename P, typename C>
const int Basic_version<P, C>::major() const {
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier>::Basic_version(Parser p, Comparator c, Modifier m)
: parser_(p), comparator_(c), modifier_(m), ver_(parser_.parse("0.0.0")) {}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier>::Basic_version(const std::string& v, Parser p, Comparator c, Modifier m)
: parser_(p), comparator_(c), modifier_(m), ver_(parser_.parse(v)) {}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier>::Basic_version(const Version_data& v, Parser p, Comparator c, Modifier m)
: parser_(p), comparator_(c), modifier_(m), ver_(v) {}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier>::Basic_version(const Basic_version<Parser, Comparator, Modifier>&) = default;
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier>& Basic_version<Parser, Comparator, Modifier>::operator=(
const Basic_version<Parser, Comparator, Modifier>&) = default;
template<typename Parser, typename Comparator, typename Modifier>
int Basic_version<Parser, Comparator, Modifier>::major() const {
return ver_.major;
}
template<typename P, typename C>
const int Basic_version<P, C>::minor() const {
template<typename Parser, typename Comparator, typename Modifier>
int Basic_version<Parser, Comparator, Modifier>::minor() const {
return ver_.minor;
}
template<typename P, typename C>
const int Basic_version<P, C>::patch() const {
template<typename Parser, typename Comparator, typename Modifier>
int Basic_version<Parser, Comparator, Modifier>::patch() const {
return ver_.patch;
}
template<typename P, typename C>
const std::string Basic_version<P, C>::prerelease() const {
template<typename Parser, typename Comparator, typename Modifier>
const std::string Basic_version<Parser, Comparator, Modifier>::prerelease() const {
std::stringstream ss;
splice(ss, ver_.prerelease_ids, ".", [](const auto& id) { return id.first;});
return ss.str();
}
template<typename P, typename C>
const std::string Basic_version<P, C>::build() const {
template<typename Parser, typename Comparator, typename Modifier>
const std::string Basic_version<Parser, Comparator, Modifier>::build() const {
std::stringstream ss;
splice(ss, ver_.build_ids, ".", [](const auto& id) { return id;});
return ss.str();
}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier> Basic_version<Parser, Comparator, Modifier>::set_major(const int m) const {
return Basic_version<Parser, Comparator, Modifier>(modifier_.set_major(ver_, m), parser_, comparator_, modifier_);
}
template<typename P, typename C>
bool operator<(const Basic_version<P, C>& l, const Basic_version<P, C>& r) {
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier> Basic_version<Parser, Comparator, Modifier>::set_minor(const int m) const {
return Basic_version<Parser, Comparator, Modifier>(modifier_.set_minor(ver_, m), parser_, comparator_, modifier_);
}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier> Basic_version<Parser, Comparator, Modifier>::set_patch(const int p) const {
return Basic_version<Parser, Comparator, Modifier>(modifier_.set_patch(ver_, p), parser_, comparator_, modifier_);
}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier> Basic_version<Parser, Comparator, Modifier>::set_prerelease(const std::string& pr) const {
auto vd = parser_.parse("0.0.0-" + pr);
return Basic_version<Parser, Comparator, Modifier>(modifier_.set_prerelease(ver_, vd.prerelease_ids), parser_, comparator_, modifier_);
}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier> Basic_version<Parser, Comparator, Modifier>::set_build(const std::string& b) const {
auto vd = parser_.parse("0.0.0+" + b);
return Basic_version<Parser, Comparator, Modifier>(modifier_.set_build(ver_, vd.build_ids), parser_, comparator_, modifier_);
}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier> Basic_version<Parser, Comparator, Modifier>::reset_major(const int m) const {
return Basic_version<Parser, Comparator, Modifier>(modifier_.reset_major(ver_, m), parser_, comparator_, modifier_);
}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier> Basic_version<Parser, Comparator, Modifier>::reset_minor(const int m) const {
return Basic_version<Parser, Comparator, Modifier>(modifier_.reset_minor(ver_, m), parser_, comparator_, modifier_);
}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier> Basic_version<Parser, Comparator, Modifier>::reset_patch(const int p) const {
return Basic_version<Parser, Comparator, Modifier>(modifier_.reset_patch(ver_, p), parser_, comparator_, modifier_);
}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier> Basic_version<Parser, Comparator, Modifier>::reset_prerelease(const std::string& pr) const {
std::string ver = "0.0.0-" + pr;
auto vd = parser_.parse(ver);
return Basic_version<Parser, Comparator, Modifier>(modifier_.reset_prerelease(ver_, vd.prerelease_ids), parser_, comparator_, modifier_);
}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier> Basic_version<Parser, Comparator, Modifier>::reset_build(const std::string& b) const {
std::string ver = "0.0.0+" + b;
auto vd = parser_.parse(ver);
return Basic_version<Parser, Comparator, Modifier>(modifier_.reset_build(ver_, vd.build_ids), parser_, comparator_, modifier_);
}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier> Basic_version<Parser, Comparator, Modifier>::inc_major(const int i) const {
return reset_major(ver_.major + i);
}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier> Basic_version<Parser, Comparator, Modifier>::inc_minor(const int i) const {
return reset_minor(ver_.minor + i);
}
template<typename Parser, typename Comparator, typename Modifier>
Basic_version<Parser, Comparator, Modifier> Basic_version<Parser, Comparator, Modifier>::inc_patch(const int i) const {
return reset_patch(ver_.patch + i);
}
template<typename Parser, typename Comparator, typename Modifier>
bool operator<(const Basic_version<Parser, Comparator, Modifier>& l,
const Basic_version<Parser, Comparator, Modifier>& r) {
return l.comparator_.compare(l.ver_, r.ver_) == -1;
}
template<typename P, typename C>
bool operator==(const version::Basic_version<P, C>& l, const version::Basic_version<P, C>& r) {
template<typename Parser, typename Comparator, typename Modifier>
bool operator==(const Basic_version<Parser, Comparator, Modifier>& l,
const Basic_version<Parser, Comparator, Modifier>& r) {
return l.comparator_.compare(l.ver_, r.ver_) == 0;
}
template<typename P, typename C>
std::ostream& operator<<(std::ostream& os, const version::Basic_version<P, C>& v) {
template<typename Parser, typename Comparator, typename Modifier>
std::ostream& operator<<(std::ostream& os,
const Basic_version<Parser, Comparator, Modifier>& v) {
os << v.ver_.major << "." << v.ver_.minor << "." << v.ver_.patch;
std::string prl = v.prerelease();
if (!prl.empty()) {
@@ -96,27 +191,27 @@ namespace version {
return os;
}
/// Compare if two version objects are different.
template<typename P, typename C>
inline bool operator!=(const version::Basic_version<P, C>& l, const version::Basic_version<P, C>& r) {
template<typename Parser, typename Comparator, typename Modifier>
inline bool operator!=(const Basic_version<Parser, Comparator, Modifier>& l,
const Basic_version<Parser, Comparator, Modifier>& r) {
return !(l == r);
}
/// Compare if left version object is greater than the right.
template<typename P, typename C>
inline bool operator>(const version::Basic_version<P, C>& l, const version::Basic_version<P, C>& r) {
template<typename Parser, typename Comparator, typename Modifier>
inline bool operator>(const Basic_version<Parser, Comparator, Modifier>& l,
const Basic_version<Parser, Comparator, Modifier>& r) {
return r < l;
}
/// Compare if left version object is greater than or equal the right.
template<typename P, typename C>
inline bool operator>=(const version::Basic_version<P, C>& l, const version::Basic_version<P, C>& r) {
template<typename Parser, typename Comparator, typename Modifier>
inline bool operator>=(const Basic_version<Parser, Comparator, Modifier>& l,
const Basic_version<Parser, Comparator, Modifier>& r) {
return !(l < r);
}
/// Compare if left version object is less than or equal the right.
template<typename P, typename C>
inline bool operator<=(const version::Basic_version<P, C>& l, const version::Basic_version<P, C>& r) {
template<typename Parser, typename Comparator, typename Modifier>
inline bool operator<=(const Basic_version<Parser, Comparator, Modifier>& l,
const Basic_version<Parser, Comparator, Modifier>& r) {
return !(l > r);
}
}
}
+1 -1
View File
@@ -3,5 +3,5 @@ include_directories(
)
add_library(semver
Semver200_comparator.cpp Semver200_parser.cpp
Semver200_comparator.cpp Semver200_parser.cpp Semver200_modifier.cpp
)
+51 -53
View File
@@ -29,69 +29,67 @@ SOFTWARE.
using namespace std;
namespace {
namespace version {
using namespace version;
namespace {
// Compare normal version identifiers.
int compare_normal(const Version_data& l, const Version_data& r) {
if (l.major > r.major) return 1;
if (l.major < r.major) return -1;
if (l.minor > r.minor) return 1;
if (l.minor < r.minor) return -1;
if (l.patch > r.patch) return 1;
if (l.patch < r.patch) return -1;
return 0;
}
// Compare normal version identifiers.
int compare_normal(const Version_data& l, const Version_data& r) {
if (l.major > r.major) return 1;
if (l.major < r.major) return -1;
if (l.minor > r.minor) return 1;
if (l.minor < r.minor) return -1;
if (l.patch > r.patch) return 1;
if (l.patch < r.patch) return -1;
return 0;
}
// Compare alphanumeric prerelease identifiers.
inline int cmp_alnum_prerel_ids(const string& l, const string& r) {
auto cmp = l.compare(r);
if (cmp == 0) {
return cmp;
} else {
return cmp > 0 ? 1 : -1;
// Compare alphanumeric prerelease identifiers.
inline int cmp_alnum_prerel_ids(const string& l, const string& r) {
auto cmp = l.compare(r);
if (cmp == 0) {
return cmp;
} else {
return cmp > 0 ? 1 : -1;
}
}
// Compare numeric prerelease identifiers.
inline int cmp_num_prerel_ids(const string& l, const string& r) {
long long li = stoll(l);
long long ri = stoll(r);
if (li == ri) return 0;
return li > ri ? 1 : -1;
}
using Prerel_type_pair = pair<Id_type, Id_type>;
using Prerel_id_comparator = function<int(const string&, const string&)>;
const map<Prerel_type_pair, Prerel_id_comparator> comparators = {
{ { Id_type::alnum, Id_type::alnum }, cmp_alnum_prerel_ids },
{ { Id_type::alnum, Id_type::num }, [](const string&, const string&) {return 1;} },
{ { Id_type::num, Id_type::alnum }, [](const string&, const string&) {return -1;} },
{ { Id_type::num, Id_type::num }, cmp_num_prerel_ids }
};
// Compare prerelease identifiers based on their types.
inline int compare_prerel_identifiers(const Prerelease_identifier& l, const Prerelease_identifier& r) {
auto cmp = comparators.at({ l.second, r.second });
return cmp(l.first, r.first);
}
inline int cmp_rel_prerel(const Prerelease_identifiers& l, const Prerelease_identifiers& r) {
if (l.empty() && !r.empty()) return 1;
if (r.empty() && !l.empty()) return -1;
return 0;
}
}
// Compare numeric prerelease identifiers.
inline int cmp_num_prerel_ids(const string& l, const string& r) {
int li = stoi(l);
int ri = stoi(r);
if (li == ri) return 0;
return li > ri ? 1 : -1;
}
using Prerel_types = pair<Identifier_type, Identifier_type>;
using Prerel_id_comparator = function<int(const string&, const string&)>;
const map<Prerel_types, Prerel_id_comparator> comparators = {
{ { Identifier_type::alnum, Identifier_type::alnum }, cmp_alnum_prerel_ids },
{ { Identifier_type::alnum, Identifier_type::num }, [](const string&, const string&) {return 1;} },
{ { Identifier_type::num, Identifier_type::alnum }, [](const string&, const string&) {return -1;} },
{ { Identifier_type::num, Identifier_type::num }, cmp_num_prerel_ids }
};
// Compare prerelease identifiers based on their types.
int compare_prerel_identifiers(const Prerelease_identifier& l, const Prerelease_identifier& r) {
auto cmp = comparators.at({ l.second, r.second });
return cmp(l.first, r.first);
}
inline int cmp_rel_prerel(const Prerelease_identifiers& l, const Prerelease_identifiers& r) {
if (l.empty() && !r.empty()) return 1;
if (r.empty() && !l.empty()) return -1;
return 0;
}
}
namespace version {
int Semver200_comparator::compare(const Version_data& l, const Version_data& r) const {
// Compare normal version components.
int cmp = compare_normal(l, r);
if (cmp != 0) return cmp;
// Compare if one version is release and the other prerelease; release is always higher than prerelease.
// Compare if one version is release and the other prerelease - release is always higher.
cmp = cmp_rel_prerel(l.prerelease_ids, r.prerelease_ids);
if (cmp != 0) return cmp;
@@ -109,4 +107,4 @@ namespace version {
return l.prerelease_ids.size() > r.prerelease_ids.size() ? 1 : -1;
}
}
}
+75
View File
@@ -0,0 +1,75 @@
/*
The MIT License (MIT)
Copyright (c) 2015 Marko Zivanovic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <climits>
#include "semver200.h"
namespace version {
Version_data Semver200_modifier::set_major(const Version_data& s, const int m) const {
if (m < 0) throw Modification_error("major version cannot be less than 0");
return Version_data{ m, s.minor, s.patch, s.prerelease_ids, s.build_ids };
}
Version_data Semver200_modifier::set_minor(const Version_data& s, const int m) const {
if (m < 0) throw Modification_error("minor version cannot be less than 0");
return Version_data{ s.major, m, s.patch, s.prerelease_ids, s.build_ids };
}
Version_data Semver200_modifier::set_patch(const Version_data& s, const int p) const {
if (p < 0) throw Modification_error("patch version cannot be less than 0");
return Version_data{ s.major, s.minor, p, s.prerelease_ids, s.build_ids };
}
Version_data Semver200_modifier::set_prerelease(const Version_data& s, const Prerelease_identifiers& pr) const {
return Version_data{ s.major, s.minor, s.patch, pr, s.build_ids };
}
Version_data Semver200_modifier::set_build(const Version_data& s, const Build_identifiers& b) const {
return Version_data{ s.major, s.minor, s.patch, s.prerelease_ids, b };
}
Version_data Semver200_modifier::reset_major(const Version_data& s, const int m) const {
if (m < 0) throw Modification_error("major version cannot be less than 0");
return Version_data{ m, 0, 0, Prerelease_identifiers{}, Build_identifiers{} };
}
Version_data Semver200_modifier::reset_minor(const Version_data& s, const int m) const {
if (m < 0) throw Modification_error("minor version cannot be less than 0");
return Version_data{ s.major, m, 0, Prerelease_identifiers{}, Build_identifiers{} };
}
Version_data Semver200_modifier::reset_patch(const Version_data& s, const int p) const {
if (p < 0) throw Modification_error("patch version cannot be less than 0");
return Version_data{ s.major, s.minor, p, Prerelease_identifiers{}, Build_identifiers{} };
}
Version_data Semver200_modifier::reset_prerelease(const Version_data& s, const Prerelease_identifiers& pr) const {
return Version_data{ s.major, s.minor, s.patch, pr, Build_identifiers{} };
}
Version_data Semver200_modifier::reset_build(const Version_data& s, const Build_identifiers& b) const {
return Version_data{ s.major, s.minor, s.patch, s.prerelease_ids, b };
}
}
+100 -96
View File
@@ -26,106 +26,110 @@ SOFTWARE.
#include <map>
#include "semver200.h"
#ifdef _MSC_VER
// disable symbol name too long warning
#pragma warning(disable:4503)
#endif
using namespace std;
namespace {
enum class Parser_state {
major, minor, patch, prerelease, build
};
using Validator = function<void(const string&, const char)>;
using State_transition_hook = function<void(string&)>;
/// State transition is described by a character that triggers it, a state to transition to and
/// optional hook to be invoked on transition.
using Transition = tuple<const char, Parser_state, State_transition_hook>;
using Transitions = vector<Transition>;
using State = tuple<Transitions, string&, Validator>;
using State_machine = map<Parser_state, State>;
// Ranges of characters allowed in prerelease and build identifiers.
const vector<pair<char, char>> allowed_prerel_id_chars = {
{ '0', '9' },{ 'A','Z' },{ 'a','z' },{ '-','-' }
};
inline Transition mkx(const char c, Parser_state p, State_transition_hook pth) {
return make_tuple(c, p, pth);
}
/// Advance parser state machine by a single step.
/**
Perform single step of parser state machine: if character matches one from transition tables -
trigger transition to next state; otherwise, validate if current token is in legal state
(throw Parse_error if not) and then add character to current token; State transition includes
preparing various vars for next state and invoking state transition hook (if specified) which is
where whole tokens are validated.
*/
inline void process_char(const char c, Parser_state& cstate, Parser_state& pstate,
const Transitions& transitions, string& target, Validator validate) {
for (const auto& transition : transitions) {
if (c == get<0>(transition)) {
if (get<2>(transition)) get<2>(transition)(target);
pstate = cstate;
cstate = get<1>(transition);
return;
}
}
validate(target, c);
target.push_back(c);
}
/// Validate normal (major, minor, patch) version components.
inline void normal_version_validator(const string& tgt, const char c) {
if (c < '0' || c > '9') throw version::Parse_error("invalid character encountered: " + string(1, c));
if (tgt.compare(0, 1, "0") == 0) throw version::Parse_error("leading 0 not allowed");
}
/// Validate that prerelease and build version identifiers are comprised of allowed chars only.
inline void prerelease_version_validator(const string&, const char c) {
bool res = false;
for (const auto& r : allowed_prerel_id_chars) {
res |= (c >= r.first && c <= r.second);
}
if (!res)
throw version::Parse_error("invalid character encountered: " + string(1, c));
}
inline bool is_identifier_numeric(const string& id) {
return id.find_first_not_of("0123456789") == string::npos;
}
inline bool check_for_leading_0(const string& str) {
return str.length() > 1 && str[0] == '0';
}
/// Validate every individual prerelease identifier, determine it's type and add it to collection.
void prerelease_hook_impl(string& id, version::Prerelease_identifiers& prerelease) {
using namespace version;
if (id.empty()) throw Parse_error("version identifier cannot be empty");
Identifier_type t = Identifier_type::alnum;
if (is_identifier_numeric(id)) {
t = Identifier_type::num;
if (check_for_leading_0(id)) {
throw Parse_error("numeric identifiers cannot have leading 0");
}
}
prerelease.push_back(Prerelease_identifier(id, t));
id.clear();
}
/// Validate every individual build identifier and add it to collection.
void build_hook_impl(string& id, Parser_state& pstate, version::Build_identifiers& build,
std::string& prerelease_id, version::Prerelease_identifiers& prerelease) {
// process last token left from parsing prerelease data
if (pstate == Parser_state::prerelease) prerelease_hook_impl(prerelease_id, prerelease);
if (id.empty()) throw version::Parse_error("version identifier cannot be empty");
build.push_back(id);
id.clear();
}
}
namespace version {
namespace {
enum class Parser_state {
major, minor, patch, prerelease, build
};
using Validator = function<void(const string&, const char)>;
using State_transition_hook = function<void(string&)>;
/// State transition is described by a character that triggers it, a state to transition to and
/// optional hook to be invoked on transition.
using Transition = tuple<const char, Parser_state, State_transition_hook>;
using Transitions = vector<Transition>;
using State = tuple<Transitions, string&, Validator>;
using State_machine = map<Parser_state, State>;
// Ranges of characters allowed in prerelease and build identifiers.
const vector<pair<char, char>> allowed_prerel_id_chars = {
{ '0', '9' },{ 'A','Z' },{ 'a','z' },{ '-','-' }
};
inline Transition mkx(const char c, Parser_state p, State_transition_hook pth) {
return make_tuple(c, p, pth);
}
/// Advance parser state machine by a single step.
/**
Perform single step of parser state machine: if character matches one from transition tables -
trigger transition to next state; otherwise, validate if current token is in legal state
(throw Parse_error if not) and then add character to current token; State transition includes
preparing various vars for next state and invoking state transition hook (if specified) which is
where whole tokens are validated.
*/
inline void process_char(const char c, Parser_state& cstate, Parser_state& pstate,
const Transitions& transitions, string& target, Validator validate) {
for (const auto& transition : transitions) {
if (c == get<0>(transition)) {
if (get<2>(transition)) get<2>(transition)(target);
pstate = cstate;
cstate = get<1>(transition);
return;
}
}
validate(target, c);
target.push_back(c);
}
/// Validate normal (major, minor, patch) version components.
inline void normal_version_validator(const string& tgt, const char c) {
if (c < '0' || c > '9') throw Parse_error("invalid character encountered: " + string(1, c));
if (tgt.compare(0, 1, "0") == 0) throw Parse_error("leading 0 not allowed");
}
/// Validate that prerelease and build version identifiers are comprised of allowed chars only.
inline void prerelease_version_validator(const string&, const char c) {
bool res = false;
for (const auto& r : allowed_prerel_id_chars) {
res |= (c >= r.first && c <= r.second);
}
if (!res)
throw Parse_error("invalid character encountered: " + string(1, c));
}
inline bool is_identifier_numeric(const string& id) {
return id.find_first_not_of("0123456789") == string::npos;
}
inline bool check_for_leading_0(const string& str) {
return str.length() > 1 && str[0] == '0';
}
/// Validate every individual prerelease identifier, determine it's type and add it to collection.
void prerelease_hook_impl(string& id, Prerelease_identifiers& prerelease) {
if (id.empty()) throw Parse_error("version identifier cannot be empty");
Id_type t = Id_type::alnum;
if (is_identifier_numeric(id)) {
t = Id_type::num;
if (check_for_leading_0(id)) {
throw Parse_error("numeric identifiers cannot have leading 0");
}
}
prerelease.push_back(Prerelease_identifier(id, t));
id.clear();
}
/// Validate every individual build identifier and add it to collection.
void build_hook_impl(string& id, Parser_state& pstate, Build_identifiers& build,
std::string& prerelease_id, Prerelease_identifiers& prerelease) {
// process last token left from parsing prerelease data
if (pstate == Parser_state::prerelease) prerelease_hook_impl(prerelease_id, prerelease);
if (id.empty()) throw Parse_error("version identifier cannot be empty");
build.push_back(id);
id.clear();
}
}
/// Parse semver 2.0.0-compatible string to Version_data structure.
/**
Version text parser is implemented as a state machine. In each step one successive character from version
+9 -3
View File
@@ -9,20 +9,26 @@ include_directories(
${Boost_INCLUDE_DIRS}
)
add_executable(semver200_parser_tests semver200_parser_tests.cpp)
add_executable(semver200_parser_tests semver200_parser_tests.cpp clang_fixes.cpp)
target_link_libraries(semver200_parser_tests
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
semver
)
add_executable(semver200_comparator_tests semver200_comparator_tests.cpp)
add_executable(semver200_comparator_tests semver200_comparator_tests.cpp clang_fixes.cpp)
target_link_libraries(semver200_comparator_tests
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
semver
)
add_executable(semver200_version_tests semver200_version_tests.cpp)
add_executable(semver200_version_tests semver200_version_tests.cpp clang_fixes.cpp)
target_link_libraries(semver200_version_tests
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
semver
)
add_executable(semver200_modifier_tests semver200_modifier_tests.cpp clang_fixes.cpp)
target_link_libraries(semver200_modifier_tests
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
semver
)
+14
View File
@@ -0,0 +1,14 @@
#ifdef __clang__
#include <string>
#include <boost/test/unit_test.hpp>
namespace boost { namespace unit_test { namespace ut_detail {
std::string normalize_test_case_name(const_string name) {
return ( name[0] == '&' ? std::string(name.begin()+1, name.size()-1) : std::string(name.begin(), name.size() ));
}
}}}
#endif
+1 -1
View File
@@ -102,4 +102,4 @@ BOOST_AUTO_TEST_CASE(compare_build) {
EQ("1.0.0", "1.0.0+build.1.2.3");
EQ("1.0.0+ZZZ", "1.0.0+build.1.2.3");
EQ("1.0.0+100", "1.0.0+200");
}
}
+314
View File
@@ -0,0 +1,314 @@
/*
The MIT License (MIT)
Copyright (c) 2017 Marko Zivanovic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#define BOOST_TEST_MODULE semver200_modifier_tests
#include <climits>
#include <boost/test/unit_test.hpp>
#include "semver200.h"
using namespace version;
Semver200_comparator c;
Semver200_parser p;
Semver200_modifier m;
#define CHECK_SRC { \
BOOST_CHECK(v.major() == 1); \
BOOST_CHECK(v.minor() == 2); \
BOOST_CHECK(v.patch() == 3); \
BOOST_CHECK(v.prerelease() == "pre.rel.0"); \
BOOST_CHECK(v.build() == "build.no.321"); \
}
BOOST_AUTO_TEST_CASE(set_major) {
Semver200_version v("1.2.3-pre.rel.0+build.no.321");
auto v2 = v.set_major(2);
// Check if setter works as expected
BOOST_CHECK(v2.major() == 2);
BOOST_CHECK(v2.minor() == 2);
BOOST_CHECK(v2.patch() == 3);
BOOST_CHECK(v2.prerelease() == "pre.rel.0");
BOOST_CHECK(v2.build() == "build.no.321");
// Check invalid values
BOOST_CHECK_THROW(v.set_major(-1), version::Modification_error);
// Check source version is unaffected
CHECK_SRC
}
BOOST_AUTO_TEST_CASE(set_minor) {
Semver200_version v("1.2.3-pre.rel.0+build.no.321");
auto v2 = v.set_minor(3);
// Check if setter works as expected
BOOST_CHECK(v2.major() == 1);
BOOST_CHECK(v2.minor() == 3);
BOOST_CHECK(v2.patch() == 3);
BOOST_CHECK(v2.prerelease() == "pre.rel.0");
BOOST_CHECK(v2.build() == "build.no.321");
// Check invalid values
BOOST_CHECK_THROW(v.set_minor(-1), version::Modification_error);
// Check source version is unaffected
CHECK_SRC
}
BOOST_AUTO_TEST_CASE(set_patch) {
Semver200_version v("1.2.3-pre.rel.0+build.no.321");
auto v2 = v.set_patch(4);
// Check if setter works as expected
BOOST_CHECK(v2.major() == 1);
BOOST_CHECK(v2.minor() == 2);
BOOST_CHECK(v2.patch() == 4);
BOOST_CHECK(v2.prerelease() == "pre.rel.0");
BOOST_CHECK(v2.build() == "build.no.321");
// Check invalid values
BOOST_CHECK_THROW(v.set_patch(-1), version::Modification_error);
// Check source version is unaffected
CHECK_SRC
}
BOOST_AUTO_TEST_CASE(set_prerelease) {
Semver200_version v("1.2.3-pre.rel.0+build.no.321");
auto v2 = v.set_prerelease("alpha.1");
// Check if setter works as expected
BOOST_CHECK(v2.major() == 1);
BOOST_CHECK(v2.minor() == 2);
BOOST_CHECK(v2.patch() == 3);
BOOST_CHECK(v2.prerelease() == "alpha.1");
BOOST_CHECK(v2.build() == "build.no.321");
// Check source version is unaffected
CHECK_SRC
}
BOOST_AUTO_TEST_CASE(set_build) {
Semver200_version v("1.2.3-pre.rel.0+build.no.321");
auto v2 = v.set_build("b123");
// Check if setter works as expected
BOOST_CHECK(v2.major() == 1);
BOOST_CHECK(v2.minor() == 2);
BOOST_CHECK(v2.patch() == 3);
BOOST_CHECK(v2.prerelease() == "pre.rel.0");
BOOST_CHECK(v2.build() == "b123");
// Check source version is unaffected
CHECK_SRC
}
BOOST_AUTO_TEST_CASE(reset_major) {
Semver200_version v("1.2.3-pre.rel.0+build.no.321");
auto v2 = v.reset_major(2);
// Check if setter works as expected
BOOST_CHECK(v2.major() == 2);
BOOST_CHECK(v2.minor() == 0);
BOOST_CHECK(v2.patch() == 0);
BOOST_CHECK(v2.prerelease() == "");
BOOST_CHECK(v2.build() == "");
// Check invalid values
BOOST_CHECK_THROW(v.reset_major(-1), version::Modification_error);
// Check source version is unaffected
CHECK_SRC
}
BOOST_AUTO_TEST_CASE(reset_minor) {
Semver200_version v("1.2.3-pre.rel.0+build.no.321");
auto v2 = v.reset_minor(3);
// Check if setter works as expected
BOOST_CHECK(v2.major() == 1);
BOOST_CHECK(v2.minor() == 3);
BOOST_CHECK(v2.patch() == 0);
BOOST_CHECK(v2.prerelease() == "");
BOOST_CHECK(v2.build() == "");
// Check invalid values
BOOST_CHECK_THROW(v.reset_minor(-1), version::Modification_error);
// Check source version is unaffected
CHECK_SRC
}
BOOST_AUTO_TEST_CASE(reset_patch) {
Semver200_version v("1.2.3-pre.rel.0+build.no.321");
auto v2 = v.reset_patch(4);
// Check if setter works as expected
BOOST_CHECK(v2.major() == 1);
BOOST_CHECK(v2.minor() == 2);
BOOST_CHECK(v2.patch() == 4);
BOOST_CHECK(v2.prerelease() == "");
BOOST_CHECK(v2.build() == "");
// Check invalid values
BOOST_CHECK_THROW(v.reset_patch(-1), version::Modification_error);
// Check source version is unaffected
CHECK_SRC
}
BOOST_AUTO_TEST_CASE(reset_prerelease) {
Semver200_version v("1.2.3-pre.rel.0+build.no.321");
auto v2 = v.reset_prerelease("alpha.1");
// Check if setter works as expected
BOOST_CHECK(v2.major() == 1);
BOOST_CHECK(v2.minor() == 2);
BOOST_CHECK(v2.patch() == 3);
BOOST_CHECK(v2.prerelease() == "alpha.1");
BOOST_CHECK(v2.build() == "");
// Check source version is unaffected
CHECK_SRC
}
BOOST_AUTO_TEST_CASE(reset_build) {
Semver200_version v("1.2.3-pre.rel.0+build.no.321");
auto v2 = v.reset_build("b123");
// Check if setter works as expected
BOOST_CHECK(v2.major() == 1);
BOOST_CHECK(v2.minor() == 2);
BOOST_CHECK(v2.patch() == 3);
BOOST_CHECK(v2.prerelease() == "pre.rel.0");
BOOST_CHECK(v2.build() == "b123");
// Check source version is unaffected
CHECK_SRC
}
BOOST_AUTO_TEST_CASE(inc_major) {
Semver200_version v("1.2.3-pre.rel.0+build.no.321");
// Check default increment
auto v2 = v.inc_major();
BOOST_CHECK(v2.major() == 2);
BOOST_CHECK(v2.minor() == 0);
BOOST_CHECK(v2.patch() == 0);
BOOST_CHECK(v2.prerelease() == "");
BOOST_CHECK(v2.build() == "");
// Check non-default increment
auto v2 = v.inc_major(3);
BOOST_CHECK(v2.major() == 4);
BOOST_CHECK(v2.minor() == 0);
BOOST_CHECK(v2.patch() == 0);
BOOST_CHECK(v2.prerelease() == "");
BOOST_CHECK(v2.build() == "");
// Check negative increment
v2 = v.inc_major(-1);
BOOST_CHECK(v2.major() == 0);
BOOST_CHECK(v2.minor() == 0);
BOOST_CHECK(v2.patch() == 0);
BOOST_CHECK(v2.prerelease() == "");
BOOST_CHECK(v2.build() == "");
// Check too negative increment
BOOST_CHECK_THROW(v.inc_major(-2), version::Modification_error);
// Check source version is unaffected
CHECK_SRC
}
BOOST_AUTO_TEST_CASE(inc_minor) {
Semver200_version v("1.2.3-pre.rel.0+build.no.321");
// Check default increment
auto v2 = v.inc_minor();
BOOST_CHECK(v2.major() == 1);
BOOST_CHECK(v2.minor() == 3);
BOOST_CHECK(v2.patch() == 0);
BOOST_CHECK(v2.prerelease() == "");
BOOST_CHECK(v2.build() == "");
// Check non-default increment
auto v2 = v.inc_minor(3);
BOOST_CHECK(v2.major() == 1);
BOOST_CHECK(v2.minor() == 5);
BOOST_CHECK(v2.patch() == 0);
BOOST_CHECK(v2.prerelease() == "");
BOOST_CHECK(v2.build() == "");
// Check negative increment
v2 = v.inc_minor(-1);
BOOST_CHECK(v2.major() == 1);
BOOST_CHECK(v2.minor() == 1);
BOOST_CHECK(v2.patch() == 0);
BOOST_CHECK(v2.prerelease() == "");
BOOST_CHECK(v2.build() == "");
// Check too negative increment
BOOST_CHECK_THROW(v.inc_minor(-3), version::Modification_error);
// Check source version is unaffected
CHECK_SRC
}
BOOST_AUTO_TEST_CASE(inc_patch) {
Semver200_version v("1.2.3-pre.rel.0+build.no.321");
// Check default increment
auto v2 = v.inc_patch();
BOOST_CHECK(v2.major() == 1);
BOOST_CHECK(v2.minor() == 2);
BOOST_CHECK(v2.patch() == 4);
BOOST_CHECK(v2.prerelease() == "");
BOOST_CHECK(v2.build() == "");
// Check default increment
auto v2 = v.inc_patch(3);
BOOST_CHECK(v2.major() == 1);
BOOST_CHECK(v2.minor() == 2);
BOOST_CHECK(v2.patch() == 6);
BOOST_CHECK(v2.prerelease() == "");
BOOST_CHECK(v2.build() == "");
// Check negative increment
v2 = v.inc_patch(-1);
BOOST_CHECK(v2.major() == 1);
BOOST_CHECK(v2.minor() == 2);
BOOST_CHECK(v2.patch() == 2);
BOOST_CHECK(v2.prerelease() == "");
BOOST_CHECK(v2.build() == "");
// Check too negative increment
BOOST_CHECK_THROW(v.inc_patch(-4), version::Modification_error);
// Check source version is unaffected
CHECK_SRC
}
+2 -2
View File
@@ -32,8 +32,8 @@ version::Semver200_parser p;
const version::Prerelease_identifiers no_rel_ids;
const version::Build_identifiers no_build_ids;
#define N Identifier_type::num
#define A Identifier_type::alnum
#define N Id_type::num
#define A Id_type::alnum
#define BOOST_PATCH BOOST_VERSION % 100
#define BOOST_MINOR BOOST_VERSION / 100 % 1000