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