This is an automated email from the ASF dual-hosted git repository.

fanng pushed a commit to branch branch-gvfs-fuse-dev
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/branch-gvfs-fuse-dev by this 
push:
     new 08e393c1f [#5886] feat (gvfs-fuse): Implement an in-memory file system 
 (#5915)
08e393c1f is described below

commit 08e393c1f5b1aceca760695bc1749fba21df9a59
Author: Yuhui <[email protected]>
AuthorDate: Wed Dec 25 20:00:48 2024 +0800

    [#5886] feat (gvfs-fuse): Implement an in-memory file system  (#5915)
    
    ### What changes were proposed in this pull request?
    
    Implement an in-memory filesystem for testing and validating the FUSE
    framework. You need to implement the PathFilesystem trait and support
    basic file and directory operations:
    
    ### Why are the changes needed?
    
    Fix: #5886
    
    ### Does this PR introduce _any_ user-facing change?
    
    No
    
    ### How was this patch tested?
    
    IT
---
 clients/filesystem-fuse/Cargo.toml                 |   2 +-
 .../filesystem-fuse/src/default_raw_filesystem.rs  | 103 +++--
 clients/filesystem-fuse/src/filesystem.rs          | 484 ++++++++++++++++++---
 clients/filesystem-fuse/src/fuse_api_handle.rs     |  23 +-
 clients/filesystem-fuse/src/fuse_server.rs         |  93 ++++
 clients/filesystem-fuse/src/lib.rs                 |  11 +
 clients/filesystem-fuse/src/main.rs                |  67 +--
 clients/filesystem-fuse/src/memory_filesystem.rs   | 281 ++++++++++++
 clients/filesystem-fuse/src/{main.rs => mount.rs}  |  66 ++-
 clients/filesystem-fuse/src/opened_file.rs         |   7 +-
 clients/filesystem-fuse/src/opened_file_manager.rs |   5 +-
 clients/filesystem-fuse/src/utils.rs               |  48 +-
 clients/filesystem-fuse/tests/fuse_test.rs         | 147 +++++++
 clients/filesystem-fuse/tests/it.rs                |  23 -
 14 files changed, 1102 insertions(+), 258 deletions(-)

diff --git a/clients/filesystem-fuse/Cargo.toml 
b/clients/filesystem-fuse/Cargo.toml
index 3bcf20f37..75a4dd713 100644
--- a/clients/filesystem-fuse/Cargo.toml
+++ b/clients/filesystem-fuse/Cargo.toml
@@ -40,6 +40,6 @@ fuse3 = { version = "0.8.1", "features" = ["tokio-runtime", 
"unprivileged"] }
 futures-util = "0.3.30"
 libc = "0.2.168"
 log = "0.4.22"
+once_cell = "1.20.2"
 tokio = { version = "1.38.0", features = ["full"] }
 tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
-
diff --git a/clients/filesystem-fuse/src/default_raw_filesystem.rs 
b/clients/filesystem-fuse/src/default_raw_filesystem.rs
index 9a66cd551..0ab92e916 100644
--- a/clients/filesystem-fuse/src/default_raw_filesystem.rs
+++ b/clients/filesystem-fuse/src/default_raw_filesystem.rs
@@ -16,14 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-use crate::filesystem::{FileStat, PathFileSystem, RawFileSystem, Result};
+use crate::filesystem::{
+    FileStat, PathFileSystem, RawFileSystem, Result, INITIAL_FILE_ID, 
ROOT_DIR_FILE_ID,
+    ROOT_DIR_PARENT_FILE_ID, ROOT_DIR_PATH,
+};
 use crate::opened_file::{FileHandle, OpenFileFlags};
 use crate::opened_file_manager::OpenedFileManager;
-use crate::utils::join_file_path;
 use async_trait::async_trait;
 use bytes::Bytes;
 use fuse3::{Errno, FileType};
 use std::collections::HashMap;
+use std::ffi::OsStr;
+use std::path::{Path, PathBuf};
 use std::sync::atomic::AtomicU64;
 use tokio::sync::RwLock;
 
@@ -43,16 +47,11 @@ pub struct DefaultRawFileSystem<T: PathFileSystem> {
 }
 
 impl<T: PathFileSystem> DefaultRawFileSystem<T> {
-    const INITIAL_FILE_ID: u64 = 10000;
-    const ROOT_DIR_PARENT_FILE_ID: u64 = 1;
-    const ROOT_DIR_FILE_ID: u64 = 1;
-    const ROOT_DIR_NAME: &'static str = "";
-
     pub(crate) fn new(fs: T) -> Self {
         Self {
             file_entry_manager: RwLock::new(FileEntryManager::new()),
             opened_file_manager: OpenedFileManager::new(),
-            file_id_generator: AtomicU64::new(Self::INITIAL_FILE_ID),
+            file_id_generator: AtomicU64::new(INITIAL_FILE_ID),
             fs,
         }
     }
@@ -70,7 +69,7 @@ impl<T: PathFileSystem> DefaultRawFileSystem<T> {
             .ok_or(Errno::from(libc::ENOENT))
     }
 
-    async fn get_file_entry_by_path(&self, path: &str) -> Option<FileEntry> {
+    async fn get_file_entry_by_path(&self, path: &Path) -> Option<FileEntry> {
         self.file_entry_manager
             .read()
             .await
@@ -123,12 +122,12 @@ impl<T: PathFileSystem> DefaultRawFileSystem<T> {
         Ok(file.file_handle())
     }
 
-    async fn remove_file_entry_locked(&self, path: &str) {
+    async fn remove_file_entry_locked(&self, path: &Path) {
         let mut file_manager = self.file_entry_manager.write().await;
         file_manager.remove(path);
     }
 
-    async fn insert_file_entry_locked(&self, parent_file_id: u64, file_id: 
u64, path: &str) {
+    async fn insert_file_entry_locked(&self, parent_file_id: u64, file_id: 
u64, path: &Path) {
         let mut file_manager = self.file_entry_manager.write().await;
         file_manager.insert(parent_file_id, file_id, path);
     }
@@ -139,9 +138,9 @@ impl<T: PathFileSystem> RawFileSystem for 
DefaultRawFileSystem<T> {
     async fn init(&self) -> Result<()> {
         // init root directory
         self.insert_file_entry_locked(
-            Self::ROOT_DIR_PARENT_FILE_ID,
-            Self::ROOT_DIR_FILE_ID,
-            Self::ROOT_DIR_NAME,
+            ROOT_DIR_PARENT_FILE_ID,
+            ROOT_DIR_FILE_ID,
+            Path::new(ROOT_DIR_PATH),
         )
         .await;
         self.fs.init().await
@@ -149,7 +148,7 @@ impl<T: PathFileSystem> RawFileSystem for 
DefaultRawFileSystem<T> {
 
     async fn get_file_path(&self, file_id: u64) -> Result<String> {
         let file_entry = self.get_file_entry(file_id).await?;
-        Ok(file_entry.path)
+        Ok(file_entry.path.to_string_lossy().to_string())
     }
 
     async fn valid_file_handle_id(&self, file_id: u64, fh: u64) -> Result<()> {
@@ -174,12 +173,15 @@ impl<T: PathFileSystem> RawFileSystem for 
DefaultRawFileSystem<T> {
         Ok(file_stat)
     }
 
-    async fn lookup(&self, parent_file_id: u64, name: &str) -> 
Result<FileStat> {
+    async fn lookup(&self, parent_file_id: u64, name: &OsStr) -> 
Result<FileStat> {
         let parent_file_entry = self.get_file_entry(parent_file_id).await?;
-        let mut file_stat = self.fs.lookup(&parent_file_entry.path, 
name).await?;
+
+        let path = parent_file_entry.path.join(name);
+        let mut file_stat = self.fs.stat(&path).await?;
         // fill the file id to file stat
         self.resolve_file_id_to_filestat(&mut file_stat, parent_file_id)
             .await;
+
         Ok(file_stat)
     }
 
@@ -203,11 +205,16 @@ impl<T: PathFileSystem> RawFileSystem for 
DefaultRawFileSystem<T> {
             .await
     }
 
-    async fn create_file(&self, parent_file_id: u64, name: &str, flags: u32) 
-> Result<FileHandle> {
+    async fn create_file(
+        &self,
+        parent_file_id: u64,
+        name: &OsStr,
+        flags: u32,
+    ) -> Result<FileHandle> {
         let parent_file_entry = self.get_file_entry(parent_file_id).await?;
         let mut file_without_id = self
             .fs
-            .create_file(&parent_file_entry.path, name, OpenFileFlags(flags))
+            .create_file(&parent_file_entry.path.join(name), 
OpenFileFlags(flags))
             .await?;
 
         file_without_id.set_file_id(parent_file_id, self.next_file_id());
@@ -226,9 +233,10 @@ impl<T: PathFileSystem> RawFileSystem for 
DefaultRawFileSystem<T> {
         Ok(opened_file_with_file_handle_id.file_handle())
     }
 
-    async fn create_dir(&self, parent_file_id: u64, name: &str) -> Result<u64> 
{
+    async fn create_dir(&self, parent_file_id: u64, name: &OsStr) -> 
Result<u64> {
         let parent_file_entry = self.get_file_entry(parent_file_id).await?;
-        let mut filestat = self.fs.create_dir(&parent_file_entry.path, 
name).await?;
+        let path = parent_file_entry.path.join(name);
+        let mut filestat = self.fs.create_dir(&path).await?;
 
         filestat.set_file_id(parent_file_id, self.next_file_id());
 
@@ -243,23 +251,23 @@ impl<T: PathFileSystem> RawFileSystem for 
DefaultRawFileSystem<T> {
         self.fs.set_attr(&file_entry.path, file_stat, true).await
     }
 
-    async fn remove_file(&self, parent_file_id: u64, name: &str) -> Result<()> 
{
+    async fn remove_file(&self, parent_file_id: u64, name: &OsStr) -> 
Result<()> {
         let parent_file_entry = self.get_file_entry(parent_file_id).await?;
-        self.fs.remove_file(&parent_file_entry.path, name).await?;
+        let path = parent_file_entry.path.join(name);
+        self.fs.remove_file(&path).await?;
 
         // remove the file from file entry manager
-        self.remove_file_entry_locked(&join_file_path(&parent_file_entry.path, 
name))
-            .await;
+        self.remove_file_entry_locked(&path).await;
         Ok(())
     }
 
-    async fn remove_dir(&self, parent_file_id: u64, name: &str) -> Result<()> {
+    async fn remove_dir(&self, parent_file_id: u64, name: &OsStr) -> 
Result<()> {
         let parent_file_entry = self.get_file_entry(parent_file_id).await?;
-        self.fs.remove_dir(&parent_file_entry.path, name).await?;
+        let path = parent_file_entry.path.join(name);
+        self.fs.remove_dir(&path).await?;
 
         // remove the dir from file entry manager
-        self.remove_file_entry_locked(&join_file_path(&parent_file_entry.path, 
name))
-            .await;
+        self.remove_file_entry_locked(&path).await;
         Ok(())
     }
 
@@ -324,7 +332,7 @@ impl<T: PathFileSystem> RawFileSystem for 
DefaultRawFileSystem<T> {
 struct FileEntry {
     file_id: u64,
     parent_file_id: u64,
-    path: String,
+    path: PathBuf,
 }
 
 /// FileEntryManager is manage all the file entries in memory. it is used 
manger the file relationship and name mapping.
@@ -333,7 +341,7 @@ struct FileEntryManager {
     file_id_map: HashMap<u64, FileEntry>,
 
     // file_path_map is a map of file path to file entry.
-    file_path_map: HashMap<String, FileEntry>,
+    file_path_map: HashMap<PathBuf, FileEntry>,
 }
 
 impl FileEntryManager {
@@ -348,21 +356,21 @@ impl FileEntryManager {
         self.file_id_map.get(&file_id).cloned()
     }
 
-    fn get_file_entry_by_path(&self, path: &str) -> Option<FileEntry> {
+    fn get_file_entry_by_path(&self, path: &Path) -> Option<FileEntry> {
         self.file_path_map.get(path).cloned()
     }
 
-    fn insert(&mut self, parent_file_id: u64, file_id: u64, path: &str) {
+    fn insert(&mut self, parent_file_id: u64, file_id: u64, path: &Path) {
         let file_entry = FileEntry {
             file_id,
             parent_file_id,
-            path: path.to_string(),
+            path: path.into(),
         };
         self.file_id_map.insert(file_id, file_entry.clone());
-        self.file_path_map.insert(path.to_string(), file_entry);
+        self.file_path_map.insert(path.into(), file_entry);
     }
 
-    fn remove(&mut self, path: &str) {
+    fn remove(&mut self, path: &Path) {
         if let Some(file) = self.file_path_map.remove(path) {
             self.file_id_map.remove(&file.file_id);
         }
@@ -372,23 +380,34 @@ impl FileEntryManager {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::filesystem::tests::TestRawFileSystem;
+    use crate::memory_filesystem::MemoryFileSystem;
 
     #[test]
     fn test_file_entry_manager() {
         let mut manager = FileEntryManager::new();
-        manager.insert(1, 2, "a/b");
+        manager.insert(1, 2, Path::new("a/b"));
         let file = manager.get_file_entry_by_id(2).unwrap();
         assert_eq!(file.file_id, 2);
         assert_eq!(file.parent_file_id, 1);
-        assert_eq!(file.path, "a/b");
+        assert_eq!(file.path, Path::new("a/b"));
 
-        let file = manager.get_file_entry_by_path("a/b").unwrap();
+        let file = manager.get_file_entry_by_path(Path::new("a/b")).unwrap();
         assert_eq!(file.file_id, 2);
         assert_eq!(file.parent_file_id, 1);
-        assert_eq!(file.path, "a/b");
+        assert_eq!(file.path, Path::new("a/b"));
 
-        manager.remove("a/b");
+        manager.remove(Path::new("a/b"));
         assert!(manager.get_file_entry_by_id(2).is_none());
-        assert!(manager.get_file_entry_by_path("a/b").is_none());
+        assert!(manager.get_file_entry_by_path(Path::new("a/b")).is_none());
+    }
+
+    #[tokio::test]
+    async fn test_default_raw_file_system() {
+        let memory_fs = MemoryFileSystem::new().await;
+        let raw_fs = DefaultRawFileSystem::new(memory_fs);
+        let _ = raw_fs.init().await;
+        let mut tester = TestRawFileSystem::new(raw_fs);
+        tester.test_raw_file_system().await;
     }
 }
diff --git a/clients/filesystem-fuse/src/filesystem.rs 
b/clients/filesystem-fuse/src/filesystem.rs
index b0d32ded2..d9440b0e6 100644
--- a/clients/filesystem-fuse/src/filesystem.rs
+++ b/clients/filesystem-fuse/src/filesystem.rs
@@ -17,14 +17,22 @@
  * under the License.
  */
 use crate::opened_file::{FileHandle, OpenFileFlags, OpenedFile};
-use crate::utils::{join_file_path, split_file_path};
 use async_trait::async_trait;
 use bytes::Bytes;
+use fuse3::FileType::{Directory, RegularFile};
 use fuse3::{Errno, FileType, Timestamp};
+use std::ffi::{OsStr, OsString};
+use std::path::{Path, PathBuf};
 use std::time::SystemTime;
 
 pub(crate) type Result<T> = std::result::Result<T, Errno>;
 
+pub(crate) const ROOT_DIR_PARENT_FILE_ID: u64 = 1;
+pub(crate) const ROOT_DIR_FILE_ID: u64 = 1;
+pub(crate) const ROOT_DIR_NAME: &str = "";
+pub(crate) const ROOT_DIR_PATH: &str = "/";
+pub(crate) const INITIAL_FILE_ID: u64 = 10000;
+
 /// RawFileSystem interface for the file system implementation. it use by 
FuseApiHandle,
 /// it ues the file id to operate the file system apis
 /// the `file_id` and `parent_file_id` it is the unique identifier for the 
file system,
@@ -47,7 +55,7 @@ pub(crate) trait RawFileSystem: Send + Sync {
     async fn stat(&self, file_id: u64) -> Result<FileStat>;
 
     /// Lookup the file by parent file id and file name, if the file exists, 
return the file stat
-    async fn lookup(&self, parent_file_id: u64, name: &str) -> 
Result<FileStat>;
+    async fn lookup(&self, parent_file_id: u64, name: &OsStr) -> 
Result<FileStat>;
 
     /// Read the directory by file id, if the file id is a valid directory, 
return the file stat list
     async fn read_dir(&self, dir_file_id: u64) -> Result<Vec<FileStat>>;
@@ -59,19 +67,24 @@ pub(crate) trait RawFileSystem: Send + Sync {
     async fn open_dir(&self, file_id: u64, flags: u32) -> Result<FileHandle>;
 
     /// Create the file by parent file id and file name and flags, if 
successful, return the file handle
-    async fn create_file(&self, parent_file_id: u64, name: &str, flags: u32) 
-> Result<FileHandle>;
+    async fn create_file(
+        &self,
+        parent_file_id: u64,
+        name: &OsStr,
+        flags: u32,
+    ) -> Result<FileHandle>;
 
     /// Create the directory by parent file id and file name, if successful, 
return the file id
-    async fn create_dir(&self, parent_file_id: u64, name: &str) -> Result<u64>;
+    async fn create_dir(&self, parent_file_id: u64, name: &OsStr) -> 
Result<u64>;
 
     /// Set the file attribute by file id and file stat
     async fn set_attr(&self, file_id: u64, file_stat: &FileStat) -> Result<()>;
 
     /// Remove the file by parent file id and file name
-    async fn remove_file(&self, parent_file_id: u64, name: &str) -> Result<()>;
+    async fn remove_file(&self, parent_file_id: u64, name: &OsStr) -> 
Result<()>;
 
     /// Remove the directory by parent file id and file name
-    async fn remove_dir(&self, parent_file_id: u64, name: &str) -> Result<()>;
+    async fn remove_dir(&self, parent_file_id: u64, name: &OsStr) -> 
Result<()>;
 
     /// Close the file by file id and file handle, if successful
     async fn close_file(&self, file_id: u64, fh: u64) -> Result<()>;
@@ -91,39 +104,31 @@ pub(crate) trait PathFileSystem: Send + Sync {
     async fn init(&self) -> Result<()>;
 
     /// Get the file stat by file path, if the file exists, return the file 
stat
-    async fn stat(&self, path: &str) -> Result<FileStat>;
-
-    /// Get the file stat by parent file path and file name, if the file 
exists, return the file stat
-    async fn lookup(&self, parent: &str, name: &str) -> Result<FileStat>;
+    async fn stat(&self, path: &Path) -> Result<FileStat>;
 
     /// Read the directory by file path, if the directory exists, return the 
file stat list
-    async fn read_dir(&self, path: &str) -> Result<Vec<FileStat>>;
+    async fn read_dir(&self, path: &Path) -> Result<Vec<FileStat>>;
 
     /// Open the file by file path and flags, if the file exists, return the 
opened file
-    async fn open_file(&self, path: &str, flags: OpenFileFlags) -> 
Result<OpenedFile>;
+    async fn open_file(&self, path: &Path, flags: OpenFileFlags) -> 
Result<OpenedFile>;
 
     /// Open the directory by file path and flags, if the file exists, return 
the opened file
-    async fn open_dir(&self, path: &str, flags: OpenFileFlags) -> 
Result<OpenedFile>;
+    async fn open_dir(&self, path: &Path, flags: OpenFileFlags) -> 
Result<OpenedFile>;
 
-    /// Create the file by parent file path and file name and flags, if 
successful return the opened file
-    async fn create_file(
-        &self,
-        parent: &str,
-        name: &str,
-        flags: OpenFileFlags,
-    ) -> Result<OpenedFile>;
+    /// Create the file by file path and flags, if successful, return the 
opened file
+    async fn create_file(&self, path: &Path, flags: OpenFileFlags) -> 
Result<OpenedFile>;
 
-    /// Create the directory by parent file path and file name, if successful, 
return the file stat
-    async fn create_dir(&self, parent: &str, name: &str) -> Result<FileStat>;
+    /// Create the directory by file path , if successful, return the file stat
+    async fn create_dir(&self, path: &Path) -> Result<FileStat>;
 
     /// Set the file attribute by file path and file stat
-    async fn set_attr(&self, path: &str, file_stat: &FileStat, flush: bool) -> 
Result<()>;
+    async fn set_attr(&self, path: &Path, file_stat: &FileStat, flush: bool) 
-> Result<()>;
 
-    /// Remove the file by parent file path and file name
-    async fn remove_file(&self, parent: &str, name: &str) -> Result<()>;
+    /// Remove the file by file path
+    async fn remove_file(&self, path: &Path) -> Result<()>;
 
-    /// Remove the directory by parent file path and file name
-    async fn remove_dir(&self, parent: &str, name: &str) -> Result<()>;
+    /// Remove the directory by file path
+    async fn remove_dir(&self, path: &Path) -> Result<()>;
 }
 
 // FileSystemContext is the system environment for the fuse file system.
@@ -166,10 +171,10 @@ pub struct FileStat {
     pub(crate) parent_file_id: u64,
 
     // file name
-    pub(crate) name: String,
+    pub(crate) name: OsString,
 
     // file path of the fuse file system root
-    pub(crate) path: String,
+    pub(crate) path: PathBuf,
 
     // file size
     pub(crate) size: u64,
@@ -191,31 +196,33 @@ pub struct FileStat {
 }
 
 impl FileStat {
-    pub fn new_file_filestat_with_path(path: &str, size: u64) -> Self {
-        let (parent, name) = split_file_path(path);
-        Self::new_file_filestat(parent, name, size)
+    pub fn new_file_filestat_with_path(path: &Path, size: u64) -> Self {
+        Self::new_filestat(path, size, RegularFile)
     }
 
-    pub fn new_dir_filestat_with_path(path: &str) -> Self {
-        let (parent, name) = split_file_path(path);
-        Self::new_dir_filestat(parent, name)
+    pub fn new_dir_filestat_with_path(path: &Path) -> Self {
+        Self::new_filestat(path, 0, Directory)
     }
 
-    pub fn new_file_filestat(parent: &str, name: &str, size: u64) -> Self {
-        Self::new_filestat(parent, name, size, FileType::RegularFile)
+    pub fn new_file_filestat(parent: &Path, name: &OsStr, size: u64) -> Self {
+        let path = parent.join(name);
+        Self::new_filestat(&path, size, RegularFile)
     }
 
-    pub fn new_dir_filestat(parent: &str, name: &str) -> Self {
-        Self::new_filestat(parent, name, 0, FileType::Directory)
+    pub fn new_dir_filestat(parent: &Path, name: &OsStr) -> Self {
+        let path = parent.join(name);
+        Self::new_filestat(&path, 0, Directory)
     }
 
-    pub fn new_filestat(parent: &str, name: &str, size: u64, kind: FileType) 
-> Self {
+    pub fn new_filestat(path: &Path, size: u64, kind: FileType) -> Self {
         let atime = Timestamp::from(SystemTime::now());
+        // root directory name is ""
+        let name = path.file_name().unwrap_or(OsStr::new(ROOT_DIR_NAME));
         Self {
             file_id: 0,
             parent_file_id: 0,
-            name: name.into(),
-            path: join_file_path(parent, name),
+            name: name.to_os_string(),
+            path: path.into(),
             size: size,
             kind: kind,
             atime: atime,
@@ -262,43 +269,414 @@ pub trait FileWriter: Sync + Send {
 }
 
 #[cfg(test)]
-mod tests {
+pub(crate) mod tests {
     use super::*;
+    use std::collections::HashMap;
+
+    pub(crate) struct TestPathFileSystem<F: PathFileSystem> {
+        files: HashMap<PathBuf, FileStat>,
+        fs: F,
+    }
+
+    impl<F: PathFileSystem> TestPathFileSystem<F> {
+        pub(crate) fn new(fs: F) -> Self {
+            Self {
+                files: HashMap::new(),
+                fs,
+            }
+        }
+
+        pub(crate) async fn test_path_file_system(&mut self) {
+            // Test root dir
+            self.test_root_dir().await;
+
+            // Test stat file
+            self.test_stat_file(Path::new("/.gvfs_meta"), RegularFile, 0)
+                .await;
+
+            // Test create file
+            self.test_create_file(Path::new("/file1.txt")).await;
+
+            // Test create dir
+            self.test_create_dir(Path::new("/dir1")).await;
+
+            // Test list dir
+            self.test_list_dir(Path::new("/")).await;
+
+            // Test remove file
+            self.test_remove_file(Path::new("/file1.txt")).await;
+
+            // Test remove dir
+            self.test_remove_dir(Path::new("/dir1")).await;
+
+            // Test file not found
+            self.test_file_not_found(Path::new("unknown")).await;
+
+            // Test list dir
+            self.test_list_dir(Path::new("/")).await;
+        }
+
+        async fn test_root_dir(&mut self) {
+            let root_dir_path = Path::new("/");
+            let root_file_stat = self.fs.stat(root_dir_path).await;
+            assert!(root_file_stat.is_ok());
+            let root_file_stat = root_file_stat.unwrap();
+            self.assert_file_stat(&root_file_stat, root_dir_path, Directory, 
0);
+        }
+
+        async fn test_stat_file(&mut self, path: &Path, expect_kind: FileType, 
expect_size: u64) {
+            let file_stat = self.fs.stat(path).await;
+            assert!(file_stat.is_ok());
+            let file_stat = file_stat.unwrap();
+            self.assert_file_stat(&file_stat, path, expect_kind, expect_size);
+            self.files.insert(file_stat.path.clone(), file_stat);
+        }
+
+        async fn test_create_file(&mut self, path: &Path) {
+            let opened_file = self.fs.create_file(path, 
OpenFileFlags(0)).await;
+            assert!(opened_file.is_ok());
+            let file = opened_file.unwrap();
+            self.assert_file_stat(&file.file_stat, path, 
FileType::RegularFile, 0);
+            self.test_stat_file(path, RegularFile, 0).await;
+        }
+
+        async fn test_create_dir(&mut self, path: &Path) {
+            let dir_stat = self.fs.create_dir(path).await;
+            assert!(dir_stat.is_ok());
+            let dir_stat = dir_stat.unwrap();
+            self.assert_file_stat(&dir_stat, path, Directory, 0);
+            self.test_stat_file(path, Directory, 0).await;
+        }
+
+        async fn test_list_dir(&self, path: &Path) {
+            let list_dir = self.fs.read_dir(path).await;
+            assert!(list_dir.is_ok());
+            let list_dir = list_dir.unwrap();
+            assert_eq!(list_dir.len(), self.files.len());
+            for file_stat in list_dir {
+                assert!(self.files.contains_key(&file_stat.path));
+                let actual_file_stat = 
self.files.get(&file_stat.path).unwrap();
+                self.assert_file_stat(
+                    &file_stat,
+                    &actual_file_stat.path,
+                    actual_file_stat.kind,
+                    actual_file_stat.size,
+                );
+            }
+        }
+
+        async fn test_remove_file(&mut self, path: &Path) {
+            let remove_file = self.fs.remove_file(path).await;
+            assert!(remove_file.is_ok());
+            self.files.remove(path);
+
+            self.test_file_not_found(path).await;
+        }
+
+        async fn test_remove_dir(&mut self, path: &Path) {
+            let remove_dir = self.fs.remove_dir(path).await;
+            assert!(remove_dir.is_ok());
+            self.files.remove(path);
+
+            self.test_file_not_found(path).await;
+        }
+
+        async fn test_file_not_found(&self, path: &Path) {
+            let not_found_file = self.fs.stat(path).await;
+            assert!(not_found_file.is_err());
+        }
+
+        fn assert_file_stat(&self, file_stat: &FileStat, path: &Path, kind: 
FileType, size: u64) {
+            assert_eq!(file_stat.path, path);
+            assert_eq!(file_stat.kind, kind);
+            assert_eq!(file_stat.size, size);
+        }
+    }
+
+    pub(crate) struct TestRawFileSystem<F: RawFileSystem> {
+        fs: F,
+        files: HashMap<u64, FileStat>,
+    }
+
+    impl<F: RawFileSystem> TestRawFileSystem<F> {
+        pub(crate) fn new(fs: F) -> Self {
+            Self {
+                fs,
+                files: HashMap::new(),
+            }
+        }
+
+        pub(crate) async fn test_raw_file_system(&mut self) {
+            // Test root dir
+            self.test_root_dir().await;
+
+            let parent_file_id = ROOT_DIR_FILE_ID;
+            // Test lookup file
+            let file_id = self
+                .test_lookup_file(parent_file_id, ".gvfs_meta".as_ref(), 
RegularFile, 0)
+                .await;
+
+            // Test get file stat
+            self.test_stat_file(file_id, Path::new("/.gvfs_meta"), 
RegularFile, 0)
+                .await;
+
+            // Test get file path
+            self.test_get_file_path(file_id, "/.gvfs_meta").await;
+
+            // Test create file
+            self.test_create_file(parent_file_id, "file1.txt".as_ref())
+                .await;
+
+            // Test open file
+            let file_handle = self
+                .test_open_file(parent_file_id, "file1.txt".as_ref())
+                .await;
+
+            // Test write file
+            self.test_write_file(&file_handle, "test").await;
+
+            // Test read file
+            self.test_read_file(&file_handle, "test").await;
+
+            // Test close file
+            self.test_close_file(&file_handle).await;
+
+            // Test create dir
+            self.test_create_dir(parent_file_id, "dir1".as_ref()).await;
+
+            // Test list dir
+            self.test_list_dir(parent_file_id).await;
+
+            // Test remove file
+            self.test_remove_file(parent_file_id, "file1.txt".as_ref())
+                .await;
+
+            // Test remove dir
+            self.test_remove_dir(parent_file_id, "dir1".as_ref()).await;
+
+            // Test list dir again
+            self.test_list_dir(parent_file_id).await;
+
+            // Test file not found
+            self.test_file_not_found(23).await;
+        }
+
+        async fn test_root_dir(&self) {
+            let root_file_stat = self.fs.stat(ROOT_DIR_FILE_ID).await;
+            assert!(root_file_stat.is_ok());
+            let root_file_stat = root_file_stat.unwrap();
+            self.assert_file_stat(
+                &root_file_stat,
+                Path::new(ROOT_DIR_PATH),
+                FileType::Directory,
+                0,
+            );
+        }
+
+        async fn test_lookup_file(
+            &mut self,
+            parent_file_id: u64,
+            expect_name: &OsStr,
+            expect_kind: FileType,
+            expect_size: u64,
+        ) -> u64 {
+            let file_stat = self.fs.lookup(parent_file_id, expect_name).await;
+            assert!(file_stat.is_ok());
+            let file_stat = file_stat.unwrap();
+            self.assert_file_stat(&file_stat, &file_stat.path, expect_kind, 
expect_size);
+            assert_eq!(file_stat.name, expect_name);
+            let file_id = file_stat.file_id;
+            self.files.insert(file_stat.file_id, file_stat);
+            file_id
+        }
+
+        async fn test_get_file_path(&mut self, file_id: u64, expect_path: 
&str) {
+            let file_path = self.fs.get_file_path(file_id).await;
+            assert!(file_path.is_ok());
+            assert_eq!(file_path.unwrap(), expect_path);
+        }
+
+        async fn test_stat_file(
+            &mut self,
+            file_id: u64,
+            expect_path: &Path,
+            expect_kind: FileType,
+            expect_size: u64,
+        ) {
+            let file_stat = self.fs.stat(file_id).await;
+            assert!(file_stat.is_ok());
+            let file_stat = file_stat.unwrap();
+            self.assert_file_stat(&file_stat, expect_path, expect_kind, 
expect_size);
+            self.files.insert(file_stat.file_id, file_stat);
+        }
+
+        async fn test_create_file(&mut self, root_file_id: u64, name: &OsStr) {
+            let file = self.fs.create_file(root_file_id, name, 0).await;
+            assert!(file.is_ok());
+            let file = file.unwrap();
+            assert!(file.handle_id > 0);
+            assert!(file.file_id >= INITIAL_FILE_ID);
+            let file_stat = self.fs.stat(file.file_id).await;
+            assert!(file_stat.is_ok());
+
+            self.test_stat_file(file.file_id, &file_stat.unwrap().path, 
RegularFile, 0)
+                .await;
+        }
+
+        async fn test_open_file(&self, root_file_id: u64, name: &OsStr) -> 
FileHandle {
+            let file = self.fs.lookup(root_file_id, name).await.unwrap();
+            let file_handle = self.fs.open_file(file.file_id, 0).await;
+            assert!(file_handle.is_ok());
+            let file_handle = file_handle.unwrap();
+            assert_eq!(file_handle.file_id, file.file_id);
+            file_handle
+        }
+
+        async fn test_write_file(&mut self, file_handle: &FileHandle, content: 
&str) {
+            let write_size = self
+                .fs
+                .write(
+                    file_handle.file_id,
+                    file_handle.handle_id,
+                    0,
+                    content.as_bytes(),
+                )
+                .await;
+            assert!(write_size.is_ok());
+            assert_eq!(write_size.unwrap(), content.len() as u32);
+
+            self.files.get_mut(&file_handle.file_id).unwrap().size = 
content.len() as u64;
+        }
+
+        async fn test_read_file(&self, file_handle: &FileHandle, 
expected_content: &str) {
+            let read_data = self
+                .fs
+                .read(
+                    file_handle.file_id,
+                    file_handle.handle_id,
+                    0,
+                    expected_content.len() as u32,
+                )
+                .await;
+            assert!(read_data.is_ok());
+            assert_eq!(read_data.unwrap(), expected_content.as_bytes());
+        }
+
+        async fn test_close_file(&self, file_handle: &FileHandle) {
+            let close_file = self
+                .fs
+                .close_file(file_handle.file_id, file_handle.handle_id)
+                .await;
+            assert!(close_file.is_ok());
+        }
+
+        async fn test_create_dir(&mut self, parent_file_id: u64, name: &OsStr) 
{
+            let dir = self.fs.create_dir(parent_file_id, name).await;
+            assert!(dir.is_ok());
+            let dir_file_id = dir.unwrap();
+            assert!(dir_file_id >= INITIAL_FILE_ID);
+            let dir_stat = self.fs.stat(dir_file_id).await;
+            assert!(dir_stat.is_ok());
+
+            self.test_stat_file(dir_file_id, &dir_stat.unwrap().path, 
Directory, 0)
+                .await;
+        }
+
+        async fn test_list_dir(&self, root_file_id: u64) {
+            let list_dir = self.fs.read_dir(root_file_id).await;
+            assert!(list_dir.is_ok());
+            let list_dir = list_dir.unwrap();
+            assert_eq!(list_dir.len(), self.files.len());
+            for file_stat in list_dir {
+                assert!(self.files.contains_key(&file_stat.file_id));
+                let actual_file_stat = 
self.files.get(&file_stat.file_id).unwrap();
+                self.assert_file_stat(
+                    &file_stat,
+                    &actual_file_stat.path,
+                    actual_file_stat.kind,
+                    actual_file_stat.size,
+                );
+            }
+        }
+
+        async fn test_remove_file(&mut self, root_file_id: u64, name: &OsStr) {
+            let file_stat = self.fs.lookup(root_file_id, name).await;
+            assert!(file_stat.is_ok());
+            let file_stat = file_stat.unwrap();
+
+            let remove_file = self.fs.remove_file(root_file_id, name).await;
+            assert!(remove_file.is_ok());
+            self.files.remove(&file_stat.file_id);
+
+            self.test_file_not_found(file_stat.file_id).await;
+        }
+
+        async fn test_remove_dir(&mut self, root_file_id: u64, name: &OsStr) {
+            let file_stat = self.fs.lookup(root_file_id, name).await;
+            assert!(file_stat.is_ok());
+            let file_stat = file_stat.unwrap();
+
+            let remove_dir = self.fs.remove_dir(root_file_id, name).await;
+            assert!(remove_dir.is_ok());
+            self.files.remove(&file_stat.file_id);
+
+            self.test_file_not_found(file_stat.file_id).await;
+        }
+
+        async fn test_file_not_found(&self, file_id: u64) {
+            let not_found_file = self.fs.stat(file_id).await;
+            assert!(not_found_file.is_err());
+        }
+
+        fn assert_file_stat(&self, file_stat: &FileStat, path: &Path, kind: 
FileType, size: u64) {
+            assert_eq!(file_stat.path, path);
+            assert_eq!(file_stat.kind, kind);
+            assert_eq!(file_stat.size, size);
+            if file_stat.file_id == 1 {
+                assert_eq!(file_stat.parent_file_id, 1);
+            } else {
+                assert!(file_stat.file_id >= INITIAL_FILE_ID);
+                assert!(
+                    file_stat.parent_file_id == 1 || file_stat.parent_file_id 
>= INITIAL_FILE_ID
+                );
+            }
+        }
+    }
 
     #[test]
     fn test_create_file_stat() {
         //test new file
-        let file_stat = FileStat::new_file_filestat("a", "b", 10);
+        let file_stat = FileStat::new_file_filestat(Path::new("a"), 
"b".as_ref(), 10);
         assert_eq!(file_stat.name, "b");
-        assert_eq!(file_stat.path, "a/b");
+        assert_eq!(file_stat.path, Path::new("a/b"));
         assert_eq!(file_stat.size, 10);
         assert_eq!(file_stat.kind, FileType::RegularFile);
 
         //test new dir
-        let file_stat = FileStat::new_dir_filestat("a", "b");
+        let file_stat = FileStat::new_dir_filestat("a".as_ref(), "b".as_ref());
         assert_eq!(file_stat.name, "b");
-        assert_eq!(file_stat.path, "a/b");
+        assert_eq!(file_stat.path, Path::new("a/b"));
         assert_eq!(file_stat.size, 0);
         assert_eq!(file_stat.kind, FileType::Directory);
 
         //test new file with path
-        let file_stat = FileStat::new_file_filestat_with_path("a/b", 10);
+        let file_stat = FileStat::new_file_filestat_with_path("a/b".as_ref(), 
10);
         assert_eq!(file_stat.name, "b");
-        assert_eq!(file_stat.path, "a/b");
+        assert_eq!(file_stat.path, Path::new("a/b"));
         assert_eq!(file_stat.size, 10);
         assert_eq!(file_stat.kind, FileType::RegularFile);
 
         //test new dir with path
-        let file_stat = FileStat::new_dir_filestat_with_path("a/b");
+        let file_stat = FileStat::new_dir_filestat_with_path("a/b".as_ref());
         assert_eq!(file_stat.name, "b");
-        assert_eq!(file_stat.path, "a/b");
+        assert_eq!(file_stat.path, Path::new("a/b"));
         assert_eq!(file_stat.size, 0);
         assert_eq!(file_stat.kind, FileType::Directory);
     }
 
     #[test]
     fn test_file_stat_set_file_id() {
-        let mut file_stat = FileStat::new_file_filestat("a", "b", 10);
+        let mut file_stat = FileStat::new_file_filestat("a".as_ref(), 
"b".as_ref(), 10);
         file_stat.set_file_id(1, 2);
         assert_eq!(file_stat.file_id, 2);
         assert_eq!(file_stat.parent_file_id, 1);
@@ -307,7 +685,7 @@ mod tests {
     #[test]
     #[should_panic(expected = "assertion failed: file_id != 0 && 
parent_file_id != 0")]
     fn test_file_stat_set_file_id_panic() {
-        let mut file_stat = FileStat::new_file_filestat("a", "b", 10);
+        let mut file_stat = FileStat::new_file_filestat("a".as_ref(), 
"b".as_ref(), 10);
         file_stat.set_file_id(1, 0);
     }
 }
diff --git a/clients/filesystem-fuse/src/fuse_api_handle.rs 
b/clients/filesystem-fuse/src/fuse_api_handle.rs
index 7dc5461ce..1f24e94ee 100644
--- a/clients/filesystem-fuse/src/fuse_api_handle.rs
+++ b/clients/filesystem-fuse/src/fuse_api_handle.rs
@@ -95,8 +95,7 @@ impl<T: RawFileSystem> Filesystem for FuseApiHandle<T> {
         parent: Inode,
         name: &OsStr,
     ) -> fuse3::Result<ReplyEntry> {
-        let name = name.to_string_lossy();
-        let file_stat = self.fs.lookup(parent, &name).await?;
+        let file_stat = self.fs.lookup(parent, name).await?;
         Ok(ReplyEntry {
             ttl: self.default_ttl,
             attr: fstat_to_file_attr(&file_stat, &self.fs_context),
@@ -154,8 +153,7 @@ impl<T: RawFileSystem> Filesystem for FuseApiHandle<T> {
         _mode: u32,
         _umask: u32,
     ) -> fuse3::Result<ReplyEntry> {
-        let name = name.to_string_lossy();
-        let handle_id = self.fs.create_dir(parent, &name).await?;
+        let handle_id = self.fs.create_dir(parent, name).await?;
         Ok(ReplyEntry {
             ttl: self.default_ttl,
             attr: dummy_file_attr(
@@ -169,14 +167,12 @@ impl<T: RawFileSystem> Filesystem for FuseApiHandle<T> {
     }
 
     async fn unlink(&self, _req: Request, parent: Inode, name: &OsStr) -> 
fuse3::Result<()> {
-        let name = name.to_string_lossy();
-        self.fs.remove_file(parent, &name).await?;
+        self.fs.remove_file(parent, name).await?;
         Ok(())
     }
 
     async fn rmdir(&self, _req: Request, parent: Inode, name: &OsStr) -> 
fuse3::Result<()> {
-        let name = name.to_string_lossy();
-        self.fs.remove_dir(parent, &name).await?;
+        self.fs.remove_dir(parent, name).await?;
         Ok(())
     }
 
@@ -267,7 +263,7 @@ impl<T: RawFileSystem> Filesystem for FuseApiHandle<T> {
             stream::iter(files.into_iter().enumerate().map(|(index, 
file_stat)| {
                 Ok(DirectoryEntry {
                     inode: file_stat.file_id,
-                    name: file_stat.name.clone().into(),
+                    name: file_stat.name.clone(),
                     kind: file_stat.kind,
                     offset: (index + 3) as i64,
                 })
@@ -313,8 +309,7 @@ impl<T: RawFileSystem> Filesystem for FuseApiHandle<T> {
         _mode: u32,
         flags: u32,
     ) -> fuse3::Result<ReplyCreated> {
-        let name = name.to_string_lossy();
-        let file_handle = self.fs.create_file(parent, &name, flags).await?;
+        let file_handle = self.fs.create_file(parent, name, flags).await?;
         Ok(ReplyCreated {
             ttl: self.default_ttl,
             attr: dummy_file_attr(
@@ -349,7 +344,7 @@ impl<T: RawFileSystem> Filesystem for FuseApiHandle<T> {
             stream::iter(files.into_iter().enumerate().map(|(index, 
file_stat)| {
                 Ok(DirectoryEntryPlus {
                     inode: file_stat.file_id,
-                    name: file_stat.name.clone().into(),
+                    name: file_stat.name.clone(),
                     kind: file_stat.kind,
                     offset: (index + 3) as i64,
                     attr: fstat_to_file_attr(&file_stat, &self.fs_context),
@@ -465,8 +460,8 @@ mod test {
         let file_stat = FileStat {
             file_id: 1,
             parent_file_id: 3,
-            name: "test".to_string(),
-            path: "".to_string(),
+            name: "test".into(),
+            path: "".into(),
             size: 10032,
             kind: FileType::RegularFile,
             atime: Timestamp { sec: 10, nsec: 3 },
diff --git a/clients/filesystem-fuse/src/fuse_server.rs 
b/clients/filesystem-fuse/src/fuse_server.rs
new file mode 100644
index 000000000..dae7c28a6
--- /dev/null
+++ b/clients/filesystem-fuse/src/fuse_server.rs
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+use fuse3::raw::{Filesystem, Session};
+use fuse3::{MountOptions, Result};
+use log::{error, info};
+use std::process::exit;
+use std::sync::Arc;
+use tokio::select;
+use tokio::sync::Notify;
+
+/// Represents a FUSE server capable of starting and stopping the FUSE 
filesystem.
+pub struct FuseServer {
+    // Notification for stop
+    close_notify: Arc<Notify>,
+
+    // Mount point of the FUSE filesystem
+    mount_point: String,
+}
+
+impl FuseServer {
+    /// Creates a new instance of `FuseServer`.
+    pub fn new(mount_point: &str) -> Self {
+        Self {
+            close_notify: Arc::new(Default::default()),
+            mount_point: mount_point.to_string(),
+        }
+    }
+
+    /// Starts the FUSE filesystem and blocks until it is stopped.
+    pub async fn start(&self, fuse_fs: impl Filesystem + Sync + 'static) -> 
Result<()> {
+        //check if the mount point exists
+        if !std::path::Path::new(&self.mount_point).exists() {
+            error!("Mount point {} does not exist", self.mount_point);
+            exit(libc::ENOENT);
+        }
+
+        info!(
+            "Starting FUSE filesystem and mounting at {}",
+            self.mount_point
+        );
+
+        let mount_options = MountOptions::default();
+        let mut mount_handle = Session::new(mount_options)
+            .mount_with_unprivileged(fuse_fs, &self.mount_point)
+            .await?;
+
+        let handle = &mut mount_handle;
+
+        select! {
+            res = handle => {
+                if res.is_err() {
+                    error!("Failed to mount FUSE filesystem: {:?}", res.err());
+                }
+            },
+            _ = self.close_notify.notified() => {
+               if let Err(e) = mount_handle.unmount().await {
+                    error!("Failed to unmount FUSE filesystem: {:?}", e);
+                } else {
+                    info!("FUSE filesystem unmounted successfully.");
+                }
+            }
+        }
+
+        // notify that the filesystem is stopped
+        self.close_notify.notify_one();
+        Ok(())
+    }
+
+    /// Stops the FUSE filesystem.
+    pub async fn stop(&self) {
+        info!("Stopping FUSE filesystem...");
+        self.close_notify.notify_one();
+
+        // wait for the filesystem to stop
+        self.close_notify.notified().await;
+    }
+}
diff --git a/clients/filesystem-fuse/src/lib.rs 
b/clients/filesystem-fuse/src/lib.rs
index c1689bac4..36e8c28d3 100644
--- a/clients/filesystem-fuse/src/lib.rs
+++ b/clients/filesystem-fuse/src/lib.rs
@@ -19,6 +19,17 @@
 mod default_raw_filesystem;
 mod filesystem;
 mod fuse_api_handle;
+mod fuse_server;
+mod memory_filesystem;
+mod mount;
 mod opened_file;
 mod opened_file_manager;
 mod utils;
+
+pub async fn gvfs_mount(mount_point: &str) -> fuse3::Result<()> {
+    mount::mount(mount_point).await
+}
+
+pub async fn gvfs_unmount() {
+    mount::unmount().await;
+}
diff --git a/clients/filesystem-fuse/src/main.rs 
b/clients/filesystem-fuse/src/main.rs
index 3d8e9dbb9..28866a9bb 100644
--- a/clients/filesystem-fuse/src/main.rs
+++ b/clients/filesystem-fuse/src/main.rs
@@ -16,69 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-mod default_raw_filesystem;
-mod filesystem;
-mod fuse_api_handle;
-mod opened_file;
-mod opened_file_manager;
-mod utils;
-
-use log::debug;
+use gvfs_fuse::{gvfs_mount, gvfs_unmount};
 use log::info;
-use std::process::exit;
+use tokio::signal;
 
 #[tokio::main]
-async fn main() {
+async fn main() -> fuse3::Result<()> {
     tracing_subscriber::fmt().init();
-    info!("Starting filesystem...");
-    debug!("Shutdown filesystem...");
-    exit(0);
-}
+    tokio::spawn(async { gvfs_mount("gvfs").await });
 
-async fn create_gvfs_fuse_filesystem() {
-    // Gvfs-fuse filesystem structure:
-    // FuseApiHandle
-    // ├─ DefaultRawFileSystem (RawFileSystem)
-    // │ └─ FileSystemLog (PathFileSystem)
-    // │    ├─ GravitinoComposedFileSystem (PathFileSystem)
-    // │    │  ├─ GravitinoFilesetFileSystem (PathFileSystem)
-    // │    │  │  └─ S3FileSystem (PathFileSystem)
-    // │    │  │     └─ OpenDALFileSystem (PathFileSystem)
-    // │    │  ├─ GravitinoFilesetFileSystem (PathFileSystem)
-    // │    │  │  └─ HDFSFileSystem (PathFileSystem)
-    // │    │  │     └─ OpenDALFileSystem (PathFileSystem)
-    // │    │  ├─ GravitinoFilesetFileSystem (PathFileSystem)
-    // │    │  │  └─ JuiceFileSystem (PathFileSystem)
-    // │    │  │     └─ NasFileSystem (PathFileSystem)
-    // │    │  ├─ GravitinoFilesetFileSystem (PathFileSystem)
-    // │    │  │  └─ XXXFileSystem (PathFileSystem)
-    //
-    // `SimpleFileSystem` is a low-level filesystem designed to communicate 
with FUSE APIs.
-    // It manages file and directory relationships, as well as file mappings.
-    // It delegates file operations to the PathFileSystem
-    //
-    // `FileSystemLog` is a decorator that adds extra debug logging 
functionality to file system APIs.
-    // Similar implementations include permissions, caching, and metrics.
-    //
-    // `GravitinoComposeFileSystem` is a composite file system that can 
combine multiple `GravitinoFilesetFileSystem`.
-    // It use the part of catalog and schema of fileset path to a find actual 
GravitinoFilesetFileSystem. delegate the operation to the real storage.
-    // If the user only mounts a fileset, this layer is not present. There 
will only be one below layer.
-    //
-    // `GravitinoFilesetFileSystem` is a file system that can access a 
fileset.It translates the fileset path to the real storage path.
-    // and delegate the operation to the real storage.
-    //
-    // `OpenDALFileSystem` is a file system that use the OpenDAL to access 
real storage.
-    // it can assess the S3, HDFS, gcs, azblob and other storage.
-    //
-    // `S3FileSystem` is a file system that use `OpenDALFileSystem` to access 
S3 storage.
-    //
-    // `HDFSFileSystem` is a file system that use `OpenDALFileSystem` to 
access HDFS storage.
-    //
-    // `NasFileSystem` is a filesystem that uses a locally accessible path 
mounted by NAS tools, such as JuiceFS.
-    //
-    // `JuiceFileSystem` is a file that use `NasFileSystem` to access JuiceFS 
storage.
-    //
-    // `XXXFileSystem is a filesystem that allows you to implement file access 
through your own extensions.
+    let _ = signal::ctrl_c().await;
+    info!("Received Ctrl+C, Unmounting gvfs...");
+    gvfs_unmount().await;
 
-    todo!("Implement the createGvfsFuseFileSystem function");
+    Ok(())
 }
diff --git a/clients/filesystem-fuse/src/memory_filesystem.rs 
b/clients/filesystem-fuse/src/memory_filesystem.rs
new file mode 100644
index 000000000..ca3f13fd9
--- /dev/null
+++ b/clients/filesystem-fuse/src/memory_filesystem.rs
@@ -0,0 +1,281 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+use crate::filesystem::{FileReader, FileStat, FileWriter, PathFileSystem, 
Result};
+use crate::opened_file::{OpenFileFlags, OpenedFile};
+use async_trait::async_trait;
+use bytes::Bytes;
+use fuse3::FileType::{Directory, RegularFile};
+use fuse3::{Errno, FileType};
+use std::collections::BTreeMap;
+use std::path::{Path, PathBuf};
+use std::sync::{Arc, Mutex, RwLock};
+
+// Simple in-memory file implementation of MemoryFileSystem
+struct MemoryFile {
+    kind: FileType,
+    data: Arc<Mutex<Vec<u8>>>,
+}
+
+// MemoryFileSystem is a simple in-memory filesystem implementation
+// It is used for testing purposes
+pub struct MemoryFileSystem {
+    // file_map is a map of file name to file size
+    file_map: RwLock<BTreeMap<PathBuf, MemoryFile>>,
+}
+
+impl MemoryFileSystem {
+    const FS_META_FILE_NAME: &'static str = "/.gvfs_meta";
+
+    pub(crate) async fn new() -> Self {
+        Self {
+            file_map: RwLock::new(Default::default()),
+        }
+    }
+
+    fn create_file_stat(&self, path: &Path, file: &MemoryFile) -> FileStat {
+        match file.kind {
+            Directory => FileStat::new_dir_filestat_with_path(path),
+            _ => {
+                FileStat::new_file_filestat_with_path(path, 
file.data.lock().unwrap().len() as u64)
+            }
+        }
+    }
+}
+
+#[async_trait]
+impl PathFileSystem for MemoryFileSystem {
+    async fn init(&self) -> Result<()> {
+        let root_file = MemoryFile {
+            kind: Directory,
+            data: Arc::new(Mutex::new(Vec::new())),
+        };
+        let root_path = PathBuf::from("/");
+        self.file_map.write().unwrap().insert(root_path, root_file);
+
+        let meta_file = MemoryFile {
+            kind: RegularFile,
+            data: Arc::new(Mutex::new(Vec::new())),
+        };
+        let meta_file_path = Path::new(Self::FS_META_FILE_NAME).to_path_buf();
+        self.file_map
+            .write()
+            .unwrap()
+            .insert(meta_file_path, meta_file);
+        Ok(())
+    }
+
+    async fn stat(&self, path: &Path) -> Result<FileStat> {
+        self.file_map
+            .read()
+            .unwrap()
+            .get(path)
+            .map(|x| self.create_file_stat(path, x))
+            .ok_or(Errno::from(libc::ENOENT))
+    }
+
+    async fn read_dir(&self, path: &Path) -> Result<Vec<FileStat>> {
+        let file_map = self.file_map.read().unwrap();
+
+        let results: Vec<FileStat> = file_map
+            .iter()
+            .filter(|x| path_in_dir(path, x.0))
+            .map(|(k, v)| self.create_file_stat(k, v))
+            .collect();
+
+        Ok(results)
+    }
+
+    async fn open_file(&self, path: &Path, _flags: OpenFileFlags) -> 
Result<OpenedFile> {
+        let file_stat = self.stat(path).await?;
+        let mut opened_file = OpenedFile::new(file_stat);
+        match opened_file.file_stat.kind {
+            Directory => Ok(opened_file),
+            RegularFile => {
+                let data = self
+                    .file_map
+                    .read()
+                    .unwrap()
+                    .get(&opened_file.file_stat.path)
+                    .unwrap()
+                    .data
+                    .clone();
+                opened_file.reader = Some(Box::new(MemoryFileReader { data: 
data.clone() }));
+                opened_file.writer = Some(Box::new(MemoryFileWriter { data: 
data }));
+                Ok(opened_file)
+            }
+            _ => Err(Errno::from(libc::EBADF)),
+        }
+    }
+
+    async fn open_dir(&self, path: &Path, flags: OpenFileFlags) -> 
Result<OpenedFile> {
+        self.open_file(path, flags).await
+    }
+
+    async fn create_file(&self, path: &Path, _flags: OpenFileFlags) -> 
Result<OpenedFile> {
+        let mut file_map = self.file_map.write().unwrap();
+        if file_map.contains_key(path) {
+            return Err(Errno::from(libc::EEXIST));
+        }
+
+        let mut opened_file = 
OpenedFile::new(FileStat::new_file_filestat_with_path(path, 0));
+
+        let data = Arc::new(Mutex::new(Vec::new()));
+        file_map.insert(
+            opened_file.file_stat.path.clone(),
+            MemoryFile {
+                kind: RegularFile,
+                data: data.clone(),
+            },
+        );
+
+        opened_file.reader = Some(Box::new(MemoryFileReader { data: 
data.clone() }));
+        opened_file.writer = Some(Box::new(MemoryFileWriter { data: data }));
+
+        Ok(opened_file)
+    }
+
+    async fn create_dir(&self, path: &Path) -> Result<FileStat> {
+        let mut file_map = self.file_map.write().unwrap();
+        if file_map.contains_key(path) {
+            return Err(Errno::from(libc::EEXIST));
+        }
+
+        let file = FileStat::new_dir_filestat_with_path(path);
+        file_map.insert(
+            file.path.clone(),
+            MemoryFile {
+                kind: Directory,
+                data: Arc::new(Mutex::new(Vec::new())),
+            },
+        );
+
+        Ok(file)
+    }
+
+    async fn set_attr(&self, _name: &Path, _file_stat: &FileStat, _flush: 
bool) -> Result<()> {
+        Ok(())
+    }
+
+    async fn remove_file(&self, path: &Path) -> Result<()> {
+        let mut file_map = self.file_map.write().unwrap();
+        if file_map.remove(path).is_none() {
+            return Err(Errno::from(libc::ENOENT));
+        }
+        Ok(())
+    }
+
+    async fn remove_dir(&self, path: &Path) -> Result<()> {
+        let mut file_map = self.file_map.write().unwrap();
+        let count = file_map.iter().filter(|x| path_in_dir(path, x.0)).count();
+
+        if count != 0 {
+            return Err(Errno::from(libc::ENOTEMPTY));
+        }
+
+        if file_map.remove(path).is_none() {
+            return Err(Errno::from(libc::ENOENT));
+        }
+        Ok(())
+    }
+}
+
+pub(crate) struct MemoryFileReader {
+    pub(crate) data: Arc<Mutex<Vec<u8>>>,
+}
+
+#[async_trait]
+impl FileReader for MemoryFileReader {
+    async fn read(&mut self, offset: u64, size: u32) -> Result<Bytes> {
+        let v = self.data.lock().unwrap();
+        let start = offset as usize;
+        let end = usize::min(start + size as usize, v.len());
+        if start >= v.len() {
+            return Ok(Bytes::default());
+        }
+        Ok(v[start..end].to_vec().into())
+    }
+}
+
+pub(crate) struct MemoryFileWriter {
+    pub(crate) data: Arc<Mutex<Vec<u8>>>,
+}
+
+#[async_trait]
+impl FileWriter for MemoryFileWriter {
+    async fn write(&mut self, offset: u64, data: &[u8]) -> Result<u32> {
+        let mut v = self.data.lock().unwrap();
+        let start = offset as usize;
+        let end = start + data.len();
+
+        if v.len() < end {
+            v.resize(end, 0);
+        }
+        v[start..end].copy_from_slice(data);
+        Ok(data.len() as u32)
+    }
+}
+
+fn path_in_dir(dir: &Path, path: &Path) -> bool {
+    if let Ok(relative_path) = path.strip_prefix(dir) {
+        relative_path.components().count() == 1
+    } else {
+        false
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::filesystem::tests::TestPathFileSystem;
+
+    #[test]
+    fn test_path_in_dir() {
+        let dir = Path::new("/parent");
+
+        let path1 = Path::new("/parent/child1");
+        let path2 = Path::new("/parent/a.txt");
+        let path3 = Path::new("/parent/child1/grandchild");
+        let path4 = Path::new("/other");
+
+        assert!(!path_in_dir(dir, dir));
+        assert!(path_in_dir(dir, path1));
+        assert!(path_in_dir(dir, path2));
+        assert!(!path_in_dir(dir, path3));
+        assert!(!path_in_dir(dir, path4));
+
+        let dir = Path::new("/");
+
+        let path1 = Path::new("/child1");
+        let path2 = Path::new("/a.txt");
+        let path3 = Path::new("/child1/grandchild");
+
+        assert!(!path_in_dir(dir, dir));
+        assert!(path_in_dir(dir, path1));
+        assert!(path_in_dir(dir, path2));
+        assert!(!path_in_dir(dir, path3));
+    }
+
+    #[tokio::test]
+    async fn test_memory_file_system() {
+        let fs = MemoryFileSystem::new().await;
+        let _ = fs.init().await;
+        let mut tester = TestPathFileSystem::new(fs);
+        tester.test_path_file_system().await;
+    }
+}
diff --git a/clients/filesystem-fuse/src/main.rs 
b/clients/filesystem-fuse/src/mount.rs
similarity index 69%
copy from clients/filesystem-fuse/src/main.rs
copy to clients/filesystem-fuse/src/mount.rs
index 3d8e9dbb9..102e24016 100644
--- a/clients/filesystem-fuse/src/main.rs
+++ b/clients/filesystem-fuse/src/mount.rs
@@ -16,26 +16,60 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-mod default_raw_filesystem;
-mod filesystem;
-mod fuse_api_handle;
-mod opened_file;
-mod opened_file_manager;
-mod utils;
-
-use log::debug;
+use crate::default_raw_filesystem::DefaultRawFileSystem;
+use crate::filesystem::FileSystemContext;
+use crate::fuse_api_handle::FuseApiHandle;
+use crate::fuse_server::FuseServer;
+use crate::memory_filesystem::MemoryFileSystem;
+use fuse3::raw::Filesystem;
 use log::info;
-use std::process::exit;
+use once_cell::sync::Lazy;
+use std::sync::Arc;
+use tokio::sync::Mutex;
+
+static SERVER: Lazy<Mutex<Option<Arc<FuseServer>>>> = Lazy::new(|| 
Mutex::new(None));
+
+pub async fn mount(mount_point: &str) -> fuse3::Result<()> {
+    info!("Starting gvfs-fuse server...");
+    let svr = Arc::new(FuseServer::new(mount_point));
+    {
+        let mut server = SERVER.lock().await;
+        *server = Some(svr.clone());
+    }
+    let fs = create_fuse_fs().await;
+    svr.start(fs).await
+}
+
+pub async fn unmount() {
+    info!("Stop gvfs-fuse server...");
+    let svr = {
+        let mut server = SERVER.lock().await;
+        if server.is_none() {
+            info!("Server is already stopped.");
+            return;
+        }
+        server.take().unwrap()
+    };
+    let _ = svr.stop().await;
+}
+
+pub async fn create_fuse_fs() -> impl Filesystem + Sync + 'static {
+    let uid = unsafe { libc::getuid() };
+    let gid = unsafe { libc::getgid() };
+    let fs_context = FileSystemContext {
+        uid: uid,
+        gid: gid,
+        default_file_perm: 0o644,
+        default_dir_perm: 0o755,
+        block_size: 4 * 1024,
+    };
 
-#[tokio::main]
-async fn main() {
-    tracing_subscriber::fmt().init();
-    info!("Starting filesystem...");
-    debug!("Shutdown filesystem...");
-    exit(0);
+    let gvfs = MemoryFileSystem::new().await;
+    let fs = DefaultRawFileSystem::new(gvfs);
+    FuseApiHandle::new(fs, fs_context)
 }
 
-async fn create_gvfs_fuse_filesystem() {
+pub async fn create_gvfs_filesystem() {
     // Gvfs-fuse filesystem structure:
     // FuseApiHandle
     // ├─ DefaultRawFileSystem (RawFileSystem)
diff --git a/clients/filesystem-fuse/src/opened_file.rs 
b/clients/filesystem-fuse/src/opened_file.rs
index ba3e41595..5bc961c9a 100644
--- a/clients/filesystem-fuse/src/opened_file.rs
+++ b/clients/filesystem-fuse/src/opened_file.rs
@@ -126,10 +126,15 @@ pub(crate) struct OpenFileFlags(pub(crate) u32);
 mod tests {
     use super::*;
     use crate::filesystem::FileStat;
+    use std::path::Path;
 
     #[test]
     fn test_open_file() {
-        let mut open_file = OpenedFile::new(FileStat::new_file_filestat("a", 
"b", 10));
+        let mut open_file = OpenedFile::new(FileStat::new_file_filestat(
+            Path::new("a"),
+            "b".as_ref(),
+            10,
+        ));
         assert_eq!(open_file.file_stat.name, "b");
         assert_eq!(open_file.file_stat.size, 10);
 
diff --git a/clients/filesystem-fuse/src/opened_file_manager.rs 
b/clients/filesystem-fuse/src/opened_file_manager.rs
index 17bfe00a3..ab6a5d823 100644
--- a/clients/filesystem-fuse/src/opened_file_manager.rs
+++ b/clients/filesystem-fuse/src/opened_file_manager.rs
@@ -69,13 +69,14 @@ impl OpenedFileManager {
 mod tests {
     use super::*;
     use crate::filesystem::FileStat;
+    use std::path::Path;
 
     #[tokio::test]
     async fn test_opened_file_manager() {
         let manager = OpenedFileManager::new();
 
-        let file1_stat = FileStat::new_file_filestat("", "a.txt", 13);
-        let file2_stat = FileStat::new_file_filestat("", "b.txt", 18);
+        let file1_stat = FileStat::new_file_filestat(Path::new(""), 
"a.txt".as_ref(), 13);
+        let file2_stat = FileStat::new_file_filestat(Path::new(""), 
"b.txt".as_ref(), 18);
 
         let file1 = OpenedFile::new(file1_stat.clone());
         let file2 = OpenedFile::new(file2_stat.clone());
diff --git a/clients/filesystem-fuse/src/utils.rs 
b/clients/filesystem-fuse/src/utils.rs
index 0c0cc80a1..21e52f86a 100644
--- a/clients/filesystem-fuse/src/utils.rs
+++ b/clients/filesystem-fuse/src/utils.rs
@@ -16,52 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-use crate::filesystem::RawFileSystem;
-
-// join the parent and name to a path
-pub fn join_file_path(parent: &str, name: &str) -> String {
-    //TODO handle corner cases
-    if parent.is_empty() {
-        name.to_string()
-    } else {
-        format!("{}/{}", parent, name)
-    }
-}
-
-// split the path to parent and name
-pub fn split_file_path(path: &str) -> (&str, &str) {
-    match path.rfind('/') {
-        Some(pos) => (&path[..pos], &path[pos + 1..]),
-        None => ("", path),
-    }
-}
-
-// convert file id to file path string if file id is invalid return "Unknown"
-pub async fn file_id_to_file_path_string(file_id: u64, fs: &impl 
RawFileSystem) -> String {
-    fs.get_file_path(file_id)
-        .await
-        .unwrap_or("Unknown".to_string())
-}
 
 #[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_join_file_path() {
-        assert_eq!(join_file_path("", "a"), "a");
-        assert_eq!(join_file_path("", "a.txt"), "a.txt");
-        assert_eq!(join_file_path("a", "b"), "a/b");
-        assert_eq!(join_file_path("a/b", "c"), "a/b/c");
-        assert_eq!(join_file_path("a/b", "c.txt"), "a/b/c.txt");
-    }
-
-    #[test]
-    fn test_split_file_path() {
-        assert_eq!(split_file_path("a"), ("", "a"));
-        assert_eq!(split_file_path("a.txt"), ("", "a.txt"));
-        assert_eq!(split_file_path("a/b"), ("a", "b"));
-        assert_eq!(split_file_path("a/b/c"), ("a/b", "c"));
-        assert_eq!(split_file_path("a/b/c.txt"), ("a/b", "c.txt"));
-    }
-}
+mod tests {}
diff --git a/clients/filesystem-fuse/tests/fuse_test.rs 
b/clients/filesystem-fuse/tests/fuse_test.rs
new file mode 100644
index 000000000..23aafbaf6
--- /dev/null
+++ b/clients/filesystem-fuse/tests/fuse_test.rs
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use gvfs_fuse::{gvfs_mount, gvfs_unmount};
+use log::info;
+use std::fs;
+use std::fs::File;
+use std::path::Path;
+use std::sync::Arc;
+use std::thread::sleep;
+use std::time::{Duration, Instant};
+use tokio::runtime::Runtime;
+use tokio::task::JoinHandle;
+
+struct FuseTest {
+    runtime: Arc<Runtime>,
+    mount_point: String,
+    gvfs_mount: Option<JoinHandle<fuse3::Result<()>>>,
+}
+
+impl FuseTest {
+    pub fn setup(&mut self) {
+        info!("Start gvfs fuse server");
+        let mount_point = self.mount_point.clone();
+        self.runtime
+            .spawn(async move { gvfs_mount(&mount_point).await });
+        let success = Self::wait_for_fuse_server_ready(&self.mount_point, 
Duration::from_secs(15));
+        assert!(success, "Fuse server cannot start up at 15 seconds");
+    }
+
+    pub fn shutdown(&mut self) {
+        self.runtime.block_on(async {
+            gvfs_unmount().await;
+        });
+    }
+
+    fn wait_for_fuse_server_ready(path: &str, timeout: Duration) -> bool {
+        let test_file = format!("{}/.gvfs_meta", path);
+        let start_time = Instant::now();
+
+        while start_time.elapsed() < timeout {
+            if file_exists(&test_file) {
+                return true;
+            }
+            info!("Wait for fuse server ready",);
+            sleep(Duration::from_secs(1));
+        }
+        false
+    }
+}
+
+impl Drop for FuseTest {
+    fn drop(&mut self) {
+        info!("Shutdown fuse server");
+        self.shutdown();
+    }
+}
+
+#[test]
+fn test_fuse_system_with_auto() {
+    tracing_subscriber::fmt().init();
+
+    let mount_point = "build/gvfs";
+    let _ = fs::create_dir_all(mount_point);
+
+    let mut test = FuseTest {
+        runtime: Arc::new(Runtime::new().unwrap()),
+        mount_point: mount_point.to_string(),
+        gvfs_mount: None,
+    };
+
+    test.setup();
+    test_fuse_filesystem(mount_point);
+}
+
+fn test_fuse_system_with_manual() {
+    test_fuse_filesystem("build/gvfs");
+}
+
+fn test_fuse_filesystem(mount_point: &str) {
+    info!("Test startup");
+    let base_path = Path::new(mount_point);
+
+    //test create file
+    let test_file = base_path.join("test_create");
+    let file = File::create(&test_file).expect("Failed to create file");
+    assert!(file.metadata().is_ok(), "Failed to get file metadata");
+    assert!(file_exists(&test_file));
+
+    //test write file
+    fs::write(&test_file, "read test").expect("Failed to write file");
+
+    //test read file
+    let content = fs::read_to_string(test_file.clone()).expect("Failed to read 
file");
+    assert_eq!(content, "read test", "File content mismatch");
+
+    //test delete file
+    fs::remove_file(test_file.clone()).expect("Failed to delete file");
+    assert!(!file_exists(test_file));
+
+    //test create directory
+    let test_dir = base_path.join("test_dir");
+    fs::create_dir(&test_dir).expect("Failed to create directory");
+
+    //test create file in directory
+    let test_file = base_path.join("test_dir/test_file");
+    let file = File::create(&test_file).expect("Failed to create file");
+    assert!(file.metadata().is_ok(), "Failed to get file metadata");
+
+    //test write file in directory
+    let test_file = base_path.join("test_dir/test_read");
+    fs::write(&test_file, "read test").expect("Failed to write file");
+
+    //test read file in directory
+    let content = fs::read_to_string(&test_file).expect("Failed to read file");
+    assert_eq!(content, "read test", "File content mismatch");
+
+    //test delete file in directory
+    fs::remove_file(&test_file).expect("Failed to delete file");
+    assert!(!file_exists(&test_file));
+
+    //test delete directory
+    fs::remove_dir_all(&test_dir).expect("Failed to delete directory");
+    assert!(!file_exists(&test_dir));
+
+    info!("Success test");
+}
+
+fn file_exists<P: AsRef<Path>>(path: P) -> bool {
+    fs::metadata(path).is_ok()
+}
diff --git a/clients/filesystem-fuse/tests/it.rs 
b/clients/filesystem-fuse/tests/it.rs
deleted file mode 100644
index 989e5f989..000000000
--- a/clients/filesystem-fuse/tests/it.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#[test]
-fn test_math_add() {
-    assert_eq!(1, 1);
-}

Reply via email to