From 9ae15831bc6dcfc4d40a751c9a747611570212ac Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 24 Sep 2024 22:35:54 +0200 Subject: [PATCH] split filesystem --- src/Sentry/GlobalSessionManager.cs | 2 +- src/Sentry/Http/HttpTransportBase.cs | 12 +- src/Sentry/ISentryJsonSerializable.cs | 5 +- src/Sentry/Internal/FileSystem.cs | 130 ------------------ src/Sentry/Internal/Http/CachingTransport.cs | 8 +- src/Sentry/Internal/IFileSystem.cs | 23 ++-- src/Sentry/Internal/InstallationIdHelper.cs | 4 +- src/Sentry/Internal/ReadOnlyFilesystem.cs | 57 ++++++++ src/Sentry/Internal/ReadWriteFileSystem.cs | 91 ++++++++++++ src/Sentry/SentryOptions.cs | 4 +- src/Sentry/SentrySdk.cs | 8 ++ .../SamplingTransactionProfilerTests.cs | 5 +- test/Sentry.Testing/FakeFileSystem.cs | 88 +++++------- test/Sentry.Tests/FileSystemTests.cs | 59 -------- .../Sentry.Tests/GlobalSessionManagerTests.cs | 5 +- test/Sentry.Tests/HubTests.cs | 7 +- .../Internals/BackgroundWorkerTests.cs | 2 +- .../Internals/Http/CachingTransportTests.cs | 8 +- .../Internals/InstallationIdHelperTests.cs | 7 +- test/Sentry.Tests/ReadWriteFileSystemTests.cs | 59 ++++++++ 20 files changed, 300 insertions(+), 284 deletions(-) delete mode 100644 src/Sentry/Internal/FileSystem.cs create mode 100644 src/Sentry/Internal/ReadOnlyFilesystem.cs create mode 100644 src/Sentry/Internal/ReadWriteFileSystem.cs delete mode 100644 test/Sentry.Tests/FileSystemTests.cs create mode 100644 test/Sentry.Tests/ReadWriteFileSystemTests.cs diff --git a/src/Sentry/GlobalSessionManager.cs b/src/Sentry/GlobalSessionManager.cs index af1d10db96..20ba25ec21 100644 --- a/src/Sentry/GlobalSessionManager.cs +++ b/src/Sentry/GlobalSessionManager.cs @@ -55,7 +55,7 @@ private void PersistSession(SessionUpdate update, DateTimeOffset? pauseTimestamp { _options.LogDebug("Creating persistence directory for session file at '{0}'.", _persistenceDirectoryPath); - if (!_options.FileSystem.CreateDirectory(_persistenceDirectoryPath)) + if (_options.FileSystem.CreateDirectory(_persistenceDirectoryPath) is not FileOperationResult.Success) { _options.LogError("Failed to create persistent directory for session file."); return; diff --git a/src/Sentry/Http/HttpTransportBase.cs b/src/Sentry/Http/HttpTransportBase.cs index 45aef5ad44..897334b14b 100644 --- a/src/Sentry/Http/HttpTransportBase.cs +++ b/src/Sentry/Http/HttpTransportBase.cs @@ -386,14 +386,14 @@ private void HandleFailure(HttpResponseMessage response, Envelope envelope) var destination = Path.Combine(destinationDirectory, "envelope_too_large", (eventId ?? SentryId.Create()).ToString()); - if (!_options.FileSystem.CreateDirectory(Path.GetDirectoryName(destination)!)) + if (_options.FileSystem.CreateDirectory(Path.GetDirectoryName(destination)!) is not FileOperationResult.Success) { _options.LogError("Failed to create directory to store the envelope."); return; } - var envelopeFile = _options.FileSystem.CreateFileForWriting(destination); - if (envelopeFile == Stream.Null) + var (result, envelopeFile) = _options.FileSystem.CreateFileForWriting(destination); + if (result is not FileOperationResult.Success) { _options.LogError("Failed to create envelope file."); return; @@ -451,14 +451,14 @@ private async Task HandleFailureAsync(HttpResponseMessage response, Envelope env var destination = Path.Combine(destinationDirectory, "envelope_too_large", (eventId ?? SentryId.Create()).ToString()); - if (!_options.FileSystem.CreateDirectory(Path.GetDirectoryName(destination)!)) + if (_options.FileSystem.CreateDirectory(Path.GetDirectoryName(destination)!) is not FileOperationResult.Success) { _options.LogError("Failed to create directory to store the envelope."); return; } - var envelopeFile = _options.FileSystem.CreateFileForWriting(destination); - if (envelopeFile == Stream.Null) + var (result, envelopeFile) = _options.FileSystem.CreateFileForWriting(destination); + if (result is not FileOperationResult.Success) { _options.LogError("Failed to create envelope file."); return; diff --git a/src/Sentry/ISentryJsonSerializable.cs b/src/Sentry/ISentryJsonSerializable.cs index e03b81f56c..dbb8cd3e6e 100644 --- a/src/Sentry/ISentryJsonSerializable.cs +++ b/src/Sentry/ISentryJsonSerializable.cs @@ -22,8 +22,8 @@ internal static class JsonSerializableExtensions { public static void WriteToFile(this ISentryJsonSerializable serializable, IFileSystem fileSystem, string filePath, IDiagnosticLogger? logger) { - using var file = fileSystem.CreateFileForWriting(filePath); - if (file == Stream.Null) + var (result, file) = fileSystem.CreateFileForWriting(filePath); + if (result is not FileOperationResult.Success) { return; } @@ -32,5 +32,6 @@ public static void WriteToFile(this ISentryJsonSerializable serializable, IFileS serializable.WriteTo(writer, logger); writer.Flush(); + file.Dispose(); } } diff --git a/src/Sentry/Internal/FileSystem.cs b/src/Sentry/Internal/FileSystem.cs deleted file mode 100644 index de9a16336a..0000000000 --- a/src/Sentry/Internal/FileSystem.cs +++ /dev/null @@ -1,130 +0,0 @@ -using Sentry.Extensibility; - -namespace Sentry.Internal; - -internal class FileSystem : IFileSystem -{ - private readonly SentryOptions? _options; - - public FileSystem(SentryOptions? options) - { - _options = options; - } - - public IEnumerable EnumerateFiles(string path) => Directory.EnumerateFiles(path); - - public IEnumerable EnumerateFiles(string path, string searchPattern) => - Directory.EnumerateFiles(path, searchPattern); - - public IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption) => - Directory.EnumerateFiles(path, searchPattern, searchOption); - - public DirectoryInfo? CreateDirectory(string path) - { - if (!_options?.DisableFileWrite is false) - { - _options?.LogDebug("Skipping creating directory. Writing to file system has been explicitly disabled."); - return null; - } - - return Directory.CreateDirectory(path); - } - - public bool? DeleteDirectory(string path, bool recursive = false) - { - if (!_options?.DisableFileWrite is false) - { - _options?.LogDebug("Skipping deleting directory. Writing to file system has been explicitly disabled."); - return false; - } - - Directory.Delete(path, recursive); - return !Directory.Exists(path); - } - - public bool DirectoryExists(string path) => Directory.Exists(path); - - public bool FileExists(string path) => File.Exists(path); - - public bool? MoveFile(string sourceFileName, string destFileName, bool overwrite = false) - { - if (!_options?.DisableFileWrite is false) - { - _options?.LogDebug("Skipping moving file. Writing to file system has been explicitly disabled."); - return null; - } - -#if NETCOREAPP3_0_OR_GREATER - File.Move(sourceFileName, destFileName, overwrite); -#else - if (overwrite) - { - File.Copy(sourceFileName, destFileName, overwrite: true); - File.Delete(sourceFileName); - } - else - { - File.Move(sourceFileName, destFileName); - } -#endif - - if (File.Exists(sourceFileName) || !File.Exists(destFileName)) - { - return false; - } - - return true; - } - - public bool? DeleteFile(string path) - { - if (!_options?.DisableFileWrite is false) - { - _options?.LogDebug("Skipping deleting file. Writing to file system has been explicitly disabled."); - return null; - } - - File.Delete(path); - return !File.Exists(path); - } - - public DateTimeOffset GetFileCreationTime(string path) => new FileInfo(path).CreationTimeUtc; - - public string ReadAllTextFromFile(string path) => File.ReadAllText(path); - - public Stream OpenFileForReading(string path) => File.OpenRead(path); - - public Stream OpenFileForReading(string path, bool useAsync, FileMode fileMode = FileMode.Open, FileAccess fileAccess = FileAccess.Read, FileShare fileShare = FileShare.ReadWrite, int bufferSize = 4096) - { - return new FileStream( - path, - fileMode, - fileAccess, - fileShare, - bufferSize: bufferSize, - useAsync: useAsync); - } - - public Stream? CreateFileForWriting(string path) - { - if (!_options?.DisableFileWrite is false) - { - _options?.LogDebug("Skipping file for writing. Writing to file system has been explicitly disabled."); - return null; - } - - return File.Create(path); - } - - public bool? WriteAllTextToFile(string path, string contents) - { - if (!_options?.DisableFileWrite is false) - { - _options?.LogDebug("Skipping writing all text to file. Writing to file system has been explicitly disabled."); - return null; - } - - File.WriteAllText(path, contents); - return File.Exists(path); - } -} diff --git a/src/Sentry/Internal/Http/CachingTransport.cs b/src/Sentry/Internal/Http/CachingTransport.cs index 395438b57b..157b0c3e13 100644 --- a/src/Sentry/Internal/Http/CachingTransport.cs +++ b/src/Sentry/Internal/Http/CachingTransport.cs @@ -451,7 +451,13 @@ private async Task StoreToCacheAsync( EnsureFreeSpaceInCache(); - var stream = _fileSystem.CreateFileForWriting(envelopeFilePath); + var (result, stream) = _fileSystem.CreateFileForWriting(envelopeFilePath); + if (result is not FileOperationResult.Success) + { + _options.LogDebug("Failed to store to cache."); + return; + } + #if NETFRAMEWORK || NETSTANDARD2_0 using(stream) #else diff --git a/src/Sentry/Internal/IFileSystem.cs b/src/Sentry/Internal/IFileSystem.cs index cee9d8388b..e5bb9bc621 100644 --- a/src/Sentry/Internal/IFileSystem.cs +++ b/src/Sentry/Internal/IFileSystem.cs @@ -1,5 +1,12 @@ namespace Sentry.Internal; +internal enum FileOperationResult +{ + Success, + Failure, + Disabled +} + internal interface IFileSystem { // Note: This is not comprehensive. If you need other filesystem methods, add to this interface, @@ -8,21 +15,21 @@ internal interface IFileSystem IEnumerable EnumerateFiles(string path); IEnumerable EnumerateFiles(string path, string searchPattern); IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption); - DirectoryInfo? CreateDirectory(string path); - bool? DeleteDirectory(string path, bool recursive = false); + FileOperationResult CreateDirectory(string path); + FileOperationResult DeleteDirectory(string path, bool recursive = false); bool DirectoryExists(string path); bool FileExists(string path); - bool? MoveFile(string sourceFileName, string destFileName, bool overwrite = false); - bool? DeleteFile(string path); + FileOperationResult MoveFile(string sourceFileName, string destFileName, bool overwrite = false); + FileOperationResult DeleteFile(string path); DateTimeOffset GetFileCreationTime(string path); string? ReadAllTextFromFile(string file); - Stream? OpenFileForReading(string path); - Stream? OpenFileForReading(string path, + Stream OpenFileForReading(string path); + Stream OpenFileForReading(string path, bool useAsync, FileMode fileMode = FileMode.Open, FileAccess fileAccess = FileAccess.Read, FileShare fileShare = FileShare.ReadWrite, int bufferSize = 4096); - Stream? CreateFileForWriting(string path); - bool? WriteAllTextToFile(string path, string contents); + (FileOperationResult, Stream) CreateFileForWriting(string path); + FileOperationResult WriteAllTextToFile(string path, string contents); } diff --git a/src/Sentry/Internal/InstallationIdHelper.cs b/src/Sentry/Internal/InstallationIdHelper.cs index 36d0200497..3a0c9e6de1 100644 --- a/src/Sentry/Internal/InstallationIdHelper.cs +++ b/src/Sentry/Internal/InstallationIdHelper.cs @@ -60,7 +60,7 @@ internal class InstallationIdHelper(SentryOptions options) var directoryPath = Path.Combine(rootPath, "Sentry", options.Dsn!.GetHashString()); var fileSystem = options.FileSystem; - if (!fileSystem.CreateDirectory(directoryPath)) + if (fileSystem.CreateDirectory(directoryPath) is not FileOperationResult.Success) { options.LogDebug("Failed to create a directory for installation ID file ({0}).", directoryPath); return null; @@ -79,7 +79,7 @@ internal class InstallationIdHelper(SentryOptions options) // Generate new installation ID and store it in a file var id = Guid.NewGuid().ToString(); - if (!fileSystem.WriteAllTextToFile(filePath, id)) + if (fileSystem.WriteAllTextToFile(filePath, id) is not FileOperationResult.Success) { options.LogDebug("Failed to write Installation ID to file ({0}).", filePath); return null; diff --git a/src/Sentry/Internal/ReadOnlyFilesystem.cs b/src/Sentry/Internal/ReadOnlyFilesystem.cs new file mode 100644 index 0000000000..acfb9731c3 --- /dev/null +++ b/src/Sentry/Internal/ReadOnlyFilesystem.cs @@ -0,0 +1,57 @@ +using Sentry.Extensibility; + +namespace Sentry.Internal; + +internal class ReadOnlyFileSystem : IFileSystem +{ + public IEnumerable EnumerateFiles(string path) => Directory.EnumerateFiles(path); + + public IEnumerable EnumerateFiles(string path, string searchPattern) => + Directory.EnumerateFiles(path, searchPattern); + + public IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption) => + Directory.EnumerateFiles(path, searchPattern, searchOption); + + public FileOperationResult CreateDirectory(string path) => FileOperationResult.Disabled; + + public FileOperationResult DeleteDirectory(string path, bool recursive = false) => FileOperationResult.Disabled; + + public bool DirectoryExists(string path) => Directory.Exists(path); + + public bool FileExists(string path) => File.Exists(path); + + public FileOperationResult MoveFile(string sourceFileName, string destFileName, bool overwrite = false) => + FileOperationResult.Disabled; + + public FileOperationResult DeleteFile(string path) => FileOperationResult.Disabled; + + public DateTimeOffset GetFileCreationTime(string path) => new FileInfo(path).CreationTimeUtc; + + public string ReadAllTextFromFile(string path) => File.ReadAllText(path); + + public Stream OpenFileForReading(string path) => File.OpenRead(path); + + public Stream OpenFileForReading(string path, + bool useAsync, + FileMode fileMode = FileMode.Open, + FileAccess fileAccess = FileAccess.Read, + FileShare fileShare = FileShare.ReadWrite, + int bufferSize = 4096) + { + return new FileStream( + path, + fileMode, + fileAccess, + fileShare, + bufferSize: bufferSize, + useAsync: useAsync); + } + + public (FileOperationResult, Stream) CreateFileForWriting(string path) => (FileOperationResult.Disabled, Stream.Null); + + public FileOperationResult WriteAllTextToFile(string path, string contents) + { + File.WriteAllText(path, contents); + return File.Exists(path) ? FileOperationResult.Success : FileOperationResult.Failure; + } +} diff --git a/src/Sentry/Internal/ReadWriteFileSystem.cs b/src/Sentry/Internal/ReadWriteFileSystem.cs new file mode 100644 index 0000000000..7cf8fc117e --- /dev/null +++ b/src/Sentry/Internal/ReadWriteFileSystem.cs @@ -0,0 +1,91 @@ +namespace Sentry.Internal; + +internal class ReadWriteFileSystem : IFileSystem +{ + public IEnumerable EnumerateFiles(string path) => Directory.EnumerateFiles(path); + + public IEnumerable EnumerateFiles(string path, string searchPattern) => + Directory.EnumerateFiles(path, searchPattern); + + public IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption) => + Directory.EnumerateFiles(path, searchPattern, searchOption); + + public FileOperationResult CreateDirectory(string path) + { + Directory.CreateDirectory(path); + return DirectoryExists(path) ? FileOperationResult.Success : FileOperationResult.Failure; + } + + public FileOperationResult DeleteDirectory(string path, bool recursive = false) + { + Directory.Delete(path, recursive); + return Directory.Exists(path) ? FileOperationResult.Failure : FileOperationResult.Success; + } + + public bool DirectoryExists(string path) => Directory.Exists(path); + + public bool FileExists(string path) => File.Exists(path); + + public FileOperationResult MoveFile(string sourceFileName, string destFileName, bool overwrite = false) + { +#if NETCOREAPP3_0_OR_GREATER + File.Move(sourceFileName, destFileName, overwrite); +#else + if (overwrite) + { + File.Copy(sourceFileName, destFileName, overwrite: true); + File.Delete(sourceFileName); + } + else + { + File.Move(sourceFileName, destFileName); + } +#endif + + if (File.Exists(sourceFileName) || !File.Exists(destFileName)) + { + return FileOperationResult.Failure; + } + + return FileOperationResult.Success; + } + + public FileOperationResult DeleteFile(string path) + { + File.Delete(path); + return File.Exists(path) ? FileOperationResult.Failure : FileOperationResult.Success; + } + + public DateTimeOffset GetFileCreationTime(string path) => new FileInfo(path).CreationTimeUtc; + + public string ReadAllTextFromFile(string path) => File.ReadAllText(path); + + public Stream OpenFileForReading(string path) => File.OpenRead(path); + + public Stream OpenFileForReading(string path, + bool useAsync, + FileMode fileMode = FileMode.Open, + FileAccess fileAccess = FileAccess.Read, + FileShare fileShare = FileShare.ReadWrite, + int bufferSize = 4096) + { + return new FileStream( + path, + fileMode, + fileAccess, + fileShare, + bufferSize: bufferSize, + useAsync: useAsync); + } + + public (FileOperationResult, Stream) CreateFileForWriting(string path) + { + return (FileOperationResult.Success, File.Create(path)); + } + + public FileOperationResult WriteAllTextToFile(string path, string contents) + { + File.WriteAllText(path, contents); + return File.Exists(path) ? FileOperationResult.Success : FileOperationResult.Failure; + } +} diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index d6c927ce16..64545ef671 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -723,7 +723,7 @@ public IList FailedRequestTargets } /// - /// Sets the filesystem instance to use. Defaults to the actual . + /// Sets the filesystem instance to use. Defaults to the actual . /// Used for testing. /// internal IFileSystem FileSystem { get; set; } @@ -1184,7 +1184,7 @@ public bool JsonPreserveReferences /// public SentryOptions() { - FileSystem = new FileSystem(this); + FileSystem = new ReadOnlyFileSystem(); SettingLocator = new SettingLocator(this); _lazyInstallationId = new(() => new InstallationIdHelper(this).TryGetInstallationId()); diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index 0c3fd52a43..6fcf748d91 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -28,6 +28,14 @@ internal static IHub InitHub(SentryOptions options) { options.SetupLogging(); + // For testing: Only overwrite the ReadOnlyFileSystem with a Read-Write one + if (options is { DisableFileWrite: true, FileSystem: ReadOnlyFileSystem }) + { + options.LogDebug("File write has been disabled."); + options.FileSystem = new ReadWriteFileSystem(); + } + + ProcessInfo.Instance ??= new ProcessInfo(options); // Locate the DSN diff --git a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs index 23f9ece269..68d55e1893 100644 --- a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs +++ b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs @@ -217,11 +217,10 @@ async Task VerifyAsync(HttpRequestMessage message) DiagnosticLogger = _testOutputLogger, TracesSampleRate = 1.0, ProfilesSampleRate = 1.0, + // This keeps all writing-to-file operations in memory instead of actually writing to disk + FileSystem = new FakeFileSystem() }; - // This keeps all writing-to-file operations in memory instead of actually writing to disk - options.FileSystem = new FakeFileSystem(options); - // Disable process exit flush to resolve "There is no currently active test." errors. options.DisableAppDomainProcessExitFlush(); diff --git a/test/Sentry.Testing/FakeFileSystem.cs b/test/Sentry.Testing/FakeFileSystem.cs index b3cc94ce94..a45fbb918b 100644 --- a/test/Sentry.Testing/FakeFileSystem.cs +++ b/test/Sentry.Testing/FakeFileSystem.cs @@ -4,15 +4,7 @@ namespace Sentry.Internal; internal class FakeFileSystem : IFileSystem { - private readonly SentryOptions _options; - - private readonly MockFileSystem _fileSystem; - - public FakeFileSystem(SentryOptions options) - { - _options = options; - _fileSystem = new MockFileSystem(); - } + private readonly MockFileSystem _fileSystem = new(); public IEnumerable EnumerateFiles(string path) => _fileSystem.Directory.EnumerateFiles(path); @@ -22,42 +14,24 @@ public IEnumerable EnumerateFiles(string path, string searchPattern) => public IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption) => _fileSystem.Directory.EnumerateFiles(path, searchPattern, searchOption); - public bool CreateDirectory(string path) + public FileOperationResult CreateDirectory(string path) { - if (!_options?.DisableFileWrite is false) - { - _options?.LogDebug("Skipping creating directory. Writing to file system has been explicitly disabled."); - return false; - } - _fileSystem.Directory.CreateDirectory(path); - return true; + return _fileSystem.Directory.Exists(path) ? FileOperationResult.Success : FileOperationResult.Failure; } - public bool DeleteDirectory(string path, bool recursive = false) + public FileOperationResult DeleteDirectory(string path, bool recursive = false) { - if (!_options?.DisableFileWrite is false) - { - _options?.LogDebug("Skipping deleting directory. Writing to file system has been explicitly disabled."); - return false; - } - _fileSystem.Directory.Delete(path, recursive); - return true; + return _fileSystem.Directory.Exists(path) ? FileOperationResult.Failure : FileOperationResult.Success; } public bool DirectoryExists(string path) => _fileSystem.Directory.Exists(path); public bool FileExists(string path) => _fileSystem.File.Exists(path); - public bool MoveFile(string sourceFileName, string destFileName, bool overwrite = false) + public FileOperationResult MoveFile(string sourceFileName, string destFileName, bool overwrite = false) { - if (!_options?.DisableFileWrite is false) - { - _options?.LogDebug("Skipping moving file. Writing to file system has been explicitly disabled."); - return false; - } - #if NETCOREAPP3_0_OR_GREATER _fileSystem.File.Move(sourceFileName, destFileName, overwrite); #else @@ -71,19 +45,19 @@ public bool MoveFile(string sourceFileName, string destFileName, bool overwrite _fileSystem.File.Move(sourceFileName, destFileName); } #endif - return true; - } - public bool DeleteFile(string path) - { - if (!_options?.DisableFileWrite is false) + if (_fileSystem.File.Exists(sourceFileName) || !_fileSystem.File.Exists(destFileName)) { - _options?.LogDebug("Skipping deleting file. Writing to file system has been explicitly disabled."); - return false; + return FileOperationResult.Failure; } + return FileOperationResult.Success; + } + + public FileOperationResult DeleteFile(string path) + { _fileSystem.File.Delete(path); - return true; + return _fileSystem.File.Exists(path) ? FileOperationResult.Failure : FileOperationResult.Success; } public DateTimeOffset GetFileCreationTime(string path) => new FileInfo(path).CreationTimeUtc; @@ -92,26 +66,30 @@ public bool DeleteFile(string path) public Stream OpenFileForReading(string path) => _fileSystem.File.OpenRead(path); - public Stream CreateFileForWriting(string path) + public Stream OpenFileForReading(string path, + bool useAsync, + FileMode fileMode = FileMode.Open, + FileAccess fileAccess = FileAccess.Read, + FileShare fileShare = FileShare.ReadWrite, + int bufferSize = 4096) { - if (!_options?.DisableFileWrite is false) - { - _options?.LogDebug("Skipping file for writing. Writing to file system has been explicitly disabled."); - return Stream.Null; - } - - return _fileSystem.File.Create(path); + return new FileStream( + path, + fileMode, + fileAccess, + fileShare, + bufferSize: bufferSize, + useAsync: useAsync); } - public bool WriteAllTextToFile(string path, string contents) + public (FileOperationResult, Stream) CreateFileForWriting(string path) { - if (!_options?.DisableFileWrite is false) - { - _options?.LogDebug("Skipping writing all text to file. Writing to file system has been explicitly disabled."); - return false; - } + return (FileOperationResult.Success, _fileSystem.File.Create(path)); + } + public FileOperationResult WriteAllTextToFile(string path, string contents) + { _fileSystem.File.WriteAllText(path, contents); - return true; + return _fileSystem.File.Exists(path) ? FileOperationResult.Success : FileOperationResult.Failure; } } diff --git a/test/Sentry.Tests/FileSystemTests.cs b/test/Sentry.Tests/FileSystemTests.cs deleted file mode 100644 index ffd93902b0..0000000000 --- a/test/Sentry.Tests/FileSystemTests.cs +++ /dev/null @@ -1,59 +0,0 @@ -namespace Sentry.Tests; - -public class FileSystemTests -{ - private class Fixture - { - public SentryOptions Options; - - public Fixture() => Options = new SentryOptions { Dsn = ValidDsn }; - - public FileSystem GetSut() => new(Options); - } - - private readonly Fixture _fixture = new(); - - [Fact] - public void CreateDirectory_FileWriteDisabled_ReturnsFalse() - { - _fixture.Options.DisableFileWrite = true; - Assert.False(_fixture.GetSut().CreateDirectory("SomeDirectory")); - } - - [Fact] - public void DeleteDirectory_FileWriteDisabled_ReturnsFalse() - { - _fixture.Options.DisableFileWrite = true; - Assert.False(_fixture.GetSut().DeleteDirectory("SomeDirectory")); - } - - [Fact] - public void MoveFile_FileWriteDisabled_ReturnsFalse() - { - _fixture.Options.DisableFileWrite = true; - Assert.False(_fixture.GetSut().MoveFile("source", "destination")); - } - - [Fact] - public void DeleteFile_FileWriteDisabled_ReturnsFalse() - { - _fixture.Options.DisableFileWrite = true; - Assert.False(_fixture.GetSut().DeleteFile("someFile")); - } - - [Fact] - public void CreateFileForWriting_FileWriteDisabled_ReturnsNullStream() - { - _fixture.Options.DisableFileWrite = true; - - var fileStream = _fixture.GetSut().CreateFileForWriting("someFile"); - Assert.Equal(Stream.Null, fileStream); - } - - [Fact] - public void WriteAllTextToFile_FileWriteDisabled_ReturnsFalse() - { - _fixture.Options.DisableFileWrite = true; - Assert.False(_fixture.GetSut().WriteAllTextToFile("someFile", "someContent")); - } -} diff --git a/test/Sentry.Tests/GlobalSessionManagerTests.cs b/test/Sentry.Tests/GlobalSessionManagerTests.cs index a36f2d4770..1d5631f60a 100644 --- a/test/Sentry.Tests/GlobalSessionManagerTests.cs +++ b/test/Sentry.Tests/GlobalSessionManagerTests.cs @@ -29,11 +29,10 @@ public Fixture(Action configureOptions = null) Debug = true, DiagnosticLogger = Logger, CacheDirectoryPath = CacheDirectory.Path, + // This keeps all writing-to-file operations in memory instead of actually writing to disk + FileSystem = new FakeFileSystem() }; - // This keeps all writing-to-file operations in memory instead of actually writing to disk - Options.FileSystem = new FakeFileSystem(Options); - configureOptions?.Invoke(Options); } diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index 084c72079e..53646a4091 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -388,12 +388,11 @@ async Task Verify(HttpRequestMessage message) // Not to send some session envelope AutoSessionTracking = false, Debug = true, - DiagnosticLogger = logger + DiagnosticLogger = logger, + // This keeps all writing-to-file operations in memory instead of actually writing to disk + FileSystem = new FakeFileSystem() }; - // This keeps all writing-to-file operations in memory instead of actually writing to disk - options.FileSystem = new FakeFileSystem(options); - // Disable process exit flush to resolve "There is no currently active test." errors. options.DisableAppDomainProcessExitFlush(); diff --git a/test/Sentry.Tests/Internals/BackgroundWorkerTests.cs b/test/Sentry.Tests/Internals/BackgroundWorkerTests.cs index f7d1543cf0..d506a179e6 100644 --- a/test/Sentry.Tests/Internals/BackgroundWorkerTests.cs +++ b/test/Sentry.Tests/Internals/BackgroundWorkerTests.cs @@ -503,7 +503,7 @@ public async Task FlushAsync_Calls_CachingTransport_FlushAsync() var options = _fixture.SentryOptions; // This keeps all writing-to-file operations in memory instead of actually writing to disk - options.FileSystem = new FakeFileSystem(options); + options.FileSystem = new FakeFileSystem(); options.CacheDirectoryPath = tempDir.Path; var innerTransport = _fixture.Transport; diff --git a/test/Sentry.Tests/Internals/Http/CachingTransportTests.cs b/test/Sentry.Tests/Internals/Http/CachingTransportTests.cs index fb7169edb1..8d62473efe 100644 --- a/test/Sentry.Tests/Internals/Http/CachingTransportTests.cs +++ b/test/Sentry.Tests/Internals/Http/CachingTransportTests.cs @@ -305,7 +305,8 @@ public async Task Handle_Malformed_Envelopes_Gracefully() var filePath = Path.Combine(processingDirectoryPath, fileName); options.FileSystem.CreateDirectory(processingDirectoryPath); // Create the processing directory - options.FileSystem.CreateFileForWriting(filePath).Dispose(); // Make a malformed envelope... just an empty file + var (result, file) = options.FileSystem.CreateFileForWriting(filePath); + file.Dispose(); // Make a malformed envelope... just an empty file options.FileSystem.FileExists(filePath).Should().BeTrue(); // Act @@ -720,9 +721,10 @@ public async Task DoesntWriteSentAtHeaderToCacheFile() Dsn = ValidDsn, DiagnosticLogger = _logger, Debug = true, - CacheDirectoryPath = cacheDirectory.Path + CacheDirectoryPath = cacheDirectory.Path, + // This keeps all writing-to-file operations in memory instead of actually writing to disk + FileSystem = new FakeFileSystem() }; - options.FileSystem = new FakeFileSystem(options); var innerTransport = Substitute.For(); await using var transport = CachingTransport.Create(innerTransport, options, startWorker: false); diff --git a/test/Sentry.Tests/Internals/InstallationIdHelperTests.cs b/test/Sentry.Tests/Internals/InstallationIdHelperTests.cs index 960fdf0f04..54e6c64e62 100644 --- a/test/Sentry.Tests/Internals/InstallationIdHelperTests.cs +++ b/test/Sentry.Tests/Internals/InstallationIdHelperTests.cs @@ -24,13 +24,12 @@ public Fixture(Action configureOptions = null) CacheDirectoryPath = _cacheDirectory.Path, Release = "test", Debug = true, - DiagnosticLogger = Logger + DiagnosticLogger = Logger, + // This keeps all writing-to-file operations in memory instead of actually writing to disk + FileSystem = new FakeFileSystem() }; configureOptions?.Invoke(Options); - - // This keeps all writing-to-file operations in memory instead of actually writing to disk - Options.FileSystem = new FakeFileSystem(Options); } public InstallationIdHelper GetSut() => new(Options); diff --git a/test/Sentry.Tests/ReadWriteFileSystemTests.cs b/test/Sentry.Tests/ReadWriteFileSystemTests.cs new file mode 100644 index 0000000000..6fb0c663ee --- /dev/null +++ b/test/Sentry.Tests/ReadWriteFileSystemTests.cs @@ -0,0 +1,59 @@ +namespace Sentry.Tests; + +public class ReadWriteFileSystemTests +{ + // private class Fixture + // { + // public SentryOptions Options; + // + // public Fixture() => Options = new SentryOptions { Dsn = ValidDsn }; + // + // public ReadWriteFileSystem GetSut() => new(Options); + // } + // + // private readonly Fixture _fixture = new(); + // + // [Fact] + // public void CreateDirectory_FileWriteDisabled_ReturnsFalse() + // { + // _fixture.Options.DisableFileWrite = true; + // Assert.False(_fixture.GetSut().CreateDirectory("SomeDirectory")); + // } + // + // [Fact] + // public void DeleteDirectory_FileWriteDisabled_ReturnsFalse() + // { + // _fixture.Options.DisableFileWrite = true; + // Assert.False(_fixture.GetSut().DeleteDirectory("SomeDirectory")); + // } + // + // [Fact] + // public void MoveFile_FileWriteDisabled_ReturnsFalse() + // { + // _fixture.Options.DisableFileWrite = true; + // Assert.False(_fixture.GetSut().MoveFile("source", "destination")); + // } + // + // [Fact] + // public void DeleteFile_FileWriteDisabled_ReturnsFalse() + // { + // _fixture.Options.DisableFileWrite = true; + // Assert.False(_fixture.GetSut().DeleteFile("someFile")); + // } + // + // [Fact] + // public void CreateFileForWriting_FileWriteDisabled_ReturnsNullStream() + // { + // _fixture.Options.DisableFileWrite = true; + // + // var fileStream = _fixture.GetSut().CreateFileForWriting("someFile"); + // Assert.Equal(Stream.Null, fileStream); + // } + // + // [Fact] + // public void WriteAllTextToFile_FileWriteDisabled_ReturnsFalse() + // { + // _fixture.Options.DisableFileWrite = true; + // Assert.False(_fixture.GetSut().WriteAllTextToFile("someFile", "someContent")); + // } +}