Skip to content

Commit e37d50d

Browse files
committed
Split up prototype code to separate files
1 parent f6fa6f7 commit e37d50d

8 files changed

Lines changed: 281 additions & 282 deletions
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Collections.Generic;
2+
3+
namespace Reqnroll.Bindings;
4+
5+
/// <summary>
6+
/// A provider of step definition descriptors that can be used to obtain the step definitions
7+
/// available to be called.
8+
/// </summary>
9+
public interface IStepDefinitionDescriptorsProvider
10+
{
11+
/// <summary>
12+
/// Gets the step definitions known to the provider.
13+
/// </summary>
14+
/// <returns>A collection of step definitions known to the provider.</returns>
15+
public IReadOnlyCollection<StepDefinitionDescriptor> GetStepDefinitions();
16+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System;
2+
using System.Reflection;
3+
4+
namespace Reqnroll.Bindings;
5+
#nullable enable
6+
7+
/// <summary>
8+
/// Provides extensions for working with step definition assemblies.
9+
/// </summary>
10+
public static class StepDefinitionAssemblyExtensions
11+
{
12+
/// <summary>
13+
/// Tries to get the step registry for an assembly.
14+
/// </summary>
15+
/// <param name="assembly">The assembly to examine for a step registry.</param>
16+
/// <param name="stepRegistry">After the method returns, contains the step registry for the assembly,
17+
/// if one was found; otherwise <c>null</c>.</param>
18+
/// <param name="error">After the method returns, if the registry could not be found, contains an exception
19+
/// indicating the condition that prevented the registry being located; otherwise <c>null</c>.</param>
20+
/// <returns><c>true</c> if a registry was found; otherwise <c>false</c>.</returns>
21+
public static bool TryGetStepDefinitionRegistry(
22+
this Assembly assembly,
23+
out IStepDefinitionDescriptorsProvider? stepRegistry,
24+
out Exception? error)
25+
{
26+
var registryAttribute = assembly.GetCustomAttribute<StepDefinitionRegistryAttribute>();
27+
stepRegistry = null;
28+
29+
if (registryAttribute == null)
30+
{
31+
error = new InvalidOperationException(
32+
$"Assembly '{assembly.FullName}' does not have a '{nameof(StepDefinitionRegistryAttribute)}' defined.");
33+
return false;
34+
}
35+
36+
if (registryAttribute.RegistryType == null)
37+
{
38+
error = new InvalidOperationException(
39+
$"Assembly '{assembly.FullName}' has a '{nameof(StepDefinitionRegistryAttribute)}' " +
40+
"defined but no registry type is specified.");
41+
return false;
42+
}
43+
44+
if (typeof(IStepDefinitionDescriptorsProvider).IsAssignableFrom(registryAttribute.RegistryType))
45+
{
46+
error = new InvalidOperationException(
47+
$"Assembly '{assembly.FullName}' speciifes its registry type is " +
48+
$"'{registryAttribute.RegistryType.FullName}' however the type does not implement " +
49+
$"'{nameof(IStepDefinitionDescriptorsProvider)}'.");
50+
return false;
51+
}
52+
53+
var instanceProperty = registryAttribute.RegistryType.GetProperty(
54+
"Instance",
55+
BindingFlags.Public | BindingFlags.Static | BindingFlags.GetProperty);
56+
57+
if (instanceProperty == null || instanceProperty.PropertyType != registryAttribute.RegistryType)
58+
{
59+
error = new InvalidOperationException(
60+
$"Assembly '{assembly.FullName}' speciifes its registry type is " +
61+
$"'{registryAttribute.RegistryType.FullName}' however the type does not expose the required " +
62+
$"'Instance' property. The property must be declared public and static and must return the single " +
63+
$"instance of the registry type.");
64+
return false;
65+
}
66+
67+
stepRegistry = (IStepDefinitionDescriptorsProvider?)instanceProperty.GetGetMethod().Invoke(null, null);
68+
69+
if (stepRegistry == null)
70+
{
71+
error = new InvalidOperationException(
72+
$"Assembly '{assembly.FullName}' speciifes its registry type is " +
73+
$"'{registryAttribute.RegistryType.FullName}' however the 'Instance' property did not return " +
74+
"an instance.");
75+
return false;
76+
}
77+
78+
error = null;
79+
return true;
80+
}
81+
82+
/// <summary>
83+
/// Gets the step registry for an assembly.
84+
/// </summary>
85+
/// <param name="assembly">The assembly to examine for a step registry.</param>
86+
/// <returns>The step registry for the assembly.</returns>
87+
/// <exception cref="InvalidOperationException">
88+
/// <para>The assembly does have a step registry.</para>
89+
/// </exception>
90+
public static IStepDefinitionDescriptorsProvider GetStepRegistry(this Assembly assembly)
91+
{
92+
if (assembly.TryGetStepDefinitionRegistry(out var registry, out var error))
93+
{
94+
return registry!;
95+
}
96+
97+
throw error!;
98+
}
99+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System;
2+
using System.Collections.Immutable;
3+
4+
namespace Reqnroll.Bindings;
5+
6+
/// <summary>
7+
/// Provides a definition of a step which can be invoked as part of a scenario.
8+
/// </summary>
9+
public class StepDefinitionDescriptor
10+
{
11+
/// <summary>
12+
/// Initializes a new instance of the <see cref="StepDefinitionDescriptor"/> class.
13+
/// </summary>
14+
/// <param name="displayName">A friendly name for this step.</param>
15+
/// <param name="matchedTypes">The step types matched by this definition.</param>
16+
/// <param name="textPattern">The text pattern that must be matched by step text.</param>
17+
/// <param name="parameters">The parameters of the step definition.</param>
18+
/// <exception cref="ArgumentException">
19+
/// <para><paramref name="matchedTypes"/> is a default value or empty.</para>
20+
/// </exception>
21+
public StepDefinitionDescriptor(
22+
string displayName,
23+
ImmutableArray<StepDefinitionType> matchedTypes,
24+
StepTextPattern textPattern,
25+
ImmutableArray<StepParameterDescriptor> parameters = default)
26+
{
27+
if (matchedTypes.IsDefaultOrEmpty)
28+
{
29+
throw new ArgumentException("Array cannot be default or empty.", nameof(matchedTypes));
30+
}
31+
32+
DisplayName = displayName;
33+
MatchedTypes = matchedTypes;
34+
TextPattern = textPattern;
35+
Parameters = parameters.IsDefault ? [] : parameters;
36+
}
37+
38+
/// <summary>
39+
/// Gets the friendly name for this step.
40+
/// </summary>
41+
public string DisplayName { get; }
42+
43+
/// <summary>
44+
/// Gets step types matched by this definition, indicating the class of keyword it will bind to.
45+
/// </summary>
46+
public ImmutableArray<StepDefinitionType> MatchedTypes { get; }
47+
48+
/// <summary>
49+
/// Gets the text pattern that must be matched by step text.
50+
/// </summary>
51+
public StepTextPattern TextPattern { get; }
52+
53+
/// <summary>
54+
/// Gets the parameters of the step definition.
55+
/// </summary>
56+
public ImmutableArray<StepParameterDescriptor> Parameters { get; }
57+
58+
/// <summary>
59+
/// Gets a string representation of the step definition.
60+
/// </summary>
61+
/// <returns>A string representing the step definition.</returns>
62+
public override string ToString() => $"Step: {DisplayName}";
63+
}

0 commit comments

Comments
 (0)