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