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