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