// Copyright Keysight Technologies 2012-2019
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at http://mozilla.org/MPL/2.0/.
using System;
using System.Collections.Generic;
using System.Linq;
namespace OpenTap
{
/// Represent an external test plan parameters that can be defined when a test plan is loaded.
public class ExternalParameter
{
/// The name of this parameter.
public string Name { get; }
readonly TestPlan plan;
/// Maps test step to member data.
public IEnumerable>> Properties
=> member.ParameterizedMembers
.Select(x => new KeyValuePair>((ITestStep)x.Source, new []{x.Member}));
/// Gets the list of PropertyInfos associated with this mask entry.
public IEnumerable PropertyInfos => Properties
.SelectMany(x => x.Value)
.Distinct();
///
/// Gets or sets the value of the combined properties. This requires the types to be the same or IConvertibles.
///
public object Value
{
get => member.GetValue(plan);
set => member.SetValue(plan, value);
}
internal void Clean(HashSet steps)
{
var members = member.ParameterizedMembers
.Where(x => steps.Contains(x.Source) == false)
.ToArray();
foreach (var item in members)
item.Member.Unparameterize(member, item.Source);
}
/// Gets the property that is bound by the step with ID step.
///
///
public List GetProperties(ITestStep step)
{
return Properties.Where(x => x.Key == step).SelectMany(x => x.Value).ToList();
}
internal readonly ParameterMemberData member;
/// Constructor for the ExternalParameter.
///
///
public ExternalParameter(TestPlan Plan, string Name)
{
this.plan = Plan;
this.Name = Name;
member = TypeData.GetTypeData(plan).GetMember(Name) as ParameterMemberData;
}
internal ExternalParameter(TestPlan plan, ParameterMemberData parameter)
{
this.plan = plan;
this.Name = parameter.Name;
member = parameter;
}
/// Adds a property to the external parameters.
///
///
public void Add(ITestStep step, IMemberData property)
{
if (step == null)
throw new ArgumentNullException(nameof(step));
if (property == null)
throw new ArgumentNullException(nameof(property));
plan.ExternalParameters.Add(step, property, Name);
}
/// Removes a step from the external parameters.
///
public void Remove(ITestStep step)
{
if (step == null)
throw new ArgumentNullException(nameof(step));
var members = member.ParameterizedMembers;
foreach (var item in members.Where(x => step == x.Source))
item.Member.Unparameterize(member, item.Source);
}
}
/// External test plan parameters.
public class ExternalParameters
{
/// Gets the list of external test plan parameters.
public IReadOnlyList Entries
{
get
{
var fwd = TypeData.GetTypeData(plan).GetMembers().OfType();
return fwd.Select(x => new ExternalParameter(plan, x)).ToList();
}
}
readonly TestPlan plan;
/// Constructor for the ExternalParameters.
///
public ExternalParameters(TestPlan plan)
{
this.plan = plan;
}
static TraceSource log = Log.CreateSource("External Parameters");
/// Adds a step property to the external test plan parameters.
///
///
///
public ExternalParameter Add(ITestStep step, IMemberData setting, string Name = null)
{
if (step == null)
throw new ArgumentNullException(nameof(step));
if (setting == null) // As it otherwise won't raise exception right away.
throw new ArgumentNullException(nameof(setting));
var existing = Find(step, setting);
if (existing != null)
return existing;
if (Name == null)
Name = setting.GetDisplayAttribute().GetFullName();
var newParameter = setting.Parameterize(plan, step, Name);
if (newParameter.ParameterizedMembers.Skip(1).Any())
{
// merge occured.
// See similar code in ExternalParameterSerializer.
if (ParameterManager.UnmergableListType(newParameter))
{
setting.Unparameterize(newParameter, step);
log.Warning("Unable merge parameters {0}, since their types do not support it.", Name);
}
}
return Get(Name);
}
/// Removes a step property from the external parameters.
/// The step owning the property.
/// The property to remove.
/// Un-used parameter.
public void Remove(ITestStep step, IMemberData propertyInfo, string name = null)
{
if (step == null)
throw new ArgumentNullException(nameof(step));
if(propertyInfo == null)
throw new ArgumentNullException(nameof(propertyInfo));
ParameterMemberData fwd = propertyInfo.GetParameter(plan, step) as ParameterMemberData;
if (fwd == null) return;
if(name != null && fwd.Name != name)
throw new InvalidOperationException("Name does not match external parameter name.");
propertyInfo.Unparameterize(fwd, step);
}
/// Ensures that each entry test step is also present the test plan.
public void Clean()
{
var steps = Utils.FlattenHeirarchy(plan.ChildTestSteps, step => step.ChildTestSteps).ToHashSet();
foreach (var entry in Entries.ToList())
entry.Clean(steps);
}
/// Gets an entry by name.
///
///
public ExternalParameter Get(string externalParameterName)
{
if(TypeData.GetTypeData(plan).GetMember(externalParameterName) is ParameterMemberData member)
return new ExternalParameter(plan, member);
return null;
}
///
/// Finds the external parameter that is defined by 'step' and 'property'. If no external parameter is found null is returned.
///
///
///
///
public ExternalParameter Find(ITestStep step, IMemberData property)
{
if (step == null)
throw new ArgumentNullException(nameof(step));
if(property == null)
throw new ArgumentNullException(nameof(property));
var parameter = property.GetParameter(plan, step) as ParameterMemberData;
if(parameter != null)
return new ExternalParameter(plan, parameter);
return null;
}
}
}