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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
//            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;
using System.IO;
 
namespace OpenTap
{
    #region Data structure
    /// <summary>
    /// A named object.
    /// </summary>
    public interface IAttributedObject
    {
        /// <summary>
        /// Name of this object.  
        /// </summary>
        string Name { get; }
        /// <summary>
        /// String describing this object.  
        /// </summary>
        string ObjectType { get; }
    }
 
    /// <summary>
    /// A named parameter.
    /// </summary>
    public interface IParameter : IAttributedObject
    {
        /// <summary>
        /// Optional name of the group of parameters to which this parameter belongs.  
        /// </summary>
        string Group { get; }
 
        /// <summary>
        /// Value of this parameter.  
        /// </summary>
        IConvertible Value { get; }
    }
 
    /// <summary>
    /// A list of parameters, with a string indexer.
    /// </summary>
    public interface IParameters : IList<IParameter>
    {
        /// <summary>
        /// Get a parameter by name. This can be the ObjectType or Name of a parameter. e.g "OpenTap.Unit" or just "Unit".
        /// </summary>
        /// <returns>Null if the parameter was not found</returns>
        IConvertible this[string Name] { get; }
    }
 
    /// <summary>
    /// An object in a hierarchy with a name and some named properties.
    /// </summary>
    public interface IData : IAttributedObject
    {
        /// <summary>
        /// Parent of this object. 
        /// </summary>
        IData Parent { get; }
        /// <summary>
        /// All parameters that describes this object.
        /// </summary>
        IParameters Parameters { get; }
 
        /// <summary>
        /// Returns an ID that identifies this object.
        /// </summary>
        /// <returns></returns>
        long GetID();
    }
 
    /// <summary>
    /// Contains data that has the same table name, column names, and result data types as a <see cref="ResultTable"/>.
    /// </summary>
    public interface IResultTable : IData
    {
        /// <summary>
        /// Array containing the result columns.  
        /// </summary>
        IResultColumn[] Columns { get; }
    }
 
    /// <summary>
    /// Interface to store <see cref="IResultTable"/> column data.
    /// </summary>
    public interface IResultColumn : IAttributedObject
    {
        /// <summary>
        /// Data in the column.  
        /// </summary>
        Array Data { get; }
    }
    
    /// <summary>
    /// An "extensible Enum" that can be used to describe attachments.
    /// </summary>
    public class AttachmentType : IEquatable<AttachmentType>
    {
        #region Support
        private string _TypeName;
 
        /// <summary>
        /// Creates an attachment type object.
        /// </summary>
        public AttachmentType(string TypeName)
        {
            this._TypeName = TypeName;
        }
 
        /// <summary>
        /// Compares this object to another.
        /// </summary>
        bool IEquatable<AttachmentType>.Equals(AttachmentType other)
        {
            return _TypeName == other._TypeName;
        }
 
        /// <summary>
        /// Compares this object to another.
        /// </summary>
        public override bool Equals(object obj)
        {
            if (obj is AttachmentType)
                return _TypeName == ((AttachmentType)obj)._TypeName;
            else
                return false;
        }
 
        /// <summary>
        /// Returns the hashcode for this object.
        /// </summary>
        public override int GetHashCode()
        {
            return _TypeName.GetHashCode();
        }
        #endregion
 
        /// <summary>
        /// A log file.
        /// </summary>
        public static AttachmentType LogFile { get { return new AttachmentType("LogFile"); } }
        /// <summary>
        /// A TestPlan XML file.
        /// </summary>
        public static AttachmentType TestPlan { get { return new AttachmentType("TestPlan"); } }
    }
#endregion
 
#region Searching
    /// <summary>
    /// The operation for <see cref="SearchParameterValue"/>.
    /// </summary>
    public enum ComparisonOperator
    {
        /// <summary>Two specified values must be the same.</summary>
        Equal,
        /// <summary>Two specified values must be different.</summary>
        NotEqual,
        /// <summary>Parameter must be less than.</summary>
        Less,
        /// <summary>Parameter must be less than or equal.</summary>
        LessEqual,
        /// <summary>Parameter must be greater than.</summary>
        Greater,
        /// <summary>Parameter must be greater than or equal.</summary>
        GreaterEqual,
 
        /// <summary>Parameter must be similar to the value.</summary>
        /// <remarks>This is guaranteed to be true if the two values are equal, but is not otherwise guaranteed to work in any specific way.</remarks>
        Like,
 
        /// <summary>
        /// Value must exist as a parameter name or value. 
        /// </summary>
        Exists
    }
 
    /// <summary>
    /// Operation for <see cref="SearchCombinator"/>.
    /// </summary>
    public enum LogicalOperator
    {
        /// <summary>
        /// Specifies that both conditions must be satisfied.
        /// </summary>
        And,
        /// <summary>
        /// Specifies that at least one of the two conditions must be satisfied.
        /// </summary>
        Or
    }
 
    /// <summary>
    /// The basic search operand from which the other search operands are derived. 
    /// </summary>
    public abstract class SearchOperand
    {
    }
 
    /// <summary> Matches all children of the specified parents. </summary>
    public class SearchChildrenOf : SearchOperand
    {
        /// <summary>
        /// The list of elements to match the children of.
        /// </summary>
        public List<IData> Parents { get; set; }
 
        /// <summary> Matches all children of the specified parents. </summary>
        /// <param name="parents"></param>
        public SearchChildrenOf(List<IData> parents)
        {
            Parents = parents;
        }
    }
 
    /// <summary>
    /// Matches the last test plan run.
    /// </summary>
    public class SearchLastRun : SearchOperand
    {
        /// <summary>
        /// Number of the last runs to select.
        /// </summary>
        public int Count { get; }
 
        /// <summary>
        /// Matches the last test plan run.
        /// </summary>
        public SearchLastRun(int count = 1)
        {
            Count = count;
        }
    }
 
    /// <summary> A binary operation between two other operations. </summary>
    public class SearchCombinator : SearchOperand
    {
        /// <summary>
        /// Left-side operand.  
        /// </summary>
        public SearchOperand A { get; }
 
        /// <summary>
        /// Operation to perform.  
        /// </summary>
        public LogicalOperator Operator { get; }
 
        /// <summary>
        /// Right-side operand.  
        /// </summary>
        public SearchOperand B { get; }
 
        /// <summary> A binary operation between two other operations. </summary>
        public SearchCombinator(SearchOperand a, LogicalOperator logicalOperator, SearchOperand b)
        {
            A = a;
            Operator = logicalOperator;
            B = b;
        }
    }
 
    /// <summary>
    /// Comparison between a named parameter and a value.
    /// </summary>
    public class SearchParameterValue : SearchOperand
    {
        /// <summary>
        /// Scope of the parameter to match ("", "plan", or "step").  
        /// </summary>
        public string Scope { get; }
 
        /// <summary>
        /// GroupName of the parameter to match (leave empty to match any group).  
        /// </summary>
        public string Group { get;}
 
        /// <summary>
        /// Name of the parameter to match.  
        /// </summary>
        public string Parameter { get;}
 
        /// <summary>
        /// Operation to perform.  
        /// </summary>
        public ComparisonOperator CompareOperator { get;}
 
        /// <summary>
        /// Value to compare against the right-side value.  
        /// </summary>
        public IConvertible Value { get; }
        /// <summary> Comparison between a named parameter and a value. </summary>
        /// <param name="parameter"></param>
        /// <param name="compareOperator"></param>
        /// <param name="value"></param>
        /// <param name="group"></param>
        /// <param name="scope"></param>
        public SearchParameterValue(string parameter, ComparisonOperator compareOperator, IConvertible value, string group = "", string scope = "")
        {
            Parameter = parameter;
            CompareOperator = compareOperator;
            Value = value;
            Group = group;
            Scope = scope;
        }
    }
 
    /// <summary>
    /// Comparison between a named parameter and a value.
    /// </summary>
    public class SearchRange : SearchOperand
    {
        /// <summary>
        /// The scope of the parameter to match. Could be "plan" or "step".
        /// </summary>
        public string Scope { get; set; }
 
        /// <summary>
        /// The name of the parameter to match.
        /// </summary>
        public string Parameter { get; set; }
 
        /// <summary>
        /// The value to compare against as the right-hand side.
        /// </summary>
        public ICombinedNumberSequence<long> Value { get; set; }
    }
 
    /// <summary>
    /// The conditions for what to search for.
    /// </summary>
    public class SearchCondition
    {
        /// <summary>
        /// When true, specifies that any matched tree nodes automatically match all parents of that <see cref="IData"/> element.  
        /// </summary>
        public bool GetParents { get; set; }
 
        /// <summary>
        /// When true, specifies that a matched tree root node automatically matches all children of that given <see cref="IData"/> element.  
        /// </summary>
        public bool GetChildren { get; set; }
 
        /// <summary>
        /// The tree of <see cref="SearchOperand"/> conditions to match for.  
        /// </summary>
        public SearchOperand Operation { get; set; }
 
        /// <summary>
        /// Condition that will match all elements in the result store.  
        /// </summary>
        /// <returns></returns>
        public static SearchCondition All()
        {
            return new SearchCondition { GetChildren = true, GetParents = true, Operation = null };
        }
 
        /// <summary>
        /// Condition that will match any elements in a list as well as all children of those elements.  
        /// </summary>
        /// <param name="testPlanRunIds"></param>
        public static SearchCondition ChildrenOf(IEnumerable<IData> testPlanRunIds)
        {
            return new SearchCondition { Operation = new SearchChildrenOf(testPlanRunIds.ToList()), GetChildren = true, GetParents = false };
        }
 
        /// <summary>
        /// Condition that will match all elements in the result store.  
        /// </summary>
        /// <returns></returns>
        public static SearchCondition LastRun()
        {
            return new SearchCondition { GetChildren = true, GetParents = true, Operation = new SearchLastRun() };
        }
    }
    #endregion
 
    /// <summary>
    /// A structure containing limit set data.
    /// </summary>
    public class LimitSet
    {
        /// <summary>
        /// The friendly name of the limit set.
        /// </summary>
        public string Name { get; private set; }
 
        /// <summary>
        /// The limits in this limit set.
        /// </summary>
        public List<Limit> Limits { get; set; }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="LimitSet"/> class.
        /// </summary>
        /// <param name="name"></param>
        public LimitSet(string name)
        {
            Name = name;
            Limits = new List<Limit>();
        }
    }
 
    /// <summary>
    /// A single limit from a limit set.
    /// </summary>
    public class Limit : ValidatingObject
    {
        /// <summary>
        /// The result name to match.
        /// </summary>
        [Display("Result Name", Description: "The name of the result to match.", Order: -10)]
        public string ResultName { get; set; }
        /// <summary>
        /// The result column to which the limits are applied.
        /// </summary>
        [Display("Column Name", Description: "The name of the result column to apply limit to.", Order: -9)]
        public string ColumnName { get; set; }
        /// <summary>
        /// The lower limit to apply to the result.
        /// </summary>
        [Display("Lower Limit", Description: "The lower limit. To pass the column value must be above this.", Order: -8)]
        public double LowerLimit { get; set; }
        /// <summary>
        /// The upper limit to apply to the result.
        /// </summary>
        [Display("Upper Limit", Description: "The upper limit. To pass the column value must be below this.", Order: -7)]
        public double UpperLimit { get; set; }
        /// <summary>
        /// The conditions that have to apply for this limit.
        /// </summary>
        [Display("Conditions", Description: "Additional conditions that apply for this limit. All conditions must evaluate to true for this limit to be used.", Order: -6)]
        public List<LimitCondition> Conditions { get; set; }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="Limit"/> class.
        /// </summary>
        public Limit()
        {
            Rules.Add(() => !string.IsNullOrWhiteSpace(ResultName), "Result Name must be valid.", "ResultName");
            Rules.Add(() => !string.IsNullOrWhiteSpace(ColumnName), "Column Name must be valid.", "ColumnName");
 
            Conditions = new List<LimitCondition>();
            ResultName = "";
            ColumnName = "";
        }
    }
 
    /// <summary>
    /// A condition for a specific limit that must be satisfied for the limit to apply.
    /// </summary>
    public class LimitCondition : ValidatingObject
    {
        /// <summary>
        /// The result column name that this condition applies to.
        /// </summary>
        [Display("When Column", Description: "The condition value tested comes from the result column with this name.", Order: -3)]
        public string ColumnName { get; set; }
        /// <summary>
        /// The lower limit for this condition.
        /// </summary>
        [Display("Greater Than", Description: "The condition value must be above this value.", Order: -2)]
        public double LowerLimit { get; set; }
        /// <summary>
        /// The upper limit for this condition.
        /// </summary>
        [Display("Less Than", Description: "The condition value must be below this value.", Order: -1)]
        public double UpperLimit { get; set; }
 
        /// <summary>
        /// Returns a string describing this object.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return String.Format("{0} < {1} < {2}", LowerLimit, ColumnName, UpperLimit);
        }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="LimitCondition"/> class.
        /// </summary>
        public LimitCondition()
        {
            Rules.Add(() => !string.IsNullOrWhiteSpace(ColumnName), "Column Name must be valid.", "ColumnName");
        }
    }
 
    /// <summary>
    /// Interface to results storage plugins.
    /// </summary>
    public interface IResultStore : IResource, ITapPlugin
    {
        /// <summary>
        /// Get list of properties on entries in the database which starts with a given string.
        /// The returned properties should be ordered by their frequency of use in the dataset.
        /// </summary>
        /// <param name="scope">Only consider parameters from this scope. Could be "plan" or "step", or empty to consider all scopes.</param>
        /// <param name="group">Only consider parameters from this parameter group. Can be empty to match any group.</param>
        /// <param name="startsWith"></param>
        /// <param name="limit"></param>
        List<string> GetProperties(string scope, string group, string startsWith, int limit);
 
        /// <summary>
        /// Gets all <see cref="IData"/> elements which match a given search condition.
        /// </summary>
        /// <param name="cond"></param>
        /// <param name="limitsets"></param>
        /// <param name="withResults"></param>
        IEnumerable<IData> GetEntries(SearchCondition cond, List<string> limitsets, bool withResults);
 
        /// <summary>
        /// Tries to delete the given entries and all sub entries.
        /// </summary>
        /// <param name="entries">Entries to delete.</param>
        bool DeleteEntries(IEnumerable<IData> entries);
 
        /// <summary>
        /// Returns all registered limit sets.
        /// </summary>
        List<LimitSet> GetLimitSets();
        /// <summary>
        /// Adds a limit set to the results store.
        /// </summary>
        /// <param name="limitSet"></param>
        void AddLimitSet(LimitSet limitSet);
        /// <summary>
        /// Deletes a limit set from the database.
        /// </summary>
        /// <param name="Name">The name of the limit set to delete.</param>
        void DeleteLimitSet(string Name);
 
        /// <summary>
        /// Returns the binary data for the given objects attachment, or null if it could not be found.
        /// </summary>
        /// <param name="entry"></param>
        /// <param name="attachmentType"></param>
        byte[] GetAttachment(IData entry, AttachmentType attachmentType);
        /// <summary>
        /// Gets a list of attachments on the given object.
        /// </summary>
        /// <param name="entry"></param>
        List<AttachmentType> GetValidAttachments(IData entry);
 
        /// <summary>
        /// Returns the average duration of the last <paramref name="averageCount"/> step runs with similar settings.
        /// </summary>
        /// <param name="step"></param>
        /// <param name="averageCount"></param>
        TimeSpan? GetAverageDuration(TestStepRun step, int averageCount);
        /// <summary>
        /// Returns the average duration of the last <paramref name="averageCount"/> PlanRuns runs with a similar plan.
        /// </summary>
        /// <param name="plan"></param>
        /// <param name="averageCount"></param>
        TimeSpan? GetAverageDuration(TestPlanRun plan, int averageCount);
    }
 
    /// <summary>
    /// IResultStore that supports attachment streams.
    /// </summary>
    public interface IAttachmentStream : IResultStore
    {
 
        /// <summary>
        /// Returns a stream that can read a given attachment entry. The returned stream must be disposed.
        /// </summary>
        /// <param name="entry"></param>
        /// <param name="attachmentType"></param>
        Stream GetAttachmentStream(IData entry, AttachmentType attachmentType);
    }
 
    /// <summary>
    /// Interface to support result tagging in the OpenTAP Results Viewer.
    /// </summary>
    public interface IResultTagging : IResultStore
    {
        /// <summary>
        /// Add a TestPlan parameter to a number of TestPlans.
        /// </summary>
        void AddTestPlanRunParameter(IEnumerable<IData> PlanRunIDs, string Group, string ParameterName, IConvertible Value);
 
        /// <summary>
        /// Delete a TestPlan parameter from a number of TestPlans.
        /// </summary>
        void DeleteTestPlanRunParameter(IEnumerable<IData> PlanRunIDs, string Group, string ParameterName, IConvertible Value);
 
        /// <summary>
        /// Get distinct TestPlan parameter values ordered by popularity (number of uses). Optionally limited to Limit values, scope and group.
        /// </summary>
        IEnumerable<string> GetTestPlanRunParameterValues(string ParameterName, string Scope = "", string Group = "", int Limit = -1);
    }
 
    /// <summary>
    /// Delegate that triggers notification updates when results are added to the store.
    /// </summary>
    public delegate void ResultUpdateEvent(object Sender);
 
    /// <summary>
    /// Interface to add notifications when the Results Viewer gets new data.
    /// </summary>
    public interface IResultStoreNotification : IResultStore
    {
        /// <summary>
        /// Event triggered when results are added to the store.
        /// </summary>
        event ResultUpdateEvent ResultUpdated;
 
        /// <summary>
        /// Event triggered when TestPlan or TestStep runs are added to the store.
        /// </summary>
        event ResultUpdateEvent RunsUpdated;
 
        /// <summary>
        /// Enables the Updated events.
        /// </summary>
        void EnableUpdateEvents();
 
        /// <summary>
        /// Disables the Updated events.
        /// </summary>
        void DisableUpdateEvents();
    }
 
    /// <summary>
    /// When implemented along with <see cref="IResultStore"/>, this interface allows files with a given extension to be opened using your <see cref="ResultListener"/>. 
    /// It also can be used to allow the export of results to a file. 
    /// </summary>
    public interface IFileResultStore
    {
        /// <summary>
        /// Gets or sets the currently chosen file.
        /// </summary>
        string FilePath { set; get; }
 
        /// <summary>
        /// Default extension without the dot (.) that files must match in order to load a file's results to Results Viewer (e.g. TapResults, not .TapResults).  
        /// </summary>
        string DefaultExtension { get; }
    }
}