• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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