Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
storage_atomic_test.cc
1 #include <gtest/gtest.h>
2 
3 #include <chrono>
4 #include <csignal>
5 #include <memory>
6 #include <random>
7 #include <string>
8 #include <thread>
9 
10 #include <boost/filesystem.hpp>
11 
12 #include "storage/sqlstorage.h"
13 #include "utilities/utils.h"
14 
15 #include "logging/logging.h"
16 
17 StorageType storage_test_type;
18 
19 std::unique_ptr<INvStorage> Storage(const StorageConfig& config) {
20  if (config.type == StorageType::kSqlite) {
21  return std::unique_ptr<INvStorage>(new SQLStorage(config, false));
22  } else {
23  throw std::runtime_error("Invalid config type");
24  }
25 }
26 
27 StorageConfig MakeConfig(StorageType type, const boost::filesystem::path& storage_dir) {
28  StorageConfig config;
29 
30  config.type = type;
31  if (config.type == StorageType::kSqlite) {
32  config.sqldb_path = storage_dir / "test.db";
33  } else {
34  throw std::runtime_error("Invalid config type");
35  }
36  return config;
37 }
38 
39 struct TightProcess {
40  pid_t pid;
41  int pipefd[2];
42  TemporaryDirectory storage_dir;
43 };
44 
45 static void tight_store_keys(const TightProcess& t) {
46  StorageConfig config = MakeConfig(storage_test_type, t.storage_dir.Path());
47  std::unique_ptr<INvStorage> storage = Storage(config);
48  unsigned int k = 0;
49  char c = 'r';
50 
51  EXPECT_EQ(write(t.pipefd[1], &c, 1), 1);
52  while (true) {
53  storage->storePrimaryKeys(std::to_string(k), std::to_string(k));
54  k += 1;
55  }
56 }
57 
58 static void check_consistent_state(const boost::filesystem::path& storage_dir) {
59  StorageConfig config = MakeConfig(storage_test_type, storage_dir);
60  std::unique_ptr<INvStorage> storage = Storage(config);
61  std::string pub, priv;
62 
63  EXPECT_TRUE(storage->loadPrimaryKeys(&pub, &priv));
64 
65  EXPECT_EQ(pub, priv);
66 }
67 
68 void atomic_test() {
69  unsigned int n_procs = 70;
70  std::list<TightProcess> procs;
71 
72  for (auto k = 0u; k < n_procs; k++) {
73  procs.emplace_back();
74  TightProcess& p = procs.back();
75 
76  EXPECT_EQ(pipe(p.pipefd), 0);
77 
78  pid_t pid = fork();
79  if (pid != 0) {
80  close(p.pipefd[1]);
81  p.pid = pid;
82 
83  } else {
84  close(p.pipefd[0]);
85  tight_store_keys(p);
86  exit(0);
87  }
88  }
89 
90  for (const auto& p : procs) {
91  // wait for readiness
92  char c;
93  EXPECT_EQ(read(p.pipefd[0], &c, 1), 1);
94  close(p.pipefd[0]);
95  }
96 
97  std::mt19937_64 eng{12};
98  std::uniform_int_distribution<> dist{1, 30};
99  while (!procs.empty()) {
100  std::this_thread::sleep_for(std::chrono::milliseconds{dist(eng)});
101 
102  TightProcess& p = procs.back();
103  kill(p.pid, SIGKILL);
104  waitpid(p.pid, NULL, 0);
105  check_consistent_state(p.storage_dir.Path());
106 
107  procs.pop_back();
108  }
109 }
110 
111 // To run these tests:
112 // ./build/tests/t_storage_atomic --gtest_also_run_disabled_tests
113 
114 TEST(DISABLED_storage_atomic, sql) {
115  // disabled for now because it uses too much resources for CI
116  storage_test_type = StorageType::kSqlite;
117  atomic_test();
118 }
119 
120 #ifndef __NO_MAIN__
121 int main(int argc, char** argv) {
122  ::testing::InitGoogleTest(&argc, argv);
123  logger_init();
124  logger_set_threshold(boost::log::trivial::trace);
125  return RUN_ALL_TESTS();
126 }
127 #endif
TightProcess
Definition: storage_atomic_test.cc:39
StorageConfig
Definition: config.h:111
TemporaryDirectory
Definition: utils.h:82
SQLStorage
Definition: sqlstorage.h:18