Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Chromium OS Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Alex Deymo | dcd423b | 2017-09-13 20:54:24 +0200 | [diff] [blame] | 5 | #include "bsdiff/test_utils.h" |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 6 | |
| 7 | #include <stdio.h> |
| 8 | #include <stdlib.h> |
| 9 | #include <string.h> |
Jed Estep | 9abc204 | 2016-02-19 11:12:14 -0800 | [diff] [blame] | 10 | #include <sys/stat.h> |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 11 | #include <unistd.h> |
| 12 | |
| 13 | #include <gtest/gtest.h> |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 14 | |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 15 | using std::vector; |
| 16 | |
| 17 | namespace { |
| 18 | |
| 19 | // If |path| is absolute, or explicit relative to the current working directory, |
| 20 | // leaves it as is. Otherwise, if TMPDIR is defined in the environment and is |
| 21 | // non-empty, prepends it to |path|. Otherwise, prepends /tmp. Returns the |
| 22 | // resulting path. |
Alex Deymo | c0461f0 | 2017-09-22 15:05:42 +0200 | [diff] [blame] | 23 | const std::string PrependTmpdir(const std::string& path) { |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 24 | if (path[0] == '/') |
| 25 | return path; |
| 26 | |
Alex Deymo | e1526cf | 2015-10-12 17:48:28 -0700 | [diff] [blame] | 27 | const char* tmpdir = getenv("TMPDIR"); |
Alex Deymo | c0461f0 | 2017-09-22 15:05:42 +0200 | [diff] [blame] | 28 | const std::string prefix = (tmpdir && *tmpdir ? tmpdir : "/tmp"); |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 29 | return prefix + "/" + path; |
| 30 | } |
| 31 | |
Alex Deymo | c0461f0 | 2017-09-22 15:05:42 +0200 | [diff] [blame] | 32 | bool MakeTempFile(const std::string& base_filename_template, |
| 33 | std::string* filename) { |
| 34 | const std::string filename_template = PrependTmpdir(base_filename_template); |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 35 | vector<char> result(filename_template.size() + 1, '\0'); |
| 36 | memcpy(result.data(), filename_template.data(), filename_template.size()); |
| 37 | |
| 38 | int mkstemp_fd = mkstemp(result.data()); |
| 39 | if (mkstemp_fd < 0) { |
Amin Hassani | 1106bf7 | 2017-11-15 17:26:03 -0800 | [diff] [blame] | 40 | PLOG(ERROR) << "mkstemp() Failed"; |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 41 | return false; |
| 42 | } |
| 43 | close(mkstemp_fd); |
| 44 | |
| 45 | if (filename) |
| 46 | *filename = result.data(); |
| 47 | return true; |
| 48 | } |
| 49 | |
| 50 | } // namespace |
| 51 | |
| 52 | namespace test_utils { |
| 53 | |
Jed Estep | 9abc204 | 2016-02-19 11:12:14 -0800 | [diff] [blame] | 54 | void BsdiffTestEnvironment::SetUp() { |
| 55 | #ifdef BSDIFF_TARGET_UNITTEST |
| 56 | #define BSDIFF_TARGET_TMP_BASE "/data/tmp" |
| 57 | if (access(BSDIFF_TARGET_TMP_BASE, F_OK) == -1) { |
| 58 | mkdir(BSDIFF_TARGET_TMP_BASE, S_IRWXU | S_IRWXG | S_IROTH | S_IWOTH); |
| 59 | } |
| 60 | setenv("TMPDIR", BSDIFF_TARGET_TMP_BASE, 1); |
Alex Deymo | c0461f0 | 2017-09-22 15:05:42 +0200 | [diff] [blame] | 61 | #endif // defined (BSDIFF_TARGET_UNITTEST) |
Jed Estep | 9abc204 | 2016-02-19 11:12:14 -0800 | [diff] [blame] | 62 | } |
| 63 | |
Alex Deymo | c0461f0 | 2017-09-22 15:05:42 +0200 | [diff] [blame] | 64 | bool ReadFile(const std::string& path, vector<uint8_t>* out) { |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 65 | FILE* fp = fopen(path.c_str(), "r"); |
| 66 | if (!fp) |
| 67 | return false; |
| 68 | out->clear(); |
| 69 | |
| 70 | uint8_t buf[16 * 1024]; |
| 71 | while (true) { |
| 72 | size_t bytes_read = fread(buf, 1, sizeof(buf), fp); |
| 73 | if (!bytes_read) |
| 74 | break; |
| 75 | out->insert(out->end(), buf, buf + bytes_read); |
| 76 | } |
| 77 | bool result = !ferror(fp); |
| 78 | fclose(fp); |
| 79 | return result; |
| 80 | } |
| 81 | |
Alex Deymo | c0461f0 | 2017-09-22 15:05:42 +0200 | [diff] [blame] | 82 | bool WriteFile(const std::string& path, vector<uint8_t> contents) { |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 83 | FILE* fp = fopen(path.c_str(), "r"); |
| 84 | if (!fp) |
| 85 | return false; |
| 86 | size_t written = fwrite(contents.data(), 1, contents.size(), fp); |
| 87 | bool result = written == contents.size() && !ferror(fp); |
| 88 | fclose(fp); |
| 89 | return result; |
| 90 | } |
| 91 | |
Alex Deymo | c0461f0 | 2017-09-22 15:05:42 +0200 | [diff] [blame] | 92 | ScopedTempFile::ScopedTempFile(const std::string& pattern) { |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 93 | EXPECT_TRUE(MakeTempFile(pattern, &filename_)); |
| 94 | } |
| 95 | |
| 96 | ScopedTempFile::~ScopedTempFile() { |
| 97 | if (!filename_.empty() && unlink(filename_.c_str()) < 0) { |
Amin Hassani | 1106bf7 | 2017-11-15 17:26:03 -0800 | [diff] [blame] | 98 | PLOG(ERROR) << "Unable to remove temporary file."; |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 99 | } |
| 100 | } |
| 101 | |
Alex Deymo | c0461f0 | 2017-09-22 15:05:42 +0200 | [diff] [blame] | 102 | bool BsdiffPatchFile::LoadFromFile(const std::string& filename) { |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 103 | vector<uint8_t> contents; |
| 104 | if (!ReadFile(filename, &contents)) |
| 105 | return false; |
| 106 | file_size = contents.size(); |
| 107 | // Check that the file includes at least the header. |
| 108 | TEST_AND_RETURN_FALSE(contents.size() >= kHeaderSize); |
Alex Deymo | c0461f0 | 2017-09-22 15:05:42 +0200 | [diff] [blame] | 109 | magic = std::string(contents.data(), contents.data() + 8); |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 110 | memcpy(&ctrl_len, contents.data() + 8, sizeof(ctrl_len)); |
| 111 | memcpy(&diff_len, contents.data() + 16, sizeof(diff_len)); |
| 112 | memcpy(&new_file_len, contents.data() + 24, sizeof(new_file_len)); |
| 113 | |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 114 | // Sanity check before we attempt to parse the bz2 streams. |
| 115 | TEST_AND_RETURN_FALSE(ctrl_len >= 0); |
| 116 | TEST_AND_RETURN_FALSE(diff_len >= 0); |
| 117 | |
Jed Estep | 9abc204 | 2016-02-19 11:12:14 -0800 | [diff] [blame] | 118 | // The cast is safe since ctrl_len and diff_len are both positive. |
| 119 | TEST_AND_RETURN_FALSE(file_size >= |
| 120 | static_cast<uint64_t>(kHeaderSize + ctrl_len + diff_len)); |
| 121 | extra_len = file_size - kHeaderSize - ctrl_len - diff_len; |
| 122 | |
Alex Deymo | a5cff22 | 2015-04-08 14:10:30 -0700 | [diff] [blame] | 123 | uint8_t* ptr = contents.data() + kHeaderSize; |
| 124 | bz2_ctrl = vector<uint8_t>(ptr, ptr + ctrl_len); |
| 125 | ptr += ctrl_len; |
| 126 | bz2_diff = vector<uint8_t>(ptr, ptr + diff_len); |
| 127 | ptr += diff_len; |
| 128 | bz2_extra = vector<uint8_t>(ptr, ptr + extra_len); |
| 129 | |
| 130 | return true; |
| 131 | } |
| 132 | |
| 133 | bool BsdiffPatchFile::IsValid() const { |
| 134 | TEST_AND_RETURN_FALSE(ctrl_len >= 0); |
| 135 | TEST_AND_RETURN_FALSE(diff_len >= 0); |
| 136 | TEST_AND_RETURN_FALSE(new_file_len >= 0); |
| 137 | |
| 138 | // TODO(deymo): Test that the length of the decompressed bz2 streams |diff| |
| 139 | // plus |extra| are equal to the |new_file_len|. |
| 140 | // TODO(deymo): Test that all the |bz2_ctrl| triplets (x, y, z) have a "x" |
| 141 | // and "y" value >= 0 ("z" can be negative). |
| 142 | return true; |
| 143 | } |
| 144 | |
| 145 | } // namespace test_utils |