Adjust to change in PkgmgrInterface
[platform/core/appfw/wgt-backend.git] / src / unit_tests / smoke_test.cc
1 // Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by an apache-2.0 license that can be
3 // found in the LICENSE file.
4
5 #include <boost/filesystem/operations.hpp>
6 #include <boost/filesystem/path.hpp>
7 #include <boost/range/iterator_range.hpp>
8 #include <boost/system/error_code.hpp>
9 #include <common/backup_paths.h>
10 #include <common/pkgmgr_interface.h>
11 #include <common/pkgmgr_registration.h>
12 #include <common/request.h>
13 #include <common/step/step_fail.h>
14 #include <gtest/gtest.h>
15 #include <gtest/gtest-death-test.h>
16 #include <pkgmgr-info.h>
17 #include <signal.h>
18 #include <unistd.h>
19 #include <tzplatform_config.h>
20
21 #include <array>
22 #include <cstdio>
23 #include <cstdlib>
24
25 #include "wgt/wgt_app_query_interface.h"
26 #include "wgt/wgt_installer.h"
27
28 #define SIZEOFARRAY(ARR)                                                       \
29   sizeof(ARR) / sizeof(ARR[0])                                                 \
30
31 namespace bf = boost::filesystem;
32 namespace bs = boost::system;
33 namespace ci = common_installer;
34
35 namespace {
36
37 const bf::path kSmokePackagesDirectory =
38     "/usr/share/wgt-backend-ut/test_samples/smoke/";
39
40 const char kApplicationDir[] = ".applications";
41 const char kApplicationDirBackup[] = ".applications.bck";
42 const char KUserAppsDir[] = "apps_rw";
43 const char KUserAppsDirBackup[] = "apps_rw.bck";
44
45 enum class RequestResult {
46   NORMAL,
47   FAIL,
48   CRASH
49 };
50
51 class TestPkgmgrInstaller : public ci::PkgmgrInstallerInterface {
52  public:
53   bool CreatePkgMgrInstaller(pkgmgr_installer** installer,
54                              ci::InstallationMode* mode) {
55     *installer = pkgmgr_installer_new();
56     if (!*installer)
57       return false;
58     *mode = ci::InstallationMode::ONLINE;
59     return true;
60   }
61
62   bool ShouldCreateSignal() const {
63     return false;
64   }
65 };
66
67 class StepCrash : public ci::Step {
68  public:
69   using Step::Step;
70
71   ci::Step::Status process() override {
72     raise(SIGSEGV);
73     return Status::OK;
74   }
75   ci::Step::Status clean() override { return ci::Step::Status::OK; }
76   ci::Step::Status undo() override { return ci::Step::Status::OK; }
77   ci::Step::Status precheck() override { return ci::Step::Status::OK; }
78 };
79
80 void RemoveAllRecoveryFiles() {
81   bf::path root_path = ci::GetRootAppPath(false);
82   if (!bf::exists(root_path))
83     return;
84   for (auto& dir_entry : boost::make_iterator_range(
85          bf::directory_iterator(root_path), bf::directory_iterator())) {
86     if (bf::is_regular_file(dir_entry)) {
87       if (dir_entry.path().string().find("/recovery") != std::string::npos) {
88         bs::error_code error;
89         bf::remove(dir_entry.path(), error);
90       }
91     }
92   }
93 }
94
95 bf::path FindRecoveryFile() {
96   bf::path root_path = ci::GetRootAppPath(false);
97   for (auto& dir_entry : boost::make_iterator_range(
98          bf::directory_iterator(root_path), bf::directory_iterator())) {
99     if (bf::is_regular_file(dir_entry)) {
100       if (dir_entry.path().string().find("/recovery") != std::string::npos) {
101         return dir_entry.path();
102       }
103     }
104   }
105   return {};
106 }
107
108 bool ValidateFileContentInPackage(const std::string& pkgid,
109                                   const std::string& relative,
110                                   const std::string& expected) {
111   bf::path root_path = ci::GetRootAppPath(false);
112   bf::path file_path = root_path / pkgid / relative;
113   if (!bf::exists(file_path)) {
114     LOG(ERROR) << file_path << " doesn't exist";
115     return false;
116   }
117   FILE* handle = fopen(file_path.c_str(), "r");
118   if (!handle) {
119     LOG(ERROR) << file_path << " cannot  be open";
120     return false;
121   }
122   std::string content;
123   std::array<char, 200> buffer;
124   while (fgets(buffer.data(), buffer.size(), handle)) {
125     content += buffer.data();
126   }
127   fclose(handle);
128   return content == expected;
129 }
130
131 void ValidatePackageFS(const std::string& pkgid, const std::string& appid) {
132   bf::path root_path = ci::GetRootAppPath(false);
133   bf::path package_path = root_path / pkgid;
134   bf::path binary_path = package_path / "bin" / appid;
135   bf::path data_path = package_path / "data";
136   bf::path shared_path = package_path / "shared";
137   bf::path cache_path = package_path / "cache";
138   ASSERT_TRUE(bf::exists(root_path));
139   ASSERT_TRUE(bf::exists(package_path));
140   ASSERT_TRUE(bf::exists(binary_path));
141   ASSERT_TRUE(bf::exists(data_path));
142   ASSERT_TRUE(bf::exists(shared_path));
143   ASSERT_TRUE(bf::exists(cache_path));
144
145   bf::path manifest_path =
146       bf::path(getUserManifestPath(getuid())) / (pkgid + ".xml");
147   bf::path icon_path = bf::path(getIconPath(getuid())) / (appid + ".png");
148   ASSERT_TRUE(bf::exists(manifest_path));
149   ASSERT_TRUE(bf::exists(icon_path));
150
151   bf::path widget_root_path = package_path / "res" / "wgt";
152   bf::path config_path = widget_root_path / "config.xml";
153   ASSERT_TRUE(bf::exists(widget_root_path));
154   ASSERT_TRUE(bf::exists(config_path));
155
156   bf::path private_tmp_path = package_path / "tmp";
157   ASSERT_TRUE(bf::exists(private_tmp_path));
158
159   // backups should not exist
160   bf::path package_backup = ci::GetBackupPathForPackagePath(package_path);
161   bf::path manifest_backup = ci::GetBackupPathForManifestFile(manifest_path);
162   bf::path icon_backup = ci::GetBackupPathForIconFile(icon_path);
163   ASSERT_FALSE(bf::exists(package_backup));
164   ASSERT_FALSE(bf::exists(manifest_backup));
165   ASSERT_FALSE(bf::exists(icon_backup));
166 }
167
168 void PackageCheckCleanup(const std::string& pkgid, const std::string& appid) {
169   bf::path root_path = ci::GetRootAppPath(false);
170   bf::path package_path = root_path / pkgid;
171   ASSERT_FALSE(bf::exists(package_path));
172
173   bf::path manifest_path =
174       bf::path(getUserManifestPath(getuid())) / (pkgid + ".xml");
175   bf::path icon_path = bf::path(getIconPath(getuid())) / (appid + ".png");
176   ASSERT_FALSE(bf::exists(manifest_path));
177   ASSERT_FALSE(bf::exists(icon_path));
178
179   // backups should not exist
180   bf::path package_backup = ci::GetBackupPathForPackagePath(package_path);
181   bf::path manifest_backup = ci::GetBackupPathForManifestFile(manifest_path);
182   bf::path icon_backup = ci::GetBackupPathForIconFile(icon_path);
183   ASSERT_FALSE(bf::exists(package_backup));
184   ASSERT_FALSE(bf::exists(manifest_backup));
185   ASSERT_FALSE(bf::exists(icon_backup));
186 }
187
188 void ValidatePackage(const std::string& pkgid, const std::string& appid) {
189   ASSERT_TRUE(ci::IsPackageInstalled(pkgid, ci::GetRequestMode()));
190   ValidatePackageFS(pkgid, appid);
191 }
192
193 void CheckPackageNonExistance(const std::string& pkgid,
194                               const std::string& appid) {
195   ASSERT_FALSE(ci::IsPackageInstalled(pkgid, ci::GetRequestMode()));
196   PackageCheckCleanup(pkgid, appid);
197 }
198
199 std::unique_ptr<ci::AppQueryInterface> CreateQueryInterface() {
200   std::unique_ptr<ci::AppQueryInterface> query_interface(
201       new wgt::WgtAppQueryInterface());
202   return query_interface;
203 }
204
205 std::unique_ptr<ci::AppInstaller> CreateInstaller(ci::PkgMgrPtr pkgmgr) {
206   std::unique_ptr<ci::AppInstaller> installer(new wgt::WgtInstaller(pkgmgr));
207   return installer;
208 }
209
210 ci::AppInstaller::Result RunInstallerWithPkgrmgr(ci::PkgMgrPtr pkgmgr,
211                                                  RequestResult mode) {
212   std::unique_ptr<ci::AppInstaller> installer = CreateInstaller(pkgmgr);
213   switch (mode) {
214   case RequestResult::FAIL:
215     installer->AddStep<ci::configuration::StepFail>();
216     break;
217   case RequestResult::CRASH:
218     installer->AddStep<StepCrash>();
219   default:
220     break;
221   }
222   return installer->Run();
223 }
224
225 ci::AppInstaller::Result Install(const bf::path& path,
226                                  RequestResult mode = RequestResult::NORMAL) {
227   const char* argv[] = {"", "-i", path.c_str()};
228   TestPkgmgrInstaller pkgmgr_installer;
229   std::unique_ptr<ci::AppQueryInterface> query_interface =
230       CreateQueryInterface();
231   auto pkgmgr =
232       ci::PkgMgrInterface::Create(SIZEOFARRAY(argv), const_cast<char**>(argv),
233                                   &pkgmgr_installer, query_interface.get());
234   if (!pkgmgr) {
235     LOG(ERROR) << "Failed to initialize pkgmgr interface";
236     return ci::AppInstaller::Result::UNKNOWN;
237   }
238   return RunInstallerWithPkgrmgr(pkgmgr, mode);
239 }
240
241 ci::AppInstaller::Result Update(const bf::path& path_old,
242                                 const bf::path& path_new,
243                                 RequestResult mode = RequestResult::NORMAL) {
244   if (Install(path_old) != ci::AppInstaller::Result::OK) {
245     LOG(ERROR) << "Failed to install application. Cannot update";
246     return ci::AppInstaller::Result::UNKNOWN;
247   }
248   return Install(path_new, mode);
249 }
250
251 ci::AppInstaller::Result Uninstall(const std::string& pkgid,
252                                    RequestResult mode = RequestResult::NORMAL) {
253   const char* argv[] = {"", "-d", pkgid.c_str()};
254   TestPkgmgrInstaller pkgmgr_installer;
255   std::unique_ptr<ci::AppQueryInterface> query_interface =
256       CreateQueryInterface();
257   auto pkgmgr =
258       ci::PkgMgrInterface::Create(SIZEOFARRAY(argv), const_cast<char**>(argv),
259                                   &pkgmgr_installer, query_interface.get());
260   if (!pkgmgr) {
261     LOG(ERROR) << "Failed to initialize pkgmgr interface";
262     return ci::AppInstaller::Result::UNKNOWN;
263   }
264   return RunInstallerWithPkgrmgr(pkgmgr, mode);
265 }
266
267 ci::AppInstaller::Result Reinstall(const bf::path& path,
268                                    const bf::path& delta_dir,
269                                    RequestResult mode = RequestResult::NORMAL) {
270   if (Install(path) != ci::AppInstaller::Result::OK) {
271     LOG(ERROR) << "Failed to install application. Cannot perform RDS";
272     return ci::AppInstaller::Result::UNKNOWN;
273   }
274   const char* argv[] = {"", "-r", delta_dir.c_str()};
275   TestPkgmgrInstaller pkgmgr_installer;
276   std::unique_ptr<ci::AppQueryInterface> query_interface =
277       CreateQueryInterface();
278   auto pkgmgr =
279       ci::PkgMgrInterface::Create(SIZEOFARRAY(argv), const_cast<char**>(argv),
280                                   &pkgmgr_installer, query_interface.get());
281   if (!pkgmgr) {
282     LOG(ERROR) << "Failed to initialize pkgmgr interface";
283     return ci::AppInstaller::Result::UNKNOWN;
284   }
285   return RunInstallerWithPkgrmgr(pkgmgr, mode);
286 }
287
288 ci::AppInstaller::Result DeltaInstall(const bf::path& path,
289     const bf::path& delta_package) {
290   if (Install(path) != ci::AppInstaller::Result::OK) {
291     LOG(ERROR) << "Failed to install application. Cannot perform RDS";
292     return ci::AppInstaller::Result::UNKNOWN;
293   }
294   return Install(delta_package);
295 }
296
297 ci::AppInstaller::Result Recover(const bf::path& recovery_file,
298                                  RequestResult mode = RequestResult::NORMAL) {
299   const char* argv[] = {"", "-b", recovery_file.c_str()};
300   TestPkgmgrInstaller pkgmgr_installer;
301   std::unique_ptr<ci::AppQueryInterface> query_interface =
302       CreateQueryInterface();
303   auto pkgmgr =
304       ci::PkgMgrInterface::Create(SIZEOFARRAY(argv), const_cast<char**>(argv),
305                                   &pkgmgr_installer, query_interface.get());
306   if (!pkgmgr) {
307     LOG(ERROR) << "Failed to initialize pkgmgr interface";
308     return ci::AppInstaller::Result::UNKNOWN;
309   }
310   return RunInstallerWithPkgrmgr(pkgmgr, mode);
311 }
312
313 }  // namespace
314
315 namespace common_installer {
316
317 class SmokeEnvironment : public testing::Environment {
318  public:
319   explicit SmokeEnvironment(const bf::path& home) : home_(home) {
320   }
321   void SetUp() override {
322     bs::error_code error;
323     bf::remove_all(home_ / kApplicationDirBackup, error);
324     bf::remove_all(home_ / KUserAppsDirBackup, error);
325     if (bf::exists(home_ / KUserAppsDir)) {
326       bf::rename(home_ / KUserAppsDir, home_ / KUserAppsDirBackup, error);
327       if (error)
328         LOG(ERROR) << "Failed to setup test environment. Does some previous"
329                    << " test crashed? Directory: "
330                    << (home_ / KUserAppsDirBackup) << " should not exist.";
331       assert(!error);
332     }
333     if (bf::exists(home_ / kApplicationDir)) {
334       bf::rename(home_ / kApplicationDir, home_ / kApplicationDirBackup, error);
335       if (error)
336         LOG(ERROR) << "Failed to setup test environment. Does some previous"
337                    << " test crashed? Directory: "
338                    << (home_ / kApplicationDirBackup) << " should not exist.";
339       assert(!error);
340     }
341   }
342   void TearDown() override {
343     bs::error_code error;
344     bf::remove_all(home_ / kApplicationDir, error);
345     bf::remove_all(home_ / KUserAppsDir, error);
346     if (bf::exists(home_ / KUserAppsDirBackup))
347       bf::rename(home_ / KUserAppsDirBackup, home_ / KUserAppsDir, error);
348     if (bf::exists(home_ / kApplicationDirBackup))
349       bf::rename(home_ / kApplicationDirBackup, home_ / kApplicationDir, error);
350   }
351
352  private:
353   bf::path home_;
354 };
355
356 class SmokeTest : public testing::Test {
357 };
358
359 TEST_F(SmokeTest, InstallationMode) {
360   bf::path path = kSmokePackagesDirectory / "InstallationMode.wgt";
361   std::string pkgid = "smokeapp03";
362   std::string appid = "smokeapp03.InstallationMode";
363   ASSERT_EQ(Install(path), ci::AppInstaller::Result::OK);
364   ValidatePackage(pkgid, appid);
365 }
366
367 TEST_F(SmokeTest, UpdateMode) {
368   bf::path path_old = kSmokePackagesDirectory / "UpdateMode.wgt";
369   bf::path path_new = kSmokePackagesDirectory / "UpdateMode_2.wgt";
370   std::string pkgid = "smokeapp04";
371   std::string appid = "smokeapp04.UpdateMode";
372   ASSERT_EQ(Update(path_old, path_new), ci::AppInstaller::Result::OK);
373   ValidatePackage(pkgid, appid);
374
375   ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/VERSION", "2\n"));
376 }
377
378 TEST_F(SmokeTest, DeinstallationMode) {
379   bf::path path = kSmokePackagesDirectory / "DeinstallationMode.wgt";
380   std::string pkgid = "smokeapp05";
381   std::string appid = "smokeapp05.DeinstallationMode";
382   ASSERT_EQ(Install(path),
383             ci::AppInstaller::Result::OK);
384   ASSERT_EQ(Uninstall(pkgid), ci::AppInstaller::Result::OK);
385   CheckPackageNonExistance(pkgid, appid);
386 }
387
388 TEST_F(SmokeTest, RDSMode) {
389   bf::path path = kSmokePackagesDirectory / "RDSMode.wgt";
390   bf::path delta_directory = kSmokePackagesDirectory / "delta_dir/";
391   std::string pkgid = "smokeapp11";
392   std::string appid = "smokeapp11.RDSMode";
393   ASSERT_EQ(Reinstall(path, delta_directory),
394             ci::AppInstaller::Result::OK);
395   ValidatePackage(pkgid, appid);
396
397   // Check delta modifications
398   bf::path root_path = ci::GetRootAppPath(false);
399   ASSERT_FALSE(bf::exists(root_path / pkgid / "res" / "wgt" / "DELETED"));
400   ASSERT_TRUE(bf::exists(root_path / pkgid / "res" / "wgt" / "ADDED"));
401   ValidateFileContentInPackage(pkgid, "res/wgt/MODIFIED", "2\n");
402 }
403
404 TEST_F(SmokeTest, DeltaMode) {
405   bf::path path = kSmokePackagesDirectory / "DeltaMode.wgt";
406   bf::path delta_package = kSmokePackagesDirectory / "DeltaMode.delta";
407   std::string pkgid = "smokeapp17";
408   std::string appid = "smokeapp17.DeltaMode";
409   ASSERT_EQ(DeltaInstall(path, delta_package),
410             ci::AppInstaller::Result::OK);
411   ValidatePackage(pkgid, appid);
412
413   // Check delta modifications
414   bf::path root_path = ci::GetRootAppPath(false);
415   ASSERT_FALSE(bf::exists(root_path / pkgid / "res" / "wgt" / "DELETED"));
416   ASSERT_TRUE(bf::exists(root_path / pkgid / "res" / "wgt" / "ADDED"));
417   ASSERT_TRUE(bf::exists(root_path / pkgid / "res" / "wgt" / "css" / "style.css"));  // NOLINT
418   ASSERT_TRUE(bf::exists(root_path / pkgid / "res" / "wgt" / "images" / "tizen_32.png"));  // NOLINT
419   ASSERT_TRUE(bf::exists(root_path / pkgid / "res" / "wgt" / "js" / "main.js"));
420   ValidateFileContentInPackage(pkgid, "res/wgt/MODIFIED", "version 2\n");
421 }
422
423 TEST_F(SmokeTest, RecoveryMode_ForInstallation) {
424   bf::path path = kSmokePackagesDirectory / "RecoveryMode_ForInstallation.wgt";
425   ASSERT_DEATH(Install(path, RequestResult::CRASH), ".*");
426
427   std::string pkgid = "smokeapp09";
428   std::string appid = "smokeapp09.RecoveryModeForInstallation";
429   bf::path recovery_file = FindRecoveryFile();
430   ASSERT_FALSE(recovery_file.empty());
431   ASSERT_EQ(Recover(recovery_file),
432       ci::AppInstaller::Result::OK);
433   CheckPackageNonExistance(pkgid, appid);
434 }
435
436 TEST_F(SmokeTest, RecoveryMode_ForUpdate) {
437   bf::path path_old = kSmokePackagesDirectory / "RecoveryMode_ForUpdate.wgt";
438   bf::path path_new = kSmokePackagesDirectory / "RecoveryMode_ForUpdate_2.wgt";
439   RemoveAllRecoveryFiles();
440   ASSERT_DEATH(Update(path_old, path_new, RequestResult::CRASH), ".*");
441
442   std::string pkgid = "smokeapp10";
443   std::string appid = "smokeapp10.RecoveryModeForUpdate";
444   bf::path recovery_file = FindRecoveryFile();
445   ASSERT_FALSE(recovery_file.empty());
446   ASSERT_EQ(Recover(recovery_file),
447             ci::AppInstaller::Result::OK);
448   ValidatePackage(pkgid, appid);
449
450   ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/VERSION", "1\n"));
451 }
452
453 TEST_F(SmokeTest, InstallationMode_GoodSignature) {
454   bf::path path = kSmokePackagesDirectory / "InstallationMode_GoodSignature.wgt";  // NOLINT
455   ASSERT_EQ(Install(path), ci::AppInstaller::Result::OK);
456 }
457
458 TEST_F(SmokeTest, InstallationMode_WrongSignature) {
459   bf::path path = kSmokePackagesDirectory / "InstallationMode_WrongSignature.wgt";  // NOLINT
460   ASSERT_EQ(Install(path), ci::AppInstaller::Result::ERROR);
461 }
462
463 TEST_F(SmokeTest, InstallationMode_Rollback) {
464   bf::path path = kSmokePackagesDirectory / "InstallationMode_Rollback.wgt";
465   std::string pkgid = "smokeapp06";
466   std::string appid = "smokeapp06.InstallationModeRollback";
467   ASSERT_EQ(Install(path, RequestResult::FAIL),
468             ci::AppInstaller::Result::ERROR);
469   CheckPackageNonExistance(pkgid, appid);
470 }
471
472 TEST_F(SmokeTest, UpdateMode_Rollback) {
473   bf::path path_old = kSmokePackagesDirectory / "UpdateMode_Rollback.wgt";
474   bf::path path_new = kSmokePackagesDirectory / "UpdateMode_Rollback_2.wgt";
475   std::string pkgid = "smokeapp07";
476   std::string appid = "smokeapp07.UpdateModeRollback";
477   ASSERT_EQ(Update(path_old, path_new, RequestResult::FAIL),
478                    ci::AppInstaller::Result::ERROR);
479   ValidatePackage(pkgid, appid);
480
481   ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/VERSION", "1\n"));
482 }
483
484 }  // namespace common_installer
485
486 int main(int argc,  char** argv) {
487   testing::InitGoogleTest(&argc, argv);
488   const char* directory = getenv("HOME");
489   if (!directory) {
490     LOG(ERROR) << "Cannot get $HOME value";
491     return 1;
492   }
493   testing::AddGlobalTestEnvironment(
494       new common_installer::SmokeEnvironment(directory));
495   return RUN_ALL_TESTS();
496 }