CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
343 lines
9.7 KiB
C++
343 lines
9.7 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Support/VirtualOutputFile.h"
|
|
#include "llvm/Testing/Support/Error.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::vfs;
|
|
|
|
namespace {
|
|
|
|
struct MockOutputFileData {
|
|
int Kept = 0;
|
|
int Discarded = 0;
|
|
int Handled = 0;
|
|
unique_function<Error()> Keeper;
|
|
unique_function<Error()> Discarder;
|
|
|
|
void handler(Error E) {
|
|
consumeError(std::move(E));
|
|
++Handled;
|
|
}
|
|
unique_function<void(Error)> getHandler() {
|
|
return [this](Error E) { handler(std::move(E)); };
|
|
}
|
|
|
|
SmallString<128> V;
|
|
std::optional<raw_svector_ostream> VOS;
|
|
raw_pwrite_stream *OS = nullptr;
|
|
|
|
MockOutputFileData() : VOS(std::in_place, V), OS(&*VOS) {}
|
|
MockOutputFileData(raw_pwrite_stream &OS) : OS(&OS) {}
|
|
};
|
|
|
|
struct MockOutputFile final : public OutputFileImpl {
|
|
Error keep() override {
|
|
++Data.Kept;
|
|
if (Data.Keeper)
|
|
return Data.Keeper();
|
|
return Error::success();
|
|
}
|
|
|
|
Error discard() override {
|
|
++Data.Discarded;
|
|
if (Data.Discarder)
|
|
return Data.Discarder();
|
|
return Error::success();
|
|
}
|
|
|
|
raw_pwrite_stream &getOS() override {
|
|
if (!Data.OS)
|
|
report_fatal_error("missing stream in MockOutputFile::getOS");
|
|
return *Data.OS;
|
|
}
|
|
|
|
MockOutputFile(MockOutputFileData &Data) : Data(Data) {}
|
|
MockOutputFileData &Data;
|
|
};
|
|
|
|
static std::unique_ptr<MockOutputFile>
|
|
createMockOutput(MockOutputFileData &Data) {
|
|
return std::make_unique<MockOutputFile>(Data);
|
|
}
|
|
|
|
static Error createCustomError() {
|
|
return createStringError(inconvertibleErrorCode(), "custom error");
|
|
}
|
|
|
|
TEST(VirtualOutputFileTest, construct) {
|
|
OutputFile F;
|
|
EXPECT_EQ("", F.getPath());
|
|
EXPECT_FALSE(F);
|
|
EXPECT_FALSE(F.isOpen());
|
|
|
|
#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
|
|
EXPECT_DEATH(F.getOS(), "Expected open output stream");
|
|
#endif
|
|
}
|
|
|
|
#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
|
|
TEST(VirtualOutputFileTest, constructNull) {
|
|
EXPECT_DEATH(OutputFile("some/file/path", nullptr),
|
|
"Expected open output file");
|
|
}
|
|
#endif
|
|
|
|
TEST(VirtualOutputFileTest, destroy) {
|
|
MockOutputFileData Data;
|
|
StringRef FilePath = "some/file/path";
|
|
|
|
// Check behaviour when destroying, first without a handler and then with
|
|
// one. The handler shouldn't be called.
|
|
std::optional<OutputFile> F(std::in_place, FilePath, createMockOutput(Data));
|
|
EXPECT_TRUE(F->isOpen());
|
|
EXPECT_EQ(FilePath, F->getPath());
|
|
EXPECT_EQ(Data.OS, &F->getOS());
|
|
#if GTEST_HAS_DEATH_TEST
|
|
EXPECT_DEATH(F.reset(), "output not closed");
|
|
#endif
|
|
F->discardOnDestroy(Data.getHandler());
|
|
EXPECT_EQ(0, Data.Discarded);
|
|
EXPECT_EQ(0, Data.Handled);
|
|
F.reset();
|
|
EXPECT_EQ(1, Data.Discarded);
|
|
EXPECT_EQ(0, Data.Handled);
|
|
|
|
// Try again when discard returns an error. This time the handler should be
|
|
// called.
|
|
Data.Discarder = createCustomError;
|
|
F.emplace("some/file/path", createMockOutput(Data));
|
|
F->discardOnDestroy(Data.getHandler());
|
|
F.reset();
|
|
EXPECT_EQ(2, Data.Discarded);
|
|
EXPECT_EQ(1, Data.Handled);
|
|
}
|
|
|
|
TEST(VirtualOutputFileTest, destroyProxy) {
|
|
MockOutputFileData Data;
|
|
|
|
std::optional<OutputFile> F(std::in_place, "some/file/path",
|
|
createMockOutput(Data));
|
|
F->discardOnDestroy(Data.getHandler());
|
|
std::unique_ptr<raw_pwrite_stream> Proxy;
|
|
EXPECT_THAT_ERROR(F->createProxy().moveInto(Proxy), Succeeded());
|
|
F.reset();
|
|
#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
|
|
EXPECT_DEATH(*Proxy << "data", "use after reset");
|
|
#endif
|
|
Proxy.reset();
|
|
}
|
|
|
|
TEST(VirtualOutputFileTest, discard) {
|
|
StringRef Content = "some data";
|
|
MockOutputFileData Data;
|
|
{
|
|
OutputFile F("some/file/path", createMockOutput(Data));
|
|
F.discardOnDestroy(Data.getHandler());
|
|
F << Content;
|
|
EXPECT_EQ(Content, Data.V);
|
|
|
|
EXPECT_THAT_ERROR(F.discard(), Succeeded());
|
|
EXPECT_FALSE(F.isOpen());
|
|
EXPECT_EQ(0, Data.Kept);
|
|
EXPECT_EQ(1, Data.Discarded);
|
|
|
|
#if GTEST_HAS_DEATH_TEST
|
|
EXPECT_DEATH(consumeError(F.keep()),
|
|
"some/file/path: output already closed");
|
|
EXPECT_DEATH(consumeError(F.discard()),
|
|
"some/file/path: output already closed");
|
|
#endif
|
|
}
|
|
EXPECT_EQ(0, Data.Kept);
|
|
EXPECT_EQ(1, Data.Discarded);
|
|
}
|
|
|
|
TEST(VirtualOutputFileTest, discardError) {
|
|
StringRef Content = "some data";
|
|
MockOutputFileData Data;
|
|
Data.Discarder = createCustomError;
|
|
{
|
|
OutputFile F("some/file/path", createMockOutput(Data));
|
|
F.discardOnDestroy(Data.getHandler());
|
|
F << Content;
|
|
EXPECT_EQ(Content, Data.V);
|
|
EXPECT_THAT_ERROR(F.discard(), FailedWithMessage("custom error"));
|
|
EXPECT_FALSE(F.isOpen());
|
|
EXPECT_EQ(0, Data.Kept);
|
|
EXPECT_EQ(1, Data.Discarded);
|
|
EXPECT_EQ(0, Data.Handled);
|
|
}
|
|
EXPECT_EQ(0, Data.Kept);
|
|
EXPECT_EQ(1, Data.Discarded);
|
|
EXPECT_EQ(0, Data.Handled);
|
|
}
|
|
|
|
TEST(VirtualOutputFileTest, discardProxy) {
|
|
StringRef Content = "some data";
|
|
MockOutputFileData Data;
|
|
OutputFile F("some/file/path", createMockOutput(Data));
|
|
F.discardOnDestroy(Data.getHandler());
|
|
|
|
std::unique_ptr<raw_pwrite_stream> Proxy;
|
|
EXPECT_THAT_ERROR(F.createProxy().moveInto(Proxy), Succeeded());
|
|
*Proxy << Content;
|
|
EXPECT_EQ(Content, Data.V);
|
|
|
|
EXPECT_THAT_ERROR(F.discard(), Succeeded());
|
|
EXPECT_FALSE(F.isOpen());
|
|
EXPECT_EQ(0, Data.Kept);
|
|
EXPECT_EQ(1, Data.Discarded);
|
|
}
|
|
|
|
TEST(VirtualOutputFileTest, discardProxyFlush) {
|
|
StringRef Content = "some data";
|
|
MockOutputFileData Data;
|
|
OutputFile F("some/file/path", createMockOutput(Data));
|
|
F.discardOnDestroy(Data.getHandler());
|
|
F.getOS().SetBufferSize(Content.size() * 2);
|
|
|
|
std::unique_ptr<raw_pwrite_stream> Proxy;
|
|
EXPECT_THAT_ERROR(F.createProxy().moveInto(Proxy), Succeeded());
|
|
*Proxy << Content;
|
|
EXPECT_EQ("", Data.V);
|
|
EXPECT_THAT_ERROR(F.discard(), Succeeded());
|
|
EXPECT_EQ(Content, Data.V);
|
|
EXPECT_FALSE(F.isOpen());
|
|
EXPECT_EQ(0, Data.Kept);
|
|
EXPECT_EQ(1, Data.Discarded);
|
|
}
|
|
|
|
TEST(VirtualOutputFileTest, keep) {
|
|
StringRef Content = "some data";
|
|
MockOutputFileData Data;
|
|
{
|
|
OutputFile F("some/file/path", createMockOutput(Data));
|
|
F.discardOnDestroy(Data.getHandler());
|
|
F << Content;
|
|
EXPECT_EQ(Content, Data.V);
|
|
|
|
EXPECT_THAT_ERROR(F.keep(), Succeeded());
|
|
EXPECT_FALSE(F.isOpen());
|
|
EXPECT_EQ(1, Data.Kept);
|
|
EXPECT_EQ(0, Data.Discarded);
|
|
|
|
#if GTEST_HAS_DEATH_TEST
|
|
EXPECT_DEATH(consumeError(F.keep()),
|
|
"some/file/path: output already closed");
|
|
EXPECT_DEATH(consumeError(F.discard()),
|
|
"some/file/path: output already closed");
|
|
#endif
|
|
}
|
|
EXPECT_EQ(1, Data.Kept);
|
|
EXPECT_EQ(0, Data.Discarded);
|
|
}
|
|
|
|
TEST(VirtualOutputFileTest, keepError) {
|
|
StringRef Content = "some data";
|
|
MockOutputFileData Data;
|
|
Data.Keeper = createCustomError;
|
|
{
|
|
OutputFile F("some/file/path", createMockOutput(Data));
|
|
F.discardOnDestroy(Data.getHandler());
|
|
F << Content;
|
|
EXPECT_EQ(Content, Data.V);
|
|
|
|
EXPECT_THAT_ERROR(F.keep(), FailedWithMessage("custom error"));
|
|
EXPECT_FALSE(F.isOpen());
|
|
EXPECT_EQ(1, Data.Kept);
|
|
EXPECT_EQ(0, Data.Discarded);
|
|
EXPECT_EQ(0, Data.Handled);
|
|
}
|
|
EXPECT_EQ(1, Data.Kept);
|
|
EXPECT_EQ(0, Data.Discarded);
|
|
EXPECT_EQ(0, Data.Handled);
|
|
}
|
|
|
|
TEST(VirtualOutputFileTest, keepProxy) {
|
|
StringRef Content = "some data";
|
|
MockOutputFileData Data;
|
|
OutputFile F("some/file/path", createMockOutput(Data));
|
|
F.discardOnDestroy(Data.getHandler());
|
|
|
|
std::unique_ptr<raw_pwrite_stream> Proxy;
|
|
EXPECT_THAT_ERROR(F.createProxy().moveInto(Proxy), Succeeded());
|
|
*Proxy << Content;
|
|
EXPECT_EQ(Content, Data.V);
|
|
Proxy.reset();
|
|
EXPECT_THAT_ERROR(F.keep(), Succeeded());
|
|
EXPECT_FALSE(F.isOpen());
|
|
EXPECT_EQ(1, Data.Kept);
|
|
EXPECT_EQ(0, Data.Discarded);
|
|
}
|
|
|
|
#if GTEST_HAS_DEATH_TEST
|
|
TEST(VirtualOutputFileTest, keepProxyStillOpen) {
|
|
StringRef Content = "some data";
|
|
MockOutputFileData Data;
|
|
OutputFile F("some/file/path", createMockOutput(Data));
|
|
F.discardOnDestroy(Data.getHandler());
|
|
|
|
std::unique_ptr<raw_pwrite_stream> Proxy;
|
|
EXPECT_THAT_ERROR(F.createProxy().moveInto(Proxy), Succeeded());
|
|
*Proxy << Content;
|
|
EXPECT_EQ(Content, Data.V);
|
|
EXPECT_DEATH(consumeError(F.keep()), "some/file/path: output has open proxy");
|
|
}
|
|
#endif
|
|
|
|
TEST(VirtualOutputFileTest, keepProxyFlush) {
|
|
StringRef Content = "some data";
|
|
MockOutputFileData Data;
|
|
OutputFile F("some/file/path", createMockOutput(Data));
|
|
F.discardOnDestroy(Data.getHandler());
|
|
F.getOS().SetBufferSize(Content.size() * 2);
|
|
|
|
std::unique_ptr<raw_pwrite_stream> Proxy;
|
|
EXPECT_THAT_ERROR(F.createProxy().moveInto(Proxy), Succeeded());
|
|
*Proxy << Content;
|
|
EXPECT_EQ("", Data.V);
|
|
Proxy.reset();
|
|
EXPECT_THAT_ERROR(F.keep(), Succeeded());
|
|
EXPECT_EQ(Content, Data.V);
|
|
EXPECT_FALSE(F.isOpen());
|
|
EXPECT_EQ(1, Data.Kept);
|
|
EXPECT_EQ(0, Data.Discarded);
|
|
}
|
|
|
|
TEST(VirtualOutputFileTest, TwoProxies) {
|
|
StringRef Content = "some data";
|
|
MockOutputFileData Data;
|
|
|
|
OutputFile F("some/file/path", createMockOutput(Data));
|
|
F.discardOnDestroy(Data.getHandler());
|
|
|
|
// Can't have two open proxies at once.
|
|
{
|
|
std::unique_ptr<raw_pwrite_stream> Proxy;
|
|
EXPECT_THAT_ERROR(F.createProxy().moveInto(Proxy), Succeeded());
|
|
EXPECT_THAT_ERROR(
|
|
F.createProxy().takeError(),
|
|
FailedWithMessage("some/file/path: output has open proxy"));
|
|
}
|
|
EXPECT_EQ(0, Data.Kept);
|
|
EXPECT_EQ(0, Data.Discarded);
|
|
|
|
// A second proxy after the first closes should work...
|
|
{
|
|
std::unique_ptr<raw_pwrite_stream> Proxy;
|
|
EXPECT_THAT_ERROR(F.createProxy().moveInto(Proxy), Succeeded());
|
|
*Proxy << Content;
|
|
EXPECT_EQ(Content, Data.V);
|
|
}
|
|
}
|
|
|
|
} // end namespace
|