diff --git a/Reqnroll.Generator/DefaultDependencyProvider.cs b/Reqnroll.Generator/DefaultDependencyProvider.cs index cf9a95e90..0dea7b598 100644 --- a/Reqnroll.Generator/DefaultDependencyProvider.cs +++ b/Reqnroll.Generator/DefaultDependencyProvider.cs @@ -1,5 +1,7 @@ using Reqnroll.BoDi; using Reqnroll.Configuration; +using Reqnroll.CucumberMessages.Configuration; +using Reqnroll.EnvironmentAccess; using Reqnroll.Generator.Configuration; using Reqnroll.Generator.Generation; using Reqnroll.Generator.Interfaces; @@ -10,6 +12,7 @@ using Reqnroll.Plugins; using Reqnroll.Tracing; using Reqnroll.Utils; +using System.IO; namespace Reqnroll.Generator { @@ -46,6 +49,9 @@ public virtual void RegisterDefaults(ObjectContainer container) container.RegisterTypeAs(); + container.RegisterTypeAs(); + container.RegisterTypeAs(); + container.RegisterTypeAs(); container.RegisterTypeAs(); diff --git a/Reqnroll.Generator/Generation/GeneratorConstants.cs b/Reqnroll.Generator/Generation/GeneratorConstants.cs index 7b14ca75e..ab60a6c06 100644 --- a/Reqnroll.Generator/Generation/GeneratorConstants.cs +++ b/Reqnroll.Generator/Generation/GeneratorConstants.cs @@ -18,9 +18,9 @@ public class GeneratorConstants public const string SCENARIO_TAGS_VARIABLE_NAME = "tagsOfScenario"; public const string SCENARIO_ARGUMENTS_VARIABLE_NAME = "argumentsOfScenario"; public const string FEATURE_TAGS_VARIABLE_NAME = "featureTags"; - public const string PICKLEINDEX_PARAMETER_NAME = "generatedParameter_pickleIndex"; + public const string PICKLEINDEX_PARAMETER_NAME = "__pickleIndex"; public const string PICKLEINDEX_VARIABLE_NAME = "m_pickleIndex"; public const string PICKLESTEPSEQUENCE_VARIABLE_NAME = "m_pickleStepSequence"; - public const string PICKLESTEPSEQUENCE_PARAMETER_NAME = "pickleStepSequence"; + public const string PICKLESTEPSEQUENCE_PARAMETER_NAME = "__pickleStepSequence"; } } \ No newline at end of file diff --git a/Reqnroll.Generator/Generation/UnitTestFeatureGenerator.cs b/Reqnroll.Generator/Generation/UnitTestFeatureGenerator.cs index 204221161..5ca45bc49 100644 --- a/Reqnroll.Generator/Generation/UnitTestFeatureGenerator.cs +++ b/Reqnroll.Generator/Generation/UnitTestFeatureGenerator.cs @@ -236,7 +236,6 @@ private void PersistStaticCucumberMessagesToFeatureInfo(TestClassGenerationConte { sourceFileLocation = Path.Combine(generationContext.Document.DocumentLocation.FeatureFolderPath, generationContext.Document.DocumentLocation.SourceFilePath); //Generate Feature level Cucumber Messages, serialize them to strings, create a FeatureLevelCucumberMessages object and add it to featureInfo - //TODO: make the type of IDGenerator configurable var IDGenStyle = _cucumberConfiguration.IDGenerationStyle; var messageConverter = new CucumberMessagesConverter(IdGeneratorFactory.Create(IDGenStyle)); var featureSourceMessage = messageConverter.ConvertToCucumberMessagesSource(generationContext.Document); diff --git a/Reqnroll/CucumberMessages/Configuration/CucumberConfiguration.cs b/Reqnroll/CucumberMessages/Configuration/CucumberConfiguration.cs index a75eda603..26eb89d40 100644 --- a/Reqnroll/CucumberMessages/Configuration/CucumberConfiguration.cs +++ b/Reqnroll/CucumberMessages/Configuration/CucumberConfiguration.cs @@ -11,6 +11,7 @@ namespace Reqnroll.CucumberMessages.Configuration { public class CucumberConfiguration : ICucumberConfiguration { + public static CucumberConfiguration Current { get; private set; } public bool Enabled => _enablementOverrideFlag && _resolvedConfiguration.Value.Enabled; public string BaseDirectory => _resolvedConfiguration.Value.BaseDirectory; public string OutputDirectory => _resolvedConfiguration.Value.OutputDirectory; @@ -29,6 +30,7 @@ public CucumberConfiguration(ITraceListener traceListener, IEnvironmentWrapper e _trace = traceListener; _environmentWrapper = environmentWrapper; _resolvedConfiguration = new Lazy(ResolveConfiguration); + Current = this; } #region Override API diff --git a/Reqnroll/CucumberMessages/Configuration/ICucumberConfiguration.cs b/Reqnroll/CucumberMessages/Configuration/ICucumberConfiguration.cs index 67c499aab..ae7840545 100644 --- a/Reqnroll/CucumberMessages/Configuration/ICucumberConfiguration.cs +++ b/Reqnroll/CucumberMessages/Configuration/ICucumberConfiguration.cs @@ -4,8 +4,8 @@ public interface ICucumberConfiguration { bool Enabled { get; } string BaseDirectory { get; } - public string OutputDirectory { get; } - public string OutputFileName { get; } - public IDGenerationStyle IDGenerationStyle { get; } + string OutputDirectory { get; } + string OutputFileName { get; } + IDGenerationStyle IDGenerationStyle { get; } } } \ No newline at end of file diff --git a/Reqnroll/CucumberMessages/ExecutionTracking/FeatureTracker.cs b/Reqnroll/CucumberMessages/ExecutionTracking/FeatureTracker.cs index 3e97fc913..d9b962634 100644 --- a/Reqnroll/CucumberMessages/ExecutionTracking/FeatureTracker.cs +++ b/Reqnroll/CucumberMessages/ExecutionTracking/FeatureTracker.cs @@ -24,6 +24,7 @@ public class FeatureTracker internal Dictionary StepDefinitionsByPattern = new(); public string FeatureName { get; set; } public bool Enabled { get; private set; } + public Dictionary PickleIds { get; } = new(); public FeatureTracker(FeatureStartedEvent featureStartedEvent) { @@ -57,6 +58,10 @@ private IEnumerable GenerateStaticMessages(FeatureStartedEvent feature string lastID = ExtractLastID(pickles); IDGenerator = IdGeneratorFactory.Create(lastID); + for(int i = 0; i < pickles.Count; i++) + { + PickleIds.Add(i.ToString(), pickles[i].Id); + } foreach (var pickle in pickles) { diff --git a/Reqnroll/CucumberMessages/ExecutionTracking/HookStepTracker.cs b/Reqnroll/CucumberMessages/ExecutionTracking/HookStepTracker.cs index 7f70317e7..9c4136f7a 100644 --- a/Reqnroll/CucumberMessages/ExecutionTracking/HookStepTracker.cs +++ b/Reqnroll/CucumberMessages/ExecutionTracking/HookStepTracker.cs @@ -1,4 +1,5 @@ using Io.Cucumber.Messages.Types; +using Reqnroll.CucumberMessages.PayloadProcessing.Cucumber; using Reqnroll.Events; using System.Collections.Generic; using System.Linq; diff --git a/Reqnroll/CucumberMessages/ExecutionTracking/TestCaseCucumberMessageTracker.cs b/Reqnroll/CucumberMessages/ExecutionTracking/TestCaseCucumberMessageTracker.cs index 755fc8895..3c8703c2e 100644 --- a/Reqnroll/CucumberMessages/ExecutionTracking/TestCaseCucumberMessageTracker.cs +++ b/Reqnroll/CucumberMessages/ExecutionTracking/TestCaseCucumberMessageTracker.cs @@ -1,6 +1,7 @@ using Gherkin.CucumberMessages; using Io.Cucumber.Messages.Types; using Reqnroll.Bindings; +using Reqnroll.CucumberMessages.PayloadProcessing.Cucumber; using Reqnroll.CucumberMessages.RuntimeSupport; using Reqnroll.Events; using System; @@ -23,15 +24,18 @@ public TestCaseCucumberMessageTracker(FeatureTracker featureTracker) Enabled = featureTracker.Enabled; IDGenerator = featureTracker.IDGenerator; StepDefinitionsByPattern = featureTracker.StepDefinitionsByPattern; + PickleIdList = featureTracker.PickleIds; } // Feature FeatureInfo and Pickle ID make up a unique identifier for tracking execution of Test Cases public string FeatureName { get; set; } public string PickleId { get; set; } = string.Empty; - public string TestCaseTrackerId { get { return FeatureName + PickleId; } } + public string TestCaseTrackerId { get { return FeatureName +@"/" + PickleId; } } public string TestCaseId { get; set; } public string TestCaseStartedId { get; private set; } + private readonly Dictionary PickleIdList; + // When this class is first created (on FeatureStarted), it will not yet be assigned a Scenario/Pickle; // When a Scenario is started, the Publisher will assign the Scenario to the first UnAssigned TestCaseCucumberMessageTracker it finds // This property will indicate that state @@ -164,7 +168,7 @@ internal IEnumerable PostProcessEvent(FeatureFinishedEvent featureFini internal void PreProcessEvent(ScenarioStartedEvent scenarioStartedEvent) { - PickleId = scenarioStartedEvent.ScenarioContext.ScenarioInfo.PickleId; + PickleId = PickleIdList[scenarioStartedEvent.ScenarioContext.ScenarioInfo.PickleIdIndex]; scenarioStartedEvent.FeatureContext.FeatureInfo.CucumberMessages_TestCaseTrackerId = TestCaseTrackerId; TestCaseId = IDGenerator.GetNewId(); TestCaseStartedId = IDGenerator.GetNewId(); diff --git a/Reqnroll/CucumberMessages/ExecutionTracking/TestStepTracker.cs b/Reqnroll/CucumberMessages/ExecutionTracking/TestStepTracker.cs index ff1669ee6..5093201ad 100644 --- a/Reqnroll/CucumberMessages/ExecutionTracking/TestStepTracker.cs +++ b/Reqnroll/CucumberMessages/ExecutionTracking/TestStepTracker.cs @@ -1,6 +1,7 @@ using Io.Cucumber.Messages.Types; using Reqnroll.Assist; using Reqnroll.Bindings; +using Reqnroll.CucumberMessages.PayloadProcessing.Cucumber; using Reqnroll.Events; using System; using System.Collections.Generic; diff --git a/Reqnroll/CucumberMessages/ExecutionTracking/CucumberMessageFactory.cs b/Reqnroll/CucumberMessages/PayloadProcessing/Cucumber/CucumberMessageFactory.cs similarity index 97% rename from Reqnroll/CucumberMessages/ExecutionTracking/CucumberMessageFactory.cs rename to Reqnroll/CucumberMessages/PayloadProcessing/Cucumber/CucumberMessageFactory.cs index 4e608c9c7..bee3e3dfa 100644 --- a/Reqnroll/CucumberMessages/ExecutionTracking/CucumberMessageFactory.cs +++ b/Reqnroll/CucumberMessages/PayloadProcessing/Cucumber/CucumberMessageFactory.cs @@ -4,6 +4,7 @@ using Reqnroll.Analytics; using Reqnroll.Bindings; using Reqnroll.CommonModels; +using Reqnroll.CucumberMessages.ExecutionTracking; using Reqnroll.CucumberMessages.PayloadProcessing; using Reqnroll.CucumberMessages.RuntimeSupport; using Reqnroll.EnvironmentAccess; @@ -17,8 +18,13 @@ using System.Threading.Tasks; using static System.Net.Mime.MediaTypeNames; -namespace Reqnroll.CucumberMessages.ExecutionTracking +namespace Reqnroll.CucumberMessages.PayloadProcessing.Cucumber { + /// + /// This class provides functions to convert execution level detail (events) into Cucumber message elements + /// + /// These are called after execution is completed for a Feature. + /// internal class CucumberMessageFactory { public static TestRunStarted ToTestRunStarted(FeatureStartedEvent featureStartedEvent) diff --git a/Reqnroll/CucumberMessages/PayloadProcessing/Gherkin/GherkinDocumentIDStyleReWriter.cs b/Reqnroll/CucumberMessages/PayloadProcessing/Gherkin/GherkinDocumentIDStyleReWriter.cs new file mode 100644 index 000000000..30a431129 --- /dev/null +++ b/Reqnroll/CucumberMessages/PayloadProcessing/Gherkin/GherkinDocumentIDStyleReWriter.cs @@ -0,0 +1,132 @@ +using Gherkin.CucumberMessages; +using Gherkin.CucumberMessages.Types; +using Reqnroll.CucumberMessages.Configuration; +using Reqnroll.CucumberMessages.RuntimeSupport; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Reqnroll.CucumberMessages.PayloadProcessing.Gherkin +{ + internal class GherkinDocumentIDStyleReWriter : GherkinTypesGherkinDocumentVisitor + { + private IIdGenerator _idGenerator; + public Dictionary IdMap = new(); + + public GherkinDocument ReWriteIds(GherkinDocument document, IDGenerationStyle targetStyle) + { + var existingIdStyle = ProbeForIdGenerationStyle(document); + + if (existingIdStyle == targetStyle) + return document; + + _idGenerator = IdGeneratorFactory.Create(targetStyle); + + AcceptDocument(document); + return document; + } + + private IDGenerationStyle ProbeForIdGenerationStyle(GherkinDocument document) + { + if (document.Feature == null) return IDGenerationStyle.UUID; + var child = document.Feature.Children.FirstOrDefault(); + if (child == null) return IDGenerationStyle.UUID; + + if (child.Rule != null) + return ParseStyle(child.Rule.Id); + + if (child.Background != null) + return ParseStyle(child.Background.Id); + + if (child.Scenario != null) + return ParseStyle(child.Scenario.Id); + + return IDGenerationStyle.UUID; + } + + private IDGenerationStyle ParseStyle(string id) + { + if (Guid.TryParse(id, out var _)) + return IDGenerationStyle.UUID; + + return IDGenerationStyle.Incrementing; + } + + protected override void OnTagVisited(Tag tag) + { + base.OnTagVisited(tag); + var oldId = tag.Id; + var newId = _idGenerator.GetNewId(); + IdMap[oldId] = newId; + tag.Id = newId; + } + protected override void OnScenarioOutlineVisited(Scenario scenarioOutline) + { + base.OnScenarioOutlineVisited(scenarioOutline); + var oldId = scenarioOutline.Id; + var newId = _idGenerator.GetNewId(); + IdMap[oldId] = newId; + scenarioOutline.Id = newId; + } + + protected override void OnScenarioVisited(Scenario scenario) + { + base.OnScenarioVisited(scenario); + var oldId = scenario.Id; + var newId = _idGenerator.GetNewId(); + IdMap[oldId] = newId; + scenario.Id = newId; + } + + protected override void OnRuleVisited(Rule rule) + { + base.OnRuleVisited(rule); + var oldId = rule.Id; + var newId = _idGenerator.GetNewId(); + IdMap[oldId] = newId; + rule.Id = newId; + } + protected override void OnBackgroundVisited(Background background) + { + base.OnBackgroundVisited(background); + var oldId = background.Id; + var newId = _idGenerator.GetNewId(); + IdMap[oldId] = newId; + background.Id = newId; + + } + protected override void OnStepVisited(Step step) + { + base.OnStepVisited(step); + var oldId = step.Id; + var newId = _idGenerator.GetNewId(); + IdMap[oldId] = newId; + step.Id = newId; + } + protected override void OnExamplesVisited(Examples examples) + { + base.OnExamplesVisited(examples); + var oldId = examples.Id; + var newId = _idGenerator.GetNewId(); + IdMap[oldId] = newId; + examples.Id = newId; + } + protected override void OnTableHeaderVisited(TableRow header) + { + base.OnTableHeaderVisited(header); + var oldId = header.Id; + var newId = _idGenerator.GetNewId(); + IdMap[oldId] = newId; + header.Id = newId; + } + protected override void OnTableRowVisited(TableRow row) + { + base.OnTableRowVisited(row); + var oldId = row.Id; + var newId = _idGenerator.GetNewId(); + IdMap[oldId] = newId; + row.Id = newId; + } + } +} diff --git a/Reqnroll/CucumberMessages/PayloadProcessing/Gherkin/GherkinTypesPickleVisitor.cs b/Reqnroll/CucumberMessages/PayloadProcessing/Gherkin/GherkinTypesPickleVisitor.cs new file mode 100644 index 000000000..ccb6a1a74 --- /dev/null +++ b/Reqnroll/CucumberMessages/PayloadProcessing/Gherkin/GherkinTypesPickleVisitor.cs @@ -0,0 +1,130 @@ +using Gherkin.CucumberMessages.Types; +using System; +using System.Collections.Generic; +using System.Data; +using System.Text; + +namespace Reqnroll.CucumberMessages.PayloadProcessing.Gherkin +{ + abstract class GherkinTypesPickleVisitor + { + + public virtual void AcceptPickle(Pickle pickle) + { + OnVisitingPickle(pickle); + + foreach (var tag in pickle.Tags) + { + AcceptTag(tag); + } + foreach (var step in pickle.Steps) + { + AcceptStep(step); + } + OnVisitedPickle(pickle); + } + + protected virtual void AcceptStep(PickleStep step) + { + OnVisitingPickleStep(step); + AcceptPickleStepArgument(step.Argument); + OnVisitedPickleStep(step); + } + + protected virtual void AcceptPickleStepArgument(PickleStepArgument argument) + { + OnVisitingPickleStepArgument(argument); + AcceptPickleTable(argument.DataTable); + OnVisitedPickleStepArgument(argument); + } + + protected virtual void AcceptPickleTable(PickleTable dataTable) + { + OnVisitingPickleTable(dataTable); + foreach (var row in dataTable.Rows) + { + AcceptPickleTableRow(row); + } + OnVisitedPickleTable(dataTable); + } + + protected virtual void AcceptPickleTableRow(PickleTableRow row) + { + OnVisitingPickleTableRow(row); + foreach (var cell in row.Cells) + { + AcceptPickleTableCell(cell); + } + OnVisitedPickleTableRow(row); + } + + protected virtual void AcceptPickleTableCell(PickleTableCell cell) + { + OnVisitedPickleTableCell(cell); + } + protected virtual void AcceptTag(PickleTag tag) + { + OnVisitedPickleTag(tag); + } + + protected virtual void OnVisitingPickle(Pickle pickle) + { + //nop + } + + protected virtual void OnVisitedPickle(Pickle pickle) + { + //nop + } + + protected virtual void OnVisitedPickleTag(PickleTag tag) + { + //nop + } + + protected virtual void OnVisitingPickleStep(PickleStep step) + { + //nop + } + + protected virtual void OnVisitedPickleStep(PickleStep step) + { + //nop + } + + protected virtual void OnVisitingPickleStepArgument(PickleStepArgument argument) + { + //nop + } + + protected virtual void OnVisitedPickleStepArgument(PickleStepArgument argument) + { + //nop + } + + protected virtual void OnVisitingPickleTable(PickleTable dataTable) + { + //nop + } + + protected virtual void OnVisitedPickleTable(PickleTable dataTable) + { + //nop + } + + protected virtual void OnVisitingPickleTableRow(PickleTableRow row) + { + //nop + } + + protected virtual void OnVisitedPickleTableRow(PickleTableRow row) + { + //nop + } + + protected virtual void OnVisitedPickleTableCell(PickleTableCell cell) + { + //nop + } + } +} diff --git a/Reqnroll/CucumberMessages/PayloadProcessing/Gherkin/PickleIDStyleReWriter.cs b/Reqnroll/CucumberMessages/PayloadProcessing/Gherkin/PickleIDStyleReWriter.cs new file mode 100644 index 000000000..88283223a --- /dev/null +++ b/Reqnroll/CucumberMessages/PayloadProcessing/Gherkin/PickleIDStyleReWriter.cs @@ -0,0 +1,76 @@ +using Gherkin.CucumberMessages; +using Gherkin.CucumberMessages.Types; +using Reqnroll.CucumberMessages.Configuration; +using Reqnroll.CucumberMessages.RuntimeSupport; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace Reqnroll.CucumberMessages.PayloadProcessing.Gherkin +{ + internal class PickleIDStyleReWriter : GherkinTypesPickleVisitor + { + private Dictionary _idMap; + private IEnumerable _originalPickles; + private IDGenerationStyle _idStyle; + private IIdGenerator _idGenerator; + + public IEnumerable ReWriteIds(IEnumerable pickles, Dictionary idMap, IDGenerationStyle targetStyle) + { + if (pickles == null || pickles.Count() == 0) return pickles; + + _idMap = idMap; + _originalPickles = pickles; + _idStyle = targetStyle; + var existingIdStyle = ProbeForIdGenerationStyle(pickles.First()); + + if (existingIdStyle == targetStyle) + return pickles; + + _idGenerator = IdGeneratorFactory.Create(targetStyle); + + + foreach (var pickle in _originalPickles) + { + AcceptPickle(pickle); + } + + return _originalPickles; + } + + private IDGenerationStyle ProbeForIdGenerationStyle(Pickle pickle) + { + if (Guid.TryParse(pickle.Id, out var _)) + return IDGenerationStyle.UUID; + + return IDGenerationStyle.Incrementing; + } + + protected override void OnVisitedPickle(Pickle pickle) + { + base.OnVisitedPickle(pickle); + + if (_idMap.TryGetValue(pickle.Id, out var newId)) + pickle.Id = newId; + pickle.AstNodeIds = pickle.AstNodeIds.Select(id => _idMap.TryGetValue(id, out var newId2) ? newId2 : id).ToList().AsReadOnly(); + } + + protected override void OnVisitedPickleStep(PickleStep step) + { + base.OnVisitedPickleStep(step); + if (_idMap.TryGetValue(step.Id, out var newId)) + step.Id = newId; + step.AstNodeIds = step.AstNodeIds.Select(id => _idMap.TryGetValue(id, out var newId2) ? newId2 : id).ToList().AsReadOnly(); + } + + protected override void OnVisitedPickleTag(PickleTag tag) + { + base.OnVisitedPickleTag(tag); + + if (_idMap.TryGetValue(tag.AstNodeId, out var newId)) + tag.AstNodeId = newId; + } + } +} diff --git a/Reqnroll/CucumberMessages/PayloadProcessing/Gherkin/ScenarioTransformationVisitor.cs b/Reqnroll/CucumberMessages/PayloadProcessing/Gherkin/ScenarioTransformationVisitor.cs deleted file mode 100644 index 2f0862702..000000000 --- a/Reqnroll/CucumberMessages/PayloadProcessing/Gherkin/ScenarioTransformationVisitor.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Gherkin.CucumberMessages.Types; - -namespace Reqnroll.CucumberMessages.PayloadProcessing.Gherkin -{ - abstract class ScenarioTransformationVisitor : GherkinTypesGherkinDocumentVisitor - { - protected GherkinDocument _sourceDocument; - private GherkinDocument _transformedDocument; - private Feature _transformedFeature; - private bool _hasTransformedScenarioInFeature = false; - private bool _hasTransformedScenarioInCurrentRule = false; - private readonly List _featureChildren = new(); - private readonly List _ruleChildren = new(); - private List _currentChildren; - - public GherkinDocument TransformDocument(GherkinDocument document) - { - Reset(); - AcceptDocument(document); - return _transformedDocument ?? document; - } - - private void Reset() - { - _sourceDocument = null; - _transformedDocument = null; - _transformedFeature = null; - _featureChildren.Clear(); - _ruleChildren.Clear(); - _hasTransformedScenarioInFeature = false; - _hasTransformedScenarioInCurrentRule = false; - _currentChildren = _featureChildren; - } - - protected abstract Scenario GetTransformedScenarioOutline(Scenario scenarioOutline); - protected abstract Scenario GetTransformedScenario(Scenario scenario); - - protected override void OnScenarioOutlineVisited(Scenario scenarioOutline) - { - var transformedScenarioOutline = GetTransformedScenarioOutline(scenarioOutline); - OnScenarioVisitedInternal(scenarioOutline, transformedScenarioOutline); - } - - protected override void OnScenarioVisited(Scenario scenario) - { - var transformedScenario = GetTransformedScenario(scenario); - OnScenarioVisitedInternal(scenario, transformedScenario); - } - - private void OnScenarioVisitedInternal(Scenario scenario, Scenario transformedScenario) - { - if (transformedScenario == null) - { - _currentChildren.Add(scenario); - return; - } - - _hasTransformedScenarioInFeature = true; - _hasTransformedScenarioInCurrentRule = true; - _currentChildren.Add(transformedScenario); - } - - protected override void OnBackgroundVisited(Background background) - { - _currentChildren.Add(background); - } - - protected override void OnRuleVisiting(Rule rule) - { - _ruleChildren.Clear(); - _hasTransformedScenarioInCurrentRule = false; - _currentChildren = _ruleChildren; - } - - protected override void OnRuleVisited(Rule rule) - { - _currentChildren = _featureChildren; - if (_hasTransformedScenarioInCurrentRule) - { - var transformedRule = new Rule( - rule.Tags?.ToArray() ?? Array.Empty(), - rule.Location, - rule.Keyword, - rule.Name, - rule.Description, - _ruleChildren.ToArray()); - _featureChildren.Add(transformedRule); - } - else - { - _featureChildren.Add(rule); - } - } - - protected override void OnFeatureVisited(Feature feature) - { - if (_hasTransformedScenarioInFeature) - _transformedFeature = new Feature( - feature.Tags?.ToArray() ?? Array.Empty(), - feature.Location, - feature.Language, - feature.Keyword, - feature.Name, - feature.Description, - _featureChildren.ToArray()); - } - - protected override void OnDocumentVisiting(GherkinDocument document) - { - _sourceDocument = document; - } - - protected override void OnDocumentVisited(GherkinDocument document) - { - if (_transformedFeature != null) - _transformedDocument = new GherkinDocument(_transformedFeature, document.Comments.ToArray()); - } - } -} diff --git a/Reqnroll/CucumberMessages/PubSub/CucumberMessagePublisher.cs b/Reqnroll/CucumberMessages/PubSub/CucumberMessagePublisher.cs index ef8b14bb7..f90e74256 100644 --- a/Reqnroll/CucumberMessages/PubSub/CucumberMessagePublisher.cs +++ b/Reqnroll/CucumberMessages/PubSub/CucumberMessagePublisher.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using Reqnroll.CucumberMessages.ExecutionTracking; +using Reqnroll.CucumberMessages.PayloadProcessing.Cucumber; namespace Reqnroll.CucumberMessages.PubSub { @@ -127,7 +128,8 @@ private void ScenarioStartedEventHandler(ScenarioStartedEvent scenarioStartedEve { if (featureTracker.Enabled) { - var id = featureName + scenarioStartedEvent.ScenarioContext.ScenarioInfo.PickleId; + var pickleId = featureTracker.PickleIds[scenarioStartedEvent.ScenarioContext.ScenarioInfo.PickleIdIndex]; + var id = featureName + @"/" + pickleId; var tccmt = new TestCaseCucumberMessageTracker(featureTracker); tccmt.ProcessEvent(scenarioStartedEvent); traceListener.WriteTestOutput($"Cucumber Message Publisher: ScenarioStartedEventHandler: {featureName} {id} started"); diff --git a/Reqnroll/CucumberMessages/PubSub/FileOutputPlugin.cs b/Reqnroll/CucumberMessages/PubSub/FileOutputPlugin.cs index 97cc9b35d..b2fc9560c 100644 --- a/Reqnroll/CucumberMessages/PubSub/FileOutputPlugin.cs +++ b/Reqnroll/CucumberMessages/PubSub/FileOutputPlugin.cs @@ -80,7 +80,6 @@ private void CloseFileSink() private void LaunchFileSink(TestRunStartedEvent testRunStarted) { - Debugger.Launch(); ICucumberConfiguration config = _configuration; if (!config.Enabled) diff --git a/Reqnroll/CucumberMessages/RuntimeSupport/FeatureLevelCucumberMessages.cs b/Reqnroll/CucumberMessages/RuntimeSupport/FeatureLevelCucumberMessages.cs index 0d4c921f6..149e4eeb6 100644 --- a/Reqnroll/CucumberMessages/RuntimeSupport/FeatureLevelCucumberMessages.cs +++ b/Reqnroll/CucumberMessages/RuntimeSupport/FeatureLevelCucumberMessages.cs @@ -1,15 +1,22 @@ -using System.Collections.Generic; +using Gherkin.CucumberMessages.Types; +using Reqnroll.CucumberMessages.Configuration; +using Reqnroll.CucumberMessages.PayloadProcessing.Gherkin; +using System.Collections.Generic; using System.Text.Json; namespace Reqnroll.CucumberMessages.RuntimeSupport { public class FeatureLevelCucumberMessages { - public FeatureLevelCucumberMessages(string serializedSourceMessage, string serializedgherkinDocument, string serializedPickles, string location) + public FeatureLevelCucumberMessages(string serializedSourceMessage, string serializedGherkinDocument, string serializedPickles, string location) { - GherkinDocument = System.Text.Json.JsonSerializer.Deserialize(serializedgherkinDocument); Source = JsonSerializer.Deserialize(serializedSourceMessage); - Pickles = JsonSerializer.Deserialize>(serializedPickles); + var gherkinDocument = System.Text.Json.JsonSerializer.Deserialize(serializedGherkinDocument); + var pickles = JsonSerializer.Deserialize>(serializedPickles); + ReWriteIds(gherkinDocument, pickles, out var newGherkinDocument, out var newPickles); + + GherkinDocument = newGherkinDocument; + Pickles = newPickles; Location = location; PickleJar = new PickleJar(Pickles); } @@ -20,5 +27,16 @@ public FeatureLevelCucumberMessages(string serializedSourceMessage, string seria public IEnumerable Pickles { get; } public PickleJar PickleJar { get; } + private void ReWriteIds(GherkinDocument gherkinDocument, IEnumerable pickles, out GherkinDocument newGherkinDocument, out IEnumerable newPickles) + { + var targetIdStyle = CucumberConfiguration.Current.IDGenerationStyle; + var gherkinDocumentIDStyleReWriter = new GherkinDocumentIDStyleReWriter(); + newGherkinDocument = gherkinDocumentIDStyleReWriter.ReWriteIds(gherkinDocument, targetIdStyle); + var idMap = gherkinDocumentIDStyleReWriter.IdMap; + + var pickleIDStyleReWriter = new PickleIDStyleReWriter(); + newPickles = pickleIDStyleReWriter.ReWriteIds(pickles, idMap, targetIdStyle); + } + } } diff --git a/Reqnroll/CucumberMessages/RuntimeSupport/IncrementingToUUIDConverter.cs b/Reqnroll/CucumberMessages/RuntimeSupport/IncrementingToUUIDConverter.cs deleted file mode 100644 index c9210e912..000000000 --- a/Reqnroll/CucumberMessages/RuntimeSupport/IncrementingToUUIDConverter.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Reqnroll.CucumberMessages.PayloadProcessing.Cucumber; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Reqnroll.CucumberMessages.RuntimeSupport -{ - internal class IncrementingToUUIDConverter : CucumberMessage_TraversalVisitorBase - { - Dictionary _mapping = new Dictionary(); - public IncrementingToUUIDConverter() - { - } - - } -} diff --git a/Reqnroll/ScenarioInfo.cs b/Reqnroll/ScenarioInfo.cs index 584160ccb..c4d8203d0 100644 --- a/Reqnroll/ScenarioInfo.cs +++ b/Reqnroll/ScenarioInfo.cs @@ -38,20 +38,21 @@ public class ScenarioInfo public string Description { get; } /// - /// The PickleId of the Scenario when exported as a Cucumber Message "pickle". + /// The PickleIdIndex of the test Scenario when exported as a Cucumber Message "pickle". + /// The index is the sequential number of the pickle in the list of pickles generated from the feature file. /// - public string PickleId { get; } + public string PickleIdIndex { get; } // The list of step PickleIds in the step sequence for this test case. public PickleStepSequence PickleStepSequence { get; private set; } - public ScenarioInfo(string title, string description, string[] tags, IOrderedDictionary arguments, string[] inheritedTags = null, string pickleId = null, PickleStepSequence stepSequence = null) + public ScenarioInfo(string title, string description, string[] tags, IOrderedDictionary arguments, string[] inheritedTags = null, string pickleIndex = null, PickleStepSequence stepSequence = null) { Title = title; Description = description; Tags = tags ?? Array.Empty(); Arguments = arguments; CombinedTags = Tags.Concat(inheritedTags ?? Array.Empty()).ToArray(); - PickleId = pickleId; + PickleIdIndex = pickleIndex; PickleStepSequence = stepSequence; } } diff --git a/Tests/CucumberMessages.CompatibilityTests/CucumberCompatibilityTestBase.cs b/Tests/CucumberMessages.CompatibilityTests/CucumberCompatibilityTestBase.cs index 1f068e851..7fc97dfc7 100644 --- a/Tests/CucumberMessages.CompatibilityTests/CucumberCompatibilityTestBase.cs +++ b/Tests/CucumberMessages.CompatibilityTests/CucumberCompatibilityTestBase.cs @@ -42,6 +42,7 @@ protected void ResetCucumberMessages(string? fileToDelete = null) DisableCucumberMessages(); DeletePreviousMessagesOutput(fileToDelete); ResetCucumberMessagesOutputFileName(); + Environment.SetEnvironmentVariable(CucumberConfigurationConstants.REQNROLL_CUCUMBER_MESSAGES_ID_GENERATION_STYLE_ENVIRONMENT_VARIABLE, null); } protected void ResetCucumberMessagesOutputFileName() @@ -113,5 +114,10 @@ protected static string ActualsResultLocationDirectory() return resultLocation; } + protected void SetEnvironmentVariableForGUIDIdGeneration() + { + Environment.SetEnvironmentVariable(CucumberConfigurationConstants.REQNROLL_CUCUMBER_MESSAGES_ID_GENERATION_STYLE_ENVIRONMENT_VARIABLE, CucumberConfigurationConstants.REQNROLL_CUCUMBER_MESSAGES_ID_GENERATION_STYLE_UUID); + } + } } diff --git a/Tests/CucumberMessages.CompatibilityTests/CucumberCompatibilityTests.cs b/Tests/CucumberMessages.CompatibilityTests/CucumberCompatibilityTests.cs index 42fea24a9..0cac5c5db 100644 --- a/Tests/CucumberMessages.CompatibilityTests/CucumberCompatibilityTests.cs +++ b/Tests/CucumberMessages.CompatibilityTests/CucumberCompatibilityTests.cs @@ -59,6 +59,28 @@ When I eat 5 cukes ShouldAllScenariosPass(); } + + [TestMethod] + public void CanGenerateGUIDIds_SmokeTest() + { + ResetCucumberMessages("CanGenerateGUIDIds_SmokeTest.ndjson"); + EnableCucumberMessages(); + SetCucumberMessagesOutputFileName("CanGenerateGUIDIds_SmokeTest.ndjson"); + SetEnvironmentVariableForGUIDIdGeneration(); + CucumberMessagesAddConfigurationFile("CucumberMessages.configuration.json"); + + AddFeatureFile(""" + Feature: Cucumber Messages Smoke Test + Scenario: Eating Cukes + When I eat 5 cukes + """); + + AddPassingStepBinding("When"); + ExecuteTests(); + + ShouldAllScenariosPass(); + } + [TestMethod] public void SmokeTestMultipleFeatures() { diff --git a/Tests/CucumberMessages.CompatibilityTests/CucumberMessagesValidator.cs b/Tests/CucumberMessages.CompatibilityTests/CucumberMessagesValidator.cs index 45020f3d2..92d449f1a 100644 --- a/Tests/CucumberMessages.CompatibilityTests/CucumberMessagesValidator.cs +++ b/Tests/CucumberMessages.CompatibilityTests/CucumberMessagesValidator.cs @@ -113,7 +113,7 @@ private void CompareMessageType() if (!(typeof(T) == typeof(TestStepFinished))) { - actual.Should().BeEquivalentTo(expected, options => options.WithTracing()); + actual.Should().BeEquivalentTo(expected, options => options.WithTracing(),"When comparing " + typeof(T).Name + "s"); } else { @@ -128,9 +128,9 @@ private void CompareMessageType() var expected_hookRelatedTestStepFinished = expected.OfType().Where(tsf => expecteds_elementsByID[tsf.TestStepId].As().HookId != null).ToList(); var expected_stepRelatedTestStepFinished = expected.OfType().Where(tsf => expecteds_elementsByID[tsf.TestStepId].As().HookId == null).ToList(); - actual_stepRelatedTestStepFinished.Should().BeEquivalentTo(expected_stepRelatedTestStepFinished, options => options.WithTracing()); + actual_stepRelatedTestStepFinished.Should().BeEquivalentTo(expected_stepRelatedTestStepFinished, options => options.WithTracing(), "when comparing TestStepFinished messages"); - actual_hookRelatedTestStepFinished.Should().BeEquivalentTo(expected_hookRelatedTestStepFinished, options => options.WithoutStrictOrdering().WithTracing()); + actual_hookRelatedTestStepFinished.Should().BeEquivalentTo(expected_hookRelatedTestStepFinished, options => options.WithoutStrictOrdering().WithTracing(), "when comparing Hook TestStepFinished messages"); } } diff --git a/Tests/CucumberMessages.CompatibilityTests/FluentAsssertionCucumberMessagePropertySelectionRule.cs b/Tests/CucumberMessages.CompatibilityTests/FluentAsssertionCucumberMessagePropertySelectionRule.cs index 1cd13c029..502eeefb2 100644 --- a/Tests/CucumberMessages.CompatibilityTests/FluentAsssertionCucumberMessagePropertySelectionRule.cs +++ b/Tests/CucumberMessages.CompatibilityTests/FluentAsssertionCucumberMessagePropertySelectionRule.cs @@ -17,7 +17,7 @@ public class FluentAsssertionCucumberMessagePropertySelectionRule : IMemberSelec { // Properties to skip - this is the default set of properties that are not comparable across platforms // Id: Ids are not assigned in the same order across platforms. - // AstNodeIds, PickleId, HookId, PickleStepId, StepDefinitionIds, TestStepId, TestCaseStartedId, TestCaseId, WorkerId: Ids are not assigned in the same order across platforms. + // AstNodeIds, PickleIdIndex, HookId, PickleStepId, StepDefinitionIds, TestStepId, TestCaseStartedId, TestCaseId, WorkerId: Ids are not assigned in the same order across platforms. // Location, Line and Column (in Location elements) are not always comparable (eg, CCK refers to source line #s in typescript) // Uri is not always comparable (eg, CCK refers to source file paths in typescript) // JavaMethod and JavaStackTraceElement contents are specific to the platform. CCK does not include these as it generates Uri references to source rather than Method references diff --git a/Tests/Reqnroll.GeneratorTests/CustomTestGeneratorProviderTest.cs b/Tests/Reqnroll.GeneratorTests/CustomTestGeneratorProviderTest.cs index a984dc039..a67dc9195 100644 --- a/Tests/Reqnroll.GeneratorTests/CustomTestGeneratorProviderTest.cs +++ b/Tests/Reqnroll.GeneratorTests/CustomTestGeneratorProviderTest.cs @@ -56,7 +56,7 @@ public static UnitTestFeatureGenerator CreateUnitTestConverter(IUnitTestGenerato runtimeConfiguration.AllowRowTests = true; runtimeConfiguration.AllowDebugGeneratedFiles = true; - return new UnitTestFeatureGenerator(testGeneratorProvider, codeDomHelper, runtimeConfiguration, new DecoratorRegistryStub(), new DefaultListener()); + return new UnitTestFeatureGenerator(testGeneratorProvider, codeDomHelper, runtimeConfiguration, new DecoratorRegistryStub(), new DefaultListener(), new SimpleCucumberMessagesConfiguration()); } /// diff --git a/Tests/Reqnroll.GeneratorTests/FeatureGeneratorProviderTests.cs b/Tests/Reqnroll.GeneratorTests/FeatureGeneratorProviderTests.cs index 3d37a9e3b..1dcabf975 100644 --- a/Tests/Reqnroll.GeneratorTests/FeatureGeneratorProviderTests.cs +++ b/Tests/Reqnroll.GeneratorTests/FeatureGeneratorProviderTests.cs @@ -18,7 +18,7 @@ private static UnitTestFeatureGeneratorProvider CreateUnitTestFeatureGeneratorPr Configuration.ReqnrollConfiguration generatorReqnrollConfiguration = ConfigurationLoader.GetDefault(); CodeDomHelper codeDomHelper = new CodeDomHelper(CodeDomProviderLanguage.CSharp); UnitTestFeatureGenerator unitTestFeatureGenerator = new UnitTestFeatureGenerator( - new NUnit3TestGeneratorProvider(codeDomHelper), codeDomHelper, generatorReqnrollConfiguration, new DecoratorRegistryStub(), new DefaultListener()); + new NUnit3TestGeneratorProvider(codeDomHelper), codeDomHelper, generatorReqnrollConfiguration, new DecoratorRegistryStub(), new DefaultListener(), new SimpleCucumberMessagesConfiguration()); return new UnitTestFeatureGeneratorProvider(unitTestFeatureGenerator); } diff --git a/Tests/Reqnroll.GeneratorTests/IUnitTestGeneratorProviderExtensions.cs b/Tests/Reqnroll.GeneratorTests/IUnitTestGeneratorProviderExtensions.cs index 75c3e5cc1..ad6590139 100644 --- a/Tests/Reqnroll.GeneratorTests/IUnitTestGeneratorProviderExtensions.cs +++ b/Tests/Reqnroll.GeneratorTests/IUnitTestGeneratorProviderExtensions.cs @@ -22,7 +22,7 @@ public static UnitTestFeatureGenerator CreateUnitTestConverter(this IUnitTestGen runtimeConfiguration.AllowRowTests = true; runtimeConfiguration.AllowDebugGeneratedFiles = true; - return new UnitTestFeatureGenerator(testGeneratorProvider, codeDomHelper, runtimeConfiguration, new DecoratorRegistryStub(), new DefaultListener()); + return new UnitTestFeatureGenerator(testGeneratorProvider, codeDomHelper, runtimeConfiguration, new DecoratorRegistryStub(), new DefaultListener(), new SimpleCucumberMessagesConfiguration()); } public static IFeatureGenerator CreateFeatureGenerator(this IUnitTestGeneratorProvider testGeneratorProvider, string[] addNonParallelizableMarkerForTags = null) diff --git a/Tests/Reqnroll.GeneratorTests/SimpleCucumberMessagesConfiguration.cs b/Tests/Reqnroll.GeneratorTests/SimpleCucumberMessagesConfiguration.cs new file mode 100644 index 000000000..6b50b72ac --- /dev/null +++ b/Tests/Reqnroll.GeneratorTests/SimpleCucumberMessagesConfiguration.cs @@ -0,0 +1,22 @@ +using Reqnroll.CucumberMessages.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Reqnroll.GeneratorTests +{ + internal class SimpleCucumberMessagesConfiguration : ICucumberConfiguration + { + public bool Enabled => false; + + public string BaseDirectory => ""; + + public string OutputDirectory => ""; + + public string OutputFileName => "reqnroll_report.ndjson"; + + public IDGenerationStyle IDGenerationStyle => IDGenerationStyle.Incrementing; + } +} diff --git a/Tests/Reqnroll.GeneratorTests/TestGeneratorTest.cs b/Tests/Reqnroll.GeneratorTests/TestGeneratorTest.cs index 2a1b542fe..55aa0a414 100644 --- a/Tests/Reqnroll.GeneratorTests/TestGeneratorTest.cs +++ b/Tests/Reqnroll.GeneratorTests/TestGeneratorTest.cs @@ -52,7 +52,7 @@ public static UnitTestFeatureGenerator CreateUnitTestConverter(IUnitTestGenerato runtimeConfiguration.AllowRowTests = true; runtimeConfiguration.AllowDebugGeneratedFiles = true; - return new UnitTestFeatureGenerator(testGeneratorProvider, codeDomHelper, runtimeConfiguration, new DecoratorRegistryStub(), new DefaultListener()); + return new UnitTestFeatureGenerator(testGeneratorProvider, codeDomHelper, runtimeConfiguration, new DecoratorRegistryStub(), new DefaultListener(), new SimpleCucumberMessagesConfiguration()); } /// diff --git a/Tests/Reqnroll.GeneratorTests/TestGeneratorTestsBase.cs b/Tests/Reqnroll.GeneratorTests/TestGeneratorTestsBase.cs index 4ea012ef8..6c2939c14 100644 --- a/Tests/Reqnroll.GeneratorTests/TestGeneratorTestsBase.cs +++ b/Tests/Reqnroll.GeneratorTests/TestGeneratorTestsBase.cs @@ -85,7 +85,7 @@ protected TestGenerator CreateTestGenerator(ProjectSettings projectSettings) { Configuration.ReqnrollConfiguration generatorReqnrollConfiguration = ConfigurationLoader.GetDefault(); CodeDomHelper codeDomHelper = new CodeDomHelper(CodeDomProviderLanguage.CSharp); - UnitTestFeatureGenerator unitTestFeatureGenerator = new UnitTestFeatureGenerator(new NUnit3TestGeneratorProvider(codeDomHelper), codeDomHelper, generatorReqnrollConfiguration, new DecoratorRegistryStub(), new DefaultListener()); + UnitTestFeatureGenerator unitTestFeatureGenerator = new UnitTestFeatureGenerator(new NUnit3TestGeneratorProvider(codeDomHelper), codeDomHelper, generatorReqnrollConfiguration, new DecoratorRegistryStub(), new DefaultListener(), new SimpleCucumberMessagesConfiguration()); var gherkinParserFactory = new ReqnrollGherkinParserFactory();