brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.7 KiB · 5d4c329 Raw
343 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/Support/VirtualOutputFile.h"10#include "llvm/Testing/Support/Error.h"11#include "gtest/gtest.h"12 13using namespace llvm;14using namespace llvm::vfs;15 16namespace {17 18struct MockOutputFileData {19  int Kept = 0;20  int Discarded = 0;21  int Handled = 0;22  unique_function<Error()> Keeper;23  unique_function<Error()> Discarder;24 25  void handler(Error E) {26    consumeError(std::move(E));27    ++Handled;28  }29  unique_function<void(Error)> getHandler() {30    return [this](Error E) { handler(std::move(E)); };31  }32 33  SmallString<128> V;34  std::optional<raw_svector_ostream> VOS;35  raw_pwrite_stream *OS = nullptr;36 37  MockOutputFileData() : VOS(std::in_place, V), OS(&*VOS) {}38  MockOutputFileData(raw_pwrite_stream &OS) : OS(&OS) {}39};40 41struct MockOutputFile final : public OutputFileImpl {42  Error keep() override {43    ++Data.Kept;44    if (Data.Keeper)45      return Data.Keeper();46    return Error::success();47  }48 49  Error discard() override {50    ++Data.Discarded;51    if (Data.Discarder)52      return Data.Discarder();53    return Error::success();54  }55 56  raw_pwrite_stream &getOS() override {57    if (!Data.OS)58      report_fatal_error("missing stream in MockOutputFile::getOS");59    return *Data.OS;60  }61 62  MockOutputFile(MockOutputFileData &Data) : Data(Data) {}63  MockOutputFileData &Data;64};65 66static std::unique_ptr<MockOutputFile>67createMockOutput(MockOutputFileData &Data) {68  return std::make_unique<MockOutputFile>(Data);69}70 71static Error createCustomError() {72  return createStringError(inconvertibleErrorCode(), "custom error");73}74 75TEST(VirtualOutputFileTest, construct) {76  OutputFile F;77  EXPECT_EQ("", F.getPath());78  EXPECT_FALSE(F);79  EXPECT_FALSE(F.isOpen());80 81#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)82  EXPECT_DEATH(F.getOS(), "Expected open output stream");83#endif84}85 86#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)87TEST(VirtualOutputFileTest, constructNull) {88  EXPECT_DEATH(OutputFile("some/file/path", nullptr),89               "Expected open output file");90}91#endif92 93TEST(VirtualOutputFileTest, destroy) {94  MockOutputFileData Data;95  StringRef FilePath = "some/file/path";96 97  // Check behaviour when destroying, first without a handler and then with98  // one. The handler shouldn't be called.99  std::optional<OutputFile> F(std::in_place, FilePath, createMockOutput(Data));100  EXPECT_TRUE(F->isOpen());101  EXPECT_EQ(FilePath, F->getPath());102  EXPECT_EQ(Data.OS, &F->getOS());103#if GTEST_HAS_DEATH_TEST104  EXPECT_DEATH(F.reset(), "output not closed");105#endif106  F->discardOnDestroy(Data.getHandler());107  EXPECT_EQ(0, Data.Discarded);108  EXPECT_EQ(0, Data.Handled);109  F.reset();110  EXPECT_EQ(1, Data.Discarded);111  EXPECT_EQ(0, Data.Handled);112 113  // Try again when discard returns an error. This time the handler should be114  // called.115  Data.Discarder = createCustomError;116  F.emplace("some/file/path", createMockOutput(Data));117  F->discardOnDestroy(Data.getHandler());118  F.reset();119  EXPECT_EQ(2, Data.Discarded);120  EXPECT_EQ(1, Data.Handled);121}122 123TEST(VirtualOutputFileTest, destroyProxy) {124  MockOutputFileData Data;125 126  std::optional<OutputFile> F(std::in_place, "some/file/path",127                              createMockOutput(Data));128  F->discardOnDestroy(Data.getHandler());129  std::unique_ptr<raw_pwrite_stream> Proxy;130  EXPECT_THAT_ERROR(F->createProxy().moveInto(Proxy), Succeeded());131  F.reset();132#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)133  EXPECT_DEATH(*Proxy << "data", "use after reset");134#endif135  Proxy.reset();136}137 138TEST(VirtualOutputFileTest, discard) {139  StringRef Content = "some data";140  MockOutputFileData Data;141  {142    OutputFile F("some/file/path", createMockOutput(Data));143    F.discardOnDestroy(Data.getHandler());144    F << Content;145    EXPECT_EQ(Content, Data.V);146 147    EXPECT_THAT_ERROR(F.discard(), Succeeded());148    EXPECT_FALSE(F.isOpen());149    EXPECT_EQ(0, Data.Kept);150    EXPECT_EQ(1, Data.Discarded);151 152#if GTEST_HAS_DEATH_TEST153    EXPECT_DEATH(consumeError(F.keep()),154                 "some/file/path: output already closed");155    EXPECT_DEATH(consumeError(F.discard()),156                 "some/file/path: output already closed");157#endif158  }159  EXPECT_EQ(0, Data.Kept);160  EXPECT_EQ(1, Data.Discarded);161}162 163TEST(VirtualOutputFileTest, discardError) {164  StringRef Content = "some data";165  MockOutputFileData Data;166  Data.Discarder = createCustomError;167  {168    OutputFile F("some/file/path", createMockOutput(Data));169    F.discardOnDestroy(Data.getHandler());170    F << Content;171    EXPECT_EQ(Content, Data.V);172    EXPECT_THAT_ERROR(F.discard(), FailedWithMessage("custom error"));173    EXPECT_FALSE(F.isOpen());174    EXPECT_EQ(0, Data.Kept);175    EXPECT_EQ(1, Data.Discarded);176    EXPECT_EQ(0, Data.Handled);177  }178  EXPECT_EQ(0, Data.Kept);179  EXPECT_EQ(1, Data.Discarded);180  EXPECT_EQ(0, Data.Handled);181}182 183TEST(VirtualOutputFileTest, discardProxy) {184  StringRef Content = "some data";185  MockOutputFileData Data;186  OutputFile F("some/file/path", createMockOutput(Data));187  F.discardOnDestroy(Data.getHandler());188 189  std::unique_ptr<raw_pwrite_stream> Proxy;190  EXPECT_THAT_ERROR(F.createProxy().moveInto(Proxy), Succeeded());191  *Proxy << Content;192  EXPECT_EQ(Content, Data.V);193 194  EXPECT_THAT_ERROR(F.discard(), Succeeded());195  EXPECT_FALSE(F.isOpen());196  EXPECT_EQ(0, Data.Kept);197  EXPECT_EQ(1, Data.Discarded);198}199 200TEST(VirtualOutputFileTest, discardProxyFlush) {201  StringRef Content = "some data";202  MockOutputFileData Data;203  OutputFile F("some/file/path", createMockOutput(Data));204  F.discardOnDestroy(Data.getHandler());205  F.getOS().SetBufferSize(Content.size() * 2);206 207  std::unique_ptr<raw_pwrite_stream> Proxy;208  EXPECT_THAT_ERROR(F.createProxy().moveInto(Proxy), Succeeded());209  *Proxy << Content;210  EXPECT_EQ("", Data.V);211  EXPECT_THAT_ERROR(F.discard(), Succeeded());212  EXPECT_EQ(Content, Data.V);213  EXPECT_FALSE(F.isOpen());214  EXPECT_EQ(0, Data.Kept);215  EXPECT_EQ(1, Data.Discarded);216}217 218TEST(VirtualOutputFileTest, keep) {219  StringRef Content = "some data";220  MockOutputFileData Data;221  {222    OutputFile F("some/file/path", createMockOutput(Data));223    F.discardOnDestroy(Data.getHandler());224    F << Content;225    EXPECT_EQ(Content, Data.V);226 227    EXPECT_THAT_ERROR(F.keep(), Succeeded());228    EXPECT_FALSE(F.isOpen());229    EXPECT_EQ(1, Data.Kept);230    EXPECT_EQ(0, Data.Discarded);231 232#if GTEST_HAS_DEATH_TEST233    EXPECT_DEATH(consumeError(F.keep()),234                 "some/file/path: output already closed");235    EXPECT_DEATH(consumeError(F.discard()),236                 "some/file/path: output already closed");237#endif238  }239  EXPECT_EQ(1, Data.Kept);240  EXPECT_EQ(0, Data.Discarded);241}242 243TEST(VirtualOutputFileTest, keepError) {244  StringRef Content = "some data";245  MockOutputFileData Data;246  Data.Keeper = createCustomError;247  {248    OutputFile F("some/file/path", createMockOutput(Data));249    F.discardOnDestroy(Data.getHandler());250    F << Content;251    EXPECT_EQ(Content, Data.V);252 253    EXPECT_THAT_ERROR(F.keep(), FailedWithMessage("custom error"));254    EXPECT_FALSE(F.isOpen());255    EXPECT_EQ(1, Data.Kept);256    EXPECT_EQ(0, Data.Discarded);257    EXPECT_EQ(0, Data.Handled);258  }259  EXPECT_EQ(1, Data.Kept);260  EXPECT_EQ(0, Data.Discarded);261  EXPECT_EQ(0, Data.Handled);262}263 264TEST(VirtualOutputFileTest, keepProxy) {265  StringRef Content = "some data";266  MockOutputFileData Data;267  OutputFile F("some/file/path", createMockOutput(Data));268  F.discardOnDestroy(Data.getHandler());269 270  std::unique_ptr<raw_pwrite_stream> Proxy;271  EXPECT_THAT_ERROR(F.createProxy().moveInto(Proxy), Succeeded());272  *Proxy << Content;273  EXPECT_EQ(Content, Data.V);274  Proxy.reset();275  EXPECT_THAT_ERROR(F.keep(), Succeeded());276  EXPECT_FALSE(F.isOpen());277  EXPECT_EQ(1, Data.Kept);278  EXPECT_EQ(0, Data.Discarded);279}280 281#if GTEST_HAS_DEATH_TEST282TEST(VirtualOutputFileTest, keepProxyStillOpen) {283  StringRef Content = "some data";284  MockOutputFileData Data;285  OutputFile F("some/file/path", createMockOutput(Data));286  F.discardOnDestroy(Data.getHandler());287 288  std::unique_ptr<raw_pwrite_stream> Proxy;289  EXPECT_THAT_ERROR(F.createProxy().moveInto(Proxy), Succeeded());290  *Proxy << Content;291  EXPECT_EQ(Content, Data.V);292  EXPECT_DEATH(consumeError(F.keep()), "some/file/path: output has open proxy");293}294#endif295 296TEST(VirtualOutputFileTest, keepProxyFlush) {297  StringRef Content = "some data";298  MockOutputFileData Data;299  OutputFile F("some/file/path", createMockOutput(Data));300  F.discardOnDestroy(Data.getHandler());301  F.getOS().SetBufferSize(Content.size() * 2);302 303  std::unique_ptr<raw_pwrite_stream> Proxy;304  EXPECT_THAT_ERROR(F.createProxy().moveInto(Proxy), Succeeded());305  *Proxy << Content;306  EXPECT_EQ("", Data.V);307  Proxy.reset();308  EXPECT_THAT_ERROR(F.keep(), Succeeded());309  EXPECT_EQ(Content, Data.V);310  EXPECT_FALSE(F.isOpen());311  EXPECT_EQ(1, Data.Kept);312  EXPECT_EQ(0, Data.Discarded);313}314 315TEST(VirtualOutputFileTest, TwoProxies) {316  StringRef Content = "some data";317  MockOutputFileData Data;318 319  OutputFile F("some/file/path", createMockOutput(Data));320  F.discardOnDestroy(Data.getHandler());321 322  // Can't have two open proxies at once.323  {324    std::unique_ptr<raw_pwrite_stream> Proxy;325    EXPECT_THAT_ERROR(F.createProxy().moveInto(Proxy), Succeeded());326    EXPECT_THAT_ERROR(327        F.createProxy().takeError(),328        FailedWithMessage("some/file/path: output has open proxy"));329  }330  EXPECT_EQ(0, Data.Kept);331  EXPECT_EQ(0, Data.Discarded);332 333  // A second proxy after the first closes should work...334  {335    std::unique_ptr<raw_pwrite_stream> Proxy;336    EXPECT_THAT_ERROR(F.createProxy().moveInto(Proxy), Succeeded());337    *Proxy << Content;338    EXPECT_EQ(Content, Data.V);339  }340}341 342} // end namespace343