From 8de3aaeaad016d37b58fc3a4700ae86919e97130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ch=CF=80?= Date: Wed, 1 Jun 2022 13:39:05 +0200 Subject: [PATCH] #314 Analyzing golang modules in sub-directory fails (#318) * #314: Fix issues with relative source paths * Add tests with git tag * Fix issues with absolute/relative source paths --- doc/changes/changes_2.4.5.md | 7 +- .../GolangDependencyChangeCalculator.java | 10 +- .../analyze/golang/GolangServices.java | 26 ++--- .../analyze/golang/GolangSourceAnalyzer.java | 35 ++++-- .../sources/analyze/golang/SimpleProcess.java | 4 +- .../stream/CollectingConsumer.java | 24 ++-- .../analyze/golang/GolangServicesTest.java | 9 +- .../golang/GolangSourceAnalyzerIT.java | 103 +++++++++++++----- .../stream/CollectingConsumerTest.java | 35 +++--- .../test/GolangProjectFixture.java | 68 ++++++++---- 10 files changed, 212 insertions(+), 109 deletions(-) diff --git a/doc/changes/changes_2.4.5.md b/doc/changes/changes_2.4.5.md index dbacaf0f..740dc701 100644 --- a/doc/changes/changes_2.4.5.md +++ b/doc/changes/changes_2.4.5.md @@ -1,16 +1,13 @@ -# Project Keeper 2.4.5, released 2022-??-?? +# Project Keeper 2.4.5, released 2022-05-01 Code name: Bugfixes for Golang projects -## Summary - -## Features - ## Bugfixes * #279: Fixed getting license for Golang test dependencies * #316: Added new builtin replacements to BrokenLinkReplacer * #313: Fixed labelling of of dependency change type +* #314: Fixed issues with Golang modules in sub-directories ## Dependency Updates diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/GolangDependencyChangeCalculator.java b/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/GolangDependencyChangeCalculator.java index 57506a48..cb05c9e5 100644 --- a/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/GolangDependencyChangeCalculator.java +++ b/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/GolangDependencyChangeCalculator.java @@ -35,7 +35,7 @@ static DependencyChangeReport calculateDepencencyChanges(final GolangServices go } private DependencyChangeReport calculate() { - this.changes = this.golangServices.getDependencyChanges(this.projectDir, this.source.getPath()); + this.changes = this.golangServices.getDependencyChanges(this.projectDir, getRelativeModPath()); final DependencyChangeReport dependencyChanges = new DependencyChangeReport(); dependencyChanges.setCompileDependencyChanges(getDependencyChanges(Type.COMPILE)); dependencyChanges.setPluginDependencyChanges(getDependencyChanges(Type.PLUGIN)); @@ -44,6 +44,14 @@ private DependencyChangeReport calculate() { return dependencyChanges; } + private Path getRelativeModPath() { + if (this.source.getPath().isAbsolute()) { + return this.projectDir.relativize(this.source.getPath()); + } else { + return this.source.getPath(); + } + } + private List getDependencyChanges(final Type type) { return this.changes.stream().filter(change -> getType(change) == type).collect(toList()); } diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/GolangServices.java b/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/GolangServices.java index af56bc13..0f4dcf29 100644 --- a/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/GolangServices.java +++ b/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/GolangServices.java @@ -35,7 +35,7 @@ class GolangServices { private static final Logger LOGGER = Logger.getLogger(GolangServices.class.getName()); private static final List COMMAND_LIST_DIRECT_DEPDENDENCIES = List.of("go", "list", "-f", "{{if not .Indirect}}{{.}}{{end}}", "-m", "all"); - private static final Duration EXECUTION_TIMEOUT = Duration.ofSeconds(5); + private static final Duration EXECUTION_TIMEOUT = Duration.ofSeconds(30); private final Supplier projectVersion; @@ -64,10 +64,10 @@ private static String extractVersion(final ProjectKeeperConfig config) { } } - Map getLicenses(final Path projectPath, final String module) { + Map getLicenses(final Path absoluteSourcePath, final String module) { final SimpleProcess process; try { - process = GoProcess.start(projectPath, List.of("go-licenses", "csv", module)); + process = GoProcess.start(absoluteSourcePath, List.of("go-licenses", "csv", module)); } catch (final IllegalStateException exception) { throw new IllegalStateException(ExaError.messageBuilder("E-PK-CORE-142") .message("Error starting the 'go-licenses' binary.") @@ -100,11 +100,11 @@ private GolangDependencyLicense convertDependencyLicense(final String line) { /** * Get information about the Golang module with it's dependencies. * - * @param projectPath the project path containing the {@code go.mod} file + * @param absoluteSourcePath the project path containing the {@code go.mod} file * @return module information */ - ModuleInfo getModuleInfo(final Path projectPath) { - final SimpleProcess process = SimpleProcess.start(projectPath, COMMAND_LIST_DIRECT_DEPDENDENCIES); + ModuleInfo getModuleInfo(final Path absoluteSourcePath) { + final SimpleProcess process = SimpleProcess.start(absoluteSourcePath, COMMAND_LIST_DIRECT_DEPDENDENCIES); process.waitUntilFinished(EXECUTION_TIMEOUT); final String[] output = process.getOutputStreamContent().split("\n"); final List dependencies = Arrays.stream(output) // @@ -131,15 +131,15 @@ private Dependency convertDependency(final String line) { /** * Get a list of {@link DependencyChange}s in the given {@code go.mod} file since the latest release. * - * @param projectDir the project root dir containing the {@code .git} directory - * @param modFile the absolute path to the {@code go.mod} file + * @param projectDir the project root dir containing the {@code .git} directory + * @param relativeModFile the absolute path to the {@code go.mod} file * @return the list of {@link DependencyChange}s */ // [impl -> dsn~golang-changed-dependency~1] - List getDependencyChanges(final Path projectDir, final Path modFile) { - final Optional lastReleaseModFile = getLastReleaseModFileContent(projectDir, modFile) + List getDependencyChanges(final Path projectDir, final Path relativeModFile) { + final Optional lastReleaseModFile = getLastReleaseModFileContent(projectDir, relativeModFile) .map(GoModFile::parse); - final GoModFile currentModFile = GoModFile.parse(readFile(modFile)); + final GoModFile currentModFile = GoModFile.parse(readFile(projectDir.resolve(relativeModFile))); return calculateChanges(lastReleaseModFile.orElse(null), currentModFile); } @@ -166,9 +166,7 @@ List calculateChanges(final GoModFile oldModFile, final GoModF private Optional getLastReleaseModFileContent(final Path projectDir, final Path modFile) { try (GitRepository repo = GitRepository.open(projectDir)) { - final Path relativeModFilePath = projectDir.relativize(modFile); - return repo.findLatestReleaseCommit(getProjectVersion()) - .map(tag -> getContent(repo, relativeModFilePath, tag)); + return repo.findLatestReleaseCommit(getProjectVersion()).map(tag -> getContent(repo, modFile, tag)); } } diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/GolangSourceAnalyzer.java b/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/GolangSourceAnalyzer.java index f927fcaa..d7791e37 100644 --- a/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/GolangSourceAnalyzer.java +++ b/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/GolangSourceAnalyzer.java @@ -41,26 +41,45 @@ public List analyze(final Path projectDir, final List so private AnalyzedSource analyzeSource(final Path projectDir, final Source source) { validateGolangSource(source); - final Path sourceDir = projectDir.resolve(source.getPath().getParent()); - final boolean isRoot = projectDir.relativize(source.getPath()).equals(Path.of("go.mod")); - final Path moduleDir = source.getPath().getParent(); - final ModuleInfo moduleInfo = this.golangServices.getModuleInfo(moduleDir); - final String projectName = source.getPath().normalize().getParent().getFileName().toString(); + final Path absoluteSourceDir = getAbsoluteSourceDir(projectDir, source); + final ModuleInfo moduleInfo = this.golangServices.getModuleInfo(absoluteSourceDir); final ProjectDependencies dependencies = GolangDependencyCalculator.calculateDependencies(this.golangServices, - sourceDir, moduleInfo); + absoluteSourceDir, moduleInfo); return AnalyzedGolangSource.builder() // .version(this.golangServices.getProjectVersion()) // - .isRootProject(isRoot) // + .isRootProject(isRootSource(source)) // .advertise(source.isAdvertise()) // .modules(source.getModules()) // .path(source.getPath()) // - .projectName(projectName).moduleName(moduleInfo.getModuleName()) // + .projectName(getProjectName(projectDir, source)) // + .moduleName(moduleInfo.getModuleName()) // .dependencies(dependencies) // .dependencyChanges(GolangDependencyChangeCalculator.calculateDepencencyChanges(this.golangServices, projectDir, source, dependencies)) // .build(); } + private String getProjectName(final Path projectDir, final Source source) { + if (isRootSource(source)) { + return projectDir.getFileName().toString(); + } else { + return source.getPath().getParent().getFileName().toString(); + } + } + + private boolean isRootSource(final Source source) { + return source.getPath().getParent() == null; + } + + private Path getAbsoluteSourceDir(final Path projectDir, final Source source) { + final Path sourceDir = source.getPath().getParent(); + if (sourceDir != null) { + return projectDir.resolve(sourceDir); + } else { + return projectDir; + } + } + private void validateGolangSource(final Source source) { if (source.getType() != SourceType.GOLANG) { throw new IllegalStateException(ExaError.messageBuilder("F-PK-CORE-130") diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/SimpleProcess.java b/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/SimpleProcess.java index 3738dd30..2b2811c3 100644 --- a/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/SimpleProcess.java +++ b/project-keeper/src/main/java/com/exasol/projectkeeper/sources/analyze/golang/SimpleProcess.java @@ -103,7 +103,7 @@ public void waitUntilFinished(final Duration executionTimeout) { */ public String getOutputStreamContent() { try { - return this.outputStreamConsumer.getContent(Duration.ofMillis(100)); + return this.outputStreamConsumer.getContent(Duration.ofMillis(500)); } catch (final InterruptedException exception) { throw handleInterruptedException(exception); } @@ -116,7 +116,7 @@ public String getOutputStreamContent() { */ public String getErrorStreamContent() { try { - return this.errorStreamConsumer.getContent(Duration.ofMillis(100)); + return this.errorStreamConsumer.getContent(Duration.ofMillis(500)); } catch (final InterruptedException exception) { throw handleInterruptedException(exception); } diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/stream/CollectingConsumer.java b/project-keeper/src/main/java/com/exasol/projectkeeper/stream/CollectingConsumer.java index 74e0026b..4cc8c2c1 100644 --- a/project-keeper/src/main/java/com/exasol/projectkeeper/stream/CollectingConsumer.java +++ b/project-keeper/src/main/java/com/exasol/projectkeeper/stream/CollectingConsumer.java @@ -16,34 +16,34 @@ public class CollectingConsumer implements StreamConsumer { private final StringBuilder stringBuilder = new StringBuilder(); @Override - public void accept(String line) { - stringBuilder.append(line).append("\n"); + public void accept(final String line) { + this.stringBuilder.append(line).append("\n"); } @Override public void readFinished() { - countDownLatch.countDown(); + this.countDownLatch.countDown(); } @Override - public void readFailed(IOException exception) { - countDownLatch.countDown(); + public void readFailed(final IOException exception) { + this.countDownLatch.countDown(); } /** * Waits until the stream was read completely and returns the read content from the stream. - * + * * @param timeout the maximum time to wait * @return the content collected from the stream * @throws InterruptedException if the current thread is interrupted while waiting */ - public String getContent(Duration timeout) throws InterruptedException { - boolean result = countDownLatch.await(timeout.toMillis(), TimeUnit.MILLISECONDS); + public String getContent(final Duration timeout) throws InterruptedException { + final boolean result = this.countDownLatch.await(timeout.toMillis(), TimeUnit.MILLISECONDS); if (!result) { - throw new IllegalStateException(ExaError.messageBuilder("E-PK-CORE-99") - .message("Stream reading did not finish after timeout of {{timeout}}", timeout).ticketMitigation() - .toString()); + throw new IllegalStateException(ExaError.messageBuilder("E-PK-CORE-99").message( + "Stream reading did not finish after timeout of {{timeout}}. Content collected until now: {{content}}.", + timeout, this.stringBuilder.toString()).ticketMitigation().toString()); } - return stringBuilder.toString(); + return this.stringBuilder.toString(); } } \ No newline at end of file diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/sources/analyze/golang/GolangServicesTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/sources/analyze/golang/GolangServicesTest.java index f5d6e5e8..2ae38aeb 100644 --- a/project-keeper/src/test/java/com/exasol/projectkeeper/sources/analyze/golang/GolangServicesTest.java +++ b/project-keeper/src/test/java/com/exasol/projectkeeper/sources/analyze/golang/GolangServicesTest.java @@ -2,8 +2,7 @@ import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.assertAll; import java.util.List; @@ -81,6 +80,12 @@ void calculateChangesIgnoresIndirectDependencies() { modFile(indirectDep("updated", "v2"), indirectDep("added", "v4"))); } + @Test + void getVersion() { + final GolangServices golangServices = new GolangServices(() -> PROJECT_VERSION); + assertThat(golangServices.getProjectVersion(), equalTo(PROJECT_VERSION)); + } + private void assertChanges(final GoModFile oldMod, final GoModFile newMod, final DependencyChange... expectedChanges) { final GolangServices golangServices = new GolangServices(() -> PROJECT_VERSION); diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/sources/analyze/golang/GolangSourceAnalyzerIT.java b/project-keeper/src/test/java/com/exasol/projectkeeper/sources/analyze/golang/GolangSourceAnalyzerIT.java index e21faef7..64b5649d 100644 --- a/project-keeper/src/test/java/com/exasol/projectkeeper/sources/analyze/golang/GolangSourceAnalyzerIT.java +++ b/project-keeper/src/test/java/com/exasol/projectkeeper/sources/analyze/golang/GolangSourceAnalyzerIT.java @@ -8,10 +8,10 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.junit.jupiter.api.io.TempDir; import com.exasol.projectkeeper.shared.config.ProjectKeeperConfig; @@ -19,8 +19,8 @@ import com.exasol.projectkeeper.shared.dependencies.License; import com.exasol.projectkeeper.shared.dependencies.ProjectDependency; import com.exasol.projectkeeper.shared.dependencies.ProjectDependency.Type; -import com.exasol.projectkeeper.shared.dependencychanges.DependencyChangeReport; -import com.exasol.projectkeeper.shared.dependencychanges.NewDependency; +import com.exasol.projectkeeper.shared.dependencychanges.*; +import com.exasol.projectkeeper.sources.AnalyzedGolangSource; import com.exasol.projectkeeper.sources.AnalyzedSource; import com.exasol.projectkeeper.test.GolangProjectFixture; @@ -37,6 +37,11 @@ void setup() { this.fixture.gitInit(); } + @AfterEach + void teardown() { + this.fixture.close(); + } + @Test void testInvalidPath() { final List sources = List.of(Source.builder().type(GOLANG).path(this.projectDir).build()); @@ -50,7 +55,7 @@ void testInvalidPath() { @Test void testMissingVersionConfig() { this.fixture.prepareProjectFiles(); - final ProjectKeeperConfig config = createDefaultConfigWithAbsolutePath().versionConfig(null).build(); + final ProjectKeeperConfig config = this.fixture.createDefaultConfig().versionConfig(null).build(); final IllegalStateException exception = assertThrows(IllegalStateException.class, () -> analyzeSingleProject(config)); assertThat(exception.getMessage(), equalTo( @@ -60,7 +65,7 @@ void testMissingVersionConfig() { @Test void testWrongVersionConfigType() { this.fixture.prepareProjectFiles(); - final ProjectKeeperConfig config = createDefaultConfigWithAbsolutePath() + final ProjectKeeperConfig config = this.fixture.createDefaultConfig() .versionConfig(new VersionFromSource(this.projectDir.resolve("file"))).build(); final IllegalStateException exception = assertThrows(IllegalStateException.class, () -> analyzeSingleProject(config)); @@ -69,27 +74,79 @@ void testWrongVersionConfigType() { } @Test - void testGetDependencyLicenses() { - this.fixture.prepareProjectFiles(); - final ProjectKeeperConfig config = createDefaultConfigWithAbsolutePath().build(); + void testGoModuleInProjectDirectory() { + prepareProjectFiles(Paths.get(".")); + final ProjectKeeperConfig config = this.fixture.createDefaultConfig().build(); + final AnalyzedSource analyzedProject = analyzeSingleProject(config); + assertAll( // + () -> assertCommonProperties(analyzedProject), // + () -> assertIsrootProject(analyzedProject, true), // + () -> assertThat("project path", analyzedProject.getPath(), equalTo(Paths.get("go.mod"))), + () -> assertThat("project name", analyzedProject.getProjectName(), + equalTo(this.projectDir.getFileName().toString()))); + } + + @Test + void testGoModuleInSubdirectory() { + prepareProjectFiles(Paths.get("subdir")); + final ProjectKeeperConfig config = ProjectKeeperConfig.builder() + .sources(List.of(ProjectKeeperConfig.Source.builder().modules(emptySet()).type(SourceType.GOLANG) + .path(Paths.get("subdir").resolve("go.mod")).build())) + .versionConfig(new ProjectKeeperConfig.FixedVersion(this.fixture.getProjectVersion())).build(); + final AnalyzedSource analyzedProject = analyzeSingleProject(config); + assertAll( // + () -> assertCommonProperties(analyzedProject), // + () -> assertIsrootProject(analyzedProject, false), + () -> assertThat("project path", analyzedProject.getPath(), equalTo(Paths.get("subdir/go.mod"))), + () -> assertThat("project name", analyzedProject.getProjectName(), equalTo("subdir"))); + } + + @Test + void testWithAbsoluteSourcePath() { + prepareProjectFiles(Paths.get("subdir")); + final Path modPath = this.projectDir.resolve("subdir/go.mod"); + final ProjectKeeperConfig config = ProjectKeeperConfig.builder() + .sources(List.of(ProjectKeeperConfig.Source.builder().modules(emptySet()).type(SourceType.GOLANG) + .path(modPath).build())) + .versionConfig(new ProjectKeeperConfig.FixedVersion(this.fixture.getProjectVersion())).build(); final AnalyzedSource analyzedProject = analyzeSingleProject(config); + assertAll( // + () -> assertCommonProperties(analyzedProject), // + () -> assertIsrootProject(analyzedProject, false), + () -> assertThat("project path", analyzedProject.getPath(), equalTo(modPath)), + () -> assertThat("project name", analyzedProject.getProjectName(), equalTo("subdir"))); + } + + private void prepareProjectFiles(final Path moduleDir) { + this.fixture.prepareProjectFiles(moduleDir, "1.15"); + this.fixture.gitAddCommitTag("1.2.2"); + this.fixture.prepareProjectFiles(moduleDir, "1.16"); + } + + private void assertCommonProperties(final AnalyzedSource analyzedProject) { assertAll( // () -> assertDependencyLicenses(analyzedProject.getDependencies().getDependencies()), - () -> assertDependencyChanges(analyzedProject.getDependencyChanges())); + () -> assertDependencyChanges(analyzedProject.getDependencyChanges()), + () -> assertThat("module name", ((AnalyzedGolangSource) analyzedProject).getModuleName(), + equalTo("github.com/exasol/my-module")), + () -> assertThat("advertise", analyzedProject.isAdvertise(), is(true)), + () -> assertThat("version", analyzedProject.getVersion(), equalTo(this.fixture.getProjectVersion()))); + } + + private void assertIsrootProject(final AnalyzedSource analyzedProject, final boolean expectedValue) { + assertThat("is root project", ((AnalyzedGolangSource) analyzedProject).isRootProject(), is(expectedValue)); } private void assertDependencyChanges(final DependencyChangeReport dependencyChanges) { assertAll(() -> assertThat("plugin dependencies", dependencyChanges.getPluginDependencyChanges(), hasSize(0)), () -> assertThat("runtime dependencies", dependencyChanges.getRuntimeDependencyChanges(), hasSize(0)), () -> assertThat("compile dependencies", dependencyChanges.getCompileDependencyChanges(), - contains(newDep("golang", "1.17"), newDep("github.com/exasol/exasol-driver-go", "v0.4.0"))), - () -> assertThat("test dependencies", dependencyChanges.getTestDependencyChanges(), - contains(newDep("github.com/exasol/exasol-test-setup-abstraction-server/go-client", - "v0.0.0-20220520062645-0dd00179907c")))); + contains(updatedDep("golang", "1.15", "1.16"))), + () -> assertThat("test dependencies", dependencyChanges.getTestDependencyChanges(), hasSize(0))); } - private NewDependency newDep(final String name, final String version) { - return new NewDependency(null, name, version); + private DependencyChange updatedDep(final String name, final String oldVersion, final String newVersion) { + return new UpdatedDependency(null, name, oldVersion, newVersion); } private void assertDependencyLicenses(final List dependencies) { @@ -105,24 +162,10 @@ private void assertDependencyLicenses(final List dependencies () -> assertThat(dependencies, contains(dependency1, dependency2))); } - @Test - void testGetProjectVersion() { - this.fixture.prepareProjectFiles(); - final ProjectKeeperConfig config = createDefaultConfigWithAbsolutePath().build(); - assertThat(analyzeSingleProject(config).getVersion(), equalTo("1.2.3")); - } - private AnalyzedSource analyzeSingleProject(final ProjectKeeperConfig config) { final GolangSourceAnalyzer analyzer = new GolangSourceAnalyzer(config); final List analyzedSources = analyzer.analyze(this.projectDir, config.getSources()); assertThat(analyzedSources, hasSize(1)); return analyzedSources.get(0); } - - private ProjectKeeperConfig.ProjectKeeperConfigBuilder createDefaultConfigWithAbsolutePath() { - return ProjectKeeperConfig.builder() - .sources(List.of(ProjectKeeperConfig.Source.builder().modules(emptySet()).type(SourceType.GOLANG) - .path(this.projectDir.resolve("go.mod")).build())) - .versionConfig(new ProjectKeeperConfig.FixedVersion("1.2.3")); - } } \ No newline at end of file diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/stream/CollectingConsumerTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/stream/CollectingConsumerTest.java index 2c8851d8..db7e4fac 100644 --- a/project-keeper/src/test/java/com/exasol/projectkeeper/stream/CollectingConsumerTest.java +++ b/project-keeper/src/test/java/com/exasol/projectkeeper/stream/CollectingConsumerTest.java @@ -16,47 +16,56 @@ class CollectingConsumerTest { @BeforeEach void setup() { - collectingConsumer = new CollectingConsumer(); + this.collectingConsumer = new CollectingConsumer(); } @Test void getEmptyContentReturnsEmptyString() throws InterruptedException { - collectingConsumer.readFinished(); + this.collectingConsumer.readFinished(); assertThat(getcontent(), equalTo("")); } @Test void getSingleLineContent() throws InterruptedException { - collectingConsumer.accept("line1"); - collectingConsumer.readFinished(); + this.collectingConsumer.accept("line1"); + this.collectingConsumer.readFinished(); assertThat(getcontent(), equalTo("line1\n")); } @Test void getMultiLineContent() throws InterruptedException { - collectingConsumer.accept("line1"); - collectingConsumer.accept("line2"); - collectingConsumer.accept("line3"); - collectingConsumer.readFinished(); + this.collectingConsumer.accept("line1"); + this.collectingConsumer.accept("line2"); + this.collectingConsumer.accept("line3"); + this.collectingConsumer.readFinished(); assertThat(getcontent(), equalTo("line1\nline2\nline3\n")); } @Test void getContentAfterFailure() throws InterruptedException { - collectingConsumer.accept("line1"); - collectingConsumer.readFailed(null); + this.collectingConsumer.accept("line1"); + this.collectingConsumer.readFailed(null); assertThat(getcontent(), equalTo("line1\n")); } @Test void getContentWithoutFinishFails() throws InterruptedException { - IllegalStateException exception = assertThrows(IllegalStateException.class, () -> getcontent()); + final IllegalStateException exception = assertThrows(IllegalStateException.class, () -> getcontent()); assertThat(exception.getMessage(), equalTo("E-PK-CORE-99: Stream reading did not finish after timeout of " + TIMEOUT - + " This is an internal error that should not happen. Please report it by opening a GitHub issue.")); + + ". Content collected until now: ''. This is an internal error that should not happen. Please report it by opening a GitHub issue.")); + } + + @Test + void getContentWithoutFinishAddsCollectedContentToExceptionMessage() throws InterruptedException { + this.collectingConsumer.accept("line1"); + final IllegalStateException exception = assertThrows(IllegalStateException.class, () -> getcontent()); + assertThat(exception.getMessage(), equalTo("E-PK-CORE-99: Stream reading did not finish after timeout of " + + TIMEOUT + + ". Content collected until now: 'line1\n'. This is an internal error that should not happen. Please report it by opening a GitHub issue.")); } private String getcontent() throws InterruptedException { - return collectingConsumer.getContent(TIMEOUT); + return this.collectingConsumer.getContent(TIMEOUT); } } diff --git a/shared-test-setup/src/main/java/com/exasol/projectkeeper/test/GolangProjectFixture.java b/shared-test-setup/src/main/java/com/exasol/projectkeeper/test/GolangProjectFixture.java index f448251f..c995872d 100644 --- a/shared-test-setup/src/main/java/com/exasol/projectkeeper/test/GolangProjectFixture.java +++ b/shared-test-setup/src/main/java/com/exasol/projectkeeper/test/GolangProjectFixture.java @@ -4,8 +4,7 @@ import java.io.*; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; +import java.nio.file.*; import java.time.Duration; import java.util.Arrays; import java.util.List; @@ -18,7 +17,7 @@ import com.exasol.projectkeeper.shared.config.ProjectKeeperConfig.ProjectKeeperConfigBuilder; import com.exasol.projectkeeper.shared.config.ProjectKeeperConfig.SourceType; -public class GolangProjectFixture { +public class GolangProjectFixture implements AutoCloseable { private static final Duration PROCESS_TIMEOUT = Duration.ofSeconds(120); private static final String GO_MOD_FILE_NAME = "go.mod"; private static final String GO_MODULE_NAME = "github.com/exasol/my-module"; @@ -26,6 +25,7 @@ public class GolangProjectFixture { private static final String PROJECT_VERSION = "1.2.3"; private final Path projectDir; + private Git gitRepo; public GolangProjectFixture(final Path projectDir) { this.projectDir = projectDir; @@ -33,9 +33,19 @@ public GolangProjectFixture(final Path projectDir) { public void gitInit() { try { - Git.init().setDirectory(this.projectDir.toFile()).call().close(); + this.gitRepo = Git.init().setDirectory(this.projectDir.toFile()).setInitialBranch("main").call(); } catch (IllegalStateException | GitAPIException exception) { - throw new AssertionError("Error running git init", exception); + throw new AssertionError("Error running git init: " + exception.getMessage(), exception); + } + } + + public void gitAddCommitTag(final String tagName) { + try { + this.gitRepo.add().addFilepattern(".").call(); + this.gitRepo.commit().setMessage("Prepare release " + tagName).call(); + this.gitRepo.tag().setName(tagName).call(); + } catch (final GitAPIException exception) { + throw new AssertionError("Error running git add/commit/tag: " + exception.getMessage(), exception); } } @@ -52,18 +62,22 @@ public void prepareProjectFiles(final ProjectKeeperConfigBuilder configBuilder) } public void prepareProjectFiles() { - writeGoModFile(); - writeMainGoFile(); - writeTestGoFile(); - splitAndExecute("go get"); - splitAndExecute("go mod tidy"); + prepareProjectFiles(Paths.get("."), GO_VERSION); + } + + public void prepareProjectFiles(final Path moduleDir, final String goVersion) { + writeGoModFile(moduleDir, goVersion); + writeMainGoFile(moduleDir); + writeTestGoFile(moduleDir); + splitAndExecute(moduleDir, "go get"); + splitAndExecute(moduleDir, "go mod tidy"); } public String getProjectVersion() { return PROJECT_VERSION; } - private void writeConfig(final ProjectKeeperConfig.ProjectKeeperConfigBuilder config) { + public void writeConfig(final ProjectKeeperConfig.ProjectKeeperConfigBuilder config) { new ProjectKeeperConfigWriter().writeConfig(config.build(), this.projectDir); } @@ -71,27 +85,30 @@ void initializeGitRepo() throws GitAPIException { Git.init().setDirectory(this.projectDir.toFile()).call().close(); } - private void writeGoModFile() { + private void writeGoModFile(final Path moduleDir, final String goVersion) { final List dependencies = List.of("github.com/exasol/exasol-driver-go v0.3.1", "github.com/exasol/exasol-test-setup-abstraction-server/go-client v0.0.0-20220520062645-0dd00179907c", "github.com/exasol/error-reporting-go v0.1.1 // indirect"); final String content = "module " + GO_MODULE_NAME + "\n" // - + "go " + GO_VERSION + "\n" // + + "go " + goVersion + "\n" // + "require (\n" // + "\t" + String.join("\n\t", dependencies) + "\n" // + ")\n"; - writeFile(this.projectDir.resolve(GO_MOD_FILE_NAME), content); + writeFile(this.projectDir.resolve(moduleDir).resolve(GO_MOD_FILE_NAME), content); } private void writeFile(final Path path, final String content) { try { + if (!Files.exists(path.getParent())) { + Files.createDirectories(path.getParent()); + } Files.writeString(path, content); } catch (final IOException exception) { throw new UncheckedIOException("Error writing content to file " + path, exception); } } - private void writeMainGoFile() { + private void writeMainGoFile(final Path moduleDir) { final String content = "package main\n" // + "import (\n" // + " \"github.com/exasol/exasol-driver-go\"\n" // @@ -99,27 +116,27 @@ private void writeMainGoFile() { + "func main() {\n" // + " exasol.NewConfig(\"sys\", \"exasol\")\n" // + "}\n"; - writeFile(this.projectDir.resolve("main.go"), content); + writeFile(this.projectDir.resolve(moduleDir).resolve("main.go"), content); } - private void writeTestGoFile() { + private void writeTestGoFile(final Path moduleDir) { final String content = "package main\n" // + "import (\n" // + " testSetupAbstraction \"github.com/exasol/exasol-test-setup-abstraction-server/go-client\"\n" // + ")\n" // + "func myTest() {\n" + " exasol := testSetupAbstraction.Create(\"myConfig.json\")\n" + " connection := exasol.CreateConnection()\n" + "}\n"; - writeFile(this.projectDir.resolve("main_test.go"), content); + writeFile(this.projectDir.resolve(moduleDir).resolve("main_test.go"), content); } - private void splitAndExecute(final String command) { - execute(command.split(" ")); + private void splitAndExecute(final Path workingDir, final String command) { + execute(workingDir, command.split(" ")); } - private void execute(final String... command) { + private void execute(final Path workingDir, final String... command) { try { final Process process = new ProcessBuilder(command) // - .directory(this.projectDir.toFile()) // + .directory(this.projectDir.resolve(workingDir).toFile()) // .redirectErrorStream(false) // .start(); final boolean success = process.waitFor(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); @@ -140,4 +157,11 @@ private void execute(final String... command) { private String readStream(final InputStream stream) throws IOException { return new String(stream.readAllBytes(), StandardCharsets.UTF_8); } + + @Override + public void close() { + if (this.gitRepo != null) { + this.gitRepo.close(); + } + } }