/* 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.IO;
using System.Linq;
using System.Reflection;
using Moq;
using NUnit.Framework;
using XenAdmin.Actions;
using XenAdmin.Core;
using XenAPI;
namespace XenAdminTests.XenModelTests
{
///
/// This unit test relies on a list of call versions. For ease, they are included
/// as a csv file
///
/// To update the csv call list:
/// 1. Build your own version on the api bindings.
/// 2. Look in the generated code output directory (not the code that's been packaged) eg. build.hg/output/api-bindings
/// 3. Copy out the "callVersions.csv" and replace the one in TestResources
///
/// To Add New Actions:
/// 1. Go down to the container of actions called "Actions"
/// 2. Add enough unit test code to get your action to run - this is failrly minimal
///
/// To Add a new version:
/// 1. Add a const string containing the correct name as extracted into the csv file
/// 2. Add this to the test ctor
/// 3. Add a DatabaseInfo member
/// 4. Check the unsupportedVersions are up to date
///
[TestFixture]
internal class TestAPICallVersions : UnitTester_TestFixture
{
public TestAPICallVersions()
: base(rio, miami, symc, orlando, george, midnight, cowley,
boston, sanibel, tampa, tallahassee, clearwater, sarasota, thefuture)
{
}
private void SetupConnection(DatabaseInfo id)
{
Mock host = ObjectManager.NewXenObject(id.name);
host.Setup(h => h.ProductVersion()).Returns(id.version);
ObjectManager.MockConnectionFor(id.name).Setup(c => c.Resolve(It.IsAny>())).Returns(host.Object);
}
[Test]
public void CompareCalls()
{
ReadCallList();
foreach (DatabaseInfo info in versionData.Where( v=> !unsupportedVersions.Contains(v.name)))
{
SetupConnection(info);
foreach (AsyncAction myAction in Actions(info.name))
{
List calls = ExtractApiCallList(myAction);
VerifyCalls(calls, info);
}
}
}
#region Data
private const string tampa = "tampa";
private const string boston = "boston";
private const string rio = "rio";
private const string sanibel = "sanibel";
private const string cowley = "cowley";
private const string george = "george";
private const string miami = "miami";
private const string midnight = "midnight-ride";
private const string orlando = "orlando";
private const string symc = "symc";
private const string tallahassee = "tallahassee";
private const string clearwater = "clearwater";
private const string sarasota = "sarasota";
private const string thefuture = "Oct 21, 2015";
///
/// Complete lookup of version numbers
///
private readonly List versionData = new List
{
new DatabaseInfo {name = rio, version = "4.0.0"},
new DatabaseInfo {name = miami, version = "4.1.0"},
new DatabaseInfo {name = symc, version = "4.1.0"},
new DatabaseInfo {name = orlando, version = "5.0.0"},
new DatabaseInfo {name = george, version = "5.5.0"},
new DatabaseInfo {name = midnight, version = "5.6.0"},
new DatabaseInfo {name = cowley, version = "5.6.100"},
new DatabaseInfo {name = boston, version = "6.0.0"},
new DatabaseInfo {name = sanibel, version = "6.0.2"},
new DatabaseInfo {name = tampa, version = "6.1.0"},
new DatabaseInfo {name = tallahassee, version = "6.1.1"},
new DatabaseInfo {name = clearwater, version = "6.1.2"},
new DatabaseInfo {name = sarasota, version = "6.5.0"},
new DatabaseInfo {name = thefuture, version = "9999.9999.9999"},
};
///
/// These versions are not checked for compatability
///
private readonly List unsupportedVersions = new List
{
rio, miami, symc, orlando, george
};
///
/// Additional calls that are added to the call list or overloads for the list
///
private readonly Dictionary additionalCalls = new Dictionary
{
{"vif_destroy", "rio"}
};
///
/// List of actions to test - you need to add one per test of interest
/// TODO: Probably best to add this to a factory at some point
/// TODO: Add more actions
///
///
///
private IEnumerable Actions(string id)
{
yield return ActionFactory.MockActionFor(id, typeof (DeleteVIFAction));
yield return ActionFactory.MockActionFor(id, typeof(AddRemoveRolesAction));
}
private Dictionary CallsInProductSince { get; set; }
#endregion
#region Helper Classes And Methods
private class DatabaseInfo
{
public string name;
public string version;
}
private void ReadCallList()
{
CallsInProductSince = additionalCalls;
using (StreamReader sr = new StreamReader(TestResource("callVersions.csv")))
{
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
if (String.IsNullOrEmpty(line))
continue;
string[] words = line.Split(',');
if (!CallsInProductSince.ContainsKey(words[0]))
CallsInProductSince.Add(words[0], words[1]);
}
}
}
private void VerifyCalls(List callsMade, DatabaseInfo dbInfo)
{
Assert.That(callsMade.Count, Is.GreaterThan(0), "The action made no api calls");
foreach (string call in callsMade)
{
if(call.Contains("_get_allowed_operations"))
continue;
if(!CallsInProductSince.ContainsKey(call))
{
string msg = String.Format("Call missing '{0}'- suggest an update of the call list", call);
Assert.Fail(msg);
}
string closureCall = call;
string callVersionNumber = versionData.Find(d => d.name == CallsInProductSince[closureCall]).version;
Assert.IsTrue(Helpers.productVersionCompare(callVersionNumber, dbInfo.version) <= 0,
String.Format("Call '{0}' made out of version. It was added in version '{1}' but called in version '{2}'", call, callVersionNumber, dbInfo.version));
}
}
private List ExtractApiCallList(AsyncAction myAction)
{
RbacMethodList rbacMethods = new RbacMethodList();
RbacCollectorProxy.GetProxy(rbacMethods);
Session session = new Session(RbacCollectorProxy.GetProxy(rbacMethods), myAction.Connection);
myAction.RunExternal(session);
return rbacMethods.ConvertAll(c => c.Method.ToLower().Replace('.', '_').Replace("async_", ""));
}
private string TestResource(string name)
{
return Path.Combine(Directory.GetCurrentDirectory(), "TestResources", name);
}
#endregion
}
}