aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2021-10-09 00:42:28 +0200
committerMel <einebeere@gmail.com>2021-10-09 00:42:28 +0200
commitdbe8c3b6cd10d2cd960ae3cd346e86a6fb1b7c68 (patch)
treef143fbb49f65a15fb0c9f8233476837142b034a4 /src
parentfad317349557bee1055d81d7444f99619247815c (diff)
downloadka-dbe8c3b6cd10d2cd960ae3cd346e86a6fb1b7c68.tar.zst
ka-dbe8c3b6cd10d2cd960ae3cd346e86a6fb1b7c68.zip
Re-implement FsMock as a fake in-memory filesystem.
Diffstat (limited to 'src')
-rw-r--r--src/actions/create.rs110
-rw-r--r--src/filesystem.rs622
2 files changed, 298 insertions, 434 deletions
diff --git a/src/actions/create.rs b/src/actions/create.rs
index 184e249..bc7f7b0 100644
--- a/src/actions/create.rs
+++ b/src/actions/create.rs
@@ -21,113 +21,3 @@ pub fn create(command_options: ActionOptions, fs: &impl Fs, timestamp: u64) -> R
Ok(())
}
-
-#[cfg(test)]
-mod tests {
- use std::{path::Path, vec};
-
- use crate::{
- actions::{create, ActionOptions},
- diff::ContentChange,
- filesystem::mock::{EntryMock, ExpectedCall as Call, ExpectedCallVariant as Type, FsMock},
- history::{
- FileChange, FileChangeVariant, FileHistory, RepositoryChange, RepositoryHistory,
- },
- };
-
- #[test]
- fn create_empty() {
- let now = 0xC0FFEE;
- let fs_mock = FsMock::new();
- let options = ActionOptions::from_path(".");
-
- let pwd = Path::new(".");
- let working_file = Path::new("./test");
- let history_file = Path::new("./.ka/files/test");
-
- let ka = Path::new("./.ka");
- let ka_files = Path::new("./.ka/files");
- let ka_index = Path::new("./.ka/index");
-
- let empty_index = RepositoryHistory::default().encode().unwrap();
- let expected_index = {
- let mut history = RepositoryHistory::default();
- history.add_change(RepositoryChange {
- affected_files: vec![working_file.to_path_buf()],
- timestamp: now,
- });
- history.cursor = 1;
- history.encode().unwrap()
- };
-
- let expected_file_history = {
- let mut history = FileHistory::default();
- let change = ContentChange::Inserted {
- at: 0,
- new_content: vec![1, 2, 3],
- };
- history.add_change(FileChange {
- change_index: 1,
- variant: FileChangeVariant::Updated(vec![change]),
- });
- history.encode().unwrap()
- };
-
- fs_mock.set_expected_calls(vec![
- // Create calls
- Call::new(ka, Type::PathExists(false)),
- Call::new(ka, Type::CreateDirectory),
- Call::new(ka_files, Type::CreateDirectory),
- Call::new(ka_index, Type::CreateFile),
- Call::new(
- ka_index,
- Type::WriteToFile {
- expected: empty_index.clone(),
- },
- ),
- // Update calls
- Call::new(ka_index, Type::OpenWritableFile),
- Call::new(
- ka_index,
- Type::ReadFile {
- returned: empty_index,
- },
- ),
- Call::new(
- pwd,
- Type::ReadDirectory {
- returned: vec![
- EntryMock::new(working_file, false),
- EntryMock::new(ka, true),
- ],
- },
- ),
- Call::new(ka_files, Type::ReadDirectory { returned: vec![] }),
- Call::new(history_file, Type::PathExists(false)),
- Call::new(working_file, Type::OpenReadableFile),
- Call::new(
- working_file,
- Type::ReadFile {
- returned: vec![1, 2, 3],
- },
- ),
- Call::new(history_file, Type::CreateFile),
- Call::new(
- history_file,
- Type::WriteToFile {
- expected: expected_file_history,
- },
- ),
- Call::new(
- ka_index,
- Type::WriteToFile {
- expected: expected_index,
- },
- ),
- ]);
-
- create(options, &fs_mock, now).expect("Action failed.");
-
- fs_mock.assert_calls();
- }
-}
diff --git a/src/filesystem.rs b/src/filesystem.rs
index f1f3af8..e1ccd2d 100644
--- a/src/filesystem.rs
+++ b/src/filesystem.rs
@@ -121,34 +121,23 @@ impl FsEntry for DirEntry {
#[allow(dead_code)]
#[cfg(test)]
pub mod mock {
- use anyhow::Result;
+ use anyhow::{anyhow, Result};
use std::{
+ collections::{hash_map, HashMap},
path::{Path, PathBuf},
- sync::{Arc, Mutex},
+ sync::{Arc, Mutex, MutexGuard},
};
use super::{Fs, FsEntry};
- // TODO: This testing style is very imperative as we have to consider every single
- // call that happens in an action. (See actions::create::tests)
- // Could we instead emulate a fake in-memory file system for FsMock, requiring only
- // an input state and an output state, with no knowledge what happens in between.
- // That would greatly simplify making tests.
-
pub struct FsMock {
- state: Arc<Mutex<FsMockState>>,
- }
-
- struct FsMockState {
- expected_calls: Vec<ExpectedCall>,
- received_calls: Vec<ReceivedCall>,
+ state: Arc<Mutex<FsState>>,
}
impl FsMock {
pub fn new() -> Self {
- let state = FsMockState {
- expected_calls: Vec::new(),
- received_calls: Vec::new(),
+ let state = FsState {
+ entries: HashMap::new(),
};
FsMock {
@@ -156,393 +145,378 @@ pub mod mock {
}
}
- pub fn set_expected_calls(&self, calls: Vec<ExpectedCall>) {
- let mut state = self.state.lock().expect("File system mock lock poisoned.");
- state.expected_calls = calls;
- }
-
- pub fn assert_calls(self) {
- let state = self.state.lock().expect("File system lock poisoned.");
-
- let longest_call_amount = state.received_calls.len().max(state.expected_calls.len());
-
- for call_index in 0..longest_call_amount {
- let expected_option = state.expected_calls.get(call_index);
- let received_option = state.received_calls.get(call_index);
-
- let expected_call = expected_option.unwrap_or_else(|| {
- panic!(
- "Received unexpected call: '{:?}'.",
- received_option.unwrap()
- )
- });
- let received_call = received_option.unwrap_or_else(|| {
- panic!(
- "Expected call: '{:?}', which was not received.",
- expected_option.unwrap()
- )
- });
-
- expected_call.assert_received(received_call);
- }
+ pub fn set_state(&mut self, new_state: FsState) {
+ let mut state = self.state.lock().expect("FsMock state lock poisoned.");
+ *state = new_state;
}
- fn add_call(&self, call: ReceivedCall) {
- let mut state = self.state.lock().expect("File system mock lock poisoned.");
- state.received_calls.push(call);
+ pub fn assert_match(&self, _expected_state: FsState) {
+ todo!()
}
- fn get_expected_call(&self) -> Option<ExpectedCallVariant> {
- let state = self.state.lock().expect("File system lock poisoned.");
-
- state
- .expected_calls
- .get(state.received_calls.len())
- .map(|e| e.variant.clone())
+ fn state(&self) -> MutexGuard<FsState> {
+ self.state.lock().expect("FsMock state lock poisoned.")
}
}
- impl Fs for FsMock {
+ impl<'fs> Fs for FsMock {
type File = FileMock;
+
type Entry = EntryMock;
fn create_file(&self, path: &Path) -> Result<Self::File> {
- let call = ReceivedCall::new(path, ReceivedCallVariant::FileCreated);
- self.add_call(call);
-
- Ok(FileMock {
- path: path.to_path_buf(),
- writable: true,
- })
+ let mut state = self.state();
+ if let Some(file) = state.get_or_create_file(path) {
+ Ok(file)
+ } else {
+ if state.is_directory(path) {
+ Err(anyhow!(
+ "The file '{}' can't be opened or created, because it is a directory.",
+ path.display()
+ ))
+ } else {
+ Err(anyhow!(
+ "The file '{}' can't be opened or created, because one of it's parent paths which have to be created is occupied.",
+ path.display()
+ ))
+ }
+ }
}
fn delete_file(&self, path: &Path) -> Result<()> {
- let call = ReceivedCall::new(path, ReceivedCallVariant::FileDeleted);
- self.add_call(call);
-
- Ok(())
+ let mut state = self.state();
+ if state.delete_if_file(path) {
+ Ok(())
+ } else {
+ if state.is_directory(path) {
+ Err(anyhow!(
+ "The file '{}' can't be deleted because it is a directory.",
+ path.display()
+ ))
+ } else {
+ Err(anyhow!(
+ "The file '{}' can't be deleted because it doesn't exist.",
+ path.display()
+ ))
+ }
+ }
}
fn open_readable_file(&self, path: &Path) -> Result<Self::File> {
- let call = ReceivedCall::new(path, ReceivedCallVariant::ReadableFileOpened);
- self.add_call(call);
-
- Ok(FileMock {
- path: path.to_path_buf(),
- writable: false,
- })
+ let state = self.state();
+ if let Some(file) = state.get_file_for_reading(path) {
+ Ok(file)
+ } else {
+ if state.is_directory(path) {
+ Err(anyhow!(
+ "The file '{}' can't be opened for reading because it is a directory.",
+ path.display()
+ ))
+ } else {
+ Err(anyhow!(
+ "The file '{}' can't be opened for reading because it doesn't exist.",
+ path.display()
+ ))
+ }
+ }
}
fn open_writable_file(&self, path: &Path) -> Result<Self::File> {
- let call = ReceivedCall::new(path, ReceivedCallVariant::WritableFileOpened);
- self.add_call(call);
-
- Ok(FileMock {
- path: path.to_path_buf(),
- writable: true,
- })
+ let state = self.state();
+ if let Some(file) = state.get_file(path) {
+ Ok(file)
+ } else {
+ if state.is_directory(path) {
+ Err(anyhow!("The file '{}' can't be opened for reading and writing because it is a directory.", path.display()))
+ } else {
+ Err(anyhow!("The file '{}' can't be opened for reading and writing because it doesn't exist.", path.display()))
+ }
+ }
}
fn create_directory(&self, path: &Path) -> Result<()> {
- let call = ReceivedCall::new(path, ReceivedCallVariant::DirectoryCreated);
- self.add_call(call);
-
- Ok(())
+ let mut state = self.state();
+ if state.create_directory(path) {
+ Ok(())
+ } else {
+ if state.is_directory(path) {
+ Err(anyhow!(
+ "The directory '{}' can't be created because it already exists.",
+ path.display()
+ ))
+ } else if state.is_file(path) {
+ Err(anyhow!("The directory '{}' can't be created because there is a file with the same path.", path.display()))
+ } else {
+ Err(anyhow!(
+ "The directory '{}' can't be opened or created, because one of it's parent paths which have to be created is occupied.",
+ path.display()
+ ))
+ }
+ }
}
fn read_directory(&self, path: &Path) -> Result<Vec<Self::Entry>> {
- let call = ReceivedCall::new(path, ReceivedCallVariant::DirectoryRead);
-
- let return_value = if let Some(expected_call) = self.get_expected_call() {
- if let ExpectedCallVariant::ReadDirectory { returned } = expected_call {
- Ok(returned.to_vec())
+ let state = self.state();
+ if let Some(entries) = state.get_entries_if_directory(path) {
+ Ok(entries)
+ } else {
+ if state.is_file(path) {
+ Err(anyhow!(
+ "The directory '{}' can't be read because it is a file.",
+ path.display()
+ ))
} else {
- panic!(
- "Received unexpected call: {:?}, expected call: {:?}.",
- call, expected_call
- );
+ Err(anyhow!(
+ "The directory '{}' can't be read because it doesn't exist.",
+ path.display()
+ ))
}
- } else {
- panic!(
- "Received unexpected call, with no expected call to compare it to: {:?}.",
- call
- );
- };
-
- self.add_call(call);
-
- return_value
+ }
}
fn delete_directory(&self, path: &Path) -> Result<()> {
- let call = ReceivedCall::new(path, ReceivedCallVariant::DirectoryDeleted);
- self.add_call(call);
-
- Ok(())
+ let mut state = self.state();
+ if state.delete_if_directory(path) {
+ Ok(())
+ } else {
+ if state.is_file(path) {
+ Err(anyhow!(
+ "The directory '{}' can't be deleted because it is a file.",
+ path.display()
+ ))
+ } else {
+ Err(anyhow!(
+ "The directory '{}' can't be deleted because it doesn't exist.",
+ path.display()
+ ))
+ }
+ }
}
fn write_to_file(&self, file: &mut Self::File, buffer: Vec<u8>) -> Result<()> {
- let call = ReceivedCall::new(
- &file.path,
- ReceivedCallVariant::FileWritten { received: buffer },
- );
- self.add_call(call);
-
- // TODO: Check whether file was opened with write flag.
-
- Ok(())
- }
-
- fn read_from_file(&self, file: &mut Self::File) -> Result<Vec<u8>> {
- let call = ReceivedCall::new(&file.path, ReceivedCallVariant::FileRead);
-
- let return_value = if let Some(expected_call) = self.get_expected_call() {
- if let ExpectedCallVariant::ReadFile { returned } = expected_call {
- Ok(returned.clone())
+ let mut state = self.state();
+ if file.writable {
+ if state.write_to_if_file(&file.path, buffer) {
+ Ok(())
} else {
- panic!(
- "Received unexpected call: {:?}, expected call: {:?}.",
- call, expected_call
- );
+ if state.is_directory(&file.path) {
+ Err(anyhow!(
+ "The file '{}' can't be written to because it is a directory.",
+ file.path.display()
+ ))
+ } else {
+ Err(anyhow!(
+ "The file '{}' can't be written to because it doesn't exist.",
+ file.path.display()
+ ))
+ }
}
} else {
- panic!(
- "Received unexpected call, with no expected call to compare it to: {:?}.",
- call
- );
- };
-
- self.add_call(call);
-
- return_value
+ Err(anyhow!(
+ "The file '{}' is not writable.",
+ file.path.display()
+ ))
+ }
}
- fn path_exists(&self, path: &Path) -> bool {
- let call = ReceivedCall::new(path, ReceivedCallVariant::DoesPathExist);
-
- let return_value = if let Some(expected_call) = self.get_expected_call() {
- if let ExpectedCallVariant::PathExists(answer) = expected_call {
- answer
+ fn read_from_file(&self, file: &mut Self::File) -> Result<Vec<u8>> {
+ let state = self.state();
+ if let Some(content) = state.get_content_if_file(&file.path) {
+ Ok(content)
+ } else {
+ if state.is_directory(&file.path) {
+ Err(anyhow!(
+ "The file '{}' can't be read from because it is a directory.",
+ file.path.display()
+ ))
} else {
- panic!(
- "Received unexpected call: {:?}, expected call: {:?}.",
- call, expected_call
- );
+ Err(anyhow!(
+ "The file '{}' can't be read from because it doesn't exist.",
+ file.path.display()
+ ))
}
- } else {
- panic!(
- "Received unexpected call, with no expected call to compare it to: {:?}.",
- call
- );
- };
-
- self.add_call(call);
+ }
+ }
- return_value
+ fn path_exists(&self, path: &Path) -> bool {
+ self.state().exists(path)
}
}
- #[derive(Debug, Clone)]
- pub struct EntryMock {
- path: PathBuf,
- is_directory: bool,
+ pub struct FsState {
+ entries: HashMap<PathBuf, EntryMock>,
}
- impl EntryMock {
- pub fn new(path: &Path, is_directory: bool) -> Self {
- EntryMock {
- path: path.to_path_buf(),
- is_directory,
+ impl FsState {
+ fn get_or_create_file(&mut self, path: &Path) -> Option<FileMock> {
+ if let Some(parent) = path.parent() {
+ if !self.is_directory(parent) && !self.create_directory(path) {
+ return None;
+ }
}
- }
- }
- impl FsEntry for EntryMock {
- fn path(&self) -> PathBuf {
- self.path.clone()
+ let path_buf = path.to_path_buf();
+ match self.entries.entry(path_buf.clone()) {
+ hash_map::Entry::Occupied(occupied) => match occupied.get() {
+ EntryMock::File(file) => Some(file.clone()),
+ _ => None,
+ },
+ hash_map::Entry::Vacant(vacant) => {
+ let file = FileMock {
+ path: path_buf,
+ writable: true,
+ content: Vec::new(),
+ };
+ vacant.insert(EntryMock::File(file.clone()));
+ Some(file)
+ }
+ }
}
- fn is_directory(&self) -> Result<bool> {
- Ok(self.is_directory)
+ fn delete_if_file(&mut self, path: &Path) -> bool {
+ if self.is_file(path) {
+ self.entries.remove(path).is_some()
+ } else {
+ false
+ }
}
- }
-
- pub struct FileMock {
- path: PathBuf,
- writable: bool,
- }
-
- // TODO: Do we need ways to return errors?
- #[derive(Debug)]
- pub struct ExpectedCall {
- pub affected_path: PathBuf,
- pub variant: ExpectedCallVariant,
- }
- impl ExpectedCall {
- pub fn new(path: &Path, variant: ExpectedCallVariant) -> Self {
- ExpectedCall {
- affected_path: path.to_path_buf(),
- variant,
+ fn get_file(&self, path: &Path) -> Option<FileMock> {
+ match self.entries.get(path) {
+ Some(entry) => match entry {
+ EntryMock::File(file) => Some(file.clone()),
+ _ => None,
+ },
+ _ => None,
}
}
- fn assert_received(&self, rec: &ReceivedCall) {
- if self.affected_path == rec.affected_path {
- use ExpectedCallVariant as E;
- use ReceivedCallVariant as R;
+ fn get_file_for_reading(&self, path: &Path) -> Option<FileMock> {
+ self.get_file(path).map(|mut f| {
+ f.writable = false;
+ f
+ })
+ }
- let e = &self.variant;
+ fn get_content_if_file(&self, path: &Path) -> Option<Vec<u8>> {
+ self.get_file(path).map(|f| f.content)
+ }
- let equal = match rec.variant {
- R::FileCreated => matches!(e, E::CreateFile),
- R::FileDeleted => matches!(e, E::DeleteFile),
- R::ReadableFileOpened => matches!(e, E::OpenReadableFile),
- R::WritableFileOpened => matches!(e, E::OpenWritableFile),
- R::DirectoryCreated => matches!(e, E::CreateDirectory),
- R::DirectoryRead => matches!(e, E::ReadDirectory { .. }),
- R::DirectoryDeleted => matches!(e, E::DeleteDirectory),
- R::FileRead => matches!(e, E::ReadFile { .. }),
- R::FileWritten { ref received } => {
- if let E::WriteToFile { expected } = e {
- expected == received
- } else {
- false
- }
+ fn write_to_if_file(&mut self, path: &Path, buffer: Vec<u8>) -> bool {
+ match self.entries.get_mut(path) {
+ Some(entry) => match entry {
+ EntryMock::File(file) => {
+ file.content = buffer;
+ true
}
- R::DoesPathExist => matches!(e, E::PathExists(_)),
- };
+ _ => false,
+ },
+ _ => false,
+ }
+ }
- if equal {
- return;
+ fn create_directory(&mut self, path: &Path) -> bool {
+ if let Some(parent) = path.parent() {
+ if !self.is_directory(parent) && !self.create_directory(path) {
+ return false;
}
}
- panic!(
- "
- Expected call does not equal received call.
- Expected: {:?}
- Recevied: {:?}
- ",
- self, rec
- );
+ let path_buf = path.to_path_buf();
+ match self.entries.entry(path_buf.clone()) {
+ hash_map::Entry::Vacant(vacant) => {
+ vacant.insert(EntryMock::Dir { path: path_buf });
+ true
+ }
+ _ => false,
+ }
}
- }
-
- #[derive(Debug, Clone)]
- pub enum ExpectedCallVariant {
- CreateFile,
- DeleteFile,
- OpenReadableFile,
- OpenWritableFile,
- CreateDirectory,
- ReadDirectory { returned: Vec<EntryMock> },
- DeleteDirectory,
- ReadFile { returned: Vec<u8> },
- WriteToFile { expected: Vec<u8> },
- PathExists(bool),
- }
- #[derive(Debug)]
- struct ReceivedCall {
- affected_path: PathBuf,
- variant: ReceivedCallVariant,
- }
-
- impl ReceivedCall {
- fn new(path: &Path, variant: ReceivedCallVariant) -> Self {
- ReceivedCall {
- affected_path: path.to_path_buf(),
- variant,
+ fn delete_if_directory(&mut self, path: &Path) -> bool {
+ if self.is_directory(path) {
+ self.entries.remove(path).is_some()
+ } else {
+ false
}
}
- }
- #[derive(Debug)]
- enum ReceivedCallVariant {
- FileCreated,
- FileDeleted,
- ReadableFileOpened,
- WritableFileOpened,
- DirectoryCreated,
- DirectoryRead,
- DirectoryDeleted,
- FileRead,
- FileWritten { received: Vec<u8> },
- DoesPathExist,
- }
-}
+ fn get_entries_if_directory(&self, path: &Path) -> Option<Vec<EntryMock>> {
+ if self.is_directory(path) {
+ let directory_entries = self
+ .entries
+ .iter()
+ .filter(|&(path, _)| {
+ if let Some(parent) = path.parent() {
+ parent == path
+ } else {
+ false
+ }
+ })
+ .map(|(_, entry)| entry.clone())
+ .collect();
-#[cfg(test)]
-mod tests {
- use std::path::Path;
+ Some(directory_entries)
+ } else {
+ None
+ }
+ }
- use crate::filesystem::{
- mock::{ExpectedCall, ExpectedCallVariant},
- Fs,
- };
+ fn is_file(&self, path: &Path) -> bool {
+ self.entries
+ .get(path)
+ .map_or(false, |e| matches!(e, EntryMock::File(_)))
+ }
- use super::mock::FsMock;
+ fn is_directory(&self, path: &Path) -> bool {
+ self.entries
+ .get(path)
+ .map_or(false, |e| matches!(e, EntryMock::Dir { .. }))
+ }
- #[test]
- fn mock_empty() {
- let fs_mock = FsMock::new();
- fs_mock.assert_calls();
+ fn exists(&self, path: &Path) -> bool {
+ self.entries.contains_key(path)
+ }
}
- #[test]
- fn mock_file_basic() {
- let fs_mock = FsMock::new();
-
- let path = Path::new("file").to_path_buf();
-
- fs_mock.set_expected_calls(vec![
- ExpectedCall::new(&path, ExpectedCallVariant::CreateFile),
- ExpectedCall::new(
- &path,
- ExpectedCallVariant::ReadFile {
- returned: vec![1, 2, 3],
- },
- ),
- ]);
-
- let mut file = fs_mock.create_file(&path).unwrap();
- let received_content = fs_mock.read_from_file(&mut file).unwrap();
-
- assert_eq!(received_content, vec![1, 2, 3]);
-
- fs_mock.assert_calls();
+ #[derive(Clone)]
+ pub struct FileMock {
+ path: PathBuf,
+ writable: bool,
+ content: Vec<u8>,
}
- #[test]
- #[should_panic]
- fn mock_unexpected_call() {
- let fs_mock = FsMock::new();
-
- let path = Path::new("file").to_path_buf();
-
- fs_mock.set_expected_calls(vec![ExpectedCall::new(
- &path,
- ExpectedCallVariant::CreateFile,
- )]);
-
- fs_mock.delete_file(&path).unwrap();
- fs_mock.assert_calls();
+ #[derive(Clone)]
+ pub enum EntryMock {
+ File(FileMock),
+ Dir { path: PathBuf },
}
- #[test]
- #[should_panic]
- fn mock_unequal_calls() {
- let fs_mock = FsMock::new();
+ impl EntryMock {
+ pub fn file(path_str: &str, content: &[u8]) -> Self {
+ EntryMock::File(FileMock {
+ path: Path::new(path_str).to_path_buf(),
+ writable: true,
+ content: content.to_vec(),
+ })
+ }
- let path = Path::new("file").to_path_buf();
+ pub fn dir(path_str: &str) -> Self {
+ EntryMock::Dir {
+ path: Path::new(path_str).to_path_buf(),
+ }
+ }
+ }
- fs_mock.set_expected_calls(vec![
- ExpectedCall::new(&path, ExpectedCallVariant::CreateFile),
- ExpectedCall::new(&path, ExpectedCallVariant::DeleteFile),
- ]);
+ impl FsEntry for EntryMock {
+ fn path(&self) -> PathBuf {
+ match self {
+ EntryMock::File(FileMock { path, .. }) => path.clone(),
+ EntryMock::Dir { path } => path.clone(),
+ }
+ }
- fs_mock.create_file(&path).unwrap();
- fs_mock.assert_calls();
+ fn is_directory(&self) -> Result<bool> {
+ Ok(matches!(self, EntryMock::Dir { .. }))
+ }
}
}