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