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