From adcf00d42be5c307c231a1ecf55dd7874d928711 Mon Sep 17 00:00:00 2001 From: Sam Bishop Date: Sun, 28 Jan 2024 22:01:18 -0700 Subject: [PATCH 01/10] Accept trailing spaces after "Needs" items --- .../openfasttrace/importer/markdown/MarkdownImporter.java | 4 ++-- .../itsallcode/openfasttrace/importer/markdown/MdPattern.java | 2 +- .../openfasttrace/importer/markdown/TestMarkdownImporter.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) 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 45cce480c..97bea184a 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 @@ -301,9 +301,9 @@ private void addDependency() private void addNeeds() { final String artifactTypes = this.stateMachine.getLastToken(); - for (final String artifactType : artifactTypes.split(",\\s*")) + for (final String artifactType : artifactTypes.split(",")) { - this.listener.addNeededArtifactType(artifactType); + this.listener.addNeededArtifactType(artifactType.trim()); } } 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 de65dc1f8..09cf70ee1 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 @@ -37,7 +37,7 @@ enum MdPattern + SpecificationItemId.ID_PATTERN + ").*?"), ID("`?((?:" + SpecificationItemId.ID_PATTERN + ")|(?:" + SpecificationItemId.LEGACY_ID_PATTERN + "))`?.*"), - NEEDS_INT("Needs:\\s*(\\w+(?:,\\s*\\w+)*)"), + NEEDS_INT("Needs:(\\s*\\w+\\s*(?:,\\s*\\w+\\s*)*)"), NEEDS("Needs:\\s*"), NEEDS_REF(PatternConstants.UP_TO_3_WHITESPACES + PatternConstants.BULLETS + "(?:.*\\W)?" // diff --git a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporter.java b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporter.java index d0bbdbe46..a4199a733 100644 --- a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporter.java +++ b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporter.java @@ -86,7 +86,7 @@ private String createCompleteSpecificationItemInMarkdownFormat() + COMMENT_LINE1 + "\n" // + COMMENT_LINE2 + "\n" // + "\nNeeds: " + NEEDS_ARTIFACT_TYPE1 // - + ", " + NEEDS_ARTIFACT_TYPE2; + + " , " + NEEDS_ARTIFACT_TYPE2 + " "; } private void runImporterOnText(final String text) @@ -279,4 +279,4 @@ void testFindTitleAfterTitle() inOrder.verify(this.listenerMock).setTitle("Title"); inOrder.verify(this.listenerMock).endSpecificationItem(); } -} \ No newline at end of file +} From 0e573d4d8df0c77902041970dfafdd239c7e255b Mon Sep 17 00:00:00 2001 From: Sam Bishop Date: Sun, 28 Jan 2024 22:10:23 -0700 Subject: [PATCH 02/10] Accept trailing spaces after "Tags" items --- .../openfasttrace/importer/markdown/MarkdownImporter.java | 4 ++-- .../itsallcode/openfasttrace/importer/markdown/MdPattern.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 97bea184a..f33c51512 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 @@ -326,9 +326,9 @@ private void addCoverage() private void addTag() { final String tags = this.stateMachine.getLastToken(); - for (final String tag : tags.split(",\\s*")) + for (final String tag : tags.split(",")) { - this.listener.addTag(tag); + this.listener.addTag(tag.trim()); } } 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 09cf70ee1..2986964bb 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 @@ -46,7 +46,7 @@ enum MdPattern NOT_EMPTY("([^\n\r]+)"), RATIONALE("Rationale:\\s*"), STATUS("Status:\\s*(approved|proposed|draft)\\s*"), - TAGS_INT("Tags:\\s*(\\w+(?:,\\s*\\w+)*)"), + TAGS_INT("Tags:(\\s*\\w+\\s*(?:,\\s*\\w+\\s*)*)"), TAGS("Tags:\\s*"), TAG_ENTRY(PatternConstants.UP_TO_3_WHITESPACES + PatternConstants.BULLETS + "\\s*" // From 57c72c360f7ca43b8f23c83dc160b684b6cf8e14 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 2 Feb 2024 10:38:38 +0100 Subject: [PATCH 03/10] Add tests, fix parsing of Tags --- .../importer/markdown/MarkdownImporter.java | 8 +- .../importer/markdown/ITMarkdownImporter.java | 247 ++++++++++++++++++ .../markdown/TestMarkdownImporter.java | 22 +- 3 files changed, 266 insertions(+), 11 deletions(-) create mode 100644 importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java 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 f33c51512..1845a0322 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 @@ -8,9 +8,7 @@ import org.itsallcode.openfasttrace.api.core.ItemStatus; import org.itsallcode.openfasttrace.api.core.SpecificationItemId; -import org.itsallcode.openfasttrace.api.importer.ImportEventListener; -import org.itsallcode.openfasttrace.api.importer.Importer; -import org.itsallcode.openfasttrace.api.importer.ImporterException; +import org.itsallcode.openfasttrace.api.importer.*; import org.itsallcode.openfasttrace.api.importer.input.InputFile; class MarkdownImporter implements Importer @@ -42,10 +40,10 @@ class MarkdownImporter implements Importer transition(SPEC_ITEM , DEPENDS , MdPattern.DEPENDS , () -> {} ), transition(SPEC_ITEM , NEEDS , MdPattern.NEEDS_INT , this::addNeeds ), transition(SPEC_ITEM , NEEDS , MdPattern.NEEDS , () -> {} ), - transition(SPEC_ITEM , DESCRIPTION, MdPattern.DESCRIPTION, this::beginDescription ), - transition(SPEC_ITEM , DESCRIPTION, MdPattern.NOT_EMPTY , this::beginDescription ), transition(SPEC_ITEM , TAGS , MdPattern.TAGS_INT , this::addTag ), transition(SPEC_ITEM , TAGS , MdPattern.TAGS , () -> {} ), + transition(SPEC_ITEM , DESCRIPTION, MdPattern.DESCRIPTION, this::beginDescription ), + transition(SPEC_ITEM , DESCRIPTION, MdPattern.NOT_EMPTY , this::beginDescription ), transition(DESCRIPTION, SPEC_ITEM , MdPattern.ID , () -> {endDescription(); beginItem();} ), transition(DESCRIPTION, TITLE , MdPattern.TITLE , () -> {endDescription(); endItem(); rememberTitle(); }), diff --git a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java new file mode 100644 index 000000000..d198f98b8 --- /dev/null +++ b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java @@ -0,0 +1,247 @@ +package org.itsallcode.openfasttrace.importer.markdown; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; +import static org.itsallcode.openfasttrace.importer.markdown.MarkdownTestConstants.*; + +import java.io.BufferedReader; +import java.io.StringReader; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Stream; + +import org.itsallcode.openfasttrace.api.core.*; +import org.itsallcode.openfasttrace.api.importer.Importer; +import org.itsallcode.openfasttrace.api.importer.SpecificationListBuilder; +import org.itsallcode.openfasttrace.api.importer.input.InputFile; +import org.itsallcode.openfasttrace.testutil.importer.input.StreamInput; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import com.github.hamstercommunity.matcher.auto.AutoMatcher; + +class ITMarkdownImporter +{ + private static final String TAG2 = "Tag2"; + private static final String TAG1 = "Tag1"; + private static final String FILENAME = "file name"; + + @Test + void testFindRequirement() + { + assertThat(runImporterOnText(createCompleteSpecificationItemInMarkdownFormat()), + AutoMatcher.contains(SpecificationItem.builder().id(ID1).title("Requirement Title") + .comment("Comment\nMore comment") + .description("Description\n\nMore description") + .rationale("Rationale\nMore rationale") + .addNeedsArtifactType("artA").addNeedsArtifactType("artB") + .addCoveredId(SpecificationItemId.parseId(COVERED_ID1)) + .addCoveredId(SpecificationItemId.parseId(COVERED_ID2)) + .addDependOnId(SpecificationItemId.parseId(DEPENDS_ON_ID1)) + .addDependOnId(SpecificationItemId.parseId(DEPENDS_ON_ID2)) + .location("file name", 2) + .build())); + + } + + // [utest->dsn~md.needs-coverage-list-compact~1] + private String createCompleteSpecificationItemInMarkdownFormat() + { + return "# " + TITLE // + + "\n" // + + "`" + ID1 + "` " // + + "\n" // + + DESCRIPTION_LINE1 + "\n" // + + DESCRIPTION_LINE2 + "\n" // + + DESCRIPTION_LINE3 + "\n" // + + "\nRationale:\n" // + + RATIONALE_LINE1 + "\n" // + + RATIONALE_LINE2 + "\n" // + + "\nCovers:\n\n" // + + " * " + COVERED_ID1 + "\n" // + + " + " + "[Link to baz2](#" + COVERED_ID2 + ")\n" // + + "\nDepends:\n\n" // + + " + " + DEPENDS_ON_ID1 + "\n" // + + " - " + DEPENDS_ON_ID2 + "\n" // + + "\nComment:\n\n" // + + COMMENT_LINE1 + "\n" // + + COMMENT_LINE2 + "\n" // + + "\nNeeds: " + NEEDS_ARTIFACT_TYPE1 // + + " , " + NEEDS_ARTIFACT_TYPE2 + " "; + } + + private List runImporterOnText(final String text) + { + final BufferedReader reader = new BufferedReader(new StringReader(text)); + final InputFile file = StreamInput.forReader(Paths.get(FILENAME), reader); + final SpecificationListBuilder specItemBuilder = SpecificationListBuilder.create(); + final Importer importer = new MarkdownImporterFactory().createImporter(file, + specItemBuilder); + importer.runImport(); + return specItemBuilder.build(); + } + + @Test + void testTwoConsecutiveSpecificationItems() + { + assertThat(runImporterOnText(createTwoConsecutiveItemsInMarkdownFormat()), + AutoMatcher + .contains(SpecificationItem.builder().id(ID1).title(TITLE).location("file name", 2).build(), + SpecificationItem.builder().id(ID2).title("").location("file name", 4).build())); + } + + private String createTwoConsecutiveItemsInMarkdownFormat() + { + return "# " + TITLE // + + "\n" // + + ID1 + "\n" // + + "\n" + ID2 + "\n" // + + "# Irrelevant Title"; + } + + @Test + void testSingleNeeds() + { + final String singleNeedsItem = "`foo~bar~1`\n\nNeeds: " + NEEDS_ARTIFACT_TYPE1; + final List items = runImporterOnText(singleNeedsItem); + assertThat(items.get(0).getNeedsArtifactTypes(), contains(NEEDS_ARTIFACT_TYPE1)); + } + + @Test + void testFindLegacyRequirement() + { + final String completeItem = createCompleteSpecificationItemInLegacyMarkdownFormat(); + assertThat(runImporterOnText(completeItem), + AutoMatcher.contains(SpecificationItem.builder().id(SpecificationItemId.parseId(LEGACY_ID)) + .title("Requirement Title") + .status(ItemStatus.PROPOSED) + .comment("Comment\nMore comment") + .description("Description\n\nMore description") + .rationale("Rationale\nMore rationale") + .addNeedsArtifactType("artA").addNeedsArtifactType("artB") + .addCoveredId(SpecificationItemId.parseId(LEGACY_COVERED_ID1)) + .addCoveredId(SpecificationItemId.parseId(LEGACY_COVERED_ID2)) + .addDependOnId(SpecificationItemId.parseId(LEGACY_DEPENDS_ON_ID1)) + .addDependOnId(SpecificationItemId.parseId(LEGACY_DEPENDS_ON_ID2)) + .addTag("Tag1").addTag("Tag2") + .location("file name", 2) + .build())); + } + + // [utest->dsn~md.needs-coverage-list~2] + private String createCompleteSpecificationItemInLegacyMarkdownFormat() + { + return "# " + TITLE // + + "\n" // + + "`" + LEGACY_ID + "`" // + + "\n" // + + "\nStatus: proposed\n" // + + "\nDescription:\n" + DESCRIPTION_LINE1 + "\n" // + + DESCRIPTION_LINE2 + "\n" // + + DESCRIPTION_LINE3 + "\n" // + + "\nRationale:\n" // + + RATIONALE_LINE1 + "\n" // + + RATIONALE_LINE2 + "\n" // + + "\nDepends:\n\n" // + + " + `" + LEGACY_DEPENDS_ON_ID1 + "`\n" // + + " - `" + LEGACY_DEPENDS_ON_ID2 + "`\n" // + + "\nCovers:\n\n" // + + " * `" + LEGACY_COVERED_ID1 + "`\n" // + + " + `" + LEGACY_COVERED_ID2 + "`\n" // + + "\nComment:\n\n" // + + COMMENT_LINE1 + "\n" // + + COMMENT_LINE2 + "\n" // + + "\nNeeds:\n" // + + " * " + NEEDS_ARTIFACT_TYPE1 + "\n"// + + "+ " + NEEDS_ARTIFACT_TYPE2 + "\n" // + + "\nTags: " + TAG1 + ", " + TAG2; + + // + "\nTags:\n" // + // + " * " + TAG1 + "\n" // + // + " + " + TAG2 + ""; + } + + // [utest->dsn~md.artifact-forwarding-notation~1] + @Test + void testForwardRequirement() + { + final List items = runImporterOnText("arch-->dsn:req~foobar~2\n" // + + " * `dsn --> impl, utest,itest : arch~bar.zoo~123`"); + assertThat(items, + AutoMatcher.contains(SpecificationItem.builder().id(SpecificationItemId.parseId("arch~foobar~2")) + .forwards(true) + .addCoveredId(SpecificationItemId.parseId("req~foobar~2")) + .addNeedsArtifactType("dsn") + .build(), + SpecificationItem.builder().id(SpecificationItemId.parseId("dsn~bar.zoo~123")) + .addCoveredId(SpecificationItemId.parseId("arch~bar.zoo~123")) + .addNeedsArtifactType("impl").addNeedsArtifactType("utest") + .addNeedsArtifactType("itest") + .forwards(true) + .build())); + } + + // [utest->dsn~md.specification-item-title~1] + @Test + void testFindTitleAfterTitle() + { + assertThat(runImporterOnText("## This title should be ignored\n\n" // + + "### Title\n" // + + "`a~b~1`"), + AutoMatcher.contains(SpecificationItem.builder().id(SpecificationItemId.parseId("a~b~1")) + .title("Title").location("file name", 4) + .build())); + } + + @ParameterizedTest + @MethodSource("needsCoverage") + void testNeedsCoverage(final String mdContent, final List expected) + { + final List items = runImporterOnText("`a~b~1`\n" + mdContent); + assertThat(items.get(0).getNeedsArtifactTypes(), equalTo(expected)); + } + + static Stream needsCoverage() + { + return Stream.of( + Arguments.of("Needs: req , dsn ", List.of("req", "dsn")), + Arguments.of("Needs: req ", List.of("req")), + Arguments.of("Needs: req,dsn ", List.of("req", "dsn")), + Arguments.of("Needs: req ,dsn", List.of("req", "dsn")), + Arguments.of("Needs: req,dsn", List.of("req", "dsn")), + Arguments.of("Needs: req,\tdsn\n", List.of("req", "dsn")), + Arguments.of("Needs:req,dsn", List.of("req", "dsn")), + Arguments.of("Needs:\n* req\n* dsn", List.of("req", "dsn")), + Arguments.of("Needs:\n * req\n * dsn", List.of("req", "dsn")), + Arguments.of("Needs:\n* req \n\t* dsn ", List.of("req", "dsn")), + Arguments.of("Needs:\n* req\n* dsn", List.of("req", "dsn"))); + } + + @ParameterizedTest + @MethodSource("tags") + void testTags(final String mdContent, final List expected) + { + final List items = runImporterOnText("`a~b~1`\n" + mdContent); + assertThat(items.get(0).getTags(), equalTo(expected)); + } + + static Stream tags() + { + return Stream.of( + Arguments.of("Tags: req , dsn ", List.of("req", "dsn")), + Arguments.of("Tags: req ", List.of("req")), + Arguments.of("Tags: req,dsn ", List.of("req", "dsn")), + Arguments.of("Tags: req ,dsn", List.of("req", "dsn")), + Arguments.of("Tags: req,dsn", List.of("req", "dsn")), + Arguments.of("Tags: req,\tdsn\n", List.of("req", "dsn")), + Arguments.of("Tags:req,dsn", List.of("req", "dsn")), + Arguments.of("Tags:\n* req\n* dsn", List.of("req", "dsn")), + Arguments.of("Tags:\n * req\n * dsn\n", List.of("req", "dsn")), + Arguments.of("Tags:\n* req \n\t* dsn ", List.of("req", "dsn")), + Arguments.of("Tags:\n* req\n* dsn", List.of("req", "dsn"))); + + } +} diff --git a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporter.java b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporter.java index a4199a733..4023bfc4c 100644 --- a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporter.java +++ b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporter.java @@ -3,13 +3,9 @@ import static org.itsallcode.openfasttrace.importer.markdown.MarkdownAsserts.assertMatch; import static org.itsallcode.openfasttrace.importer.markdown.MarkdownAsserts.assertMismatch; import static org.itsallcode.openfasttrace.importer.markdown.MarkdownTestConstants.*; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.*; -import java.io.BufferedReader; -import java.io.Reader; -import java.io.StringReader; +import java.io.*; import java.nio.file.Paths; import org.itsallcode.openfasttrace.api.core.ItemStatus; @@ -55,6 +51,20 @@ void testIdentifyTitle() assertMismatch(MdPattern.TITLE, "Title", "Title #", " # Title"); } + @Test + void testIdentifyNeeds() + { + assertMatch(MdPattern.NEEDS_INT, "Needs: req, dsn", "Needs:req,dsn", "Needs: \treq , dsn "); + assertMismatch(MdPattern.NEEDS_INT, "Needs:"); + } + + @Test + void testIdentifyTags() + { + assertMatch(MdPattern.TAGS_INT, "Tags: req, dsn", "Tags:req,dsn", "Tags: \treq , dsn "); + assertMismatch(MdPattern.TAGS_INT, "Tags:"); + } + @Test void testFindRequirement() { From 327b5b53d0091782899f7c7aac2acc6b900c478b Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 2 Feb 2024 10:39:24 +0100 Subject: [PATCH 04/10] Upgrade test dependencies --- parent/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/parent/pom.xml b/parent/pom.xml index a997194c0..a05ef2517 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -8,7 +8,7 @@ Free requirement tracking suite https://github.com/itsallcode/openfasttrace - 3.7.1 + 3.7.2 11 5.10.1 3.2.5 @@ -202,7 +202,7 @@ org.mockito mockito-junit-jupiter - 5.9.0 + 5.10.0 test @@ -214,7 +214,7 @@ org.itsallcode hamcrest-auto-matcher - 0.5.0 + 0.6.0 test @@ -590,4 +590,4 @@ - \ No newline at end of file + From 7844eeb90e639872ba2604b200aaeeb52c68c01c Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 2 Feb 2024 10:39:43 +0100 Subject: [PATCH 05/10] Add changelog entry --- doc/changes/changes.md | 3 ++- doc/changes/changes_3.7.2.md | 11 +++++++++++ doc/developer_guide.md | 18 +++++++++--------- 3 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 doc/changes/changes_3.7.2.md diff --git a/doc/changes/changes.md b/doc/changes/changes.md index a9377ccf6..4194c8ca4 100644 --- a/doc/changes/changes.md +++ b/doc/changes/changes.md @@ -1,5 +1,6 @@ # Changes +* [3.7.2](changes_3.7.2.md) * [3.7.1](changes_3.7.1.md) * [3.7.0](changes_3.7.0.md) * [3.6.0](changes_3.6.0.md) @@ -37,4 +38,4 @@ * 0.4.0 * 0.3.0 * 0.2.0 -* 0.1.0 \ No newline at end of file +* 0.1.0 diff --git a/doc/changes/changes_3.7.2.md b/doc/changes/changes_3.7.2.md new file mode 100644 index 000000000..1e8d541a1 --- /dev/null +++ b/doc/changes/changes_3.7.2.md @@ -0,0 +1,11 @@ +# OpenFastTrace 3.7.2, released 2024-??-?? + +Code name: Bugfixes for parsing `Needs` and `Tags` in MarkDown + +## Summary + +This release fixes parsing of `Needs` and `Tags` entries in MarkDown. OFT now ignores whitespace in both and also correctly parses `Tags` in beginning of a requirement item. + +## Bugfixes + +* #373: Ignore spaces after items in "Needs:" and "Tags:" lists (thanks to [@sambishop](https://github.com/sambishop) for his contribution!) diff --git a/doc/developer_guide.md b/doc/developer_guide.md index 9d2a492b0..e9f40f5ae 100644 --- a/doc/developer_guide.md +++ b/doc/developer_guide.md @@ -134,19 +134,19 @@ Add the following to your `~/.m2/settings.xml`: ### Prepare the Release 1. Checkout the `main` branch. -1. Create a new "prepare-release" branch. -1. Update version in - * `openfasttrace-parent/pom.xml` (`revision` property) - * `README.md` - * `doc/developer_guide.md` -1. Add changes in new version to `CHANGELOG.md` and update the release date. -1. Verify that build runs successfully: +2. Create a new "prepare-release" branch. +3. Update version in + * `openfasttrace-parent/pom.xml` (`revision` property) + * `README.md` + * `doc/developer_guide.md` +4. Add changes in new version to `doc/changes/changes.md` and `doc/changes/changes_$VERSION.md` and update the release date. +5. Verify that build runs successfully: ```bash mvn clean verify ``` -1. Commit and push changes. -1. Create a new Pull Request, have it reviewed and merged. +6. Commit and push changes. +7. Create a new Pull Request, have it reviewed and merged. ### Perform the Release From c8684ca89cc6590e319e2f325ee2ad079bd4383b Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 2 Feb 2024 10:43:00 +0100 Subject: [PATCH 06/10] Fix imports of automatcher --- .../core/matcher/SpecificationItemIdMatcher.java | 5 ++--- .../core/matcher/SpecificationItemMatcher.java | 9 +++------ .../importer/markdown/ITMarkdownImporter.java | 2 +- .../openfasttrace/importer/tag/TestTagImporter.java | 8 +++----- .../exporter/specobject/TestSpecobjectExportImport.java | 9 +++------ .../importer/specobject/TestSpecobjectImportExport.java | 5 ++--- 6 files changed, 14 insertions(+), 24 deletions(-) diff --git a/core/src/test/java/org/itsallcode/openfasttrace/core/matcher/SpecificationItemIdMatcher.java b/core/src/test/java/org/itsallcode/openfasttrace/core/matcher/SpecificationItemIdMatcher.java index d437984b1..18973f179 100644 --- a/core/src/test/java/org/itsallcode/openfasttrace/core/matcher/SpecificationItemIdMatcher.java +++ b/core/src/test/java/org/itsallcode/openfasttrace/core/matcher/SpecificationItemIdMatcher.java @@ -8,11 +8,10 @@ import org.hamcrest.Matcher; import org.hamcrest.collection.IsEmptyIterable; import org.hamcrest.collection.IsIterableContainingInAnyOrder; +import org.itsallcode.matcher.config.ConfigurableMatcher; +import org.itsallcode.matcher.config.MatcherConfig; import org.itsallcode.openfasttrace.api.core.SpecificationItemId; -import com.github.hamstercommunity.matcher.config.ConfigurableMatcher; -import com.github.hamstercommunity.matcher.config.MatcherConfig; - /** * {@link Matcher} for {@link SpecificationItemId} */ diff --git a/core/src/test/java/org/itsallcode/openfasttrace/core/matcher/SpecificationItemMatcher.java b/core/src/test/java/org/itsallcode/openfasttrace/core/matcher/SpecificationItemMatcher.java index 53300407e..762e90970 100644 --- a/core/src/test/java/org/itsallcode/openfasttrace/core/matcher/SpecificationItemMatcher.java +++ b/core/src/test/java/org/itsallcode/openfasttrace/core/matcher/SpecificationItemMatcher.java @@ -5,16 +5,13 @@ import java.util.Collection; import java.util.stream.StreamSupport; -import org.hamcrest.Factory; -import org.hamcrest.Matcher; -import org.hamcrest.Matchers; +import org.hamcrest.*; import org.hamcrest.collection.IsEmptyIterable; import org.hamcrest.collection.IsIterableContainingInAnyOrder; +import org.itsallcode.matcher.config.ConfigurableMatcher; +import org.itsallcode.matcher.config.MatcherConfig; import org.itsallcode.openfasttrace.api.core.SpecificationItem; -import com.github.hamstercommunity.matcher.config.ConfigurableMatcher; -import com.github.hamstercommunity.matcher.config.MatcherConfig; - /** * {@link Matcher} for {@link SpecificationItem} */ diff --git a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java index d198f98b8..3d12598f5 100644 --- a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java +++ b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.stream.Stream; +import org.itsallcode.matcher.auto.AutoMatcher; import org.itsallcode.openfasttrace.api.core.*; import org.itsallcode.openfasttrace.api.importer.Importer; import org.itsallcode.openfasttrace.api.importer.SpecificationListBuilder; @@ -21,7 +22,6 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import com.github.hamstercommunity.matcher.auto.AutoMatcher; class ITMarkdownImporter { diff --git a/importer/tag/src/test/java/org/itsallcode/openfasttrace/importer/tag/TestTagImporter.java b/importer/tag/src/test/java/org/itsallcode/openfasttrace/importer/tag/TestTagImporter.java index 30977dbeb..b9fda6b5d 100644 --- a/importer/tag/src/test/java/org/itsallcode/openfasttrace/importer/tag/TestTagImporter.java +++ b/importer/tag/src/test/java/org/itsallcode/openfasttrace/importer/tag/TestTagImporter.java @@ -2,7 +2,6 @@ import static java.util.Collections.emptyList; import static org.hamcrest.MatcherAssert.assertThat; - import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.hasSize; @@ -13,6 +12,7 @@ import java.util.List; import java.util.zip.CRC32; +import org.itsallcode.matcher.auto.AutoMatcher; import org.itsallcode.openfasttrace.api.core.SpecificationItem; import org.itsallcode.openfasttrace.api.core.SpecificationItem.Builder; import org.itsallcode.openfasttrace.api.core.SpecificationItemId; @@ -22,8 +22,6 @@ import org.itsallcode.openfasttrace.testutil.importer.input.StreamInput; import org.junit.jupiter.api.Test; -import com.github.hamstercommunity.matcher.auto.AutoMatcher; - // [utest->dsn~import.full-coverage-tag~1] class TestTagImporter { @@ -304,7 +302,7 @@ private static SpecificationItem item(final String artifactType, final int lineN } private static SpecificationItem itemWithReadableName(final String artifactType, final int lineNumber, - final SpecificationItemId coveredId, List neededArtifactTypes) + final SpecificationItemId coveredId, final List neededArtifactTypes) { final SpecificationItemId generatedId = SpecificationItemId.createId(artifactType, coveredId.getName(), 0); @@ -317,7 +315,7 @@ private static SpecificationItem itemWithReadableName(final String artifactType, } private static SpecificationItem item(final String artifactType, final int lineNumber, - final int counter, final SpecificationItemId coveredId, List neededArtifactTypes) + final int counter, final SpecificationItemId coveredId, final List neededArtifactTypes) { final SpecificationItemId generatedId = SpecificationItemId.createId(artifactType, generateName(coveredId, lineNumber, counter), 0); diff --git a/product/src/test/java/org/itsallcode/openfasttrace/exporter/specobject/TestSpecobjectExportImport.java b/product/src/test/java/org/itsallcode/openfasttrace/exporter/specobject/TestSpecobjectExportImport.java index b26f872e4..61f0e1e61 100644 --- a/product/src/test/java/org/itsallcode/openfasttrace/exporter/specobject/TestSpecobjectExportImport.java +++ b/product/src/test/java/org/itsallcode/openfasttrace/exporter/specobject/TestSpecobjectExportImport.java @@ -3,17 +3,16 @@ import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; +import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.util.List; import javax.xml.stream.*; +import javax.xml.stream.Location; +import org.itsallcode.matcher.auto.AutoMatcher; import org.itsallcode.openfasttrace.api.core.*; -import org.itsallcode.openfasttrace.api.core.Location; import org.itsallcode.openfasttrace.api.importer.SpecificationListBuilder; import org.itsallcode.openfasttrace.api.importer.input.InputFile; import org.itsallcode.openfasttrace.importer.specobject.SpecobjectImporterFactory; @@ -21,8 +20,6 @@ import org.itsallcode.openfasttrace.testutil.xml.IndentingXMLStreamWriter; import org.junit.jupiter.api.Test; -import com.github.hamstercommunity.matcher.auto.AutoMatcher; - class TestSpecobjectExportImport { @Test diff --git a/product/src/test/java/org/itsallcode/openfasttrace/importer/specobject/TestSpecobjectImportExport.java b/product/src/test/java/org/itsallcode/openfasttrace/importer/specobject/TestSpecobjectImportExport.java index 66c25ca94..1dcac7a24 100644 --- a/product/src/test/java/org/itsallcode/openfasttrace/importer/specobject/TestSpecobjectImportExport.java +++ b/product/src/test/java/org/itsallcode/openfasttrace/importer/specobject/TestSpecobjectImportExport.java @@ -9,6 +9,7 @@ import java.nio.file.Paths; import java.util.List; +import org.itsallcode.matcher.auto.AutoMatcher; import org.itsallcode.openfasttrace.api.core.*; import org.itsallcode.openfasttrace.api.importer.Importer; import org.itsallcode.openfasttrace.api.importer.SpecificationListBuilder; @@ -18,8 +19,6 @@ import org.itsallcode.openfasttrace.testutil.importer.input.StreamInput; import org.junit.jupiter.api.Test; -import com.github.hamstercommunity.matcher.auto.AutoMatcher; - class TestSpecobjectImportExport { @Test @@ -98,4 +97,4 @@ private List parse(final String content) importer.runImport(); return listener.build(); } -} \ No newline at end of file +} From 64e5d779f6003259c461ed7c46435d69cd9f174a Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 2 Feb 2024 10:49:23 +0100 Subject: [PATCH 07/10] Fix import of Location class --- .../specobject/TestSpecobjectExportImport.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/product/src/test/java/org/itsallcode/openfasttrace/exporter/specobject/TestSpecobjectExportImport.java b/product/src/test/java/org/itsallcode/openfasttrace/exporter/specobject/TestSpecobjectExportImport.java index 61f0e1e61..c71d844ae 100644 --- a/product/src/test/java/org/itsallcode/openfasttrace/exporter/specobject/TestSpecobjectExportImport.java +++ b/product/src/test/java/org/itsallcode/openfasttrace/exporter/specobject/TestSpecobjectExportImport.java @@ -9,7 +9,6 @@ import java.util.List; import javax.xml.stream.*; -import javax.xml.stream.Location; import org.itsallcode.matcher.auto.AutoMatcher; import org.itsallcode.openfasttrace.api.core.*; @@ -29,7 +28,7 @@ void testExportImportSimpleSpecObjectWithMandatoryElements() final SpecificationItem item = SpecificationItem.builder() // .id(SpecificationItemId.createId("foo", "bar", 1)) // .description("the description") // - .location(Location.create("dummy.xml", 4)) // + .location(location(4)) // .build(); assertExportAndImport(item); } @@ -48,7 +47,7 @@ void testExportImportSpecObjectWithOptionalElements() throws IOException, XMLStr .addDependOnId("req", "depend-on", 1) // .addNeedsArtifactType("impl") // .addTag("the tag") // - .location(Location.create("dummy.xml", 4)) // + .location(location(4)) // .build(); assertExportAndImport(item); } @@ -62,7 +61,7 @@ void testExportImportTwoSpecObjects() throws IOException, XMLStreamException .description("the description") // .rationale("the rationale") // .comment("the comment") // - .location(Location.create("dummy.xml", 4)) // + .location(location(4)) // .build(); final SpecificationItem itemB = SpecificationItem.builder() // .id(SpecificationItemId.createId("baz", "zoo", 2)) // @@ -70,11 +69,16 @@ void testExportImportTwoSpecObjects() throws IOException, XMLStreamException .description("another\ndescription") // .rationale("another\nrationale") // .comment("another\ncomment") // - .location(Location.create("dummy.xml", 5)) // + .location(location(5)) // .build(); assertExportAndImport(itemA, itemB); } + private org.itsallcode.openfasttrace.api.core.Location location(final int line) + { + return org.itsallcode.openfasttrace.api.core.Location.create("dummy.xml", line); + } + private void assertExportAndImport(final SpecificationItem... items) { final String exportedItems = exportToString(items); From 4f2c9733580f097e15c0dbb655f90eec19d3ce83 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 2 Feb 2024 10:56:43 +0100 Subject: [PATCH 08/10] Fix windows specific tests --- .../importer/markdown/ITMarkdownImporter.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java index 3d12598f5..5893c818d 100644 --- a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java +++ b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java @@ -25,6 +25,7 @@ class ITMarkdownImporter { + private static final String NL = System.lineSeparator(); private static final String TAG2 = "Tag2"; private static final String TAG1 = "Tag1"; private static final String FILENAME = "file name"; @@ -34,9 +35,9 @@ void testFindRequirement() { assertThat(runImporterOnText(createCompleteSpecificationItemInMarkdownFormat()), AutoMatcher.contains(SpecificationItem.builder().id(ID1).title("Requirement Title") - .comment("Comment\nMore comment") - .description("Description\n\nMore description") - .rationale("Rationale\nMore rationale") + .comment("Comment" + NL + "More comment") + .description("Description" + NL + NL + "More description") + .rationale("Rationale" + NL + "More rationale") .addNeedsArtifactType("artA").addNeedsArtifactType("artB") .addCoveredId(SpecificationItemId.parseId(COVERED_ID1)) .addCoveredId(SpecificationItemId.parseId(COVERED_ID2)) @@ -118,9 +119,9 @@ void testFindLegacyRequirement() AutoMatcher.contains(SpecificationItem.builder().id(SpecificationItemId.parseId(LEGACY_ID)) .title("Requirement Title") .status(ItemStatus.PROPOSED) - .comment("Comment\nMore comment") - .description("Description\n\nMore description") - .rationale("Rationale\nMore rationale") + .comment("Comment" + NL + "More comment") + .description("Description" + NL + NL + "More description") + .rationale("Rationale" + NL + "More rationale") .addNeedsArtifactType("artA").addNeedsArtifactType("artB") .addCoveredId(SpecificationItemId.parseId(LEGACY_COVERED_ID1)) .addCoveredId(SpecificationItemId.parseId(LEGACY_COVERED_ID2)) @@ -242,6 +243,5 @@ static Stream tags() Arguments.of("Tags:\n * req\n * dsn\n", List.of("req", "dsn")), Arguments.of("Tags:\n* req \n\t* dsn ", List.of("req", "dsn")), Arguments.of("Tags:\n* req\n* dsn", List.of("req", "dsn"))); - } } From 88e151a7c921e8909aafa03027d2cc582f2c6eed Mon Sep 17 00:00:00 2001 From: Sam Bishop Date: Fri, 2 Feb 2024 06:02:51 -0700 Subject: [PATCH 09/10] Simplify auto-generated 'if (...) return false; else return true;' code to 'return !(...);'. (It was flagged by Sonar as a code smell.) --- .../openfasttrace/api/core/SpecificationItemId.java | 6 +----- .../org/itsallcode/openfasttrace/api/core/TracedLink.java | 8 ++------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/api/src/main/java/org/itsallcode/openfasttrace/api/core/SpecificationItemId.java b/api/src/main/java/org/itsallcode/openfasttrace/api/core/SpecificationItemId.java index 59d2f090a..9f101343e 100644 --- a/api/src/main/java/org/itsallcode/openfasttrace/api/core/SpecificationItemId.java +++ b/api/src/main/java/org/itsallcode/openfasttrace/api/core/SpecificationItemId.java @@ -137,11 +137,7 @@ else if (!this.name.equals(other.name)) { return false; } - if ((other.revision != REVISION_WILDCARD) && (this.revision != other.revision)) - { - return false; - } - return true; + return (other.revision == REVISION_WILDCARD) || (this.revision == other.revision); } @Override diff --git a/api/src/main/java/org/itsallcode/openfasttrace/api/core/TracedLink.java b/api/src/main/java/org/itsallcode/openfasttrace/api/core/TracedLink.java index 2c5762a55..2b770ee83 100644 --- a/api/src/main/java/org/itsallcode/openfasttrace/api/core/TracedLink.java +++ b/api/src/main/java/org/itsallcode/openfasttrace/api/core/TracedLink.java @@ -100,10 +100,6 @@ else if (!this.otherLinkEnd.equals(other.otherLinkEnd)) { return false; } - if (this.status != other.status) - { - return false; - } - return true; + return this.status == other.status; } -} \ No newline at end of file +} From 319ef5b3f895a209efdd05a98817320dabd4cb0c Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 2 Feb 2024 15:49:00 +0100 Subject: [PATCH 10/10] Implement review findings by @redcatbear --- doc/changes/changes_3.7.2.md | 4 ++-- .../importer/markdown/ITMarkdownImporter.java | 4 ---- .../markdown/TestMarkdownImporter.java | 22 ++++++++++--------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/doc/changes/changes_3.7.2.md b/doc/changes/changes_3.7.2.md index 1e8d541a1..0770a62c2 100644 --- a/doc/changes/changes_3.7.2.md +++ b/doc/changes/changes_3.7.2.md @@ -1,10 +1,10 @@ # OpenFastTrace 3.7.2, released 2024-??-?? -Code name: Bugfixes for parsing `Needs` and `Tags` in MarkDown +Code name: Bugfixes for parsing `Needs` and `Tags` in Markdown ## Summary -This release fixes parsing of `Needs` and `Tags` entries in MarkDown. OFT now ignores whitespace in both and also correctly parses `Tags` in beginning of a requirement item. +This release fixes parsing of `Needs` and `Tags` entries in Markdown. OFT now ignores whitespace in both and also correctly parses `Tags` in beginning of a requirement item. ## Bugfixes diff --git a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java index 5893c818d..3862af5bb 100644 --- a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java +++ b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/ITMarkdownImporter.java @@ -159,10 +159,6 @@ private String createCompleteSpecificationItemInLegacyMarkdownFormat() + " * " + NEEDS_ARTIFACT_TYPE1 + "\n"// + "+ " + NEEDS_ARTIFACT_TYPE2 + "\n" // + "\nTags: " + TAG1 + ", " + TAG2; - - // + "\nTags:\n" // - // + " * " + TAG1 + "\n" // - // + " + " + TAG2 + ""; } // [utest->dsn~md.artifact-forwarding-notation~1] diff --git a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporter.java b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporter.java index 4023bfc4c..00487ee4d 100644 --- a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporter.java +++ b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporter.java @@ -3,6 +3,7 @@ import static org.itsallcode.openfasttrace.importer.markdown.MarkdownAsserts.assertMatch; import static org.itsallcode.openfasttrace.importer.markdown.MarkdownAsserts.assertMismatch; import static org.itsallcode.openfasttrace.importer.markdown.MarkdownTestConstants.*; +import static org.junit.jupiter.api.Assertions.assertAll; import static org.mockito.Mockito.*; import java.io.*; @@ -37,32 +38,33 @@ class TestMarkdownImporter @Test void testIdentifyId() { - assertMatch(MdPattern.ID, "req~foo~1", "a~b~0", "req~test~1", - "req~test~999", "req~test.requirement~1", "req~test_underscore~1", - "`req~test1~1`arbitrary text"); - assertMismatch(MdPattern.ID, "test~1", "req-test~1", "req~4test~1"); + assertAll( + () -> assertMatch(MdPattern.ID, "req~foo~1", "a~b~0", "req~test~1", + "req~test~999", "req~test.requirement~1", "req~test_underscore~1", + "`req~test1~1`arbitrary text"), + () -> assertMismatch(MdPattern.ID, "test~1", "req-test~1", "req~4test~1")); } // [utest->dsn~md.specification-item-title~1] @Test void testIdentifyTitle() { - assertMatch(MdPattern.TITLE, "#Title", "# Title", "###### Title", "# Title"); - assertMismatch(MdPattern.TITLE, "Title", "Title #", " # Title"); + assertAll(() -> assertMatch(MdPattern.TITLE, "#Title", "# Title", "###### Title", "# Title"), + () -> assertMismatch(MdPattern.TITLE, "Title", "Title #", " # Title")); } @Test void testIdentifyNeeds() { - assertMatch(MdPattern.NEEDS_INT, "Needs: req, dsn", "Needs:req,dsn", "Needs: \treq , dsn "); - assertMismatch(MdPattern.NEEDS_INT, "Needs:"); + assertAll(() -> assertMatch(MdPattern.NEEDS_INT, "Needs: req, dsn", "Needs:req,dsn", "Needs: \treq , dsn "), + () -> assertMismatch(MdPattern.NEEDS_INT, "Needs:")); } @Test void testIdentifyTags() { - assertMatch(MdPattern.TAGS_INT, "Tags: req, dsn", "Tags:req,dsn", "Tags: \treq , dsn "); - assertMismatch(MdPattern.TAGS_INT, "Tags:"); + assertAll(() -> assertMatch(MdPattern.TAGS_INT, "Tags: req, dsn", "Tags:req,dsn", "Tags: \treq , dsn "), + () -> assertMismatch(MdPattern.TAGS_INT, "Tags:")); } @Test