Skip to content

Commit a8b2103

Browse files
authored
fix: add tests (#5)
1 parent 1496c52 commit a8b2103

File tree

5 files changed

+119
-40
lines changed

5 files changed

+119
-40
lines changed

.github/workflows/ubuntu.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Ubuntu 22.04 CI
1+
name: Ubuntu 24.04 CI
22

33
on:
44
pull_request:
@@ -22,17 +22,17 @@ concurrency:
2222

2323
jobs:
2424
ubuntu-build:
25-
runs-on: ubuntu-22.04
25+
runs-on: ubuntu-24.04
2626
strategy:
2727
matrix:
2828
shared: [ON, OFF]
29-
cxx: [g++-12, clang++-14]
29+
cxx: [g++-14]
3030
steps:
3131
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
3232
- name: Setup Ninja
3333
run: sudo apt-get install ninja-build
3434
- name: Prepare
35-
run: cmake -D ADA_BENCHMARKS=ON -DBUILD_SHARED_LIBS=${{matrix.shared}} -G Ninja -B build
35+
run: cmake -DBUILD_SHARED_LIBS=${{matrix.shared}} -G Ninja -B build
3636
env:
3737
CXX: ${{matrix.cxx}}
3838
- name: Build

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.10)
22
project(version_weaver)
33

4-
set(CMAKE_CXX_STANDARD 17)
4+
set(CMAKE_CXX_STANDARD 23)
55
set(CMAKE_CXX_STANDARD_REQUIRED True)
66
if (NOT CMAKE_BUILD_TYPE)
77
message(STATUS "No build type selected, default to Release")

include/version_weaver.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#include <optional>
44
#include <string>
55
#include <string_view>
6-
#include <variant>
6+
#include <expected>
77

88
namespace version_weaver {
99

@@ -58,7 +58,25 @@ enum ParseError {
5858
INVALID_INPUT,
5959
};
6060

61-
std::variant<Version, ParseError> parse(std::string_view version);
61+
std::expected<Version, ParseError> parse(std::string_view version);
6262
} // namespace version_weaver
6363

64+
inline bool operator!=(const version_weaver::Version& first,
65+
const version_weaver::Version& second) {
66+
if (first.major != second.major) return first.major != second.major;
67+
if (first.minor != second.minor) return first.minor != second.minor;
68+
return first.patch != second.patch;
69+
}
70+
inline bool operator==(const version_weaver::Version& first,
71+
const version_weaver::Version& second) {
72+
if (first.major != second.major) return first.major == second.major;
73+
if (first.minor != second.minor) return first.minor == second.minor;
74+
return first.patch == second.patch;
75+
}
76+
/*
77+
inline auto operator<=>(const version_weaver::Version& first, const
78+
version_weaver::Version& second) { if (first.major != second.major) return
79+
first.major <=> second.major; if (first.minor != second.minor) return
80+
first.minor <=> second.minor; return first.patch <=> second.patch;
81+
}*/
6482
#endif // VERSION_WEAVER_H

src/version_weaver.cpp

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#include "version_weaver.h"
2+
#include <algorithm>
3+
#include <cctype>
24
namespace version_weaver {
3-
bool validate(std::string_view version) {
4-
return std::holds_alternative<Version>(parse(version));
5-
}
5+
bool validate(std::string_view version) { return bool(parse(version)); }
66

77
bool gt(std::string_view version1, std::string_view version2) { return true; }
88
bool lt(std::string_view version1, std::string_view version2) { return true; }
@@ -13,66 +13,81 @@ std::string coerce(std::string_view version) { return ""; }
1313
std::string minimum(std::string_view range) { return ""; }
1414
std::string clean(std::string_view range) { return ""; }
1515

16-
std::variant<Version, ParseError> parse(std::string_view input) {
16+
inline std::string_view trim_whitespace(std::string_view input) noexcept {
17+
while (!input.empty() && std::isspace(input.front())) {
18+
input.remove_prefix(1);
19+
}
20+
while (!input.empty() && std::isspace(input.back())) {
21+
input.remove_suffix(1);
22+
}
23+
return input;
24+
}
25+
26+
std::expected<Version, ParseError> parse(std::string_view input) {
1727
if (input.size() > MAX_VERSION_LENGTH) {
18-
return ParseError::VERSION_LARGER_THAN_MAX_LENGTH;
28+
return std::unexpected(ParseError::VERSION_LARGER_THAN_MAX_LENGTH);
1929
}
2030

21-
std::string_view input_copy = input;
22-
// TODO: Trim leading and trailing whitespace
31+
std::string_view input_copy = trim_whitespace(input);
2332

2433
auto dot_iterator = input_copy.find('.');
2534
if (dot_iterator == std::string_view::npos) {
2635
// Only major exists. No minor or patch.
27-
return ParseError::INVALID_INPUT;
36+
return std::unexpected(ParseError::INVALID_INPUT);
2837
}
2938
Version version;
3039
auto major = input_copy.substr(0, dot_iterator);
3140

3241
if (major.empty() || major.front() == '0') {
3342
// Version components can not have leading zeroes.
34-
return ParseError::INVALID_INPUT;
43+
return std::unexpected(ParseError::INVALID_INPUT);
3544
}
3645
version.major = major;
3746
input_copy = input_copy.substr(dot_iterator + 1);
3847
dot_iterator = input_copy.find('.');
3948
if (dot_iterator == std::string_view::npos) {
4049
// Only major and minor exists. No patch.
41-
return ParseError::INVALID_INPUT;
50+
return std::unexpected(ParseError::INVALID_INPUT);
4251
}
4352

4453
auto minor = input_copy.substr(0, dot_iterator);
45-
if (minor.empty() || minor.front() == '0') {
54+
if (minor.empty() || (minor.front() == '0' && minor.size() > 1)) {
4655
// Version components can not have leading zeroes.
47-
return ParseError::INVALID_INPUT;
56+
return std::unexpected(ParseError::INVALID_INPUT);
4857
}
4958
version.minor = minor;
5059
input_copy = input_copy.substr(dot_iterator + 1);
51-
dot_iterator = input_copy.find('.');
52-
if (dot_iterator == std::string_view::npos) {
53-
// Only major, minor and patch exists.
54-
return ParseError::INVALID_INPUT;
55-
}
56-
57-
auto patch = input_copy.substr(0, dot_iterator);
58-
if (patch.empty() || patch.front() == '0') {
59-
// Version components can not have leading zeroes.
60-
return ParseError::INVALID_INPUT;
60+
dot_iterator = input_copy.find_first_of("-+");
61+
auto patch = (dot_iterator == std::string_view::npos)
62+
? input_copy
63+
: input_copy.substr(0, dot_iterator);
64+
if (patch.empty() || (patch.front() == '0' && patch.size() > 1)) {
65+
return std::unexpected(ParseError::INVALID_INPUT);
6166
}
6267
version.patch = patch;
68+
if (dot_iterator == std::string_view::npos) {
69+
return version;
70+
}
71+
bool is_pre_release = input_copy[dot_iterator] == '-';
6372
input_copy = input_copy.substr(dot_iterator + 1);
64-
65-
auto pre_release_iterator = input_copy.find('-');
66-
if (pre_release_iterator != std::string_view::npos) {
67-
version.pre_release = input_copy.substr(0, pre_release_iterator);
68-
input_copy = input_copy.substr(pre_release_iterator + 1);
69-
if (input_copy.find('.') != std::string_view::npos) {
70-
// Build metadata exists.
71-
auto build_iterator = input_copy.find('.');
72-
version.build = input_copy.substr(0, build_iterator);
73+
if (is_pre_release) {
74+
dot_iterator = input_copy.find('+');
75+
auto prerelease = (dot_iterator == std::string_view::npos)
76+
? input_copy
77+
: input_copy.substr(0, dot_iterator);
78+
if (prerelease.empty()) {
79+
return std::unexpected(ParseError::INVALID_INPUT);
80+
}
81+
version.pre_release = prerelease;
82+
if (dot_iterator == std::string_view::npos) {
83+
return version;
7384
}
85+
input_copy = input_copy.substr(dot_iterator + 1);
7486
}
75-
87+
if (input_copy.empty()) {
88+
return std::unexpected(ParseError::INVALID_INPUT);
89+
}
90+
version.build = input_copy;
7691
return version;
7792
}
7893
} // namespace version_weaver

tests/basictests.cpp

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,49 @@
11
#include "version_weaver.h"
22
#include <cstdlib>
3-
int main() { return EXIT_SUCCESS; }
3+
#include <vector>
4+
#include <format>
5+
6+
std::vector<std::pair<std::string, std::expected<version_weaver::Version,
7+
version_weaver::ParseError>>>
8+
test_values = {
9+
{"1.0.0", version_weaver::Version{"1", "0", "0"}},
10+
{"1.0.0-alpha", version_weaver::Version{"1", "0", "0", "alpha"}},
11+
{"1.0.0-alpha.1", version_weaver::Version{"1", "0", "0", "alpha.1"}},
12+
{"1.0.0-0.3.7", version_weaver::Version{"1", "0", "0", "0.3.7"}},
13+
{"1.0.0-x.7.z.92", version_weaver::Version{"1", "0", "0", "x.7.z.92"}},
14+
{"1.0.0-x-y-z.--", version_weaver::Version{"1", "0", "0", "x-y-z.--"}},
15+
{"1.0.0-alpha+001",
16+
version_weaver::Version{"1", "0", "0", "alpha", "001"}},
17+
{"1.0.0+20130313144700",
18+
version_weaver::Version{"1", "0", "0", "", "20130313144700"}},
19+
{"1.0.0-beta+exp.sha.5114f85",
20+
version_weaver::Version{"1", "0", "0", "beta", "exp.sha.5114f85"}},
21+
{"1.0.0+21AF26D3----117B344092BD",
22+
version_weaver::Version{"1", "0", "0", "",
23+
"21AF26D3----117B344092BD"}},
24+
};
25+
26+
bool test_parse() {
27+
for (const auto& [input, expected] : test_values) {
28+
auto result = version_weaver::parse(input);
29+
if (bool(result) != bool(expected)) {
30+
std::printf("Expected %d, got %d\n", bool(expected), bool(result));
31+
return false;
32+
}
33+
if (bool(result)) {
34+
if (result.value() != expected.value()) {
35+
std::printf("Expected \n");
36+
return false;
37+
}
38+
}
39+
}
40+
return true;
41+
}
42+
int main() {
43+
if (!test_parse()) {
44+
std::printf("Test failed\n");
45+
return EXIT_FAILURE;
46+
}
47+
std::printf("Tests succeeded!\n");
48+
return EXIT_SUCCESS;
49+
}

0 commit comments

Comments
 (0)