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