mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-03-22 20:11:38 +01:00
Merge with upstream
This commit is contained in:
@@ -35,23 +35,16 @@ package sonia.scm.repository.spi.javahg;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.aragost.javahg.DateTime;
|
||||
import com.aragost.javahg.Repository;
|
||||
import com.aragost.javahg.internals.AbstractCommand;
|
||||
import com.aragost.javahg.internals.HgInputStream;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import sonia.scm.repository.FileObject;
|
||||
import sonia.scm.repository.SubRepository;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Mercurial command to list files of a repository.
|
||||
*
|
||||
@@ -60,7 +53,6 @@ import java.util.LinkedList;
|
||||
public class HgFileviewCommand extends AbstractCommand
|
||||
{
|
||||
|
||||
public static final char TRUNCATED_MARK = 't';
|
||||
private boolean disableLastCommit = false;
|
||||
|
||||
private HgFileviewCommand(Repository repository)
|
||||
@@ -182,139 +174,9 @@ public class HgFileviewCommand extends AbstractCommand
|
||||
{
|
||||
cmdAppend("-t");
|
||||
|
||||
Deque<FileObject> stack = new LinkedList<>();
|
||||
|
||||
HgInputStream stream = launchStream();
|
||||
|
||||
FileObject last = null;
|
||||
while (stream.peek() != -1 && stream.peek() != TRUNCATED_MARK) {
|
||||
FileObject file = read(stream);
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
FileObject current = stack.peek();
|
||||
if (isParent(current, file)) {
|
||||
current.addChild(file);
|
||||
break;
|
||||
} else {
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
if (file.isDirectory()) {
|
||||
stack.push(file);
|
||||
}
|
||||
last = file;
|
||||
}
|
||||
|
||||
if (stack.isEmpty()) {
|
||||
// if the stack is empty, the requested path is probably a file
|
||||
return last;
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
||||
private FileObject read(HgInputStream stream) throws IOException {
|
||||
char type = (char) stream.read();
|
||||
|
||||
FileObject file;
|
||||
switch (type) {
|
||||
case 'd':
|
||||
file = readDirectory(stream);
|
||||
break;
|
||||
case 'f':
|
||||
file = readFile(stream);
|
||||
break;
|
||||
case 's':
|
||||
file = readSubRepository(stream);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("unknown file object type: " + type);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private boolean isParent(FileObject parent, FileObject child) {
|
||||
String parentPath = parent.getPath();
|
||||
if (parentPath.equals("")) {
|
||||
return true;
|
||||
}
|
||||
return child.getParentPath().equals(parentPath);
|
||||
}
|
||||
|
||||
private FileObject readDirectory(HgInputStream stream) throws IOException {
|
||||
FileObject directory = new FileObject();
|
||||
String path = removeTrailingSlash(stream.textUpTo('\0'));
|
||||
|
||||
directory.setName(getNameFromPath(path));
|
||||
directory.setDirectory(true);
|
||||
directory.setPath(path);
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
private FileObject readFile(HgInputStream stream) throws IOException {
|
||||
FileObject file = new FileObject();
|
||||
String path = removeTrailingSlash(stream.textUpTo('\n'));
|
||||
|
||||
file.setName(getNameFromPath(path));
|
||||
file.setPath(path);
|
||||
file.setDirectory(false);
|
||||
file.setLength((long) stream.decimalIntUpTo(' '));
|
||||
|
||||
DateTime timestamp = stream.dateTimeUpTo(' ');
|
||||
String description = stream.textUpTo('\0');
|
||||
|
||||
if (!disableLastCommit) {
|
||||
file.setCommitDate(timestamp.getDate().getTime());
|
||||
file.setDescription(description);
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
private FileObject readSubRepository(HgInputStream stream) throws IOException {
|
||||
FileObject directory = new FileObject();
|
||||
String path = removeTrailingSlash(stream.textUpTo('\n'));
|
||||
|
||||
directory.setName(getNameFromPath(path));
|
||||
directory.setDirectory(true);
|
||||
directory.setPath(path);
|
||||
|
||||
String revision = stream.textUpTo(' ');
|
||||
String url = stream.textUpTo('\0');
|
||||
|
||||
SubRepository subRepository = new SubRepository(url);
|
||||
|
||||
if (!Strings.isNullOrEmpty(revision)) {
|
||||
subRepository.setRevision(revision);
|
||||
}
|
||||
|
||||
directory.setSubRepository(subRepository);
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
private String removeTrailingSlash(String path) {
|
||||
if (path.endsWith("/")) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private String getNameFromPath(String path) {
|
||||
int index = path.lastIndexOf('/');
|
||||
|
||||
if (index > 0) {
|
||||
path = path.substring(index + 1);
|
||||
}
|
||||
|
||||
return path;
|
||||
return new HgFileviewCommandResultReader(stream, disableLastCommit).parseResult();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
package sonia.scm.repository.spi.javahg;
|
||||
|
||||
import com.aragost.javahg.DateTime;
|
||||
import com.aragost.javahg.internals.HgInputStream;
|
||||
import com.google.common.base.Strings;
|
||||
import sonia.scm.repository.FileObject;
|
||||
import sonia.scm.repository.SubRepository;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
|
||||
class HgFileviewCommandResultReader {
|
||||
|
||||
private static final char TRUNCATED_MARK = 't';
|
||||
|
||||
private final HgInputStream stream;
|
||||
private final boolean disableLastCommit;
|
||||
|
||||
HgFileviewCommandResultReader(HgInputStream stream, boolean disableLastCommit) {
|
||||
this.stream = stream;
|
||||
this.disableLastCommit = disableLastCommit;
|
||||
}
|
||||
|
||||
FileObject parseResult() throws IOException {
|
||||
Deque<FileObject> stack = new LinkedList<>();
|
||||
|
||||
FileObject last = null;
|
||||
while (stream.peek() != -1 && stream.peek() != TRUNCATED_MARK) {
|
||||
FileObject file = read(stream);
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
FileObject current = stack.peek();
|
||||
if (isParent(current, file)) {
|
||||
current.addChild(file);
|
||||
break;
|
||||
} else if (isAncestor(current, file)) {
|
||||
Collection<FileObject> missingParents = createMissingParents(current, file);
|
||||
for (FileObject subDir : missingParents) {
|
||||
current.addChild(subDir);
|
||||
stack.push(subDir);
|
||||
current = stack.peek();
|
||||
}
|
||||
current.addChild(file);
|
||||
break;
|
||||
} else {
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
if (file.isDirectory()) {
|
||||
stack.push(file);
|
||||
}
|
||||
last = file;
|
||||
}
|
||||
|
||||
if (stack.isEmpty()) {
|
||||
// if the stack is empty, the requested path is probably a file
|
||||
return last;
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
||||
private FileObject read(HgInputStream stream) throws IOException {
|
||||
char type = (char) stream.read();
|
||||
|
||||
FileObject file;
|
||||
switch (type) {
|
||||
case 'd':
|
||||
file = readDirectory(stream);
|
||||
break;
|
||||
case 'f':
|
||||
file = readFile(stream);
|
||||
break;
|
||||
case 's':
|
||||
file = readSubRepository(stream);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("unknown file object type: " + type);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private boolean isParent(FileObject parent, FileObject child) {
|
||||
String parentPath = parent.getPath();
|
||||
return child.getParentPath().equals(parentPath);
|
||||
}
|
||||
|
||||
private boolean isAncestor(FileObject ancestor, FileObject child) {
|
||||
String ancestorPath = ancestor.getPath();
|
||||
return ancestorPath.equals("") || child.getParentPath().startsWith(ancestorPath + '/');
|
||||
}
|
||||
|
||||
private Collection<FileObject> createMissingParents(FileObject current, FileObject file) {
|
||||
String missingPath = file.getPath().substring(current.getPath().length(), file.getPath().lastIndexOf('/'));
|
||||
|
||||
FileObject directory = new FileObject();
|
||||
directory.setName(getNameFromPath(missingPath));
|
||||
directory.setDirectory(true);
|
||||
directory.setPath(missingPath);
|
||||
|
||||
Collection<FileObject> parents = new ArrayList<>();
|
||||
|
||||
if (!isParent(current, directory)) {
|
||||
parents.addAll(createMissingParents(current, directory));
|
||||
}
|
||||
|
||||
parents.add(directory);
|
||||
|
||||
return parents;
|
||||
}
|
||||
|
||||
private FileObject readDirectory(HgInputStream stream) throws IOException {
|
||||
FileObject directory = new FileObject();
|
||||
String path = removeTrailingSlash(stream.textUpTo('\0'));
|
||||
|
||||
directory.setName(getNameFromPath(path));
|
||||
directory.setDirectory(true);
|
||||
directory.setPath(path);
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
private FileObject readFile(HgInputStream stream) throws IOException {
|
||||
FileObject file = new FileObject();
|
||||
String path = removeTrailingSlash(stream.textUpTo('\n'));
|
||||
|
||||
file.setName(getNameFromPath(path));
|
||||
file.setPath(path);
|
||||
file.setDirectory(false);
|
||||
file.setLength((long) stream.decimalIntUpTo(' '));
|
||||
|
||||
DateTime timestamp = stream.dateTimeUpTo(' ');
|
||||
String description = stream.textUpTo('\0');
|
||||
|
||||
if (!disableLastCommit) {
|
||||
file.setCommitDate(timestamp.getDate().getTime());
|
||||
file.setDescription(description);
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
private FileObject readSubRepository(HgInputStream stream) throws IOException {
|
||||
FileObject directory = new FileObject();
|
||||
String path = removeTrailingSlash(stream.textUpTo('\n'));
|
||||
|
||||
directory.setName(getNameFromPath(path));
|
||||
directory.setDirectory(true);
|
||||
directory.setPath(path);
|
||||
|
||||
String revision = stream.textUpTo(' ');
|
||||
String url = stream.textUpTo('\0');
|
||||
|
||||
SubRepository subRepository = new SubRepository(url);
|
||||
|
||||
if (!Strings.isNullOrEmpty(revision)) {
|
||||
subRepository.setRevision(revision);
|
||||
}
|
||||
|
||||
directory.setSubRepository(subRepository);
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
private String removeTrailingSlash(String path) {
|
||||
if (path.endsWith("/")) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private String getNameFromPath(String path) {
|
||||
int index = path.lastIndexOf('/');
|
||||
|
||||
if (index > 0) {
|
||||
path = path.substring(index + 1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@@ -217,18 +217,21 @@ class File_Printer:
|
||||
self.revCtx = revCtx
|
||||
self.disableLastCommit = disableLastCommit
|
||||
self.transport = transport
|
||||
self.result_count = -1
|
||||
self.result_count = 0
|
||||
self.initial_path_printed = False
|
||||
self.limit = limit
|
||||
self.offset = offset
|
||||
|
||||
def print_directory(self, path):
|
||||
if self.shouldPrintResult():
|
||||
if not self.initial_path_printed or self.offset == 0 or self.shouldPrintResult():
|
||||
self.initial_path_printed = True
|
||||
format = '%s/\n'
|
||||
if self.transport:
|
||||
format = 'd%s/\0'
|
||||
self.writer.write( format % path)
|
||||
|
||||
def print_file(self, path):
|
||||
self.result_count += 1
|
||||
if self.shouldPrintResult():
|
||||
file = self.revCtx[path]
|
||||
date = '0 0'
|
||||
@@ -258,10 +261,7 @@ class File_Printer:
|
||||
self.print_file(file.path)
|
||||
|
||||
def shouldPrintResult(self):
|
||||
# The first result is the selected path (or root if not specified). This
|
||||
# always has to be printed. Therefore we start counting with -1.
|
||||
self.result_count += 1
|
||||
return self.result_count == 0 or self.offset < self.result_count <= self.limit + self.offset
|
||||
return self.offset < self.result_count <= self.limit + self.offset
|
||||
|
||||
def isTruncated(self):
|
||||
return self.result_count > self.limit + self.offset
|
||||
|
||||
@@ -26,7 +26,7 @@ class File_Object_Collector():
|
||||
|
||||
def __init__(self):
|
||||
self.stack = []
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
if len(self.stack) == 0 and key == 0:
|
||||
return self.last
|
||||
@@ -43,7 +43,7 @@ class File_Object_Collector():
|
||||
if file.directory:
|
||||
self.stack.append(file)
|
||||
self.last = file
|
||||
|
||||
|
||||
class CollectingWriter:
|
||||
def __init__(self):
|
||||
self.stack = []
|
||||
@@ -85,14 +85,13 @@ class Test_File_Viewer(unittest.TestCase):
|
||||
|
||||
def test_printer_with_limit(self):
|
||||
paths = ["a", "b", "c/d.txt", "c/e.txt", "f.txt", "c/g/h.txt"]
|
||||
writer = self.view_with_limit_and_offset(paths, 3, 0)
|
||||
writer = self.view_with_limit_and_offset(paths, 1, 0)
|
||||
self.assertPaths(writer, ["/", "c/", "c/g/", "c/g/h.txt"])
|
||||
|
||||
# TODO fix
|
||||
def x_test_printer_with_offset(self):
|
||||
paths = ["a", "b", "c/d.txt", "c/e.txt", "f.txt", "c/g/h.txt"]
|
||||
writer = self.view_with_limit_and_offset(paths, 100, 3)
|
||||
self.assertPaths(writer, ["/", "c", "c/d.txt", "c/e.txt", "a", "b", "f.txt"])
|
||||
def test_printer_with_offset(self):
|
||||
paths = ["c/g/h.txt", "c/g/i.txt", "c/d.txt", "c/e.txt", "a", "b", "f.txt"]
|
||||
writer = self.view_with_limit_and_offset(paths, 100, 1)
|
||||
self.assertPaths(writer, ["/", "c/g/i.txt", "c/d.txt", "c/e.txt", "a", "b", "f.txt"])
|
||||
|
||||
def view_with_limit_and_offset(self, paths, limit, offset):
|
||||
revCtx = DummyRevContext(paths)
|
||||
@@ -113,9 +112,9 @@ class Test_File_Viewer(unittest.TestCase):
|
||||
self.assertTrue(nextChar == " " or nextChar == "\n", expected + " does not match " + actual)
|
||||
|
||||
def assertPaths(self, actual, expected):
|
||||
self.assertEqual(len(actual), len(expected))
|
||||
for idx,item in enumerate(actual):
|
||||
self.assertPath(item, expected[idx])
|
||||
self.assertEqual(len(actual), len(expected))
|
||||
|
||||
def test_recursive_with_path(self):
|
||||
root = self.collect(["a", "b", "c/d.txt", "c/e.txt", "f.txt", "c/g/h.txt"], "c", True)
|
||||
@@ -178,7 +177,7 @@ class Test_File_Viewer(unittest.TestCase):
|
||||
self.assertEqual(len(parent), len(expectedPaths))
|
||||
for idx,item in enumerate(parent.children):
|
||||
self.assertEqual(item.path, expectedPaths[idx])
|
||||
|
||||
|
||||
def assertFile(self, file, expectedPath):
|
||||
self.assertEquals(file.path, expectedPath)
|
||||
self.assertFalse(file.directory)
|
||||
|
||||
@@ -183,14 +183,14 @@ public class HgBrowseCommandTest extends AbstractHgCommandTestBase {
|
||||
@Test
|
||||
public void testLimit() throws IOException {
|
||||
BrowseCommandRequest request = new BrowseCommandRequest();
|
||||
request.setLimit(2);
|
||||
request.setLimit(1);
|
||||
|
||||
BrowserResult result = new HgBrowseCommand(cmdContext, repository).getBrowserResult(request);
|
||||
FileObject root = result.getFile();
|
||||
|
||||
Collection<FileObject> foList = root.getChildren();
|
||||
|
||||
assertThat(foList).extracting("name").containsExactlyInAnyOrder("c", "a.txt");
|
||||
assertThat(foList).extracting("name").containsExactly("c", "a.txt");
|
||||
assertThat(root.isTruncated()).isTrue();
|
||||
}
|
||||
|
||||
@@ -198,14 +198,14 @@ public class HgBrowseCommandTest extends AbstractHgCommandTestBase {
|
||||
public void testOffset() throws IOException {
|
||||
BrowseCommandRequest request = new BrowseCommandRequest();
|
||||
request.setLimit(2);
|
||||
request.setOffset(2);
|
||||
request.setOffset(1);
|
||||
|
||||
BrowserResult result = new HgBrowseCommand(cmdContext, repository).getBrowserResult(request);
|
||||
FileObject root = result.getFile();
|
||||
|
||||
Collection<FileObject> foList = root.getChildren();
|
||||
|
||||
assertThat(foList).extracting("name").containsExactlyInAnyOrder("b.txt", "f.txt");
|
||||
assertThat(foList).extracting("name").containsExactly("b.txt", "f.txt");
|
||||
assertThat(root.isTruncated()).isFalse();
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ public class HgBrowseCommandTest extends AbstractHgCommandTestBase {
|
||||
public void testRecursiveLimit() throws IOException {
|
||||
BrowseCommandRequest request = new BrowseCommandRequest();
|
||||
|
||||
request.setLimit(4);
|
||||
request.setLimit(3);
|
||||
request.setRecursive(true);
|
||||
|
||||
FileObject root = new HgBrowseCommand(cmdContext, repository).getBrowserResult(request).getFile();
|
||||
@@ -237,7 +237,7 @@ public class HgBrowseCommandTest extends AbstractHgCommandTestBase {
|
||||
public void testRecursiveLimitInSubDir() throws IOException {
|
||||
BrowseCommandRequest request = new BrowseCommandRequest();
|
||||
|
||||
request.setLimit(2);
|
||||
request.setLimit(1);
|
||||
request.setRecursive(true);
|
||||
|
||||
FileObject root = new HgBrowseCommand(cmdContext, repository).getBrowserResult(request).getFile();
|
||||
@@ -260,7 +260,7 @@ public class HgBrowseCommandTest extends AbstractHgCommandTestBase {
|
||||
public void testRecursiveOffset() throws IOException {
|
||||
BrowseCommandRequest request = new BrowseCommandRequest();
|
||||
|
||||
request.setOffset(2);
|
||||
request.setOffset(1);
|
||||
request.setRecursive(true);
|
||||
|
||||
FileObject root = new HgBrowseCommand(cmdContext, repository).getBrowserResult(request).getFile();
|
||||
@@ -308,8 +308,7 @@ public class HgBrowseCommandTest extends AbstractHgCommandTestBase {
|
||||
Collection<FileObject> foList = root.getChildren();
|
||||
|
||||
assertNotNull(foList);
|
||||
assertFalse(foList.isEmpty());
|
||||
assertEquals(4, foList.size());
|
||||
assertThat(foList).extracting("name").containsExactly("c", "a.txt", "b.txt", "f.txt");
|
||||
|
||||
return foList;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
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;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Iterator;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.Optional.empty;
|
||||
import static java.util.Optional.of;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class HgFileviewCommandResultReaderTest {
|
||||
|
||||
@Test
|
||||
void shouldParseSimpleAttributes() throws IOException {
|
||||
Instant time1 = Instant.now().truncatedTo(ChronoUnit.SECONDS);
|
||||
Instant time2 = time1.minus(1, ChronoUnit.DAYS);
|
||||
|
||||
HgFileviewCommandResultReader reader = new MockInput()
|
||||
.dir("")
|
||||
.dir("dir")
|
||||
.file("a.txt", 10, time1.toEpochMilli(), "file a")
|
||||
.file("b.txt", 100, time2.toEpochMilli(), "file b\nwith some\nmore text")
|
||||
.build();
|
||||
|
||||
FileObject fileObject = reader.parseResult();
|
||||
|
||||
assertThat(fileObject.isDirectory()).isTrue();
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("name")
|
||||
.containsExactly("dir", "a.txt", "b.txt");
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("directory")
|
||||
.containsExactly(true, false, false);
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("length")
|
||||
.containsExactly(OptionalLong.empty(), OptionalLong.of(10L), OptionalLong.of(100L));
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("description")
|
||||
.containsExactly(empty(), of("file a"), of("file b\nwith some\nmore text"));
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("commitDate")
|
||||
.containsExactly(OptionalLong.empty(), OptionalLong.of(time1.toEpochMilli()), OptionalLong.of(time2.toEpochMilli()));
|
||||
assertThat(fileObject.isTruncated()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseTruncatedFlag() throws IOException {
|
||||
HgFileviewCommandResultReader reader = new MockInput()
|
||||
.dir("")
|
||||
.dir("dir")
|
||||
.file("a.txt")
|
||||
.truncated();
|
||||
|
||||
FileObject fileObject = reader.parseResult();
|
||||
|
||||
assertThat(fileObject.isTruncated()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseSubDirectory() throws IOException {
|
||||
HgFileviewCommandResultReader reader = new MockInput()
|
||||
.dir("dir")
|
||||
.file("dir/a.txt")
|
||||
.build();
|
||||
|
||||
FileObject fileObject = reader.parseResult();
|
||||
|
||||
assertThat(fileObject.isDirectory()).isTrue();
|
||||
assertThat(fileObject.getName()).isEqualTo("dir");
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("name")
|
||||
.containsExactly("a.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseRecursiveResult() throws IOException {
|
||||
HgFileviewCommandResultReader reader = new MockInput()
|
||||
.dir("")
|
||||
.dir("dir")
|
||||
.dir("dir/more")
|
||||
.file("dir/more/c.txt")
|
||||
.file("dir/a.txt")
|
||||
.file("dir/b.txt")
|
||||
.file("d.txt")
|
||||
.build();
|
||||
|
||||
FileObject fileObject = reader.parseResult();
|
||||
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("name")
|
||||
.containsExactly("dir", "d.txt");
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("directory")
|
||||
.containsExactly(true, false);
|
||||
|
||||
FileObject subDir = fileObject.getChildren().iterator().next();
|
||||
assertThat(subDir.getChildren())
|
||||
.extracting("name")
|
||||
.containsExactly("more", "a.txt", "b.txt");
|
||||
assertThat(subDir.getChildren())
|
||||
.extracting("directory")
|
||||
.containsExactly(true, false, false);
|
||||
|
||||
FileObject subSubDir = subDir.getChildren().iterator().next();
|
||||
assertThat(subSubDir.getChildren())
|
||||
.extracting("name")
|
||||
.containsExactly("c.txt");
|
||||
assertThat(subSubDir.getChildren())
|
||||
.extracting("directory")
|
||||
.containsExactly(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateDirectoriesImplicitly() throws IOException {
|
||||
HgFileviewCommandResultReader reader = new MockInput()
|
||||
.dir("")
|
||||
.file("dir/a.txt")
|
||||
.file("dir/b.txt")
|
||||
.file("d.txt")
|
||||
.build();
|
||||
|
||||
FileObject fileObject = reader.parseResult();
|
||||
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("name")
|
||||
.containsExactly("dir", "d.txt");
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("directory")
|
||||
.containsExactly(true, false);
|
||||
|
||||
FileObject subDir = fileObject.getChildren().iterator().next();
|
||||
assertThat(subDir.getChildren())
|
||||
.extracting("name")
|
||||
.containsExactly("a.txt", "b.txt");
|
||||
assertThat(subDir.getChildren())
|
||||
.extracting("directory")
|
||||
.containsExactly(false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateSubSubDirectoriesImplicitly() throws IOException {
|
||||
HgFileviewCommandResultReader reader = new MockInput()
|
||||
.dir("")
|
||||
.file("dir/more/a.txt")
|
||||
.file("dir/b.txt")
|
||||
.file("d.txt")
|
||||
.build();
|
||||
|
||||
FileObject fileObject = reader.parseResult();
|
||||
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("name")
|
||||
.containsExactly("dir", "d.txt");
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("directory")
|
||||
.containsExactly(true, false);
|
||||
|
||||
FileObject subDir = fileObject.getChildren().iterator().next();
|
||||
assertThat(subDir.getChildren())
|
||||
.extracting("name")
|
||||
.containsExactly("more", "b.txt");
|
||||
assertThat(subDir.getChildren())
|
||||
.extracting("directory")
|
||||
.containsExactly(true, false);
|
||||
|
||||
FileObject subSubDir = subDir.getChildren().iterator().next();
|
||||
assertThat(subSubDir.getChildren())
|
||||
.extracting("name")
|
||||
.containsExactly("a.txt");
|
||||
assertThat(subSubDir.getChildren())
|
||||
.extracting("directory")
|
||||
.containsExactly(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateSimilarSubDirectoriesCorrectly() throws IOException {
|
||||
HgFileviewCommandResultReader reader = new MockInput()
|
||||
.dir("")
|
||||
.file("dir/a.txt")
|
||||
.file("directory/b.txt")
|
||||
.build();
|
||||
|
||||
FileObject fileObject = reader.parseResult();
|
||||
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("name")
|
||||
.containsExactly("dir", "directory");
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("directory")
|
||||
.containsExactly(true, true);
|
||||
|
||||
Iterator<FileObject> fileIterator = fileObject.getChildren().iterator();
|
||||
FileObject firstSubDir = fileIterator.next();
|
||||
assertThat(firstSubDir.getChildren())
|
||||
.extracting("name")
|
||||
.containsExactly("a.txt");
|
||||
assertThat(firstSubDir.getChildren())
|
||||
.extracting("directory")
|
||||
.containsExactly(false);
|
||||
|
||||
FileObject secondSubDir = fileIterator.next();
|
||||
assertThat(secondSubDir.getChildren())
|
||||
.extracting("name")
|
||||
.containsExactly("b.txt");
|
||||
assertThat(secondSubDir.getChildren())
|
||||
.extracting("directory")
|
||||
.containsExactly(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIgnoreTimeAndCommentWhenDisabled() throws IOException {
|
||||
HgFileviewCommandResultReader reader = new MockInput()
|
||||
.dir("")
|
||||
.dir("c")
|
||||
.file("a.txt")
|
||||
.build();
|
||||
|
||||
FileObject fileObject = reader.parseResult();
|
||||
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("description")
|
||||
.containsOnly(empty());
|
||||
assertThat(fileObject.getChildren())
|
||||
.extracting("commitDate")
|
||||
.containsOnly(OptionalLong.empty());
|
||||
}
|
||||
|
||||
private HgInputStream createInputStream(String input) {
|
||||
return new HgInputStream(new ByteArrayInputStream(input.getBytes(UTF_8)), UTF_8.newDecoder());
|
||||
}
|
||||
|
||||
private class MockInput {
|
||||
private final StringBuilder stringBuilder = new StringBuilder();
|
||||
private boolean disableLastCommit = false;
|
||||
|
||||
MockInput dir(String name) {
|
||||
stringBuilder
|
||||
.append('d')
|
||||
.append(name)
|
||||
.append('/')
|
||||
.append('\0');
|
||||
return this;
|
||||
}
|
||||
|
||||
MockInput file(String name) {
|
||||
disableLastCommit = true;
|
||||
return file(name, 1024, 0, "n/a");
|
||||
}
|
||||
|
||||
MockInput file(String name, int length, long time, String comment) {
|
||||
stringBuilder
|
||||
.append('f')
|
||||
.append(name)
|
||||
.append('\n')
|
||||
.append(length)
|
||||
.append(' ')
|
||||
.append(time/1000)
|
||||
.append(' ')
|
||||
.append(0)
|
||||
.append(' ')
|
||||
.append(comment)
|
||||
.append('\0');
|
||||
return this;
|
||||
}
|
||||
|
||||
HgFileviewCommandResultReader truncated() {
|
||||
stringBuilder.append("t");
|
||||
return build();
|
||||
}
|
||||
|
||||
HgFileviewCommandResultReader build() {
|
||||
HgInputStream inputStream = createInputStream(stringBuilder.toString());
|
||||
return new HgFileviewCommandResultReader(inputStream, disableLastCommit);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user