blob: e76ccbdfb54de93f9f38ebcaf5dd1d5a0f061597 [file] [log] [blame]
Tao Bao97555da2016-12-15 10:15:06 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Tao Baoc0e1c462017-02-01 10:20:10 -080017#include <stdio.h>
18
Tianjie Xu2903cdd2017-08-18 18:15:47 -070019#include <algorithm>
Tao Bao97555da2016-12-15 10:15:06 -080020#include <string>
Tianjie Xu2903cdd2017-08-18 18:15:47 -070021#include <tuple>
Tao Bao97555da2016-12-15 10:15:06 -080022#include <vector>
23
24#include <android-base/file.h>
Tianjie Xu12b90552017-03-07 14:44:14 -080025#include <android-base/memory.h>
Tianjie Xu2903cdd2017-08-18 18:15:47 -070026#include <android-base/stringprintf.h>
Tao Bao45685822017-10-13 14:54:12 -070027#include <android-base/strings.h>
Tao Bao97555da2016-12-15 10:15:06 -080028#include <applypatch/imgdiff.h>
Tianjie Xu2903cdd2017-08-18 18:15:47 -070029#include <applypatch/imgdiff_image.h>
Tao Bao97555da2016-12-15 10:15:06 -080030#include <applypatch/imgpatch.h>
31#include <gtest/gtest.h>
32#include <ziparchive/zip_writer.h>
33
Tianjie Xu113fe052017-10-24 16:12:35 -070034#include "common/test_constants.h"
35
Tianjie Xu12b90552017-03-07 14:44:14 -080036using android::base::get_unaligned;
Tao Bao97555da2016-12-15 10:15:06 -080037
Tao Bao97555da2016-12-15 10:15:06 -080038// Sanity check for the given imgdiff patch header.
39static void verify_patch_header(const std::string& patch, size_t* num_normal, size_t* num_raw,
40 size_t* num_deflate) {
41 const size_t size = patch.size();
42 const char* data = patch.data();
43
44 ASSERT_GE(size, 12U);
45 ASSERT_EQ("IMGDIFF2", std::string(data, 8));
46
Tianjie Xu12b90552017-03-07 14:44:14 -080047 const int num_chunks = get_unaligned<int32_t>(data + 8);
Tao Bao97555da2016-12-15 10:15:06 -080048 ASSERT_GE(num_chunks, 0);
49
50 size_t normal = 0;
51 size_t raw = 0;
52 size_t deflate = 0;
53
54 size_t pos = 12;
55 for (int i = 0; i < num_chunks; ++i) {
56 ASSERT_LE(pos + 4, size);
Tianjie Xu12b90552017-03-07 14:44:14 -080057 int type = get_unaligned<int32_t>(data + pos);
Tao Bao97555da2016-12-15 10:15:06 -080058 pos += 4;
59 if (type == CHUNK_NORMAL) {
60 pos += 24;
61 ASSERT_LE(pos, size);
62 normal++;
63 } else if (type == CHUNK_RAW) {
64 ASSERT_LE(pos + 4, size);
Tianjie Xu12b90552017-03-07 14:44:14 -080065 ssize_t data_len = get_unaligned<int32_t>(data + pos);
Tao Bao97555da2016-12-15 10:15:06 -080066 ASSERT_GT(data_len, 0);
67 pos += 4 + data_len;
68 ASSERT_LE(pos, size);
69 raw++;
70 } else if (type == CHUNK_DEFLATE) {
71 pos += 60;
72 ASSERT_LE(pos, size);
73 deflate++;
74 } else {
75 FAIL() << "Invalid patch type: " << type;
76 }
77 }
78
79 if (num_normal != nullptr) *num_normal = normal;
80 if (num_raw != nullptr) *num_raw = raw;
81 if (num_deflate != nullptr) *num_deflate = deflate;
82}
83
Tianjie Xu2903cdd2017-08-18 18:15:47 -070084static void GenerateTarget(const std::string& src, const std::string& patch, std::string* patched) {
85 patched->clear();
86 ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
87 reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
88 [&](const unsigned char* data, size_t len) {
89 patched->append(reinterpret_cast<const char*>(data), len);
90 return len;
91 }));
92}
93
Tao Baoc0e1c462017-02-01 10:20:10 -080094static void verify_patched_image(const std::string& src, const std::string& patch,
95 const std::string& tgt) {
96 std::string patched;
Tianjie Xu2903cdd2017-08-18 18:15:47 -070097 GenerateTarget(src, patch, &patched);
Tao Baoc0e1c462017-02-01 10:20:10 -080098 ASSERT_EQ(tgt, patched);
99}
100
Tao Bao97555da2016-12-15 10:15:06 -0800101TEST(ImgdiffTest, invalid_args) {
102 // Insufficient inputs.
103 ASSERT_EQ(2, imgdiff(1, (const char* []){ "imgdiff" }));
104 ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-z" }));
105 ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-b" }));
106 ASSERT_EQ(2, imgdiff(3, (const char* []){ "imgdiff", "-z", "-b" }));
107
108 // Failed to read bonus file.
109 ASSERT_EQ(1, imgdiff(3, (const char* []){ "imgdiff", "-b", "doesntexist" }));
110
111 // Failed to read input files.
112 ASSERT_EQ(1, imgdiff(4, (const char* []){ "imgdiff", "doesntexist", "doesntexist", "output" }));
113 ASSERT_EQ(
114 1, imgdiff(5, (const char* []){ "imgdiff", "-z", "doesntexist", "doesntexist", "output" }));
115}
116
117TEST(ImgdiffTest, image_mode_smoke) {
118 // Random bytes.
119 const std::string src("abcdefg");
120 TemporaryFile src_file;
121 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
122
123 const std::string tgt("abcdefgxyz");
124 TemporaryFile tgt_file;
125 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
126
127 TemporaryFile patch_file;
128 std::vector<const char*> args = {
129 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
130 };
131 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
132
133 // Verify.
134 std::string patch;
135 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
136
137 // Expect one CHUNK_RAW entry.
138 size_t num_normal;
139 size_t num_raw;
140 size_t num_deflate;
141 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
142 ASSERT_EQ(0U, num_normal);
143 ASSERT_EQ(0U, num_deflate);
144 ASSERT_EQ(1U, num_raw);
145
Tao Baoc0e1c462017-02-01 10:20:10 -0800146 verify_patched_image(src, patch, tgt);
Tao Bao97555da2016-12-15 10:15:06 -0800147}
148
149TEST(ImgdiffTest, zip_mode_smoke_store) {
150 // Construct src and tgt zip files.
151 TemporaryFile src_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700152 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
Tao Bao97555da2016-12-15 10:15:06 -0800153 ZipWriter src_writer(src_file_ptr);
154 ASSERT_EQ(0, src_writer.StartEntry("file1.txt", 0)); // Store mode.
155 const std::string src_content("abcdefg");
156 ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
157 ASSERT_EQ(0, src_writer.FinishEntry());
158 ASSERT_EQ(0, src_writer.Finish());
159 ASSERT_EQ(0, fclose(src_file_ptr));
160
161 TemporaryFile tgt_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700162 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
Tao Bao97555da2016-12-15 10:15:06 -0800163 ZipWriter tgt_writer(tgt_file_ptr);
164 ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", 0)); // Store mode.
165 const std::string tgt_content("abcdefgxyz");
166 ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
167 ASSERT_EQ(0, tgt_writer.FinishEntry());
168 ASSERT_EQ(0, tgt_writer.Finish());
169 ASSERT_EQ(0, fclose(tgt_file_ptr));
170
171 // Compute patch.
172 TemporaryFile patch_file;
173 std::vector<const char*> args = {
174 "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
175 };
176 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
177
178 // Verify.
179 std::string tgt;
180 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
181 std::string src;
182 ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
183 std::string patch;
184 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
185
186 // Expect one CHUNK_RAW entry.
187 size_t num_normal;
188 size_t num_raw;
189 size_t num_deflate;
190 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
191 ASSERT_EQ(0U, num_normal);
192 ASSERT_EQ(0U, num_deflate);
193 ASSERT_EQ(1U, num_raw);
194
Tao Baoc0e1c462017-02-01 10:20:10 -0800195 verify_patched_image(src, patch, tgt);
Tao Bao97555da2016-12-15 10:15:06 -0800196}
197
198TEST(ImgdiffTest, zip_mode_smoke_compressed) {
Tianjie Xucc61cf62018-05-23 22:23:31 -0700199 // Generate 1 block of random data.
200 std::string random_data;
201 random_data.reserve(4096);
202 generate_n(back_inserter(random_data), 4096, []() { return rand() % 256; });
203
Tao Bao97555da2016-12-15 10:15:06 -0800204 // Construct src and tgt zip files.
205 TemporaryFile src_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700206 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
Tao Bao97555da2016-12-15 10:15:06 -0800207 ZipWriter src_writer(src_file_ptr);
208 ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
Tianjie Xucc61cf62018-05-23 22:23:31 -0700209 const std::string src_content = random_data;
Tao Bao97555da2016-12-15 10:15:06 -0800210 ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
211 ASSERT_EQ(0, src_writer.FinishEntry());
212 ASSERT_EQ(0, src_writer.Finish());
213 ASSERT_EQ(0, fclose(src_file_ptr));
214
215 TemporaryFile tgt_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700216 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
Tao Bao97555da2016-12-15 10:15:06 -0800217 ZipWriter tgt_writer(tgt_file_ptr);
218 ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
Tianjie Xucc61cf62018-05-23 22:23:31 -0700219 const std::string tgt_content = random_data + "extra contents";
Tao Bao97555da2016-12-15 10:15:06 -0800220 ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
221 ASSERT_EQ(0, tgt_writer.FinishEntry());
222 ASSERT_EQ(0, tgt_writer.Finish());
223 ASSERT_EQ(0, fclose(tgt_file_ptr));
224
225 // Compute patch.
226 TemporaryFile patch_file;
227 std::vector<const char*> args = {
228 "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
229 };
230 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
231
232 // Verify.
233 std::string tgt;
234 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
235 std::string src;
236 ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
237 std::string patch;
238 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
239
240 // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
241 size_t num_normal;
242 size_t num_raw;
243 size_t num_deflate;
244 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
245 ASSERT_EQ(0U, num_normal);
246 ASSERT_EQ(1U, num_deflate);
247 ASSERT_EQ(2U, num_raw);
248
Tao Baoc0e1c462017-02-01 10:20:10 -0800249 verify_patched_image(src, patch, tgt);
Tao Bao97555da2016-12-15 10:15:06 -0800250}
251
Tianjie Xucc61cf62018-05-23 22:23:31 -0700252TEST(ImgdiffTest, zip_mode_empty_target) {
253 TemporaryFile src_file;
254 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
255 ZipWriter src_writer(src_file_ptr);
256 ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
257 const std::string src_content = "abcdefg";
258 ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
259 ASSERT_EQ(0, src_writer.FinishEntry());
260 ASSERT_EQ(0, src_writer.Finish());
261 ASSERT_EQ(0, fclose(src_file_ptr));
262
263 // Construct a empty entry in the target zip.
264 TemporaryFile tgt_file;
265 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
266 ZipWriter tgt_writer(tgt_file_ptr);
267 ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
268 const std::string tgt_content;
269 ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
270 ASSERT_EQ(0, tgt_writer.FinishEntry());
271 ASSERT_EQ(0, tgt_writer.Finish());
272
273 // Compute patch.
274 TemporaryFile patch_file;
275 std::vector<const char*> args = {
276 "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
277 };
278 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
279
280 // Verify.
281 std::string tgt;
282 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
283 std::string src;
284 ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
285 std::string patch;
286 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
287
288 verify_patched_image(src, patch, tgt);
289}
290
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800291TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) {
Tianjie Xucc61cf62018-05-23 22:23:31 -0700292 // Generate 1 block of random data.
293 std::string random_data;
294 random_data.reserve(4096);
295 generate_n(back_inserter(random_data), 4096, []() { return rand() % 256; });
296
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800297 // Construct src and tgt zip files.
298 TemporaryFile src_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700299 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800300 ZipWriter src_writer(src_file_ptr);
301 ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
Tianjie Xucc61cf62018-05-23 22:23:31 -0700302 const std::string src_content = random_data;
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800303 ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
304 ASSERT_EQ(0, src_writer.FinishEntry());
305 ASSERT_EQ(0, src_writer.Finish());
306 ASSERT_EQ(0, fclose(src_file_ptr));
307
308 TemporaryFile tgt_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700309 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800310 ZipWriter tgt_writer(tgt_file_ptr);
311 ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
Tianjie Xucc61cf62018-05-23 22:23:31 -0700312 const std::string tgt_content = random_data + "abcdefg";
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800313 ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
314 ASSERT_EQ(0, tgt_writer.FinishEntry());
315 ASSERT_EQ(0, tgt_writer.Finish());
316 // Add trailing zeros to the target zip file.
317 std::vector<uint8_t> zeros(10);
318 ASSERT_EQ(zeros.size(), fwrite(zeros.data(), sizeof(uint8_t), zeros.size(), tgt_file_ptr));
319 ASSERT_EQ(0, fclose(tgt_file_ptr));
320
321 // Compute patch.
322 TemporaryFile patch_file;
323 std::vector<const char*> args = {
324 "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
325 };
326 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
327
328 // Verify.
329 std::string tgt;
330 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
331 std::string src;
332 ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
333 std::string patch;
334 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
335
336 // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
337 size_t num_normal;
338 size_t num_raw;
339 size_t num_deflate;
340 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
341 ASSERT_EQ(0U, num_normal);
342 ASSERT_EQ(1U, num_deflate);
343 ASSERT_EQ(2U, num_raw);
344
Tao Baoc0e1c462017-02-01 10:20:10 -0800345 verify_patched_image(src, patch, tgt);
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800346}
347
Tao Bao97555da2016-12-15 10:15:06 -0800348TEST(ImgdiffTest, image_mode_simple) {
Tianjie Xucc61cf62018-05-23 22:23:31 -0700349 std::string gzipped_source_path = from_testdata_base("gzipped_source");
350 std::string gzipped_source;
351 ASSERT_TRUE(android::base::ReadFileToString(gzipped_source_path, &gzipped_source));
352
353 const std::string src = "abcdefg" + gzipped_source;
Tao Bao97555da2016-12-15 10:15:06 -0800354 TemporaryFile src_file;
355 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
356
Tianjie Xucc61cf62018-05-23 22:23:31 -0700357 std::string gzipped_target_path = from_testdata_base("gzipped_target");
358 std::string gzipped_target;
359 ASSERT_TRUE(android::base::ReadFileToString(gzipped_target_path, &gzipped_target));
360 const std::string tgt = "abcdefgxyz" + gzipped_target;
361
Tao Bao97555da2016-12-15 10:15:06 -0800362 TemporaryFile tgt_file;
363 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
364
365 TemporaryFile patch_file;
366 std::vector<const char*> args = {
367 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
368 };
369 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
370
371 // Verify.
372 std::string patch;
373 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
374
375 // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
376 size_t num_normal;
377 size_t num_raw;
378 size_t num_deflate;
379 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
380 ASSERT_EQ(0U, num_normal);
381 ASSERT_EQ(1U, num_deflate);
382 ASSERT_EQ(2U, num_raw);
383
Tao Baoc0e1c462017-02-01 10:20:10 -0800384 verify_patched_image(src, patch, tgt);
Tao Bao97555da2016-12-15 10:15:06 -0800385}
386
Tianjie Xu14ebc1e2017-07-05 12:04:07 -0700387TEST(ImgdiffTest, image_mode_bad_gzip) {
388 // Modify the uncompressed length in the gzip footer.
389 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
390 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
391 '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
392 '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
393 '\xff', '\xff', '\xff' };
394 const std::string src(src_data.cbegin(), src_data.cend());
395 TemporaryFile src_file;
396 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
397
398 // Modify the uncompressed length in the gzip footer.
399 const std::vector<char> tgt_data = {
400 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
401 '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
402 '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\xff', '\xff', '\xff'
403 };
404 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
405 TemporaryFile tgt_file;
406 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
407
408 TemporaryFile patch_file;
409 std::vector<const char*> args = {
410 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
411 };
412 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
413
414 // Verify.
415 std::string patch;
416 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
417 verify_patched_image(src, patch, tgt);
418}
419
Tao Bao97555da2016-12-15 10:15:06 -0800420TEST(ImgdiffTest, image_mode_different_num_chunks) {
421 // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd) + gzipped "test".
422 const std::vector<char> src_data = {
423 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\x1f', '\x8b', '\x08',
424 '\x00', '\xc4', '\x1e', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac', '\x02',
425 '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03', '\x00', '\x00', '\x00', '\x1f', '\x8b',
426 '\x08', '\x00', '\xb2', '\x3a', '\x53', '\x58', '\x00', '\x03', '\x2b', '\x49', '\x2d',
427 '\x2e', '\x01', '\x00', '\x0c', '\x7e', '\x7f', '\xd8', '\x04', '\x00', '\x00', '\x00'
428 };
429 const std::string src(src_data.cbegin(), src_data.cend());
430 TemporaryFile src_file;
431 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
432
433 // tgt: "abcdefgxyz" + gzipped "xxyyzz".
434 const std::vector<char> tgt_data = {
435 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
436 '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
437 '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
438 };
439 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
440 TemporaryFile tgt_file;
441 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
442
443 TemporaryFile patch_file;
444 std::vector<const char*> args = {
445 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
446 };
447 ASSERT_EQ(1, imgdiff(args.size(), args.data()));
448}
449
450TEST(ImgdiffTest, image_mode_merge_chunks) {
Tianjie Xucc61cf62018-05-23 22:23:31 -0700451 // src: "abcdefg" + gzipped_source.
452 std::string gzipped_source_path = from_testdata_base("gzipped_source");
453 std::string gzipped_source;
454 ASSERT_TRUE(android::base::ReadFileToString(gzipped_source_path, &gzipped_source));
455
456 const std::string src = "abcdefg" + gzipped_source;
Tao Bao97555da2016-12-15 10:15:06 -0800457 TemporaryFile src_file;
458 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
459
Tianjie Xucc61cf62018-05-23 22:23:31 -0700460 // tgt: gzipped_target + "abcdefgxyz".
461 std::string gzipped_target_path = from_testdata_base("gzipped_target");
462 std::string gzipped_target;
463 ASSERT_TRUE(android::base::ReadFileToString(gzipped_target_path, &gzipped_target));
464
465 const std::string tgt = gzipped_target + "abcdefgxyz";
Tao Bao97555da2016-12-15 10:15:06 -0800466 TemporaryFile tgt_file;
467 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
468
469 // Since a gzipped entry will become CHUNK_RAW (header) + CHUNK_DEFLATE (data) +
470 // CHUNK_RAW (footer), they both should contain the same chunk types after merging.
471
472 TemporaryFile patch_file;
473 std::vector<const char*> args = {
474 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
475 };
476 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
477
478 // Verify.
479 std::string patch;
480 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
481
482 // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
483 size_t num_normal;
484 size_t num_raw;
485 size_t num_deflate;
486 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
487 ASSERT_EQ(0U, num_normal);
488 ASSERT_EQ(1U, num_deflate);
489 ASSERT_EQ(2U, num_raw);
490
Tao Baoc0e1c462017-02-01 10:20:10 -0800491 verify_patched_image(src, patch, tgt);
Tao Bao97555da2016-12-15 10:15:06 -0800492}
493
494TEST(ImgdiffTest, image_mode_spurious_magic) {
495 // src: "abcdefgh" + '0x1f8b0b00' + some bytes.
496 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
497 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
498 '\x53', '\x58', 't', 'e', 's', 't' };
499 const std::string src(src_data.cbegin(), src_data.cend());
500 TemporaryFile src_file;
501 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
502
503 // tgt: "abcdefgxyz".
504 const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
505 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
506 TemporaryFile tgt_file;
507 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
508
509 TemporaryFile patch_file;
510 std::vector<const char*> args = {
511 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
512 };
513 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
514
515 // Verify.
516 std::string patch;
517 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
518
519 // Expect one CHUNK_RAW (header) entry.
520 size_t num_normal;
521 size_t num_raw;
522 size_t num_deflate;
523 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
524 ASSERT_EQ(0U, num_normal);
525 ASSERT_EQ(0U, num_deflate);
526 ASSERT_EQ(1U, num_raw);
527
Tao Baoc0e1c462017-02-01 10:20:10 -0800528 verify_patched_image(src, patch, tgt);
Tao Bao97555da2016-12-15 10:15:06 -0800529}
530
Tao Baod37ce8f2016-12-17 17:10:04 -0800531TEST(ImgdiffTest, image_mode_short_input1) {
532 // src: "abcdefgh" + '0x1f8b0b'.
533 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f',
534 'g', 'h', '\x1f', '\x8b', '\x08' };
535 const std::string src(src_data.cbegin(), src_data.cend());
536 TemporaryFile src_file;
537 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
538
539 // tgt: "abcdefgxyz".
540 const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
541 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
542 TemporaryFile tgt_file;
543 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
544
545 TemporaryFile patch_file;
546 std::vector<const char*> args = {
547 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
548 };
549 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
550
551 // Verify.
552 std::string patch;
553 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
554
555 // Expect one CHUNK_RAW (header) entry.
556 size_t num_normal;
557 size_t num_raw;
558 size_t num_deflate;
559 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
560 ASSERT_EQ(0U, num_normal);
561 ASSERT_EQ(0U, num_deflate);
562 ASSERT_EQ(1U, num_raw);
563
Tao Baoc0e1c462017-02-01 10:20:10 -0800564 verify_patched_image(src, patch, tgt);
Tao Baod37ce8f2016-12-17 17:10:04 -0800565}
566
567TEST(ImgdiffTest, image_mode_short_input2) {
568 // src: "abcdefgh" + '0x1f8b0b00'.
569 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f',
570 'g', 'h', '\x1f', '\x8b', '\x08', '\x00' };
571 const std::string src(src_data.cbegin(), src_data.cend());
572 TemporaryFile src_file;
573 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
574
575 // tgt: "abcdefgxyz".
576 const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
577 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
578 TemporaryFile tgt_file;
579 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
580
581 TemporaryFile patch_file;
582 std::vector<const char*> args = {
583 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
584 };
585 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
586
587 // Verify.
588 std::string patch;
589 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
590
591 // Expect one CHUNK_RAW (header) entry.
592 size_t num_normal;
593 size_t num_raw;
594 size_t num_deflate;
595 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
596 ASSERT_EQ(0U, num_normal);
597 ASSERT_EQ(0U, num_deflate);
598 ASSERT_EQ(1U, num_raw);
599
Tao Baoc0e1c462017-02-01 10:20:10 -0800600 verify_patched_image(src, patch, tgt);
Tao Baod37ce8f2016-12-17 17:10:04 -0800601}
602
Tao Bao97555da2016-12-15 10:15:06 -0800603TEST(ImgdiffTest, image_mode_single_entry_long) {
604 // src: "abcdefgh" + '0x1f8b0b00' + some bytes.
605 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
606 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
607 '\x53', '\x58', 't', 'e', 's', 't' };
608 const std::string src(src_data.cbegin(), src_data.cend());
609 TemporaryFile src_file;
610 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
611
612 // tgt: "abcdefgxyz" + 200 bytes.
613 std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
614 tgt_data.resize(tgt_data.size() + 200);
615
616 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
617 TemporaryFile tgt_file;
618 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
619
620 TemporaryFile patch_file;
621 std::vector<const char*> args = {
622 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
623 };
624 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
625
626 // Verify.
627 std::string patch;
628 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
629
630 // Expect one CHUNK_NORMAL entry, since it's exceeding the 160-byte limit for RAW.
631 size_t num_normal;
632 size_t num_raw;
633 size_t num_deflate;
634 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
635 ASSERT_EQ(1U, num_normal);
636 ASSERT_EQ(0U, num_deflate);
637 ASSERT_EQ(0U, num_raw);
638
Tao Baoc0e1c462017-02-01 10:20:10 -0800639 verify_patched_image(src, patch, tgt);
Tao Bao97555da2016-12-15 10:15:06 -0800640}
Tianjie Xuce5fa5e2017-05-15 12:32:33 -0700641
642TEST(ImgpatchTest, image_mode_patch_corruption) {
643 // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd).
644 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
645 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
646 '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
647 '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
648 '\x00', '\x00', '\x00' };
649 const std::string src(src_data.cbegin(), src_data.cend());
650 TemporaryFile src_file;
651 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
652
653 // tgt: "abcdefgxyz" + gzipped "xxyyzz".
654 const std::vector<char> tgt_data = {
655 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
656 '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
657 '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
658 };
659 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
660 TemporaryFile tgt_file;
661 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
662
663 TemporaryFile patch_file;
664 std::vector<const char*> args = {
665 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
666 };
667 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
668
669 // Verify.
670 std::string patch;
671 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
672 verify_patched_image(src, patch, tgt);
673
674 // Corrupt the end of the patch and expect the ApplyImagePatch to fail.
675 patch.insert(patch.end() - 10, 10, '0');
676 ASSERT_EQ(-1, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
677 reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
678 [](const unsigned char* /*data*/, size_t len) { return len; }));
679}
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700680
681static void construct_store_entry(const std::vector<std::tuple<std::string, size_t, char>>& info,
682 ZipWriter* writer) {
683 for (auto& t : info) {
684 // Create t(1) blocks of t(2), and write the data to t(0)
685 ASSERT_EQ(0, writer->StartEntry(std::get<0>(t).c_str(), 0));
686 const std::string content(std::get<1>(t) * 4096, std::get<2>(t));
687 ASSERT_EQ(0, writer->WriteBytes(content.data(), content.size()));
688 ASSERT_EQ(0, writer->FinishEntry());
689 }
690}
691
692static void construct_deflate_entry(const std::vector<std::tuple<std::string, size_t, size_t>>& info,
693 ZipWriter* writer, const std::string& data) {
694 for (auto& t : info) {
695 // t(0): entry_name; t(1): block offset; t(2) length in blocks.
696 ASSERT_EQ(0, writer->StartEntry(std::get<0>(t).c_str(), ZipWriter::kCompress));
697 ASSERT_EQ(0, writer->WriteBytes(data.data() + std::get<1>(t) * 4096, std::get<2>(t) * 4096));
698 ASSERT_EQ(0, writer->FinishEntry());
699 }
700}
701
Tao Bao54c1db42017-11-01 22:21:55 -0700702// Look for the source and patch pieces in debug_dir. Generate a target piece from each pair.
703// Concatenate all the target pieces and match against the orignal one. Used pieces in debug_dir
704// will be cleaned up.
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700705static void GenerateAndCheckSplitTarget(const std::string& debug_dir, size_t count,
706 const std::string& tgt) {
707 std::string patched;
708 for (size_t i = 0; i < count; i++) {
709 std::string split_src_path = android::base::StringPrintf("%s/src-%zu", debug_dir.c_str(), i);
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700710 std::string split_src;
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700711 ASSERT_TRUE(android::base::ReadFileToString(split_src_path, &split_src));
Tao Bao54c1db42017-11-01 22:21:55 -0700712 ASSERT_EQ(0, unlink(split_src_path.c_str()));
713
714 std::string split_patch_path =
715 android::base::StringPrintf("%s/patch-%zu", debug_dir.c_str(), i);
716 std::string split_patch;
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700717 ASSERT_TRUE(android::base::ReadFileToString(split_patch_path, &split_patch));
Tao Bao54c1db42017-11-01 22:21:55 -0700718 ASSERT_EQ(0, unlink(split_patch_path.c_str()));
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700719
720 std::string split_tgt;
721 GenerateTarget(split_src, split_patch, &split_tgt);
722 patched += split_tgt;
723 }
724
725 // Verify we can get back the original target image.
726 ASSERT_EQ(tgt, patched);
727}
728
729std::vector<ImageChunk> ConstructImageChunks(
730 const std::vector<uint8_t>& content, const std::vector<std::tuple<std::string, size_t>>& info) {
731 std::vector<ImageChunk> chunks;
732 size_t start = 0;
733 for (const auto& t : info) {
734 size_t length = std::get<1>(t);
735 chunks.emplace_back(CHUNK_NORMAL, start, &content, length, std::get<0>(t));
736 start += length;
737 }
738
739 return chunks;
740}
741
742TEST(ImgdiffTest, zip_mode_split_image_smoke) {
743 std::vector<uint8_t> content;
744 content.reserve(4096 * 50);
745 uint8_t n = 0;
746 generate_n(back_inserter(content), 4096 * 50, [&n]() { return n++ / 4096; });
747
748 ZipModeImage tgt_image(false, 4096 * 10);
749 std::vector<ImageChunk> tgt_chunks = ConstructImageChunks(content, { { "a", 100 },
750 { "b", 4096 * 2 },
751 { "c", 4096 * 3 },
752 { "d", 300 },
753 { "e-0", 4096 * 10 },
754 { "e-1", 4096 * 5 },
755 { "CD", 200 } });
756 tgt_image.Initialize(std::move(tgt_chunks),
757 std::vector<uint8_t>(content.begin(), content.begin() + 82520));
758
759 tgt_image.DumpChunks();
760
761 ZipModeImage src_image(true, 4096 * 10);
762 std::vector<ImageChunk> src_chunks = ConstructImageChunks(content, { { "b", 4096 * 3 },
763 { "c-0", 4096 * 10 },
764 { "c-1", 4096 * 2 },
765 { "a", 4096 * 5 },
766 { "e-0", 4096 * 10 },
767 { "e-1", 10000 },
768 { "CD", 5000 } });
769 src_image.Initialize(std::move(src_chunks),
770 std::vector<uint8_t>(content.begin(), content.begin() + 137880));
771
772 std::vector<ZipModeImage> split_tgt_images;
773 std::vector<ZipModeImage> split_src_images;
774 std::vector<SortedRangeSet> split_src_ranges;
775
776 ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images,
777 &split_src_images, &split_src_ranges);
778
779 // src_piece 1: a 5 blocks, b 3 blocks
780 // src_piece 2: c-0 10 blocks
781 // src_piece 3: d 0 block, e-0 10 blocks
782 // src_piece 4: e-1 2 blocks; CD 2 blocks
783 ASSERT_EQ(split_tgt_images.size(), split_src_images.size());
784 ASSERT_EQ(static_cast<size_t>(4), split_tgt_images.size());
785
786 ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[0].NumOfChunks());
787 ASSERT_EQ(static_cast<size_t>(12288), split_tgt_images[0][0].DataLengthForPatch());
788 ASSERT_EQ("4,0,3,15,20", split_src_ranges[0].ToString());
789
790 ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[1].NumOfChunks());
791 ASSERT_EQ(static_cast<size_t>(12288), split_tgt_images[1][0].DataLengthForPatch());
792 ASSERT_EQ("2,3,13", split_src_ranges[1].ToString());
793
794 ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[2].NumOfChunks());
795 ASSERT_EQ(static_cast<size_t>(40960), split_tgt_images[2][0].DataLengthForPatch());
796 ASSERT_EQ("2,20,30", split_src_ranges[2].ToString());
797
798 ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[3].NumOfChunks());
799 ASSERT_EQ(static_cast<size_t>(16984), split_tgt_images[3][0].DataLengthForPatch());
800 ASSERT_EQ("2,30,34", split_src_ranges[3].ToString());
801}
802
803TEST(ImgdiffTest, zip_mode_store_large_apk) {
804 // Construct src and tgt zip files with limit = 10 blocks.
805 // src tgt
806 // 12 blocks 'd' 3 blocks 'a'
807 // 8 blocks 'c' 3 blocks 'b'
808 // 3 blocks 'b' 8 blocks 'c' (exceeds limit)
809 // 3 blocks 'a' 12 blocks 'd' (exceeds limit)
810 // 3 blocks 'e'
811 TemporaryFile tgt_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700812 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700813 ZipWriter tgt_writer(tgt_file_ptr);
814 construct_store_entry(
815 { { "a", 3, 'a' }, { "b", 3, 'b' }, { "c", 8, 'c' }, { "d", 12, 'd' }, { "e", 3, 'e' } },
816 &tgt_writer);
817 ASSERT_EQ(0, tgt_writer.Finish());
818 ASSERT_EQ(0, fclose(tgt_file_ptr));
819
820 TemporaryFile src_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700821 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700822 ZipWriter src_writer(src_file_ptr);
823 construct_store_entry({ { "d", 12, 'd' }, { "c", 8, 'c' }, { "b", 3, 'b' }, { "a", 3, 'a' } },
824 &src_writer);
825 ASSERT_EQ(0, src_writer.Finish());
826 ASSERT_EQ(0, fclose(src_file_ptr));
827
828 // Compute patch.
829 TemporaryFile patch_file;
Tianjie Xu82582b42017-08-31 18:05:19 -0700830 TemporaryFile split_info_file;
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700831 TemporaryDir debug_dir;
Tianjie Xu82582b42017-08-31 18:05:19 -0700832 std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
Tianjie Xu8ba7c452017-09-12 11:13:02 -0700833 std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700834 std::vector<const char*> args = {
Tianjie Xu82582b42017-08-31 18:05:19 -0700835 "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(),
836 src_file.path, tgt_file.path, patch_file.path,
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700837 };
838 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
839
840 std::string tgt;
841 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
842
Tao Baof29ed3e2017-10-24 16:48:32 -0700843 // Expect 4 pieces of patch. (Roughly 3'a',3'b'; 8'c'; 10'd'; 2'd'3'e')
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700844 GenerateAndCheckSplitTarget(debug_dir.path, 4, tgt);
845}
846
847TEST(ImgdiffTest, zip_mode_deflate_large_apk) {
Tianjie Xu113fe052017-10-24 16:12:35 -0700848 // Src and tgt zip files are constructed as follows.
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700849 // src tgt
850 // 22 blocks, "d" 4 blocks, "a"
851 // 5 blocks, "b" 4 blocks, "b"
Tianjie Xu113fe052017-10-24 16:12:35 -0700852 // 3 blocks, "a" 8 blocks, "c" (exceeds limit)
853 // 1 block, "g" 20 blocks, "d" (exceeds limit)
854 // 8 blocks, "c" 2 blocks, "e"
855 // 1 block, "f" 1 block , "f"
856 std::string tgt_path = from_testdata_base("deflate_tgt.zip");
857 std::string src_path = from_testdata_base("deflate_src.zip");
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700858
859 ZipModeImage src_image(true, 10 * 4096);
860 ZipModeImage tgt_image(false, 10 * 4096);
Tianjie Xu113fe052017-10-24 16:12:35 -0700861 ASSERT_TRUE(src_image.Initialize(src_path));
862 ASSERT_TRUE(tgt_image.Initialize(tgt_path));
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700863 ASSERT_TRUE(ZipModeImage::CheckAndProcessChunks(&tgt_image, &src_image));
864
865 src_image.DumpChunks();
866 tgt_image.DumpChunks();
867
868 std::vector<ZipModeImage> split_tgt_images;
869 std::vector<ZipModeImage> split_src_images;
870 std::vector<SortedRangeSet> split_src_ranges;
871 ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images,
872 &split_src_images, &split_src_ranges);
873
Tianjie Xu113fe052017-10-24 16:12:35 -0700874 // Expected split images with limit = 10 blocks.
875 // src_piece 0: a 3 blocks, b 5 blocks
876 // src_piece 1: c 8 blocks
877 // src_piece 2: d-0 10 block
878 // src_piece 3: d-1 10 blocks
879 // src_piece 4: e 1 block, CD
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700880 ASSERT_EQ(split_tgt_images.size(), split_src_images.size());
881 ASSERT_EQ(static_cast<size_t>(5), split_tgt_images.size());
882
883 ASSERT_EQ(static_cast<size_t>(2), split_src_images[0].NumOfChunks());
884 ASSERT_EQ("a", split_src_images[0][0].GetEntryName());
885 ASSERT_EQ("b", split_src_images[0][1].GetEntryName());
886
887 ASSERT_EQ(static_cast<size_t>(1), split_src_images[1].NumOfChunks());
888 ASSERT_EQ("c", split_src_images[1][0].GetEntryName());
889
890 ASSERT_EQ(static_cast<size_t>(0), split_src_images[2].NumOfChunks());
891 ASSERT_EQ(static_cast<size_t>(0), split_src_images[3].NumOfChunks());
892 ASSERT_EQ(static_cast<size_t>(0), split_src_images[4].NumOfChunks());
893
894 // Compute patch.
895 TemporaryFile patch_file;
Tianjie Xu82582b42017-08-31 18:05:19 -0700896 TemporaryFile split_info_file;
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700897 TemporaryDir debug_dir;
898 ASSERT_TRUE(ZipModeImage::GeneratePatches(split_tgt_images, split_src_images, split_src_ranges,
Tianjie Xu82582b42017-08-31 18:05:19 -0700899 patch_file.path, split_info_file.path, debug_dir.path));
900
901 // Verify the content of split info.
902 // Expect 5 pieces of patch. ["a","b"; "c"; "d-0"; "d-1"; "e"]
903 std::string split_info_string;
904 android::base::ReadFileToString(split_info_file.path, &split_info_string);
905 std::vector<std::string> info_list =
906 android::base::Split(android::base::Trim(split_info_string), "\n");
907
908 ASSERT_EQ(static_cast<size_t>(7), info_list.size());
909 ASSERT_EQ("2", android::base::Trim(info_list[0]));
910 ASSERT_EQ("5", android::base::Trim(info_list[1]));
911
Tianjie Xu113fe052017-10-24 16:12:35 -0700912 std::string tgt;
913 ASSERT_TRUE(android::base::ReadFileToString(tgt_path, &tgt));
914 ASSERT_EQ(static_cast<size_t>(160385), tgt.size());
915 std::vector<std::string> tgt_file_ranges = {
916 "36864 2,22,31", "32768 2,31,40", "40960 2,0,11", "40960 2,11,21", "8833 4,21,22,40,41",
917 };
918
Tianjie Xu82582b42017-08-31 18:05:19 -0700919 for (size_t i = 0; i < 5; i++) {
Tianjie Xu113fe052017-10-24 16:12:35 -0700920 struct stat st;
Tianjie Xu82582b42017-08-31 18:05:19 -0700921 std::string path = android::base::StringPrintf("%s/patch-%zu", debug_dir.path, i);
922 ASSERT_EQ(0, stat(path.c_str(), &st));
Tianjie Xu113fe052017-10-24 16:12:35 -0700923 ASSERT_EQ(std::to_string(st.st_size) + " " + tgt_file_ranges[i],
924 android::base::Trim(info_list[i + 2]));
Tianjie Xu82582b42017-08-31 18:05:19 -0700925 }
926
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700927 GenerateAndCheckSplitTarget(debug_dir.path, 5, tgt);
928}
929
930TEST(ImgdiffTest, zip_mode_no_match_source) {
931 // Generate 20 blocks of random data.
932 std::string random_data;
933 random_data.reserve(4096 * 20);
934 generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; });
935
936 TemporaryFile tgt_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700937 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700938 ZipWriter tgt_writer(tgt_file_ptr);
939
940 construct_deflate_entry({ { "a", 0, 4 }, { "b", 5, 5 }, { "c", 11, 5 } }, &tgt_writer,
941 random_data);
942
943 ASSERT_EQ(0, tgt_writer.Finish());
944 ASSERT_EQ(0, fclose(tgt_file_ptr));
945
946 // We don't have a matching source entry.
947 TemporaryFile src_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700948 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700949 ZipWriter src_writer(src_file_ptr);
950 construct_store_entry({ { "d", 1, 'd' } }, &src_writer);
951 ASSERT_EQ(0, src_writer.Finish());
952 ASSERT_EQ(0, fclose(src_file_ptr));
953
954 // Compute patch.
955 TemporaryFile patch_file;
Tianjie Xu82582b42017-08-31 18:05:19 -0700956 TemporaryFile split_info_file;
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700957 TemporaryDir debug_dir;
Tianjie Xu82582b42017-08-31 18:05:19 -0700958 std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
Tianjie Xu8ba7c452017-09-12 11:13:02 -0700959 std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700960 std::vector<const char*> args = {
Tianjie Xu82582b42017-08-31 18:05:19 -0700961 "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), split_info_arg.c_str(),
962 src_file.path, tgt_file.path, patch_file.path,
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700963 };
964 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
965
966 std::string tgt;
967 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
968
969 // Expect 1 pieces of patch due to no matching source entry.
970 GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt);
971}
972
973TEST(ImgdiffTest, zip_mode_large_enough_limit) {
974 // Generate 20 blocks of random data.
975 std::string random_data;
976 random_data.reserve(4096 * 20);
977 generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; });
978
979 TemporaryFile tgt_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700980 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700981 ZipWriter tgt_writer(tgt_file_ptr);
982
983 construct_deflate_entry({ { "a", 0, 10 }, { "b", 10, 5 } }, &tgt_writer, random_data);
984
985 ASSERT_EQ(0, tgt_writer.Finish());
986 ASSERT_EQ(0, fclose(tgt_file_ptr));
987
988 // Construct 10 blocks of source.
989 TemporaryFile src_file;
Tao Baof29ed3e2017-10-24 16:48:32 -0700990 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700991 ZipWriter src_writer(src_file_ptr);
992 construct_deflate_entry({ { "a", 1, 10 } }, &src_writer, random_data);
993 ASSERT_EQ(0, src_writer.Finish());
994 ASSERT_EQ(0, fclose(src_file_ptr));
995
996 // Compute patch with a limit of 20 blocks.
997 TemporaryFile patch_file;
Tianjie Xu82582b42017-08-31 18:05:19 -0700998 TemporaryFile split_info_file;
Tianjie Xu2903cdd2017-08-18 18:15:47 -0700999 TemporaryDir debug_dir;
Tianjie Xu82582b42017-08-31 18:05:19 -07001000 std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
Tianjie Xu8ba7c452017-09-12 11:13:02 -07001001 std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
Tianjie Xu2903cdd2017-08-18 18:15:47 -07001002 std::vector<const char*> args = {
Tianjie Xu82582b42017-08-31 18:05:19 -07001003 "imgdiff", "-z", "--block-limit=20", split_info_arg.c_str(), debug_dir_arg.c_str(),
1004 src_file.path, tgt_file.path, patch_file.path,
Tianjie Xu2903cdd2017-08-18 18:15:47 -07001005 };
1006 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
1007
1008 std::string tgt;
1009 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
1010
Tao Baof29ed3e2017-10-24 16:48:32 -07001011 // Expect 1 piece of patch since limit is larger than the zip file size.
Tianjie Xu2903cdd2017-08-18 18:15:47 -07001012 GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt);
1013}
Tianjie Xu572abbb2018-02-22 15:40:39 -08001014
1015TEST(ImgdiffTest, zip_mode_large_apk_small_target_chunk) {
1016 TemporaryFile tgt_file;
1017 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
1018 ZipWriter tgt_writer(tgt_file_ptr);
1019
1020 // The first entry is less than 4096 bytes, followed immediately by an entry that has a very
1021 // large counterpart in the source file. Therefore the first entry will be patched separately.
1022 std::string small_chunk("a", 2000);
1023 ASSERT_EQ(0, tgt_writer.StartEntry("a", 0));
1024 ASSERT_EQ(0, tgt_writer.WriteBytes(small_chunk.data(), small_chunk.size()));
1025 ASSERT_EQ(0, tgt_writer.FinishEntry());
1026 construct_store_entry(
1027 {
1028 { "b", 12, 'b' }, { "c", 3, 'c' },
1029 },
1030 &tgt_writer);
1031 ASSERT_EQ(0, tgt_writer.Finish());
1032 ASSERT_EQ(0, fclose(tgt_file_ptr));
1033
1034 TemporaryFile src_file;
1035 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
1036 ZipWriter src_writer(src_file_ptr);
1037 construct_store_entry({ { "a", 1, 'a' }, { "b", 13, 'b' }, { "c", 1, 'c' } }, &src_writer);
1038 ASSERT_EQ(0, src_writer.Finish());
1039 ASSERT_EQ(0, fclose(src_file_ptr));
1040
1041 // Compute patch.
1042 TemporaryFile patch_file;
1043 TemporaryFile split_info_file;
1044 TemporaryDir debug_dir;
1045 std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
1046 std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
1047 std::vector<const char*> args = {
1048 "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(),
1049 src_file.path, tgt_file.path, patch_file.path,
1050 };
1051 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
1052
1053 std::string tgt;
1054 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
1055
1056 // Expect three split src images:
1057 // src_piece 0: a 1 blocks
1058 // src_piece 1: b-0 10 blocks
1059 // src_piece 2: b-1 3 blocks, c 1 blocks, CD
1060 GenerateAndCheckSplitTarget(debug_dir.path, 3, tgt);
1061}
1062
1063TEST(ImgdiffTest, zip_mode_large_apk_skipped_small_target_chunk) {
1064 TemporaryFile tgt_file;
1065 FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
1066 ZipWriter tgt_writer(tgt_file_ptr);
1067
1068 construct_store_entry(
1069 {
1070 { "a", 11, 'a' },
1071 },
1072 &tgt_writer);
1073
1074 // Construct a tiny target entry of 1 byte, which will be skipped due to the tail alignment of
1075 // the previous entry.
1076 std::string small_chunk("b", 1);
1077 ASSERT_EQ(0, tgt_writer.StartEntry("b", 0));
1078 ASSERT_EQ(0, tgt_writer.WriteBytes(small_chunk.data(), small_chunk.size()));
1079 ASSERT_EQ(0, tgt_writer.FinishEntry());
1080
1081 ASSERT_EQ(0, tgt_writer.Finish());
1082 ASSERT_EQ(0, fclose(tgt_file_ptr));
1083
1084 TemporaryFile src_file;
1085 FILE* src_file_ptr = fdopen(src_file.release(), "wb");
1086 ZipWriter src_writer(src_file_ptr);
1087 construct_store_entry(
1088 {
1089 { "a", 11, 'a' }, { "b", 11, 'b' },
1090 },
1091 &src_writer);
1092 ASSERT_EQ(0, src_writer.Finish());
1093 ASSERT_EQ(0, fclose(src_file_ptr));
1094
1095 // Compute patch.
1096 TemporaryFile patch_file;
1097 TemporaryFile split_info_file;
1098 TemporaryDir debug_dir;
1099 std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
1100 std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
1101 std::vector<const char*> args = {
1102 "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(),
1103 src_file.path, tgt_file.path, patch_file.path,
1104 };
1105 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
1106
1107 std::string tgt;
1108 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
1109
1110 // Expect two split src images:
1111 // src_piece 0: a-0 10 blocks
1112 // src_piece 1: a-0 1 block, CD
1113 GenerateAndCheckSplitTarget(debug_dir.path, 2, tgt);
1114}