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