From 059482f8aba1cf9af59698ec996faa2db9a8f07f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Tue, 22 Sep 2020 17:29:40 +0200 Subject: [PATCH] Detect missing paths in hg fileview command If a not existing path is request in the fileview command, it will be treated as an empty directory. But such a thing does not exist in hg. Therefore we handle this result as what it is: a not existing path - and we throw a not found exception. --- CHANGELOG.md | 2 ++ .../scm/repository/spi/HgBrowseCommand.java | 6 +++- .../spi/javahg/HgFileviewCommand.java | 6 ++-- .../javahg/HgFileviewCommandResultReader.java | 16 +++++++--- .../HgFileviewCommandResultReaderTest.java | 31 +++++++++++++------ 5 files changed, 43 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed03e68e98..0a7d445ea5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ + # Changelog All notable changes to this project will be documented in this file. @@ -17,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Overflow for too long branch names ([#1339](https://github.com/scm-manager/scm-manager/pull/1339)) - Set default branch in branch selector if nothing is selected ([#1338](https://github.com/scm-manager/scm-manager/pull/1338)) - Handling of branch with slashes in source view ([#1340](https://github.com/scm-manager/scm-manager/pull/1340)) +- Detect not existing paths correctly in Mercurial ([#1343](https://github.com/scm-manager/scm-manager/pull/1343)) ## [2.5.0] - 2020-09-10 ### Added diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBrowseCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBrowseCommand.java index 6c83368cc4..8c217d6d4c 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBrowseCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBrowseCommand.java @@ -36,6 +36,9 @@ import sonia.scm.repository.spi.javahg.HgFileviewCommand; import java.io.IOException; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + //~--- JDK imports ------------------------------------------------------------ /** @@ -93,7 +96,8 @@ public class HgBrowseCommand extends AbstractCommand implements BrowseCommand cmd.setLimit(request.getLimit()); cmd.setOffset(request.getOffset()); - FileObject file = cmd.execute(); + FileObject file = cmd.execute() + .orElseThrow(() -> notFound(entity("File", request.getPath()).in("Revision", revision).in(getRepository()))); return new BrowserResult(c == null? "tip": c.getNode(), revision, file); } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java index 776bfcaa7d..0ecd0a7d27 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi.javahg; //~--- non-JDK imports -------------------------------------------------------- @@ -29,12 +29,12 @@ package sonia.scm.repository.spi.javahg; import com.aragost.javahg.Repository; import com.aragost.javahg.internals.AbstractCommand; import com.aragost.javahg.internals.HgInputStream; - import sonia.scm.repository.FileObject; //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; +import java.util.Optional; /** * Mercurial command to list files of a repository. @@ -161,7 +161,7 @@ public class HgFileviewCommand extends AbstractCommand * * @throws IOException */ - public FileObject execute() throws IOException + public Optional execute() throws IOException { cmdAppend("-t"); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReader.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReader.java index 80d6a69a5a..c9e315ab00 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReader.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReader.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi.javahg; import com.aragost.javahg.DateTime; @@ -35,6 +35,10 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.LinkedList; +import java.util.Optional; + +import static java.util.Optional.empty; +import static java.util.Optional.of; class HgFileviewCommandResultReader { @@ -48,7 +52,7 @@ class HgFileviewCommandResultReader { this.disableLastCommit = disableLastCommit; } - FileObject parseResult() throws IOException { + Optional parseResult() throws IOException { Deque stack = new LinkedList<>(); FileObject last = null; @@ -82,13 +86,17 @@ class HgFileviewCommandResultReader { if (stack.isEmpty()) { // if the stack is empty, the requested path is probably a file - return last; + return of(last); + } else if (stack.size() == 1 && stack.getFirst().isDirectory() && stack.getFirst().getChildren().isEmpty()) { + // There are no empty directories in hg. When we get this, + // we just get the requested path as a directory, but it does not exist. + return empty(); } else { // if the stack is not empty, the requested path is a directory if (stream.read() == TRUNCATED_MARK) { stack.getLast().setTruncated(true); } - return stack.getLast(); + return of(stack.getLast()); } } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReaderTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReaderTest.java index 2cb6a4af04..c268175108 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReaderTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReaderTest.java @@ -21,11 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi.javahg; import com.aragost.javahg.internals.HgInputStream; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import sonia.scm.repository.FileObject; @@ -34,6 +33,7 @@ import java.io.IOException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Iterator; +import java.util.Optional; import java.util.OptionalLong; import static java.nio.charset.StandardCharsets.UTF_8; @@ -55,7 +55,7 @@ class HgFileviewCommandResultReaderTest { .file("b.txt", 100, time2.toEpochMilli(), "file b\nwith some\nmore text") .build(); - FileObject fileObject = reader.parseResult(); + FileObject fileObject = reader.parseResult().get(); assertThat(fileObject.isDirectory()).isTrue(); assertThat(fileObject.getChildren()) @@ -84,7 +84,7 @@ class HgFileviewCommandResultReaderTest { .file("a.txt") .truncated(); - FileObject fileObject = reader.parseResult(); + FileObject fileObject = reader.parseResult().get(); assertThat(fileObject.isTruncated()).isTrue(); } @@ -96,7 +96,7 @@ class HgFileviewCommandResultReaderTest { .file("dir/a.txt") .build(); - FileObject fileObject = reader.parseResult(); + FileObject fileObject = reader.parseResult().get(); assertThat(fileObject.isDirectory()).isTrue(); assertThat(fileObject.getName()).isEqualTo("dir"); @@ -117,7 +117,7 @@ class HgFileviewCommandResultReaderTest { .file("d.txt") .build(); - FileObject fileObject = reader.parseResult(); + FileObject fileObject = reader.parseResult().get(); assertThat(fileObject.getChildren()) .extracting("name") @@ -152,7 +152,7 @@ class HgFileviewCommandResultReaderTest { .file("d.txt") .build(); - FileObject fileObject = reader.parseResult(); + FileObject fileObject = reader.parseResult().get(); assertThat(fileObject.getChildren()) .extracting("name") @@ -179,7 +179,7 @@ class HgFileviewCommandResultReaderTest { .file("d.txt") .build(); - FileObject fileObject = reader.parseResult(); + FileObject fileObject = reader.parseResult().get(); assertThat(fileObject.getChildren()) .extracting("name") @@ -213,7 +213,7 @@ class HgFileviewCommandResultReaderTest { .file("directory/b.txt") .build(); - FileObject fileObject = reader.parseResult(); + FileObject fileObject = reader.parseResult().get(); assertThat(fileObject.getChildren()) .extracting("name") @@ -248,7 +248,7 @@ class HgFileviewCommandResultReaderTest { .file("a.txt") .build(); - FileObject fileObject = reader.parseResult(); + FileObject fileObject = reader.parseResult().get(); assertThat(fileObject.getChildren()) .extracting("description") @@ -258,6 +258,17 @@ class HgFileviewCommandResultReaderTest { .containsOnly(OptionalLong.empty()); } + @Test + void shouldIgnoreSingleEmptyDir() throws IOException { + HgFileviewCommandResultReader reader = new MockInput() + .dir("empty") + .build(); + + Optional fileObject = reader.parseResult(); + + assertThat(fileObject).isEmpty(); + } + private HgInputStream createInputStream(String input) { return new HgInputStream(new ByteArrayInputStream(input.getBytes(UTF_8)), UTF_8.newDecoder()); }