Aktualizr
C++ SOTA Client
aktualizr_test.cc
1 #include <gtest/gtest.h>
2 
3 #include <chrono>
4 #include <future>
5 #include <string>
6 #include <unordered_map>
7 #include <vector>
8 
9 #include <boost/filesystem.hpp>
10 #include "json/json.h"
11 
12 #include "libaktualizr/aktualizr.h"
13 #include "libaktualizr/config.h"
14 #include "libaktualizr/events.h"
15 
16 #include "httpfake.h"
17 #include "primary/aktualizr_helpers.h"
18 #include "primary/sotauptaneclient.h"
19 #include "uptane_test_common.h"
20 #include "utilities/utils.h"
21 #include "virtualsecondary.h"
22 
23 #include "utilities/fault_injection.h"
24 
25 boost::filesystem::path uptane_repos_dir;
26 boost::filesystem::path fake_meta_dir;
27 
28 void verifyNothingInstalled(const Json::Value& manifest) {
29  // Verify nothing has installed for the Primary.
30  EXPECT_EQ(
31  manifest["ecu_version_manifests"]["CA:FE:A6:D2:84:9D"]["signed"]["custom"]["operation_result"]["id"].asString(),
32  "");
33  EXPECT_EQ(
34  manifest["ecu_version_manifests"]["CA:FE:A6:D2:84:9D"]["signed"]["custom"]["operation_result"]["result_code"]
35  .asInt(),
36  static_cast<int>(data::ResultCode::Numeric::kOk));
37  EXPECT_EQ(
38  manifest["ecu_version_manifests"]["CA:FE:A6:D2:84:9D"]["signed"]["custom"]["operation_result"]["result_text"]
39  .asString(),
40  "");
41  EXPECT_EQ(manifest["ecu_version_manifests"]["CA:FE:A6:D2:84:9D"]["signed"]["installed_image"]["filepath"].asString(),
42  "unknown");
43  // Verify nothing has installed for the Secondary.
44  EXPECT_EQ(
45  manifest["ecu_version_manifests"]["secondary_ecu_serial"]["signed"]["installed_image"]["filepath"].asString(),
46  "noimage");
47 }
48 
49 /*
50  * Initialize -> UptaneCycle -> no updates -> no further action or events.
51  */
52 TEST(Aktualizr, FullNoUpdates) {
53  TemporaryDirectory temp_dir;
54  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "noupdates", fake_meta_dir);
55  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
56 
57  auto storage = INvStorage::newStorage(conf.storage);
58  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
59 
60  struct {
61  size_t num_events{0};
62  std::future<void> future;
63  std::promise<void> promise;
64  } ev_state;
65  ev_state.future = ev_state.promise.get_future();
66 
67  auto f_cb = [&ev_state](const std::shared_ptr<event::BaseEvent>& event) {
68  if (event->isTypeOf<event::DownloadProgressReport>()) {
69  return;
70  }
71  LOG_INFO << "Got " << event->variant;
72  switch (ev_state.num_events) {
73  case 0: {
74  ASSERT_EQ(event->variant, "UpdateCheckComplete");
75  const auto targets_event = dynamic_cast<event::UpdateCheckComplete*>(event.get());
76  EXPECT_EQ(targets_event->result.ecus_count, 0);
77  EXPECT_EQ(targets_event->result.updates.size(), 0);
78  EXPECT_EQ(targets_event->result.status, result::UpdateStatus::kNoUpdatesAvailable);
79  break;
80  }
81  case 1: {
82  ASSERT_EQ(event->variant, "UpdateCheckComplete");
83  const auto targets_event = dynamic_cast<event::UpdateCheckComplete*>(event.get());
84  EXPECT_EQ(targets_event->result.ecus_count, 0);
85  EXPECT_EQ(targets_event->result.updates.size(), 0);
86  EXPECT_EQ(targets_event->result.status, result::UpdateStatus::kNoUpdatesAvailable);
87  ev_state.promise.set_value();
88  break;
89  }
90  case 5:
91  // Don't let the test run indefinitely!
92  FAIL() << "Unexpected events!";
93  default:
94  std::cout << "event #" << ev_state.num_events << " is: " << event->variant << "\n";
95  ASSERT_EQ(event->variant, "");
96  }
97  ++ev_state.num_events;
98  };
99  boost::signals2::connection conn = aktualizr.SetSignalHandler(f_cb);
100 
101  aktualizr.Initialize();
102  aktualizr.UptaneCycle();
103  // Run the cycle twice so that we can check for a second UpdateCheckComplete
104  // and guarantee that nothing unexpected happened after the first fetch.
105  aktualizr.UptaneCycle();
106  auto status = ev_state.future.wait_for(std::chrono::seconds(20));
107  if (status != std::future_status::ready) {
108  FAIL() << "Timed out waiting for metadata to be fetched.";
109  }
110 
111  verifyNothingInstalled(aktualizr.uptane_client()->AssembleManifest());
112 }
113 
114 /*
115  * Compute device installation failure code as concatenation of ECU failure
116  * codes during installation.
117  */
118 TEST(Aktualizr, DeviceInstallationResult) {
119  TemporaryDirectory temp_dir;
120  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "", fake_meta_dir);
121  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
122 
123  auto storage = INvStorage::newStorage(conf.storage);
124  EcuSerials serials{
125  {Uptane::EcuSerial("CA:FE:A6:D2:84:9D"), Uptane::HardwareIdentifier("primary_hw")},
126  {Uptane::EcuSerial("secondary_ecu_serial"), Uptane::HardwareIdentifier("secondary_hw")},
127  {Uptane::EcuSerial("ecuserial3"), Uptane::HardwareIdentifier("hw_id3")},
128  };
129  storage->storeEcuSerials(serials);
130 
131  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
132  Primary::VirtualSecondaryConfig ecu_config = UptaneTestCommon::altVirtualConfiguration(temp_dir.Path());
133  aktualizr.AddSecondary(std::make_shared<Primary::VirtualSecondary>(ecu_config));
134  aktualizr.Initialize();
135 
136  storage->saveEcuInstallationResult(Uptane::EcuSerial("ecuserial3"), data::InstallationResult());
137  storage->saveEcuInstallationResult(Uptane::EcuSerial("CA:FE:A6:D2:84:9D"), data::InstallationResult());
138  storage->saveEcuInstallationResult(Uptane::EcuSerial("CA:FE:A6:D2:84:9D"),
140 
142  aktualizr.uptane_client()->computeDeviceInstallationResult(&result, nullptr);
143  auto res_json = result.toJson();
144  EXPECT_EQ(res_json["code"].asString(), "primary_hw:INSTALL_FAILED");
145  EXPECT_EQ(res_json["success"], false);
146 
147  storage->saveEcuInstallationResult(
148  Uptane::EcuSerial("ecuserial3"),
150  aktualizr.uptane_client()->computeDeviceInstallationResult(&result, nullptr);
151  res_json = result.toJson();
152  EXPECT_EQ(res_json["code"].asString(), "primary_hw:INSTALL_FAILED|hw_id3:SECOND_FAIL");
153  EXPECT_EQ(res_json["success"], false);
154 }
155 
156 #ifdef FIU_ENABLE
157 
158 /*
159  * Compute device installation failure code as concatenation of ECU failure
160  * codes from sending metadata to Secondaries.
161  */
162 TEST(Aktualizr, DeviceInstallationResultMetadata) {
163  TemporaryDirectory temp_dir;
164  // Use "hasupdates" to make sure Image repo Root gets fetched, despite that we
165  // won't use the default update.
166  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates", fake_meta_dir);
167  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
168  UptaneTestCommon::addDefaultSecondary(conf, temp_dir, "sec_serial1", "sec_hw1");
169  UptaneTestCommon::addDefaultSecondary(conf, temp_dir, "sec_serial2", "sec_hw2");
170  UptaneTestCommon::addDefaultSecondary(conf, temp_dir, "sec_serial3", "sec_hw3");
171 
172  auto storage = INvStorage::newStorage(conf.storage);
173  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
174  aktualizr.Initialize();
175  auto update_result = aktualizr.CheckUpdates().get();
176  EXPECT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
177 
178  fault_injection_init();
179  fiu_enable("secondary_putmetadata", 1, nullptr, 0);
180 
181  // Try updating two Secondaries; leave the third one alone.
182  std::vector<Uptane::Target> targets;
183  Json::Value target_json;
184  target_json["custom"]["targetFormat"] = "BINARY";
185  target_json["custom"]["ecuIdentifiers"]["sec_serial1"]["hardwareId"] = "sec_hw1";
186  target_json["custom"]["ecuIdentifiers"]["sec_serial3"]["hardwareId"] = "sec_hw3";
187  targets.emplace_back(Uptane::Target("test", target_json));
188 
190  aktualizr.uptane_client()->sendMetadataToEcus(targets, &result, nullptr);
191  auto res_json = result.toJson();
192  EXPECT_EQ(res_json["code"].asString(), "sec_hw1:VERIFICATION_FAILED|sec_hw3:VERIFICATION_FAILED");
193  EXPECT_EQ(res_json["success"], false);
194 
195  fiu_disable("secondary_putmetadata");
196 
197  // Retry after disabling fault injection to verify the test.
198  aktualizr.uptane_client()->sendMetadataToEcus(targets, &result, nullptr);
199  res_json = result.toJson();
200  EXPECT_EQ(res_json["code"].asString(), "OK");
201  EXPECT_EQ(res_json["success"], true);
202 }
203 
204 #endif // FIU_ENABLE
205 
207  public:
208  HttpFakeEventCounter(const boost::filesystem::path& test_dir_in, const boost::filesystem::path& meta_dir_in)
209  : HttpFake(test_dir_in, "hasupdates", meta_dir_in) {}
210 
211  HttpResponse handle_event(const std::string& url, const Json::Value& data) override {
212  (void)url;
213  for (const Json::Value& event : data) {
214  ++events_seen;
215  std::string event_type = event["eventType"]["id"].asString();
216  if (event_type.find("Ecu") == 0) {
217  EXPECT_EQ(event["event"]["correlationId"], "id0");
218  }
219 
220  std::cout << "got event #" << events_seen << ": " << event_type << "\n";
221  if (events_seen >= 1 && events_seen <= 4) {
222  EXPECT_TRUE(event_type == "EcuDownloadStarted" || event_type == "EcuDownloadCompleted");
223  } else if (events_seen >= 5 && events_seen <= 8) {
224  EXPECT_TRUE(event_type == "EcuInstallationStarted" || event_type == "EcuInstallationCompleted");
225  } else {
226  std::cout << "Unexpected event";
227  EXPECT_EQ(0, 1);
228  }
229  }
230  return HttpResponse("", 200, CURLE_OK, "");
231  }
232 
233  unsigned int events_seen{0};
234 };
235 
236 /*
237  * Initialize -> UptaneCycle -> updates downloaded and installed for Primary and
238  * Secondary.
239  */
240 TEST(Aktualizr, FullWithUpdates) {
241  TemporaryDirectory temp_dir;
242  auto http = std::make_shared<HttpFakeEventCounter>(temp_dir.Path(), fake_meta_dir);
243  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
244 
245  auto storage = INvStorage::newStorage(conf.storage);
246  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
247 
248  struct {
249  size_t num_events{0};
250  std::future<void> future;
251  std::promise<void> promise;
252  } ev_state;
253  ev_state.future = ev_state.promise.get_future();
254 
255  auto f_cb = [&ev_state](const std::shared_ptr<event::BaseEvent>& event) {
256  if (event->isTypeOf<event::DownloadProgressReport>()) {
257  return;
258  }
259  LOG_INFO << "Got " << event->variant;
260  switch (ev_state.num_events) {
261  case 0: {
262  ASSERT_EQ(event->variant, "UpdateCheckComplete");
263  const auto targets_event = dynamic_cast<event::UpdateCheckComplete*>(event.get());
264  EXPECT_EQ(targets_event->result.ecus_count, 2);
265  EXPECT_EQ(targets_event->result.updates.size(), 2u);
266  EXPECT_EQ(targets_event->result.updates[0].filename(), "primary_firmware.txt");
267  EXPECT_EQ(targets_event->result.updates[1].filename(), "secondary_firmware.txt");
268  EXPECT_EQ(targets_event->result.status, result::UpdateStatus::kUpdatesAvailable);
269  break;
270  }
271  case 1:
272  case 2: {
273  ASSERT_EQ(event->variant, "DownloadTargetComplete");
274  const auto download_event = dynamic_cast<event::DownloadTargetComplete*>(event.get());
275  EXPECT_TRUE(download_event->update.filename() == "primary_firmware.txt" ||
276  download_event->update.filename() == "secondary_firmware.txt");
277  EXPECT_TRUE(download_event->success);
278  break;
279  }
280  case 3: {
281  ASSERT_EQ(event->variant, "AllDownloadsComplete");
282  const auto downloads_complete = dynamic_cast<event::AllDownloadsComplete*>(event.get());
283  EXPECT_EQ(downloads_complete->result.updates.size(), 2);
284  EXPECT_TRUE(downloads_complete->result.updates[0].filename() == "primary_firmware.txt" ||
285  downloads_complete->result.updates[1].filename() == "primary_firmware.txt");
286  EXPECT_TRUE(downloads_complete->result.updates[0].filename() == "secondary_firmware.txt" ||
287  downloads_complete->result.updates[1].filename() == "secondary_firmware.txt");
288  EXPECT_EQ(downloads_complete->result.status, result::DownloadStatus::kSuccess);
289  break;
290  }
291  case 4: {
292  // Primary always gets installed first. (Not a requirement, just how it
293  // works at present.)
294  ASSERT_EQ(event->variant, "InstallStarted");
295  const auto install_started = dynamic_cast<event::InstallStarted*>(event.get());
296  EXPECT_EQ(install_started->serial.ToString(), "CA:FE:A6:D2:84:9D");
297  break;
298  }
299  case 5: {
300  // Primary should complete before Secondary begins. (Again not a
301  // requirement per se.)
302  ASSERT_EQ(event->variant, "InstallTargetComplete");
303  const auto install_complete = dynamic_cast<event::InstallTargetComplete*>(event.get());
304  EXPECT_EQ(install_complete->serial.ToString(), "CA:FE:A6:D2:84:9D");
305  EXPECT_TRUE(install_complete->success);
306  break;
307  }
308  case 6: {
309  ASSERT_EQ(event->variant, "InstallStarted");
310  const auto install_started = dynamic_cast<event::InstallStarted*>(event.get());
311  EXPECT_EQ(install_started->serial.ToString(), "secondary_ecu_serial");
312  break;
313  }
314  case 7: {
315  ASSERT_EQ(event->variant, "InstallTargetComplete");
316  const auto install_complete = dynamic_cast<event::InstallTargetComplete*>(event.get());
317  EXPECT_EQ(install_complete->serial.ToString(), "secondary_ecu_serial");
318  EXPECT_TRUE(install_complete->success);
319  break;
320  }
321  case 8: {
322  ASSERT_EQ(event->variant, "AllInstallsComplete");
323  const auto installs_complete = dynamic_cast<event::AllInstallsComplete*>(event.get());
324  EXPECT_EQ(installs_complete->result.ecu_reports.size(), 2);
325  EXPECT_EQ(installs_complete->result.ecu_reports[0].install_res.result_code.num_code,
326  data::ResultCode::Numeric::kOk);
327  EXPECT_EQ(installs_complete->result.ecu_reports[1].install_res.result_code.num_code,
328  data::ResultCode::Numeric::kOk);
329  break;
330  }
331  case 9: {
332  ASSERT_EQ(event->variant, "PutManifestComplete");
333  const auto put_complete = dynamic_cast<event::PutManifestComplete*>(event.get());
334  EXPECT_TRUE(put_complete->success);
335  ev_state.promise.set_value();
336  break;
337  }
338  case 12:
339  // Don't let the test run indefinitely!
340  FAIL() << "Unexpected events!";
341  default:
342  std::cout << "event #" << ev_state.num_events << " is: " << event->variant << "\n";
343  ASSERT_EQ(event->variant, "");
344  }
345  ++ev_state.num_events;
346  };
347  boost::signals2::connection conn = aktualizr.SetSignalHandler(f_cb);
348 
349  aktualizr.Initialize();
350  aktualizr.UptaneCycle();
351  auto status = ev_state.future.wait_for(std::chrono::seconds(20));
352  if (status != std::future_status::ready) {
353  FAIL() << "Timed out waiting for installation to complete.";
354  }
355  EXPECT_EQ(http->events_seen, 8);
356 }
357 
358 class HttpFakeSplit : public HttpFake {
359  public:
360  HttpFakeSplit(const boost::filesystem::path& test_dir_in, const boost::filesystem::path& meta_dir_in)
361  : HttpFake(test_dir_in, "hasupdates", meta_dir_in) {}
362 
363  HttpResponse handle_event(const std::string& url, const Json::Value& data) override {
364  (void)url;
365  for (const Json::Value& event : data) {
366  ++events_seen;
367  std::string event_type = event["eventType"]["id"].asString();
368  if (event_type.find("Ecu") == 0) {
369  EXPECT_EQ(event["event"]["correlationId"], "id0");
370  }
371 
372  std::cout << "got event #" << events_seen << ": " << event_type << "\n";
373  switch (events_seen) {
374  case 1:
375  case 5:
376  EXPECT_TRUE(event_type == "EcuDownloadStarted");
377  break;
378  case 2:
379  case 6:
380  EXPECT_TRUE(event_type == "EcuDownloadCompleted");
381  break;
382  case 3:
383  case 7:
384  EXPECT_TRUE(event_type == "EcuInstallationStarted");
385  break;
386  case 4:
387  case 8:
388  EXPECT_TRUE(event_type == "EcuInstallationCompleted");
389  break;
390  default:
391  std::cout << "Unexpected event";
392  EXPECT_EQ(0, 1);
393  break;
394  }
395  }
396  return HttpResponse("", 200, CURLE_OK, "");
397  }
398 
399  unsigned int events_seen{0};
400 };
401 
402 /*
403  * Initialize -> CheckUpdates -> download and install one update -> download and
404  * install second update
405  *
406  * This is intended to cover the case where an implementer splits an update with
407  * multiple targets and downloads and/or installs them separately. This is
408  * supported as long as a check for updates isn't done inbetween. It was briefly
409  * broken by overzealously dropping Targets metadata and fixed in
410  * 99c7f7ef20da76a2d6eefd08be5529c36434b9a6.
411  */
412 TEST(Aktualizr, SplitUpdates) {
413  TemporaryDirectory temp_dir;
414  auto http = std::make_shared<HttpFakeSplit>(temp_dir.Path(), fake_meta_dir);
415  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
416 
417  auto storage = INvStorage::newStorage(conf.storage);
418  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
419 
420  struct {
421  size_t num_events{0};
422  std::future<void> future;
423  std::promise<void> promise;
424  } ev_state;
425  ev_state.future = ev_state.promise.get_future();
426 
427  auto f_cb = [&ev_state](const std::shared_ptr<event::BaseEvent>& event) {
428  if (event->isTypeOf<event::DownloadProgressReport>()) {
429  return;
430  }
431  LOG_INFO << "Got " << event->variant;
432  switch (ev_state.num_events) {
433  case 0: {
434  ASSERT_EQ(event->variant, "UpdateCheckComplete");
435  const auto targets_event = dynamic_cast<event::UpdateCheckComplete*>(event.get());
436  EXPECT_EQ(targets_event->result.ecus_count, 2);
437  EXPECT_EQ(targets_event->result.updates.size(), 2u);
438  EXPECT_EQ(targets_event->result.updates[0].filename(), "primary_firmware.txt");
439  EXPECT_EQ(targets_event->result.updates[1].filename(), "secondary_firmware.txt");
440  EXPECT_EQ(targets_event->result.status, result::UpdateStatus::kUpdatesAvailable);
441  break;
442  }
443  case 1: {
444  ASSERT_EQ(event->variant, "DownloadTargetComplete");
445  const auto download_event = dynamic_cast<event::DownloadTargetComplete*>(event.get());
446  EXPECT_TRUE(download_event->update.filename() == "primary_firmware.txt");
447  EXPECT_TRUE(download_event->success);
448  break;
449  }
450  case 2: {
451  ASSERT_EQ(event->variant, "AllDownloadsComplete");
452  const auto downloads_complete = dynamic_cast<event::AllDownloadsComplete*>(event.get());
453  EXPECT_EQ(downloads_complete->result.updates.size(), 1);
454  EXPECT_TRUE(downloads_complete->result.updates[0].filename() == "primary_firmware.txt");
455  EXPECT_EQ(downloads_complete->result.status, result::DownloadStatus::kSuccess);
456  break;
457  }
458  case 3: {
459  // Primary always gets installed first. (Not a requirement, just how it
460  // works at present.)
461  ASSERT_EQ(event->variant, "InstallStarted");
462  const auto install_started = dynamic_cast<event::InstallStarted*>(event.get());
463  EXPECT_EQ(install_started->serial.ToString(), "CA:FE:A6:D2:84:9D");
464  break;
465  }
466  case 4: {
467  // Primary should complete before Secondary begins. (Again not a
468  // requirement per se.)
469  ASSERT_EQ(event->variant, "InstallTargetComplete");
470  const auto install_complete = dynamic_cast<event::InstallTargetComplete*>(event.get());
471  EXPECT_EQ(install_complete->serial.ToString(), "CA:FE:A6:D2:84:9D");
472  EXPECT_TRUE(install_complete->success);
473  break;
474  }
475  case 5: {
476  ASSERT_EQ(event->variant, "AllInstallsComplete");
477  const auto installs_complete = dynamic_cast<event::AllInstallsComplete*>(event.get());
478  EXPECT_EQ(installs_complete->result.ecu_reports.size(), 1);
479  EXPECT_EQ(installs_complete->result.ecu_reports[0].install_res.result_code.num_code,
480  data::ResultCode::Numeric::kOk);
481  break;
482  }
483  case 6: {
484  ASSERT_EQ(event->variant, "DownloadTargetComplete");
485  const auto download_event = dynamic_cast<event::DownloadTargetComplete*>(event.get());
486  EXPECT_TRUE(download_event->update.filename() == "secondary_firmware.txt");
487  EXPECT_TRUE(download_event->success);
488  break;
489  }
490  case 7: {
491  ASSERT_EQ(event->variant, "AllDownloadsComplete");
492  const auto downloads_complete = dynamic_cast<event::AllDownloadsComplete*>(event.get());
493  EXPECT_EQ(downloads_complete->result.updates.size(), 1);
494  EXPECT_TRUE(downloads_complete->result.updates[0].filename() == "secondary_firmware.txt");
495  EXPECT_EQ(downloads_complete->result.status, result::DownloadStatus::kSuccess);
496  break;
497  }
498  case 8: {
499  ASSERT_EQ(event->variant, "InstallStarted");
500  const auto install_started = dynamic_cast<event::InstallStarted*>(event.get());
501  EXPECT_EQ(install_started->serial.ToString(), "secondary_ecu_serial");
502  break;
503  }
504  case 9: {
505  ASSERT_EQ(event->variant, "InstallTargetComplete");
506  const auto install_complete = dynamic_cast<event::InstallTargetComplete*>(event.get());
507  EXPECT_EQ(install_complete->serial.ToString(), "secondary_ecu_serial");
508  EXPECT_TRUE(install_complete->success);
509  break;
510  }
511  case 10: {
512  ASSERT_EQ(event->variant, "AllInstallsComplete");
513  const auto installs_complete = dynamic_cast<event::AllInstallsComplete*>(event.get());
514  EXPECT_EQ(installs_complete->result.ecu_reports.size(), 1);
515  EXPECT_EQ(installs_complete->result.ecu_reports[0].install_res.result_code.num_code,
516  data::ResultCode::Numeric::kOk);
517  break;
518  }
519  case 11: {
520  ASSERT_EQ(event->variant, "UpdateCheckComplete");
521  const auto targets_event = dynamic_cast<event::UpdateCheckComplete*>(event.get());
522  EXPECT_EQ(targets_event->result.ecus_count, 0);
523  EXPECT_EQ(targets_event->result.updates.size(), 0);
524  EXPECT_EQ(targets_event->result.status, result::UpdateStatus::kNoUpdatesAvailable);
525  ev_state.promise.set_value();
526  break;
527  }
528  case 15:
529  // Don't let the test run indefinitely!
530  FAIL() << "Unexpected events!";
531  default:
532  std::cout << "event #" << ev_state.num_events << " is: " << event->variant << "\n";
533  ASSERT_EQ(event->variant, "");
534  }
535  ++ev_state.num_events;
536  };
537  boost::signals2::connection conn = aktualizr.SetSignalHandler(f_cb);
538 
539  aktualizr.Initialize();
540  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
541  ASSERT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
542  ASSERT_EQ(update_result.ecus_count, 2);
543 
544  // Try to install the updates in two separate calls.
545  result::Download download_result = aktualizr.Download({update_result.updates[0]}).get();
546  ASSERT_EQ(download_result.status, result::DownloadStatus::kSuccess);
547  aktualizr.Install(download_result.updates);
548 
549  download_result = aktualizr.Download({update_result.updates[1]}).get();
550  ASSERT_EQ(download_result.status, result::DownloadStatus::kSuccess);
551  aktualizr.Install(download_result.updates);
552 
553  update_result = aktualizr.CheckUpdates().get();
554  ASSERT_EQ(update_result.status, result::UpdateStatus::kNoUpdatesAvailable);
555 
556  auto status = ev_state.future.wait_for(std::chrono::seconds(20));
557  if (status != std::future_status::ready) {
558  FAIL() << "Timed out waiting for installation to complete.";
559  }
560  EXPECT_EQ(http->events_seen, 8);
561 }
562 
563 class HttpFakePutCounter : public HttpFake {
564  public:
565  HttpFakePutCounter(const boost::filesystem::path& test_dir_in, const boost::filesystem::path& meta_dir_in)
566  : HttpFake(test_dir_in, "hasupdates", meta_dir_in) {}
567 
568  HttpResponse put(const std::string& url, const Json::Value& data) override {
569  if (url.find("/manifest") != std::string::npos) {
570  manifest_sends += 1;
571  }
572  return HttpFake::put(url, data);
573  }
574 
575  HttpResponse handle_event(const std::string& url, const Json::Value& data) override {
576  (void)url;
577  // store all events in an array for later inspection
578  for (const Json::Value& event : data) {
579  events.push_back(event);
580  }
581  return HttpResponse("", 200, CURLE_OK, "");
582  }
583 
584  size_t count_event_with_type(const std::string& event_type) {
585  auto c = std::count_if(events.cbegin(), events.cend(), [&event_type](const Json::Value& v) {
586  return v["eventType"]["id"].asString() == event_type;
587  });
588  return static_cast<size_t>(c);
589  }
590 
591  unsigned int manifest_sends{0};
592  std::vector<Json::Value> events;
593 };
594 
595 /*
596  * Initialize -> UptaneCycle -> updates downloaded and installed for Primary
597  * (after reboot) and Secondary (aktualizr_test.cc)
598  *
599  * It simulates closely the OSTree case which needs a reboot after applying an
600  * update, but uses `PackageManagerFake`.
601  *
602  * Checks actions:
603  *
604  * - [x] Finalize a pending update that requires reboot
605  * - [x] Store installation result
606  * - [x] Send manifest
607  * - [x] Update is not in pending state anymore after successful finalization
608  *
609  * - [x] Install an update on the Primary
610  * - [x] Set new version to pending status after an OSTree update trigger
611  * - [x] Send EcuInstallationAppliedReport to server after an OSTree update trigger
612  * - [x] Uptane check for updates and manifest sends are disabled while an installation
613  * is pending reboot
614  */
615 TEST(Aktualizr, FullWithUpdatesNeedReboot) {
616  TemporaryDirectory temp_dir;
617  auto http = std::make_shared<HttpFakePutCounter>(temp_dir.Path(), fake_meta_dir);
618  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
619  conf.pacman.fake_need_reboot = true;
620 
621  {
622  // first run: do the install
623  auto storage = INvStorage::newStorage(conf.storage);
624  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
625  aktualizr.Initialize();
626  aktualizr.UptaneCycle();
627 
628  // check that a version is here, set to pending
629 
630  boost::optional<Uptane::Target> pending_target;
631  storage->loadPrimaryInstalledVersions(nullptr, &pending_target);
632  EXPECT_TRUE(!!pending_target);
633  }
634 
635  // check that no manifest has been sent after the update application
636  EXPECT_EQ(http->manifest_sends, 1);
637  EXPECT_EQ(http->count_event_with_type("EcuInstallationStarted"), 2); // two ECUs have started
638  EXPECT_EQ(http->count_event_with_type("EcuInstallationApplied"), 1); // Primary ECU has been applied
639  EXPECT_EQ(http->count_event_with_type("EcuInstallationCompleted"), 1); // Secondary ECU has been updated
640 
641  {
642  // second run: before reboot, re-use the storage
643  auto storage = INvStorage::newStorage(conf.storage);
644  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
645  aktualizr.Initialize();
646 
647  // check that everything is still pending
648  boost::optional<Uptane::Target> pending_target;
649  storage->loadPrimaryInstalledVersions(nullptr, &pending_target);
650  EXPECT_TRUE(!!pending_target);
651 
652  result::UpdateCheck update_res = aktualizr.CheckUpdates().get();
653  EXPECT_EQ(update_res.status, result::UpdateStatus::kError);
654 
655  // simulate a reboot
656  boost::filesystem::remove(conf.bootloader.reboot_sentinel_dir / conf.bootloader.reboot_sentinel_name);
657  }
658 
659  // still no manifest
660  EXPECT_EQ(http->manifest_sends, 1);
661  EXPECT_EQ(http->count_event_with_type("EcuInstallationCompleted"), 1); // no more installation completed
662 
663  {
664  // third run: after reboot, re-use the storage
665  auto storage = INvStorage::newStorage(conf.storage);
666  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
667  aktualizr.Initialize();
668 
669  result::UpdateCheck update_res = aktualizr.CheckUpdates().get();
670  EXPECT_EQ(update_res.status, result::UpdateStatus::kNoUpdatesAvailable);
671 
672  // Primary is installed, nothing pending
673  boost::optional<Uptane::Target> current_target;
674  boost::optional<Uptane::Target> pending_target;
675  storage->loadPrimaryInstalledVersions(&current_target, &pending_target);
676  EXPECT_TRUE(!!current_target);
677  EXPECT_FALSE(!!pending_target);
678 
679  // Secondary is installed, nothing pending
680  boost::optional<Uptane::Target> sec_current_target;
681  boost::optional<Uptane::Target> sec_pending_target;
682  storage->loadInstalledVersions("secondary_ecu_serial", &sec_current_target, &sec_pending_target);
683  EXPECT_TRUE(!!sec_current_target);
684  EXPECT_FALSE(!!sec_pending_target);
685  }
686 
687  // check that the manifest has been sent
688  EXPECT_EQ(http->manifest_sends, 3);
689  EXPECT_EQ(http->count_event_with_type("EcuInstallationCompleted"), 2); // 2 installations completed
690 
691  // check good correlation ids
692  for (const Json::Value& event : http->events) {
693  std::cout << "got event " << event["eventType"]["id"].asString() << "\n";
694  EXPECT_EQ(event["event"]["correlationId"].asString(), "id0");
695  }
696 }
697 
698 #ifdef FIU_ENABLE
699 
700 class HttpInstallationFailed : public HttpFake {
701  public:
702  HttpInstallationFailed(const boost::filesystem::path& test_dir_in, const boost::filesystem::path& meta_dir_in)
703  : HttpFake(test_dir_in, "hasupdates", meta_dir_in) {}
704 
705  HttpResponse handle_event(const std::string& url, const Json::Value& data) override {
706  (void)url;
707 
708  for (auto& event_data : data) {
709  auto event_id = event_data["eventType"]["id"].asString();
710  auto ecu = event_data["event"]["ecu"].asString();
711  auto correlationID = event_data["event"]["correlationId"].asString();
712 
713  if (event_id == "EcuInstallationCompleted") {
714  install_completion_status_[ecu] = event_data["event"]["success"].asBool();
715  }
716  report_events_.push_back(event_id);
717  }
718  return HttpResponse("", 200, CURLE_OK, "");
719  }
720 
721  bool checkReceivedReports(const std::vector<std::string>& expected_event_order) {
722  bool result = true;
723  auto received_event_it = report_events_.begin();
724 
725  for (auto& expected_event : expected_event_order) {
726  auto received_event = *received_event_it;
727  if (expected_event != received_event) {
728  result = false;
729  break;
730  }
731  ++received_event_it;
732  }
733  return result;
734  }
735 
736  bool wasInstallSuccessful(const std::string& ecu_serial) const {
737  auto find_it = install_completion_status_.find(ecu_serial);
738  if (find_it == install_completion_status_.end()) {
739  return false;
740  } else {
741  return (*find_it).second;
742  }
743  }
744  void clearReportEvents() { report_events_.clear(); }
745 
746  private:
747  std::vector<std::string> report_events_;
748  std::unordered_map<std::string, bool> install_completion_status_;
749 };
750 
751 class EventHandler {
752  public:
753  EventHandler(Aktualizr& aktualizr) {
754  functor_ = std::bind(&EventHandler::operator(), this, std::placeholders::_1);
755  aktualizr.SetSignalHandler(functor_);
756  }
757 
758  void operator()(const std::shared_ptr<event::BaseEvent>& event) {
759  ASSERT_NE(event, nullptr);
760  received_events_.push_back(event->variant);
761  }
762 
763  bool checkReceivedEvents(const std::vector<std::string>& expected_event_order) {
764  bool result = true;
765  auto received_event_it = received_events_.begin();
766 
767  for (auto& expected_event : expected_event_order) {
768  auto received_event = *received_event_it;
769  if (expected_event != received_event) {
770  result = false;
771  break;
772  }
773 
774  if (received_event == "DownloadProgressReport") {
775  while (*(++received_event_it) == "DownloadProgressReport") {
776  }
777  } else {
778  ++received_event_it;
779  }
780  }
781  return result;
782  }
783 
784  private:
785  std::function<void(std::shared_ptr<event::BaseEvent>)> functor_;
786  std::vector<std::string> received_events_{};
787 };
788 
789 /*
790  * Initialize -> UptaneCycle -> download updates and install them ->
791  * -> reboot emulated -> Initialize -> Fail installation finalization
792  *
793  * Verifies whether the Uptane client is not at pending state after installation finalization failure
794  *
795  * Checks actions:
796  *
797  * - [x] Update is not in pending state anymore after failed finalization
798  */
799 TEST(Aktualizr, FinalizationFailure) {
800  TemporaryDirectory temp_dir;
801  auto http_server_mock = std::make_shared<HttpInstallationFailed>(temp_dir.Path(), fake_meta_dir);
802  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http_server_mock->tls_server);
803  conf.pacman.fake_need_reboot = true;
804  conf.uptane.force_install_completion = true;
805  conf.uptane.polling_sec = 0;
806  auto storage = INvStorage::newStorage(conf.storage);
807 
808  std::vector<std::string> expected_event_order = {
809  "SendDeviceDataComplete", "UpdateCheckComplete", "DownloadProgressReport", "DownloadTargetComplete",
810  "DownloadProgressReport", "DownloadTargetComplete", "AllDownloadsComplete", "InstallStarted",
811  "InstallTargetComplete", "InstallStarted", "InstallTargetComplete", "AllInstallsComplete"};
812 
813  std::vector<std::string> expected_report_order = {
814  "EcuDownloadStarted", "EcuDownloadCompleted", "EcuDownloadStarted", "EcuDownloadCompleted",
815  "EcuInstallationStarted", "EcuInstallationApplied", "EcuInstallationStarted", "EcuInstallationCompleted"};
816 
817  const std::string primary_ecu_id = "CA:FE:A6:D2:84:9D";
818  const std::string secondary_ecu_id = "secondary_ecu_serial";
819 
820  {
821  // get update, install them and emulate reboot
822  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http_server_mock);
823  EventHandler event_hdlr(aktualizr);
824  aktualizr.Initialize();
825 
826  // verify currently installed version
827  boost::optional<Uptane::Target> current_version;
828  boost::optional<Uptane::Target> pending_version;
829  ASSERT_TRUE(storage->loadInstalledVersions(primary_ecu_id, &current_version, &pending_version));
830 
831  // for some reason there is no any installed version at initial Aktualizr boot/run
832  // IMHO it should return currently installed version
833  EXPECT_FALSE(!!pending_version);
834  EXPECT_FALSE(!!current_version);
835 
836  auto aktualizr_cycle_thread = aktualizr.RunForever();
837  auto aktualizr_cycle_thread_status = aktualizr_cycle_thread.wait_for(std::chrono::seconds(20));
838 
839  ASSERT_EQ(aktualizr_cycle_thread_status, std::future_status::ready);
840  EXPECT_TRUE(aktualizr.uptane_client()->isInstallCompletionRequired());
841  EXPECT_TRUE(event_hdlr.checkReceivedEvents(expected_event_order));
842  EXPECT_TRUE(aktualizr.uptane_client()->hasPendingUpdates());
843  EXPECT_TRUE(http_server_mock->checkReceivedReports(expected_report_order));
844  // Aktualizr reports to a server that installation was successfull for the Secondary
845  // checkReceivedReports() verifies whether EcuInstallationApplied was reported for the Primary
846  EXPECT_FALSE(http_server_mock->wasInstallSuccessful(primary_ecu_id));
847  EXPECT_TRUE(http_server_mock->wasInstallSuccessful(secondary_ecu_id));
848 
849  data::InstallationResult dev_installation_res;
850  std::string report;
851  std::string correlation_id;
852  ASSERT_TRUE(storage->loadDeviceInstallationResult(&dev_installation_res, &report, &correlation_id));
853  EXPECT_EQ(dev_installation_res.result_code.num_code, data::ResultCode::Numeric::kNeedCompletion);
854 
855  std::vector<std::pair<Uptane::EcuSerial, data::InstallationResult>> ecu_installation_res;
856  ASSERT_TRUE(storage->loadEcuInstallationResults(&ecu_installation_res));
857  EXPECT_EQ(ecu_installation_res.size(), 2);
858 
859  for (const auto& ecu_install_res : ecu_installation_res) {
860  auto ecu_id = ecu_install_res.first.ToString();
861 
862  if (ecu_id == primary_ecu_id) {
863  EXPECT_EQ(ecu_install_res.second.result_code.num_code, data::ResultCode::Numeric::kNeedCompletion);
864  EXPECT_EQ(ecu_install_res.second.success, false);
865  EXPECT_EQ(ecu_install_res.second.description, "Application successful, need reboot");
866 
867  } else if (ecu_id == secondary_ecu_id) {
868  EXPECT_EQ(ecu_install_res.second.result_code.num_code, data::ResultCode::Numeric::kOk);
869  EXPECT_EQ(ecu_install_res.second.success, true);
870  EXPECT_EQ(ecu_install_res.second.description, "");
871 
872  } else {
873  FAIL() << "Unexpected ECU serial: " << ecu_install_res.first;
874  }
875  }
876 
877  pending_version = boost::none;
878  current_version = boost::none;
879 
880  ASSERT_TRUE(storage->loadInstalledVersions(primary_ecu_id, &current_version, &pending_version));
881  EXPECT_FALSE(!!current_version);
882  EXPECT_TRUE(!!pending_version);
883  EXPECT_TRUE(pending_version->IsValid());
884 
885  pending_version = boost::none;
886  current_version = boost::none;
887 
888  ASSERT_TRUE(storage->loadInstalledVersions(secondary_ecu_id, &current_version, &pending_version));
889  EXPECT_TRUE(!!current_version);
890  EXPECT_TRUE(current_version->IsValid());
891  EXPECT_FALSE(!!pending_version);
892  }
893 
894  {
895  // now try to finalize the previously installed updates with enabled failure injection
896  fault_injection_init();
897  fiu_enable("fake_install_finalization_failure", 1, nullptr, 0);
898  http_server_mock->clearReportEvents();
899 
900  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http_server_mock);
901  EventHandler event_hdlr(aktualizr);
902  aktualizr.Initialize();
903 
904  fiu_disable("fake_install_finalization_failure");
905 
906  EXPECT_FALSE(aktualizr.uptane_client()->hasPendingUpdates());
907  EXPECT_TRUE(http_server_mock->checkReceivedReports({"EcuInstallationCompleted"}));
908  EXPECT_FALSE(http_server_mock->wasInstallSuccessful(primary_ecu_id));
909 
910  data::InstallationResult dev_installation_res;
911  std::string report;
912  std::string correlation_id;
913 
914  // `device_installation_result` and `ecu_installation_results` are cleared
915  // at finalizeAfterReboot()->putManifestSimple() once a device manifest is successfully sent to a server
916  EXPECT_FALSE(storage->loadDeviceInstallationResult(&dev_installation_res, &report, &correlation_id));
917 
918  // it's used to return `true` even if there is no any record in DB
919  // of the Uptane cycle just after sending manifest
920  // made it consistent with loadDeviceInstallationResult
921  std::vector<std::pair<Uptane::EcuSerial, data::InstallationResult>> ecu_installation_res;
922  EXPECT_FALSE(storage->loadEcuInstallationResults(&ecu_installation_res));
923 
924  // verify currently installed version
925  boost::optional<Uptane::Target> current_version;
926  boost::optional<Uptane::Target> pending_version;
927 
928  ASSERT_TRUE(storage->loadInstalledVersions(primary_ecu_id, &current_version, &pending_version));
929  EXPECT_FALSE(!!current_version);
930  EXPECT_FALSE(!!pending_version);
931 
932  current_version = boost::none;
933  pending_version = boost::none;
934 
935  ASSERT_TRUE(storage->loadInstalledVersions(secondary_ecu_id, &current_version, &pending_version));
936  EXPECT_TRUE(!!current_version);
937  EXPECT_FALSE(!!pending_version);
938  }
939 }
940 
941 /*
942  * Initialize -> UptaneCycle -> download updates -> fail installation
943  *
944  * Verifies whether the Uptane client is not at pending state after installation failure
945  *
946  * Checks actions:
947  *
948  * - [x] Update is not in pending state anymore after failed installation
949  * - [x] Store negative device installation result when an ECU installation failed
950  */
951 TEST(Aktualizr, InstallationFailure) {
952  std::vector<std::string> expected_event_order = {
953  "UpdateCheckComplete", "DownloadProgressReport", "DownloadTargetComplete", "DownloadProgressReport",
954  "DownloadTargetComplete", "AllDownloadsComplete", "InstallStarted", "InstallTargetComplete",
955  "InstallStarted", "InstallTargetComplete", "AllInstallsComplete", "PutManifestComplete"};
956 
957  std::vector<std::string> expected_report_order = {
958  "EcuDownloadStarted", "EcuDownloadCompleted", "EcuDownloadStarted", "EcuDownloadCompleted",
959  "EcuInstallationStarted", "EcuInstallationCompleted", "EcuInstallationStarted", "EcuInstallationCompleted"};
960 
961  const std::string primary_ecu_id = "CA:FE:A6:D2:84:9D";
962  const std::string secondary_ecu_id = "secondary_ecu_serial";
963 
964  {
965  TemporaryDirectory temp_dir;
966  auto http_server_mock = std::make_shared<HttpInstallationFailed>(temp_dir.Path(), fake_meta_dir);
967  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http_server_mock->tls_server);
968  auto storage = INvStorage::newStorage(conf.storage);
969 
970  fault_injection_init();
971  fiu_enable("fake_package_install", 1, nullptr, 0);
972 
973  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http_server_mock);
974  EventHandler event_hdlr(aktualizr);
975  aktualizr.Initialize();
976 
977  // verify currently installed version
978  boost::optional<Uptane::Target> current_version;
979  boost::optional<Uptane::Target> pending_version;
980 
981  ASSERT_TRUE(storage->loadInstalledVersions(primary_ecu_id, &current_version, &pending_version));
982 
983  EXPECT_FALSE(!!pending_version);
984  EXPECT_FALSE(!!current_version);
985 
986  aktualizr.UptaneCycle();
987  aktualizr.uptane_client()->completeInstall();
988 
989  EXPECT_TRUE(event_hdlr.checkReceivedEvents(expected_event_order));
990  EXPECT_FALSE(aktualizr.uptane_client()->hasPendingUpdates());
991  EXPECT_TRUE(http_server_mock->checkReceivedReports(expected_report_order));
992  EXPECT_FALSE(http_server_mock->wasInstallSuccessful(primary_ecu_id));
993  EXPECT_TRUE(http_server_mock->wasInstallSuccessful(secondary_ecu_id));
994 
995  data::InstallationResult dev_installation_res;
996  std::string report;
997  std::string correlation_id;
998 
999  // `device_installation_result` and `ecu_installation_results` are cleared
1000  // at UptaneCycle()->putManifest() once a device manifest is successfully sent to a server
1001  EXPECT_FALSE(storage->loadDeviceInstallationResult(&dev_installation_res, &report, &correlation_id));
1002 
1003  std::vector<std::pair<Uptane::EcuSerial, data::InstallationResult>> ecu_installation_res;
1004  EXPECT_FALSE(storage->loadEcuInstallationResults(&ecu_installation_res));
1005  EXPECT_EQ(ecu_installation_res.size(), 0);
1006 
1007  ASSERT_TRUE(storage->loadInstalledVersions(primary_ecu_id, &current_version, &pending_version));
1008  // it says that no any installed version found,
1009  // which is, on one hand is correct since installation of the found update failed hence nothing was installed,
1010  // on the other hand some version should have been installed prior to the failed update
1011  EXPECT_FALSE(!!current_version);
1012  EXPECT_FALSE(!!pending_version);
1013 
1014  fiu_disable("fake_package_install");
1015  }
1016 
1017  // Primary and Secondary failure
1018  for (std::string prefix : {"secondary_install_", "secondary_sendfirmware_"}) {
1019  TemporaryDirectory temp_dir;
1020  auto http_server_mock = std::make_shared<HttpInstallationFailed>(temp_dir.Path(), fake_meta_dir);
1021  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http_server_mock->tls_server);
1022  auto storage = INvStorage::newStorage(conf.storage);
1023  const std::string sec_fault_name = prefix + secondary_ecu_id;
1024 
1025  fault_injection_init();
1026  fault_injection_enable("fake_package_install", 1, "PRIMFAIL", 0);
1027  fault_injection_enable(sec_fault_name.c_str(), 1, "SECFAIL", 0);
1028 
1029  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http_server_mock);
1030  EventHandler event_hdlr(aktualizr);
1031  aktualizr.Initialize();
1032 
1033  // verify currently installed version
1034  boost::optional<Uptane::Target> current_version;
1035  boost::optional<Uptane::Target> pending_version;
1036 
1037  ASSERT_TRUE(storage->loadInstalledVersions(primary_ecu_id, &current_version, &pending_version));
1038 
1039  EXPECT_FALSE(!!pending_version);
1040  EXPECT_FALSE(!!current_version);
1041 
1042  aktualizr.UptaneCycle();
1043  aktualizr.uptane_client()->completeInstall();
1044 
1045  EXPECT_TRUE(event_hdlr.checkReceivedEvents(expected_event_order));
1046  EXPECT_FALSE(aktualizr.uptane_client()->hasPendingUpdates());
1047  EXPECT_TRUE(http_server_mock->checkReceivedReports(expected_report_order));
1048  EXPECT_FALSE(http_server_mock->wasInstallSuccessful(primary_ecu_id));
1049  EXPECT_FALSE(http_server_mock->wasInstallSuccessful(secondary_ecu_id));
1050 
1051  LOG_INFO << http_server_mock->last_manifest;
1052  Json::Value installation_report = http_server_mock->last_manifest["signed"]["installation_report"]["report"];
1053  EXPECT_EQ(installation_report["items"][0]["result"]["code"].asString(), "PRIMFAIL");
1054  EXPECT_EQ(installation_report["items"][1]["result"]["code"].asString(), "SECFAIL");
1055  EXPECT_EQ(installation_report["result"]["code"].asString(), "primary_hw:PRIMFAIL|secondary_hw:SECFAIL");
1056 
1057  data::InstallationResult dev_installation_res;
1058  std::string report;
1059  std::string correlation_id;
1060 
1061  // `device_installation_result` and `ecu_installation_results` are cleared
1062  // at UptaneCycle()->putManifest() once a device manifest is successfully sent to a server
1063  EXPECT_FALSE(storage->loadDeviceInstallationResult(&dev_installation_res, &report, &correlation_id));
1064 
1065  std::vector<std::pair<Uptane::EcuSerial, data::InstallationResult>> ecu_installation_res;
1066  EXPECT_FALSE(storage->loadEcuInstallationResults(&ecu_installation_res));
1067  EXPECT_EQ(ecu_installation_res.size(), 0);
1068 
1069  ASSERT_TRUE(storage->loadInstalledVersions(primary_ecu_id, &current_version, &pending_version));
1070  // it says that no any installed version found,
1071  // which is, on one hand is correct since installation of the found update failed hence nothing was installed,
1072  // on the other hand some version should have been installed prior to the failed update
1073  EXPECT_FALSE(!!current_version);
1074  EXPECT_FALSE(!!pending_version);
1075 
1076  fault_injection_disable("fake_package_install");
1077  fault_injection_disable(sec_fault_name.c_str());
1078  }
1079 }
1080 
1081 /*
1082  * Verifies that updates fail after metadata verification failure reported by Secondaries
1083  */
1084 TEST(Aktualizr, SecondaryMetaFailure) {
1085  TemporaryDirectory temp_dir;
1086  auto http_server_mock = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates", fake_meta_dir);
1087  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http_server_mock->tls_server);
1088  auto storage = INvStorage::newStorage(conf.storage);
1089 
1090  fault_injection_init();
1091  fiu_enable("secondary_putmetadata", 1, nullptr, 0);
1092 
1093  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http_server_mock);
1094 
1095  struct {
1097  std::promise<void> promise;
1098  } ev_state;
1099 
1100  auto f_cb = [&ev_state](const std::shared_ptr<event::BaseEvent>& event) {
1101  if (event->variant != "AllInstallsComplete") {
1102  return;
1103  }
1104  const auto installs_complete = dynamic_cast<event::AllInstallsComplete*>(event.get());
1105  ev_state.result = installs_complete->result;
1106 
1107  ev_state.promise.set_value();
1108  };
1109  boost::signals2::connection conn = aktualizr.SetSignalHandler(f_cb);
1110  aktualizr.Initialize();
1111  aktualizr.UptaneCycle();
1112 
1113  auto status = ev_state.promise.get_future().wait_for(std::chrono::seconds(20));
1114  if (status != std::future_status::ready) {
1115  FAIL() << "Timed out waiting for installation to complete.";
1116  }
1117 
1118  ASSERT_FALSE(ev_state.result.dev_report.isSuccess());
1119 
1120  fiu_disable("secondary_putmetadata");
1121 }
1122 
1123 #endif // FIU_ENABLE
1124 
1125 /*
1126  * Initialize -> UptaneCycle -> updates downloaded and installed for Primary
1127  * -> reboot emulated -> Initialize -> Finalize Installation After Reboot
1128  *
1129  * Verifies an auto reboot and pending updates finalization after a reboot
1130  * in case of the fake package manager usage
1131  *
1132  * Checks actions:
1133  *
1134  * - [x] Emulate a reboot at the end of the installation process in case of the fake package manager usage
1135  * - [x] Finalize a pending update that requires reboot
1136  */
1137 TEST(Aktualizr, AutoRebootAfterUpdate) {
1138  TemporaryDirectory temp_dir;
1139  auto http = std::make_shared<HttpFakePutCounter>(temp_dir.Path(), fake_meta_dir);
1140  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1141  conf.pacman.fake_need_reboot = true;
1142  conf.uptane.force_install_completion = true;
1143  conf.uptane.polling_sec = 0;
1144 
1145  {
1146  // first run: do the install, exit UptaneCycle and emulate reboot since force_install_completion is set
1147  auto storage = INvStorage::newStorage(conf.storage);
1148  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1149 
1150  aktualizr.Initialize();
1151  auto aktualizr_cycle_thread = aktualizr.RunForever();
1152  auto aktualizr_cycle_thread_status = aktualizr_cycle_thread.wait_for(std::chrono::seconds(20));
1153  aktualizr.Shutdown();
1154 
1155  EXPECT_EQ(aktualizr_cycle_thread_status, std::future_status::ready);
1156  EXPECT_TRUE(aktualizr.uptane_client()->isInstallCompletionRequired());
1157  }
1158 
1159  {
1160  // second run: after emulated reboot, check if the update was applied
1161  auto storage = INvStorage::newStorage(conf.storage);
1162  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1163 
1164  aktualizr.Initialize();
1165 
1166  result::UpdateCheck update_res = aktualizr.CheckUpdates().get();
1167  EXPECT_EQ(update_res.status, result::UpdateStatus::kNoUpdatesAvailable);
1168 
1169  // Primary is installed, nothing pending
1170  boost::optional<Uptane::Target> current_target;
1171  boost::optional<Uptane::Target> pending_target;
1172  storage->loadPrimaryInstalledVersions(&current_target, &pending_target);
1173  EXPECT_TRUE(!!current_target);
1174  EXPECT_FALSE(!!pending_target);
1175  EXPECT_EQ(http->manifest_sends, 3);
1176  }
1177 }
1178 
1179 /*
1180  * Initialize -> UptaneCycle -> updates downloaded and installed for Secondaries
1181  * without changing the Primary.
1182 
1183  * Store installation result for Secondary
1184  */
1185 TEST(Aktualizr, FullMultipleSecondaries) {
1186  TemporaryDirectory temp_dir;
1187  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "multisec", fake_meta_dir);
1188  Config conf("tests/config/basic.toml");
1189  conf.provision.primary_ecu_serial = "testecuserial";
1190  conf.provision.primary_ecu_hardware_id = "testecuhwid";
1191  conf.storage.path = temp_dir.Path();
1192  conf.pacman.images_path = temp_dir.Path() / "images";
1193  conf.tls.server = http->tls_server;
1194  conf.uptane.director_server = http->tls_server + "/director";
1195  conf.uptane.repo_server = http->tls_server + "/repo";
1196 
1197  TemporaryDirectory temp_dir2;
1198  UptaneTestCommon::addDefaultSecondary(conf, temp_dir, "sec_serial1", "sec_hw1");
1199 
1200  auto storage = INvStorage::newStorage(conf.storage);
1201  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1202  UptaneTestCommon::addDefaultSecondary(conf, temp_dir2, "sec_serial2", "sec_hw2");
1203  ASSERT_NO_THROW(aktualizr.AddSecondary(std::make_shared<Primary::VirtualSecondary>(
1204  Primary::VirtualSecondaryConfig::create_from_file(conf.uptane.secondary_config_file)[0])));
1205 
1206  struct {
1207  int started{0};
1208  int complete{0};
1209  bool allcomplete{false};
1210  bool manifest{false};
1211  std::future<void> future;
1212  std::promise<void> promise;
1213  } ev_state;
1214  ev_state.future = ev_state.promise.get_future();
1215 
1216  auto f_cb = [&ev_state](const std::shared_ptr<event::BaseEvent>& event) {
1217  if (event->variant == "InstallStarted") {
1218  ++ev_state.started;
1219  } else if (event->variant == "InstallTargetComplete") {
1220  ++ev_state.complete;
1221  } else if (event->variant == "AllInstallsComplete") {
1222  ev_state.allcomplete = true;
1223  } else if (event->variant == "PutManifestComplete") {
1224  ev_state.manifest = true;
1225  }
1226  // It is possible for the PutManifestComplete to come before we get the
1227  // InstallTargetComplete depending on the threading, so check for both.
1228  if (ev_state.allcomplete && ev_state.complete == 2 && ev_state.manifest) {
1229  ev_state.promise.set_value();
1230  }
1231  };
1232  boost::signals2::connection conn = aktualizr.SetSignalHandler(f_cb);
1233 
1234  aktualizr.Initialize();
1235  aktualizr.UptaneCycle();
1236 
1237  auto status = ev_state.future.wait_for(std::chrono::seconds(20));
1238  if (status != std::future_status::ready) {
1239  FAIL() << "Timed out waiting for installation to complete.";
1240  }
1241 
1242  EXPECT_EQ(ev_state.started, 2);
1243  EXPECT_EQ(ev_state.complete, 2);
1244 
1245  const Json::Value manifest = http->last_manifest["signed"];
1246  const Json::Value manifest_versions = manifest["ecu_version_manifests"];
1247 
1248  // Make sure filepath were correctly written and formatted.
1249  EXPECT_EQ(manifest_versions["sec_serial1"]["signed"]["installed_image"]["filepath"].asString(),
1250  "secondary_firmware.txt");
1251  EXPECT_EQ(manifest_versions["sec_serial1"]["signed"]["installed_image"]["fileinfo"]["length"].asUInt(), 15);
1252  EXPECT_EQ(manifest_versions["sec_serial2"]["signed"]["installed_image"]["filepath"].asString(),
1253  "secondary_firmware2.txt");
1254  EXPECT_EQ(manifest_versions["sec_serial2"]["signed"]["installed_image"]["fileinfo"]["length"].asUInt(), 21);
1255 
1256  // Make sure there is no result for the Primary by checking the size
1257  EXPECT_EQ(manifest["installation_report"]["report"]["items"].size(), 2);
1258  EXPECT_EQ(manifest["installation_report"]["report"]["items"][0]["ecu"].asString(), "sec_serial1");
1259  EXPECT_TRUE(manifest["installation_report"]["report"]["items"][0]["result"]["success"].asBool());
1260  EXPECT_EQ(manifest["installation_report"]["report"]["items"][1]["ecu"].asString(), "sec_serial2");
1261  EXPECT_TRUE(manifest["installation_report"]["report"]["items"][1]["result"]["success"].asBool());
1262 }
1263 
1264 /*
1265  * Initialize -> CheckUpdates -> no updates -> no further action or events.
1266  */
1267 TEST(Aktualizr, CheckNoUpdates) {
1268  TemporaryDirectory temp_dir;
1269  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "noupdates", fake_meta_dir);
1270  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1271 
1272  auto storage = INvStorage::newStorage(conf.storage);
1273  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1274 
1275  struct {
1276  size_t num_events{0};
1277  std::future<void> future;
1278  std::promise<void> promise;
1279  } ev_state;
1280  ev_state.future = ev_state.promise.get_future();
1281 
1282  auto f_cb = [&ev_state](const std::shared_ptr<event::BaseEvent>& event) {
1283  if (event->isTypeOf<event::DownloadProgressReport>()) {
1284  return;
1285  }
1286  LOG_INFO << "Got " << event->variant;
1287  switch (ev_state.num_events) {
1288  case 0: {
1289  ASSERT_EQ(event->variant, "UpdateCheckComplete");
1290  const auto targets_event = dynamic_cast<event::UpdateCheckComplete*>(event.get());
1291  EXPECT_EQ(targets_event->result.ecus_count, 0);
1292  EXPECT_EQ(targets_event->result.updates.size(), 0);
1293  EXPECT_EQ(targets_event->result.status, result::UpdateStatus::kNoUpdatesAvailable);
1294  break;
1295  }
1296  case 1: {
1297  ASSERT_EQ(event->variant, "UpdateCheckComplete");
1298  const auto targets_event = dynamic_cast<event::UpdateCheckComplete*>(event.get());
1299  EXPECT_EQ(targets_event->result.ecus_count, 0);
1300  EXPECT_EQ(targets_event->result.updates.size(), 0);
1301  EXPECT_EQ(targets_event->result.status, result::UpdateStatus::kNoUpdatesAvailable);
1302  ev_state.promise.set_value();
1303  break;
1304  }
1305  case 5:
1306  // Don't let the test run indefinitely!
1307  FAIL() << "Unexpected events!";
1308  default:
1309  std::cout << "event #" << ev_state.num_events << " is: " << event->variant << "\n";
1310  ASSERT_EQ(event->variant, "");
1311  }
1312  ++ev_state.num_events;
1313  };
1314 
1315  boost::signals2::connection conn = aktualizr.SetSignalHandler(f_cb);
1316 
1317  aktualizr.Initialize();
1318  result::UpdateCheck result = aktualizr.CheckUpdates().get();
1319  EXPECT_EQ(result.ecus_count, 0);
1320  EXPECT_EQ(result.updates.size(), 0);
1321  EXPECT_EQ(result.status, result::UpdateStatus::kNoUpdatesAvailable);
1322  // Fetch twice so that we can check for a second UpdateCheckComplete and
1323  // guarantee that nothing unexpected happened after the first fetch.
1324  result = aktualizr.CheckUpdates().get();
1325  EXPECT_EQ(result.ecus_count, 0);
1326  EXPECT_EQ(result.updates.size(), 0);
1327  EXPECT_EQ(result.status, result::UpdateStatus::kNoUpdatesAvailable);
1328 
1329  auto status = ev_state.future.wait_for(std::chrono::seconds(20));
1330  if (status != std::future_status::ready) {
1331  FAIL() << "Timed out waiting for metadata to be fetched.";
1332  }
1333 
1334  verifyNothingInstalled(aktualizr.uptane_client()->AssembleManifest());
1335 }
1336 
1337 /*
1338  * Initialize -> Download -> nothing to download.
1339  *
1340  * Initialize -> CheckUpdates -> Download -> updates downloaded but not
1341  * installed.
1342  */
1343 TEST(Aktualizr, DownloadWithUpdates) {
1344  TemporaryDirectory temp_dir;
1345  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates", fake_meta_dir);
1346  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1347 
1348  auto storage = INvStorage::newStorage(conf.storage);
1349  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1350 
1351  struct {
1352  size_t num_events{0};
1353  std::future<void> future;
1354  std::promise<void> promise;
1355  } ev_state;
1356  ev_state.future = ev_state.promise.get_future();
1357 
1358  auto f_cb = [&ev_state](const std::shared_ptr<event::BaseEvent>& event) {
1359  if (event->isTypeOf<event::DownloadProgressReport>()) {
1360  return;
1361  }
1362  LOG_INFO << "Got " << event->variant;
1363  switch (ev_state.num_events) {
1364  case 0: {
1365  ASSERT_EQ(event->variant, "AllDownloadsComplete");
1366  const auto downloads_complete = dynamic_cast<event::AllDownloadsComplete*>(event.get());
1367  EXPECT_EQ(downloads_complete->result.updates.size(), 0);
1368  EXPECT_EQ(downloads_complete->result.status, result::DownloadStatus::kError);
1369  break;
1370  }
1371  case 1: {
1372  ASSERT_EQ(event->variant, "UpdateCheckComplete");
1373  const auto targets_event = dynamic_cast<event::UpdateCheckComplete*>(event.get());
1374  EXPECT_EQ(targets_event->result.ecus_count, 2);
1375  EXPECT_EQ(targets_event->result.updates.size(), 2u);
1376  EXPECT_EQ(targets_event->result.updates[0].filename(), "primary_firmware.txt");
1377  EXPECT_EQ(targets_event->result.updates[1].filename(), "secondary_firmware.txt");
1378  EXPECT_EQ(targets_event->result.status, result::UpdateStatus::kUpdatesAvailable);
1379  break;
1380  }
1381  case 2:
1382  case 3: {
1383  ASSERT_EQ(event->variant, "DownloadTargetComplete");
1384  const auto download_event = dynamic_cast<event::DownloadTargetComplete*>(event.get());
1385  EXPECT_TRUE(download_event->update.filename() == "primary_firmware.txt" ||
1386  download_event->update.filename() == "secondary_firmware.txt");
1387  EXPECT_TRUE(download_event->success);
1388  break;
1389  }
1390  case 4: {
1391  ASSERT_EQ(event->variant, "AllDownloadsComplete");
1392  const auto downloads_complete = dynamic_cast<event::AllDownloadsComplete*>(event.get());
1393  EXPECT_EQ(downloads_complete->result.updates.size(), 2);
1394  EXPECT_TRUE(downloads_complete->result.updates[0].filename() == "primary_firmware.txt" ||
1395  downloads_complete->result.updates[1].filename() == "primary_firmware.txt");
1396  EXPECT_TRUE(downloads_complete->result.updates[0].filename() == "secondary_firmware.txt" ||
1397  downloads_complete->result.updates[1].filename() == "secondary_firmware.txt");
1398  EXPECT_EQ(downloads_complete->result.status, result::DownloadStatus::kSuccess);
1399  ev_state.promise.set_value();
1400  break;
1401  }
1402  case 8:
1403  // Don't let the test run indefinitely!
1404  FAIL() << "Unexpected events!";
1405  default:
1406  std::cout << "event #" << ev_state.num_events << " is: " << event->variant << "\n";
1407  ASSERT_EQ(event->variant, "");
1408  }
1409  ++ev_state.num_events;
1410  };
1411  boost::signals2::connection conn = aktualizr.SetSignalHandler(f_cb);
1412 
1413  aktualizr.Initialize();
1414  // First try downloading nothing. Nothing should happen.
1415  result::Download result = aktualizr.Download(std::vector<Uptane::Target>()).get();
1416  EXPECT_EQ(result.updates.size(), 0);
1417  EXPECT_EQ(result.status, result::DownloadStatus::kError);
1418 
1419  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1420  aktualizr.Download(update_result.updates);
1421 
1422  auto status = ev_state.future.wait_for(std::chrono::seconds(20));
1423  if (status != std::future_status::ready) {
1424  FAIL() << "Timed out waiting for downloads to complete.";
1425  }
1426 
1427  verifyNothingInstalled(aktualizr.uptane_client()->AssembleManifest());
1428 }
1429 
1431  public:
1432  using Responses = std::vector<std::pair<std::string, HttpResponse>>;
1433 
1434  public:
1435  HttpDownloadFailure(const boost::filesystem::path& test_dir_in, const Responses& file_to_response, std::string flavor,
1436  const boost::filesystem::path& meta_dir_in)
1437  : HttpFake(test_dir_in, flavor, meta_dir_in) {
1438  for (auto resp : file_to_response) {
1439  url_to_response_[tls_server + target_dir_ + resp.first] = resp.second;
1440  }
1441  }
1442 
1443  HttpResponse download(const std::string& url, curl_write_callback write_cb, curl_xferinfo_callback progress_cb,
1444  void* userp, curl_off_t from) override {
1445  const auto found_response_it = url_to_response_.find(url);
1446  if (found_response_it == url_to_response_.end()) {
1447  return HttpResponse("", 500, CURLE_HTTP_RETURNED_ERROR, "Internal Server Error");
1448  }
1449 
1450  auto response = url_to_response_.at(url);
1451  if (response.isOk()) {
1452  return HttpFake::download(url, write_cb, progress_cb, userp, from);
1453  }
1454 
1455  return url_to_response_[url];
1456  }
1457 
1458  private:
1459  const std::string target_dir_{"/repo/targets/"};
1460  std::map<std::string, HttpResponse> url_to_response_;
1461 };
1462 
1463 /**
1464  * Checks whether events are sent if download is unsuccessful
1465  *
1466  * Checks actions:
1467  * - Send DownloadTargetComplete event if download is unsuccessful
1468  * - Send AllDownloadsComplete event after partial download success
1469  * - Send AllDownloadsComplete event if all downloads are unsuccessful
1470  */
1471 TEST(Aktualizr, DownloadFailures) {
1472  class DownloadEventHandler {
1473  public:
1474  DownloadEventHandler(Aktualizr& aktualizr) {
1475  functor_ = std::bind(&DownloadEventHandler::operator(), this, std::placeholders::_1);
1476  aktualizr.SetSignalHandler(functor_);
1477  }
1478 
1479  void operator()(const std::shared_ptr<event::BaseEvent>& event) {
1480  ASSERT_NE(event, nullptr);
1481 
1482  if (event->isTypeOf<event::DownloadTargetComplete>()) {
1483  auto download_target_complete_event = dynamic_cast<event::DownloadTargetComplete*>(event.get());
1484  auto target_filename = download_target_complete_event->update.filename();
1485  download_status[target_filename] = download_target_complete_event->success;
1486 
1487  } else if (event->isTypeOf<event::AllDownloadsComplete>()) {
1488  auto all_download_complete_event = dynamic_cast<event::AllDownloadsComplete*>(event.get());
1489  all_download_completed_status = all_download_complete_event->result;
1490  }
1491  }
1492 
1493  public:
1494  std::map<std::string, bool> download_status;
1495  result::Download all_download_completed_status;
1496 
1497  private:
1498  std::function<void(std::shared_ptr<event::BaseEvent>)> functor_;
1499  };
1500 
1501  struct TestParams {
1502  HttpDownloadFailure::Responses downloadResponse;
1503  std::vector<std::pair<std::string, bool>> downloadResult;
1504  result::DownloadStatus allDownloadsStatus;
1505  };
1506 
1507  TestParams test_case_params[]{
1508  {// test case 0: each target download fails
1509  {{"primary_firmware.txt", HttpResponse("", 500, CURLE_HTTP_RETURNED_ERROR, "Internal Server Error")},
1510  {"secondary_firmware.txt", HttpResponse("", 500, CURLE_HTTP_RETURNED_ERROR, "Internal Server Error")}},
1511  {{"primary_firmware.txt", false}, {"secondary_firmware.txt", false}},
1512  result::DownloadStatus::kError},
1513  {// test case 1: first target download succeeds, second target download fails
1514  {{"primary_firmware.txt", HttpResponse("", 200, CURLE_OK, "")},
1515  {"secondary_firmware.txt", HttpResponse("", 404, CURLE_HTTP_RETURNED_ERROR, "Not found")}},
1516  {{"primary_firmware.txt", true}, {"secondary_firmware.txt", false}},
1517  result::DownloadStatus::kPartialSuccess},
1518  {// test case 2: first target download fails, second target download succeeds
1519  {{"primary_firmware.txt", HttpResponse("", 404, CURLE_HTTP_RETURNED_ERROR, "Not found")},
1520  {"secondary_firmware.txt", HttpResponse("", 200, CURLE_OK, "")}},
1521  {{"primary_firmware.txt", false}, {"secondary_firmware.txt", true}},
1522  result::DownloadStatus::kPartialSuccess}};
1523 
1524  for (auto test_params : test_case_params) {
1525  TemporaryDirectory temp_dir;
1526 
1527  auto http = std::make_shared<HttpDownloadFailure>(temp_dir.Path(), test_params.downloadResponse, "hasupdates",
1528  fake_meta_dir);
1529  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1530  auto storage = INvStorage::newStorage(conf.storage);
1531 
1532  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1533  DownloadEventHandler event_hdlr{aktualizr};
1534 
1535  aktualizr.Initialize();
1536  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1537  ASSERT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
1538  result::Download download_result = aktualizr.Download(update_result.updates).get();
1539 
1540  for (auto& expected_result : test_params.downloadResult) {
1541  // check if DownloadTargetComplete was received
1542  ASSERT_NE(event_hdlr.download_status.find(expected_result.first), event_hdlr.download_status.end());
1543  EXPECT_EQ(event_hdlr.download_status.at(expected_result.first), expected_result.second);
1544  }
1545  EXPECT_EQ(event_hdlr.all_download_completed_status.status, test_params.allDownloadsStatus);
1546  }
1547 }
1548 
1549 /*
1550  * List targets in storage via API.
1551  * Remove targets in storage via API.
1552  */
1553 TEST(Aktualizr, DownloadListRemove) {
1554  TemporaryDirectory temp_dir;
1555  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates", fake_meta_dir);
1556  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1557 
1558  auto storage = INvStorage::newStorage(conf.storage);
1559  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1560 
1561  aktualizr.Initialize();
1562  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1563 
1564  std::vector<Uptane::Target> targets = aktualizr.GetStoredTargets();
1565  EXPECT_EQ(targets.size(), 0);
1566 
1567  aktualizr.Download(update_result.updates).get();
1568 
1569  targets = aktualizr.GetStoredTargets();
1570  EXPECT_EQ(targets.size(), 2);
1571 
1572  // check that we can remove from the result of GetStoredTarget and
1573  // CheckUpdates
1574  aktualizr.DeleteStoredTarget(targets[0]);
1575  aktualizr.DeleteStoredTarget(update_result.updates[1]);
1576 
1577  targets = aktualizr.GetStoredTargets();
1578  EXPECT_EQ(targets.size(), 0);
1579 }
1580 
1581 /*
1582  * Automatically remove old targets during installation cycles.
1583  * Get log of installation.
1584  */
1585 TEST(Aktualizr, TargetAutoremove) {
1586  TemporaryDirectory temp_dir;
1587  const boost::filesystem::path local_metadir = temp_dir / "metadir";
1588  Utils::createDirectories(local_metadir, S_IRWXU);
1589  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "", local_metadir / "repo");
1590 
1591  UptaneRepo repo{local_metadir, "2021-07-04T16:33:27Z", "id0"};
1592  repo.generateRepo(KeyType::kED25519);
1593  const std::string hwid = "primary_hw";
1594  repo.addImage(fake_meta_dir / "fake_meta/primary_firmware.txt", "primary_firmware.txt", hwid, "", {});
1595  repo.addTarget("primary_firmware.txt", hwid, "CA:FE:A6:D2:84:9D", "");
1596  repo.signTargets();
1597 
1598  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1599  auto storage = INvStorage::newStorage(conf.storage);
1600  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1601 
1602  // attach the autoclean handler
1603  boost::signals2::connection ac_conn =
1604  aktualizr.SetSignalHandler(std::bind(targets_autoclean_cb, std::ref(aktualizr), std::placeholders::_1));
1605  aktualizr.Initialize();
1606 
1607  {
1608  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1609  aktualizr.Download(update_result.updates).get();
1610 
1611  EXPECT_EQ(aktualizr.GetStoredTargets().size(), 1);
1612 
1613  result::Install install_result = aktualizr.Install(update_result.updates).get();
1614  EXPECT_TRUE(install_result.dev_report.success);
1615 
1616  std::vector<Uptane::Target> targets = aktualizr.GetStoredTargets();
1617  ASSERT_EQ(targets.size(), 1);
1618  EXPECT_EQ(targets[0].filename(), "primary_firmware.txt");
1619  }
1620 
1621  // second install
1622  repo.emptyTargets();
1623  repo.addImage(fake_meta_dir / "fake_meta/dummy_firmware.txt", "dummy_firmware.txt", hwid, "", {});
1624  repo.addTarget("dummy_firmware.txt", hwid, "CA:FE:A6:D2:84:9D", "");
1625  repo.signTargets();
1626 
1627  {
1628  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1629  aktualizr.Download(update_result.updates).get();
1630 
1631  EXPECT_EQ(aktualizr.GetStoredTargets().size(), 2);
1632 
1633  result::Install install_result = aktualizr.Install(update_result.updates).get();
1634  EXPECT_TRUE(install_result.dev_report.success);
1635 
1636  // all targets are kept (current and previous)
1637  std::vector<Uptane::Target> targets = aktualizr.GetStoredTargets();
1638  ASSERT_EQ(targets.size(), 2);
1639  }
1640 
1641  // third install (first firmware again)
1642  repo.emptyTargets();
1643  repo.addImage(fake_meta_dir / "fake_meta/primary_firmware.txt", "primary_firmware.txt", hwid, "", {});
1644  repo.addTarget("primary_firmware.txt", hwid, "CA:FE:A6:D2:84:9D", "");
1645  repo.signTargets();
1646 
1647  {
1648  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1649  aktualizr.Download(update_result.updates).get();
1650 
1651  EXPECT_EQ(aktualizr.GetStoredTargets().size(), 2);
1652 
1653  result::Install install_result = aktualizr.Install(update_result.updates).get();
1654  EXPECT_TRUE(install_result.dev_report.success);
1655 
1656  // all targets are kept again (current and previous)
1657  std::vector<Uptane::Target> targets = aktualizr.GetStoredTargets();
1658  ASSERT_EQ(targets.size(), 2);
1659  }
1660 
1661  // fourth install (some new third firmware)
1662  repo.emptyTargets();
1663  repo.addImage(fake_meta_dir / "fake_meta/secondary_firmware.txt", "secondary_firmware.txt", hwid, "", {});
1664  repo.addTarget("secondary_firmware.txt", hwid, "CA:FE:A6:D2:84:9D", "");
1665  repo.signTargets();
1666 
1667  {
1668  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1669  aktualizr.Download(update_result.updates).get();
1670 
1671  EXPECT_EQ(aktualizr.GetStoredTargets().size(), 3);
1672 
1673  result::Install install_result = aktualizr.Install(update_result.updates).get();
1674  EXPECT_TRUE(install_result.dev_report.success);
1675 
1676  // only two targets are left: dummy_firmware has been cleaned up
1677  std::vector<Uptane::Target> targets = aktualizr.GetStoredTargets();
1678  ASSERT_EQ(targets.size(), 2);
1679  }
1680 
1681  Aktualizr::InstallationLog log = aktualizr.GetInstallationLog();
1682  ASSERT_EQ(log.size(), 2);
1683 
1684  EXPECT_EQ(log[0].installs.size(), 4);
1685  EXPECT_EQ(log[1].installs.size(), 0);
1686 
1687  std::vector<std::string> fws{"primary_firmware.txt", "dummy_firmware.txt", "primary_firmware.txt",
1688  "secondary_firmware.txt"};
1689  for (auto it = log[0].installs.begin(); it < log[0].installs.end(); it++) {
1690  auto idx = static_cast<size_t>(it - log[0].installs.begin());
1691  EXPECT_EQ(it->filename(), fws[idx]);
1692  }
1693 }
1694 
1695 /*
1696  * Initialize -> Install -> nothing to install.
1697  *
1698  * Initialize -> CheckUpdates -> Download -> Install -> updates installed.
1699  */
1700 TEST(Aktualizr, InstallWithUpdates) {
1701  TemporaryDirectory temp_dir;
1702  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates", fake_meta_dir);
1703  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1704 
1705  auto storage = INvStorage::newStorage(conf.storage);
1706  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1707 
1708  struct {
1709  size_t num_events{0};
1710  std::vector<Uptane::Target> updates;
1711  std::future<void> future;
1712  std::promise<void> promise;
1713  } ev_state;
1714  ev_state.future = ev_state.promise.get_future();
1715 
1716  auto f_cb = [&ev_state](const std::shared_ptr<event::BaseEvent>& event) {
1717  // Note that we do not expect a PutManifestComplete since we don't call
1718  // UptaneCycle() and that's the only function that generates that.
1719  if (event->isTypeOf<event::DownloadProgressReport>()) {
1720  return;
1721  }
1722  LOG_INFO << "Got " << event->variant;
1723  switch (ev_state.num_events) {
1724  case 0: {
1725  ASSERT_EQ(event->variant, "AllInstallsComplete");
1726  const auto installs_complete = dynamic_cast<event::AllInstallsComplete*>(event.get());
1727  EXPECT_EQ(installs_complete->result.ecu_reports.size(), 0);
1728  break;
1729  }
1730  case 1: {
1731  ASSERT_EQ(event->variant, "UpdateCheckComplete");
1732  const auto targets_event = dynamic_cast<event::UpdateCheckComplete*>(event.get());
1733  EXPECT_EQ(targets_event->result.ecus_count, 2);
1734  EXPECT_EQ(targets_event->result.updates.size(), 2u);
1735  EXPECT_EQ(targets_event->result.updates[0].filename(), "primary_firmware.txt");
1736  EXPECT_EQ(targets_event->result.updates[1].filename(), "secondary_firmware.txt");
1737  EXPECT_EQ(targets_event->result.status, result::UpdateStatus::kUpdatesAvailable);
1738  ev_state.updates = targets_event->result.updates;
1739  break;
1740  }
1741  case 2:
1742  case 3: {
1743  ASSERT_EQ(event->variant, "DownloadTargetComplete");
1744  const auto download_event = dynamic_cast<event::DownloadTargetComplete*>(event.get());
1745  EXPECT_TRUE(download_event->update.filename() == "primary_firmware.txt" ||
1746  download_event->update.filename() == "secondary_firmware.txt");
1747  EXPECT_TRUE(download_event->success);
1748  break;
1749  }
1750  case 4: {
1751  ASSERT_EQ(event->variant, "AllDownloadsComplete");
1752  const auto downloads_complete = dynamic_cast<event::AllDownloadsComplete*>(event.get());
1753  EXPECT_EQ(downloads_complete->result.updates.size(), 2);
1754  EXPECT_TRUE(downloads_complete->result.updates[0].filename() == "primary_firmware.txt" ||
1755  downloads_complete->result.updates[1].filename() == "primary_firmware.txt");
1756  EXPECT_TRUE(downloads_complete->result.updates[0].filename() == "secondary_firmware.txt" ||
1757  downloads_complete->result.updates[1].filename() == "secondary_firmware.txt");
1758  EXPECT_EQ(downloads_complete->result.status, result::DownloadStatus::kSuccess);
1759  break;
1760  }
1761  case 5: {
1762  // Primary always gets installed first. (Not a requirement, just how it
1763  // works at present.)
1764  ASSERT_EQ(event->variant, "InstallStarted");
1765  const auto install_started = dynamic_cast<event::InstallStarted*>(event.get());
1766  EXPECT_EQ(install_started->serial.ToString(), "CA:FE:A6:D2:84:9D");
1767  break;
1768  }
1769  case 6: {
1770  // Primary should complete before Secondary begins. (Again not a
1771  // requirement per se.)
1772  ASSERT_EQ(event->variant, "InstallTargetComplete");
1773  const auto install_complete = dynamic_cast<event::InstallTargetComplete*>(event.get());
1774  EXPECT_EQ(install_complete->serial.ToString(), "CA:FE:A6:D2:84:9D");
1775  EXPECT_TRUE(install_complete->success);
1776  break;
1777  }
1778  case 7: {
1779  ASSERT_EQ(event->variant, "InstallStarted");
1780  const auto install_started = dynamic_cast<event::InstallStarted*>(event.get());
1781  EXPECT_EQ(install_started->serial.ToString(), "secondary_ecu_serial");
1782  break;
1783  }
1784  case 8: {
1785  ASSERT_EQ(event->variant, "InstallTargetComplete");
1786  const auto install_complete = dynamic_cast<event::InstallTargetComplete*>(event.get());
1787  EXPECT_EQ(install_complete->serial.ToString(), "secondary_ecu_serial");
1788  EXPECT_TRUE(install_complete->success);
1789  break;
1790  }
1791  case 9: {
1792  ASSERT_EQ(event->variant, "AllInstallsComplete");
1793  const auto installs_complete = dynamic_cast<event::AllInstallsComplete*>(event.get());
1794  EXPECT_EQ(installs_complete->result.ecu_reports.size(), 2);
1795  EXPECT_EQ(installs_complete->result.ecu_reports[0].install_res.result_code.num_code,
1796  data::ResultCode::Numeric::kOk);
1797  EXPECT_EQ(installs_complete->result.ecu_reports[1].install_res.result_code.num_code,
1798  data::ResultCode::Numeric::kOk);
1799  ev_state.promise.set_value();
1800  break;
1801  }
1802  case 12:
1803  // Don't let the test run indefinitely!
1804  FAIL() << "Unexpected events!";
1805  default:
1806  std::cout << "event #" << ev_state.num_events << " is: " << event->variant << "\n";
1807  ASSERT_EQ(event->variant, "");
1808  }
1809  ++ev_state.num_events;
1810  };
1811 
1812  boost::signals2::connection conn = aktualizr.SetSignalHandler(f_cb);
1813 
1814  aktualizr.Initialize();
1815  Json::Value primary_json;
1816  primary_json["hashes"]["sha256"] = "74e653bbf6c00a88b21f0992159b1002f5af38506e6d2fbc7eb9846711b2d75f";
1817  primary_json["hashes"]["sha512"] =
1818  "91814ad1c13ebe2af8d65044893633c4c3ce964edb8cb58b0f357406c255f7be94f42547e108b300346a42cd57662e4757b9d843b7acbc09"
1819  "0df0bc05fe55297f";
1820  primary_json["length"] = 59;
1821  Uptane::Target primary_target("primary_firmware.txt", primary_json);
1822 
1823  Json::Value secondary_json;
1824  secondary_json["hashes"]["sha256"] = "1bbb15aa921ffffd5079567d630f43298dbe5e7cbc1b14e0ccdd6718fde28e47";
1825  secondary_json["hashes"]["sha512"] =
1826  "7dbae4c36a2494b731a9239911d3085d53d3e400886edb4ae2b9b78f40bda446649e83ba2d81653f614cc66f5dd5d4dbd95afba854f148af"
1827  "bfae48d0ff4cc38a";
1828  secondary_json["length"] = 15;
1829  Uptane::Target secondary_target("secondary_firmware.txt", secondary_json);
1830 
1831  // First try installing nothing. Nothing should happen.
1832  result::Install result = aktualizr.Install(ev_state.updates).get();
1833  EXPECT_EQ(result.ecu_reports.size(), 0);
1834 
1835  EXPECT_THROW(aktualizr.OpenStoredTarget(primary_target).get(), std::runtime_error)
1836  << "Primary firmware is present in storage before the download";
1837  EXPECT_THROW(aktualizr.OpenStoredTarget(secondary_target).get(), std::runtime_error)
1838  << "Secondary firmware is present in storage before the download";
1839 
1840  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1841  aktualizr.Download(update_result.updates).get();
1842  EXPECT_NO_THROW(aktualizr.OpenStoredTarget(primary_target))
1843  << "Primary firmware is not present in storage after the download";
1844  EXPECT_NO_THROW(aktualizr.OpenStoredTarget(secondary_target))
1845  << "Secondary firmware is not present in storage after the download";
1846 
1847  // After updates have been downloaded, try to install them.
1848  aktualizr.Install(update_result.updates);
1849 
1850  auto status = ev_state.future.wait_for(std::chrono::seconds(20));
1851  if (status != std::future_status::ready) {
1852  FAIL() << "Timed out waiting for installation to complete.";
1853  }
1854 }
1855 
1856 /**
1857  * Verifies reporting of update download progress
1858  *
1859  * Checks actions:
1860  *
1861  * - [x] Report download progress
1862  */
1863 TEST(Aktualizr, ReportDownloadProgress) {
1864  // The test initialization part is repeated in many tests so maybe it makes sense
1865  // to define a fixture and move this common initialization part into the fixture's SetUp() method
1866  TemporaryDirectory temp_dir;
1867  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates", fake_meta_dir);
1868  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1869  auto storage = INvStorage::newStorage(conf.storage);
1870  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1871 
1872  unsigned int report_counter = {0};
1873  std::shared_ptr<const event::DownloadProgressReport> lastProgressReport{nullptr};
1874 
1875  std::function<void(std::shared_ptr<event::BaseEvent> event)> report_event_hdlr =
1876  [&](const std::shared_ptr<event::BaseEvent>& event) {
1877  ASSERT_NE(event, nullptr);
1878  if (!event->isTypeOf<event::DownloadProgressReport>()) {
1879  return;
1880  }
1881 
1882  auto download_progress_event = std::dynamic_pointer_cast<event::DownloadProgressReport>(event);
1883  ASSERT_NE(download_progress_event, nullptr);
1884  ASSERT_NE(download_progress_event.get(), nullptr);
1885  if (lastProgressReport) {
1886  EXPECT_GE(download_progress_event->progress, lastProgressReport->progress);
1887  }
1888  lastProgressReport = download_progress_event;
1889  ++report_counter;
1890  };
1891 
1892  aktualizr.SetSignalHandler(report_event_hdlr);
1893  aktualizr.Initialize();
1894 
1895  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1896  ASSERT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
1897  // The test mocks are tailored to emulate a device with a Primary ECU and one Secondary ECU
1898  // for sake of the download progress report testing it's suffice to test it agains just one of the ECUs
1899  update_result.updates.pop_back();
1900 
1901  result::Download download_result = aktualizr.Download(update_result.updates).get();
1902  EXPECT_EQ(download_result.status, result::DownloadStatus::kSuccess);
1903 
1904  ASSERT_NE(lastProgressReport, nullptr);
1905  ASSERT_NE(lastProgressReport.get(), nullptr);
1906  EXPECT_TRUE(event::DownloadProgressReport::isDownloadCompleted(*lastProgressReport));
1907  EXPECT_GT(report_counter, 1);
1908 }
1909 
1910 class HttpFakeCampaign : public HttpFake {
1911  public:
1912  HttpFakeCampaign(const boost::filesystem::path& test_dir_in, const boost::filesystem::path& meta_dir_in)
1913  : HttpFake(test_dir_in, "", meta_dir_in) {}
1914 
1915  HttpResponse get(const std::string& url, int64_t maxsize) override {
1916  EXPECT_NE(url.find("campaigner/"), std::string::npos);
1917  boost::filesystem::path path = meta_dir / url.substr(tls_server.size() + strlen("campaigner/"));
1918 
1919  if (url.find("campaigner/campaigns") != std::string::npos) {
1920  return HttpResponse(Utils::readFile(path.parent_path() / "campaigner/campaigns.json"), 200, CURLE_OK, "");
1921  }
1922  return HttpFake::get(url, maxsize);
1923  }
1924 
1925  HttpResponse handle_event(const std::string& url, const Json::Value& data) override {
1926  (void)url;
1927  for (auto it = data.begin(); it != data.end(); it++) {
1928  auto ev = *it;
1929  auto id = ev["eventType"]["id"];
1930  if (id == "campaign_accepted" || id == "campaign_declined" || id == "campaign_postponed") {
1931  seen_events.push_back(ev);
1932  }
1933  }
1934  return HttpResponse("", 204, CURLE_OK, "");
1935  }
1936 
1937  std::vector<Json::Value> seen_events;
1938 };
1939 
1941  public:
1942  bool campaignaccept_seen{false};
1943  bool campaigndecline_seen{false};
1944  bool campaignpostpone_seen{false};
1945 
1946  void handler(const std::shared_ptr<event::BaseEvent>& event) {
1947  std::cout << event->variant << "\n";
1948  if (event->variant == "CampaignCheckComplete") {
1949  auto concrete_event = std::static_pointer_cast<event::CampaignCheckComplete>(event);
1950  EXPECT_EQ(concrete_event->result.campaigns.size(), 1);
1951  EXPECT_EQ(concrete_event->result.campaigns[0].name, "campaign1");
1952  EXPECT_EQ(concrete_event->result.campaigns[0].id, "c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493");
1953  EXPECT_EQ(concrete_event->result.campaigns[0].size, 62470);
1954  EXPECT_EQ(concrete_event->result.campaigns[0].autoAccept, true);
1955  EXPECT_EQ(concrete_event->result.campaigns[0].description, "this is my message to show on the device");
1956  } else if (event->variant == "CampaignAcceptComplete") {
1957  campaignaccept_seen = true;
1958  } else if (event->variant == "CampaignDeclineComplete") {
1959  campaigndecline_seen = true;
1960  } else if (event->variant == "CampaignPostponeComplete") {
1961  campaignpostpone_seen = true;
1962  }
1963  }
1964 };
1965 
1966 /* Check for campaigns with manual control.
1967  * Send CampaignCheckComplete event with campaign data.
1968  * Fetch campaigns from the server.
1969  *
1970  * Accept a campaign.
1971  * Send campaign acceptance report.
1972  * Send CampaignAcceptComplete event.
1973  *
1974  * Decline a campaign.
1975  * Send campaign decline report.
1976  * Send CampaignDeclineComplete event.
1977  *
1978  * Postpone a campaign.
1979  * Send campaign postpone report.
1980  * Send CampaignPostponeComplete event.
1981  */
1982 TEST(Aktualizr, CampaignCheckAndControl) {
1983  TemporaryDirectory temp_dir;
1984  auto http = std::make_shared<HttpFakeCampaign>(temp_dir.Path(), fake_meta_dir);
1985  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1986 
1987  CampaignEvents campaign_events;
1988 
1989  {
1990  auto storage = INvStorage::newStorage(conf.storage);
1991  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1992  aktualizr.SetSignalHandler(std::bind(&CampaignEvents::handler, &campaign_events, std::placeholders::_1));
1993  aktualizr.Initialize();
1994 
1995  // check for campaign
1996  auto result = aktualizr.CampaignCheck().get();
1997  EXPECT_EQ(result.campaigns.size(), 1);
1998 
1999  // accept the campaign
2000  aktualizr.CampaignControl("c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493", campaign::Cmd::Accept).get();
2001 
2002  aktualizr.CampaignControl("c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493", campaign::Cmd::Decline).get();
2003 
2004  aktualizr.CampaignControl("c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493", campaign::Cmd::Postpone).get();
2005  }
2006 
2007  ASSERT_EQ(http->seen_events.size(), 3);
2008  for (const auto& ev : http->seen_events) {
2009  EXPECT_EQ(ev["event"]["campaignId"], "c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493");
2010  }
2011  EXPECT_TRUE(campaign_events.campaignaccept_seen);
2012  EXPECT_TRUE(campaign_events.campaigndecline_seen);
2013  EXPECT_TRUE(campaign_events.campaignpostpone_seen);
2014 }
2015 
2017  public:
2018  HttpFakeNoCorrelationId(const boost::filesystem::path& test_dir_in, const boost::filesystem::path& meta_dir_in)
2019  : HttpFake(test_dir_in, "", meta_dir_in) {}
2020 
2021  HttpResponse handle_event(const std::string& url, const Json::Value& data) override {
2022  (void)url;
2023  for (const Json::Value& event : data) {
2024  ++events_seen;
2025  EXPECT_EQ(event["event"]["correlationId"].asString(), "");
2026  }
2027  return HttpResponse("", 200, CURLE_OK, "");
2028  }
2029 
2030  unsigned int events_seen{0};
2031 };
2032 
2033 /* Correlation ID is empty if none was provided in Targets metadata. */
2034 TEST(Aktualizr, FullNoCorrelationId) {
2035  TemporaryDirectory temp_dir;
2036  TemporaryDirectory meta_dir;
2037  Utils::copyDir(uptane_repos_dir / "full_no_correlation_id/repo/repo", meta_dir.Path() / "repo");
2038  Utils::copyDir(uptane_repos_dir / "full_no_correlation_id/repo/director", meta_dir.Path() / "director");
2039  auto http = std::make_shared<HttpFakeNoCorrelationId>(temp_dir.Path(), meta_dir.Path());
2040 
2041  // scope `Aktualizr` object, so that the ReportQueue flushes its events before
2042  // we count them at the end
2043  {
2044  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
2045 
2046  auto storage = INvStorage::newStorage(conf.storage);
2047  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
2048 
2049  aktualizr.Initialize();
2050  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
2051  EXPECT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
2052 
2053  result::Download download_result = aktualizr.Download(update_result.updates).get();
2054  EXPECT_EQ(download_result.status, result::DownloadStatus::kSuccess);
2055 
2056  result::Install install_result = aktualizr.Install(download_result.updates).get();
2057  for (const auto& r : install_result.ecu_reports) {
2058  EXPECT_EQ(r.install_res.result_code.num_code, data::ResultCode::Numeric::kOk);
2059  }
2060  }
2061 
2062  EXPECT_EQ(http->events_seen, 8);
2063 }
2064 
2065 TEST(Aktualizr, ManifestCustom) {
2066  TemporaryDirectory temp_dir;
2067  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "", fake_meta_dir);
2068 
2069  {
2070  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
2071 
2072  auto storage = INvStorage::newStorage(conf.storage);
2073  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
2074 
2075  aktualizr.Initialize();
2076  Json::Value custom = Utils::parseJSON(R"({"test_field":"test_value"})");
2077  ASSERT_EQ(custom["test_field"].asString(), "test_value"); // Shouldn't fail, just check that test itself is correct
2078  ASSERT_EQ(true, aktualizr.SendManifest(custom).get()) << "Failed to upload manifest with HttpFake server";
2079  EXPECT_EQ(http->last_manifest["signed"]["custom"], custom);
2080  }
2081 }
2082 
2083 TEST(Aktualizr, CustomInstallationRawReport) {
2084  TemporaryDirectory temp_dir;
2085  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdate", fake_meta_dir);
2086 
2087  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
2088  auto storage = INvStorage::newStorage(conf.storage);
2089  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
2090 
2091  aktualizr.Initialize();
2092  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
2093  result::Download download_result = aktualizr.Download(update_result.updates).get();
2094  result::Install install_result = aktualizr.Install(download_result.updates).get();
2095 
2096  auto custom_raw_report = "Installation's custom raw report!";
2097  EXPECT_TRUE(aktualizr.SetInstallationRawReport(custom_raw_report));
2098  aktualizr.SendManifest().get();
2099  EXPECT_EQ(http->last_manifest["signed"]["installation_report"]["report"]["raw_report"], custom_raw_report);
2100 
2101  // After sending manifest, an installation report will be removed from the DB,
2102  // so Aktualzr::SetInstallationRawReport must return a negative value.
2103  EXPECT_FALSE(aktualizr.SetInstallationRawReport(custom_raw_report));
2104 }
2105 
2107  public:
2108  CountUpdateCheckEvents() = default;
2109  // Non-copyable
2111  CountUpdateCheckEvents& operator=(const CountUpdateCheckEvents&) = delete;
2112 
2113  std::function<void(std::shared_ptr<event::BaseEvent>)> Signal() {
2114  return std::bind(&CountUpdateCheckEvents::count, this, std::placeholders::_1);
2115  }
2116 
2117  void count(std::shared_ptr<event::BaseEvent> event) {
2118  std::cout << event->variant << "\n";
2119  if (event->variant == "UpdateCheckComplete") {
2120  total_events_++;
2121  if (std::static_pointer_cast<event::UpdateCheckComplete>(event)->result.status == result::UpdateStatus::kError) {
2122  error_events_++;
2123  }
2124  }
2125  }
2126 
2127  int total_events() const { return total_events_; }
2128  int error_events() const { return error_events_; }
2129 
2130  private:
2131  int total_events_{0};
2132  int error_events_{0};
2133 };
2134 
2135 TEST(Aktualizr, APICheck) {
2136  TemporaryDirectory temp_dir;
2137  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates", fake_meta_dir);
2138 
2139  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
2140 
2141  auto storage = INvStorage::newStorage(conf.storage);
2142 
2143  CountUpdateCheckEvents counter;
2144  {
2145  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
2146  boost::signals2::connection conn = aktualizr.SetSignalHandler(counter.Signal());
2147  aktualizr.Initialize();
2148  std::vector<std::future<result::UpdateCheck>> futures;
2149  for (int i = 0; i < 5; ++i) {
2150  futures.push_back(aktualizr.CheckUpdates());
2151  }
2152 
2153  for (auto& f : futures) {
2154  f.get();
2155  }
2156  }
2157 
2158  EXPECT_EQ(counter.total_events(), 5);
2159 
2160  // try again, but shutdown before it finished all calls
2161  CountUpdateCheckEvents counter2;
2162  {
2163  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
2164  boost::signals2::connection conn = aktualizr.SetSignalHandler(counter2.Signal());
2165  aktualizr.Initialize();
2166 
2167  std::future<result::UpdateCheck> ft = aktualizr.CheckUpdates();
2168  for (int i = 0; i < 99; ++i) {
2169  aktualizr.CheckUpdates();
2170  }
2171  ft.get();
2172  }
2173  EXPECT_LT(counter2.total_events(), 100);
2174  EXPECT_GT(counter2.total_events(), 0);
2175 }
2176 
2178  public:
2179  HttpPutManifestFail(const boost::filesystem::path& test_dir_in, const boost::filesystem::path& meta_dir_in)
2180  : HttpFake(test_dir_in, "", meta_dir_in) {}
2181  HttpResponse put(const std::string& url, const Json::Value& data) override {
2182  (void)data;
2183  return HttpResponse(url, 504, CURLE_OK, "");
2184  }
2185 };
2186 
2187 /* Send UpdateCheckComplete event after failure */
2188 TEST(Aktualizr, UpdateCheckCompleteError) {
2189  TemporaryDirectory temp_dir;
2190  auto http = std::make_shared<HttpPutManifestFail>(temp_dir.Path(), fake_meta_dir);
2191 
2192  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, "http://updatefail");
2193 
2194  auto storage = INvStorage::newStorage(conf.storage);
2195  CountUpdateCheckEvents counter;
2196 
2197  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
2198  boost::signals2::connection conn = aktualizr.SetSignalHandler(counter.Signal());
2199  aktualizr.Initialize();
2200  auto result = aktualizr.CheckUpdates().get();
2201  EXPECT_EQ(result.status, result::UpdateStatus::kError);
2202  EXPECT_EQ(counter.error_events(), 1);
2203 }
2204 
2205 /*
2206  * Suspend API calls during pause.
2207  * Catch up with calls queue after resume.
2208  */
2209 
2211  public:
2212  HttpFakePauseCounter(const boost::filesystem::path& test_dir_in, const boost::filesystem::path& meta_dir_in)
2213  : HttpFake(test_dir_in, "noupdates", meta_dir_in) {}
2214 
2215  HttpResponse handle_event(const std::string& url, const Json::Value& data) override {
2216  (void)url;
2217  for (const Json::Value& event : data) {
2218  ++events_seen;
2219  std::string event_type = event["eventType"]["id"].asString();
2220 
2221  std::cout << "got event #" << events_seen << ": " << event_type << "\n";
2222  if (events_seen == 1) {
2223  EXPECT_EQ(event_type, "DevicePaused");
2224  EXPECT_EQ(event["event"]["correlationId"], "id0");
2225  } else if (events_seen == 2) {
2226  EXPECT_EQ(event_type, "DeviceResumed");
2227  EXPECT_EQ(event["event"]["correlationId"], "id0");
2228  } else {
2229  std::cout << "Unexpected event";
2230  EXPECT_EQ(0, 1);
2231  }
2232  }
2233  return HttpResponse("", 200, CURLE_OK, "");
2234  }
2235 
2236  unsigned int events_seen{0};
2237 };
2238 
2239 TEST(Aktualizr, PauseResumeQueue) {
2240  TemporaryDirectory temp_dir;
2241  auto http = std::make_shared<HttpFakePauseCounter>(temp_dir.Path(), fake_meta_dir);
2242  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
2243 
2244  auto storage = INvStorage::newStorage(conf.storage);
2245  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
2246  aktualizr.Initialize();
2247 
2248  std::mutex mutex;
2249  std::promise<void> end_promise{};
2250  size_t n_events = 0;
2251  std::atomic_bool is_paused{false};
2252  std::function<void(std::shared_ptr<event::BaseEvent>)> cb = [&end_promise, &n_events, &mutex,
2253  &is_paused](std::shared_ptr<event::BaseEvent> event) {
2254  switch (n_events) {
2255  case 0:
2256  ASSERT_EQ(event->variant, "UpdateCheckComplete");
2257  break;
2258  case 1: {
2259  std::lock_guard<std::mutex> guard(mutex);
2260 
2261  ASSERT_EQ(event->variant, "UpdateCheckComplete");
2262  // the event shouldn't happen when the system is paused
2263  EXPECT_FALSE(is_paused);
2264  end_promise.set_value();
2265  break;
2266  }
2267  default:
2268  FAIL() << "Unexpected event";
2269  }
2270  n_events += 1;
2271  };
2272  boost::signals2::connection conn = aktualizr.SetSignalHandler(cb);
2273 
2274  // trigger the first UpdateCheck
2275  aktualizr.CheckUpdates().get();
2276 
2277  {
2278  std::lock_guard<std::mutex> guard(mutex);
2279  EXPECT_EQ(aktualizr.Pause().status, result::PauseStatus::kSuccess);
2280  is_paused = true;
2281  }
2282 
2283  EXPECT_EQ(aktualizr.Pause().status, result::PauseStatus::kAlreadyPaused);
2284 
2285  aktualizr.CheckUpdates();
2286 
2287  // Theoritically racy, could cause bad implem to succeeed sometimes
2288  std::this_thread::sleep_for(std::chrono::seconds(1));
2289 
2290  {
2291  std::lock_guard<std::mutex> guard(mutex);
2292  is_paused = false;
2293  EXPECT_EQ(aktualizr.Resume().status, result::PauseStatus::kSuccess);
2294  }
2295 
2296  EXPECT_EQ(aktualizr.Resume().status, result::PauseStatus::kAlreadyRunning);
2297 
2298  auto status = end_promise.get_future().wait_for(std::chrono::seconds(20));
2299  if (status != std::future_status::ready) {
2300  FAIL() << "Timed out waiting for UpdateCheck event";
2301  }
2302 }
2303 
2304 const std::string custom_hwinfo =
2305  R"([{"description":"ECU1","ECU P/N":"AAA","HW P/N":"BBB","HW Version":"1.234",
2306  "SW P/N":"CCC","SW Version":"4.321","ECU Serial":"AAA-BBB-CCC"},
2307  {"description":"ECU2","ECU P/N":"ZZZ","HW P/N":"XXX","HW Version":"9.876",
2308  "SW P/N":"YYY","SW Version":"6.789","ECU Serial":"VVV-NNN-MMM"}])";
2309 
2310 class HttpSystemInfo : public HttpFake {
2311  public:
2312  HttpSystemInfo(const boost::filesystem::path& test_dir_in, const boost::filesystem::path& meta_dir_in)
2313  : HttpFake(test_dir_in, "", meta_dir_in) {}
2314 
2315  HttpResponse put(const std::string& url, const Json::Value& data) override {
2316  if (url.find(hwinfo_ep_) == url.length() - hwinfo_ep_.length()) {
2317  if (info_count_ == 0) { // expect lshw data
2318  EXPECT_TRUE(data.isObject());
2319  EXPECT_TRUE(data.isMember("description"));
2320  } else if (info_count_ == 1) { // expect custom data
2321  auto hwinfo = Utils::parseJSON(custom_hwinfo);
2322  EXPECT_EQ(hwinfo, data);
2323  }
2324  info_count_++;
2325  return HttpResponse("", 200, CURLE_OK, "");
2326  } else if (url.find("/manifest") != std::string::npos) {
2327  return HttpResponse("", 200, CURLE_OK, "");
2328  }
2329  return HttpResponse("", 404, CURLE_HTTP_RETURNED_ERROR, "Not found");
2330  }
2331 
2332  ~HttpSystemInfo() { EXPECT_EQ(info_count_, 2); }
2333 
2334  int info_count_{0};
2335  std::string hwinfo_ep_{"/system_info"};
2336 };
2337 
2338 TEST(Aktualizr, CustomHwInfo) {
2339  TemporaryDirectory temp_dir;
2340  auto http = std::make_shared<HttpSystemInfo>(temp_dir.Path(), fake_meta_dir);
2341  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
2342 
2343  auto storage = INvStorage::newStorage(conf.storage);
2344  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
2345  aktualizr.Initialize();
2346 
2347  aktualizr.SendDeviceData().get();
2348 
2349  auto hwinfo = Utils::parseJSON(custom_hwinfo);
2350  aktualizr.SendDeviceData(hwinfo).get();
2351 }
2352 
2353 #ifndef __NO_MAIN__
2354 int main(int argc, char** argv) {
2355  ::testing::InitGoogleTest(&argc, argv);
2356  if (argc != 2) {
2357  std::cerr << "Error: " << argv[0] << " requires the path to the base directory of Uptane repos.\n";
2358  return EXIT_FAILURE;
2359  }
2360  uptane_repos_dir = argv[1];
2361 
2362  logger_init();
2363  logger_set_threshold(boost::log::trivial::trace);
2364 
2365  TemporaryDirectory tmp_dir;
2366  fake_meta_dir = tmp_dir.Path();
2367  MetaFake meta_fake(fake_meta_dir);
2368 
2369  return RUN_ALL_TESTS();
2370 }
2371 #endif
2372 
2373 // vim: set tabstop=2 shiftwidth=2 expandtab:
Aktualizr::Resume
result::Pause Resume()
Resume the library operations.
Definition: aktualizr.cc:181
HttpFakePauseCounter
Definition: aktualizr_test.cc:2210
data::ResultCode
Definition: types.h:219
Aktualizr::Install
std::future< result::Install > Install(const std::vector< Uptane::Target > &updates)
Install targets.
Definition: aktualizr.cc:158
Aktualizr::SendDeviceData
std::future< void > SendDeviceData(const Json::Value &custom_hwinfo=Json::nullValue)
Send local device data to the server.
Definition: aktualizr.cc:142
Aktualizr::OpenStoredTarget
std::ifstream OpenStoredTarget(const Uptane::Target &target)
Get target downloaded in Download call.
Definition: aktualizr.cc:223
HttpFake
Definition: httpfake.h:20
UptaneTestCommon::TestAktualizr
Definition: uptane_test_common.h:21
event::AllDownloadsComplete
All targets for an update have been downloaded.
Definition: events.h:109
event::UpdateCheckComplete
An update is available for download from the server.
Definition: events.h:57
Aktualizr::DeleteStoredTarget
void DeleteStoredTarget(const Uptane::Target &target)
Delete a stored target from storage.
Definition: aktualizr.cc:221
data::InstallationResult
Definition: types.h:277
HttpDownloadFailure
Definition: aktualizr_test.cc:1430
event::DownloadTargetComplete
A target has been downloaded.
Definition: events.h:93
Aktualizr::RunForever
std::future< void > RunForever(const Json::Value &custom_hwinfo=Json::nullValue)
Asynchronously run aktualizr indefinitely until Shutdown is called.
Definition: aktualizr.cc:75
Aktualizr::SendManifest
std::future< bool > SendManifest(const Json::Value &custom=Json::nullValue)
Send installation report to the backend.
Definition: aktualizr.cc:167
CountUpdateCheckEvents
Definition: aktualizr_test.cc:2106
Aktualizr::CampaignControl
std::future< void > CampaignControl(const std::string &campaign_id, campaign::Cmd cmd)
Act on campaign: accept, decline or postpone.
Definition: aktualizr.cc:123
Aktualizr::SetInstallationRawReport
bool SetInstallationRawReport(const std::string &custom_raw_report)
SetInstallationRawReport allows setting a custom raw report field in the device installation result.
Definition: aktualizr.cc:163
Aktualizr::Shutdown
void Shutdown()
Shuts down currently running RunForever() method.
Definition: aktualizr.cc:95
event::AllInstallsComplete
All ECU installation attempts for an update have completed.
Definition: events.h:148
result::UpdateCheck
Container for information about available updates.
Definition: results.h:37
Aktualizr::Initialize
void Initialize()
Initialize aktualizr.
Definition: aktualizr.cc:33
Aktualizr::CheckUpdates
std::future< result::UpdateCheck > CheckUpdates()
Fetch Uptane metadata and check for updates.
Definition: aktualizr.cc:147
data
General data structures.
Definition: types.h:217
Uptane::HardwareIdentifier
Definition: types.h:315
HttpResponse
Definition: httpinterface.h:17
CampaignEvents
Definition: aktualizr_test.cc:1940
Aktualizr::CampaignCheck
std::future< result::CampaignCheck > CampaignCheck()
Check for campaigns.
Definition: aktualizr.cc:118
Config
Configuration object for an aktualizr instance running on a Primary ECU.
Definition: config.h:208
events.h
event::InstallTargetComplete
An installation attempt on an ECU has completed.
Definition: events.h:132
Uptane::EcuSerial
Definition: types.h:346
Aktualizr
This class provides the main APIs necessary for launching and controlling libaktualizr.
Definition: aktualizr.h:24
result::Download
Container for information about downloading an update.
Definition: results.h:116
Aktualizr::AddSecondary
void AddSecondary(const std::shared_ptr< SecondaryInterface > &secondary)
Add new Secondary to aktualizr.
Definition: aktualizr.cc:103
Aktualizr::GetStoredTargets
std::vector< Uptane::Target > GetStoredTargets()
Get list of targets currently in storage.
Definition: aktualizr.cc:219
Primary::VirtualSecondaryConfig
Definition: virtualsecondary.h:11
Aktualizr::SetSignalHandler
boost::signals2::connection SetSignalHandler(const SigHandler &handler)
Provide a function to receive event notifications.
Definition: aktualizr.cc:192
TemporaryDirectory
Definition: utils.h:82
Aktualizr::GetInstallationLog
InstallationLog GetInstallationLog()
Get log of installations.
Definition: aktualizr.cc:197
HttpFakeCampaign
Definition: aktualizr_test.cc:1910
result
Results of libaktualizr API calls.
Definition: results.h:12
event::PutManifestComplete
A manifest has been sent to the server.
Definition: events.h:47
result::Install
Container for information about installing an update.
Definition: results.h:129
Aktualizr::UptaneCycle
bool UptaneCycle()
Synchronously run an Uptane cycle: check for updates, download any new targets, install them,...
Definition: aktualizr.cc:38
HttpFakePutCounter
Definition: aktualizr_test.cc:563
event::InstallStarted
An ECU has begun installation of an update.
Definition: events.h:121
HttpFakeNoCorrelationId
Definition: aktualizr_test.cc:2016
event::DownloadProgressReport
A report for a download in progress.
Definition: events.h:68
Uptane::Target
Definition: types.h:379
HttpFakeSplit
Definition: aktualizr_test.cc:358
UptaneRepo
Definition: uptane_repo.h:7
HttpPutManifestFail
Definition: aktualizr_test.cc:2177
data::ResultCode::Numeric::kInstallFailed
@ kInstallFailed
Package installation failed.
MetaFake
Definition: metafake.h:14
Aktualizr::Download
std::future< result::Download > Download(const std::vector< Uptane::Target > &updates)
Download targets.
Definition: aktualizr.cc:152
event
Aktualizr status events.
Definition: events.h:15
HttpSystemInfo
Definition: aktualizr_test.cc:2310
HttpFakeEventCounter
Definition: aktualizr_test.cc:206
result::DownloadStatus
DownloadStatus
Status of an update download.
Definition: results.h:79
Aktualizr::Pause
result::Pause Pause()
Pause the library operations.
Definition: aktualizr.cc:172