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

JaCraig / Mecha / 12871043181

20 Jan 2025 03:17PM UTC coverage: 81.554% (-0.3%) from 81.902%
12871043181

push

github

JaCraig
feat(core): enhance exception handling and refactor code

This commit introduces several enhancements and refactorings across multiple files to improve exception handling, code readability, and consistency.

- Added `ArgumentThrowingClass` to `ExceptionHandlerTests.cs` to test `ArgumentNullException.ThrowIfNull`.
- Added two new test methods `IgnoreException` and `IgnoreExceptionCalledFromDotThrowsX` in `ExceptionHandlerTests.cs`.
- Updated `coverlet.msbuild` and `coverlet.collector` package versions in `Mecha.Core.Tests.csproj` from `6.0.3` to `6.0.4`.
- Replaced array initialization using `Array.Empty<T>()` with `[]` in multiple files for brevity.
- Modified `ExceptionHandler` class to handle nullable exceptions and methods, and added a new method `IsFromThrowsMethodOnException`. This check looks for exceptions that are thrown by calling the .ThrowIfX methods on exception types.
- Updated `Services` class to use nullable `IServiceProvider` and renamed `ServiceProviderLock` to `_ServiceProviderLock`.
- Refactored constructors in `FileStreamGenerator`, `HttpClientGenerator`, `SpecialGenerator`, and `StreamGenerator` to use new C# 9.0 record-like syntax.
- Updated `GeneratorManager` constructor to use new C# 9.0 record-like syntax and replaced array initialization with `[.. generators.OrderBy(x => x.Order)]`.
- Changed `ParameterValues` class to handle nullable `ParameterValue` and refactored the `Same` method for better readability.
- Updated `Mech` class to handle nullable `ServiceProvider`.
- Refactored `AttemptToSubstitute` method in `Mech` class to use new C# 9.0 collection initialization syntax.
- Updated `MutatorManager` constructor to use new C# 9.0 collection initialization syntax.
- Changed `RunnerBaseClass` constructor to public and updated `DefaultRunner` to use it.
- Updated `MethodInvoker<TTarget>` to use a constructor with `MethodInfo` parameter.
- Changed loop variable names for consistency.
- Updated several methods to use shorthand ar... (continued)

745 of 1052 branches covered (70.82%)

Branch coverage included in aggregate %.

94 of 109 new or added lines in 22 files covered. (86.24%)

2 existing lines in 2 files now uncovered.

1333 of 1496 relevant lines covered (89.1%)

6421052.13 hits per line

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

70.97
/Mecha.Core/ExceptionHandler.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using System.Reflection;
5
using System.Threading.Tasks;
6

7
namespace Mecha.Core
8
{
9
    /// <summary>
10
    /// Exception handler
11
    /// </summary>
12
    public class ExceptionHandler
13
    {
14
        /// <summary>
15
        /// Gets the exception handlers.
16
        /// </summary>
17
        /// <value>The exception handlers.</value>
18
        private Dictionary<Type, Func<Exception, MethodInfo, bool>> ExceptionHandlers { get; } = [];
14,750✔
19

20
        /// <summary>
21
        /// Determines whether this instance can ignore the specified exception.
22
        /// </summary>
23
        /// <param name="exception">The exception.</param>
24
        /// <param name="method">The method.</param>
25
        /// <returns>
26
        /// <c>true</c> if this instance can ignore the specified exception; otherwise, <c>false</c>.
27
        /// </returns>
28
        public bool CanIgnore(Exception? exception, MethodInfo? method)
29
        {
30
            if (exception is null || method is null)
6✔
31
            {
32
                return true;
2✔
33
            }
34
            else if (TryGetValue(exception.GetType(), out Func<Exception, MethodInfo, bool>? Handler))
4!
35
            {
36
                return Handler(exception, method);
4✔
37
            }
38
            else
39
            {
NEW
40
                return exception.InnerException is not null
×
UNCOV
41
                           && TryGetValue(exception.InnerException.GetType(), out Func<Exception, MethodInfo, bool>? InnerHandler)
×
NEW
42
                           && InnerHandler(exception.InnerException, method);
×
43
            }
44
        }
45

46
        /// <summary>
47
        /// Adds an exception to the ignore list.
48
        /// </summary>
49
        /// <typeparam name="TException">The type of the exception.</typeparam>
50
        /// <param name="handler">The handler (if not provided, a default handler is used).</param>
51
        /// <returns>This.</returns>
52
        public ExceptionHandler IgnoreException<TException>(Func<Exception, MethodInfo, bool>? handler = null)
53
        {
54
            ExceptionHandlers.Add(typeof(TException), handler ?? DefaultHandler);
9,825✔
55
            return this;
9,825✔
56
        }
57

58
        /// <summary>
59
        /// Are the methods equal.
60
        /// </summary>
61
        /// <param name="left">The left.</param>
62
        /// <param name="right">The right.</param>
63
        /// <returns>True if they are, false otherwise.</returns>
64
        private static bool AreMethodsEqual(MethodBase? left, MethodBase? right)
65
        {
66
            if (left is null && right is null)
4!
67
                return true;
×
68
            if (left is null || right is null)
4✔
69
                return false;
1✔
70
            if (left.Equals(right))
3✔
71
                return true;
1✔
72
            try
73
            {
74
                MethodInfo? RightMethod = left.DeclaringType?.GetMethod(right.Name, right.GetGenericArguments().Length, right.GetParameters().Select(p => p.ParameterType).ToArray());
4!
75
                if (RightMethod is null)
2✔
76
                    return false;
1✔
77
                MethodInfo? LeftMethod = left.DeclaringType?.GetMethod(left.Name, left.GetGenericArguments().Length, left.GetParameters().Select(p => p.ParameterType).ToArray());
2!
78
                return LeftMethod?.Equals(RightMethod) == true;
1!
79
            }
80
            catch
×
81
            {
82
                return false;
×
83
            }
84
        }
2✔
85

86
        /// <summary>
87
        /// Default handler for exceptions.
88
        /// </summary>
89
        /// <param name="exception">The exception.</param>
90
        /// <param name="method">The method.</param>
91
        /// <returns>True if it can be ignored, false otherwise.</returns>
92
        private static bool DefaultHandler(Exception exception, MethodInfo method)
93
        {
94
            MethodInfo GenericMethod = method.IsGenericMethod ? method.GetGenericMethodDefinition() : method;
4!
95
            var ParameterCheckReturn = ParameterCheck(exception as ArgumentException, method.GetParameters());
4✔
96
            return typeof(Task).IsAssignableFrom(GenericMethod.ReturnType)
4!
97
                || (ParameterCheckReturn && AreMethodsEqual(GenericMethod, exception.TargetSite))
4✔
98
                || (ParameterCheckReturn && IsFromThrowsMethodOnException(exception, GenericMethod));
4✔
99
        }
100

101
        /// <summary>
102
        /// Determines if the exception originated from the specified method but using ThrowsX
103
        /// methods on exception classes.
104
        /// </summary>
105
        /// <param name="exception">The exception to check.</param>
106
        /// <param name="method">The method to compare against.</param>
107
        /// <returns>
108
        /// <c>true</c> if the exception originated from the specified method; otherwise, <c>false</c>.
109
        /// </returns>
110
        private static bool IsFromThrowsMethodOnException(Exception exception, MethodInfo method)
111
        {
112
            if (exception is null || method is null)
2!
NEW
113
                return false;
×
114

115
            if (exception.TargetSite is null || exception.TargetSite.DeclaringType is null)
2✔
116
                return false;
1✔
117
            if (!exception.TargetSite.DeclaringType.IsAssignableFrom(exception.GetType()))
1!
NEW
118
                return false;
×
119

120
            var SplitStackTrace = exception.StackTrace?.Split('\n', StringSplitOptions.RemoveEmptyEntries);
1!
121
            if (SplitStackTrace is null || SplitStackTrace.Length < 3)
1!
NEW
122
                return false;
×
123
            var MethodFullName = method.DeclaringType?.FullName + "." + method.Name;
1!
124

125
            if (MethodFullName is null)
1!
NEW
126
                return false;
×
127

128
            return SplitStackTrace[2].Contains(MethodFullName) || SplitStackTrace[1].Contains(MethodFullName);
1!
129
        }
130

131
        /// <summary>
132
        /// Parses the exception.
133
        /// </summary>
134
        /// <param name="exception">The exception.</param>
135
        /// <param name="parameters">The parameters.</param>
136
        /// <returns>True if it the parameter is found in the method, false otherwise.</returns>
137
        private static bool ParameterCheck(ArgumentException? exception, ParameterInfo[] parameters)
138
        {
139
            if (exception is null)
4✔
140
                return true;
1✔
141
            parameters ??= [];
3!
142
            return parameters.Length == 0 || parameters.Any(x => x.Name == exception.ParamName || x.Name == exception.Message);
5!
143
        }
144

145
        /// <summary>
146
        /// Tries to get the handler
147
        /// </summary>
148
        /// <param name="type">The type.</param>
149
        /// <param name="handler">The handler.</param>
150
        /// <returns>True if it is found, false otherwise.</returns>
151
        private bool TryGetValue(Type type, out Func<Exception, MethodInfo, bool> handler)
152
        {
153
            Type? HandlerKey = ExceptionHandlers.Keys.FirstOrDefault(x => x.IsAssignableFrom(type));
11✔
154
            if (HandlerKey is null)
4!
155
            {
156
                handler = (_, __) => false;
×
157
                return false;
×
158
            }
159
            handler = ExceptionHandlers[HandlerKey];
4✔
160
            return handler is not null;
4✔
161
        }
162
    }
163
}
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

© 2026 Coveralls, Inc