1 // Part of Measurement Kit <https://measurement-kit.github.io/>.
2 // Measurement Kit is free software under the BSD license. See AUTHORS
3 // and LICENSE for more information on the copying conditions.
4
5 #include "test/winsock.hpp"
6
7 #include "include/private/catch.hpp"
8
9 #include "src/libmeasurement_kit/common/utils.hpp"
10 #include "src/libmeasurement_kit/report/base_reporter.hpp"
11 #include "src/libmeasurement_kit/report/error.hpp"
12 #include "src/libmeasurement_kit/report/report_legacy.hpp"
13
14 using namespace mk;
15 using namespace mk::report;
16
17 class CountedReporter : public BaseReporter {
18 public:
make()19 static SharedPtr<CountedReporter> make() {
20 return SharedPtr<CountedReporter>(new CountedReporter);
21 }
22
23 ~CountedReporter() override;
24
open(ReportLegacy &)25 Continuation<Error> open(ReportLegacy &) override {
26 return do_open_([=](Callback<Error> cb) {
27 ++open_count;
28 return cb(NoError());
29 });
30 }
31
write_entry(nlohmann::json e)32 Continuation<Error> write_entry(nlohmann::json e) override {
33 return do_write_entry_(e, [=](Callback<Error> cb) {
34 ++write_count;
35 return cb(NoError());
36 });
37 }
38
close()39 Continuation<Error> close() override {
40 return do_close_([=](Callback<Error> cb) {
41 ++close_count;
42 return cb(NoError());
43 });
44 }
45
46 int close_count = 0;
47 int open_count = 0;
48 int write_count = 0;
49 };
50
~CountedReporter()51 CountedReporter::~CountedReporter() {}
52
53 class FailingReporter : public BaseReporter {
54 public:
make()55 static SharedPtr<FailingReporter> make() {
56 return SharedPtr<FailingReporter>(new FailingReporter);
57 }
58
59 ~FailingReporter() override;
60
open(ReportLegacy &)61 Continuation<Error> open(ReportLegacy & /*report*/) override {
62 return do_open_([=](Callback<Error> cb) {
63 if (open_count++ == 0) {
64 cb(MockedError());
65 return;
66 }
67 cb(NoError());
68 });
69 }
70
write_entry(nlohmann::json e)71 Continuation<Error> write_entry(nlohmann::json e) override {
72 return do_write_entry_(e, [=](Callback<Error> cb) {
73 if (write_count++ == 0) {
74 cb(MockedError());
75 return;
76 }
77 cb(NoError());
78 });
79 }
80
close()81 Continuation<Error> close() override {
82 return do_close_([=](Callback<Error> cb) {
83 if (close_count++ == 0) {
84 cb(MockedError());
85 return;
86 }
87 cb(NoError());
88 });
89 }
90
91 int close_count = 0;
92 int open_count = 0;
93 int write_count = 0;
94 };
95
~FailingReporter()96 FailingReporter::~FailingReporter() {}
97
98 TEST_CASE("The constructor works correctly") {
99 REQUIRE_NOTHROW(ReportLegacy());
100 }
101
102 TEST_CASE("The open() method works correctly") {
103 ReportLegacy report;
104 report.add_reporter(BaseReporter::make());
__anon6dbc696e0702(Error err) 105 report.open([&](Error err) {
106 REQUIRE(!err);
107 report.open([&](Error err) {
108 REQUIRE(err == NoError());
109 REQUIRE(err.child_errors.size() == 1);
110 REQUIRE(err.child_errors[0] == NoError());
111 REQUIRE(err.child_errors[0].child_errors.size() == 1);
112 REQUIRE(err.child_errors[0].child_errors[0] ==
113 ReportAlreadyOpenError());
114 });
115 });
116 }
117
118 TEST_CASE("We can retry a partially successful open") {
119 SharedPtr<CountedReporter> counted_reporter = CountedReporter::make();
120 SharedPtr<FailingReporter> failing_reporter = FailingReporter::make();
121 ReportLegacy report;
122 report.add_reporter(counted_reporter.as<BaseReporter>());
123 report.add_reporter(failing_reporter.as<BaseReporter>());
__anon6dbc696e0902(Error err) 124 report.open([&](Error err) {
125 REQUIRE(err == ParallelOperationError());
126 REQUIRE(err.child_errors.size() == 2);
127 REQUIRE(err.child_errors[0] == NoError());
128 REQUIRE(err.child_errors[1] == MockedError());
129 report.open([&](Error err) {
130 REQUIRE(err == NoError());
131 REQUIRE(err.child_errors.size() == 2);
132 REQUIRE(err.child_errors[0] == NoError());
133 REQUIRE(err.child_errors[0].child_errors.size() == 1);
134 REQUIRE(err.child_errors[0].child_errors[0] ==
135 ReportAlreadyOpenError());
136 REQUIRE(err.child_errors[1] == NoError());
137 });
138 });
139 REQUIRE(counted_reporter->open_count == 1);
140 REQUIRE(failing_reporter->open_count == 2);
141 }
142
143 TEST_CASE("The write_entry() method works correctly") {
144 ReportLegacy report;
145 report.add_reporter(BaseReporter::make());
146 nlohmann::json entry;
__anon6dbc696e0b02(Error err) 147 report.write_entry(entry, [&](Error err) {
148 REQUIRE(err == ParallelOperationError());
149 REQUIRE(err.child_errors.size() == 1);
150 REQUIRE(err.child_errors[0] == ReportNotOpenError());
151 report.open([&](Error err) {
152 REQUIRE(!err);
153 entry["foobar"] = 1;
154 report.write_entry(entry, [&](Error err) {
155 REQUIRE(!err);
156 REQUIRE(err.child_errors.size() == 1);
157 REQUIRE(err.child_errors[0] == NoError());
158 REQUIRE(err.child_errors[0].child_errors.size() == 0);
159 entry["foobar"] = 2;
160 report.write_entry(entry, [&](Error err) {
161 REQUIRE(!err);
162 REQUIRE(err.child_errors.size() == 1);
163 REQUIRE(err.child_errors[0] == NoError());
164 REQUIRE(err.child_errors[0].child_errors.size() == 0);
165 entry["foobar"] = 3;
166 report.write_entry(entry, [&](Error err) {
167 REQUIRE(!err);
168 report.close([&](Error err) {
169 REQUIRE(!err);
170 REQUIRE(err.child_errors.size() == 1);
171 REQUIRE(
172 err.child_errors[0] == NoError());
173 REQUIRE(
174 err.child_errors[0].child_errors.size() == 0);
175 entry["foobar"] = 4;
176 report.write_entry(entry, [&](Error err) {
177 REQUIRE(err ==
178 ParallelOperationError());
179 REQUIRE(err.child_errors.size() == 1);
180 REQUIRE(err.child_errors[0] ==
181 ReportAlreadyClosedError());
182 }, Logger::make());
183 });
184 }, Logger::make());
185 }, Logger::make());
186 }, Logger::make());
187 });
188 }, Logger::make());
189 }
190
191 TEST_CASE("We can retry a partially successful write_entry()") {
192 SharedPtr<CountedReporter> counted_reporter = CountedReporter::make();
193 SharedPtr<FailingReporter> failing_reporter = FailingReporter::make();
194 failing_reporter->open_count = 1; // So open won't fail
195 ReportLegacy report;
196 report.add_reporter(counted_reporter.as<BaseReporter>());
197 report.add_reporter(failing_reporter.as<BaseReporter>());
__anon6dbc696e1202(Error err) 198 report.open([&](Error err) {
199 REQUIRE(err == NoError());
200 nlohmann::json entry;
201 entry["foobar"] = 17;
202 entry["baz"] = "foobar";
203 report.write_entry(entry, [&](Error err) {
204 REQUIRE(err == ParallelOperationError());
205 REQUIRE(err.child_errors.size() == 2);
206 REQUIRE(err.child_errors[0] == NoError());
207 REQUIRE(err.child_errors[1] == MockedError());
208 report.write_entry(entry, [&](Error err) {
209 REQUIRE(err == NoError());
210 REQUIRE(err.child_errors.size() == 2);
211 REQUIRE(err.child_errors[0] == NoError());
212 REQUIRE(err.child_errors[0].child_errors.size() == 1);
213 REQUIRE(err.child_errors[0].child_errors[0] ==
214 DuplicateEntrySubmitError());
215 REQUIRE(err.child_errors[1] == NoError());
216 }, Logger::make());
217 }, Logger::make());
218 });
219 REQUIRE(counted_reporter->write_count == 1);
220 REQUIRE(failing_reporter->write_count == 2);
221 }
222
223 TEST_CASE("The close() method works correctly") {
224 ReportLegacy report;
225 report.add_reporter(BaseReporter::make());
__anon6dbc696e1502(Error err) 226 report.open([&](Error err) {
227 REQUIRE(!err);
228 report.close([&](Error err) {
229 REQUIRE(!err);
230 report.close([&](Error err) {
231 REQUIRE(err == NoError());
232 REQUIRE(err.child_errors.size() == 1);
233 REQUIRE(err.child_errors[0] == NoError());
234 REQUIRE(err.child_errors[0].child_errors.size() == 1);
235 REQUIRE(err.child_errors[0].child_errors[0] ==
236 ReportAlreadyClosedError());
237 });
238 });
239 });
240 }
241
242 TEST_CASE("We can retry a partially successful close") {
243 SharedPtr<CountedReporter> counted_reporter = CountedReporter::make();
244 SharedPtr<FailingReporter> failing_reporter = FailingReporter::make();
245 failing_reporter->open_count = 1; // So open won't fail
246 ReportLegacy report;
247 report.add_reporter(counted_reporter.as<BaseReporter>());
248 report.add_reporter(failing_reporter.as<BaseReporter>());
__anon6dbc696e1802(Error err) 249 report.open([&](Error err) {
250 REQUIRE(err == NoError());
251 report.close([&](Error err) {
252 REQUIRE(err == ParallelOperationError());
253 REQUIRE(err.child_errors.size() == 2);
254 REQUIRE(err.child_errors[0] == NoError());
255 REQUIRE(err.child_errors[1] == MockedError());
256 report.close([&](Error err) {
257 REQUIRE(err == NoError());
258 REQUIRE(err.child_errors.size() == 2);
259 REQUIRE(err.child_errors[0] == NoError());
260 REQUIRE(err.child_errors[0].child_errors.size() == 1);
261 REQUIRE(err.child_errors[0].child_errors[0] ==
262 ReportAlreadyClosedError());
263 REQUIRE(err.child_errors[1] == NoError());
264 });
265 });
266 });
267 REQUIRE(counted_reporter->close_count == 1);
268 REQUIRE(failing_reporter->close_count == 2);
269 }
270
271 TEST_CASE("We can override software name and version") {
272 ReportLegacy report;
273 nlohmann::json entry;
274
275 // We must set the `test_start_time` because otherwise the
276 // test fails with an `error_or_contains_error` failure.
277 mk::utc_time_now(&report.test_start_time);
278
279 SECTION("We have a default") {
280 report.fill_entry(entry);
281 REQUIRE(entry["software_name"] == report.software_name);
282 REQUIRE(entry["software_version"] == report.software_version);
283 }
284
285 SECTION("We can override the default") {
286 report.options["software_name"] = "ooniprobe-ios";
287 report.options["software_version"] = "1.0.1";
288 report.fill_entry(entry);
289 REQUIRE(entry["software_name"] == "ooniprobe-ios");
290 REQUIRE(entry["software_version"] == "1.0.1");
291 }
292 }
293