Skip to content

Commit 267a75a

Browse files
authored
Merge pull request #23 from poyrazK/test/coverage-add-operators
test: add unit tests for operators, storage, and distributed
2 parents 98a22b5 + 4dd759a commit 267a75a

File tree

7 files changed

+1660
-0
lines changed

7 files changed

+1660
-0
lines changed

CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ if(BUILD_TESTS)
131131
add_cloudsql_test(multi_raft_tests tests/multi_raft_tests.cpp)
132132
add_cloudsql_test(distributed_txn_tests tests/distributed_txn_tests.cpp)
133133
add_cloudsql_test(analytics_tests tests/analytics_tests.cpp)
134+
add_cloudsql_test(raft_manager_tests tests/raft_manager_tests.cpp)
135+
add_cloudsql_test(raft_protocol_tests tests/raft_protocol_tests.cpp)
136+
add_cloudsql_test(columnar_table_tests tests/columnar_table_tests.cpp)
137+
add_cloudsql_test(storage_manager_tests tests/storage_manager_tests.cpp)
138+
add_cloudsql_test(rpc_server_tests tests/rpc_server_tests.cpp)
139+
add_cloudsql_test(operator_tests tests/operator_tests.cpp)
134140

135141
add_custom_target(run-tests
136142
COMMAND ${CMAKE_CTEST_COMMAND}

tests/columnar_table_tests.cpp

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
/**
2+
* @file columnar_table_tests.cpp
3+
* @brief Unit tests for ColumnarTable - column-oriented storage
4+
*/
5+
6+
#include <gtest/gtest.h>
7+
8+
#include <cstdint>
9+
#include <cstdio>
10+
#include <memory>
11+
#include <vector>
12+
13+
#include "common/value.hpp"
14+
#include "executor/types.hpp"
15+
#include "storage/columnar_table.hpp"
16+
#include "storage/storage_manager.hpp"
17+
18+
using namespace cloudsql;
19+
using namespace cloudsql::storage;
20+
using namespace cloudsql::executor;
21+
22+
namespace {
23+
24+
static void cleanup_table(const std::string& name) {
25+
// clang-format off
26+
std::remove(("./test_data/" + name + ".meta.bin").c_str());
27+
std::remove(("./test_data/" + name + ".col0.nulls.bin").c_str());
28+
std::remove(("./test_data/" + name + ".col0.data.bin").c_str());
29+
std::remove(("./test_data/" + name + ".col1.nulls.bin").c_str());
30+
std::remove(("./test_data/" + name + ".col1.data.bin").c_str());
31+
// clang-format on
32+
}
33+
34+
class ColumnarTableTests : public ::testing::Test {
35+
protected:
36+
void SetUp() override {
37+
sm_ = std::make_unique<StorageManager>("./test_data");
38+
sm_->create_dir_if_not_exists();
39+
}
40+
41+
void TearDown() override { sm_.reset(); }
42+
43+
std::unique_ptr<StorageManager> sm_;
44+
};
45+
46+
TEST_F(ColumnarTableTests, BasicInt64Lifecycle) {
47+
const std::string name = "col_test_int";
48+
cleanup_table(name);
49+
50+
Schema schema;
51+
schema.add_column("id", common::ValueType::TYPE_INT64);
52+
schema.add_column("val", common::ValueType::TYPE_INT64);
53+
54+
ColumnarTable table(name, *sm_, schema);
55+
56+
ASSERT_TRUE(table.create());
57+
ASSERT_TRUE(table.open());
58+
ASSERT_EQ(table.row_count(), 0U);
59+
60+
// Build batch with 2 rows
61+
auto batch = VectorBatch::create(schema);
62+
batch->get_column(0).append(common::Value::make_int64(1));
63+
batch->get_column(0).append(common::Value::make_int64(2));
64+
batch->get_column(1).append(common::Value::make_int64(100));
65+
batch->get_column(1).append(common::Value::make_int64(200));
66+
batch->set_row_count(2);
67+
68+
ASSERT_TRUE(table.append_batch(*batch));
69+
ASSERT_EQ(table.row_count(), 2U);
70+
71+
// Reopen and verify
72+
ColumnarTable table2(name, *sm_, schema);
73+
ASSERT_TRUE(table2.open());
74+
ASSERT_EQ(table2.row_count(), 2U);
75+
76+
auto read_batch = VectorBatch::create(schema);
77+
ASSERT_TRUE(table2.read_batch(0, 10, *read_batch));
78+
ASSERT_EQ(read_batch->row_count(), 2U);
79+
}
80+
81+
TEST_F(ColumnarTableTests, BasicFloat64Lifecycle) {
82+
const std::string name = "col_test_float";
83+
cleanup_table(name);
84+
85+
Schema schema;
86+
schema.add_column("x", common::ValueType::TYPE_FLOAT64);
87+
schema.add_column("y", common::ValueType::TYPE_FLOAT64);
88+
89+
ColumnarTable table(name, *sm_, schema);
90+
ASSERT_TRUE(table.create());
91+
92+
auto batch = VectorBatch::create(schema);
93+
batch->get_column(0).append(common::Value::make_float64(1.5));
94+
batch->get_column(1).append(common::Value::make_float64(2.7));
95+
batch->set_row_count(1);
96+
ASSERT_TRUE(table.append_batch(*batch));
97+
ASSERT_EQ(table.row_count(), 1U);
98+
99+
auto out = VectorBatch::create(schema);
100+
ASSERT_TRUE(table.read_batch(0, 1, *out));
101+
ASSERT_EQ(out->row_count(), 1U);
102+
103+
auto& col_x = dynamic_cast<NumericVector<double>&>(out->get_column(0));
104+
EXPECT_FLOAT_EQ(col_x.get(0).to_float64(), 1.5);
105+
}
106+
107+
TEST_F(ColumnarTableTests, NullValueHandling) {
108+
const std::string name = "col_test_null";
109+
cleanup_table(name);
110+
111+
Schema schema;
112+
schema.add_column("nullable_col", common::ValueType::TYPE_INT64);
113+
114+
ColumnarTable table(name, *sm_, schema);
115+
ASSERT_TRUE(table.create());
116+
117+
auto batch = VectorBatch::create(schema);
118+
batch->get_column(0).append(common::Value::make_null());
119+
batch->get_column(0).append(common::Value::make_int64(42));
120+
batch->set_row_count(2);
121+
ASSERT_TRUE(table.append_batch(*batch));
122+
123+
auto out = VectorBatch::create(schema);
124+
ASSERT_TRUE(table.read_batch(0, 2, *out));
125+
ASSERT_EQ(out->row_count(), 2U);
126+
127+
auto& col = dynamic_cast<NumericVector<int64_t>&>(out->get_column(0));
128+
EXPECT_TRUE(col.is_null(0));
129+
EXPECT_FALSE(col.is_null(1));
130+
EXPECT_EQ(col.get(1).to_int64(), 42);
131+
}
132+
133+
TEST_F(ColumnarTableTests, MultiBatchAppendRead) {
134+
const std::string name = "col_test_multi";
135+
cleanup_table(name);
136+
137+
Schema schema;
138+
schema.add_column("val", common::ValueType::TYPE_INT64);
139+
140+
ColumnarTable table(name, *sm_, schema);
141+
ASSERT_TRUE(table.create());
142+
143+
// Append 3 batches of 100 rows each
144+
for (int batch_num = 0; batch_num < 3; ++batch_num) {
145+
auto batch = VectorBatch::create(schema);
146+
for (int i = 0; i < 100; ++i) {
147+
batch->get_column(0).append(common::Value::make_int64(batch_num * 100 + i));
148+
}
149+
batch->set_row_count(100);
150+
ASSERT_TRUE(table.append_batch(*batch));
151+
}
152+
153+
ASSERT_EQ(table.row_count(), 300U);
154+
155+
// Read in pages
156+
auto out = VectorBatch::create(schema);
157+
ASSERT_TRUE(table.read_batch(0, 100, *out));
158+
ASSERT_EQ(out->row_count(), 100U);
159+
160+
out = VectorBatch::create(schema);
161+
ASSERT_TRUE(table.read_batch(100, 100, *out));
162+
ASSERT_EQ(out->row_count(), 100U);
163+
164+
out = VectorBatch::create(schema);
165+
ASSERT_TRUE(table.read_batch(200, 100, *out));
166+
ASSERT_EQ(out->row_count(), 100U);
167+
}
168+
169+
TEST_F(ColumnarTableTests, ReadBatchBeyondEnd) {
170+
const std::string name = "col_test_beyond";
171+
cleanup_table(name);
172+
173+
Schema schema;
174+
schema.add_column("id", common::ValueType::TYPE_INT64);
175+
176+
ColumnarTable table(name, *sm_, schema);
177+
ASSERT_TRUE(table.create());
178+
179+
auto batch = VectorBatch::create(schema);
180+
batch->get_column(0).append(common::Value::make_int64(1));
181+
batch->set_row_count(1);
182+
ASSERT_TRUE(table.append_batch(*batch));
183+
184+
auto out = VectorBatch::create(schema);
185+
EXPECT_FALSE(table.read_batch(5, 10, *out));
186+
EXPECT_FALSE(table.read_batch(1, 10, *out));
187+
}
188+
189+
TEST_F(ColumnarTableTests, ReadBatchPartial) {
190+
const std::string name = "col_test_partial";
191+
cleanup_table(name);
192+
193+
Schema schema;
194+
schema.add_column("id", common::ValueType::TYPE_INT64);
195+
196+
ColumnarTable table(name, *sm_, schema);
197+
ASSERT_TRUE(table.create());
198+
199+
auto batch = VectorBatch::create(schema);
200+
for (int i = 0; i < 10; ++i) {
201+
batch->get_column(0).append(common::Value::make_int64(i));
202+
}
203+
batch->set_row_count(10);
204+
ASSERT_TRUE(table.append_batch(*batch));
205+
206+
// Read starting mid-table with batch_size larger than remaining
207+
auto out = VectorBatch::create(schema);
208+
ASSERT_TRUE(table.read_batch(8, 100, *out));
209+
ASSERT_EQ(out->row_count(), 2U);
210+
}
211+
212+
TEST_F(ColumnarTableTests, UnsupportedTypeThrows) {
213+
const std::string name = "col_test_unsupported";
214+
cleanup_table(name);
215+
216+
Schema schema;
217+
schema.add_column("text_col", common::ValueType::TYPE_TEXT);
218+
219+
// VectorBatch::create() throws when it sees TYPE_TEXT (unsupported)
220+
EXPECT_THROW([[maybe_unused]] auto batch = VectorBatch::create(schema), std::runtime_error);
221+
}
222+
223+
TEST_F(ColumnarTableTests, CreateTwice) {
224+
const std::string name = "col_test_twice";
225+
cleanup_table(name);
226+
227+
Schema schema;
228+
schema.add_column("id", common::ValueType::TYPE_INT64);
229+
230+
ColumnarTable table(name, *sm_, schema);
231+
ASSERT_TRUE(table.create());
232+
233+
// Second create() on existing files - behavior depends on ofstream flags
234+
// This is a basic sanity check
235+
ASSERT_TRUE(table.create());
236+
}
237+
238+
TEST_F(ColumnarTableTests, OpenWithoutCreate) {
239+
const std::string name = "col_test_missing";
240+
241+
Schema schema;
242+
schema.add_column("id", common::ValueType::TYPE_INT64);
243+
244+
ColumnarTable table(name, *sm_, schema);
245+
ASSERT_FALSE(table.open());
246+
}
247+
248+
TEST_F(ColumnarTableTests, EmptyBatch) {
249+
const std::string name = "col_test_empty";
250+
cleanup_table(name);
251+
252+
Schema schema;
253+
schema.add_column("id", common::ValueType::TYPE_INT64);
254+
255+
ColumnarTable table(name, *sm_, schema);
256+
ASSERT_TRUE(table.create());
257+
258+
auto batch = VectorBatch::create(schema);
259+
batch->set_row_count(0);
260+
ASSERT_TRUE(table.append_batch(*batch));
261+
ASSERT_EQ(table.row_count(), 0U);
262+
263+
auto out = VectorBatch::create(schema);
264+
ASSERT_FALSE(table.read_batch(0, 10, *out));
265+
}
266+
267+
TEST_F(ColumnarTableTests, SchemaAccessor) {
268+
const std::string name = "col_test_schema";
269+
cleanup_table(name);
270+
271+
Schema schema;
272+
schema.add_column("col1", common::ValueType::TYPE_INT64);
273+
schema.add_column("col2", common::ValueType::TYPE_FLOAT64);
274+
275+
ColumnarTable table(name, *sm_, schema);
276+
ASSERT_TRUE(table.create());
277+
278+
const auto& retrieved_schema = table.schema();
279+
ASSERT_EQ(retrieved_schema.column_count(), 2U);
280+
ASSERT_EQ(retrieved_schema.get_column(0).name(), "col1");
281+
ASSERT_EQ(retrieved_schema.get_column(1).type(), common::ValueType::TYPE_FLOAT64);
282+
}
283+
284+
} // namespace

0 commit comments

Comments
 (0)