Index: console/app.config
===================================================================
--- console/app.config (revision 3982)
+++ console/app.config (working copy)
@@ -3,7 +3,8 @@
-
+
+
@@ -13,7 +14,11 @@
-
+
+
+
+
+
@@ -48,22 +53,19 @@
-
+
-
-
+
+
-
-
-
+
+
-
-
-
+
+
-
@@ -80,4 +82,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: core/configuration/ServerConfigurationHandler.cs
===================================================================
--- core/configuration/ServerConfigurationHandler.cs (revision 0)
+++ core/configuration/ServerConfigurationHandler.cs (revision 0)
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Xml;
+using ThoughtWorks.CruiseControl.Remote;
+
+namespace ThoughtWorks.CruiseControl.Core.Config
+{
+ ///
+ /// Provides additional configuration settings for the server.
+ ///
+ ///
+ /// Currently this only retrieves a list of type names, but it could be extended in future
+ /// to load additional settings (perhaps in the same way as the custom builders work).
+ ///
+ public sealed class ServerConfigurationHandler
+ : IConfigurationSectionHandler
+ {
+ #region Create()
+ ///
+ /// Retrieve the list of extensions to load.
+ ///
+ /// The parent.
+ /// The context.
+ /// The section that is being loaded.
+ /// An array of strings containing the type names.
+ public object Create(object parent, object configContext, XmlNode section)
+ {
+ List extensions = new List();
+
+ foreach (XmlNode node in section.SelectNodes("extension"))
+ {
+ ExtensionConfiguration config = new ExtensionConfiguration();
+ config.Type = node.Attributes["type"].Value;
+ extensions.Add(config);
+ }
+
+ return extensions;
+ }
+ #endregion
+ }
+}
Index: core/core.csproj
===================================================================
--- core/core.csproj (revision 3982)
+++ core/core.csproj (working copy)
@@ -187,6 +187,7 @@
+
Code
Index: core/CruiseServer.cs
===================================================================
--- core/CruiseServer.cs (revision 3982)
+++ core/CruiseServer.cs (working copy)
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using ThoughtWorks.CruiseControl.Core.Config;
@@ -14,12 +15,15 @@
private readonly IConfigurationService configurationService;
private readonly ICruiseManager manager;
private readonly ManualResetEvent monitor = new ManualResetEvent(true);
+ private readonly List _extensions = new List();
private bool disposed;
private IntegrationQueueManager integrationQueueManager;
public CruiseServer(IConfigurationService configurationService,
- IProjectIntegratorListFactory projectIntegratorListFactory, IProjectSerializer projectSerializer)
+ IProjectIntegratorListFactory projectIntegratorListFactory,
+ IProjectSerializer projectSerializer,
+ List extensionList)
{
this.configurationService = configurationService;
this.configurationService.AddConfigurationUpdateHandler(new ConfigurationUpdateHandler(Restart));
@@ -32,6 +36,12 @@
IConfiguration configuration = configurationService.Load();
// TODO - does this need to go through a factory? GD
integrationQueueManager = new IntegrationQueueManager(projectIntegratorListFactory, configuration);
+
+ // Load the extensions
+ if (extensionList != null)
+ {
+ InitialiseExtensions(extensionList);
+ }
}
public void Start()
@@ -39,8 +49,15 @@
Log.Info("Starting CruiseControl.NET Server");
monitor.Reset();
integrationQueueManager.StartAllProjects();
- }
+ // Start the extensions
+ Log.Info("Starting Extensions");
+ foreach (ICruiseServerExtension extension in _extensions)
+ {
+ extension.Start();
+ }
+ }
+
///
/// Start integrator for specified project.
///
@@ -54,7 +71,14 @@
///
public void Stop()
{
- Log.Info("Stopping CruiseControl.NET Server");
+ // Stop the extensions
+ Log.Info("Stopping Extensions");
+ foreach (ICruiseServerExtension extension in _extensions)
+ {
+ extension.Stop();
+ }
+
+ Log.Info("Stopping CruiseControl.NET Server");
integrationQueueManager.StopAllProjects();
monitor.Set();
}
@@ -72,7 +96,14 @@
///
public void Abort()
{
- Log.Info("Aborting CruiseControl.NET Server");
+ // Abort the extensions
+ Log.Info("Aborting Extensions");
+ foreach (ICruiseServerExtension extension in _extensions)
+ {
+ extension.Abort();
+ }
+
+ Log.Info("Aborting CruiseControl.NET Server");
integrationQueueManager.Abort();
monitor.Set();
}
@@ -93,7 +124,7 @@
///
public void WaitForExit()
{
- monitor.WaitOne();
+ monitor.WaitOne();
}
///
@@ -301,9 +332,33 @@
private IProjectIntegrator GetIntegrator(string projectName)
{
return integrationQueueManager.GetIntegrator(projectName);
- }
+ }
- void IDisposable.Dispose()
+ #region InitialiseExtensions()
+ ///
+ /// Initialise all the extensions for the server.
+ ///
+ /// The extensions to load.
+ private void InitialiseExtensions(List extensionList)
+ {
+ foreach (ExtensionConfiguration extensionConfig in extensionList)
+ {
+ // See if we can find the type
+ Type extensionType = Type.GetType(extensionConfig.Type);
+ if (extensionType == null) throw new NullReferenceException("Unable to find extension: " + extensionConfig.Type);
+
+ // Load and initialise the extension
+ ICruiseServerExtension extension = Activator.CreateInstance(extensionType) as ICruiseServerExtension;
+ if (extension == null) throw new NullReferenceException("Unable to create an instance of " + extensionType.FullName);
+ extension.Initialise(this, extensionConfig);
+
+ // Add to the list of extensions
+ _extensions.Add(extension);
+ }
+ }
+ #endregion
+
+ void IDisposable.Dispose()
{
lock (this)
{
Index: core/CruiseServerFactory.cs
===================================================================
--- core/CruiseServerFactory.cs (revision 3982)
+++ core/CruiseServerFactory.cs (working copy)
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Configuration;
using System.IO;
using ThoughtWorks.CruiseControl.Core.Config;
@@ -21,10 +22,15 @@
private static ICruiseServer CreateLocal(string configFile)
{
+ // Load the extensions configuration
+ List extensionList = null;
+ extensionList = ConfigurationManager.GetSection("cruiseServer") as List;
+
return new CruiseServer(
NewConfigurationService(configFile),
new ProjectIntegratorListFactory(),
- new NetReflectorProjectSerializer());
+ new NetReflectorProjectSerializer(),
+ extensionList);
}
private static IConfigurationService NewConfigurationService(string configFile)
Index: Remote/ExtensionConfiguration.cs
===================================================================
--- Remote/ExtensionConfiguration.cs (revision 0)
+++ Remote/ExtensionConfiguration.cs (revision 0)
@@ -0,0 +1,25 @@
+using System;
+
+namespace ThoughtWorks.CruiseControl.Remote
+{
+ ///
+ /// Defines the configuration for a server extension.
+ ///
+ public class ExtensionConfiguration
+ {
+ #region Private fields
+ private string _type;
+ #endregion
+
+ #region Public properties
+ ///
+ /// Gets or sets the type of the component.
+ ///
+ public string Type
+ {
+ get { return _type; }
+ set { _type = value; }
+ }
+ #endregion
+ }
+}
Index: Remote/ICruiseServerExtension.cs
===================================================================
--- Remote/ICruiseServerExtension.cs (revision 0)
+++ Remote/ICruiseServerExtension.cs (revision 0)
@@ -0,0 +1,39 @@
+using System;
+
+namespace ThoughtWorks.CruiseControl.Remote
+{
+ ///
+ /// Provides an extension to ICruiseServer basic functionality.
+ ///
+ public interface ICruiseServerExtension
+ {
+ #region Initialise()
+ ///
+ /// Initialises the extension.
+ ///
+ /// The server that this extension is for.
+ void Initialise(ICruiseServer server, ExtensionConfiguration extensionConfig);
+ #endregion
+
+ #region Start()
+ ///
+ /// Starts the extension.
+ ///
+ void Start();
+ #endregion
+
+ #region Stop()
+ ///
+ /// Stops the extension.
+ ///
+ void Stop();
+ #endregion
+
+ #region Abort()
+ ///
+ /// Terminates the extension immediately.
+ ///
+ void Abort();
+ #endregion
+ }
+}
Index: Remote/Remote.csproj
===================================================================
--- Remote/Remote.csproj (revision 3982)
+++ Remote/Remote.csproj (working copy)
@@ -129,6 +129,7 @@
Code
+
Code
@@ -141,6 +142,7 @@
Code
+
Code
Index: UnitTests/Core/Config/ServerConfigurationHandlerTest.cs
===================================================================
--- UnitTests/Core/Config/ServerConfigurationHandlerTest.cs (revision 0)
+++ UnitTests/Core/Config/ServerConfigurationHandlerTest.cs (revision 0)
@@ -0,0 +1,25 @@
+using NUnit.Framework;
+using System.Collections.Generic;
+using System.Configuration;
+using ThoughtWorks.CruiseControl.Remote;
+
+namespace ThoughtWorks.CruiseControl.UnitTests.Core.Config
+{
+ ///
+ /// Tests the ServerConfigurationHandler class.
+ ///
+ [TestFixture]
+ public class ServerConfigurationHandlerTest : CustomAssertion
+ {
+ #region GetConfig()
+ [Test]
+ public void GetConfig()
+ {
+ List config = ConfigurationManager.GetSection("cruiseServer") as List;
+ Assert.IsNotNull(config);
+ Assert.AreEqual(1, config.Count);
+ Assert.AreEqual("ThoughtWorks.CruiseControl.UnitTests.Remote.ServerExtensionStub,ThoughtWorks.CruiseControl.UnitTests", config[0].Type);
+ }
+ #endregion
+ }
+}
Index: UnitTests/Core/CruiseServerTest.cs
===================================================================
--- UnitTests/Core/CruiseServerTest.cs (revision 3982)
+++ UnitTests/Core/CruiseServerTest.cs (working copy)
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Threading;
using NMock;
using NUnit.Framework;
@@ -40,54 +41,60 @@
[SetUp]
protected void SetUp()
{
- projectSerializerMock = new DynamicMock(typeof (IProjectSerializer));
+ InitialiseMocks();
- integratorMock1 = new DynamicMock(typeof (IProjectIntegrator));
- integratorMock2 = new DynamicMock(typeof (IProjectIntegrator));
- integratorMock3 = new DynamicMock(typeof (IProjectIntegrator));
- integrator1 = (IProjectIntegrator) integratorMock1.MockInstance;
- integrator2 = (IProjectIntegrator) integratorMock2.MockInstance;
- integrator3 = (IProjectIntegrator) integratorMock3.MockInstance;
- integratorMock1.SetupResult("Name", "Project 1");
- integratorMock2.SetupResult("Name", "Project 2");
- integratorMock3.SetupResult("Name", "Project 3");
+ server = new CruiseServer((IConfigurationService) configServiceMock.MockInstance,
+ (IProjectIntegratorListFactory) projectIntegratorListFactoryMock.MockInstance,
+ (IProjectSerializer) projectSerializerMock.MockInstance,
+ null);
+ }
- integrationQueue = null; // We have no way of injecting currently.
+ private void InitialiseMocks()
+ {
+ projectSerializerMock = new DynamicMock(typeof(IProjectSerializer));
- configuration = new Configuration();
- project1 = new Project();
- project1.Name = "Project 1";
-
- project2 = new Project();
- project2.Name = "Project 2";
+ integratorMock1 = new DynamicMock(typeof(IProjectIntegrator));
+ integratorMock2 = new DynamicMock(typeof(IProjectIntegrator));
+ integratorMock3 = new DynamicMock(typeof(IProjectIntegrator));
+ integrator1 = (IProjectIntegrator)integratorMock1.MockInstance;
+ integrator2 = (IProjectIntegrator)integratorMock2.MockInstance;
+ integrator3 = (IProjectIntegrator)integratorMock3.MockInstance;
+ integratorMock1.SetupResult("Name", "Project 1");
+ integratorMock2.SetupResult("Name", "Project 2");
+ integratorMock3.SetupResult("Name", "Project 3");
- mockProject = new DynamicMock(typeof(IProject));
- mockProject.ExpectAndReturn("Name", "Project 3");
- mockProject.ExpectAndReturn("QueueName", "Project 3");
- mockProjectInstance = (IProject) mockProject.MockInstance;
- mockProject.ExpectAndReturn("Name", "Project 3");
- integratorMock3.ExpectAndReturn("Project", mockProjectInstance);
+ integrationQueue = null; // We have no way of injecting currently.
- configuration.AddProject(project1);
- configuration.AddProject(project2);
- configuration.AddProject(mockProjectInstance);
+ configuration = new Configuration();
+ project1 = new Project();
+ project1.Name = "Project 1";
- integratorList = new ProjectIntegratorList();
- integratorList.Add(integrator1);
- integratorList.Add(integrator2);
- integratorList.Add(integrator3);
+ project2 = new Project();
+ project2.Name = "Project 2";
- configServiceMock = new DynamicMock(typeof (IConfigurationService));
- configServiceMock.ExpectAndReturn("Load", configuration);
+ mockProject = new DynamicMock(typeof(IProject));
+ mockProject.ExpectAndReturn("Name", "Project 3");
+ mockProject.ExpectAndReturn("QueueName", "Project 3");
+ mockProjectInstance = (IProject)mockProject.MockInstance;
+ mockProject.ExpectAndReturn("Name", "Project 3");
+ integratorMock3.ExpectAndReturn("Project", mockProjectInstance);
- projectIntegratorListFactoryMock = new DynamicMock(typeof (IProjectIntegratorListFactory));
- projectIntegratorListFactoryMock.ExpectAndReturn("CreateProjectIntegrators", integratorList, configuration.Projects, integrationQueue);
+ configuration.AddProject(project1);
+ configuration.AddProject(project2);
+ configuration.AddProject(mockProjectInstance);
- server = new CruiseServer((IConfigurationService) configServiceMock.MockInstance,
- (IProjectIntegratorListFactory) projectIntegratorListFactoryMock.MockInstance,
- (IProjectSerializer) projectSerializerMock.MockInstance);
- }
+ integratorList = new ProjectIntegratorList();
+ integratorList.Add(integrator1);
+ integratorList.Add(integrator2);
+ integratorList.Add(integrator3);
+ configServiceMock = new DynamicMock(typeof(IConfigurationService));
+ configServiceMock.ExpectAndReturn("Load", configuration);
+
+ projectIntegratorListFactoryMock = new DynamicMock(typeof(IProjectIntegratorListFactory));
+ projectIntegratorListFactoryMock.ExpectAndReturn("CreateProjectIntegrators", integratorList, configuration.Projects, integrationQueue);
+ }
+
private void VerifyAll()
{
configServiceMock.Verify();
@@ -314,5 +321,50 @@
integratorMock1.Verify();
integratorMock2.Verify();
}
- }
+
+ ///
+ /// Attempts to start a new cruise server with an extension.
+ ///
+ [Test]
+ public void NewServerWithExtension()
+ {
+ // Define the extension
+ ExtensionConfiguration extensionConfig = new ExtensionConfiguration();
+ extensionConfig.Type = "ThoughtWorks.CruiseControl.UnitTests.Remote.ServerExtensionStub,ThoughtWorks.CruiseControl.UnitTests";
+ List extensions = new List();
+ extensions.Add(extensionConfig);
+
+ // Re-initialise all the mocks so we start with a clean slate
+ InitialiseMocks();
+
+ // See if we can start a new server
+ CruiseServer server = new CruiseServer((IConfigurationService)configServiceMock.MockInstance,
+ (IProjectIntegratorListFactory)projectIntegratorListFactoryMock.MockInstance,
+ (IProjectSerializer)projectSerializerMock.MockInstance,
+ extensions);
+ }
+
+ ///
+ /// Attempts to start a new cruise server with an extension.
+ ///
+ [Test]
+ [ExpectedException(typeof(NullReferenceException), "Unable to find extension: ThoughtWorks.CruiseControl.UnitTests.Remote.ServerExtensionStub2,ThoughtWorks.CruiseControl.UnitTests")]
+ public void NewServerWithInvalidExtension()
+ {
+ // Define the extension
+ ExtensionConfiguration extensionConfig = new ExtensionConfiguration();
+ extensionConfig.Type = "ThoughtWorks.CruiseControl.UnitTests.Remote.ServerExtensionStub2,ThoughtWorks.CruiseControl.UnitTests";
+ List extensions = new List();
+ extensions.Add(extensionConfig);
+
+ // Re-initialise all the mocks so we start with a clean slate
+ InitialiseMocks();
+
+ // See if we can start a new server
+ CruiseServer server = new CruiseServer((IConfigurationService)configServiceMock.MockInstance,
+ (IProjectIntegratorListFactory)projectIntegratorListFactoryMock.MockInstance,
+ (IProjectSerializer)projectSerializerMock.MockInstance,
+ extensions);
+ }
+ }
}
Index: UnitTests/Remote/ServerExtensionStub.cs
===================================================================
--- UnitTests/Remote/ServerExtensionStub.cs (revision 0)
+++ UnitTests/Remote/ServerExtensionStub.cs (revision 0)
@@ -0,0 +1,31 @@
+using System;
+using ThoughtWorks.CruiseControl.Remote;
+
+namespace ThoughtWorks.CruiseControl.UnitTests.Remote
+{
+ public class ServerExtensionStub
+ : ICruiseServerExtension
+ {
+ #region ICruiseServerExtension Members
+ public void Initialise(ICruiseServer server, ExtensionConfiguration extensionConfig)
+ {
+ }
+
+ public void Start()
+ {
+ }
+
+ public void Stop()
+ {
+ }
+
+ public void Abort()
+ {
+ }
+
+ public void WaitForExit()
+ {
+ }
+ #endregion
+ }
+}
Index: UnitTests/test.config
===================================================================
--- UnitTests/test.config (revision 3982)
+++ UnitTests/test.config (working copy)
@@ -7,9 +7,13 @@
-
-
+
+
+
+
+
+
Index: UnitTests/UnitTests.csproj
===================================================================
--- UnitTests/UnitTests.csproj (revision 3982)
+++ UnitTests/UnitTests.csproj (working copy)
@@ -336,6 +336,8 @@
Code
+
+
Code
Index: WcfExtension/CruiseControlImplementation.cs
===================================================================
--- WcfExtension/CruiseControlImplementation.cs (revision 0)
+++ WcfExtension/CruiseControlImplementation.cs (revision 0)
@@ -0,0 +1,152 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Principal;
+using System.ServiceModel;
+using ThoughtWorks.CruiseControl.Core.Util;
+using ThoughtWorks.CruiseControl.Remote;
+using ThoughtWorks.CruiseControl.WcfExtension.DataObjects;
+
+namespace ThoughtWorks.CruiseControl.WcfExtension
+{
+ ///
+ /// Implements the CruiseControl contract.
+ ///
+ [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
+ public class CruiseControlImplementation
+ : ICruiseControlContract
+ {
+ #region Private fields
+ private ICruiseServer _cruiseServer;
+ #endregion
+
+ #region Constructors
+ ///
+ /// Initialises a new instance of this implementation.
+ ///
+ /// The associated server.
+ public CruiseControlImplementation(ICruiseServer server)
+ {
+ // Validate the input parameters
+ server.ValidateNotNull("Cannot pass in a null server");
+
+ // Store the parameters that we need for later
+ _cruiseServer = server;
+ }
+ #endregion
+
+ #region Public methods
+ #region RetrieveVersion()
+ ///
+ /// Retrieves the current server version.
+ ///
+ /// The current version of the server.
+ public string RetrieveVersion()
+ {
+ Log.Debug("RetrieveVersion()");
+ return _cruiseServer.GetVersion();
+ }
+ #endregion
+
+ #region RetrieveSnapshot()
+ ///
+ /// Retrieves a snapshot of the current status of the server.
+ ///
+ /// The snapshot of the current status.
+ public Snapshot RetrieveSnapshot()
+ {
+ Log.Debug("RetrieveSnapshot()");
+
+ // Retrieve the current snapshot and return it in a format that WCF can handle
+ var oSnapshot = new Snapshot(_cruiseServer.GetCruiseServerSnapshot());
+
+ // Add some extra details that aren't in the base snapshot
+ oSnapshot.ServerVersion = RetrieveVersion();
+
+ // Return the completed snapshot
+ return oSnapshot;
+ }
+ #endregion
+
+ #region RetrieveProjects()
+ ///
+ /// Retrieves a list of all the projects configured on the server.
+ ///
+ /// The list of projects.
+ public List RetrieveProjects()
+ {
+ Log.Debug("RetrieveProjects()");
+
+ // Retrieve the current projects
+ var snapshot = _cruiseServer.GetCruiseServerSnapshot();
+ var projectsList = (from project in snapshot.ProjectStatuses
+ select new Project
+ {
+ Name = project.Name
+ }).ToList();
+
+ return projectsList;
+ }
+ #endregion
+
+ #region ForceBuild()
+ ///
+ /// Forces the build of a project.
+ ///
+ /// The name of the project to build.
+ ///
+ /// TDetail may be typed as:
+ ///
+ /// : Thrown when is invalid (null, empty or not in the list of projects).
+ ///
+ ///
+ public void ForceBuild(string projectName)
+ {
+ Log.Debug("ForceBuild(" + projectName + ")");
+
+ // Validate the input data
+ if (string.IsNullOrEmpty(projectName)) throw new FaultException(new ServerFault("ForceBuild", "Project name cannot be null or empty"), new FaultReason("Invalid project name"));
+ if (!CheckProjectExists(projectName)) throw new FaultException(new ServerFault("ForceBuild", "Project name does not exist"), new FaultReason("Invalid project name"));
+
+ // Get the current user
+ string userName = string.Empty;
+ if (ServiceSecurityContext.Current != null)
+ {
+ IIdentity currentUser = ServiceSecurityContext.Current.PrimaryIdentity;
+ if (currentUser != null) userName = currentUser.Name;
+ }
+
+ // Force the build
+ _cruiseServer.ForceBuild(projectName, userName);
+ }
+ #endregion
+ #endregion
+
+ #region Private methods
+ #region CheckProjectExists()
+ ///
+ /// Check that the project name exists on the server.
+ ///
+ /// The project name to check for.
+ /// True if the project name exists, false otherwise.
+ private bool CheckProjectExists(string projectName)
+ {
+ Log.Debug("CheckProjectExists(" + projectName + ")");
+
+ // Retrieve the list of current projects
+ ProjectStatus[] currentProjects = _cruiseServer.GetProjectStatus();
+
+ // Check if the project is in the list
+ bool projectExists = false;
+ foreach (var project in currentProjects)
+ {
+ if (string.Equals(projectName, project.Name, StringComparison.InvariantCulture)) projectExists = true;
+ }
+
+ // Return the result of the check
+ return projectExists;
+ }
+ #endregion
+ #endregion
+ }
+}
Index: WcfExtension/DataObjects/Project.cs
===================================================================
--- WcfExtension/DataObjects/Project.cs (revision 0)
+++ WcfExtension/DataObjects/Project.cs (revision 0)
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using ThoughtWorks.CruiseControl.Remote;
+
+namespace ThoughtWorks.CruiseControl.WcfExtension.DataObjects
+{
+ ///
+ /// Defines a project.
+ ///
+ [DataContract(Namespace = "http://ccnet.thoughtworks.com/1/5/wcf")]
+ public class Project
+ {
+ ///
+ /// Starts a new blank project.
+ ///
+ public Project() { }
+
+ ///
+ /// Starts a new project from a project status.
+ ///
+ /// The source project status.
+ public Project(ProjectStatus status)
+ {
+ Name = status.Name;
+ Status = status.BuildStatus.ToString();
+ LastBuildTime = status.LastBuildDate;
+ Activity = status.Activity.ToString();
+ LastBuildLabel = status.LastBuildLabel;
+ WebUrl = status.WebURL;
+ }
+
+ ///
+ /// The name of the project.
+ ///
+ [DataMember]
+ public string Name { get; set; }
+
+ ///
+ /// The status of the project.
+ ///
+ [DataMember]
+ public string Status { get; set; }
+
+ ///
+ /// The current activity of the project.
+ ///
+ [DataMember]
+ public string Activity { get; set; }
+
+ ///
+ /// The last build label of the project.
+ ///
+ [DataMember]
+ public string LastBuildLabel { get; set; }
+
+ ///
+ /// The date and time this project was last built.
+ ///
+ [DataMember]
+ public DateTime LastBuildTime { get; set; }
+
+ ///
+ /// The URL to the dashboard for this project.
+ ///
+ [DataMember]
+ public string WebUrl { get; set; }
+ }
+}
Index: WcfExtension/DataObjects/Queue.cs
===================================================================
--- WcfExtension/DataObjects/Queue.cs (revision 0)
+++ WcfExtension/DataObjects/Queue.cs (revision 0)
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using ThoughtWorks.CruiseControl.Remote;
+
+namespace ThoughtWorks.CruiseControl.WcfExtension.DataObjects
+{
+ ///
+ /// Defines a queue.
+ ///
+ [DataContract(Namespace = "http://ccnet.thoughtworks.com/1/5/wcf")]
+ public class Queue
+ {
+ ///
+ /// Starts a new blank queue.
+ ///
+ public Queue() { }
+
+ ///
+ /// Starts a new queue from a queue snapshot.
+ ///
+ /// The source snapshot.
+ public Queue(QueueSnapshot snapshot)
+ {
+ Name = snapshot.QueueName;
+ Requests = (from request in snapshot.Requests.OfType()
+ select new QueueRequest(request)).ToList();
+ }
+
+ ///
+ /// The name of the queue.
+ ///
+ [DataMember]
+ public string Name { get; set; }
+
+ ///
+ /// The currently queued requests.
+ ///
+ [DataMember]
+ public List Requests { get; set; }
+ }
+}
Index: WcfExtension/DataObjects/QueueRequest.cs
===================================================================
--- WcfExtension/DataObjects/QueueRequest.cs (revision 0)
+++ WcfExtension/DataObjects/QueueRequest.cs (revision 0)
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using ThoughtWorks.CruiseControl.Remote;
+
+namespace ThoughtWorks.CruiseControl.WcfExtension.DataObjects
+{
+ ///
+ /// Defines a queue.
+ ///
+ [DataContract(Namespace = "http://ccnet.thoughtworks.com/1/5/wcf")]
+ public class QueueRequest
+ {
+ ///
+ /// Starts a new blank queue.
+ ///
+ public QueueRequest() { }
+
+ ///
+ /// Starts a new queue from a queue snapshot.
+ ///
+ /// The queued request.
+ public QueueRequest(QueuedRequestSnapshot request)
+ {
+ ProjectName = request.ProjectName;
+ Activity = request.Activity.ToString();
+ }
+
+ ///
+ /// The name of the queued project.
+ ///
+ [DataMember]
+ public string ProjectName { get; set; }
+
+ ///
+ /// The current activity of the queued project.
+ ///
+ [DataMember]
+ public string Activity { get; set; }
+ }
+}
Index: WcfExtension/DataObjects/Snapshot.cs
===================================================================
--- WcfExtension/DataObjects/Snapshot.cs (revision 0)
+++ WcfExtension/DataObjects/Snapshot.cs (revision 0)
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using ThoughtWorks.CruiseControl.Remote;
+
+namespace ThoughtWorks.CruiseControl.WcfExtension.DataObjects
+{
+ ///
+ /// Provides a snapshot of the current server status.
+ ///
+ [DataContract(Namespace = "http://ccnet.thoughtworks.com/1/5/wcf")]
+ public class Snapshot
+ {
+ ///
+ /// Initialise a new blank snapshot.
+ ///
+ public Snapshot() { }
+
+ ///
+ /// Initialise a new snapshot from a cruise server snapshot.
+ ///
+ /// The cruise server snapshot.
+ public Snapshot(CruiseServerSnapshot serverSnapshot)
+ {
+ // Copy over the projects
+ Projects = new List();
+ Projects.AddRange(from project in serverSnapshot.ProjectStatuses
+ select new Project(project));
+
+ // Copy over the queues
+ Queues = new List();
+ foreach (QueueSnapshot queueSnapshot in serverSnapshot.QueueSetSnapshot.Queues)
+ {
+ Queues.Add(new Queue(queueSnapshot));
+ }
+ }
+
+ ///
+ /// The current list of projects.
+ ///
+ [DataMember]
+ public List Projects { get; set; }
+
+ ///
+ /// The current list of queues.
+ ///
+ [DataMember]
+ public List Queues { get; set; }
+
+ ///
+ /// The version of the server.
+ ///
+ [DataMember]
+ public string ServerVersion { get; set; }
+ }
+}
Index: WcfExtension/ICruiseControlContract.cs
===================================================================
--- WcfExtension/ICruiseControlContract.cs (revision 0)
+++ WcfExtension/ICruiseControlContract.cs (revision 0)
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.ServiceModel;
+using System.ServiceModel.Web;
+using ThoughtWorks.CruiseControl.Remote;
+using ThoughtWorks.CruiseControl.WcfExtension.DataObjects;
+
+namespace ThoughtWorks.CruiseControl.WcfExtension
+{
+ ///
+ /// The contract of operations that will be provided.
+ ///
+ [ServiceContract(Namespace = "http://ccnet.thoughtworks.com/1/5/wcf")]
+ public interface ICruiseControlContract
+ {
+ #region RetrieveVersion()
+ ///
+ /// Gets the current version of the server.
+ ///
+ /// The current version number of the server.
+ [OperationContract]
+ string RetrieveVersion();
+ #endregion
+
+ #region RetrieveSnapshot()
+ ///
+ /// Retrieves a snapshot of the current status of the server.
+ ///
+ /// The snapshot of the server.
+ [OperationContract]
+ [WebGet(UriTemplate="snapshot", ResponseFormat=WebMessageFormat.Xml)]
+ Snapshot RetrieveSnapshot();
+ #endregion
+
+ #region RetrieveProjects()
+ ///
+ /// Retrieves a list of all the projects configured on the server.
+ ///
+ /// The list of projects.
+ [OperationContract]
+ [WebGet(UriTemplate = "projects", ResponseFormat = WebMessageFormat.Xml)]
+ List RetrieveProjects();
+ #endregion
+
+ #region ForceBuild()
+ ///
+ /// Forces the build of a project.
+ ///
+ /// The name of the project to build.
+ [OperationContract]
+ [WebInvoke(UriTemplate="projects/{projectName}/build")]
+ [FaultContract(typeof(ServerFault))]
+ void ForceBuild(string projectName);
+ #endregion
+ }
+}
Index: WcfExtension/ObjectExtensions.cs
===================================================================
--- WcfExtension/ObjectExtensions.cs (revision 0)
+++ WcfExtension/ObjectExtensions.cs (revision 0)
@@ -0,0 +1,22 @@
+using System;
+
+namespace ThoughtWorks.CruiseControl.WcfExtension
+{
+ ///
+ /// Provides some helper methods for working with object data.
+ ///
+ public static class ObjectExtensions
+ {
+ #region ValidateNotNull()
+ ///
+ /// Validates that the value is not null.
+ ///
+ /// The object to validate.
+ /// The error message to throw if the value is null.
+ public static void ValidateNotNull(this object value, string errorMessage)
+ {
+ if (value == null) throw new NullReferenceException(errorMessage);
+ }
+ #endregion
+ }
+}
Index: WcfExtension/Properties/AssemblyInfo.cs
===================================================================
--- WcfExtension/Properties/AssemblyInfo.cs (revision 0)
+++ WcfExtension/Properties/AssemblyInfo.cs (revision 0)
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("WcfExtension")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("healthAlliance")]
+[assembly: AssemblyProduct("WcfExtension")]
+[assembly: AssemblyCopyright("Copyright © healthAlliance 2008")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("03d45135-c2c2-4727-aa8e-1dc56af828b9")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
Index: WcfExtension/ServerFault.cs
===================================================================
--- WcfExtension/ServerFault.cs (revision 0)
+++ WcfExtension/ServerFault.cs (revision 0)
@@ -0,0 +1,39 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace ThoughtWorks.CruiseControl.WcfExtension
+{
+ ///
+ /// Defines a standard fault contract that can be passed back to the client saying why an operation failed.
+ ///
+ [DataContract(Namespace = "http://ccnet.thoughtworks.com/1/5/wcf")]
+ public class ServerFault
+ {
+ #region Constructors
+ ///
+ /// Starts a new fault with an operation and reason.
+ ///
+ /// The operation that failed.
+ /// The reason for the failure.
+ public ServerFault(string operation, string reason)
+ {
+ Operation = operation;
+ Reason = reason;
+ }
+ #endregion
+
+ #region Public properties
+ ///
+ /// Gets the operation that failed.
+ ///
+ [DataMember]
+ public string Operation { get; private set; }
+
+ ///
+ /// Gets the reason the operation failed.
+ ///
+ [DataMember]
+ public string Reason { get; private set; }
+ #endregion
+ }
+}
Index: WcfExtension/WcfExtension.csproj
===================================================================
--- WcfExtension/WcfExtension.csproj (revision 0)
+++ WcfExtension/WcfExtension.csproj (revision 0)
@@ -0,0 +1,88 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.21022
+ 2.0
+ {EBB38881-14B2-48A6-9DB6-3AFDC21159F6}
+ Library
+ Properties
+ ThoughtWorks.CruiseControl.WcfExtension
+ WcfExtension
+ v3.5
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+ bin\Debug\WcfExtension.XML
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+ 3.5
+
+
+ 3.0
+
+
+ 3.0
+
+
+ 3.5
+
+
+ 3.5
+
+
+ 3.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {F8113DB9-6C47-4FD1-8A01-655FCF151747}
+ core
+
+
+ {E820CF3B-8C5A-4002-BC16-B7818D3D54A8}
+ Remote
+
+
+
+
+
\ No newline at end of file
Index: WcfExtension/WcfServerExtension.cs
===================================================================
--- WcfExtension/WcfServerExtension.cs (revision 0)
+++ WcfExtension/WcfServerExtension.cs (revision 0)
@@ -0,0 +1,118 @@
+using System;
+using System.ServiceModel;
+using System.ServiceModel.Channels;
+using System.ServiceModel.Description;
+using System.Runtime.Serialization;
+using ThoughtWorks.CruiseControl.Core.Util;
+using ThoughtWorks.CruiseControl.Remote;
+
+namespace ThoughtWorks.CruiseControl.WcfExtension
+{
+ ///
+ /// This class extends CruiseServer and provides a WCF interface to it.
+ ///
+ public class WcfServerExtension
+ : ICruiseServerExtension, IDisposable
+ {
+ #region Private fields
+ private ServiceHost _wcfServiceHost;
+ private ICruiseServer _cruiseServer;
+ #endregion
+
+ #region Public properties
+ #region IsRunning
+ ///
+ /// Gets whether the service is currently listening for requests.
+ ///
+ public bool IsRunning
+ {
+ get { return (_wcfServiceHost.State == CommunicationState.Opened); }
+ }
+ #endregion
+ #endregion
+
+ #region Public methods
+ #region Initialise()
+ ///
+ /// Initialises the service host to use.
+ ///
+ /// The CruiseServer that is initialising this extension.
+ /// The configuration for the extension.
+ public void Initialise(ICruiseServer server, ExtensionConfiguration extensionConfig)
+ {
+ // Validate the input parameters
+ server.ValidateNotNull("Cannot pass in a null server");
+
+ // Store the parameters that we need for later
+ _cruiseServer = server;
+
+ // Create a new service host
+ _wcfServiceHost = new ServiceHost(new CruiseControlImplementation(_cruiseServer));
+ }
+ #endregion
+
+ #region Start()
+ ///
+ /// Starts listening for WCF requests.
+ ///
+ public void Start()
+ {
+ // Start the service host
+ if ((_wcfServiceHost.State != CommunicationState.Opened) &&
+ (_wcfServiceHost.State != CommunicationState.Opening))
+ {
+ Log.Info("Opening service host");
+ _wcfServiceHost.Open();
+ Log.Debug("Service host opened");
+ }
+ }
+ #endregion
+
+ #region Stop()
+ ///
+ /// Stops listening for WCF requests.
+ ///
+ public void Stop()
+ {
+ // Stop the service host without waiting
+ if ((_wcfServiceHost.State != CommunicationState.Closed) &&
+ (_wcfServiceHost.State != CommunicationState.Closing) &&
+ (_wcfServiceHost.State != CommunicationState.Faulted))
+ {
+ Log.Info("Closing service host");
+ _wcfServiceHost.Close();
+ Log.Debug("Service host closed");
+ }
+ }
+ #endregion
+
+ #region Abort()
+ ///
+ /// Stops listening for WCF requests.
+ ///
+ public void Abort()
+ {
+ // Stop the service host and wait for it to close
+ if ((_wcfServiceHost.State != CommunicationState.Closed) &&
+ (_wcfServiceHost.State != CommunicationState.Closing) &&
+ (_wcfServiceHost.State != CommunicationState.Faulted))
+ {
+ Log.Info("Aborting service host");
+ _wcfServiceHost.Abort();
+ Log.Debug("Service host aborted");
+ }
+ }
+ #endregion
+
+ #region Dispose()
+ ///
+ /// Make sure everything is closed.
+ ///
+ public void Dispose()
+ {
+ Abort();
+ }
+ #endregion
+ #endregion
+ }
+}
Index: WcfExtensionUnitTests/CruiseControlImplementationTests.cs
===================================================================
--- WcfExtensionUnitTests/CruiseControlImplementationTests.cs (revision 0)
+++ WcfExtensionUnitTests/CruiseControlImplementationTests.cs (revision 0)
@@ -0,0 +1,90 @@
+using NMock;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.ServiceModel;
+using System.Text;
+using ThoughtWorks.CruiseControl.Remote;
+using ThoughtWorks.CruiseControl.WcfExtension;
+using ThoughtWorks.CruiseControl.WcfExtension.DataObjects;
+
+namespace ThoughtWorks.CruiseControl.WcfExtension.UnitTests
+{
+ [TestFixture]
+ public class CruiseControlImplementationTests
+ {
+ #region Private fields
+ private CruiseControlImplementation _implementation;
+ private DynamicMock _cruiseServerMock;
+ private ProjectStatus[] _projectsList = new ProjectStatus[]{
+ new ProjectStatus("Test Project", IntegrationStatus.Unknown, DateTime.MinValue)
+ };
+ #endregion
+
+ #region Fixture methods
+ [SetUp]
+ public void Initialise()
+ {
+ _cruiseServerMock = new DynamicMock(typeof(ICruiseServer));
+ _implementation = new CruiseControlImplementation((ICruiseServer)_cruiseServerMock.MockInstance);
+ }
+
+ [TearDown]
+ public void CleanUp()
+ {
+ }
+ #endregion
+
+ #region Tests
+ [Test]
+ [ExpectedException(typeof(NullReferenceException), ExpectedMessage = "Cannot pass in a null server")]
+ public void Create_WithoutServer()
+ {
+ var testValue = new CruiseControlImplementation(null);
+ }
+
+ [Test]
+ public void RetrieveVersion()
+ {
+ _cruiseServerMock.ExpectAndReturn("GetVersion", "Test Version");
+ var result = _implementation.RetrieveVersion();
+ _cruiseServerMock.Verify();
+ Assert.AreEqual("Test Version", result);
+ }
+
+ [Test]
+ public void RetrieveSnapshot()
+ {
+ var expected = new CruiseServerSnapshot();
+ _cruiseServerMock.ExpectAndReturn("GetCruiseServerSnapshot", expected);
+ var result = _implementation.RetrieveSnapshot();
+ _cruiseServerMock.Verify();
+ Assert.IsInstanceOfType(typeof(Snapshot), result);
+ }
+
+ [Test]
+ public void ForceBuild_Valid()
+ {
+ _cruiseServerMock.ExpectAndReturn("GetProjectStatus", _projectsList);
+ _cruiseServerMock.Expect("ForceBuild", "Test Project", string.Empty);
+ _implementation.ForceBuild("Test Project");
+ _cruiseServerMock.Verify();
+ }
+
+ [Test]
+ [ExpectedException(typeof(FaultException), ExpectedMessage="Invalid project name")]
+ public void ForceBuild_NoProjectName()
+ {
+ _implementation.ForceBuild(string.Empty);
+ }
+
+ [Test]
+ [ExpectedException(typeof(FaultException), ExpectedMessage = "Invalid project name")]
+ public void ForceBuild_InvalidProjectName()
+ {
+ _cruiseServerMock.SetupResult("GetProjectStatus", _projectsList);
+ }
+ #endregion
+ }
+}
Index: WcfExtensionUnitTests/ObjectExtensionsTests.cs
===================================================================
--- WcfExtensionUnitTests/ObjectExtensionsTests.cs (revision 0)
+++ WcfExtensionUnitTests/ObjectExtensionsTests.cs (revision 0)
@@ -0,0 +1,26 @@
+using System;
+using NUnit.Framework;
+
+namespace ThoughtWorks.CruiseControl.WcfExtension.UnitTests
+{
+ [TestFixture]
+ public class ObjectExtensionsTests
+ {
+ #region Tests
+ [Test]
+ [ExpectedException(typeof(NullReferenceException), ExpectedMessage="Testing")]
+ public void ValidateNotNull_NullValue()
+ {
+ object testValue = null;
+ testValue.ValidateNotNull("Testing");
+ }
+
+ [Test]
+ public void ValidateNotNull_NonNullValue()
+ {
+ object testValue = "Testing";
+ testValue.ValidateNotNull("Testing");
+ }
+ #endregion
+ }
+}
Index: WcfExtensionUnitTests/Properties/AssemblyInfo.cs
===================================================================
--- WcfExtensionUnitTests/Properties/AssemblyInfo.cs (revision 0)
+++ WcfExtensionUnitTests/Properties/AssemblyInfo.cs (revision 0)
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("WcfExtensionUnitTests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("healthAlliance")]
+[assembly: AssemblyProduct("WcfExtensionUnitTests")]
+[assembly: AssemblyCopyright("Copyright © healthAlliance 2008")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("35b873cb-a7d8-4c71-b295-a6b36be30547")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
Index: WcfExtensionUnitTests/test.config
===================================================================
--- WcfExtensionUnitTests/test.config (revision 0)
+++ WcfExtensionUnitTests/test.config (revision 0)
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: WcfExtensionUnitTests/WcfExtensionUnitTests.csproj
===================================================================
--- WcfExtensionUnitTests/WcfExtensionUnitTests.csproj (revision 0)
+++ WcfExtensionUnitTests/WcfExtensionUnitTests.csproj (revision 0)
@@ -0,0 +1,92 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.21022
+ 2.0
+ {3361E47A-0ABC-491B-A4D4-08FFCCDA5FEB}
+ Library
+ Properties
+ ThoughtWorks.CruiseControl.WcfExtension.UnitTests
+ WcfExtensionUnitTests
+ v3.5
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ False
+ ..\..\lib\nmock.dll
+
+
+ False
+ ..\..\tools\nunit\nunit.framework.dll
+
+
+
+ 3.5
+
+
+ 3.0
+
+
+ 3.5
+
+
+ 3.5
+
+
+
+
+
+
+
+
+
+
+
+
+ {E820CF3B-8C5A-4002-BC16-B7818D3D54A8}
+ Remote
+
+
+ {EBB38881-14B2-48A6-9DB6-3AFDC21159F6}
+ WcfExtension
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: WcfExtensionUnitTests/WcfServerExtensionTests.cs
===================================================================
--- WcfExtensionUnitTests/WcfServerExtensionTests.cs (revision 0)
+++ WcfExtensionUnitTests/WcfServerExtensionTests.cs (revision 0)
@@ -0,0 +1,68 @@
+using NMock;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using ThoughtWorks.CruiseControl.Remote;
+using ThoughtWorks.CruiseControl.WcfExtension;
+
+namespace ThoughtWorks.CruiseControl.WcfExtension.UnitTests
+{
+ [TestFixture]
+ public class WcfServerExtensionTests
+ {
+ #region Private fields
+ private WcfServerExtension _extension = new WcfServerExtension();
+ private DynamicMock _cruiseServerMock;
+ #endregion
+
+ #region Fixture methods
+ [SetUp]
+ public void Initialise()
+ {
+ _cruiseServerMock = new DynamicMock(typeof(ICruiseServer));
+ _extension.Initialise((ICruiseServer)_cruiseServerMock.MockInstance, null);
+ }
+
+ [TearDown]
+ public void CleanUp()
+ {
+ _extension.Dispose();
+ }
+ #endregion
+
+ #region Tests
+ [Test]
+ [ExpectedException(typeof(NullReferenceException), ExpectedMessage = "Cannot pass in a null server")]
+ public void Initialise_WithoutServer()
+ {
+ var testValue = new WcfServerExtension();
+ testValue.Initialise(null, null);
+ }
+
+ [Test]
+ public void Start()
+ {
+ _extension.Start();
+ Assert.IsTrue(_extension.IsRunning);
+ }
+
+ [Test]
+ public void Stop()
+ {
+ _extension.Start();
+ _extension.Stop();
+ Assert.IsFalse(_extension.IsRunning);
+ }
+
+ [Test]
+ public void Abort()
+ {
+ _extension.Start();
+ _extension.Abort();
+ Assert.IsFalse(_extension.IsRunning);
+ }
+ #endregion
+ }
+}