Skip to content

Commit bb01c06

Browse files
committed
[rfile] Throw exception in the factory methods when file cannot be opened
1 parent 0bc9944 commit bb01c06

File tree

3 files changed

+54
-5
lines changed

3 files changed

+54
-5
lines changed

io/io/inc/ROOT/RFile.hxx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,18 @@ public:
131131

132132
///// Factory methods /////
133133

134-
/// Opens the file for reading
134+
/// Opens the file for reading. `path` may be a regular file path or a remote URL.
135+
/// \throw ROOT::RException if the file at `path` could not be opened.
135136
static std::unique_ptr<RFile> Open(std::string_view path);
136137

137-
/// Opens the file for reading/writing, overwriting it if it already exists
138+
/// Opens the file for reading/writing, overwriting it if it already exists.
139+
/// \throw ROOT::RException if a file could not be created at `path` (e.g. if the specified
140+
/// directory tree does not exist).
138141
static std::unique_ptr<RFile> Recreate(std::string_view path);
139142

140-
/// Opens the file for updating
143+
/// Opens the file for updating, creating a new one if it doesn't exist.
144+
/// \throw ROOT::RException if the file at `path` could neither be read nor created
145+
/// (e.g. if the specified directory tree does not exist).
141146
static std::unique_ptr<RFile> Update(std::string_view path);
142147

143148
///// Instance methods /////
@@ -185,7 +190,7 @@ public:
185190
PutInternal(path, obj, flags);
186191
}
187192

188-
/// Writes all objects to disk with the file structure.
193+
/// Writes all objects and the file structure to disk.
189194
/// Returns the number of bytes written.
190195
size_t Flush();
191196

io/io/src/RFile.cxx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,9 @@ std::unique_ptr<RFile> RFile::Open(std::string_view path)
190190

191191
TDirectory::TContext ctx(nullptr); // XXX: probably not thread safe?
192192
auto tfile = std::unique_ptr<TFile>(TFile::Open(std::string(path).c_str(), "READ_WITHOUT_GLOBALREGISTRATION"));
193+
if (!tfile || tfile->IsZombie())
194+
throw ROOT::RException(R__FAIL("failed to open file " + std::string(path) + " for reading"));
195+
193196
auto rfile = std::unique_ptr<RFile>(new RFile(std::move(tfile)));
194197
return rfile;
195198
}
@@ -200,6 +203,9 @@ std::unique_ptr<RFile> RFile::Update(std::string_view path)
200203

201204
TDirectory::TContext ctx(nullptr); // XXX: probably not thread safe?
202205
auto tfile = std::unique_ptr<TFile>(TFile::Open(std::string(path).c_str(), "UPDATE_WITHOUT_GLOBALREGISTRATION"));
206+
if (!tfile || tfile->IsZombie())
207+
throw ROOT::RException(R__FAIL("failed to open file " + std::string(path) + " for updating"));
208+
203209
auto rfile = std::unique_ptr<RFile>(new RFile(std::move(tfile)));
204210
return rfile;
205211
}
@@ -210,6 +216,9 @@ std::unique_ptr<RFile> RFile::Recreate(std::string_view path)
210216

211217
TDirectory::TContext ctx(nullptr); // XXX: probably not thread safe?
212218
auto tfile = std::unique_ptr<TFile>(TFile::Open(std::string(path).c_str(), "RECREATE_WITHOUT_GLOBALREGISTRATION"));
219+
if (!tfile || tfile->IsZombie())
220+
throw ROOT::RException(R__FAIL("failed to open file " + std::string(path) + " for writing"));
221+
213222
auto rfile = std::unique_ptr<RFile>(new RFile(std::move(tfile)));
214223
return rfile;
215224
}
@@ -281,7 +290,7 @@ void *RFile::GetUntyped(std::string_view pathSV, const std::type_info &type) con
281290
if (auto autoAddFunc = cls->GetDirectoryAutoAdd(); autoAddFunc) {
282291
autoAddFunc(obj, nullptr);
283292
}
284-
} else if (key && !GetROOT()->IsBatch()) { // XXX: do we really want this warning?
293+
} else if (key && !GetROOT()->IsBatch()) {
285294
R__LOG_WARNING(RFileLog()) << "Tried to get object '" << path << "' of type " << cls->GetName()
286295
<< " but that path contains an object of type " << key->GetClassName();
287296
}

io/io/test/rfile.cxx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <TFile.h>
55
#include <TH1D.h>
66
#include <TROOT.h>
7+
#include <TSystem.h>
78
#include <ROOT/RError.hxx>
89
#include <ROOT/RFile.hxx>
910
#include <ROOT/TestSupport.hxx>
@@ -69,6 +70,40 @@ TEST(RFile, Open)
6970
EXPECT_THROW(file->Put("foo", foo), ROOT::RException);
7071
}
7172

73+
TEST(RFile, OpenInexistent)
74+
{
75+
FileRaii fileGuard("does_not_exist.root");
76+
77+
// make sure that the file really does not exist, in case a previous test didn't clean it up.
78+
gSystem->Unlink(fileGuard.GetPath().c_str());
79+
80+
ROOT::TestSupport::CheckDiagsRAII diags;
81+
diags.optionalDiag(kSysError, "TFile::TFile", "", false);
82+
diags.optionalDiag(kError, "TFile::TFile", "", false);
83+
84+
try {
85+
auto f = RFile::Open("does_not_exist.root");
86+
FAIL() << "trying to open an inexistent file should throw";
87+
} catch (const ROOT::RException &e) {
88+
EXPECT_THAT(e.what(), testing::HasSubstr("failed to open file"));
89+
}
90+
try {
91+
auto f = RFile::Update("/a/random/directory/that/definitely/does_not_exist.root");
92+
FAIL() << "trying to update a file under an inexistent directory should throw";
93+
} catch (const ROOT::RException &e) {
94+
EXPECT_THAT(e.what(), testing::HasSubstr("failed to open file"));
95+
}
96+
try {
97+
auto f = RFile::Recreate("/a/random/directory/that/definitely/does_not_exist.root");
98+
FAIL() << "trying to create a file under an inexistent directory should throw";
99+
} catch (const ROOT::RException &e) {
100+
EXPECT_THAT(e.what(), testing::HasSubstr("failed to open file"));
101+
}
102+
103+
// This succeeds because Update creates the file if it doesn't exist.
104+
EXPECT_NO_THROW(RFile::Update("does_not_exist.root"));
105+
}
106+
72107
TEST(RFile, OpenForWriting)
73108
{
74109
FileRaii fileGuard("test_rfile_write.root");

0 commit comments

Comments
 (0)