Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/utils/linear_congruential_generator_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

// Copyright 2024-present the vsag project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "linear_congruential_generator.h"

#include <catch2/catch_test_macros.hpp>

using namespace vsag;

TEST_CASE("LinearCongruentialGenerator Basic", "[ut][LinearCongruentialGenerator]") {
LinearCongruentialGenerator gen;

SECTION("value range") {
for (int i = 0; i < 2000; ++i) {
float v = gen.NextFloat();
REQUIRE(v >= 0.0F);
REQUIRE(v <= 1.0F);
}
}

SECTION("sequence changes") {
float first = gen.NextFloat();
bool has_different = false;
for (int i = 0; i < 100; ++i) {
if (gen.NextFloat() != first) {
has_different = true;
break;
}
}
REQUIRE(has_different);
}
}
78 changes: 78 additions & 0 deletions src/utils/lock_strategy_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@

// Copyright 2024-present the vsag project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "lock_strategy.h"

#include <catch2/catch_test_macros.hpp>

#include <thread>

#include "impl/allocator/default_allocator.h"

using namespace vsag;

TEST_CASE("LockStrategy Basic", "[ut][LockStrategy]") {
auto allocator = std::make_shared<DefaultAllocator>();

SECTION("points mutex lock and shared lock") {
PointsMutex mutex_array(8, allocator.get());
mutex_array.Lock(0);
mutex_array.Unlock(0);
mutex_array.SharedLock(0);
mutex_array.SharedUnlock(0);
REQUIRE(mutex_array.GetMemoryUsage() > 0);
}

SECTION("resize updates memory usage") {
PointsMutex mutex_array(4, allocator.get());
auto before = mutex_array.GetMemoryUsage();
mutex_array.Resize(10);
auto after = mutex_array.GetMemoryUsage();
REQUIRE(after > before);
mutex_array.Resize(2);
REQUIRE(mutex_array.GetMemoryUsage() < after);
Comment on lines +43 to +45
}

SECTION("lock guards protect concurrent increment") {
auto mutex_impl = std::make_shared<PointsMutex>(1, allocator.get());
int counter = 0;
constexpr int thread_num = 8;
constexpr int loops = 1000;
std::vector<std::thread> threads;
threads.reserve(thread_num);
for (int i = 0; i < thread_num; ++i) {
threads.emplace_back([&]() {
for (int j = 0; j < loops; ++j) {
LockGuard guard(mutex_impl, 0);
++counter;
}
});
}
for (auto& t : threads) {
t.join();
}
REQUIRE(counter == thread_num * loops);
}

SECTION("empty mutex no-op") {
EmptyMutex mutex_array;
mutex_array.Lock(0);
mutex_array.Unlock(0);
mutex_array.SharedLock(0);
mutex_array.SharedUnlock(0);
mutex_array.Resize(100);
REQUIRE(mutex_array.GetMemoryUsage() == 0);
}
}
42 changes: 42 additions & 0 deletions src/utils/slow_task_timer_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

// Copyright 2024-present the vsag project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "slow_task_timer.h"

#include <catch2/catch_test_macros.hpp>

#include <thread>

using namespace vsag;

TEST_CASE("SlowTaskTimer Basic", "[ut][SlowTaskTimer]") {
SECTION("construct and destruct with default threshold") {
SlowTaskTimer timer("default-threshold");
REQUIRE(timer.name == "default-threshold");
REQUIRE(timer.threshold == 0);
}

SECTION("construct and destruct with custom threshold") {
SlowTaskTimer timer("custom-threshold", 1000);
REQUIRE(timer.name == "custom-threshold");
REQUIRE(timer.threshold == 1000);
}

SECTION("slow path with tiny threshold") {
SlowTaskTimer timer("tiny-threshold", 0);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
REQUIRE(timer.threshold == 0);
}
}
68 changes: 68 additions & 0 deletions src/utils/sparse_vector_transform_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@

// Copyright 2024-present the vsag project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "sparse_vector_transform.h"

#include <catch2/catch_test_macros.hpp>

#include "impl/allocator/default_allocator.h"

using namespace vsag;

TEST_CASE("SparseVectorTransform Basic", "[ut][SparseVectorTransform]") {
auto allocator = std::make_shared<DefaultAllocator>();

SECTION("sort sparse vector by value descending") {
std::vector<uint32_t> ids{1, 2, 3};
std::vector<float> vals{0.5F, 1.2F, 0.7F};
SparseVector sparse{static_cast<uint32_t>(ids.size()), ids.data(), vals.data()};

Vector<std::pair<uint32_t, float>> sorted(allocator.get());
sort_sparse_vector(sparse, sorted);
REQUIRE(sorted.size() == 3);
REQUIRE(sorted[0].first == 2);
REQUIRE(sorted[1].first == 3);
REQUIRE(sorted[2].first == 1);
}

SECTION("subset positive and negative cases") {
std::vector<uint32_t> ids1{1, 2};
std::vector<float> vals1{0.1F, 0.2F};
SparseVector sv1{static_cast<uint32_t>(ids1.size()), ids1.data(), vals1.data()};

std::vector<uint32_t> ids2{1, 2, 3};
std::vector<float> vals2{0.1F, 0.2F, 0.3F};
SparseVector sv2{static_cast<uint32_t>(ids2.size()), ids2.data(), vals2.data()};
REQUIRE(is_subset_of_sparse_vector(sv1, sv2));

std::vector<uint32_t> ids3{1, 2};
std::vector<float> vals3{0.1F, 0.25F};
SparseVector sv3{static_cast<uint32_t>(ids3.size()), ids3.data(), vals3.data()};
REQUIRE_FALSE(is_subset_of_sparse_vector(sv3, sv2));

std::vector<uint32_t> ids4{1, 4};
std::vector<float> vals4{0.1F, 0.2F};
SparseVector sv4{static_cast<uint32_t>(ids4.size()), ids4.data(), vals4.data()};
REQUIRE_FALSE(is_subset_of_sparse_vector(sv4, sv2));
}

SECTION("empty sparse vector is subset") {
SparseVector empty{};
std::vector<uint32_t> ids{1};
std::vector<float> vals{0.1F};
SparseVector non_empty{static_cast<uint32_t>(ids.size()), ids.data(), vals.data()};
REQUIRE(is_subset_of_sparse_vector(empty, non_empty));
}
}
47 changes: 47 additions & 0 deletions src/utils/timer_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@

// Copyright 2024-present the vsag project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "timer.h"

#include <catch2/catch_test_macros.hpp>

#include <thread>

using namespace vsag;

TEST_CASE("Timer Basic", "[ut][Timer]") {
SECTION("record elapsed time") {
Timer timer;
std::this_thread::sleep_for(std::chrono::milliseconds(2));
auto elapsed_ms = timer.Record();
REQUIRE(elapsed_ms >= 1.0);
Comment on lines +27 to +29
}

SECTION("threshold and overtime") {
Timer timer;
timer.SetThreshold(1.0);
std::this_thread::sleep_for(std::chrono::milliseconds(2));
REQUIRE(timer.CheckOvertime());
}

SECTION("write back to reference in destructor") {
double cost = 0.0;
{
Timer timer(cost);
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
REQUIRE(cost >= 1.0);
}
}
107 changes: 107 additions & 0 deletions src/utils/util_functions_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@

// Copyright 2024-present the vsag project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "util_functions.h"

#include <catch2/catch_test_macros.hpp>

#include <cmath>
#include <sstream>
#include <unordered_set>

#include "impl/allocator/default_allocator.h"

using namespace vsag;

TEST_CASE("UtilFunctions Basic", "[ut][UtilFunctions]") {
SECTION("format map") {
std::unordered_map<std::string, std::string> mappings{{"name", "vsag"}, {"v", "1.0"}};
std::string formatted = format_map("{name}-{v}", mappings);
REQUIRE(formatted == "vsag-1.0");
}

SECTION("split string") {
auto parts = split_string("a,,b", ',');
REQUIRE(parts.size() == 3);
REQUIRE(parts[0] == "a");
REQUIRE(parts[1].empty());
REQUIRE(parts[2] == "b");
}

SECTION("align up") {
REQUIRE(align_up(7, 4) == 8);
REQUIRE(align_up(8, 4) == 8);
}

SECTION("approx zero") {
REQUIRE(is_approx_zero(1e-6F));
REQUIRE_FALSE(is_approx_zero(1e-3F));
}

SECTION("next multiple of power of two") {
REQUIRE(next_multiple_of_power_of_two(5, 2) == 8);
REQUIRE(next_multiple_of_power_of_two(16, 4) == 16);
REQUIRE_THROWS(next_multiple_of_power_of_two(1, 64));
}

SECTION("select k numbers") {
auto selected = select_k_numbers(100, 10);
REQUIRE(selected.size() == 10);
std::unordered_set<int> dedup(selected.begin(), selected.end());
REQUIRE(dedup.size() == 10);
for (int v : selected) {
REQUIRE(v >= 0);
REQUIRE(v < 100);
}
REQUIRE_THROWS(select_k_numbers(10, 0));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test asserts that select_k_numbers(10, 0) throws an exception. While the test correctly reflects the current implementation's behavior, it highlights a potential area for API improvement. In many standard libraries and conventions, requesting 0 elements is a valid operation that should return an empty collection rather than throwing an error. Consider updating select_k_numbers to handle k=0 gracefully by returning an empty vector. The test could then be changed to REQUIRE(select_k_numbers(10, 0).empty());, leading to a more intuitive and robust API.

REQUIRE_THROWS(select_k_numbers(10, 11));
}

SECTION("string stream equality") {
std::stringstream s1;
std::stringstream s2;
s1 << "abc";
s2 << "abc";
REQUIRE(check_equal_on_string_stream(s1, s2));

std::stringstream s3;
std::stringstream s4;
s3 << "abc";
s4 << "abd";
REQUIRE_FALSE(check_equal_on_string_stream(s3, s4));
}

SECTION("base64 roundtrip") {
std::string input = "vsag-base64-roundtrip";
std::string encoded = base64_encode(input);
std::string decoded = base64_decode(encoded);
REQUIRE(decoded == input);
}

SECTION("create fast dataset") {
auto allocator = std::make_shared<DefaultAllocator>();
auto [dataset, dists, ids] = create_fast_dataset(4, allocator.get());
REQUIRE(dataset != nullptr);
REQUIRE(dataset->GetDim() == 4);
REQUIRE(dataset->GetNumElements() == 1);
REQUIRE(dists != nullptr);
REQUIRE(ids != nullptr);
}

SECTION("current time string") {
auto t = get_current_time();
REQUIRE_FALSE(t.empty());
}
}
Loading
Loading