From f61c56c2ae73489fb31b95d551d558d07e961b13 Mon Sep 17 00:00:00 2001 From: Sangyoon Jang Date: Wed, 7 Dec 2016 15:42:00 +0900 Subject: [PATCH 01/16] Adjust to change of support ambient Requires: - https://review.tizen.org/gerrit/102774 Change-Id: I0f64c7eeb825fd82f25940c0829c4725356c2c9b Signed-off-by: Sangyoon Jang --- src/wgt/step/configuration/step_parse.cc | 8 ++++---- src/wgt/step/pkgmgr/step_generate_xml.cc | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/wgt/step/configuration/step_parse.cc b/src/wgt/step/configuration/step_parse.cc index d2908fa..e1f817c 100644 --- a/src/wgt/step/configuration/step_parse.cc +++ b/src/wgt/step/configuration/step_parse.cc @@ -347,10 +347,10 @@ bool StepParse::FillMainApplicationInfo(manifest_x* manifest) { SetApplicationXDefaults(application); if (has_watch_category) - application->ambient_support = + application->support_ambient = strdup(app_info->ambient_support() ? "true" : "false"); else - application->ambient_support = strdup("false"); + application->support_ambient = strdup("false"); application->package = strdup(app_info->package().c_str()); application->exec = @@ -398,7 +398,7 @@ bool StepParse::FillServiceApplicationInfo(manifest_x* manifest) { application->nodisplay = strdup("false"); application->taskmanage = strdup("true"); SetApplicationXDefaults(application); - application->ambient_support = strdup("false"); + application->support_ambient = strdup("false"); application->package = strdup(manifest->package); for (auto& pair : service_info.names()) { @@ -452,7 +452,7 @@ bool StepParse::FillWidgetApplicationInfo(manifest_x* manifest) { application->nodisplay = strdup("true"); application->taskmanage = strdup("false"); SetApplicationXDefaults(application); - application->ambient_support = strdup("false"); + application->support_ambient = strdup("false"); application->package = strdup(manifest->package); if (!app_widget.label.default_value.empty()) { diff --git a/src/wgt/step/pkgmgr/step_generate_xml.cc b/src/wgt/step/pkgmgr/step_generate_xml.cc index b34417b..a23e71e 100644 --- a/src/wgt/step/pkgmgr/step_generate_xml.cc +++ b/src/wgt/step/pkgmgr/step_generate_xml.cc @@ -161,9 +161,9 @@ bool WriteWidgetApplicationAttributesAndElements( void WriteWatchApplicationAttributes( xmlTextWriterPtr writer, application_x* app) { - if (app->ambient_support) + if (app->support_ambient) xmlTextWriterWriteAttribute(writer, BAD_CAST "ambient-support", - BAD_CAST app->ambient_support); + BAD_CAST app->support_ambient); } } // namespace -- 2.7.4 From 068064e3410b19831e47bba6ff7a27fbd2b67f31 Mon Sep 17 00:00:00 2001 From: Sangyoon Jang Date: Thu, 8 Dec 2016 15:29:31 +0900 Subject: [PATCH 02/16] Fix package icon path It should be under at shared/res for access by other applications. Change-Id: I1cdb835db681a98ea05987a52b9e55b23d5c8f17 Signed-off-by: Sangyoon Jang --- src/wgt/step/filesystem/step_wgt_patch_icons.cc | 53 ++++++++++++++++--------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/src/wgt/step/filesystem/step_wgt_patch_icons.cc b/src/wgt/step/filesystem/step_wgt_patch_icons.cc index ade10df..1a5b3cb 100644 --- a/src/wgt/step/filesystem/step_wgt_patch_icons.cc +++ b/src/wgt/step/filesystem/step_wgt_patch_icons.cc @@ -17,6 +17,33 @@ namespace { const char kDefaultIconPath[] = "/usr/share/wgt-backend/default.png"; +bool PatchIcon(icon_x* icon, const bf::path& dst_path) { + bs::error_code error; + bf::path icon_text(icon->text); + bf::path icon_path = dst_path; + if (strcmp(icon->lang, DEFAULT_LOCALE)) { + icon_path += "."; + icon_path += icon->lang; + } + if (icon_text.has_extension()) + icon_path += icon_text.extension(); + else + icon_path += ".png"; + + bf::copy_file(icon->text, icon_path, + bf::copy_option::overwrite_if_exists, error); + if (error) { + LOG(ERROR) << "Failed to move icon from " << icon->text << " to " + << icon_path; + return false; + } + if (icon->text) + free(const_cast(icon->text)); + icon->text = strdup(icon_path.c_str()); + + return true; +} + } // namespace namespace wgt { @@ -26,6 +53,12 @@ common_installer::Step::Status StepWgtPatchIcons::process() { bf::path common_icon_location = context_->pkg_path.get() / "shared" / "res"; bs::error_code error; bf::create_directories(common_icon_location, error); + for (icon_x* icon : + GListRange(context_->manifest_data.get()->icon)) { + bf::path icon_path = common_icon_location / context_->pkgid.get(); + if (!PatchIcon(icon, icon_path)) + return Status::ICON_ERROR; + } for (application_x* app : GListRange(context_->manifest_data.get()->application)) { if (strcmp(app->type, "webapp") != 0) @@ -33,27 +66,9 @@ common_installer::Step::Status StepWgtPatchIcons::process() { if (app->icon) { // edit icon->text and copy icons to common location for (auto& icon : GListRange(app->icon)) { - bf::path icon_text(icon->text); bf::path icon_path = common_icon_location / app->appid; - if (strcmp(icon->lang, DEFAULT_LOCALE)) { - icon_path += "."; - icon_path += icon->lang; - } - if (icon_text.has_extension()) - icon_path += icon_text.extension(); - else - icon_path += ".png"; - - bf::copy_file(icon->text, icon_path, - bf::copy_option::overwrite_if_exists, error); - if (error) { - LOG(ERROR) << "Failed to move icon from " << icon->text << " to " - << icon_path; + if (!PatchIcon(icon, icon_path)) return Status::ICON_ERROR; - } - if (icon->text) - free(const_cast(icon->text)); - icon->text = strdup(icon_path.c_str()); } } else { LOG(INFO) << "Application provides no icon. Using Tizen default icon."; -- 2.7.4 From 1ca8c3c81dcc7cf0ccb3c3ef05a2ea318d31cf46 Mon Sep 17 00:00:00 2001 From: jongmyeongko Date: Thu, 8 Dec 2016 21:42:38 +0900 Subject: [PATCH 03/16] change the name of StepChageOwner to StepChangeOwnershipAndPermission Change-Id: I6d6dc6bcf83b5be91b1bc4cb50aaf88a3a4e7742 Signed-off-by: jongmyeongko --- src/hybrid/hybrid_installer.cc | 16 ++++++++-------- src/wgt/wgt_installer.cc | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/hybrid/hybrid_installer.cc b/src/hybrid/hybrid_installer.cc index dbd4244..8da9357 100644 --- a/src/hybrid/hybrid_installer.cc +++ b/src/hybrid/hybrid_installer.cc @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -141,7 +141,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep( ci::Plugin::ActionType::Install); AddStep(); - AddStep(); + AddStep(); AddStep(); break; case ci::RequestType::Update: @@ -188,7 +188,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); - AddStep(); + AddStep(); AddStep(); break; case ci::RequestType::Uninstall: @@ -267,7 +267,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); - AddStep(); + AddStep(); AddStep(); break; case ci::RequestType::Recovery: @@ -336,7 +336,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep( ci::Plugin::ActionType::Install); AddStep(); - AddStep(); + AddStep(); AddStep(); break; case ci::RequestType::MountUpdate: @@ -382,7 +382,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); - AddStep(); + AddStep(); AddStep(); break; case ci::RequestType::ManifestDirectInstall: @@ -410,7 +410,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep( ci::Plugin::ActionType::Install); AddStep(); - AddStep(); + AddStep(); AddStep(); break; case ci::RequestType::ManifestDirectUpdate: @@ -441,7 +441,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); - AddStep(); + AddStep(); AddStep(); break; case ci::RequestType::EnablePkg: diff --git a/src/wgt/wgt_installer.cc b/src/wgt/wgt_installer.cc index 63d8906..7ca8306 100755 --- a/src/wgt/wgt_installer.cc +++ b/src/wgt/wgt_installer.cc @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -146,7 +146,7 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep( ci::Plugin::ActionType::Install); AddStep(); - AddStep(); + AddStep(); AddStep(); break; } @@ -190,7 +190,7 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); - AddStep(); + AddStep(); AddStep(); break; } @@ -238,7 +238,7 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep(); AddStep(); - AddStep(); + AddStep(); AddStep(); break; } @@ -286,7 +286,7 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); - AddStep(); + AddStep(); AddStep(); break; } @@ -352,7 +352,7 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep( ci::Plugin::ActionType::Install); - AddStep(); + AddStep(); AddStep(); break; } @@ -393,7 +393,7 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); - AddStep(); + AddStep(); AddStep(); break; } @@ -418,7 +418,7 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep(); AddStep(ci::Plugin::ActionType::Install); - AddStep(); + AddStep(); AddStep(); break; } @@ -447,7 +447,7 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); - AddStep(); + AddStep(); AddStep(); break; } @@ -488,7 +488,7 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep(); AddStep(ci::Plugin::ActionType::Upgrade); - AddStep(); + AddStep(); AddStep(); break; } -- 2.7.4 From 156b74e8dd6cc21e740cca99780d8f58b9aa6a98 Mon Sep 17 00:00:00 2001 From: jongmyeongko Date: Sun, 4 Dec 2016 18:26:07 +0900 Subject: [PATCH 04/16] apply new features : partial install/update/uninstall, force-clean Submit with: https://review.tizen.org/gerrit/#/c/103359/ https://review.tizen.org/gerrit/#/c/103361/ Change-Id: I63a24f916d1e8b8a379d579962513ba1551904dc Signed-off-by: jongmyeongko --- src/hybrid/hybrid_installer.cc | 61 +++++++++++++++++++++++++++++++++++++++++- src/wgt/wgt_installer.cc | 54 +++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/src/hybrid/hybrid_installer.cc b/src/hybrid/hybrid_installer.cc index 8da9357..ccdd3f7 100644 --- a/src/hybrid/hybrid_installer.cc +++ b/src/hybrid/hybrid_installer.cc @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -195,6 +196,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(pkgmgr_); AddStep(); AddStep(); + AddStep(); AddStep( ci::configuration::StepParseManifest::ManifestLocation::INSTALLED, ci::configuration::StepParseManifest::StoreLocation::NORMAL); @@ -415,7 +417,6 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) break; case ci::RequestType::ManifestDirectUpdate: AddStep(pkgmgr_); - AddStep(); AddStep( ci::configuration::StepParseManifest::ManifestLocation::INSTALLED, ci::configuration::StepParseManifest::StoreLocation::NORMAL); @@ -444,6 +445,64 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); break; + case ci::RequestType::ManifestPartialInstall: { + AddStep(pkgmgr_); + AddStep( + ci::configuration::StepParseManifest::ManifestLocation::INSTALLED, + ci::configuration::StepParseManifest::StoreLocation::NORMAL); + AddStep(); + AddStep( + wgt::configuration::StepParse::ConfigLocation::INSTALLED, true); + AddStep(); + AddStep(); + AddStep(); + AddStep(); + AddStep(); + AddStep( + ci::Plugin::ActionType::Install); + AddStep(); + AddStep(); + break; + } + case ci::RequestType::ManifestPartialUpdate: { + AddStep(pkgmgr_); + AddStep( + ci::configuration::StepParseManifest::ManifestLocation::INSTALLED, + ci::configuration::StepParseManifest::StoreLocation::NORMAL); + AddStep(); + AddStep( + wgt::configuration::StepParse::ConfigLocation::INSTALLED, true); + AddStep(); + AddStep(); + AddStep( + ci::configuration::StepParseManifest::ManifestLocation::INSTALLED, + ci::configuration::StepParseManifest::StoreLocation::BACKUP); + AddStep(); + AddStep(); + AddStep(); + AddStep( + ci::Plugin::ActionType::Upgrade); + AddStep(); + break; + } + case ci::RequestType::PartialUninstall: { + AddStep(pkgmgr_); + AddStep(); + AddStep(); + AddStep(); + AddStep( + ci::configuration::StepParseManifest::ManifestLocation::INSTALLED, + ci::configuration::StepParseManifest::StoreLocation::NORMAL); + AddStep( + ci::Plugin::ActionType::Uninstall); + AddStep(); + AddStep(); + AddStep(); + AddStep(); + AddStep(); + AddStep(); + break; + } case ci::RequestType::EnablePkg: AddStep(pkgmgr_); AddStep( diff --git a/src/wgt/wgt_installer.cc b/src/wgt/wgt_installer.cc index 7ca8306..e087ea7 100755 --- a/src/wgt/wgt_installer.cc +++ b/src/wgt/wgt_installer.cc @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -198,6 +199,7 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(pkgmgr_); AddStep(); AddStep(); + AddStep(); AddStep( ci::configuration::StepParseManifest::ManifestLocation::INSTALLED, ci::configuration::StepParseManifest::StoreLocation::NORMAL); @@ -418,6 +420,7 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep(); AddStep(ci::Plugin::ActionType::Install); + AddStep(); AddStep(); AddStep(); break; @@ -515,6 +518,57 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep(); AddStep(ci::Plugin::ActionType::Upgrade); + case ci::RequestType::ManifestPartialInstall: { + AddStep(pkgmgr_); + AddStep( + wgt::configuration::StepParse::ConfigLocation::INSTALLED, true); + AddStep(); + AddStep( + ci::security::StepPrivilegeCompatibility::InternalPrivType::WGT); + AddStep(); + AddStep(); + AddStep(); + AddStep(); + AddStep(ci::Plugin::ActionType::Install); + AddStep(); + AddStep(); + break; + } + case ci::RequestType::ManifestPartialUpdate: { + AddStep(pkgmgr_); + AddStep( + wgt::configuration::StepParse::ConfigLocation::INSTALLED, true); + AddStep(); + AddStep( + ci::configuration::StepParseManifest::ManifestLocation::INSTALLED, + ci::configuration::StepParseManifest::StoreLocation::BACKUP); + AddStep( + ci::security::StepPrivilegeCompatibility::InternalPrivType::WGT); + AddStep(); + AddStep(); + AddStep(); + AddStep(); + AddStep( + ci::Plugin::ActionType::Upgrade); + AddStep(); + break; + } + case ci::RequestType::PartialUninstall: { + AddStep(pkgmgr_); + AddStep(); + AddStep(); + AddStep(); + AddStep( + ci::configuration::StepParseManifest::ManifestLocation::INSTALLED, + ci::configuration::StepParseManifest::StoreLocation::NORMAL); + AddStep(); + AddStep(); + AddStep( + ci::Plugin::ActionType::Uninstall); + AddStep(); + AddStep(); + AddStep(); + AddStep(); break; } case ci::RequestType::Move: { -- 2.7.4 From 2263e92bcc42520c944c66fae97170d9b318a27d Mon Sep 17 00:00:00 2001 From: jongmyeongko Date: Fri, 9 Dec 2016 17:21:11 +0900 Subject: [PATCH 05/16] fix missing brace Change-Id: I1d2d6137481f9a2a53fbfd2823fa5398a2375387 Signed-off-by: jongmyeongko --- src/wgt/wgt_installer.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wgt/wgt_installer.cc b/src/wgt/wgt_installer.cc index e087ea7..4623c28 100755 --- a/src/wgt/wgt_installer.cc +++ b/src/wgt/wgt_installer.cc @@ -518,6 +518,7 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep(); AddStep(ci::Plugin::ActionType::Upgrade); + } case ci::RequestType::ManifestPartialInstall: { AddStep(pkgmgr_); AddStep( -- 2.7.4 From 5bb13da02f9fa8e1f50f7abe1b00aba229f20c90 Mon Sep 17 00:00:00 2001 From: Junghyun Yeon Date: Tue, 13 Dec 2016 15:15:21 +0900 Subject: [PATCH 06/16] Add missed 'break' Change-Id: Iea19cafbf53d996c1b0123bb9bafd230a105a006 Signed-off-by: Junghyun Yeon --- src/wgt/wgt_installer.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wgt/wgt_installer.cc b/src/wgt/wgt_installer.cc index 4623c28..5929586 100755 --- a/src/wgt/wgt_installer.cc +++ b/src/wgt/wgt_installer.cc @@ -518,6 +518,7 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep(); AddStep(ci::Plugin::ActionType::Upgrade); + break; } case ci::RequestType::ManifestPartialInstall: { AddStep(pkgmgr_); -- 2.7.4 From eaf100f2bde807947ca2df99d804a1d5f94604cd Mon Sep 17 00:00:00 2001 From: Piotr Dabrowski Date: Fri, 16 Dec 2016 15:16:37 +0100 Subject: [PATCH 07/16] Create private tmp directory in skel Submit together: https://review.tizen.org/gerrit/#/c/105467/ https://review.tizen.org/gerrit/#/c/105468/ Change-Id: I7454b05b3d83bac2939594f0d82e8d22d457789c --- src/hybrid/hybrid_installer.cc | 13 +++++++++---- src/hybrid/shared_dirs.h | 20 ++++++++++++++++++++ src/wgt/shared_dirs.h | 20 ++++++++++++++++++++ src/wgt/wgt_installer.cc | 13 +++++++++---- 4 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 src/hybrid/shared_dirs.h create mode 100644 src/wgt/shared_dirs.h diff --git a/src/hybrid/hybrid_installer.cc b/src/hybrid/hybrid_installer.cc index ccdd3f7..ed90220 100644 --- a/src/hybrid/hybrid_installer.cc +++ b/src/hybrid/hybrid_installer.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "hybrid/hybrid_installer.h" +#include "hybrid/shared_dirs.h" #include #include @@ -141,7 +142,8 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( ci::Plugin::ActionType::Install); - AddStep(); + AddStep( + wgt::filesystem::HybridAdditionalSharedDirs); AddStep(); AddStep(); break; @@ -337,7 +339,8 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( ci::Plugin::ActionType::Install); - AddStep(); + AddStep( + wgt::filesystem::HybridAdditionalSharedDirs); AddStep(); AddStep(); break; @@ -411,7 +414,8 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( ci::Plugin::ActionType::Install); - AddStep(); + AddStep( + wgt::filesystem::HybridAdditionalSharedDirs); AddStep(); AddStep(); break; @@ -460,7 +464,8 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( ci::Plugin::ActionType::Install); - AddStep(); + AddStep( + wgt::filesystem::HybridAdditionalSharedDirs); AddStep(); break; } diff --git a/src/hybrid/shared_dirs.h b/src/hybrid/shared_dirs.h new file mode 100644 index 0000000..a6a4683 --- /dev/null +++ b/src/hybrid/shared_dirs.h @@ -0,0 +1,20 @@ +// Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by an apache-2.0 license that can be +// found in the LICENSE file. + +#ifndef HYBRID_SHARED_DIRS_H_ +#define HYBRID_SHARED_DIRS_H_ + +#include + +namespace wgt { +namespace filesystem { + +const std::vector HybridAdditionalSharedDirs = { + {"tmp"}, +}; + +} // namespace filesystem +} // namespace wgt + +#endif // HYBRID_SHARED_DIRS_H_ diff --git a/src/wgt/shared_dirs.h b/src/wgt/shared_dirs.h new file mode 100644 index 0000000..2b75e91 --- /dev/null +++ b/src/wgt/shared_dirs.h @@ -0,0 +1,20 @@ +// Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by an apache-2.0 license that can be +// found in the LICENSE file. + +#ifndef HYBRID_SHARED_DIRS_H_ +#define HYBRID_SHARED_DIRS_H_ + +#include + +namespace wgt { +namespace filesystem { + +const std::vector WgtAdditionalSharedDirs = { + {"tmp"}, +}; + +} // namespace filesystem +} // namespace wgt + +#endif // HYBRID_SHARED_DIRS_H_ diff --git a/src/wgt/wgt_installer.cc b/src/wgt/wgt_installer.cc index 5929586..cef4e4c 100755 --- a/src/wgt/wgt_installer.cc +++ b/src/wgt/wgt_installer.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "wgt/wgt_installer.h" +#include "wgt/shared_dirs.h" #include @@ -146,7 +147,8 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep( ci::Plugin::ActionType::Install); - AddStep(); + AddStep( + wgt::filesystem::WgtAdditionalSharedDirs); AddStep(); AddStep(); break; @@ -348,7 +350,8 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep(); AddStep(); - AddStep(); + AddStep( + wgt::filesystem::WgtAdditionalSharedDirs); AddStep(); AddStep(); AddStep(); @@ -420,7 +423,8 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep(); AddStep(ci::Plugin::ActionType::Install); - AddStep(); + AddStep( + wgt::filesystem::WgtAdditionalSharedDirs); AddStep(); AddStep(); break; @@ -532,7 +536,8 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep(); AddStep(ci::Plugin::ActionType::Install); - AddStep(); + AddStep( + wgt::filesystem::WgtAdditionalSharedDirs); AddStep(); break; } -- 2.7.4 From 8c06a60991d120d64f3147bce724dc1c348276c0 Mon Sep 17 00:00:00 2001 From: Sangyoon Jang Date: Fri, 16 Dec 2016 18:54:40 +0900 Subject: [PATCH 08/16] Fix generating manifest when install hybrid package Currently, wgt-backend generate manifest file using manifest_x which is parsed by manifest parser. This data contains only the parser knows. However, the manifest file for native packages have many element which are not parsed manifest parser but parsed by plugin parsers. So the manifest generated by wgt-backend loses some elements. This patch will make the manifest have elements as it was before. StepGenerateXml from hybrid installer will merge original native manifest into generated widget manifest(config.xml). Change-Id: Iea0a68c3b695243248469f0794f62f7bec4fa757 Signed-off-by: Sangyoon Jang --- src/hybrid/CMakeLists.txt | 4 +- src/hybrid/hybrid_installer.cc | 22 ++-- src/hybrid/step/pkgmgr/step_generate_xml.cc | 189 ++++++++++++++++++++++++++++ src/hybrid/step/pkgmgr/step_generate_xml.h | 45 +++++++ 4 files changed, 252 insertions(+), 8 deletions(-) create mode 100644 src/hybrid/step/pkgmgr/step_generate_xml.cc create mode 100644 src/hybrid/step/pkgmgr/step_generate_xml.h diff --git a/src/hybrid/CMakeLists.txt b/src/hybrid/CMakeLists.txt index e65d713..016c783 100644 --- a/src/hybrid/CMakeLists.txt +++ b/src/hybrid/CMakeLists.txt @@ -1,10 +1,12 @@ AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} HYBRID_SRCS) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/step/configuration HYBRID_STEP_CONFIGURATION_SRCS) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/step/encryption HYBRID_STEP_ENCRYPTION_SRCS) +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/step/pkgmgr HYBRID_STEP_PKGMGR_SRCS) ADD_LIBRARY(${TARGET_LIBNAME_HYBRID} STATIC ${HYBRID_SRCS} ${HYBRID_STEP_CONFIGURATION_SRCS} - ${HYBRID_STEP_ENCRYPTION_SRCS}) + ${HYBRID_STEP_ENCRYPTION_SRCS} + ${HYBRID_STEP_PKGMGR_SRCS}) TARGET_INCLUDE_DIRECTORIES(${TARGET_LIBNAME_HYBRID} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../") diff --git a/src/hybrid/hybrid_installer.cc b/src/hybrid/hybrid_installer.cc index ed90220..5aef5e2 100644 --- a/src/hybrid/hybrid_installer.cc +++ b/src/hybrid/hybrid_installer.cc @@ -80,6 +80,7 @@ #include "hybrid/step/configuration/step_merge_tpk_config.h" #include "hybrid/step/configuration/step_stash_tpk_config.h" #include "hybrid/step/encryption/step_encrypt_resources.h" +#include "hybrid/step/pkgmgr/step_generate_xml.h" #include "wgt/step/configuration/step_parse.h" #include "wgt/step/encryption/step_remove_encryption_data.h" #include "wgt/step/filesystem/step_copy_preview_icons.h" @@ -112,7 +113,6 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( wgt::configuration::StepParse::ConfigLocation::RESOURCE_WGT, true); - AddStep(); AddStep(); AddStep(); AddStep(); @@ -138,6 +138,8 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); + AddStep(); + AddStep(); AddStep(); AddStep(); AddStep( @@ -156,7 +158,6 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( wgt::configuration::StepParse::ConfigLocation::RESOURCE_WGT, true); - AddStep(); AddStep(); AddStep(); AddStep(); @@ -188,6 +189,8 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); + AddStep(); + AddStep(); AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); @@ -238,7 +241,6 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( wgt::configuration::StepParse::ConfigLocation::RESOURCE_WGT, true); - AddStep(); AddStep(); AddStep(); AddStep(); @@ -268,6 +270,8 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); + AddStep(); + AddStep(); AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); @@ -309,7 +313,6 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( wgt::configuration::StepParse::ConfigLocation::RESOURCE_WGT, true); - AddStep(); AddStep(); AddStep(); AddStep(); @@ -335,6 +338,8 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); + AddStep(); + AddStep(); AddStep(); AddStep(); AddStep( @@ -353,7 +358,6 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( wgt::configuration::StepParse::ConfigLocation::RESOURCE_WGT, true); - AddStep(); AddStep(); AddStep(); AddStep(); @@ -384,6 +388,8 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); + AddStep(); + AddStep(); AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); @@ -399,7 +405,6 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( wgt::configuration::StepParse::ConfigLocation::INSTALLED, true); - AddStep(); AddStep(); AddStep(); AddStep(); @@ -410,6 +415,8 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); + AddStep(); + AddStep(); AddStep(); AddStep(); AddStep( @@ -427,7 +434,6 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep( wgt::configuration::StepParse::ConfigLocation::INSTALLED, true); - AddStep(); AddStep(); AddStep(); AddStep(); @@ -443,6 +449,8 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); + AddStep(); + AddStep(); AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); diff --git a/src/hybrid/step/pkgmgr/step_generate_xml.cc b/src/hybrid/step/pkgmgr/step_generate_xml.cc new file mode 100644 index 0000000..7d6f42d --- /dev/null +++ b/src/hybrid/step/pkgmgr/step_generate_xml.cc @@ -0,0 +1,189 @@ +// Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by an apache-2.0 license that can be +// found in the LICENSE file. + +#include "hybrid/step/pkgmgr/step_generate_xml.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "hybrid/hybrid_backend_data.h" + +namespace ci = common_installer; +namespace bf = boost::filesystem; +namespace bs = boost::system; + +namespace { + +const char kXmlXPathAppExpr[] = + "//*[local-name()='ui-application' or local-name()='service-application' " + "or local-name()='widget-application' or local-name()='watch-application']"; +const std::vector kBlackListNodes = { + {"author"}, + {"description"}, + {"profile"}, +}; +const std::vector kNeedMergeNodes = { + {"manifest"}, + {"privileges"}, +}; +const std::vector> kApplicationNodeNames = { + {"ui-application", "uiapp"}, + {"service-application", "svcapp"}, + {"widget-application", "widgetapp"}, + {"watch-application", "watchapp"}, +}; + +} // namespace + +namespace hybrid { +namespace pkgmgr { + +bool StepGenerateXml::LoadXmlDocument(const bf::path& wgt_xml_path, + const bf::path& tpk_xml_path) { + // trim blanks + xmlKeepBlanksDefault(0); + wgt_doc_ = xmlParseFile(wgt_xml_path.string().c_str()); + if (!wgt_doc_) { + LOG(ERROR) << "Failed to parse file: " << wgt_xml_path; + return false; + } + tpk_doc_ = xmlParseFile(tpk_xml_path.string().c_str()); + if (!tpk_doc_) { + LOG(ERROR) << "Failed to parse file: " << tpk_xml_path; + return false; + } + return true; +} + +xmlNodePtr StepGenerateXml::GetXmlNode(const xmlDocPtr doc, + const std::string& name, const std::string& attr, + const std::string& attr_val) { + xmlXPathContextPtr ctxt = xmlXPathNewContext(doc); + if (!ctxt) { + LOG(ERROR) << "Failed to create XPath context"; + return nullptr; + } + + std::string expr = "//*[local-name()='" + name + "']"; + if (!attr.empty() && !attr_val.empty()) + expr.append("[@" + attr + "='" + attr_val + "']"); + + xmlXPathObjectPtr obj = + xmlXPathEvalExpression((const xmlChar*)expr.c_str(), ctxt); + if (!obj || !obj->nodesetval || !obj->nodesetval->nodeNr) { + xmlXPathFreeContext(ctxt); + return nullptr; + } + + xmlNodePtr node = obj->nodesetval->nodeTab[0]; + xmlXPathFreeObject(obj); + xmlXPathFreeContext(ctxt); + + return node; +} + +void StepGenerateXml::MergeXmlNode(xmlNodePtr node1, xmlNodePtr node2) { + xmlNodePtr last = xmlGetLastChild(node1); + xmlNodePtr next; + // merge node2's child into node1 + for (xmlNodePtr cur = node2->children; cur; cur = next) { + next = cur->next; + if (std::find(kBlackListNodes.begin(), kBlackListNodes.end(), + reinterpret_cast(cur->name)) != kBlackListNodes.end()) + continue; + // to avoid duplicate + if (std::find(kNeedMergeNodes.begin(), kNeedMergeNodes.end(), + reinterpret_cast(cur->name)) != kNeedMergeNodes.end()) + continue; + xmlAddNextSibling(last, cur); + last = last->next; + } +} + +void StepGenerateXml::SetXmlNodeAttribute(xmlNodePtr node, + const std::string& attr, const std::string& attr_val) { + xmlSetProp(node, reinterpret_cast(attr.c_str()), + reinterpret_cast(attr_val.c_str())); +} + +ci::Step::Status StepGenerateXml::process() { + bf::path wgt_xml_path = context_->xml_path.get(); + bf::path tpk_xml_path = context_->pkg_path.get() / "tizen-manifest.xml"; + + if (!LoadXmlDocument(wgt_xml_path, tpk_xml_path)) + return Step::Status::MANIFEST_ERROR; + + for (auto& entry : kNeedMergeNodes) { + xmlNodePtr tpk_node = GetXmlNode(tpk_doc_, entry); + if (tpk_node == nullptr) + continue; + // TODO(s89.jang): If cannot find node from wgt doc, merge tpk node itself + // instead of merging its child nodes. + xmlNodePtr wgt_node = GetXmlNode(wgt_doc_, entry); + MergeXmlNode(wgt_node, tpk_node); + } + + // set app's executable path + HybridBackendData* data = + static_cast(context_->backend_data.get()); + manifest_x* tpk_data = data->tpk_manifest_data.get(); + for (application_x* app : + GListRange(tpk_data->application)) { + auto r = std::find_if(kApplicationNodeNames.begin(), + kApplicationNodeNames.end(), + [app](const std::pair& e) { + return strcmp(e.second.c_str(), app->component_type) == 0; + }); + if (r == kApplicationNodeNames.end()) { + LOG(WARNING) << "Cannot find component type!"; + continue; + } + xmlNodePtr node = GetXmlNode(wgt_doc_, (*r).first, "appid", app->appid); + SetXmlNodeAttribute(node, "exec", app->exec); + } + + if (xmlSaveFormatFile(wgt_xml_path.string().c_str(), wgt_doc_, 1) == -1) { + LOG(ERROR) << "Cannot write xml file"; + return Step::Status::MANIFEST_ERROR; + } + + if (pkgmgr_parser_check_manifest_validation( + wgt_xml_path.string().c_str()) != 0) { + LOG(ERROR) << "Merged manifest is not valid"; + return Step::Status::MANIFEST_ERROR; + } + + return Status::OK; +} + +ci::Step::Status StepGenerateXml::precheck() { + bf::path wgt_xml_path = context_->xml_path.get(); + if (!bf::exists(wgt_xml_path)) { + LOG(ERROR) << "Converted config file not found: " << wgt_xml_path; + return Step::Status::MANIFEST_ERROR; + } + + bf::path tpk_xml_path = context_->pkg_path.get() / "tizen-manifest.xml"; + if (!bf::exists(tpk_xml_path)) { + LOG(ERROR) << "Native manifest file not found: " << tpk_xml_path; + return Step::Status::MANIFEST_ERROR; + } + + return Status::OK; +} + +} // namespace pkgmgr +} // namespace hybrid diff --git a/src/hybrid/step/pkgmgr/step_generate_xml.h b/src/hybrid/step/pkgmgr/step_generate_xml.h new file mode 100644 index 0000000..b2a0f6e --- /dev/null +++ b/src/hybrid/step/pkgmgr/step_generate_xml.h @@ -0,0 +1,45 @@ +// Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by an apache-2.0 license that can be +// found in the LICENSE file. + +#ifndef HYBRID_STEP_PKGMGR_STEP_GENERATE_XML_H_ +#define HYBRID_STEP_PKGMGR_STEP_GENERATE_XML_H_ + +#include + +#include +#include +#include + +#include + +namespace hybrid { +namespace pkgmgr { + +class StepGenerateXml : public common_installer::Step { + public: + using Step::Step; + Status process() override; + Status clean() override { return Status::OK; } + Status undo() override { return Status::OK; } + Status precheck() override; + + private: + bool LoadXmlDocument(const boost::filesystem::path& wgt_xml_path, + const boost::filesystem::path& tpk_xml_path); + xmlNodePtr GetXmlNode(const xmlDocPtr doc, const std::string& name, + const std::string& attr = {}, const std::string& attr_val = {}); + void MergeXmlNode(xmlNodePtr node1, xmlNodePtr node2); + void SetXmlNodeAttribute(xmlNodePtr node, const std::string& attr, + const std::string& attr_val); + + xmlDocPtr wgt_doc_; + xmlDocPtr tpk_doc_; + + STEP_NAME(GenerateXml2); +}; + +} // namespace pkgmgr +} // namespace hybrid + +#endif // HYBRID_STEP_PKGMGR_STEP_GENERATE_XML_H_ -- 2.7.4 From 162f3c411544eda1969df2549bb2c0f0f4d3268b Mon Sep 17 00:00:00 2001 From: Sangyoon Jang Date: Thu, 22 Dec 2016 14:54:25 +0900 Subject: [PATCH 09/16] Fix mount install failure on global installation Security registration should be done before creating per user storage directories because StepCreatePerUserStorage calls security_manager_paths_register() and it needs security context. Change-Id: I90c846f55c5ba2cdc294c1a762edb9d97cc76b50 Signed-off-by: Sangyoon Jang --- src/wgt/wgt_installer.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wgt/wgt_installer.cc b/src/wgt/wgt_installer.cc index cef4e4c..1f6c18c 100755 --- a/src/wgt/wgt_installer.cc +++ b/src/wgt/wgt_installer.cc @@ -350,13 +350,13 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); AddStep(); AddStep(); - AddStep( - wgt::filesystem::WgtAdditionalSharedDirs); AddStep(); AddStep(); AddStep(); AddStep( ci::Plugin::ActionType::Install); + AddStep( + wgt::filesystem::WgtAdditionalSharedDirs); AddStep(); AddStep(); break; -- 2.7.4 From e0ef309d989019f5c9904bcaa717e5e91985f625 Mon Sep 17 00:00:00 2001 From: Junghyun Yeon Date: Thu, 22 Dec 2016 16:17:17 +0900 Subject: [PATCH 10/16] Remove cleardata feature - Cleardata will be handled by pkg_cleardata so remove features related with it from backend Related changes: [pkgmgr-server] : https://review.tizen.org/gerrit/106486 [pkgmgr-tool] : https://review.tizen.org/gerrit/106485 [app-installers] : https://review.tizen.org/gerrit/105691 [tpk-backend] : https://review.tizen.org/gerrit/106603 Change-Id: I5df69fbe6416fae0188156b0af7d9657951ec32b Signed-off-by: Junghyun Yeon --- src/hybrid/hybrid_installer.cc | 8 -------- src/unit_tests/smoke_test.cc | 24 ------------------------ src/unit_tests/test_samples/smoke/ClearMode.wgt | Bin 37643 -> 0 bytes src/wgt/wgt_installer.cc | 9 --------- 4 files changed, 41 deletions(-) delete mode 100644 src/unit_tests/test_samples/smoke/ClearMode.wgt diff --git a/src/hybrid/hybrid_installer.cc b/src/hybrid/hybrid_installer.cc index 5aef5e2..af34823 100644 --- a/src/hybrid/hybrid_installer.cc +++ b/src/hybrid/hybrid_installer.cc @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -296,13 +295,6 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); break; - case ci::RequestType::Clear: - AddStep(pkgmgr_); - AddStep( - ci::configuration::StepParseManifest::ManifestLocation::INSTALLED, - ci::configuration::StepParseManifest::StoreLocation::NORMAL); - AddStep(); - break; case ci::RequestType::MountInstall: AddStep(pkgmgr_); AddStep(); diff --git a/src/unit_tests/smoke_test.cc b/src/unit_tests/smoke_test.cc index 7b185d3..22ad465 100644 --- a/src/unit_tests/smoke_test.cc +++ b/src/unit_tests/smoke_test.cc @@ -366,13 +366,6 @@ ci::AppInstaller::Result DeltaInstall(const bf::path& path, return Install(delta_package, type); } -ci::AppInstaller::Result Clear(const std::string& pkgid, - PackageType type, - RequestResult mode = RequestResult::NORMAL) { - const char* argv[] = {"", "-c", pkgid.c_str(), "-u", kTestUserIdStr.c_str()}; - return CallBackend(SIZEOFARRAY(argv), argv, type, mode); -} - ci::AppInstaller::Result EnablePackage(const std::string& pkgid, PackageType type, RequestResult mode = RequestResult::NORMAL) { @@ -549,23 +542,6 @@ TEST_F(SmokeTest, DisablePkg) { ValidatePackageFS(pkgid, {appid}); } -TEST_F(SmokeTest, ClearMode) { - bf::path path = kSmokePackagesDirectory / "ClearMode.wgt"; - std::string pkgid = "smokeapp20"; - std::string appid = "smokeapp20.ClearMode"; - ASSERT_EQ(Install(path, PackageType::WGT), - ci::AppInstaller::Result::OK); - bs::error_code error; - bf::create_directory(GetPackageRoot(pkgid) / "data" / "dir", error); - ASSERT_FALSE(error); - ASSERT_TRUE(TouchFile(GetPackageRoot(pkgid) / "data" / "dir" / "file")); - ASSERT_TRUE(TouchFile(GetPackageRoot(pkgid) / "data" / "file")); - ASSERT_EQ(Clear(pkgid, PackageType::WGT), ci::AppInstaller::Result::OK); - ValidatePackage(pkgid, {appid}); - ASSERT_FALSE(bf::exists(GetPackageRoot(pkgid) / "data" / "dir" / "file")); - ASSERT_FALSE(bf::exists(GetPackageRoot(pkgid) / "res" / "file")); -} - TEST_F(SmokeTest, DeltaMode) { bf::path path = kSmokePackagesDirectory / "DeltaMode.wgt"; bf::path delta_package = kSmokePackagesDirectory / "DeltaMode.delta"; diff --git a/src/unit_tests/test_samples/smoke/ClearMode.wgt b/src/unit_tests/test_samples/smoke/ClearMode.wgt deleted file mode 100644 index f6bbe1866f79e27908588ef66cbb4cd6ce8d100b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37643 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 diff --git a/src/wgt/wgt_installer.cc b/src/wgt/wgt_installer.cc index 1f6c18c..b1dcd43 100755 --- a/src/wgt/wgt_installer.cc +++ b/src/wgt/wgt_installer.cc @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -313,14 +312,6 @@ WgtInstaller::WgtInstaller(ci::PkgMgrPtr pkgrmgr) AddStep(); break; } - case ci::RequestType::Clear: { - AddStep(pkgmgr_); - AddStep( - ci::configuration::StepParseManifest::ManifestLocation::INSTALLED, - ci::configuration::StepParseManifest::StoreLocation::NORMAL); - AddStep(); - break; - } case ci::RequestType::MountInstall: { AddStep(pkgmgr_); AddStep(); -- 2.7.4 From 35c96f5534a26cd337f61eb54c6db0d2110e1459 Mon Sep 17 00:00:00 2001 From: Sangyoon Jang Date: Fri, 23 Dec 2016 14:28:03 +0900 Subject: [PATCH 11/16] Fix smoketest Smoketest should test both global/local case. This patch make smoketest can test global/local test, but test only global case because of current platform policy. It should be test local case also later. Change-Id: Ib9cb9754058a34a6fc18ba845c872ed819cd3050 Signed-off-by: Sangyoon Jang --- src/unit_tests/smoke_test.cc | 344 ++++++++++++++++++++++++++----------------- 1 file changed, 210 insertions(+), 134 deletions(-) diff --git a/src/unit_tests/smoke_test.cc b/src/unit_tests/smoke_test.cc index 22ad465..f86a813 100644 --- a/src/unit_tests/smoke_test.cc +++ b/src/unit_tests/smoke_test.cc @@ -42,20 +42,27 @@ namespace ci = common_installer; namespace { -const uid_t kTestUserId = tzplatform_getuid(TZ_SYS_DEFAULT_USER); -const gid_t kTestGroupId = tzplatform_getgid(TZ_SYS_DEFAULT_USER); -const char kTestGroupName[] = "system_share"; +const uid_t kGlobalUserUid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER); +const uid_t kGlobalUserGid = tzplatform_getgid(TZ_SYS_GLOBALAPP_USER); +const uid_t kTestUserId = kGlobalUserUid; +const gid_t kTestGroupId = kGlobalUserGid; +const char kSystemShareGroupName[] = "system_share"; const std::string kTestUserIdStr = std::to_string(kTestUserId); const bf::path kSmokePackagesDirectory = "/usr/share/wgt-backend-ut/test_samples/smoke/"; -const char kApplicationDir[] = ".applications"; -const char kApplicationDirBackup[] = ".applications.bck"; -const char KUserAppsDir[] = "apps_rw"; -const char KUserAppsDirBackup[] = "apps_rw.bck"; -const char kUserDataBaseDir[] = "/opt/dbspace/user"; +// common entries +const std::vector kDBEntries = { + {".pkgmgr_parser.db"}, + {".pkgmgr_parser.db-journal"}, + {".pkgmgr_cert.db"}, + {".pkgmgr_cert.db-journal"}, +}; +// globaluser entries +const char kGlobalManifestDir[] = "/opt/share/packages"; +const char kSkelDir[] = "/etc/skel/apps_rw"; enum class RequestResult { NORMAL, @@ -148,15 +155,15 @@ bf::path FindRecoveryFile() { return {}; } -bf::path GetPackageRoot(const std::string& pkgid) { - bf::path root_path = ci::GetRootAppPath(false, kTestUserId); +bf::path GetPackageRoot(const std::string& pkgid, uid_t uid) { + bf::path root_path = ci::GetRootAppPath(false, uid); return root_path / pkgid; } bool ValidateFileContentInPackage(const std::string& pkgid, const std::string& relative, const std::string& expected) { - bf::path file_path = GetPackageRoot(pkgid) / relative; + bf::path file_path = GetPackageRoot(pkgid, kTestUserId) / relative; if (!bf::exists(file_path)) { LOG(ERROR) << file_path << " doesn't exist"; return false; @@ -175,34 +182,77 @@ bool ValidateFileContentInPackage(const std::string& pkgid, return content == expected; } -void AddDataFiles(const std::string& pkgid) { - auto pkg_path = GetPackageRoot(pkgid); - ASSERT_TRUE(TouchFile(pkg_path / "data" / "file1.txt")); - ASSERT_TRUE(TouchFile(pkg_path / "data" / "file2.txt")); +void AddDataFiles(const std::string& pkgid, uid_t uid) { + if (uid == kGlobalUserUid) { + ci::UserList list = ci::GetUserList(); + for (auto l : list) { + auto pkg_path = GetPackageRoot(pkgid, std::get<0>(l)); + ASSERT_TRUE(TouchFile(pkg_path / "data" / "file1.txt")); + ASSERT_TRUE(TouchFile(pkg_path / "data" / "file2.txt")); + } + } else { + auto pkg_path = GetPackageRoot(pkgid, uid); + ASSERT_TRUE(TouchFile(pkg_path / "data" / "file1.txt")); + ASSERT_TRUE(TouchFile(pkg_path / "data" / "file2.txt")); + } } -void ValidateDataFiles(const std::string& pkgid) { - auto pkg_path = GetPackageRoot(pkgid); - ASSERT_TRUE(bf::exists(pkg_path / "data" / "file1.txt")); - ASSERT_TRUE(bf::exists(pkg_path / "data" / "file2.txt")); +void ValidateDataFiles(const std::string& pkgid, uid_t uid) { + if (uid == kGlobalUserUid) { + ci::UserList list = ci::GetUserList(); + for (auto l : list) { + auto pkg_path = GetPackageRoot(pkgid, std::get<0>(l)); + ASSERT_TRUE(bf::exists(pkg_path / "data" / "file1.txt")); + ASSERT_TRUE(bf::exists(pkg_path / "data" / "file2.txt")); + } + } else { + auto pkg_path = GetPackageRoot(pkgid, uid); + ASSERT_TRUE(bf::exists(pkg_path / "data" / "file1.txt")); + ASSERT_TRUE(bf::exists(pkg_path / "data" / "file2.txt")); + } } -void ValidatePackageFS(const std::string& pkgid, - const std::vector& appids) { - bf::path root_path = ci::GetRootAppPath(false, kTestUserId); - bf::path package_path = GetPackageRoot(pkgid); +void ValidatePackageRWFS(const std::string& pkgid, uid_t uid, gid_t gid) { + bf::path root_path = ci::GetRootAppPath(false, uid); + bf::path package_path = root_path / pkgid; bf::path data_path = package_path / "data"; - bf::path shared_path = package_path / "shared"; bf::path cache_path = package_path / "cache"; + bf::path shared_data_path = package_path / "shared" / "data"; + + ASSERT_TRUE(bf::exists(data_path)); + ASSERT_TRUE(bf::exists(cache_path)); + + struct stat stats; + stat(data_path.c_str(), &stats); + // gid of data, and shared/data should be system_share + boost::optional system_share = + ci::GetGidByGroupName(kSystemShareGroupName); + ASSERT_EQ(uid, stats.st_uid) << "Invalid gid: " << data_path; + ASSERT_EQ(*system_share, stats.st_gid) << "Invalid gid: " << data_path; + if (bf::exists(shared_data_path)) { + stat(shared_data_path.c_str(), &stats); + ASSERT_EQ(uid, stats.st_uid) << "Invalid gid: " << shared_data_path; + ASSERT_EQ(*system_share, stats.st_gid) << "Invalid gid: " + << shared_data_path; + } + + stat(cache_path.c_str(), &stats); + ASSERT_EQ(uid, stats.st_uid) << "Invalid gid: " << cache_path; + ASSERT_EQ(gid, stats.st_gid) << "Invalid gid: " << cache_path; +} + +void ValidatePackageFS(const std::string& pkgid, + const std::vector& appids, + uid_t uid, gid_t gid) { + bf::path root_path = ci::GetRootAppPath(false, uid); + bf::path package_path = GetPackageRoot(pkgid, uid); + bf::path shared_path = package_path / "shared"; ASSERT_TRUE(bf::exists(root_path)); ASSERT_TRUE(bf::exists(package_path)); - ASSERT_TRUE(bf::exists(data_path)); ASSERT_TRUE(bf::exists(shared_path)); - ASSERT_TRUE(bf::exists(cache_path)); bf::path manifest_path = - bf::path(getUserManifestPath( - kTestUserId, false)) / (pkgid + ".xml"); + bf::path(getUserManifestPath(uid, false)) / (pkgid + ".xml"); ASSERT_TRUE(bf::exists(manifest_path)); for (auto& appid : appids) { @@ -228,21 +278,18 @@ void ValidatePackageFS(const std::string& pkgid, iter != bf::recursive_directory_iterator(); ++iter) { if (bf::is_symlink(symlink_status(iter->path()))) continue; + if (iter->path().filename() == "data") + continue; struct stat stats; stat(iter->path().c_str(), &stats); - ASSERT_EQ(kTestUserId, stats.st_uid) << "Invalid uid: " << iter->path(); - if (iter->path().filename() == "data") { - boost::optional gid = ci::GetGidByGroupName(kTestGroupName); - ASSERT_EQ(*gid, stats.st_gid) << "Invalid gid: " << iter->path(); - } else { - ASSERT_EQ(kTestGroupId, stats.st_gid) << "Invalid gid: " << iter->path(); - } + ASSERT_EQ(uid, stats.st_uid) << "Invalid uid: " << iter->path(); + ASSERT_EQ(gid, stats.st_gid) << "Invalid gid: " << iter->path(); } } void PackageCheckCleanup(const std::string& pkgid, const std::vector&) { - bf::path package_path = GetPackageRoot(pkgid); + bf::path package_path = GetPackageRoot(pkgid, kTestUserId); ASSERT_FALSE(bf::exists(package_path)); bf::path manifest_path = @@ -262,7 +309,14 @@ void ValidatePackage(const std::string& pkgid, ASSERT_TRUE(ci::QueryIsPackageInstalled( pkgid, ci::GetRequestMode(kTestUserId), kTestUserId)); - ValidatePackageFS(pkgid, appids); + ValidatePackageFS(pkgid, appids, kTestUserId, kTestGroupId); + if (kTestUserId == kGlobalUserUid) { + ci::UserList list = ci::GetUserList(); + for (auto& l : list) + ValidatePackageRWFS(pkgid, std::get<0>(l), std::get<1>(l)); + } else { + ValidatePackageRWFS(pkgid, kTestUserId, kTestGroupId); + } } void CheckPackageNonExistance(const std::string& pkgid, @@ -398,65 +452,93 @@ ci::AppInstaller::Result Recover(const bf::path& recovery_file, return RunInstallerWithPkgrmgr(pkgmgr, type, mode); } +void BackupPath(const bf::path& path) { + bf::path backup_path = path.string() + ".bck"; + std::cout << "Backup path: " << path << " to " << backup_path << std::endl; + bs::error_code error; + bf::remove_all(backup_path, error); + if (error) + LOG(ERROR) << "Remove failed: " << backup_path + << " (" << error.message() << ")"; + if (bf::exists(path)) { + bf::rename(path, backup_path, error); + if (error) + LOG(ERROR) << "Failed to setup test environment. Does some previous" + << " test crashed? Path: " + << backup_path << " should not exist."; + assert(!error); + } +} + +void RestorePath(const bf::path& path) { + bf::path backup_path = path.string() + ".bck"; + std::cout << "Restore path: " << path << " from " << backup_path << std::endl; + bs::error_code error; + bf::remove_all(path, error); + if (error) + LOG(ERROR) << "Remove failed: " << path + << " (" << error.message() << ")"; + if (bf::exists(backup_path)) { + bf::rename(backup_path, path, error); + if (error) + LOG(ERROR) << "Failed to restore backup path: " << backup_path + << " (" << error.message() << ")"; + } +} + +std::vector SetupBackupDirectories(uid_t uid) { + std::vector entries; + bf::path db_dir = bf::path("/opt/dbspace"); + if (uid != kGlobalUserUid) + db_dir = db_dir / "user" / std::to_string(uid); + for (auto e : kDBEntries) { + bf::path path = db_dir / e; + entries.emplace_back(path); + } + + if (uid == kGlobalUserUid) { + entries.emplace_back(kSkelDir); + entries.emplace_back(kGlobalManifestDir); + ci::UserList list = ci::GetUserList(); + for (auto l : list) { + bf::path apps = std::get<2>(l) / "apps_rw"; + entries.emplace_back(apps); + } + } else { + tzplatform_set_user(uid); + bf::path approot = tzplatform_getenv(TZ_USER_APPROOT); + tzplatform_reset_user(); + entries.emplace_back(approot); + } + + bf::path apps_rw = ci::GetRootAppPath(false, uid); + entries.emplace_back(apps_rw); + + return entries; +} + } // namespace namespace common_installer { class SmokeEnvironment : public testing::Environment { public: - explicit SmokeEnvironment(const bf::path& home) : home_(home) { + explicit SmokeEnvironment(uid_t uid) : uid_(uid) { } void SetUp() override { - bf::path UserDBDir = bf::path(kUserDataBaseDir) / kTestUserIdStr; - bf::path UserDBDirBackup = UserDBDir.string() + std::string(".bck"); - - bs::error_code error; - bf::remove_all(home_ / kApplicationDirBackup, error); - bf::remove_all(home_ / KUserAppsDirBackup, error); - bf::remove_all(UserDBDirBackup, error); - if (bf::exists(home_ / KUserAppsDir)) { - bf::rename(home_ / KUserAppsDir, home_ / KUserAppsDirBackup, error); - if (error) - LOG(ERROR) << "Failed to setup test environment. Does some previous" - << " test crashed? Directory: " - << (home_ / KUserAppsDirBackup) << " should not exist."; - assert(!error); - } - if (bf::exists(home_ / kApplicationDir)) { - bf::rename(home_ / kApplicationDir, home_ / kApplicationDirBackup, error); - if (error) - LOG(ERROR) << "Failed to setup test environment. Does some previous" - << " test crashed? Directory: " - << (home_ / kApplicationDirBackup) << " should not exist."; - assert(!error); - } - if (bf::exists(UserDBDir)) { - bf::rename(UserDBDir, UserDBDirBackup, error); - if (error) - LOG(ERROR) << "Failed to setup test environment. Does some previous" - << " test crashed? Directory: " - << UserDBDirBackup << " should not exist."; - assert(!error); - } + backups_ = SetupBackupDirectories(uid_); + for (auto& path : backups_) + BackupPath(path); } void TearDown() override { - bf::path UserDBDir = bf::path(kUserDataBaseDir) / kTestUserIdStr; - bf::path UserDBDirBackup = UserDBDir.string() + std::string(".bck"); - - bs::error_code error; - bf::remove_all(home_ / kApplicationDir, error); - bf::remove_all(home_ / KUserAppsDir, error); - bf::remove_all(UserDBDir, error); - if (bf::exists(home_ / KUserAppsDirBackup)) - bf::rename(home_ / KUserAppsDirBackup, home_ / KUserAppsDir, error); - if (bf::exists(home_ / kApplicationDirBackup)) - bf::rename(home_ / kApplicationDirBackup, home_ / kApplicationDir, error); - if (bf::exists(UserDBDirBackup)) - bf::rename(UserDBDirBackup, UserDBDir, error); + // TODO(s89.jang): Uninstall smoke packages to clear security context + for (auto& path : backups_) + RestorePath(path); } private: - bf::path home_; + uid_t uid_; + std::vector backups_; }; class SmokeTest : public testing::Test { @@ -476,12 +558,12 @@ TEST_F(SmokeTest, UpdateMode) { std::string pkgid = "smokeapp04"; std::string appid = "smokeapp04.UpdateMode"; ASSERT_EQ(Install(path_old, PackageType::WGT), ci::AppInstaller::Result::OK); - AddDataFiles(pkgid); + AddDataFiles(pkgid, kTestUserId); ASSERT_EQ(Install(path_new, PackageType::WGT), ci::AppInstaller::Result::OK); ValidatePackage(pkgid, {appid}); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/VERSION", "2\n")); - ValidateDataFiles(pkgid); + ValidateDataFiles(pkgid, kTestUserId); } TEST_F(SmokeTest, DeinstallationMode) { @@ -510,8 +592,8 @@ TEST_F(SmokeTest, RDSMode) { ValidatePackage(pkgid, {appid}); // Check delta modifications - ASSERT_FALSE(bf::exists(GetPackageRoot(pkgid) / "res" / "wgt" / "DELETED")); - ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid) / "res" / "wgt" / "ADDED")); + ASSERT_FALSE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / "res" / "wgt" / "DELETED")); + ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / "res" / "wgt" / "ADDED")); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/MODIFIED", "2\n")); } @@ -539,7 +621,7 @@ TEST_F(SmokeTest, DisablePkg) { ASSERT_EQ(DisablePackage(pkgid, PackageType::WGT), ci::AppInstaller::Result::OK); ASSERT_TRUE(ci::QueryIsDisabledPackage(pkgid, kTestUserId)); - ValidatePackageFS(pkgid, {appid}); + ValidatePackage(pkgid, {appid}); } TEST_F(SmokeTest, DeltaMode) { @@ -552,11 +634,16 @@ TEST_F(SmokeTest, DeltaMode) { ValidatePackage(pkgid, {appid}); // Check delta modifications - ASSERT_FALSE(bf::exists(GetPackageRoot(pkgid) / "res" / "wgt" / "DELETED")); - ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid) / "res" / "wgt" / "ADDED")); - ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid) / "res" / "wgt" / "css" / "style.css")); // NOLINT - ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid) / "res" / "wgt" / "images" / "tizen_32.png")); // NOLINT - ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid) / "res" / "wgt" / "js" / "main.js")); // NOLINT + ASSERT_FALSE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / + "res" / "wgt" / "DELETED")); + ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / + "res" / "wgt" / "ADDED")); + ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / + "res" / "wgt" / "css" / "style.css")); // NOLINT + ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / + "res" / "wgt" / "images" / "tizen_32.png")); // NOLINT + ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / + "res" / "wgt" / "js" / "main.js")); // NOLINT ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/MODIFIED", "version 2\n")); // NOLINT } @@ -582,7 +669,7 @@ TEST_F(SmokeTest, RecoveryMode_ForUpdate) { ASSERT_EQ(Install(path_old, PackageType::WGT), ci::AppInstaller::Result::OK); std::string pkgid = "smokeapp10"; std::string appid = "smokeapp10.RecoveryModeForUpdate"; - AddDataFiles(pkgid); + AddDataFiles(pkgid, kTestUserId); Subprocess backend_crash("/usr/bin/wgt-backend-ut/smoke-test-helper"); backend_crash.Run("-i", path_new.string(), "-u", kTestUserIdStr.c_str()); ASSERT_NE(backend_crash.Wait(), 0); @@ -594,7 +681,7 @@ TEST_F(SmokeTest, RecoveryMode_ForUpdate) { ValidatePackage(pkgid, {appid}); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/VERSION", "1\n")); - ValidateDataFiles(pkgid); + ValidateDataFiles(pkgid, kTestUserId); } TEST_F(SmokeTest, RecoveryMode_ForDelta) { @@ -643,7 +730,7 @@ TEST_F(SmokeTest, RecoveryMode_ForMountUpdate) { RemoveAllRecoveryFiles(); ASSERT_EQ(MountInstall(path_old, PackageType::WGT), ci::AppInstaller::Result::OK); - AddDataFiles(pkgid); + AddDataFiles(pkgid, kTestUserId); Subprocess backend_crash("/usr/bin/wgt-backend-ut/smoke-test-helper"); backend_crash.Run("-w", path_new.string(), "-u", kTestUserIdStr.c_str()); ASSERT_NE(backend_crash.Wait(), 0); @@ -660,7 +747,7 @@ TEST_F(SmokeTest, RecoveryMode_ForMountUpdate) { ScopedTzipInterface interface(pkgid); ValidatePackage(pkgid, {appid}); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/VERSION", "1\n")); - ValidateDataFiles(pkgid); + ValidateDataFiles(pkgid, kTestUserId); } TEST_F(SmokeTest, InstallationMode_GoodSignature) { @@ -688,22 +775,22 @@ TEST_F(SmokeTest, UpdateMode_Rollback) { std::string pkgid = "smokeapp07"; std::string appid = "smokeapp07.UpdateModeRollback"; ASSERT_EQ(Install(path_old, PackageType::WGT), ci::AppInstaller::Result::OK); - AddDataFiles(pkgid); + AddDataFiles(pkgid, kTestUserId); ASSERT_EQ(Install(path_new, PackageType::WGT, RequestResult::FAIL), ci::AppInstaller::Result::ERROR); ValidatePackage(pkgid, {appid}); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/VERSION", "1\n")); - ValidateDataFiles(pkgid); + ValidateDataFiles(pkgid, kTestUserId); } TEST_F(SmokeTest, InstallationMode_Hybrid) { bf::path path = kSmokePackagesDirectory / "InstallationMode_Hybrid.wgt"; std::string pkgid = "smokehyb01"; + // Excutable for native app doesn't create symlink std::string appid1 = "smokehyb01.Web"; - std::string appid2 = "smokehyb01.Native"; ASSERT_EQ(Install(path, PackageType::HYBRID), ci::AppInstaller::Result::OK); - ValidatePackage(pkgid, {appid1, appid2}); + ValidatePackage(pkgid, {appid1}); } TEST_F(SmokeTest, UpdateMode_Hybrid) { @@ -711,29 +798,27 @@ TEST_F(SmokeTest, UpdateMode_Hybrid) { bf::path path_new = kSmokePackagesDirectory / "UpdateMode_Hybrid_2.wgt"; std::string pkgid = "smokehyb02"; std::string appid1 = "smokehyb02.Web"; - std::string appid2 = "smokehyb02.Native"; ASSERT_EQ(Install(path_old, PackageType::HYBRID), ci::AppInstaller::Result::OK); - AddDataFiles(pkgid); +// AddDataFiles(pkgid, kTestUserId); ASSERT_EQ(Install(path_new, PackageType::HYBRID), ci::AppInstaller::Result::OK); - ValidatePackage(pkgid, {appid1, appid2}); + ValidatePackage(pkgid, {appid1}); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/VERSION", "2\n")); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "VERSION", "2\n")); - ValidateDataFiles(pkgid); +// ValidateDataFiles(pkgid, kTestUserId); } TEST_F(SmokeTest, DeinstallationMode_Hybrid) { bf::path path = kSmokePackagesDirectory / "DeinstallationMode_Hybrid.wgt"; std::string pkgid = "smokehyb03"; std::string appid1 = "smokehyb03.Web"; - std::string appid2 = "smokehyb03.Native"; ASSERT_EQ(Install(path, PackageType::HYBRID), ci::AppInstaller::Result::OK); ASSERT_EQ(Uninstall(pkgid, PackageType::HYBRID), ci::AppInstaller::Result::OK); - CheckPackageNonExistance(pkgid, {appid1, appid2}); + CheckPackageNonExistance(pkgid, {appid1}); } TEST_F(SmokeTest, DeltaMode_Hybrid) { @@ -741,10 +826,9 @@ TEST_F(SmokeTest, DeltaMode_Hybrid) { bf::path delta_package = kSmokePackagesDirectory / "DeltaMode_Hybrid.delta"; std::string pkgid = "smokehyb04"; std::string appid1 = "smokehyb04.Web"; - std::string appid2 = "smokehyb04.Native"; ASSERT_EQ(DeltaInstall(path, delta_package, PackageType::HYBRID), ci::AppInstaller::Result::OK); - ValidatePackage(pkgid, {appid1, appid2}); + ValidatePackage(pkgid, {appid1}); // Check delta modifications bf::path root_path = ci::GetRootAppPath(false, @@ -764,11 +848,10 @@ TEST_F(SmokeTest, MountInstallationMode_Hybrid) { bf::path path = kSmokePackagesDirectory / "MountInstallationMode_Hybrid.wgt"; std::string pkgid = "smokehyb05"; std::string appid1 = "smokehyb05.web"; - std::string appid2 = "smokehyb05.service"; ASSERT_EQ(MountInstall(path, PackageType::HYBRID), ci::AppInstaller::Result::OK); ScopedTzipInterface interface(pkgid); - ValidatePackage(pkgid, {appid1, appid2}); + ValidatePackage(pkgid, {appid1}); } TEST_F(SmokeTest, MountUpdateMode_Hybrid) { @@ -776,18 +859,17 @@ TEST_F(SmokeTest, MountUpdateMode_Hybrid) { bf::path path_new = kSmokePackagesDirectory / "MountUpdateMode_Hybrid_2.wgt"; std::string pkgid = "smokehyb06"; std::string appid1 = "smokehyb06.web"; - std::string appid2 = "smokehyb06.service"; ASSERT_EQ(MountInstall(path_old, PackageType::HYBRID), ci::AppInstaller::Result::OK); - AddDataFiles(pkgid); + AddDataFiles(pkgid, kTestUserId); ASSERT_EQ(MountInstall(path_new, PackageType::HYBRID), ci::AppInstaller::Result::OK); ScopedTzipInterface interface(pkgid); - ValidatePackage(pkgid, {appid1, appid2}); + ValidatePackage(pkgid, {appid1}); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/VERSION", "2\n")); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "lib/VERSION", "2\n")); - ValidateDataFiles(pkgid); + ValidateDataFiles(pkgid, kTestUserId); } TEST_F(SmokeTest, InstallationMode_Rollback_Hybrid) { @@ -795,10 +877,9 @@ TEST_F(SmokeTest, InstallationMode_Rollback_Hybrid) { "InstallationMode_Rollback_Hybrid.wgt"; std::string pkgid = "smokehyb07"; std::string appid1 = "smokehyb07.web"; - std::string appid2 = "smokehyb07.service"; ASSERT_EQ(Install(path, PackageType::HYBRID, RequestResult::FAIL), ci::AppInstaller::Result::ERROR); - CheckPackageNonExistance(pkgid, {appid1, appid2}); + CheckPackageNonExistance(pkgid, {appid1}); } TEST_F(SmokeTest, UpdateMode_Rollback_Hybrid) { @@ -808,17 +889,16 @@ TEST_F(SmokeTest, UpdateMode_Rollback_Hybrid) { "UpdateMode_Rollback_Hybrid_2.wgt"; std::string pkgid = "smokehyb08"; std::string appid1 = "smokehyb08.web"; - std::string appid2 = "smokehyb08.service"; ASSERT_EQ(Install(path_old, PackageType::HYBRID), ci::AppInstaller::Result::OK); - AddDataFiles(pkgid); + AddDataFiles(pkgid, kTestUserId); ASSERT_EQ(Install(path_new, PackageType::HYBRID, RequestResult::FAIL), ci::AppInstaller::Result::ERROR); - ValidatePackage(pkgid, {appid1, appid2}); + ValidatePackage(pkgid, {appid1}); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/VERSION", "1\n")); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "lib/VERSION", "1\n")); - ValidateDataFiles(pkgid); + ValidateDataFiles(pkgid, kTestUserId); } TEST_F(SmokeTest, MountInstallationMode_Rollback_Hybrid) { @@ -826,11 +906,10 @@ TEST_F(SmokeTest, MountInstallationMode_Rollback_Hybrid) { "MountInstallationMode_Rollback_Hybrid.wgt"; std::string pkgid = "smokehyb09"; std::string appid1 = "smokehyb09.web"; - std::string appid2 = "smokehyb09.service"; ASSERT_EQ(MountInstall(path, PackageType::HYBRID, RequestResult::FAIL), ci::AppInstaller::Result::ERROR); ScopedTzipInterface interface(pkgid); - CheckPackageNonExistance(pkgid, {appid1, appid2}); + CheckPackageNonExistance(pkgid, {appid1}); } TEST_F(SmokeTest, MountUpdateMode_Rollback_Hybrid) { @@ -840,18 +919,17 @@ TEST_F(SmokeTest, MountUpdateMode_Rollback_Hybrid) { "MountUpdateMode_Rollback_Hybrid_2.wgt"; std::string pkgid = "smokehyb10"; std::string appid1 = "smokehyb10.web"; - std::string appid2 = "smokehyb10.service"; ASSERT_EQ(MountInstall(path_old, PackageType::HYBRID), ci::AppInstaller::Result::OK); - AddDataFiles(pkgid); + AddDataFiles(pkgid, kTestUserId); ASSERT_EQ(MountInstall(path_new, PackageType::HYBRID, RequestResult::FAIL), ci::AppInstaller::Result::ERROR); ScopedTzipInterface interface(pkgid); - ValidatePackage(pkgid, {appid1, appid2}); + ValidatePackage(pkgid, {appid1}); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/VERSION", "1\n")); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "lib/VERSION", "1\n")); - ValidateDataFiles(pkgid); + ValidateDataFiles(pkgid, kTestUserId); } TEST_F(SmokeTest, MountInstallationMode) { @@ -870,14 +948,14 @@ TEST_F(SmokeTest, MountUpdateMode) { std::string appid = "smokeapp29.UpdateMode"; ASSERT_EQ(MountInstall(path_old, PackageType::WGT), ci::AppInstaller::Result::OK); - AddDataFiles(pkgid); + AddDataFiles(pkgid, kTestUserId); ASSERT_EQ(MountInstall(path_new, PackageType::WGT), ci::AppInstaller::Result::OK); ScopedTzipInterface interface(pkgid); ValidatePackage(pkgid, {appid}); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/VERSION", "2\n")); - ValidateDataFiles(pkgid); + ValidateDataFiles(pkgid, kTestUserId); } TEST_F(SmokeTest, MountInstallationMode_Rollback) { @@ -899,14 +977,14 @@ TEST_F(SmokeTest, MountUpdateMode_Rollback) { std::string appid = "smokeapp34.web"; ASSERT_EQ(MountInstall(path_old, PackageType::WGT), ci::AppInstaller::Result::OK); - AddDataFiles(pkgid); + AddDataFiles(pkgid, kTestUserId); ASSERT_EQ(MountInstall(path_new, PackageType::WGT, RequestResult::FAIL), ci::AppInstaller::Result::ERROR); ScopedTzipInterface interface(pkgid); ValidatePackage(pkgid, {appid}); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/VERSION", "1\n")); - ValidateDataFiles(pkgid); + ValidateDataFiles(pkgid, kTestUserId); } TEST_F(SmokeTest, UserDefinedPlugins) { @@ -931,9 +1009,7 @@ TEST_F(SmokeTest, UserDefinedPlugins) { int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); - bf::path directory = - bf::path("/home") / tzplatform_getenv(TZ_SYS_DEFAULT_USER); testing::AddGlobalTestEnvironment( - new common_installer::SmokeEnvironment(directory)); + new common_installer::SmokeEnvironment(kGlobalUserUid)); return RUN_ALL_TESTS(); } -- 2.7.4 From 65a54d07d82473770a32cc48b62bdcdc03800f62 Mon Sep 17 00:00:00 2001 From: Sangyoon Jang Date: Fri, 23 Dec 2016 16:28:28 +0900 Subject: [PATCH 12/16] Adjust step order of hybrid installer Step for creating symlink should be called after merging manifest. Change-Id: Ifdf323c5c0424c59cdbc99a0526f771050d5120d Signed-off-by: Sangyoon Jang --- src/hybrid/hybrid_installer.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/hybrid/hybrid_installer.cc b/src/hybrid/hybrid_installer.cc index af34823..e537659 100644 --- a/src/hybrid/hybrid_installer.cc +++ b/src/hybrid/hybrid_installer.cc @@ -134,11 +134,11 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); - AddStep(); - AddStep(); AddStep(); AddStep(); AddStep(); + AddStep(); + AddStep(); AddStep(); AddStep(); AddStep( @@ -184,12 +184,12 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); - AddStep(); - AddStep(); AddStep(); AddStep(); AddStep(); AddStep(); + AddStep(); + AddStep(); AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); @@ -265,12 +265,12 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); - AddStep(); - AddStep(); AddStep(); AddStep(); AddStep(); AddStep(); + AddStep(); + AddStep(); AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); @@ -327,11 +327,11 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); - AddStep(); - AddStep(); AddStep(); AddStep(); AddStep(); + AddStep(); + AddStep(); AddStep(); AddStep(); AddStep( @@ -376,12 +376,12 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); - AddStep(); - AddStep(); AddStep(); AddStep(); AddStep(); AddStep(); + AddStep(); + AddStep(); AddStep(); AddStep( ci::Plugin::ActionType::Upgrade); -- 2.7.4 From 00c60dba4687a889eb82ed676125269b1a19baed Mon Sep 17 00:00:00 2001 From: Junghyun Yeon Date: Mon, 26 Dec 2016 16:32:44 +0900 Subject: [PATCH 13/16] Remove codes which refers deleted attribute Related changes: [pkgmgr-info] : https://review.tizen.org/gerrit/107032 [app-installers] : https://review.tizen.org/gerrit/107033 [tpk-backend] : https://review.tizen.org/gerrit/107034 Change-Id: I54b8508488321a67ef35733a1063496544f31b22 Signed-off-by: Junghyun Yeon --- src/wgt/step/configuration/step_parse.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wgt/step/configuration/step_parse.cc b/src/wgt/step/configuration/step_parse.cc index e1f817c..d9a96ba 100644 --- a/src/wgt/step/configuration/step_parse.cc +++ b/src/wgt/step/configuration/step_parse.cc @@ -95,7 +95,6 @@ void AppendWidgetMetadata(GList** metadatas, void SetApplicationXDefaults(application_x* application) { application->effectimage_type = strdup("image"); - application->enabled = strdup("true"); application->guestmode_visibility = strdup("true"); application->hwacceleration = strdup("default"); application->indicatordisplay = strdup("true"); -- 2.7.4 From 6e047fdaa5a2d20251ab1b16f020f5b469a38b55 Mon Sep 17 00:00:00 2001 From: Sangyoon Jang Date: Tue, 27 Dec 2016 16:29:09 +0900 Subject: [PATCH 14/16] Remove unused string constant variable Change-Id: I1d78321f4d144fb2aff76057036b7cb02792b85d Signed-off-by: Sangyoon Jang --- src/hybrid/step/pkgmgr/step_generate_xml.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/hybrid/step/pkgmgr/step_generate_xml.cc b/src/hybrid/step/pkgmgr/step_generate_xml.cc index 7d6f42d..ccb8f16 100644 --- a/src/hybrid/step/pkgmgr/step_generate_xml.cc +++ b/src/hybrid/step/pkgmgr/step_generate_xml.cc @@ -27,9 +27,6 @@ namespace bs = boost::system; namespace { -const char kXmlXPathAppExpr[] = - "//*[local-name()='ui-application' or local-name()='service-application' " - "or local-name()='widget-application' or local-name()='watch-application']"; const std::vector kBlackListNodes = { {"author"}, {"description"}, -- 2.7.4 From c78c1285f46a00758957c9589189749fca719ef9 Mon Sep 17 00:00:00 2001 From: Sangyoon Jang Date: Tue, 27 Dec 2016 16:26:24 +0900 Subject: [PATCH 15/16] Fix privilege handling on hybrid installation To handle privileges properly, it should merge privileges first before StepPrivilegeCompatibility. Change-Id: I5e40ebb3374939652d89e258a23051b91cb1781a Signed-off-by: Sangyoon Jang --- src/hybrid/hybrid_installer.cc | 8 ++++++ .../step/configuration/step_merge_tpk_config.cc | 4 --- .../configuration/step_merge_tpk_privileges.cc | 28 +++++++++++++++++++++ .../step/configuration/step_merge_tpk_privileges.h | 29 ++++++++++++++++++++++ src/hybrid/step/pkgmgr/step_generate_xml.cc | 2 +- 5 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 src/hybrid/step/configuration/step_merge_tpk_privileges.cc create mode 100644 src/hybrid/step/configuration/step_merge_tpk_privileges.h diff --git a/src/hybrid/hybrid_installer.cc b/src/hybrid/hybrid_installer.cc index e537659..0eebf85 100644 --- a/src/hybrid/hybrid_installer.cc +++ b/src/hybrid/hybrid_installer.cc @@ -77,6 +77,7 @@ #include "hybrid/hybrid_backend_data.h" #include "hybrid/step/configuration/step_merge_tpk_config.h" +#include "hybrid/step/configuration/step_merge_tpk_privileges.h" #include "hybrid/step/configuration/step_stash_tpk_config.h" #include "hybrid/step/encryption/step_encrypt_resources.h" #include "hybrid/step/pkgmgr/step_generate_xml.h" @@ -116,6 +117,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); + AddStep(); AddStep( ci::security::StepPrivilegeCompatibility::InternalPrivType::BOTH); AddStep(); @@ -160,6 +162,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); + AddStep(); AddStep( ci::security::StepPrivilegeCompatibility::InternalPrivType::BOTH); AddStep(); @@ -243,6 +246,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); + AddStep(); AddStep( ci::security::StepPrivilegeCompatibility::InternalPrivType::BOTH); AddStep(); @@ -309,6 +313,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); + AddStep(); AddStep( ci::security::StepPrivilegeCompatibility::InternalPrivType::BOTH); AddStep(); @@ -353,6 +358,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); + AddStep(); AddStep( ci::security::StepPrivilegeCompatibility::InternalPrivType::BOTH); AddStep(); @@ -400,6 +406,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); + AddStep(); AddStep( ci::security::StepPrivilegeCompatibility::InternalPrivType::BOTH); AddStep(); @@ -429,6 +436,7 @@ HybridInstaller::HybridInstaller(common_installer::PkgMgrPtr pkgmgr) AddStep(); AddStep(); AddStep(); + AddStep(); AddStep( ci::security::StepPrivilegeCompatibility::InternalPrivType::BOTH); AddStep(); diff --git a/src/hybrid/step/configuration/step_merge_tpk_config.cc b/src/hybrid/step/configuration/step_merge_tpk_config.cc index 913ad98..15d59c3 100644 --- a/src/hybrid/step/configuration/step_merge_tpk_config.cc +++ b/src/hybrid/step/configuration/step_merge_tpk_config.cc @@ -21,10 +21,6 @@ common_installer::Step::Status StepMergeTpkConfig::process() { g_list_concat(wgt_data->application, tpk_data->application); tpk_data->application = nullptr; - wgt_data->privileges = - g_list_concat(wgt_data->privileges, tpk_data->privileges); - tpk_data->privileges = nullptr; - return Status::OK; } diff --git a/src/hybrid/step/configuration/step_merge_tpk_privileges.cc b/src/hybrid/step/configuration/step_merge_tpk_privileges.cc new file mode 100644 index 0000000..48f13f5 --- /dev/null +++ b/src/hybrid/step/configuration/step_merge_tpk_privileges.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by an apache-2.0 license that can be +// found in the LICENSE file. + +#include "hybrid/step/configuration/step_merge_tpk_privileges.h" + +#include + +#include "hybrid/hybrid_backend_data.h" + +namespace hybrid { +namespace configuration { + +common_installer::Step::Status StepMergeTpkPrivileges::process() { + HybridBackendData* data = + static_cast(context_->backend_data.get()); + manifest_x* tpk_data = data->tpk_manifest_data.get(); + manifest_x* wgt_data = context_->manifest_data.get(); + + wgt_data->privileges = + g_list_concat(wgt_data->privileges, tpk_data->privileges); + tpk_data->privileges = nullptr; + + return Status::OK; +} + +} // namespace configuration +} // namespace hybrid diff --git a/src/hybrid/step/configuration/step_merge_tpk_privileges.h b/src/hybrid/step/configuration/step_merge_tpk_privileges.h new file mode 100644 index 0000000..98651fc --- /dev/null +++ b/src/hybrid/step/configuration/step_merge_tpk_privileges.h @@ -0,0 +1,29 @@ +// Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by an apache-2.0 license that can be +// found in the LICENSE file. + +#ifndef HYBRID_STEP_CONFIGURATION_STEP_MERGE_TPK_PRIVILEGES_H_ +#define HYBRID_STEP_CONFIGURATION_STEP_MERGE_TPK_PRIVILEGES_H_ + +#include +#include + +namespace hybrid { +namespace configuration { + +class StepMergeTpkPrivileges : public common_installer::Step { + public: + using Step::Step; + + Status process() override; + Status clean() override { return Status::OK; } + Status undo() override { return Status::OK; } + Status precheck() override { return Status::OK; } + + STEP_NAME(MergeTpkPrivileges) +}; + +} // namespace configuration +} // namespace hybrid + +#endif // HYBRID_STEP_CONFIGURATION_STEP_MERGE_TPK_PRIVILEGES_H_ diff --git a/src/hybrid/step/pkgmgr/step_generate_xml.cc b/src/hybrid/step/pkgmgr/step_generate_xml.cc index ccb8f16..8ab0826 100644 --- a/src/hybrid/step/pkgmgr/step_generate_xml.cc +++ b/src/hybrid/step/pkgmgr/step_generate_xml.cc @@ -31,10 +31,10 @@ const std::vector kBlackListNodes = { {"author"}, {"description"}, {"profile"}, + {"privileges"}, }; const std::vector kNeedMergeNodes = { {"manifest"}, - {"privileges"}, }; const std::vector> kApplicationNodeNames = { {"ui-application", "uiapp"}, -- 2.7.4 From 59a20f87ebd56999807c7704f9642fff96165d0a Mon Sep 17 00:00:00 2001 From: Sangyoon Jang Date: Tue, 27 Dec 2016 16:48:27 +0900 Subject: [PATCH 16/16] Fix code style Remove errors/warnings from tools/check-coding-styles. Change-Id: Idd2a1faca15b8ac48049c20ebfe758a4917f152f Signed-off-by: Sangyoon Jang --- src/hybrid/hybrid_installer.cc | 2 +- src/lib/wgt_archive_info.cc | 14 +++++++------- src/lib/wgt_archive_info.h | 2 +- src/unit_tests/smoke_test.cc | 19 +++++++++++-------- src/wgt/shared_dirs.h | 6 +++--- src/wgt/wgt_app_query_interface.h | 4 ++-- src/wgt/wgt_installer.cc | 2 +- 7 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/hybrid/hybrid_installer.cc b/src/hybrid/hybrid_installer.cc index 0eebf85..73f212f 100644 --- a/src/hybrid/hybrid_installer.cc +++ b/src/hybrid/hybrid_installer.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "hybrid/hybrid_installer.h" -#include "hybrid/shared_dirs.h" #include #include @@ -76,6 +75,7 @@ #include #include "hybrid/hybrid_backend_data.h" +#include "hybrid/shared_dirs.h" #include "hybrid/step/configuration/step_merge_tpk_config.h" #include "hybrid/step/configuration/step_merge_tpk_privileges.h" #include "hybrid/step/configuration/step_stash_tpk_config.h" diff --git a/src/lib/wgt_archive_info.cc b/src/lib/wgt_archive_info.cc index bcaa43b..aeed341 100644 --- a/src/lib/wgt_archive_info.cc +++ b/src/lib/wgt_archive_info.cc @@ -38,7 +38,7 @@ bool ExtractPackageArchive(const char* file_path, const bf::path& tmp_dir) { return true; } -bool GetPackageInfo(wgt::parse::WidgetConfigParser& parser, +bool GetPackageInfo(const wgt::parse::WidgetConfigParser& parser, package_manager_pkg_detail_info_t* info) { auto widget_info = std::static_pointer_cast( @@ -66,7 +66,7 @@ bool GetPackageInfo(wgt::parse::WidgetConfigParser& parser, return true; } -bool GetPrivilegesInfo(wgt::parse::WidgetConfigParser& parser, +bool GetPrivilegesInfo(const wgt::parse::WidgetConfigParser& parser, package_manager_pkg_detail_info_t* info) { auto privileges_info = std::static_pointer_cast( @@ -83,8 +83,8 @@ bool GetPrivilegesInfo(wgt::parse::WidgetConfigParser& parser, return true; } -bool GetLabelInfo(wgt::parse::WidgetConfigParser& parser, const char* locale, - package_manager_pkg_detail_info_t* info) { +bool GetLabelInfo(const wgt::parse::WidgetConfigParser& parser, + const char* locale, package_manager_pkg_detail_info_t* info) { auto widget_info = std::static_pointer_cast( parser.GetManifestData(wgt::parse::WidgetInfo::Key())); @@ -106,7 +106,7 @@ bool GetLabelInfo(wgt::parse::WidgetConfigParser& parser, const char* locale, return false; } -bool GetDescriptionInfo(wgt::parse::WidgetConfigParser& parser, +bool GetDescriptionInfo(const wgt::parse::WidgetConfigParser& parser, const char* locale, package_manager_pkg_detail_info_t* info) { auto widget_info = std::static_pointer_cast( @@ -161,8 +161,8 @@ bool ReadIcon(const bf::path& icon, const bf::path& tmp_dir, return true; } -bool GetIconInfo(wgt::parse::WidgetConfigParser& parser, - bf::path& tmp_dir, package_manager_pkg_detail_info_t* info) { +bool GetIconInfo(const wgt::parse::WidgetConfigParser& parser, + const bf::path& tmp_dir, package_manager_pkg_detail_info_t* info) { auto icons_info = std::static_pointer_cast( parser.GetManifestData(wgt::parse::ApplicationIconsInfo::Key())); diff --git a/src/lib/wgt_archive_info.h b/src/lib/wgt_archive_info.h index 3882f78..e2bf8d1 100644 --- a/src/lib/wgt_archive_info.h +++ b/src/lib/wgt_archive_info.h @@ -20,4 +20,4 @@ class WgtArchiveInfo : public common_installer::Singleton { SCOPE_LOG_TAG(WgtArchiveInfo) }; -#endif // LIB_WGT_ARCHIVE_INFO +#endif // LIB_WGT_ARCHIVE_INFO_H_ diff --git a/src/unit_tests/smoke_test.cc b/src/unit_tests/smoke_test.cc index f86a813..2d14a1f 100644 --- a/src/unit_tests/smoke_test.cc +++ b/src/unit_tests/smoke_test.cc @@ -592,8 +592,10 @@ TEST_F(SmokeTest, RDSMode) { ValidatePackage(pkgid, {appid}); // Check delta modifications - ASSERT_FALSE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / "res" / "wgt" / "DELETED")); - ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / "res" / "wgt" / "ADDED")); + ASSERT_FALSE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / + "res" / "wgt" / "DELETED")); + ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / + "res" / "wgt" / "ADDED")); ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/MODIFIED", "2\n")); } @@ -635,16 +637,17 @@ TEST_F(SmokeTest, DeltaMode) { // Check delta modifications ASSERT_FALSE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / - "res" / "wgt" / "DELETED")); + "res" / "wgt" / "DELETED")); ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / - "res" / "wgt" / "ADDED")); + "res" / "wgt" / "ADDED")); ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / - "res" / "wgt" / "css" / "style.css")); // NOLINT + "res" / "wgt" / "css" / "style.css")); ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / - "res" / "wgt" / "images" / "tizen_32.png")); // NOLINT + "res" / "wgt" / "images" / "tizen_32.png")); ASSERT_TRUE(bf::exists(GetPackageRoot(pkgid, kTestUserId) / - "res" / "wgt" / "js" / "main.js")); // NOLINT - ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/wgt/MODIFIED", "version 2\n")); // NOLINT + "res" / "wgt" / "js" / "main.js")); + ASSERT_TRUE(ValidateFileContentInPackage( + pkgid, "res/wgt/MODIFIED", "version 2\n")); } TEST_F(SmokeTest, RecoveryMode_ForInstallation) { diff --git a/src/wgt/shared_dirs.h b/src/wgt/shared_dirs.h index 2b75e91..61bb319 100644 --- a/src/wgt/shared_dirs.h +++ b/src/wgt/shared_dirs.h @@ -2,8 +2,8 @@ // Use of this source code is governed by an apache-2.0 license that can be // found in the LICENSE file. -#ifndef HYBRID_SHARED_DIRS_H_ -#define HYBRID_SHARED_DIRS_H_ +#ifndef WGT_SHARED_DIRS_H_ +#define WGT_SHARED_DIRS_H_ #include @@ -17,4 +17,4 @@ const std::vector WgtAdditionalSharedDirs = { } // namespace filesystem } // namespace wgt -#endif // HYBRID_SHARED_DIRS_H_ +#endif // WGT_SHARED_DIRS_H_ diff --git a/src/wgt/wgt_app_query_interface.h b/src/wgt/wgt_app_query_interface.h index b8ed790..3610da9 100644 --- a/src/wgt/wgt_app_query_interface.h +++ b/src/wgt/wgt_app_query_interface.h @@ -5,12 +5,12 @@ #ifndef WGT_WGT_APP_QUERY_INTERFACE_H_ #define WGT_WGT_APP_QUERY_INTERFACE_H_ +#include + #include #include -#include - namespace wgt { /** diff --git a/src/wgt/wgt_installer.cc b/src/wgt/wgt_installer.cc index b1dcd43..e90da5c 100755 --- a/src/wgt/wgt_installer.cc +++ b/src/wgt/wgt_installer.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "wgt/wgt_installer.h" -#include "wgt/shared_dirs.h" #include @@ -78,6 +77,7 @@ #include +#include "wgt/shared_dirs.h" #include "wgt/step/configuration/step_check_rds_manifest.h" #include "wgt/step/configuration/step_check_start_files.h" #include "wgt/step/configuration/step_parse.h" -- 2.7.4