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