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