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