diff --git a/.agents/skills/openfasttrace/SKILL.md b/.agents/skills/openfasttrace/SKILL.md index d410cd71d..c90c95124 100644 --- a/.agents/skills/openfasttrace/SKILL.md +++ b/.agents/skills/openfasttrace/SKILL.md @@ -20,7 +20,7 @@ OpenFastTrace is a tool for requirement tracing across various artifacts (specif - `Description: `: Optional keyword to start description. - `Rationale: `, `Comment: `. -## Syntax (Markdown) +## Syntax: Markdown ```markdown ### Title @@ -37,6 +37,32 @@ Needs: dsn, impl, utest - **Forwarding**: `arch --> dsn : req~id~1` (delegates coverage without repeating). - **Exclusion**: Use `` and `` to skip parsing. +## Syntax: Coverage Tags (many file formats) + +Implementation covering design in a C++ file: + +```C++ +\\ [impl -> dsn~hash-sum-calculation~1] +``` + +Unit test in a Java file: + +```shell +\\ [utest -> dsn~hash-sum-calculation~1] +``` + +Coverage in a YMAL file (e.g., GitHub workflow) + +```yaml +# [bld -> dsn~create-sbom~2] +``` + +Require coverage: + +```plantuml +# [req -> dsn~hash-sum-calculation~1 >> impl, utest] +``` + ## Tracing Tracing can be performed via CLI, Maven, or Gradle. diff --git a/doc/changes/changes_4.6.0.md b/doc/changes/changes_4.6.0.md index ddd4b6f72..f9f1b6bc3 100644 --- a/doc/changes/changes_4.6.0.md +++ b/doc/changes/changes_4.6.0.md @@ -1,17 +1,20 @@ -# OpenFastTrace 4.6.0, released 2026-06-?? +# OpenFastTrace 4.6.0, released 2026-07-?? Code name: ?? +We also updated test and build dependencies to fix vulnerabilities. Runtime code is not affected, so no update is required. + ## Summary We moved some GitHub action permissions from workflow-level to job-level. ## Security -* # +* #556: Updated Junit, PlantUML, Jacoco Maven plugin and Central publishing Plugin dependencies to fix vulnerabilities ## Refactoring +* #552: Parameterized tests in `TestSpecobjectImporter` * #546: Replaced `OsDetector` with JUnit5's `EnabledOnOs` annotation. * #544: Replaced optional parameter with null check * #543: Made `CliException` a `RuntimeException` diff --git a/importer/specobject/src/test/java/org/itsallcode/openfasttrace/importer/specobject/TestSpecobjectImporter.java b/importer/specobject/src/test/java/org/itsallcode/openfasttrace/importer/specobject/TestSpecobjectImporter.java index d3b0ad263..5ce21cee0 100644 --- a/importer/specobject/src/test/java/org/itsallcode/openfasttrace/importer/specobject/TestSpecobjectImporter.java +++ b/importer/specobject/src/test/java/org/itsallcode/openfasttrace/importer/specobject/TestSpecobjectImporter.java @@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.params.provider.Arguments.argumentSet; import static org.mockito.Mockito.*; import java.io.BufferedReader; @@ -19,25 +20,73 @@ import org.itsallcode.openfasttrace.importer.xmlparser.XmlParserFactory; 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.FieldSource; class TestSpecobjectImporter { private static final String PSEUDO_FILENAME = "pseudo_filename"; private static final Location STANDARD_LOCATION = Location.create(PSEUDO_FILENAME, 2); - @Test - void testImportOfMinimalSpecObject() + static List idImportChecks = List.of( + argumentSet("minimal import", """ + + + minimal + 1 + + """, + "req~minimal~1"), + + argumentSet("strip superfluous artifact type", """ + + + impl:strip_duplicate_prefix + 0 + + """, + "impl~strip_duplicate_prefix~0"), + + argumentSet("ignore unknown XML elements", """ + + + ignore-selected-xml-elements + 12345 + 1970-01-01 + john doe + + """, + "feat~ignore-selected-xml-elements~12345"), + + argumentSet("fullfilled-by is ignored", """ + + + with-fulfilled-by + 1 + + + impl + ffb-a + 1 + + + utest + ffb-b + 2 + + + + """, "req~with-fulfilled-by~1")); + + @ParameterizedTest + @FieldSource("idImportChecks") + void testImportSeesTheCorrectId(final String input, final String expectedId) { - final ImportEventListener listenerMock = importFromString(""" - - - minimal - 1 - - """); + final ImportEventListener listenerMock = importFromString(input); verify(listenerMock).beginSpecificationItem(); - verify(listenerMock).setLocation(STANDARD_LOCATION); - verify(listenerMock).setId(SpecificationItemId.parseId("req~minimal~1")); + verify(listenerMock).setLocation(TestSpecobjectImporter.STANDARD_LOCATION); + verify(listenerMock).setId(SpecificationItemId.parseId(expectedId)); verify(listenerMock).endSpecificationItem(); verifyNoMoreInteractions(listenerMock); } @@ -103,26 +152,6 @@ void testImportOnlyShortDescription() verifyNoMoreInteractions(listenerMock); } - @Test - void testSelectedElementsAreIgnoredDuringImport() - { - final ImportEventListener listenerMock = importFromString(""" - - - ignore-selected-xml-elements - 12345 - 1970-01-01 - john doe - - """); - verify(listenerMock).beginSpecificationItem(); - verify(listenerMock).setLocation(STANDARD_LOCATION); - verify(listenerMock) - .setId(SpecificationItemId.parseId("feat~ignore-selected-xml-elements~12345")); - verify(listenerMock).endSpecificationItem(); - verifyNoMoreInteractions(listenerMock); - } - @Test void testStripSuperfluousArtifactPrefixFromName() { @@ -146,13 +175,13 @@ void testTakeOverLocationFromImportedFile() final String expectedFileName = "/home/johndoe/openfasttrace/examples/specobject.xml"; final int expectedLine = 42; final ImportEventListener listenerMock = importFromString("\n" // - + " \n" // - + " takeOverLocation\n" // - + " 99999999\n" // - + " " + expectedFileName + "" // - + " " + expectedLine + "" // " - + " \n" // - + ""); + + " \n" // + + " takeOverLocation\n" // + + " 99999999\n" // + + " " + expectedFileName + "" // + + " " + expectedLine + "" // " + + " \n" // + + ""); verify(listenerMock).beginSpecificationItem(); verify(listenerMock).setLocation(Location.create(expectedFileName, expectedLine)); verify(listenerMock).setId(SpecificationItemId.parseId("utest~takeOverLocation~99999999")); @@ -229,35 +258,6 @@ void testImportWithDependencies() verifyNoMoreInteractions(listenerMock); } - @Test - void testImportFulfilledByIsIgnored() - { - final ImportEventListener listenerMock = importFromString(""" - - - with-fulfilled-by - 1 - - - impl - ffb-a - 1 - - - utest - ffb-b - 2 - - - - """); - verify(listenerMock).beginSpecificationItem(); - verify(listenerMock).setLocation(STANDARD_LOCATION); - verify(listenerMock).setId(SpecificationItemId.parseId("req~with-fulfilled-by~1")); - verify(listenerMock).endSpecificationItem(); - verifyNoMoreInteractions(listenerMock); - } - @Test void testImportProvidesCoverage() { @@ -290,7 +290,7 @@ void testImportProvidesCoverage() @Test void testCustomTagsWithoutNamespacesLogsWarning() { - try( final RecordingLogHandler logHandler = new RecordingLogHandler() ) + try (final RecordingLogHandler logHandler = new RecordingLogHandler()) { importFromString(""" \ @@ -314,7 +314,7 @@ void testCustomTagsWithoutNamespacesLogsWarning() @Test void testCustomTagsWithNamespacesLogsNoWarning() { - try( final RecordingLogHandler logHandler = new RecordingLogHandler() ) + try (final RecordingLogHandler logHandler = new RecordingLogHandler()) { importFromString(""" @@ -336,7 +336,7 @@ void testCustomTagsWithNamespacesLogsNoWarning() @Test void testCustomTagsWithNamespacesPlusOftNamespaceLogsNoWarning() { - try( final RecordingLogHandler logHandler = new RecordingLogHandler() ) + try (final RecordingLogHandler logHandler = new RecordingLogHandler()) { importFromString(""" getLogRecords() return logRecords.stream(); } - @Override public void close() + @Override + public void close() { Arrays.stream(rootLogger.getHandlers()) .filter((RecordingLogHandler.class::isInstance)) diff --git a/parent/pom.xml b/parent/pom.xml index 45f59b174..f611843ca 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -10,7 +10,7 @@ Free requirement tracking suite https://github.com/itsallcode/openfasttrace - 4.5.0 + 4.6.0 17 6.1.0-M1 6.1.1