/* Copyright (c) Cloud Software Group, Inc. * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.Linq; using Moq; using XenAdmin; using XenAdmin.Model; using XenAdmin.Network; using XenAPI; namespace XenAdminTests { public interface IObjectManager : IDisposable { List AllConnections { get; } void CreateNewConnection(params string[] ids); } /// /// Provide the infrastructure for the mock connection to the server /// connection will provide mock versions of a cache, proxy and session, pre-setup /// /// This class provides access to the Mock objects it auto generates an connects up /// The mocked connection is pre-setup to provide access to the underlying objects /// /// Note: if you cannot mock a method, you will need to make it virtual /// public class MockObjectManager : IObjectManager { private readonly Dictionary> connections = new Dictionary>(); private readonly Dictionary> caches = new Dictionary>(); private readonly Dictionary> xenObjects = new Dictionary>(); private readonly Dictionary> clients = new Dictionary>(); private readonly Dictionary> sessions = new Dictionary>(); private readonly Mock config = new Mock(); private const string defaultUserName = "Default User Name"; private const string defaultPassword = "Default Password"; public Mock MockConfigProvider{ get { return config; } } public List AllConnections { get { return connections.Values.ToList().ConvertAll(c => c.Object); } } public IXenConnection ConnectionFor(string connectionId) { return connections[connectionId].Object; } /// /// Get the mock form of a generated connection /// You can set your expectations for connection methods /// The basic ones are already provided /// /// /// public Mock MockConnectionFor(string connectionId) { return connections[connectionId]; } /// /// Get the mock for of the cache for a specified connection id /// The XenObject types are pre-populated if you use the NewXenObject method /// /// /// public Mock MockCacheFor(string connectionId) { return caches[connectionId]; } /// /// Provide access to the underlying proxy for the connection - this allows you to intercept static API calls /// So, if your class calls Pool.get_uuid, you need to mock setup the method pool_get_uuid /// /// /// public Mock MockProxyFor(string connectionId) { return clients[connectionId]; } /// /// Provide access to a mock session for the connection /// /// /// public Mock MockSessionFor(string connectionId) { return sessions[connectionId]; } public void ClearXenObjects(string connectionId) { xenObjects[connectionId].Clear(); } public void CreateNewConnection(params string[] connectionId) { CreateNewConnection(defaultUserName, defaultPassword, connectionId); } public void CreateNewConnection(string username, string password, params string[] connectionId) { foreach (string db in connectionId) { CreateNewConnection(db, username, password); } } protected void CreateNewConnection(string connectionId, string username, string password) { XenAdminConfigManager.Provider = config.Object; connections.Add(connectionId, new Mock()); xenObjects.Add(connectionId, new List()); caches.Add(connectionId, new Mock()); clients.Add(connectionId, new Mock()); sessions.Add(connectionId, new Mock(clients[connectionId].Object, connections[connectionId].Object)); connections[connectionId].Setup(c => c.Session).Returns(sessions[connectionId].Object); connections[connectionId].Setup(c => c.DuplicateSession()).Returns(sessions[connectionId].Object); connections[connectionId].Setup(c => c.Cache).Returns(caches[connectionId].Object); connections[connectionId].Setup(c => c.Name).Returns(connectionId); connections[connectionId].Setup(c => c.Username).Returns(username); connections[connectionId].Setup(c => c.Password).Returns(password); RefreshCache(connectionId); } /// /// Refresh the cache /// If a new XenObject type has been added to the API it'll need to be added here too /// /// public void RefreshCache(string connectionId) { caches[connectionId].Setup(c => c.Bonds).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.Folders).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.GPU_groups).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.Host_cpus).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.Hosts).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.Messages).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.Networks).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.PBDs).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.PGPUs).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.PIFs).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.Pool_patches).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.Pools).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.Roles).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.SMs).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.SRs).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.Subjects).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.Tunnels).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.VBDs).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.VDIs).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.VGPUs).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.VIFs).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.VMSSs).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.VM_appliances).Returns(GeneratedXenObjects(connectionId)); caches[connectionId].Setup(c => c.VMs).Returns(GeneratedXenObjects(connectionId)); } private T[] GeneratedXenObjects(string connectionId) where T : XenObject { return GeneratedXenObjectsMocks(connectionId).ConvertAll(m => m.Object).ToArray(); } public List> GeneratedXenObjectsMocks(string connectionId) where T : XenObject { List> mockObjects = new List>(); foreach (Mock mockedObject in xenObjects[connectionId]) { if (typeof(T) == mockedObject.Object.GetType().BaseType) mockObjects.Add(mockedObject as Mock); } return mockObjects; } /// /// Generate a mock object for the connection with specified Id /// The mock's underlying object will become accessible via. a call to the cache as well as the mock.Object /// The cache is refreshed automatically /// /// The method *must* be virtual to be mocked. You may need to add the virtual keyword to your method /// /// sr = ObjectManager.NewXenObject("id"); /// sr.Setup(s => s.HBALunPerVDI).Returns(true); /// SR[] srs = ObjectManager.ConnectionFor("id").Cache.SRs; /// Assert.IsTrue(srs[0].HBALunPerVDI); /// /// ]]> /// /// /// /// automatically refresh the cache /// /// public Mock NewXenObject(string connectionId, bool refreshCache) where T : XenObject { Mock mock = new Mock(); if(!connections.ContainsKey(connectionId)) throw new KeyNotFoundException("Connection id missing: " + connectionId); mock.Object.Connection = connections[connectionId].Object; mock.Object.opaque_ref = string.Format("OpaqueRef:{0}", DateTime.UtcNow.Ticks); xenObjects[connectionId].Add(mock); if(refreshCache) RefreshCache(connectionId); return mock; } /// /// Create XenObject and auto-refresh the cache /// /// /// /// public Mock NewXenObject(string connectionId) where T : XenObject { return NewXenObject(connectionId, true); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public void VerifyAllMocks(string connectionId) { connections[connectionId].VerifyAll(); caches[connectionId].VerifyAll(); clients[connectionId].VerifyAll(); sessions[connectionId].VerifyAll(); xenObjects[connectionId].ForEach(m=>m.VerifyAll()); config.VerifyAll(); } private bool disposed; protected void Dispose(bool disposing) { if(!disposed) { if(!disposing) { connections.Clear(); caches.Clear(); xenObjects.Clear(); clients.Clear(); sessions.Clear(); XenAdminConfigManager.Provider = new WinformsXenAdminConfigProvider(); //Bit hacky but stops subsequent UI tests from failing } disposed = true; } } } }