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
//            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.Reflection;
using System.Threading;
 
namespace OpenTap
{
    /// <summary>
    /// Represents a reference to a resource. A reference is defined as something in the TestPlan that references a Resource, and thus causes it to get opened. Used by <see cref="IResourceReferences"/>
    /// </summary>
    public class ResourceReference
    {
        /// <summary>
        /// The TestStep or other Resource that is using some Resource
        /// </summary>
        public object Instance { get; }
        /// <summary>
        /// The property on <see cref="Instance"/> that references the Resource
        /// </summary>
        public PropertyInfo Property { get; }
        
        /// <summary> The property that references the Resource. </summary>
        public IMemberData Member { get; }
 
        /// <summary> Creates an immutable instance of this class. </summary>
        public ResourceReference(object obj, PropertyInfo prop)
        {
            Instance = obj;
            Property = prop;
            Member = MemberData.Create(prop);
        }
        
        /// <summary> Creates an immutable instance of this class. </summary>
        public ResourceReference(object obj, IMemberData prop)
        {
            Instance = obj;
            Member = prop;
            if (prop is MemberData md)
                Property = md.Member as PropertyInfo;
        }
    }
 
    /// <summary>
    /// Used in <see cref="ILockManager"/> to represents a resource and its references
    /// </summary>
    public interface IResourceReferences
    {
        /// <summary>
        /// The resource this item represents.
        /// </summary>
        IResource Resource { get; set; }
        /// <summary>
        /// References to <see cref="Resource"/> from TestSteps (or other Resources). These references are the reason this resources needs to be opened when running a TestPlan.
        /// </summary>
        List<ResourceReference> References { get; }
    }
 
    /// <summary>
    /// Implementing this interface will enable hooks before and after resources are opened and closed.
    /// </summary>
    public interface ILockManager : ITapPlugin
    {
        /// <summary>
        /// This hook is triggered before <see cref="IResource.Open"/> is executed. Only called once when e.g. a TestStep with mulitple resources starts.
        /// </summary>
        /// <param name="resources">The resources that will be opened.</param>
        /// <param name="abortToken">A token that will be signalled if the locking action should be cancelled.</param>
        void BeforeOpen(IEnumerable<IResourceReferences> resources, CancellationToken abortToken);
 
        /// <summary>
        /// This hook is triggered after the TestStep or TestPlan is done executing. If triggered by a TestStep, defered actions may still be running.
        /// </summary>
        /// <param name="resources">This will contain the same resources as given to <see cref="BeforeOpen"/>".</param>
        /// <param name="abortToken">A token that will be signalled if the locking action should be cancelled.</param>
        void AfterClose(IEnumerable<IResourceReferences> resources, CancellationToken abortToken);
    }
 
    class LockManager
    {
        readonly ILockManager[] managers;
        static readonly TraceSource log = Log.CreateSource("LockManager");
        public LockManager()
        {
            managers = PluginManager.GetPlugins<ILockManager>()
                .OrderByDescending(t => t.GetDisplayAttribute().Order)
                .TrySelect(t => (ILockManager)t.CreateInstance(), (ex, t) =>
                {
                    log.Error("Unable to create an instance of {0}: {1}", t, ex.Message);
                    log.Debug(ex);
                })
                .ToArray();
        }
        
        internal void BeforeOpen(IEnumerable<IResourceReferences> resources, CancellationToken cancellationToken)
        {
            foreach (ILockManager lockManager in managers)
            {
                lockManager.BeforeOpen(resources, cancellationToken);
            }
        }
 
        internal void AfterClose(IEnumerable<IResourceReferences> resources, CancellationToken cancellationToken)
        {
            foreach (ILockManager lockManager in Enumerable.Reverse(managers))
            {
                lockManager.AfterClose(resources, cancellationToken);
            }
        }
    }
}