blob: 3711859de86c30333f3a0c882bb6e45b9b23c920 [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
17#include <string>
18#include <vector>
19
20#include <android-base/file.h>
21#include <android-base/test_utils.h>
22#include <applypatch/imgdiff.h>
23#include <applypatch/imgpatch.h>
24#include <gtest/gtest.h>
25#include <ziparchive/zip_writer.h>
26
27#include "applypatch/utils.h"
28
29static ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) {
30 std::string* s = static_cast<std::string*>(token);
31 s->append(reinterpret_cast<const char*>(data), len);
32 return len;
33}
34
35// Sanity check for the given imgdiff patch header.
36static void verify_patch_header(const std::string& patch, size_t* num_normal, size_t* num_raw,
37 size_t* num_deflate) {
38 const size_t size = patch.size();
39 const char* data = patch.data();
40
41 ASSERT_GE(size, 12U);
42 ASSERT_EQ("IMGDIFF2", std::string(data, 8));
43
44 const int num_chunks = Read4(data + 8);
45 ASSERT_GE(num_chunks, 0);
46
47 size_t normal = 0;
48 size_t raw = 0;
49 size_t deflate = 0;
50
51 size_t pos = 12;
52 for (int i = 0; i < num_chunks; ++i) {
53 ASSERT_LE(pos + 4, size);
54 int type = Read4(data + pos);
55 pos += 4;
56 if (type == CHUNK_NORMAL) {
57 pos += 24;
58 ASSERT_LE(pos, size);
59 normal++;
60 } else if (type == CHUNK_RAW) {
61 ASSERT_LE(pos + 4, size);
62 ssize_t data_len = Read4(data + pos);
63 ASSERT_GT(data_len, 0);
64 pos += 4 + data_len;
65 ASSERT_LE(pos, size);
66 raw++;
67 } else if (type == CHUNK_DEFLATE) {
68 pos += 60;
69 ASSERT_LE(pos, size);
70 deflate++;
71 } else {
72 FAIL() << "Invalid patch type: " << type;
73 }
74 }
75
76 if (num_normal != nullptr) *num_normal = normal;
77 if (num_raw != nullptr) *num_raw = raw;
78 if (num_deflate != nullptr) *num_deflate = deflate;
79}
80
81TEST(ImgdiffTest, invalid_args) {
82 // Insufficient inputs.
83 ASSERT_EQ(2, imgdiff(1, (const char* []){ "imgdiff" }));
84 ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-z" }));
85 ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-b" }));
86 ASSERT_EQ(2, imgdiff(3, (const char* []){ "imgdiff", "-z", "-b" }));
87
88 // Failed to read bonus file.
89 ASSERT_EQ(1, imgdiff(3, (const char* []){ "imgdiff", "-b", "doesntexist" }));
90
91 // Failed to read input files.
92 ASSERT_EQ(1, imgdiff(4, (const char* []){ "imgdiff", "doesntexist", "doesntexist", "output" }));
93 ASSERT_EQ(
94 1, imgdiff(5, (const char* []){ "imgdiff", "-z", "doesntexist", "doesntexist", "output" }));
95}
96
97TEST(ImgdiffTest, image_mode_smoke) {
98 // Random bytes.
99 const std::string src("abcdefg");
100 TemporaryFile src_file;
101 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
102
103 const std::string tgt("abcdefgxyz");
104 TemporaryFile tgt_file;
105 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
106
107 TemporaryFile patch_file;
108 std::vector<const char*> args = {
109 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
110 };
111 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
112
113 // Verify.
114 std::string patch;
115 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
116
117 // Expect one CHUNK_RAW entry.
118 size_t num_normal;
119 size_t num_raw;
120 size_t num_deflate;
121 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
122 ASSERT_EQ(0U, num_normal);
123 ASSERT_EQ(0U, num_deflate);
124 ASSERT_EQ(1U, num_raw);
125
126 std::string patched;
127 ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
128 reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
129 MemorySink, &patched));
130 ASSERT_EQ(tgt, patched);
131}
132
133TEST(ImgdiffTest, zip_mode_smoke_store) {
134 // Construct src and tgt zip files.
135 TemporaryFile src_file;
136 FILE* src_file_ptr = fdopen(src_file.fd, "wb");
137 ZipWriter src_writer(src_file_ptr);
138 ASSERT_EQ(0, src_writer.StartEntry("file1.txt", 0)); // Store mode.
139 const std::string src_content("abcdefg");
140 ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
141 ASSERT_EQ(0, src_writer.FinishEntry());
142 ASSERT_EQ(0, src_writer.Finish());
143 ASSERT_EQ(0, fclose(src_file_ptr));
144
145 TemporaryFile tgt_file;
146 FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb");
147 ZipWriter tgt_writer(tgt_file_ptr);
148 ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", 0)); // Store mode.
149 const std::string tgt_content("abcdefgxyz");
150 ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
151 ASSERT_EQ(0, tgt_writer.FinishEntry());
152 ASSERT_EQ(0, tgt_writer.Finish());
153 ASSERT_EQ(0, fclose(tgt_file_ptr));
154
155 // Compute patch.
156 TemporaryFile patch_file;
157 std::vector<const char*> args = {
158 "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
159 };
160 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
161
162 // Verify.
163 std::string tgt;
164 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
165 std::string src;
166 ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
167 std::string patch;
168 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
169
170 // Expect one CHUNK_RAW entry.
171 size_t num_normal;
172 size_t num_raw;
173 size_t num_deflate;
174 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
175 ASSERT_EQ(0U, num_normal);
176 ASSERT_EQ(0U, num_deflate);
177 ASSERT_EQ(1U, num_raw);
178
179 std::string patched;
180 ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
181 reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
182 MemorySink, &patched));
183 ASSERT_EQ(tgt, patched);
184}
185
186TEST(ImgdiffTest, zip_mode_smoke_compressed) {
187 // Construct src and tgt zip files.
188 TemporaryFile src_file;
189 FILE* src_file_ptr = fdopen(src_file.fd, "wb");
190 ZipWriter src_writer(src_file_ptr);
191 ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
192 const std::string src_content("abcdefg");
193 ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
194 ASSERT_EQ(0, src_writer.FinishEntry());
195 ASSERT_EQ(0, src_writer.Finish());
196 ASSERT_EQ(0, fclose(src_file_ptr));
197
198 TemporaryFile tgt_file;
199 FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb");
200 ZipWriter tgt_writer(tgt_file_ptr);
201 ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
202 const std::string tgt_content("abcdefgxyz");
203 ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
204 ASSERT_EQ(0, tgt_writer.FinishEntry());
205 ASSERT_EQ(0, tgt_writer.Finish());
206 ASSERT_EQ(0, fclose(tgt_file_ptr));
207
208 // Compute patch.
209 TemporaryFile patch_file;
210 std::vector<const char*> args = {
211 "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
212 };
213 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
214
215 // Verify.
216 std::string tgt;
217 ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
218 std::string src;
219 ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
220 std::string patch;
221 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
222
223 // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
224 size_t num_normal;
225 size_t num_raw;
226 size_t num_deflate;
227 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
228 ASSERT_EQ(0U, num_normal);
229 ASSERT_EQ(1U, num_deflate);
230 ASSERT_EQ(2U, num_raw);
231
232 std::string patched;
233 ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
234 reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
235 MemorySink, &patched));
236 ASSERT_EQ(tgt, patched);
237}
238
239TEST(ImgdiffTest, image_mode_simple) {
240 // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd).
241 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
242 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
243 '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
244 '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
245 '\x00', '\x00', '\x00' };
246 const std::string src(src_data.cbegin(), src_data.cend());
247 TemporaryFile src_file;
248 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
249
250 // tgt: "abcdefgxyz" + gzipped "xxyyzz".
251 const std::vector<char> tgt_data = {
252 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
253 '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
254 '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
255 };
256 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
257 TemporaryFile tgt_file;
258 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
259
260 TemporaryFile patch_file;
261 std::vector<const char*> args = {
262 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
263 };
264 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
265
266 // Verify.
267 std::string patch;
268 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
269
270 // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
271 size_t num_normal;
272 size_t num_raw;
273 size_t num_deflate;
274 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
275 ASSERT_EQ(0U, num_normal);
276 ASSERT_EQ(1U, num_deflate);
277 ASSERT_EQ(2U, num_raw);
278
279 std::string patched;
280 ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
281 reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
282 MemorySink, &patched));
283 ASSERT_EQ(tgt, patched);
284}
285
286TEST(ImgdiffTest, image_mode_different_num_chunks) {
287 // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd) + gzipped "test".
288 const std::vector<char> src_data = {
289 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\x1f', '\x8b', '\x08',
290 '\x00', '\xc4', '\x1e', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac', '\x02',
291 '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03', '\x00', '\x00', '\x00', '\x1f', '\x8b',
292 '\x08', '\x00', '\xb2', '\x3a', '\x53', '\x58', '\x00', '\x03', '\x2b', '\x49', '\x2d',
293 '\x2e', '\x01', '\x00', '\x0c', '\x7e', '\x7f', '\xd8', '\x04', '\x00', '\x00', '\x00'
294 };
295 const std::string src(src_data.cbegin(), src_data.cend());
296 TemporaryFile src_file;
297 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
298
299 // tgt: "abcdefgxyz" + gzipped "xxyyzz".
300 const std::vector<char> tgt_data = {
301 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
302 '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
303 '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
304 };
305 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
306 TemporaryFile tgt_file;
307 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
308
309 TemporaryFile patch_file;
310 std::vector<const char*> args = {
311 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
312 };
313 ASSERT_EQ(1, imgdiff(args.size(), args.data()));
314}
315
316TEST(ImgdiffTest, image_mode_merge_chunks) {
317 // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd).
318 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
319 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
320 '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
321 '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
322 '\x00', '\x00', '\x00' };
323 const std::string src(src_data.cbegin(), src_data.cend());
324 TemporaryFile src_file;
325 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
326
327 // tgt: gzipped "xyz" + "abcdefgh".
328 const std::vector<char> tgt_data = {
329 '\x1f', '\x8b', '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8',
330 '\xa8', '\xac', '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00',
331 '\x00', '\x00', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z'
332 };
333 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
334 TemporaryFile tgt_file;
335 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
336
337 // Since a gzipped entry will become CHUNK_RAW (header) + CHUNK_DEFLATE (data) +
338 // CHUNK_RAW (footer), they both should contain the same chunk types after merging.
339
340 TemporaryFile patch_file;
341 std::vector<const char*> args = {
342 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
343 };
344 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
345
346 // Verify.
347 std::string patch;
348 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
349
350 // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
351 size_t num_normal;
352 size_t num_raw;
353 size_t num_deflate;
354 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
355 ASSERT_EQ(0U, num_normal);
356 ASSERT_EQ(1U, num_deflate);
357 ASSERT_EQ(2U, num_raw);
358
359 std::string patched;
360 ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
361 reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
362 MemorySink, &patched));
363 ASSERT_EQ(tgt, patched);
364}
365
366TEST(ImgdiffTest, image_mode_spurious_magic) {
367 // src: "abcdefgh" + '0x1f8b0b00' + some bytes.
368 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
369 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
370 '\x53', '\x58', 't', 'e', 's', 't' };
371 const std::string src(src_data.cbegin(), src_data.cend());
372 TemporaryFile src_file;
373 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
374
375 // tgt: "abcdefgxyz".
376 const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
377 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
378 TemporaryFile tgt_file;
379 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
380
381 TemporaryFile patch_file;
382 std::vector<const char*> args = {
383 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
384 };
385 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
386
387 // Verify.
388 std::string patch;
389 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
390
391 // Expect one CHUNK_RAW (header) entry.
392 size_t num_normal;
393 size_t num_raw;
394 size_t num_deflate;
395 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
396 ASSERT_EQ(0U, num_normal);
397 ASSERT_EQ(0U, num_deflate);
398 ASSERT_EQ(1U, num_raw);
399
400 std::string patched;
401 ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
402 reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
403 MemorySink, &patched));
404 ASSERT_EQ(tgt, patched);
405}
406
407TEST(ImgdiffTest, image_mode_single_entry_long) {
408 // src: "abcdefgh" + '0x1f8b0b00' + some bytes.
409 const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
410 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
411 '\x53', '\x58', 't', 'e', 's', 't' };
412 const std::string src(src_data.cbegin(), src_data.cend());
413 TemporaryFile src_file;
414 ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
415
416 // tgt: "abcdefgxyz" + 200 bytes.
417 std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
418 tgt_data.resize(tgt_data.size() + 200);
419
420 const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
421 TemporaryFile tgt_file;
422 ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
423
424 TemporaryFile patch_file;
425 std::vector<const char*> args = {
426 "imgdiff", src_file.path, tgt_file.path, patch_file.path,
427 };
428 ASSERT_EQ(0, imgdiff(args.size(), args.data()));
429
430 // Verify.
431 std::string patch;
432 ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
433
434 // Expect one CHUNK_NORMAL entry, since it's exceeding the 160-byte limit for RAW.
435 size_t num_normal;
436 size_t num_raw;
437 size_t num_deflate;
438 verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
439 ASSERT_EQ(1U, num_normal);
440 ASSERT_EQ(0U, num_deflate);
441 ASSERT_EQ(0U, num_raw);
442
443 std::string patched;
444 ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
445 reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
446 MemorySink, &patched));
447 ASSERT_EQ(tgt, patched);
448}