diff options
| author | Dan Goodliffe <dan@randomdan.homeip.net> | 2019-09-23 19:33:42 +0100 | 
|---|---|---|
| committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2019-09-24 23:13:15 +0100 | 
| commit | c4cc5b556ce79e55fda1dd1e4a46bc23448306fb (patch) | |
| tree | 12877c0fb921b059fce5a9c24130b208e64cd173 | |
| parent | Add option terminator to test run command lines (diff) | |
| download | netfs-c4cc5b556ce79e55fda1dd1e4a46bc23448306fb.tar.bz2 netfs-c4cc5b556ce79e55fda1dd1e4a46bc23448306fb.tar.xz netfs-c4cc5b556ce79e55fda1dd1e4a46bc23448306fb.zip  | |
Fuse separation
First swing at separating the fuse library interface with the fuse operations
| -rw-r--r-- | Jamroot.jam | 2 | ||||
| -rw-r--r-- | netfs/daemon/daemonDirectory.cpp | 1 | ||||
| -rw-r--r-- | netfs/fuse/fuseApp.cpp | 65 | ||||
| -rw-r--r-- | netfs/fuse/fuseApp.h | 21 | ||||
| -rw-r--r-- | netfs/fuse/fuseAppBase.cpp | 47 | ||||
| -rw-r--r-- | netfs/fuse/fuseAppBase.h | 47 | ||||
| -rw-r--r-- | netfs/fuse/netfs.cpp | 45 | ||||
| -rw-r--r-- | netfs/unittests/Jamfile.jam | 13 | ||||
| -rw-r--r-- | netfs/unittests/mockFuse.cpp | 41 | ||||
| -rw-r--r-- | netfs/unittests/mockFuse.h | 8 | ||||
| -rw-r--r-- | netfs/unittests/testFuse.cpp | 108 | 
11 files changed, 225 insertions, 173 deletions
diff --git a/Jamroot.jam b/Jamroot.jam index cd93e3c..7c6a0bd 100644 --- a/Jamroot.jam +++ b/Jamroot.jam @@ -25,9 +25,11 @@ project  			<toolset>tidy:<checkxx>clang-*  			<toolset>tidy:<checkxx>misc-*  			<toolset>tidy:<checkxx>modernize-* +			<toolset>tidy:<xcheckxx>modernize-use-trailing-return-type  			<toolset>tidy:<checkxx>hicpp-*  			<toolset>tidy:<xcheckxx>hicpp-vararg  			<toolset>tidy:<xcheckxx>hicpp-signed-bitwise +			<toolset>tidy:<xcheckxx>hicpp-no-array-decay  			<toolset>tidy:<checkxx>performance-*  	; diff --git a/netfs/daemon/daemonDirectory.cpp b/netfs/daemon/daemonDirectory.cpp index 5e5c8ad..22c0e4f 100644 --- a/netfs/daemon/daemonDirectory.cpp +++ b/netfs/daemon/daemonDirectory.cpp @@ -54,7 +54,6 @@ DirectoryServer::listdir(const Ice::Current&)  			// LCOV_EXCL_STOP  		}  		struct stat s {}; -		// NOLINTNEXTLINE(hicpp-no-array-decay)  		if (::fstatat(fd, d->d_name, &s, AT_SYMLINK_NOFOLLOW) != 0) {  			// LCOV_EXCL_START  			throw NetFS::SystemError(errno); diff --git a/netfs/fuse/fuseApp.cpp b/netfs/fuse/fuseApp.cpp index ef1ca7f..aaec9bd 100644 --- a/netfs/fuse/fuseApp.cpp +++ b/netfs/fuse/fuseApp.cpp @@ -14,12 +14,22 @@ namespace AdHoc {  	template class CallCacheable<struct stat, std::string>;  } -NetFS::FuseApp::FuseApp(Ice::StringSeq a) : -	iceArgs(std::move(a)), +NetFS::FuseApp::FuseApp(Ice::StringSeq && a) : +	ic(Ice::initialize(a)),  	sessionOpened(false),  	openHandleId(0),  	converter(userLookup, groupLookup)  { +	if (!a.empty()) { +		const auto & arg = a.front(); +		if (arg.find("://") != std::string::npos) { +			fcr = configureFromUri(arg); +		} +		else if (auto colon = arg.find(':'); colon != std::string::npos) { +			fcr = configureFromFile(arg.substr(0, colon), arg.substr(colon + 1)); +		} +	} +	BOOST_ASSERT(fcr);  }  NetFS::FuseApp::~FuseApp() @@ -65,25 +75,12 @@ NetFS::FuseApp::~FuseApp()  	}  } -NetFS::Client::ConfigurationPtr -NetFS::FuseApp::ReadConfiguration(const std::filesystem::path & path) const -{ -	auto s = Slicer::FileDeserializerFactory::createNew(path.extension().string(), path); -	return Slicer::DeserializeAnyWith<NetFS::Client::ConfigurationPtr>(s); -} - -void * -NetFS::FuseApp::init(struct fuse_conn_info *) -{ -	ic = Ice::initialize(iceArgs); -	fcr = configurator(); -	return nullptr; -} -  NetFS::Client::ResourcePtr -NetFS::FuseApp::configureFromFile(const std::string & configPath, const std::string & resourceName) const +NetFS::FuseApp::configureFromFile(const std::filesystem::path & path, const std::string & resourceName) const  { -	return AdHoc::safeMapLookup<Client::ResourceNotFound>(ReadConfiguration(configPath)->Resources, resourceName); +	auto s = Slicer::FileDeserializerFactory::createNew(path.extension().string(), path); +	auto c = Slicer::DeserializeAnyWith<NetFS::Client::ConfigurationPtr>(s); +	return AdHoc::safeMapLookup<Client::ResourceNotFound>(c->Resources, resourceName);  }  AdHocFormatter(IceEndpointFmt, "%? -h %? -p %?"); @@ -101,36 +98,6 @@ NetFS::FuseApp::configureFromUri(const std::string & uriString) const  	return r;  } -int -NetFS::FuseApp::opt_parse(void *, const char * arg, int, struct fuse_args *) -{ -	if (strncmp(arg, "--Ice.", 6) == 0) { -		iceArgs.push_back(arg); -		return 0; -	} -	else if (strncmp(arg, "_netdev", 7) == 0) { -		return 0; -	} -	else if (arg[0] == '-') { -		return 1; -	} -	else if (!configurator) { -		if (strstr(arg, "://")) { -			configurator = std::bind(&NetFS::FuseApp::configureFromUri, this, arg); -		} -		else { -			const char * colon = strchr(arg, ':'); -			configurator = std::bind(&NetFS::FuseApp::configureFromFile, this, std::string(arg, colon), colon + 1); -		} -		return 0; -	} -	else if (mountPoint.empty()) { -		mountPoint = arg; -		return 1; -	} -	return 1; -} -  void  NetFS::FuseApp::connectSession()  { diff --git a/netfs/fuse/fuseApp.h b/netfs/fuse/fuseApp.h index df342b6..5b5a8fa 100644 --- a/netfs/fuse/fuseApp.h +++ b/netfs/fuse/fuseApp.h @@ -16,7 +16,7 @@  #include <boost/icl/interval_map.hpp>  namespace NetFS { -	class DLL_PUBLIC FuseApp : public FuseAppBase { +	class DLL_PUBLIC FuseApp : public FuseAppBaseT<FuseApp> {  		private:  			class OpenDir {  				public: @@ -49,15 +49,12 @@ namespace NetFS {  			typedef std::map<int, OpenFilePtr> OpenFiles;  		public: -			FuseApp(Ice::StringSeq); +			FuseApp(Ice::StringSeq &&);  			~FuseApp(); -		private: -			void * init (struct fuse_conn_info * info) override; -			int opt_parse(void *, const char * arg, int key, struct fuse_args *) override; - +		protected:  			void connectSession(); -			void connectToService(); +			virtual void connectToService();  			void connectToVolume();  			void connectHandles();  			void verifyConnection(); @@ -100,13 +97,10 @@ namespace NetFS {  			virtual struct fuse_context * fuse_get_context() = 0;  		protected: -			typedef std::function<Client::ResourcePtr()> Configurator; -			Configurator configurator; -			virtual NetFS::Client::ConfigurationPtr ReadConfiguration(const std::filesystem::path &) const; -			virtual NetFS::Client::ResourcePtr configureFromFile(const std::string &, const std::string &) const; -			virtual NetFS::Client::ResourcePtr configureFromUri(const std::string &) const; +			NetFS::Client::ResourcePtr configureFromFile(const std::filesystem::path &, const std::string &) const; +			NetFS::Client::ResourcePtr configureFromUri(const std::string &) const; -		private: +		protected:  			template<typename Handle, typename ... Params>  			void setProxy(uint64_t & fh, const Params & ...);  			template<typename Handle> @@ -121,7 +115,6 @@ namespace NetFS {  			ReqEnv reqEnv(); -			Ice::StringSeq iceArgs;  			Ice::CommunicatorPtr ic;  			Client::ResourcePtr fcr;  			mutable std::shared_mutex _proxymaplock; diff --git a/netfs/fuse/fuseAppBase.cpp b/netfs/fuse/fuseAppBase.cpp index 01ec8e1..0b6a90e 100644 --- a/netfs/fuse/fuseAppBase.cpp +++ b/netfs/fuse/fuseAppBase.cpp @@ -5,22 +5,28 @@  #include <unistd.h>  #include <cstdlib>  #include <typeinfo> +#include <boost/assert.hpp>  FuseAppBase * FuseAppBase::fuseApp; +FuseAppBase::FuseAppBase() +{ +	BOOST_ASSERT(!fuseApp); +	BOOST_ASSERT(this); +	fuseApp = this; +} + +FuseAppBase::~FuseAppBase() +{ +	BOOST_ASSERT(fuseApp); +	fuseApp = nullptr; +} +  // LCOV_EXCL_START  // These are all excluded from coverage because it is impossible  // to call them in a realistic manner. They exist only as the default  // implementation of the function which is never passed to libfuse  // unless it is overridden. -void * FuseAppBase::init(fuse_conn_info*) -{ -	return nullptr; -} -int FuseAppBase::opt_parse(void*, const char *, int, fuse_args*) -{ -	return 1; -}  int FuseAppBase::access(const char *, int)  {  	return -ENOSYS; @@ -190,11 +196,8 @@ void FuseAppBase::log(int level, const char * message) const noexcept  void FuseAppBase::logf(int level, const char * fmt, ...) const noexcept  {  	va_list args; -	// NOLINTNEXTLINE(hicpp-no-array-decay)  	va_start(args, fmt); -	// NOLINTNEXTLINE(hicpp-no-array-decay)  	vlogf(level, fmt, args); -	// NOLINTNEXTLINE(hicpp-no-array-decay)  	va_end(args);  } @@ -208,25 +211,3 @@ void FuseAppBase::beforeOperation()  {  } -void * FuseAppBase::fuseInit (struct fuse_conn_info *conn) -{ -	return fuseApp->init(conn); -} -void FuseAppBase::fuseDestroy(void *) -{ -	delete fuseApp; -} - -struct fuse_args -FuseAppBase::runint(int argc, char ** argv) -{ -	std::array<fuse_opt, 1> fuse_opts {}; -	fuseApp = this; -	struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -	if (fuse_opt_parse(&args, fuseApp, fuse_opts.data(), -			&internalHelper<decltype(&FuseAppBase::opt_parse), &FuseAppBase::opt_parse>) == -1) { -		exit(1); -	} -	return args; -} - diff --git a/netfs/fuse/fuseAppBase.h b/netfs/fuse/fuseAppBase.h index 2775c00..97810d2 100644 --- a/netfs/fuse/fuseAppBase.h +++ b/netfs/fuse/fuseAppBase.h @@ -15,9 +15,9 @@  class DLL_PUBLIC FuseAppBase {  	public: -		virtual ~FuseAppBase() = default; -		virtual void * init (struct fuse_conn_info * info); -		virtual int opt_parse(void *, const char * arg, int key, struct fuse_args *); +		FuseAppBase(); +		virtual ~FuseAppBase(); +  		virtual int access(const char *, int);  		virtual int chmod(const char *, mode_t);  		virtual int chown(const char *, uid_t, gid_t); @@ -64,18 +64,19 @@ class DLL_PUBLIC FuseAppBase {  		void logf(int level, const char * fmt, ...) const throw() __attribute__ ((__format__ (__printf__, 3, 4)));  		virtual void vlogf(int level, const char * fmt, va_list) const throw() __attribute__ ((__format__ (__printf__, 3, 0))) = 0; -		virtual int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc) = 0; -		virtual int main(int, char **, const struct fuse_operations *) = 0; +		mutable std::shared_mutex _lock; +	protected: +		static FuseAppBase * fuseApp; +}; +template <typename FuseApp> +class FuseAppBaseT : public FuseAppBase { +	public:  #define GetHelper(func) getHelper<decltype(&FuseAppBase::func), decltype(&FuseApp::func), &FuseAppBase::func>(&FuseAppBase::func) -		template <typename FuseApp> -		static int run(int argc, char ** argv, FuseApp * fa) -		{ -			auto args = fa->runint(argc, argv); -			struct fuse_operations operations = { +		FuseAppBaseT() : operations({  				GetHelper(getattr),  				GetHelper(readlink), -				NULL, // getdir deprecated +				nullptr, // getdir deprecated  				GetHelper(mknod),  				GetHelper(mkdir),  				GetHelper(unlink), @@ -86,7 +87,7 @@ class DLL_PUBLIC FuseAppBase {  				GetHelper(chmod),  				GetHelper(chown),  				GetHelper(truncate), -				NULL, // utime deprecated +				nullptr, // utime deprecated  				GetHelper(open),  				GetHelper(read),  				GetHelper(write), @@ -102,8 +103,8 @@ class DLL_PUBLIC FuseAppBase {  				GetHelper(readdir),  				GetHelper(releasedir),  				GetHelper(fsyncdir), -				fuseInit, -				fuseDestroy, +				nullptr, //fuseInit +				nullptr, //fuseDestroy  				GetHelper(access),  				GetHelper(create),  				GetHelper(ftruncate), @@ -129,15 +130,14 @@ class DLL_PUBLIC FuseAppBase {  #endif  #endif  #endif -			}; -			return fa->main(args.argc, args.argv, &operations); +			}) +		{  		} -		struct fuse_args runint(int, char **); +#undef GetHelper -	private: -		static void * fuseInit(struct fuse_conn_info *conn); -		static void fuseDestroy(void *); +		const struct fuse_operations operations; +	private:  		template <typename BFunc, typename IFunc, BFunc bfunc, typename ... Args>  		static int(*getHelper(int(FuseAppBase::*)(Args...)))(Args...)  		{ @@ -169,16 +169,13 @@ class DLL_PUBLIC FuseAppBase {  			}  			return nullptr;  		} + +	protected:  		template <typename Func, Func f, typename ... Args>  		static int internalHelper(Args ... a)  		{  			return (fuseApp->*f)(a...);  		} - -		static FuseAppBase * fuseApp; - -	protected: -		mutable std::shared_mutex _lock;  };  #endif diff --git a/netfs/fuse/netfs.cpp b/netfs/fuse/netfs.cpp index c21711c..14c3c93 100644 --- a/netfs/fuse/netfs.cpp +++ b/netfs/fuse/netfs.cpp @@ -1,10 +1,37 @@  #include "fuseApp.h"  #include <syslog.h> -class FuseImpl : public NetFS::FuseApp { +class FuseImpl : public fuse_args, public NetFS::FuseApp {  	public: -		explicit FuseImpl(const Ice::StringSeq & a) : -			NetFS::FuseApp(a) +		static int opt_parse(void * data, const char * arg, int, struct fuse_args *) +		{ +			auto iceArgs = static_cast<Ice::StringSeq *>(data); +			if (strncmp(arg, "--Ice.", 6) == 0) { +				iceArgs->push_back(arg); +				return 0; +			} +			else if (strncmp(arg, "_netdev", 7) == 0) { +				return 0; +			} +			else if (arg[0] == '-') { +				return 1; +			} +			else if (iceArgs->empty() || strncmp(iceArgs->front().c_str(), "--Ice.", 6) == 0) { +				iceArgs->insert(iceArgs->begin(), arg); +				return 0; +			} +			return 1; +		} + +		FuseImpl(int c, char ** v) : +			fuse_args(FUSE_ARGS_INIT(c, v)), +			NetFS::FuseApp([this](){ +				Ice::StringSeq iceArgs; +				if (fuse_opt_parse(this, &iceArgs, nullptr, opt_parse) == -1) { +					exit(-1); +				} +				return iceArgs; +			}())  		{  			openlog("netfs", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);  		} @@ -24,15 +51,9 @@ class FuseImpl : public NetFS::FuseApp {  			return ::fuse_get_context();  		} -		// NOLINTNEXTLINE(modernize-avoid-c-arrays, hicpp-avoid-c-arrays) -		int fuse_opt_parse(struct fuse_args * args, void * data, const struct fuse_opt opts[], fuse_opt_proc_t proc) override -		{ -			return ::fuse_opt_parse(args, data, opts, proc); -		} - -		int main(int argc, char ** argv, const struct fuse_operations * ops) override +		int run()  		{ -			return ::fuse_main(argc, argv, ops, this); +			return ::fuse_main(argc, argv, &operations, this);  		}  		void vlogf(int priority, const char * fmt, va_list args) const noexcept override @@ -44,6 +65,6 @@ class FuseImpl : public NetFS::FuseApp {  int  main(int argc, char* argv[])  { -	return FuseAppBase::run(argc, argv, new FuseImpl(Ice::argsToStringSeq(argc, argv))); +	return FuseImpl(argc, argv).run();  } diff --git a/netfs/unittests/Jamfile.jam b/netfs/unittests/Jamfile.jam index 2d3fac3..2fbaf4a 100644 --- a/netfs/unittests/Jamfile.jam +++ b/netfs/unittests/Jamfile.jam @@ -50,7 +50,7 @@ run testCore.cpp  run testGlacier.cpp  	: -- :  	defaultDaemon.xml -	defaultFuse.xml  +	defaultFuse.xml  	:  	<define>BOOST_TEST_DYN_LINK  	<library>boost_utf @@ -60,10 +60,19 @@ run testGlacier.cpp  run testEdgeCases.cpp  	: -- :  	defaultDaemon.xml -	defaultFuse.xml  +	defaultFuse.xml  	:  	<define>BOOST_TEST_DYN_LINK  	<library>boost_utf  	<library>testMocks  	: testEdgeCases ; +run testFuse.cpp +	: : +	defaultDaemon.xml +	: +	<define>BOOST_TEST_DYN_LINK +	<library>boost_utf +	<library>testMocks +	; + diff --git a/netfs/unittests/mockFuse.cpp b/netfs/unittests/mockFuse.cpp index da6b07f..fd93388 100644 --- a/netfs/unittests/mockFuse.cpp +++ b/netfs/unittests/mockFuse.cpp @@ -3,7 +3,6 @@  FuseMock::FuseMock(std::string ep, Ice::StringSeq a) :  	NetFS::FuseApp(std::move(a)), -	ops({}),  	testEndpoint(std::move(ep)),  	context({})  { @@ -19,34 +18,21 @@ FuseMock::fuse_get_context()  	return &context;  } -int -// NOLINTNEXTLINE(modernize-avoid-c-arrays, hicpp-avoid-c-arrays) -FuseMock::fuse_opt_parse(struct fuse_args * args, void * data, const struct fuse_opt [], fuse_opt_proc_t proc) +void +FuseMock::connectToService()  { -	for (int n = 0; n < args->argc; n += 1) { -		proc(data, args->argv[n], n, args); +	if (testEndpoint.empty()) { +		return FuseApp::connectToService();  	} -	return 0; -} -int -FuseMock::main(int, char **, const struct fuse_operations * o) -{ -	o->init(nullptr); -	ops = *o; -	return 0; -} - -NetFS::Client::ConfigurationPtr -FuseMock::ReadConfiguration(const std::filesystem::path & path) const -{ -	auto c = FuseApp::ReadConfiguration(path); -	for(const auto & r : c->Resources) { -		for(auto & e : r.second->Endpoints) { -			e = testEndpoint; +	if (!service) { +		Lock(_lock); +		std::string addr = fcr->ServiceIdentity + ":" + testEndpoint; +		service = Ice::checkedCast<NetFS::ServicePrx>(ic->stringToProxy(addr)); +		if (!service) { +			throw std::runtime_error("Invalid service proxy: " + testEndpoint);  		}  	} -	return c;  }  void @@ -57,12 +43,7 @@ FuseMock::vlogf(int, const char * fmt, va_list args) const noexcept  FuseMockHost::FuseMockHost(std::string ep, const Ice::StringSeq & a) :  	app(std::make_unique<FuseMock>(std::move(ep), a)), -	fuse(&app->ops) +	fuse(&app->operations)  { -	std::vector<char *> argv; -	for (auto & arg : a) { -		argv.push_back(const_cast<char *>(arg.c_str())); -	} -	FuseAppBase::run(a.size(), &argv.front(), app.get());  } diff --git a/netfs/unittests/mockFuse.h b/netfs/unittests/mockFuse.h index 7e4f3ad..4461da6 100644 --- a/netfs/unittests/mockFuse.h +++ b/netfs/unittests/mockFuse.h @@ -9,15 +9,9 @@ class DLL_PUBLIC FuseMock : public NetFS::FuseApp {  		FuseMock(std::string, Ice::StringSeq);  		struct fuse_context * fuse_get_context() override; -		int fuse_opt_parse(struct fuse_args * args, void * data, const struct fuse_opt [], fuse_opt_proc_t proc) override; -		int main(int, char **, const struct fuse_operations * o) override;  		void vlogf(int, const char *, va_list) const throw() override; -		fuse_operations ops; - -	protected: -		virtual NetFS::Client::ConfigurationPtr ReadConfiguration(const std::filesystem::path &) const override; - +		void connectToService() override;  	private:  		const std::string testEndpoint;  		fuse_context context; diff --git a/netfs/unittests/testFuse.cpp b/netfs/unittests/testFuse.cpp new file mode 100644 index 0000000..c5f9bbf --- /dev/null +++ b/netfs/unittests/testFuse.cpp @@ -0,0 +1,108 @@ +#define BOOST_TEST_MODULE TestNetFSFuse +#define FUSE_USE_VERSION 26 +#include <boost/test/unit_test.hpp> +#include <definedDirs.h> +#include <filesystem> +#include <ostream> +#include <thread> +#include <fuse.h> +#include <fuseApp.h> +#include "mockDaemon.h" + +static const std::filesystem::path mntpnt { binDir / "mnt" }; +const std::string testEndpoint("tcp -h localhost -p 12015"); +const std::string testUri("tcp://localhost:12015/testvol"); + +class FuseMountPoint : public MockDaemonHost, public NetFS::FuseApp { +	public: +		FuseMountPoint() : +			MockDaemonHost(::testEndpoint, { +				"--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string() +			}), +			NetFS::FuseApp({::testUri}) +		{ +			std::filesystem::remove(mntpnt); +			std::filesystem::create_directory(mntpnt); +			struct fuse_args fargs { }; +			ch = ::fuse_mount(mntpnt.c_str(), &fargs); +			BOOST_REQUIRE(ch); +			fs = ::fuse_new(ch, &fargs, &operations, sizeof(fuse_operations), this); +			BOOST_REQUIRE(fs); +			th = std::make_unique<std::thread>(::fuse_loop, fs); +		} + +		~FuseMountPoint() override +		{ +			if (ch) { +				::fuse_unmount(mntpnt.c_str(), ch); +			} +			if (th) { +				th->join(); +			} +			std::filesystem::remove(mntpnt); +		} + +		FuseMountPoint(const FuseMountPoint &) = delete; +		FuseMountPoint(FuseMountPoint &&) = delete; + +		FuseMountPoint & operator=(const FuseMountPoint &) = delete; +		FuseMountPoint & operator=(FuseMountPoint &&) = delete; + +		struct fuse_context * fuse_get_context() override +		{ +			return ::fuse_get_context(); +		} + +		static inline char * vstrdupf(const char * fmt, va_list args) +		{ +			char * out {}; +			BOOST_REQUIRE_GE(vasprintf(&out, fmt, args), 0); +			BOOST_REQUIRE(out); +			return out; +		} + +		void vlogf(int, const char * fmt, va_list args) const noexcept override +		{ +			std::unique_ptr<char, void(*)(void*)> msg(vstrdupf(fmt, args), std::free); +			BOOST_TEST_MESSAGE(msg.get()); +		} + +		struct fuse_chan * ch; +		struct fuse * fs; +		std::unique_ptr<std::thread> th; +}; + +struct pathrange { +	const std::filesystem::path path; + +	[[nodiscard]] std::filesystem::directory_iterator begin() const +	{ +		return std::filesystem::directory_iterator(path); +	} + +	[[nodiscard]] std::filesystem::directory_iterator end() const +	{ +		return {}; +	} +}; + +BOOST_FIXTURE_TEST_SUITE(fmp, FuseMountPoint); + +BOOST_AUTO_TEST_CASE(fuse, * boost::unit_test::timeout(5)) +{ +	BOOST_REQUIRE(std::filesystem::is_directory(mntpnt)); +} + +BOOST_AUTO_TEST_CASE(fuse_ls, * boost::unit_test::timeout(5)) +{ +	BOOST_REQUIRE(std::filesystem::is_directory(mntpnt)); +	std::filesystem::create_symlink(selfExe, mntpnt / "me"); +	std::set<std::filesystem::path> paths(std::filesystem::directory_iterator(mntpnt), {}); +	BOOST_REQUIRE_EQUAL(paths.size(), 1); +	BOOST_CHECK_EQUAL(paths.begin()->filename(), "me"); +	BOOST_CHECK(std::filesystem::is_symlink(mntpnt / "me")); +	BOOST_CHECK(std::filesystem::is_symlink(mntpnt / "me")); +} + +BOOST_AUTO_TEST_SUITE_END(); +  | 
