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