From 7bb485ed4ea32479c80f9f4802b55126b5e5d118 Mon Sep 17 00:00:00 2001 From: Renaud Paquay Date: Fri, 10 Dec 2010 23:45:55 -0800 Subject: [PATCH] Making specflow tests faster When setting up specflow tests, we ended up copying the full content of the "bin" folder of every module. These folder contain not only the module assembly, but also many other assemblies used by the Orchard framework (the framework itself, and all dependencies). We know skip these files, which dramatically decreases the amount of file I/O needed to start these tests. Also adding logging to trace what's going on during initialization. --HG-- branch : dev --- src/Orchard.Specs/Hosting/WebHost.cs | 83 +++++++++++++++++++----- src/Orchard.Specs/Util/PathExtensions.cs | 2 + 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/src/Orchard.Specs/Hosting/WebHost.cs b/src/Orchard.Specs/Hosting/WebHost.cs index aff5f7d89..08673f9fb 100644 --- a/src/Orchard.Specs/Hosting/WebHost.cs +++ b/src/Orchard.Specs/Hosting/WebHost.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Reflection; using System.Threading; using System.Web; @@ -14,33 +16,55 @@ namespace Orchard.Specs.Hosting { private Path _tempSite; private Path _orchardWebPath; private Path _codeGenDir; + private IEnumerable _knownModules; + private IEnumerable _knownThemes; + private IEnumerable _knownBinAssemblies; public WebHost(Path orchardTemp) { _orchardTemp = orchardTemp; } public void Initialize(string templateName, string virtualDirectory) { + Stopwatch stopwatch = new Stopwatch(); + stopwatch.Start(); + var baseDir = Path.Get(AppDomain.CurrentDomain.BaseDirectory); - _tempSite = Path.Get(_orchardTemp).Combine(System.IO.Path.GetRandomFileName()); + _tempSite = _orchardTemp.Combine(System.IO.Path.GetRandomFileName()); try { _tempSite.Delete(); } catch { } - // Trying the two known relative paths to the Orchard.Web directory. // The second one is for the target "spec" in orchard.proj. _orchardWebPath = baseDir.Up(3).Combine("Orchard.Web"); if (!_orchardWebPath.Exists) { _orchardWebPath = baseDir.Parent.Combine("stage"); } + Log("Initialization of ASP.NET host for template web site \"{0}\":", templateName); + Log(" Source location: \"{0}\"", _orchardWebPath); + Log(" Temporary location: \"{0}\"", _tempSite); + _knownModules = _orchardWebPath.Combine("Modules").Directories.Where(d => d.Combine("module.txt").Exists).Select(d => d.FileName).ToList(); + foreach (var filename in _knownModules) + Log("Available Module: \"{0}\"", filename); + + _knownThemes = _orchardWebPath.Combine("Themes").Directories.Where(d => d.Combine("theme.txt").Exists).Select(d => d.FileName).ToList(); + foreach (var filename in _knownThemes) + Log("Available Theme: \"{0}\"", filename); + + _knownBinAssemblies = _orchardWebPath.Combine("bin").GetFiles("*.dll").Select(f => f.FileNameWithoutExtension); + foreach (var filename in _knownBinAssemblies) + Log("Assembly in ~/bin: \"{0}\"", filename); + + Log("Copy files from template \"{0}\"", templateName); baseDir.Combine("Hosting").Combine(templateName) .DeepCopy(_tempSite); + Log("Copy binaries of the \"Orchard.Web\" project"); _orchardWebPath.Combine("bin") .ShallowCopy("*.dll", _tempSite.Combine("bin")) .ShallowCopy("*.pdb", _tempSite.Combine("bin")); - // Copy SqlCe binaries + Log("Copy SqlCe binaries"); if (_orchardWebPath.Combine("bin").Combine("x86").IsDirectory) { _orchardWebPath.Combine("bin").Combine("x86") .ShallowCopy("*.dll", _tempSite.Combine("bin").Combine("x86")) @@ -53,11 +77,16 @@ namespace Orchard.Specs.Hosting { .ShallowCopy("*.pdb", _tempSite.Combine("bin").Combine("amd64")); } + // Copy binaries of this project, so that remote execution of lambda + // can be achieved through serialization to the ASP.NET appdomain + // (see Execute(Action) method) + Log("Copy Orchard.Specflow test project binaries"); baseDir .ShallowCopy("*.dll", _tempSite.Combine("bin")) .ShallowCopy("*.exe", _tempSite.Combine("bin")) .ShallowCopy("*.pdb", _tempSite.Combine("bin")); + Log("Starting up ASP.NET host"); HostName = "localhost"; PhysicalDirectory = _tempSite; VirtualDirectory = virtualDirectory; @@ -70,6 +99,8 @@ namespace Orchard.Specs.Hosting { // ASP.NET folder seems to be always nested into an empty directory _codeGenDir = shuttle.CodeGenDir; _codeGenDir = _codeGenDir.Parent; + Log("ASP.NET CodeGenDir: \"{0}\"", _codeGenDir); + Log("ASP.NET host initialization completed in {0} sec", stopwatch.Elapsed.TotalSeconds); } [Serializable] @@ -85,39 +116,45 @@ namespace Orchard.Specs.Hosting { Clean(); } + private void Log(string format, params object[] args) { + Trace.WriteLine(string.Format(format, args)); + } + public void Clean() { // Try to delete temporary files for up to ~1.2 seconds. for (int i = 0; i < 4; i++) { - Trace.WriteLine("Waiting 300msec before trying to delete temporary files"); + Log("Waiting 300msec before trying to delete temporary files"); Thread.Sleep(300); - if (TryDeleteTempFiles()) { - Trace.WriteLine("Successfully deleted all temporary files"); + if (TryDeleteTempFiles(i == 4)) { + Log("Successfully deleted all temporary files"); break; } } } - private bool TryDeleteTempFiles() { + private bool TryDeleteTempFiles(bool lastTry) { var result = true; if (_codeGenDir != null && _codeGenDir.Exists) { - Trace.WriteLine(string.Format("Trying to delete temporary files at '{0}", _codeGenDir)); + Log("Trying to delete temporary files at \"{0}\"", _codeGenDir); try { _codeGenDir.Delete(true); // <- clean as much as possible } catch(Exception e) { - Trace.WriteLine(string.Format("failure: '{0}", e)); + if (lastTry) + Log("Failure: \"{0}\"", e); result = false; } } if (_tempSite != null && _tempSite.Exists) try { - Trace.WriteLine(string.Format("Trying to delete temporary files at '{0}", _tempSite)); + Log("Trying to delete temporary files at \"{0}\"", _tempSite); _tempSite.Delete(true); // <- progressively clean as much as possible } catch (Exception e) { - Trace.WriteLine(string.Format("failure: '{0}", e)); + if (lastTry) + Log("failure: \"{0}\"", e); result = false; } @@ -125,6 +162,7 @@ namespace Orchard.Specs.Hosting { } public void CopyExtension(string extensionFolder, string extensionName, ExtensionDeploymentOptions deploymentOptions) { + Log("Copy extension \"{0}\\{1}\" (options={2})", extensionFolder, extensionName, deploymentOptions); var sourceModule = _orchardWebPath.Combine(extensionFolder).Combine(extensionName); var targetModule = _tempSite.Combine(extensionFolder).Combine(extensionName); @@ -145,15 +183,15 @@ namespace Orchard.Specs.Hosting { } private bool IsExtensionBinaryFile(Path path, string extensionName, ExtensionDeploymentOptions deploymentOptions) { - bool isValidExtension = - StringComparer.OrdinalIgnoreCase.Equals(path.Extension, ".exe") || - StringComparer.OrdinalIgnoreCase.Equals(path.Extension, ".dll") || - StringComparer.OrdinalIgnoreCase.Equals(path.Extension, ".pdb"); - + bool isValidExtension = IsAssemblyFile(path); if (!isValidExtension) return false; - bool isExtensionAssembly = StringComparer.OrdinalIgnoreCase.Equals(path.FileNameWithoutExtension, extensionName); + bool isAssemblyInWebAppBin = _knownBinAssemblies.Contains(path.FileNameWithoutExtension, StringComparer.OrdinalIgnoreCase); + if (isAssemblyInWebAppBin) + return false; + + bool isExtensionAssembly = IsOrchardExtensionFile(path); bool copyExtensionAssembly = (deploymentOptions & ExtensionDeploymentOptions.CompiledAssembly) == ExtensionDeploymentOptions.CompiledAssembly; if (isExtensionAssembly && !copyExtensionAssembly) return false; @@ -161,6 +199,17 @@ namespace Orchard.Specs.Hosting { return true; } + private bool IsAssemblyFile(Path path) { + return StringComparer.OrdinalIgnoreCase.Equals(path.Extension, ".exe") || + StringComparer.OrdinalIgnoreCase.Equals(path.Extension, ".dll") || + StringComparer.OrdinalIgnoreCase.Equals(path.Extension, ".pdb"); + } + + private bool IsOrchardExtensionFile(Path path) { + return _knownModules.Where(name => StringComparer.OrdinalIgnoreCase.Equals(name, path.FileNameWithoutExtension)).Any() || + _knownThemes.Where(name => StringComparer.OrdinalIgnoreCase.Equals(name, path.FileNameWithoutExtension)).Any(); + } + public string HostName { get; set; } public string PhysicalDirectory { get; private set; } public string VirtualDirectory { get; private set; } diff --git a/src/Orchard.Specs/Util/PathExtensions.cs b/src/Orchard.Specs/Util/PathExtensions.cs index 92222a075..ea69006b9 100644 --- a/src/Orchard.Specs/Util/PathExtensions.cs +++ b/src/Orchard.Specs/Util/PathExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.IO; using Path = Bleroy.FluentPath.Path; @@ -46,6 +47,7 @@ namespace Orchard.Specs.Util { private static void FileCopy(Path sourcePath, Path targetPath, Path sourceFile) { var targetFile = targetPath.Combine(sourceFile.GetRelativePath(sourcePath)); targetFile.Parent.CreateDirectory(); + Trace.WriteLine(string.Format("Copying file '{0}' to '{1}'", sourceFile, targetFile)); File.Copy(sourceFile, targetFile, true /*overwrite*/); } }