* Copyright (C) 2023 Xiaomi Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <binder/ProcessState.h>
#include <gtest/gtest.h>
#include <filesystem>
#include <future>
#include <map>
#include <memory>
#include "PackageUtils.h"
#include "pm/PackageManager.h"
namespace os {
namespace pm {
using android::binder::Status;
using std::filesystem::directory_iterator;
using std::filesystem::exists;
struct TestParams {
std::string packageName;
std::string rpkPath;
std::string notExistPackageName;
std::string notExistRpkPath;
const std::string dump() const {
return std::string("packageName: ")
.append(packageName)
.append("\n")
.append("rpkPath: ")
.append(rpkPath)
.append("\n")
.append("notExistPackageName: ")
.append(notExistPackageName)
.append("\n")
.append("notExistRpkPath: ")
.append(notExistRpkPath);
}
};
static TestParams g_testParams = {"com.application.demo",
"/resource/package/com.application.demo.debug.1.0.0.rpk",
"com.vela.demo1", "/data/package/com.vela.demo.rpk"};
class PmTest : public testing::Test {
public:
unsigned int getDirectoryCount(const std::string &path) {
unsigned int count = 0;
for (const auto &entry : directory_iterator(path.c_str())) {
if (entry.is_directory()) {
count++;
}
}
return count;
}
protected:
virtual void SetUp() override {
android::ProcessState::self()->startThreadPool();
}
virtual void TearDown() override {}
public:
PackageManager pm;
};
class InstallListenerTest : public BnInstallObserver, public std::promise<int32_t> {
public:
Status onInstallProcess(const std::string &packageName, int32_t process) override {
return Status::ok();
}
Status onInstallResult(const std::string &packageName, int32_t code,
const std::string &msg) override {
printf("onInstallResult: %s(%s %" PRIi32 ")\n", packageName.c_str(), msg.c_str(), code);
promise_.set_value(code);
return Status::ok();
}
std::future<int32_t> get_future() {
return promise_.get_future();
}
private:
std::promise<int32_t> promise_;
};
class UninstallListenerTest : public BnUninstallObserver, public std::promise<int32_t> {
public:
Status onUninstallResult(const std::string &packageName, int32_t code,
const std::string &msg) override {
printf("onUninstallResult: %s(%s %" PRIi32 ")\n", packageName.c_str(), msg.c_str(), code);
promise_.set_value(code);
return Status::ok();
}
std::future<int32_t> get_future() {
return promise_.get_future();
}
private:
std::promise<int32_t> promise_;
};
TEST_F(PmTest, InitStart) {
rapidjson::Document doc;
getDocument(PACKAGE_CFG, doc);
std::string appPresetPath = getValue<std::string>(doc, "appPresetPath", "/system/app");
std::string appInstalledPath = getValue<std::string>(doc, "appInstalledPath", "/data/app");
std::string packageListPath = joinPath(appInstalledPath, PACKAGE_LIST);
rapidjson::Document docForLoad;
getDocument(packageListPath.c_str(), docForLoad);
const auto baseArray = rapidjson::Value(rapidjson::kArrayType);
const auto &packagesArray =
getValue<const rapidjson::Value &>(docForLoad, "packages", baseArray);
std::map<std::string, PackageInfo> pkgInfosForLoad;
for (unsigned int i = 0; i < packagesArray.Size(); i++) {
PackageInfo info;
info.packageName = getValue<std::string>(packagesArray[i], "package", "");
if (!(info.packageName.empty())) {
pkgInfosForLoad.insert(std::make_pair(info.packageName, info));
}
}
std::vector<PackageInfo> pkgInfos;
pm.getAllPackageInfo(&pkgInfos);
unsigned int manifestCount = 0;
manifestCount += getDirectoryCount(appPresetPath);
manifestCount += getDirectoryCount(appInstalledPath);
EXPECT_EQ(exists(packageListPath), true);
EXPECT_EQ(pkgInfosForLoad.size(), pkgInfos.size());
EXPECT_EQ(manifestCount, packagesArray.Size());
}
TEST_F(PmTest, CheckKeyField) {
std::vector<PackageInfo> pkgInfos;
pm.getAllPackageInfo(&pkgInfos);
for (const auto &info : pkgInfos) {
rapidjson::Document doc;
getDocument(info.manifest.c_str(), doc);
EXPECT_STREQ(getValue<std::string>(doc, "package", "").c_str(), info.packageName.c_str());
EXPECT_STREQ(getValue<std::string>(doc, "appType", "QUICKAPP").c_str(),
info.appType.c_str());
EXPECT_STREQ(getValue<std::string>(doc, "versionName", "").c_str(), info.version.c_str());
EXPECT_STREQ(getValue<std::string>(doc, "name", "").c_str(), info.name.c_str());
EXPECT_STREQ(getValue<std::string>(doc, "icon", "").c_str(), info.icon.c_str());
EXPECT_EQ(getProcessPriority(getValue<std::string>(doc, "priority", "middle")),
info.priority);
EXPECT_EQ(getValue<bool>(doc, "isSystemUI", false), info.isSystemUI);
}
}
TEST_F(PmTest, GetNotExistPackage) {
PackageInfo info;
EXPECT_NE(pm.getPackageInfo(g_testParams.notExistPackageName, &info), 0);
}
TEST_F(PmTest, InstallExistPackage) {
InstallParam param;
param.path = g_testParams.rpkPath;
sp<InstallListenerTest> listener = new InstallListenerTest();
int ret = pm.installPackage(param, listener);
EXPECT_EQ(ret, 0);
auto f = listener->get_future();
int result = f.get();
EXPECT_EQ(result, 0);
EXPECT_EQ(exists(g_testParams.rpkPath.c_str()), true);
PackageInfo info;
EXPECT_EQ(pm.getPackageInfo(g_testParams.packageName, &info), 0);
}
TEST_F(PmTest, GetExistPackage) {
PackageInfo info;
EXPECT_EQ(pm.getPackageInfo(g_testParams.packageName, &info), 0);
auto str1 = g_testParams.packageName.c_str();
auto str2 = info.packageName.c_str();
EXPECT_EQ(strcmp(str1, str2), 0);
}
TEST_F(PmTest, InstallRepeatPackage) {
InstallParam param;
param.path = g_testParams.rpkPath;
sp<InstallListenerTest> listener = new InstallListenerTest();
int ret = pm.installPackage(param, listener);
EXPECT_EQ(ret, 0);
auto f = listener->get_future();
int result = f.get();
EXPECT_EQ(result, 0);
PackageInfo info;
EXPECT_EQ(pm.getPackageInfo(g_testParams.packageName, &info), 0);
EXPECT_EQ(exists(info.installedPath.c_str()), true);
}
TEST_F(PmTest, UninstallPackage) {
UninstallParam uninstallparam;
uninstallparam.packageName = g_testParams.packageName;
sp<UninstallListenerTest> listener = new UninstallListenerTest();
int ret = pm.uninstallPackage(uninstallparam, listener);
EXPECT_EQ(ret, 0);
auto f = listener->get_future();
int result = f.get();
EXPECT_EQ(result, 0);
PackageInfo pkginfo;
EXPECT_EQ(pm.getPackageInfo(g_testParams.packageName, &pkginfo), Status::EX_SERVICE_SPECIFIC);
}
TEST_F(PmTest, UninstallNotExistPackage) {
UninstallParam uninstallparam;
uninstallparam.packageName = g_testParams.notExistPackageName;
sp<UninstallListenerTest> listener = new UninstallListenerTest();
int ret = pm.uninstallPackage(uninstallparam, listener);
EXPECT_EQ(ret, 0);
auto f = listener->get_future();
int result = f.get();
EXPECT_EQ(result, android::NAME_NOT_FOUND);
PackageInfo pkginfo;
EXPECT_EQ(pm.getPackageInfo(g_testParams.notExistPackageName, &pkginfo),
Status::EX_SERVICE_SPECIFIC);
}
TEST_F(PmTest, InstallNotExistPackage) {
PackageInfo info;
if (pm.getPackageInfo(g_testParams.notExistPackageName, &info) != 0) {
InstallParam param;
param.path = g_testParams.notExistRpkPath;
sp<InstallListenerTest> listener = new InstallListenerTest();
int ret = pm.installPackage(param, listener);
EXPECT_EQ(ret, 0);
auto f = listener->get_future();
int result = f.get();
EXPECT_EQ(result, android::NAME_NOT_FOUND);
EXPECT_NE(pm.getPackageInfo(g_testParams.notExistPackageName, &info), 0);
}
}
extern "C" int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
std::vector<std::string> args(argv + 1, argv + argc);
for (auto it = args.begin(); it != args.end();) {
if (it->find("--packageName=") == 0) {
g_testParams.packageName = it->substr(15);
it = args.erase(it);
} else if (it->find("--rpkPath=") == 0) {
g_testParams.rpkPath = it->substr(10);
it = args.erase(it);
} else if (*it == "--packageName" && next(it) != args.end()) {
g_testParams.packageName = *(++it);
it = args.erase(prev(it), it + 1);
} else if (*it == "--rpkPath" && next(it) != args.end()) {
g_testParams.rpkPath = *(++it);
it = args.erase(prev(it), it + 1);
} else {
++it;
}
}
::testing::GTEST_FLAG(filter) = "PmTest.*";
return RUN_ALL_TESTS();
}
}
}