| // Copyright 2023 The BoringSSL Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #ifndef OPENSSL_HEADER_CRYPTO_TEST_FILE_UTIL_H | |
| #define OPENSSL_HEADER_CRYPTO_TEST_FILE_UTIL_H | |
| #include <stdio.h> | |
| #include <memory> | |
| #include <set> | |
| #include <string> | |
| #include <string_view> | |
| #include <utility> | |
| #include <openssl/span.h> | |
| #if defined(OPENSSL_WINDOWS) | |
| #include <io.h> | |
| #else | |
| #include <unistd.h> | |
| #endif | |
| BSSL_NAMESPACE_BEGIN | |
| struct FileDeleter { | |
| void operator()(FILE *f) const { | |
| if (f != nullptr) { | |
| fclose(f); | |
| } | |
| } | |
| }; | |
| using ScopedFILE = std::unique_ptr<FILE, FileDeleter>; | |
| class ScopedFD { | |
| public: | |
| ScopedFD() = default; | |
| explicit ScopedFD(int fd) : fd_(fd) {} | |
| ~ScopedFD() { reset(); } | |
| ScopedFD(ScopedFD &&other) { *this = std::move(other); } | |
| ScopedFD &operator=(ScopedFD other) { | |
| reset(other.release()); | |
| return *this; | |
| } | |
| bool is_valid() const { return fd_ >= 0; } | |
| int get() const { return fd_; } | |
| int release() { return std::exchange(fd_, -1); } | |
| void reset(int fd = -1) { | |
| if (is_valid()) { | |
| #if defined(OPENSSL_WINDOWS) | |
| _close(fd_); | |
| #else | |
| close(fd_); | |
| #endif | |
| } | |
| fd_ = fd; | |
| } | |
| private: | |
| int fd_ = -1; | |
| }; | |
| // SkipTempFileTests returns true and prints a warning if tests involving | |
| // temporary files should be skipped because of platform issues. | |
| bool SkipTempFileTests(); | |
| // TemporaryFile manages a temporary file for testing. | |
| class TemporaryFile { | |
| public: | |
| TemporaryFile() = default; | |
| ~TemporaryFile(); | |
| TemporaryFile(TemporaryFile &other) { *this = std::move(other); } | |
| TemporaryFile& operator=(TemporaryFile&&other) { | |
| // Ensure `path_` is empty so it doesn't try to delete the File. | |
| path_ = std::exchange(other.path_, {}); | |
| return *this; | |
| } | |
| // Init initializes the temporary file with the specified content. It returns | |
| // true on success and false on error. On error, callers should call | |
| // `IgnoreTempFileErrors` to determine whether to ignore the error. | |
| bool Init(bssl::Span<const uint8_t> content = {}); | |
| bool Init(std::string_view content) { | |
| return Init(bssl::StringAsBytes(content)); | |
| } | |
| // Open opens the file as a `FILE` with the specified mode. | |
| ScopedFILE Open(const char *mode) const; | |
| // Open opens the file as a file descriptor with the specified flags. | |
| ScopedFD OpenFD(int flags) const; | |
| // path returns the path to the temporary file. | |
| const std::string &path() const { return path_; } | |
| private: | |
| std::string path_; | |
| }; | |
| // TemporaryDirectory manages a temporary directory for testing. | |
| class TemporaryDirectory { | |
| public: | |
| TemporaryDirectory() = default; | |
| ~TemporaryDirectory(); | |
| TemporaryDirectory(TemporaryDirectory &other) { *this = std::move(other); } | |
| TemporaryDirectory& operator=(TemporaryDirectory&&other) { | |
| // Ensure `other_` is empty so it doesn't try to delete the directory. | |
| path_ = std::exchange(other.path_, {}); | |
| files_ = std::exchange(other.files_, {}); | |
| return *this; | |
| } | |
| // Init initializes the temporary directory. It returns true on success and | |
| // false on error. On error, callers should call `IgnoreTempFileErrors` to | |
| // determine whether to ignore the error. | |
| bool Init(); | |
| // path returns the path to the temporary directory. | |
| const std::string &path() const { return path_; } | |
| // AddFile adds a file to the temporary directory with the specified content. | |
| // It returns true on success and false on error. Subdirectories in the | |
| // temporary directory are not currently supported. | |
| bool AddFile(const std::string &filename, bssl::Span<const uint8_t> content); | |
| bool AddFile(const std::string &filename, std::string_view content) { | |
| return AddFile(filename, bssl::StringAsBytes(content)); | |
| } | |
| // GetFilePath returns the path to the specified file within the temporary | |
| // directory. | |
| std::string GetFilePath(const std::string &filename) { | |
| #if defined(OPENSSL_WINDOWS) | |
| return path_ + '\\' + filename; | |
| #else | |
| return path_ + '/' + filename; | |
| #endif | |
| } | |
| private: | |
| std::string path_; | |
| std::set<std::string> files_; | |
| }; | |
| BSSL_NAMESPACE_END | |
| #endif // OPENSSL_HEADER_CRYPTO_TEST_FILE_UTIL_H |