blob: cb4868a4a38eb324968114699672fb3392f5ad8b [file] [log] [blame]
Tao Bao97555da2016-12-15 10:15:06 -08001/*
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
Tao Baoc0e1c462017-02-01 10:20:10 -080017#include <stdio.h>
18
Tianjie Xu2903cdd2017-08-18 18:15:47 -070019#include <algorithm>
Tao Bao97555da2016-12-15 10:15:06 -080020#include <string>
Tianjie Xu2903cdd2017-08-18 18:15:47 -070021#include <tuple>
Tao Bao97555da2016-12-15 10:15:06 -080022#include <vector>
23
24#include <android-base/file.h>
Tianjie Xu12b90552017-03-07 14:44:14 -080025#include <android-base/memory.h>
Tianjie Xu2903cdd2017-08-18 18:15:47 -070026#include <android-base/stringprintf.h>
Tao Bao45685822017-10-13 14:54:12 -070027#include <android-base/strings.h>
Tao Bao97555da2016-12-15 10:15:06 -080028#include <android-base/test_utils.h>
29#include <applypatch/imgdiff.h>
Tianjie Xu2903cdd2017-08-18 18:15:47 -070030#include <applypatch/imgdiff_image.h>
Tao Bao97555da2016-12-15 10:15:06 -080031#include <applypatch/imgpatch.h>
32#include <gtest/gtest.h>
33#include <ziparchive/zip_writer.h>
34
Tianjie Xu113fe052017-10-24 16:12:35 -070035#include "common/test_constants.h"
36
Tianjie Xu12b90552017-03-07 14:44:14 -080037using android::base::get_unaligned;
Tao Bao97555da2016-12-15 10:15:06 -080038
Tao Bao97555da2016-12-15 10:15:06 -080039// Sanity check for the given imgdiff patch header.
40static void verify_patch_header(const std::string& patch, size_t* num_normal, size_t* num_raw,
41 size_t* num_deflate) {
42 const size_t size = patch.size();
43 const char* data = patch.data();
44
45 ASSERT_GE(size, 12U);
46 ASSERT_EQ("IMGDIFF2", std::string(data, 8));
47
Tianjie Xu12b90552017-03-07 14:44:14 -080048 const int num_chunks = get_unaligned<int32_t>(data + 8);
Tao Bao97555da2016-12-15 10:15:06 -080049 ASSERT_GE(num_chunks, 0);
50
51 size_t normal = 0;
52 size_t raw = 0;
53 size_t deflate = 0;
54
55 size_t pos = 12;
56 for (int i = 0; i < num_chunks; ++i) {
57 ASSERT_LE(pos + 4, size);
Tianjie Xu12b90552017-03-07 14:44:14 -080058 int type = get_unaligned<int32_t>(data + pos);
Tao Bao97555da2016-12-15 10:15:06 -080059 pos += 4;
60 if (type == CHUNK_NORMAL) {
61 pos += 24;
62 ASSERT_LE(pos, size);
63 normal++;
64 } else if (type == CHUNK_RAW) {
65 ASSERT_LE(pos + 4, size);
Tianjie Xu12b90552017-03-07 14:44:14 -080066 ssize_t data_len = get_unaligned<int32_t>(data + pos);
Tao Bao97555da2016-12-15 10:15:06 -080067 ASSERT_GT(data_len, 0);
68 pos += 4 + data_len;
69 ASSERT_LE(pos, size);
70 raw++;
71 } else if (type == CHUNK_DEFLATE) {
72 pos += 60;
73 ASSERT_LE(pos, size);
74 deflate++;
75 } else {
76 FAIL() << "Invalid patch type: " << type;
77 }
78 }
79
80 if (num_normal != nullptr) *num_normal = normal;
81 if (num_raw != nullptr) *num_raw = raw;
82 if (num_deflate != nullptr) *num_deflate = deflate;
83}
84
Tianjie Xu2903cdd2017-08-18 18:15:47 -070085static void GenerateTarget(const std::string& src, const std::string& patch, std::string* patched) {
86 patched->clear();
87 ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
88 reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
89 [&](const unsigned char* data, size_t len) {
90 patched->append(reinterpret_cast<const char*>(data), len);
91 return len;
92 }));
93}
94
Tao Baoc0e1c462017-02-01 10:20:10 -080095static void verify_patched_image(const std::string& src, const std::string& patch,
96 const std::string& tgt) {
97 std::string patched;
Tianjie Xu2903cdd2017-08-18 18:15:47 -070098 GenerateTarget(src, patch, &patched);
Tao Baoc0e1c462017-02-01 10:20:10 -080099 ASSERT_EQ(tgt, patched);
100}
101
Tao Bao97555da2016-12-15 10:15:06 -0800102TEST(ImgdiffTest, invalid_args) {
103 // Insufficient inputs.
104 ASSERT_EQ(2, imgdiff(1, (const char* []){ "imgdiff" }));
105 ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-z" }));
106 ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-b" }));
107 ASSERT_EQ(2, imgdiff(3, (const char* []){ "imgdiff", "-z", "-b" }));
108
109 // Failed to read bonus file.
110 ASSERT_EQ(1, imgdiff(3, (const char* []){ "imgdiff", "-b", "doesntexist" }));
111
112 // Failed to read input files.
113 ASSERT_EQ(1, imgdiff(4, (const char* []){ "imgdiff", "doesntexist", "doesntexist", "output" }));
114 ASSERT_EQ(
115 1, imgdiff(5, (const char* []){ "imgdiff", "-z", "doesntexist", "doesntexist", "output" }));
116}
117
118TEST(ImgdiffTest, image_mode_smoke) {
119 // Random bytes.
120 const std::string src("abcdefg");
121 TemporaryFile src_file;
122 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
123
124 const std::string tgt("abcdefgxyz");
125 TemporaryFile tgt_file;
126 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
127
128 TemporaryFile patch_file;
129 std::vector<const char*> args = {
130 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
131 };
132 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
133
134 // Verify.
135 std::string patch;
136 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
137
138 // Expect one CHUNK_RAW entry.
139 size_t num_normal;
140 size_t num_raw;
141 size_t num_deflate;
142 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
143 ASSERT_EQ(0U, num_normal);
144 ASSERT_EQ(0U, num_deflate);
145 ASSERT_EQ(1U, num_raw);
146
Tao Baoc0e1c462017-02-01 10:20:10 -0800147 verify_patched_image(src, patch, tgt);
Tao Bao97555da2016-12-15 10:15:06 -0800148}
149
150TEST(ImgdiffTest, zip_mode_smoke_store) {
151 // Construct src and tgt zip files.
152 TemporaryFile src_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700153 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
Tao Bao97555da2016-12-15 10:15:06 -0800154 ZipWriter src_writer(src_file_ptr);
155 ASSERT_EQ(0, src_writer.StartEntry("file1.txt", 0)); // Store mode.
156 const std::string src_content("abcdefg");
157 ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
158 ASSERT_EQ(0, src_writer.FinishEntry());
159 ASSERT_EQ(0, src_writer.Finish());
160 ASSERT_EQ(0, fclose(src_file_ptr));
161
162 TemporaryFile tgt_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700163 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
Tao Bao97555da2016-12-15 10:15:06 -0800164 ZipWriter tgt_writer(tgt_file_ptr);
165 ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", 0)); // Store mode.
166 const std::string tgt_content("abcdefgxyz");
167 ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
168 ASSERT_EQ(0, tgt_writer.FinishEntry());
169 ASSERT_EQ(0, tgt_writer.Finish());
170 ASSERT_EQ(0, fclose(tgt_file_ptr));
171
172 // Compute patch.
173 TemporaryFile patch_file;
174 std::vector<const char*> args = {
175 "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
176 };
177 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
178
179 // Verify.
180 std::string tgt;
181 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
182 std::string src;
183 ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
184 std::string patch;
185 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
186
187 // Expect one CHUNK_RAW entry.
188 size_t num_normal;
189 size_t num_raw;
190 size_t num_deflate;
191 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
192 ASSERT_EQ(0U, num_normal);
193 ASSERT_EQ(0U, num_deflate);
194 ASSERT_EQ(1U, num_raw);
195
Tao Baoc0e1c462017-02-01 10:20:10 -0800196 verify_patched_image(src, patch, tgt);
Tao Bao97555da2016-12-15 10:15:06 -0800197}
198
199TEST(ImgdiffTest, zip_mode_smoke_compressed) {
Tianjie Xucc61cf62018-05-23 22:23:31 -0700200 // Generate 1 block of random data.
201 std::string random_data;
202 random_data.reserve(4096);
203 generate_n(back_inserter(random_data), 4096, []() { return rand() % 256; });
204
Tao Bao97555da2016-12-15 10:15:06 -0800205 // Construct src and tgt zip files.
206 TemporaryFile src_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700207 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
Tao Bao97555da2016-12-15 10:15:06 -0800208 ZipWriter src_writer(src_file_ptr);
209 ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
Tianjie Xucc61cf62018-05-23 22:23:31 -0700210 const std::string src_content = random_data;
Tao Bao97555da2016-12-15 10:15:06 -0800211 ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
212 ASSERT_EQ(0, src_writer.FinishEntry());
213 ASSERT_EQ(0, src_writer.Finish());
214 ASSERT_EQ(0, fclose(src_file_ptr));
215
216 TemporaryFile tgt_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700217 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
Tao Bao97555da2016-12-15 10:15:06 -0800218 ZipWriter tgt_writer(tgt_file_ptr);
219 ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
Tianjie Xucc61cf62018-05-23 22:23:31 -0700220 const std::string tgt_content = random_data + "extra contents";
Tao Bao97555da2016-12-15 10:15:06 -0800221 ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
222 ASSERT_EQ(0, tgt_writer.FinishEntry());
223 ASSERT_EQ(0, tgt_writer.Finish());
224 ASSERT_EQ(0, fclose(tgt_file_ptr));
225
226 // Compute patch.
227 TemporaryFile patch_file;
228 std::vector<const char*> args = {
229 "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
230 };
231 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
232
233 // Verify.
234 std::string tgt;
235 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
236 std::string src;
237 ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
238 std::string patch;
239 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
240
241 // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
242 size_t num_normal;
243 size_t num_raw;
244 size_t num_deflate;
245 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
246 ASSERT_EQ(0U, num_normal);
247 ASSERT_EQ(1U, num_deflate);
248 ASSERT_EQ(2U, num_raw);
249
Tao Baoc0e1c462017-02-01 10:20:10 -0800250 verify_patched_image(src, patch, tgt);
Tao Bao97555da2016-12-15 10:15:06 -0800251}
252
Tianjie Xucc61cf62018-05-23 22:23:31 -0700253TEST(ImgdiffTest, zip_mode_empty_target) {
254 TemporaryFile src_file;
255 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
256 ZipWriter src_writer(src_file_ptr);
257 ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
258 const std::string src_content = "abcdefg";
259 ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
260 ASSERT_EQ(0, src_writer.FinishEntry());
261 ASSERT_EQ(0, src_writer.Finish());
262 ASSERT_EQ(0, fclose(src_file_ptr));
263
264 // Construct a empty entry in the target zip.
265 TemporaryFile tgt_file;
266 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
267 ZipWriter tgt_writer(tgt_file_ptr);
268 ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
269 const std::string tgt_content;
270 ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
271 ASSERT_EQ(0, tgt_writer.FinishEntry());
272 ASSERT_EQ(0, tgt_writer.Finish());
273
274 // Compute patch.
275 TemporaryFile patch_file;
276 std::vector<const char*> args = {
277 "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
278 };
279 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
280
281 // Verify.
282 std::string tgt;
283 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
284 std::string src;
285 ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
286 std::string patch;
287 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
288
289 verify_patched_image(src, patch, tgt);
290}
291
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800292TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) {
Tianjie Xucc61cf62018-05-23 22:23:31 -0700293 // Generate 1 block of random data.
294 std::string random_data;
295 random_data.reserve(4096);
296 generate_n(back_inserter(random_data), 4096, []() { return rand() % 256; });
297
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800298 // Construct src and tgt zip files.
299 TemporaryFile src_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700300 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800301 ZipWriter src_writer(src_file_ptr);
302 ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
Tianjie Xucc61cf62018-05-23 22:23:31 -0700303 const std::string src_content = random_data;
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800304 ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
305 ASSERT_EQ(0, src_writer.FinishEntry());
306 ASSERT_EQ(0, src_writer.Finish());
307 ASSERT_EQ(0, fclose(src_file_ptr));
308
309 TemporaryFile tgt_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700310 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800311 ZipWriter tgt_writer(tgt_file_ptr);
312 ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
Tianjie Xucc61cf62018-05-23 22:23:31 -0700313 const std::string tgt_content = random_data + "abcdefg";
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800314 ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
315 ASSERT_EQ(0, tgt_writer.FinishEntry());
316 ASSERT_EQ(0, tgt_writer.Finish());
317 // Add trailing zeros to the target zip file.
318 std::vector<uint8_t> zeros(10);
319 ASSERT_EQ(zeros.size(), fwrite(zeros.data(), sizeof(uint8_t), zeros.size(), tgt_file_ptr));
320 ASSERT_EQ(0, fclose(tgt_file_ptr));
321
322 // Compute patch.
323 TemporaryFile patch_file;
324 std::vector<const char*> args = {
325 "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
326 };
327 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
328
329 // Verify.
330 std::string tgt;
331 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
332 std::string src;
333 ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
334 std::string patch;
335 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
336
337 // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
338 size_t num_normal;
339 size_t num_raw;
340 size_t num_deflate;
341 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
342 ASSERT_EQ(0U, num_normal);
343 ASSERT_EQ(1U, num_deflate);
344 ASSERT_EQ(2U, num_raw);
345
Tao Baoc0e1c462017-02-01 10:20:10 -0800346 verify_patched_image(src, patch, tgt);
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800347}
348
Tao Bao97555da2016-12-15 10:15:06 -0800349TEST(ImgdiffTest, image_mode_simple) {
Tianjie Xucc61cf62018-05-23 22:23:31 -0700350 std::string gzipped_source_path = from_testdata_base("gzipped_source");
351 std::string gzipped_source;
352 ASSERT_TRUE(android::base::ReadFileToString(gzipped_source_path, &gzipped_source));
353
354 const std::string src = "abcdefg" + gzipped_source;
Tao Bao97555da2016-12-15 10:15:06 -0800355 TemporaryFile src_file;
356 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
357
Tianjie Xucc61cf62018-05-23 22:23:31 -0700358 std::string gzipped_target_path = from_testdata_base("gzipped_target");
359 std::string gzipped_target;
360 ASSERT_TRUE(android::base::ReadFileToString(gzipped_target_path, &gzipped_target));
361 const std::string tgt = "abcdefgxyz" + gzipped_target;
362
Tao Bao97555da2016-12-15 10:15:06 -0800363 TemporaryFile tgt_file;
364 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
365
366 TemporaryFile patch_file;
367 std::vector<const char*> args = {
368 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
369 };
370 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
371
372 // Verify.
373 std::string patch;
374 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
375
376 // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
377 size_t num_normal;
378 size_t num_raw;
379 size_t num_deflate;
380 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
381 ASSERT_EQ(0U, num_normal);
382 ASSERT_EQ(1U, num_deflate);
383 ASSERT_EQ(2U, num_raw);
384
Tao Baoc0e1c462017-02-01 10:20:10 -0800385 verify_patched_image(src, patch, tgt);
Tao Bao97555da2016-12-15 10:15:06 -0800386}
387
Tianjie Xu14ebc1e2017-07-05 12:04:07 -0700388TEST(ImgdiffTest, image_mode_bad_gzip) {
389 // Modify the uncompressed length in the gzip footer.
390 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
391 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
392 '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
393 '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
394 '\xff', '\xff', '\xff' };
395 const std::string src(src_data.cbegin(), src_data.cend());
396 TemporaryFile src_file;
397 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
398
399 // Modify the uncompressed length in the gzip footer.
400 const std::vector<char> tgt_data = {
401 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
402 '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
403 '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\xff', '\xff', '\xff'
404 };
405 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
406 TemporaryFile tgt_file;
407 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
408
409 TemporaryFile patch_file;
410 std::vector<const char*> args = {
411 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
412 };
413 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
414
415 // Verify.
416 std::string patch;
417 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
418 verify_patched_image(src, patch, tgt);
419}
420
Tao Bao97555da2016-12-15 10:15:06 -0800421TEST(ImgdiffTest, image_mode_different_num_chunks) {
422 // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd) + gzipped "test".
423 const std::vector<char> src_data = {
424 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\x1f', '\x8b', '\x08',
425 '\x00', '\xc4', '\x1e', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac', '\x02',
426 '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03', '\x00', '\x00', '\x00', '\x1f', '\x8b',
427 '\x08', '\x00', '\xb2', '\x3a', '\x53', '\x58', '\x00', '\x03', '\x2b', '\x49', '\x2d',
428 '\x2e', '\x01', '\x00', '\x0c', '\x7e', '\x7f', '\xd8', '\x04', '\x00', '\x00', '\x00'
429 };
430 const std::string src(src_data.cbegin(), src_data.cend());
431 TemporaryFile src_file;
432 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
433
434 // tgt: "abcdefgxyz" + gzipped "xxyyzz".
435 const std::vector<char> tgt_data = {
436 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
437 '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
438 '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
439 };
440 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
441 TemporaryFile tgt_file;
442 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
443
444 TemporaryFile patch_file;
445 std::vector<const char*> args = {
446 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
447 };
448 ASSERT_EQ(1, imgdiff(args.size(), args.data()));
449}
450
451TEST(ImgdiffTest, image_mode_merge_chunks) {
Tianjie Xucc61cf62018-05-23 22:23:31 -0700452 // src: "abcdefg" + gzipped_source.
453 std::string gzipped_source_path = from_testdata_base("gzipped_source");
454 std::string gzipped_source;
455 ASSERT_TRUE(android::base::ReadFileToString(gzipped_source_path, &gzipped_source));
456
457 const std::string src = "abcdefg" + gzipped_source;
Tao Bao97555da2016-12-15 10:15:06 -0800458 TemporaryFile src_file;
459 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
460
Tianjie Xucc61cf62018-05-23 22:23:31 -0700461 // tgt: gzipped_target + "abcdefgxyz".
462 std::string gzipped_target_path = from_testdata_base("gzipped_target");
463 std::string gzipped_target;
464 ASSERT_TRUE(android::base::ReadFileToString(gzipped_target_path, &gzipped_target));
465
466 const std::string tgt = gzipped_target + "abcdefgxyz";
Tao Bao97555da2016-12-15 10:15:06 -0800467 TemporaryFile tgt_file;
468 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
469
470 // Since a gzipped entry will become CHUNK_RAW (header) + CHUNK_DEFLATE (data) +
471 // CHUNK_RAW (footer), they both should contain the same chunk types after merging.
472
473 TemporaryFile patch_file;
474 std::vector<const char*> args = {
475 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
476 };
477 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
478
479 // Verify.
480 std::string patch;
481 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
482
483 // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
484 size_t num_normal;
485 size_t num_raw;
486 size_t num_deflate;
487 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
488 ASSERT_EQ(0U, num_normal);
489 ASSERT_EQ(1U, num_deflate);
490 ASSERT_EQ(2U, num_raw);
491
Tao Baoc0e1c462017-02-01 10:20:10 -0800492 verify_patched_image(src, patch, tgt);
Tao Bao97555da2016-12-15 10:15:06 -0800493}
494
495TEST(ImgdiffTest, image_mode_spurious_magic) {
496 // src: "abcdefgh" + '0x1f8b0b00' + some bytes.
497 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
498 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
499 '\x53', '\x58', 't', 'e', 's', 't' };
500 const std::string src(src_data.cbegin(), src_data.cend());
501 TemporaryFile src_file;
502 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
503
504 // tgt: "abcdefgxyz".
505 const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
506 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
507 TemporaryFile tgt_file;
508 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
509
510 TemporaryFile patch_file;
511 std::vector<const char*> args = {
512 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
513 };
514 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
515
516 // Verify.
517 std::string patch;
518 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
519
520 // Expect one CHUNK_RAW (header) entry.
521 size_t num_normal;
522 size_t num_raw;
523 size_t num_deflate;
524 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
525 ASSERT_EQ(0U, num_normal);
526 ASSERT_EQ(0U, num_deflate);
527 ASSERT_EQ(1U, num_raw);
528
Tao Baoc0e1c462017-02-01 10:20:10 -0800529 verify_patched_image(src, patch, tgt);
Tao Bao97555da2016-12-15 10:15:06 -0800530}
531
Tao Baod37ce8f2016-12-17 17:10:04 -0800532TEST(ImgdiffTest, image_mode_short_input1) {
533 // src: "abcdefgh" + '0x1f8b0b'.
534 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f',
535 'g', 'h', '\x1f', '\x8b', '\x08' };
536 const std::string src(src_data.cbegin(), src_data.cend());
537 TemporaryFile src_file;
538 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
539
540 // tgt: "abcdefgxyz".
541 const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
542 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
543 TemporaryFile tgt_file;
544 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
545
546 TemporaryFile patch_file;
547 std::vector<const char*> args = {
548 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
549 };
550 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
551
552 // Verify.
553 std::string patch;
554 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
555
556 // Expect one CHUNK_RAW (header) entry.
557 size_t num_normal;
558 size_t num_raw;
559 size_t num_deflate;
560 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
561 ASSERT_EQ(0U, num_normal);
562 ASSERT_EQ(0U, num_deflate);
563 ASSERT_EQ(1U, num_raw);
564
Tao Baoc0e1c462017-02-01 10:20:10 -0800565 verify_patched_image(src, patch, tgt);
Tao Baod37ce8f2016-12-17 17:10:04 -0800566}
567
568TEST(ImgdiffTest, image_mode_short_input2) {
569 // src: "abcdefgh" + '0x1f8b0b00'.
570 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f',
571 'g', 'h', '\x1f', '\x8b', '\x08', '\x00' };
572 const std::string src(src_data.cbegin(), src_data.cend());
573 TemporaryFile src_file;
574 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
575
576 // tgt: "abcdefgxyz".
577 const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
578 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
579 TemporaryFile tgt_file;
580 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
581
582 TemporaryFile patch_file;
583 std::vector<const char*> args = {
584 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
585 };
586 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
587
588 // Verify.
589 std::string patch;
590 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
591
592 // Expect one CHUNK_RAW (header) entry.
593 size_t num_normal;
594 size_t num_raw;
595 size_t num_deflate;
596 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
597 ASSERT_EQ(0U, num_normal);
598 ASSERT_EQ(0U, num_deflate);
599 ASSERT_EQ(1U, num_raw);
600
Tao Baoc0e1c462017-02-01 10:20:10 -0800601 verify_patched_image(src, patch, tgt);
Tao Baod37ce8f2016-12-17 17:10:04 -0800602}
603
Tao Bao97555da2016-12-15 10:15:06 -0800604TEST(ImgdiffTest, image_mode_single_entry_long) {
605 // src: "abcdefgh" + '0x1f8b0b00' + some bytes.
606 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
607 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
608 '\x53', '\x58', 't', 'e', 's', 't' };
609 const std::string src(src_data.cbegin(), src_data.cend());
610 TemporaryFile src_file;
611 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
612
613 // tgt: "abcdefgxyz" + 200 bytes.
614 std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
615 tgt_data.resize(tgt_data.size() + 200);
616
617 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
618 TemporaryFile tgt_file;
619 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
620
621 TemporaryFile patch_file;
622 std::vector<const char*> args = {
623 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
624 };
625 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
626
627 // Verify.
628 std::string patch;
629 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
630
631 // Expect one CHUNK_NORMAL entry, since it's exceeding the 160-byte limit for RAW.
632 size_t num_normal;
633 size_t num_raw;
634 size_t num_deflate;
635 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
636 ASSERT_EQ(1U, num_normal);
637 ASSERT_EQ(0U, num_deflate);
638 ASSERT_EQ(0U, num_raw);
639
Tao Baoc0e1c462017-02-01 10:20:10 -0800640 verify_patched_image(src, patch, tgt);
Tao Bao97555da2016-12-15 10:15:06 -0800641}
Tianjie Xuce5fa5e2017-05-15 12:32:33 -0700642
643TEST(ImgpatchTest, image_mode_patch_corruption) {
644 // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd).
645 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
646 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
647 '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
648 '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
649 '\x00', '\x00', '\x00' };
650 const std::string src(src_data.cbegin(), src_data.cend());
651 TemporaryFile src_file;
652 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
653
654 // tgt: "abcdefgxyz" + gzipped "xxyyzz".
655 const std::vector<char> tgt_data = {
656 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
657 '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
658 '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
659 };
660 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
661 TemporaryFile tgt_file;
662 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
663
664 TemporaryFile patch_file;
665 std::vector<const char*> args = {
666 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
667 };
668 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
669
670 // Verify.
671 std::string patch;
672 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
673 verify_patched_image(src, patch, tgt);
674
675 // Corrupt the end of the patch and expect the ApplyImagePatch to fail.
676 patch.insert(patch.end() - 10, 10, '0');
677 ASSERT_EQ(-1, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
678 reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
679 [](const unsigned char* /*data*/, size_t len) { return len; }));
680}
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700681
682static void construct_store_entry(const std::vector<std::tuple<std::string, size_t, char>>& info,
683 ZipWriter* writer) {
684 for (auto& t : info) {
685 // Create t(1) blocks of t(2), and write the data to t(0)
686 ASSERT_EQ(0, writer->StartEntry(std::get<0>(t).c_str(), 0));
687 const std::string content(std::get<1>(t) * 4096, std::get<2>(t));
688 ASSERT_EQ(0, writer->WriteBytes(content.data(), content.size()));
689 ASSERT_EQ(0, writer->FinishEntry());
690 }
691}
692
693static void construct_deflate_entry(const std::vector<std::tuple<std::string, size_t, size_t>>& info,
694 ZipWriter* writer, const std::string& data) {
695 for (auto& t : info) {
696 // t(0): entry_name; t(1): block offset; t(2) length in blocks.
697 ASSERT_EQ(0, writer->StartEntry(std::get<0>(t).c_str(), ZipWriter::kCompress));
698 ASSERT_EQ(0, writer->WriteBytes(data.data() + std::get<1>(t) * 4096, std::get<2>(t) * 4096));
699 ASSERT_EQ(0, writer->FinishEntry());
700 }
701}
702
Tao Bao54c1db42017-11-01 22:21:55 -0700703// Look for the source and patch pieces in debug_dir. Generate a target piece from each pair.
704// Concatenate all the target pieces and match against the orignal one. Used pieces in debug_dir
705// will be cleaned up.
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700706static void GenerateAndCheckSplitTarget(const std::string& debug_dir, size_t count,
707 const std::string& tgt) {
708 std::string patched;
709 for (size_t i = 0; i < count; i++) {
710 std::string split_src_path = android::base::StringPrintf("%s/src-%zu", debug_dir.c_str(), i);
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700711 std::string split_src;
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700712 ASSERT_TRUE(android::base::ReadFileToString(split_src_path, &split_src));
Tao Bao54c1db42017-11-01 22:21:55 -0700713 ASSERT_EQ(0, unlink(split_src_path.c_str()));
714
715 std::string split_patch_path =
716 android::base::StringPrintf("%s/patch-%zu", debug_dir.c_str(), i);
717 std::string split_patch;
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700718 ASSERT_TRUE(android::base::ReadFileToString(split_patch_path, &split_patch));
Tao Bao54c1db42017-11-01 22:21:55 -0700719 ASSERT_EQ(0, unlink(split_patch_path.c_str()));
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700720
721 std::string split_tgt;
722 GenerateTarget(split_src, split_patch, &split_tgt);
723 patched += split_tgt;
724 }
725
726 // Verify we can get back the original target image.
727 ASSERT_EQ(tgt, patched);
728}
729
730std::vector<ImageChunk> ConstructImageChunks(
731 const std::vector<uint8_t>& content, const std::vector<std::tuple<std::string, size_t>>& info) {
732 std::vector<ImageChunk> chunks;
733 size_t start = 0;
734 for (const auto& t : info) {
735 size_t length = std::get<1>(t);
736 chunks.emplace_back(CHUNK_NORMAL, start, &content, length, std::get<0>(t));
737 start += length;
738 }
739
740 return chunks;
741}
742
743TEST(ImgdiffTest, zip_mode_split_image_smoke) {
744 std::vector<uint8_t> content;
745 content.reserve(4096 * 50);
746 uint8_t n = 0;
747 generate_n(back_inserter(content), 4096 * 50, [&n]() { return n++ / 4096; });
748
749 ZipModeImage tgt_image(false, 4096 * 10);
750 std::vector<ImageChunk> tgt_chunks = ConstructImageChunks(content, { { "a", 100 },
751 { "b", 4096 * 2 },
752 { "c", 4096 * 3 },
753 { "d", 300 },
754 { "e-0", 4096 * 10 },
755 { "e-1", 4096 * 5 },
756 { "CD", 200 } });
757 tgt_image.Initialize(std::move(tgt_chunks),
758 std::vector<uint8_t>(content.begin(), content.begin() + 82520));
759
760 tgt_image.DumpChunks();
761
762 ZipModeImage src_image(true, 4096 * 10);
763 std::vector<ImageChunk> src_chunks = ConstructImageChunks(content, { { "b", 4096 * 3 },
764 { "c-0", 4096 * 10 },
765 { "c-1", 4096 * 2 },
766 { "a", 4096 * 5 },
767 { "e-0", 4096 * 10 },
768 { "e-1", 10000 },
769 { "CD", 5000 } });
770 src_image.Initialize(std::move(src_chunks),
771 std::vector<uint8_t>(content.begin(), content.begin() + 137880));
772
773 std::vector<ZipModeImage> split_tgt_images;
774 std::vector<ZipModeImage> split_src_images;
775 std::vector<SortedRangeSet> split_src_ranges;
776
777 ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images,
778 &split_src_images, &split_src_ranges);
779
780 // src_piece 1: a 5 blocks, b 3 blocks
781 // src_piece 2: c-0 10 blocks
782 // src_piece 3: d 0 block, e-0 10 blocks
783 // src_piece 4: e-1 2 blocks; CD 2 blocks
784 ASSERT_EQ(split_tgt_images.size(), split_src_images.size());
785 ASSERT_EQ(static_cast<size_t>(4), split_tgt_images.size());
786
787 ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[0].NumOfChunks());
788 ASSERT_EQ(static_cast<size_t>(12288), split_tgt_images[0][0].DataLengthForPatch());
789 ASSERT_EQ("4,0,3,15,20", split_src_ranges[0].ToString());
790
791 ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[1].NumOfChunks());
792 ASSERT_EQ(static_cast<size_t>(12288), split_tgt_images[1][0].DataLengthForPatch());
793 ASSERT_EQ("2,3,13", split_src_ranges[1].ToString());
794
795 ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[2].NumOfChunks());
796 ASSERT_EQ(static_cast<size_t>(40960), split_tgt_images[2][0].DataLengthForPatch());
797 ASSERT_EQ("2,20,30", split_src_ranges[2].ToString());
798
799 ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[3].NumOfChunks());
800 ASSERT_EQ(static_cast<size_t>(16984), split_tgt_images[3][0].DataLengthForPatch());
801 ASSERT_EQ("2,30,34", split_src_ranges[3].ToString());
802}
803
804TEST(ImgdiffTest, zip_mode_store_large_apk) {
805 // Construct src and tgt zip files with limit = 10 blocks.
806 // src tgt
807 // 12 blocks 'd' 3 blocks 'a'
808 // 8 blocks 'c' 3 blocks 'b'
809 // 3 blocks 'b' 8 blocks 'c' (exceeds limit)
810 // 3 blocks 'a' 12 blocks 'd' (exceeds limit)
811 // 3 blocks 'e'
812 TemporaryFile tgt_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700813 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700814 ZipWriter tgt_writer(tgt_file_ptr);
815 construct_store_entry(
816 { { "a", 3, 'a' }, { "b", 3, 'b' }, { "c", 8, 'c' }, { "d", 12, 'd' }, { "e", 3, 'e' } },
817 &tgt_writer);
818 ASSERT_EQ(0, tgt_writer.Finish());
819 ASSERT_EQ(0, fclose(tgt_file_ptr));
820
821 TemporaryFile src_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700822 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700823 ZipWriter src_writer(src_file_ptr);
824 construct_store_entry({ { "d", 12, 'd' }, { "c", 8, 'c' }, { "b", 3, 'b' }, { "a", 3, 'a' } },
825 &src_writer);
826 ASSERT_EQ(0, src_writer.Finish());
827 ASSERT_EQ(0, fclose(src_file_ptr));
828
829 // Compute patch.
830 TemporaryFile patch_file;
Tianjie Xu82582b42017-08-31 18:05:19 -0700831 TemporaryFile split_info_file;
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700832 TemporaryDir debug_dir;
Tianjie Xu82582b42017-08-31 18:05:19 -0700833 std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
Tianjie Xu8ba7c452017-09-12 11:13:02 -0700834 std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700835 std::vector<const char*> args = {
Tianjie Xu82582b42017-08-31 18:05:19 -0700836 "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(),
837 src_file.path, tgt_file.path, patch_file.path,
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700838 };
839 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
840
841 std::string tgt;
842 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
843
Tao Baof29ed3e2017-10-24 16:48:32 -0700844 // Expect 4 pieces of patch. (Roughly 3'a',3'b'; 8'c'; 10'd'; 2'd'3'e')
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700845 GenerateAndCheckSplitTarget(debug_dir.path, 4, tgt);
846}
847
848TEST(ImgdiffTest, zip_mode_deflate_large_apk) {
Tianjie Xu113fe052017-10-24 16:12:35 -0700849 // Src and tgt zip files are constructed as follows.
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700850 // src tgt
851 // 22 blocks, "d" 4 blocks, "a"
852 // 5 blocks, "b" 4 blocks, "b"
Tianjie Xu113fe052017-10-24 16:12:35 -0700853 // 3 blocks, "a" 8 blocks, "c" (exceeds limit)
854 // 1 block, "g" 20 blocks, "d" (exceeds limit)
855 // 8 blocks, "c" 2 blocks, "e"
856 // 1 block, "f" 1 block , "f"
857 std::string tgt_path = from_testdata_base("deflate_tgt.zip");
858 std::string src_path = from_testdata_base("deflate_src.zip");
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700859
860 ZipModeImage src_image(true, 10 * 4096);
861 ZipModeImage tgt_image(false, 10 * 4096);
Tianjie Xu113fe052017-10-24 16:12:35 -0700862 ASSERT_TRUE(src_image.Initialize(src_path));
863 ASSERT_TRUE(tgt_image.Initialize(tgt_path));
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700864 ASSERT_TRUE(ZipModeImage::CheckAndProcessChunks(&tgt_image, &src_image));
865
866 src_image.DumpChunks();
867 tgt_image.DumpChunks();
868
869 std::vector<ZipModeImage> split_tgt_images;
870 std::vector<ZipModeImage> split_src_images;
871 std::vector<SortedRangeSet> split_src_ranges;
872 ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images,
873 &split_src_images, &split_src_ranges);
874
Tianjie Xu113fe052017-10-24 16:12:35 -0700875 // Expected split images with limit = 10 blocks.
876 // src_piece 0: a 3 blocks, b 5 blocks
877 // src_piece 1: c 8 blocks
878 // src_piece 2: d-0 10 block
879 // src_piece 3: d-1 10 blocks
880 // src_piece 4: e 1 block, CD
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700881 ASSERT_EQ(split_tgt_images.size(), split_src_images.size());
882 ASSERT_EQ(static_cast<size_t>(5), split_tgt_images.size());
883
884 ASSERT_EQ(static_cast<size_t>(2), split_src_images[0].NumOfChunks());
885 ASSERT_EQ("a", split_src_images[0][0].GetEntryName());
886 ASSERT_EQ("b", split_src_images[0][1].GetEntryName());
887
888 ASSERT_EQ(static_cast<size_t>(1), split_src_images[1].NumOfChunks());
889 ASSERT_EQ("c", split_src_images[1][0].GetEntryName());
890
891 ASSERT_EQ(static_cast<size_t>(0), split_src_images[2].NumOfChunks());
892 ASSERT_EQ(static_cast<size_t>(0), split_src_images[3].NumOfChunks());
893 ASSERT_EQ(static_cast<size_t>(0), split_src_images[4].NumOfChunks());
894
895 // Compute patch.
896 TemporaryFile patch_file;
Tianjie Xu82582b42017-08-31 18:05:19 -0700897 TemporaryFile split_info_file;
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700898 TemporaryDir debug_dir;
899 ASSERT_TRUE(ZipModeImage::GeneratePatches(split_tgt_images, split_src_images, split_src_ranges,
Tianjie Xu82582b42017-08-31 18:05:19 -0700900 patch_file.path, split_info_file.path, debug_dir.path));
901
902 // Verify the content of split info.
903 // Expect 5 pieces of patch. ["a","b"; "c"; "d-0"; "d-1"; "e"]
904 std::string split_info_string;
905 android::base::ReadFileToString(split_info_file.path, &split_info_string);
906 std::vector<std::string> info_list =
907 android::base::Split(android::base::Trim(split_info_string), "\n");
908
909 ASSERT_EQ(static_cast<size_t>(7), info_list.size());
910 ASSERT_EQ("2", android::base::Trim(info_list[0]));
911 ASSERT_EQ("5", android::base::Trim(info_list[1]));
912
Tianjie Xu113fe052017-10-24 16:12:35 -0700913 std::string tgt;
914 ASSERT_TRUE(android::base::ReadFileToString(tgt_path, &tgt));
915 ASSERT_EQ(static_cast<size_t>(160385), tgt.size());
916 std::vector<std::string> tgt_file_ranges = {
917 "36864 2,22,31", "32768 2,31,40", "40960 2,0,11", "40960 2,11,21", "8833 4,21,22,40,41",
918 };
919
Tianjie Xu82582b42017-08-31 18:05:19 -0700920 for (size_t i = 0; i < 5; i++) {
Tianjie Xu113fe052017-10-24 16:12:35 -0700921 struct stat st;
Tianjie Xu82582b42017-08-31 18:05:19 -0700922 std::string path = android::base::StringPrintf("%s/patch-%zu", debug_dir.path, i);
923 ASSERT_EQ(0, stat(path.c_str(), &st));
Tianjie Xu113fe052017-10-24 16:12:35 -0700924 ASSERT_EQ(std::to_string(st.st_size) + " " + tgt_file_ranges[i],
925 android::base::Trim(info_list[i + 2]));
Tianjie Xu82582b42017-08-31 18:05:19 -0700926 }
927
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700928 GenerateAndCheckSplitTarget(debug_dir.path, 5, tgt);
929}
930
931TEST(ImgdiffTest, zip_mode_no_match_source) {
932 // Generate 20 blocks of random data.
933 std::string random_data;
934 random_data.reserve(4096 * 20);
935 generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; });
936
937 TemporaryFile tgt_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700938 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700939 ZipWriter tgt_writer(tgt_file_ptr);
940
941 construct_deflate_entry({ { "a", 0, 4 }, { "b", 5, 5 }, { "c", 11, 5 } }, &tgt_writer,
942 random_data);
943
944 ASSERT_EQ(0, tgt_writer.Finish());
945 ASSERT_EQ(0, fclose(tgt_file_ptr));
946
947 // We don't have a matching source entry.
948 TemporaryFile src_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700949 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700950 ZipWriter src_writer(src_file_ptr);
951 construct_store_entry({ { "d", 1, 'd' } }, &src_writer);
952 ASSERT_EQ(0, src_writer.Finish());
953 ASSERT_EQ(0, fclose(src_file_ptr));
954
955 // Compute patch.
956 TemporaryFile patch_file;
Tianjie Xu82582b42017-08-31 18:05:19 -0700957 TemporaryFile split_info_file;
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700958 TemporaryDir debug_dir;
Tianjie Xu82582b42017-08-31 18:05:19 -0700959 std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
Tianjie Xu8ba7c452017-09-12 11:13:02 -0700960 std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700961 std::vector<const char*> args = {
Tianjie Xu82582b42017-08-31 18:05:19 -0700962 "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), split_info_arg.c_str(),
963 src_file.path, tgt_file.path, patch_file.path,
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700964 };
965 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
966
967 std::string tgt;
968 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
969
970 // Expect 1 pieces of patch due to no matching source entry.
971 GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt);
972}
973
974TEST(ImgdiffTest, zip_mode_large_enough_limit) {
975 // Generate 20 blocks of random data.
976 std::string random_data;
977 random_data.reserve(4096 * 20);
978 generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; });
979
980 TemporaryFile tgt_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700981 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700982 ZipWriter tgt_writer(tgt_file_ptr);
983
984 construct_deflate_entry({ { "a", 0, 10 }, { "b", 10, 5 } }, &tgt_writer, random_data);
985
986 ASSERT_EQ(0, tgt_writer.Finish());
987 ASSERT_EQ(0, fclose(tgt_file_ptr));
988
989 // Construct 10 blocks of source.
990 TemporaryFile src_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700991 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700992 ZipWriter src_writer(src_file_ptr);
993 construct_deflate_entry({ { "a", 1, 10 } }, &src_writer, random_data);
994 ASSERT_EQ(0, src_writer.Finish());
995 ASSERT_EQ(0, fclose(src_file_ptr));
996
997 // Compute patch with a limit of 20 blocks.
998 TemporaryFile patch_file;
Tianjie Xu82582b42017-08-31 18:05:19 -0700999 TemporaryFile split_info_file;
Tianjie Xu2903cdd2017-08-18 18:15:47 -07001000 TemporaryDir debug_dir;
Tianjie Xu82582b42017-08-31 18:05:19 -07001001 std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
Tianjie Xu8ba7c452017-09-12 11:13:02 -07001002 std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
Tianjie Xu2903cdd2017-08-18 18:15:47 -07001003 std::vector<const char*> args = {
Tianjie Xu82582b42017-08-31 18:05:19 -07001004 "imgdiff", "-z", "--block-limit=20", split_info_arg.c_str(), debug_dir_arg.c_str(),
1005 src_file.path, tgt_file.path, patch_file.path,
Tianjie Xu2903cdd2017-08-18 18:15:47 -07001006 };
1007 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
1008
1009 std::string tgt;
1010 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
1011
Tao Baof29ed3e2017-10-24 16:48:32 -07001012 // Expect 1 piece of patch since limit is larger than the zip file size.
Tianjie Xu2903cdd2017-08-18 18:15:47 -07001013 GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt);
1014}
Tianjie Xu572abbb2018-02-22 15:40:39 -08001015
1016TEST(ImgdiffTest, zip_mode_large_apk_small_target_chunk) {
1017 TemporaryFile tgt_file;
1018 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
1019 ZipWriter tgt_writer(tgt_file_ptr);
1020
1021 // The first entry is less than 4096 bytes, followed immediately by an entry that has a very
1022 // large counterpart in the source file. Therefore the first entry will be patched separately.
1023 std::string small_chunk("a", 2000);
1024 ASSERT_EQ(0, tgt_writer.StartEntry("a", 0));
1025 ASSERT_EQ(0, tgt_writer.WriteBytes(small_chunk.data(), small_chunk.size()));
1026 ASSERT_EQ(0, tgt_writer.FinishEntry());
1027 construct_store_entry(
1028 {
1029 { "b", 12, 'b' }, { "c", 3, 'c' },
1030 },
1031 &tgt_writer);
1032 ASSERT_EQ(0, tgt_writer.Finish());
1033 ASSERT_EQ(0, fclose(tgt_file_ptr));
1034
1035 TemporaryFile src_file;
1036 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
1037 ZipWriter src_writer(src_file_ptr);
1038 construct_store_entry({ { "a", 1, 'a' }, { "b", 13, 'b' }, { "c", 1, 'c' } }, &src_writer);
1039 ASSERT_EQ(0, src_writer.Finish());
1040 ASSERT_EQ(0, fclose(src_file_ptr));
1041
1042 // Compute patch.
1043 TemporaryFile patch_file;
1044 TemporaryFile split_info_file;
1045 TemporaryDir debug_dir;
1046 std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
1047 std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
1048 std::vector<const char*> args = {
1049 "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(),
1050 src_file.path, tgt_file.path, patch_file.path,
1051 };
1052 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
1053
1054 std::string tgt;
1055 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
1056
1057 // Expect three split src images:
1058 // src_piece 0: a 1 blocks
1059 // src_piece 1: b-0 10 blocks
1060 // src_piece 2: b-1 3 blocks, c 1 blocks, CD
1061 GenerateAndCheckSplitTarget(debug_dir.path, 3, tgt);
1062}
1063
1064TEST(ImgdiffTest, zip_mode_large_apk_skipped_small_target_chunk) {
1065 TemporaryFile tgt_file;
1066 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
1067 ZipWriter tgt_writer(tgt_file_ptr);
1068
1069 construct_store_entry(
1070 {
1071 { "a", 11, 'a' },
1072 },
1073 &tgt_writer);
1074
1075 // Construct a tiny target entry of 1 byte, which will be skipped due to the tail alignment of
1076 // the previous entry.
1077 std::string small_chunk("b", 1);
1078 ASSERT_EQ(0, tgt_writer.StartEntry("b", 0));
1079 ASSERT_EQ(0, tgt_writer.WriteBytes(small_chunk.data(), small_chunk.size()));
1080 ASSERT_EQ(0, tgt_writer.FinishEntry());
1081
1082 ASSERT_EQ(0, tgt_writer.Finish());
1083 ASSERT_EQ(0, fclose(tgt_file_ptr));
1084
1085 TemporaryFile src_file;
1086 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
1087 ZipWriter src_writer(src_file_ptr);
1088 construct_store_entry(
1089 {
1090 { "a", 11, 'a' }, { "b", 11, 'b' },
1091 },
1092 &src_writer);
1093 ASSERT_EQ(0, src_writer.Finish());
1094 ASSERT_EQ(0, fclose(src_file_ptr));
1095
1096 // Compute patch.
1097 TemporaryFile patch_file;
1098 TemporaryFile split_info_file;
1099 TemporaryDir debug_dir;
1100 std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
1101 std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
1102 std::vector<const char*> args = {
1103 "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(),
1104 src_file.path, tgt_file.path, patch_file.path,
1105 };
1106 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
1107
1108 std::string tgt;
1109 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
1110
1111 // Expect two split src images:
1112 // src_piece 0: a-0 10 blocks
1113 // src_piece 1: a-0 1 block, CD
1114 GenerateAndCheckSplitTarget(debug_dir.path, 2, tgt);
1115}