Home | History | Annotate | Download | only in proto
      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 
     22 using ::google::protobuf::io::StringOutputStream;
     23 using ::testing::Eq;
     24 using ::testing::NotNull;
     25 using ::testing::SizeIs;
     26 
     27 namespace aapt {
     28 
     29 TEST(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 
    139 TEST(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 
    211 TEST(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