Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ public class SpecificationItemId implements Comparable<SpecificationItemId>
* determined for a legacy item ID.
*/
public static final String UNKNOWN_ARTIFACT_TYPE = "unknown";
private static final String ITEM_REVISION_PATTERN = "(\\d+)";
/** Regexp pattern for revision number. */
public static final String ITEM_REVISION_PATTERN = "(\\d+)";
/** Regexp pattern for item names. */
public static final String ITEM_NAME_PATTERN = "(?U)(\\p{Alpha}[\\w-]*(?:\\.\\p{Alpha}[\\w-]*)*+)";
private static final String LEGACY_ID_NAME = "(\\p{Alpha}+)(?:~\\p{Alpha}+)?:"
Expand Down
3 changes: 3 additions & 0 deletions doc/changes/changes_3.8.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ The test coverage for the Markdown importer is now at 100%, and we were able to

The HTML report now allows expanding all details sections with command line option `--details-section-display expand`.

Now you can also specify a revision for coverage tags instead of the default revision `0`, e.g. `[impl~~42->req~example_name~17]`.

## Features

* #378: Added an RST importer
* #377: Allow expanding the details sections in the HTML report
* #379: Allow specifying a revision for coverage tags

## Bugfixes

Expand Down
20 changes: 20 additions & 0 deletions doc/spec/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,26 @@ Covers:

Needs: impl, utest

#### Full Coverage Tag Format Allows Specifying a Revision
`dsn~import.full-coverage-tag-with-revision~1`

OFT imports full coverage tags with an optional revision:

full-tag-with-revision = "[" *WSP reference "~" "~" revision "->" requirement-id "]"

full-tag-with-revision-with-needed-coverage = "[" *WSP reference "~" "~" revision *WSP "->" *WSP requirement-id
*WSP ">>" *WSP artifact-type *WSP *("," *WSP artifact-type) "]"

Rationale:

Specifying an explicit revision in coverage tags allows incrementing the revision when the implementation changes. Without this, OFT would always assign the default revision `0`.

Covers:

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

Needs: impl, utest

#### Coverage Tag With Needed Coverage Generates Readable Names
`dsn~import.full-coverage-tag-with-needed-coverage-readable-names~1`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ private ChecksumCalculator()
{
}

public static long calculateCrc32(final String value)
static long calculateCrc32(final String value)
{
final CRC32 checksum = new CRC32();
checksum.update(value.getBytes(StandardCharsets.UTF_8));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ class LineReader
this.file = file;
}

public static LineReader create(final InputFile file)
static LineReader create(final InputFile file)
{
return new LineReader(file);
}

public void readLines(final LineConsumer consumer)
void readLines(final LineConsumer consumer)
{
int currentLineNumber = 0;
try (final LineNumberReader reader = new LineNumberReader(this.file.createReader()))
Expand All @@ -46,10 +46,10 @@ private void processLine(final LineConsumer consumer, final int currentLineNumbe
{
consumer.readLine(currentLineNumber, line);
}
catch (final Exception e)
catch (final Exception exception)
{
throw new ImporterException("Error processing line " + this.file.getPath() + ":"
+ currentLineNumber + " (" + line + "): " + e.getMessage(), e);
+ currentLineNumber + " (" + line + "): " + exception.getMessage(), exception);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;

import java.util.Arrays;
import java.util.List;
import java.util.*;
import java.util.function.Predicate;
import java.util.logging.Logger;
import java.util.regex.Matcher;
Expand All @@ -21,13 +20,16 @@ class LongTagImportingLineConsumer extends RegexLineConsumer
.getLogger(LongTagImportingLineConsumer.class.getName());

private static final String COVERING_ARTIFACT_TYPE_PATTERN = "\\p{Alpha}+";
// [impl->dsn~import.full-coverage-tag-with-revision~1]
private static final String OPTIONAL_WHITESPACE = "\\s*";
private static final String TAG_PREFIX = "\\[";
private static final String TAG_SUFFIX = "\\]";
private static final String NEEDS_COVERAGE = ">>" + OPTIONAL_WHITESPACE + "(\\p{Alpha}+(?:" + OPTIONAL_WHITESPACE
+ "," + OPTIONAL_WHITESPACE + "\\p{Alpha}+)*)";
private static final String TAG_REGEX = TAG_PREFIX + OPTIONAL_WHITESPACE//
+ "(" + COVERING_ARTIFACT_TYPE_PATTERN + ")" //
+ "(" + COVERING_ARTIFACT_TYPE_PATTERN + ")"
+ optional(SpecificationItemId.ARTIFACT_TYPE_SEPARATOR + SpecificationItemId.REVISION_SEPARATOR
+ SpecificationItemId.ITEM_REVISION_PATTERN) //
+ OPTIONAL_WHITESPACE + "->" + OPTIONAL_WHITESPACE //
+ "(" + SpecificationItemId.ID_PATTERN + ")" //
+ OPTIONAL_WHITESPACE + optional(NEEDS_COVERAGE + OPTIONAL_WHITESPACE) //
Expand All @@ -36,7 +38,7 @@ class LongTagImportingLineConsumer extends RegexLineConsumer
private final InputFile file;
private final ImportEventListener listener;

private static String optional(String regex)
private static String optional(final String regex)
{
return "(?:" + regex + ")?";
}
Expand All @@ -53,10 +55,12 @@ public void processMatch(final Matcher matcher, final int lineNumber, final int
{
this.listener.beginSpecificationItem();
this.listener.setLocation(this.file.getPath(), lineNumber);
final SpecificationItemId coveredId = SpecificationItemId.parseId(matcher.group(2));
final List<String> neededArtifactTypes = parseCommaSeparatedList(matcher.group(6));
final SpecificationItemId generatedId = generateItemId(lineNumber, lineMatchCount, coveredId, matcher.group(1),
neededArtifactTypes);
final SpecificationItemId coveredId = SpecificationItemId.parseId(matcher.group(3));
final List<String> neededArtifactTypes = parseCommaSeparatedList(matcher.group(7));
final String artifactType = matcher.group(1);
final String revision = matcher.group(2);
final SpecificationItemId generatedId = generateItemId(lineNumber, lineMatchCount, coveredId, artifactType,
Comment thread
kaklakariada marked this conversation as resolved.
revision, neededArtifactTypes);

if (neededArtifactTypes.isEmpty())
{
Expand All @@ -74,7 +78,7 @@ public void processMatch(final Matcher matcher, final int lineNumber, final int
this.listener.endSpecificationItem();
}

private List<String> parseCommaSeparatedList(String input)
private List<String> parseCommaSeparatedList(final String input)
{
if (input == null)
{
Expand All @@ -88,15 +92,23 @@ private List<String> parseCommaSeparatedList(String input)
}

private SpecificationItemId generateItemId(final int lineNumber, final int lineMatchCount,
final SpecificationItemId coveredId, String artifactType, List<String> neededArtifactTypes)
final SpecificationItemId coveredId, final String artifactType, final String revision,
final List<String> neededArtifactTypes)
{
final String name = getItemName(lineNumber, lineMatchCount, coveredId, neededArtifactTypes);
return SpecificationItemId.createId(artifactType, name, 0);
return SpecificationItemId.createId(artifactType, name, parseRevision(revision));
}

private int parseRevision(final String revision)
{
return Optional.ofNullable(revision)
.map(Integer::parseInt)
.orElse(0);
}

// [impl->dsn~import.full-coverage-tag-with-needed-coverage-readable-names~1]
private String getItemName(final int lineNumber, final int lineMatchCount, final SpecificationItemId coveredId,
List<String> neededArtifactTypes)
final List<String> neededArtifactTypes)
{
if (neededArtifactTypes.isEmpty())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ public void readLine(final int lineNumber, final String line)
* @param matcher
* regular expression {@link Matcher}.
* @param lineNumber
* line number of the matched the input, starting with {@code 1} for the first line.
* line number of the matched the input, starting with {@code 1}
* for the first line.
* @param lineMatchCount
* number of the current match in the context of the current line,
* starting with {@code 0} for the first match in a line.
* number of the current match in the context of the current
Comment thread
kaklakariada marked this conversation as resolved.
* line, starting with {@code 0} for the first match in a line.
*/
abstract void processMatch(Matcher matcher, int lineNumber, int lineMatchCount);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
// [impl->dsn~import.short-coverage-tag~1]
class ShortTagImportingLineConsumer extends RegexLineConsumer
{
private static final Logger LOG = Logger
.getLogger(ShortTagImportingLineConsumer.class.getName());
private static final Logger LOG = Logger.getLogger(ShortTagImportingLineConsumer.class.getName());

private static final String TAG_PREFIX = "\\[\\[";
private static final String TAG_SUFFIX = "\\]\\]";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class TagImporter implements Importer
this.file = file;
}

public static TagImporter create(final Optional<PathConfig> config, final InputFile file,
static TagImporter create(final Optional<PathConfig> config, final InputFile file,
final ImportEventListener listener)
{
final LineConsumer lineConsumer = createLineConsumer(config, file, listener);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,29 @@ void tagWithMultipleRequiredCoverageWithSpaces()
itemWithReadableName(COVERING_ARTIFACT_TYPE1, 1, ID1, List.of(NEEDED_COVERAGE1, NEEDED_COVERAGE2)));
}

// [utest->dsn~import.full-coverage-tag-with-revision~1]
@Test
void tagWithRevision()
{
// Concatenation required to avoid problems during self-trace
assertItems("[impl~~42->req~example_name~17" + "]",
Comment thread
kaklakariada marked this conversation as resolved.
SpecificationItem.builder().id("impl", "example_name-4044529862", 42)
.addCoveredId("req", "example_name", 17)
.location(FILENAME, 1).build());
}

// [utest->dsn~import.full-coverage-tag-with-revision~1]
@Test
void tagWithRevisionAndNeededCoverage()
{
// Concatenation required to avoid problems during self-trace
assertItems("[impl~~42->req~example_name~17>>test" + "]",
SpecificationItem.builder().id("impl", "example_name", 42)
.addCoveredId("req", "example_name", 17)
.addNeedsArtifactType("test")
.location(FILENAME, 1).build());
}

@Test
void requiredCoverageIndicatorWithMissingTagIgnored()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@

import static java.util.stream.Collectors.toList;

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.*;
import java.nio.file.*;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
Expand Down Expand Up @@ -63,14 +59,19 @@ protected String createSelfTraceReport(final Trace trace)

private ImportSettings buildOftSettings()
{
final Path baseDir = Paths.get(new StandardDirectoryService().getCurrent()).resolve("..");
final Path baseDir = getProjectBaseDir();
return ImportSettings.builder() //
.addInputs(baseDir.resolve("doc/spec/")) //
.addInputs(findInputDirectories(baseDir)) //
.build();
}

private List<Path> findInputDirectories(Path rootProjectDir)
private Path getProjectBaseDir()
{
return Paths.get(new StandardDirectoryService().getCurrent()).resolve("..").normalize();
Comment thread
kaklakariada marked this conversation as resolved.
}

private List<Path> findInputDirectories(final Path rootProjectDir)
{
try (final Stream<Path> stream = Files.walk(rootProjectDir.normalize(), 5))
{
Expand All @@ -87,12 +88,12 @@ private List<Path> findInputDirectories(Path rootProjectDir)
}
}

private Predicate<Path> containsDir(String dirName)
private Predicate<Path> containsDir(final String dirName)
{
return path -> path.toString().contains(File.separator + dirName + File.separator);
}

private Predicate<Path> endsWith(Path subPath)
private Predicate<Path> endsWith(final Path subPath)
{
return path -> path.toString().endsWith(File.separator + subPath.toString());
}
Expand Down