Adam Lesinski | 8cdca1b | 2017-09-28 15:50:03 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 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 | |
Adam Lesinski | 4670805 | 2017-09-29 14:49:15 -0700 | [diff] [blame] | 17 | #include "format/proto/ProtoSerialize.h" |
Adam Lesinski | 8cdca1b | 2017-09-28 15:50:03 -0700 | [diff] [blame] | 18 | |
Adam Lesinski | 8cdca1b | 2017-09-28 15:50:03 -0700 | [diff] [blame] | 19 | #include "ResourceUtils.h" |
Adam Lesinski | 4670805 | 2017-09-29 14:49:15 -0700 | [diff] [blame] | 20 | #include "format/proto/ProtoDeserialize.h" |
Adam Lesinski | 8cdca1b | 2017-09-28 15:50:03 -0700 | [diff] [blame] | 21 | #include "test/Test.h" |
| 22 | |
| 23 | using ::android::StringPiece; |
Adam Lesinski | 8cdca1b | 2017-09-28 15:50:03 -0700 | [diff] [blame] | 24 | using ::testing::Eq; |
| 25 | using ::testing::IsEmpty; |
| 26 | using ::testing::NotNull; |
| 27 | using ::testing::SizeIs; |
| 28 | using ::testing::StrEq; |
| 29 | |
| 30 | namespace aapt { |
| 31 | |
Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 32 | class MockFileCollection : public io::IFileCollection { |
| 33 | public: |
| 34 | MOCK_METHOD1(FindFile, io::IFile*(const StringPiece& path)); |
| 35 | MOCK_METHOD0(Iterator, std::unique_ptr<io::IFileCollectionIterator>()); |
| 36 | }; |
| 37 | |
Adam Lesinski | 8cdca1b | 2017-09-28 15:50:03 -0700 | [diff] [blame] | 38 | TEST(ProtoSerializeTest, SerializeSinglePackage) { |
| 39 | std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); |
| 40 | std::unique_ptr<ResourceTable> table = |
| 41 | test::ResourceTableBuilder() |
| 42 | .SetPackageId("com.app.a", 0x7f) |
| 43 | .AddFileReference("com.app.a:layout/main", ResourceId(0x7f020000), "res/layout/main.xml") |
| 44 | .AddReference("com.app.a:layout/other", ResourceId(0x7f020001), "com.app.a:layout/main") |
| 45 | .AddString("com.app.a:string/text", {}, "hi") |
| 46 | .AddValue("com.app.a:id/foo", {}, util::make_unique<Id>()) |
| 47 | .SetSymbolState("com.app.a:bool/foo", {}, SymbolState::kUndefined, true /*allow_new*/) |
| 48 | .Build(); |
| 49 | |
| 50 | Symbol public_symbol; |
| 51 | public_symbol.state = SymbolState::kPublic; |
| 52 | ASSERT_TRUE(table->SetSymbolState(test::ParseNameOrDie("com.app.a:layout/main"), |
| 53 | ResourceId(0x7f020000), public_symbol, |
| 54 | context->GetDiagnostics())); |
| 55 | |
| 56 | Id* id = test::GetValue<Id>(table.get(), "com.app.a:id/foo"); |
| 57 | ASSERT_THAT(id, NotNull()); |
| 58 | |
| 59 | // Make a plural. |
| 60 | std::unique_ptr<Plural> plural = util::make_unique<Plural>(); |
| 61 | plural->values[Plural::One] = util::make_unique<String>(table->string_pool.MakeRef("one")); |
| 62 | ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.app.a:plurals/hey"), ConfigDescription{}, |
| 63 | {}, std::move(plural), context->GetDiagnostics())); |
| 64 | |
| 65 | // Make a styled string. |
| 66 | StyleString style_string; |
| 67 | style_string.str = "hello"; |
| 68 | style_string.spans.push_back(Span{"b", 0u, 4u}); |
| 69 | ASSERT_TRUE( |
| 70 | table->AddResource(test::ParseNameOrDie("com.app.a:string/styled"), ConfigDescription{}, {}, |
| 71 | util::make_unique<StyledString>(table->string_pool.MakeRef(style_string)), |
| 72 | context->GetDiagnostics())); |
| 73 | |
| 74 | // Make a resource with different products. |
| 75 | ASSERT_TRUE(table->AddResource( |
| 76 | test::ParseNameOrDie("com.app.a:integer/one"), test::ParseConfigOrDie("land"), {}, |
| 77 | test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 123u), context->GetDiagnostics())); |
| 78 | ASSERT_TRUE(table->AddResource( |
| 79 | test::ParseNameOrDie("com.app.a:integer/one"), test::ParseConfigOrDie("land"), "tablet", |
| 80 | test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 321u), context->GetDiagnostics())); |
| 81 | |
| 82 | // Make a reference with both resource name and resource ID. |
| 83 | // The reference should point to a resource outside of this table to test that both name and id |
| 84 | // get serialized. |
| 85 | Reference expected_ref; |
| 86 | expected_ref.name = test::ParseNameOrDie("android:layout/main"); |
| 87 | expected_ref.id = ResourceId(0x01020000); |
| 88 | ASSERT_TRUE(table->AddResource( |
| 89 | test::ParseNameOrDie("com.app.a:layout/abc"), ConfigDescription::DefaultConfig(), {}, |
| 90 | util::make_unique<Reference>(expected_ref), context->GetDiagnostics())); |
| 91 | |
| 92 | pb::ResourceTable pb_table; |
| 93 | SerializeTableToPb(*table, &pb_table); |
| 94 | |
Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 95 | test::TestFile file_a("res/layout/main.xml"); |
| 96 | MockFileCollection files; |
| 97 | EXPECT_CALL(files, FindFile(Eq("res/layout/main.xml"))) |
| 98 | .WillRepeatedly(::testing::Return(&file_a)); |
| 99 | |
Adam Lesinski | 8cdca1b | 2017-09-28 15:50:03 -0700 | [diff] [blame] | 100 | ResourceTable new_table; |
| 101 | std::string error; |
Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 102 | ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error)); |
Adam Lesinski | 8cdca1b | 2017-09-28 15:50:03 -0700 | [diff] [blame] | 103 | EXPECT_THAT(error, IsEmpty()); |
| 104 | |
| 105 | Id* new_id = test::GetValue<Id>(&new_table, "com.app.a:id/foo"); |
| 106 | ASSERT_THAT(new_id, NotNull()); |
| 107 | EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak())); |
| 108 | |
| 109 | Maybe<ResourceTable::SearchResult> result = |
| 110 | new_table.FindResource(test::ParseNameOrDie("com.app.a:layout/main")); |
| 111 | ASSERT_TRUE(result); |
| 112 | |
| 113 | EXPECT_THAT(result.value().type->symbol_status.state, Eq(SymbolState::kPublic)); |
| 114 | EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic)); |
| 115 | |
| 116 | result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo")); |
| 117 | ASSERT_TRUE(result); |
| 118 | EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kUndefined)); |
| 119 | EXPECT_TRUE(result.value().entry->symbol_status.allow_new); |
| 120 | |
| 121 | // Find the product-dependent values |
| 122 | BinaryPrimitive* prim = test::GetValueForConfigAndProduct<BinaryPrimitive>( |
| 123 | &new_table, "com.app.a:integer/one", test::ParseConfigOrDie("land"), ""); |
| 124 | ASSERT_THAT(prim, NotNull()); |
| 125 | EXPECT_THAT(prim->value.data, Eq(123u)); |
| 126 | |
| 127 | prim = test::GetValueForConfigAndProduct<BinaryPrimitive>( |
| 128 | &new_table, "com.app.a:integer/one", test::ParseConfigOrDie("land"), "tablet"); |
| 129 | ASSERT_THAT(prim, NotNull()); |
| 130 | EXPECT_THAT(prim->value.data, Eq(321u)); |
| 131 | |
| 132 | Reference* actual_ref = test::GetValue<Reference>(&new_table, "com.app.a:layout/abc"); |
| 133 | ASSERT_THAT(actual_ref, NotNull()); |
| 134 | ASSERT_TRUE(actual_ref->name); |
| 135 | ASSERT_TRUE(actual_ref->id); |
| 136 | EXPECT_THAT(*actual_ref, Eq(expected_ref)); |
| 137 | |
Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 138 | FileReference* actual_file_ref = |
| 139 | test::GetValue<FileReference>(&new_table, "com.app.a:layout/main"); |
| 140 | ASSERT_THAT(actual_file_ref, NotNull()); |
| 141 | EXPECT_THAT(actual_file_ref->file, Eq(&file_a)); |
| 142 | |
Adam Lesinski | 8cdca1b | 2017-09-28 15:50:03 -0700 | [diff] [blame] | 143 | StyledString* actual_styled_str = |
| 144 | test::GetValue<StyledString>(&new_table, "com.app.a:string/styled"); |
| 145 | ASSERT_THAT(actual_styled_str, NotNull()); |
| 146 | EXPECT_THAT(actual_styled_str->value->value, Eq("hello")); |
| 147 | ASSERT_THAT(actual_styled_str->value->spans, SizeIs(1u)); |
| 148 | EXPECT_THAT(*actual_styled_str->value->spans[0].name, Eq("b")); |
| 149 | EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u)); |
| 150 | EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u)); |
| 151 | } |
| 152 | |
Adam Lesinski | 8cdca1b | 2017-09-28 15:50:03 -0700 | [diff] [blame] | 153 | TEST(ProtoSerializeTest, SerializeAndDeserializeXml) { |
| 154 | xml::Element element; |
| 155 | element.line_number = 22; |
| 156 | element.column_number = 23; |
| 157 | element.name = "element"; |
| 158 | element.namespace_uri = "uri://"; |
| 159 | |
| 160 | xml::NamespaceDecl decl; |
| 161 | decl.prefix = "android"; |
| 162 | decl.uri = xml::kSchemaAndroid; |
| 163 | decl.line_number = 21; |
| 164 | decl.column_number = 24; |
| 165 | |
| 166 | element.namespace_decls.push_back(decl); |
| 167 | |
| 168 | xml::Attribute attr; |
| 169 | attr.name = "name"; |
| 170 | attr.namespace_uri = xml::kSchemaAndroid; |
| 171 | attr.value = "23dp"; |
| 172 | attr.compiled_attribute = xml::AaptAttribute({}, ResourceId(0x01010000)); |
| 173 | attr.compiled_value = |
| 174 | ResourceUtils::TryParseItemForAttribute(attr.value, android::ResTable_map::TYPE_DIMENSION); |
| 175 | attr.compiled_value->SetSource(Source().WithLine(25)); |
| 176 | element.attributes.push_back(std::move(attr)); |
| 177 | |
| 178 | std::unique_ptr<xml::Text> text = util::make_unique<xml::Text>(); |
| 179 | text->line_number = 25; |
| 180 | text->column_number = 3; |
| 181 | text->text = "hey there"; |
| 182 | element.AppendChild(std::move(text)); |
| 183 | |
| 184 | std::unique_ptr<xml::Element> child = util::make_unique<xml::Element>(); |
| 185 | child->name = "child"; |
| 186 | |
| 187 | text = util::make_unique<xml::Text>(); |
| 188 | text->text = "woah there"; |
| 189 | child->AppendChild(std::move(text)); |
| 190 | |
| 191 | element.AppendChild(std::move(child)); |
| 192 | |
| 193 | pb::XmlNode pb_xml; |
| 194 | SerializeXmlToPb(element, &pb_xml); |
| 195 | |
| 196 | StringPool pool; |
| 197 | xml::Element actual_el; |
| 198 | std::string error; |
| 199 | ASSERT_TRUE(DeserializeXmlFromPb(pb_xml, &actual_el, &pool, &error)); |
| 200 | ASSERT_THAT(error, IsEmpty()); |
| 201 | |
| 202 | EXPECT_THAT(actual_el.name, StrEq("element")); |
| 203 | EXPECT_THAT(actual_el.namespace_uri, StrEq("uri://")); |
| 204 | EXPECT_THAT(actual_el.line_number, Eq(22u)); |
| 205 | EXPECT_THAT(actual_el.column_number, Eq(23u)); |
| 206 | |
| 207 | ASSERT_THAT(actual_el.namespace_decls, SizeIs(1u)); |
| 208 | const xml::NamespaceDecl& actual_decl = actual_el.namespace_decls[0]; |
| 209 | EXPECT_THAT(actual_decl.prefix, StrEq("android")); |
| 210 | EXPECT_THAT(actual_decl.uri, StrEq(xml::kSchemaAndroid)); |
| 211 | EXPECT_THAT(actual_decl.line_number, Eq(21u)); |
| 212 | EXPECT_THAT(actual_decl.column_number, Eq(24u)); |
| 213 | |
| 214 | ASSERT_THAT(actual_el.attributes, SizeIs(1u)); |
| 215 | const xml::Attribute& actual_attr = actual_el.attributes[0]; |
| 216 | EXPECT_THAT(actual_attr.name, StrEq("name")); |
| 217 | EXPECT_THAT(actual_attr.namespace_uri, StrEq(xml::kSchemaAndroid)); |
| 218 | EXPECT_THAT(actual_attr.value, StrEq("23dp")); |
| 219 | |
| 220 | ASSERT_THAT(actual_attr.compiled_value, NotNull()); |
| 221 | const BinaryPrimitive* prim = ValueCast<BinaryPrimitive>(actual_attr.compiled_value.get()); |
| 222 | ASSERT_THAT(prim, NotNull()); |
| 223 | EXPECT_THAT(prim->value.dataType, Eq(android::Res_value::TYPE_DIMENSION)); |
| 224 | |
| 225 | ASSERT_TRUE(actual_attr.compiled_attribute); |
| 226 | ASSERT_TRUE(actual_attr.compiled_attribute.value().id); |
| 227 | |
| 228 | ASSERT_THAT(actual_el.children, SizeIs(2u)); |
| 229 | const xml::Text* child_text = xml::NodeCast<xml::Text>(actual_el.children[0].get()); |
| 230 | ASSERT_THAT(child_text, NotNull()); |
| 231 | const xml::Element* child_el = xml::NodeCast<xml::Element>(actual_el.children[1].get()); |
| 232 | ASSERT_THAT(child_el, NotNull()); |
| 233 | |
| 234 | EXPECT_THAT(child_text->line_number, Eq(25u)); |
| 235 | EXPECT_THAT(child_text->column_number, Eq(3u)); |
| 236 | EXPECT_THAT(child_text->text, StrEq("hey there")); |
| 237 | |
| 238 | EXPECT_THAT(child_el->name, StrEq("child")); |
| 239 | ASSERT_THAT(child_el->children, SizeIs(1u)); |
| 240 | |
| 241 | child_text = xml::NodeCast<xml::Text>(child_el->children[0].get()); |
| 242 | ASSERT_THAT(child_text, NotNull()); |
| 243 | EXPECT_THAT(child_text->text, StrEq("woah there")); |
| 244 | } |
| 245 | |
| 246 | static void ExpectConfigSerializes(const StringPiece& config_str) { |
| 247 | const ConfigDescription expected_config = test::ParseConfigOrDie(config_str); |
| 248 | pb::Configuration pb_config; |
| 249 | SerializeConfig(expected_config, &pb_config); |
| 250 | |
| 251 | ConfigDescription actual_config; |
| 252 | std::string error; |
| 253 | ASSERT_TRUE(DeserializeConfigFromPb(pb_config, &actual_config, &error)); |
| 254 | ASSERT_THAT(error, IsEmpty()); |
| 255 | EXPECT_EQ(expected_config, actual_config); |
| 256 | } |
| 257 | |
| 258 | TEST(ProtoSerializeTest, SerializeDeserializeConfiguration) { |
| 259 | ExpectConfigSerializes(""); |
| 260 | |
| 261 | ExpectConfigSerializes("mcc123"); |
| 262 | |
| 263 | ExpectConfigSerializes("mnc123"); |
| 264 | |
| 265 | ExpectConfigSerializes("en"); |
| 266 | ExpectConfigSerializes("en-rGB"); |
| 267 | ExpectConfigSerializes("b+en+GB"); |
| 268 | |
| 269 | ExpectConfigSerializes("ldltr"); |
| 270 | ExpectConfigSerializes("ldrtl"); |
| 271 | |
| 272 | ExpectConfigSerializes("sw3600dp"); |
| 273 | |
| 274 | ExpectConfigSerializes("w300dp"); |
| 275 | |
| 276 | ExpectConfigSerializes("h400dp"); |
| 277 | |
| 278 | ExpectConfigSerializes("small"); |
| 279 | ExpectConfigSerializes("normal"); |
| 280 | ExpectConfigSerializes("large"); |
| 281 | ExpectConfigSerializes("xlarge"); |
| 282 | |
| 283 | ExpectConfigSerializes("long"); |
| 284 | ExpectConfigSerializes("notlong"); |
| 285 | |
| 286 | ExpectConfigSerializes("round"); |
| 287 | ExpectConfigSerializes("notround"); |
| 288 | |
| 289 | ExpectConfigSerializes("widecg"); |
| 290 | ExpectConfigSerializes("nowidecg"); |
| 291 | |
| 292 | ExpectConfigSerializes("highdr"); |
| 293 | ExpectConfigSerializes("lowdr"); |
| 294 | |
| 295 | ExpectConfigSerializes("port"); |
| 296 | ExpectConfigSerializes("land"); |
| 297 | ExpectConfigSerializes("square"); |
| 298 | |
| 299 | ExpectConfigSerializes("desk"); |
| 300 | ExpectConfigSerializes("car"); |
| 301 | ExpectConfigSerializes("television"); |
| 302 | ExpectConfigSerializes("appliance"); |
| 303 | ExpectConfigSerializes("watch"); |
| 304 | ExpectConfigSerializes("vrheadset"); |
| 305 | |
| 306 | ExpectConfigSerializes("night"); |
| 307 | ExpectConfigSerializes("notnight"); |
| 308 | |
| 309 | ExpectConfigSerializes("300dpi"); |
| 310 | ExpectConfigSerializes("hdpi"); |
| 311 | |
| 312 | ExpectConfigSerializes("notouch"); |
| 313 | ExpectConfigSerializes("stylus"); |
| 314 | ExpectConfigSerializes("finger"); |
| 315 | |
| 316 | ExpectConfigSerializes("keysexposed"); |
| 317 | ExpectConfigSerializes("keyshidden"); |
| 318 | ExpectConfigSerializes("keyssoft"); |
| 319 | |
| 320 | ExpectConfigSerializes("nokeys"); |
| 321 | ExpectConfigSerializes("qwerty"); |
| 322 | ExpectConfigSerializes("12key"); |
| 323 | |
| 324 | ExpectConfigSerializes("navhidden"); |
| 325 | ExpectConfigSerializes("navexposed"); |
| 326 | |
| 327 | ExpectConfigSerializes("nonav"); |
| 328 | ExpectConfigSerializes("dpad"); |
| 329 | ExpectConfigSerializes("trackball"); |
| 330 | ExpectConfigSerializes("wheel"); |
| 331 | |
| 332 | ExpectConfigSerializes("300x200"); |
| 333 | |
| 334 | ExpectConfigSerializes("v8"); |
| 335 | |
| 336 | ExpectConfigSerializes( |
| 337 | "mcc123-mnc456-b+en+GB-ldltr-sw300dp-w300dp-h400dp-large-long-round-widecg-highdr-land-car-" |
| 338 | "night-xhdpi-stylus-keysexposed-qwerty-navhidden-dpad-300x200-v23"); |
| 339 | } |
| 340 | |
| 341 | } // namespace aapt |