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
2 changes: 1 addition & 1 deletion .settings/org.eclipse.jdt.core.prefs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=wa
org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=warning
org.eclipse.jdt.core.compiler.problem.nullReference=warning
org.eclipse.jdt.core.compiler.problem.nullSpecViolation=warning
org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning
Expand Down
5 changes: 3 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
"sonarlint.connectedMode.project": {
"connectionId": "itsallcode",
"projectKey": "org.itsallcode:openfasttrace-gradle"
}
}
},
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx12G -Xms100m"
}
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [3.0.0] - 2024-06-16

- [Issue #26](https://github.com/itsallcode/openfasttrace-gradle/issues/26)
- Added option `failBuild` that lets the build fail when it finds defects
- **Breaking Change:** `failBuild` is set to `true` by default. To keep the previous behaviour use `failBuild = false` in your build.

## [2.0.0] - 2024-06-13

- [PR #44](https://github.com/itsallcode/openfasttrace-gradle/pull/35)
Expand Down
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Gradle plugin for the requirement tracing suite [OpenFastTrace](https://github.c

```gradle
plugins {
id "org.itsallcode.openfasttrace" version "2.0.0"
id "org.itsallcode.openfasttrace" version "3.0.0"
}
```

Expand All @@ -36,6 +36,7 @@ Gradle plugin for the requirement tracing suite [OpenFastTrace](https://github.c

```gradle
requirementTracing {
failBuild = true
inputDirectories = files('custom-dir')
reportFile = file('build/custom-report.txt')
reportFormat = 'plain'
Expand All @@ -46,6 +47,7 @@ requirementTracing {

You can configure the following properties:

* `failBuild`: Fail build when tracing finds any issues (default: `true`)
* `inputDirectories`: Files or directories to import
* `reportFile`: Path to the report file
* `reportFormat`: Format of the report
Expand All @@ -63,7 +65,7 @@ You can configure the following properties:
* `collapse` - hide details (default)
* `expand` - show details

### Configuring the short tag importer
### Configuring the Short Tag Importer

The short tag importer allows omitting artifact type and the covered artifact type. Optionally you can add a prefix to the item name, e.g. a common module name.

Expand Down Expand Up @@ -93,7 +95,7 @@ As a benefit the tags are much shorter and contain only the name and revision:

See [multi-project/sub1](https://github.com/itsallcode/openfasttrace-gradle/tree/main/example-projects/multi-project/sub1) for a basic example.

### Sharing requirements
### Sharing Requirements

In bigger setups you might want to share requirements between multiple projects.

Expand All @@ -104,7 +106,7 @@ Example: The Software Architecture Design project `swad` contains overall requir
1. Both components publish their requirements as artefacts `component-a-req` and `component-b-req` to the shared Maven repository.
1. A regular job check that all requirements from `swad` are covered by tracing `swad-req`, `component-a-req` and `component-b-req`.

#### Publishing requirements to a Maven repository
#### Publishing Requirements to a Maven Repository

If you want to publish requirements to a Maven repository you can use the following configuration in your `build.gradle`:

Expand Down Expand Up @@ -134,7 +136,7 @@ publishing {

See [publish-config](https://github.com/itsallcode/openfasttrace-gradle/tree/main/example-projects/publish-config) for a basic example.

#### Importing external requirements
#### Importing External Requirements

You can import requirements from another project using the `importedRequirements` configuration. The requirements must be published to a repository as a zip file and can be referenced using the usual gradle dependency syntax:

Expand All @@ -154,7 +156,8 @@ See [dependency-config](https://github.com/itsallcode/openfasttrace-gradle/tree/
## Development

```sh
git clone https://github.com/itsallcode/openfasttrace-gradle-gradle.git
git clone https://github.com/itsallcode/openfasttrace-gradle.git
cd openfasttrace-gradle
./gradlew check
# Test report: build/reports/tests/index.html
```
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ repositories {

apply from: 'gradle/workAroundJacocoGradleTestKitIssueOnWindows.gradle'

version = '2.0.0'
version = '3.0.0'
group = 'org.itsallcode'

ext {
Expand Down
3 changes: 2 additions & 1 deletion example-projects/custom-config/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ plugins {
}

requirementTracing {
failBuild = findProperty('failBuild') == 'true'
inputDirectories = files('custom-dir')
reportFile = file('build/custom-report.txt')
reportFormat = 'plain'
reportVerbosity = 'ALL'
}
}
3 changes: 2 additions & 1 deletion example-projects/dependency-config/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ repositories {
}

requirementTracing {
failBuild = false
reportFormat = 'plain'
reportVerbosity = 'ALL'
importedRequirements = [':requirements:1.0@zip']
}
}
3 changes: 2 additions & 1 deletion example-projects/html-report/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ plugins {
}

requirementTracing {
failBuild = false
inputDirectories = files('custom-dir')
reportFormat = 'html'
detailsSectionDisplay = 'EXPAND'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ private static void configureTask(final Project rootProject,
task.setDescription("Trace requirements and generate tracing report");
task.dependsOn(collectTask);
final TracingConfig config = getConfig(rootProject);
task.getFailBuild().set(config.getFailBuild());
task.getRequirementsFile().set(collectTask.get().getOutputFile());
if (config.getReportFile().isPresent())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class TracingConfig
private final ListProperty<String> filteredArtifactTypes;
private final Property<Boolean> filterAcceptsItemsWithoutTag;
private final Property<DetailsSectionDisplay> detailsSectionDisplay;
private final Property<Boolean> failBuild;

public TracingConfig(final Project project)
{
Expand All @@ -41,6 +42,8 @@ public TracingConfig(final Project project)
this.filterAcceptsItemsWithoutTag.set(true);
this.detailsSectionDisplay = project.getObjects().property(DetailsSectionDisplay.class);
this.detailsSectionDisplay.set(DetailsSectionDisplay.COLLAPSE);
this.failBuild = project.getObjects().property(Boolean.class);
this.failBuild.set(true);
}

public Property<ReportVerbosity> getReportVerbosity()
Expand Down Expand Up @@ -143,11 +146,21 @@ public TagPathConfiguration getTagPathConfig()
return ((ExtensionAware) this).getExtensions().getByType(TagPathConfiguration.class);
}

public Property<Boolean> getFailBuild()
{
return failBuild;
}

public void setFailBuild(final boolean failBuild)
{
this.failBuild.set(failBuild);
}

@Override
public String toString()
{
return "TracingConfig [reportVerbosity=" + reportVerbosity + ", inputDirectories="
+ inputDirectories + ", reportFile=" + reportFile + ", pathConfig="
+ getTagPathConfig() + "]";
+ getTagPathConfig() + ", failBuild=" + failBuild + "]";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public class TraceTask extends DefaultTask
.setProperty(String.class);
private final Property<Boolean> filterAcceptsItemsWithoutTag = getProject().getObjects()
.property(Boolean.class);
private final Property<Boolean> failBuild = getProject().getObjects().property(Boolean.class);

@InputFile
public RegularFileProperty getRequirementsFile()
Expand Down Expand Up @@ -92,6 +93,17 @@ public Property<DetailsSectionDisplay> getDetailsSectionDisplay()
return detailsSectionDisplay;
}

@Input
public Property<Boolean> getFailBuild()
{
return failBuild;
}

private boolean shouldFailBuild()
{
return failBuild.getOrElse(true);
}

@TaskAction
public void trace()
{
Expand All @@ -103,10 +115,15 @@ public void trace()
importSettings.getInputs());
final List<LinkedSpecificationItem> linkedItems = oft.link(importedItems);
final Trace trace = oft.trace(linkedItems);
final Path report = getOutputFileInternal().toPath();
final Path reportPath = getOutputFileInternal().toPath();
getLogger().info("Tracing result: {} total items, {} defects. Writing report to {}",
trace.count(), trace.countDefects(), report);
oft.reportToPath(trace, report, getReportSettings());
trace.count(), trace.countDefects(), reportPath);
oft.reportToPath(trace, reportPath, getReportSettings());
if (trace.countDefects() > 0 && shouldFailBuild())
{
throw new IllegalStateException("Requirement tracing found " + trace.countDefects()
+ " defects. See report at " + reportPath + " for details.");
}
}

private ReportSettings getReportSettings()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.joining;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.*;

import java.io.*;
import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -154,6 +154,40 @@ void testTraceExampleProjectWithCustomConfig(final GradleTestConfig config) thro
"not ok - 2 total, 1 defect");
}

@ParameterizedTest(name = "testTraceExampleProjectWithCustomConfigFailBuild {0}")
@EnumSource
void testTraceExampleProjectWithCustomConfigFailBuild(final GradleTestConfig config)
throws IOException
{
final BuildResult buildResult = runBuildExpectFailure(config, PROJECT_CUSTOM_CONFIG_DIR,
"clean", "traceRequirements", "-PfailBuild=true");
assertEquals(TaskOutcome.FAILED, buildResult.task(":traceRequirements").getOutcome());
assertFileContent(PROJECT_CUSTOM_CONFIG_DIR.resolve("build/custom-report.txt"),
"not ok [ in: 1 / 1 ✔ | out: 0 / 0 ] dsn~exampleB~1 (impl, -utest)", //
"not ok - 2 total, 1 defect");
}

@ParameterizedTest(name = "testTraceExampleProjectWithCustomConfigFailBuild {0}")
@EnumSource
void testTraceExampleProjectWithCustomConfigFailBuildErrorMessage(final GradleTestConfig config)
{
try
{
runBuild(config, PROJECT_CUSTOM_CONFIG_DIR, "clean", "traceRequirements",
"-PfailBuild=true");
}
catch (final UnexpectedBuildFailure e)
{
assertAll(
() -> assertEquals(TaskOutcome.FAILED,
e.getBuildResult().task(":traceRequirements").getOutcome()),
() -> assertThat(e.getMessage(),
startsWith("Unexpected build execution failure")),
() -> assertThat(e.getMessage(),
containsString("Requirement tracing found 1 defects. See report at")));
}
}

@ParameterizedTest(name = "testTraceMultiProject {0}")
@EnumSource
void testTraceMultiProject(final GradleTestConfig config) throws IOException
Expand Down Expand Up @@ -252,8 +286,20 @@ private static String fileContent(final Path file) throws IOException
return new String(Files.readAllBytes(file), StandardCharsets.UTF_8);
}

private static BuildResult runBuildExpectFailure(final GradleTestConfig config,
final Path projectDir, final String... arguments)
{
return createGradleRunner(config, projectDir, arguments).buildAndFail();
}

private static BuildResult runBuild(final GradleTestConfig config, final Path projectDir,
final String... arguments)
{
return createGradleRunner(config, projectDir, arguments).build();
}

private static GradleRunner createGradleRunner(final GradleTestConfig config,
final Path projectDir, final String... arguments)
{
configureJacoco(projectDir);
final List<String> allArgs = new ArrayList<>();
Expand All @@ -272,7 +318,7 @@ private static BuildResult runBuild(final GradleTestConfig config, final Path pr
{
runner.withGradleVersion(config.gradleVersion);
}
return runner.build();
return runner;
}

private static void configureJacoco(final Path projectDir)
Expand Down