chr
2026-04-05 fe750b791d5b517cc4e9bc8e99a9a75139a0cfba
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
//            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
{
 
    /// <summary> Represent an external test plan parameters that can be defined when a test plan is loaded. </summary>
    public class ExternalParameter
    {
        /// <summary> The name of this parameter. </summary>
        public string Name { get; }
 
        readonly TestPlan plan;
        /// <summary> Maps test step to member data. </summary>
        public IEnumerable<KeyValuePair<ITestStep, IEnumerable<IMemberData>>> Properties
            => member.ParameterizedMembers
                .Select(x => new KeyValuePair<ITestStep, IEnumerable<IMemberData>>((ITestStep)x.Source, new []{x.Member}));
 
 
        /// <summary> Gets the list of PropertyInfos associated with this mask entry. </summary>
        public IEnumerable<IMemberData> PropertyInfos => Properties
            .SelectMany(x => x.Value)
            .Distinct();
 
        /// <summary>
        /// Gets or sets the value of the combined properties. This requires the types to be the same or IConvertibles.
        /// </summary>
        public object Value
        {
            get => member.GetValue(plan);
            set => member.SetValue(plan, value);
        }
 
        internal void Clean(HashSet<ITestStep> steps)
        {
            var members = member.ParameterizedMembers
                .Where(x => steps.Contains(x.Source) == false)
                .ToArray();
            foreach (var item in members)
                item.Member.Unparameterize(member, item.Source);
        }
 
        /// <summary> Gets the property that is bound by the step with ID step. </summary>
        /// <param name="step"></param>
        /// <returns></returns>
        public List<IMemberData> GetProperties(ITestStep step)
        {
            return Properties.Where(x => x.Key == step).SelectMany(x => x.Value).ToList();
        }
 
        internal readonly ParameterMemberData member;
 
        /// <summary> Constructor for the ExternalParameter. </summary>
        /// <param name="Plan"></param>
        /// <param name="Name"></param>
        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;
        }
 
        /// <summary> Adds a property to the external parameters. </summary>
        /// <param name="step"></param>
        /// <param name="property"></param>
        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);
        }
 
        /// <summary> Removes a step from the external parameters. </summary>
        /// <param name="step"></param>
        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);
        }
    }
 
    /// <summary> External test plan parameters. </summary>
    public class ExternalParameters
    {
 
        /// <summary> Gets the list of external test plan parameters. </summary>
        public IReadOnlyList<ExternalParameter> Entries
        {
            get
            {
                var fwd = TypeData.GetTypeData(plan).GetMembers().OfType<ParameterMemberData>();
                return fwd.Select(x => new ExternalParameter(plan, x)).ToList();
            }
        }
 
        readonly TestPlan plan;
 
        /// <summary> Constructor for the ExternalParameters. </summary>
        /// <param name="plan"></param>
        public ExternalParameters(TestPlan plan)
        {
            this.plan = plan;
        }
 
        static TraceSource log = Log.CreateSource("External Parameters"); 
        /// <summary> Adds a step property to the external test plan parameters. </summary>
        /// <param name="step"></param>
        /// <param name="setting"></param>
        /// <param name="Name"></param>
        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);
        }
 
        /// <summary> Removes a step property from the external parameters. </summary>
        /// <param name="step">The step owning the property. </param>
        /// <param name="propertyInfo"> The property to remove. </param>
        /// <param name="name">Un-used parameter. </param>
        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);
        }
 
        /// <summary> Ensures that each entry test step is also present the test plan. </summary>
        public void Clean()
        {
            var steps = Utils.FlattenHeirarchy(plan.ChildTestSteps, step => step.ChildTestSteps).ToHashSet();
            foreach (var entry in Entries.ToList())
                entry.Clean(steps);
        }
 
        /// <summary> Gets an entry by name. </summary>
        /// <param name="externalParameterName"></param>
        /// <returns></returns>
        public ExternalParameter Get(string externalParameterName)
        {
            if(TypeData.GetTypeData(plan).GetMember(externalParameterName) is ParameterMemberData member)
                return new ExternalParameter(plan, member);
            return null;
        }
 
        /// <summary>
        /// Finds the external parameter that is defined by 'step' and 'property'. If no external parameter is found null is returned.
        /// </summary>
        /// <param name="step"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        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;
        }
    }
}