// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "scoped_temp_path.h"

#include <string.h>  // for strlen.
#include <sys/stat.h>

#include <vector>

#include "base/logging.h"
#include "compat/string.h"
#include "compat/test.h"

namespace {

// For testing the creation of multiple temp paths.
const int kNumMultiplePaths = 32;

// When testing non-empty directories, populate them with this many files.
const int kNumFilesPerNonEmptyDirectory = 10;

// The length of the path template suffix used internally by ScopedTempPath and
// derived classes.
const size_t kTemplateSuffixSize = strlen("XXXXXX");

// Temporary paths use this prefix by default. Copied from scoped_temp_path.cc.
const char kTempPathTemplatePrefix[] = "/tmp/quipper.";

// Tests if |path| exists on the file system.
bool PathExists(const string& path) {
  struct stat buf;
  // stat() returns 0 on success, i.e. if the path exists and is valid.
  return !stat(path.c_str(), &buf);
}

// Creates some files in a directory. Returns the number of files created.
int PopulateDirectoryWithFiles(const string& dir, int num_files) {
  // The last six characters of the file template must be "XXXXXX".
  const char kPathTemplateSuffix[] = "/testXXXXXX";

  // The string providing the path template for creating temp files must not be
  // constant, so allocate some space here.
  size_t buf_size = dir.size() + strlen(kPathTemplateSuffix) + 1;
  char* path_template = new char[buf_size];

  int num_files_created = 0;
  for (int i = 0; i < num_files; ++i) {
    // Construct the mutable path template.
    snprintf(path_template, buf_size, "%s%s", dir.c_str(), kPathTemplateSuffix);
    // Create the file and make sure it is valid.
    int fd = mkstemp(path_template);
    if (fd == -1) {
      LOG(ERROR) << "Could not create file, errno=" << errno;
      continue;
    }
    ++num_files_created;
    close(fd);
  }
  delete[] path_template;

  return num_files_created;
}

}  // namespace

namespace quipper {

// Create one file and make sure it is deleted when out of scope.
TEST(ScopedTempPathTest, OneFile) {
  string path;
  {
    ScopedTempFile temp_file;
    path = temp_file.path();
    EXPECT_TRUE(PathExists(path)) << path;
    EXPECT_EQ(strlen(kTempPathTemplatePrefix) + kTemplateSuffixSize,
              path.size());
    EXPECT_EQ(kTempPathTemplatePrefix,
              path.substr(0, strlen(kTempPathTemplatePrefix)));
  }
  EXPECT_FALSE(PathExists(path)) << path;
}

// Create a file with a custom template filename.
TEST(ScopedTempPathTest, CustomFileTemplate) {
  string path;
  {
    const string prefix = "/tmp/foobar.";
    ScopedTempFile temp_file(prefix);
    path = temp_file.path();
    EXPECT_TRUE(PathExists(path)) << path;
    EXPECT_EQ(prefix.size() + kTemplateSuffixSize, path.size());
    EXPECT_EQ(prefix, path.substr(0, prefix.size()));
  }
  EXPECT_FALSE(PathExists(path)) << path;
}

// Create many files and make sure they are deleted when out of scope.
TEST(ScopedTempPathTest, MultipleFiles) {
  std::vector<string> paths(kNumMultiplePaths);
  {
    std::vector<ScopedTempFile> temp_files(kNumMultiplePaths);
    for (size_t i = 0; i < kNumMultiplePaths; ++i) {
      paths[i] = temp_files[i].path();
      EXPECT_TRUE(PathExists(paths[i])) << paths[i];
    }
  }
  for (size_t i = 0; i < kNumMultiplePaths; ++i) {
    EXPECT_FALSE(PathExists(paths[i])) << paths[i];
  }
}

// Create one empty directory and make sure it is deleted when out of scope.
TEST(ScopedTempPathTest, OneEmptyDir) {
  string path;
  {
    ScopedTempDir temp_path;
    path = temp_path.path();
    EXPECT_TRUE(PathExists(path)) << path;
    EXPECT_EQ('/', path.back()) << "Should append a slash";
    EXPECT_EQ(strlen(kTempPathTemplatePrefix) + kTemplateSuffixSize + 1,
              path.size());
    EXPECT_EQ(kTempPathTemplatePrefix,
              path.substr(0, strlen(kTempPathTemplatePrefix)));
  }
  EXPECT_FALSE(PathExists(path)) << path;
}

// Create a file with a custom template dirname.
TEST(ScopedTempPathTest, CustomDirTemplate) {
  string path;
  {
    const string prefix = "/tmp/foobar.";
    ScopedTempDir temp_path(prefix);
    path = temp_path.path();
    EXPECT_TRUE(PathExists(path)) << path;
    EXPECT_EQ('/', path.back()) << "Should append a slash";
    // Check prefix matches:
    EXPECT_EQ(prefix.size() + kTemplateSuffixSize + 1, path.size());
    EXPECT_EQ(prefix, path.substr(0, prefix.size()));
  }
  EXPECT_FALSE(PathExists(path)) << path;
}

// Create many empty directories and make sure they are deleted when out of
// scope.
TEST(ScopedTempPathTest, MultipleEmptyDirs) {
  std::vector<string> paths(kNumMultiplePaths);
  {
    std::vector<ScopedTempDir> temp_dirs(kNumMultiplePaths);
    for (size_t i = 0; i < kNumMultiplePaths; ++i) {
      paths[i] = temp_dirs[i].path();
      EXPECT_TRUE(PathExists(paths[i])) << paths[i];
    }
  }
  for (size_t i = 0; i < kNumMultiplePaths; ++i) {
    EXPECT_FALSE(PathExists(paths[i])) << paths[i];
  }
}

// Create a directory with some files in it, and make sure it is deleted when
// out of scope.
TEST(ScopedTempPathTest, OneNonEmptyDir) {
  string path;
  {
    ScopedTempDir temp_path;
    path = temp_path.path();
    EXPECT_TRUE(PathExists(path)) << path;
    // Populate the directory with files.
    EXPECT_EQ(kNumFilesPerNonEmptyDirectory,
              PopulateDirectoryWithFiles(path, kNumFilesPerNonEmptyDirectory));
  }
  EXPECT_FALSE(PathExists(path)) << path;
}

// Create many empty directories with files in them, and make sure they are
// deleted when out of scope.
TEST(ScopedTempPathTest, MultipleNonEmptyDirs) {
  std::vector<string> paths(kNumMultiplePaths);
  {
    std::vector<ScopedTempDir> temp_dirs(kNumMultiplePaths);
    for (size_t i = 0; i < kNumMultiplePaths; ++i) {
      paths[i] = temp_dirs[i].path();
      EXPECT_TRUE(PathExists(paths[i])) << paths[i];
      // Populate the directory with files.
      EXPECT_EQ(
          kNumFilesPerNonEmptyDirectory,
          PopulateDirectoryWithFiles(paths[i], kNumFilesPerNonEmptyDirectory));
    }
  }
  for (size_t i = 0; i < kNumMultiplePaths; ++i) {
    EXPECT_FALSE(PathExists(paths[i])) << paths[i];
  }
}

}  // namespace quipper