using System;
|
using System.Collections.Generic;
|
using System.ComponentModel;
|
using System.Linq;
|
using System.Xml.Linq;
|
using NUnit.Framework;
|
using OpenTap.EngineUnitTestUtils;
|
using OpenTap.Package;
|
using OpenTap.Plugins.BasicSteps;
|
using OpenTap.UnitTests;
|
|
namespace OpenTap.Engine.UnitTests
|
{
|
[TestFixture]
|
public class SerializerTests
|
{
|
public class TestPlanWithMetaData : TestPlan
|
{
|
[MetaData(Name= "X Setting")]
|
public string X { get; set; }
|
|
[MetaData]
|
public string Y { get; set; }
|
}
|
[Test]
|
public void TestSerializeTestPlanMetaData()
|
{
|
var plan = new TestPlanWithMetaData();
|
var xml = plan.SerializeToString();
|
var xdoc = XDocument.Parse(xml);
|
Assert.AreEqual("X Setting", xdoc.Root.Element("X").Attribute("Metadata").Value);
|
Assert.AreEqual("Y", xdoc.Root.Element("Y").Attribute("Metadata").Value);
|
}
|
public class PackageVersionTestStep : TestStep
|
{
|
public PackageVersion[] PackageVersion { get; set; }
|
public override void Run()
|
{
|
|
}
|
}
|
|
[Test]
|
[TestCase("No Trailing Space")]
|
[TestCase("Yes Trailing Space ")]
|
public void TestSerializeInstrumentsWithTrailingSpaceInName(string name)
|
{
|
using var session = Session.Create(SessionOptions.OverlayComponentSettings);
|
var ins = new ScpiInstrument() { Name = name };
|
var scpiStep1 = new ScpiTestStep() { Instrument = ins };
|
InstrumentSettings.Current.Add(ins);
|
var plan = new TestPlan()
|
{
|
ChildTestSteps = { scpiStep1 }
|
};
|
|
{ // Verify deserialization completed without errors
|
var str = plan.SerializeToString();
|
var ser = new TapSerializer();
|
var plan2 = ser.DeserializeFromString(str) as TestPlan;
|
var scpiStep2 = plan2.ChildTestSteps[0] as ScpiTestStep;
|
|
Assert.AreSame(scpiStep1.Instrument, scpiStep2.Instrument);
|
Assert.That(ser.Errors.Count(), Is.EqualTo(0));
|
}
|
}
|
|
[Test]
|
public void TestPackageVersionLicenseSerializer()
|
{
|
var packageVersion = new PackageVersion("pkg", SemanticVersion.Parse("1.0.0"), "Linux", CpuArchitecture.AnyCPU, DateTime.Now,
|
new List<string>()
|
{
|
"License 1",
|
"License 2"
|
});
|
var ser = new TapSerializer();
|
var str = ser.SerializeToString(packageVersion);
|
var des = ser.DeserializeFromString(str);
|
|
Assert.AreEqual(packageVersion, des);
|
CollectionAssert.AreEqual(packageVersion.Licenses, ((des as PackageVersion)!).Licenses);
|
}
|
|
[Test]
|
public void SerializeInputInTestPlanReferenceTest()
|
{
|
using (Session.Create(SessionOptions.OverlayComponentSettings))
|
{
|
TestTraceListener tapTraceListener = new TestTraceListener();
|
Log.AddListener(tapTraceListener);
|
|
const string PlanName = "ParameterizedIfStep.TapPlan";
|
{ // Create child plan
|
var childPlan = new TestPlan();
|
var ifstep = new IfStep();
|
childPlan.ChildTestSteps.Add(ifstep);
|
var a = AnnotationCollection.Annotate(ifstep);
|
var m = a.GetMember(nameof(ifstep.InputVerdict));
|
var items = m.Get<MenuAnnotation>().MenuItems.ToArray();
|
var icons = items.ToLookup(item =>
|
item.Get<IIconAnnotation>()?.IconName ?? "");
|
var parameterizeOnTestPlan = icons[IconNames.ParameterizeOnTestPlan].First();
|
var method = parameterizeOnTestPlan.Get<IMethodAnnotation>();
|
method.Invoke();
|
|
childPlan.Save(PlanName);
|
}
|
|
tapTraceListener.Flush();
|
Assert.IsEmpty(tapTraceListener.ErrorMessage);
|
|
// Try to load the child plan
|
{
|
var plan = new TestPlan()
|
{
|
ChildTestSteps =
|
{
|
new TestPlanReference()
|
{
|
Filepath = new MacroString() { Text = PlanName }
|
}
|
}
|
};
|
|
tapTraceListener.Flush();
|
Assert.IsEmpty(tapTraceListener.ErrorMessage);
|
|
var a = AnnotationCollection.Annotate(plan.ChildTestSteps[0]);
|
var m = a.Get<IMembersAnnotation>().Members.First(m => m.Name == "Load Test Plan");
|
var load = m.Get<IMethodAnnotation>();
|
load.Invoke();
|
tapTraceListener.Flush();
|
Assert.IsEmpty(tapTraceListener.ErrorMessage);
|
}
|
}
|
}
|
|
[Test]
|
public void TestPackageDependencySerializer()
|
{
|
var plan = new TestPlan()
|
{
|
ChildTestSteps = { new DelayStep() }
|
};
|
var packageVersion = new PackageVersion("pkg", SemanticVersion.Parse("1.0.0"), "Linux", CpuArchitecture.AnyCPU, DateTime.Now, new List<string>());
|
|
{ // verify that a serialized plan has package dependencies
|
var ser = new TapSerializer();
|
var str = ser.SerializeToString(plan);
|
CollectionAssert.IsEmpty(ser.Errors);
|
var elem = XElement.Parse(str);
|
Assert.AreEqual(1, elem.Elements("Package.Dependencies").Count());
|
}
|
{ // verify that a serialized collection of plans has package dependencies
|
var ser = new TapSerializer();
|
var plans = new TestPlan[]
|
{
|
plan,
|
plan
|
};
|
var str = ser.SerializeToString(plans);
|
CollectionAssert.IsEmpty(ser.Errors);
|
var elem = XElement.Parse(str);
|
Assert.AreEqual(1, elem.Elements("Package.Dependencies").Count());
|
}
|
{ // verify that a serialized package version does not have package dependencies
|
var ser = new TapSerializer();
|
var str = ser.SerializeToString(packageVersion);
|
CollectionAssert.IsEmpty(ser.Errors);
|
var elem = XElement.Parse(str);
|
Assert.AreEqual(0, elem.Elements("Package.Dependencies").Count());
|
|
var deserialized = ser.DeserializeFromString(str);
|
Assert.AreEqual(packageVersion, deserialized);
|
}
|
{ // verify that a serialized list of package versions does not have package dependencies
|
var ser = new TapSerializer();
|
var versions = new PackageVersion[] { packageVersion };
|
var str = ser.SerializeToString(versions);
|
CollectionAssert.IsEmpty(ser.Errors);
|
var elem = XElement.Parse(str);
|
Assert.AreEqual(0, elem.Elements("Package.Dependencies").Count());
|
|
var deserialized = ser.DeserializeFromString(str);
|
if (deserialized is PackageVersion[] versions2)
|
{
|
Assert.AreEqual(1, versions2.Count());
|
CollectionAssert.AreEqual(versions, versions2, "Deserialized versions were different from the serialized versions.");
|
}
|
else
|
{
|
Assert.Fail($"Failed to deserialize serialized version array.");
|
}
|
}
|
{ // Verify that a test plan still has package dependencies when it contains a PackageVersion property
|
var ser = new TapSerializer();
|
var plan2 = new TestPlan()
|
{
|
ChildTestSteps =
|
{
|
new PackageVersionTestStep()
|
{
|
PackageVersion = new[] { packageVersion }
|
}
|
}
|
};
|
var str = ser.SerializeToString(plan2);
|
CollectionAssert.IsEmpty(ser.Errors);
|
var elem = XElement.Parse(str);
|
Assert.AreEqual(1, elem.Elements("Package.Dependencies").Count());
|
}
|
}
|
|
public interface IPSU : IInstrument
|
{
|
|
}
|
|
public class InstrA : Instrument, IPSU
|
{
|
|
}
|
public class InstrB : Instrument, IPSU
|
{
|
|
}
|
|
public class StepA : TestStep
|
{
|
public IPSU Instr { get; set; }
|
public List<IPSU> Instrs { get; set; } = new List<IPSU>();
|
|
public override void Run() { }
|
}
|
|
[Test]
|
public void TestSerializingResourceReferences()
|
{
|
using (Session.Create())
|
{
|
InstrumentSettings.Current.Clear();
|
var instr = new InstrA {Name = "AABBCC"};
|
var instr2 = new InstrB {Name = "AABBCC"};
|
var instr3 = new InstrB();
|
var instr4 = new InstrB();
|
InstrumentSettings.Current.Add(instr);
|
var step = new StepA()
|
{
|
Instr = instr
|
};
|
step.Instrs.Add(instr);
|
var plan = new TestPlan();
|
plan.ChildTestSteps.Add(step);
|
|
var x = plan.SerializeToString();
|
InstrumentSettings.Current.Remove(instr);
|
InstrumentSettings.Current.AddRange([instr3, instr2, instr4]);
|
var serializer = new TapSerializer();
|
var plan2 = (TestPlan)serializer.DeserializeFromString(x);
|
var step2 = (StepA)plan2.ChildTestSteps[0];
|
Assert.AreEqual(instr2, step2.Instr);
|
Assert.AreEqual(instr2, step2.Instrs[0]);
|
var msg = serializer.XmlMessages.FirstOrDefault();
|
StringAssert.Contains("Selected resource 'AABBCC' of type InstrB instead of declared type InstrA.", msg.ToString());
|
}
|
}
|
|
public class StepWithLicenseException : TestStep
|
{
|
public static bool ThrowLicense;
|
public StepWithLicenseException()
|
{
|
if (ThrowLicense)
|
throw new LicenseException(GetType());
|
}
|
public override void Run() { }
|
}
|
|
|
[Test]
|
public void DeserializeLicensedTest()
|
{
|
TestTraceListener tapTraceListener = new TestTraceListener();
|
|
using (Session.Create(SessionOptions.RedirectLogging))
|
{
|
Log.AddListener(tapTraceListener);
|
var step = new StepWithLicenseException();
|
var plan = new TestPlan();
|
plan.Steps.Add(step);
|
var str = plan.SerializeToString();
|
try
|
{
|
StepWithLicenseException.ThrowLicense = true;
|
new TapSerializer().DeserializeFromString(str);
|
}
|
finally
|
{
|
StepWithLicenseException.ThrowLicense = false;
|
}
|
}
|
// there should only be one error message (the license error)
|
Assert.AreEqual(1, tapTraceListener.ErrorMessage.Count);
|
Assert.AreEqual(1,
|
tapTraceListener.ErrorMessage.Count(x =>
|
x.Contains("Unable to read StepWithLicenseException. A valid license cannot be granted")));
|
}
|
|
[AllowAnyChild]
|
public class TestSerializerChildSteps : TestStep
|
{
|
public TestSerializerChildSteps()
|
{
|
ChildTestSteps.Add(new DelayStep());
|
ChildTestSteps.Add(new DelayStep());
|
ChildTestSteps.Add(new DelayStep());
|
ChildTestSteps.Add(new DelayStep());
|
}
|
public override void Run()
|
{
|
}
|
}
|
|
[Test]
|
public void TestDeserializeStep()
|
{
|
{ // round one
|
var s = new TestSerializerChildSteps();
|
s.ChildTestSteps.Clear();
|
var xml = new TapSerializer().SerializeToString(s);
|
var s2 = new TapSerializer().DeserializeFromString(xml) as TestSerializerChildSteps;
|
|
Assert.AreEqual(s.ChildTestSteps.Count, s2.ChildTestSteps.Count);
|
}
|
{ // round two
|
var chld = new TestSerializerChildSteps();
|
var plan = new TestPlan()
|
{
|
ChildTestSteps = { chld }
|
};
|
|
var expected = chld.ChildTestSteps.Count;
|
|
Assert.AreEqual(expected, plan.ChildTestSteps[0].ChildTestSteps.Count);
|
Reload();
|
Assert.AreEqual(expected, plan.ChildTestSteps[0].ChildTestSteps.Count);
|
while (plan.ChildTestSteps[0].ChildTestSteps.Count > 0)
|
{
|
expected = plan.ChildTestSteps[0].ChildTestSteps.Count - 1;
|
plan.ChildTestSteps[0].ChildTestSteps.RemoveAt(0);
|
Reload();
|
Assert.AreEqual(expected, plan.ChildTestSteps[0].ChildTestSteps.Count);
|
}
|
|
void Reload()
|
{
|
var planXml = new TapSerializer().SerializeToString(plan);
|
plan = new TapSerializer().DeserializeFromString(planXml) as TestPlan;
|
}
|
}
|
}
|
|
public class DynamicDependencySerializerPlugin : TapSerializerPlugin, ITapSerializerPluginDependencyMarker
|
{
|
|
public override double Order => 1000;
|
HashSet<XElement> visitedNodes = new HashSet<XElement>();
|
static XName testPlanName = "TestPlan";
|
public override bool Deserialize(XElement node, ITypeData t, Action<object> setter)
|
{
|
if (node.Name == testPlanName)
|
{
|
if (visitedNodes.Add(node))
|
return Serializer.Deserialize(node, setter, t);
|
}
|
return false;
|
}
|
public override bool Serialize(XElement node, object obj, ITypeData expectedType)
|
{
|
if (visitedNodes.Add(node) == false) return false;
|
return Serializer.Serialize(node, obj, expectedType, true);
|
}
|
|
public bool NeededForDeserialization
|
{
|
get;
|
set;
|
}
|
}
|
|
public class NestedObject2
|
{
|
public double X { get; set; }
|
public double Y { get; set; }
|
}
|
|
public class NestedObject
|
{
|
public NestedObject2 A { get; set; } = new NestedObject2() {X = 1, Y = 2};
|
public NestedObject2 B { get; set; } = new NestedObject2() {X = 4, Y = 10};
|
public List<NestedObject2> Objects { get; set; }= new List<NestedObject2>();
|
public double C { get; set; } = 5.0;
|
}
|
|
[Test]
|
public void TestSerializeNestedObject()
|
{
|
var obj0 = new NestedObject();
|
obj0.C = 10;
|
obj0.A = new NestedObject2 {X = 3, Y = 3};
|
obj0.B = new NestedObject2 {X = 10, Y = 11};
|
obj0.Objects.Add(new NestedObject2 {X = 1, Y = -1});
|
var serializer = new TapSerializer();
|
var xml = serializer.SerializeToString(obj0);
|
var obj1 = (NestedObject) serializer.DeserializeFromString(xml);
|
|
Assert.AreEqual(10.0, obj1.C);
|
Assert.AreEqual(3.0, obj1.A.X);
|
Assert.AreEqual(3.0, obj1.A.Y);
|
Assert.AreEqual(10.0, obj1.B.X);
|
Assert.AreEqual(11.0, obj1.B.Y);
|
|
Assert.AreEqual(1, obj1.Objects.Count);
|
Assert.AreEqual(1, obj1.Objects[0].X);
|
Assert.AreEqual(-1, obj1.Objects[0].Y);
|
|
}
|
|
[TestCase(true)]
|
[TestCase(false)]
|
public void SerializerMaybeUsedType(bool addSerializerPluginDependency)
|
{
|
var serializer = new TapSerializer();
|
var obj = new TestPlan();
|
serializer.GetSerializer<DynamicDependencySerializerPlugin>().NeededForDeserialization = addSerializerPluginDependency;
|
serializer.SerializeToString(obj);
|
var usedTypes = serializer.GetUsedTypes();
|
var wasUsed = usedTypes.Contains(TypeData.FromType(typeof(DynamicDependencySerializerPlugin)));
|
if (addSerializerPluginDependency)
|
{
|
Assert.IsTrue(wasUsed);
|
}
|
else
|
{
|
Assert.IsFalse(wasUsed);
|
}
|
}
|
|
public class ObjectWithNullable
|
{
|
// if the value is 0x1337 means it has not been set. If its deliberately set to null
|
// we should not treat it as default.
|
public double? NullableDouble { get; set; } = 0x1337;
|
}
|
|
[Test]
|
public void TestSerializerDeferOrder()
|
{
|
var ser = new TapSerializer();
|
int count = 0;
|
|
Action Assertion(int expected)
|
{
|
return () =>
|
{
|
count++;
|
Assert.That(expected, Is.EqualTo(count));
|
};
|
}
|
|
// Even though this defer is inserted first, it should run last
|
ser.DeferLoad(Assertion(7), TapSerializer.DeferredLoadOrder.ExternalParameter);
|
// This defer should run after all normal Defers
|
ser.DeferLoad(Assertion(5), TapSerializer.DeferredLoadOrder.ParameterMemberDataSetter);
|
// These defers should run first although they are inserted late
|
ser.DeferLoad(Assertion(1), TapSerializer.DeferredLoadOrder.Normal);
|
ser.DeferLoad(Assertion(2), TapSerializer.DeferredLoadOrder.Normal);
|
ser.DeferLoad(Assertion(3), TapSerializer.DeferredLoadOrder.Normal);
|
|
ser.DeferLoad(() =>
|
{
|
// The defer inserted in this defer should run before parameter defers
|
ser.DeferLoad(Assertion(4), TapSerializer.DeferredLoadOrder.Normal);
|
}, TapSerializer.DeferredLoadOrder.Normal);
|
|
ser.DeferLoad(() =>
|
{
|
// This defer is inserted during the parameter defer phase, and is expected to run before the external parameter defers
|
ser.DeferLoad(Assertion(6), TapSerializer.DeferredLoadOrder.Normal);
|
}, TapSerializer.DeferredLoadOrder.ParameterMemberDataSetter);
|
|
Assert.That(count, Is.EqualTo(0));
|
ser.Flush();
|
Assert.That(count, Is.EqualTo(7));
|
}
|
|
[Test]
|
public void SerializeDeserializePropertyWithNullable()
|
{
|
var a = new ObjectWithNullable
|
{
|
NullableDouble = 5
|
};
|
var b = new ObjectWithNullable()
|
{
|
NullableDouble = null
|
};
|
|
// The problem only occurs if the type is not explicit. Note in the future we will probably
|
// not require explicit type since it can be inferred through reflection, so this should work without.
|
var aXml = new TapSerializer().SerializeToString(a).Replace("type=\"System.Double\"", "");
|
var bXml = new TapSerializer().SerializeToString(b).Replace("type=\"System.Double\"", "");
|
var a2 = (ObjectWithNullable)new TapSerializer().DeserializeFromString(aXml);
|
var b2 = (ObjectWithNullable)new TapSerializer().DeserializeFromString(bXml);
|
Assert.AreEqual(a.NullableDouble, a2.NullableDouble);
|
Assert.AreEqual(b.NullableDouble, b2.NullableDouble);
|
|
|
|
}
|
}
|
}
|