Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
cae6f1c
#563: Added Gherkin `.feature` specification import
Jul 23, 2026
49394cc
Fix self-trace and tests
Jul 23, 2026
4dbb227
Fix sonar findings
Jul 23, 2026
b9ddc6b
Fix test under windows
Jul 23, 2026
d96d46c
Add changelog summary
Jul 23, 2026
37a263e
Extract classes from GherkinImporter
Jul 23, 2026
2be3215
Add finish method to LineReader
Jul 23, 2026
32f32ed
Fix self-trace
Jul 24, 2026
e26c3f3
Code cleanup
Jul 24, 2026
beab254
Fix review findings
Jul 24, 2026
2ae911b
Add missing eclipse formatter config
Jul 24, 2026
56c9523
Reformat sources, add qualifying this
Jul 24, 2026
58f5502
Add method ImportEventListener.addSpecificationItem()
Jul 24, 2026
ff8b7f2
Simplify tag importers by removing unnecessary complexity and improvi…
Jul 24, 2026
7fdda54
Remove unnecessary EventBuffer
Jul 24, 2026
b41045a
Improve comments
Jul 24, 2026
a2e77c3
Remove already implemented changeset
Jul 24, 2026
aa0739c
#562: Add implementation plan
Jul 24, 2026
31e104d
Improve plan
Jul 24, 2026
a1ebbe1
#562: Support coverage tags in .md and .rst files
Jul 24, 2026
e32cef6
Cleanup test code
Jul 24, 2026
7b90673
Merge branch 'main' into feature/562_support_coverage_tags_in_markdow…
Jul 27, 2026
46480ef
Pre-compile regexp
Jul 27, 2026
d699867
Code cleanup
Jul 27, 2026
1f3e84a
Fix sonar findings
Jul 27, 2026
6c40f63
Fix formatting
Jul 27, 2026
3555288
Fix sonar warnings about regex
Jul 27, 2026
4bf3746
Update doc/spec/system_requirements.md
kaklakariada Jul 27, 2026
f3bf37f
Remove support for short tags
Jul 27, 2026
a23fa03
Remove mentions of shor tags
Jul 27, 2026
03637a5
Merge branch 'feature/562_support_coverage_tags_in_markdown_and_restr…
Jul 27, 2026
14aecb4
Remove changeset
Jul 27, 2026
8b0551c
Remove unnecessary line wraps
Jul 27, 2026
2d0c5e6
Implement review findings
Jul 28, 2026
722c92c
Update release date
Jul 28, 2026
1d4774c
Revert change to importer
Jul 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion doc/changes/changes_4.7.0.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# OpenFastTrace 4.7.0, released 2026-07-??
# OpenFastTrace 4.7.0, released 2026-07-28

Code name: Gherkin Importer

Expand All @@ -9,6 +9,9 @@ OpenFastTrace can now import traced specifications directly from Gherkin
optional `Covers` and `Needs` metadata; existing coverage tags remain supported
when written in Gherkin comments.

Markdown and RST documentation can now also cover specification items with coverage tags in their native comments.

## New Features

* #563: Added Gherkin `.feature` specification import for annotated scenarios and scenario outlines.
* #562: Added coverage tag import from Markdown and RST comments.
38 changes: 30 additions & 8 deletions doc/spec/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ For each specification artifact type OFT uses an importer. The importer uses the

### Shared Coverage Tag Parser

The `importer/tag-importer-common` module provides the reusable line scanning and coverage-tag parsing used by importers. Its public API consists of `LineReader`, which forwards input lines to a consumer, and `CoverageTagParser`, which recognizes full coverage tags and optionally configured short coverage tags.
The `importer/tag-importer-common` module provides the reusable line scanning and coverage-tag parsing used by importers. Its public API consists of `LineReader`, which forwards input lines to a consumer, and `CoverageTagParser`, which recognizes coverage tags.

The tag importer remains responsible for selecting its input files and creating the shared parser. Parsing implementation classes remain encapsulated in the shared module so that future importers can reuse the same coverage-tag semantics without depending on tag-importer internals.

Expand Down Expand Up @@ -382,6 +382,28 @@ Needs: impl, utest, itest

RST and Markdown share a common underlying parser that operates on a line-by-line basis.

##### Markdown Comment Coverage Tags
`dsn~markdown.comment-coverage-tags~1`

For a Markdown input file, the lightweight-markup importer delegates a line to the shared coverage-tag parser only when the complete line is a single-line HTML comment, apart from optional surrounding whitespace. The dedicated Markdown importer keeps its priority ahead of the Tag Importer, and the existing markup state machine continues to process every input line.

Covers:

* `scn~markdown.comment-coverage-tags~1`

Needs: impl, utest, itest

##### RST Comment Coverage Tags
`dsn~rst.comment-coverage-tags~1`

For an RST input file, the lightweight-markup importer delegates a line to the shared coverage-tag parser only when it begins with optional whitespace, `..`, and whitespace, and is not an RST directive. The dedicated RST importer keeps its priority ahead of the Tag Importer, and the existing markup state machine continues to process every input line.

Covers:

* `scn~rst.comment-coverage-tags~1`

Needs: impl, utest, itest

##### Disabling OFT Parsing for Parts of a Markup File
`dsn~disabling-oft-parsing-for-parts-of-a-markup-file~1`

Expand Down Expand Up @@ -953,7 +975,7 @@ OFT imports coverage tags in the full tag format:

Covers:

* `req~import.full-coverage-tag-format~1`
* `req~import.full-coverage-tag-format~2`

Needs: impl, utest

Expand All @@ -973,7 +995,7 @@ Especially when used for design document files like UML models, requiring covera

Covers:

* `req~import.full-coverage-tag-format~1`
* `req~import.full-coverage-tag-format~2`

Needs: impl, utest

Expand All @@ -991,7 +1013,7 @@ An item can cover multiple IDs. This avoids creating multiple IDs for the same i

Covers:

* `req~import.full-coverage-tag-format~1`
* `req~import.full-coverage-tag-format~2`

Needs: impl, utest

Expand All @@ -1011,7 +1033,7 @@ Specifying an explicit revision in coverage tags allows incrementing the revisio

Covers:

* `req~import.full-coverage-tag-format~1`
* `req~import.full-coverage-tag-format~2`

Needs: impl, utest

Expand All @@ -1035,7 +1057,7 @@ Specifying an explicit name in coverage tags allows overriding the auto-generate

Covers:

* `req~import.full-coverage-tag-format~1`
* `req~import.full-coverage-tag-format~2`

Needs: impl, utest

Expand All @@ -1050,7 +1072,7 @@ When you need to cover these items it's important that the name is predictable a

Covers:

* `req~import.full-coverage-tag-format~1`
* `req~import.full-coverage-tag-format~2`

Needs: impl, utest

Expand All @@ -1070,7 +1092,7 @@ During import of short tags OFT requires the following configuration:

Covers:

* `req~import.short-coverage-tag-format~1`
* `req~import.short-coverage-tag-format~2`

Needs: impl, utest

Expand Down
54 changes: 44 additions & 10 deletions doc/spec/system_requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,13 @@ One of the OpenFastTrace design goals is to provide a Java-based drop-in replace
Needs: req

### Coverage Tag Import
`feat~coverage-tag-import~1`
`feat~coverage-tag-import~2`

OFT imports coverage tags from source code files.
OFT imports coverage tags from source code files and supported documentation files.

Rationale:

Coverage tags indicate parts of the source code that implements a certain requirement.
Coverage tags indicate parts of source code or documentation that cover a certain requirement.

Needs: req

Expand Down Expand Up @@ -395,27 +395,61 @@ Needs: dsn

#### Coverage Tags

Developers add coverage tags as comments to the source code to indicate where certain specification items are covered.
Developers add coverage tags as comments to source code or supported documentation to indicate where certain specification items are covered.

##### Import Full Coverage Tag Format
`req~import.full-coverage-tag-format~1`
`req~import.full-coverage-tag-format~2`

OFT imports coverage tags from source files in a full format that contains all necessary information for tracing.
OFT imports coverage tags from source files and supported documentation files in a full format that contains all necessary information for tracing.

Covers:

* [feat~coverage-tag-import~1](#coverage-tag-import)
* [feat~coverage-tag-import~2](#coverage-tag-import)

Needs: dsn
Needs: scn, dsn

##### Import Short Coverage Tag Format
`req~import.short-coverage-tag-format~1`
`req~import.short-coverage-tag-format~2`

OFT imports coverage tags from source files in a short format that requires additional configuration during import.

Covers:

* [feat~coverage-tag-import~1](#coverage-tag-import)
* [feat~coverage-tag-import~2](#coverage-tag-import)

Needs: scn, dsn

##### Import Coverage Tags from Markdown Comments
`scn~markdown.comment-coverage-tags~1`

**Given** a Markdown documentation artifact with coverage tags in standalone,
single-line HTML comments,
**when** OFT imports the artifact,
**then** it creates coverage items at the tag-line location while continuing to
import ordinary Markdown specification items unchanged. Text that resembles a
coverage tag outside such a comment does not create a coverage item.

Covers:

* `req~import.full-coverage-tag-format~2`
* `req~import.short-coverage-tag-format~2`

Needs: dsn

##### Import Coverage Tags from RST Comments
`scn~rst.comment-coverage-tags~1`

**Given** an RST documentation artifact with coverage tags in standalone,
single-line RST comments,
**when** OFT imports the artifact,
**then** it creates coverage items at the tag-line location while continuing to
import ordinary RST specification items unchanged. Text that resembles a
coverage tag outside such a comment does not create a coverage item.

Covers:

* `req~import.full-coverage-tag-format~2`
* `req~import.short-coverage-tag-format~2`

Needs: dsn

Expand Down
14 changes: 14 additions & 0 deletions doc/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,20 @@ private validate(final AuthenticationRequest request){
}
```

##### Tags in Markdown and RST Documentation

Markdown documentation files (`.md` and `.markdown`) and RST files (`.rst`) can cover specification items without being routed through the Tag Importer. Place a full tag in a standalone, single-line native comment:

```markdown
<!-- [doc->req~user-guide~1] -->
```

```rst
.. [doc->req~user-guide~1]
```

Only complete, standalone Markdown HTML comments and single-line RST comments are recognized. Inline or multi-line comments and RST directives do not import coverage tags. Text outside a native comment that merely resembles a tag is also ignored.

##### Optional Elements

Tags can optionally specify a revision number or name and revision number:
Expand Down
4 changes: 4 additions & 0 deletions importer/lightweightmarkup/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
<groupId>org.itsallcode.openfasttrace</groupId>
<artifactId>openfasttrace-api</artifactId>
</dependency>
<dependency>
<groupId>org.itsallcode.openfasttrace</groupId>
<artifactId>openfasttrace-importer-tag-importer-common</artifactId>
</dependency>
<dependency>
<groupId>org.itsallcode.openfasttrace</groupId>
<artifactId>openfasttrace-testutil</artifactId>
Expand Down
1 change: 1 addition & 0 deletions importer/lightweightmarkup/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@

requires java.logging;
requires transitive org.itsallcode.openfasttrace.api;
requires org.itsallcode.openfasttrace.importer.tag.common;
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package org.itsallcode.openfasttrace.importer.lightweightmarkup;

import org.itsallcode.openfasttrace.api.core.ItemStatus;
import org.itsallcode.openfasttrace.api.core.SpecificationItem;
import org.itsallcode.openfasttrace.api.core.SpecificationItemId;
import org.itsallcode.openfasttrace.api.core.*;
import org.itsallcode.openfasttrace.api.importer.ImportEventListener;
import org.itsallcode.openfasttrace.api.importer.Importer;
import org.itsallcode.openfasttrace.api.importer.input.InputFile;
import org.itsallcode.openfasttrace.importer.lightweightmarkup.linereader.*;
import org.itsallcode.openfasttrace.importer.lightweightmarkup.statemachine.*;
import org.itsallcode.openfasttrace.importer.tag.common.CoverageTagParser;
import org.itsallcode.openfasttrace.importer.tag.common.LineReader.LineConsumer;

/**
* Base class for importers of lightweight markup text.
Expand All @@ -20,13 +20,14 @@ public abstract class AbstractLightWeightMarkupImporter implements Importer, Lin
protected final ImportEventListener listener;
/** State machine for a line-by-line parser */
protected final LineParserStateMachine stateMachine;
private final LineConsumer coverageTagParser;
private String lastTitle;
private boolean inSpecificationItem;
private LineContext currentContext;

/**
* Create a new {@link AbstractLightWeightMarkupImporter}.
*
*
* @param file
* input file
* @param listener
Expand All @@ -40,6 +41,7 @@ protected AbstractLightWeightMarkupImporter(final InputFile file, final ImportEv
this.file = file;
this.listener = listener;
this.stateMachine = new LineParserStateMachine(configureTransitions());
this.coverageTagParser = CoverageTagParser.create(null, file, listener);
}

@Override
Expand All @@ -50,21 +52,37 @@ public void runImport()

/**
* Define the transitions of the parser statemachine.
*
*
* @return parser statemachine transitions
*/
protected abstract Transition[] configureTransitions();

/**
* Tells whether a line is a native comment that can contain a coverage tag.
*
* @param context
* current line and neighboring source lines
* @return {@code true} if the current line is a coverage-tag comment
* candidate
*/
protected abstract boolean isCoverageTagCommentCandidate(LineContext context);

@Override
public void nextLine(final LineContext context)
{
this.currentContext = context;
if (isCoverageTagCommentCandidate(context))
{
// [impl->dsn~markdown.comment-coverage-tags~1]
// [impl->dsn~rst.comment-coverage-tags~1]
this.coverageTagParser.readLine(context.lineNumber(), context.currentLine());
}
this.stateMachine.step(this.currentContext.currentLine(), this.currentContext.nextLine());
}

/**
* Define a transition in the parser statemachine.
*
*
* @param from
* state to be matched against the parsers current state
* @param to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import static org.itsallcode.openfasttrace.importer.lightweightmarkup.statemachine.LineParserState.*;

import java.util.regex.Pattern;

import org.itsallcode.openfasttrace.api.importer.ImportEventListener;
import org.itsallcode.openfasttrace.api.importer.input.InputFile;
import org.itsallcode.openfasttrace.importer.lightweightmarkup.AbstractLightWeightMarkupImporter;
import org.itsallcode.openfasttrace.importer.lightweightmarkup.linereader.LineContext;
import org.itsallcode.openfasttrace.importer.lightweightmarkup.statemachine.*;

/**
Expand All @@ -21,6 +24,8 @@
class MarkdownImporter extends AbstractLightWeightMarkupImporter
{
private static final LinePattern SECTION_TITLE = new MdSectionTitlePattern();
private static final Pattern COVERAGE_TAG_COMMENT = Pattern.compile("\\s*<!--.*-->\\s*",
Pattern.UNICODE_CHARACTER_CLASS);

/**
* Creates a {@link MarkdownImporter} object with the given parameters.
Expand All @@ -35,7 +40,14 @@ class MarkdownImporter extends AbstractLightWeightMarkupImporter
super(fileName, listener);
}

@SuppressWarnings("squid:S138") // Transition table is OK be larger than 75 lines.
@Override
protected boolean isCoverageTagCommentCandidate(final LineContext context)
{
return COVERAGE_TAG_COMMENT.matcher(context.currentLine()).matches();
}

// Transition table is OK be larger than 75 lines.
@SuppressWarnings("squid:S138")
protected Transition[] configureTransitions()
{
// @formatter:off
Expand Down
Loading
Loading