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