Skip to content

Commit 3f8429c

Browse files
committed
Add string_literal unit tests
Fix erroneous behavior in string_literal
1 parent 6c7dd04 commit 3f8429c

2 files changed

Lines changed: 232 additions & 31 deletions

File tree

src/openvic-simulation/core/string/StringLiteral.hpp

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,19 @@ namespace OpenVic {
4646

4747
value_type _data[N];
4848

49-
[[nodiscard]] constexpr string_literal() noexcept = default;
49+
[[nodiscard]] constexpr string_literal() noexcept {
50+
_data[size()] = '\0';
51+
}
5052

5153
[[nodiscard]] constexpr string_literal(const value_type (&literal)[N]) noexcept {
52-
for (auto i = 0u; i != N; i++) {
54+
for (size_type i = 0u; i != N; i++) {
5355
_data[i] = literal[i];
5456
}
5557
}
5658

57-
[[nodiscard]] constexpr string_literal(value_type c) noexcept : _data {} {
59+
[[nodiscard]] constexpr string_literal(value_type c) noexcept {
5860
_data[0] = c;
61+
_data[size()] = '\0';
5962
}
6063

6164
[[nodiscard]] constexpr const_iterator begin() const noexcept {
@@ -94,7 +97,7 @@ namespace OpenVic {
9497
}
9598

9699
[[nodiscard]] constexpr const_reference operator[](size_type pos) const noexcept {
97-
return as_string_view()[pos];
100+
return _data[pos];
98101
}
99102

100103
[[nodiscard]] constexpr bool empty() const {
@@ -136,63 +139,71 @@ namespace OpenVic {
136139
return ends_with(other.as_string_view());
137140
}
138141

139-
[[nodiscard]] constexpr auto clear() const noexcept {
140-
return string_literal<0, value_type, traits_type> {};
142+
[[nodiscard]] constexpr string_literal<1, value_type, traits_type> clear() const noexcept {
143+
return string_literal<1, value_type, traits_type> {};
141144
}
142145

143-
[[nodiscard]] constexpr auto push_back(value_type c) const noexcept {
144-
return *this + string_literal { c };
146+
[[nodiscard]] constexpr string_literal<size() + 2, value_type, traits_type> push_back(value_type c) const noexcept {
147+
return *this + string_literal<2, value_type, traits_type> { c };
145148
}
146149

147150
[[nodiscard]] constexpr string_literal<size(), value_type, traits_type> pop_back() const noexcept {
148151
string_literal<size(), value_type, traits_type> result {};
149-
for (size_type i = 0u; i != size(); i++) {
152+
for (size_type i = 0u; i != size() - 1; i++) {
150153
result._data[i] = _data[i];
151154
}
155+
result._data[size() - 1] = '\0';
152156
return result;
153157
}
154158

155159
template<size_type count>
156160
[[nodiscard]] constexpr string_literal<N + count, value_type, traits_type> append(value_type ch) const noexcept {
157-
string_literal<N + count, value_type, traits_type> result {};
158-
for (auto i = 0u; i != N; i++) {
161+
string_literal<N + count, value_type, traits_type> result;
162+
for (size_type i = 0u; i != size(); i++) {
159163
result._data[i] = _data[i];
160164
}
161165

162-
for (auto i = N; i != N + count; i++) {
166+
for (size_type i = size(); i != size() + count; i++) {
163167
result._data[i] = ch;
164168
}
169+
result._data[size() + count] = '\0';
165170

166171
return result;
167172
}
168173

169174
template<size_type N2>
170-
[[nodiscard]] constexpr auto append(string_literal<N2, value_type, traits_type> str) const noexcept {
175+
[[nodiscard]] constexpr string_literal<size() + N2, value_type, traits_type> append( //
176+
string_literal<N2, value_type, traits_type> str
177+
) const noexcept {
171178
return *this + str;
172179
}
173180

174181
template<size_type N2>
175-
[[nodiscard]] constexpr auto append(const value_type (&literal)[N2]) const noexcept {
182+
[[nodiscard]] constexpr string_literal<size() + N2, value_type, traits_type> append( //
183+
const value_type (&literal)[N2]
184+
) const noexcept {
176185
return *this + literal;
177186
}
178187

179188
template<size_type pos = 0, size_type count = npos>
180-
[[nodiscard]] constexpr auto substr() const noexcept {
181-
static_assert(pos <= N, "pos must be less than or equal to N");
182-
constexpr size_type result_size = std::min(count - pos, N - pos);
189+
[[nodiscard]] constexpr string_literal<std::min(count, size() - pos) + 1, value_type, traits_type> substr() //
190+
const noexcept {
191+
static_assert(pos <= size(), "pos must be less than or equal to size");
192+
constexpr size_type result_size = std::min(count, size() - pos) + 1;
183193

184-
string_literal<result_size, value_type, traits_type> result {};
185-
for (size_type i = 0u, i2 = pos; i != result_size; i++, i2++) {
194+
string_literal<result_size, value_type, traits_type> result;
195+
for (size_type i = 0u, i2 = pos; i != result.size(); i++, i2++) {
186196
result._data[i] = _data[i2];
187197
}
198+
result._data[result.size()] = '\0';
188199
return result;
189200
}
190201

191-
[[nodiscard]] constexpr auto substr() const noexcept {
192-
return substr<>();
202+
[[nodiscard]] constexpr string_literal const& substr() const noexcept {
203+
return *this;
193204
}
194205

195-
[[nodiscard]] constexpr std::basic_string_view<CharT, Traits> substr( //
206+
[[nodiscard]] constexpr std::basic_string_view<value_type, traits_type> substr( //
196207
size_type pos, size_type count = npos
197208
) const noexcept {
198209
return as_string_view().substr(pos, count);
@@ -207,13 +218,13 @@ namespace OpenVic {
207218
return as_string_view().find(literal, pos, N2 - 1);
208219
}
209220

210-
[[nodiscard]] constexpr size_type rfind(std::string_view str, size_type pos = 0) const noexcept {
221+
[[nodiscard]] constexpr size_type rfind(std::string_view str, size_type pos = npos) const noexcept {
211222
return as_string_view().rfind(str, pos);
212223
}
213224

214225
template<size_type N2>
215-
[[nodiscard]] constexpr size_type rfind(const value_type (&literal)[N2], size_type pos = 0) const noexcept {
216-
return as_string_view().find(literal, pos, N2 - 1);
226+
[[nodiscard]] constexpr size_type rfind(const value_type (&literal)[N2], size_type pos = npos) const noexcept {
227+
return as_string_view().rfind(literal, pos, N2 - 1);
217228
}
218229

219230
[[nodiscard]] constexpr int compare(std::string_view str) const noexcept {
@@ -246,9 +257,11 @@ namespace OpenVic {
246257
}
247258

248259
template<size_type N2>
249-
[[nodiscard]] constexpr auto operator+(string_literal<N2, value_type, traits_type> const& other) const noexcept {
250-
string_literal<size() + N2, value_type, traits_type> result {};
251-
for (size_type i = 0u; i != N; i++) {
260+
[[nodiscard]] constexpr string_literal<size() + N2, value_type, traits_type> operator+( //
261+
string_literal<N2, value_type, traits_type> const& other
262+
) const noexcept {
263+
string_literal<size() + N2, value_type, traits_type> result;
264+
for (size_type i = 0u; i != size(); i++) {
252265
result._data[i] = _data[i];
253266
}
254267

@@ -259,12 +272,14 @@ namespace OpenVic {
259272
}
260273

261274
template<size_type N2>
262-
[[nodiscard]] constexpr auto operator+(const value_type (&rhs)[N2]) const noexcept {
275+
[[nodiscard]] constexpr string_literal<size() + N2, value_type, traits_type> operator+( //
276+
const value_type (&rhs)[N2]
277+
) const noexcept {
263278
return *this + _to_string(rhs);
264279
}
265280

266281
template<size_type N2>
267-
[[nodiscard]] friend constexpr auto operator+( //
282+
[[nodiscard]] friend constexpr string_literal<size() + N2, value_type, traits_type> operator+( //
268283
const value_type (&lhs)[N2], string_literal<N, value_type, traits_type> rhs
269284
) noexcept {
270285
return _to_string(lhs) + rhs;
@@ -273,6 +288,11 @@ namespace OpenVic {
273288
template<std::size_t N, typename CharT = char, class Traits = std::char_traits<CharT>>
274289
string_literal(const CharT (&)[N]) -> string_literal<N, CharT, Traits>;
275290

291+
// Size of 2 to include null terminator
292+
template<typename CharT = char, class Traits = std::char_traits<CharT>>
293+
string_literal(CharT) -> string_literal<2, CharT, Traits>;
294+
295+
// Size of 1 to include null terminator
276296
template<typename CharT = char, class Traits = std::char_traits<CharT>>
277-
string_literal(CharT) -> string_literal<1, CharT, Traits>;
297+
string_literal() -> string_literal<1, CharT, Traits>;
278298
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
#include "openvic-simulation/core/string/StringLiteral.hpp"
2+
3+
#include <cstddef>
4+
#include <iterator>
5+
#include <string_view>
6+
7+
#include <range/v3/algorithm/equal.hpp>
8+
#include <range/v3/view/drop.hpp>
9+
10+
#include "Helper.hpp" // IWYU pragma: keep
11+
#include <snitch/snitch_append.hpp>
12+
#include <snitch/snitch_macros_check.hpp>
13+
#include <snitch/snitch_macros_constexpr.hpp>
14+
#include <snitch/snitch_macros_misc.hpp>
15+
#include <snitch/snitch_macros_test_case.hpp>
16+
#include <snitch/snitch_string.hpp>
17+
18+
using namespace OpenVic;
19+
using namespace std::string_view_literals;
20+
21+
namespace snitch {
22+
template<std::size_t N, typename CharT, class Traits>
23+
[[nodiscard]] inline static constexpr bool append( //
24+
snitch::small_string_span ss, OpenVic::string_literal<N, CharT, Traits> const& s
25+
) noexcept {
26+
return append(ss, s.as_string_view());
27+
}
28+
}
29+
30+
TEST_CASE("string_literal Constructor methods", "[string_literal][string_literal-constructor]") {
31+
static constexpr string_literal empty {};
32+
CONSTEXPR_CHECK(empty.empty());
33+
34+
static constexpr string_literal char_value = 'c';
35+
CONSTEXPR_CHECK(char_value.size() == 1);
36+
CONSTEXPR_CHECK(char_value[0] == 'c');
37+
38+
static constexpr string_literal value = "value";
39+
CONSTEXPR_CHECK(value.size() == 5);
40+
CONSTEXPR_CHECK(value[0] == 'v');
41+
CONSTEXPR_CHECK(value[1] == 'a');
42+
CONSTEXPR_CHECK(value[2] == 'l');
43+
CONSTEXPR_CHECK(value[3] == 'u');
44+
CONSTEXPR_CHECK(value[4] == 'e');
45+
}
46+
47+
TEST_CASE("string_literal Operators", "[string_literal][string_literal-operators]") {
48+
static constexpr string_literal check1 = "check1";
49+
CONSTEXPR_CHECK(check1[0] == 'c');
50+
CONSTEXPR_CHECK(check1[1] == 'h');
51+
CONSTEXPR_CHECK(check1[2] == 'e');
52+
CONSTEXPR_CHECK(check1[3] == 'c');
53+
CONSTEXPR_CHECK(check1[4] == 'k');
54+
CONSTEXPR_CHECK(check1[5] == '1');
55+
CONSTEXPR_CHECK(check1 == check1);
56+
57+
static constexpr string_literal check2 = "check2";
58+
CONSTEXPR_CHECK(check1 != check2);
59+
CONSTEXPR_CHECK(static_cast<std::string_view>(check2) == "check2"sv);
60+
CONSTEXPR_CHECK(static_cast<char const*>(check2) == "check2"sv);
61+
CONSTEXPR_CHECK((check1 + check2) == "check1check2"sv);
62+
CONSTEXPR_CHECK((check1 + "check2") == "check1check2"sv);
63+
CONSTEXPR_CHECK(("check1" + check2) == "check1check2"sv);
64+
}
65+
66+
TEST_CASE("string_literal Iterator methods", "[string_literal][string_literal-iterator]") {
67+
static constexpr string_literal iterator = "iterator";
68+
CONSTEXPR_CHECK(*iterator.begin() == 'i');
69+
CONSTEXPR_CHECK(*(iterator.begin() + 1) == 't');
70+
CONSTEXPR_CHECK(*(iterator.begin() + 2) == 'e');
71+
CONSTEXPR_CHECK(*(iterator.begin() + 3) == 'r');
72+
CONSTEXPR_CHECK(*(iterator.begin() + 4) == 'a');
73+
CONSTEXPR_CHECK(*(iterator.begin() + 5) == 't');
74+
CONSTEXPR_CHECK(*(iterator.begin() + 6) == 'o');
75+
CONSTEXPR_CHECK(*(iterator.begin() + 7) == 'r');
76+
CONSTEXPR_CHECK((iterator.begin() + 8) == iterator.end());
77+
78+
79+
CONSTEXPR_CHECK(*iterator.rbegin() == 'r');
80+
CONSTEXPR_CHECK(*(iterator.rbegin() + 1) == 'o');
81+
CONSTEXPR_CHECK(*(iterator.rbegin() + 2) == 't');
82+
CONSTEXPR_CHECK(*(iterator.rbegin() + 3) == 'a');
83+
CONSTEXPR_CHECK(*(iterator.rbegin() + 4) == 'r');
84+
CONSTEXPR_CHECK(*(iterator.rbegin() + 5) == 'e');
85+
CONSTEXPR_CHECK(*(iterator.rbegin() + 6) == 't');
86+
CONSTEXPR_CHECK(*(iterator.rbegin() + 7) == 'i');
87+
CONSTEXPR_CHECK((iterator.rbegin() + 8) == iterator.rend());
88+
89+
CONSTEXPR_CHECK(std::reverse_iterator { iterator.begin() } == iterator.rend());
90+
CONSTEXPR_CHECK(std::reverse_iterator { iterator.end() } == iterator.rbegin());
91+
CONSTEXPR_CHECK(iterator.cbegin() == iterator.begin());
92+
CONSTEXPR_CHECK(iterator.cend() == iterator.end());
93+
CONSTEXPR_CHECK(iterator.crbegin() == iterator.rbegin());
94+
CONSTEXPR_CHECK(iterator.crend() == iterator.rend());
95+
}
96+
97+
TEST_CASE("string_literal Access methods", "[string_literal][string_literal-access]") {
98+
static constexpr string_literal access = "access";
99+
CONSTEXPR_CHECK(access.at(0) == 'a');
100+
CONSTEXPR_CHECK(access.at(1) == 'c');
101+
CONSTEXPR_CHECK(access.at(2) == 'c');
102+
CONSTEXPR_CHECK(access.at(3) == 'e');
103+
CONSTEXPR_CHECK(access.at(4) == 's');
104+
CONSTEXPR_CHECK(access.at(5) == 's');
105+
106+
CONSTEXPR_CHECK(access.front() == 'a');
107+
CONSTEXPR_CHECK(access.back() == 's');
108+
109+
CONSTEXPR_CHECK(access.data()[0] == 'a');
110+
CONSTEXPR_CHECK(access.data()[1] == 'c');
111+
CONSTEXPR_CHECK(access.data()[2] == 'c');
112+
CONSTEXPR_CHECK(access.data()[3] == 'e');
113+
CONSTEXPR_CHECK(access.data()[4] == 's');
114+
CONSTEXPR_CHECK(access.data()[5] == 's');
115+
116+
CONSTEXPR_CHECK(access.c_str() == "access"sv);
117+
}
118+
119+
TEST_CASE("string_literal Inspect methods", "[string_literal][string_literal-Inspect]") {
120+
static constexpr string_literal inspect = "inspect";
121+
CONSTEXPR_CHECK(inspect.compare("inspect"sv) == 0);
122+
CONSTEXPR_CHECK(inspect.compare("inspect") == 0);
123+
CONSTEXPR_CHECK(inspect.compare("notinspect"sv) == -1);
124+
CONSTEXPR_CHECK(inspect.compare("notinspect") == -1);
125+
CONSTEXPR_CHECK(inspect.compare("aotinspect"sv) == 1);
126+
CONSTEXPR_CHECK(inspect.compare("aotinspect") == 1);
127+
128+
CONSTEXPR_CHECK(inspect.starts_with("in"sv));
129+
CONSTEXPR_CHECK(inspect.starts_with('i'));
130+
CONSTEXPR_CHECK(inspect.starts_with("in"));
131+
CONSTEXPR_CHECK_FALSE(inspect.starts_with("et"sv));
132+
CONSTEXPR_CHECK_FALSE(inspect.starts_with('b'));
133+
CONSTEXPR_CHECK_FALSE(inspect.starts_with("da"));
134+
135+
static constexpr string_literal in = "in";
136+
CONSTEXPR_CHECK(inspect.starts_with(in));
137+
138+
static constexpr string_literal not_inspect = "not inspect";
139+
CONSTEXPR_CHECK_FALSE(inspect.starts_with(not_inspect));
140+
141+
CONSTEXPR_CHECK(inspect.ends_with("ect"sv)); // spellchecker:disable-line
142+
CONSTEXPR_CHECK(inspect.ends_with('t'));
143+
CONSTEXPR_CHECK(inspect.ends_with("pect"));
144+
CONSTEXPR_CHECK_FALSE(inspect.ends_with("nb"sv));
145+
CONSTEXPR_CHECK_FALSE(inspect.ends_with('q'));
146+
CONSTEXPR_CHECK_FALSE(inspect.ends_with("rp"));
147+
148+
static constexpr string_literal ct = "ct";
149+
CONSTEXPR_CHECK(inspect.ends_with(ct));
150+
151+
CONSTEXPR_CHECK_FALSE(inspect.ends_with(not_inspect));
152+
153+
CONSTEXPR_CHECK(inspect.find("ec") == 4);
154+
CONSTEXPR_CHECK(inspect.find("ec"sv) == 4);
155+
CONSTEXPR_CHECK(inspect.find("ec", 3) == 4);
156+
CONSTEXPR_CHECK(inspect.find("ec"sv, 2) == 4);
157+
CONSTEXPR_CHECK(inspect.find("non") == inspect.npos);
158+
CONSTEXPR_CHECK(inspect.find("non"sv) == inspect.npos);
159+
160+
CONSTEXPR_CHECK(inspect.rfind("ec") == 4);
161+
CONSTEXPR_CHECK(inspect.rfind("ec"sv) == 4);
162+
CONSTEXPR_CHECK(inspect.rfind("ns", 1) == 1);
163+
CONSTEXPR_CHECK(inspect.rfind("ns"sv, 2) == 1);
164+
CONSTEXPR_CHECK(inspect.rfind("non") == inspect.npos);
165+
CONSTEXPR_CHECK(inspect.rfind("non"sv) == inspect.npos);
166+
}
167+
168+
TEST_CASE("string_literal Modifier methods", "[string_literal][string_literal-modifier]") {
169+
static constexpr string_literal modifier = "modifier";
170+
CONSTEXPR_CHECK(modifier.clear().empty());
171+
CONSTEXPR_CHECK(modifier.push_back('a') == "modifiera"sv);
172+
CONSTEXPR_CHECK(modifier.pop_back() == "modifie"sv);
173+
CONSTEXPR_CHECK(modifier.append<3>('b') == "modifierbbb"sv);
174+
CONSTEXPR_CHECK(modifier.append("hey") == "modifierhey"sv);
175+
CONSTEXPR_CHECK(modifier.append(modifier) == "modifiermodifier"sv);
176+
CONSTEXPR_CHECK(modifier.substr<1, 3>() == "odi"sv);
177+
CONSTEXPR_CHECK(modifier.substr<1>() == "odifier"sv);
178+
CONSTEXPR_CHECK(modifier.substr() == modifier);
179+
CONSTEXPR_CHECK(modifier.substr(2, 3) == "dif"sv);
180+
CONSTEXPR_CHECK(modifier.substr(3) == "ifier"sv);
181+
}

0 commit comments

Comments
 (0)