// 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; } } }