From 30d18320da55789ba9b9eed3496127a16b2b8084 Mon Sep 17 00:00:00 2001
From: Christoph Pirkl
Date: Thu, 8 Feb 2024 10:09:22 +0100
Subject: [PATCH] #515 Parse changes file (#526)
Co-authored-by: Christoph Kuhnke
---
.../analyze/generic/MavenProcessBuilder.java | 1 -
.../validators/VersionCollector.java | 6 +-
.../changelog/ChangelogFileGenerator.java | 7 +-
.../changelog/ChangelogFileValidator.java | 6 +-
.../validators/changesfile/ChangesFile.java | 341 +++++++++++-------
.../validators/changesfile/ChangesFileIO.java | 157 ++++++--
.../changesfile/ChangesFileName.java | 83 +++++
.../changesfile/ChangesFileSection.java | 114 ++++--
.../changesfile/ChangesFileValidator.java | 13 +-
.../changesfile/DependencySectionFixer.java | 20 +-
.../DependencyChangeReportRenderer.java | 18 +-
.../files/LatestChangesFileValidator.java | 6 +-
.../validators/VersionCollectorTest.java | 17 +-
.../changelog/ChangelogFileGeneratorTest.java | 8 +-
.../changesfile/ChangesFileIOTest.java | 146 +++++++-
.../changesfile/ChangesFileNameTest.java | 12 +
.../changesfile/ChangesFileSectionTest.java | 13 +
.../changesfile/ChangesFileTest.java | 61 ++++
.../changesfile/ChangesFileValidatorTest.java | 31 +-
.../DependencySectionFixerTest.java | 54 ++-
.../DependencyChangeReportRendererTest.java | 28 +-
.../files/LatestChangesFileValidatorTest.java | 6 +-
22 files changed, 846 insertions(+), 302 deletions(-)
create mode 100644 project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileName.java
create mode 100644 project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileNameTest.java
create mode 100644 project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileSectionTest.java
create mode 100644 project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileTest.java
diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/generic/MavenProcessBuilder.java b/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/generic/MavenProcessBuilder.java
index d7024931..b8252558 100644
--- a/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/generic/MavenProcessBuilder.java
+++ b/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/generic/MavenProcessBuilder.java
@@ -7,7 +7,6 @@
import java.util.List;
import com.exasol.projectkeeper.OsCheck;
-import com.exasol.projectkeeper.OsCheck.OSType;
/**
* This class allows building and starting a {@code mvn} command.
diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/VersionCollector.java b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/VersionCollector.java
index e3030c8f..f5674c74 100644
--- a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/VersionCollector.java
+++ b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/VersionCollector.java
@@ -8,7 +8,7 @@
import java.util.stream.Stream;
import com.exasol.errorreporting.ExaError;
-import com.exasol.projectkeeper.validators.changesfile.ChangesFile;
+import com.exasol.projectkeeper.validators.changesfile.ChangesFileName;
/**
* This class list all project-versions by scanning the doc/changes/ folder.
@@ -30,10 +30,10 @@ public VersionCollector(final Path projectDirectory) {
*
* @return list of changes files
*/
- public List collectChangesFiles() {
+ public List collectChangesFiles() {
try (final Stream filesStream = Files.walk(this.projectDirectory.resolve(Path.of("doc", "changes")))) {
return filesStream //
- .map(ChangesFile.Filename::from) //
+ .map(ChangesFileName::from) //
.flatMap(Optional::stream) //
.sorted(Comparator.reverseOrder()) //
.collect(Collectors.toList());
diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changelog/ChangelogFileGenerator.java b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changelog/ChangelogFileGenerator.java
index ea1a64f7..8e7ff839 100644
--- a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changelog/ChangelogFileGenerator.java
+++ b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changelog/ChangelogFileGenerator.java
@@ -2,8 +2,7 @@
import java.util.List;
-import com.exasol.projectkeeper.validators.changesfile.ChangesFile;
-import com.exasol.projectkeeper.validators.changesfile.ChangesFile.Filename;
+import com.exasol.projectkeeper.validators.changesfile.ChangesFileName;
/**
* This class generates the content for the changelog file.
@@ -19,9 +18,9 @@ class ChangelogFileGenerator {
*/
final StringBuilder templateBuilder = new StringBuilder();
- String generate(final List filenames) {
+ String generate(final List filenames) {
this.templateBuilder.append("# Changes" + NL + NL);
- for (final Filename file : filenames) {
+ for (final ChangesFileName file : filenames) {
this.templateBuilder.append("* [" + file.version() + "](" + file.filename() + ")" + NL);
}
return this.templateBuilder.toString();
diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changelog/ChangelogFileValidator.java b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changelog/ChangelogFileValidator.java
index a6dc485c..111612d0 100644
--- a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changelog/ChangelogFileValidator.java
+++ b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changelog/ChangelogFileValidator.java
@@ -8,14 +8,14 @@
import com.exasol.projectkeeper.Validator;
import com.exasol.projectkeeper.validators.AbstractFileContentValidator;
import com.exasol.projectkeeper.validators.VersionCollector;
-import com.exasol.projectkeeper.validators.changesfile.ChangesFile;
+import com.exasol.projectkeeper.validators.changesfile.ChangesFileName;
import com.exasol.projectkeeper.validators.finding.SimpleValidationFinding;
import com.exasol.projectkeeper.validators.finding.ValidationFinding;
/**
* This is a {@link Validator} for the changelog files.
*/
-//[impl->dsn~verify-changelog-file~1]
+// [impl->dsn~verify-changelog-file~1]
public class ChangelogFileValidator extends AbstractFileContentValidator {
private final Path projectDirectory;
@@ -44,7 +44,7 @@ protected List validateContent(final String content) {
@Override
protected String getTemplate() {
- final List versions = new VersionCollector(this.projectDirectory).collectChangesFiles();
+ final List versions = new VersionCollector(this.projectDirectory).collectChangesFiles();
return new ChangelogFileGenerator().generate(versions);
}
}
diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFile.java b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFile.java
index 38e3c4fd..c2e02c54 100644
--- a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFile.java
+++ b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFile.java
@@ -1,105 +1,47 @@
package com.exasol.projectkeeper.validators.changesfile;
import java.nio.file.Path;
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import com.exasol.projectkeeper.mavenrepo.Version;
+import com.exasol.errorreporting.ExaError;
import com.vdurmont.semver4j.Semver;
-import com.vdurmont.semver4j.Semver.SemverType;
/**
* This class represents a doc/changes/changes_x.x.x.md file.
*/
-public class ChangesFile {
+public final class ChangesFile {
/** Headline of the dependency updates section. */
public static final String DEPENDENCY_UPDATES_HEADING = "## Dependency Updates";
- private final List headerSectionLines;
+ /** Headline of the Summary section. */
+ public static final String SUMMARY_HEADING = "## Summary";
+ private final String projectName;
+ private final Semver projectVersion;
+ private final String releaseDate;
+ private final String codeName;
+ private final ChangesFileSection summarySection;
private final List sections;
+ private final ChangesFileSection dependencyChangeSection;
- /**
- * Create a new instance of {@link ChangesFile}.
- *
- * @param headerLines lines of the changes file until the first level section
- * @param sections sections of the changes file
- */
- public ChangesFile(final List headerLines, final List sections) {
- this.headerSectionLines = headerLines;
- this.sections = sections;
+ private ChangesFile(final Builder builder) {
+ this.projectName = builder.projectName;
+ this.projectVersion = builder.projectVersion;
+ this.releaseDate = builder.releaseDate;
+ this.codeName = builder.codeName;
+ this.summarySection = builder.summarySection;
+ this.sections = List.copyOf(builder.sections);
+ this.dependencyChangeSection = builder.dependencyChangeSection;
}
/**
- * Filename of a changes file, e.g. "changes_1.2.3.md".
+ * Get the relative path of the changes file for the given version.
+ *
+ * @param projectVersion project version
+ * @return relative path of the changes file, e.g. {@code doc/changes/changes_1.2.3.md}
*/
- public static class Filename implements Comparable {
- /** Regular expression to identify valid names of changes files and to extract version number. **/
- public static final Pattern PATTERN = Pattern.compile("changes_(" + Version.PATTERN.pattern() + ")\\.md");
-
- /**
- * @param path path to create a {@link Filename} for
- * @return If path matches regular expression for valid changes filenames then an {@link Optional} containing a
- * new instance of {@link Filename}, otherwise {@code Optional.empty()}.
- */
- public static Optional from(final Path path) {
- final String filename = path.getFileName().toString();
- final Matcher matcher = PATTERN.matcher(filename);
- if (!matcher.matches()) {
- return Optional.empty();
- }
- return Optional.of(new Filename(matcher.replaceFirst("$1")));
- }
-
- private final Semver version;
-
- /**
- * Create a new instance of {@link ChangesFile.Filename}.
- *
- * @param version version to use for new instance
- */
- public Filename(final String version) {
- this.version = new Semver(version, SemverType.LOOSE);
- }
-
- /**
- * @return filename of the current {@link ChangesFile.Filename} as string
- */
- public String filename() {
- return "changes_" + this.version + ".md";
- }
-
- @Override
- public int compareTo(final Filename o) {
- return this.version.compareTo(o.version);
- }
-
- /**
- * @return version number contained in the filename of current {@link ChangesFile.Filename}
- */
- public String version() {
- return this.version.getValue();
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(this.version);
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final Filename other = (Filename) obj;
- return Objects.equals(this.version, other.version);
- }
+ public static Path getPathForVersion(final String projectVersion) {
+ return Path.of("doc", "changes", new ChangesFileName(projectVersion).filename());
}
/**
@@ -112,24 +54,65 @@ public static Builder builder() {
}
/**
- * Get the header of the changes section.
- *
- * The header includes all lines until the first section {@code ##} starts.
- *
- *
- * @return list of lines of the header
+ * Get a builder configured with the this ChangesFile. This is useful for creating a copy and modify some parts of
+ * this object.
+ *
+ * @return a preconfigured builder
*/
- public List getHeaderSectionLines() {
- return this.headerSectionLines;
+ public Builder toBuilder() {
+ return builder().projectName(this.projectName).projectVersion(this.projectVersion.toString())
+ .releaseDate(this.releaseDate).codeName(this.codeName).summary(this.summarySection)
+ .sections(List.copyOf(this.sections)).dependencyChangeSection(this.dependencyChangeSection);
}
/**
- * Get the heading of the file.
- *
- * @return heading (1. line)
+ * Get the project name for the first header line, e.g. {@code Project Keeper}.
+ *
+ * @return project name
+ */
+ public String getProjectName() {
+ return projectName;
+ }
+
+ /**
+ * Get the project version for the first header line, e.g. {@code 1.2.3}.
+ *
+ * @return project version
+ */
+ public Semver getProjectVersion() {
+ return projectVersion;
+ }
+
+ /**
+ * Get the release date for the first header line, e.g. {@code 2024-01-29} or {@code 2024-??-??}.
+ *
+ * @return release date
+ */
+ public String getReleaseDate() {
+ return releaseDate;
+ }
+
+ /**
+ * Get the code name of the release.
+ *
+ * @return code name
*/
- public String getHeading() {
- return this.headerSectionLines.get(0);
+ public String getCodeName() {
+ return codeName;
+ }
+
+ /**
+ * Get the parsed release date for the first header line. If the date is not valid (e.g. {@code 2024-??-??}), this
+ * will return an empty {@link Optional}.
+ *
+ * @return release date
+ */
+ public Optional getParsedReleaseDate() {
+ try {
+ return Optional.of(LocalDate.parse(this.getReleaseDate()));
+ } catch (final DateTimeParseException exception) {
+ return Optional.empty();
+ }
}
/**
@@ -141,60 +124,175 @@ public List getSections() {
return this.sections;
}
+ /**
+ * Get the dependency change section.
+ *
+ * @return dependency change section
+ */
+ public Optional getDependencyChangeSection() {
+ return Optional.ofNullable(this.dependencyChangeSection);
+ }
+
+ /**
+ * Get the summary section.
+ *
+ * @return summary section
+ */
+ public Optional getSummarySection() {
+ return Optional.ofNullable(this.summarySection);
+ }
+
@Override
- public boolean equals(final Object other) {
- if (this == other) {
- return true;
- }
- if ((other == null) || (getClass() != other.getClass())) {
- return false;
- }
- final ChangesFile that = (ChangesFile) other;
- return Objects.equals(this.headerSectionLines, that.headerSectionLines)
- && Objects.equals(this.sections, that.sections);
+ public String toString() {
+ return "ChangesFile [projectName=" + projectName + ", projectVersion=" + projectVersion + ", releaseDate="
+ + releaseDate + ", codeName=" + codeName + ", summarySection=" + summarySection + ", sections="
+ + sections + ", dependencyChangeSection=" + dependencyChangeSection + "]";
}
@Override
public int hashCode() {
- return Objects.hash(this.headerSectionLines, this.sections);
+ return Objects.hash(projectName, projectVersion, releaseDate, codeName, summarySection, sections,
+ dependencyChangeSection);
}
@Override
- public String toString() {
- return String.join("\n", this.headerSectionLines) + "\n"
- + this.sections.stream().map(ChangesFileSection::toString).collect(Collectors.joining("\n"));
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final ChangesFile other = (ChangesFile) obj;
+ return Objects.equals(projectName, other.projectName) && Objects.equals(projectVersion, other.projectVersion)
+ && Objects.equals(releaseDate, other.releaseDate) && Objects.equals(codeName, other.codeName)
+ && Objects.equals(summarySection, other.summarySection) && Objects.equals(sections, other.sections)
+ && Objects.equals(dependencyChangeSection, other.dependencyChangeSection);
}
/**
* Builder for {@link ChangesFile}.
*/
public static class Builder {
- private final List sections = new ArrayList<>();
- private List header = Collections.emptyList();
+
+ private String projectName;
+ private Semver projectVersion;
+ private String releaseDate;
+ private String codeName;
+ private ChangesFileSection summarySection;
+ private List sections = new ArrayList<>();
+ private ChangesFileSection dependencyChangeSection;
private Builder() {
// private constructor to hide public default
}
/**
- * Set the header of the changes file.
+ * Set the project name for the first header line, e.g. {@code Project Keeper}.
+ *
+ * @param projectName project name
+ * @return self for fluent programming
+ */
+ public Builder projectName(final String projectName) {
+ this.projectName = projectName;
+ return this;
+ }
+
+ /**
+ * Set the project version for the first header line, e.g. {@code 1.2.3}.
*
- * @param header list of lines
+ * @param projectVersion project version
* @return self for fluent programming
*/
- public Builder setHeader(final List header) {
- this.header = header;
+ public Builder projectVersion(final String projectVersion) {
+ this.projectVersion = new Semver(projectVersion);
+ return this;
+ }
+
+ /**
+ * Set the release date for the first header line, e.g. {@code 2024-01-29} or {@code 2024-??-??}.
+ *
+ * @param releaseDate release date
+ * @return self for fluent programming
+ */
+ public Builder releaseDate(final String releaseDate) {
+ this.releaseDate = releaseDate;
+ return this;
+ }
+
+ /**
+ * Set the code name of the release.
+ *
+ * @param codeName code name
+ * @return self for fluent programming
+ */
+ public Builder codeName(final String codeName) {
+ this.codeName = codeName != null && codeName.isBlank() ? null : codeName;
return this;
}
/**
* Add a section to the changes file.
*
- * @param lines list of lines
+ * @param section section
* @return self for fluent programming
*/
- public Builder addSection(final List lines) {
- this.sections.add(new ChangesFileSection(lines));
+ public Builder addSection(final ChangesFileSection section) {
+ this.sections.add(section);
+ return this;
+ }
+
+ /**
+ * Set the {@code Summary} section for the changes file.
+ *
+ * @param section section
+ * @return self for fluent programming
+ */
+ public Builder summary(final ChangesFileSection section) {
+ if (section == null) {
+ this.summarySection = null;
+ return this;
+ }
+ if (!section.getHeading().equals(SUMMARY_HEADING)) {
+ throw new IllegalArgumentException(ExaError.messageBuilder("E-PK-CORE-178").message(
+ "Dependency change section has invalid heading {{heading}}, expected {{expected heading}}",
+ section.getHeading(), SUMMARY_HEADING).ticketMitigation().toString());
+ }
+ this.summarySection = section;
+ return this;
+ }
+
+ /**
+ * Set all sections of the changes file.
+ *
+ * @param sections list of sections
+ * @return self for fluent programming
+ */
+ public Builder sections(final List sections) {
+ this.sections = List.copyOf(sections);
+ return this;
+ }
+
+ /**
+ * Add a an optional {@code Dependency Updates} section to the changes file.
+ *
+ * @param section section
+ * @return self for fluent programming
+ */
+ public Builder dependencyChangeSection(final ChangesFileSection section) {
+ if (section == null) {
+ this.dependencyChangeSection = null;
+ return this;
+ }
+ if (!section.getHeading().equals(DEPENDENCY_UPDATES_HEADING)) {
+ throw new IllegalArgumentException(ExaError.messageBuilder("E-PK-CORE-179").message(
+ "Dependency change section has invalid heading {{heading}}, expected {{expected heading}}",
+ section.getHeading(), DEPENDENCY_UPDATES_HEADING).ticketMitigation().toString());
+ }
+ this.dependencyChangeSection = section;
return this;
}
@@ -204,7 +302,8 @@ public Builder addSection(final List lines) {
* @return built {@link ChangesFile}
*/
public ChangesFile build() {
- return new ChangesFile(this.header, this.sections);
+ return new ChangesFile(this);
}
+
}
}
diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileIO.java b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileIO.java
index 3853c6f2..a34765f0 100644
--- a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileIO.java
+++ b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileIO.java
@@ -2,18 +2,24 @@
import java.io.*;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Optional;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.exasol.errorreporting.ExaError;
+import com.exasol.projectkeeper.validators.changesfile.ChangesFile.Builder;
/**
* This class reads and writes a {@link ChangesFile} from disk.
*/
public class ChangesFileIO {
- private static final Pattern SECTION_HEADING_PATTERN = Pattern.compile("\\s*##\\s.*");
- private static final String LINE_SEPARATOR = System.lineSeparator();
+ private static final String CODE_NAME = "Code name:";
+ private static final String PROJECT_NAME_PATTERN = "[\\w\\s-]+";
+ private static final String VERSION_PATTERN = "\\d+\\.\\d+\\.\\d+";
+ private static final String DATE_PATTERN = "\\d{4}-[\\d?]{2}-[\\d?]{2}";
+ private static final Pattern FIRST_LINE_PATTERN = Pattern
+ .compile("^# (" + PROJECT_NAME_PATTERN + ") (" + VERSION_PATTERN + "), released (" + DATE_PATTERN + ")$");
+ private static final String LINE_SEPARATOR = "\n";
/**
* Read a {@link ChangesFile} from disk.
@@ -23,19 +29,7 @@ public class ChangesFileIO {
*/
public ChangesFile read(final Path file) {
try (final var fileReader = new BufferedReader(new FileReader(file.toFile()))) {
- String sectionHeader = null;
- String line;
- final var builder = ChangesFile.builder();
- final List lineBuffer = new ArrayList<>();
- while ((line = fileReader.readLine()) != null) {
- if (SECTION_HEADING_PATTERN.matcher(line).matches()) {
- makeSection(sectionHeader, builder, lineBuffer);
- sectionHeader = line;
- }
- lineBuffer.add(line);
- }
- makeSection(sectionHeader, builder, lineBuffer);
- return builder.build();
+ return read(file, fileReader);
} catch (final IOException exception) {
throw new IllegalStateException(ExaError.messageBuilder("F-PK-CORE-39")
.message("Failed to read changes file {{file}}.").parameter("file", file.toString()).toString(),
@@ -43,15 +37,83 @@ public ChangesFile read(final Path file) {
}
}
- private void makeSection(final String sectionHeader, final ChangesFile.Builder builder,
- final List lineBuffer) {
- if (!lineBuffer.isEmpty()) {
- if (sectionHeader == null) {
- builder.setHeader(List.copyOf(lineBuffer));
- } else {
- builder.addSection(List.copyOf(lineBuffer));
+ ChangesFile read(final Path file, final BufferedReader fileReader) throws IOException {
+ return new Parser(file, fileReader).parse();
+ }
+
+ private static class Parser {
+ final Path file;
+ final BufferedReader reader;
+ final Builder builder = ChangesFile.builder();
+ ChangesFileSection.Builder currentSection;
+ int lineCount = 0;
+
+ Parser(final Path file, final BufferedReader reader) {
+ this.file = file;
+ this.reader = reader;
+ }
+
+ ChangesFile parse() throws IOException {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ parseLine(line);
+ lineCount++;
+ }
+ addSection();
+ return builder.build();
+ }
+
+ private void parseLine(final String line) {
+ if (lineCount == 0) {
+ parseFirstLine(file, line);
+ return;
+ }
+ if (line.startsWith(CODE_NAME)) {
+ builder.codeName(line.substring(CODE_NAME.length()).trim());
+ return;
+ }
+ if (line.startsWith("## ")) {
+ addSection();
+ currentSection = ChangesFileSection.builder(line);
+ return;
+ }
+ if (currentSection != null) {
+ currentSection.addLine(line);
}
- lineBuffer.clear();
+ }
+
+ private void addSection() {
+ if (currentSection == null) {
+ return;
+ }
+ final ChangesFileSection section = currentSection.build();
+ currentSection = null;
+ switch (section.getHeading()) {
+ case ChangesFile.SUMMARY_HEADING:
+ builder.summary(section);
+ break;
+ case ChangesFile.DEPENDENCY_UPDATES_HEADING:
+ builder.dependencyChangeSection(section);
+ break;
+ default:
+ builder.addSection(section);
+ break;
+ }
+ }
+
+ private void parseFirstLine(final Path filePath, final String line) {
+ final Matcher matcher = FIRST_LINE_PATTERN.matcher(line);
+ if (!matcher.matches()) {
+ throw new IllegalStateException(ExaError.messageBuilder("E-PK-CORE-171")
+ .message("Changes file {{file path}} contains invalid first line {{first line}}.", filePath,
+ line)
+ .mitigation("Update first line so that it matches regex {{expected regular expression}}",
+ FIRST_LINE_PATTERN)
+ .toString());
+ }
+ builder.projectName(matcher.group(1)) //
+ .projectVersion(matcher.group(2)) //
+ .releaseDate(matcher.group(3));
}
}
@@ -62,12 +124,8 @@ private void makeSection(final String sectionHeader, final ChangesFile.Builder b
* @param destinationFile file to write to
*/
public void write(final ChangesFile changesFile, final Path destinationFile) {
- try (final var fileWriter = new BufferedWriter(new FileWriter(destinationFile.toFile()))) {
- writeSection(fileWriter, changesFile.getHeaderSectionLines());
- for (final ChangesFileSection section : changesFile.getSections()) {
- writeSection(fileWriter, section.getContent());
- }
- fileWriter.flush();
+ try (final var writer = new BufferedWriter(new FileWriter(destinationFile.toFile()))) {
+ write(changesFile, writer);
} catch (final IOException exception) {
throw new IllegalStateException(
ExaError.messageBuilder("E-PK-CORE-41").message("Failed to write changes file {{file name}}.")
@@ -76,10 +134,39 @@ public void write(final ChangesFile changesFile, final Path destinationFile) {
}
}
- private void writeSection(final BufferedWriter fileWriter, final List content) throws IOException {
- for (final String line : content) {
- fileWriter.write(line);
- fileWriter.write(LINE_SEPARATOR);
+ void write(final ChangesFile changesFile, final Writer writer) throws IOException {
+ writeHeader(writer, changesFile);
+ for (final ChangesFileSection section : changesFile.getSections()) {
+ writeSection(writer, section);
+ }
+ final Optional dependencyChangeSection = changesFile.getDependencyChangeSection();
+ if (dependencyChangeSection.isPresent()) {
+ writer.write(dependencyChangeSection.get().toString());
+ writer.write(LINE_SEPARATOR);
+ }
+ }
+
+ private void writeHeader(final Writer writer, final ChangesFile changesFile) throws IOException {
+ writer.write("# " + changesFile.getProjectName() + " " + changesFile.getProjectVersion() + ", released "
+ + changesFile.getReleaseDate());
+ writer.write(LINE_SEPARATOR);
+ writer.write(LINE_SEPARATOR);
+ writer.write(CODE_NAME + (changesFile.getCodeName() != null ? " " + changesFile.getCodeName() : ""));
+ writer.write(LINE_SEPARATOR);
+ writer.write(LINE_SEPARATOR);
+ final Optional summarySection = changesFile.getSummarySection();
+ if (summarySection.isPresent()) {
+ writer.write(summarySection.get().toString());
+ writer.write(LINE_SEPARATOR);
+ }
+ }
+
+ private void writeSection(final Writer writer, final ChangesFileSection section) throws IOException {
+ writer.write(section.getHeading());
+ writer.write(LINE_SEPARATOR);
+ for (final String line : section.getContent()) {
+ writer.write(line);
+ writer.write(LINE_SEPARATOR);
}
}
}
diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileName.java b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileName.java
new file mode 100644
index 00000000..390c6df5
--- /dev/null
+++ b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileName.java
@@ -0,0 +1,83 @@
+package com.exasol.projectkeeper.validators.changesfile;
+
+import java.nio.file.Path;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.exasol.projectkeeper.mavenrepo.Version;
+import com.vdurmont.semver4j.Semver;
+import com.vdurmont.semver4j.Semver.SemverType;
+
+/**
+ * Filename of a changes file, e.g. {@code changes_1.2.3.md}.
+ */
+public final class ChangesFileName implements Comparable {
+ /** Regular expression to identify valid names of changes files and to extract version number. **/
+ public static final Pattern PATTERN = Pattern.compile("changes_(" + Version.PATTERN.pattern() + ")\\.md");
+
+ /**
+ * @param path path to create a {@link ChangesFileName} for
+ * @return If path matches regular expression for valid changes filenames then an {@link Optional} containing a new
+ * instance of {@link ChangesFileName}, otherwise {@code Optional.empty()}.
+ */
+ public static Optional from(final Path path) {
+ final String filename = path.getFileName().toString();
+ final Matcher matcher = PATTERN.matcher(filename);
+ if (!matcher.matches()) {
+ return Optional.empty();
+ }
+ return Optional.of(new ChangesFileName(matcher.replaceFirst("$1")));
+ }
+
+ private final Semver version;
+
+ /**
+ * Create a new instance of {@link ChangesFileName}.
+ *
+ * @param version version to use for new instance
+ */
+ public ChangesFileName(final String version) {
+ this.version = new Semver(version, SemverType.LOOSE);
+ }
+
+ /**
+ * @return filename of the current {@link ChangesFileName} as string
+ */
+ public String filename() {
+ return "changes_" + this.version + ".md";
+ }
+
+ @Override
+ public int compareTo(final ChangesFileName o) {
+ return this.version.compareTo(o.version);
+ }
+
+ /**
+ * @return version number contained in the filename of current {@link ChangesFileName}
+ */
+ public String version() {
+ return this.version.getValue();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.version);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final ChangesFileName other = (ChangesFileName) obj;
+ return Objects.equals(this.version, other.version);
+ }
+}
diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileSection.java b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileSection.java
index b5109bf9..5e5a1ac1 100644
--- a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileSection.java
+++ b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileSection.java
@@ -1,9 +1,8 @@
package com.exasol.projectkeeper.validators.changesfile;
-import java.util.List;
-import java.util.Objects;
+import static java.util.Arrays.asList;
-import com.exasol.errorreporting.ExaError;
+import java.util.*;
/**
* Section of a {@link ChangesFile}.
@@ -11,20 +10,14 @@
* Each level two heading (##) starts a new section.
*
*/
-public class ChangesFileSection {
+public final class ChangesFileSection {
+
+ private final String heading;
private final List content;
- /**
- * Create a new instance of {@link ChangesFileSection}.
- *
- * @param content lines
- */
- public ChangesFileSection(final List content) {
- if (content.isEmpty()) {
- throw new IllegalStateException(ExaError.messageBuilder("F-PK-CORE-36")
- .message("changes file sections must not be empty.").ticketMitigation().toString());
- }
- this.content = content;
+ private ChangesFileSection(final Builder builder) {
+ this.heading = Objects.requireNonNull(builder.heading, "header");
+ this.content = List.copyOf(builder.lines);
}
/**
@@ -33,7 +26,7 @@ public ChangesFileSection(final List content) {
* @return heading
*/
public String getHeading() {
- return this.content.get(0);
+ return this.heading;
}
/**
@@ -46,22 +39,91 @@ public List getContent() {
}
@Override
- public boolean equals(final Object other) {
- if (this == other)
- return true;
- if (other == null || getClass() != other.getClass())
- return false;
- final ChangesFileSection that = (ChangesFileSection) other;
- return Objects.equals(this.content, that.content);
+ public int hashCode() {
+ return Objects.hash(heading, content);
}
@Override
- public int hashCode() {
- return Objects.hash(this.content);
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final ChangesFileSection other = (ChangesFileSection) obj;
+ return Objects.equals(heading, other.heading) && Objects.equals(content, other.content);
}
@Override
public String toString() {
- return String.join("\n", this.content);
+ return heading + "\n" + String.join("\n", this.content);
+ }
+
+ /**
+ * Create a new {@link Builder} for creating a {@link ChangesFileSection}.
+ *
+ * @param heading the heading for the new section
+ * @return a new builder
+ */
+ public static Builder builder(final String heading) {
+ return new Builder(heading);
+ }
+
+ /**
+ * A builder for creating {@link ChangesFileSection}s.
+ */
+ public static class Builder {
+ private final String heading;
+ private final List lines = new ArrayList<>();
+
+ private Builder(final String heading) {
+ this.heading = heading;
+ }
+
+ /**
+ * Add the given lines to the content of the new {@code ChangesFileSection}.
+ *
+ * @param lines lines to add
+ * @return {@code this} for fluent programming
+ */
+ public Builder addLines(final String... lines) {
+ this.lines.addAll(asList(lines));
+ return this;
+ }
+
+ /**
+ * Add the given lines to the content of the new {@code ChangesFileSection}.
+ *
+ * @param lines lines to add
+ * @return {@code this} for fluent programming
+ */
+ public Builder addLines(final List lines) {
+ this.lines.addAll(lines);
+ return this;
+ }
+
+ /**
+ * Add the given line to the content of the new {@code ChangesFileSection}.
+ *
+ * @param line line to add
+ * @return {@code this} for fluent programming
+ */
+ public Builder addLine(final String line) {
+ this.lines.add(line);
+ return this;
+ }
+
+ /**
+ * Build a new {@link ChangesFileSection}.
+ *
+ * @return a new {@link ChangesFileSection}.
+ */
+ public ChangesFileSection build() {
+ return new ChangesFileSection(this);
+ }
}
}
diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidator.java b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidator.java
index 30a8bb5b..ba21db83 100644
--- a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidator.java
+++ b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidator.java
@@ -31,7 +31,7 @@ public class ChangesFileValidator extends AbstractFileValidator {
*/
public ChangesFileValidator(final String projectVersion, final String projectName, final Path projectDirectory,
final List sources) {
- super(projectDirectory, Path.of("doc", "changes", new ChangesFile.Filename(projectVersion).filename()));
+ super(projectDirectory, ChangesFile.getPathForVersion(projectVersion));
this.projectVersion = projectVersion;
this.projectName = projectName;
this.sources = sources;
@@ -72,10 +72,13 @@ private ChangesFile fixSections(final ChangesFile changesFile) {
}
private ChangesFile getTemplate() {
- final var changesFile = ChangesFile.builder()
- .setHeader(List.of("# " + this.projectName + " " + this.projectVersion + ", released "
- + LocalDateTime.now().getYear() + "-??-??", "", "Code name:", "")) //
- .addSection(List.of("## Summary", "", "## Features", "", "* ISSUE_NUMBER: description", "")) //
+ final String releaseDate = LocalDateTime.now().getYear() + "-??-??";
+ final var changesFile = ChangesFile.builder().projectName(this.projectName).projectVersion(this.projectVersion)
+ .releaseDate(releaseDate) //
+ .codeName("") //
+ .summary(ChangesFileSection.builder("## Summary").build())
+ .addSection(ChangesFileSection.builder("## Features").addLines("", "* ISSUE_NUMBER: description", "")
+ .build()) //
.build();
return fixSections(changesFile);
}
diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/DependencySectionFixer.java b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/DependencySectionFixer.java
index 01a08241..9db14cf0 100644
--- a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/DependencySectionFixer.java
+++ b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/DependencySectionFixer.java
@@ -1,9 +1,7 @@
package com.exasol.projectkeeper.validators.changesfile;
-import static com.exasol.projectkeeper.validators.changesfile.ChangesFile.DEPENDENCY_UPDATES_HEADING;
-
-import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.stream.Collectors;
import com.exasol.projectkeeper.sources.AnalyzedSource;
@@ -12,7 +10,7 @@
/**
* This class fixes the dependency section of a {@link ChangesFile}.
*/
-//[impl->dsn~dependency-section-in-changes_x.x.x.md-file-validator~1]
+// [impl->dsn~dependency-section-in-changes_x.x.x.md-file-validator~1]
class DependencySectionFixer {
private final List sources;
@@ -35,20 +33,12 @@ public DependencySectionFixer(final List sources) {
public ChangesFile fix(final ChangesFile changesFile) {
final List reports = this.sources.stream().map(this::getDependencyChangesOfSource)
.collect(Collectors.toList());
- final List renderedReport = new DependencyChangeReportRenderer().render(reports);
- final List sections = new ArrayList<>(changesFile.getSections());
- removeDependencySection(sections);
- if (!renderedReport.isEmpty()) {
- sections.add(new ChangesFileSection(renderedReport));
- }
- return new ChangesFile(List.copyOf(changesFile.getHeaderSectionLines()), sections);
+ final Optional dependencyChanges = new DependencyChangeReportRenderer().render(reports);
+ return changesFile.toBuilder() //
+ .dependencyChangeSection(dependencyChanges.orElse(null)).build();
}
private NamedDependencyChangeReport getDependencyChangesOfSource(final AnalyzedSource source) {
return new NamedDependencyChangeReport(source.getProjectName(), source.getDependencyChanges());
}
-
- private void removeDependencySection(final List sections) {
- sections.removeIf(section -> section.getHeading().compareToIgnoreCase(DEPENDENCY_UPDATES_HEADING) == 0);
- }
}
diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/dependencies/DependencyChangeReportRenderer.java b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/dependencies/DependencyChangeReportRenderer.java
index aca0d23d..34695e34 100644
--- a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/dependencies/DependencyChangeReportRenderer.java
+++ b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/dependencies/DependencyChangeReportRenderer.java
@@ -2,14 +2,12 @@
import static com.exasol.projectkeeper.ApStyleFormatter.capitalizeApStyle;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
import com.exasol.projectkeeper.shared.dependencies.BaseDependency.Type;
import com.exasol.projectkeeper.shared.dependencychanges.DependencyChange;
import com.exasol.projectkeeper.shared.dependencychanges.DependencyChangeReport;
-import com.exasol.projectkeeper.validators.changesfile.ChangesFile;
-import com.exasol.projectkeeper.validators.changesfile.NamedDependencyChangeReport;
+import com.exasol.projectkeeper.validators.changesfile.*;
/**
* String renderer for {@link DependencyChangeReport}.
@@ -20,17 +18,15 @@ public class DependencyChangeReportRenderer {
* Render a {@link DependencyChangeReport} to string.
*
* @param reports reports to render
- * @return rendered report as a list of lines
+ * @return rendered report as a section
*/
- public List render(final List reports) {
+ public Optional render(final List reports) {
final List content = renderContent(reports);
- final List lines = new ArrayList<>();
if (content.isEmpty()) {
- return lines;
+ return Optional.empty();
}
- lines.add(ChangesFile.DEPENDENCY_UPDATES_HEADING);
- lines.addAll(content);
- return lines;
+ return Optional.of(ChangesFileSection.builder(ChangesFile.DEPENDENCY_UPDATES_HEADING) //
+ .addLines(content).build());
}
private List renderContent(final List reports) {
diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/files/LatestChangesFileValidator.java b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/files/LatestChangesFileValidator.java
index afd5cb05..86236087 100644
--- a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/files/LatestChangesFileValidator.java
+++ b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/files/LatestChangesFileValidator.java
@@ -7,7 +7,7 @@
import com.exasol.errorreporting.ExaError;
import com.exasol.projectkeeper.Validator;
import com.exasol.projectkeeper.validators.VersionCollector;
-import com.exasol.projectkeeper.validators.changesfile.ChangesFile;
+import com.exasol.projectkeeper.validators.changesfile.ChangesFileName;
import com.exasol.projectkeeper.validators.finding.SimpleValidationFinding;
import com.exasol.projectkeeper.validators.finding.ValidationFinding;
@@ -32,11 +32,11 @@ public LatestChangesFileValidator(final Path projectDir, final String projectVer
@Override
public List validate() {
final List empty = Collections.emptyList();
- final List list = new VersionCollector(this.projectDirectory).collectChangesFiles();
+ final List list = new VersionCollector(this.projectDirectory).collectChangesFiles();
if (list.isEmpty()) {
return empty;
}
- final ChangesFile.Filename latest = list.get(0);
+ final ChangesFileName latest = list.get(0);
if (latest.version().equals(this.projectVersion)) {
return empty;
}
diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/VersionCollectorTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/VersionCollectorTest.java
index 56c91af0..0f178b0f 100644
--- a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/VersionCollectorTest.java
+++ b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/VersionCollectorTest.java
@@ -13,8 +13,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-import com.exasol.projectkeeper.validators.changesfile.ChangesFile;
-import com.exasol.projectkeeper.validators.changesfile.ChangesFile.Filename;
+import com.exasol.projectkeeper.validators.changesfile.ChangesFileName;
class VersionCollectorTest {
@Test
@@ -29,20 +28,20 @@ void sorted(@TempDir final Path tempDir) throws IOException {
"1.0.0")) {
createChangesFile(folder, version);
}
- final List expected = Stream.of( //
+ final List expected = Stream.of( //
"1.1.0", //
"1.0.10", //
"1.0.2", //
"1.0.0", //
"0.3.0") //
- .map(ChangesFile.Filename::new) //
+ .map(ChangesFileName::new) //
.collect(Collectors.toList());
assertThat(new VersionCollector(tempDir).collectChangesFiles(), equalTo(expected));
}
- private ChangesFile.Filename createChangesFile(final Path folder, final String version) throws IOException {
- final Filename cfile = new Filename(version);
- Files.createFile(folder.resolve(cfile.filename()));
- return cfile;
+ private ChangesFileName createChangesFile(final Path folder, final String version) throws IOException {
+ final ChangesFileName file = new ChangesFileName(version);
+ Files.createFile(folder.resolve(file.filename()));
+ return file;
}
-}
\ No newline at end of file
+}
diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changelog/ChangelogFileGeneratorTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changelog/ChangelogFileGeneratorTest.java
index 58a45393..3f66983a 100644
--- a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changelog/ChangelogFileGeneratorTest.java
+++ b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changelog/ChangelogFileGeneratorTest.java
@@ -12,7 +12,7 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
-import com.exasol.projectkeeper.validators.changesfile.ChangesFile;
+import com.exasol.projectkeeper.validators.changesfile.ChangesFileName;
class ChangelogFileGeneratorTest {
@Test
@@ -27,7 +27,7 @@ void testNonStandardVersionFormats(final String version) {
assertThat(new ChangelogFileGenerator().generate(files(version)), containsString(version));
}
- private List files(final String... versions) {
- return Arrays.stream(versions).map(ChangesFile.Filename::new).collect(Collectors.toList());
+ private List files(final String... versions) {
+ return Arrays.stream(versions).map(ChangesFileName::new).collect(Collectors.toList());
}
-}
\ No newline at end of file
+}
diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileIOTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileIOTest.java
index 191984a6..3f7748c1 100644
--- a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileIOTest.java
+++ b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileIOTest.java
@@ -1,12 +1,13 @@
package com.exasol.projectkeeper.validators.changesfile;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.contains;
-import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.*;
+import static org.junit.jupiter.api.Assertions.assertThrows;
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
import java.nio.file.*;
+import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -24,37 +25,142 @@ void testParsing() throws IOException {
final ChangesFile changesFile = new ChangesFileIO().read(changesFilePath);
final List headings = changesFile.getSections().stream().map(ChangesFileSection::getHeading)
.collect(Collectors.toList());
- assertThat(changesFile.getHeading(), equalTo("# My Project 0.1.0, released 1980-01-01"));
- assertThat(headings, contains("## Summary", "## Features", "## Bug Fixes", "## Documentation", "## Refactoring",
- "## Dependency Updates"));
+ assertThat(changesFile.getProjectName(), equalTo("My Project"));
+ assertThat(changesFile.getProjectVersion().toString(), equalTo("0.1.0"));
+ assertThat(changesFile.getReleaseDate(), equalTo("1980-01-01"));
+ assertThat(changesFile.getParsedReleaseDate().get(), equalTo(LocalDate.parse("1980-01-01")));
+ assertThat(changesFile.getSummarySection().get().getContent(), contains("", "My summary", ""));
+ assertThat(headings, contains("## Features", "## Bug Fixes", "## Documentation", "## Refactoring"));
+ assertThat(changesFile.getDependencyChangeSection().get().getHeading(), equalTo("## Dependency Updates"));
+ assertThat(changesFile.getDependencyChangeSection().get().getContent(), hasSize(18));
}
@Test
void testWriting() throws IOException {
- final ChangesFile changesFile = ChangesFile.builder().setHeader(List.of("# MyChanges"))
- .addSection(List.of("## My Subsection")).build();
+ final ChangesFile changesFile = ChangesFile.builder().projectName("project").projectVersion("1.2.3")
+ .releaseDate("2023-??-??").codeName("my code name")
+ .summary(ChangesFileSection.builder("## Summary").addLine("my summary content").build())
+ .addSection(ChangesFileSection.builder("# MyChanges").build())
+ .addSection(ChangesFileSection.builder("## My Subsection").addLine("content").build()).build();
final Path testFile = this.tempDir.resolve("myFile.md");
new ChangesFileIO().write(changesFile, testFile);
- assertThat(Files.readString(testFile),
- equalTo("# MyChanges" + System.lineSeparator() + "## My Subsection" + System.lineSeparator()));
+ assertThat(Files.readString(testFile), equalTo(
+ "# project 1.2.3, released 2023-??-??\n\nCode name: my code name\n\n## Summary\nmy summary content\n# MyChanges\n## My Subsection\ncontent\n"));
}
@Test
void testReadAndWrite() throws IOException {
- final Path changesFilePath = loadExampleFileToTempDir();
- final ChangesFileIO changesFileIO = new ChangesFileIO();
- final ChangesFile changesFile = changesFileIO.read(changesFilePath);
- final Path testFile = this.tempDir.resolve("result.md");
- changesFileIO.write(changesFile, testFile);
- assertThat(Files.readString(testFile), equalTo(Files.readString(changesFilePath)));
+ final String content = readExampleFile();
+ assertReadWrite(content);
+ }
+
+ @Test
+ void testReadInvalidFirstLineFails() {
+ final IllegalStateException exception = assertThrows(IllegalStateException.class,
+ () -> readFromString("# invalid first line"));
+ assertThat(exception.getMessage(), startsWith(
+ "E-PK-CORE-171: Changes file 'dummy-file' contains invalid first line '# invalid first line'. Update first line so that it matches regex"));
+ }
+
+ @Test
+ void testReadFirstLineWithDummyReleaseDate() throws IOException {
+ final ChangesFile changesFile = readFromString(
+ "# Project Name 1.2.3, released 2024-??-??\nCode name: my code name\n## Summary\n\n");
+ assertThat(changesFile.getProjectName(), equalTo("Project Name"));
+ assertThat(changesFile.getProjectVersion().toString(), equalTo("1.2.3"));
+ assertThat(changesFile.getReleaseDate(), equalTo("2024-??-??"));
+ assertThat(changesFile.getCodeName(), equalTo("my code name"));
+ assertThat(changesFile.getParsedReleaseDate().isPresent(), is(false));
+ assertWriteRead(changesFile);
+ }
+
+ @Test
+ void testReadFirstLineWithValidReleaseDate() throws IOException {
+ final ChangesFile changesFile = readFromString("# Project Name 1.2.3, released 2024-01-29\n## Summary\n\n");
+ assertThat(changesFile.getProjectName(), equalTo("Project Name"));
+ assertThat(changesFile.getProjectVersion().toString(), equalTo("1.2.3"));
+ assertThat(changesFile.getReleaseDate(), equalTo("2024-01-29"));
+ assertThat(changesFile.getParsedReleaseDate().get(), equalTo(LocalDate.parse("2024-01-29")));
+ assertWriteRead(changesFile);
+ }
+
+ @Test
+ void testReadMissingSummary() throws IOException {
+ final ChangesFile changesFile = readFromString("# Project Name 1.2.3, released 2024-01-29");
+ assertThat(changesFile.getSummarySection().isEmpty(), is(true));
+ }
+
+ @Test
+ void testReadEmptySummary() throws IOException {
+ final ChangesFile changesFile = readFromString("# Project Name 1.2.3, released 2024-01-29\n## Summary");
+ final ChangesFileSection summary = changesFile.getSummarySection().get();
+ assertThat(summary.getHeading(), equalTo("## Summary"));
+ assertThat(summary.getContent(), emptyIterable());
+ }
+
+ @Test
+ void testReadSummary() throws IOException {
+ final ChangesFile changesFile = readFromString(
+ "# Project Name 1.2.3, released 2024-01-29\n## Summary\nmy\ncontent\n");
+ final ChangesFileSection summary = changesFile.getSummarySection().get();
+ assertThat(summary.getHeading(), equalTo("## Summary"));
+ assertThat(summary.getContent(), contains("my", "content"));
+ assertWriteRead(changesFile);
+ }
+
+ @Test
+ void testReadNoDependencySection() throws IOException {
+ final ChangesFile changesFile = readFromString("# Project Name 1.2.3, released 2024-01-29\n## Summary");
+ assertThat(changesFile.getDependencyChangeSection().isEmpty(), is(true));
+ }
+
+ @Test
+ void testReadDependencySection() throws IOException {
+ final ChangesFile changesFile = readFromString(
+ "# Project Name 1.2.3, released 2024-01-29\n## Summary\n## Dependency Updates\nmy\ncontent");
+ assertThat(changesFile.getDependencyChangeSection().isEmpty(), is(false));
+ assertThat(changesFile.getDependencyChangeSection().get().getHeading(), equalTo("## Dependency Updates"));
+ assertThat(changesFile.getDependencyChangeSection().get().getContent(), contains("my", "content"));
}
private Path loadExampleFileToTempDir() throws IOException {
final Path changesFile = this.tempDir.resolve("changed_0.1.0.md");
- try (final InputStream exampleFileStream = getClass().getClassLoader()
- .getResourceAsStream("changesFileExample1.md")) {
+ try (final InputStream exampleFileStream = getExampleFileStream()) {
Files.copy(Objects.requireNonNull(exampleFileStream), changesFile, StandardCopyOption.REPLACE_EXISTING);
}
return changesFile;
}
-}
\ No newline at end of file
+
+ private String readExampleFile() throws IOException {
+ try (final InputStream exampleFileStream = getExampleFileStream()) {
+ return new String(exampleFileStream.readAllBytes(), StandardCharsets.UTF_8).replace("\r\n", "\n");
+ }
+ }
+
+ private InputStream getExampleFileStream() {
+ return getClass().getClassLoader().getResourceAsStream("changesFileExample1.md");
+ }
+
+ private void assertWriteRead(final ChangesFile changesFile) throws IOException {
+ final String content = writeToString(changesFile);
+ final ChangesFile readChangesFile = readFromString(content);
+ assertThat(readChangesFile.toString(), equalTo(changesFile.toString()));
+ assertThat(readChangesFile, equalTo(changesFile));
+ }
+
+ private void assertReadWrite(final String content) throws IOException {
+ final ChangesFile changesFile = readFromString(content);
+ final String writtenContent = writeToString(changesFile);
+ assertThat(writtenContent, equalTo(content));
+ }
+
+ private String writeToString(final ChangesFile changesFile) throws IOException {
+ final StringWriter stringWriter = new StringWriter();
+ new ChangesFileIO().write(changesFile, stringWriter);
+ return stringWriter.toString();
+ }
+
+ private ChangesFile readFromString(final String content) throws IOException {
+ return new ChangesFileIO().read(Path.of("dummy-file"), new BufferedReader(new StringReader(content)));
+ }
+}
diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileNameTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileNameTest.java
new file mode 100644
index 00000000..d1a792ee
--- /dev/null
+++ b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileNameTest.java
@@ -0,0 +1,12 @@
+package com.exasol.projectkeeper.validators.changesfile;
+
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class ChangesFileNameTest {
+ @Test
+ void equalsContractFilename() {
+ EqualsVerifier.forClass(ChangesFileName.class).verify();
+ }
+}
diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileSectionTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileSectionTest.java
new file mode 100644
index 00000000..929afba6
--- /dev/null
+++ b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileSectionTest.java
@@ -0,0 +1,13 @@
+package com.exasol.projectkeeper.validators.changesfile;
+
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class ChangesFileSectionTest {
+
+ @Test
+ void equalsContract() {
+ EqualsVerifier.forClass(ChangesFileSection.class).verify();
+ }
+}
diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileTest.java
new file mode 100644
index 00000000..d4b3ab20
--- /dev/null
+++ b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileTest.java
@@ -0,0 +1,61 @@
+package com.exasol.projectkeeper.validators.changesfile;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+
+import java.nio.file.Path;
+import java.time.LocalDate;
+
+import org.junit.jupiter.api.Test;
+
+import com.exasol.projectkeeper.validators.changesfile.ChangesFile.Builder;
+import com.jparams.verifier.tostring.ToStringVerifier;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class ChangesFileTest {
+
+ @Test
+ void equalsContract() {
+ EqualsVerifier.forClass(ChangesFile.class).verify();
+ }
+
+ @Test
+ void testToString() {
+ ToStringVerifier.forClass(ChangesFile.class).verify();
+ }
+
+ @Test
+ void getPathForVersion() {
+ assertThat(ChangesFile.getPathForVersion("1.2.3"), equalTo(Path.of("doc/changes/changes_1.2.3.md")));
+ }
+
+ @Test
+ void toBuilderCreatesCopy() {
+ final ChangesFile changesFile = builder().build();
+ final ChangesFile copy = changesFile.toBuilder().build();
+ assertThat(copy, equalTo(changesFile));
+ assertThat(changesFile.equals(copy), is(true));
+ }
+
+ private Builder builder() {
+ return ChangesFile.builder().projectName("name").projectVersion("1.2.3").releaseDate("2023-??-??")
+ .codeName("my code name")
+ .summary(ChangesFileSection.builder("## Summary").addLine("summary content").build())
+ .dependencyChangeSection(ChangesFileSection.builder("## Dependency Updates")
+ .addLine("dependency update content").build())
+ .addSection(ChangesFileSection.builder("section 1").build());
+ }
+
+ @Test
+ void getParsedReleaseDateValid() {
+ assertThat(builder().releaseDate("2024-01-29").build().getParsedReleaseDate().get(),
+ equalTo(LocalDate.of(2024, 1, 29)));
+ }
+
+ @Test
+ void getParsedReleaseDateInvalid() {
+ assertThat(builder().releaseDate("invalid").build().getParsedReleaseDate().isPresent(), is(false));
+ }
+}
diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidatorTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidatorTest.java
index 9cf9bf31..62234975 100644
--- a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidatorTest.java
+++ b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidatorTest.java
@@ -4,10 +4,8 @@
import static com.exasol.projectkeeper.HasNoMoreFindingsAfterApplyingFixesMatcher.hasNoMoreFindingsAfterApplyingFixes;
import static com.exasol.projectkeeper.HasValidationFindingWithMessageMatcher.hasNoValidationFindings;
import static com.exasol.projectkeeper.HasValidationFindingWithMessageMatcher.hasValidationFindingWithMessage;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -35,6 +33,7 @@
class ChangesFileValidatorTest {
private static final String A_VERSION = "1.2.3";
private static final String A_PROJECT_NAME = "my-project";
+ private static final String LINE_SEPARATOR = "\n";
@TempDir
Path tempDir;
@@ -55,12 +54,18 @@ void testValidationForSnapshotVersion() throws IOException {
}
@Test
- void noDepdendencyUpdates() throws IOException {
+ void noDependencyUpdates() throws IOException {
final AnalyzedMavenSource source = createTestSetup(new TestMavenModel(), Collections.emptyList());
final Logger log = mock(Logger.class);
createValidator(source).validate().forEach(finding -> new FindingsFixer(log).fixFindings(List.of(finding)));
final Path changesFile = this.tempDir.resolve(Path.of("doc", "changes", "changes_1.2.3.md"));
- assertThat(changesFile, hasContent(startsWith("# my-project 1.2.3, release")));
+ assertThat(changesFile,
+ hasContent(equalTo("# my-project 1.2.3, released 2024-??-??" + LINE_SEPARATOR + LINE_SEPARATOR //
+ + "Code name:" + LINE_SEPARATOR + LINE_SEPARATOR //
+ + "## Summary" + LINE_SEPARATOR + LINE_SEPARATOR //
+ + "## Features" + LINE_SEPARATOR + LINE_SEPARATOR //
+ + "* ISSUE_NUMBER: description" + LINE_SEPARATOR + LINE_SEPARATOR)));
+
verify(log).info("Created 'doc" + File.separator + "changes" + File.separator
+ "changes_1.2.3.md'. Don't forget to update its content!");
final List findings = createValidator(source).validate();
@@ -73,7 +78,15 @@ void testFixCreatedTemplate() throws IOException {
final Logger log = mock(Logger.class);
createValidator(source).validate().forEach(finding -> new FindingsFixer(log).fixFindings(List.of(finding)));
final Path changesFile = this.tempDir.resolve(Path.of("doc", "changes", "changes_1.2.3.md"));
- assertThat(changesFile, hasContent(startsWith("# my-project 1.2.3, release")));
+ assertThat(changesFile,
+ hasContent(equalTo("# my-project 1.2.3, released 2024-??-??" + LINE_SEPARATOR + LINE_SEPARATOR //
+ + "Code name:" + LINE_SEPARATOR + LINE_SEPARATOR //
+ + "## Summary" + LINE_SEPARATOR + LINE_SEPARATOR //
+ + "## Features" + LINE_SEPARATOR + LINE_SEPARATOR //
+ + "* ISSUE_NUMBER: description" + LINE_SEPARATOR + LINE_SEPARATOR //
+ + "## Dependency Updates" + LINE_SEPARATOR + LINE_SEPARATOR //
+ + "### Compile Dependency Updates" + LINE_SEPARATOR + LINE_SEPARATOR
+ + "* Added `com.example:my-lib:1.2.3`" + LINE_SEPARATOR)));
verify(log).info("Created 'doc" + File.separator + "changes" + File.separator
+ "changes_1.2.3.md'. Don't forget to update its content!");
}
@@ -85,7 +98,9 @@ void testFixContainsDependencyUpdates() throws IOException {
final AnalyzedMavenSource source = createTestSetup(model);
createValidator(source).validate().forEach(FindingFixHelper::fix);
final Path changesFile = this.tempDir.resolve(Path.of("doc", "changes", "changes_1.2.3.md"));
- assertThat(changesFile, hasContent(containsString("my-lib")));
+ assertThat(changesFile, hasContent(endsWith("## Dependency Updates" + LINE_SEPARATOR + LINE_SEPARATOR //
+ + "### Compile Dependency Updates" + LINE_SEPARATOR + LINE_SEPARATOR //
+ + "* Added `com.example:my-lib:1.2.3`" + LINE_SEPARATOR)));
}
@Test
@@ -117,4 +132,4 @@ private AnalyzedMavenSource createTestSetup(final TestMavenModel mavenModel,
.dependencyChanges(DependencyChangeReport.builder().typed(Type.COMPILE, dependencyChanges).build()) //
.build();
}
-}
\ No newline at end of file
+}
diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/DependencySectionFixerTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/DependencySectionFixerTest.java
index 90ca108e..d02f8e7f 100644
--- a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/DependencySectionFixerTest.java
+++ b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/DependencySectionFixerTest.java
@@ -2,8 +2,7 @@
import static com.exasol.projectkeeper.validators.changesfile.ChangesFile.DEPENDENCY_UPDATES_HEADING;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.*;
import java.util.List;
@@ -13,9 +12,10 @@
import com.exasol.projectkeeper.shared.dependencychanges.DependencyChangeReport;
import com.exasol.projectkeeper.shared.dependencychanges.NewDependency;
import com.exasol.projectkeeper.sources.AnalyzedMavenSource;
+import com.exasol.projectkeeper.validators.changesfile.ChangesFile.Builder;
@Tag("integration")
-//[utest->dsn~dependency-section-in-changes_x.x.x.md-file-validator~1]
+// [utest->dsn~dependency-section-in-changes_x.x.x.md-file-validator~1]
class DependencySectionFixerTest {
private static AnalyzedMavenSource source;
@@ -32,29 +32,43 @@ static void beforeAll() {
@Test
void testSectionIsAdded() {
- final ChangesFile changesFile = ChangesFile.builder().setHeader(List.of("heading")).build();
- final List sections = new DependencySectionFixer(List.of(source)).fix(changesFile)
- .getSections();
- assertThat(sections.size(), equalTo(1));
- assertThat(sections.get(0).getHeading(), equalTo(DEPENDENCY_UPDATES_HEADING));
+ final ChangesFile changesFile = changesFileBuilder().addSection(ChangesFileSection.builder("heading").build())
+ .build();
+ final ChangesFile fixedChangesFile = new DependencySectionFixer(List.of(source)).fix(changesFile);
+ assertThat(fixedChangesFile.getDependencyChangeSection().get().getHeading(),
+ equalTo(DEPENDENCY_UPDATES_HEADING));
+ }
+
+ private Builder changesFileBuilder() {
+ return ChangesFile.builder().projectName("projectName").projectVersion("1.2.3").releaseDate("releaseDate")
+ .summary(ChangesFileSection.builder("## Summary").build());
}
@Test
void testSectionIsUpdated() {
- final ChangesFile changesFile = ChangesFile.builder().setHeader(List.of("heading"))
- .addSection(List.of(DEPENDENCY_UPDATES_HEADING, "myLine")).build();
+ final ChangesFile changesFile = changesFileBuilder().dependencyChangeSection(
+ ChangesFileSection.builder("## Dependency Updates").addLine("content will be overwritten").build())
+ .build();
final ChangesFile fixedChangesFile = new DependencySectionFixer(List.of(source)).fix(changesFile);
- final List sections = fixedChangesFile.getSections();
- assertThat(sections.size(), equalTo(1));
- assertThat(sections.get(0).getHeading(), equalTo(DEPENDENCY_UPDATES_HEADING));
- assertThat("dependency fixer changed the changes file", changesFile, not(equalTo(fixedChangesFile)));
+ final ChangesFileSection changesFileSection = fixedChangesFile.getDependencyChangeSection().get();
+ assertThat(changesFileSection.getHeading(), equalTo(DEPENDENCY_UPDATES_HEADING));
+ assertThat(changesFileSection.getContent(),
+ contains("", "### Compile Dependency Updates", "", "* Added `com.example:my-lib:1.2.3`"));
+ assertThat("dependency fixer changed the changes file", changesFile,
+ allOf(not(equalTo(fixedChangesFile)), not(sameInstance(fixedChangesFile))));
}
@Test
- void testHeaderIsPreserved() {
- final ChangesFile changesFile = ChangesFile.builder().setHeader(List.of("heading"))
- .addSection(List.of(DEPENDENCY_UPDATES_HEADING, "myLine")).build();
- final ChangesFile fixedChangesFile = new DependencySectionFixer(List.of(source)).fix(changesFile);
- assertThat(changesFile.getHeaderSectionLines(), equalTo(fixedChangesFile.getHeaderSectionLines()));
+ void testDependencySectionIsRemoved() {
+ final ChangesFile changesFile = changesFileBuilder().addSection(ChangesFileSection.builder("heading").build())
+ .dependencyChangeSection(ChangesFileSection.builder("## Dependency Updates")
+ .addLine("content will be preserved").build())
+ .build();
+ final ChangesFile fixedChangesFile = new DependencySectionFixer(List.of()).fix(changesFile);
+
+ assertThat(fixedChangesFile.getDependencyChangeSection().isPresent(), is(false));
+ assertThat(fixedChangesFile, not(sameInstance(changesFile)));
+ assertThat("dependency fixer changed the changes file", changesFile,
+ allOf(not(equalTo(fixedChangesFile)), not(sameInstance(fixedChangesFile))));
}
-}
\ No newline at end of file
+}
diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/dependencies/DependencyChangeReportRendererTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/dependencies/DependencyChangeReportRendererTest.java
index 6ad5cde4..12eb1942 100644
--- a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/dependencies/DependencyChangeReportRendererTest.java
+++ b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/dependencies/DependencyChangeReportRendererTest.java
@@ -1,16 +1,19 @@
package com.exasol.projectkeeper.validators.changesfile.dependencies;
+import static java.util.Arrays.asList;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.emptyString;
import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
import java.util.List;
+import java.util.Optional;
import org.junit.jupiter.api.Test;
import com.exasol.projectkeeper.shared.dependencies.BaseDependency.Type;
import com.exasol.projectkeeper.shared.dependencychanges.DependencyChangeReport;
import com.exasol.projectkeeper.shared.dependencychanges.NewDependency;
+import com.exasol.projectkeeper.validators.changesfile.ChangesFileSection;
import com.exasol.projectkeeper.validators.changesfile.NamedDependencyChangeReport;
class DependencyChangeReportRendererTest {
@@ -22,17 +25,15 @@ class DependencyChangeReportRendererTest {
@Test
void testRenderSingleSourceReport() {
final NamedDependencyChangeReport namedReport = new NamedDependencyChangeReport("my-project", REPORT);
- final String result = String.join("\n", new DependencyChangeReportRenderer().render(List.of(namedReport)));
- assertThat(result, equalTo("## Dependency Updates\n" + "\n" + "### Compile Dependency Updates\n" + "\n"
- + "* Added `com.example:my-lib:1.2.3`"));
+ assertThat(render(namedReport), equalTo("## Dependency Updates\n" + "\n" + "### Compile Dependency Updates\n"
+ + "\n" + "* Added `com.example:my-lib:1.2.3`"));
}
@Test
void testRenderMultiSourceReport() {
final NamedDependencyChangeReport sourceA = new NamedDependencyChangeReport("project A", REPORT);
final NamedDependencyChangeReport sourceB = new NamedDependencyChangeReport("project B", REPORT);
- final String result = String.join("\n", new DependencyChangeReportRenderer().render(List.of(sourceA, sourceB)));
- assertThat(result, equalTo(
+ assertThat(render(sourceA, sourceB), equalTo(
"## Dependency Updates\n\n### Project A\n\n#### Compile Dependency Updates\n\n* Added `com.example:my-lib:1.2.3`\n\n### Project B\n\n#### Compile Dependency Updates\n\n* Added `com.example:my-lib:1.2.3`"));
}
@@ -40,15 +41,20 @@ void testRenderMultiSourceReport() {
void testRenderMultiSourceReportWithNoChangesInOneReport() {
final NamedDependencyChangeReport sourceA = new NamedDependencyChangeReport("project A", REPORT);
final NamedDependencyChangeReport sourceB = new NamedDependencyChangeReport("project B", EMPTY_REPORT);
- final String result = String.join("\n", new DependencyChangeReportRenderer().render(List.of(sourceA, sourceB)));
- assertThat(result, equalTo(
+ assertThat(render(sourceA, sourceB), equalTo(
"## Dependency Updates\n\n### Project A\n\n#### Compile Dependency Updates\n\n* Added `com.example:my-lib:1.2.3`"));
}
@Test
void testRenderSourceReportWithoutChanges() {
final NamedDependencyChangeReport sourceA = new NamedDependencyChangeReport("project A", EMPTY_REPORT);
- final String result = String.join("\n", new DependencyChangeReportRenderer().render(List.of(sourceA)));
- assertThat(result, emptyString());
+ final Optional result = new DependencyChangeReportRenderer().render(List.of(sourceA));
+ assertThat(result.isPresent(), is(false));
}
-}
\ No newline at end of file
+
+ private String render(final NamedDependencyChangeReport... reports) {
+ final Optional section = new DependencyChangeReportRenderer().render(asList(reports));
+ assertThat(section.isPresent(), is(true));
+ return section.get().toString();
+ }
+}
diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/files/LatestChangesFileValidatorTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/files/LatestChangesFileValidatorTest.java
index a030479a..64ba39c5 100644
--- a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/files/LatestChangesFileValidatorTest.java
+++ b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/files/LatestChangesFileValidatorTest.java
@@ -11,7 +11,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-import com.exasol.projectkeeper.validators.changesfile.ChangesFile;
+import com.exasol.projectkeeper.validators.changesfile.ChangesFileName;
import com.exasol.projectkeeper.validators.finding.SimpleValidationFinding;
class LatestChangesFileValidatorTest {
@@ -38,8 +38,8 @@ private LatestChangesFileValidator testee(final Path tempDir, final String... ve
final Path folder = tempDir.resolve(Path.of("doc", "changes"));
Files.createDirectories(folder);
for (final String v : versions) {
- final ChangesFile.Filename cfile = new ChangesFile.Filename(v);
- Files.createFile(folder.resolve(cfile.filename()));
+ final ChangesFileName file = new ChangesFileName(v);
+ Files.createFile(folder.resolve(file.filename()));
}
return new LatestChangesFileValidator(tempDir, "2.0.0");
}