1 // 2 // Copyright (C) 2019 The Android Open Source Project 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 #ifndef UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_ 18 #define UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_ 19 20 #include <stdint.h> 21 22 #include <iostream> 23 #include <map> 24 #include <memory> 25 #include <string> 26 #include <vector> 27 28 #include <base/strings/string_util.h> 29 #include <fs_mgr.h> 30 #include <gmock/gmock.h> 31 #include <gtest/gtest.h> 32 #include <liblp/builder.h> 33 #include <storage_literals/storage_literals.h> 34 35 #include "update_engine/common/boot_control_interface.h" 36 #include "update_engine/update_metadata.pb.h" 37 38 namespace chromeos_update_engine { 39 40 using android::fs_mgr::MetadataBuilder; 41 using testing::_; 42 using testing::MakeMatcher; 43 using testing::Matcher; 44 using testing::MatcherInterface; 45 using testing::MatchResultListener; 46 using namespace android::storage_literals; // NOLINT(build/namespaces) 47 48 constexpr const uint32_t kMaxNumSlots = 2; 49 constexpr const char* kSlotSuffixes[kMaxNumSlots] = {"_a", "_b"}; 50 constexpr const char* kFakeDevicePath = "/fake/dev/path/"; 51 constexpr const char* kFakeDmDevicePath = "/fake/dm/dev/path/"; 52 constexpr const uint32_t kFakeMetadataSize = 65536; 53 constexpr const char* kDefaultGroup = "foo"; 54 constexpr const char* kFakeSuper = "fake_super"; 55 56 // A map describing the size of each partition. 57 // "{name, size}" 58 using PartitionSizes = std::map<std::string, uint64_t>; 59 60 // "{name_a, size}" 61 using PartitionSuffixSizes = std::map<std::string, uint64_t>; 62 63 constexpr uint64_t kDefaultGroupSize = 5_GiB; 64 // Super device size. 1 MiB for metadata. 65 constexpr uint64_t kDefaultSuperSize = kDefaultGroupSize * 2 + 1_MiB; 66 67 template <typename U, typename V> 68 inline std::ostream& operator<<(std::ostream& os, const std::map<U, V>& param) { 69 os << "{"; 70 bool first = true; 71 for (const auto& pair : param) { 72 if (!first) 73 os << ", "; 74 os << pair.first << ":" << pair.second; 75 first = false; 76 } 77 return os << "}"; 78 } 79 80 template <typename V> 81 inline void VectorToStream(std::ostream& os, const V& param) { 82 os << "["; 83 bool first = true; 84 for (const auto& e : param) { 85 if (!first) 86 os << ", "; 87 os << e; 88 first = false; 89 } 90 os << "]"; 91 } 92 93 inline std::ostream& operator<<(std::ostream& os, const PartitionUpdate& p) { 94 return os << "{" << p.partition_name() << ", " 95 << p.new_partition_info().size() << "}"; 96 } 97 98 inline std::ostream& operator<<(std::ostream& os, 99 const DynamicPartitionGroup& g) { 100 os << "{" << g.name() << ", " << g.size() << ", "; 101 VectorToStream(os, g.partition_names()); 102 return os << "}"; 103 } 104 105 inline std::ostream& operator<<(std::ostream& os, 106 const DeltaArchiveManifest& m) { 107 os << "{.groups = "; 108 VectorToStream(os, m.dynamic_partition_metadata().groups()); 109 os << ", .partitions = "; 110 VectorToStream(os, m.partitions()); 111 return os; 112 } 113 114 inline std::string GetDevice(const std::string& name) { 115 return kFakeDevicePath + name; 116 } 117 118 inline std::string GetDmDevice(const std::string& name) { 119 return kFakeDmDevicePath + name; 120 } 121 122 inline DynamicPartitionGroup* AddGroup(DeltaArchiveManifest* manifest, 123 const std::string& group, 124 uint64_t group_size) { 125 auto* g = manifest->mutable_dynamic_partition_metadata()->add_groups(); 126 g->set_name(group); 127 g->set_size(group_size); 128 return g; 129 } 130 131 inline void AddPartition(DeltaArchiveManifest* manifest, 132 DynamicPartitionGroup* group, 133 const std::string& partition, 134 uint64_t partition_size) { 135 group->add_partition_names(partition); 136 auto* p = manifest->add_partitions(); 137 p->set_partition_name(partition); 138 p->mutable_new_partition_info()->set_size(partition_size); 139 } 140 141 // To support legacy tests, auto-convert {name_a: size} map to 142 // DeltaArchiveManifest. 143 inline DeltaArchiveManifest PartitionSuffixSizesToManifest( 144 const PartitionSuffixSizes& partition_sizes) { 145 DeltaArchiveManifest manifest; 146 for (const char* suffix : kSlotSuffixes) { 147 AddGroup(&manifest, std::string(kDefaultGroup) + suffix, kDefaultGroupSize); 148 } 149 for (const auto& pair : partition_sizes) { 150 for (size_t suffix_idx = 0; suffix_idx < kMaxNumSlots; ++suffix_idx) { 151 if (base::EndsWith(pair.first, 152 kSlotSuffixes[suffix_idx], 153 base::CompareCase::SENSITIVE)) { 154 AddPartition( 155 &manifest, 156 manifest.mutable_dynamic_partition_metadata()->mutable_groups( 157 suffix_idx), 158 pair.first, 159 pair.second); 160 } 161 } 162 } 163 return manifest; 164 } 165 166 // To support legacy tests, auto-convert {name: size} map to PartitionMetadata. 167 inline DeltaArchiveManifest PartitionSizesToManifest( 168 const PartitionSizes& partition_sizes) { 169 DeltaArchiveManifest manifest; 170 auto* g = AddGroup(&manifest, std::string(kDefaultGroup), kDefaultGroupSize); 171 for (const auto& pair : partition_sizes) { 172 AddPartition(&manifest, g, pair.first, pair.second); 173 } 174 return manifest; 175 } 176 177 inline std::unique_ptr<MetadataBuilder> NewFakeMetadata( 178 const DeltaArchiveManifest& manifest, uint32_t partition_attr = 0) { 179 auto builder = 180 MetadataBuilder::New(kDefaultSuperSize, kFakeMetadataSize, kMaxNumSlots); 181 for (const auto& group : manifest.dynamic_partition_metadata().groups()) { 182 EXPECT_TRUE(builder->AddGroup(group.name(), group.size())); 183 for (const auto& partition_name : group.partition_names()) { 184 EXPECT_NE( 185 nullptr, 186 builder->AddPartition(partition_name, group.name(), partition_attr)); 187 } 188 } 189 for (const auto& partition : manifest.partitions()) { 190 auto p = builder->FindPartition(partition.partition_name()); 191 EXPECT_TRUE(p && builder->ResizePartition( 192 p, partition.new_partition_info().size())); 193 } 194 return builder; 195 } 196 197 class MetadataMatcher : public MatcherInterface<MetadataBuilder*> { 198 public: 199 explicit MetadataMatcher(const PartitionSuffixSizes& partition_sizes) 200 : manifest_(PartitionSuffixSizesToManifest(partition_sizes)) {} 201 explicit MetadataMatcher(const DeltaArchiveManifest& manifest) 202 : manifest_(manifest) {} 203 204 bool MatchAndExplain(MetadataBuilder* metadata, 205 MatchResultListener* listener) const override { 206 bool success = true; 207 for (const auto& group : manifest_.dynamic_partition_metadata().groups()) { 208 for (const auto& partition_name : group.partition_names()) { 209 auto p = metadata->FindPartition(partition_name); 210 if (p == nullptr) { 211 if (!success) 212 *listener << "; "; 213 *listener << "No partition " << partition_name; 214 success = false; 215 continue; 216 } 217 const auto& partition_updates = manifest_.partitions(); 218 auto it = std::find_if(partition_updates.begin(), 219 partition_updates.end(), 220 [&](const auto& p) { 221 return p.partition_name() == partition_name; 222 }); 223 if (it == partition_updates.end()) { 224 *listener << "Can't find partition update " << partition_name; 225 success = false; 226 continue; 227 } 228 auto partition_size = it->new_partition_info().size(); 229 if (p->size() != partition_size) { 230 if (!success) 231 *listener << "; "; 232 *listener << "Partition " << partition_name << " has size " 233 << p->size() << ", expected " << partition_size; 234 success = false; 235 } 236 if (p->group_name() != group.name()) { 237 if (!success) 238 *listener << "; "; 239 *listener << "Partition " << partition_name << " has group " 240 << p->group_name() << ", expected " << group.name(); 241 success = false; 242 } 243 } 244 } 245 return success; 246 } 247 248 void DescribeTo(std::ostream* os) const override { 249 *os << "expect: " << manifest_; 250 } 251 252 void DescribeNegationTo(std::ostream* os) const override { 253 *os << "expect not: " << manifest_; 254 } 255 256 private: 257 DeltaArchiveManifest manifest_; 258 }; 259 260 inline Matcher<MetadataBuilder*> MetadataMatches( 261 const PartitionSuffixSizes& partition_sizes) { 262 return MakeMatcher(new MetadataMatcher(partition_sizes)); 263 } 264 265 inline Matcher<MetadataBuilder*> MetadataMatches( 266 const DeltaArchiveManifest& manifest) { 267 return MakeMatcher(new MetadataMatcher(manifest)); 268 } 269 270 MATCHER_P(HasGroup, group, " has group " + group) { 271 auto groups = arg->ListGroups(); 272 return std::find(groups.begin(), groups.end(), group) != groups.end(); 273 } 274 275 struct TestParam { 276 uint32_t source; 277 uint32_t target; 278 }; 279 inline std::ostream& operator<<(std::ostream& os, const TestParam& param) { 280 return os << "{source: " << param.source << ", target:" << param.target 281 << "}"; 282 } 283 284 } // namespace chromeos_update_engine 285 286 #endif // UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_ 287