text: // Copyright 2018 The Fuchsia Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef ZIRCON_SYSTEM_UTEST_RUNTESTS_UTILS_RUNTESTS_UTILS_TEST_UTILS_H_
  #define ZIRCON_SYSTEM_UTEST_RUNTESTS_UTILS_RUNTESTS_UTILS_TEST_UTILS_H_
  
  #include <dirent.h>
  #include <errno.h>
  #include <fcntl.h>
  #include <limits.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <sys/stat.h>
  #include <unistd.h>
  
  #include <fbl/auto_call.h>
  #include <fbl/string.h>
  #include <fbl/string_buffer.h>
  #include <fbl/unique_fd.h>
  #include <fbl/vector.h>
  #include <runtests-utils/runtests-utils.h>
  #include <zxtest/zxtest.h>
  
  namespace runtests {
  // Root directory of memfs installed for duration of test.
  static constexpr char kMemFsRoot[] = "/test-memfs";
  
  static constexpr char kExpectedJSONOutputPrefix[] = "{\n  \"tests\": [\n";
  // We don't want to count the null terminator.
  static constexpr size_t kExpectedJSONOutputPrefixSize = sizeof(kExpectedJSONOutputPrefix) - 1;
  
  fbl::String packaged_script_dir();
  
  // Represents a script generated at build time, selected from the appropriate
  // target directory.
  class PackagedScriptFile {
   public:
    PackagedScriptFile(const fbl::StringPiece path);
    ~PackagedScriptFile();
    fbl::StringPiece path() const;
  
   private:
    fbl::String path_;
  };
  
  // Creates an empty file and deletes it in its destructor.
  class ScopedStubFile {
   public:
    ScopedStubFile(const fbl::StringPiece path);
    ~ScopedStubFile();
  
   private:
    const fbl::StringPiece path_;
  };
  
  // Creates a script file with given contents in its constructor and deletes it
  // in its destructor.
  class ScopedTestFile {
   public:
    // |path| is the path of the file to be created. Should start with kMemFsPath.
    // |contents| are the script contents. Shebang line will be added automatically.
    ScopedTestFile(const fbl::StringPiece path, const fbl::StringPiece file);
    ~ScopedTestFile();
    fbl::StringPiece path() const;
  
   private:
    const fbl::StringPiece path_;
  };
  
  // Creates a subdirectory of kMemFsRoot in its constructor and deletes it in
  // its destructor.
  class ScopedTestDir {
   public:
    ScopedTestDir() : basename_(NextBasename()), path_(JoinPath(kMemFsRoot, basename_)) {
      if (mkdir(path_.c_str(), 0755)) {
        printf("FAILURE: mkdir failed to open %s: %s\n", path_.c_str(), strerror(errno));
        exit(1);
      }
    }
    ~ScopedTestDir() { CleanUpDir(path_.c_str()); }
    const char* basename() { return basename_.c_str(); }
    const char* path() { return path_.c_str(); }
  
   private:
    fbl::String NextBasename() {
      // More than big enough to print INT_MAX.
      char buf[64];
      sprintf(buf, "%d", num_test_dirs_created_++);
      return fbl::String(buf);
    }
  
    // Recursively removes the directory at |dir_path| and its contents.
    static void CleanUpDir(const char* dir_path) {
      struct dirent* entry;
      DIR* dp;
  
      dp = opendir(dir_path);
      if (dp == nullptr) {
        // File found; remove it.
        remove(dir_path);
        return;
      }
  
      while ((entry = readdir(dp))) {
        // Skip "." and "..".
        if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
          continue;
        }
        fbl::String sub_dir_name = JoinPath(dir_path, entry->d_name);
        CleanUpDir(sub_dir_name.c_str());
      }
      closedir(dp);
  
      // Directory is now empty: remove it.
      rmdir(dir_path);
    }
  
    const fbl::String basename_;
    const fbl::String path_;
  
    // Used to generate unique subdirectories of kMemFsRoot.
    static int num_test_dirs_created_;
  };
  
  class TestStopwatch : public Stopwatch {
   public:
    void Start() override { start_called_ = true; }
    int64_t DurationInMsecs() override {
      EXPECT_TRUE(start_called_);
      return 14u;
    }
  
   private:
    bool start_called_ = false;
  };
  
  // Returns the number of files or subdirectories in a given directory.
  int NumEntriesInDir(const char* dir_path);
  
  // Computes the relative path within |output_dir| of the output file of the
  // test at |test_path|, setting |output_file_rel_path| as its value if
  // successful.
  // Returns true iff successful.
  bool GetOutputFileRelPath(const fbl::StringPiece& output_dir, const fbl::StringPiece& test_path,
                            fbl::String* output_file_rel_path);
  
  }  // namespace runtests
  
  #endif  // ZIRCON_SYSTEM_UTEST_RUNTESTS_UTILS_RUNTESTS_UTILS_TEST_UTILS_H_
  