Line data Source code
1 : #define BOOST_TEST_DYN_LINK
2 : #define BOOST_TEST_MODULE (StringLineRoundTrip test)
3 :
4 : #include <algorithm>
5 : #include <boost/test/unit_test.hpp>
6 : #include <fstream>
7 : #include <numeric>
8 : #include <random>
9 :
10 : #include "../DatabaseConfigurationInterface.h"
11 : #include "artdaq-database/DataFormats/Fhicl/helper_functions.h"
12 : #include "artdaq-database/JsonDocument/JSONDocument.h"
13 :
14 : using namespace ots;
15 : using artdaq::database::docrecord::JSONDocument;
16 : using artdaq::database::fhicl::from_json_string;
17 : using artdaq::database::fhicl::to_json_string;
18 :
19 : namespace {
20 :
21 4 : bool isQuickMode() {
22 4 : auto* q = getenv("QUICK_TEST");
23 12 : return q && (std::string(q) == "1" || std::string(q) == "true");
24 : }
25 :
26 2 : std::vector<std::string> loadLines(const std::string& filename) {
27 2 : std::vector<std::string> lines;
28 2 : std::ifstream file(filename);
29 2 : std::string line;
30 3092 : while (std::getline(file, line))
31 3088 : if (!line.empty()) lines.push_back(line);
32 4 : return lines;
33 2 : }
34 :
35 894 : std::string wrapInJson(const std::string& value) { return "{\"value\": \"" + to_json_string(value) + "\"}"; }
36 :
37 1788 : std::string extractValue(const std::string& json) { return from_json_string(JSONDocument(json).findChild("value").value()); }
38 :
39 : struct TestFixture {
40 2 : TestFixture() : baseVersion((srand(time(nullptr)), rand() % 99999 + 100000)) {
41 2 : lines = loadLines("test_cases.txt");
42 :
43 2 : if (isQuickMode() && lines.size() > 10) {
44 2 : std::vector<size_t> idx(lines.size());
45 2 : std::iota(idx.begin(), idx.end(), 0);
46 2 : std::shuffle(idx.begin(), idx.end(), std::mt19937{std::random_device{}()});
47 2 : idx.resize(lines.size() / 10);
48 2 : std::sort(idx.begin(), idx.end());
49 2 : std::vector<std::string> sampled;
50 300 : for (auto i : idx) sampled.push_back(lines[i]);
51 2 : lines = std::move(sampled);
52 2 : }
53 :
54 2 : std::cout << "Loaded " << lines.size() << " test lines" << (isQuickMode() ? " (QUICK MODE)" : "") << "\n";
55 2 : }
56 :
57 1192 : int nextVersion() { return baseVersion + (++versionOffset); }
58 :
59 : const int baseVersion;
60 : std::vector<std::string> lines;
61 :
62 : private:
63 : int versionOffset = 0;
64 : };
65 :
66 : } // namespace
67 :
68 : TestFixture fixture;
69 :
70 : BOOST_AUTO_TEST_SUITE(string_line_roundtrip_test)
71 :
72 4 : BOOST_AUTO_TEST_CASE(json_roundtrip_ast_comparison) {
73 2 : DatabaseConfigurationInterface ifc;
74 2 : int passed = 0, failed = 0;
75 :
76 300 : for (const auto& line : fixture.lines) {
77 298 : int version = fixture.nextVersion();
78 298 : std::string inputJson = wrapInJson(line);
79 :
80 298 : auto writer = std::make_shared<TestConfiguration001>();
81 298 : writer->getView().fillFromJSON(inputJson);
82 298 : writer->getView().version = version;
83 298 : if (ifc.saveActiveVersion(writer.get()) != 0) {
84 0 : failed++;
85 0 : continue;
86 : }
87 :
88 298 : auto reader = std::make_shared<TestConfiguration001>();
89 298 : if (ifc.fill(reader.get(), version) != 0) {
90 0 : failed++;
91 0 : continue;
92 : }
93 :
94 298 : std::string outputJson = reader->getView()._json;
95 298 : std::string extractedValue = extractValue(outputJson);
96 :
97 298 : bool astMatch = (JSONDocument(inputJson) == JSONDocument(outputJson));
98 298 : bool verbatimMatch = (extractedValue == line);
99 :
100 298 : if (astMatch && verbatimMatch) {
101 298 : passed++;
102 : } else {
103 0 : failed++;
104 0 : std::cout << "FAIL: " << line.substr(0, 50) << (line.size() > 50 ? "..." : "") << " [AST:" << (astMatch ? "OK" : "FAIL")
105 0 : << " Verbatim:" << (verbatimMatch ? "OK" : "FAIL") << "]\n";
106 : }
107 298 : }
108 :
109 2 : std::cout << "Passed: " << passed << "/" << fixture.lines.size() << "\n";
110 2 : BOOST_CHECK_EQUAL(failed, 0);
111 2 : }
112 :
113 4 : BOOST_AUTO_TEST_CASE(multiple_roundtrips_stability) {
114 2 : DatabaseConfigurationInterface ifc;
115 2 : int stable = 0, unstable = 0;
116 :
117 300 : for (const auto& line : fixture.lines) {
118 298 : std::string current = wrapInJson(line);
119 298 : std::vector<std::string> reads;
120 :
121 1192 : for (int round = 0; round < 3; ++round) {
122 894 : int version = fixture.nextVersion();
123 894 : auto writer = std::make_shared<TestConfiguration001>();
124 894 : writer->getView().fillFromJSON(current);
125 894 : writer->getView().version = version;
126 894 : if (ifc.saveActiveVersion(writer.get()) != 0) break;
127 :
128 894 : auto reader = std::make_shared<TestConfiguration001>();
129 894 : if (ifc.fill(reader.get(), version) != 0) break;
130 894 : current = reader->getView()._json;
131 894 : reads.push_back(current);
132 894 : }
133 :
134 298 : if (reads.size() == 3) {
135 298 : JSONDocument d0(wrapInJson(line)), d1(reads[0]), d2(reads[1]), d3(reads[2]);
136 298 : bool astStable = (d0 == d1 && d1 == d2 && d2 == d3);
137 298 : bool verbatimMatch = (extractValue(reads[0]) == line);
138 :
139 298 : if (astStable && verbatimMatch) {
140 298 : stable++;
141 : } else {
142 0 : unstable++;
143 0 : std::cout << "UNSTABLE: " << line.substr(0, 40) << "..."
144 0 : << " [AST:" << (astStable ? "OK" : "FAIL") << " Verbatim:" << (verbatimMatch ? "OK" : "FAIL") << "]\n";
145 : }
146 298 : }
147 298 : }
148 :
149 2 : std::cout << "Stable: " << stable << "/" << fixture.lines.size() << "\n";
150 2 : BOOST_CHECK_EQUAL(unstable, 0);
151 2 : }
152 :
153 : BOOST_AUTO_TEST_SUITE_END()
|