diff --git a/src/Umbraco.Core/IO/FileSystemProviderManager.cs b/src/Umbraco.Core/IO/FileSystemProviderManager.cs index ae1dbdf051..8763f1ac12 100644 --- a/src/Umbraco.Core/IO/FileSystemProviderManager.cs +++ b/src/Umbraco.Core/IO/FileSystemProviderManager.cs @@ -14,13 +14,16 @@ namespace Umbraco.Core.IO private readonly FileSystemProvidersSection _config; private readonly ConcurrentSet _wrappers = new ConcurrentSet(); - private readonly ShadowWrapper _macroPartialFileSystem; - private readonly ShadowWrapper _partialViewsFileSystem; - private readonly ShadowWrapper _stylesheetsFileSystem; - private readonly ShadowWrapper _scriptsFileSystem; - private readonly ShadowWrapper _xsltFileSystem; - private readonly ShadowWrapper _masterPagesFileSystem; - private readonly ShadowWrapper _mvcViewsFileSystem; + private readonly ConcurrentDictionary _providerLookup = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _filesystems = new ConcurrentDictionary(); + + private ShadowWrapper _macroPartialFileSystem; + private ShadowWrapper _partialViewsFileSystem; + private ShadowWrapper _stylesheetsFileSystem; + private ShadowWrapper _scriptsFileSystem; + private ShadowWrapper _xsltFileSystem; + private ShadowWrapper _masterPagesFileSystem; + private ShadowWrapper _mvcViewsFileSystem; #region Singleton & Constructor @@ -31,16 +34,30 @@ namespace Umbraco.Core.IO get { return Instance; } } + // for tests only, totally unsafe + internal void Reset() + { + _wrappers.Clear(); + _providerLookup.Clear(); + _filesystems.Clear(); + CreateWellKnownFileSystems(); + } + private IScopeProviderInternal ScopeProvider { // fixme - 'course this is bad, but enough for now + // beware: means that we capture the "current" scope provider - take care in tests! get { return ApplicationContext.Current == null ? null : ApplicationContext.Current.ScopeProvider as IScopeProviderInternal; } } internal FileSystemProviderManager() { _config = (FileSystemProvidersSection) ConfigurationManager.GetSection("umbracoConfiguration/FileSystemProviders"); + CreateWellKnownFileSystems(); + } + private void CreateWellKnownFileSystems() + { var macroPartialFileSystem = new PhysicalFileSystem(SystemDirectories.MacroPartials); var partialViewsFileSystem = new PhysicalFileSystem(SystemDirectories.PartialViews); var stylesheetsFileSystem = new PhysicalFileSystem(SystemDirectories.Css); @@ -88,9 +105,6 @@ namespace Umbraco.Core.IO //public string ProviderAlias { get; set; } } - private readonly ConcurrentDictionary _providerLookup = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _filesystems = new ConcurrentDictionary(); - /// /// Gets an underlying (non-typed) filesystem supporting a strongly-typed filesystem. /// @@ -266,6 +280,14 @@ namespace Umbraco.Core.IO } } + public void Clear() + { + lock (_set) + { + _set.Clear(); + } + } + public T[] ToArray() { lock (_set) diff --git a/src/Umbraco.Tests/IO/FileSystemProviderManagerTests.cs b/src/Umbraco.Tests/IO/FileSystemProviderManagerTests.cs index 4088a7d470..12142d064d 100644 --- a/src/Umbraco.Tests/IO/FileSystemProviderManagerTests.cs +++ b/src/Umbraco.Tests/IO/FileSystemProviderManagerTests.cs @@ -21,6 +21,17 @@ namespace Umbraco.Tests.IO // media fs wants this ApplicationContext.Current = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of())); + + // start clean + // because some tests will create corrupt or weird filesystems + FileSystemProviderManager.Current.Reset(); + } + + [TearDown] + public void TearDown() + { + // stay clean (see note in SetUp) + FileSystemProviderManager.Current.Reset(); } [Test] @@ -39,22 +50,53 @@ namespace Umbraco.Tests.IO Assert.NotNull(fs); } - [Test] + [Test] + public void Singleton_Typed_File_System() + { + var fs1 = FileSystemProviderManager.Current.GetFileSystemProvider(); + var fs2 = FileSystemProviderManager.Current.GetFileSystemProvider(); + + Assert.AreSame(fs1, fs2); + } + + [Test] public void Exception_Thrown_On_Invalid_Typed_File_System() { Assert.Throws(() => FileSystemProviderManager.Current.GetFileSystemProvider()); } - /// - /// Used in unit tests, for a typed file system we need to inherit from FileSystemWrapper and they MUST have a ctor - /// that only accepts a base IFileSystem object - /// - internal class InvalidTypedFileSystem : FileSystemWrapper + [Test] + public void Exception_Thrown_On_NonConfigured_Typed_File_System() + { + // note: we need to reset the manager between tests else the Accept_Fallback test would corrupt that one + Assert.Throws(() => FileSystemProviderManager.Current.GetFileSystemProvider()); + } + + [Test] + public void Accept_Fallback_On_NonConfigured_Typed_File_System() + { + var fs = FileSystemProviderManager.Current.GetFileSystemProvider(() => new PhysicalFileSystem("~/App_Data/foo")); + + Assert.NotNull(fs); + } + + /// + /// Used in unit tests, for a typed file system we need to inherit from FileSystemWrapper and they MUST have a ctor + /// that only accepts a base IFileSystem object + /// + internal class InvalidTypedFileSystem : FileSystemWrapper { - public InvalidTypedFileSystem(IFileSystem wrapped, string invalidParam) : base(wrapped) - { - } + public InvalidTypedFileSystem(IFileSystem wrapped, string invalidParam) + : base(wrapped) + { } } + [FileSystemProvider("noconfig")] + internal class NonConfiguredTypeFileSystem : FileSystemWrapper + { + public NonConfiguredTypeFileSystem(IFileSystem wrapped) + : base(wrapped) + { } + } } } diff --git a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs index 448c9673f0..03cc9364ce 100644 --- a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs +++ b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs @@ -806,6 +806,7 @@ namespace Umbraco.Tests.IO } [Test] + [Ignore("Does not work on all environments, Directory.GetFiles is broken.")] public void ShadowGetFilesUsingWildcardAndSingleCharacterFilter() { // Arrange @@ -846,6 +847,7 @@ namespace Umbraco.Tests.IO } [Test] + [Ignore("Does not work on all environments, Directory.GetFiles is broken.")] public void ShadowFileSystemFilterIsAsBrokenAsRealFileSystemFilter() { // Arrange diff --git a/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs b/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs index e2a9b2f05b..2c38e4ff1c 100644 --- a/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs @@ -32,6 +32,7 @@ namespace Umbraco.Tests.Scoping private static void ClearFiles() { + TestHelper.DeleteDirectory(IOHelper.MapPath("media")); TestHelper.DeleteDirectory(IOHelper.MapPath("FileSysTests")); TestHelper.DeleteDirectory(IOHelper.MapPath("App_Data")); } @@ -43,6 +44,8 @@ namespace Umbraco.Tests.Scoping var physMediaFileSystem = new PhysicalFileSystem(IOHelper.MapPath("media"), "ignore"); var mediaFileSystem = FileSystemProviderManager.Current.MediaFileSystem; + Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); + var scopeProvider = ApplicationContext.ScopeProvider; using (var scope = scopeProvider.CreateScope(scopeFileSystems: true)) { diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 2370ad1dfd..d19e49b34d 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -24,6 +24,7 @@ using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Security; using umbraco.BusinessLogic; using Umbraco.Core.Events; +using Umbraco.Core.IO; using Umbraco.Core.Scoping; namespace Umbraco.Tests.TestHelpers @@ -110,6 +111,7 @@ namespace Umbraco.Tests.TestHelpers { IsReady = true }; + return _appContext; } diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs index 43b6171b94..b739422804 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs @@ -18,6 +18,7 @@ using Umbraco.Web; using Umbraco.Web.Models.Mapping; using umbraco.BusinessLogic; using Umbraco.Core.Events; +using Umbraco.Core.IO; using Umbraco.Core.Scoping; namespace Umbraco.Tests.TestHelpers @@ -150,6 +151,11 @@ namespace Umbraco.Tests.TestHelpers { var applicationContext = CreateApplicationContext(); ApplicationContext.Current = applicationContext; + + // FileSystemProviderManager captures the current ApplicationContext ScopeProvider + // in its current static instance (yea...) so we need to reset it here to ensure + // it is using the proper ScopeProvider + FileSystemProviderManager.Current.Reset(); } protected virtual ApplicationContext CreateApplicationContext() @@ -170,6 +176,7 @@ namespace Umbraco.Tests.TestHelpers { IsReady = true }; + return applicationContext; }