Line data Source code
1 : #define BOOST_TEST_DYN_LINK
2 : #define BOOST_TEST_MODULE (StringRoundTrip test)
3 :
4 : #include <algorithm>
5 : #include <boost/test/unit_test.hpp>
6 : #include <fstream>
7 : #include <numeric>
8 : #include <random>
9 : #include <sstream>
10 :
11 : #include "../DatabaseConfigurationInterface.h"
12 : #include "artdaq-database/JsonDocument/JSONDocument.h"
13 :
14 : using namespace ots;
15 : using artdaq::database::docrecord::JSONDocument;
16 :
17 : namespace {
18 :
19 4 : bool isQuickMode() {
20 4 : auto* quick = getenv("QUICK_TEST");
21 4 : return quick && (std::string(quick) == "1" || std::string(quick) == "true");
22 : }
23 :
24 2 : std::vector<std::string> loadInputsFromFile(const std::string& filename) {
25 2 : std::vector<std::string> inputs;
26 2 : std::ifstream file(filename);
27 2 : if (!file) return inputs;
28 :
29 2 : std::stringstream buf;
30 2 : buf << file.rdbuf();
31 2 : std::string content = buf.str();
32 :
33 2 : size_t pos = 0;
34 178 : while ((pos = content.find("\"input\":", pos)) != std::string::npos) {
35 176 : size_t start = content.find('{', pos);
36 176 : if (start == std::string::npos) break;
37 :
38 176 : int depth = 1;
39 176 : size_t end = start + 1;
40 5904 : while (depth > 0 && end < content.size()) {
41 5728 : if (content[end] == '{')
42 8 : depth++;
43 5720 : else if (content[end] == '}')
44 184 : depth--;
45 5536 : else if (content[end] == '"') {
46 1958 : end++;
47 16792 : while (end < content.size() && content[end] != '"') {
48 14834 : if (content[end] == '\\') end++;
49 14834 : end++;
50 : }
51 : }
52 5728 : end++;
53 : }
54 176 : inputs.push_back(content.substr(start, end - start));
55 176 : pos = end;
56 : }
57 2 : return inputs;
58 2 : }
59 :
60 : struct TestFixture {
61 2 : TestFixture() : baseVersion((srand(time(nullptr)), rand() % 99999 + 100000)) {
62 2 : inputs = loadInputsFromFile("test_cases.json");
63 :
64 2 : if (isQuickMode() && inputs.size() > 10) {
65 0 : std::vector<size_t> idx(inputs.size());
66 0 : std::iota(idx.begin(), idx.end(), 0);
67 0 : std::shuffle(idx.begin(), idx.end(), std::mt19937{std::random_device{}()});
68 0 : idx.resize(inputs.size() / 10);
69 0 : std::sort(idx.begin(), idx.end());
70 0 : std::vector<std::string> sampled;
71 0 : for (auto i : idx) sampled.push_back(inputs[i]);
72 0 : inputs = std::move(sampled);
73 0 : }
74 :
75 2 : std::cout << "Loaded " << inputs.size() << " test cases" << (isQuickMode() ? " (QUICK MODE)" : "") << "\n";
76 2 : }
77 :
78 704 : int nextVersion() { return baseVersion + (++versionOffset); }
79 :
80 : const int baseVersion;
81 : std::vector<std::string> inputs;
82 :
83 : private:
84 : int versionOffset = 0;
85 : };
86 :
87 : } // namespace
88 :
89 : TestFixture fixture;
90 :
91 : BOOST_AUTO_TEST_SUITE(string_roundtrip_test)
92 :
93 4 : BOOST_AUTO_TEST_CASE(json_roundtrip_ast_comparison) {
94 2 : DatabaseConfigurationInterface ifc;
95 2 : int passed = 0, failed = 0;
96 :
97 178 : for (const auto& inputJson : fixture.inputs) {
98 176 : int version = fixture.nextVersion();
99 :
100 176 : auto writer = std::make_shared<TestConfiguration001>();
101 176 : writer->getView().fillFromJSON(inputJson);
102 176 : writer->getView().version = version;
103 176 : if (ifc.saveActiveVersion(writer.get()) != 0) {
104 0 : failed++;
105 0 : continue;
106 : }
107 :
108 176 : auto reader = std::make_shared<TestConfiguration001>();
109 176 : if (ifc.fill(reader.get(), version) != 0) {
110 0 : failed++;
111 0 : continue;
112 : }
113 :
114 176 : JSONDocument inputDoc(inputJson);
115 176 : JSONDocument outputDoc(reader->getView()._json);
116 :
117 176 : if (inputDoc == outputDoc) {
118 176 : passed++;
119 : } else {
120 0 : failed++;
121 0 : std::cout << "FAIL: " << inputJson.substr(0, 60) << "...\n";
122 : }
123 176 : }
124 :
125 2 : std::cout << "Passed: " << passed << "/" << fixture.inputs.size() << "\n";
126 2 : BOOST_CHECK_EQUAL(failed, 0);
127 2 : }
128 :
129 4 : BOOST_AUTO_TEST_CASE(multiple_roundtrips_stability) {
130 2 : DatabaseConfigurationInterface ifc;
131 2 : int stable = 0, unstable = 0;
132 :
133 178 : for (const auto& inputJson : fixture.inputs) {
134 176 : std::vector<std::string> reads;
135 176 : std::string current = inputJson;
136 :
137 704 : for (int round = 0; round < 3; ++round) {
138 528 : int version = fixture.nextVersion();
139 528 : auto writer = std::make_shared<TestConfiguration001>();
140 528 : writer->getView().fillFromJSON(current);
141 528 : writer->getView().version = version;
142 528 : if (ifc.saveActiveVersion(writer.get()) != 0) break;
143 :
144 528 : auto reader = std::make_shared<TestConfiguration001>();
145 528 : if (ifc.fill(reader.get(), version) != 0) break;
146 528 : current = reader->getView()._json;
147 528 : reads.push_back(current);
148 528 : }
149 :
150 176 : if (reads.size() == 3) {
151 176 : JSONDocument d0(inputJson), d1(reads[0]), d2(reads[1]), d3(reads[2]);
152 176 : if (d0 == d1 && d1 == d2 && d2 == d3) {
153 176 : stable++;
154 : } else {
155 0 : unstable++;
156 : }
157 176 : }
158 176 : }
159 :
160 2 : std::cout << "Stable: " << stable << "/" << fixture.inputs.size() << "\n";
161 2 : BOOST_CHECK_EQUAL(unstable, 0);
162 2 : }
163 :
164 : BOOST_AUTO_TEST_SUITE_END()
|