From 5968b90a7c9bb054d2651ddf88f511b0fbc540cd Mon Sep 17 00:00:00 2001 From: redcatbaer Date: Sun, 5 Jan 2025 12:49:11 +0100 Subject: [PATCH 1/7] #280: Inside Markdown code blocks requirements are now ignored. --- .../statemachine/LineParserState.java | 2 ++ .../importer/markdown/MarkdownImporter.java | 6 ++-- .../importer/markdown/MdPattern.java | 2 ++ .../markdown/TestMarkdownMarkupImporter.java | 34 +++++++++++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/importer/lightweightmarkup/src/main/java/org/itsallcode/openfasttrace/importer/lightweightmarkup/statemachine/LineParserState.java b/importer/lightweightmarkup/src/main/java/org/itsallcode/openfasttrace/importer/lightweightmarkup/statemachine/LineParserState.java index 7bd3cc73..649eba11 100644 --- a/importer/lightweightmarkup/src/main/java/org/itsallcode/openfasttrace/importer/lightweightmarkup/statemachine/LineParserState.java +++ b/importer/lightweightmarkup/src/main/java/org/itsallcode/openfasttrace/importer/lightweightmarkup/statemachine/LineParserState.java @@ -29,6 +29,8 @@ public enum LineParserState TITLE, /** Found tags */ TAGS, + /** Code block */ + CODE_BLOCK, /** Reached the end of the file */ EOF } diff --git a/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MarkdownImporter.java b/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MarkdownImporter.java index 4014375b..586eb113 100644 --- a/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MarkdownImporter.java +++ b/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MarkdownImporter.java @@ -42,6 +42,7 @@ protected Transition[] configureTransitions() transition(START , SPEC_ITEM , MdPattern.ID , this::beginItem ), transition(START , TITLE , SECTION_TITLE , this::rememberTitle ), transition(START , START , MdPattern.FORWARD , this::forward ), + transition(START , CODE_BLOCK , MdPattern.CODE_BEGIN , () -> {} ), transition(START , START , MdPattern.EVERYTHING , () -> {} ), transition(TITLE , SPEC_ITEM , MdPattern.ID , this::beginItem ), @@ -99,7 +100,6 @@ protected Transition[] configureTransitions() transition(COMMENT , TAGS , MdPattern.TAGS , () -> {} ), transition(COMMENT , COMMENT , MdPattern.EVERYTHING , this::appendComment ), - // [impl->dsn~md.covers-list~1] transition(COVERS , SPEC_ITEM , MdPattern.ID , this::beginItem ), transition(COVERS , TITLE , SECTION_TITLE , () -> {endItem(); rememberTitle();}), @@ -156,7 +156,9 @@ protected Transition[] configureTransitions() transition(TAGS , COVERS , MdPattern.COVERS , () -> {} ), transition(TAGS , TAGS , MdPattern.TAGS , () -> {} ), transition(TAGS , TAGS , MdPattern.TAGS_INT , this::addTag ), - transition(TAGS , START , MdPattern.FORWARD , () -> {endItem(); forward();} ) + transition(TAGS , START , MdPattern.FORWARD , () -> {endItem(); forward();} ), + + transition(CODE_BLOCK , START , MdPattern.CODE_END , () -> {} ) }; // @formatter:on } diff --git a/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MdPattern.java b/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MdPattern.java index b5cd0c4c..510e9f4e 100644 --- a/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MdPattern.java +++ b/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MdPattern.java @@ -15,6 +15,8 @@ enum MdPattern // [impl->dsn~md.artifact-forwarding-notation~1] // @formatter:off + CODE_BEGIN("```\\w*\\s*"), + CODE_END("```"), COMMENT("Comment:\\s*"), COVERS("Covers:\\s*"), COVERS_REF(PatternConstants.REFERENCE_AFTER_BULLET), diff --git a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownMarkupImporter.java b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownMarkupImporter.java index 671435e6..fbb51dcc 100644 --- a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownMarkupImporter.java +++ b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownMarkupImporter.java @@ -3,6 +3,7 @@ import static org.itsallcode.matcher.auto.AutoMatcher.contains; import static org.itsallcode.openfasttrace.testutil.core.ItemBuilderFactory.item; +import org.hamcrest.Matchers; import org.itsallcode.openfasttrace.api.core.SpecificationItemId; import org.itsallcode.openfasttrace.api.importer.ImporterFactory; import org.itsallcode.openfasttrace.testutil.importer.lightweightmarkup.AbstractLightWeightMarkupImporterTest; @@ -138,4 +139,37 @@ void testLessThenThreeUnderliningCharactersAreNotDetectedAsTitleUnderlines() .location("z", 3) .build())); } + + @Test + void testWhenInsideMarkdownCodeBlockThenNoSpecificationItemMustBeDetected() { + assertImport("file_with_code_block.md", """ + ``` + This is a code block, the following requirement must be ignored. + + req~example~1 + ``` + """, + Matchers.emptyIterable()); + } + + // Unit Test + @Test + void testWhenCodeBlockIsInsideCommentSectionThenItIsImportedAsPartOfComment() { + assertImport("file_with_code_block_in_comment.md", """ + req~comment_with_code_block~1 + Comment: + + ``` + This is a code block inside a comment. + ``` + """, + contains(item() + .id(SpecificationItemId.createId("req", "comment_with_code_block", 1)) + .comment(""" + ``` + This is a code block inside a comment. + ```""") + .location("file_with_code_block_in_comment.md", 1) + .build())); + } } From bb54b0cb42e4a79211b46915aec79731cd2e08fc Mon Sep 17 00:00:00 2001 From: redcatbaer Date: Sun, 5 Jan 2025 12:54:37 +0100 Subject: [PATCH 2/7] #280: Inside Markdown code blocks requirements are now ignored. --- doc/changes/changes.md | 1 + doc/changes/changes_4.2.0.md | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 doc/changes/changes_4.2.0.md diff --git a/doc/changes/changes.md b/doc/changes/changes.md index 9f618092..eea7a754 100644 --- a/doc/changes/changes.md +++ b/doc/changes/changes.md @@ -1,5 +1,6 @@ # Changes +* [4.2.0](changes_4.2.0.md) * [4.1.0](changes_4.1.0.md) * [4.0.2](changes_4.0.2.md) * [4.0.1](changes_4.0.1.md) diff --git a/doc/changes/changes_4.2.0.md b/doc/changes/changes_4.2.0.md new file mode 100644 index 00000000..69f7dca0 --- /dev/null +++ b/doc/changes/changes_4.2.0.md @@ -0,0 +1,11 @@ +# OpenFastTrace 4.2.0, released ??? + +Code name: Markdown code blocks + +## Summary + +In this release we changed the behavior of the Markdown importer, so that if we are inside a code block, no OFT specification items are found. This is a corner case, but we think that this behavior is what users would expect (#480). + +## Features + +* #480: Ignore specification items inside Markdown code blocks From 3233979669c0f32d9f05b9a86955fa5ce1ccb73a Mon Sep 17 00:00:00 2001 From: redcatbaer Date: Mon, 6 Jan 2025 13:16:32 +0100 Subject: [PATCH 3/7] #280: Added test for different start or end markers. --- .../markdown/TestMarkdownMarkupImporter.java | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownMarkupImporter.java b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownMarkupImporter.java index fbb51dcc..37d63c51 100644 --- a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownMarkupImporter.java +++ b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownMarkupImporter.java @@ -1,14 +1,15 @@ package org.itsallcode.openfasttrace.importer.markdown; +import static org.hamcrest.Matchers.emptyIterable; import static org.itsallcode.matcher.auto.AutoMatcher.contains; import static org.itsallcode.openfasttrace.testutil.core.ItemBuilderFactory.item; -import org.hamcrest.Matchers; import org.itsallcode.openfasttrace.api.core.SpecificationItemId; import org.itsallcode.openfasttrace.api.importer.ImporterFactory; import org.itsallcode.openfasttrace.testutil.importer.lightweightmarkup.AbstractLightWeightMarkupImporterTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; class TestMarkdownMarkupImporter extends AbstractLightWeightMarkupImporterTest @@ -140,25 +141,36 @@ void testLessThenThreeUnderliningCharactersAreNotDetectedAsTitleUnderlines() .build())); } - @Test - void testWhenInsideMarkdownCodeBlockThenNoSpecificationItemMustBeDetected() { + @ParameterizedTest + @CsvSource( + { + "```,```", + "``` ,``` ", + "``` ,```", + "```java, ```", + "```java , ``` ", + }) + void testWhenInsideMarkdownCodeBlockThenNoSpecificationItemMustBeDetected(final String startMarker, + final String endMarker) + { assertImport("file_with_code_block.md", """ - ``` - This is a code block, the following requirement must be ignored. - - req~example~1 - ``` - """, - Matchers.emptyIterable()); + %s + This is a code block, the following requirement must be ignored. + + req~example~1 + %s + """.formatted(startMarker, endMarker), + emptyIterable()); } // Unit Test @Test - void testWhenCodeBlockIsInsideCommentSectionThenItIsImportedAsPartOfComment() { + void testWhenCodeBlockIsInsideCommentSectionThenItIsImportedAsPartOfComment() + { assertImport("file_with_code_block_in_comment.md", """ req~comment_with_code_block~1 Comment: - + ``` This is a code block inside a comment. ``` From 6182701488d90e11c56e79635beb4f109c9a7101 Mon Sep 17 00:00:00 2001 From: redcatbaer Date: Mon, 6 Jan 2025 13:28:43 +0100 Subject: [PATCH 4/7] #280: Added more allowed variants for code block start and end tags (see https://github.github.com/gfm/#fenced-code-blocks for specification). --- .../openfasttrace/importer/markdown/MdPattern.java | 4 ++-- .../importer/markdown/TestMarkdownMarkupImporter.java | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MdPattern.java b/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MdPattern.java index 510e9f4e..5e848b63 100644 --- a/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MdPattern.java +++ b/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MdPattern.java @@ -15,8 +15,8 @@ enum MdPattern // [impl->dsn~md.artifact-forwarding-notation~1] // @formatter:off - CODE_BEGIN("```\\w*\\s*"), - CODE_END("```"), + CODE_BEGIN(" {0,3}[`~]{3,30}\\w*\\s*"), + CODE_END(" {0,3}[`~]{3,30}\\s*"), COMMENT("Comment:\\s*"), COVERS("Covers:\\s*"), COVERS_REF(PatternConstants.REFERENCE_AFTER_BULLET), diff --git a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownMarkupImporter.java b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownMarkupImporter.java index 37d63c51..67d66a56 100644 --- a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownMarkupImporter.java +++ b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownMarkupImporter.java @@ -147,8 +147,15 @@ void testLessThenThreeUnderliningCharactersAreNotDetectedAsTitleUnderlines() "```,```", "``` ,``` ", "``` ,```", + "````, ````", + " ```, ```", + " ```, ```", + " ```, ```", "```java, ```", "```java , ``` ", + "~~~, ~~~", + "~~~java, ~~~", + " ~~~~java, ~~~~ " }) void testWhenInsideMarkdownCodeBlockThenNoSpecificationItemMustBeDetected(final String startMarker, final String endMarker) From f74ba3784fb3734457ece962dac0b97b2a06619c Mon Sep 17 00:00:00 2001 From: redcatbaer Date: Mon, 6 Jan 2025 14:44:01 +0100 Subject: [PATCH 5/7] #431: Add "unwanted coverage" to user documentation --- doc/changes/changes_4.2.0.md | 5 ++- doc/user_guide.md | 71 ++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/doc/changes/changes_4.2.0.md b/doc/changes/changes_4.2.0.md index 69f7dca0..e8c7ec0e 100644 --- a/doc/changes/changes_4.2.0.md +++ b/doc/changes/changes_4.2.0.md @@ -4,8 +4,11 @@ Code name: Markdown code blocks ## Summary -In this release we changed the behavior of the Markdown importer, so that if we are inside a code block, no OFT specification items are found. This is a corner case, but we think that this behavior is what users would expect (#480). +In this release we changed the behavior of the Markdown importer, so that if we are inside a code block, no OFT specification items are found. This is a corner case, but we think that this behavior is what users would expect (#480). + +We also added a whole section about understanding and fixing broken links between specification items to the user guide. ## Features +* #431: Documented "unwanted coverage" in user guide. * #480: Ignore specification items inside Markdown code blocks diff --git a/doc/user_guide.md b/doc/user_guide.md index 8534f2bc..25d76de4 100644 --- a/doc/user_guide.md +++ b/doc/user_guide.md @@ -462,6 +462,77 @@ While plain text reports are perfect for debugging your tracing chain, sometimes oft trace -o html ``` +### Understanding and Fixing Broken Requirement Branches + +Requirements — or specification items as we call them more broadly — in OFT are internally organized in a graph. If you haven't heard of that term, don't worry. In most cases it is close enough to think of the relationships between the specification items like a forest where the highest level of the specification are tree trunks from which details branch out into big branches, twigs and eventually leaves. + +Requirement engineering calls this the "traceability matrix". That term is a bit clunky, but we thought, you should have heard of it at least once. + +#### Everything That can go Wrong… + +What we want to achieve in any role that has to do with requirement engineering is healthy trees with their leaves attached all the way to the trunks. We don't want twigs without leaves, and we definitely don't want leaves lying on the ground. + +Unfortunately, we are only human and humans make mistakes. Here is a non-exhaustive list of typical mistakes that happen when maintaining a traceability matrix: + +| Mistake | How it manifests in OFT | +|---------------------------------|----------------------------------------------------| +| Copy and paste errors | duplicates, covering the wrong item | +| Unimplemented feature | missing leaves in the implementation | +| Missing tests | missing leaves in the test | +| Typos in requirement IDs | causing branches to be cut somewhere in the middle | +| Wrong artifact type in coverage | both missing and unexpected coverage | + +#### Reading and Understanding the Link Error Types + +Depending, from where you look at a specification item, it can have links that point towards it (incoming) or away from it (outgoing). And those links can be broken. This is a typical sign that the requirement matrix contains wrong coverage, is incomplete or has excess parts. + +#### Outgoing Link Statuses + +| Status | Explanation | Ok | +|------------------|------------------------------------------------------------------------|----| +| Covers | This item covers another item | ✔️ | +| Predated | This item covers a newer revision of another item | ❌ | +| Outdated | This item covers an older revision of another item | ❌ | +| Ambiguous | Two items with the same id are covered by another item | ❌ | +| Unwanted | This item covers another item that does not require this coverage | ❌ | +| Orphaned | This item covers a non-existing item | ❌ | + +"Covers" means everything is fine. + +When the outgoing link from this item is "predated", that means it points to a newer version of the covered item than it should. This is usually a typo that you need to simply fix. On rarer occasion it can hint at a merge error or a problem when multiple teams contribute to the same specification. Check the document history if unsure. + +"Outdated" coverage typically happens when an existing specification item was updated, but the coverage wasn't. This is one of the most useful safeguards in OFT. + +Copy & paste often leads to "ambiguous" coverage, where two items are defined with the same ID. Treat this like you would fix a typo, but be careful, if the ID wasn't updated, then there are likely other c&p errors hiding in the vicinity. + +Coverage is "unwanted" when the specification item that it points to didn't ask for it. Check for typos in both IDs. The most common mistake here is that either the required artifact types or the coveraging artifact types are wrong. + +"Orphaned" finally means that this item claims to cover a requirement that does not exist. Or ceased to exist. In this case first check for typos and if it is not a typo, check the history of the documents to see if there is maybe coverage left for something that has been obsoleted higher up in a specification. + + +#### Incoming Link Statuses + +| Status | Explanation | Ok | +|------------------|--------------------------------------------------------------------------|----| +| Covered Shallow | This item is directly covered by another item | ✔️ | +| Covered Unwanted | This item is covered by another item though it does not require coverage | ❌ | +| Covered Predated | This item is covered by another item that specifies a newer revision | ❌ | +| Covered Outdated | This item is covered by another item that specifies an older revision | ❌ | + +If you see "covered shallow" on an incoming link, this means that there is at least one specification item providing the required coverage. + +"Covered unwanted" means that another item covers the one you are looking at, but it shouldn't, because that coverage was not required. In most cases you are looking at a copy & paste error. Sometimes it is simply a typo the in artifact types. In rarer circumstances this happens the person who wrote the higher level item disagreed with the one who did the coverage. The last variant can be solved by talking to each other. + +"Covered predated" means some other specification item claims to cover a newer version of this item than is currently present in the spec. "Covered outdated" is the opposite situation. The problem resolution is the same as in the [section above](#outgoing-link-statuses) where the predated and outdated incoming links were discussed. + +#### Bidirectional Link Statuses + +| Status | Explanation | Ok | +|------------------|--------------------------------------------------------------------------|----| +| Duplicate | Two items have the same ID | ❌ | + +Duplicate links are special. They don't have a clear direction, since OFT cannot tell which specification item is the original and which one is the duplicate. In either case, you are most likely looking at a copy & past error again. Handle this with care and check if you maybe forgot to adapt other aspects of the copy too, not only the ID. + ## Reference ### OFT Command Line From 87831ba06dd205c34666d14d5431f3a7acc1027e Mon Sep 17 00:00:00 2001 From: redcatbaer Date: Sun, 19 Jan 2025 18:49:38 +0100 Subject: [PATCH 6/7] #431: Moved changelog entry to "documentation" section. --- doc/changes/changes_4.2.0.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/changes/changes_4.2.0.md b/doc/changes/changes_4.2.0.md index 66dc878f..9f46e4a1 100644 --- a/doc/changes/changes_4.2.0.md +++ b/doc/changes/changes_4.2.0.md @@ -10,6 +10,9 @@ We also added a whole section about understanding and fixing broken links betwee ## Features -* #431: Documented "unwanted coverage" in user guide. * #437: Upgrade build and test dependencies on top of 4.1.0 -* #480: Ignore specification items inside Markdown code blocks \ No newline at end of file +* #480: Ignore specification items inside Markdown code blocks + +## Documentation + +* #431: Documented "unwanted coverage" in user guide. From f1fb434cf67ef03824e3ba20ed602fd9e938cde9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=A4r?= Date: Sun, 19 Jan 2025 19:05:07 +0100 Subject: [PATCH 7/7] Update doc/user_guide.md Co-authored-by: Christoph Pirkl <4711730+kaklakariada@users.noreply.github.com> --- doc/user_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user_guide.md b/doc/user_guide.md index 25d76de4..89afea1f 100644 --- a/doc/user_guide.md +++ b/doc/user_guide.md @@ -503,7 +503,7 @@ When the outgoing link from this item is "predated", that means it points to a n "Outdated" coverage typically happens when an existing specification item was updated, but the coverage wasn't. This is one of the most useful safeguards in OFT. -Copy & paste often leads to "ambiguous" coverage, where two items are defined with the same ID. Treat this like you would fix a typo, but be careful, if the ID wasn't updated, then there are likely other c&p errors hiding in the vicinity. +Copy & paste often leads to "ambiguous" coverage, where two items are defined with the same ID. Treat this like you would fix a typo, but be careful, if the ID wasn't updated, then there are likely other copy & paste errors hiding in the vicinity. Coverage is "unwanted" when the specification item that it points to didn't ask for it. Check for typos in both IDs. The most common mistake here is that either the required artifact types or the coveraging artifact types are wrong.