mirror of https://github.com/winsw/winsw
Merge branch 'master' into winsw-2.0
Conflicts: src/Core/ServiceWrapper/Main.cs src/Test/winswTests/winswTests.csproj Signed-off-by: Oleg Nenashev <o.v.nenashev@gmail.com>pull/71/head
commit
4a2249d30c
|
@ -171,6 +171,9 @@ Long human-readable description of the service. This gets displayed in Windows s
|
||||||
### executable
|
### executable
|
||||||
This element specifies the executable to be launched. It can be either absolute path, or you can just specify the executable name and let it be searched from `PATH` (although note that the services often run in a different user account and therefore it might have different `PATH` than your shell does.)
|
This element specifies the executable to be launched. It can be either absolute path, or you can just specify the executable name and let it be searched from `PATH` (although note that the services often run in a different user account and therefore it might have different `PATH` than your shell does.)
|
||||||
|
|
||||||
|
### startmode - Optional Element
|
||||||
|
This element specifies the start mode of the Windows service. It can be one of the following values: Boot, System, Automatic, or Manual. See [MSDN](https://msdn.microsoft.com/en-us/library/aa384896%28v=vs.85%29.aspx) for details. The default is Automatic.
|
||||||
|
|
||||||
### depend
|
### depend
|
||||||
Specify IDs of other services that this service depends on. When service X depends on service Y, X can only run if Y is running.
|
Specify IDs of other services that this service depends on. When service X depends on service Y, X can only run if Y is running.
|
||||||
|
|
||||||
|
@ -305,6 +308,17 @@ Optionally specify the order of service shutdown. If true, the parent process is
|
||||||
Developer info
|
Developer info
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
### Project status
|
||||||
|
|
||||||
|
* WinSW 1.x - Maintenance only
|
||||||
|
* [winsw-1.17] fixes the most of active issues
|
||||||
|
* [winsw-1.17-beta.2] is available for the evaluation
|
||||||
|
* New versions may be released on-demand
|
||||||
|
* All new fixes will be ported to WinSW-2.x
|
||||||
|
* WinSW 2.x - Active development, no stable releases available
|
||||||
|
* [winsw-2.0] - Current development branch
|
||||||
|
* API stability is not guaranteed till the first release, the project structure is in flux
|
||||||
|
|
||||||
### Build Environment
|
### Build Environment
|
||||||
|
|
||||||
* IDE: [Visual Studio Community 2013][MVS2013] (free for open-source projects)
|
* IDE: [Visual Studio Community 2013][MVS2013] (free for open-source projects)
|
||||||
|
@ -313,3 +327,6 @@ Developer info
|
||||||
* The certificate is in <code>.gitignore</code> list. Please do not add it to the repository
|
* The certificate is in <code>.gitignore</code> list. Please do not add it to the repository
|
||||||
|
|
||||||
[MVS2013]: http://www.visualstudio.com/en-us/news/vs2013-community-vs.aspx
|
[MVS2013]: http://www.visualstudio.com/en-us/news/vs2013-community-vs.aspx
|
||||||
|
[winsw-1.17]: https://github.com/kohsuke/winsw/milestones/winsw-1.17
|
||||||
|
[winsw-1.17-beta.2]: https://github.com/kohsuke/winsw/releases/tag/1.17-beta.2
|
||||||
|
[WinSW-2.0]: https://github.com/kohsuke/winsw/milestones/winsw-2.0
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<repositories>
|
||||||
|
<repository path="..\Tests\winswTests\packages.config" />
|
||||||
|
</repositories>
|
|
@ -16,6 +16,7 @@ using log4net.Repository.Hierarchy;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using WMI;
|
using WMI;
|
||||||
using ServiceType = WMI.ServiceType;
|
using ServiceType = WMI.ServiceType;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace winsw
|
namespace winsw
|
||||||
{
|
{
|
||||||
|
@ -36,9 +37,20 @@ namespace winsw
|
||||||
private bool _orderlyShutdown;
|
private bool _orderlyShutdown;
|
||||||
private bool _systemShuttingdown;
|
private bool _systemShuttingdown;
|
||||||
|
|
||||||
public WrapperService()
|
/// <summary>
|
||||||
|
/// Version of Windows service wrapper
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The version will be taken from <see cref="AssemblyInfo"/>
|
||||||
|
/// </remarks>
|
||||||
|
public static Version Version
|
||||||
{
|
{
|
||||||
_descriptor = new ServiceDescriptor();
|
get { return Assembly.GetExecutingAssembly().GetName().Version; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public WrapperService(ServiceDescriptor descriptor)
|
||||||
|
{
|
||||||
|
_descriptor = descriptor;
|
||||||
ServiceName = _descriptor.Id;
|
ServiceName = _descriptor.Id;
|
||||||
CanShutdown = true;
|
CanShutdown = true;
|
||||||
CanStop = true;
|
CanStop = true;
|
||||||
|
@ -47,6 +59,10 @@ namespace winsw
|
||||||
_systemShuttingdown = false;
|
_systemShuttingdown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WrapperService() : this (new ServiceDescriptor())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Process the file copy instructions, so that we can replace files that are always in use while
|
/// Process the file copy instructions, so that we can replace files that are always in use while
|
||||||
/// the service runs.
|
/// the service runs.
|
||||||
|
@ -517,10 +533,10 @@ namespace winsw
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static void Run(string[] _args)
|
public static void Run(string[] _args, ServiceDescriptor descriptor = null)
|
||||||
{
|
{
|
||||||
bool isCLIMode = _args.Length > 0;
|
bool isCLIMode = _args.Length > 0;
|
||||||
var d = new ServiceDescriptor();
|
var d = descriptor ?? new ServiceDescriptor();
|
||||||
|
|
||||||
// Configure the wrapper-internal logging
|
// Configure the wrapper-internal logging
|
||||||
// STDIN and STDOUT of the child process will be handled independently
|
// STDIN and STDOUT of the child process will be handled independently
|
||||||
|
@ -560,6 +576,14 @@ namespace winsw
|
||||||
args[0] = args[0].ToLower();
|
args[0] = args[0].ToLower();
|
||||||
if (args[0] == "install")
|
if (args[0] == "install")
|
||||||
{
|
{
|
||||||
|
// Check if the service exists
|
||||||
|
if (s != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Service with id '" + d.Id + "' already exists");
|
||||||
|
Console.WriteLine("To install the service, delete the existing one or change service Id in the configuration file");
|
||||||
|
throw new Exception("Installation failure: Service with id '" + d.Id + "' already exists");
|
||||||
|
}
|
||||||
|
|
||||||
string username=null, password=null;
|
string username=null, password=null;
|
||||||
bool setallowlogonasaserviceright = false;
|
bool setallowlogonasaserviceright = false;
|
||||||
if (args.Count > 1 && args[1] == "/p")
|
if (args.Count > 1 && args[1] == "/p")
|
||||||
|
@ -599,7 +623,7 @@ namespace winsw
|
||||||
"\"" + d.ExecutablePath + "\"",
|
"\"" + d.ExecutablePath + "\"",
|
||||||
ServiceType.OwnProcess,
|
ServiceType.OwnProcess,
|
||||||
ErrorControl.UserNotified,
|
ErrorControl.UserNotified,
|
||||||
StartMode.Automatic,
|
d.StartMode,
|
||||||
d.Interactive,
|
d.Interactive,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
|
@ -627,11 +651,15 @@ namespace winsw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (args[0] == "uninstall")
|
if (args[0] == "uninstall")
|
||||||
{
|
{
|
||||||
if (s == null)
|
if (s == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Warning! The service with id '" + d.Id + "' does not exist. Nothing to uninstall");
|
||||||
return; // there's no such service, so consider it already uninstalled
|
return; // there's no such service, so consider it already uninstalled
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
s.Delete();
|
s.Delete();
|
||||||
|
@ -642,16 +670,19 @@ namespace winsw
|
||||||
return; // it's already uninstalled, so consider it a success
|
return; // it's already uninstalled, so consider it a success
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (args[0] == "start")
|
if (args[0] == "start")
|
||||||
{
|
{
|
||||||
if (s == null) ThrowNoSuchService();
|
if (s == null) ThrowNoSuchService();
|
||||||
s.StartService();
|
s.StartService();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (args[0] == "stop")
|
if (args[0] == "stop")
|
||||||
{
|
{
|
||||||
if (s == null) ThrowNoSuchService();
|
if (s == null) ThrowNoSuchService();
|
||||||
s.StopService();
|
s.StopService();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (args[0] == "restart")
|
if (args[0] == "restart")
|
||||||
{
|
{
|
||||||
|
@ -668,6 +699,7 @@ namespace winsw
|
||||||
}
|
}
|
||||||
|
|
||||||
s.StartService();
|
s.StartService();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (args[0] == "restart!")
|
if (args[0] == "restart!")
|
||||||
{
|
{
|
||||||
|
@ -681,6 +713,7 @@ namespace winsw
|
||||||
{
|
{
|
||||||
throw new Exception("Failed to invoke restart: "+Marshal.GetLastWin32Error());
|
throw new Exception("Failed to invoke restart: "+Marshal.GetLastWin32Error());
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (args[0] == "status")
|
if (args[0] == "status")
|
||||||
{
|
{
|
||||||
|
@ -691,6 +724,7 @@ namespace winsw
|
||||||
Console.WriteLine("Started");
|
Console.WriteLine("Started");
|
||||||
else
|
else
|
||||||
Console.WriteLine("Stopped");
|
Console.WriteLine("Stopped");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (args[0] == "test")
|
if (args[0] == "test")
|
||||||
{
|
{
|
||||||
|
@ -698,9 +732,25 @@ namespace winsw
|
||||||
wsvc.OnStart(args.ToArray());
|
wsvc.OnStart(args.ToArray());
|
||||||
Thread.Sleep(1000);
|
Thread.Sleep(1000);
|
||||||
wsvc.OnStop();
|
wsvc.OnStop();
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (args[0] == "help" || args[0] == "--help" || args[0] == "-h"
|
||||||
|
|| args[0] == "-?" || args[0] == "/?")
|
||||||
|
{
|
||||||
|
printHelp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (args[0] == "version")
|
||||||
|
{
|
||||||
|
printVersion();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("Unknown command: " + args[0]);
|
||||||
|
printAvailableCommandsInfo();
|
||||||
|
throw new Exception("Unknown command: " + args[0]);
|
||||||
|
|
||||||
|
}
|
||||||
Run(new WrapperService());
|
Run(new WrapperService());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -764,5 +814,43 @@ namespace winsw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void printHelp()
|
||||||
|
{
|
||||||
|
Console.WriteLine("A wrapper binary that can be used to host executables as Windows services");
|
||||||
|
Console.WriteLine("");
|
||||||
|
Console.WriteLine("Usage: winsw [/redirect file] <command> [<args>]");
|
||||||
|
Console.WriteLine(" Missing arguments trigger the service mode");
|
||||||
|
Console.WriteLine("");
|
||||||
|
printAvailableCommandsInfo();
|
||||||
|
Console.WriteLine("");
|
||||||
|
Console.WriteLine("Extra options:");
|
||||||
|
Console.WriteLine("- '/redirect' - redirect the wrapper's STDOUT and STDERR to the specified file");
|
||||||
|
Console.WriteLine("");
|
||||||
|
printVersion();
|
||||||
|
Console.WriteLine("More info: https://github.com/kohsuke/winsw");
|
||||||
|
Console.WriteLine("Bug tracker: https://github.com/kohsuke/winsw/issues");
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Rework to enum in winsw-2.0
|
||||||
|
private static void printAvailableCommandsInfo()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Available commands:");
|
||||||
|
Console.WriteLine("- 'install' - install the service to Windows Service Controller");
|
||||||
|
Console.WriteLine("- 'uninstall' - uninstall the service");
|
||||||
|
Console.WriteLine("- 'start' - start the service (must be installed before)");
|
||||||
|
Console.WriteLine("- 'stop' - stop the service");
|
||||||
|
Console.WriteLine("- 'restart' - restart the service");
|
||||||
|
Console.WriteLine("- 'restart!' - self-restart (can be called from child processes)");
|
||||||
|
Console.WriteLine("- 'status' - check the current status of the service");
|
||||||
|
Console.WriteLine("- 'test' - check if the service can be started and then stopped");
|
||||||
|
Console.WriteLine("- 'version' - print the version info");
|
||||||
|
Console.WriteLine("- 'help' - print the help info (aliases: -h,--help,-?,/?)");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printVersion()
|
||||||
|
{
|
||||||
|
Console.WriteLine("WinSW " + Version);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,5 +28,5 @@ using System.Runtime.InteropServices;
|
||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.1.0.0")]
|
[assembly: AssemblyVersion("1.17.0.0")]
|
||||||
[assembly: AssemblyFileVersion("1.1.0.0")]
|
[assembly: AssemblyFileVersion("1.17.0.0")]
|
||||||
|
|
|
@ -5,6 +5,7 @@ using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
using WMI;
|
||||||
|
|
||||||
namespace winsw
|
namespace winsw
|
||||||
{
|
{
|
||||||
|
@ -36,8 +37,7 @@ namespace winsw
|
||||||
// this returns the executable name as given by the calling process, so
|
// this returns the executable name as given by the calling process, so
|
||||||
// it needs to be absolutized.
|
// it needs to be absolutized.
|
||||||
string p = Environment.GetCommandLineArgs()[0];
|
string p = Environment.GetCommandLineArgs()[0];
|
||||||
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, p);
|
return Path.GetFullPath(p);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,18 +45,24 @@ namespace winsw
|
||||||
{
|
{
|
||||||
// find co-located configuration xml. We search up to the ancestor directories to simplify debugging,
|
// find co-located configuration xml. We search up to the ancestor directories to simplify debugging,
|
||||||
// as well as trimming off ".vshost" suffix (which is used during debugging)
|
// as well as trimming off ".vshost" suffix (which is used during debugging)
|
||||||
|
//Get the first parent to go into the recursive loop
|
||||||
string p = ExecutablePath;
|
string p = ExecutablePath;
|
||||||
string baseName = Path.GetFileNameWithoutExtension(p);
|
string baseName = Path.GetFileNameWithoutExtension(p);
|
||||||
if (baseName.EndsWith(".vshost")) baseName = baseName.Substring(0, baseName.Length - 7);
|
if (baseName.EndsWith(".vshost")) baseName = baseName.Substring(0, baseName.Length - 7);
|
||||||
|
DirectoryInfo d = new DirectoryInfo(Path.GetDirectoryName(p));
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
p = Path.GetDirectoryName(p);
|
if (File.Exists(Path.Combine(d.FullName, baseName + ".xml")))
|
||||||
if (File.Exists(Path.Combine(p, baseName + ".xml")))
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (d.Parent == null)
|
||||||
|
throw new FileNotFoundException("Unable to locate "+baseName+".xml file within executable directory or any parents");
|
||||||
|
|
||||||
|
d = d.Parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseName = baseName;
|
BaseName = baseName;
|
||||||
BasePath = Path.Combine(p, BaseName);
|
BasePath = Path.Combine(d.FullName, BaseName);
|
||||||
|
|
||||||
dom.Load(BasePath + ".xml");
|
dom.Load(BasePath + ".xml");
|
||||||
|
|
||||||
|
@ -393,6 +399,31 @@ namespace winsw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start mode of the Service
|
||||||
|
/// </summary>
|
||||||
|
public StartMode StartMode
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var p = SingleElement("startmode", true);
|
||||||
|
if (p == null) return StartMode.Automatic; // default value
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (StartMode)Enum.Parse(typeof(StartMode), p, true);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine("Start mode in XML must be one of the following:");
|
||||||
|
foreach (string sm in Enum.GetNames(typeof(StartMode)))
|
||||||
|
{
|
||||||
|
Console.WriteLine(sm);
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// True if the service should when finished on shutdown.
|
/// True if the service should when finished on shutdown.
|
||||||
/// This doesn't work on some OSes. See http://msdn.microsoft.com/en-us/library/ms679277%28VS.85%29.aspx
|
/// This doesn't work on some OSes. See http://msdn.microsoft.com/en-us/library/ms679277%28VS.85%29.aspx
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
<OutputPath>bin\Debug\</OutputPath>
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
@ -58,6 +58,8 @@
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<DocumentationFile>
|
||||||
|
</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="log4net">
|
<Reference Include="log4net">
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
using NUnit.Framework;
|
||||||
|
using winsw;
|
||||||
|
using winswTests.Util;
|
||||||
|
|
||||||
|
namespace winswTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
class MainTest
|
||||||
|
{
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void PrintVersion()
|
||||||
|
{
|
||||||
|
string expectedVersion = WrapperService.Version.ToString();
|
||||||
|
string cliOut = CLITestHelper.CLITest(new[] { "version" });
|
||||||
|
StringAssert.Contains(expectedVersion, cliOut, "Expected that version contains " + expectedVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void PrintHelp()
|
||||||
|
{
|
||||||
|
string expectedVersion = WrapperService.Version.ToString();
|
||||||
|
string cliOut = CLITestHelper.CLITest(new[] { "help" });
|
||||||
|
|
||||||
|
StringAssert.Contains(expectedVersion, cliOut, "Expected that help contains " + expectedVersion);
|
||||||
|
StringAssert.Contains("start", cliOut, "Expected that help refers start command");
|
||||||
|
StringAssert.Contains("help", cliOut, "Expected that help refers help command");
|
||||||
|
StringAssert.Contains("version", cliOut, "Expected that help refers version command");
|
||||||
|
//TODO: check all commands after the migration of ccommands to enum
|
||||||
|
|
||||||
|
// Extra options
|
||||||
|
StringAssert.Contains("/redirect", cliOut, "Expected that help message refers the redirect message");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void FailOnUnsupportedCommand()
|
||||||
|
{
|
||||||
|
const string commandName = "nonExistentCommand";
|
||||||
|
string expectedMessage = "Unknown command: " + commandName.ToLower();
|
||||||
|
CLITestResult res = CLITestHelper.CLIErrorTest(new[] {commandName});
|
||||||
|
|
||||||
|
Assert.True(res.HasException, "Expected an exception due to the wrong command");
|
||||||
|
StringAssert.Contains(expectedMessage, res.Out, "Expected the message about unknown command");
|
||||||
|
// ReSharper disable once PossibleNullReferenceException
|
||||||
|
StringAssert.Contains(expectedMessage, res.Exception.Message, "Expected the message about unknown command");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,9 @@ using winsw;
|
||||||
|
|
||||||
namespace winswTests
|
namespace winswTests
|
||||||
{
|
{
|
||||||
|
using System;
|
||||||
|
using WMI;
|
||||||
|
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class ServiceDescriptorTests
|
public class ServiceDescriptorTests
|
||||||
{
|
{
|
||||||
|
@ -41,6 +44,66 @@ namespace winswTests
|
||||||
_extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
_extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void DefaultStartMode()
|
||||||
|
{
|
||||||
|
Assert.That(_extendedServiceDescriptor.StartMode, Is.EqualTo(StartMode.Automatic));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[ExpectedException(typeof(System.ArgumentException))]
|
||||||
|
public void IncorrectStartMode()
|
||||||
|
{
|
||||||
|
const string SeedXml = "<service>"
|
||||||
|
+ "<id>service.exe</id>"
|
||||||
|
+ "<name>Service</name>"
|
||||||
|
+ "<description>The service.</description>"
|
||||||
|
+ "<executable>node.exe</executable>"
|
||||||
|
+ "<arguments>My Arguments</arguments>"
|
||||||
|
+ "<startmode>rotate</startmode>"
|
||||||
|
+ "<logmode>rotate</logmode>"
|
||||||
|
+ "<serviceaccount>"
|
||||||
|
+ "<domain>" + Domain + "</domain>"
|
||||||
|
+ "<user>" + Username + "</user>"
|
||||||
|
+ "<password>" + Password + "</password>"
|
||||||
|
+ "<allowservicelogon>" + AllowServiceAccountLogonRight + "</allowservicelogon>"
|
||||||
|
+ "</serviceaccount>"
|
||||||
|
+ "<workingdirectory>"
|
||||||
|
+ ExpectedWorkingDirectory
|
||||||
|
+ "</workingdirectory>"
|
||||||
|
+ @"<logpath>C:\logs</logpath>"
|
||||||
|
+ "</service>";
|
||||||
|
|
||||||
|
_extendedServiceDescriptor = ServiceDescriptor.FromXML(SeedXml);
|
||||||
|
Assert.That(_extendedServiceDescriptor.StartMode, Is.EqualTo(StartMode.Manual));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ChangedStartMode()
|
||||||
|
{
|
||||||
|
const string SeedXml = "<service>"
|
||||||
|
+ "<id>service.exe</id>"
|
||||||
|
+ "<name>Service</name>"
|
||||||
|
+ "<description>The service.</description>"
|
||||||
|
+ "<executable>node.exe</executable>"
|
||||||
|
+ "<arguments>My Arguments</arguments>"
|
||||||
|
+ "<startmode>manual</startmode>"
|
||||||
|
+ "<logmode>rotate</logmode>"
|
||||||
|
+ "<serviceaccount>"
|
||||||
|
+ "<domain>" + Domain + "</domain>"
|
||||||
|
+ "<user>" + Username + "</user>"
|
||||||
|
+ "<password>" + Password + "</password>"
|
||||||
|
+ "<allowservicelogon>" + AllowServiceAccountLogonRight + "</allowservicelogon>"
|
||||||
|
+ "</serviceaccount>"
|
||||||
|
+ "<workingdirectory>"
|
||||||
|
+ ExpectedWorkingDirectory
|
||||||
|
+ "</workingdirectory>"
|
||||||
|
+ @"<logpath>C:\logs</logpath>"
|
||||||
|
+ "</service>";
|
||||||
|
|
||||||
|
_extendedServiceDescriptor = ServiceDescriptor.FromXML(SeedXml);
|
||||||
|
Assert.That(_extendedServiceDescriptor.StartMode, Is.EqualTo(StartMode.Manual));
|
||||||
|
}
|
||||||
[Test]
|
[Test]
|
||||||
public void VerifyWorkingDirectory()
|
public void VerifyWorkingDirectory()
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using winsw;
|
||||||
|
|
||||||
|
namespace winswTests.Util
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helper for WinSW CLI testing
|
||||||
|
/// </summary>
|
||||||
|
public static class CLITestHelper
|
||||||
|
{
|
||||||
|
private const string SeedXml = "<service>"
|
||||||
|
+ "<id>service.exe</id>"
|
||||||
|
+ "<name>Service</name>"
|
||||||
|
+ "<description>The service.</description>"
|
||||||
|
+ "<executable>node.exe</executable>"
|
||||||
|
+ "<arguments>My Arguments</arguments>"
|
||||||
|
+ "<logmode>rotate</logmode>"
|
||||||
|
+ "<workingdirectory>"
|
||||||
|
+ @"C:\winsw\workdir"
|
||||||
|
+ "</workingdirectory>"
|
||||||
|
+ @"<logpath>C:\winsw\logs</logpath>"
|
||||||
|
+ "</service>";
|
||||||
|
private static readonly ServiceDescriptor DefaultServiceDescriptor = ServiceDescriptor.FromXML(SeedXml);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs a simle test, which returns the output CLI
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">CLI arguments to be passed</param>
|
||||||
|
/// <param name="descriptor">Optional Service descriptor (will be used for initializationpurposes)</param>
|
||||||
|
/// <returns>STDOUT if there's no exceptions</returns>
|
||||||
|
/// <exception cref="Exception">Command failure</exception>
|
||||||
|
[NotNull]
|
||||||
|
public static string CLITest(String[] args, ServiceDescriptor descriptor = null)
|
||||||
|
{
|
||||||
|
using (StringWriter sw = new StringWriter())
|
||||||
|
{
|
||||||
|
TextWriter tmp = Console.Out;
|
||||||
|
Console.SetOut(sw);
|
||||||
|
WrapperService.Run(args, descriptor ?? DefaultServiceDescriptor);
|
||||||
|
Console.SetOut(tmp);
|
||||||
|
Console.Write(sw.ToString());
|
||||||
|
return sw.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs a simle test, which returns the output CLI
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">CLI arguments to be passed</param>
|
||||||
|
/// <param name="descriptor">Optional Service descriptor (will be used for initializationpurposes)</param>
|
||||||
|
/// <returns>Test results</returns>
|
||||||
|
[NotNull]
|
||||||
|
public static CLITestResult CLIErrorTest(String[] args, ServiceDescriptor descriptor = null)
|
||||||
|
{
|
||||||
|
StringWriter swOut, swErr;
|
||||||
|
Exception testEx = null;
|
||||||
|
TextWriter tmpOut = Console.Out;
|
||||||
|
TextWriter tmpErr = Console.Error;
|
||||||
|
|
||||||
|
using (swOut = new StringWriter())
|
||||||
|
using (swErr = new StringWriter())
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.SetOut(swOut);
|
||||||
|
Console.SetError(swErr);
|
||||||
|
WrapperService.Run(args, descriptor ?? DefaultServiceDescriptor);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
testEx = ex;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Console.SetOut(tmpOut);
|
||||||
|
Console.SetError(tmpErr);
|
||||||
|
Console.WriteLine("\n>>> Output: ");
|
||||||
|
Console.Write(swOut.ToString());
|
||||||
|
Console.WriteLine("\n>>> Error: ");
|
||||||
|
Console.Write(swErr.ToString());
|
||||||
|
if (testEx != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("\n>>> Exception: ");
|
||||||
|
Console.WriteLine(testEx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CLITestResult(swOut.ToString(), swErr.ToString(), testEx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Aggregated test report
|
||||||
|
/// </summary>
|
||||||
|
public class CLITestResult
|
||||||
|
{
|
||||||
|
[NotNull]
|
||||||
|
public String Out { get; private set; }
|
||||||
|
|
||||||
|
[NotNull]
|
||||||
|
public String Err { get; private set; }
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
public Exception Exception { get; private set; }
|
||||||
|
|
||||||
|
public bool HasException { get { return Exception != null; } }
|
||||||
|
|
||||||
|
public CLITestResult(String output, String err, Exception exception = null)
|
||||||
|
{
|
||||||
|
Out = output;
|
||||||
|
Err = err;
|
||||||
|
Exception = exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="NUnit" version="2.6.4" targetFramework="net20" />
|
<package id="NUnit" version="2.6.4" targetFramework="net20" />
|
||||||
|
<package id="JetBrains.Annotations" version="8.0.5.0" targetFramework="net20" />
|
||||||
</packages>
|
</packages>
|
|
@ -38,17 +38,24 @@
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="JetBrains.Annotations, Version=8.0.5.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\..\packages\JetBrains.Annotations.8.0.5.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
<Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
|
<HintPath>..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.ServiceProcess" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="MainTest.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ServiceDescriptorTests.cs" />
|
<Compile Include="ServiceDescriptorTests.cs" />
|
||||||
|
<Compile Include="Util\CLITestHelper.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Core\ServiceWrapper\winsw.csproj">
|
<ProjectReference Include="..\..\Core\ServiceWrapper\winsw.csproj">
|
||||||
|
|
|
@ -16,34 +16,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{6BDF40
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Debug|Mixed Platforms = Debug|Mixed Platforms
|
|
||||||
Debug|Win32 = Debug|Win32
|
Debug|Win32 = Debug|Win32
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
Release|Mixed Platforms = Release|Mixed Platforms
|
|
||||||
Release|Win32 = Release|Win32
|
Release|Win32 = Release|Win32
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
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 = Release|Any CPU
|
|
||||||
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Mixed Platforms.Build.0 = Release|Any CPU
|
|
||||||
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||||
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Win32.Build.0 = Debug|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.ActiveCfg = Release|Any CPU
|
||||||
{93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Win32.Build.0 = Release|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.ActiveCfg = Debug|Any CPU
|
||||||
{93843402-842B-44B4-B303-AEE829BE0B43}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Win32.Build.0 = Debug|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.ActiveCfg = Release|Any CPU
|
||||||
|
{93843402-842B-44B4-B303-AEE829BE0B43}.Release|Win32.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
Loading…
Reference in New Issue