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 17#include "proto/ProtoSerialize.h" 18 19#include "ResourceTable.h" 20#include "test/Test.h" 21 22using ::google::protobuf::io::StringOutputStream; 23using ::testing::Eq; 24using ::testing::NotNull; 25using ::testing::SizeIs; 26 27namespace aapt { 28 29TEST(TableProtoSerializer, SerializeSinglePackage) { 30 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); 31 std::unique_ptr<ResourceTable> table = 32 test::ResourceTableBuilder() 33 .SetPackageId("com.app.a", 0x7f) 34 .AddFileReference("com.app.a:layout/main", ResourceId(0x7f020000), "res/layout/main.xml") 35 .AddReference("com.app.a:layout/other", ResourceId(0x7f020001), "com.app.a:layout/main") 36 .AddString("com.app.a:string/text", {}, "hi") 37 .AddValue("com.app.a:id/foo", {}, util::make_unique<Id>()) 38 .SetSymbolState("com.app.a:bool/foo", {}, SymbolState::kUndefined, true /*allow_new*/) 39 .Build(); 40 41 Symbol public_symbol; 42 public_symbol.state = SymbolState::kPublic; 43 ASSERT_TRUE(table->SetSymbolState(test::ParseNameOrDie("com.app.a:layout/main"), 44 ResourceId(0x7f020000), public_symbol, 45 context->GetDiagnostics())); 46 47 Id* id = test::GetValue<Id>(table.get(), "com.app.a:id/foo"); 48 ASSERT_THAT(id, NotNull()); 49 50 // Make a plural. 51 std::unique_ptr<Plural> plural = util::make_unique<Plural>(); 52 plural->values[Plural::One] = util::make_unique<String>(table->string_pool.MakeRef("one")); 53 ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.app.a:plurals/hey"), 54 ConfigDescription{}, {}, std::move(plural), 55 context->GetDiagnostics())); 56 57 // Make a styled string. 58 StyleString style_string; 59 style_string.str = "hello"; 60 style_string.spans.push_back(Span{"b", 0u, 4u}); 61 ASSERT_TRUE( 62 table->AddResource(test::ParseNameOrDie("com.app.a:string/styled"), ConfigDescription{}, {}, 63 util::make_unique<StyledString>(table->string_pool.MakeRef(style_string)), 64 context->GetDiagnostics())); 65 66 // Make a resource with different products. 67 ASSERT_TRUE(table->AddResource( 68 test::ParseNameOrDie("com.app.a:integer/one"), 69 test::ParseConfigOrDie("land"), {}, 70 test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 123u), 71 context->GetDiagnostics())); 72 ASSERT_TRUE(table->AddResource( 73 test::ParseNameOrDie("com.app.a:integer/one"), 74 test::ParseConfigOrDie("land"), "tablet", 75 test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 321u), 76 context->GetDiagnostics())); 77 78 // Make a reference with both resource name and resource ID. 79 // The reference should point to a resource outside of this table to test that both name and id 80 // get serialized. 81 Reference expected_ref; 82 expected_ref.name = test::ParseNameOrDie("android:layout/main"); 83 expected_ref.id = ResourceId(0x01020000); 84 ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.app.a:layout/abc"), 85 ConfigDescription::DefaultConfig(), {}, 86 util::make_unique<Reference>(expected_ref), 87 context->GetDiagnostics())); 88 89 std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(table.get()); 90 ASSERT_THAT(pb_table, NotNull()); 91 92 std::unique_ptr<ResourceTable> new_table = DeserializeTableFromPb( 93 *pb_table, Source{"test"}, context->GetDiagnostics()); 94 ASSERT_THAT(new_table, NotNull()); 95 96 Id* new_id = test::GetValue<Id>(new_table.get(), "com.app.a:id/foo"); 97 ASSERT_THAT(new_id, NotNull()); 98 EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak())); 99 100 Maybe<ResourceTable::SearchResult> result = 101 new_table->FindResource(test::ParseNameOrDie("com.app.a:layout/main")); 102 ASSERT_TRUE(result); 103 104 EXPECT_THAT(result.value().type->symbol_status.state, Eq(SymbolState::kPublic)); 105 EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic)); 106 107 result = new_table->FindResource(test::ParseNameOrDie("com.app.a:bool/foo")); 108 ASSERT_TRUE(result); 109 EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kUndefined)); 110 EXPECT_TRUE(result.value().entry->symbol_status.allow_new); 111 112 // Find the product-dependent values 113 BinaryPrimitive* prim = test::GetValueForConfigAndProduct<BinaryPrimitive>( 114 new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), ""); 115 ASSERT_THAT(prim, NotNull()); 116 EXPECT_THAT(prim->value.data, Eq(123u)); 117 118 prim = test::GetValueForConfigAndProduct<BinaryPrimitive>( 119 new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "tablet"); 120 ASSERT_THAT(prim, NotNull()); 121 EXPECT_THAT(prim->value.data, Eq(321u)); 122 123 Reference* actual_ref = test::GetValue<Reference>(new_table.get(), "com.app.a:layout/abc"); 124 ASSERT_THAT(actual_ref, NotNull()); 125 ASSERT_TRUE(actual_ref->name); 126 ASSERT_TRUE(actual_ref->id); 127 EXPECT_THAT(*actual_ref, Eq(expected_ref)); 128 129 StyledString* actual_styled_str = 130 test::GetValue<StyledString>(new_table.get(), "com.app.a:string/styled"); 131 ASSERT_THAT(actual_styled_str, NotNull()); 132 EXPECT_THAT(actual_styled_str->value->value, Eq("hello")); 133 ASSERT_THAT(actual_styled_str->value->spans, SizeIs(1u)); 134 EXPECT_THAT(*actual_styled_str->value->spans[0].name, Eq("b")); 135 EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u)); 136 EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u)); 137} 138 139TEST(TableProtoSerializer, SerializeFileHeader) { 140 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); 141 142 ResourceFile f; 143 f.config = test::ParseConfigOrDie("hdpi-v9"); 144 f.name = test::ParseNameOrDie("com.app.a:layout/main"); 145 f.source.path = "res/layout-hdpi-v9/main.xml"; 146 f.exported_symbols.push_back( 147 SourcedResourceName{test::ParseNameOrDie("id/unchecked"), 23u}); 148 149 const std::string expected_data1 = "123"; 150 const std::string expected_data2 = "1234"; 151 152 std::string output_str; 153 { 154 std::unique_ptr<pb::internal::CompiledFile> pb_file1 = SerializeCompiledFileToPb(f); 155 156 f.name.entry = "__" + f.name.entry + "$0"; 157 std::unique_ptr<pb::internal::CompiledFile> pb_file2 = SerializeCompiledFileToPb(f); 158 159 StringOutputStream out_stream(&output_str); 160 CompiledFileOutputStream out_file_stream(&out_stream); 161 out_file_stream.WriteLittleEndian32(2); 162 out_file_stream.WriteCompiledFile(pb_file1.get()); 163 out_file_stream.WriteData(expected_data1.data(), expected_data1.size()); 164 out_file_stream.WriteCompiledFile(pb_file2.get()); 165 out_file_stream.WriteData(expected_data2.data(), expected_data2.size()); 166 ASSERT_FALSE(out_file_stream.HadError()); 167 } 168 169 CompiledFileInputStream in_file_stream(output_str.data(), output_str.size()); 170 uint32_t num_files = 0; 171 ASSERT_TRUE(in_file_stream.ReadLittleEndian32(&num_files)); 172 ASSERT_EQ(2u, num_files); 173 174 // Read the first compiled file. 175 176 pb::internal::CompiledFile new_pb_file; 177 ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_file)); 178 179 std::unique_ptr<ResourceFile> file = DeserializeCompiledFileFromPb( 180 new_pb_file, Source("test"), context->GetDiagnostics()); 181 ASSERT_THAT(file, NotNull()); 182 183 uint64_t offset, len; 184 ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len)); 185 186 std::string actual_data(output_str.data() + offset, len); 187 EXPECT_EQ(expected_data1, actual_data); 188 189 // Expect the data to be aligned. 190 EXPECT_EQ(0u, offset & 0x03); 191 192 ASSERT_EQ(1u, file->exported_symbols.size()); 193 EXPECT_EQ(test::ParseNameOrDie("id/unchecked"), file->exported_symbols[0].name); 194 195 // Read the second compiled file. 196 197 ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_file)); 198 199 file = DeserializeCompiledFileFromPb(new_pb_file, Source("test"), context->GetDiagnostics()); 200 ASSERT_THAT(file, NotNull()); 201 202 ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len)); 203 204 actual_data = std::string(output_str.data() + offset, len); 205 EXPECT_EQ(expected_data2, actual_data); 206 207 // Expect the data to be aligned. 208 EXPECT_EQ(0u, offset & 0x03); 209} 210 211TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) { 212 ResourceFile f; 213 std::unique_ptr<pb::internal::CompiledFile> pb_file = SerializeCompiledFileToPb(f); 214 215 const std::string expected_data = "1234"; 216 217 std::string output_str; 218 { 219 StringOutputStream out_stream(&output_str); 220 CompiledFileOutputStream out_file_stream(&out_stream); 221 out_file_stream.WriteLittleEndian32(1); 222 out_file_stream.WriteCompiledFile(pb_file.get()); 223 out_file_stream.WriteData(expected_data.data(), expected_data.size()); 224 ASSERT_FALSE(out_file_stream.HadError()); 225 } 226 227 output_str[4] = 0xff; 228 229 CompiledFileInputStream in_file_stream(output_str.data(), output_str.size()); 230 231 uint32_t num_files = 0; 232 EXPECT_TRUE(in_file_stream.ReadLittleEndian32(&num_files)); 233 EXPECT_EQ(1u, num_files); 234 235 pb::internal::CompiledFile new_pb_file; 236 EXPECT_FALSE(in_file_stream.ReadCompiledFile(&new_pb_file)); 237 238 uint64_t offset, len; 239 EXPECT_FALSE(in_file_stream.ReadDataMetaData(&offset, &len)); 240} 241 242} // namespace aapt 243