Line data Source code
1 : #include "test/common.h"
2 :
3 : #include "artdaq-database/ConfigurationDB/common.h"
4 : #include "artdaq-database/ConfigurationDB/conftoolifc.h"
5 :
6 : #include "artdaq-database/BasicTypes/basictypes.h"
7 : #include "artdaq-database/DataFormats/Json/json_reader.h"
8 : #include "artdaq-database/DataFormats/Json/presort_json.h"
9 : #include "artdaq-database/JsonDocument/JSONDocument.h"
10 : #include "artdaq-database/JsonDocument/JSONDocumentBuilder.h"
11 :
12 : #include <fstream>
13 :
14 : namespace db = artdaq::database;
15 : namespace bpo = boost::program_options;
16 : namespace impl = db::configuration::json;
17 :
18 : using artdaq::database::docrecord::JSONDocument;
19 : using artdaq::database::docrecord::JSONDocumentBuilder;
20 : namespace ovl = artdaq::database::overlay;
21 :
22 : namespace apiliteral = db::configapi::literal;
23 :
24 : // Helper function to write content to a file
25 0 : bool write_to_file(const std::string& filename, const std::string& content) {
26 0 : std::ofstream outfile(filename);
27 :
28 0 : if (!outfile.is_open()) {
29 0 : std::cerr << "ERROR: Failed to open file for writing: " << filename << "\n";
30 0 : return false;
31 : }
32 :
33 0 : outfile << content;
34 0 : outfile.close();
35 :
36 0 : std::cout << "Debug file written: " << filename << "\n";
37 0 : return true;
38 0 : }
39 :
40 112 : int main(int argc, char* argv[]) try {
41 : #if 1
42 :
43 112 : impl::enable_trace();
44 :
45 112 : debug::registerUngracefullExitHandlers();
46 : // artdaq::database::useFakeTime(true);
47 : #endif
48 :
49 112 : std::unique_ptr<db::configuration::OperationBase> options = std::make_unique<db::configuration::OperationBase>("ConfigurationDB_t");
50 : // artdaq::database::filesystem::index::shouldAutoRebuildSearchIndex(true);
51 :
52 336 : auto make_opt_name = [](auto& long_name, auto& short_name) { return std::string{long_name}.append(",").append(short_name); };
53 :
54 112 : bpo::variables_map vm;
55 :
56 112 : bpo::options_description opts;
57 112 : opts.add_options()("help,h", "Produce help message");
58 112 : opts.add_options()(make_opt_name(apiliteral::option::operation, "o").c_str(), bpo::value<std::string>(), "Operation");
59 :
60 : try {
61 112 : bpo::store(bpo::command_line_parser(argc, argv).options(opts).allow_unregistered().run(), vm);
62 112 : bpo::notify(vm);
63 0 : } catch (bpo::error const& e) {
64 0 : std::cerr << "Exception from command line processing in " << options->processName() << ": " << e.what() << "\n";
65 0 : return process_exit_code::INVALID_ARGUMENT;
66 0 : }
67 :
68 224 : if (vm.count("help") != 0u) {
69 0 : std::cerr << opts << std::endl;
70 0 : return process_exit_code::HELP;
71 : }
72 :
73 224 : if (vm.count(apiliteral::option::operation) == 0u) {
74 0 : std::cerr << "Operation option is missing\n";
75 0 : std::cerr << opts << std::endl;
76 :
77 0 : return process_exit_code::INVALID_ARGUMENT;
78 : }
79 :
80 112 : auto opname = vm[apiliteral::option::operation].as<std::string>();
81 :
82 112 : if (opname == apiliteral::operation::addversionalias || opname == apiliteral::operation::rmversionalias) {
83 4 : options = std::make_unique<db::configuration::ManageAliasesOperation>(argv[0]);
84 108 : } else if (opname == apiliteral::operation::findconfigs) {
85 4 : options = std::make_unique<db::configuration::ManageConfigsOperation>(argv[0]);
86 : } else {
87 104 : options = std::make_unique<db::configuration::ManageDocumentOperation>(argv[0]);
88 : }
89 :
90 112 : auto result = db::conftool_impl(options, argc, argv);
91 :
92 112 : if (!result.first) {
93 0 : std::cout << "Failed; error message: " << result.second << "\n";
94 0 : std::cout << ::debug::current_exception_diagnostic_information();
95 0 : return process_exit_code::FAILURE;
96 : }
97 :
98 112 : auto returned = std::string{result.second};
99 112 : if (!result.first) {
100 0 : std::cout << "Test failed; error message: " << result.second << "\n";
101 0 : std::cout << ::debug::current_exception_diagnostic_information();
102 0 : return process_exit_code::FAILURE;
103 : }
104 :
105 112 : if (result.second.empty()) {
106 : std::cout << "Test failed; error message: result is empty."
107 0 : << "\n";
108 0 : return process_exit_code::FAILURE;
109 : }
110 :
111 112 : auto retdoc = std::string{result.second};
112 :
113 112 : auto cmpdoc = std::string{};
114 112 : db::read_buffer_from_file(cmpdoc, options->resultFileName());
115 :
116 : using cfo::data_format_t;
117 :
118 112 : if (options->format() == data_format_t::db) {
119 12 : auto rdoc = JSONDocument{retdoc};
120 12 : auto cdoc = JSONDocument{cmpdoc};
121 12 : JSONDocumentBuilder returned{rdoc};
122 12 : JSONDocumentBuilder expected{cdoc};
123 :
124 : using namespace artdaq::database::overlay;
125 12 : ovl::useCompareMask(DOCUMENT_COMPARE_MUTE_TIMESTAMPS | DOCUMENT_COMPARE_MUTE_OUIDS | DOCUMENT_COMPARE_MUTE_UPDATES);
126 :
127 12 : auto result = returned == expected;
128 :
129 12 : if (result.first) {
130 12 : return process_exit_code::SUCCESS;
131 : }
132 :
133 0 : std::cout << "Error returned!=expected.\n";
134 0 : std::cerr << "returned:\n" << returned << "\n";
135 0 : std::cerr << "expected:\n" << expected << "\n";
136 0 : std::cerr << "error:\n" << result.second << "\n";
137 :
138 : // Write debug files
139 0 : std::ostringstream returned_stream, expected_stream;
140 0 : returned_stream << returned;
141 0 : expected_stream << expected;
142 0 : write_to_file(options->operation() + ".output.json", returned_stream.str());
143 0 : write_to_file(options->operation() + ".expected.json", expected_stream.str());
144 :
145 0 : return process_exit_code::FAILURE;
146 112 : } else if (options->format() == data_format_t::gui) {
147 : // Presort GUI JSON documents by "name" field before comparison
148 34 : auto sorted_retdoc = artdaq::database::json::presort_gui_json_by_name(retdoc);
149 34 : auto sorted_cmpdoc = artdaq::database::json::presort_gui_json_by_name(cmpdoc);
150 :
151 34 : auto compare_result = artdaq::database::json::compare_json_objects(sorted_retdoc, sorted_cmpdoc);
152 34 : if (compare_result.first) {
153 34 : std::cout << "returned:\n" << retdoc << "\n";
154 :
155 34 : return process_exit_code::SUCCESS;
156 : }
157 0 : std::cout << "Test failed (expected!=returned); error message: " << compare_result.second << "\n";
158 :
159 : // Write debug files
160 0 : write_to_file(options->operation() + ".output.json", sorted_retdoc);
161 0 : write_to_file(options->operation() + ".expected.json", sorted_cmpdoc);
162 168 : } else if (options->format() == data_format_t::json) {
163 40 : auto compare_result = artdaq::database::json::compare_json_objects(retdoc, cmpdoc);
164 40 : if (compare_result.first) {
165 40 : std::cout << "returned:\n" << retdoc << "\n";
166 :
167 40 : return process_exit_code::SUCCESS;
168 : }
169 0 : std::cout << "Test failed (expected!=returned); error message: " << compare_result.second << "\n";
170 :
171 : // Write debug files
172 0 : write_to_file(options->operation() + ".output.json", retdoc);
173 0 : write_to_file(options->operation() + ".expected.json", cmpdoc);
174 66 : } else if (cmpdoc == retdoc) {
175 24 : std::cout << "returned:\n" << retdoc << "\n";
176 :
177 24 : return process_exit_code::SUCCESS;
178 2 : } else if (cmpdoc.pop_back(), cmpdoc == retdoc) {
179 2 : std::cout << "returned:\n" << retdoc << "\n";
180 :
181 2 : return process_exit_code::SUCCESS;
182 : }
183 :
184 0 : std::cout << "Test failed (expected!=returned)\n";
185 :
186 0 : std::cout << "returned:\n" << retdoc << "\n";
187 0 : std::cout << "expected:\n" << cmpdoc << "\n";
188 :
189 0 : auto mismatch = std::mismatch(cmpdoc.begin(), cmpdoc.end(), retdoc.begin());
190 0 : std::cout << "File sizes (exp,ret)=(" << std::distance(cmpdoc.begin(), cmpdoc.end()) << "," << std::distance(retdoc.begin(), retdoc.end()) << ")\n";
191 :
192 0 : std::cout << "First mismatch at position " << std::distance(cmpdoc.begin(), mismatch.first) << ", (exp,ret)=(0x" << std::hex
193 0 : << static_cast<unsigned int>(*mismatch.first) << ",0x" << static_cast<unsigned int>(*mismatch.second) << ")\n";
194 :
195 : // Write simple debug files in current directory
196 0 : write_to_file(options->operation() + ".output.txt", retdoc);
197 0 : write_to_file(options->operation() + ".expected.txt", cmpdoc);
198 :
199 : // Also write to temp directory for compatibility with existing workflows
200 0 : auto file_out_name = std::string(db::make_temp_dir())
201 0 : .append("/")
202 0 : .append(argv[0])
203 0 : .append("-")
204 0 : .append(options->operation())
205 0 : .append("-")
206 0 : .append(basename(const_cast<char*>(options->resultFileName().c_str())))
207 0 : .append(".txt");
208 :
209 0 : db::write_buffer_to_file(retdoc, file_out_name);
210 :
211 0 : std::cout << "Wrote file:" << file_out_name << "\n";
212 :
213 0 : return process_exit_code::FAILURE;
214 112 : } catch (...) {
215 0 : std::cout << "Process exited with error: " << ::debug::current_exception_diagnostic_information();
216 0 : return process_exit_code::UNCAUGHT_EXCEPTION;
217 0 : }
|