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