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 #endif // FIU_ENABLE
1086 
1087 /*
1088  * Initialize -> UptaneCycle -> updates downloaded and installed for primary
1089  * -> reboot emulated -> Initialize -> Finalize Installation After Reboot
1090  *
1091  * Verifies an auto reboot and pending updates finalization after a reboot
1092  * in case of the fake package manager usage
1093  *
1094  * Checks actions:
1095  *
1096  * - [x] Emulate a reboot at the end of the installation process in case of the fake package manager usage
1097  * - [x] Finalize a pending update that requires reboot
1098  */
1099 TEST(Aktualizr, AutoRebootAfterUpdate) {
1100  TemporaryDirectory temp_dir;
1101  auto http = std::make_shared<HttpFakePutCounter>(temp_dir.Path(), fake_meta_dir);
1102  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1103  conf.pacman.fake_need_reboot = true;
1104  conf.uptane.force_install_completion = true;
1105  conf.uptane.polling_sec = 0;
1106 
1107  {
1108  // first run: do the install, exit UptaneCycle and emulate reboot since force_install_completion is set
1109  auto storage = INvStorage::newStorage(conf.storage);
1110  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1111 
1112  aktualizr.Initialize();
1113  auto aktualizr_cycle_thread = aktualizr.RunForever();
1114  auto aktualizr_cycle_thread_status = aktualizr_cycle_thread.wait_for(std::chrono::seconds(20));
1115  aktualizr.Shutdown();
1116 
1117  EXPECT_EQ(aktualizr_cycle_thread_status, std::future_status::ready);
1118  EXPECT_TRUE(aktualizr.uptane_client()->isInstallCompletionRequired());
1119  }
1120 
1121  {
1122  // second run: after emulated reboot, check if the update was applied
1123  auto storage = INvStorage::newStorage(conf.storage);
1124  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1125 
1126  aktualizr.Initialize();
1127 
1128  result::UpdateCheck update_res = aktualizr.CheckUpdates().get();
1129  EXPECT_EQ(update_res.status, result::UpdateStatus::kNoUpdatesAvailable);
1130 
1131  // primary is installed, nothing pending
1132  boost::optional<Uptane::Target> current_target;
1133  boost::optional<Uptane::Target> pending_target;
1134  storage->loadPrimaryInstalledVersions(&current_target, &pending_target);
1135  EXPECT_TRUE(!!current_target);
1136  EXPECT_FALSE(!!pending_target);
1137  EXPECT_EQ(http->manifest_sends, 4);
1138  }
1139 }
1140 
1141 /*
1142  * Initialize -> UptaneCycle -> updates downloaded and installed for secondaries
1143  * without changing the primary.
1144 
1145  * Store installation result for secondary
1146  */
1147 TEST(Aktualizr, FullMultipleSecondaries) {
1148  TemporaryDirectory temp_dir;
1149  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "multisec", fake_meta_dir);
1150  Config conf("tests/config/basic.toml");
1151  conf.provision.primary_ecu_serial = "testecuserial";
1152  conf.provision.primary_ecu_hardware_id = "testecuhwid";
1153  conf.storage.path = temp_dir.Path();
1154  conf.tls.server = http->tls_server;
1155  conf.uptane.director_server = http->tls_server + "/director";
1156  conf.uptane.repo_server = http->tls_server + "/repo";
1157 
1158  TemporaryDirectory temp_dir2;
1159  UptaneTestCommon::addDefaultSecondary(conf, temp_dir, "sec_serial1", "sec_hw1");
1160 
1161  auto storage = INvStorage::newStorage(conf.storage);
1162  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1163  UptaneTestCommon::addDefaultSecondary(conf, temp_dir2, "sec_serial2", "sec_hw2");
1164  ASSERT_NO_THROW(aktualizr.AddSecondary(std::make_shared<Primary::VirtualSecondary>(
1165  Primary::VirtualSecondaryConfig::create_from_file(conf.uptane.secondary_config_file)[0])));
1166 
1167  struct {
1168  int started{0};
1169  int complete{0};
1170  bool allcomplete{false};
1171  bool manifest{false};
1172  std::future<void> future;
1173  std::promise<void> promise;
1174  } ev_state;
1175  ev_state.future = ev_state.promise.get_future();
1176 
1177  auto f_cb = [&ev_state](const std::shared_ptr<event::BaseEvent>& event) {
1178  if (event->variant == "InstallStarted") {
1179  ++ev_state.started;
1180  } else if (event->variant == "InstallTargetComplete") {
1181  ++ev_state.complete;
1182  } else if (event->variant == "AllInstallsComplete") {
1183  ev_state.allcomplete = true;
1184  } else if (event->variant == "PutManifestComplete") {
1185  ev_state.manifest = true;
1186  }
1187  // It is possible for the PutManifestComplete to come before we get the
1188  // InstallTargetComplete depending on the threading, so check for both.
1189  if (ev_state.allcomplete && ev_state.complete == 2 && ev_state.manifest) {
1190  ev_state.promise.set_value();
1191  }
1192  };
1193  boost::signals2::connection conn = aktualizr.SetSignalHandler(f_cb);
1194 
1195  aktualizr.Initialize();
1196  aktualizr.UptaneCycle();
1197 
1198  auto status = ev_state.future.wait_for(std::chrono::seconds(20));
1199  if (status != std::future_status::ready) {
1200  FAIL() << "Timed out waiting for installation to complete.";
1201  }
1202 
1203  EXPECT_EQ(ev_state.started, 2);
1204  EXPECT_EQ(ev_state.complete, 2);
1205 
1206  const Json::Value manifest = http->last_manifest["signed"];
1207  const Json::Value manifest_versions = manifest["ecu_version_manifests"];
1208 
1209  // Make sure filepath were correctly written and formatted.
1210  EXPECT_EQ(manifest_versions["sec_serial1"]["signed"]["installed_image"]["filepath"].asString(),
1211  "secondary_firmware.txt");
1212  EXPECT_EQ(manifest_versions["sec_serial1"]["signed"]["installed_image"]["fileinfo"]["length"].asUInt(), 15);
1213  EXPECT_EQ(manifest_versions["sec_serial2"]["signed"]["installed_image"]["filepath"].asString(),
1214  "secondary_firmware2.txt");
1215  EXPECT_EQ(manifest_versions["sec_serial2"]["signed"]["installed_image"]["fileinfo"]["length"].asUInt(), 21);
1216 
1217  // Make sure there is no result for the primary by checking the size
1218  EXPECT_EQ(manifest["installation_report"]["report"]["items"].size(), 2);
1219  EXPECT_EQ(manifest["installation_report"]["report"]["items"][0]["ecu"].asString(), "sec_serial1");
1220  EXPECT_TRUE(manifest["installation_report"]["report"]["items"][0]["result"]["success"].asBool());
1221  EXPECT_EQ(manifest["installation_report"]["report"]["items"][1]["ecu"].asString(), "sec_serial2");
1222  EXPECT_TRUE(manifest["installation_report"]["report"]["items"][1]["result"]["success"].asBool());
1223 }
1224 
1225 /*
1226  * Initialize -> CheckUpdates -> no updates -> no further action or events.
1227  */
1228 TEST(Aktualizr, CheckNoUpdates) {
1229  TemporaryDirectory temp_dir;
1230  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "noupdates", fake_meta_dir);
1231  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1232 
1233  auto storage = INvStorage::newStorage(conf.storage);
1234  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1235 
1236  struct {
1237  size_t num_events{0};
1238  std::future<void> future;
1239  std::promise<void> promise;
1240  } ev_state;
1241  ev_state.future = ev_state.promise.get_future();
1242 
1243  auto f_cb = [&ev_state](const std::shared_ptr<event::BaseEvent>& event) {
1244  if (event->isTypeOf<event::DownloadProgressReport>()) {
1245  return;
1246  }
1247  LOG_INFO << "Got " << event->variant;
1248  switch (ev_state.num_events) {
1249  case 0: {
1250  ASSERT_EQ(event->variant, "UpdateCheckComplete");
1251  const auto targets_event = dynamic_cast<event::UpdateCheckComplete*>(event.get());
1252  EXPECT_EQ(targets_event->result.ecus_count, 0);
1253  EXPECT_EQ(targets_event->result.updates.size(), 0);
1254  EXPECT_EQ(targets_event->result.status, result::UpdateStatus::kNoUpdatesAvailable);
1255  break;
1256  }
1257  case 1: {
1258  ASSERT_EQ(event->variant, "UpdateCheckComplete");
1259  const auto targets_event = dynamic_cast<event::UpdateCheckComplete*>(event.get());
1260  EXPECT_EQ(targets_event->result.ecus_count, 0);
1261  EXPECT_EQ(targets_event->result.updates.size(), 0);
1262  EXPECT_EQ(targets_event->result.status, result::UpdateStatus::kNoUpdatesAvailable);
1263  ev_state.promise.set_value();
1264  break;
1265  }
1266  case 5:
1267  // Don't let the test run indefinitely!
1268  FAIL() << "Unexpected events!";
1269  default:
1270  std::cout << "event #" << ev_state.num_events << " is: " << event->variant << "\n";
1271  ASSERT_EQ(event->variant, "");
1272  }
1273  ++ev_state.num_events;
1274  };
1275 
1276  boost::signals2::connection conn = aktualizr.SetSignalHandler(f_cb);
1277 
1278  aktualizr.Initialize();
1279  result::UpdateCheck result = aktualizr.CheckUpdates().get();
1280  EXPECT_EQ(result.ecus_count, 0);
1281  EXPECT_EQ(result.updates.size(), 0);
1282  EXPECT_EQ(result.status, result::UpdateStatus::kNoUpdatesAvailable);
1283  // Fetch twice so that we can check for a second UpdateCheckComplete and
1284  // guarantee that nothing unexpected happened after the first fetch.
1285  result = aktualizr.CheckUpdates().get();
1286  EXPECT_EQ(result.ecus_count, 0);
1287  EXPECT_EQ(result.updates.size(), 0);
1288  EXPECT_EQ(result.status, result::UpdateStatus::kNoUpdatesAvailable);
1289 
1290  auto status = ev_state.future.wait_for(std::chrono::seconds(20));
1291  if (status != std::future_status::ready) {
1292  FAIL() << "Timed out waiting for metadata to be fetched.";
1293  }
1294 
1295  verifyNothingInstalled(aktualizr.uptane_client()->AssembleManifest());
1296 }
1297 
1298 /*
1299  * Initialize -> Download -> nothing to download.
1300  *
1301  * Initialize -> CheckUpdates -> Download -> updates downloaded but not
1302  * installed.
1303  */
1304 TEST(Aktualizr, DownloadWithUpdates) {
1305  TemporaryDirectory temp_dir;
1306  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates", fake_meta_dir);
1307  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1308 
1309  auto storage = INvStorage::newStorage(conf.storage);
1310  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1311 
1312  struct {
1313  size_t num_events{0};
1314  std::future<void> future;
1315  std::promise<void> promise;
1316  } ev_state;
1317  ev_state.future = ev_state.promise.get_future();
1318 
1319  auto f_cb = [&ev_state](const std::shared_ptr<event::BaseEvent>& event) {
1320  if (event->isTypeOf<event::DownloadProgressReport>()) {
1321  return;
1322  }
1323  LOG_INFO << "Got " << event->variant;
1324  switch (ev_state.num_events) {
1325  case 0: {
1326  ASSERT_EQ(event->variant, "AllDownloadsComplete");
1327  const auto downloads_complete = dynamic_cast<event::AllDownloadsComplete*>(event.get());
1328  EXPECT_EQ(downloads_complete->result.updates.size(), 0);
1329  EXPECT_EQ(downloads_complete->result.status, result::DownloadStatus::kError);
1330  break;
1331  }
1332  case 1: {
1333  ASSERT_EQ(event->variant, "UpdateCheckComplete");
1334  const auto targets_event = dynamic_cast<event::UpdateCheckComplete*>(event.get());
1335  EXPECT_EQ(targets_event->result.ecus_count, 2);
1336  EXPECT_EQ(targets_event->result.updates.size(), 2u);
1337  EXPECT_EQ(targets_event->result.updates[0].filename(), "primary_firmware.txt");
1338  EXPECT_EQ(targets_event->result.updates[1].filename(), "secondary_firmware.txt");
1339  EXPECT_EQ(targets_event->result.status, result::UpdateStatus::kUpdatesAvailable);
1340  break;
1341  }
1342  case 2:
1343  case 3: {
1344  ASSERT_EQ(event->variant, "DownloadTargetComplete");
1345  const auto download_event = dynamic_cast<event::DownloadTargetComplete*>(event.get());
1346  EXPECT_TRUE(download_event->update.filename() == "primary_firmware.txt" ||
1347  download_event->update.filename() == "secondary_firmware.txt");
1348  EXPECT_TRUE(download_event->success);
1349  break;
1350  }
1351  case 4: {
1352  ASSERT_EQ(event->variant, "AllDownloadsComplete");
1353  const auto downloads_complete = dynamic_cast<event::AllDownloadsComplete*>(event.get());
1354  EXPECT_EQ(downloads_complete->result.updates.size(), 2);
1355  EXPECT_TRUE(downloads_complete->result.updates[0].filename() == "primary_firmware.txt" ||
1356  downloads_complete->result.updates[1].filename() == "primary_firmware.txt");
1357  EXPECT_TRUE(downloads_complete->result.updates[0].filename() == "secondary_firmware.txt" ||
1358  downloads_complete->result.updates[1].filename() == "secondary_firmware.txt");
1359  EXPECT_EQ(downloads_complete->result.status, result::DownloadStatus::kSuccess);
1360  ev_state.promise.set_value();
1361  break;
1362  }
1363  case 8:
1364  // Don't let the test run indefinitely!
1365  FAIL() << "Unexpected events!";
1366  default:
1367  std::cout << "event #" << ev_state.num_events << " is: " << event->variant << "\n";
1368  ASSERT_EQ(event->variant, "");
1369  }
1370  ++ev_state.num_events;
1371  };
1372  boost::signals2::connection conn = aktualizr.SetSignalHandler(f_cb);
1373 
1374  aktualizr.Initialize();
1375  // First try downloading nothing. Nothing should happen.
1376  result::Download result = aktualizr.Download(std::vector<Uptane::Target>()).get();
1377  EXPECT_EQ(result.updates.size(), 0);
1378  EXPECT_EQ(result.status, result::DownloadStatus::kError);
1379  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1380  aktualizr.Download(update_result.updates);
1381 
1382  auto status = ev_state.future.wait_for(std::chrono::seconds(20));
1383  if (status != std::future_status::ready) {
1384  FAIL() << "Timed out waiting for downloads to complete.";
1385  }
1386 
1387  verifyNothingInstalled(aktualizr.uptane_client()->AssembleManifest());
1388 }
1389 
1391  public:
1392  using Responses = std::vector<std::pair<std::string, HttpResponse>>;
1393 
1394  public:
1395  HttpDownloadFailure(const boost::filesystem::path& test_dir_in, const Responses& file_to_response, std::string flavor,
1396  const boost::filesystem::path& meta_dir_in)
1397  : HttpFake(test_dir_in, flavor, meta_dir_in) {
1398  for (auto resp : file_to_response) {
1399  url_to_response_[tls_server + target_dir_ + resp.first] = resp.second;
1400  }
1401  }
1402 
1403  HttpResponse download(const std::string& url, curl_write_callback write_cb, curl_xferinfo_callback progress_cb,
1404  void* userp, curl_off_t from) override {
1405  const auto found_response_it = url_to_response_.find(url);
1406  if (found_response_it == url_to_response_.end()) {
1407  return HttpResponse("", 500, CURLE_HTTP_RETURNED_ERROR, "Internal Server Error");
1408  }
1409 
1410  auto response = url_to_response_.at(url);
1411  if (response.isOk()) {
1412  return HttpFake::download(url, write_cb, progress_cb, userp, from);
1413  }
1414 
1415  return url_to_response_[url];
1416  }
1417 
1418  private:
1419  const std::string target_dir_{"/repo/targets/"};
1420  std::map<std::string, HttpResponse> url_to_response_;
1421 };
1422 
1423 /**
1424  * Checks whether events are sent if download is unsuccessful
1425  *
1426  * Checks actions:
1427  * - Send DownloadTargetComplete event if download is unsuccessful
1428  * - Send AllDownloadsComplete event after partial download success
1429  * - Send AllDownloadsComplete event if all downloads are unsuccessful
1430  */
1431 TEST(Aktualizr, DownloadFailures) {
1432  class DownloadEventHandler {
1433  public:
1434  DownloadEventHandler(Aktualizr& aktualizr) {
1435  functor_ = std::bind(&DownloadEventHandler::operator(), this, std::placeholders::_1);
1436  aktualizr.SetSignalHandler(functor_);
1437  }
1438 
1439  void operator()(const std::shared_ptr<event::BaseEvent>& event) {
1440  ASSERT_NE(event, nullptr);
1441 
1442  if (event->isTypeOf<event::DownloadTargetComplete>()) {
1443  auto download_target_complete_event = dynamic_cast<event::DownloadTargetComplete*>(event.get());
1444  auto target_filename = download_target_complete_event->update.filename();
1445  download_status[target_filename] = download_target_complete_event->success;
1446 
1447  } else if (event->isTypeOf<event::AllDownloadsComplete>()) {
1448  auto all_download_complete_event = dynamic_cast<event::AllDownloadsComplete*>(event.get());
1449  all_download_completed_status = all_download_complete_event->result;
1450  }
1451  }
1452 
1453  public:
1454  std::map<std::string, bool> download_status;
1455  result::Download all_download_completed_status;
1456 
1457  private:
1458  std::function<void(std::shared_ptr<event::BaseEvent>)> functor_;
1459  };
1460 
1461  struct TestParams {
1462  HttpDownloadFailure::Responses downloadResponse;
1463  std::vector<std::pair<std::string, bool>> downloadResult;
1464  result::DownloadStatus allDownloadsStatus;
1465  };
1466 
1467  TestParams test_case_params[]{
1468  {// test case 0: each target download fails
1469  {{"primary_firmware.txt", HttpResponse("", 500, CURLE_HTTP_RETURNED_ERROR, "Internal Server Error")},
1470  {"secondary_firmware.txt", HttpResponse("", 500, CURLE_HTTP_RETURNED_ERROR, "Internal Server Error")}},
1471  {{"primary_firmware.txt", false}, {"secondary_firmware.txt", false}},
1472  result::DownloadStatus::kError},
1473  {// test case 1: first target download succeeds, second target download fails
1474  {{"primary_firmware.txt", HttpResponse("", 200, CURLE_OK, "")},
1475  {"secondary_firmware.txt", HttpResponse("", 404, CURLE_HTTP_RETURNED_ERROR, "Not found")}},
1476  {{"primary_firmware.txt", true}, {"secondary_firmware.txt", false}},
1477  result::DownloadStatus::kPartialSuccess},
1478  {// test case 2: first target download fails, second target download succeeds
1479  {{"primary_firmware.txt", HttpResponse("", 404, CURLE_HTTP_RETURNED_ERROR, "Not found")},
1480  {"secondary_firmware.txt", HttpResponse("", 200, CURLE_OK, "")}},
1481  {{"primary_firmware.txt", false}, {"secondary_firmware.txt", true}},
1482  result::DownloadStatus::kPartialSuccess}};
1483 
1484  for (auto test_params : test_case_params) {
1485  TemporaryDirectory temp_dir;
1486 
1487  auto http = std::make_shared<HttpDownloadFailure>(temp_dir.Path(), test_params.downloadResponse, "hasupdates",
1488  fake_meta_dir);
1489  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1490  auto storage = INvStorage::newStorage(conf.storage);
1491 
1492  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1493  DownloadEventHandler event_hdlr{aktualizr};
1494 
1495  aktualizr.Initialize();
1496  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1497  ASSERT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
1498  result::Download download_result = aktualizr.Download(update_result.updates).get();
1499 
1500  for (auto& expected_result : test_params.downloadResult) {
1501  // check if DownloadTargetComplete was received
1502  ASSERT_NE(event_hdlr.download_status.find(expected_result.first), event_hdlr.download_status.end());
1503  EXPECT_EQ(event_hdlr.download_status.at(expected_result.first), expected_result.second);
1504  }
1505  EXPECT_EQ(event_hdlr.all_download_completed_status.status, test_params.allDownloadsStatus);
1506  }
1507 }
1508 
1509 /*
1510  * List targets in storage via API.
1511  * Remove targets in storage via API.
1512  */
1513 TEST(Aktualizr, DownloadListRemove) {
1514  TemporaryDirectory temp_dir;
1515  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates", fake_meta_dir);
1516  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1517 
1518  auto storage = INvStorage::newStorage(conf.storage);
1519  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1520 
1521  aktualizr.Initialize();
1522  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1523 
1524  std::vector<Uptane::Target> targets = aktualizr.GetStoredTargets();
1525  EXPECT_EQ(targets.size(), 0);
1526 
1527  aktualizr.Download(update_result.updates).get();
1528 
1529  targets = aktualizr.GetStoredTargets();
1530  EXPECT_EQ(targets.size(), 2);
1531 
1532  // check that we can remove from the result of GetStoredTarget and
1533  // CheckUpdates
1534  aktualizr.DeleteStoredTarget(targets[0]);
1535  aktualizr.DeleteStoredTarget(update_result.updates[1]);
1536 
1537  targets = aktualizr.GetStoredTargets();
1538  EXPECT_EQ(targets.size(), 0);
1539 }
1540 
1541 /*
1542  * Automatically remove old targets during installation cycles.
1543  * Get log of installation.
1544  */
1545 TEST(Aktualizr, TargetAutoremove) {
1546  TemporaryDirectory temp_dir;
1547  const boost::filesystem::path local_metadir = temp_dir / "metadir";
1548  Utils::createDirectories(local_metadir, S_IRWXU);
1549  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "", local_metadir / "repo");
1550 
1551  UptaneRepo repo{local_metadir, "2021-07-04T16:33:27Z", "id0"};
1552  repo.generateRepo(KeyType::kED25519);
1553  const std::string hwid = "primary_hw";
1554  repo.addImage(fake_meta_dir / "fake_meta/primary_firmware.txt", "primary_firmware.txt", hwid, "", {});
1555  repo.addTarget("primary_firmware.txt", hwid, "CA:FE:A6:D2:84:9D", "");
1556  repo.signTargets();
1557 
1558  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1559  auto storage = INvStorage::newStorage(conf.storage);
1560  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1561 
1562  // attach the autoclean handler
1563  boost::signals2::connection ac_conn =
1564  aktualizr.SetSignalHandler(std::bind(targets_autoclean_cb, std::ref(aktualizr), std::placeholders::_1));
1565  aktualizr.Initialize();
1566 
1567  {
1568  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1569  aktualizr.Download(update_result.updates).get();
1570 
1571  EXPECT_EQ(aktualizr.GetStoredTargets().size(), 1);
1572 
1573  result::Install install_result = aktualizr.Install(update_result.updates).get();
1574  EXPECT_TRUE(install_result.dev_report.success);
1575 
1576  std::vector<Uptane::Target> targets = aktualizr.GetStoredTargets();
1577  ASSERT_EQ(targets.size(), 1);
1578  EXPECT_EQ(targets[0].filename(), "primary_firmware.txt");
1579  }
1580 
1581  // second install
1582  repo.emptyTargets();
1583  repo.addImage(fake_meta_dir / "fake_meta/dummy_firmware.txt", "dummy_firmware.txt", hwid, "", {});
1584  repo.addTarget("dummy_firmware.txt", hwid, "CA:FE:A6:D2:84:9D", "");
1585  repo.signTargets();
1586 
1587  {
1588  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1589  aktualizr.Download(update_result.updates).get();
1590 
1591  EXPECT_EQ(aktualizr.GetStoredTargets().size(), 2);
1592 
1593  result::Install install_result = aktualizr.Install(update_result.updates).get();
1594  EXPECT_TRUE(install_result.dev_report.success);
1595 
1596  // all targets are kept (current and previous)
1597  std::vector<Uptane::Target> targets = aktualizr.GetStoredTargets();
1598  ASSERT_EQ(targets.size(), 2);
1599  }
1600 
1601  // third install (first firmware again)
1602  repo.emptyTargets();
1603  repo.addImage(fake_meta_dir / "fake_meta/primary_firmware.txt", "primary_firmware.txt", hwid, "", {});
1604  repo.addTarget("primary_firmware.txt", hwid, "CA:FE:A6:D2:84:9D", "");
1605  repo.signTargets();
1606 
1607  {
1608  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1609  aktualizr.Download(update_result.updates).get();
1610 
1611  EXPECT_EQ(aktualizr.GetStoredTargets().size(), 2);
1612 
1613  result::Install install_result = aktualizr.Install(update_result.updates).get();
1614  EXPECT_TRUE(install_result.dev_report.success);
1615 
1616  // all targets are kept again (current and previous)
1617  std::vector<Uptane::Target> targets = aktualizr.GetStoredTargets();
1618  ASSERT_EQ(targets.size(), 2);
1619  }
1620 
1621  // fourth install (some new third firmware)
1622  repo.emptyTargets();
1623  repo.addImage(fake_meta_dir / "fake_meta/secondary_firmware.txt", "secondary_firmware.txt", hwid, "", {});
1624  repo.addTarget("secondary_firmware.txt", hwid, "CA:FE:A6:D2:84:9D", "");
1625  repo.signTargets();
1626 
1627  {
1628  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1629  aktualizr.Download(update_result.updates).get();
1630 
1631  EXPECT_EQ(aktualizr.GetStoredTargets().size(), 3);
1632 
1633  result::Install install_result = aktualizr.Install(update_result.updates).get();
1634  EXPECT_TRUE(install_result.dev_report.success);
1635 
1636  // only two targets are left: dummy_firmware has been cleaned up
1637  std::vector<Uptane::Target> targets = aktualizr.GetStoredTargets();
1638  ASSERT_EQ(targets.size(), 2);
1639  }
1640 
1641  Aktualizr::InstallationLog log = aktualizr.GetInstallationLog();
1642  ASSERT_EQ(log.size(), 2);
1643 
1644  EXPECT_EQ(log[0].installs.size(), 4);
1645  EXPECT_EQ(log[1].installs.size(), 0);
1646 
1647  std::vector<std::string> fws{"primary_firmware.txt", "dummy_firmware.txt", "primary_firmware.txt",
1648  "secondary_firmware.txt"};
1649  for (auto it = log[0].installs.begin(); it < log[0].installs.end(); it++) {
1650  auto idx = static_cast<size_t>(it - log[0].installs.begin());
1651  EXPECT_EQ(it->filename(), fws[idx]);
1652  }
1653 }
1654 
1655 /*
1656  * Initialize -> Install -> nothing to install.
1657  *
1658  * Initialize -> CheckUpdates -> Download -> Install -> updates installed.
1659  */
1660 TEST(Aktualizr, InstallWithUpdates) {
1661  TemporaryDirectory temp_dir;
1662  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates", fake_meta_dir);
1663  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1664 
1665  auto storage = INvStorage::newStorage(conf.storage);
1666  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1667 
1668  struct {
1669  size_t num_events{0};
1670  std::vector<Uptane::Target> updates;
1671  std::future<void> future;
1672  std::promise<void> promise;
1673  } ev_state;
1674  ev_state.future = ev_state.promise.get_future();
1675 
1676  auto f_cb = [&ev_state](const std::shared_ptr<event::BaseEvent>& event) {
1677  // Note that we do not expect a PutManifestComplete since we don't call
1678  // UptaneCycle() and that's the only function that generates that.
1679  if (event->isTypeOf<event::DownloadProgressReport>()) {
1680  return;
1681  }
1682  LOG_INFO << "Got " << event->variant;
1683  switch (ev_state.num_events) {
1684  case 0: {
1685  ASSERT_EQ(event->variant, "AllInstallsComplete");
1686  const auto installs_complete = dynamic_cast<event::AllInstallsComplete*>(event.get());
1687  EXPECT_EQ(installs_complete->result.ecu_reports.size(), 0);
1688  break;
1689  }
1690  case 1: {
1691  ASSERT_EQ(event->variant, "UpdateCheckComplete");
1692  const auto targets_event = dynamic_cast<event::UpdateCheckComplete*>(event.get());
1693  EXPECT_EQ(targets_event->result.ecus_count, 2);
1694  EXPECT_EQ(targets_event->result.updates.size(), 2u);
1695  EXPECT_EQ(targets_event->result.updates[0].filename(), "primary_firmware.txt");
1696  EXPECT_EQ(targets_event->result.updates[1].filename(), "secondary_firmware.txt");
1697  EXPECT_EQ(targets_event->result.status, result::UpdateStatus::kUpdatesAvailable);
1698  ev_state.updates = targets_event->result.updates;
1699  break;
1700  }
1701  case 2:
1702  case 3: {
1703  ASSERT_EQ(event->variant, "DownloadTargetComplete");
1704  const auto download_event = dynamic_cast<event::DownloadTargetComplete*>(event.get());
1705  EXPECT_TRUE(download_event->update.filename() == "primary_firmware.txt" ||
1706  download_event->update.filename() == "secondary_firmware.txt");
1707  EXPECT_TRUE(download_event->success);
1708  break;
1709  }
1710  case 4: {
1711  ASSERT_EQ(event->variant, "AllDownloadsComplete");
1712  const auto downloads_complete = dynamic_cast<event::AllDownloadsComplete*>(event.get());
1713  EXPECT_EQ(downloads_complete->result.updates.size(), 2);
1714  EXPECT_TRUE(downloads_complete->result.updates[0].filename() == "primary_firmware.txt" ||
1715  downloads_complete->result.updates[1].filename() == "primary_firmware.txt");
1716  EXPECT_TRUE(downloads_complete->result.updates[0].filename() == "secondary_firmware.txt" ||
1717  downloads_complete->result.updates[1].filename() == "secondary_firmware.txt");
1718  EXPECT_EQ(downloads_complete->result.status, result::DownloadStatus::kSuccess);
1719  break;
1720  }
1721  case 5: {
1722  // Primary always gets installed first. (Not a requirement, just how it
1723  // works at present.)
1724  ASSERT_EQ(event->variant, "InstallStarted");
1725  const auto install_started = dynamic_cast<event::InstallStarted*>(event.get());
1726  EXPECT_EQ(install_started->serial.ToString(), "CA:FE:A6:D2:84:9D");
1727  break;
1728  }
1729  case 6: {
1730  // Primary should complete before secondary begins. (Again not a
1731  // requirement per se.)
1732  ASSERT_EQ(event->variant, "InstallTargetComplete");
1733  const auto install_complete = dynamic_cast<event::InstallTargetComplete*>(event.get());
1734  EXPECT_EQ(install_complete->serial.ToString(), "CA:FE:A6:D2:84:9D");
1735  EXPECT_TRUE(install_complete->success);
1736  break;
1737  }
1738  case 7: {
1739  ASSERT_EQ(event->variant, "InstallStarted");
1740  const auto install_started = dynamic_cast<event::InstallStarted*>(event.get());
1741  EXPECT_EQ(install_started->serial.ToString(), "secondary_ecu_serial");
1742  break;
1743  }
1744  case 8: {
1745  ASSERT_EQ(event->variant, "InstallTargetComplete");
1746  const auto install_complete = dynamic_cast<event::InstallTargetComplete*>(event.get());
1747  EXPECT_EQ(install_complete->serial.ToString(), "secondary_ecu_serial");
1748  EXPECT_TRUE(install_complete->success);
1749  break;
1750  }
1751  case 9: {
1752  ASSERT_EQ(event->variant, "AllInstallsComplete");
1753  const auto installs_complete = dynamic_cast<event::AllInstallsComplete*>(event.get());
1754  EXPECT_EQ(installs_complete->result.ecu_reports.size(), 2);
1755  EXPECT_EQ(installs_complete->result.ecu_reports[0].install_res.result_code.num_code,
1756  data::ResultCode::Numeric::kOk);
1757  EXPECT_EQ(installs_complete->result.ecu_reports[1].install_res.result_code.num_code,
1758  data::ResultCode::Numeric::kOk);
1759  ev_state.promise.set_value();
1760  break;
1761  }
1762  case 12:
1763  // Don't let the test run indefinitely!
1764  FAIL() << "Unexpected events!";
1765  default:
1766  std::cout << "event #" << ev_state.num_events << " is: " << event->variant << "\n";
1767  ASSERT_EQ(event->variant, "");
1768  }
1769  ++ev_state.num_events;
1770  };
1771 
1772  boost::signals2::connection conn = aktualizr.SetSignalHandler(f_cb);
1773 
1774  aktualizr.Initialize();
1775  Json::Value primary_json;
1776  primary_json["hashes"]["sha256"] = "74e653bbf6c00a88b21f0992159b1002f5af38506e6d2fbc7eb9846711b2d75f";
1777  primary_json["hashes"]["sha512"] =
1778  "91814ad1c13ebe2af8d65044893633c4c3ce964edb8cb58b0f357406c255f7be94f42547e108b300346a42cd57662e4757b9d843b7acbc09"
1779  "0df0bc05fe55297f";
1780  primary_json["length"] = 2;
1781  Uptane::Target primary_target("primary_firmware.txt", primary_json);
1782 
1783  Json::Value secondary_json;
1784  secondary_json["hashes"]["sha256"] = "1bbb15aa921ffffd5079567d630f43298dbe5e7cbc1b14e0ccdd6718fde28e47";
1785  secondary_json["hashes"]["sha512"] =
1786  "7dbae4c36a2494b731a9239911d3085d53d3e400886edb4ae2b9b78f40bda446649e83ba2d81653f614cc66f5dd5d4dbd95afba854f148af"
1787  "bfae48d0ff4cc38a";
1788  secondary_json["length"] = 2;
1789  Uptane::Target secondary_target("secondary_firmware.txt", secondary_json);
1790 
1791  // First try installing nothing. Nothing should happen.
1792  result::Install result = aktualizr.Install(ev_state.updates).get();
1793  EXPECT_EQ(result.ecu_reports.size(), 0);
1794 
1795  EXPECT_THROW(aktualizr.OpenStoredTarget(primary_target).get(), std::runtime_error)
1796  << "Primary firmware is present in storage before the download";
1797  EXPECT_THROW(aktualizr.OpenStoredTarget(secondary_target).get(), std::runtime_error)
1798  << "Secondary firmware is present in storage before the download";
1799 
1800  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1801  aktualizr.Download(update_result.updates).get();
1802  EXPECT_NE(aktualizr.OpenStoredTarget(primary_target).get(), nullptr)
1803  << "Primary firmware is not present in storage after the download";
1804  EXPECT_NE(aktualizr.OpenStoredTarget(secondary_target).get(), nullptr)
1805  << "Secondary firmware is not present in storage after the download";
1806 
1807  // After updates have been downloaded, try to install them.
1808  aktualizr.Install(update_result.updates);
1809 
1810  auto status = ev_state.future.wait_for(std::chrono::seconds(20));
1811  if (status != std::future_status::ready) {
1812  FAIL() << "Timed out waiting for installation to complete.";
1813  }
1814 }
1815 
1816 /**
1817  * Verifies reporting of update download progress
1818  *
1819  * Checks actions:
1820  *
1821  * - [x] Report download progress
1822  */
1823 TEST(Aktualizr, ReportDownloadProgress) {
1824  // The test initialization part is repeated in many tests so maybe it makes sense
1825  // to define a fixture and move this common initialization part into the fixture's SetUp() method
1826  TemporaryDirectory temp_dir;
1827  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates", fake_meta_dir);
1828  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1829  auto storage = INvStorage::newStorage(conf.storage);
1830  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1831 
1832  unsigned int report_counter = {0};
1833  std::shared_ptr<const event::DownloadProgressReport> lastProgressReport{nullptr};
1834 
1835  std::function<void(std::shared_ptr<event::BaseEvent> event)> report_event_hdlr =
1836  [&](const std::shared_ptr<event::BaseEvent>& event) {
1837  ASSERT_NE(event, nullptr);
1838  if (!event->isTypeOf<event::DownloadProgressReport>()) {
1839  return;
1840  }
1841 
1842  auto download_progress_event = std::dynamic_pointer_cast<event::DownloadProgressReport>(event);
1843  ASSERT_NE(download_progress_event, nullptr);
1844  ASSERT_NE(download_progress_event.get(), nullptr);
1845  if (lastProgressReport) {
1846  EXPECT_GE(download_progress_event->progress, lastProgressReport->progress);
1847  }
1848  lastProgressReport = download_progress_event;
1849  ++report_counter;
1850  };
1851 
1852  aktualizr.SetSignalHandler(report_event_hdlr);
1853  aktualizr.Initialize();
1854 
1855  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
1856  ASSERT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
1857  // The test mocks are tailored to emulate a device with a primary ECU and one secondary ECU
1858  // for sake of the download progress report testing it's suffice to test it agains just one of the ECUs
1859  update_result.updates.pop_back();
1860 
1861  result::Download download_result = aktualizr.Download(update_result.updates).get();
1862  EXPECT_EQ(download_result.status, result::DownloadStatus::kSuccess);
1863 
1864  ASSERT_NE(lastProgressReport, nullptr);
1865  ASSERT_NE(lastProgressReport.get(), nullptr);
1866  EXPECT_TRUE(event::DownloadProgressReport::isDownloadCompleted(*lastProgressReport));
1867  EXPECT_GT(report_counter, 1);
1868 }
1869 
1870 class HttpFakeCampaign : public HttpFake {
1871  public:
1872  HttpFakeCampaign(const boost::filesystem::path& test_dir_in, const boost::filesystem::path& meta_dir_in)
1873  : HttpFake(test_dir_in, "", meta_dir_in) {}
1874 
1875  HttpResponse get(const std::string& url, int64_t maxsize) override {
1876  EXPECT_NE(url.find("campaigner/"), std::string::npos);
1877  boost::filesystem::path path = meta_dir / url.substr(tls_server.size() + strlen("campaigner/"));
1878 
1879  if (url.find("campaigner/campaigns") != std::string::npos) {
1880  return HttpResponse(Utils::readFile(path.parent_path() / "campaigner/campaigns.json"), 200, CURLE_OK, "");
1881  }
1882  return HttpFake::get(url, maxsize);
1883  }
1884 
1885  HttpResponse handle_event(const std::string& url, const Json::Value& data) override {
1886  (void)url;
1887  for (auto it = data.begin(); it != data.end(); it++) {
1888  auto ev = *it;
1889  auto id = ev["eventType"]["id"];
1890  if (id == "campaign_accepted" || id == "campaign_declined" || id == "campaign_postponed") {
1891  seen_events.push_back(ev);
1892  }
1893  }
1894  return HttpResponse("", 204, CURLE_OK, "");
1895  }
1896 
1897  std::vector<Json::Value> seen_events;
1898 };
1899 
1901  public:
1902  bool campaignaccept_seen{false};
1903  bool campaigndecline_seen{false};
1904  bool campaignpostpone_seen{false};
1905 
1906  void handler(const std::shared_ptr<event::BaseEvent>& event) {
1907  std::cout << event->variant << "\n";
1908  if (event->variant == "CampaignCheckComplete") {
1909  auto concrete_event = std::static_pointer_cast<event::CampaignCheckComplete>(event);
1910  EXPECT_EQ(concrete_event->result.campaigns.size(), 1);
1911  EXPECT_EQ(concrete_event->result.campaigns[0].name, "campaign1");
1912  EXPECT_EQ(concrete_event->result.campaigns[0].id, "c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493");
1913  EXPECT_EQ(concrete_event->result.campaigns[0].size, 62470);
1914  EXPECT_EQ(concrete_event->result.campaigns[0].autoAccept, true);
1915  EXPECT_EQ(concrete_event->result.campaigns[0].description, "this is my message to show on the device");
1916  } else if (event->variant == "CampaignAcceptComplete") {
1917  campaignaccept_seen = true;
1918  } else if (event->variant == "CampaignDeclineComplete") {
1919  campaigndecline_seen = true;
1920  } else if (event->variant == "CampaignPostponeComplete") {
1921  campaignpostpone_seen = true;
1922  }
1923  }
1924 };
1925 
1926 /* Check for campaigns with manual control.
1927  * Send CampaignCheckComplete event with campaign data.
1928  * Fetch campaigns from the server.
1929  *
1930  * Accept a campaign.
1931  * Send campaign acceptance report.
1932  * Send CampaignAcceptComplete event.
1933  *
1934  * Decline a campaign.
1935  * Send campaign decline report.
1936  * Send CampaignDeclineComplete event.
1937  *
1938  * Postpone a campaign.
1939  * Send campaign postpone report.
1940  * Send CampaignPostponeComplete event.
1941  */
1942 TEST(Aktualizr, CampaignCheckAndControl) {
1943  TemporaryDirectory temp_dir;
1944  auto http = std::make_shared<HttpFakeCampaign>(temp_dir.Path(), fake_meta_dir);
1945  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
1946 
1947  CampaignEvents campaign_events;
1948 
1949  {
1950  auto storage = INvStorage::newStorage(conf.storage);
1951  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
1952  aktualizr.SetSignalHandler(std::bind(&CampaignEvents::handler, &campaign_events, std::placeholders::_1));
1953  aktualizr.Initialize();
1954 
1955  // check for campaign
1956  auto result = aktualizr.CampaignCheck().get();
1957  EXPECT_EQ(result.campaigns.size(), 1);
1958 
1959  // accept the campaign
1960  aktualizr.CampaignControl("c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493", campaign::Cmd::Accept).get();
1961 
1962  aktualizr.CampaignControl("c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493", campaign::Cmd::Decline).get();
1963 
1964  aktualizr.CampaignControl("c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493", campaign::Cmd::Postpone).get();
1965  }
1966 
1967  ASSERT_EQ(http->seen_events.size(), 3);
1968  for (const auto& ev : http->seen_events) {
1969  EXPECT_EQ(ev["event"]["campaignId"], "c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493");
1970  }
1971  EXPECT_TRUE(campaign_events.campaignaccept_seen);
1972  EXPECT_TRUE(campaign_events.campaigndecline_seen);
1973  EXPECT_TRUE(campaign_events.campaignpostpone_seen);
1974 }
1975 
1977  public:
1978  HttpFakeNoCorrelationId(const boost::filesystem::path& test_dir_in, const boost::filesystem::path& meta_dir_in)
1979  : HttpFake(test_dir_in, "", meta_dir_in) {}
1980 
1981  HttpResponse handle_event(const std::string& url, const Json::Value& data) override {
1982  (void)url;
1983  for (const Json::Value& event : data) {
1984  ++events_seen;
1985  EXPECT_EQ(event["event"]["correlationId"].asString(), "");
1986  }
1987  return HttpResponse("", 200, CURLE_OK, "");
1988  }
1989 
1990  unsigned int events_seen{0};
1991 };
1992 
1993 /* Correlation ID is empty if none was provided in targets metadata. */
1994 TEST(Aktualizr, FullNoCorrelationId) {
1995  TemporaryDirectory temp_dir;
1996  TemporaryDirectory meta_dir;
1997  Utils::copyDir(uptane_repos_dir / "full_no_correlation_id/repo/repo", meta_dir.Path() / "repo");
1998  Utils::copyDir(uptane_repos_dir / "full_no_correlation_id/repo/director", meta_dir.Path() / "director");
1999  auto http = std::make_shared<HttpFakeNoCorrelationId>(temp_dir.Path(), meta_dir.Path());
2000 
2001  // scope `Aktualizr` object, so that the ReportQueue flushes its events before
2002  // we count them at the end
2003  {
2004  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
2005 
2006  auto storage = INvStorage::newStorage(conf.storage);
2007  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
2008 
2009  aktualizr.Initialize();
2010  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
2011  EXPECT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
2012 
2013  result::Download download_result = aktualizr.Download(update_result.updates).get();
2014  EXPECT_EQ(download_result.status, result::DownloadStatus::kSuccess);
2015 
2016  result::Install install_result = aktualizr.Install(download_result.updates).get();
2017  for (const auto& r : install_result.ecu_reports) {
2018  EXPECT_EQ(r.install_res.result_code.num_code, data::ResultCode::Numeric::kOk);
2019  }
2020  }
2021 
2022  EXPECT_EQ(http->events_seen, 8);
2023 }
2024 
2025 TEST(Aktualizr, ManifestCustom) {
2026  TemporaryDirectory temp_dir;
2027  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "", fake_meta_dir);
2028 
2029  {
2030  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
2031 
2032  auto storage = INvStorage::newStorage(conf.storage);
2033  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
2034 
2035  aktualizr.Initialize();
2036  Json::Value custom = Utils::parseJSON(R"({"test_field":"test_value"})");
2037  ASSERT_EQ(custom["test_field"].asString(), "test_value"); // Shouldn't fail, just check that test itself is correct
2038  ASSERT_EQ(true, aktualizr.SendManifest(custom).get()) << "Failed to upload manifest with HttpFake server";
2039  EXPECT_EQ(http->last_manifest["signed"]["custom"], custom);
2040  }
2041 }
2042 
2044  public:
2045  CountUpdateCheckEvents() = default;
2046  // Non-copyable
2048  CountUpdateCheckEvents& operator=(const CountUpdateCheckEvents&) = delete;
2049 
2050  std::function<void(std::shared_ptr<event::BaseEvent>)> Signal() {
2051  return std::bind(&CountUpdateCheckEvents::count, this, std::placeholders::_1);
2052  }
2053 
2054  void count(std::shared_ptr<event::BaseEvent> event) {
2055  std::cout << event->variant << "\n";
2056  if (event->variant == "UpdateCheckComplete") {
2057  total_events_++;
2058  if (std::static_pointer_cast<event::UpdateCheckComplete>(event)->result.status == result::UpdateStatus::kError) {
2059  error_events_++;
2060  }
2061  }
2062  }
2063 
2064  int total_events() const { return total_events_; }
2065  int error_events() const { return error_events_; }
2066 
2067  private:
2068  int total_events_{0};
2069  int error_events_{0};
2070 };
2071 
2072 TEST(Aktualizr, APICheck) {
2073  TemporaryDirectory temp_dir;
2074  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates", fake_meta_dir);
2075 
2076  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
2077 
2078  auto storage = INvStorage::newStorage(conf.storage);
2079 
2080  CountUpdateCheckEvents counter;
2081  {
2082  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
2083  boost::signals2::connection conn = aktualizr.SetSignalHandler(counter.Signal());
2084  aktualizr.Initialize();
2085  std::vector<std::future<result::UpdateCheck>> futures;
2086  for (int i = 0; i < 5; ++i) {
2087  futures.push_back(aktualizr.CheckUpdates());
2088  }
2089 
2090  for (auto& f : futures) {
2091  f.get();
2092  }
2093  }
2094 
2095  EXPECT_EQ(counter.total_events(), 5);
2096 
2097  // try again, but shutdown before it finished all calls
2098  CountUpdateCheckEvents counter2;
2099  {
2100  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
2101  boost::signals2::connection conn = aktualizr.SetSignalHandler(counter2.Signal());
2102  aktualizr.Initialize();
2103 
2104  std::future<result::UpdateCheck> ft = aktualizr.CheckUpdates();
2105  for (int i = 0; i < 99; ++i) {
2106  aktualizr.CheckUpdates();
2107  }
2108  ft.get();
2109  }
2110  EXPECT_LT(counter2.total_events(), 100);
2111  EXPECT_GT(counter2.total_events(), 0);
2112 }
2113 
2115  public:
2116  HttpPutManifestFail(const boost::filesystem::path& test_dir_in, const boost::filesystem::path& meta_dir_in)
2117  : HttpFake(test_dir_in, "", meta_dir_in) {}
2118  HttpResponse put(const std::string& url, const Json::Value& data) override {
2119  (void)data;
2120  return HttpResponse(url, 504, CURLE_OK, "");
2121  }
2122 };
2123 
2124 /* Send UpdateCheckComplete event after failure */
2125 TEST(Aktualizr, UpdateCheckCompleteError) {
2126  TemporaryDirectory temp_dir;
2127  auto http = std::make_shared<HttpPutManifestFail>(temp_dir.Path(), fake_meta_dir);
2128 
2129  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, "http://updatefail");
2130 
2131  auto storage = INvStorage::newStorage(conf.storage);
2132  CountUpdateCheckEvents counter;
2133 
2134  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
2135  boost::signals2::connection conn = aktualizr.SetSignalHandler(counter.Signal());
2136  aktualizr.Initialize();
2137  auto result = aktualizr.CheckUpdates().get();
2138  EXPECT_EQ(result.status, result::UpdateStatus::kError);
2139  EXPECT_EQ(counter.error_events(), 1);
2140 }
2141 
2142 /*
2143  * Suspend API calls during pause.
2144  * Catch up with calls queue after resume.
2145  */
2146 
2148  public:
2149  HttpFakePauseCounter(const boost::filesystem::path& test_dir_in, const boost::filesystem::path& meta_dir_in)
2150  : HttpFake(test_dir_in, "noupdates", meta_dir_in) {}
2151 
2152  HttpResponse handle_event(const std::string& url, const Json::Value& data) override {
2153  (void)url;
2154  for (const Json::Value& event : data) {
2155  ++events_seen;
2156  std::string event_type = event["eventType"]["id"].asString();
2157 
2158  std::cout << "got event #" << events_seen << ": " << event_type << "\n";
2159  if (events_seen == 1) {
2160  EXPECT_EQ(event_type, "DevicePaused");
2161  EXPECT_EQ(event["event"]["correlationId"], "id0");
2162  } else if (events_seen == 2) {
2163  EXPECT_EQ(event_type, "DeviceResumed");
2164  EXPECT_EQ(event["event"]["correlationId"], "id0");
2165  } else {
2166  std::cout << "Unexpected event";
2167  EXPECT_EQ(0, 1);
2168  }
2169  }
2170  return HttpResponse("", 200, CURLE_OK, "");
2171  }
2172 
2173  unsigned int events_seen{0};
2174 };
2175 
2176 TEST(Aktualizr, PauseResumeQueue) {
2177  TemporaryDirectory temp_dir;
2178  auto http = std::make_shared<HttpFakePauseCounter>(temp_dir.Path(), fake_meta_dir);
2179  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
2180 
2181  auto storage = INvStorage::newStorage(conf.storage);
2182  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
2183  aktualizr.Initialize();
2184 
2185  std::mutex mutex;
2186  std::promise<void> end_promise{};
2187  size_t n_events = 0;
2188  std::atomic_bool is_paused{false};
2189  std::function<void(std::shared_ptr<event::BaseEvent>)> cb = [&end_promise, &n_events, &mutex,
2190  &is_paused](std::shared_ptr<event::BaseEvent> event) {
2191  switch (n_events) {
2192  case 0:
2193  ASSERT_EQ(event->variant, "UpdateCheckComplete");
2194  break;
2195  case 1: {
2196  std::lock_guard<std::mutex> guard(mutex);
2197 
2198  ASSERT_EQ(event->variant, "UpdateCheckComplete");
2199  // the event shouldn't happen when the system is paused
2200  EXPECT_FALSE(is_paused);
2201  end_promise.set_value();
2202  break;
2203  }
2204  default:
2205  FAIL() << "Unexpected event";
2206  }
2207  n_events += 1;
2208  };
2209  boost::signals2::connection conn = aktualizr.SetSignalHandler(cb);
2210 
2211  // trigger the first UpdateCheck
2212  aktualizr.CheckUpdates().get();
2213 
2214  {
2215  std::lock_guard<std::mutex> guard(mutex);
2216  EXPECT_EQ(aktualizr.Pause().status, result::PauseStatus::kSuccess);
2217  is_paused = true;
2218  }
2219 
2220  EXPECT_EQ(aktualizr.Pause().status, result::PauseStatus::kAlreadyPaused);
2221 
2222  aktualizr.CheckUpdates();
2223 
2224  // Theoritically racy, could cause bad implem to succeeed sometimes
2225  std::this_thread::sleep_for(std::chrono::seconds(1));
2226 
2227  {
2228  std::lock_guard<std::mutex> guard(mutex);
2229  is_paused = false;
2230  EXPECT_EQ(aktualizr.Resume().status, result::PauseStatus::kSuccess);
2231  }
2232 
2233  EXPECT_EQ(aktualizr.Resume().status, result::PauseStatus::kAlreadyRunning);
2234 
2235  auto status = end_promise.get_future().wait_for(std::chrono::seconds(20));
2236  if (status != std::future_status::ready) {
2237  FAIL() << "Timed out waiting for UpdateCheck event";
2238  }
2239 }
2240 
2241 #ifndef __NO_MAIN__
2242 int main(int argc, char** argv) {
2243  ::testing::InitGoogleTest(&argc, argv);
2244  if (argc != 2) {
2245  std::cerr << "Error: " << argv[0] << " requires the path to the base directory of uptane repos.\n";
2246  return EXIT_FAILURE;
2247  }
2248  uptane_repos_dir = argv[1];
2249 
2250  logger_init();
2251  logger_set_threshold(boost::log::trivial::trace);
2252 
2253  TemporaryDirectory tmp_dir;
2254  fake_meta_dir = tmp_dir.Path();
2255  MetaFake meta_fake(fake_meta_dir);
2256 
2257  return RUN_ALL_TESTS();
2258 }
2259 #endif
2260 
2261 // vim: set tabstop=2 shiftwidth=2 expandtab:
Aktualizr::Resume
result::Pause Resume()
Resume the library operations.
Definition: aktualizr.cc:174
Aktualizr::RunForever
std::future< void > RunForever()
Asynchronously run aktualizr indefinitely until Shutdown is called.
Definition: aktualizr.cc:72
HttpFakePauseCounter
Definition: aktualizr_test.cc:2147
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
HttpFake
Definition: httpfake.h:22
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:1390
event::DownloadTargetComplete
A target has been downloaded.
Definition: events.h:96
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:2043
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:54
Uptane::HardwareIdentifier
Definition: tuf.h:143
HttpResponse
Definition: httpinterface.h:17
CampaignEvents
Definition: aktualizr_test.cc:1900
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:174
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:1870
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:1976
event::DownloadProgressReport
A report for a download in progress.
Definition: events.h:71
Uptane::Target
Definition: tuf.h:238
HttpFakeSplit
Definition: aktualizr_test.cc:362
UptaneRepo
Definition: uptane_repo.h:7
HttpPutManifestFail
Definition: aktualizr_test.cc:2114
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
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