blob: 24c3d6250c5719cae58fdd448a8d885f94844dd3 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* 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 "apex_info.h"
#include "apex_info_cache.h"
#include <string>
#include <unistd.h>
#include <utility>
#include <android-base/file.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "com_android_apex.h"
using namespace std::literals;
using ::android::apex::util::ApexInfoCache;
using ::android::apex::util::ApexType;
using ::android::base::WriteStringToFile;
// Create an ApexInfo object based on inputs, writing data to the apex_dir/name
// directory
com::android::apex::ApexInfo
CreateApex(const std::string &name, const std::string &pre_path, int version) {
auto version_name = std::to_string(version);
std::string path = "/apex/" + name;
return (com::android::apex::ApexInfo(name, path, pre_path, version,
version_name, false, true, 0, false));
}
// Write info file
void UpdateApexInfoList(const std::string &info_file,
std::vector<com::android::apex::ApexInfo> &apex_infos) {
com::android::apex::ApexInfoList apex_info_list(apex_infos);
std::stringstream xml;
com::android::apex::write(xml, apex_info_list);
ASSERT_TRUE(WriteStringToFile(xml.str(), info_file));
}
void CreateApexData(const std::string &apex_dir, const std::string &info_file,
std::vector<com::android::apex::ApexInfo> &apex_infos) {
ASSERT_NE(-1, mkdir(apex_dir.c_str(), 0755) == -1)
<< "Failed to create apex directory" << apex_dir;
// Create partition of a given APEX type and add to apex info
auto ASSERT_CREATE_PARTITION_APEX = [&apex_dir, &apex_infos](
std::set<std::string> &names,
const std::string &partition) {
for (const auto &name : names) {
apex_infos.emplace_back(
CreateApex(name, std::string(partition).append("/").append(name), 1));
}
};
// Partition APEXes
std::set<std::string> vendor{"com.test_vendor.1", "com.test_vendor.2",
"com.test_vendor.3"};
std::set<std::string> system{"com.test_system.1"};
std::set<std::string> system_ext{"com.test_system_ext.1"};
std::set<std::string> product{"com.test_product.1"};
std::set<std::string> odm{"com.test_odm.1"};
ASSERT_CREATE_PARTITION_APEX(vendor, "/vendor/apex");
ASSERT_CREATE_PARTITION_APEX(system, "/system/apex");
ASSERT_CREATE_PARTITION_APEX(system_ext, "/system_ext/apex");
ASSERT_CREATE_PARTITION_APEX(product, "/product/apex");
ASSERT_CREATE_PARTITION_APEX(odm, "/odm/apex");
// Write the info list
UpdateApexInfoList(info_file, apex_infos);
auto apex = android::apex::util::GetApexes(apex_dir, info_file);
auto exp_size = system.size() + system_ext.size() + product.size() +
vendor.size() + odm.size();
ASSERT_TRUE(apex.ok());
ASSERT_EQ((*apex).size(), exp_size)
<< "Got apex size " << (*apex).size() << " expected size " << exp_size;
for (const auto &obj : *apex) {
switch (obj.Type()) {
case (ApexType::kSystem):
ASSERT_EQ(system.count(obj.ManifestName()) +
system_ext.count(obj.ManifestName()),
1);
system.erase(obj.ManifestName());
system_ext.erase(obj.ManifestName());
break;
case (ApexType::kProduct):
ASSERT_EQ(product.count(obj.ManifestName()), 1);
product.erase(obj.ManifestName());
break;
case (ApexType::kVendor):
ASSERT_EQ(vendor.count(obj.ManifestName()), 1);
vendor.erase(obj.ManifestName());
break;
case (ApexType::kOdm):
ASSERT_EQ(odm.count(obj.ManifestName()), 1);
odm.erase(obj.ManifestName());
break;
}
}
// Assert all modules identified
ASSERT_EQ(system.size(), 0);
ASSERT_EQ(system_ext.size(), 0);
ASSERT_EQ(product.size(), 0);
ASSERT_EQ(vendor.size(), 0);
ASSERT_EQ(odm.size(), 0);
}
TEST(ApexUtil, ApexInfo) {
TemporaryDir td;
const std::string apex_dir = td.path + "/apex/"s;
const std::string info_file = apex_dir + ::android::apex::kApexInfoFileName;
std::vector<com::android::apex::ApexInfo> apex_infos;
CreateApexData(apex_dir, info_file, apex_infos);
}
TEST(ApexUtil, ApexInfoCache) {
// Monitor based on Update()
TemporaryDir td;
const std::string apex_dir = td.path + "/apex/"s;
const std::string info_file = apex_dir + ::android::apex::kApexInfoFileName;
ASSERT_EQ(mkdir(apex_dir.c_str(), 0755), 0)
<< "Failed to create apex directory" << apex_dir;
ApexInfoCache m(apex_dir, info_file);
auto ASSERT_NEW_DATA = [&m](bool bExp) {
auto ret = m.HasNewData();
ASSERT_EQ(ret.ok(), true);
ASSERT_EQ(*ret, bExp);
};
auto ASSERT_UPDATE = [&m](bool bExp) {
auto ret = m.Update();
ASSERT_EQ(ret.ok(), true);
ASSERT_EQ(*ret, bExp);
};
auto ASSERT_UPDATE_ERROR = [&m]() {
auto ret = m.Update();
ASSERT_EQ(ret.ok(), false);
};
ASSERT_UPDATE_ERROR();
// Need to setup actual data to use update
std::vector<com::android::apex::ApexInfo> apex_infos;
CreateApexData(apex_dir, info_file, apex_infos);
// validate new data is available
ASSERT_NEW_DATA(true);
ASSERT_UPDATE(true);
// No new updates
ASSERT_NEW_DATA(false);
ASSERT_UPDATE(false);
// Check existing file update
sleep(1); // make sure timestamps change
UpdateApexInfoList(info_file, apex_infos);
ASSERT_NEW_DATA(true);
ASSERT_UPDATE(true);
// Check that new data can be detected directly with update
sleep(1); // make sure timestamps change
UpdateApexInfoList(info_file, apex_infos);
ASSERT_UPDATE(true);
ASSERT_NEW_DATA(false); // new data should be consumed by above call
}