Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
VideoLAN
medialibrary
Commits
c7cf425c
Commit
c7cf425c
authored
Dec 23, 2015
by
Hugo Beauzée-Luyssen
Browse files
tests: Refactor filesystem mocks
Ease up the modifications of devices & their content.
parent
f7d30374
Changes
9
Hide whitespace changes
Inline
Side-by-side
src/discoverer/FsDiscoverer.cpp
View file @
c7cf425c
...
...
@@ -66,6 +66,11 @@ bool FsDiscoverer::discover( const std::string &entryPoint )
LOG_ERROR
(
"Failed to create an IDirectory for "
,
entryPoint
,
": "
,
ex
.
what
());
return
false
;
}
if
(
fsDir
==
nullptr
)
{
LOG_ERROR
(
"Failed to create an IDirectory for "
,
entryPoint
);
return
false
;
}
auto
blist
=
blacklist
();
if
(
isBlacklisted
(
fsDir
->
path
(),
blist
)
==
true
)
return
false
;
...
...
@@ -85,7 +90,10 @@ void FsDiscoverer::reload()
{
auto
folder
=
m_fsFactory
->
createDirectory
(
f
->
path
()
);
if
(
folder
->
lastModificationDate
()
==
f
->
lastModificationDate
()
)
{
LOG_INFO
(
f
->
path
(),
" isn't modified"
);
continue
;
}
checkSubfolders
(
folder
.
get
(),
f
.
get
(),
blist
);
checkFiles
(
folder
.
get
(),
f
.
get
()
);
f
->
setLastModificationDate
(
folder
->
lastModificationDate
()
);
...
...
src/utils/Filename.cpp
View file @
c7cf425c
...
...
@@ -52,6 +52,30 @@ std::string fileName(const std::string& filePath)
return
filePath
.
substr
(
pos
+
1
);
}
std
::
string
firstFolder
(
const
std
::
string
&
path
)
{
size_t
offset
=
0
;
while
(
path
[
offset
]
==
'/'
)
offset
++
;
auto
pos
=
path
.
find_first_of
(
'/'
,
offset
);
if
(
pos
==
std
::
string
::
npos
)
return
{};
return
path
.
substr
(
offset
,
pos
-
offset
);
}
std
::
string
removeFirstFolder
(
const
std
::
string
&
path
)
{
auto
f
=
firstFolder
(
path
);
if
(
f
.
length
()
==
0
)
return
path
;
auto
pos
=
path
.
find
(
f
)
+
f
.
length
();
while
(
path
[
pos
]
==
'/'
)
pos
++
;
if
(
pos
>=
path
.
length
()
)
return
{};
return
path
.
substr
(
pos
);
}
}
}
src/utils/Filename.h
View file @
c7cf425c
...
...
@@ -32,6 +32,8 @@ namespace file
std
::
string
extension
(
const
std
::
string
&
fileName
);
std
::
string
directory
(
const
std
::
string
&
filePath
);
std
::
string
fileName
(
const
std
::
string
&
filePath
);
std
::
string
firstFolder
(
const
std
::
string
&
path
);
std
::
string
removeFirstFolder
(
const
std
::
string
&
path
);
}
}
test/mocks/FileSystem.cpp
0 → 100644
View file @
c7cf425c
/*****************************************************************************
* Media Library
*****************************************************************************
* Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
*
* Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include
"FileSystem.h"
namespace
mock
{
const
std
::
string
FileSystemFactory
::
Root
=
"/a"
;
const
std
::
string
FileSystemFactory
::
SubFolder
=
"/a/folder"
;
const
std
::
string
FileSystemFactory
::
RootDeviceUuid
=
"{fake-root-device}"
;
}
test/mocks/FileSystem.h
View file @
c7cf425c
...
...
@@ -25,6 +25,9 @@
#include
<string>
#include
<unordered_map>
#include
<memory>
#include
<algorithm>
#include
<cassert>
#include
"filesystem/IDirectory.h"
#include
"filesystem/IFile.h"
...
...
@@ -32,6 +35,9 @@
#include
"factory/IFileSystem.h"
#include
"utils/Filename.h"
//REMOVE ME
#include
"logging/Logger.h"
namespace
mock
{
...
...
@@ -86,30 +92,12 @@ public:
unsigned
int
m_lastModification
;
};
class
Device
:
public
fs
::
IDevice
{
public:
Device
(
const
std
::
string
&
mountpoint
,
const
std
::
string
&
uuid
)
:
m_uuid
(
uuid
),
m_removable
(
false
),
m_present
(
true
),
m_mountpoint
(
mountpoint
)
{}
virtual
const
std
::
string
&
uuid
()
const
override
{
return
m_uuid
;
}
virtual
bool
isRemovable
()
const
override
{
return
m_removable
;
}
virtual
bool
isPresent
()
const
override
{
return
m_present
;
}
virtual
const
std
::
string
&
mountpoint
()
const
override
{
return
m_mountpoint
;
}
void
setRemovable
(
bool
value
)
{
m_removable
=
value
;
}
void
setPresent
(
bool
value
)
{
m_present
=
value
;
}
private:
std
::
string
m_uuid
;
bool
m_removable
;
bool
m_present
;
std
::
string
m_mountpoint
;
};
class
Device
;
class
Directory
:
public
fs
::
IDirectory
{
public:
Directory
(
std
::
shared_ptr
<
mock
::
Directory
>
parent
,
const
std
::
string
&
path
,
unsigned
int
lastModif
,
std
::
shared_ptr
<
fs
::
I
Device
>
device
)
Directory
(
Directory
*
parent
,
const
std
::
string
&
path
,
unsigned
int
lastModif
,
std
::
shared_ptr
<
Device
>
device
)
:
m_path
(
path
)
,
m_parent
(
parent
)
,
m_lastModificationDate
(
lastModif
)
...
...
@@ -125,16 +113,22 @@ public:
virtual
const
std
::
vector
<
std
::
string
>&
files
()
override
{
if
(
m_device
==
nullptr
||
m_device
->
isPresent
()
==
false
)
return
m_emptyfiles
;
return
m_files
;
if
(
m_filePathes
.
size
()
==
0
)
{
for
(
const
auto
&
f
:
m_files
)
m_filePathes
.
push_back
(
m_path
+
'/'
+
f
.
first
);
}
return
m_filePathes
;
}
virtual
const
std
::
vector
<
std
::
string
>&
dirs
()
override
{
if
(
m_device
==
nullptr
||
m_device
->
isPresent
()
==
false
)
return
m_emptydirs
;
return
m_dirs
;
if
(
m_dirPathes
.
size
()
==
0
)
{
for
(
const
auto
&
d
:
m_dirs
)
m_dirPathes
.
push_back
(
m_path
+
"/"
+
d
.
first
);
}
return
m_dirPathes
;
}
virtual
unsigned
int
lastModificationDate
()
const
override
...
...
@@ -144,49 +138,138 @@ public:
virtual
std
::
shared_ptr
<
fs
::
IDevice
>
device
()
const
override
{
return
m_device
;
return
std
::
static_pointer_cast
<
fs
::
IDevice
>
(
m_device
)
;
}
void
setDevice
(
std
::
shared_ptr
<
fs
::
IDevice
>
device
)
void
addFile
(
const
std
::
string
&
filePath
)
{
m_device
=
device
;
auto
subFolder
=
utils
::
file
::
firstFolder
(
filePath
);
if
(
subFolder
.
empty
()
==
true
)
{
m_files
[
utils
::
file
::
fileName
(
filePath
)]
=
std
::
make_shared
<
File
>
(
m_path
+
"/"
+
filePath
);
m_filePathes
.
clear
();
markAsModified
();
}
else
{
auto
it
=
m_dirs
.
find
(
subFolder
);
assert
(
it
!=
end
(
m_dirs
)
);
auto
remainingPath
=
utils
::
file
::
removeFirstFolder
(
filePath
);
it
->
second
->
addFile
(
remainingPath
);
}
}
void
addF
ile
(
const
std
::
string
&
f
ileName
)
void
addF
older
(
const
std
::
string
&
f
older
,
unsigned
int
lastModif
)
{
m_files
.
emplace_back
(
m_path
+
fileName
);
markAsModified
();
auto
subFolder
=
utils
::
file
::
firstFolder
(
folder
);
if
(
subFolder
.
empty
()
==
true
)
{
auto
folderName
=
utils
::
file
::
fileName
(
folder
);
auto
dir
=
std
::
make_shared
<
Directory
>
(
this
,
m_path
+
"/"
+
folderName
,
lastModif
,
m_device
);
m_dirs
[
folderName
]
=
dir
;
m_dirPathes
.
clear
();
markAsModified
();
}
else
{
auto
it
=
m_dirs
.
find
(
subFolder
);
assert
(
it
!=
end
(
m_dirs
)
);
auto
remainingPath
=
utils
::
file
::
removeFirstFolder
(
folder
);
it
->
second
->
addFolder
(
remainingPath
,
lastModif
);
}
}
void
addFolder
(
const
std
::
string
&
f
older
)
void
removeFile
(
const
std
::
string
&
f
ilePath
)
{
m_dirs
.
emplace_back
(
m_path
+
folder
);
markAsModified
();
auto
subFolder
=
utils
::
file
::
firstFolder
(
filePath
);
if
(
subFolder
.
empty
()
==
true
)
{
auto
it
=
m_files
.
find
(
filePath
);
assert
(
it
!=
end
(
m_files
)
);
m_files
.
erase
(
it
);
m_filePathes
.
clear
();
markAsModified
();
}
else
{
auto
it
=
m_dirs
.
find
(
subFolder
);
assert
(
it
!=
end
(
m_dirs
)
);
auto
remainingPath
=
utils
::
file
::
removeFirstFolder
(
filePath
);
it
->
second
->
removeFile
(
remainingPath
);
}
}
void
removeF
ile
(
const
std
::
string
&
file
Name
)
std
::
shared_ptr
<
File
>
f
ile
(
const
std
::
string
&
file
Path
)
{
auto
it
=
std
::
find
(
begin
(
m_files
),
end
(
m_files
),
fileName
);
if
(
it
==
end
(
m_files
)
)
throw
std
::
runtime_error
(
"Invalid filename"
);
m_files
.
erase
(
it
);
markAsModified
();
auto
subFolder
=
utils
::
file
::
firstFolder
(
filePath
);
if
(
subFolder
.
empty
()
==
true
)
{
auto
it
=
m_files
.
find
(
filePath
);
assert
(
it
!=
end
(
m_files
)
);
return
it
->
second
;
}
else
{
auto
it
=
m_dirs
.
find
(
subFolder
);
assert
(
it
!=
end
(
m_dirs
)
);
auto
remainingPath
=
utils
::
file
::
removeFirstFolder
(
filePath
);
return
it
->
second
->
file
(
remainingPath
);
}
}
void
remove
(
)
std
::
shared_ptr
<
Directory
>
directory
(
const
std
::
string
&
path
)
{
if
(
m_parent
==
nullptr
)
return
;
m_parent
->
removeFolder
(
m_path
);
auto
subFolder
=
utils
::
file
::
firstFolder
(
path
);
if
(
subFolder
.
empty
()
==
true
)
{
auto
it
=
m_dirs
.
find
(
path
);
assert
(
it
!=
end
(
m_dirs
)
);
return
it
->
second
;
}
else
{
auto
it
=
m_dirs
.
find
(
subFolder
);
assert
(
it
!=
end
(
m_dirs
)
);
auto
remainingPath
=
utils
::
file
::
removeFirstFolder
(
path
);
return
it
->
second
->
directory
(
remainingPath
);
}
}
void
removeFolder
(
const
std
::
string
&
path
)
{
auto
it
=
std
::
find
(
begin
(
m_dirs
),
end
(
m_dirs
),
path
);
if
(
it
==
end
(
m_dirs
)
)
throw
std
::
runtime_error
(
"Invalid subfolder to remove"
);
m_dirs
.
erase
(
it
);
markAsModified
();
auto
subFolder
=
utils
::
file
::
firstFolder
(
path
);
if
(
subFolder
.
empty
()
==
true
)
{
auto
it
=
m_dirs
.
find
(
path
);
assert
(
it
!=
end
(
m_dirs
)
);
m_dirs
.
erase
(
it
);
m_dirPathes
.
clear
();
markAsModified
();
}
else
{
auto
it
=
m_dirs
.
find
(
subFolder
);
assert
(
it
!=
end
(
m_dirs
)
);
auto
remainingPath
=
utils
::
file
::
removeFirstFolder
(
path
);
it
->
second
->
removeFolder
(
remainingPath
);
}
}
void
addMountpoint
(
const
std
::
string
&
path
)
{
auto
subFolder
=
utils
::
file
::
firstFolder
(
path
);
if
(
subFolder
.
empty
()
==
true
)
{
m_dirs
[
path
]
=
nullptr
;
}
else
{
auto
it
=
m_dirs
.
find
(
subFolder
);
assert
(
it
!=
end
(
m_dirs
)
);
auto
remainingPath
=
utils
::
file
::
removeFirstFolder
(
path
);
it
->
second
->
addMountpoint
(
remainingPath
);
}
}
void
markAsModified
()
...
...
@@ -206,120 +289,237 @@ public:
m_isRemovable
=
true
;
}
private:
std
::
string
m_path
;
std
::
vector
<
std
::
string
>
m_files
;
std
::
vector
<
std
::
string
>
m_dirs
;
std
::
vector
<
std
::
string
>
m_
emptyfil
es
;
std
::
vector
<
std
::
string
>
m_
emptydir
s
;
std
::
shared_ptr
<
mock
::
Directory
>
m_parent
;
std
::
unordered_map
<
std
::
string
,
std
::
shared_ptr
<
File
>
>
m_files
;
std
::
unordered_map
<
std
::
string
,
std
::
shared_ptr
<
Directory
>
>
m_dirs
;
std
::
vector
<
std
::
string
>
m_
filePath
es
;
std
::
vector
<
std
::
string
>
m_
dirPathe
s
;
Directory
*
m_parent
;
unsigned
int
m_lastModificationDate
;
bool
m_isRemovable
;
std
::
shared_ptr
<
fs
::
I
Device
>
m_device
;
std
::
shared_ptr
<
Device
>
m_device
;
};
class
Device
:
public
fs
::
IDevice
,
public
std
::
enable_shared_from_this
<
Device
>
{
public:
Device
(
const
std
::
string
&
mountpoint
,
const
std
::
string
&
uuid
)
:
m_uuid
(
uuid
)
,
m_removable
(
false
)
,
m_present
(
true
)
,
m_mountpoint
(
mountpoint
)
{
}
// We need at least one existing shared ptr before calling shared_from_this.
// Let the device be initialized and stored in a shared_ptr by the FileSystemFactory, and then
// initialize our fake root folder
void
setupRoot
()
{
m_root
=
std
::
make_shared
<
Directory
>
(
nullptr
,
m_mountpoint
,
0
,
shared_from_this
()
);
}
virtual
const
std
::
string
&
uuid
()
const
override
{
return
m_uuid
;
}
virtual
bool
isRemovable
()
const
override
{
return
m_removable
;
}
virtual
bool
isPresent
()
const
override
{
return
m_present
;
}
virtual
const
std
::
string
&
mountpoint
()
const
override
{
return
m_mountpoint
;
}
void
setRemovable
(
bool
value
)
{
m_removable
=
value
;
}
void
setPresent
(
bool
value
)
{
m_present
=
value
;
}
std
::
string
relativePath
(
const
std
::
string
&
path
)
{
auto
res
=
path
.
substr
(
m_mountpoint
.
length
()
);
while
(
res
[
0
]
==
'/'
)
res
.
erase
(
res
.
begin
()
);
return
res
;
}
void
addFile
(
const
std
::
string
&
filePath
)
{
m_root
->
addFile
(
relativePath
(
filePath
)
);
}
void
addFolder
(
const
std
::
string
&
path
,
unsigned
int
lastModif
)
{
m_root
->
addFolder
(
relativePath
(
path
),
lastModif
);
}
void
removeFile
(
const
std
::
string
&
filePath
)
{
m_root
->
removeFile
(
relativePath
(
filePath
)
);
}
void
removeFolder
(
const
std
::
string
&
filePath
)
{
auto
relPath
=
relativePath
(
filePath
);
if
(
relPath
.
empty
()
==
true
)
m_root
=
nullptr
;
else
m_root
->
removeFolder
(
relPath
);
}
std
::
shared_ptr
<
File
>
file
(
const
std
::
string
&
filePath
)
{
if
(
m_root
==
nullptr
)
return
nullptr
;
return
m_root
->
file
(
relativePath
(
filePath
)
);
}
std
::
shared_ptr
<
Directory
>
directory
(
const
std
::
string
&
path
)
{
if
(
m_root
==
nullptr
)
return
nullptr
;
const
auto
relPath
=
relativePath
(
path
);
if
(
relPath
.
empty
()
==
true
)
return
m_root
;
return
m_root
->
directory
(
relPath
);
}
void
addMountpoint
(
const
std
::
string
&
path
)
{
auto
relPath
=
relativePath
(
path
);
// m_root is already a mountpoint, we can't add a mountpoint to it.
assert
(
relPath
.
empty
()
==
false
);
m_root
->
addMountpoint
(
relPath
);
}
private:
std
::
string
m_uuid
;
bool
m_removable
;
bool
m_present
;
std
::
string
m_mountpoint
;
std
::
shared_ptr
<
Directory
>
m_root
;
};
struct
FileSystemFactory
:
public
factory
::
IFileSystem
{
static
constexpr
const
char
*
Root
=
"/a/"
;
static
constexpr
const
char
*
SubFolder
=
"/a/folder/"
;
static
const
std
::
string
Root
;
static
const
std
::
string
SubFolder
;
static
const
std
::
string
RootDeviceUuid
;
FileSystemFactory
()
{
rootDevice
=
std
::
make_shared
<
Device
>
(
std
::
string
{
Root
},
"root"
);
removableDevice
=
std
::
make_shared
<
Device
>
(
std
::
string
{
SubFolder
},
"removable"
);
removableDevice
->
setRemovable
(
true
);
dirs
[
Root
]
=
std
::
unique_ptr
<
mock
::
Directory
>
(
new
Directory
{
nullptr
,
Root
,
123
,
rootDevice
}
);
addFile
(
Root
,
"video.avi"
);
addFile
(
Root
,
"audio.mp3"
);
addFile
(
Root
,
"not_a_media.something"
);
addFile
(
Root
,
"some_other_file.seaotter"
);
addFolder
(
Root
,
"folder/"
,
456
,
removableDevice
);
addFile
(
SubFolder
,
"subfile.mp4"
);
// Add a root device unremovable
auto
rootDevice
=
addDevice
(
Root
,
RootDeviceUuid
);
rootDevice
->
addFile
(
Root
+
"/video.avi"
);
rootDevice
->
addFile
(
Root
+
"/audio.mp3"
);
rootDevice
->
addFile
(
Root
+
"/not_a_media.something"
);
rootDevice
->
addFile
(
Root
+
"/some_other_file.seaotter"
);
rootDevice
->
addFolder
(
SubFolder
,
456
);
rootDevice
->
addFile
(
SubFolder
+
"/subfile.mp4"
);
}
void
addFil
e
(
const
std
::
string
&
path
,
const
std
::
string
&
fileName
)
std
::
shared_ptr
<
Device
>
addDevic
e
(
const
std
::
string
&
mountpoint
,
const
std
::
string
&
uuid
)
{
dirs
[
path
]
->
addFile
(
fileName
);
files
[
path
+
fileName
]
=
std
::
unique_ptr
<
mock
::
File
>
(
new
mock
::
File
(
path
+
fileName
)
);
auto
dev
=
std
::
make_shared
<
Device
>
(
mountpoint
,
uuid
);
dev
->
setupRoot
();
// If this folder is already known, mark it as a nullptr and assume this means a mountpoint
auto
d
=
device
(
mountpoint
);
if
(
d
!=
nullptr
)
d
->
addMountpoint
(
mountpoint
);
devices
.
push_back
(
dev
);
return
dev
;
}
void
add
Folder
(
const
std
::
string
&
parentPath
,
const
std
::
string
&
path
,
unsigned
int
lastModif
,
std
::
shared_ptr
<
fs
::
I
Device
>
device
)
void
add
Device
(
std
::
shared_ptr
<
Device
>
device
)
{
auto
parent
=
dirs
[
parentPath
];
parent
->
addFolder
(
path
);
dirs
[
parentPath
+
path
]
=
std
::
unique_ptr
<
mock
::
Directory
>
(
new
Directory
(
parent
,
parentPath
+
path
,
lastModif
,
device
)
);
devices
.
push_back
(
device
);
}
void
remove
File
(
const
std
::
string
&
path
,
const
std
::
string
&
fileName
)
void
add
File
(
const
std
::
string
&
filePath
)
{
auto
it
=
files
.
find
(
path
+
fileName
);
if
(
it
==
end
(
files
)
)
throw
std
::
runtime_error
(
"Invalid file to remove"
);
files
.
erase
(
it
);
dirs
[
path
]
->
removeFile
(
path
+
fileName
);
auto
d
=
device
(
filePath
);
d
->
addFile
(
filePath
);
}
void
addFolder
(
const
std
::
string
&
path
,
unsigned
int
lastModif
)
{
auto
d
=
device
(
path
);
d
->
addFolder
(
path
,
lastModif
);
}
void
removeFile
(
const
std
::
string
&
filePath
)
{
auto
d
=
device
(
filePath
);
d
->
removeFile
(
filePath
);
}
void
removeFolder
(
const
std
::
string
&
path
)
{
auto
it
=
dirs
.
find
(
path
);
if
(
it
==
end
(
dirs
)
)
throw
std
::
runtime_error
(
"Invalid directory to remove"
);
for
(
const
auto
&
f
:
it
->
second
->
files
()
)
{
removeFile
(
path
,
utils
::
file
::
fileName
(
f
)
);
}
it
->
second
->
remove
();
dirs
.
erase
(
it
);
auto
d
=
device
(
path
);
d
->
removeFolder
(
path
);
}
std
::
shared_ptr
<
File
>
file
(
const
std
::
string
&
filePath
)
{
auto
d
=
device
(
filePath
);
return
d
->
file
(
filePath
);
}
std
::
shared_ptr
<
Directory
>
directory
(
const
std
::
string
&
path
)
{
auto
it
=
dirs
.
find
(
path
);
if
(
it
!=
end
(
dirs
)
)
return
it
->
second
;
return
nullptr
;
auto
d
=
device
(
path
);
return
d
->
directory
(
path
);
}
virtual
std
::
shared_ptr
<
fs
::
IDirectory
>
createDirectory
(
const
std
::
string
&
path
)
override
virtual
std
::
shared_ptr
<
fs
::
IDirectory
>
createDirectory
(
const
std
::
string
&
path
)
override
{
mock
::
Directory
*
res
=
nullptr
;
if
(
path
==
"."
)
{
res
=
dirs
[
Root
].
get
();
}
else
{
auto
it
=
dirs
.
find
(
path
);
if
(
it
!=
end
(
dirs
)
)
res
=
it
->
second
.
get
();
}
if
(
res
==
nullptr
)
throw
std
::
runtime_error
(
"Invalid path"
);
return
std
::
shared_ptr
<
fs
::
IDirectory
>
(
new
Directory
(
*
res
)
);
auto
d
=
device
(
path
);
if
(
d
==
nullptr
)
return
nullptr
;
return
d
->
directory
(
path
);
}
virtual
std
::
unique_ptr
<
fs
::
IFile
>
createFile
(
const
std
::
string
&
filePath
)
override
{
const
auto
it
=
files
.
find
(
filePath
);
if
(
it
==
end
(
files
)
)
files
[
filePath
].
reset
(
new
File
(
filePath
)
);
return
std
::
unique_ptr
<
fs
::
IFile
>
(
new
File
(
static_cast
<
const
mock
::
File
&>
(
*
it
->
second
.
get
()
)
)
);
auto
d
=
device
(
filePath
);
if
(
d
==
nullptr
)
return
nullptr
;
auto
f
=
d
->
file
(
filePath
);
if
(
f
==
nullptr
)
return
nullptr
;
return
std
::
unique_ptr
<
fs
::
IFile
>
(
new
File
(
*
f
)
);
}