Skip to content

Commit 8c60fd5

Browse files
WIP: Add tests for reflection table
1 parent 41e889b commit 8c60fd5

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed

tests/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ target_link_libraries(test_read_h5_array GTest::gtest_main dx2 hdf5::hdf5)
88
add_executable(test_write_h5_array test_write_h5_array.cxx)
99
target_link_libraries(test_write_h5_array GTest::gtest_main dx2 hdf5::hdf5)
1010

11+
add_executable(test_reflection_table test_reflection_table.cxx)
12+
target_link_libraries(test_reflection_table GTest::gtest_main dx2)
13+
1114
include(GoogleTest)
1215

1316
gtest_discover_tests(test_crystal PROPERTIES LABELS dx2tests)
1417
gtest_discover_tests(test_read_h5_array WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/tests" PROPERTIES LABELS dx2tests)
1518
gtest_discover_tests(test_write_h5_array WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/tests" PROPERTIES LABELS dx2tests)
19+
gtest_discover_tests(test_reflection_table WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/tests" PROPERTIES LABELS dx2tests)

tests/test_reflection_table.cxx

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
#include <dx2/reflection.hpp>
2+
#include <filesystem>
3+
#include <gtest/gtest.h>
4+
#include <iostream>
5+
#include <optional>
6+
#include <string>
7+
#include <vector>
8+
9+
/*
10+
* Test fixture for ReflectionTable tests. This fixture sets up the test
11+
* environment for the ReflectionTable class and provides a common path
12+
* to the test file.
13+
*/
14+
class ReflectionTableTest : public ::testing::Test {
15+
protected:
16+
std::filesystem::path test_file_path;
17+
18+
void SetUp() override {
19+
test_file_path = std::filesystem::current_path() / "data/cut_strong.refl";
20+
}
21+
};
22+
23+
TEST_F(ReflectionTableTest, LoadsAllColumns) {
24+
ReflectionTable table(test_file_path.string());
25+
26+
auto names = table.get_column_names();
27+
std::cout << "Loaded column names:\n";
28+
for (const auto &name : names) {
29+
std::cout << " - " << name << "\n";
30+
}
31+
32+
EXPECT_FALSE(names.empty());
33+
EXPECT_NE(std::find(names.begin(), names.end(), "xyzobs.px.value"),
34+
names.end());
35+
}
36+
37+
TEST_F(ReflectionTableTest, TryGetColumnSucceedsOnCorrectType) {
38+
ReflectionTable table(test_file_path.string());
39+
40+
auto px = table.get_column<double>("xyzobs.px.value");
41+
ASSERT_TRUE(px.has_value());
42+
43+
const auto &span = px.value();
44+
std::cout << "xyzobs.px.value shape: " << span.extent(0) << "x"
45+
<< span.extent(1) << "\n";
46+
EXPECT_EQ(span.extent(1), 3);
47+
EXPECT_GT(span.extent(0), 0);
48+
}
49+
50+
TEST_F(ReflectionTableTest, TryGetColumnFailsOnWrongType) {
51+
ReflectionTable table(test_file_path.string());
52+
53+
auto col = table.get_column<int>("xyzobs.px.value");
54+
std::cout << "Column retrieval (int): "
55+
<< (col.has_value() ? "found" : "not found") << "\n";
56+
EXPECT_FALSE(col.has_value());
57+
}
58+
59+
TEST_F(ReflectionTableTest, SelectSubsetUsingFindRows) {
60+
ReflectionTable table(test_file_path.string());
61+
62+
auto px = table.get_column<double>("xyzobs.px.value");
63+
ASSERT_TRUE(px);
64+
65+
const auto &span = px.value();
66+
67+
auto selected = table.find_rows([&](size_t i) {
68+
return span(i, 2) > 1.0; // z > 1.0
69+
});
70+
71+
std::cout << "Selected " << selected.size() << " rows where z > 1.0\n";
72+
EXPECT_FALSE(selected.empty());
73+
74+
auto filtered = table.select(selected);
75+
auto filtered_px = filtered.get_column<double>("xyzobs.px.value");
76+
ASSERT_TRUE(filtered_px);
77+
const auto &filtered_span = filtered_px.value();
78+
79+
for (size_t i = 0; i < std::min<size_t>(filtered_span.extent(0), 5); ++i) {
80+
std::cout << "z[" << i << "] = " << filtered_span(i, 2) << "\n";
81+
}
82+
83+
for (size_t i = 0; i < filtered_span.extent(0); ++i) {
84+
EXPECT_GT(filtered_span(i, 2), 1.0);
85+
}
86+
}
87+
88+
TEST_F(ReflectionTableTest, SelectWithMaskReturnsSameResultAsExplicitIndices) {
89+
ReflectionTable table(test_file_path.string());
90+
91+
auto px = table.get_column<double>("xyzobs.px.value");
92+
ASSERT_TRUE(px);
93+
const auto &span = px.value();
94+
95+
std::vector<bool> mask(span.extent(0), false);
96+
std::vector<size_t> indices;
97+
98+
for (size_t i = 0; i < span.extent(0); ++i) {
99+
if (span(i, 2) > 1.0) {
100+
mask[i] = true;
101+
indices.push_back(i);
102+
}
103+
}
104+
105+
std::cout << "Mask selected " << indices.size() << " rows\n";
106+
107+
auto table_from_mask = table.select(mask);
108+
auto table_from_indices = table.select(indices);
109+
110+
auto mask_span =
111+
table_from_mask.get_column<double>("xyzobs.px.value").value();
112+
auto idx_span =
113+
table_from_indices.get_column<double>("xyzobs.px.value").value();
114+
115+
ASSERT_EQ(mask_span.extent(0), idx_span.extent(0));
116+
ASSERT_EQ(mask_span.extent(1), idx_span.extent(1));
117+
118+
for (size_t i = 0; i < std::min<size_t>(mask_span.extent(0), 3); ++i) {
119+
std::cout << "Row " << i << ": "
120+
<< "mask_z = " << mask_span(i, 2)
121+
<< ", idx_z = " << idx_span(i, 2) << "\n";
122+
}
123+
124+
for (size_t i = 0; i < mask_span.extent(0); ++i) {
125+
for (size_t j = 0; j < mask_span.extent(1); ++j) {
126+
EXPECT_EQ(mask_span(i, j), idx_span(i, j));
127+
}
128+
}
129+
}
130+
131+
TEST_F(ReflectionTableTest, SelectEmptyReturnsEmptyTable) {
132+
ReflectionTable table(test_file_path.string());
133+
ReflectionTable empty = table.select(std::vector<size_t>{});
134+
135+
std::cout << "Selecting with empty row set...\n";
136+
for (const auto &name : table.get_column_names()) {
137+
auto span = empty.get_column<double>(name);
138+
if (span) {
139+
std::cout << " " << name << " has " << span->extent(0) << " rows\n";
140+
EXPECT_EQ(span.value().extent(0), 0);
141+
}
142+
}
143+
}
144+
145+
TEST_F(ReflectionTableTest, AllDatasetsLoadSuccessfully) {
146+
ReflectionTable table(test_file_path.string());
147+
148+
std::vector<std::string> expected = get_datasets_in_group(
149+
test_file_path.string(), "/dials/processing/group_0");
150+
151+
std::vector<std::string> loaded = table.get_column_names();
152+
153+
for (const std::string &full_dataset_path : expected) {
154+
std::string name = get_dataset_name(full_dataset_path);
155+
156+
// Make sure it's present
157+
auto it = std::find(loaded.begin(), loaded.end(), name);
158+
EXPECT_NE(it, loaded.end()) << "Dataset not loaded: " << name;
159+
160+
if (it != loaded.end()) {
161+
std::cout << "✅ Dataset loaded: " << name << "\n";
162+
} else {
163+
std::cerr << "❌ Dataset missing: " << name << "\n";
164+
}
165+
}
166+
}

0 commit comments

Comments
 (0)