1 /*
2  * Copyright (C) 2021 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 <android-base/file.h>
18 
19 #include <gtest/gtest.h>
20 
21 #include <MemoryBuffer.h>
22 #include <MemoryXz.h>
23 
24 namespace unwindstack {
25 
26 class MemoryXzTest : public ::testing::Test {
27  protected:
SetUp()28   void SetUp() override { expected_content_ = ReadFile("boot_arm.oat.gnu_debugdata"); }
29 
ReadFile(const char * filename)30   static std::unique_ptr<MemoryBuffer> ReadFile(const char* filename) {
31     std::string dir = android::base::GetExecutableDirectory() + "/tests/files/";
32     std::string data;  // NB: It is actually binary data.
33     EXPECT_TRUE(android::base::ReadFileToString(dir + filename, &data)) << filename;
34     EXPECT_GT(data.size(), 0u);
35     auto memory = std::make_unique<MemoryBuffer>(data.size());
36     EXPECT_EQ(data.size(), memory->Size());
37     memcpy(memory->GetPtr(0), data.data(), data.size());
38     return memory;
39   }
40 
VerifyContent(MemoryXz & xz,uint64_t offset,uint64_t size)41   void VerifyContent(MemoryXz& xz, uint64_t offset, uint64_t size) {
42     EXPECT_EQ(xz.Size(), expected_content_->Size());
43     EXPECT_LE(offset + size, expected_content_->Size());
44     std::vector<uint8_t> seen_content(size);
45     xz.ReadFully(offset, seen_content.data(), size);
46     EXPECT_EQ(memcmp(seen_content.data(), expected_content_->GetPtr(offset), size), 0);
47   }
48 
49   std::unique_ptr<MemoryBuffer> expected_content_;
50 };
51 
52 // Test the expected random-accessible format.
TEST_F(MemoryXzTest,Decompress)53 TEST_F(MemoryXzTest, Decompress) {
54   auto compressed = ReadFile("boot_arm.oat.gnu_debugdata.xz");
55   MemoryXz xz(compressed.get(), 0, compressed->Size(), "boot_arm.oat");
56   EXPECT_TRUE(xz.Init());
57   EXPECT_GT(xz.BlockCount(), 1u);
58   EXPECT_EQ(xz.BlockSize(), 16 * 1024u);
59   EXPECT_EQ(xz.MemoryUsage(), 0u);
60   VerifyContent(xz, 0, expected_content_->Size());
61   EXPECT_EQ(xz.MemoryUsage(), xz.Size());
62 }
63 
64 // Test one big monolithic compressed block.
TEST_F(MemoryXzTest,DecompressOneBlock)65 TEST_F(MemoryXzTest, DecompressOneBlock) {
66   auto compressed = ReadFile("boot_arm.oat.gnu_debugdata.xz.one-block");
67   MemoryXz xz(compressed.get(), 0, compressed->Size(), "boot_arm.oat");
68   EXPECT_TRUE(xz.Init());
69   EXPECT_EQ(xz.BlockCount(), 1u);
70   EXPECT_GT(xz.BlockSize(), xz.Size());
71   EXPECT_EQ(xz.MemoryUsage(), 0u);
72   VerifyContent(xz, 0, expected_content_->Size());
73   EXPECT_EQ(xz.MemoryUsage(), xz.Size());
74 }
75 
76 // Test fallback (non-consistent block sizes).
TEST_F(MemoryXzTest,DecompressOddSizes)77 TEST_F(MemoryXzTest, DecompressOddSizes) {
78   auto compressed = ReadFile("boot_arm.oat.gnu_debugdata.xz.odd-sizes");
79   MemoryXz xz(compressed.get(), 0, compressed->Size(), "boot_arm.oat");
80   EXPECT_TRUE(xz.Init());
81   EXPECT_EQ(xz.BlockCount(), 1u);
82   EXPECT_GT(xz.BlockSize(), xz.Size());
83   EXPECT_EQ(xz.MemoryUsage(), xz.Size());
84   VerifyContent(xz, 0, expected_content_->Size());
85 }
86 
87 // Test fallback (non-power-of-2 block size).
TEST_F(MemoryXzTest,DecompressNonPower)88 TEST_F(MemoryXzTest, DecompressNonPower) {
89   auto compressed = ReadFile("boot_arm.oat.gnu_debugdata.xz.non-power");
90   MemoryXz xz(compressed.get(), 0, compressed->Size(), "boot_arm.oat");
91   EXPECT_TRUE(xz.Init());
92   EXPECT_EQ(xz.BlockCount(), 1u);
93   EXPECT_GT(xz.BlockSize(), xz.Size());
94   EXPECT_EQ(xz.MemoryUsage(), xz.Size());
95   VerifyContent(xz, 0, expected_content_->Size());
96 }
97 
98 // Read first byte of some blocks.
TEST_F(MemoryXzTest,ReadFirstByte)99 TEST_F(MemoryXzTest, ReadFirstByte) {
100   auto compressed = ReadFile("boot_arm.oat.gnu_debugdata.xz");
101   MemoryXz xz(compressed.get(), 0, compressed->Size(), "boot_arm.oat");
102   EXPECT_TRUE(xz.Init());
103   EXPECT_GT(xz.BlockCount(), 1u);
104   EXPECT_EQ(xz.BlockSize(), 16 * 1024u);
105   for (size_t i = 0; i < xz.BlockCount(); i += 3) {
106     VerifyContent(xz, i * xz.BlockSize(), 1);
107   }
108   EXPECT_LT(xz.MemoryUsage(), xz.Size());  // We didn't decompress all blocks.
109 }
110 
111 // Read last byte of some blocks.
TEST_F(MemoryXzTest,ReadLastByte)112 TEST_F(MemoryXzTest, ReadLastByte) {
113   auto compressed = ReadFile("boot_arm.oat.gnu_debugdata.xz");
114   MemoryXz xz(compressed.get(), 0, compressed->Size(), "boot_arm.oat");
115   EXPECT_TRUE(xz.Init());
116   EXPECT_GT(xz.BlockCount(), 1u);
117   EXPECT_EQ(xz.BlockSize(), 16 * 1024u);
118   for (size_t i = 1; i < xz.BlockCount(); i += 3) {
119     VerifyContent(xz, i * xz.BlockSize() - 1, 1);
120   }
121   EXPECT_LT(xz.MemoryUsage(), xz.Size());  // We didn't decompress all blocks.
122 }
123 
124 // Read across boundary of blocks.
TEST_F(MemoryXzTest,ReadBoundary)125 TEST_F(MemoryXzTest, ReadBoundary) {
126   auto compressed = ReadFile("boot_arm.oat.gnu_debugdata.xz");
127   MemoryXz xz(compressed.get(), 0, compressed->Size(), "boot_arm.oat");
128   EXPECT_TRUE(xz.Init());
129   EXPECT_GT(xz.BlockCount(), 1u);
130   EXPECT_EQ(xz.BlockSize(), 16 * 1024u);
131   for (size_t i = 1; i < xz.BlockCount(); i += 3) {
132     VerifyContent(xz, i * xz.BlockSize() - 1, 2);
133   }
134   EXPECT_LT(xz.MemoryUsage(), xz.Size());  // We didn't decompress all blocks.
135 }
136 
137 }  // namespace unwindstack
138