initial commit
This commit is contained in:
63
support/test/include/test.hpp
Normal file
63
support/test/include/test.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
#ifndef FIFTHGRID_TEST_INCLUDE_TEST_HPP_
|
||||
#define FIFTHGRID_TEST_INCLUDE_TEST_HPP_
|
||||
|
||||
#if defined(U)
|
||||
#undef U
|
||||
#endif // defined(U)
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using ::testing::_;
|
||||
using namespace ::testing;
|
||||
|
||||
#define COMMA ,
|
||||
|
||||
#include "utils/all.hpp"
|
||||
|
||||
namespace fifthgrid::test {
|
||||
[[nodiscard]] auto create_random_file(std::size_t size)
|
||||
-> utils::file::i_file &;
|
||||
|
||||
[[nodiscard]] auto
|
||||
generate_test_file_name(std::string_view file_name_no_extension) -> std::string;
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
template <typename buffer_t, typename result_t>
|
||||
static void decrypt_and_verify(const buffer_t &buffer, std::string_view token,
|
||||
result_t &result) {
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(token, buffer, result));
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
auto generate_test_directory() -> utils::file::i_directory &;
|
||||
|
||||
[[nodiscard]] auto get_test_config_dir() -> std::string;
|
||||
|
||||
[[nodiscard]] auto get_test_input_dir() -> std::string;
|
||||
|
||||
[[nodiscard]] auto get_test_output_dir() -> std::string;
|
||||
} // namespace fifthgrid::test
|
||||
|
||||
#endif // FIFTHGRID_TEST_INCLUDE_TEST_HPP_
|
||||
148
support/test/src/test.cpp
Normal file
148
support/test/src/test.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
extern int PROJECT_TEST_RESULT;
|
||||
|
||||
namespace {
|
||||
static std::recursive_mutex file_mtx{};
|
||||
|
||||
static std::vector<std::unique_ptr<fifthgrid::utils::file::i_fs_item>>
|
||||
generated_files{};
|
||||
|
||||
struct file_deleter final {
|
||||
std::string test_output_dir;
|
||||
|
||||
~file_deleter() {
|
||||
generated_files.clear();
|
||||
|
||||
if (PROJECT_TEST_RESULT == 0) {
|
||||
EXPECT_TRUE(fifthgrid::utils::file::directory(test_output_dir)
|
||||
.remove_recursively());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const auto deleter{
|
||||
std::make_unique<file_deleter>(fifthgrid::test::get_test_output_dir()),
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace fifthgrid::test {
|
||||
auto create_random_file(std::size_t size) -> utils::file::i_file & {
|
||||
auto path = generate_test_file_name("random");
|
||||
auto file = utils::file::file::open_or_create_file(path);
|
||||
EXPECT_TRUE(*file);
|
||||
if (*file) {
|
||||
data_buffer buf(size);
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
randombytes_buf(buf.data(), buf.size());
|
||||
#else // !defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
thread_local std::mt19937 gen{
|
||||
static_cast<std::uint_fast32_t>(std::time(nullptr)) ^
|
||||
static_cast<std::uint_fast32_t>(std::random_device{}()),
|
||||
};
|
||||
std::uniform_int_distribution<std::uint8_t> dis(0U, 255U);
|
||||
std::generate(buf.begin(), buf.end(), [&]() -> auto { return dis(gen); });
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_TRUE(file->write(buf, 0U, &bytes_written));
|
||||
EXPECT_EQ(size, bytes_written);
|
||||
|
||||
EXPECT_EQ(size, file->size());
|
||||
}
|
||||
|
||||
recur_mutex_lock lock{file_mtx};
|
||||
generated_files.emplace_back(std::move(file));
|
||||
return *dynamic_cast<utils::file::i_file *>(generated_files.back().get());
|
||||
}
|
||||
|
||||
auto generate_test_directory() -> utils::file::i_directory & {
|
||||
auto path = utils::path::combine(
|
||||
get_test_output_dir(),
|
||||
{
|
||||
std::string{"test_dir"} + std::to_string(generated_files.size()),
|
||||
});
|
||||
|
||||
recur_mutex_lock lock{file_mtx};
|
||||
generated_files.emplace_back(std::unique_ptr<utils::file::i_fs_item>(
|
||||
new utils::file::directory{path}));
|
||||
|
||||
auto &ret =
|
||||
*dynamic_cast<utils::file::i_directory *>(generated_files.back().get());
|
||||
EXPECT_TRUE(ret.create_directory());
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto generate_test_file_name(std::string_view file_name_no_extension)
|
||||
-> std::string {
|
||||
auto path = utils::path::combine(
|
||||
get_test_output_dir(), {
|
||||
std::string{file_name_no_extension} +
|
||||
std::to_string(generated_files.size()),
|
||||
});
|
||||
|
||||
recur_mutex_lock lock{file_mtx};
|
||||
generated_files.emplace_back(
|
||||
std::unique_ptr<utils::file::i_file>(new utils::file::file{path}));
|
||||
return generated_files.back()->get_path();
|
||||
}
|
||||
|
||||
auto get_test_config_dir() -> std::string {
|
||||
static auto test_path = ([]() -> std::string {
|
||||
auto dir = utils::get_environment_variable("PROJECT_TEST_CONFIG_DIR");
|
||||
return utils::path::combine(dir.empty() ? "." : dir, {"test_config"});
|
||||
})();
|
||||
|
||||
return test_path;
|
||||
}
|
||||
|
||||
auto get_test_input_dir() -> std::string {
|
||||
static auto test_path = ([]() -> std::string {
|
||||
auto dir = utils::get_environment_variable("PROJECT_TEST_INPUT_DIR");
|
||||
return utils::path::combine(dir.empty() ? "." : dir, {"test_input"});
|
||||
})();
|
||||
|
||||
return test_path;
|
||||
}
|
||||
|
||||
auto get_test_output_dir() -> std::string {
|
||||
static auto test_path = ([]() -> std::string {
|
||||
auto temp = utils::file::create_temp_name("project_test");
|
||||
|
||||
#if defined(_WIN32)
|
||||
auto path = utils::path::combine("%TEMP%", {temp});
|
||||
#else // !defined(_WIN32)
|
||||
auto path = utils::path::combine("/tmp", {temp});
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
if (not utils::file::directory(path).exists()) {
|
||||
EXPECT_TRUE(utils::file::directory{path}.create_directory());
|
||||
}
|
||||
|
||||
return path;
|
||||
})();
|
||||
|
||||
return test_path;
|
||||
}
|
||||
} // namespace fifthgrid::test
|
||||
73
support/test/src/utils/atomic_test.cpp
Normal file
73
support/test/src/utils/atomic_test.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
namespace {
|
||||
struct config final {
|
||||
std::string a;
|
||||
std::string b;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace fifthgrid {
|
||||
TEST(utils_atomic_test, atomic_primitive) {
|
||||
utils::atomic<std::uint16_t> value;
|
||||
value = 5U;
|
||||
EXPECT_EQ(5U, static_cast<std::uint16_t>(value));
|
||||
EXPECT_EQ(5U, value.load());
|
||||
|
||||
value.store(6U);
|
||||
EXPECT_EQ(6U, static_cast<std::uint16_t>(value));
|
||||
EXPECT_EQ(6U, value.load());
|
||||
}
|
||||
|
||||
TEST(utils_atomic_test, atomic_primitive_equality) {
|
||||
utils::atomic<std::uint16_t> value1{5U};
|
||||
utils::atomic<std::uint16_t> value2{5U};
|
||||
EXPECT_EQ(value1, value1);
|
||||
EXPECT_EQ(value2, value2);
|
||||
EXPECT_EQ(value1, value2);
|
||||
EXPECT_EQ(static_cast<std::uint16_t>(value1), 5U);
|
||||
EXPECT_EQ(static_cast<std::uint16_t>(value2), 5U);
|
||||
}
|
||||
|
||||
TEST(utils_atomic_test, atomic_primitive_inequality) {
|
||||
utils::atomic<std::uint16_t> value1{5U};
|
||||
utils::atomic<std::uint16_t> value2{6U};
|
||||
EXPECT_NE(value1, value2);
|
||||
EXPECT_NE(static_cast<std::uint16_t>(value1), 6U);
|
||||
EXPECT_NE(static_cast<std::uint16_t>(value2), 5U);
|
||||
}
|
||||
|
||||
TEST(utils_atomic_test, atomic_struct) {
|
||||
utils::atomic<config> value{
|
||||
config{
|
||||
.a = "a",
|
||||
.b = "b",
|
||||
},
|
||||
};
|
||||
|
||||
auto data = static_cast<config>(value);
|
||||
EXPECT_STREQ("a", data.a.c_str());
|
||||
EXPECT_STREQ("b", data.b.c_str());
|
||||
}
|
||||
} // namespace fifthgrid
|
||||
389
support/test/src/utils/base64_test.cpp
Normal file
389
support/test/src/utils/base64_test.cpp
Normal file
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
using macaron::Base64::Decode;
|
||||
using macaron::Base64::Encode;
|
||||
using macaron::Base64::EncodeUrlSafe;
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] auto decode_to_string(std::string_view str) -> std::string {
|
||||
auto vec = Decode(str);
|
||||
return {vec.begin(), vec.end()};
|
||||
}
|
||||
|
||||
[[nodiscard]] auto standard_to_url_safe(std::string str, bool keep_padding)
|
||||
-> std::string {
|
||||
for (auto &cur_ch : str) {
|
||||
if (cur_ch == '+') {
|
||||
cur_ch = '-';
|
||||
} else if (cur_ch == '/') {
|
||||
cur_ch = '_';
|
||||
}
|
||||
}
|
||||
if (not keep_padding) {
|
||||
while (not str.empty() && str.back() == '=') {
|
||||
str.pop_back();
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(utils_base64, rfc4648_known_vectors_standard_padded) {
|
||||
struct vec_case {
|
||||
std::string_view in;
|
||||
std::string_view b64;
|
||||
};
|
||||
const std::array<vec_case, 7> vectors{{
|
||||
{"", ""},
|
||||
{"f", "Zg=="},
|
||||
{"fo", "Zm8="},
|
||||
{"foo", "Zm9v"},
|
||||
{"foob", "Zm9vYg=="},
|
||||
{"fooba", "Zm9vYmE="},
|
||||
{"foobar", "Zm9vYmFy"},
|
||||
}};
|
||||
|
||||
for (const auto &vec_entry : vectors) {
|
||||
const auto enc_str =
|
||||
Encode(reinterpret_cast<const unsigned char *>(vec_entry.in.data()),
|
||||
vec_entry.in.size(), /*url_safe=*/false, /*pad=*/true);
|
||||
EXPECT_EQ(enc_str, vec_entry.b64);
|
||||
|
||||
const auto dec_vec = Decode(vec_entry.b64);
|
||||
EXPECT_EQ(std::string(dec_vec.begin(), dec_vec.end()), vec_entry.in);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_base64, url_safe_padded_and_unpadded_match_transformed_standard) {
|
||||
const std::string payload =
|
||||
std::string("This+/needs/URL-safe mapping and padding checks.") +
|
||||
std::string("\x00\x01\xFE\xFF", 4);
|
||||
|
||||
const auto std_padded =
|
||||
Encode(reinterpret_cast<const unsigned char *>(payload.data()),
|
||||
payload.size(), /*url_safe=*/false, /*pad=*/true);
|
||||
const auto url_padded =
|
||||
Encode(reinterpret_cast<const unsigned char *>(payload.data()),
|
||||
payload.size(), /*url_safe=*/true, /*pad=*/true);
|
||||
const auto url_unpadded =
|
||||
Encode(reinterpret_cast<const unsigned char *>(payload.data()),
|
||||
payload.size(), /*url_safe=*/true, /*pad=*/false);
|
||||
|
||||
const auto url_from_std_padded =
|
||||
standard_to_url_safe(std_padded, /*keep_padding=*/true);
|
||||
const auto url_from_std_unpadded =
|
||||
standard_to_url_safe(std_padded, /*keep_padding=*/false);
|
||||
|
||||
EXPECT_EQ(url_padded, url_from_std_padded);
|
||||
EXPECT_EQ(url_unpadded, url_from_std_unpadded);
|
||||
|
||||
const auto dec_one = Decode(url_padded);
|
||||
const auto dec_two = Decode(url_unpadded);
|
||||
EXPECT_EQ(std::string(dec_one.begin(), dec_one.end()), payload);
|
||||
EXPECT_EQ(std::string(dec_two.begin(), dec_two.end()), payload);
|
||||
}
|
||||
|
||||
TEST(utils_base64, empty_input) {
|
||||
const std::string empty_str;
|
||||
const auto enc_empty_std =
|
||||
Encode(reinterpret_cast<const unsigned char *>(empty_str.data()),
|
||||
empty_str.size(), /*url_safe=*/false, /*pad=*/true);
|
||||
const auto enc_empty_url =
|
||||
Encode(reinterpret_cast<const unsigned char *>(empty_str.data()),
|
||||
empty_str.size(), /*url_safe=*/true, /*pad=*/false);
|
||||
EXPECT_TRUE(enc_empty_std.empty());
|
||||
EXPECT_TRUE(enc_empty_url.empty());
|
||||
|
||||
const auto dec_empty = Decode("");
|
||||
EXPECT_TRUE(dec_empty.empty());
|
||||
}
|
||||
|
||||
TEST(utils_base64, remainder_boundaries_round_trip) {
|
||||
const std::string str_one = "A"; // rem 1
|
||||
const std::string str_two = "AB"; // rem 2
|
||||
const std::string str_thr = "ABC"; // rem 0
|
||||
const std::string str_fou = "ABCD"; // rem 1 after blocks
|
||||
const std::string str_fiv = "ABCDE"; // rem 2 after blocks
|
||||
|
||||
for (const auto *str_ptr :
|
||||
{&str_one, &str_two, &str_thr, &str_fou, &str_fiv}) {
|
||||
const auto enc_std =
|
||||
Encode(reinterpret_cast<const unsigned char *>(str_ptr->data()),
|
||||
str_ptr->size(), false, true);
|
||||
const auto dec_std = Decode(enc_std);
|
||||
EXPECT_EQ(std::string(dec_std.begin(), dec_std.end()), *str_ptr);
|
||||
|
||||
const auto enc_url_pad =
|
||||
Encode(reinterpret_cast<const unsigned char *>(str_ptr->data()),
|
||||
str_ptr->size(), true, true);
|
||||
const auto dec_url_pad = Decode(enc_url_pad);
|
||||
EXPECT_EQ(std::string(dec_url_pad.begin(), dec_url_pad.end()), *str_ptr);
|
||||
|
||||
const auto enc_url_nopad =
|
||||
Encode(reinterpret_cast<const unsigned char *>(str_ptr->data()),
|
||||
str_ptr->size(), true, false);
|
||||
const auto dec_url_nopad = Decode(enc_url_nopad);
|
||||
EXPECT_EQ(std::string(dec_url_nopad.begin(), dec_url_nopad.end()),
|
||||
*str_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_base64, decode_accepts_standard_and_url_safe_forms) {
|
||||
const std::string input_str = "Man is distinguished, not only by his reason.";
|
||||
const auto std_padded =
|
||||
Encode(reinterpret_cast<const unsigned char *>(input_str.data()),
|
||||
input_str.size(), false, true);
|
||||
const auto url_padded =
|
||||
Encode(reinterpret_cast<const unsigned char *>(input_str.data()),
|
||||
input_str.size(), true, true);
|
||||
const auto url_unpadded =
|
||||
Encode(reinterpret_cast<const unsigned char *>(input_str.data()),
|
||||
input_str.size(), true, false);
|
||||
|
||||
const auto dec_std = Decode(std_padded);
|
||||
const auto dec_url_pad = Decode(url_padded);
|
||||
const auto dec_url_nopad = Decode(url_unpadded);
|
||||
|
||||
EXPECT_EQ(std::string(dec_std.begin(), dec_std.end()), input_str);
|
||||
EXPECT_EQ(std::string(dec_url_pad.begin(), dec_url_pad.end()), input_str);
|
||||
EXPECT_EQ(std::string(dec_url_nopad.begin(), dec_url_nopad.end()), input_str);
|
||||
}
|
||||
|
||||
TEST(utils_base64, all_byte_values_round_trip) {
|
||||
std::vector<unsigned char> byte_vec(256);
|
||||
for (size_t idx = 0; idx < byte_vec.size(); ++idx) {
|
||||
byte_vec[idx] = static_cast<unsigned char>(idx);
|
||||
}
|
||||
|
||||
const auto enc_std = Encode(byte_vec.data(), byte_vec.size(), false, true);
|
||||
const auto dec_std = Decode(enc_std);
|
||||
ASSERT_EQ(dec_std.size(), byte_vec.size());
|
||||
EXPECT_TRUE(std::equal(dec_std.begin(), dec_std.end(), byte_vec.begin()));
|
||||
|
||||
const auto enc_url = Encode(byte_vec.data(), byte_vec.size(), true, false);
|
||||
const auto dec_url = Decode(enc_url);
|
||||
ASSERT_EQ(dec_url.size(), byte_vec.size());
|
||||
EXPECT_TRUE(std::equal(dec_url.begin(), dec_url.end(), byte_vec.begin()));
|
||||
}
|
||||
|
||||
TEST(utils_base64, wrapper_encode_url_safe_equals_flagged_encode) {
|
||||
const std::string data_str = "wrap me!";
|
||||
const auto enc_wrap_a =
|
||||
EncodeUrlSafe(reinterpret_cast<const unsigned char *>(data_str.data()),
|
||||
data_str.size(), /*pad=*/false);
|
||||
const auto enc_wrap_b =
|
||||
Encode(reinterpret_cast<const unsigned char *>(data_str.data()),
|
||||
data_str.size(), /*url_safe=*/true, /*pad=*/false);
|
||||
EXPECT_EQ(enc_wrap_a, enc_wrap_b);
|
||||
|
||||
const auto enc_wrap_a2 = EncodeUrlSafe(data_str, /*pad=*/true);
|
||||
const auto enc_wrap_b2 = Encode(data_str, /*url_safe=*/true, /*pad=*/true);
|
||||
EXPECT_EQ(enc_wrap_a2, enc_wrap_b2);
|
||||
}
|
||||
|
||||
TEST(utils_base64, unpadded_length_rules) {
|
||||
const auto enc_one = Encode("f", /*url_safe=*/true, /*pad=*/false);
|
||||
const auto enc_two = Encode("fo", /*url_safe=*/true, /*pad=*/false);
|
||||
const auto enc_thr = Encode("foo", /*url_safe=*/true, /*pad=*/false);
|
||||
EXPECT_EQ(enc_one.size(), 2U);
|
||||
EXPECT_EQ(enc_two.size(), 3U);
|
||||
EXPECT_EQ(enc_thr.size(), 4U);
|
||||
|
||||
EXPECT_EQ(Decode(enc_one), std::vector<unsigned char>({'f'}));
|
||||
EXPECT_EQ(Decode(enc_two), std::vector<unsigned char>({'f', 'o'}));
|
||||
EXPECT_EQ(Decode(enc_thr), std::vector<unsigned char>({'f', 'o', 'o'}));
|
||||
}
|
||||
|
||||
TEST(utils_base64, errors_length_mod4_eq_1) {
|
||||
EXPECT_THROW(Decode("A"), std::runtime_error);
|
||||
EXPECT_THROW(Decode("AAAAA"), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(utils_base64, errors_invalid_characters) {
|
||||
EXPECT_THROW(Decode("Zm9v YmFy"), std::runtime_error);
|
||||
EXPECT_THROW(Decode("Zm9v*YmFy"), std::runtime_error);
|
||||
EXPECT_THROW(Decode("Z=g="), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(utils_base64, reject_whitespace_and_controls) {
|
||||
// newline, tab, and space should be rejected (decoder does not skip
|
||||
// whitespace)
|
||||
EXPECT_THROW(Decode("Zg==\n"), std::runtime_error);
|
||||
EXPECT_THROW(Decode("Zg==\t"), std::runtime_error);
|
||||
EXPECT_THROW(Decode("Z g=="), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(utils_base64, reject_padding_in_nonfinal_quartet) {
|
||||
// '=' cannot appear before the final quartet
|
||||
EXPECT_THROW(Decode("AAA=AAAA"), std::runtime_error);
|
||||
EXPECT_THROW(Decode("Zg==Zg=="), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(utils_base64, reject_padding_in_first_two_slots_of_final_quartet) {
|
||||
// '=' only allowed in slots 3 and/or 4 of the final quartet
|
||||
EXPECT_THROW(Decode("=AAA"), std::runtime_error);
|
||||
EXPECT_THROW(Decode("A=AA"), std::runtime_error);
|
||||
EXPECT_THROW(
|
||||
Decode("Z=g="),
|
||||
std::runtime_error); // already in your suite, kept for completeness
|
||||
}
|
||||
|
||||
TEST(utils_base64, reject_incorrect_padding_count_for_length) {
|
||||
// "f" must be "Zg==" (two '='). One '=' is invalid.
|
||||
EXPECT_THROW(Decode("Zg="), std::runtime_error);
|
||||
|
||||
// "foo" must be unpadded ("Zm9v"). Extra '=' is invalid.
|
||||
EXPECT_THROW(Decode("Zm9v="), std::runtime_error);
|
||||
|
||||
// "fo" must have exactly one '=' -> "Zm8="
|
||||
// Correct cases:
|
||||
EXPECT_NO_THROW(Decode("Zm8="));
|
||||
EXPECT_NO_THROW(Decode("Zm9v"));
|
||||
}
|
||||
|
||||
TEST(utils_base64, accept_unpadded_equivalents_when_legal) {
|
||||
EXPECT_EQ(decode_to_string("Zg"), "f");
|
||||
EXPECT_EQ(decode_to_string("Zm8"), "fo");
|
||||
EXPECT_EQ(decode_to_string("Zm9v"), "foo");
|
||||
EXPECT_EQ(decode_to_string("Zm9vYmE"), "fooba");
|
||||
}
|
||||
|
||||
TEST(utils_base64, mixed_alphabet_is_accepted) {
|
||||
const std::string input_str = "any+/mix_/of+chars/";
|
||||
const auto std_padded =
|
||||
Encode(reinterpret_cast<const unsigned char *>(input_str.data()),
|
||||
input_str.size(), /*url_safe=*/false, /*pad=*/true);
|
||||
|
||||
std::string mixed = std_padded;
|
||||
for (char &cur_ch : mixed) {
|
||||
if (cur_ch == '+') {
|
||||
cur_ch = '-';
|
||||
} else if (cur_ch == '/') {
|
||||
cur_ch = '_';
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(decode_to_string(mixed), input_str);
|
||||
}
|
||||
|
||||
TEST(utils_base64, invalid_non_ascii_octets_in_input) {
|
||||
// Extended bytes like 0xFF are not valid Base64 characters
|
||||
std::string bad = "Zg==";
|
||||
bad[1] = static_cast<char>(0xFF);
|
||||
EXPECT_THROW(Decode(bad), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(utils_base64, large_buffer_round_trip_and_sizes) {
|
||||
// Deterministic pseudo-random buffer
|
||||
const std::size_t byte_len = 1 << 20; // 1 MiB
|
||||
std::vector<unsigned char> data_vec(byte_len);
|
||||
unsigned int val = 0x12345678U;
|
||||
for (unsigned char &idx : data_vec) {
|
||||
val ^= val << 13;
|
||||
val ^= val >> 17;
|
||||
val ^= val << 5; // xorshift32
|
||||
idx = static_cast<unsigned char>(val & 0xFFU);
|
||||
}
|
||||
|
||||
// Padded encode length should be 4 * ceil(N/3)
|
||||
const auto enc_pad = Encode(data_vec.data(), data_vec.size(),
|
||||
/*url_safe=*/false, /*pad=*/true);
|
||||
const std::size_t expected_padded = 4U * ((byte_len + 2U) / 3U);
|
||||
EXPECT_EQ(enc_pad.size(), expected_padded);
|
||||
|
||||
// Unpadded encode length rule (RFC 4648 §5)
|
||||
const auto enc_nopad = Encode(data_vec.data(), data_vec.size(),
|
||||
/*url_safe=*/true, /*pad=*/false);
|
||||
const std::size_t rem = byte_len % 3U;
|
||||
const std::size_t expected_unpadded =
|
||||
4U * (byte_len / 3U) + (rem == 0U ? 0U : (rem == 1U ? 2U : 3U));
|
||||
EXPECT_EQ(enc_nopad.size(), expected_unpadded);
|
||||
|
||||
// Round-trips
|
||||
const auto dec_pad = Decode(enc_pad);
|
||||
const auto dec_nopad = Decode(enc_nopad);
|
||||
ASSERT_EQ(dec_pad.size(), data_vec.size());
|
||||
ASSERT_EQ(dec_nopad.size(), data_vec.size());
|
||||
EXPECT_TRUE(std::equal(dec_pad.begin(), dec_pad.end(), data_vec.begin()));
|
||||
EXPECT_TRUE(std::equal(dec_nopad.begin(), dec_nopad.end(), data_vec.begin()));
|
||||
}
|
||||
|
||||
TEST(utils_base64, url_safe_round_trip_various_lengths) {
|
||||
for (std::size_t len : {0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 32U, 33U, 64U, 65U}) {
|
||||
std::vector<unsigned char> buf(len);
|
||||
for (std::size_t i = 0; i < len; ++i) {
|
||||
buf[i] = static_cast<unsigned char>(i * 13U + 7U);
|
||||
}
|
||||
|
||||
const auto enc_unpadded =
|
||||
Encode(buf.data(), buf.size(), /*url_safe=*/true, /*pad=*/false);
|
||||
const auto enc_padded =
|
||||
Encode(buf.data(), buf.size(), /*url_safe=*/true, /*pad=*/true);
|
||||
|
||||
const auto dec_unpadded = Decode(enc_unpadded);
|
||||
const auto dec_padded = Decode(enc_padded);
|
||||
|
||||
ASSERT_EQ(dec_unpadded.size(), buf.size());
|
||||
ASSERT_EQ(dec_padded.size(), buf.size());
|
||||
EXPECT_TRUE(
|
||||
std::equal(dec_unpadded.begin(), dec_unpadded.end(), buf.begin()));
|
||||
EXPECT_TRUE(std::equal(dec_padded.begin(), dec_padded.end(), buf.begin()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_base64, reject_trailing_garbage_after_padding) {
|
||||
// Anything after final '=' padding is invalid
|
||||
EXPECT_THROW(Decode("Zg==A"), std::runtime_error);
|
||||
EXPECT_THROW(Decode("Zm8=A"), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(utils_base64, reject_three_padding_chars_total) {
|
||||
// Any string with total length %4==1 is invalid (e.g., "Zg===")
|
||||
EXPECT_THROW(Decode("Zg==="), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(utils_base64, standard_vs_url_safe_encoding_equivalence) {
|
||||
const std::string msg = "base64 / url-safe + cross-check";
|
||||
|
||||
const auto std_enc =
|
||||
Encode(reinterpret_cast<const unsigned char *>(msg.data()), msg.size(),
|
||||
/*url_safe=*/false, /*pad=*/true);
|
||||
const auto url_enc =
|
||||
Encode(reinterpret_cast<const unsigned char *>(msg.data()), msg.size(),
|
||||
/*url_safe=*/true, /*pad=*/true);
|
||||
|
||||
std::string transformed = std_enc;
|
||||
for (char &cur_ch : transformed) {
|
||||
if (cur_ch == '+') {
|
||||
cur_ch = '-';
|
||||
} else if (cur_ch == '/') {
|
||||
cur_ch = '_';
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(url_enc, transformed);
|
||||
|
||||
// decode once, then construct
|
||||
EXPECT_EQ(decode_to_string(url_enc), msg);
|
||||
}
|
||||
250
support/test/src/utils/collection_test.cpp
Normal file
250
support/test/src/utils/collection_test.cpp
Normal file
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
namespace fifthgrid {
|
||||
TEST(utils_collection, excludes) {
|
||||
auto data = {"cow", "moose", "dog", "chicken"};
|
||||
EXPECT_FALSE(utils::collection::excludes(data, "chicken"));
|
||||
EXPECT_FALSE(utils::collection::excludes(data, "cow"));
|
||||
EXPECT_FALSE(utils::collection::excludes(data, "dog"));
|
||||
EXPECT_FALSE(utils::collection::excludes(data, "moose"));
|
||||
EXPECT_TRUE(utils::collection::excludes(data, "mouse"));
|
||||
}
|
||||
|
||||
TEST(utils_collection, includes) {
|
||||
auto data = {"cow", "moose", "dog", "chicken"};
|
||||
EXPECT_FALSE(utils::collection::includes(data, "mice"));
|
||||
EXPECT_TRUE(utils::collection::includes(data, "chicken"));
|
||||
EXPECT_TRUE(utils::collection::includes(data, "cow"));
|
||||
EXPECT_TRUE(utils::collection::includes(data, "dog"));
|
||||
EXPECT_TRUE(utils::collection::includes(data, "moose"));
|
||||
}
|
||||
|
||||
TEST(utils_collection, from_hex_string) {
|
||||
{
|
||||
auto data = "0xABCDEF10";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(4U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = " 0xABCDEF10 ";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(4U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = "ABCDEF10";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(4U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = "ACDEF";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(3U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = " ACDEF ";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(3U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = "";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L"0xABCDEF10";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(4U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L" 0xABCDEF10 ";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(4U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L"ABCDEF10";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(4U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L"ACDEF";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(3U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L" ACDEF ";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(3U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L"";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_collection, from_hex_string_fails) {
|
||||
{
|
||||
auto data = "ABCDEF1Z";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = "ABC DEF1";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = "0x";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = " 0x ";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L"ABCDEF1Z";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L"ABC DEF1";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L"0x";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L" 0x";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_collection, to_hex_string) {
|
||||
{
|
||||
std::array<std::int8_t, 2U> col{
|
||||
static_cast<std::int8_t>(0xFF),
|
||||
static_cast<std::int8_t>(0xEE),
|
||||
};
|
||||
|
||||
auto str = utils::collection::to_hex_string(col);
|
||||
EXPECT_STREQ("ffee", str.c_str());
|
||||
|
||||
auto w_str = utils::collection::to_hex_wstring(col);
|
||||
EXPECT_STREQ(L"ffee", w_str.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
std::array<std::uint8_t, 2U> col{
|
||||
static_cast<std::uint8_t>(0xFF),
|
||||
static_cast<std::uint8_t>(0xEE),
|
||||
};
|
||||
|
||||
auto str = utils::collection::to_hex_string(col);
|
||||
EXPECT_STREQ("ffee", str.c_str());
|
||||
|
||||
auto w_str = utils::collection::to_hex_wstring(col);
|
||||
EXPECT_STREQ(L"ffee", w_str.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_collection, remove_element) {
|
||||
{
|
||||
std::vector<std::uint8_t> col{
|
||||
static_cast<std::uint8_t>(0xFF),
|
||||
static_cast<std::uint8_t>(0xEE),
|
||||
};
|
||||
|
||||
utils::collection::remove_element(col, 0xFF);
|
||||
EXPECT_EQ(1U, col.size());
|
||||
EXPECT_EQ(static_cast<std::uint8_t>(0xEE), col.at(0U));
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::uint8_t> col{
|
||||
static_cast<std::uint8_t>(0xFF),
|
||||
static_cast<std::uint8_t>(0xEE),
|
||||
};
|
||||
|
||||
utils::collection::remove_element(col, 0xEE);
|
||||
EXPECT_EQ(1U, col.size());
|
||||
EXPECT_EQ(static_cast<std::uint8_t>(0xFF), col.at(0U));
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::uint8_t> col{
|
||||
static_cast<std::uint8_t>(0xFF),
|
||||
static_cast<std::uint8_t>(0xEE),
|
||||
};
|
||||
|
||||
utils::collection::remove_element(col, 0xEF);
|
||||
EXPECT_EQ(2U, col.size());
|
||||
EXPECT_EQ(static_cast<std::uint8_t>(0xFF), col.at(0U));
|
||||
EXPECT_EQ(static_cast<std::uint8_t>(0xEE), col.at(1U));
|
||||
}
|
||||
}
|
||||
} // namespace fifthgrid
|
||||
306
support/test/src/utils/common_test.cpp
Normal file
306
support/test/src/utils/common_test.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
namespace fifthgrid {
|
||||
TEST(utils_common, calculate_read_size) {
|
||||
auto read_size = utils::calculate_read_size(0U, 0U, 0U);
|
||||
EXPECT_EQ(0U, read_size);
|
||||
|
||||
read_size = utils::calculate_read_size(5U, 0U, 0U);
|
||||
EXPECT_EQ(0U, read_size);
|
||||
|
||||
read_size = utils::calculate_read_size(0U, 6U, 7U);
|
||||
EXPECT_EQ(0U, read_size);
|
||||
|
||||
read_size = utils::calculate_read_size(7U, 1U, 7U);
|
||||
EXPECT_EQ(0U, read_size);
|
||||
|
||||
read_size = utils::calculate_read_size(5U, 5U, 0U);
|
||||
EXPECT_EQ(5U, read_size);
|
||||
|
||||
read_size = utils::calculate_read_size(5U, 5U, 1U);
|
||||
EXPECT_EQ(4U, read_size);
|
||||
}
|
||||
|
||||
TEST(utils_common, version_equal) {
|
||||
EXPECT_EQ(0, utils::compare_version_strings("", ""));
|
||||
EXPECT_EQ(0, utils::compare_version_strings("1.0", "1.0"));
|
||||
EXPECT_EQ(0, utils::compare_version_strings("1.0.0", "1.0"));
|
||||
EXPECT_EQ(0, utils::compare_version_strings("1.0.0.0", "1.0"));
|
||||
EXPECT_EQ(0, utils::compare_version_strings("1.0.0.0", "1.0.0"));
|
||||
|
||||
EXPECT_EQ(0, utils::compare_version_strings(L"", L""));
|
||||
EXPECT_EQ(0, utils::compare_version_strings(L"1.0", L"1.0"));
|
||||
EXPECT_EQ(0, utils::compare_version_strings(L"1.0.0", L"1.0"));
|
||||
EXPECT_EQ(0, utils::compare_version_strings(L"1.0.0.0", L"1.0"));
|
||||
EXPECT_EQ(0, utils::compare_version_strings(L"1.0.0.0", L"1.0.0"));
|
||||
}
|
||||
|
||||
TEST(utils_common, version_greater) {
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1", ""));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1", "1.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1", "1.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1", "1.0.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1.0", "1.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1.0", "1.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1.0", "1.0.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0", "0.9.9"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1", "0.9.9"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1.0", "0.9.9"));
|
||||
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1", ""));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1", L"1.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1", L"1.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1", L"1.0.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1.0", L"1.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1.0", L"1.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1.0", L"1.0.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0", L"0.9.9"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1", L"0.9.9"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1.0", L"0.9.9"));
|
||||
}
|
||||
|
||||
TEST(utils_common, version_less) {
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("", "1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("0.9.9", "1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("0.9.9", "1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("0.9.9", "1.0.1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("1.0", "1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("1.0", "1.0.1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("1.0.0", "1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("1.0.0", "1.0.1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("1.0.0.0", "1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("1.0.0.0", "1.0.1.0"));
|
||||
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"", L"1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"0.9.9", L"1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"0.9.9", L"1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"0.9.9", L"1.0.1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"1.0", L"1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"1.0", L"1.0.1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"1.0.0", L"1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"1.0.0", L"1.0.1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"1.0.0.0", L"1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"1.0.0.0", L"1.0.1.0"));
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_STDUUID)
|
||||
TEST(utils_common, create_uuid_string) {
|
||||
{
|
||||
const auto uuid1 = utils::create_uuid_string();
|
||||
const auto uuid2 = utils::create_uuid_string();
|
||||
ASSERT_EQ(36U, uuid1.size());
|
||||
ASSERT_EQ(36U, uuid2.size());
|
||||
ASSERT_STRNE(uuid1.c_str(), uuid2.c_str());
|
||||
}
|
||||
{
|
||||
const auto uuid1 = utils::create_uuid_wstring();
|
||||
const auto uuid2 = utils::create_uuid_wstring();
|
||||
ASSERT_EQ(36U, uuid1.size());
|
||||
ASSERT_EQ(36U, uuid2.size());
|
||||
ASSERT_STRNE(uuid1.c_str(), uuid2.c_str());
|
||||
}
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_STDUUID)
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
TEST(utils_common, generate_secure_random) {
|
||||
{
|
||||
auto r1 = utils::generate_secure_random<std::size_t>();
|
||||
auto r2 = utils::generate_secure_random<std::size_t>();
|
||||
EXPECT_NE(r1, r2);
|
||||
}
|
||||
|
||||
{
|
||||
auto r1 = utils::generate_secure_random<std::vector<std::uint8_t>>(6U);
|
||||
auto r2 = utils::generate_secure_random<std::vector<std::uint8_t>>(6U);
|
||||
EXPECT_EQ(6U, r1.size());
|
||||
EXPECT_EQ(r1.size(), r2.size());
|
||||
EXPECT_NE(r1, r2);
|
||||
}
|
||||
|
||||
{
|
||||
auto r1 = utils::generate_secure_random<std::array<std::uint8_t, 4U>>();
|
||||
auto r2 = utils::generate_secure_random<std::array<std::uint8_t, 4U>>();
|
||||
EXPECT_EQ(4U, r1.size());
|
||||
EXPECT_EQ(r1.size(), r2.size());
|
||||
EXPECT_NE(0, std::memcmp(r1.data(), r2.data(), r1.size()));
|
||||
}
|
||||
|
||||
{
|
||||
auto r1 = utils::generate_secure_random<std::string>(6U);
|
||||
auto r2 = utils::generate_secure_random<std::string>(6U);
|
||||
EXPECT_EQ(6U, r1.size());
|
||||
EXPECT_EQ(r1.size(), r2.size());
|
||||
EXPECT_NE(0, std::memcmp(r1.data(), r2.data(), r1.size()));
|
||||
}
|
||||
|
||||
{
|
||||
auto r1 = utils::generate_secure_random<std::wstring>(6U);
|
||||
auto r2 = utils::generate_secure_random<std::wstring>(6U);
|
||||
EXPECT_EQ(6U, r1.size());
|
||||
EXPECT_EQ(r1.size(), r2.size());
|
||||
EXPECT_NE(0, std::memcmp(r1.data(), r2.data(), r1.size()));
|
||||
}
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
TEST(utils_common, divide_with_ceiling) {
|
||||
auto r = utils::divide_with_ceiling(12, 5);
|
||||
EXPECT_EQ(3, r);
|
||||
|
||||
r = utils::divide_with_ceiling(12, 4);
|
||||
EXPECT_EQ(3, r);
|
||||
|
||||
r = utils::divide_with_ceiling(1, 2);
|
||||
EXPECT_EQ(1, r);
|
||||
|
||||
r = utils::divide_with_ceiling(2, 2);
|
||||
EXPECT_EQ(1, r);
|
||||
|
||||
r = utils::divide_with_ceiling(0, 2);
|
||||
EXPECT_EQ(0, r);
|
||||
}
|
||||
|
||||
TEST(utils_common, generate_random_between_for_signed_integers) {
|
||||
static constexpr auto max_iterations{1000000UL};
|
||||
|
||||
for (std::size_t idx = 0U; idx < max_iterations; ++idx) {
|
||||
auto res = utils::generate_random_between(5, 12);
|
||||
EXPECT_GE(res, 5);
|
||||
EXPECT_LE(res, 12);
|
||||
}
|
||||
|
||||
for (std::size_t idx = 0U; idx < max_iterations; ++idx) {
|
||||
auto res = utils::generate_random_between(-5, 12);
|
||||
EXPECT_GE(res, -5);
|
||||
EXPECT_LE(res, 12);
|
||||
}
|
||||
|
||||
for (std::size_t idx = 0U; idx < max_iterations; ++idx) {
|
||||
auto res = utils::generate_random_between(-5, -1);
|
||||
EXPECT_GE(res, -5);
|
||||
EXPECT_LE(res, -1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_common, generate_random_between_for_unsigned_integers) {
|
||||
static constexpr auto max_iterations{1000000UL};
|
||||
|
||||
for (std::size_t idx = 0U; idx < max_iterations; ++idx) {
|
||||
auto res = utils::generate_random_between(5U, 12U);
|
||||
EXPECT_GE(res, 5);
|
||||
EXPECT_LE(res, 12);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_common, generate_random_between_throws_error_on_invalid_range) {
|
||||
EXPECT_THROW(
|
||||
{
|
||||
try {
|
||||
[[maybe_unused]] auto res = utils::generate_random_between(12, 5);
|
||||
} catch (const std::range_error &e) {
|
||||
EXPECT_STREQ("end must be greater than begin", e.what());
|
||||
throw;
|
||||
}
|
||||
},
|
||||
std::range_error);
|
||||
|
||||
EXPECT_THROW(
|
||||
{
|
||||
try {
|
||||
[[maybe_unused]] auto res = utils::generate_random_between(12, 12);
|
||||
} catch (const std::range_error &e) {
|
||||
EXPECT_STREQ("end must be greater than begin", e.what());
|
||||
throw;
|
||||
}
|
||||
},
|
||||
std::range_error);
|
||||
}
|
||||
|
||||
TEST(utils_common, generate_random_string) {
|
||||
static constexpr auto max_iterations{10000L};
|
||||
|
||||
const auto test_string = [](auto str) {
|
||||
static std::vector<decltype(str)> list{};
|
||||
|
||||
EXPECT_FALSE(utils::collection::includes(list, str));
|
||||
list.push_back(str);
|
||||
|
||||
EXPECT_EQ(16U, str.size());
|
||||
for (auto &&ch : str) {
|
||||
auto ch_int = static_cast<std::uint32_t>(ch);
|
||||
EXPECT_GE(ch_int, 48U);
|
||||
EXPECT_LE(ch_int, 73U + 48U);
|
||||
}
|
||||
};
|
||||
|
||||
for (std::size_t idx = 0U; idx < max_iterations; ++idx) {
|
||||
test_string(utils::generate_random_string(16U));
|
||||
test_string(utils::generate_random_wstring(16U));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_common, generate_random_string_for_zero_length) {
|
||||
EXPECT_TRUE(utils::generate_random_string(0U).empty());
|
||||
EXPECT_TRUE(utils::generate_random_wstring(0U).empty());
|
||||
}
|
||||
|
||||
TEST(utils_common, get_environment_variable) {
|
||||
static constexpr std::string path_env{"PATH"};
|
||||
std::string path;
|
||||
|
||||
#if defined(_WIN32)
|
||||
path.resize(fifthgrid::max_path_length + 1U);
|
||||
auto size = ::GetEnvironmentVariableA(path_env.c_str(), path.data(), 0U);
|
||||
|
||||
path.resize(size);
|
||||
::GetEnvironmentVariableA(path_env.c_str(), path.data(),
|
||||
static_cast<DWORD>(path.size()));
|
||||
#else // !defined(_WIN32)
|
||||
path = std::getenv(path_env.c_str());
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
EXPECT_STREQ(path.c_str(), utils::get_environment_variable(path_env).c_str());
|
||||
EXPECT_STREQ(
|
||||
utils::string::from_utf8(path).c_str(),
|
||||
utils::get_environment_variable(utils::string::from_utf8(path_env))
|
||||
.c_str());
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
TEST(utils_common, get_next_available_port) {
|
||||
std::uint16_t available_port{};
|
||||
for (std::uint16_t port = 1025U; port < 1030U; ++port) {
|
||||
EXPECT_TRUE(utils::get_next_available_port(port, available_port));
|
||||
EXPECT_GE(available_port, port);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_common, get_next_available_port_fails_if_starting_point_is_zero) {
|
||||
std::uint16_t available_port{};
|
||||
EXPECT_FALSE(utils::get_next_available_port(0U, available_port));
|
||||
EXPECT_EQ(0U, available_port);
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
} // namespace fifthgrid
|
||||
300
support/test/src/utils/db_sqlite_test.cpp
Normal file
300
support/test/src/utils/db_sqlite_test.cpp
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
namespace fifthgrid {
|
||||
class utils_db_sqlite : public ::testing::Test {
|
||||
public:
|
||||
utils::db::sqlite::db3_t db3;
|
||||
|
||||
void SetUp() override {
|
||||
{
|
||||
sqlite3 *db3_ptr{nullptr};
|
||||
auto res = sqlite3_open(":memory:", &db3_ptr);
|
||||
ASSERT_EQ(SQLITE_OK, res);
|
||||
ASSERT_TRUE(db3_ptr != nullptr);
|
||||
db3 = utils::db::sqlite::db3_t{
|
||||
db3_ptr,
|
||||
utils::db::sqlite::sqlite3_deleter(),
|
||||
};
|
||||
}
|
||||
|
||||
utils::db::sqlite::db3_stmt_t db3_stmt;
|
||||
{
|
||||
std::string sql{
|
||||
"CREATE TABLE [table] (column1 TEXT PRIMARY KEY UNIQUE "
|
||||
"NOT NULL, column2 TEXT NOT NULL);",
|
||||
};
|
||||
|
||||
sqlite3_stmt *stmt_ptr{nullptr};
|
||||
auto res =
|
||||
sqlite3_prepare_v2(db3.get(), sql.c_str(), -1, &stmt_ptr, nullptr);
|
||||
db3_stmt = utils::db::sqlite::db3_stmt_t{
|
||||
stmt_ptr,
|
||||
utils::db::sqlite::sqlite3_statement_deleter(),
|
||||
};
|
||||
ASSERT_EQ(SQLITE_OK, res);
|
||||
}
|
||||
|
||||
auto res = sqlite3_step(db3_stmt.get());
|
||||
ASSERT_EQ(SQLITE_DONE, res);
|
||||
}
|
||||
|
||||
void TearDown() override { db3.reset(); }
|
||||
};
|
||||
|
||||
static void common_insert(sqlite3 &db3, bool dump = false) {
|
||||
auto query = utils::db::sqlite::db_insert{db3, "table"}
|
||||
.column_value("column1", "test0")
|
||||
.column_value("column2", "test1");
|
||||
if (dump) {
|
||||
std::cout << query.dump() << std::endl;
|
||||
}
|
||||
|
||||
auto res = query.go();
|
||||
EXPECT_TRUE(res.ok());
|
||||
}
|
||||
|
||||
static void common_select(sqlite3 &db3, std::string value1, std::string value2,
|
||||
bool dump = false) {
|
||||
auto query = utils::db::sqlite::db_select{db3, "table"};
|
||||
if (dump) {
|
||||
std::cout << query.dump() << std::endl;
|
||||
}
|
||||
|
||||
auto res = query.go();
|
||||
EXPECT_TRUE(res.ok());
|
||||
EXPECT_TRUE(res.has_row());
|
||||
|
||||
std::size_t row_count{};
|
||||
while (res.has_row()) {
|
||||
std::optional<utils::db::sqlite::db_result::row> row;
|
||||
EXPECT_TRUE(res.get_row(row));
|
||||
EXPECT_TRUE(row.has_value());
|
||||
if (row.has_value()) {
|
||||
auto columns = row.value().get_columns();
|
||||
EXPECT_EQ(std::size_t(2U), columns.size());
|
||||
EXPECT_STREQ("column1", columns[0U].get_name().c_str());
|
||||
EXPECT_STREQ(value1.c_str(),
|
||||
columns[0U].get_value<std::string>().c_str());
|
||||
EXPECT_STREQ("column2", columns[1U].get_name().c_str());
|
||||
EXPECT_STREQ(value2.c_str(),
|
||||
columns[1U].get_value<std::string>().c_str());
|
||||
for (auto &&column : columns) {
|
||||
std::cout << column.get_index() << ':';
|
||||
std::cout << column.get_name() << ':';
|
||||
std::cout << column.get_value<std::string>() << std::endl;
|
||||
}
|
||||
}
|
||||
++row_count;
|
||||
}
|
||||
|
||||
EXPECT_EQ(std::size_t(1U), row_count);
|
||||
}
|
||||
|
||||
static void common_delete(sqlite3 &db3, bool dump = false) {
|
||||
{
|
||||
auto query = utils::db::sqlite::db_delete{db3, "table"};
|
||||
if (dump) {
|
||||
std::cout << query.dump() << std::endl;
|
||||
}
|
||||
|
||||
auto res = query.go();
|
||||
EXPECT_TRUE(res.ok());
|
||||
}
|
||||
|
||||
{
|
||||
auto query = utils::db::sqlite::db_select{db3, "table"};
|
||||
auto res = query.go();
|
||||
EXPECT_TRUE(res.ok());
|
||||
|
||||
std::size_t row_count{};
|
||||
while (res.has_row()) {
|
||||
++row_count;
|
||||
}
|
||||
|
||||
EXPECT_EQ(std::size_t(0U), row_count);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_delete_query) {
|
||||
auto query = utils::db::sqlite::db_delete{*db3.get(), "table"};
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(R"(DELETE FROM "table";)", query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_delete_where_query) {
|
||||
auto query = utils::db::sqlite::db_delete{*db3.get(), "table"}
|
||||
.where("column1")
|
||||
.equals("test1")
|
||||
.and_()
|
||||
.where("column2")
|
||||
.equals("test2");
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(R"(DELETE FROM "table" WHERE "column1"=?1 AND "column2"=?2;)",
|
||||
query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_insert_query) {
|
||||
auto query = utils::db::sqlite::db_insert{*db3.get(), "table"}
|
||||
.column_value("column1", "test9")
|
||||
.column_value("column2", "test9");
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(R"(INSERT INTO "table" ("column1", "column2") VALUES (?1, ?2);)",
|
||||
query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_insert_or_replace_query) {
|
||||
auto query = utils::db::sqlite::db_insert{*db3.get(), "table"}
|
||||
.or_replace()
|
||||
.column_value("column1", "test1")
|
||||
.column_value("column2", "test2");
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(
|
||||
R"(INSERT OR REPLACE INTO "table" ("column1", "column2") VALUES (?1, ?2);)",
|
||||
query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_select_query) {
|
||||
auto query = utils::db::sqlite::db_select{*db3.get(), "table"};
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(R"(SELECT * FROM "table";)", query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_select_where_query) {
|
||||
auto query = utils::db::sqlite::db_select{*db3.get(), "table"}
|
||||
.where("column1")
|
||||
.equals("test1")
|
||||
.and_()
|
||||
.where("column2")
|
||||
.equals("test2");
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(R"(SELECT * FROM "table" WHERE "column1"=?1 AND "column2"=?2;)",
|
||||
query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_select_where_with_group_query) {
|
||||
auto query =
|
||||
utils::db::sqlite::db_select{*db3.get(), "table"}
|
||||
.group([](auto &grp) {
|
||||
grp.where("column1").equals("a").or_().where("column1").equals("b");
|
||||
})
|
||||
.and_()
|
||||
.group([](auto &grp) {
|
||||
grp.where("column2").equals("c").or_().where("column2").equals("d");
|
||||
})
|
||||
.or_()
|
||||
.group([](auto &grp) {
|
||||
grp.where("column1").equals("e").or_().where("column2").equals("f");
|
||||
});
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(
|
||||
R"(SELECT * FROM "table" WHERE ("column1"=?1 OR "column1"=?2) AND ("column2"=?3 OR "column2"=?4) OR ("column1"=?5 OR "column2"=?6);)",
|
||||
query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_select_columns_query) {
|
||||
auto query = utils::db::sqlite::db_select{*db3.get(), "table"}
|
||||
.column("column1")
|
||||
.column("column2")
|
||||
.where("column1")
|
||||
.equals("test1")
|
||||
.and_()
|
||||
.where("column2")
|
||||
.equals("test2");
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(
|
||||
R"(SELECT column1, column2 FROM "table" WHERE "column1"=?1 AND "column2"=?2;)",
|
||||
query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_update_query) {
|
||||
auto query = utils::db::sqlite::db_update{*db3.get(), "table"}
|
||||
.column_value("column1", "moose")
|
||||
.where("column1")
|
||||
.equals("test1")
|
||||
.and_()
|
||||
.where("column2")
|
||||
.equals("test2");
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(
|
||||
R"(UPDATE "table" SET "column1"=?1 WHERE "column1"=?2 AND "column2"=?3;)",
|
||||
query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, insert_select_delete) {
|
||||
common_insert(*db3.get(), true);
|
||||
|
||||
common_select(*db3.get(), "test0", "test1", true);
|
||||
|
||||
common_delete(*db3.get(), true);
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, insert_update_delete) {
|
||||
common_insert(*db3.get());
|
||||
|
||||
{
|
||||
auto query = utils::db::sqlite::db_update{*db3.get(), "table"}
|
||||
.column_value("column1", "moose")
|
||||
.where("column1")
|
||||
.equals("test0");
|
||||
std::cout << query.dump() << std::endl;
|
||||
auto res = query.go();
|
||||
EXPECT_TRUE(res.ok());
|
||||
}
|
||||
|
||||
common_select(*db3.get(), "moose", "test1");
|
||||
|
||||
common_delete(*db3.get());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, insert_or_replace_and_delete) {
|
||||
common_insert(*db3.get());
|
||||
|
||||
{
|
||||
auto query = utils::db::sqlite::db_insert{*db3.get(), "table"}
|
||||
.or_replace()
|
||||
.column_value("column1", "test0")
|
||||
.column_value("column2", "moose");
|
||||
std::cout << query.dump() << std::endl;
|
||||
auto res = query.go();
|
||||
EXPECT_TRUE(res.ok());
|
||||
}
|
||||
|
||||
common_select(*db3.get(), "test0", "moose");
|
||||
|
||||
common_delete(*db3.get());
|
||||
}
|
||||
} // namespace fifthgrid
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
713
support/test/src/utils/encrypting_reader_test.cpp
Normal file
713
support/test/src/utils/encrypting_reader_test.cpp
Normal file
@@ -0,0 +1,713 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
namespace {
|
||||
const auto get_stop_requested = []() -> bool { return false; };
|
||||
} // namespace
|
||||
|
||||
namespace fifthgrid {
|
||||
TEST(utils_encrypting_reader, read_file_data) {
|
||||
const auto token = std::string("moose");
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token,
|
||||
std::nullopt);
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i++) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size());
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
ASSERT_EQ(
|
||||
buffer.size() / 2U,
|
||||
utils::encryption::encrypting_reader::reader_function(
|
||||
reinterpret_cast<char *>(&buffer[(buffer.size() / 2U) * j]),
|
||||
buffer.size() / 2U, 1U, &reader));
|
||||
}
|
||||
|
||||
data_buffer decrypted_data;
|
||||
EXPECT_TRUE(
|
||||
utils::encryption::decrypt_data(token, buffer, decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size() * i,
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encrypting_reader, read_file_data_using_argon2id) {
|
||||
const auto token = std::string("moose");
|
||||
utils::encryption::kdf_config cfg;
|
||||
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token, cfg,
|
||||
std::nullopt);
|
||||
|
||||
std::array<std::uint8_t, utils::encryption::kdf_config::size()> hdr;
|
||||
EXPECT_EQ(hdr.size(), utils::encryption::encrypting_reader::reader_function(
|
||||
reinterpret_cast<char *>(hdr.data()), hdr.size(),
|
||||
1U, &reader));
|
||||
EXPECT_TRUE(utils::encryption::kdf_config::from_header(hdr, cfg));
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i++) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size());
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
ASSERT_EQ(
|
||||
buffer.size() / 2U,
|
||||
utils::encryption::encrypting_reader::reader_function(
|
||||
reinterpret_cast<char *>(&buffer[(buffer.size() / 2U) * j]),
|
||||
buffer.size() / 2U, 1U, &reader));
|
||||
}
|
||||
|
||||
data_buffer decrypted_data;
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(
|
||||
token, *reader.get_kdf_config_for_data(), buffer, decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size() * i,
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encrypting_reader, read_file_data_using_argon2id_master_key) {
|
||||
const auto token = std::string("moose");
|
||||
utils::encryption::kdf_config cfg;
|
||||
|
||||
auto master_key =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg);
|
||||
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, master_key, cfg,
|
||||
std::nullopt);
|
||||
|
||||
std::array<std::uint8_t, utils::encryption::kdf_config::size()> hdr;
|
||||
EXPECT_EQ(hdr.size(), utils::encryption::encrypting_reader::reader_function(
|
||||
reinterpret_cast<char *>(hdr.data()), hdr.size(),
|
||||
1U, &reader));
|
||||
EXPECT_TRUE(utils::encryption::kdf_config::from_header(hdr, cfg));
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i++) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size());
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
ASSERT_EQ(
|
||||
buffer.size() / 2U,
|
||||
utils::encryption::encrypting_reader::reader_function(
|
||||
reinterpret_cast<char *>(&buffer[(buffer.size() / 2U) * j]),
|
||||
buffer.size() / 2U, 1U, &reader));
|
||||
}
|
||||
|
||||
auto data_cfg = *reader.get_kdf_config_for_data();
|
||||
utils::hash::hash_256_t data_key;
|
||||
std::tie(data_key, std::ignore) = cfg.create_subkey(
|
||||
utils::encryption::kdf_context::data, data_cfg.unique_id, master_key);
|
||||
data_buffer decrypted_data;
|
||||
EXPECT_TRUE(
|
||||
utils::encryption::decrypt_data(data_key, buffer, decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size() * i,
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encrypting_reader, read_file_data_in_multiple_chunks) {
|
||||
const auto token = std::string("moose");
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token,
|
||||
std::nullopt);
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i += 2U) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size() *
|
||||
2U);
|
||||
EXPECT_EQ(buffer.size(),
|
||||
utils::encryption::encrypting_reader::reader_function(
|
||||
reinterpret_cast<char *>(buffer.data()), buffer.size(), 1U,
|
||||
&reader));
|
||||
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
data_buffer decrypted_data;
|
||||
const auto offset = (j * (buffer.size() / 2U));
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(
|
||||
token,
|
||||
data_buffer(
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(offset)),
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(
|
||||
offset + (buffer.size() / 2U)))),
|
||||
decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
(utils::encryption::encrypting_reader::get_data_chunk_size() * i) +
|
||||
(j *
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size()),
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encrypting_reader,
|
||||
read_file_data_in_multiple_chunks_using_argon2id) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
|
||||
const auto token = std::string("moose");
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token, cfg,
|
||||
std::nullopt);
|
||||
|
||||
std::array<std::uint8_t, utils::encryption::kdf_config::size()> hdr;
|
||||
EXPECT_EQ(hdr.size(), utils::encryption::encrypting_reader::reader_function(
|
||||
reinterpret_cast<char *>(hdr.data()), hdr.size(),
|
||||
1U, &reader));
|
||||
EXPECT_TRUE(utils::encryption::kdf_config::from_header(hdr, cfg));
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i += 2U) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size() *
|
||||
2U);
|
||||
EXPECT_EQ(buffer.size(),
|
||||
utils::encryption::encrypting_reader::reader_function(
|
||||
reinterpret_cast<char *>(buffer.data()), buffer.size(), 1U,
|
||||
&reader));
|
||||
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
data_buffer decrypted_data;
|
||||
const auto offset = (j * (buffer.size() / 2U));
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(
|
||||
token, *reader.get_kdf_config_for_data(),
|
||||
data_buffer(
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(offset)),
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(
|
||||
offset + (buffer.size() / 2U)))),
|
||||
decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
(utils::encryption::encrypting_reader::get_data_chunk_size() * i) +
|
||||
(j *
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size()),
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encrypting_reader,
|
||||
read_file_data_in_multiple_chunks_using_argon2id_master_key) {
|
||||
const auto token = std::string("moose");
|
||||
utils::encryption::kdf_config cfg;
|
||||
|
||||
auto master_key =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg);
|
||||
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, master_key, cfg,
|
||||
std::nullopt);
|
||||
|
||||
std::array<std::uint8_t, utils::encryption::kdf_config::size()> hdr;
|
||||
EXPECT_EQ(hdr.size(), utils::encryption::encrypting_reader::reader_function(
|
||||
reinterpret_cast<char *>(hdr.data()), hdr.size(),
|
||||
1U, &reader));
|
||||
EXPECT_TRUE(utils::encryption::kdf_config::from_header(hdr, cfg));
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i += 2U) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size() *
|
||||
2U);
|
||||
EXPECT_EQ(buffer.size(),
|
||||
utils::encryption::encrypting_reader::reader_function(
|
||||
reinterpret_cast<char *>(buffer.data()), buffer.size(), 1U,
|
||||
&reader));
|
||||
|
||||
auto data_cfg = *reader.get_kdf_config_for_data();
|
||||
utils::hash::hash_256_t data_key;
|
||||
std::tie(data_key, std::ignore) = cfg.create_subkey(
|
||||
utils::encryption::kdf_context::data, data_cfg.unique_id, master_key);
|
||||
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
data_buffer decrypted_data;
|
||||
const auto offset = (j * (buffer.size() / 2U));
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(
|
||||
data_key,
|
||||
data_buffer(
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(offset)),
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(
|
||||
offset + (buffer.size() / 2U)))),
|
||||
decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
(utils::encryption::encrypting_reader::get_data_chunk_size() * i) +
|
||||
(j *
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size()),
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encrypting_reader, read_file_data_as_stream) {
|
||||
const auto token = std::string("moose");
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token,
|
||||
std::nullopt);
|
||||
auto io_stream = reader.create_iostream();
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
EXPECT_EQ(reader.get_total_size(),
|
||||
static_cast<std::uint64_t>(io_stream->tellg()));
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::beg).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i++) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size());
|
||||
EXPECT_FALSE(
|
||||
io_stream->seekg(static_cast<std::streamoff>(i * buffer.size()))
|
||||
.fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
EXPECT_FALSE(
|
||||
io_stream
|
||||
->read(
|
||||
reinterpret_cast<char *>(&buffer[(buffer.size() / 2U) * j]),
|
||||
static_cast<std::streamsize>(buffer.size()) / 2U)
|
||||
.fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
}
|
||||
|
||||
data_buffer decrypted_data;
|
||||
EXPECT_TRUE(
|
||||
utils::encryption::decrypt_data(token, buffer, decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size() * i,
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encrypting_reader, read_file_data_as_stream_using_argon2id) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
|
||||
const auto token = std::string("moose");
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token, cfg,
|
||||
std::nullopt);
|
||||
auto io_stream = reader.create_iostream();
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
EXPECT_EQ(reader.get_total_size(),
|
||||
static_cast<std::uint64_t>(io_stream->tellg()));
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::beg).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i++) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size());
|
||||
EXPECT_FALSE(
|
||||
io_stream
|
||||
->seekg(static_cast<std::streamoff>(
|
||||
i * buffer.size() + utils::encryption::kdf_config::size()))
|
||||
.fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
EXPECT_FALSE(
|
||||
io_stream
|
||||
->read(
|
||||
reinterpret_cast<char *>(&buffer[(buffer.size() / 2U) * j]),
|
||||
static_cast<std::streamsize>(buffer.size()) / 2U)
|
||||
.fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
}
|
||||
|
||||
data_buffer decrypted_data;
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(
|
||||
token, *reader.get_kdf_config_for_data(), buffer, decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size() * i,
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encrypting_reader,
|
||||
read_file_data_as_stream_using_argon2id_master_key) {
|
||||
const auto token = std::string("moose");
|
||||
utils::encryption::kdf_config cfg;
|
||||
|
||||
auto master_key =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg);
|
||||
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, master_key, cfg,
|
||||
std::nullopt);
|
||||
auto io_stream = reader.create_iostream();
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
EXPECT_EQ(reader.get_total_size(),
|
||||
static_cast<std::uint64_t>(io_stream->tellg()));
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::beg).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i++) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size());
|
||||
EXPECT_FALSE(
|
||||
io_stream
|
||||
->seekg(static_cast<std::streamoff>(
|
||||
i * buffer.size() + utils::encryption::kdf_config::size()))
|
||||
.fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
EXPECT_FALSE(
|
||||
io_stream
|
||||
->read(
|
||||
reinterpret_cast<char *>(&buffer[(buffer.size() / 2U) * j]),
|
||||
static_cast<std::streamsize>(buffer.size()) / 2U)
|
||||
.fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
}
|
||||
|
||||
auto data_cfg = *reader.get_kdf_config_for_data();
|
||||
utils::hash::hash_256_t data_key;
|
||||
std::tie(data_key, std::ignore) = cfg.create_subkey(
|
||||
utils::encryption::kdf_context::data, data_cfg.unique_id, master_key);
|
||||
|
||||
data_buffer decrypted_data;
|
||||
EXPECT_TRUE(
|
||||
utils::encryption::decrypt_data(data_key, buffer, decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size() * i,
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encrypting_reader, read_file_data_in_multiple_chunks_as_stream) {
|
||||
const auto token = std::string("moose");
|
||||
auto &source_file = test::create_random_file(
|
||||
8u * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token,
|
||||
std::nullopt);
|
||||
auto io_stream = reader.create_iostream();
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
EXPECT_EQ(reader.get_total_size(),
|
||||
static_cast<std::uint64_t>(io_stream->tellg()));
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::beg).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i += 2U) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size() *
|
||||
2U);
|
||||
EXPECT_FALSE(io_stream
|
||||
->read(reinterpret_cast<char *>(buffer.data()),
|
||||
static_cast<std::streamsize>(buffer.size()))
|
||||
.fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
data_buffer decrypted_data;
|
||||
const auto offset = (j * (buffer.size() / 2U));
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(
|
||||
token,
|
||||
data_buffer(
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(offset)),
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(
|
||||
offset + (buffer.size() / 2U)))),
|
||||
decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
(utils::encryption::encrypting_reader::get_data_chunk_size() * i) +
|
||||
(j *
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size()),
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encrypting_reader,
|
||||
read_file_data_in_multiple_chunks_as_stream_using_argon2id) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
|
||||
const auto token = std::string("moose");
|
||||
auto &source_file = test::create_random_file(
|
||||
8u * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token, cfg,
|
||||
std::nullopt);
|
||||
auto io_stream = reader.create_iostream();
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
EXPECT_EQ(reader.get_total_size(),
|
||||
static_cast<std::uint64_t>(io_stream->tellg()));
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::beg).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
|
||||
EXPECT_FALSE(io_stream
|
||||
->seekg(static_cast<std::streamoff>(
|
||||
utils::encryption::kdf_config::size()))
|
||||
.fail());
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i += 2U) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size() *
|
||||
2U);
|
||||
EXPECT_FALSE(io_stream
|
||||
->read(reinterpret_cast<char *>(buffer.data()),
|
||||
static_cast<std::streamsize>(buffer.size()))
|
||||
.fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
data_buffer decrypted_data;
|
||||
const auto offset = (j * (buffer.size() / 2U));
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(
|
||||
token, *reader.get_kdf_config_for_data(),
|
||||
data_buffer(
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(offset)),
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(
|
||||
offset + (buffer.size() / 2U)))),
|
||||
decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
(utils::encryption::encrypting_reader::get_data_chunk_size() * i) +
|
||||
(j *
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size()),
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encrypting_reader,
|
||||
read_file_data_in_multiple_chunks_as_stream_using_argon2id_master_key) {
|
||||
const auto token = std::string("moose");
|
||||
utils::encryption::kdf_config cfg;
|
||||
|
||||
auto master_key =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg);
|
||||
|
||||
auto &source_file = test::create_random_file(
|
||||
8u * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, master_key, cfg,
|
||||
std::nullopt);
|
||||
auto io_stream = reader.create_iostream();
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
EXPECT_EQ(reader.get_total_size(),
|
||||
static_cast<std::uint64_t>(io_stream->tellg()));
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::beg).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
|
||||
EXPECT_FALSE(io_stream
|
||||
->seekg(static_cast<std::streamoff>(
|
||||
utils::encryption::kdf_config::size()))
|
||||
.fail());
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i += 2U) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size() *
|
||||
2U);
|
||||
EXPECT_FALSE(io_stream
|
||||
->read(reinterpret_cast<char *>(buffer.data()),
|
||||
static_cast<std::streamsize>(buffer.size()))
|
||||
.fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
|
||||
auto data_cfg = *reader.get_kdf_config_for_data();
|
||||
utils::hash::hash_256_t data_key;
|
||||
std::tie(data_key, std::ignore) = cfg.create_subkey(
|
||||
utils::encryption::kdf_context::data, data_cfg.unique_id, master_key);
|
||||
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
data_buffer decrypted_data;
|
||||
const auto offset = (j * (buffer.size() / 2U));
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(
|
||||
data_key,
|
||||
data_buffer(
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(offset)),
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(
|
||||
offset + (buffer.size() / 2U)))),
|
||||
decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
(utils::encryption::encrypting_reader::get_data_chunk_size() * i) +
|
||||
(j *
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size()),
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace fifthgrid
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
402
support/test/src/utils/encryption_kdf_config_test.cpp
Normal file
402
support/test/src/utils/encryption_kdf_config_test.cpp
Normal file
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
namespace fifthgrid {
|
||||
TEST(utils_encryption_kdf_config, can_construct_using_default_constructor) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
|
||||
EXPECT_EQ(utils::encryption::kdf_version::v1, cfg.version);
|
||||
EXPECT_EQ(utils::encryption::kdf_type::argon2id, cfg.kdf);
|
||||
EXPECT_EQ(utils::encryption::memlimit_level::level3, cfg.memlimit);
|
||||
EXPECT_EQ(utils::encryption::opslimit_level::level2, cfg.opslimit);
|
||||
EXPECT_EQ(utils::encryption::kdf_config::salt_t{}, cfg.salt);
|
||||
EXPECT_EQ(0U, cfg.unique_id);
|
||||
EXPECT_EQ(0U, cfg.checksum);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, can_seal) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.seal();
|
||||
EXPECT_NE(utils::encryption::kdf_config::salt_t{}, cfg.salt);
|
||||
|
||||
auto orig_salt = cfg.salt;
|
||||
cfg.seal();
|
||||
EXPECT_NE(orig_salt, cfg.salt);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, can_generate_checksum) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
EXPECT_EQ(13087047540462255120ULL, cfg.generate_checksum());
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, seal_calculates_checksum) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.seal();
|
||||
|
||||
EXPECT_NE(0U, cfg.checksum);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, can_create_header_and_restore) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.unique_id = 2U;
|
||||
cfg.seal();
|
||||
auto hdr = cfg.to_header();
|
||||
|
||||
EXPECT_EQ(utils::encryption::kdf_config::size(), hdr.size());
|
||||
|
||||
utils::encryption::kdf_config restored_cfg;
|
||||
EXPECT_TRUE(utils::encryption::kdf_config::from_header(hdr, restored_cfg));
|
||||
auto restored_hdr = restored_cfg.to_header();
|
||||
|
||||
EXPECT_EQ(hdr, restored_hdr);
|
||||
EXPECT_EQ(cfg.version, restored_cfg.version);
|
||||
EXPECT_EQ(cfg.kdf, restored_cfg.kdf);
|
||||
EXPECT_EQ(cfg.memlimit, restored_cfg.memlimit);
|
||||
EXPECT_EQ(cfg.opslimit, restored_cfg.opslimit);
|
||||
EXPECT_EQ(cfg.salt, restored_cfg.salt);
|
||||
EXPECT_EQ(cfg.checksum, restored_cfg.checksum);
|
||||
EXPECT_EQ(cfg.unique_id, restored_cfg.unique_id);
|
||||
EXPECT_EQ(cfg, restored_cfg);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, header_restore_fails_if_version_is_invalid) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.version = static_cast<utils::encryption::kdf_version>(0x11);
|
||||
cfg.seal();
|
||||
|
||||
auto hdr = cfg.to_header();
|
||||
utils::encryption::kdf_config restored_cfg;
|
||||
EXPECT_FALSE(utils::encryption::kdf_config::from_header(hdr, restored_cfg));
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, header_restore_fails_if_kdf_is_invalid) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.kdf = static_cast<utils::encryption::kdf_type>(0x11);
|
||||
cfg.seal();
|
||||
|
||||
auto hdr = cfg.to_header();
|
||||
utils::encryption::kdf_config restored_cfg;
|
||||
EXPECT_FALSE(utils::encryption::kdf_config::from_header(hdr, restored_cfg));
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, header_restore_fails_if_memlimit_is_invalid) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.memlimit = static_cast<utils::encryption::memlimit_level>(0x11);
|
||||
cfg.seal();
|
||||
|
||||
auto hdr = cfg.to_header();
|
||||
utils::encryption::kdf_config restored_cfg;
|
||||
EXPECT_FALSE(utils::encryption::kdf_config::from_header(hdr, restored_cfg));
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, header_restore_fails_if_opslimit_is_invalid) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.opslimit = static_cast<utils::encryption::opslimit_level>(0x11);
|
||||
cfg.seal();
|
||||
|
||||
auto hdr = cfg.to_header();
|
||||
utils::encryption::kdf_config restored_cfg;
|
||||
EXPECT_FALSE(utils::encryption::kdf_config::from_header(hdr, restored_cfg));
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, header_restore_fails_if_salt_is_invalid) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.seal();
|
||||
cfg.salt = utils::encryption::kdf_config::salt_t{};
|
||||
|
||||
auto hdr = cfg.to_header();
|
||||
utils::encryption::kdf_config restored_cfg;
|
||||
EXPECT_FALSE(utils::encryption::kdf_config::from_header(hdr, restored_cfg));
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, header_restore_fails_if_id_is_invalid) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.seal();
|
||||
cfg.unique_id = 22U;
|
||||
|
||||
auto hdr = cfg.to_header();
|
||||
utils::encryption::kdf_config restored_cfg;
|
||||
EXPECT_FALSE(utils::encryption::kdf_config::from_header(hdr, restored_cfg));
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, create_subkey_sets_id_and_updates_checksum) {
|
||||
using hash_t = utils::hash::hash_256_t;
|
||||
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.seal();
|
||||
|
||||
hash_t master_key =
|
||||
utils::encryption::generate_key<hash_t>("root-master-key");
|
||||
|
||||
constexpr std::size_t sub_id = 42;
|
||||
auto [subkey, out_cfg] = cfg.create_subkey<hash_t>(
|
||||
utils::encryption::kdf_context::path, sub_id, master_key);
|
||||
|
||||
EXPECT_NE(subkey, hash_t{});
|
||||
EXPECT_NE(subkey, master_key);
|
||||
|
||||
EXPECT_EQ(out_cfg.unique_id, static_cast<std::uint64_t>(sub_id));
|
||||
EXPECT_EQ(out_cfg.checksum, out_cfg.generate_checksum());
|
||||
|
||||
EXPECT_EQ(out_cfg.version, cfg.version);
|
||||
EXPECT_EQ(out_cfg.kdf, cfg.kdf);
|
||||
EXPECT_EQ(out_cfg.memlimit, cfg.memlimit);
|
||||
EXPECT_EQ(out_cfg.opslimit, cfg.opslimit);
|
||||
EXPECT_EQ(out_cfg.salt, cfg.salt);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config,
|
||||
create_subkey_is_deterministic_for_same_inputs) {
|
||||
using hash_t = utils::hash::hash_256_t;
|
||||
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.seal();
|
||||
|
||||
hash_t master_key =
|
||||
utils::encryption::generate_key<hash_t>("root-master-key");
|
||||
|
||||
constexpr auto ctx = utils::encryption::kdf_context::data;
|
||||
constexpr std::size_t sub_id = 7;
|
||||
|
||||
auto [k1, c1] = cfg.create_subkey<hash_t>(ctx, sub_id, master_key);
|
||||
auto [k2, c2] = cfg.create_subkey<hash_t>(ctx, sub_id, master_key);
|
||||
|
||||
EXPECT_EQ(k1, k2);
|
||||
EXPECT_EQ(c1.unique_id, c2.unique_id);
|
||||
EXPECT_EQ(c1.checksum, c2.checksum);
|
||||
EXPECT_EQ(c1, c2);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, create_subkey_varies_with_different_id) {
|
||||
using hash_t = utils::hash::hash_256_t;
|
||||
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.seal();
|
||||
|
||||
hash_t master_key =
|
||||
utils::encryption::generate_key<hash_t>("root-master-key");
|
||||
|
||||
constexpr auto ctx = utils::encryption::kdf_context::data;
|
||||
|
||||
auto [k1, c1] = cfg.create_subkey<hash_t>(ctx, 1, master_key);
|
||||
auto [k2, c2] = cfg.create_subkey<hash_t>(ctx, 2, master_key);
|
||||
|
||||
EXPECT_NE(k1, k2);
|
||||
EXPECT_NE(c1.unique_id, c2.unique_id);
|
||||
EXPECT_NE(c1.checksum, c2.checksum);
|
||||
|
||||
EXPECT_EQ(c1.version, c2.version);
|
||||
EXPECT_EQ(c1.kdf, c2.kdf);
|
||||
EXPECT_EQ(c1.memlimit, c2.memlimit);
|
||||
EXPECT_EQ(c1.opslimit, c2.opslimit);
|
||||
EXPECT_EQ(c1.salt, c2.salt);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, create_subkey_varies_with_different_context) {
|
||||
using hash_t = utils::hash::hash_256_t;
|
||||
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.seal();
|
||||
|
||||
hash_t master_key =
|
||||
utils::encryption::generate_key<hash_t>("root-master-key");
|
||||
|
||||
constexpr std::size_t sub_id = 123;
|
||||
|
||||
auto [ka, ca] = cfg.create_subkey<hash_t>(
|
||||
utils::encryption::kdf_context::data, sub_id, master_key);
|
||||
auto [kb, cb] = cfg.create_subkey<hash_t>(
|
||||
utils::encryption::kdf_context::path, sub_id, master_key);
|
||||
|
||||
EXPECT_NE(ka, kb);
|
||||
EXPECT_EQ(ca.unique_id, cb.unique_id);
|
||||
EXPECT_EQ(ca.checksum, cb.checksum);
|
||||
EXPECT_EQ(ca, cb);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config,
|
||||
create_subkey_with_undefined_context_uses_fallback) {
|
||||
using hash_t = utils::hash::hash_256_t;
|
||||
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.seal();
|
||||
|
||||
hash_t master_key =
|
||||
utils::encryption::generate_key<hash_t>("root-master-key");
|
||||
|
||||
constexpr std::size_t sub_id = 55;
|
||||
|
||||
auto [k_def, c_def] = cfg.create_subkey<hash_t>(
|
||||
utils::encryption::kdf_context::undefined, sub_id, master_key);
|
||||
auto [k_dat, c_dat] = cfg.create_subkey<hash_t>(
|
||||
utils::encryption::kdf_context::data, sub_id, master_key);
|
||||
|
||||
EXPECT_NE(k_def, hash_t{});
|
||||
EXPECT_NE(k_dat, hash_t{});
|
||||
EXPECT_NE(k_def, k_dat);
|
||||
|
||||
EXPECT_EQ(c_def, c_dat);
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_JSON)
|
||||
TEST(utils_encryption_kdf_config, can_convert_kdf_config_to_and_from_json) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.unique_id = 2U;
|
||||
cfg.seal();
|
||||
|
||||
nlohmann::json json_kdf(cfg);
|
||||
|
||||
auto cfg2 = json_kdf.get<utils::encryption::kdf_config>();
|
||||
EXPECT_EQ(cfg, cfg2);
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_JSON)
|
||||
|
||||
TEST(utils_encryption_kdf_config, equality) {
|
||||
{
|
||||
utils::encryption::kdf_config cfg;
|
||||
utils::encryption::kdf_config cfg2;
|
||||
|
||||
EXPECT_EQ(cfg, cfg2);
|
||||
}
|
||||
|
||||
{
|
||||
utils::encryption::kdf_config cfg;
|
||||
utils::encryption::kdf_config cfg2{cfg};
|
||||
|
||||
EXPECT_EQ(cfg, cfg2);
|
||||
}
|
||||
|
||||
{
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.seal();
|
||||
|
||||
utils::encryption::kdf_config cfg2{cfg};
|
||||
|
||||
EXPECT_EQ(cfg, cfg2);
|
||||
}
|
||||
|
||||
{
|
||||
utils::encryption::kdf_config cfg;
|
||||
utils::encryption::kdf_config cfg2;
|
||||
cfg2 = cfg;
|
||||
|
||||
EXPECT_EQ(cfg, cfg2);
|
||||
}
|
||||
|
||||
{
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.seal();
|
||||
|
||||
utils::encryption::kdf_config cfg2;
|
||||
cfg2 = cfg;
|
||||
|
||||
EXPECT_EQ(cfg, cfg2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, sealed_is_not_equal_to_unsealed) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.seal();
|
||||
|
||||
utils::encryption::kdf_config cfg2;
|
||||
|
||||
EXPECT_NE(cfg, cfg2);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, sealed_is_not_equal_to_sealed) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.seal();
|
||||
|
||||
utils::encryption::kdf_config cfg2;
|
||||
cfg2.seal();
|
||||
|
||||
EXPECT_NE(cfg, cfg2);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, is_not_equal_to_different_id) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.unique_id = 2UL;
|
||||
|
||||
utils::encryption::kdf_config cfg2;
|
||||
|
||||
EXPECT_NE(cfg, cfg2);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, is_not_equal_to_different_version) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.version = static_cast<utils::encryption::kdf_version>(0x11);
|
||||
|
||||
utils::encryption::kdf_config cfg2;
|
||||
|
||||
EXPECT_NE(cfg, cfg2);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, is_not_equal_to_different_kdf) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.kdf = static_cast<utils::encryption::kdf_type>(0x11);
|
||||
|
||||
utils::encryption::kdf_config cfg2;
|
||||
|
||||
EXPECT_NE(cfg, cfg2);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, is_not_equal_to_different_memlimit) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.memlimit = static_cast<utils::encryption::memlimit_level>(0x11);
|
||||
|
||||
utils::encryption::kdf_config cfg2;
|
||||
|
||||
EXPECT_NE(cfg, cfg2);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, is_not_equal_to_different_opslimit) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.opslimit = static_cast<utils::encryption::opslimit_level>(0x11);
|
||||
|
||||
utils::encryption::kdf_config cfg2;
|
||||
|
||||
EXPECT_NE(cfg, cfg2);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, is_not_equal_to_different_salt) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.salt[0U] = 1U;
|
||||
|
||||
utils::encryption::kdf_config cfg2;
|
||||
|
||||
EXPECT_NE(cfg, cfg2);
|
||||
}
|
||||
|
||||
TEST(utils_encryption_kdf_config, is_not_equal_to_different_checksum) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
cfg.checksum = 2U;
|
||||
|
||||
utils::encryption::kdf_config cfg2;
|
||||
|
||||
EXPECT_NE(cfg, cfg2);
|
||||
}
|
||||
} // namespace fifthgrid
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
450
support/test/src/utils/encryption_read_encrypted_range_test.cpp
Normal file
450
support/test/src/utils/encryption_read_encrypted_range_test.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] auto make_random_plain(std::size_t size)
|
||||
-> std::vector<unsigned char> {
|
||||
std::vector<unsigned char> ret;
|
||||
ret.resize(size);
|
||||
|
||||
constexpr std::size_t chunk_size = 4096U;
|
||||
using buf_t = std::array<unsigned char, chunk_size>;
|
||||
|
||||
std::size_t written = 0U;
|
||||
while (written < size) {
|
||||
auto block = fifthgrid::utils::generate_secure_random<buf_t>();
|
||||
auto to_copy = std::min<std::size_t>(chunk_size, size - written);
|
||||
std::memcpy(ret.data() + written, block.data(), to_copy);
|
||||
written += to_copy;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto
|
||||
build_encrypted_blob(const std::vector<unsigned char> &plain,
|
||||
const fifthgrid::utils::hash::hash_256_t &key,
|
||||
bool with_kdf,
|
||||
fifthgrid::utils::encryption::kdf_config &kdf)
|
||||
-> std::pair<fifthgrid::data_buffer, std::uint64_t> {
|
||||
fifthgrid::data_buffer blob;
|
||||
|
||||
if (with_kdf) {
|
||||
auto hdr = kdf.to_header();
|
||||
blob.insert(blob.end(), hdr.begin(), hdr.end());
|
||||
}
|
||||
|
||||
auto data_chunk =
|
||||
fifthgrid::utils::encryption::encrypting_reader::get_data_chunk_size();
|
||||
std::size_t offset = 0U;
|
||||
|
||||
while (offset < plain.size()) {
|
||||
auto take = std::min<std::size_t>(data_chunk, plain.size() - offset);
|
||||
fifthgrid::data_buffer buffer;
|
||||
fifthgrid::utils::encryption::encrypt_data(key, plain.data() + offset, take,
|
||||
buffer);
|
||||
blob.insert(blob.end(), buffer.begin(), buffer.end());
|
||||
offset += take;
|
||||
}
|
||||
|
||||
return {std::move(blob), static_cast<std::uint64_t>(plain.size())};
|
||||
}
|
||||
|
||||
[[nodiscard]] auto make_reader(const fifthgrid::data_buffer &cipher_blob)
|
||||
-> fifthgrid::utils::encryption::reader_func_t {
|
||||
return [&cipher_blob](fifthgrid::data_buffer &out, std::uint64_t start,
|
||||
std::uint64_t end) -> bool {
|
||||
if (end < start) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (end >= static_cast<std::uint64_t>(cipher_blob.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto len = static_cast<std::size_t>(end - start + 1U);
|
||||
out.assign(
|
||||
std::next(cipher_blob.begin(), static_cast<std::ptrdiff_t>(start)),
|
||||
std::next(cipher_blob.begin(),
|
||||
static_cast<std::ptrdiff_t>(start + len)));
|
||||
return true;
|
||||
};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace fifthgrid {
|
||||
class utils_encryption_read_encrypted_range_fixture
|
||||
: public ::testing::Test,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
protected:
|
||||
bool uses_kdf{};
|
||||
utils::hash::hash_256_t key{};
|
||||
utils::encryption::kdf_config kdf{};
|
||||
std::size_t chunk{};
|
||||
std::size_t plain_sz{};
|
||||
std::vector<unsigned char> plain;
|
||||
data_buffer cipher_blob;
|
||||
std::uint64_t total_size{};
|
||||
utils::encryption::reader_func_t reader;
|
||||
|
||||
void SetUp() override {
|
||||
uses_kdf = GetParam();
|
||||
|
||||
key =
|
||||
uses_kdf
|
||||
? utils::encryption::generate_key<utils::hash::hash_256_t>("moose",
|
||||
kdf)
|
||||
: utils::encryption::generate_key<utils::hash::hash_256_t>("moose");
|
||||
chunk = utils::encryption::encrypting_reader::get_data_chunk_size();
|
||||
|
||||
plain_sz = (2U * chunk) + (chunk / 2U);
|
||||
|
||||
plain = make_random_plain(plain_sz);
|
||||
std::tie(cipher_blob, total_size) =
|
||||
build_encrypted_blob(plain, key, uses_kdf, kdf);
|
||||
reader = make_reader(cipher_blob);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture,
|
||||
within_chunk_data_buffer) {
|
||||
std::uint64_t end_cap = chunk ? static_cast<std::uint64_t>(chunk) - 1U : 0U;
|
||||
std::uint64_t begin = 123U;
|
||||
std::uint64_t end = 4567U;
|
||||
if (end > end_cap) {
|
||||
end = end_cap;
|
||||
}
|
||||
|
||||
if (end < begin) {
|
||||
begin = end;
|
||||
}
|
||||
|
||||
ASSERT_GE(end, begin);
|
||||
ASSERT_LT(end, plain_sz);
|
||||
|
||||
http_range range{begin, end};
|
||||
data_buffer out;
|
||||
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(range, key, uses_kdf,
|
||||
reader, total_size, out));
|
||||
|
||||
std::vector<unsigned char> want(
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(begin),
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(end) + 1U);
|
||||
EXPECT_EQ(out, want);
|
||||
}
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture,
|
||||
cross_chunk_boundary_data_buffer) {
|
||||
std::uint64_t begin = static_cast<std::uint64_t>(chunk) - 512U;
|
||||
std::uint64_t end = begin + 1024U - 1U;
|
||||
ASSERT_GE(end, begin);
|
||||
ASSERT_LT(end, plain_sz);
|
||||
|
||||
http_range range{begin, end};
|
||||
data_buffer out;
|
||||
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(range, key, uses_kdf,
|
||||
reader, total_size, out));
|
||||
|
||||
std::vector<unsigned char> want(
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(begin),
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(end) + 1U);
|
||||
EXPECT_EQ(out, want);
|
||||
}
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture,
|
||||
multi_chunk_span_data_buffer) {
|
||||
std::uint64_t begin = static_cast<std::uint64_t>(chunk) - 10U;
|
||||
std::uint64_t end = static_cast<std::uint64_t>(2U * chunk) + 19U;
|
||||
ASSERT_GE(end, begin);
|
||||
ASSERT_LT(end, plain_sz);
|
||||
|
||||
http_range range{begin, end};
|
||||
data_buffer out;
|
||||
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(range, key, uses_kdf,
|
||||
reader, total_size, out));
|
||||
|
||||
std::vector<unsigned char> want(
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(begin),
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(end) + 1U);
|
||||
EXPECT_EQ(out, want);
|
||||
}
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture,
|
||||
tail_of_file_data_buffer) {
|
||||
std::uint64_t begin = static_cast<std::uint64_t>(plain_sz) - 200U;
|
||||
std::uint64_t end = static_cast<std::uint64_t>(plain_sz) - 1U;
|
||||
ASSERT_GE(end, begin);
|
||||
|
||||
http_range range{begin, end};
|
||||
data_buffer out;
|
||||
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(range, key, uses_kdf,
|
||||
reader, total_size, out));
|
||||
|
||||
std::vector<unsigned char> want(
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(begin),
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(end) + 1U);
|
||||
EXPECT_EQ(out, want);
|
||||
}
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture, whole_file_data_buffer) {
|
||||
std::uint64_t begin = 0U;
|
||||
std::uint64_t end = static_cast<std::uint64_t>(plain_sz - 1U);
|
||||
ASSERT_GE(end, begin);
|
||||
|
||||
http_range range{begin, end};
|
||||
data_buffer out;
|
||||
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(range, key, uses_kdf,
|
||||
reader, total_size, out));
|
||||
EXPECT_EQ(out, plain);
|
||||
}
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture,
|
||||
pointer_sink_cross_chunk_with_array) {
|
||||
std::uint64_t begin = static_cast<std::uint64_t>(chunk) - 256U;
|
||||
constexpr std::size_t data_len = 2048U;
|
||||
std::uint64_t end = begin + data_len - 1U;
|
||||
ASSERT_GE(end, begin);
|
||||
ASSERT_LT(end, plain_sz);
|
||||
|
||||
http_range range{begin, end};
|
||||
|
||||
std::array<unsigned char, data_len> sink{};
|
||||
std::size_t bytes_read = 0U;
|
||||
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(
|
||||
range, key, uses_kdf, reader, total_size, sink.data(), sink.size(),
|
||||
bytes_read));
|
||||
EXPECT_EQ(bytes_read, sink.size());
|
||||
|
||||
std::vector<unsigned char> want(
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(begin),
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(end) + 1U);
|
||||
EXPECT_TRUE(std::equal(sink.begin(), sink.end(), want.begin(), want.end()));
|
||||
}
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture,
|
||||
reader_failure_for_both_overloads) {
|
||||
std::size_t call_count = 0U;
|
||||
auto flaky_reader = [this, &call_count](data_buffer &out, std::uint64_t start,
|
||||
std::uint64_t end) -> bool {
|
||||
if (++call_count == 1U) {
|
||||
return false;
|
||||
}
|
||||
auto len = static_cast<std::size_t>(end - start + 1U);
|
||||
out.assign(
|
||||
std::next(cipher_blob.begin(), static_cast<std::ptrdiff_t>(start)),
|
||||
std::next(cipher_blob.begin(),
|
||||
static_cast<std::ptrdiff_t>(start + len)));
|
||||
return true;
|
||||
};
|
||||
|
||||
std::uint64_t begin = 0U;
|
||||
constexpr std::size_t data_len = 1024U;
|
||||
std::uint64_t end = begin + data_len - 1U;
|
||||
http_range range{begin, end};
|
||||
|
||||
{
|
||||
data_buffer out;
|
||||
EXPECT_FALSE(utils::encryption::read_encrypted_range(
|
||||
range, key, uses_kdf, flaky_reader, total_size, out));
|
||||
EXPECT_TRUE(out.empty());
|
||||
}
|
||||
|
||||
call_count = 0U;
|
||||
{
|
||||
std::array<unsigned char, data_len> buf{};
|
||||
std::size_t bytes_read = 0U;
|
||||
EXPECT_FALSE(utils::encryption::read_encrypted_range(
|
||||
range, key, uses_kdf, flaky_reader, total_size, buf.data(), buf.size(),
|
||||
bytes_read));
|
||||
EXPECT_EQ(bytes_read, 0U);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture,
|
||||
invalid_range_end_before_begin) {
|
||||
std::uint64_t begin = 100U;
|
||||
std::uint64_t end = 99U;
|
||||
http_range range{begin, end};
|
||||
|
||||
{
|
||||
data_buffer out;
|
||||
EXPECT_TRUE(utils::encryption::read_encrypted_range(
|
||||
range, key, uses_kdf, reader, total_size, out));
|
||||
EXPECT_TRUE(out.empty());
|
||||
}
|
||||
|
||||
{
|
||||
std::array<unsigned char, 16U> buf{};
|
||||
std::size_t bytes_read = 0U;
|
||||
EXPECT_TRUE(utils::encryption::read_encrypted_range(
|
||||
range, key, uses_kdf, reader, total_size, buf.data(), buf.size(),
|
||||
bytes_read));
|
||||
EXPECT_EQ(bytes_read, 0U);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture, single_byte_read) {
|
||||
std::uint64_t pos = 777U;
|
||||
if (pos >= plain_sz) {
|
||||
pos = plain_sz ? static_cast<std::uint64_t>(plain_sz) - 1U : 0U;
|
||||
}
|
||||
|
||||
http_range range{pos, pos};
|
||||
data_buffer out;
|
||||
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(range, key, uses_kdf,
|
||||
reader, total_size, out));
|
||||
ASSERT_EQ(out.size(), 1U);
|
||||
EXPECT_EQ(out[0], plain[pos]);
|
||||
|
||||
std::array<unsigned char, 1U> buf{};
|
||||
std::size_t bytes_read = 0U;
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(
|
||||
range, key, uses_kdf, reader, total_size, buf.data(), buf.size(),
|
||||
bytes_read));
|
||||
EXPECT_EQ(bytes_read, 1U);
|
||||
EXPECT_EQ(buf[0], plain[pos]);
|
||||
}
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture,
|
||||
begin_at_exact_chunk_boundary) {
|
||||
|
||||
auto begin = static_cast<std::uint64_t>(chunk);
|
||||
std::uint64_t end = begin + 1024U - 1U;
|
||||
if (end >= plain_sz)
|
||||
end = static_cast<std::uint64_t>(plain_sz) - 1U;
|
||||
ASSERT_GE(end, begin);
|
||||
|
||||
http_range range{begin, end};
|
||||
data_buffer out;
|
||||
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(range, key, uses_kdf,
|
||||
reader, total_size, out));
|
||||
std::vector<unsigned char> want(
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(begin),
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(end) + 1U);
|
||||
EXPECT_EQ(out, want);
|
||||
}
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture, last_byte_only) {
|
||||
std::uint64_t pos = plain_sz ? static_cast<std::uint64_t>(plain_sz) - 1U : 0U;
|
||||
http_range range{pos, pos};
|
||||
data_buffer out;
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(range, key, uses_kdf,
|
||||
reader, total_size, out));
|
||||
ASSERT_EQ(out.size(), 1U);
|
||||
EXPECT_EQ(out[0], plain[pos]);
|
||||
}
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture, tiny_file_whole_read) {
|
||||
plain = make_random_plain(37U);
|
||||
std::tie(cipher_blob, total_size) =
|
||||
build_encrypted_blob(plain, key, uses_kdf, kdf);
|
||||
reader = make_reader(cipher_blob);
|
||||
|
||||
http_range range{0U, static_cast<std::uint64_t>(plain.size() - 1U)};
|
||||
data_buffer out;
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(range, key, uses_kdf,
|
||||
reader, total_size, out));
|
||||
EXPECT_EQ(out, plain);
|
||||
}
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture,
|
||||
pointer_sink_exact_small_window) {
|
||||
std::uint64_t begin = 5U;
|
||||
std::uint64_t end = begin + 7U;
|
||||
ASSERT_GE(end, begin);
|
||||
ASSERT_LT(end, plain_sz);
|
||||
http_range range{begin, end};
|
||||
|
||||
std::array<unsigned char, 8U> sink{};
|
||||
std::size_t bytes_read = 0U;
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(
|
||||
range, key, uses_kdf, reader, total_size, sink.data(), sink.size(),
|
||||
bytes_read));
|
||||
EXPECT_EQ(bytes_read, sink.size());
|
||||
EXPECT_TRUE(std::equal(sink.begin(), sink.end(),
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(begin)));
|
||||
}
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture,
|
||||
range_past_eof_truncates) {
|
||||
std::uint64_t begin = static_cast<std::uint64_t>(plain_sz) - 10U;
|
||||
std::uint64_t end = static_cast<std::uint64_t>(plain_sz);
|
||||
http_range range{begin, end};
|
||||
|
||||
data_buffer out;
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(range, key, uses_kdf,
|
||||
reader, total_size, out));
|
||||
|
||||
std::size_t expected_len =
|
||||
static_cast<std::size_t>(static_cast<std::uint64_t>(plain_sz) - begin);
|
||||
std::vector<unsigned char> want(
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(begin),
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(plain_sz));
|
||||
ASSERT_EQ(out.size(), expected_len);
|
||||
EXPECT_EQ(out, want);
|
||||
|
||||
std::array<unsigned char, 32U> buf{};
|
||||
std::size_t bytes_read = 0U;
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(
|
||||
range, key, uses_kdf, reader, total_size, buf.data(), buf.size(),
|
||||
bytes_read));
|
||||
EXPECT_EQ(bytes_read, std::min<std::size_t>(buf.size(), expected_len));
|
||||
EXPECT_TRUE(std::equal(buf.begin(), buf.begin() + bytes_read,
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(begin)));
|
||||
}
|
||||
|
||||
TEST_P(utils_encryption_read_encrypted_range_fixture,
|
||||
pointer_sink_larger_buffer) {
|
||||
std::uint64_t begin = 42U;
|
||||
std::uint64_t end = begin + 63U;
|
||||
ASSERT_GE(end, begin);
|
||||
ASSERT_LT(end, plain_sz);
|
||||
http_range range{begin, end};
|
||||
|
||||
std::array<unsigned char, 128U> buf{};
|
||||
std::size_t bytes_read = 0U;
|
||||
|
||||
ASSERT_TRUE(utils::encryption::read_encrypted_range(
|
||||
range, key, uses_kdf, reader, total_size, buf.data(), buf.size(),
|
||||
bytes_read));
|
||||
EXPECT_EQ(bytes_read, 64U);
|
||||
EXPECT_TRUE(std::equal(buf.begin(), buf.begin() + 64U,
|
||||
plain.begin() + static_cast<std::ptrdiff_t>(begin)));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(no_kdf_and_kdf,
|
||||
utils_encryption_read_encrypted_range_fixture,
|
||||
::testing::Values(false, true));
|
||||
} // namespace fifthgrid
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
385
support/test/src/utils/encryption_test.cpp
Normal file
385
support/test/src/utils/encryption_test.cpp
Normal file
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
namespace {
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
const std::string buffer = "cow moose dog chicken";
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
const auto get_stop_requested = []() -> bool { return false; };
|
||||
} // namespace
|
||||
|
||||
namespace fifthgrid {
|
||||
static constexpr std::string_view token{"moose"};
|
||||
static constexpr std::wstring_view token_w{L"moose"};
|
||||
|
||||
TEST(utils_encryption, generate_key) {
|
||||
auto key1 = utils::encryption::generate_key<utils::hash::hash_256_t>(token);
|
||||
EXPECT_STREQ(
|
||||
"ab4a0b004e824962913f7c0f79582b6ec7a3b8726426ca61d1a0a28ce5049e96",
|
||||
utils::collection::to_hex_string(key1).c_str());
|
||||
|
||||
auto key2 = utils::encryption::generate_key<utils::hash::hash_256_t>("moose");
|
||||
auto key3 = utils::encryption::generate_key<utils::hash::hash_256_t>("moose");
|
||||
EXPECT_EQ(key2, key3);
|
||||
|
||||
auto key4 =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>("moose2");
|
||||
EXPECT_NE(key2, key4);
|
||||
|
||||
auto key1_w =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token_w);
|
||||
EXPECT_NE(key1, key1_w);
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
L"4f5eb2a2ab34e3777b230465283923080b9ba59311e74058ccd74185131d11fe",
|
||||
utils::collection::to_hex_wstring(key1_w).c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
L"0392d95ed3eee9772fbb9af68fedf829a8eb0adbe8575d9691cc9a752196766a",
|
||||
utils::collection::to_hex_wstring(key1_w).c_str());
|
||||
#endif
|
||||
|
||||
auto key2_w =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(L"moose");
|
||||
auto key3_w =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(L"moose");
|
||||
EXPECT_EQ(key2_w, key3_w);
|
||||
EXPECT_NE(key2_w, key2);
|
||||
EXPECT_NE(key3_w, key3);
|
||||
|
||||
auto key4_w =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(L"moose2");
|
||||
EXPECT_NE(key2_w, key4_w);
|
||||
EXPECT_NE(key4_w, key4);
|
||||
}
|
||||
|
||||
TEST(utils_encryption, generate_key_default_hasher_is_blake2b_256) {
|
||||
auto key1 = utils::encryption::generate_key<utils::hash::hash_256_t>(token);
|
||||
auto key2 = utils::encryption::generate_key<utils::hash::hash_256_t>(
|
||||
token, [](auto &&data, auto &&size) -> auto {
|
||||
return utils::hash::create_hash_blake2b_256(
|
||||
std::string_view(reinterpret_cast<const char *>(data), size));
|
||||
});
|
||||
EXPECT_EQ(key1, key2);
|
||||
|
||||
auto key1_w =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token_w);
|
||||
auto key2_w = utils::encryption::generate_key<utils::hash::hash_256_t>(
|
||||
token_w, [](auto &&data, auto &&size) -> auto {
|
||||
return utils::hash::create_hash_blake2b_256(std::wstring_view(
|
||||
reinterpret_cast<const wchar_t *>(data), size / sizeof(wchar_t)));
|
||||
});
|
||||
EXPECT_EQ(key1_w, key2_w);
|
||||
|
||||
EXPECT_NE(key1_w, key1);
|
||||
EXPECT_NE(key2_w, key2);
|
||||
}
|
||||
|
||||
TEST(utils_encryption, generate_key_with_hasher) {
|
||||
auto key1 = utils::encryption::generate_key<utils::hash::hash_256_t>(
|
||||
token, utils::hash::blake2b_256_hasher);
|
||||
EXPECT_STREQ(
|
||||
"ab4a0b004e824962913f7c0f79582b6ec7a3b8726426ca61d1a0a28ce5049e96",
|
||||
utils::collection::to_hex_string(key1).c_str());
|
||||
|
||||
auto key2 = utils::encryption::generate_key<utils::hash::hash_256_t>(
|
||||
token, utils::hash::sha256_hasher);
|
||||
EXPECT_NE(key1, key2);
|
||||
|
||||
EXPECT_STREQ(
|
||||
"182072537ada59e4d6b18034a80302ebae935f66adbdf0f271d3d36309c2d481",
|
||||
utils::collection::to_hex_string(key2).c_str());
|
||||
|
||||
auto key1_w = utils::encryption::generate_key<utils::hash::hash_256_t>(
|
||||
token_w, utils::hash::blake2b_256_hasher);
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
L"4f5eb2a2ab34e3777b230465283923080b9ba59311e74058ccd74185131d11fe",
|
||||
utils::collection::to_hex_wstring(key1_w).c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
L"0392d95ed3eee9772fbb9af68fedf829a8eb0adbe8575d9691cc9a752196766a",
|
||||
utils::collection::to_hex_wstring(key1_w).c_str());
|
||||
#endif
|
||||
|
||||
auto key2_w = utils::encryption::generate_key<utils::hash::hash_256_t>(
|
||||
token_w, utils::hash::sha256_hasher);
|
||||
EXPECT_NE(key1_w, key2_w);
|
||||
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
L"918e4c6d39bb373f139b5fac8ec0548a9770da399b2835608974ffeac7fab6c4",
|
||||
utils::collection::to_hex_wstring(key2_w).c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
L"590ac70125bec4501172937f6a2cbdeb22a87b5e40d5595eccd06b2b20548d8f",
|
||||
utils::collection::to_hex_wstring(key2_w).c_str());
|
||||
#endif
|
||||
|
||||
EXPECT_NE(key1_w, key1);
|
||||
EXPECT_NE(key2_w, key2);
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
TEST(utils_encryption, generate_argon2id_key) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
|
||||
{
|
||||
auto key1 =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg);
|
||||
|
||||
auto key2 =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg);
|
||||
EXPECT_NE(key1, key2);
|
||||
|
||||
auto key3 =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg);
|
||||
EXPECT_NE(key3, key1);
|
||||
|
||||
auto key4 =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg);
|
||||
EXPECT_NE(key4, key2);
|
||||
|
||||
EXPECT_NE(key3, key4);
|
||||
}
|
||||
|
||||
{
|
||||
auto key1 =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token_w, cfg);
|
||||
|
||||
auto key2 =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token_w, cfg);
|
||||
EXPECT_NE(key1, key2);
|
||||
|
||||
auto key3 =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token_w, cfg);
|
||||
EXPECT_NE(key3, key1);
|
||||
|
||||
auto key4 =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token_w, cfg);
|
||||
EXPECT_NE(key4, key2);
|
||||
|
||||
EXPECT_NE(key3, key4);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encryption, recreate_argon2id_key) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
|
||||
{
|
||||
auto key1 =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg);
|
||||
|
||||
auto key2 =
|
||||
utils::encryption::recreate_key<utils::hash::hash_256_t>(token, cfg);
|
||||
EXPECT_EQ(key1, key2);
|
||||
}
|
||||
|
||||
{
|
||||
auto key1 =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token_w, cfg);
|
||||
|
||||
auto key2 =
|
||||
utils::encryption::recreate_key<utils::hash::hash_256_t>(token_w, cfg);
|
||||
EXPECT_EQ(key1, key2);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_encrypted_result(const data_buffer &result) {
|
||||
EXPECT_EQ(buffer.size() + utils::encryption::encryption_header_size,
|
||||
result.size());
|
||||
std::string data;
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(token, result, data));
|
||||
EXPECT_EQ(buffer.size(), data.size());
|
||||
EXPECT_STREQ(buffer.c_str(), data.c_str());
|
||||
}
|
||||
|
||||
static void
|
||||
test_encrypted_result_using_argon2id(const data_buffer &result,
|
||||
const utils::encryption::kdf_config &cfg) {
|
||||
EXPECT_EQ(buffer.size() + utils::encryption::encryption_header_size,
|
||||
result.size());
|
||||
std::string data;
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(token, cfg, result, data));
|
||||
EXPECT_EQ(buffer.size(), data.size());
|
||||
EXPECT_STREQ(buffer.c_str(), data.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_encryption, encrypt_data_buffer) {
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(token, buffer, result);
|
||||
test_encrypted_result(result);
|
||||
}
|
||||
|
||||
TEST(utils_encryption, encrypt_data_buffer_with_key) {
|
||||
const auto key =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token);
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(key, buffer, result);
|
||||
test_encrypted_result(result);
|
||||
}
|
||||
|
||||
TEST(utils_encryption, encrypt_data_pointer) {
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
token, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
buffer.size(), result);
|
||||
test_encrypted_result(result);
|
||||
}
|
||||
|
||||
TEST(utils_encryption, encrypt_data_pointer_with_key) {
|
||||
const auto key =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token);
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
key, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
buffer.size(), result);
|
||||
test_encrypted_result(result);
|
||||
}
|
||||
|
||||
TEST(utils_encryption, decrypt_data_pointer) {
|
||||
const auto key =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token);
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
key, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
buffer.size(), result);
|
||||
|
||||
std::string data;
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(token, result.data(),
|
||||
result.size(), data));
|
||||
|
||||
EXPECT_EQ(buffer.size(), data.size());
|
||||
EXPECT_STREQ(buffer.c_str(), data.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_encryption, decrypt_data_buffer_with_key) {
|
||||
const auto key =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token);
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
key, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
buffer.size(), result);
|
||||
|
||||
std::string data;
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(key, result, data));
|
||||
|
||||
EXPECT_EQ(buffer.size(), data.size());
|
||||
EXPECT_STREQ(buffer.c_str(), data.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_encryption, decrypt_data_pointer_with_key) {
|
||||
const auto key =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token);
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
key, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
buffer.size(), result);
|
||||
|
||||
std::string data;
|
||||
EXPECT_TRUE(
|
||||
utils::encryption::decrypt_data(key, result.data(), result.size(), data));
|
||||
|
||||
EXPECT_EQ(buffer.size(), data.size());
|
||||
EXPECT_STREQ(buffer.c_str(), data.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_encryption, decryption_failure) {
|
||||
const auto key =
|
||||
utils::encryption::generate_key<utils::hash::hash_256_t>(token);
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
key, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
buffer.size(), result);
|
||||
result[0U] = 0U;
|
||||
result[1U] = 1U;
|
||||
result[2U] = 2U;
|
||||
|
||||
std::string data;
|
||||
EXPECT_FALSE(utils::encryption::decrypt_data(key, result, data));
|
||||
}
|
||||
|
||||
TEST(utils_encryption, decrypt_file_name) {
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token,
|
||||
std::nullopt);
|
||||
|
||||
auto file_name = reader.get_encrypted_file_name();
|
||||
|
||||
EXPECT_EQ(true, utils::encryption::decrypt_file_name(token, file_name));
|
||||
EXPECT_STREQ("test.dat", file_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encryption, decrypt_file_path) {
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token,
|
||||
"moose/cow");
|
||||
|
||||
auto file_path = reader.get_encrypted_file_path();
|
||||
|
||||
EXPECT_EQ(true, utils::encryption::decrypt_file_path(token, file_path));
|
||||
EXPECT_STREQ("/moose/cow/test.dat", file_path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encryption, encrypt_data_buffer_using_argon2id) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(token, cfg, buffer, result);
|
||||
test_encrypted_result_using_argon2id(result, cfg);
|
||||
}
|
||||
|
||||
TEST(utils_encryption, encrypt_data_pointer_using_argon2id) {
|
||||
utils::encryption::kdf_config cfg;
|
||||
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
token, cfg, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
buffer.size(), result);
|
||||
test_encrypted_result_using_argon2id(result, cfg);
|
||||
}
|
||||
|
||||
// TEST(utils_encryption, decrypt_file_name_using_argon2id) {}
|
||||
|
||||
// TEST(utils_encryption, decrypt_file_path_using_argon2id) {}
|
||||
//
|
||||
// TEST(utils_encryption, decrypt_file_name_using_argon2id_master_key) {}
|
||||
|
||||
// TEST(utils_encryption, decrypt_file_path_using_argon2id_master_key) {}
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
} // namespace fifthgrid
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
116
support/test/src/utils/error_test.cpp
Normal file
116
support/test/src/utils/error_test.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
namespace fifthgrid {
|
||||
template <typename T, typename U>
|
||||
constexpr bool is_decay_equ = std::is_same_v<std::decay_t<T>, U>;
|
||||
|
||||
TEST(utils_error, check_default_exception_handler) {
|
||||
EXPECT_TRUE(utils::error::get_exception_handler() != nullptr);
|
||||
if (&utils::error::default_exception_handler ==
|
||||
utils::error::get_exception_handler()) {
|
||||
#if defined(PROJECT_ENABLE_SPDLOG) && defined(PROJECT_ENABLE_V2_ERRORS)
|
||||
auto default_handler_is_spdlog =
|
||||
is_decay_equ<decltype(utils::error::default_exception_handler),
|
||||
utils::error::spdlog_exception_handler>;
|
||||
EXPECT_TRUE(default_handler_is_spdlog);
|
||||
#else // !defined(PROJECT_ENABLE_SPDLOG) || !defined(PROJECT_ENABLE_V2_ERRORS)
|
||||
auto default_handler_is_iostream =
|
||||
is_decay_equ<decltype(utils::error::default_exception_handler),
|
||||
utils::error::iostream_exception_handler>;
|
||||
EXPECT_TRUE(default_handler_is_iostream);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_error, can_override_exception_handler) {
|
||||
struct my_exc_handler final : public utils::error::i_exception_handler {
|
||||
#if defined(PROJECT_ENABLE_V2_ERRORS)
|
||||
MOCK_METHOD(void, handle_debug,
|
||||
(std::string_view function_name, std::string_view msg),
|
||||
(const, override));
|
||||
#endif // defined(PROJECT_ENABLE_V2_ERRORS)
|
||||
|
||||
MOCK_METHOD(void, handle_error,
|
||||
(std::string_view function_name, std::string_view msg),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(void, handle_exception, (std::string_view function_name),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(void, handle_exception,
|
||||
(std::string_view function_name, const std::exception &ex),
|
||||
(const, override));
|
||||
|
||||
#if defined(PROJECT_ENABLE_V2_ERRORS)
|
||||
MOCK_METHOD(void, handle_info,
|
||||
(std::string_view function_name, std::string_view msg),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(void, handle_trace,
|
||||
(std::string_view function_name, std::string_view msg),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(void, handle_warn,
|
||||
(std::string_view function_name, std::string_view msg),
|
||||
(const, override));
|
||||
#endif // defined(PROJECT_ENABLE_V2_ERRORS)
|
||||
};
|
||||
|
||||
my_exc_handler handler{};
|
||||
utils::error::set_exception_handler(&handler);
|
||||
|
||||
#if defined(PROJECT_ENABLE_V2_ERRORS)
|
||||
EXPECT_CALL(handler, handle_debug("test_func", "debug")).WillOnce(Return());
|
||||
utils::error::handle_debug("test_func", "debug");
|
||||
#endif // defined(PROJECT_ENABLE_V2_ERRORS)
|
||||
|
||||
EXPECT_CALL(handler, handle_error("test_func", "error")).WillOnce(Return());
|
||||
utils::error::handle_error("test_func", "error");
|
||||
|
||||
EXPECT_CALL(handler, handle_exception("test_func")).WillOnce(Return());
|
||||
utils::error::handle_exception("test_func");
|
||||
|
||||
auto ex = std::runtime_error("moose");
|
||||
EXPECT_CALL(handler, handle_exception(_, _))
|
||||
.WillOnce(
|
||||
[&ex](std::string_view function_name, const std::exception &ex2) {
|
||||
EXPECT_EQ("test_func_ex", function_name);
|
||||
EXPECT_STREQ(ex.what(), ex2.what());
|
||||
});
|
||||
utils::error::handle_exception("test_func_ex", ex);
|
||||
|
||||
#if defined(PROJECT_ENABLE_V2_ERRORS)
|
||||
EXPECT_CALL(handler, handle_info("test_func", "info")).WillOnce(Return());
|
||||
utils::error::handle_info("test_func", "info");
|
||||
|
||||
EXPECT_CALL(handler, handle_trace("test_func", "trace")).WillOnce(Return());
|
||||
utils::error::handle_trace("test_func", "trace");
|
||||
|
||||
EXPECT_CALL(handler, handle_warn("test_func", "warn")).WillOnce(Return());
|
||||
utils::error::handle_warn("test_func", "warn");
|
||||
#endif // defined(PROJECT_ENABLE_V2_ERRORS)
|
||||
|
||||
utils::error::set_exception_handler(&utils::error::default_exception_handler);
|
||||
}
|
||||
} // namespace fifthgrid
|
||||
603
support/test/src/utils/file_test.cpp
Normal file
603
support/test/src/utils/file_test.cpp
Normal file
@@ -0,0 +1,603 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
namespace {
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
#include "utils/file_enc_file.hpp"
|
||||
constexpr auto file_type_count{3U};
|
||||
#else
|
||||
constexpr auto file_type_count{2U};
|
||||
#endif
|
||||
|
||||
[[nodiscard]] auto create_file(auto idx, auto path, bool read_only = false)
|
||||
-> auto {
|
||||
switch (idx) {
|
||||
case 0U:
|
||||
return fifthgrid::utils::file::file::open_or_create_file(path, read_only);
|
||||
case 1U:
|
||||
return fifthgrid::utils::file::thread_file::open_or_create_file(path,
|
||||
read_only);
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
case 2U:
|
||||
return fifthgrid::utils::file::enc_file::attach_file(
|
||||
fifthgrid::utils::file::file::open_or_create_file(path, read_only));
|
||||
#endif
|
||||
default:
|
||||
throw std::runtime_error("not supported");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto open_file(auto idx, auto path, bool read_only = false)
|
||||
-> auto {
|
||||
switch (idx) {
|
||||
case 0U:
|
||||
return fifthgrid::utils::file::file::open_file(path, read_only);
|
||||
case 1U:
|
||||
return fifthgrid::utils::file::thread_file::open_file(path, read_only);
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
case 2U:
|
||||
return fifthgrid::utils::file::enc_file::attach_file(
|
||||
fifthgrid::utils::file::file::open_file(path, read_only));
|
||||
#endif
|
||||
default:
|
||||
throw std::runtime_error("not supported");
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace fifthgrid {
|
||||
TEST(utils_file, can_create_and_remove_file) {
|
||||
for (auto idx = 0U; idx < file_type_count; ++idx) {
|
||||
auto path = test::generate_test_file_name("utils_file");
|
||||
EXPECT_FALSE(utils::file::file(path).exists() ||
|
||||
utils::file::directory(path).exists());
|
||||
|
||||
auto file{create_file(idx, path)};
|
||||
EXPECT_TRUE(*file);
|
||||
|
||||
EXPECT_TRUE(utils::file::file(path).exists());
|
||||
EXPECT_TRUE(file->exists());
|
||||
|
||||
EXPECT_TRUE(file->remove());
|
||||
|
||||
EXPECT_FALSE(utils::file::file(path).exists());
|
||||
EXPECT_FALSE(file->exists());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, can_open_file) {
|
||||
for (auto idx = 0U; idx < file_type_count; ++idx) {
|
||||
auto path = test::generate_test_file_name("utils_file");
|
||||
|
||||
{
|
||||
auto file{create_file(idx, path)};
|
||||
EXPECT_TRUE(*file);
|
||||
}
|
||||
|
||||
{
|
||||
auto file{create_file(idx, path)};
|
||||
EXPECT_TRUE(*file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, open_file_fails_if_not_found) {
|
||||
for (auto idx = 0U; idx < file_type_count; ++idx) {
|
||||
auto path = test::generate_test_file_name("utils_file");
|
||||
|
||||
auto file{open_file(idx, path)};
|
||||
EXPECT_FALSE(*file);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, write_fails_for_read_only_file) {
|
||||
for (auto idx = 0U; idx < file_type_count; ++idx) {
|
||||
auto path = test::generate_test_file_name("utils_file");
|
||||
|
||||
auto file{create_file(idx, path, true)};
|
||||
EXPECT_TRUE(utils::file::file(path).exists());
|
||||
EXPECT_TRUE(*file);
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_FALSE(file->write(reinterpret_cast<const unsigned char *>("0"), 1U,
|
||||
0U, &bytes_written));
|
||||
EXPECT_EQ(0U, bytes_written);
|
||||
}
|
||||
}
|
||||
|
||||
// TEST(utils_file, can_attach_file) {
|
||||
// for (auto idx = 0U; idx < file_type_count; ++idx) {
|
||||
// auto path = test::generate_test_file_name("utils_file");
|
||||
// auto file = idx == 0U ? utils::file::file::open_or_create_file(path)
|
||||
// :
|
||||
// utils::file::thread_file::open_or_create_file(path);
|
||||
// auto file2 =
|
||||
// idx == 0U ? utils::file::file::attach_file(file->get_handle())
|
||||
// :
|
||||
// utils::file::thread_file::attach_file(file->get_handle());
|
||||
// EXPECT_TRUE(*file);
|
||||
// EXPECT_TRUE(*file2);
|
||||
// EXPECT_EQ(file->get_path(), file2->get_path());
|
||||
// }
|
||||
// }
|
||||
|
||||
#if defined(PROJECT_ENABLE_JSON)
|
||||
TEST(utils_file, read_and_write_json_file) {
|
||||
auto path = test::generate_test_file_name("utils_file");
|
||||
|
||||
auto json_data = nlohmann::json({{"moose", "cow"}});
|
||||
EXPECT_TRUE(utils::file::write_json_file(path, json_data));
|
||||
|
||||
{
|
||||
nlohmann::json result_data{};
|
||||
EXPECT_TRUE(utils::file::read_json_file(path, result_data));
|
||||
EXPECT_STREQ(json_data.dump().c_str(), result_data.dump().c_str());
|
||||
}
|
||||
|
||||
{
|
||||
nlohmann::json result_data{};
|
||||
EXPECT_TRUE(utils::file::read_json_file(utils::string::from_utf8(path),
|
||||
result_data));
|
||||
EXPECT_STREQ(json_data.dump().c_str(), result_data.dump().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
TEST(utils_file, read_and_write_json_file_encrypted) {
|
||||
{
|
||||
auto path = test::generate_test_file_name("utils_file");
|
||||
|
||||
auto json_data = nlohmann::json({{"moose", "cow"}});
|
||||
EXPECT_TRUE(utils::file::write_json_file(path, json_data, "moose"));
|
||||
|
||||
nlohmann::json result_data{};
|
||||
EXPECT_TRUE(utils::file::read_json_file(path, result_data, "moose"));
|
||||
EXPECT_STREQ(json_data.dump().c_str(), result_data.dump().c_str());
|
||||
|
||||
{
|
||||
auto file = utils::file::file::open_file(path);
|
||||
data_buffer encrypted_data{};
|
||||
EXPECT_TRUE(file->read_all(encrypted_data, 0U));
|
||||
|
||||
data_buffer decrypted_data{};
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data("moose", encrypted_data,
|
||||
decrypted_data));
|
||||
EXPECT_STREQ(json_data.dump().c_str(),
|
||||
nlohmann::json::parse(std::string(decrypted_data.begin(),
|
||||
decrypted_data.end()))
|
||||
.dump()
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto path =
|
||||
utils::string::from_utf8(test::generate_test_file_name("utils_file"));
|
||||
|
||||
auto json_data = nlohmann::json({{"moose", "cow"}});
|
||||
EXPECT_TRUE(utils::file::write_json_file(path, json_data, L"moose"));
|
||||
|
||||
nlohmann::json result_data{};
|
||||
EXPECT_TRUE(utils::file::read_json_file(path, result_data, L"moose"));
|
||||
EXPECT_STREQ(json_data.dump().c_str(), result_data.dump().c_str());
|
||||
|
||||
{
|
||||
auto file = utils::file::file::open_file(path);
|
||||
data_buffer encrypted_data{};
|
||||
EXPECT_TRUE(file->read_all(encrypted_data, 0U));
|
||||
|
||||
data_buffer decrypted_data{};
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data("moose", encrypted_data,
|
||||
decrypted_data));
|
||||
EXPECT_STREQ(json_data.dump().c_str(),
|
||||
nlohmann::json::parse(std::string(decrypted_data.begin(),
|
||||
decrypted_data.end()))
|
||||
.dump()
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
#endif // defined(PROJECT_ENABLE_JSON)
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBDSM)
|
||||
TEST(utils_file, smb_create_smb_path) {
|
||||
const auto *path = "//server/share";
|
||||
const auto *rel_path = "test/test.txt";
|
||||
auto smb_path = utils::file::smb_create_smb_path(path, rel_path);
|
||||
EXPECT_STREQ("//server/share/test/test.txt", smb_path.c_str());
|
||||
|
||||
rel_path = "/test/test.txt";
|
||||
smb_path = utils::file::smb_create_smb_path(path, rel_path);
|
||||
EXPECT_STREQ("//server/share/test/test.txt", smb_path.c_str());
|
||||
|
||||
rel_path = "test\\test.txt";
|
||||
smb_path = utils::file::smb_create_smb_path(path, rel_path);
|
||||
EXPECT_STREQ("//server/share/test/test.txt", smb_path.c_str());
|
||||
|
||||
rel_path = "\\test\\test.txt";
|
||||
smb_path = utils::file::smb_create_smb_path(path, rel_path);
|
||||
EXPECT_STREQ("//server/share/test/test.txt", smb_path.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_file, smb_create_relative_path) {
|
||||
const auto *path = "//server/share/test.txt";
|
||||
auto rel_path = utils::file::smb_create_relative_path(path);
|
||||
EXPECT_STREQ("\\test.txt", rel_path.c_str());
|
||||
|
||||
path = "//server/share/test";
|
||||
rel_path = utils::file::smb_create_relative_path(path);
|
||||
EXPECT_STREQ("\\test", rel_path.c_str());
|
||||
|
||||
path = "//server/share/test/";
|
||||
rel_path = utils::file::smb_create_relative_path(path);
|
||||
EXPECT_STREQ("\\test", rel_path.c_str());
|
||||
|
||||
path = "//server/share/test/";
|
||||
rel_path = utils::file::smb_create_relative_path(path);
|
||||
EXPECT_STREQ("\\test", rel_path.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_file, smb_create_search_path) {
|
||||
const auto *path = "//server/share";
|
||||
auto search_path = utils::file::smb_create_search_path(path);
|
||||
EXPECT_STREQ("\\*", search_path.c_str());
|
||||
|
||||
path = "//server/share/";
|
||||
search_path = utils::file::smb_create_search_path(path);
|
||||
EXPECT_STREQ("\\*", search_path.c_str());
|
||||
|
||||
path = "//server/share/folder";
|
||||
search_path = utils::file::smb_create_search_path(path);
|
||||
EXPECT_STREQ("\\folder\\*", search_path.c_str());
|
||||
|
||||
path = "//server/share/folder/";
|
||||
search_path = utils::file::smb_create_search_path(path);
|
||||
EXPECT_STREQ("\\folder\\*", search_path.c_str());
|
||||
|
||||
path = "//server/share/folder/next";
|
||||
search_path = utils::file::smb_create_search_path(path);
|
||||
EXPECT_STREQ("\\folder\\next\\*", search_path.c_str());
|
||||
|
||||
path = "//server/share/folder/next/";
|
||||
search_path = utils::file::smb_create_search_path(path);
|
||||
EXPECT_STREQ("\\folder\\next\\*", search_path.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_file, smb_parent_is_same) {
|
||||
const auto *path1 = "//server/share";
|
||||
const auto *path2 = "//server/share";
|
||||
EXPECT_TRUE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server/share/";
|
||||
path2 = "//server/share/";
|
||||
EXPECT_TRUE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server/share/one";
|
||||
path2 = "//server/share/two";
|
||||
EXPECT_TRUE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "// server/cow";
|
||||
path2 = "// server/cow";
|
||||
EXPECT_TRUE(utils::file::smb_parent_is_same(path1, path2));
|
||||
}
|
||||
|
||||
TEST(utils_file, smb_parent_is_not_same) {
|
||||
const auto *path1 = "server/share";
|
||||
const auto *path2 = "//server/share";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "server/share/";
|
||||
path2 = "server/share/";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server1/share/one";
|
||||
path2 = "//server/share/two";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server/share";
|
||||
path2 = "//server/share2";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server/share/";
|
||||
path2 = "//server/share2/";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server/share/one";
|
||||
path2 = "//server/share2/two";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server";
|
||||
path2 = "//server/share/two";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server/";
|
||||
path2 = "//server/";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server";
|
||||
path2 = "//server";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBDSM)
|
||||
|
||||
TEST(utils_file, directory_exists_in_path) {
|
||||
auto &test_dir = test::generate_test_directory();
|
||||
EXPECT_FALSE(
|
||||
utils::file::directory_exists_in_path(test_dir.get_path(), "moose"));
|
||||
|
||||
EXPECT_FALSE(utils::file::directory_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose"));
|
||||
|
||||
EXPECT_FALSE(utils::file::file_exists_in_path(test_dir.get_path(), "moose"));
|
||||
|
||||
EXPECT_FALSE(utils::file::file_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose"));
|
||||
|
||||
auto sub_dir = test_dir.create_directory("moose");
|
||||
EXPECT_TRUE(sub_dir != nullptr);
|
||||
if (sub_dir) {
|
||||
EXPECT_TRUE(
|
||||
utils::file::directory_exists_in_path(test_dir.get_path(), "moose"));
|
||||
|
||||
EXPECT_FALSE(
|
||||
utils::file::file_exists_in_path(test_dir.get_path(), "moose"));
|
||||
|
||||
EXPECT_TRUE(utils::file::directory_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose"));
|
||||
|
||||
EXPECT_FALSE(utils::file::file_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, directory_can_get_empty_directory_count) {
|
||||
auto &test_dir = test::generate_test_directory();
|
||||
EXPECT_EQ(0U, test_dir.count());
|
||||
EXPECT_EQ(0U, test_dir.count(false));
|
||||
}
|
||||
|
||||
TEST(utils_file, directory_can_get_empty_directory_count_recursively) {
|
||||
auto &test_dir = test::generate_test_directory();
|
||||
EXPECT_EQ(0U, test_dir.count(true));
|
||||
}
|
||||
|
||||
TEST(utils_file, directory_can_get_non_empty_directory_count) {
|
||||
auto &test_dir = test::generate_test_directory();
|
||||
auto sub_dir = test_dir.create_directory("sub_dir");
|
||||
EXPECT_TRUE(sub_dir != nullptr);
|
||||
if (sub_dir) {
|
||||
sub_dir->create_directory("sub_dir");
|
||||
|
||||
EXPECT_EQ(1U, test_dir.count());
|
||||
EXPECT_EQ(1U, test_dir.count(false));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, directory_can_get_non_empty_directory_count_recursively) {
|
||||
auto &test_dir = test::generate_test_directory();
|
||||
auto sub_dir = test_dir.create_directory("sub_dir");
|
||||
EXPECT_TRUE(sub_dir != nullptr);
|
||||
if (sub_dir) {
|
||||
sub_dir = sub_dir->create_directory("sub_dir");
|
||||
EXPECT_TRUE(sub_dir != nullptr);
|
||||
}
|
||||
|
||||
EXPECT_EQ(2U, test_dir.count(true));
|
||||
}
|
||||
|
||||
TEST(utils_file, file_exists_in_path) {
|
||||
auto &test_dir = test::generate_test_directory();
|
||||
EXPECT_FALSE(
|
||||
utils::file::file_exists_in_path(test_dir.get_path(), "moose.txt"));
|
||||
|
||||
EXPECT_FALSE(utils::file::file_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose.txt"));
|
||||
|
||||
EXPECT_FALSE(
|
||||
utils::file::directory_exists_in_path(test_dir.get_path(), "moose.txt"));
|
||||
|
||||
EXPECT_FALSE(utils::file::directory_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose.txt"));
|
||||
|
||||
auto sub_file = test_dir.create_file("moose.txt", false);
|
||||
EXPECT_TRUE(sub_file != nullptr);
|
||||
if (sub_file) {
|
||||
EXPECT_TRUE(
|
||||
utils::file::file_exists_in_path(test_dir.get_path(), "moose.txt"));
|
||||
|
||||
EXPECT_FALSE(utils::file::directory_exists_in_path(test_dir.get_path(),
|
||||
"moose.txt"));
|
||||
|
||||
EXPECT_TRUE(utils::file::file_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose.txt"));
|
||||
|
||||
EXPECT_FALSE(utils::file::directory_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose.txt"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, get_free_drive_space) {
|
||||
#if defined(_WIN32)
|
||||
auto space = utils::file::get_free_drive_space("C:");
|
||||
auto space2 = utils::file::get_free_drive_space(L"C:");
|
||||
#else // defined(_WIN32)
|
||||
auto space = utils::file::get_free_drive_space("/");
|
||||
auto space2 = utils::file::get_free_drive_space(L"/");
|
||||
#endif // !defined(_WIN32)
|
||||
|
||||
EXPECT_TRUE(space.has_value());
|
||||
EXPECT_LT(0U, space.value());
|
||||
|
||||
EXPECT_TRUE(space2.has_value());
|
||||
EXPECT_EQ(space.value(), space2.value());
|
||||
}
|
||||
|
||||
TEST(utils_file, get_free_drive_space_fails_for_bad_path) {
|
||||
std::string name{"free_drive_space_test_XXXXXX"};
|
||||
auto temp = utils::file::create_temp_name("free_drive_space_test");
|
||||
|
||||
auto space = utils::file::get_free_drive_space(temp);
|
||||
EXPECT_FALSE(space.has_value());
|
||||
}
|
||||
|
||||
TEST(utils_file, get_total_drive_space) {
|
||||
#if defined(_WIN32)
|
||||
auto space = utils::file::get_total_drive_space("C:");
|
||||
auto space2 = utils::file::get_total_drive_space(L"C:");
|
||||
#else // defined(_WIN32)
|
||||
auto space = utils::file::get_total_drive_space("/");
|
||||
auto space2 = utils::file::get_total_drive_space(L"/");
|
||||
#endif // !defined(_WIN32)
|
||||
|
||||
EXPECT_TRUE(space.has_value());
|
||||
EXPECT_LT(0U, space.value());
|
||||
|
||||
EXPECT_TRUE(space2.has_value());
|
||||
EXPECT_EQ(space.value(), space2.value());
|
||||
}
|
||||
|
||||
TEST(utils_file, create_temp_name) {
|
||||
{
|
||||
auto temp = utils::file::create_temp_name("test_temp");
|
||||
EXPECT_EQ(18U, temp.size());
|
||||
|
||||
auto temp2 = utils::file::create_temp_name("test_temp");
|
||||
EXPECT_STRNE(temp.c_str(), temp2.c_str());
|
||||
|
||||
EXPECT_TRUE(utils::string::begins_with(temp, "test_temp_"));
|
||||
}
|
||||
|
||||
{
|
||||
auto temp = utils::file::create_temp_name(L"test_temp");
|
||||
EXPECT_EQ(18U, temp.size());
|
||||
|
||||
auto temp2 = utils::file::create_temp_name(L"test_temp");
|
||||
EXPECT_STRNE(temp.c_str(), temp2.c_str());
|
||||
|
||||
EXPECT_TRUE(utils::string::begins_with(temp, L"test_temp_"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, get_total_drive_space_fails_for_bad_path) {
|
||||
auto temp = utils::file::create_temp_name("total_drive_space_test");
|
||||
auto space = utils::file::get_total_drive_space(temp);
|
||||
EXPECT_FALSE(space.has_value());
|
||||
}
|
||||
|
||||
TEST(utils_file, get_times) {
|
||||
{
|
||||
auto times =
|
||||
utils::file::get_times(test::create_random_file(1U).get_path());
|
||||
EXPECT_TRUE(times.has_value());
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::accessed));
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::changed));
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::created));
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::modified));
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::written));
|
||||
}
|
||||
|
||||
{
|
||||
auto times = utils::file::get_times(
|
||||
utils::string::from_utf8(test::create_random_file(1U).get_path()));
|
||||
EXPECT_TRUE(times.has_value());
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::accessed));
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::changed));
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::created));
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::modified));
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::written));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, get_times_fails_if_not_found) {
|
||||
auto temp = utils::path::combine(".", {"get_times_test"});
|
||||
auto times = utils::file::get_times(temp);
|
||||
EXPECT_FALSE(times.has_value());
|
||||
}
|
||||
|
||||
TEST(utils_file, get_time) {
|
||||
{
|
||||
auto file_path = test::create_random_file(1U).get_path();
|
||||
auto file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::accessed);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
|
||||
file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::changed);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
|
||||
file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::created);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
|
||||
file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::modified);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
|
||||
file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::written);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
}
|
||||
|
||||
{
|
||||
auto file_path =
|
||||
utils::string::from_utf8(test::create_random_file(1U).get_path());
|
||||
|
||||
auto file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::accessed);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
|
||||
file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::changed);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
|
||||
file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::created);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
|
||||
file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::modified);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
|
||||
file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::written);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, get_time_fails_if_not_found) {
|
||||
auto temp = utils::path::combine(".", {"get_times_test"});
|
||||
auto file_time =
|
||||
utils::file::get_time(temp, utils::file::time_type::accessed);
|
||||
EXPECT_FALSE(file_time.has_value());
|
||||
}
|
||||
} // namespace fifthgrid
|
||||
249
support/test/src/utils/hash_test.cpp
Normal file
249
support/test/src/utils/hash_test.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
namespace fifthgrid {
|
||||
TEST(utils_hash, hash_type_sizes) {
|
||||
EXPECT_EQ(4U, utils::hash::hash_32_t{}.size());
|
||||
EXPECT_EQ(8U, utils::hash::hash_64_t{}.size());
|
||||
EXPECT_EQ(16U, utils::hash::hash_128_t{}.size());
|
||||
EXPECT_EQ(32U, utils::hash::hash_256_t{}.size());
|
||||
EXPECT_EQ(48U, utils::hash::hash_384_t{}.size());
|
||||
EXPECT_EQ(64U, utils::hash::hash_512_t{}.size());
|
||||
}
|
||||
|
||||
TEST(utils_hash, default_hasher_is_blake2b) {
|
||||
EXPECT_EQ(&utils::hash::blake2b_32_hasher,
|
||||
&utils::hash::default_create_hash<utils::hash::hash_32_t>());
|
||||
|
||||
EXPECT_EQ(&utils::hash::blake2b_64_hasher,
|
||||
&utils::hash::default_create_hash<utils::hash::hash_64_t>());
|
||||
|
||||
EXPECT_EQ(&utils::hash::blake2b_128_hasher,
|
||||
&utils::hash::default_create_hash<utils::hash::hash_128_t>());
|
||||
|
||||
EXPECT_EQ(&utils::hash::blake2b_256_hasher,
|
||||
&utils::hash::default_create_hash<utils::hash::hash_256_t>());
|
||||
|
||||
EXPECT_EQ(&utils::hash::blake2b_384_hasher,
|
||||
&utils::hash::default_create_hash<utils::hash::hash_384_t>());
|
||||
|
||||
EXPECT_EQ(&utils::hash::blake2b_512_hasher,
|
||||
&utils::hash::default_create_hash<utils::hash::hash_512_t>());
|
||||
}
|
||||
|
||||
TEST(utils_hash, blake2b_32) {
|
||||
auto hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_32("a"));
|
||||
EXPECT_STREQ("ca234c55", hash.c_str());
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_32(L"a"));
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ("4c368117", hash.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ("02a631b8", hash.c_str());
|
||||
#endif
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_32({1U}));
|
||||
EXPECT_STREQ("593bda73", hash.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_hash, blake2b_64) {
|
||||
auto hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_64("a"));
|
||||
EXPECT_STREQ("40f89e395b66422f", hash.c_str());
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_64(L"a"));
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ("4dd0bb1c45b748c1", hash.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ("85ff8cc55b79d38a", hash.c_str());
|
||||
#endif
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_64({1U}));
|
||||
EXPECT_STREQ("00e83d0a3f7519ad", hash.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_hash, blake2b_128) {
|
||||
auto hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_128("a"));
|
||||
EXPECT_STREQ("27c35e6e9373877f29e562464e46497e", hash.c_str());
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_128(L"a"));
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ("396660e76c84bb7786f979f10b58fa79", hash.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ("dae64afb310a3426ad84f0739fde5cef", hash.c_str());
|
||||
#endif
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_128({1U}));
|
||||
EXPECT_STREQ("4a9e6f9b8d43f6ad008f8c291929dee2", hash.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_hash, blake2b_256) {
|
||||
auto hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_256("a"));
|
||||
EXPECT_STREQ(
|
||||
"8928aae63c84d87ea098564d1e03ad813f107add474e56aedd286349c0c03ea4",
|
||||
hash.c_str());
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_256(L"a"));
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"d2373b17cd8a8e19e39f52fa4905a274f93805fbb8bb4c7f3cb4b2cd6708ec8a",
|
||||
hash.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"9fdf5757d7eea386f0d34d2c0e202527986febf1ebb4315fcf7fff40776fa41d",
|
||||
hash.c_str());
|
||||
#endif
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_256({1U}));
|
||||
EXPECT_STREQ(
|
||||
"ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25",
|
||||
hash.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_hash, blake2b_384) {
|
||||
auto hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_384("a"));
|
||||
EXPECT_STREQ("7d40de16ff771d4595bf70cbda0c4ea0a066a6046fa73d34471cd4d93d827d7"
|
||||
"c94c29399c50de86983af1ec61d5dcef0",
|
||||
hash.c_str());
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_384(L"a"));
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ("637fe31d1e955760ef31043d525d9321826a778ddbe82fcde45a98394241380"
|
||||
"96675e2f87e36b53ab223a7fd254198fd",
|
||||
hash.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ("9d469bd9dab9d4b48b8688de7c22704a8de1b081294f9be294100dfa9f05c92"
|
||||
"e8d3616476e46cd14f9e613fed80fd157",
|
||||
hash.c_str());
|
||||
#endif
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_384({1U}));
|
||||
EXPECT_STREQ("42cfe875d08d816538103b906bb0b05202e0b09c4e981680c1110684fc7845b"
|
||||
"c91c178fa167afcc445490644b2bf5f5b",
|
||||
hash.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_hash, blake2b_512) {
|
||||
auto hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_512("a"));
|
||||
EXPECT_STREQ(
|
||||
"333fcb4ee1aa7c115355ec66ceac917c8bfd815bf7587d325aec1864edd24e34d5abe2c6"
|
||||
"b1b5ee3face62fed78dbef802f2a85cb91d455a8f5249d330853cb3c",
|
||||
hash.c_str());
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_512(L"a"));
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"05970b95468b0b1941066ff189091493e73859ce41cde5ad08118e93ea1d81a57a144296"
|
||||
"a26a9fe7781481bde97b886725e36e30b305d8bd5cce1ae36bf1564a",
|
||||
hash.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"bbc187c6e4d8525655d0ada62d16eed59f3db3ab07e04fb0483fd4ae21d88b984774add9"
|
||||
"b3fbcff56f9638091013994f8e2d4646fdbbcb4879e2b5160bbb755d",
|
||||
hash.c_str());
|
||||
#endif
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::hash::create_hash_blake2b_512({1U}));
|
||||
EXPECT_STREQ(
|
||||
"9545ba37b230d8a2e716c4707586542780815b7c4088edcb9af6a9452d50f32474d5ba9a"
|
||||
"ab52a67aca864ef2696981c2eadf49020416136afd838fb048d21653",
|
||||
hash.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_hash, sha256) {
|
||||
auto hash =
|
||||
utils::collection::to_hex_string(utils::hash::create_hash_sha256("a"));
|
||||
EXPECT_STREQ(
|
||||
"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
|
||||
hash.c_str());
|
||||
|
||||
hash =
|
||||
utils::collection::to_hex_string(utils::hash::create_hash_sha256(L"a"));
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"ffe9aaeaa2a2d5048174df0b80599ef0197ec024c4b051bc9860cff58ef7f9f3",
|
||||
hash.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"a2d398922901344d08180dc41d3e9d73d8c148c7f6e092835bbb28e02dbcf184",
|
||||
hash.c_str());
|
||||
#endif
|
||||
|
||||
hash =
|
||||
utils::collection::to_hex_string(utils::hash::create_hash_sha256({1U}));
|
||||
EXPECT_STREQ(
|
||||
"4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a",
|
||||
hash.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_hash, sha512) {
|
||||
auto hash =
|
||||
utils::collection::to_hex_string(utils::hash::create_hash_sha512("a"));
|
||||
EXPECT_STREQ(
|
||||
"1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c65"
|
||||
"2bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75",
|
||||
hash.c_str());
|
||||
|
||||
hash =
|
||||
utils::collection::to_hex_string(utils::hash::create_hash_sha512(L"a"));
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"5c2ca3d50f46ece6066c53bd1a490cbe5f72d2738ae9417332e91e5c3f75205c639d71a9"
|
||||
"a41d67d965fa137dddf439e0ab9443a6ea44915e90d8b5b566d1c076",
|
||||
hash.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"a93498d992e81915075144cb304d2bdf040b336283f888252244882d8366dd3a6e2d9749"
|
||||
"077114dda1a9aa1a7b69d33f7a781f003ccd12e599a6341014f29aaf",
|
||||
hash.c_str());
|
||||
#endif
|
||||
|
||||
hash =
|
||||
utils::collection::to_hex_string(utils::hash::create_hash_sha512({1U}));
|
||||
EXPECT_STREQ(
|
||||
"7b54b66836c1fbdd13d2441d9e1434dc62ca677fb68f5fe66a464baadecdbd00576f8d6b"
|
||||
"5ac3bcc80844b7d50b1cc6603444bbe7cfcf8fc0aa1ee3c636d9e339",
|
||||
hash.c_str());
|
||||
}
|
||||
} // namespace fifthgrid
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
552
support/test/src/utils/path_test.cpp
Normal file
552
support/test/src/utils/path_test.cpp
Normal file
@@ -0,0 +1,552 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
#if defined(_WIN32)
|
||||
namespace {
|
||||
static const auto test_path = [](std::string str) -> std::string {
|
||||
#if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
if (fifthgrid::utils::string::begins_with(str, "\\")) {
|
||||
str = fifthgrid::utils::string::to_lower(
|
||||
std::filesystem::current_path().string().substr(0U, 2U)) +
|
||||
str;
|
||||
}
|
||||
|
||||
str = std::string{fifthgrid::utils::path::long_notation} + str;
|
||||
#else // !defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
if (fifthgrid::utils::string::begins_with(str, "\\")) {
|
||||
str = fifthgrid::utils::string::to_lower(
|
||||
std::filesystem::current_path().string().substr(0U, 2U)) +
|
||||
str;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
|
||||
return fifthgrid::utils::string::right_trim(str, '\\');
|
||||
};
|
||||
} // namespace
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
namespace fifthgrid {
|
||||
TEST(utils_path, constants) {
|
||||
EXPECT_EQ(std::string_view{"\\"}, utils::path::backslash);
|
||||
EXPECT_EQ(std::wstring_view{L"\\"}, utils::path::backslash_w);
|
||||
EXPECT_EQ(std::string_view{"."}, utils::path::dot);
|
||||
EXPECT_EQ(std::wstring_view{L"."}, utils::path::dot_w);
|
||||
EXPECT_EQ(std::string_view{".\\"}, utils::path::dot_backslash);
|
||||
EXPECT_EQ(std::wstring_view{L".\\"}, utils::path::dot_backslash_w);
|
||||
EXPECT_EQ(std::string_view{"./"}, utils::path::dot_slash);
|
||||
EXPECT_EQ(std::wstring_view{L"./"}, utils::path::dot_slash_w);
|
||||
EXPECT_EQ(std::string_view{"/"}, utils::path::slash);
|
||||
EXPECT_EQ(std::wstring_view{L"/"}, utils::path::slash_w);
|
||||
|
||||
#if defined(_WIN32)
|
||||
EXPECT_EQ(std::string_view{"\\\\"}, utils::path::unc_notation);
|
||||
EXPECT_EQ(std::wstring_view{L"\\\\"}, utils::path::unc_notation_w);
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils_path, directory_seperator) {
|
||||
#if defined(_WIN32)
|
||||
EXPECT_EQ(utils::path::backslash, utils::path::directory_seperator);
|
||||
EXPECT_EQ(utils::path::backslash_w, utils::path::directory_seperator_w);
|
||||
|
||||
EXPECT_EQ(utils::path::slash, utils::path::not_directory_seperator);
|
||||
EXPECT_EQ(utils::path::slash_w, utils::path::not_directory_seperator_w);
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_EQ(utils::path::slash, utils::path::directory_seperator);
|
||||
EXPECT_EQ(utils::path::slash_w, utils::path::directory_seperator_w);
|
||||
|
||||
EXPECT_EQ(utils::path::backslash, utils::path::not_directory_seperator);
|
||||
EXPECT_EQ(utils::path::backslash_w, utils::path::not_directory_seperator_w);
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils_path, get_directory_seperator) {
|
||||
#if defined(_WIN32)
|
||||
EXPECT_EQ(utils::path::backslash,
|
||||
utils::path::get_directory_seperator<char>());
|
||||
EXPECT_EQ(utils::path::backslash_w,
|
||||
utils::path::get_directory_seperator<wchar_t>());
|
||||
|
||||
EXPECT_EQ(utils::path::slash,
|
||||
utils::path::get_not_directory_seperator<char>());
|
||||
EXPECT_EQ(utils::path::slash_w,
|
||||
utils::path::get_not_directory_seperator<wchar_t>());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_EQ(utils::path::slash, utils::path::get_directory_seperator<char>());
|
||||
EXPECT_EQ(utils::path::slash_w,
|
||||
utils::path::get_directory_seperator<wchar_t>());
|
||||
|
||||
EXPECT_EQ(utils::path::backslash,
|
||||
utils::path::get_not_directory_seperator<char>());
|
||||
EXPECT_EQ(utils::path::backslash_w,
|
||||
utils::path::get_not_directory_seperator<wchar_t>());
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils_path, get_backslash) {
|
||||
EXPECT_EQ(utils::path::backslash, utils::path::get_backslash<char>());
|
||||
EXPECT_EQ(utils::path::backslash_w, utils::path::get_backslash<wchar_t>());
|
||||
}
|
||||
|
||||
TEST(utils_path, get_dot) {
|
||||
EXPECT_EQ(utils::path::dot, utils::path::get_dot<char>());
|
||||
EXPECT_EQ(utils::path::dot_w, utils::path::get_dot<wchar_t>());
|
||||
}
|
||||
|
||||
TEST(utils_path, get_dot_backslash) {
|
||||
EXPECT_EQ(utils::path::dot_backslash, utils::path::get_dot_backslash<char>());
|
||||
EXPECT_EQ(utils::path::dot_backslash_w,
|
||||
utils::path::get_dot_backslash<wchar_t>());
|
||||
}
|
||||
|
||||
TEST(utils_path, get_dot_slash) {
|
||||
EXPECT_EQ(utils::path::dot_slash, utils::path::get_dot_slash<char>());
|
||||
EXPECT_EQ(utils::path::dot_slash_w, utils::path::get_dot_slash<wchar_t>());
|
||||
}
|
||||
|
||||
TEST(utils_path, get_slash) {
|
||||
EXPECT_EQ(utils::path::slash, utils::path::get_slash<char>());
|
||||
EXPECT_EQ(utils::path::slash_w, utils::path::get_slash<wchar_t>());
|
||||
}
|
||||
|
||||
TEST(utils_path, get_long_notation) {
|
||||
EXPECT_EQ(utils::path::long_notation, utils::path::get_long_notation<char>());
|
||||
EXPECT_EQ(utils::path::long_notation_w,
|
||||
utils::path::get_long_notation<wchar_t>());
|
||||
}
|
||||
|
||||
TEST(utils_path, combine) {
|
||||
auto s = utils::path::combine(R"(\test\path)", {});
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\test\path)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/test/path", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::combine(R"(\test)", {R"(\path)"});
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\test\path)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/test/path", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::combine(R"(\test)", {R"(\path)", R"(\again\)"});
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\test\path\again)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/test/path/again", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::combine("/home/test/.dest", {".state"});
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\home\test\.dest\.state)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/home/test/.dest/.state", s.c_str());
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
s = utils::path::combine(R"(R:\test)", {R"(\path)", R"(\again\)"});
|
||||
EXPECT_STREQ(test_path(R"(r:\test\path\again)").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::combine("R:", {R"(\path)", R"(\again\)"});
|
||||
EXPECT_STREQ(test_path(R"(r:\path\again)").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::combine("R:", {});
|
||||
EXPECT_STREQ(test_path("r:").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::combine("R:", {"\\"});
|
||||
EXPECT_STREQ(test_path("r:").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::combine("\\\\moose", {"cow"});
|
||||
EXPECT_STREQ("\\\\moose\\cow", s.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(utils_path, format_path) {
|
||||
std::string path{"./"};
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ(".", path.c_str());
|
||||
|
||||
path = "~/.test";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("~/.test", path.c_str());
|
||||
|
||||
path = "\\";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
|
||||
path = "\\\\";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
|
||||
path = "\\\\\\";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
|
||||
path = "\\\\\\\\";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
|
||||
path = "/";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
path = "//";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
|
||||
path = "///";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
|
||||
path = "////";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_path, create_api_path) {
|
||||
auto s = utils::path::create_api_path("");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path(R"(\)");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("/");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path(".");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("./");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path(R"(\\)");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("//");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("/cow///moose/////dog/chicken");
|
||||
EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("\\cow\\\\\\moose\\\\\\\\dog\\chicken/");
|
||||
EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("/cow\\\\/moose\\\\/\\dog\\chicken\\");
|
||||
EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path(".state");
|
||||
EXPECT_STREQ("/.state", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("/.state/.local");
|
||||
EXPECT_STREQ("/.state/.local", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("./.state/.local");
|
||||
EXPECT_STREQ("/.state/.local", s.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_path, get_parent_api_path) {
|
||||
auto s = utils::path::get_parent_api_path("");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::get_parent_api_path("/");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::get_parent_api_path("/moose");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::get_parent_api_path("/moose/cow");
|
||||
EXPECT_STREQ("/moose", s.c_str());
|
||||
|
||||
s = utils::path::get_parent_api_path("/moose/cow/");
|
||||
EXPECT_STREQ("/moose", s.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_path, finalize) {
|
||||
auto s = utils::path::finalize("");
|
||||
EXPECT_STREQ("", s.c_str());
|
||||
|
||||
s = utils::path::finalize(R"(\)");
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::finalize("/");
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::finalize(R"(\\)");
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ("\\\\", s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::finalize("//");
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ("\\\\", s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::finalize("/cow///moose/////dog/chicken");
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\cow\moose\dog\chicken)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::finalize("\\cow\\\\\\moose\\\\\\\\dog\\chicken/");
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\cow\moose\dog\chicken)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::finalize("/cow\\\\/moose\\\\/\\dog\\chicken\\");
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\cow\moose\dog\chicken)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str());
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
s = utils::path::finalize("D:");
|
||||
EXPECT_STREQ(test_path("d:").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::finalize("D:\\");
|
||||
EXPECT_STREQ(test_path("d:").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::finalize("D:\\moose");
|
||||
EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::finalize("D:\\moose\\");
|
||||
EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::finalize("D:/");
|
||||
EXPECT_STREQ(test_path("d:").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::finalize("D:/moose");
|
||||
EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::finalize("D:/moose/");
|
||||
EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::finalize("\\\\moose\\cow");
|
||||
EXPECT_STREQ("\\\\moose\\cow", s.c_str());
|
||||
|
||||
s = utils::path::finalize("//moose/cow");
|
||||
EXPECT_STREQ("\\\\moose\\cow", s.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
s = utils::path::finalize("\\\\moose\\cow");
|
||||
EXPECT_STREQ("/moose/cow", s.c_str());
|
||||
|
||||
s = utils::path::finalize("//moose/cow");
|
||||
EXPECT_STREQ("/moose/cow", s.c_str());
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils_path, absolute) {
|
||||
auto dir = utils::path::get_current_path<std::string>();
|
||||
auto path = utils::path::absolute(".");
|
||||
EXPECT_STREQ(dir.c_str(), path.c_str());
|
||||
|
||||
path = utils::path::absolute("./");
|
||||
EXPECT_STREQ(dir.c_str(), path.c_str());
|
||||
|
||||
path = utils::path::absolute(R"(.\)");
|
||||
EXPECT_STREQ(dir.c_str(), path.c_str());
|
||||
|
||||
#if defined(_WIN32)
|
||||
path = utils::path::absolute(R"(.\moose)");
|
||||
EXPECT_STREQ((dir + R"(\moose)").c_str(), path.c_str());
|
||||
|
||||
path = utils::path::absolute(R"(./moose)");
|
||||
EXPECT_STREQ((dir + R"(\moose)").c_str(), path.c_str());
|
||||
|
||||
path = utils::path::absolute(R"(\\server\share)");
|
||||
EXPECT_STREQ(R"(\\server\share)", path.c_str());
|
||||
|
||||
path = utils::path::absolute(R"(//server/share)");
|
||||
EXPECT_STREQ(R"(\\server\share)", path.c_str());
|
||||
|
||||
auto home_env = utils::get_environment_variable("USERPROFILE");
|
||||
#else // !defined(_WIN32)
|
||||
path = utils::path::absolute(R"(.\moose)");
|
||||
EXPECT_STREQ((dir + R"(/moose)").c_str(), path.c_str());
|
||||
|
||||
path = utils::path::absolute(R"(./moose)");
|
||||
EXPECT_STREQ((dir + R"(/moose)").c_str(), path.c_str());
|
||||
|
||||
path = utils::path::absolute(R"(\\server\share)");
|
||||
EXPECT_STREQ(R"(/server/share)", path.c_str());
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils_path, absolute_can_resolve_path_variables) {
|
||||
#if defined(_WIN32)
|
||||
auto home =
|
||||
utils::path::absolute(utils::get_environment_variable("USERPROFILE"));
|
||||
EXPECT_STREQ(home.c_str(), utils::path::absolute("%USERPROFILE%").c_str());
|
||||
#else // !defined(_WIN32)
|
||||
auto home = utils::path::absolute(utils::get_environment_variable("HOME"));
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
auto expanded_str = utils::path::absolute("~\\");
|
||||
EXPECT_STREQ(home.c_str(), expanded_str.c_str());
|
||||
|
||||
expanded_str = utils::path::absolute("~/");
|
||||
EXPECT_STREQ(home.c_str(), expanded_str.c_str());
|
||||
|
||||
expanded_str = utils::path::absolute("~");
|
||||
EXPECT_STREQ("~", expanded_str.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_path, get_parent_path) {
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
auto dir = R"(c:\test)";
|
||||
auto parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ("c:", parent.c_str());
|
||||
|
||||
dir = R"(c:\test\file.txt)";
|
||||
parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ(R"(c:\test)", parent.c_str());
|
||||
|
||||
dir = "c:";
|
||||
parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ("c:", parent.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
auto dir = LR"(c:\test)";
|
||||
auto parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ(L"c:", parent.c_str());
|
||||
|
||||
dir = LR"(c:\test\file.txt)";
|
||||
parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ(LR"(c:\test)", parent.c_str());
|
||||
|
||||
dir = L"c:";
|
||||
parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ(L"c:", parent.c_str());
|
||||
}
|
||||
#else // !defined(_WIN32)
|
||||
{
|
||||
auto dir = "/test";
|
||||
auto parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ("/", parent.c_str());
|
||||
|
||||
dir = "/test/test";
|
||||
parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ("/test", parent.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
auto dir = L"/test";
|
||||
auto parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ(L"/", parent.c_str());
|
||||
|
||||
dir = L"/test/test";
|
||||
parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ(L"/test", parent.c_str());
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils_path, contains_trash_directory) {
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
auto dir = R"(c:\$recycle.bin)";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = R"(c:\$recycle.bin\moose.txt)";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
|
||||
{
|
||||
auto dir = LR"(c:\$recycle.bin)";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = LR"(c:\$recycle.bin\moose.txt)";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
#else // !defined(_WIN32)
|
||||
{
|
||||
auto dir = "/$recycle.bin";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = "/$recycle.bin/moose.txt";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
|
||||
{
|
||||
auto dir = L"/$recycle.bin";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = L"/$recycle.bin/moose.txt";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils_path, does_not_contain_trash_directory) {
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
auto dir = R"(c:\recycle.bin)";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = R"(c:\recycle.bin\moose.txt)";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
|
||||
{
|
||||
auto dir = LR"(c:\recycle.bin)";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = LR"(c:\recycle.bin\moose.txt)";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
#else // !defined(_WIN32)
|
||||
{
|
||||
auto dir = "/recycle.bin";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = "/recycle.bin/moose.txt)";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
|
||||
{
|
||||
auto dir = L"/recycle.bin";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = L"/recycle.bin/moose.txt)";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
} // namespace fifthgrid
|
||||
143
support/test/src/utils/string_test.cpp
Normal file
143
support/test/src/utils/string_test.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
namespace fifthgrid {
|
||||
TEST(utils_string, begins_with) {
|
||||
std::string str{"moose"};
|
||||
EXPECT_TRUE(utils::string::begins_with(str, "m"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str, "mo"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str, "moo"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str, "moos"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str, "moose"));
|
||||
|
||||
EXPECT_FALSE(utils::string::begins_with(str, "a"));
|
||||
EXPECT_FALSE(utils::string::begins_with(str, "ma"));
|
||||
EXPECT_FALSE(utils::string::begins_with(str, "moose1"));
|
||||
|
||||
std::wstring str_w{L"moose"};
|
||||
EXPECT_TRUE(utils::string::begins_with(str_w, L"m"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str_w, L"mo"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str_w, L"moo"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str_w, L"moos"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str_w, L"moose"));
|
||||
|
||||
EXPECT_FALSE(utils::string::begins_with(str_w, L"a"));
|
||||
EXPECT_FALSE(utils::string::begins_with(str_w, L"ma"));
|
||||
EXPECT_FALSE(utils::string::begins_with(str_w, L"moose1"));
|
||||
}
|
||||
|
||||
TEST(utils_string, contains) {
|
||||
std::string str{R"(\\)"};
|
||||
EXPECT_TRUE(utils::string::contains(str, "\\"));
|
||||
EXPECT_FALSE(utils::string::contains(str, "/"));
|
||||
|
||||
std::wstring str_w{LR"(\\)"};
|
||||
EXPECT_TRUE(utils::string::contains(str_w, L"\\"));
|
||||
EXPECT_FALSE(utils::string::contains(str_w, L"/"));
|
||||
}
|
||||
|
||||
TEST(utils_string, replace) {
|
||||
std::string str{"moose"};
|
||||
utils::string::replace(str, 'o', '0');
|
||||
EXPECT_STREQ("m00se", str.c_str());
|
||||
|
||||
std::wstring str_w{L"moose"};
|
||||
utils::string::replace(str_w, 'o', '0');
|
||||
EXPECT_STREQ(L"m00se", str_w.c_str());
|
||||
|
||||
std::string str2{"\\\\\\"};
|
||||
utils::string::replace(str2, '\\', '/');
|
||||
EXPECT_STREQ("///", str2.c_str());
|
||||
|
||||
std::wstring str_w2{L"\\\\\\"};
|
||||
utils::string::replace(str_w2, '\\', '/');
|
||||
EXPECT_STREQ(L"///", str_w2.c_str());
|
||||
|
||||
std::string str3{"///"};
|
||||
utils::string::replace(str3, '/', '\\');
|
||||
EXPECT_STREQ("\\\\\\", str3.c_str());
|
||||
|
||||
std::wstring str_w3{L"///"};
|
||||
utils::string::replace(str_w3, '/', '\\');
|
||||
EXPECT_STREQ(L"\\\\\\", str_w3.c_str());
|
||||
|
||||
str.clear();
|
||||
utils::string::replace(str, '/', '\\');
|
||||
EXPECT_STREQ("", str.c_str());
|
||||
|
||||
str_w.clear();
|
||||
utils::string::replace(str_w, '/', '\\');
|
||||
EXPECT_STREQ(L"", str_w.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_string, replace_string) {
|
||||
std::string str{"moose"};
|
||||
utils::string::replace(str, "o", "0");
|
||||
EXPECT_STREQ("m00se", str.c_str());
|
||||
|
||||
std::wstring str_w{L"moose"};
|
||||
utils::string::replace(str_w, L"o", L"0");
|
||||
EXPECT_STREQ(L"m00se", str_w.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_string, is_numeric) {
|
||||
EXPECT_TRUE(utils::string::is_numeric("100"));
|
||||
EXPECT_TRUE(utils::string::is_numeric("+100"));
|
||||
EXPECT_TRUE(utils::string::is_numeric("-100"));
|
||||
|
||||
EXPECT_TRUE(utils::string::is_numeric("100.00"));
|
||||
EXPECT_TRUE(utils::string::is_numeric("+100.00"));
|
||||
EXPECT_TRUE(utils::string::is_numeric("-100.00"));
|
||||
|
||||
EXPECT_FALSE(utils::string::is_numeric("1.00.00"));
|
||||
EXPECT_FALSE(utils::string::is_numeric("+1.00.00"));
|
||||
EXPECT_FALSE(utils::string::is_numeric("-1.00.00"));
|
||||
|
||||
EXPECT_FALSE(utils::string::is_numeric("a1"));
|
||||
EXPECT_FALSE(utils::string::is_numeric("1a"));
|
||||
|
||||
EXPECT_FALSE(utils::string::is_numeric("+"));
|
||||
EXPECT_FALSE(utils::string::is_numeric("-"));
|
||||
|
||||
EXPECT_FALSE(utils::string::is_numeric(""));
|
||||
}
|
||||
|
||||
TEST(utils_string, to_bool) {
|
||||
EXPECT_TRUE(utils::string::to_bool("1"));
|
||||
EXPECT_TRUE(utils::string::to_bool("-1"));
|
||||
EXPECT_TRUE(utils::string::to_bool("0.1"));
|
||||
EXPECT_TRUE(utils::string::to_bool("-0.1"));
|
||||
EXPECT_TRUE(utils::string::to_bool("00000.1000000"));
|
||||
EXPECT_TRUE(utils::string::to_bool("true"));
|
||||
|
||||
EXPECT_FALSE(utils::string::to_bool("false"));
|
||||
EXPECT_FALSE(utils::string::to_bool("0"));
|
||||
EXPECT_FALSE(utils::string::to_bool("00000.00000"));
|
||||
}
|
||||
|
||||
TEST(utils_string, utf8_string_conversion) {
|
||||
std::wstring ws = L"Hello 🌍 — 𝄞 漢字";
|
||||
std::wstring ws2 = utils::string::from_utf8(utils::string::to_utf8(ws));
|
||||
EXPECT_STREQ(ws.c_str(), ws2.c_str());
|
||||
}
|
||||
} // namespace fifthgrid
|
||||
253
support/test/src/utils/ttl_cache_test.cpp
Normal file
253
support/test/src/utils/ttl_cache_test.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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 "test.hpp"
|
||||
|
||||
namespace fifthgrid {
|
||||
TEST(utils_ttl_cache, can_construct_cache) {
|
||||
utils::ttl_cache<std::uint8_t> cache;
|
||||
EXPECT_EQ(utils::ttl_cache<std::uint8_t>::default_expiration,
|
||||
cache.get_ttl());
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, can_construct_cache_with_ttl) {
|
||||
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(1000U));
|
||||
EXPECT_EQ(std::chrono::milliseconds(1000U), cache.get_ttl());
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, can_change_ttl) {
|
||||
utils::ttl_cache<std::uint8_t> cache;
|
||||
cache.set_ttl(std::chrono::milliseconds(1000U));
|
||||
EXPECT_EQ(std::chrono::milliseconds(1000U), cache.get_ttl());
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, can_set_and_get) {
|
||||
utils::ttl_cache<std::uint8_t> cache;
|
||||
cache.set("/test", 21U);
|
||||
auto data = cache.get("/test");
|
||||
ASSERT_NE(nullptr, data.get());
|
||||
|
||||
EXPECT_EQ(21U, data->load());
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, get_returns_nullptr_for_api_path_not_in_cache) {
|
||||
utils::ttl_cache<std::uint8_t> cache;
|
||||
auto data = cache.get("/test");
|
||||
ASSERT_EQ(nullptr, data.get());
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, set_and_get_returns_value_and_refreshes_ttl) {
|
||||
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(1000U));
|
||||
|
||||
cache.set("/test", 7U);
|
||||
auto data = cache.get("/test");
|
||||
{
|
||||
EXPECT_TRUE(cache.contains("/test"));
|
||||
ASSERT_NE(data, nullptr);
|
||||
EXPECT_EQ(7U, data->load());
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200U));
|
||||
{
|
||||
EXPECT_TRUE(cache.contains("/test"));
|
||||
auto data2 = cache.get("/test");
|
||||
ASSERT_NE(data2, nullptr);
|
||||
ASSERT_EQ(data.get(), data2.get());
|
||||
EXPECT_EQ(7U, data2->load());
|
||||
}
|
||||
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(800U));
|
||||
cache.purge_expired();
|
||||
|
||||
auto data3 = cache.get("/test");
|
||||
EXPECT_TRUE(cache.contains("/test"));
|
||||
|
||||
ASSERT_NE(data3, nullptr);
|
||||
ASSERT_EQ(data.get(), data3.get());
|
||||
EXPECT_EQ(7U, data3->load());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, entry_expires_without_refresh) {
|
||||
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(50U));
|
||||
cache.set("/test", 42U);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(51U));
|
||||
cache.purge_expired();
|
||||
EXPECT_FALSE(cache.contains("/test"));
|
||||
|
||||
auto data = cache.get("/test");
|
||||
EXPECT_EQ(nullptr, data.get());
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, can_erase) {
|
||||
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(50U));
|
||||
cache.set("/test", 42U);
|
||||
cache.erase("/test");
|
||||
|
||||
EXPECT_FALSE(cache.contains("/test"));
|
||||
auto data = cache.get("/test");
|
||||
EXPECT_EQ(nullptr, data.get());
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, can_clear) {
|
||||
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(50U));
|
||||
|
||||
cache.set("/test", 42U);
|
||||
cache.set("/test2", 42U);
|
||||
EXPECT_TRUE(cache.contains("/test"));
|
||||
EXPECT_TRUE(cache.contains("/test2"));
|
||||
cache.clear();
|
||||
|
||||
{
|
||||
EXPECT_FALSE(cache.contains("/test"));
|
||||
auto data = cache.get("/test");
|
||||
EXPECT_EQ(nullptr, data.get());
|
||||
}
|
||||
|
||||
{
|
||||
EXPECT_FALSE(cache.contains("/test2"));
|
||||
auto data = cache.get("/test2");
|
||||
EXPECT_EQ(nullptr, data.get());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, can_handle_concurrent_access) {
|
||||
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(5000U));
|
||||
|
||||
std::atomic<bool> start{false};
|
||||
std::thread writer([&] {
|
||||
while (not start.load()) {
|
||||
}
|
||||
for (std::uint8_t ttl = 0U; ttl < 100U; ++ttl) {
|
||||
cache.set("/key", ttl);
|
||||
std::this_thread::yield();
|
||||
}
|
||||
});
|
||||
|
||||
std::thread reader([&] {
|
||||
while (not start.load()) {
|
||||
}
|
||||
for (std::uint8_t ttl = 0U; ttl < 100U; ++ttl) {
|
||||
auto data = cache.get("/key");
|
||||
if (data) {
|
||||
[[maybe_unused]] auto res = data->load();
|
||||
}
|
||||
std::this_thread::yield();
|
||||
}
|
||||
});
|
||||
|
||||
start = true;
|
||||
writer.join();
|
||||
reader.join();
|
||||
|
||||
auto data = cache.get("/key");
|
||||
ASSERT_NE(data, nullptr);
|
||||
[[maybe_unused]] auto res = data->load();
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, can_handle_custom_atomic) {
|
||||
utils::ttl_cache<std::string, utils::atomic> cache(
|
||||
std::chrono::milliseconds(5000U));
|
||||
cache.set("/test", "test");
|
||||
auto data = cache.get("/test");
|
||||
ASSERT_NE(nullptr, data.get());
|
||||
EXPECT_STREQ("test", data->load().c_str());
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, get_renews_after_ttl_if_purge_expired_is_not_called) {
|
||||
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(50U));
|
||||
cache.set("/test", 9U);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(75U));
|
||||
|
||||
auto data = cache.get("/test");
|
||||
ASSERT_NE(nullptr, data.get());
|
||||
EXPECT_EQ(9U, data->load());
|
||||
|
||||
cache.purge_expired();
|
||||
EXPECT_TRUE(cache.contains("/test"));
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, can_update_data) {
|
||||
utils::ttl_cache<std::uint8_t> cache;
|
||||
|
||||
cache.set("/test", 1U);
|
||||
auto data = cache.get("/test");
|
||||
ASSERT_NE(nullptr, data.get());
|
||||
EXPECT_EQ(1U, data->load());
|
||||
|
||||
cache.set("/test", 2U);
|
||||
auto data2 = cache.get("/test");
|
||||
ASSERT_NE(nullptr, data2.get());
|
||||
EXPECT_EQ(data.get(), data2.get());
|
||||
EXPECT_EQ(2U, data2->load());
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, purge_expired_removes_only_expired_entries) {
|
||||
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(1000U));
|
||||
cache.set("/test1", 1U);
|
||||
cache.set("/test2", 2U);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500U));
|
||||
auto data = cache.get("/test2");
|
||||
ASSERT_NE(data, nullptr);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(501U));
|
||||
cache.purge_expired();
|
||||
|
||||
EXPECT_FALSE(cache.contains("/test1"));
|
||||
EXPECT_TRUE(cache.contains("/test2"));
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, can_handle_non_existing_items_without_failure) {
|
||||
utils::ttl_cache<std::uint8_t> cache;
|
||||
cache.set("/exists", 5U);
|
||||
EXPECT_TRUE(cache.contains("/exists"));
|
||||
|
||||
cache.erase("/not_found");
|
||||
EXPECT_TRUE(cache.contains("/exists"));
|
||||
|
||||
auto data = cache.get("/exists");
|
||||
ASSERT_NE(nullptr, data.get());
|
||||
EXPECT_EQ(5U, data->load());
|
||||
}
|
||||
|
||||
TEST(utils_ttl_cache, changing_ttl_affects_only_future_expirations) {
|
||||
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(1000U));
|
||||
cache.set("/test", 11U);
|
||||
|
||||
cache.set_ttl(std::chrono::milliseconds(100U));
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200U));
|
||||
cache.purge_expired();
|
||||
EXPECT_TRUE(cache.contains("/test"));
|
||||
|
||||
auto data = cache.get("/test");
|
||||
ASSERT_NE(nullptr, data.get());
|
||||
EXPECT_EQ(11U, data->load());
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200U));
|
||||
cache.purge_expired();
|
||||
EXPECT_FALSE(cache.contains("/test"));
|
||||
}
|
||||
} // namespace fifthgrid
|
||||
Reference in New Issue
Block a user