Add ophandle for FileSystemEntity.exists/existsSync (#185)


Co-authored-by: Jenn Magder <magder@google.com>
diff --git a/packages/file/CHANGELOG.md b/packages/file/CHANGELOG.md
index 69175de..61e6ae2 100644
--- a/packages/file/CHANGELOG.md
+++ b/packages/file/CHANGELOG.md
@@ -1,3 +1,7 @@
+#### 6.1.2
+
+* `MemoryFileSystem` now provides `opHandle`s for exists operations.
+
 #### 6.1.1
 
 * `MemoryFile` now provides `opHandle`s for copy and open operations.
diff --git a/packages/file/lib/src/backends/memory/memory_directory.dart b/packages/file/lib/src/backends/memory/memory_directory.dart
index c5c590e..9288f7e 100644
--- a/packages/file/lib/src/backends/memory/memory_directory.dart
+++ b/packages/file/lib/src/backends/memory/memory_directory.dart
@@ -38,7 +38,10 @@
   }
 
   @override
-  bool existsSync() => backingOrNull?.stat.type == expectedType;
+  bool existsSync() {
+    fileSystem.opHandle.call(path, FileSystemOp.exists);
+    return backingOrNull?.stat.type == expectedType;
+  }
 
   @override
   Future<Directory> create({bool recursive = false}) async {
diff --git a/packages/file/lib/src/backends/memory/memory_file.dart b/packages/file/lib/src/backends/memory/memory_file.dart
index 988ffd2..be3cadc 100644
--- a/packages/file/lib/src/backends/memory/memory_file.dart
+++ b/packages/file/lib/src/backends/memory/memory_file.dart
@@ -42,7 +42,10 @@
   io.FileSystemEntityType get expectedType => io.FileSystemEntityType.file;
 
   @override
-  bool existsSync() => backingOrNull?.stat.type == expectedType;
+  bool existsSync() {
+    fileSystem.opHandle.call(path, FileSystemOp.exists);
+    return backingOrNull?.stat.type == expectedType;
+  }
 
   @override
   Future<File> create({bool recursive = false}) async {
diff --git a/packages/file/lib/src/backends/memory/memory_link.dart b/packages/file/lib/src/backends/memory/memory_link.dart
index a3cba7c..7d5afb4 100644
--- a/packages/file/lib/src/backends/memory/memory_link.dart
+++ b/packages/file/lib/src/backends/memory/memory_link.dart
@@ -22,7 +22,10 @@
   io.FileSystemEntityType get expectedType => io.FileSystemEntityType.link;
 
   @override
-  bool existsSync() => backingOrNull?.type == expectedType;
+  bool existsSync() {
+    fileSystem.opHandle.call(path, FileSystemOp.exists);
+    return backingOrNull?.type == expectedType;
+  }
 
   @override
   Future<Link> rename(String newPath) async => renameSync(newPath);
diff --git a/packages/file/lib/src/backends/memory/operations.dart b/packages/file/lib/src/backends/memory/operations.dart
index e0bf55f..9fc7462 100644
--- a/packages/file/lib/src/backends/memory/operations.dart
+++ b/packages/file/lib/src/backends/memory/operations.dart
@@ -56,6 +56,12 @@
   /// * [File.copySync]
   static const FileSystemOp copy = FileSystemOp._(5);
 
+  /// A file system operation used for all exists methods.
+  ///
+  /// * [FileSystemEntity.exists]
+  /// * [FileSystemEntity.existsSync]
+  static const FileSystemOp exists = FileSystemOp._(6);
+
   @override
   String toString() {
     switch (_value) {
@@ -71,6 +77,8 @@
         return 'FileSystemOp.open';
       case 5:
         return 'FileSystemOp.copy';
+      case 6:
+        return 'FileSystemOp.exists';
       default:
         throw StateError('Invalid FileSytemOp type: $this');
     }
diff --git a/packages/file/pubspec.yaml b/packages/file/pubspec.yaml
index 14ac96a..3037f3a 100644
--- a/packages/file/pubspec.yaml
+++ b/packages/file/pubspec.yaml
@@ -1,5 +1,5 @@
 name: file
-version: 6.1.1
+version: 6.1.2
 description:
   A pluggable, mockable file system abstraction for Dart. Supports local file
   system access, as well as in-memory file systems, record-replay file systems,
@@ -7,7 +7,7 @@
 homepage: https://github.com/google/file.dart
 
 environment:
-  sdk: '>=2.12.0-0 <3.0.0'
+  sdk: '>=2.12.0 <3.0.0'
 
 dependencies:
   meta: ^1.3.0
diff --git a/packages/file/test/memory_operations_test.dart b/packages/file/test/memory_operations_test.dart
index 2efa569..696a166 100644
--- a/packages/file/test/memory_operations_test.dart
+++ b/packages/file/test/memory_operations_test.dart
@@ -187,10 +187,46 @@
     ]);
   });
 
+  test('Exists operations invoke opHandle', () async {
+    List<String> contexts = <String>[];
+    List<FileSystemOp> operations = <FileSystemOp>[];
+    MemoryFileSystem fs = MemoryFileSystem.test(
+        opHandle: (String context, FileSystemOp operation) {
+      if (operation == FileSystemOp.exists) {
+        contexts.add(context);
+        operations.add(operation);
+      }
+    });
+    fs.file('testA').existsSync();
+    await fs.file('testB').exists();
+    fs.directory('testDirA').existsSync();
+    await fs.directory('testDirB').exists();
+    fs.link('testLinkA').existsSync();
+    await fs.link('testLinkB').exists();
+
+    expect(contexts, <dynamic>[
+      'testA',
+      'testB',
+      'testDirA',
+      'testDirB',
+      'testLinkA',
+      'testLinkB',
+    ]);
+    expect(operations, <FileSystemOp>[
+      FileSystemOp.exists,
+      FileSystemOp.exists,
+      FileSystemOp.exists,
+      FileSystemOp.exists,
+      FileSystemOp.exists,
+      FileSystemOp.exists,
+    ]);
+  });
+
   test('FileSystemOp toString', () {
     expect(FileSystemOp.create.toString(), 'FileSystemOp.create');
     expect(FileSystemOp.delete.toString(), 'FileSystemOp.delete');
     expect(FileSystemOp.read.toString(), 'FileSystemOp.read');
     expect(FileSystemOp.write.toString(), 'FileSystemOp.write');
+    expect(FileSystemOp.exists.toString(), 'FileSystemOp.exists');
   });
 }