diff --git a/src/Core/ServiceWrapper/Extensions/AbstractWinSWExtension.cs b/src/Core/ServiceWrapper/Extensions/AbstractWinSWExtension.cs new file mode 100644 index 0000000..acba93c --- /dev/null +++ b/src/Core/ServiceWrapper/Extensions/AbstractWinSWExtension.cs @@ -0,0 +1,27 @@ +using System; +using System.Xml; +using winsw.Util; + +namespace winsw.Extensions +{ + public abstract class AbstractWinSWExtension : IWinSWExtension + { + public abstract String DisplayName { get; } + public WinSWExtensionDescriptor Descriptor { get; set; } + + public virtual void Configure(ServiceDescriptor descriptor, XmlNode node, IEventWriter logger) + { + // Do nothing + } + + public virtual void OnStart(IEventWriter eventWriter) + { + // Do nothing + } + + public virtual void OnStop(IEventWriter eventWriter) + { + // Do nothing + } + } +} diff --git a/src/Core/ServiceWrapper/Extensions/ExtensionException.cs b/src/Core/ServiceWrapper/Extensions/ExtensionException.cs new file mode 100644 index 0000000..563c5ed --- /dev/null +++ b/src/Core/ServiceWrapper/Extensions/ExtensionException.cs @@ -0,0 +1,30 @@ +using System; +using winsw.Util; + +namespace winsw.Extensions +{ + public class ExtensionException : WinSWException + { + public String ExtensionId { get; private set; } + + public ExtensionException(String extensionName, String message) + : base(message) + { + ExtensionId = extensionName; + } + + public ExtensionException(String extensionName, String message, Exception innerException) + : base(message, innerException) + { + ExtensionId = extensionName; + } + + public override string Message + { + get + { + return ExtensionId + ": " + base.Message; + } + } + } +} diff --git a/src/Core/ServiceWrapper/Extensions/IWinSWExtension.cs b/src/Core/ServiceWrapper/Extensions/IWinSWExtension.cs new file mode 100644 index 0000000..23f1b80 --- /dev/null +++ b/src/Core/ServiceWrapper/Extensions/IWinSWExtension.cs @@ -0,0 +1,44 @@ +using System; +using System.Xml; +using winsw.Util; + +namespace winsw.Extensions +{ + /// + /// Interface for Win Service Wrapper Extension + /// + /// All implementations should provide the default empty constructor. The initialization will be performed by Init methods + public interface IWinSWExtension + { + /// + /// Extension name to be displayed in logs + /// + String DisplayName { get; } + + /// + /// Extension descriptor + /// + WinSWExtensionDescriptor Descriptor { get; set; } + + /// + /// Init handler. Extension should load it's config during that step + /// + /// Service descriptor + /// Configuration node + void Configure(ServiceDescriptor descriptor, XmlNode node, IEventWriter logger); + + /// + /// Start handler. Called during start of the service + /// + /// Logger + /// Any error during execution + void OnStart(IEventWriter logger); + + /// + /// Stop handler. Called during stop of the service + /// + /// Logger + /// Any error during execution + void OnStop(IEventWriter logger); + } +} diff --git a/src/Core/ServiceWrapper/Extensions/WinSWExtensionDescriptor.cs b/src/Core/ServiceWrapper/Extensions/WinSWExtensionDescriptor.cs new file mode 100644 index 0000000..b659fee --- /dev/null +++ b/src/Core/ServiceWrapper/Extensions/WinSWExtensionDescriptor.cs @@ -0,0 +1,39 @@ +using System; +using System.Xml; +using winsw.Util; + +namespace winsw.Extensions +{ + public class WinSWExtensionDescriptor + { + /// + /// Unique extension ID + /// + public String Id { get; private set; } + + /// + /// Exception is enabled + /// + public bool Enabled { get; private set; } + + /// + /// Extension classname + /// + public String ClassName { get; private set; } + + private WinSWExtensionDescriptor(string id, string className, bool enabled) + { + Id = id; + Enabled = enabled; + ClassName = className; + } + + public static WinSWExtensionDescriptor FromXml(XmlElement node) + { + bool enabled = XmlHelper.SingleAttribute(node, "enabled", true); + string className = XmlHelper.SingleAttribute(node, "className"); + string id = XmlHelper.SingleAttribute(node, "id"); + return new WinSWExtensionDescriptor(id, className, enabled); + } + } +} diff --git a/src/Core/ServiceWrapper/Extensions/WinSWExtensionManager.cs b/src/Core/ServiceWrapper/Extensions/WinSWExtensionManager.cs new file mode 100644 index 0000000..c305409 --- /dev/null +++ b/src/Core/ServiceWrapper/Extensions/WinSWExtensionManager.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Xml; +using System.Reflection; +using System.Diagnostics; +using winsw.Util; + +namespace winsw.Extensions +{ + public class WinSWExtensionManager + { + public Dictionary Extensions { private set; get; } + public ServiceDescriptor ServiceDescriptor { private set; get; } + + public WinSWExtensionManager(ServiceDescriptor serviceDescriptor) + { + ServiceDescriptor = serviceDescriptor; + Extensions = new Dictionary(); + } + + /// + /// Starts all extensions + /// + /// Start failure + public void OnStart(IEventWriter logger) + { + foreach (var ext in Extensions) + { + ext.Value.OnStart(logger); + } + } + + /// + /// Stops all extensions + /// + /// Stop failure + public void OnStop(IEventWriter logger) + { + foreach (var ext in Extensions) + { + ext.Value.OnStop(logger); + } + } + + //TODO: Implement loading of external extensions. Current version supports internal hack + #region Extension load management + + public void LoadExtensions(IEventWriter logger) + { + var extensionIds = ServiceDescriptor.ExtensionIds; + foreach (String extensionId in extensionIds) + { + LoadExtension(extensionId, logger); + } + } + + /// + /// Loads extensions from the configuration file + /// + /// Extension ID + /// Logger + /// Loading failure + private void LoadExtension(string id, IEventWriter logger) + { + if (Extensions.ContainsKey(id)) + { + throw new ExtensionException(id, "Extension has been already loaded"); + } + + var extensionsConfig = ServiceDescriptor.ExtensionsConfiguration; + XmlElement configNode =(extensionsConfig != null) ? extensionsConfig.SelectSingleNode("extension[@id='"+id+"'][1]") as XmlElement : null; + if (configNode == null) + { + throw new ExtensionException(id, "Cannot get the configuration entry"); + } + + var descriptor = WinSWExtensionDescriptor.FromXml(configNode); + if (descriptor.Enabled) + { + IWinSWExtension extension = CreateExtensionInstance(descriptor.Id, descriptor.ClassName); + extension.Descriptor = descriptor; + extension.Configure(ServiceDescriptor, configNode, logger); + Extensions.Add(id, extension); + logger.LogEvent("Extension loaded: "+id, EventLogEntryType.Information); + } + else + { + logger.LogEvent("Extension is disabled: " + id, EventLogEntryType.Warning); + } + + } + + private IWinSWExtension CreateExtensionInstance(string id, string className) + { + ActivationContext ac = AppDomain.CurrentDomain.ActivationContext; + Assembly assembly = Assembly.GetCallingAssembly(); + Object created; + + try + { + Type t = Type.GetType(className); + if (t == null) + { + throw new ExtensionException(id, "Class "+className+" does not exist"); + } + created = Activator.CreateInstance(t); + } + catch (Exception ex) + { + throw new ExtensionException(id, "Cannot load the class by name: "+className, ex); + } + + var extension = created as IWinSWExtension; + if (extension == null) + { + throw new ExtensionException(id, "The loaded class is not a WinSW extension: " + className + ". Type is " + created.GetType()); + } + return extension; + } + + #endregion + } +} diff --git a/src/Core/ServiceWrapper/IEventWriter.cs b/src/Core/ServiceWrapper/IEventWriter.cs new file mode 100644 index 0000000..a09421f --- /dev/null +++ b/src/Core/ServiceWrapper/IEventWriter.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; + +namespace winsw.util +{ + public interface IEventWriter + { + void LogEvent(String message); + void LogEvent(String message, EventLogEntryType type); + } +} diff --git a/src/Core/ServiceWrapper/Main.cs b/src/Core/ServiceWrapper/Main.cs index bf29bbf..f9de0cb 100644 --- a/src/Core/ServiceWrapper/Main.cs +++ b/src/Core/ServiceWrapper/Main.cs @@ -14,13 +14,15 @@ using log4net.Core; using log4net.Layout; using log4net.Repository.Hierarchy; using Microsoft.Win32; +using winsw.Extensions; +using winsw.Util; using WMI; using ServiceType = WMI.ServiceType; using System.Reflection; namespace winsw { - public class WrapperService : ServiceBase, EventLogger + public class WrapperService : ServiceBase, EventLogger, IEventWriter { private SERVICE_STATUS _wrapperServiceStatus; @@ -28,6 +30,8 @@ namespace winsw private readonly ServiceDescriptor _descriptor; private Dictionary _envs; + internal WinSWExtensionManager ExtensionManager { private set; get; } + private static readonly ILog Log = LogManager.GetLogger("WinSW"); /// @@ -52,6 +56,7 @@ namespace winsw { _descriptor = descriptor; ServiceName = _descriptor.Id; + ExtensionManager = new WinSWExtensionManager(_descriptor); CanShutdown = true; CanStop = true; CanPauseAndContinue = false; @@ -246,6 +251,22 @@ namespace winsw LogEvent("Starting " + _descriptor.Executable + ' ' + startarguments); WriteEvent("Starting " + _descriptor.Executable + ' ' + startarguments); + // Load and start extensions + ExtensionManager.LoadExtensions(this); + try + { + ExtensionManager.OnStart(this); + } + catch (ExtensionException ex) + { + LogEvent("Failed to start extension " + ex.ExtensionId + "\n" + ex.Message, EventLogEntryType.Error); + WriteEvent("Failed to start extension " + ex.ExtensionId, ex); + //TODO: Exit on error? + } + + LogEvent("Starting " + _descriptor.Executable + ' ' + startarguments); + WriteEvent("Starting " + _descriptor.Executable + ' ' + startarguments); + StartProcess(_process, startarguments, _descriptor.Executable); // send stdout and stderr to its respective output file. @@ -327,6 +348,17 @@ namespace winsw SignalShutdownComplete(); } + // Stop extensions + try + { + ExtensionManager.OnStop(this); + } + catch (ExtensionException ex) + { + LogEvent("Failed to stop extension " + ex.ExtensionId + "\n" + ex.Message, EventLogEntryType.Error); + WriteEvent("Failed to stop extension " + ex.ExtensionId, ex); + } + if (_systemShuttingdown && _descriptor.BeepOnShutdown) { Console.Beep(); diff --git a/src/Core/ServiceWrapper/ServiceDescriptor.cs b/src/Core/ServiceWrapper/ServiceDescriptor.cs index 1263087..8e161ba 100755 --- a/src/Core/ServiceWrapper/ServiceDescriptor.cs +++ b/src/Core/ServiceWrapper/ServiceDescriptor.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.IO; using System.Reflection; using System.Xml; +using winsw.Util; using WMI; namespace winsw @@ -227,9 +228,7 @@ namespace winsw } } - /// - /// Optional working directory. - /// + public string WorkingDirectory { get { var wd = SingleElement("workingdirectory", true); @@ -237,6 +236,37 @@ namespace winsw } } + public List ExtensionIds + { + get + { + List res = new List(); + + XmlNode argumentNode = ExtensionsConfiguration; + XmlNodeList extensions = argumentNode != null ? argumentNode.SelectNodes("extension") : null; + if ( extensions != null) + { + foreach (XmlNode e in extensions) + { + XmlElement extension = (XmlElement)e; + String extensionId = XmlHelper.SingleAttribute(extension, "id"); + res.Add(extensionId); + } + } + + return res; + } + } + + public XmlNode ExtensionsConfiguration + { + get + { + XmlNode argumentNode = dom.SelectSingleNode("//extensions"); + return argumentNode; + } + } + /// /// Combines the contents of all the elements of the given name, /// or return null if no element exists. Handles whitespace quotation. diff --git a/src/Core/ServiceWrapper/Util/IEventWriter.cs b/src/Core/ServiceWrapper/Util/IEventWriter.cs new file mode 100644 index 0000000..7f49dfa --- /dev/null +++ b/src/Core/ServiceWrapper/Util/IEventWriter.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; + +namespace winsw.Util +{ + public interface IEventWriter + { + void LogEvent(String message); + void LogEvent(String message, EventLogEntryType type); + } +} diff --git a/src/Core/ServiceWrapper/Util/WinSWException.cs b/src/Core/ServiceWrapper/Util/WinSWException.cs new file mode 100644 index 0000000..3d1c8c4 --- /dev/null +++ b/src/Core/ServiceWrapper/Util/WinSWException.cs @@ -0,0 +1,15 @@ +using System; + +namespace winsw.Util +{ + public class WinSWException : Exception + { + public WinSWException(String message) + : base(message) + { } + + public WinSWException(String message, Exception innerException) + : base(message, innerException) + { } + } +} diff --git a/src/Core/ServiceWrapper/Util/XmlHelper.cs b/src/Core/ServiceWrapper/Util/XmlHelper.cs new file mode 100644 index 0000000..6269976 --- /dev/null +++ b/src/Core/ServiceWrapper/Util/XmlHelper.cs @@ -0,0 +1,73 @@ +using System; +using System.IO; +using System.Xml; + +namespace winsw.Util +{ + public class XmlHelper + { + /// + /// Retrieves a single string element + /// + /// Parent node + /// Element name + /// If otional, don't throw an exception if the elemen is missing + /// String value or null + /// The required element is missing + public static string SingleElement(XmlNode node, string tagName, Boolean optional) + { + var n = node.SelectSingleNode(tagName); + if (n == null && !optional) throw new InvalidDataException("<" + tagName + "> is missing in configuration XML"); + return n == null ? null : Environment.ExpandEnvironmentVariables(n.InnerText); + } + + /// + /// Retrieves a single node + /// + /// Parent node + /// Element name + /// If otional, don't throw an exception if the elemen is missing + /// String value or null + /// The required element is missing + public static XmlNode SingleNode(XmlNode node, string tagName, Boolean optional) + { + var n = node.SelectSingleNode(tagName); + if (n == null && !optional) throw new InvalidDataException("<" + tagName + "> is missing in configuration XML"); + return n; + } + + /// + /// Retrieves a single mandatory attribute + /// + /// Parent node + /// Attribute name + /// Attribute value + /// The required attribute is missing + public static TAttributeType SingleAttribute (XmlElement node, string attributeName) + { + if (!node.HasAttribute(attributeName)) + { + throw new InvalidDataException("Attribute <" + attributeName + "> is missing in configuration XML"); + } + + return SingleAttribute(node, attributeName, default(TAttributeType)); + } + + /// + /// Retrieves a single optional attribute + /// + /// Parent node + /// Attribute name + /// Default value + /// Attribute value (or default) + public static TAttributeType SingleAttribute(XmlElement node, string attributeName, TAttributeType defaultValue) + { + if (!node.HasAttribute(attributeName)) return defaultValue; + + string rawValue = node.GetAttribute(attributeName); + string substitutedValue = Environment.ExpandEnvironmentVariables(rawValue); + var value = (TAttributeType)Convert.ChangeType(substitutedValue, typeof(TAttributeType)); + return value; + } + } +} diff --git a/src/Core/ServiceWrapper/WinSWException.cs b/src/Core/ServiceWrapper/WinSWException.cs new file mode 100644 index 0000000..9db697f --- /dev/null +++ b/src/Core/ServiceWrapper/WinSWException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace winsw.extensions +{ + public class WinSWException : Exception + { + public WinSWException(String message) + : base(message) + { } + + public WinSWException(String message, Exception innerException) + : base(message, innerException) + { } + } +} diff --git a/src/Core/ServiceWrapper/XmlHelper.cs b/src/Core/ServiceWrapper/XmlHelper.cs new file mode 100644 index 0000000..2d6155b --- /dev/null +++ b/src/Core/ServiceWrapper/XmlHelper.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; +using System.IO; + +namespace winsw.Utils +{ + internal class XmlHelper + { + /// + /// Retrieves a single string element + /// + /// Parent node + /// Element name + /// If otional, don't throw an exception if the elemen is missing + /// String value or null + /// The required element is missing + public static string SingleElement(XmlNode node, string tagName, Boolean optional) + { + var n = node.SelectSingleNode(tagName); + if (n == null && !optional) throw new InvalidDataException("<" + tagName + "> is missing in configuration XML"); + return n == null ? null : Environment.ExpandEnvironmentVariables(n.InnerText); + } + + /// + /// Retrieves a single node + /// + /// Parent node + /// Element name + /// If otional, don't throw an exception if the elemen is missing + /// String value or null + /// The required element is missing + public static XmlNode SingleNode(XmlNode node, string tagName, Boolean optional) + { + var n = node.SelectSingleNode(tagName); + if (n == null && !optional) throw new InvalidDataException("<" + tagName + "> is missing in configuration XML"); + return n; + } + + /// + /// Retrieves a single mandatory attribute + /// + /// Parent node + /// Attribute name + /// Attribute value + /// The required attribute is missing + public static TAttributeType SingleAttribute (XmlElement node, string attributeName) + { + if (!node.HasAttribute(attributeName)) + { + throw new InvalidDataException("Attribute <" + attributeName + "> is missing in configuration XML"); + } + + return SingleAttribute(node, attributeName, default(TAttributeType)); + } + + /// + /// Retrieves a single optional attribute + /// + /// Parent node + /// Attribute name + /// Default value + /// Attribute value (or default) + public static TAttributeType SingleAttribute(XmlElement node, string attributeName, TAttributeType defaultValue) + { + if (!node.HasAttribute(attributeName)) return defaultValue; + + string rawValue = node.GetAttribute(attributeName); + string substitutedValue = Environment.ExpandEnvironmentVariables(rawValue); + var value = (TAttributeType)Convert.ChangeType(substitutedValue, typeof(TAttributeType)); + return value; + } + } +} diff --git a/src/Core/ServiceWrapper/winsw.csproj b/src/Core/ServiceWrapper/winsw.csproj index 0844ff4..eb2754a 100644 --- a/src/Core/ServiceWrapper/winsw.csproj +++ b/src/Core/ServiceWrapper/winsw.csproj @@ -77,6 +77,11 @@ + + + + + @@ -86,6 +91,9 @@ + + + @@ -114,6 +122,7 @@ true + + \ No newline at end of file diff --git a/src/Plugins/SharedDirectoryMapper/SharedDirectoryMapperConfig.cs b/src/Plugins/SharedDirectoryMapper/SharedDirectoryMapperConfig.cs new file mode 100644 index 0000000..0a6387e --- /dev/null +++ b/src/Plugins/SharedDirectoryMapper/SharedDirectoryMapperConfig.cs @@ -0,0 +1,31 @@ +using System; +using System.Xml; +using winsw.Util; + +namespace winsw.Plugins.SharedDirectoryMapper +{ + /// + /// Stores configuration entries for SharedDirectoryMapper extension. + /// + internal class SharedDirectoryMapperConfig + { + public bool EnableMapping { get; set; } + public String Label { get; set; } + public String UNCPath { get; set; } + + public SharedDirectoryMapperConfig(bool enableMapping, string label, string uncPath) + { + EnableMapping = enableMapping; + Label = label; + UNCPath = uncPath; + } + + public static SharedDirectoryMapperConfig FromXml(XmlElement node) + { + bool enableMapping = XmlHelper.SingleAttribute(node, "enabled", true); + string label = XmlHelper.SingleAttribute(node, "label"); + string uncPath = XmlHelper.SingleAttribute(node, "uncpath"); + return new SharedDirectoryMapperConfig(enableMapping, label, uncPath); + } + } +} diff --git a/src/Plugins/SharedDirectoryMapper/SharedDirectoryMapperHelper.cs b/src/Plugins/SharedDirectoryMapper/SharedDirectoryMapperHelper.cs new file mode 100644 index 0000000..8016a93 --- /dev/null +++ b/src/Plugins/SharedDirectoryMapper/SharedDirectoryMapperHelper.cs @@ -0,0 +1,73 @@ +using System; +using System.Diagnostics; +using winsw.Util; + +namespace winsw.Plugins.SharedDirectoryMapper +{ + class SharedDirectoryMappingHelper + { + /// + /// Invokes a system command + /// + /// + /// Command to be executed + /// Command arguments + /// Operation failure + private void InvokeCommand(String command, String args) + { + Process p = new Process + { + StartInfo = + { + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardError = true, + RedirectStandardOutput = true, + FileName = command, + Arguments = args + } + }; + + p.Start(); + p.WaitForExit(); + if (p.ExitCode != 0) + { + throw new MapperException(p, command, args); + } + } + + /// + /// Maps the remote directory + /// + /// Disk label + /// UNC path to the directory + /// Operation failure + public void MapDirectory(String label, String uncPath) + { + InvokeCommand("net.exe", " use " + label + " " + uncPath); + } + + /// + /// Unmaps the label + /// + /// Disk label + /// Operation failure + public void UnmapDirectory(String label) + { + InvokeCommand("net.exe", " use /DELETE /YES " + label); + } + } + + class MapperException : WinSWException + { + public String Call { get; private set; } + public Process Process { get; private set; } + + public MapperException(Process process, string command, string args) + : base("Command " + command + " " + args + " failed with code " + process.ExitCode) + { + Call = command + " " + args; + Process = process; + } + } +} diff --git a/src/Plugins/SharedDirectoryMapper/sampleConfig.xml b/src/Plugins/SharedDirectoryMapper/sampleConfig.xml new file mode 100644 index 0000000..8cf5907 --- /dev/null +++ b/src/Plugins/SharedDirectoryMapper/sampleConfig.xml @@ -0,0 +1,18 @@ + + + SERVICE_NAME + Jenkins Slave + This service runs a slave for Jenkins continuous integration system. + C:\Program Files\Java\jre7\bin\java.exe + -Xrs -jar "%BASE%\slave.jar" -jnlpUrl ... + rotate + + + + + + + + + + \ No newline at end of file diff --git a/src/Test/winswTests/Extensions/WinSWExtensionManagerTest.cs b/src/Test/winswTests/Extensions/WinSWExtensionManagerTest.cs new file mode 100644 index 0000000..5ccd638 --- /dev/null +++ b/src/Test/winswTests/Extensions/WinSWExtensionManagerTest.cs @@ -0,0 +1,62 @@ +using winsw; +using NUnit.Framework; +using winsw.Extensions; +using winsw.Plugins.SharedDirectoryMapper; +using winswTests.util; + +namespace winswTests.extensions +{ + [TestFixture] + class WinSWExtensionManagerTest + { + ServiceDescriptor _testServiceDescriptor; + readonly TestLogger _logger = new TestLogger(); + + [SetUp] + public void SetUp() + { + string testExtension = typeof (SharedDirectoryMapper).ToString(); + string seedXml = "" + + " " + + " SERVICE_NAME " + + " Jenkins Slave " + + " This service runs a slave for Jenkins continuous integration system. " + + " C:\\Program Files\\Java\\jre7\\bin\\java.exe " + + " -Xrs -jar \\\"%BASE%\\slave.jar\\\" -jnlpUrl ... " + + " rotate " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + ""; + _testServiceDescriptor = ServiceDescriptor.FromXML(seedXml); + } + + [Test] + public void LoadExtensions() + { + WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor); + manager.LoadExtensions(_logger); + Assert.AreEqual(2, manager.Extensions.Count, "Two extensions should be loaded"); + } + + [Test] + public void StartStopExtension() + { + WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor); + manager.LoadExtensions(_logger); + manager.OnStart(_logger); + manager.OnStop(_logger); + } + } +} diff --git a/src/Test/winswTests/NunitTest.nunit b/src/Test/winswTests/NunitTest.nunit new file mode 100644 index 0000000..3eaf587 --- /dev/null +++ b/src/Test/winswTests/NunitTest.nunit @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/Test/winswTests/Util/TestLogger.cs b/src/Test/winswTests/Util/TestLogger.cs new file mode 100644 index 0000000..d73ffab --- /dev/null +++ b/src/Test/winswTests/Util/TestLogger.cs @@ -0,0 +1,19 @@ +using System; +using System.Diagnostics; +using winsw.Util; + +namespace winswTests.util +{ + class TestLogger : IEventWriter + { + public void LogEvent(String message) + { + Console.WriteLine(message); + } + + public void LogEvent(String message, EventLogEntryType type) + { + Console.WriteLine("[" + type + "]" + message); + } + } +} diff --git a/src/Test/winswTests/winswTests.csproj b/src/Test/winswTests/winswTests.csproj index 83bddf7..0bf6911 100644 --- a/src/Test/winswTests/winswTests.csproj +++ b/src/Test/winswTests/winswTests.csproj @@ -52,9 +52,11 @@ + + @@ -62,10 +64,15 @@ {0DE77F55-ADE5-43C1-999A-0BC81153B039} winsw + + {ca5c71db-c5a8-4c27-bf83-8e6daed9d6b5} + SharedDirectoryMapper + + diff --git a/src/winsw.sln b/src/winsw.sln index 15d09bd..4bb2cad 100644 --- a/src/winsw.sln +++ b/src/winsw.sln @@ -6,6 +6,9 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winsw", "Core\ServiceWrapper\winsw.csproj", "{0DE77F55-ADE5-43C1-999A-0BC81153B039}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winswTests", "Test\winswTests\winswTests.csproj", "{93843402-842B-44B4-B303-AEE829BE0B43}" + ProjectSection(ProjectDependencies) = postProject + {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5} = {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5} + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{6BDF4025-D46C-4C69-BDB2-5CE434C857AA}" ProjectSection(SolutionItems) = preProject @@ -14,22 +17,64 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{6BDF40 .nuget\NuGet.targets = .nuget\NuGet.targets EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedDirectoryMapper", "Plugins\SharedDirectoryMapper\SharedDirectoryMapper.csproj", "{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{077C2CEC-B687-4B53-86E9-C1A1BF5554E5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{BC4AD891-E87E-4F30-867C-FD8084A29E5D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms Debug|Win32 = Debug|Win32 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Win32.ActiveCfg = Debug|Any CPU {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Win32.Build.0 = Debug|Any CPU + {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Any CPU.Build.0 = Release|Any CPU + {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Mixed Platforms.Build.0 = Release|Any CPU {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Win32.ActiveCfg = Release|Any CPU {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Win32.Build.0 = Release|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Win32.ActiveCfg = Debug|Any CPU {93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Win32.Build.0 = Debug|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Release|Any CPU.Build.0 = Release|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Release|Mixed Platforms.Build.0 = Release|Any CPU {93843402-842B-44B4-B303-AEE829BE0B43}.Release|Win32.ActiveCfg = Release|Any CPU {93843402-842B-44B4-B303-AEE829BE0B43}.Release|Win32.Build.0 = Release|Any CPU + {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Debug|Win32.ActiveCfg = Debug|Any CPU + {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Debug|Win32.Build.0 = Debug|Any CPU + {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Release|Any CPU.Build.0 = Release|Any CPU + {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Release|Win32.ActiveCfg = Release|Any CPU + {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Release|Win32.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {93843402-842B-44B4-B303-AEE829BE0B43} = {077C2CEC-B687-4B53-86E9-C1A1BF5554E5} + {CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5} = {BC4AD891-E87E-4F30-867C-FD8084A29E5D} + EndGlobalSection EndGlobal diff --git a/src/winsw.sln.DotSettings b/src/winsw.sln.DotSettings new file mode 100644 index 0000000..45d451a --- /dev/null +++ b/src/winsw.sln.DotSettings @@ -0,0 +1,3 @@ + + SW + UNC \ No newline at end of file