• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

luttje / Key2Joy / 6557152774

18 Oct 2023 06:36AM UTC coverage: 52.519% (+39.8%) from 12.718%
6557152774

push

github

web-flow
Adding more tests, fixing bugs in the process (#48)

* Add config manager tests

* legacy config and mapping profile tests (should fix #42)

* remove comment, problem was caused by transfer across appdomain (or to/fro scripting environment)

* Test core functionality #48 + includes minor refactoring to be able to test + added docs

* Add interop tests + implement and test async test utility (refactors away from singletons)

* fix not all tests running in workflow

* config and interop tests

* Refactor and allow mocking global input hook class

* add capture action test

* Make Execute override optional for script only methods

* add dependency injection + refactor and try test gamepad service

* Refactor config singleton to using dependency injection

* add tests for scripting

* add tests for plugin set + fix plugin showing as loaded even if checksum match failed

* fix tests failing because it relied on config exist (I guess the test order was accidentally correct earlier, this means we should really fix cleanup so we catch this sooner)

* refactor docs code + fix wrong enum summary

* refactor docs builder and start testing it a bit

* fix cmd project structure

* ignore designer files in tests

* cleanup and refactor UI code + show latest version in help

* truncate listview action column

* allow user config to minimize app when pressing X (defaults to shut down app) resolves #45

696 of 1757 branches covered (0.0%)

Branch coverage included in aggregate %.

4597 of 4597 new or added lines in 138 files covered. (100.0%)

3619 of 6459 relevant lines covered (56.03%)

17089.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.67
/Core/Key2Joy.Core/Mapping/Actions/Scripting/ExposedMethod.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using System.Reflection;
5
using System.Runtime.Serialization;
6
using Key2Joy.Plugins;
7
using Key2Joy.Util;
8

9
namespace Key2Joy.Mapping.Actions.Scripting;
10

11
public delegate object ParameterTransformerDelegate(object parameter, Type methodParameterType);
12

13
public delegate object ParameterTransformerDelegate<T>(T parameter, Type methodParameterType);
14

15
public abstract class ExposedMethod
16
{
17
    public string FunctionName { get; protected set; }
351✔
18
    public string MethodName { get; protected set; }
475✔
19
    public bool IsPrepared => this.Instance != null;
190✔
20

21
    protected object Instance { get; private set; }
299✔
22

23
    private readonly Dictionary<Type, ParameterTransformerDelegate> parameterTransformers = new();
258✔
24
    protected IList<Type> ParameterTypes { get; private set; } = new List<Type>();
387✔
25

26
    public ExposedMethod(string functionName, string methodName)
258✔
27
    {
28
        this.FunctionName = functionName;
258✔
29
        this.MethodName = methodName;
258✔
30
    }
258✔
31

32
    public void Prepare(object instance)
33
    {
34
        this.Instance = instance;
96✔
35
        this.ParameterTypes = this.GetParameterTypes();
96✔
36
    }
96✔
37

38
    public abstract IList<Type> GetParameterTypes();
39

40
    public abstract object InvokeMethod(object[] transformedParameters);
41

42
    /// <summary>
43
    /// Register a transformer for certain types coming from scripts.
44
    /// The transformer will get the parameter value and the type of the method parameter.
45
    /// The transformer must return an object that will be passed to the method.
46
    /// </summary>
47
    /// <typeparam name="T"></typeparam>
48
    /// <param name="transformer"></param>
49
    public void RegisterParameterTransformer<T>(ParameterTransformerDelegate<T> transformer)
50
    {
51
        if (!this.IsPrepared)
177✔
52
        {
53
            throw new InvalidOperationException("Cannot register parameter transformer before preparing the method. Call .Prepare on the exposed method first.");
1✔
54
        }
55

56
        var key = typeof(T);
176✔
57

58
        if (this.parameterTransformers.ContainsKey(key))
176✔
59
        {
60
            this.parameterTransformers.Remove(key);
1✔
61
        }
62

63
        this.parameterTransformers.Add(key, (p, t) => transformer((T)p, t));
177✔
64
    }
176✔
65

66
    /// <summary>
67
    /// Will try to transform the parameter to the type of the method parameter.
68
    /// </summary>
69
    /// <param name="parameters"></param>
70
    /// <returns></returns>
71
    /// <exception cref="NotImplementedException"></exception>
72
    public object TransformAndRedirect(params object[] parameters)
73
    {
74
        if (!this.IsPrepared)
13✔
75
        {
76
            throw new InvalidOperationException("Cannot TransformAndRedirect before preparing the method. Call .Prepare on the exposed method first.");
2✔
77
        }
78

79
        // When we pass TransformAndRedirect any array as the only parameter, C# treats that
80
        // array as the parameters array. Whilst we likely want to send through the array as is.
81
        // To workaround this we'll check that if we get more parameters than we expect, we'll
82
        // wrap it in an object[]
83
        if (this.ParameterTypes.Count == 1
11✔
84
            && parameters.Length > this.ParameterTypes.Count)
11✔
85
        {
86
            parameters = new object[] { parameters };
1✔
87
        }
88

89
        var transformedParameters = parameters.Select((parameter, parameterIndex) =>
11✔
90
        {
11✔
91
            var parameterType = this.ParameterTypes[parameterIndex];
14✔
92

11✔
93
            if (this.parameterTransformers.TryGetValue(parameter.GetType(), out var transformer))
14✔
94
            {
11✔
95
                return transformer(parameter, parameterType);
1✔
96
            }
11✔
97

11✔
98
            // If the parameter type is an enumeration, we try to convert the parameter to the enum value.
11✔
99
            if (parameterType.IsEnum)
13✔
100
            {
11✔
101
                return Enum.Parse(parameterType, parameter.ToString());
6✔
102
            }
11✔
103

11✔
104
            if (parameter is object[] objectArrayParameter && parameterType.IsArray)
7✔
105
            {
11✔
106
                // TODO: This breaks the reference to the original array
11✔
107
                // TODO: Inform plugin creators that if they want to keep the original reference, they should
11✔
108
                //       use the 'object' type and cast the array to the correct type themselves.
11✔
109
                parameter = objectArrayParameter.CopyArrayToNewType(parameterType.GetElementType());
3✔
110
            }
11✔
111

11✔
112
            if (parameter is MarshalByRefObject or ISerializable)
7✔
113
            {
11✔
114
                return parameter;
×
115
            }
11✔
116

11✔
117
            if (parameter.GetType().IsSerializable)
7✔
118
            {
11✔
119
                return parameter;
7✔
120
            }
11✔
121

11✔
122
            throw new NotImplementedException("Parameter type not supported as an exposed method parameter: " + parameter.GetType().FullName);
×
123
        }).ToArray();
11✔
124

125
        return this.InvokeMethod(transformedParameters);
11✔
126
    }
127

128
    /// <summary>
129
    /// MethodInfo that can be bound to scripts
130
    /// </summary>
131
    /// <returns></returns>
132
    public virtual MethodInfo GetExecutorMethodInfo() => typeof(ExposedMethod).GetMethod(nameof(TransformAndRedirect));
87✔
133
}
134

135
public class TypeExposedMethod : ExposedMethod
136
{
137
    public Type Type { get; protected set; }
412✔
138

139
    private readonly MethodInfo cachedMethodInfo;
140

141
    public TypeExposedMethod(string functionName, string methodName, Type type)
142
        : base(functionName, methodName)
206✔
143
    {
144
        this.Type = type;
206✔
145
        this.cachedMethodInfo = this.Type.GetMethod(this.MethodName);
206✔
146
    }
206✔
147

148
    public override IList<Type> GetParameterTypes()
149
        => this.cachedMethodInfo.GetParameters().Select(p => p.ParameterType).ToList();
241✔
150

151
    public override object InvokeMethod(object[] transformedParameters)
152
        => this.cachedMethodInfo.Invoke(this.Instance, transformedParameters);
1✔
153
}
154

155
public class PluginExposedMethod : ExposedMethod
156
{
157
    public string TypeName { get; protected set; }
45✔
158

159
    public PluginExposedMethod(string typeName, string functionName, string methodName)
160
        : base(functionName, methodName)
45✔
161
        => this.TypeName = typeName;
45✔
162

163
    public override IList<Type> GetParameterTypes()
164
    {
165
        var instance = (PluginActionProxy)this.Instance;
5✔
166
        return instance.GetMethodParameterTypes(this.MethodName);
5✔
167
    }
168

169
    public override object InvokeMethod(object[] transformedParameters)
170
    {
171
        var instance = (PluginActionProxy)this.Instance;
6✔
172
        return instance.InvokeScriptMethod(this.MethodName, transformedParameters);
6✔
173
    }
174
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc