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