chromium/chrome/utility/safe_browsing/mac/read_stream_unittest.cc

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/utility/safe_browsing/mac/read_stream.h"

#include <stddef.h>
#include <stdint.h>

#include <array>
#include <memory>
#include <vector>

#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/path_service.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/utility/safe_browsing/mac/dmg_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace safe_browsing {
namespace dmg {

using ::testing::ElementsAre;

struct MemoryReadStreamTest {
  void SetUp() {}

  const char* TestName() {
    return "MemoryReadStream";
  }

  std::vector<uint8_t> data;
};

struct FileReadStreamTest {
  void SetUp() {
    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  }

  const char* TestName() {
    return "FileReadStream";
  }

  base::ScopedTempDir temp_dir;
  base::File file;
};

template <typename T>
class ReadStreamTest : public testing::Test {
 protected:
  void SetUp() override {
    test_helper_.SetUp();
  }

  void TearDown() override {
    if (HasFailure())
      ADD_FAILURE() << "Failing type is " << test_helper_.TestName();
  }

  std::unique_ptr<ReadStream> CreateStream(size_t data_size);

 private:
  T test_helper_;
};

template <>
std::unique_ptr<ReadStream> ReadStreamTest<MemoryReadStreamTest>::CreateStream(
    size_t data_size) {
  test_helper_.data.resize(data_size);
  for (size_t i = 0; i < data_size; ++i) {
    test_helper_.data[i] = i % 255;
  }
  return std::make_unique<MemoryReadStream>(test_helper_.data);
}

template <>
std::unique_ptr<ReadStream> ReadStreamTest<FileReadStreamTest>::CreateStream(
    size_t data_size) {
  base::FilePath path = test_helper_.temp_dir.GetPath().Append("stream");
  test_helper_.file.Initialize(path,
      base::File::FLAG_CREATE | base::File::FLAG_WRITE);
  if (!test_helper_.file.IsValid()) {
    ADD_FAILURE() << "Failed to create temp file";
    return nullptr;
  }

  for (size_t i = 0; i < data_size; ++i) {
    char value = i % 255;
    EXPECT_EQ(1, UNSAFE_TODO(test_helper_.file.WriteAtCurrentPos(&value, 1)));
  }

  test_helper_.file.Close();

  test_helper_.file.Initialize(path,
      base::File::FLAG_OPEN | base::File::FLAG_READ);
  if (!test_helper_.file.IsValid()) {
    ADD_FAILURE() << "Failed to open temp file";
    return nullptr;
  }

  return base::WrapUnique(
      new FileReadStream(test_helper_.file.GetPlatformFile()));
}

using ReadStreamImpls = testing::Types<MemoryReadStreamTest,
                                       FileReadStreamTest>;
TYPED_TEST_SUITE(ReadStreamTest, ReadStreamImpls);

TYPED_TEST(ReadStreamTest, Read) {
  std::unique_ptr<ReadStream> stream =
      ReadStreamTest<TypeParam>::CreateStream(128);
  uint8_t buf[128] = {0};
  size_t bytes_read;

  {
    EXPECT_TRUE(stream->Read(base::span(buf).first(4u), &bytes_read));
    EXPECT_EQ(4u, bytes_read);
    uint8_t expected[] = { 0, 1, 2, 3, 0, 0, 0 };
    EXPECT_EQ(0, memcmp(expected, buf, sizeof(expected)));
  }

  {
    EXPECT_TRUE(stream->Read(base::span(buf).first(9u), &bytes_read));
    EXPECT_EQ(9u, bytes_read);
    uint8_t expected[] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0 };
    EXPECT_EQ(0, memcmp(expected, buf, sizeof(expected)));
  }
}

TYPED_TEST(ReadStreamTest, CopyStreamToFileTest) {
  constexpr size_t kStreamSize = 4242;
  std::unique_ptr<ReadStream> stream =
      ReadStreamTest<TypeParam>::CreateStream(kStreamSize);
  base::FilePath temp_path;
  base::File temp_file;
  base::CreateTemporaryFile(&temp_path);
  temp_file.Initialize(
      temp_path, (base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ |
                  base::File::FLAG_WRITE | base::File::FLAG_WIN_TEMPORARY |
                  base::File::FLAG_DELETE_ON_CLOSE));
  EXPECT_TRUE(CopyStreamToFile(*stream, temp_file));
  EXPECT_EQ(kStreamSize, static_cast<size_t>(temp_file.GetLength()));
  std::array<uint8_t, kStreamSize> file_buf;
  std::array<uint8_t, 15> expected = {0, 1, 2,  3,  4,  5,  6, 7,
                                      8, 9, 10, 11, 12, 13, 14};
  temp_file.ReadAndCheck(0, file_buf);
  // Range is set to 1020 - 1035 to check the copying loop point of
  // the CopyStreamToFile function which is 1024.
  for (int i = 1020; i < 1035; i++) {
    EXPECT_EQ(expected[i - 1020], file_buf[i]);
  }
}

TYPED_TEST(ReadStreamTest, ReadAll) {
  const size_t kStreamSize = 4242;
  std::unique_ptr<ReadStream> stream =
      ReadStreamTest<TypeParam>::CreateStream(kStreamSize);

  auto maybe_data = ReadEntireStream(*stream);
  ASSERT_TRUE(maybe_data.has_value());
  EXPECT_EQ(kStreamSize, maybe_data->size());
}

TYPED_TEST(ReadStreamTest, SeekSet) {
  std::unique_ptr<ReadStream> stream =
      ReadStreamTest<TypeParam>::CreateStream(255);
  uint8_t buf[32] = {0};
  size_t bytes_read;

  {
    EXPECT_EQ(250, stream->Seek(250, SEEK_SET));
    EXPECT_TRUE(stream->Read(buf, &bytes_read));
    EXPECT_EQ(5u, bytes_read);
    uint8_t expected[] = { 250, 251, 252, 253, 254, 0, 0 };
    EXPECT_EQ(0, memcmp(expected, buf, sizeof(expected)));
  }

  {
    EXPECT_EQ(5, stream->Seek(5, SEEK_SET));
    EXPECT_TRUE(stream->Read(base::span(buf).first(3u), &bytes_read));
    EXPECT_EQ(3u, bytes_read);
    uint8_t expected[] = { 5, 6, 7, 253, 254, 0, 0 };
    EXPECT_EQ(0, memcmp(expected, buf, sizeof(expected)));
  }
}

TYPED_TEST(ReadStreamTest, SeekEnd) {
  std::unique_ptr<ReadStream> stream =
      ReadStreamTest<TypeParam>::CreateStream(32);
  uint8_t buf[32] = {0};
  size_t bytes_read;

  {
    EXPECT_EQ(32, stream->Seek(0, SEEK_END));
    EXPECT_TRUE(stream->Read(buf, &bytes_read));
    EXPECT_EQ(0u, bytes_read);
  }

  {
    EXPECT_EQ(28, stream->Seek(-4, SEEK_END));
    EXPECT_TRUE(stream->Read(buf, &bytes_read));
    EXPECT_EQ(4u, bytes_read);
    uint8_t expected[] = { 28, 29, 30, 31, 0, 0, 0 };
    EXPECT_EQ(0, memcmp(expected, buf, sizeof(expected)));
  }
}

TYPED_TEST(ReadStreamTest, SeekCur) {
  std::unique_ptr<ReadStream> stream =
      ReadStreamTest<TypeParam>::CreateStream(100);
  std::array<uint8_t, 32> buf;
  size_t bytes_read;

  {
    EXPECT_EQ(0, stream->Seek(0, SEEK_CUR));
  }

  {
    EXPECT_TRUE(stream->Read(buf, &bytes_read));
    EXPECT_EQ(buf.size(), bytes_read);
    for (size_t i = 0; i < buf.size(); ++i) {
      EXPECT_EQ(i, buf[i]);
    }
    EXPECT_EQ(32, stream->Seek(0, SEEK_CUR));
  }

  {
    EXPECT_EQ(30, stream->Seek(-2, SEEK_CUR));
    EXPECT_TRUE(stream->Read(base::span(buf).first(3u), &bytes_read));
    EXPECT_EQ(3u, bytes_read);
    EXPECT_THAT(base::span(buf).first(3u), ElementsAre(30, 31, 32));
  }

  {
    EXPECT_EQ(100, stream->Seek(0, SEEK_END));
    EXPECT_EQ(100, stream->Seek(0, SEEK_CUR));
  }
}

}  // namespace dmg
}  // namespace safe_browsing