SDDS ToolKit Programs and Libraries for C and Python
Loading...
Searching...
No Matches
test_sdds3.cc
Go to the documentation of this file.
1/**
2 * @file test_sdds3.cc
3 * @brief Integration tests for the C++17 SDDS implementation.
4 *
5 * @details Covers layouts, typed pages, ASCII and binary files, compression,
6 * compatibility includes, and the deprecated migration facade.
7 *
8 * @copyright
9 * - (c) 2026 The University of Chicago
10 *
11 * @license
12 * This file is distributed under the terms of the Software License Agreement
13 * found in the file LICENSE included with this distribution.
14 */
15
16#include "SDDS.hpp"
17#include "SDDS3.h"
18
19#include <cassert>
20#include <cmath>
21#include <cstdint>
22#include <filesystem>
23#include <iostream>
24#include <string>
25#include <vector>
26
27static_assert(sdds::libraryName == "SDDS++");
28
29namespace {
30
31sdds::Layout makeLayout(sdds::DataMode mode, sdds::MajorOrder major,
32 sdds::ByteOrder order, sdds::RowCountMode rowCounts) {
33 sdds::DataOptions data;
34 data.mode = mode;
35 data.majorOrder = major;
36 data.byteOrder = order;
37 data.rowCountMode = rowCounts;
38 sdds::LayoutBuilder builder;
39 builder.setDescription("C++17 round trip", "SDDS++ integration test")
40 .setDataOptions(data)
41 .addParameter({{"p64", std::nullopt, "count", std::nullopt, std::nullopt,
42 sdds::Type::Int64}, std::nullopt})
43 .addParameter({{"fixed", std::nullopt, std::nullopt, std::nullopt, std::nullopt,
44 sdds::Type::String}, std::string("fixed value")})
45 .addArray({{"matrix", std::nullopt, std::nullopt, std::nullopt, std::nullopt,
46 sdds::Type::UInt64}, 0, 2, std::string("group")})
47 .addColumn({{"ld", std::nullopt, std::nullopt, std::nullopt, std::nullopt,
48 sdds::Type::LongDouble}, 0})
49 .addColumn({{"value", std::nullopt, "m", std::nullopt, std::nullopt,
50 sdds::Type::Double}, 0})
51 .addColumn({{"u64", std::nullopt, std::nullopt, std::nullopt, std::nullopt,
52 sdds::Type::UInt64}, 0})
53 .addColumn({{"label", std::nullopt, std::nullopt, std::nullopt, std::nullopt,
54 sdds::Type::String}, 0})
55 .addColumn({{"code", std::nullopt, std::nullopt, std::nullopt, std::nullopt,
56 sdds::Type::Character}, 0})
57 .addAssociate({"source", std::string("source.sdds"), std::nullopt,
58 std::string("test associate"), std::nullopt, true});
59 return builder.build();
60}
61
62sdds::Page makePage(const std::shared_ptr<const sdds::Layout> &layout, std::int64_t base) {
63 sdds::Page page(layout);
64 page.setParameter("p64", base);
65 page.setParameter("fixed", std::string("fixed value"));
66 page.setArray("matrix", {{2, 2}, std::vector<std::uint64_t>{1, 2, 3, 4}});
67 page.setColumn("ld", std::vector<long double>{1.25L, -2.5L, 3.75L});
68 page.setColumn("value", std::vector<double>{0.5, 1.5, 2.5});
69 page.setColumn("u64", std::vector<std::uint64_t>{UINT64_C(1), UINT64_C(4294967297),
70 UINT64_C(18446744073709551614)});
71 page.setColumn("label", std::vector<std::string>{"plain", "space value", "bang!\\quote\""});
72 page.setColumn("code", std::vector<char>{'A', ' ', '!'});
73 return page;
74}
75
76void verifyPage(const sdds::Page &page, std::int64_t base) {
77 assert(page.rowCount() == 3);
78 assert(page.parameterAs<std::int64_t>("p64") == base);
79 assert(page.parameterAs<std::string>("fixed") == "fixed value");
80 assert((page.array("matrix").dimensions == std::vector<std::int32_t>{2, 2}));
81 assert((page.arrayAs<std::uint64_t>("matrix") == std::vector<std::uint64_t>{1, 2, 3, 4}));
82 assert(std::fabs(page.columnAs<long double>("ld")[2] - 3.75L) < 1e-12L);
83 assert(page.columnAs<double>("value")[1] == 1.5);
84 assert(page.columnAs<std::uint64_t>("u64")[1] == UINT64_C(4294967297));
85 assert(page.columnAs<std::string>("label")[2] == "bang!\\quote\"");
86 assert(page.columnAs<char>("code")[1] == ' ');
87}
88
89void roundTrip(const std::filesystem::path &path, sdds::DataMode mode,
90 sdds::MajorOrder major, sdds::ByteOrder order,
91 sdds::RowCountMode rowCounts) {
92 auto layout = makeLayout(mode, major, order, rowCounts);
93 auto shared = std::make_shared<const sdds::Layout>(layout);
94 auto writer = sdds::Writer::create(path, layout);
95 writer.write(makePage(shared, INT64_C(9223372036854775000)));
96 writer.write(makePage(shared, 17));
97 writer.close();
98
99 auto reader = sdds::Reader::open(path);
100 assert(reader.layout().version == 5);
101 auto first = reader.next();
102 assert(first && first->number() == 1);
103 verifyPage(*first, INT64_C(9223372036854775000));
104 auto second = reader.next();
105 assert(second && second->number() == 2);
106 verifyPage(*second, 17);
107 assert(!reader.next());
108 reader.close();
109}
110
111void selectionAndGoto(const std::filesystem::path &path) {
112 auto reader = sdds::Reader::open(path);
113 sdds::ReadRequest request;
114 request.rows.stride = 2;
115 auto page = reader.next(request);
116 assert(page && page->rowCount() == 2);
117 assert((page->columnAs<double>("value") == std::vector<double>{0.5, 2.5}));
118 reader.gotoPage(2);
119 page = reader.next();
120 assert(page && page->parameterAs<std::int64_t>("p64") == 17);
121 reader.close();
122}
123
124void appendAndUpdate(const std::filesystem::path &path) {
125 sdds::DataOptions data;
126 data.mode = sdds::DataMode::Binary;
127 sdds::LayoutBuilder builder;
128 builder.setDataOptions(data)
129 .addColumn({{"x", std::nullopt, std::nullopt, std::nullopt, std::nullopt,
130 sdds::Type::Int32}, 0});
131 const auto layout = builder.build();
132
133 auto writer = sdds::Writer::create(path, layout);
134 writer.beginPage(3);
135 writer.setColumn("x", std::vector<std::int32_t>{1, 2});
136 writer.updatePage();
137 writer.setColumn("x", std::vector<std::int32_t>{3}, 2);
138 writer.commitPage();
139 writer.close();
140
141 writer = sdds::Writer::append(path);
142 writer.beginPage(1);
143 writer.setColumn("x", std::vector<std::int32_t>{4});
144 writer.commitPage();
145 writer.close();
146
147 writer = sdds::Writer::appendToLastPage(path, 1);
148 writer.setColumn("x", std::vector<std::int32_t>{5, 6}, 1);
149 writer.updatePage(true);
150 writer.close();
151
152 auto reader = sdds::Reader::open(path);
153 auto first = reader.next();
154 assert(first && (first->columnAs<std::int32_t>("x") ==
155 std::vector<std::int32_t>{1, 2, 3}));
156 auto second = reader.next();
157 assert(second && (second->columnAs<std::int32_t>("x") ==
158 std::vector<std::int32_t>{4, 5, 6}));
159 assert(!reader.next());
160 reader.close();
161}
162
163void includesLimitsAndRecovery(const std::filesystem::path &directory,
164 const std::filesystem::path &testData) {
165 const auto previousDirectory = std::filesystem::current_path();
166 std::filesystem::current_path(testData.parent_path().parent_path());
167 auto reader = sdds::Reader::open(testData);
168 auto dataset = reader.readAll();
169 reader.close();
170 std::filesystem::current_path(previousDirectory);
171 assert(dataset.layout.parameters.size() == 1);
172 assert(dataset.pages.size() == 1);
173 assert(dataset.pages[0].parameterAs<std::string>("origin") == "included");
174 assert((dataset.pages[0].arrayAs<std::int32_t>("shape") ==
175 std::vector<std::int32_t>{4, 5, 6}));
176
177 const auto flattened = directory / "flattened-include.sdds";
178 auto writer = sdds::Writer::create(flattened, dataset.layout);
179 writer.write(dataset.pages[0]);
180 writer.close();
181 reader = sdds::Reader::open(flattened);
182 assert(reader.layout().parameters.size() == 1);
183 assert(reader.next());
184 reader.close();
185
186 const auto recoveryFile = directory / "fixed-recovery.sdds";
187 sdds::DataOptions data;
188 data.mode = sdds::DataMode::Binary;
189 data.rowCountMode = sdds::RowCountMode::Fixed;
190 sdds::LayoutBuilder builder;
191 builder.setDataOptions(data)
192 .addColumn({{"x", std::nullopt, std::nullopt, std::nullopt, std::nullopt,
193 sdds::Type::Int32}, 0});
194 const auto layout = builder.build();
195 writer = sdds::Writer::create(recoveryFile, layout);
196 writer.beginPage(3);
197 writer.setColumn("x", std::vector<std::int32_t>{1, 2, 3});
198 writer.commitPage();
199 writer.close();
200 std::filesystem::resize_file(recoveryFile, std::filesystem::file_size(recoveryFile) - 2);
201
202 reader = sdds::Reader::open(recoveryFile);
203 auto recovered = reader.next();
204 assert(recovered && recovered->recovered() && recovered->rowCount() == 2);
205 reader.close();
206
207 sdds::ReaderOptions strict;
208 strict.recovery = sdds::RecoveryMode::Strict;
209 bool threw = false;
210 try {
211 reader = sdds::Reader::open(recoveryFile, strict);
212 (void)reader.next();
213 } catch (const sdds::FormatError &) {
214 threw = true;
215 }
216 assert(threw);
217
218 sdds::ReaderOptions limited;
219 limited.limits.maxRows = 1;
220 threw = false;
221 try {
222 reader = sdds::Reader::open(directory / "binary-little.sdds", limited);
223 (void)reader.next();
224 } catch (const sdds::LimitError &) {
225 threw = true;
226 }
227 assert(threw);
228}
229
230void legacyFacade(const std::filesystem::path &path) {
231#if defined(__GNUC__)
232# pragma GCC diagnostic push
233# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
234#endif
235 SDDSFile file(path.string().c_str(), true);
236 char parameter[] = "p";
237 char column[] = "x";
238 assert(file.defineParameter(parameter, SDDS_LONG64) == 0);
239 assert(file.defineColumn(column, SDDS_DOUBLE) == 0);
240 assert(file.setParameter(1, parameter, 7.0) == 1);
241 double values[] = {1, 2};
242 assert(file.setColumn(column, 1, 0, values, 2) == 1);
243 assert(file.writeFile() == 1);
244
245 SDDSFile input(path.string().c_str());
246 assert(input.readFile() == 1);
247 assert(input.pageCount() == 1);
248 assert(input.getParameterInDouble(parameter, 1) == 7);
249 assert(input.getColumnInDouble(column, 1)[1] == 2);
250#if defined(__GNUC__)
251# pragma GCC diagnostic pop
252#endif
253}
254
255} // namespace
256
257int main(int argc, char **argv) {
258 if (argc < 2) {
259 std::cerr << "usage: test_sddspp ARTIFACT_DIRECTORY [SDDS_FILE...]\n";
260 return 2;
261 }
262 const std::filesystem::path directory(argv[1]);
263 std::filesystem::create_directories(directory);
264 roundTrip(directory / "ascii.sdds", sdds::DataMode::Ascii, sdds::MajorOrder::Row,
265 sdds::ByteOrder::Native, sdds::RowCountMode::Variable);
266 roundTrip(directory / "ascii-no-count.sdds", sdds::DataMode::Ascii, sdds::MajorOrder::Row,
267 sdds::ByteOrder::Native, sdds::RowCountMode::None);
268 roundTrip(directory / "binary-little.sdds", sdds::DataMode::Binary, sdds::MajorOrder::Row,
269 sdds::ByteOrder::Little, sdds::RowCountMode::Variable);
270 roundTrip(directory / "binary-big.sdds", sdds::DataMode::Binary, sdds::MajorOrder::Column,
271 sdds::ByteOrder::Big, sdds::RowCountMode::Variable);
272 roundTrip(directory / "binary.sdds.gz", sdds::DataMode::Binary, sdds::MajorOrder::Row,
273 sdds::ByteOrder::Little, sdds::RowCountMode::Variable);
274 roundTrip(directory / "binary.sdds.xz", sdds::DataMode::Binary, sdds::MajorOrder::Column,
275 sdds::ByteOrder::Little, sdds::RowCountMode::Variable);
276 selectionAndGoto(directory / "binary-little.sdds");
277 appendAndUpdate(directory / "append-update.sdds");
278 const auto testData = std::filesystem::absolute(argv[0]).parent_path().parent_path() /
279 "testdata/include-main.sdds";
280 includesLimitsAndRecovery(directory, testData);
281 legacyFacade(directory / "legacy.sdds");
282 for (int argument = 2; argument < argc; ++argument) {
283 auto reader = sdds::Reader::open(argv[argument]);
284 auto dataset = reader.readAll();
285 assert(!dataset.pages.empty());
286 reader.close();
287 }
288 std::cout << "SDDS++ C++17 integration tests passed\n";
289 return 0;
290}
#define SDDS_DOUBLE
Identifier for the double data type.
Definition SDDStypes.h:37
#define SDDS_LONG64
Identifier for the signed 64-bit integer data type.
Definition SDDStypes.h:49