diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 4487e21..03fee46 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -8,11 +8,7 @@ name: Java CI with Maven -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] +on: [push, pull_request] jobs: build: @@ -38,8 +34,7 @@ jobs: native-image-job-reports: 'true' - name: Build executable file run: | - native-image -march=compatibility -cp target/gis-*.jar "org.nqm.Gis" --no-fallback --initialize-at-run-time=io.netty.handler.ssl.BouncyCastleAlpnSslUtils - mv org.nqm.gis gis + docker build -f Containerfile -t gis . || return 1; docker create --name dkgis_ gis:latest; docker cp dkgis_:/app/gis/gis .; docker rm -f dkgis_ ./gis --version du -sh gis - name: Upload artifact diff --git a/pom.xml b/pom.xml index 5f653fb..885eda4 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,7 @@ 3.3.0 0.8.12 9.0.1 + 6.10.0.202406032230-r @@ -32,6 +33,23 @@ picocli ${picocli.version} + + org.eclipse.jgit + org.eclipse.jgit + ${jgit.version} + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.slf4j + slf4j-simple + ${slf4j.version} + @@ -64,18 +82,6 @@ ${testcontainers.version} test - - org.slf4j - slf4j-api - ${slf4j.version} - test - - - org.slf4j - slf4j-simple - ${slf4j.version} - test - @@ -180,6 +186,26 @@ + + default-check + + check + + + + + BUNDLE + + + COMPLEXITY + COVEREDRATIO + 0.90 + + + + + + diff --git a/src/main/java/org/nqm/Gis.java b/src/main/java/org/nqm/Gis.java index d1555ba..6083ee3 100644 --- a/src/main/java/org/nqm/Gis.java +++ b/src/main/java/org/nqm/Gis.java @@ -1,7 +1,7 @@ package org.nqm; +import org.nqm.command.GisCommand; import org.nqm.command.GisVersion; -import org.nqm.command.GitCommand; import org.nqm.config.GisLog; import org.nqm.utils.GisProcessUtils; import picocli.CommandLine; @@ -16,7 +16,7 @@ description = "Git extension wrapper which supports submodules", mixinStandardHelpOptions = true, versionProvider = GisVersion.class) -public class Gis extends GitCommand { +public class Gis extends GisCommand { private static final IExecutionExceptionHandler GLOBAL_EXCEPTION_HANLER = new IExecutionExceptionHandler() { @Override @@ -38,9 +38,14 @@ public static void setDryRun(boolean dryRun) { } public static void main(String... args) { + // try { + // System.out.println(GitStatusCommand.status(Path.of("/home/minh/projects/test/small-git-root-module/small-git-domain/.git"), false)); + // } catch (NoWorkTreeException | IOException | GitAPIException e) { + // e.printStackTrace(); + // } + var gis = new CommandLine(new Gis()); gis.setExecutionExceptionHandler(GLOBAL_EXCEPTION_HANLER); - gis.execute(args.length == 0 ? new String[] {GIT_STATUS, "--one-line"} : args); diff --git a/src/main/java/org/nqm/command/CommandVerticle.java b/src/main/java/org/nqm/command/CommandVerticle.java index 38aa62d..429ade0 100644 --- a/src/main/java/org/nqm/command/CommandVerticle.java +++ b/src/main/java/org/nqm/command/CommandVerticle.java @@ -1,6 +1,6 @@ package org.nqm.command; -import static org.nqm.command.GitCommand.HOOKS_OPTION; +import static org.nqm.command.GisCommand.HOOKS_OPTION; import static org.nqm.utils.GisStringUtils.isNotBlank; import static org.nqm.utils.StdOutUtils.gitStatus; import static org.nqm.utils.StdOutUtils.gitStatusOneLine; @@ -95,7 +95,7 @@ private static void safelyPrint( var sb = new StringBuilder(infof("%s", "" + path.getFileName())); var isOneLineOpt = Stream.of(gisOptions).anyMatch("--gis-one-line"::equals); while (isNotBlank(line = input.readLine())) { - if (commandWithArgs[1].equals(GitCommand.GIT_STATUS)) { + if (commandWithArgs[1].equals(GisCommand.GIT_STATUS)) { sb.append(isOneLineOpt ? gitStatusOneLine(line) : gitStatus(line)); } else { sb.append("%n %s".formatted(line)); diff --git a/src/main/java/org/nqm/command/GitCommand.java b/src/main/java/org/nqm/command/GisCommand.java similarity index 93% rename from src/main/java/org/nqm/command/GitCommand.java rename to src/main/java/org/nqm/command/GisCommand.java index 24c54b1..187ae67 100644 --- a/src/main/java/org/nqm/command/GitCommand.java +++ b/src/main/java/org/nqm/command/GisCommand.java @@ -1,6 +1,7 @@ package org.nqm.command; import static org.nqm.command.Wrapper.forEachModuleDo; +import static org.nqm.command.Wrapper.forEachModuleStatus; import static org.nqm.command.Wrapper.forEachModuleWith; import static org.nqm.config.GisConfig.currentDir; import java.io.BufferedReader; @@ -15,9 +16,13 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Optional; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Predicate; import java.util.regex.Pattern; +import java.util.stream.Collectors; import java.util.stream.Stream; +import org.eclipse.jgit.api.errors.GitAPIException; import org.nqm.GisException; import org.nqm.config.GisConfig; import org.nqm.config.GisLog; @@ -28,7 +33,7 @@ import picocli.CommandLine.Option; import picocli.CommandLine.Parameters; -public class GitCommand { +public class GisCommand { private static final String CHECKOUT = "checkout"; private static final String FETCHED_AT = "(fetched at: %s)"; @@ -47,12 +52,18 @@ void pull() throws IOException { } @Command(name = GIT_STATUS, aliases = "st", description = "Show the working trees status") - void status(@Option(names = "--one-line") boolean oneLineOpt) throws IOException { - if (oneLineOpt) { - forEachModuleDo(GIT_STATUS, "-sb", "--ignore-submodules", "--porcelain=v2", "--gis-one-line"); - } else { - forEachModuleDo(GIT_STATUS, "-sb", "--ignore-submodules", "--porcelain=v2"); - } + void status(@Option(names = "--one-line") boolean oneLiner) throws IOException { + var result = new ConcurrentLinkedQueue(); + forEachModuleStatus(f -> { + try { + result.add(GitStatusCommand.status(f, oneLiner)); + } catch (IOException | GitAPIException e) { + GisLog.debug(e); + } + }); + StdOutUtils.println(String.join( + GisStringUtils.NEWLINE, + result.stream().collect(Collectors.toCollection(TreeSet::new)))); if (Files.exists(TMP_FILE)) { var lastFetched = Files.readString(TMP_FILE); if (GisStringUtils.isNotBlank(lastFetched)) { @@ -235,7 +246,7 @@ void generateCompletion( try (var out = new FileOutputStream(file.toFile())) { while ((line = buffer.readLine()) != null) { out.write(line.getBytes()); - out.write("%n".formatted().getBytes()); + out.write(GisStringUtils.NEWLINE.getBytes()); } } return; diff --git a/src/main/java/org/nqm/command/GitStatusCommand.java b/src/main/java/org/nqm/command/GitStatusCommand.java new file mode 100644 index 0000000..640ed63 --- /dev/null +++ b/src/main/java/org/nqm/command/GitStatusCommand.java @@ -0,0 +1,138 @@ +package org.nqm.command; + +import static org.nqm.model.FileStatus.ADDED; +import static org.nqm.model.FileStatus.CHANGED; +import static org.nqm.model.FileStatus.CONFLICT; +import static org.nqm.model.FileStatus.MISSING; +import static org.nqm.model.FileStatus.MODIFIED; +import static org.nqm.model.FileStatus.REMOVED; +import static org.nqm.model.FileStatus.UNTRACKED; +import static org.nqm.model.FileStatus.UNTRACKED_DIRS; +import static org.nqm.utils.GisStringUtils.getDirectoryName; +import static org.nqm.utils.StdOutUtils.CL_GREEN; +import static org.nqm.utils.StdOutUtils.CL_RED; +import static org.nqm.utils.StdOutUtils.coloringBranch; +import static org.nqm.utils.StdOutUtils.coloringWord; +import static org.nqm.utils.StdOutUtils.infof; +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.Status; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.BranchConfig; +import org.eclipse.jgit.lib.BranchTrackingStatus; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.eclipse.jgit.submodule.SubmoduleWalk.IgnoreSubmoduleMode; +import org.nqm.model.FileStatus; +import org.nqm.utils.GisStringUtils; +import org.nqm.utils.StdOutUtils; + +public class GitStatusCommand { + + private static Repository openRepo(Path f) throws IOException { + var repo = new FileRepositoryBuilder().readEnvironment(); + if (f != null) { + repo.findGitDir(f.toFile()); + } else { + repo.findGitDir(); + } + return repo.build(); + } + + private static String aheadBehind(Repository repo) throws IOException { + var trackingStatus = BranchTrackingStatus.of(repo, repo.getBranch()); + int ahead = trackingStatus.getAheadCount(); + int behind = trackingStatus.getBehindCount(); + return StdOutUtils.buildAheadBehind(ahead, behind); + } + + private static String statusOneLiner(Repository repo, Status status, String localBranch) + throws IOException { + // TODO: add styling modified and changed files + // TODO: add styling for upstream? + var result = new StringBuilder(" ") + .append(coloringBranch(localBranch)) + .append(aheadBehind(repo)); + + var updatedFiles = Stream.of(status.getModified().stream(), + status.getChanged().stream(), + status.getAdded().stream(), + status.getMissing().stream(), + status.getRemoved().stream(), + status.getUntracked().stream(), + status.getConflicting().stream(), + status.getUntrackedFolders().stream()) + .reduce(Stream::concat) + .orElseGet(Stream::empty) + .distinct() + .collect(Collectors.joining(" ")); + if (GisStringUtils.isNotBlank(updatedFiles)) { + result.append(" ").append(updatedFiles); + } + return result.toString(); + } + + private static String statusFull(Repository repo, Status status, String localBranch) throws IOException { + var trackingBranch = new BranchConfig(repo.getConfig(), localBranch) + .getTrackingBranch() + .replace("refs/remotes/", ""); + var result = new StringBuilder() + .append(GisStringUtils.NEWLINE) + .append(" ## ") + .append(coloringWord(localBranch, CL_GREEN)) + .append("...") + .append(coloringWord(trackingBranch, CL_RED)) + .append(" ") + .append(aheadBehind(repo)); + + var statusByFiles = new HashMap>(); + + Function> computeStatus = s -> f -> { + var value = statusByFiles.computeIfAbsent(f, v -> new ArrayList()); + value.add(s); + statusByFiles.put(f, value); + }; + + // TODO how do we know of file rename status + status.getModified().stream().forEach(computeStatus.apply(MODIFIED)); + status.getChanged().stream().forEach(computeStatus.apply(CHANGED)); + status.getAdded().stream().forEach(computeStatus.apply(ADDED)); + status.getMissing().stream().forEach(computeStatus.apply(MISSING)); + status.getRemoved().stream().forEach(computeStatus.apply(REMOVED)); + status.getUntracked().stream().forEach(computeStatus.apply(UNTRACKED)); + status.getConflicting().stream().forEach(computeStatus.apply(CONFLICT)); + status.getUntrackedFolders().stream().forEach(computeStatus.apply(UNTRACKED_DIRS)); + + statusByFiles.forEach((k, v) -> result.append(GisStringUtils.NEWLINE) + .append(" ") + .append(FileStatus.toPrint(k, v))); + return result.toString(); + } + + public static String status(Path f, boolean oneLiner) throws IOException, GitAPIException { + var repo = openRepo(f.resolve(".git")); + Status status; + try (var git = new Git(repo)) { + status = git.status() + .setIgnoreSubmodules(IgnoreSubmoduleMode.ALL) + .call(); + } + if (status == null) { + return ""; + } + var result = new StringBuilder(infof(getDirectoryName(repo.getWorkTree().toPath()))); + var localBranch = repo.getBranch(); + result.append(oneLiner + ? statusOneLiner(repo, status, localBranch) + : statusFull(repo, status, localBranch)); + return result.toString(); + } +} diff --git a/src/main/java/org/nqm/command/Wrapper.java b/src/main/java/org/nqm/command/Wrapper.java index 004689a..32ae5e9 100644 --- a/src/main/java/org/nqm/command/Wrapper.java +++ b/src/main/java/org/nqm/command/Wrapper.java @@ -9,10 +9,11 @@ import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; -import org.nqm.config.GisLog; import org.nqm.GisException; +import org.nqm.config.GisLog; import org.nqm.utils.StdOutUtils; public final class Wrapper { @@ -69,4 +70,26 @@ public static void forEachModuleWith(Predicate pred, String... args) throw public static void forEachModuleDo(String... args) throws IOException { forEachModuleWith(p -> true, args); } + + public static void forEachModuleStatus(Consumer gitStatus) throws IOException { + var gitModulesFilePath = getFileMarker(); + var currentDir = currentDir(); + try (var exe = Executors.newVirtualThreadPerTaskExecutor()) { + Optional.of(Path.of(currentDir)) + .ifPresent(root -> exe.submit(() -> gitStatus.accept(root))); + Files.readAllLines(gitModulesFilePath.toPath()).stream() + .map(String::trim) + .filter(s -> s.startsWith("path")) + .map(s -> s.replace("path = ", "")) + .map(dir -> Path.of(currentDir, dir)) + .filter(dir -> { + if (dir.toFile().exists()) { + return true; + } + StdOutUtils.errln("directory '%s' does not exist, will be ignored!".formatted("" + dir)); + return false; + }) + .forEach(dir -> exe.submit(() -> gitStatus.accept(dir))); + } + } } diff --git a/src/main/java/org/nqm/config/GisConfig.java b/src/main/java/org/nqm/config/GisConfig.java index bb0575b..9141980 100644 --- a/src/main/java/org/nqm/config/GisConfig.java +++ b/src/main/java/org/nqm/config/GisConfig.java @@ -47,7 +47,6 @@ public static String[] getDefaultBranches() { public static String[] getFeatureBranchPrefixes() { return Optional.of(props) .map(props -> props.getProperty(FEATURE_BRANCH_PREFIXES_KEY)) - .map(String::toLowerCase) .map(splitValue) .orElse(FEATURE_BRANCH_PREFIX_VALS); } diff --git a/src/main/java/org/nqm/model/FileStatus.java b/src/main/java/org/nqm/model/FileStatus.java new file mode 100644 index 0000000..41932ff --- /dev/null +++ b/src/main/java/org/nqm/model/FileStatus.java @@ -0,0 +1,58 @@ +package org.nqm.model; + +import static org.nqm.utils.StdOutUtils.CL_GREEN; +import static org.nqm.utils.StdOutUtils.CL_RED; +import static org.nqm.utils.StdOutUtils.coloringWord; +import java.util.List; + +public enum FileStatus { + + // @formatter:off + MODIFIED(1, "M"), + CHANGED(0, "M"), + ADDED(0, "A"), + MISSING(1, "D"), + REMOVED(0, "D"), + UNTRACKED(1, "?"), + CONFLICT(1, "U"), + UNTRACKED_DIRS(1, "?"); + // @formatter:on + + /** + * The level base on staging, 0 means staged, 1 means unstagged + */ + private int level; + + private String symbol; + + FileStatus(int level, String symbol) { + this.level = level; + this.symbol = symbol; + } + + public static String toPrint(String file, List statuses) { + if (statuses == null || statuses.isEmpty()) { + return ""; + } + var size = statuses.size(); + var result = "%s%s %s"; + if (size > 1) { + statuses.sort((a, b) -> a.level - b.level); + var staged = statuses.getFirst(); + var unstaged = statuses.getLast(); + result = result.formatted( + coloringWord(staged.symbol, CL_GREEN), + coloringWord(unstaged.symbol, CL_RED), + file); + } else { + var st = statuses.getFirst(); + int level = st.level; + if (level == 0) { + result = result.formatted(coloringWord(st.symbol, CL_GREEN), ".", file); + } else { + result = result.formatted(".", coloringWord(st.symbol, CL_RED), file); + } + } + return result; + } +} diff --git a/src/main/java/org/nqm/utils/GisStringUtils.java b/src/main/java/org/nqm/utils/GisStringUtils.java index 358bf56..8cb1ff4 100644 --- a/src/main/java/org/nqm/utils/GisStringUtils.java +++ b/src/main/java/org/nqm/utils/GisStringUtils.java @@ -2,11 +2,14 @@ import java.io.ByteArrayInputStream; import java.io.InputStreamReader; +import java.nio.file.Path; public class GisStringUtils { private GisStringUtils() {} + public static final String NEWLINE = "%n".formatted(); + public static boolean isNotBlank(String s) { return s != null && !s.isBlank(); } @@ -18,4 +21,8 @@ public static boolean isBlank(String s) { public static InputStreamReader toInputStreamReader(String s) { return new InputStreamReader(new ByteArrayInputStream(s.getBytes())); } + + public static String getDirectoryName(Path p) { + return "" + p.subpath(p.getNameCount() - 1, p.getNameCount()); + } } diff --git a/src/main/java/org/nqm/utils/StdOutUtils.java b/src/main/java/org/nqm/utils/StdOutUtils.java index 37aeab7..0232a6c 100644 --- a/src/main/java/org/nqm/utils/StdOutUtils.java +++ b/src/main/java/org/nqm/utils/StdOutUtils.java @@ -59,11 +59,15 @@ public static void debugln(String msg) { out.println(" " + CL_YELLOW + "[DEBUG] " + msg + CL_RESET); } - public static String infof(String msgFormat, String word) { + public static String infof(String msgFormat, String word) { // TODO: remove this return msgFormat.formatted(CL_CYAN + word + CL_RESET); } - private static String coloringBranch(String branch) { + public static String infof(String word) { + return "%s".formatted(CL_CYAN + word + CL_RESET); + } + + public static String coloringBranch(String branch) { if (Stream.of(GisConfig.getDefaultBranches()).anyMatch(branch::equals)) { return coloringWord(branch, CL_RED); } @@ -75,7 +79,7 @@ private static String coloringBranch(String branch) { return coloringWord(branch, CL_GREEN); } - private static String coloringWord(String word, String color) { + public static String coloringWord(String word, String color) { return color + word + CL_RESET; } @@ -96,6 +100,26 @@ private static String buildStaging(char[] chars) { + " "; } + public static String buildAheadBehind(int a, int b) { + if (a == 0 && b == 0) { + return ""; + } + var ahead = Optional.of(a) + .filter(x -> x > 0) + .map(x -> "" + x) + .map(s -> "ahead " + coloringWord(s, CL_GREEN)) + .orElse(""); + var behind = Optional.of(b) + .filter(x -> x > 0) + .map(x -> "" + x) + .map(s -> "behind " + coloringWord(s, CL_RED)) + .orElse(""); + return "[%s]".formatted(Stream.of(ahead, behind) + .filter(not(String::isBlank)) + .collect(Collectors.joining(", "))); + } + + @Deprecated(since = "2.0.0") private static String buildAheadBehind(String[] splitS) { var ahead = Optional.of(splitS[2]) .map(s -> s.replace("+", "")) diff --git a/src/test/java/org/nqm/command/GitCommandIntTest.java b/src/test/java/org/nqm/command/GitCommandIntTest.java index 4829452..366f8b2 100644 --- a/src/test/java/org/nqm/command/GitCommandIntTest.java +++ b/src/test/java/org/nqm/command/GitCommandIntTest.java @@ -17,15 +17,16 @@ import org.nqm.helper.GisConfigMock; import org.nqm.helper.GisProcessUtilsMock; import org.nqm.helper.GitBaseTest; +import org.nqm.utils.GisStringUtils; class GitCommandIntTest extends GitBaseTest { - private GitCommand gis; + private GisCommand gis; @Override protected void additionalSetup() { GisConfigMock.mockCurrentDirectory("" + tempPath); - gis = new GitCommand(); + gis = new GisCommand(); } @Override @@ -137,7 +138,7 @@ void listBranches_withoutModuleNames_OK() throws IOException { gis.listBranches(true, false); // then: - assertThat(outCaptor.toString().split("%n".formatted())).containsExactlyInAnyOrder( + assertThat(outCaptor.toString().split(GisStringUtils.NEWLINE)).containsExactlyInAnyOrder( "bb1", "master", "bb1", "master", "bb1", "master"); } @@ -162,7 +163,7 @@ void listBranchesWithRemote_withoutModuleNames_OK() throws IOException { gis.listBranches(true, true); // then: - assertThat(outCaptor.toString().split("%n".formatted())) + assertThat(outCaptor.toString().split(GisStringUtils.NEWLINE)) .containsExactlyInAnyOrder( "bb1", "bb2", @@ -193,7 +194,7 @@ void listFilesChanged_OK() throws IOException { gis.files(); // then: - assertThat(outCaptor.toString().split("%n".formatted())).containsExactlyInAnyOrder( + assertThat(outCaptor.toString().split(GisStringUtils.NEWLINE)).containsExactlyInAnyOrder( "otq_1_a/filescramble1", "otq_3_c/filescramble1", "otq_2_b/filescramble1"); diff --git a/src/test/java/org/nqm/command/GitCommandTest.java b/src/test/java/org/nqm/command/GitCommandTest.java index bd57783..03b8822 100644 --- a/src/test/java/org/nqm/command/GitCommandTest.java +++ b/src/test/java/org/nqm/command/GitCommandTest.java @@ -5,7 +5,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.nqm.command.GitCommand.GIS_AUTOCOMPLETE_FILE; +import static org.nqm.command.GisCommand.GIS_AUTOCOMPLETE_FILE; import static org.nqm.config.GisConfig.GIT_HOME_DIR; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -27,7 +27,7 @@ @ExtendWith(MockitoExtension.class) class GitCommandTest extends StdBaseTest { - private GitCommand gis; + private GisCommand gis; @TempDir private Path tempPath; @@ -47,7 +47,7 @@ void ignoreMarkerFile() throws IOException { @Override protected void additionalSetup() throws IOException { - gis = new GitCommand(); + gis = new GisCommand(); ignoreMarkerFile(); try { @@ -431,7 +431,7 @@ void generateCompletionToFile_withFileAlreadyExist_shouldOverwrite() throws IOEx @Test void confirmYesPattern_false() throws Exception { // given: - var pattern = GitCommand.CONFIRM_YES; + var pattern = GisCommand.CONFIRM_YES; // then: assertThat(pattern.matcher("Yey").matches()).isFalse(); @@ -447,7 +447,7 @@ void confirmYesPattern_false() throws Exception { @Test void confirmYesPattern_true() throws Exception { // given: - var pattern = GitCommand.CONFIRM_YES; + var pattern = GisCommand.CONFIRM_YES; // then: assertThat(pattern.matcher("YES").matches()).isTrue(); diff --git a/src/test/java/org/nqm/command/GitCommandWithoutDependMarkerFileTest.java b/src/test/java/org/nqm/command/GitCommandWithoutDependMarkerFileTest.java index 4f1e756..9e6c9e8 100644 --- a/src/test/java/org/nqm/command/GitCommandWithoutDependMarkerFileTest.java +++ b/src/test/java/org/nqm/command/GitCommandWithoutDependMarkerFileTest.java @@ -10,7 +10,7 @@ class GitCommandWithoutDependMarkerFileTest extends StdBaseTest { @Test void pull_withoutAnyMarkerFiles_NOK() { // given: - var gis = new GitCommand(); + var gis = new GisCommand(); // when then: Assertions.assertThatThrownBy(() -> gis.pull()) diff --git a/src/test/java/org/nqm/helper/GisConfigMock.java b/src/test/java/org/nqm/helper/GisConfigMock.java index c2b43a0..aa0aaf3 100644 --- a/src/test/java/org/nqm/helper/GisConfigMock.java +++ b/src/test/java/org/nqm/helper/GisConfigMock.java @@ -19,10 +19,18 @@ public static void mockBranchesColorDefault() { if (mock == null || mock.isClosed()) { mock = Mockito.mockStatic(GisConfig.class); } - mock.when(GisConfig::getDefaultBranches).thenReturn(new String[] {"master"}); + mock.when(GisConfig::getDefaultBranches).thenReturn(new String[] {"master", "main", "develop"}); mock.when(GisConfig::getFeatureBranchPrefixes).thenReturn(new String[] {"feature/"}); } + public static void mockBranchesColorDefault(String[] defaultBranches, String[] prefixes) { + if (mock == null || mock.isClosed()) { + mock = Mockito.mockStatic(GisConfig.class); + } + mock.when(GisConfig::getDefaultBranches).thenReturn(defaultBranches); + mock.when(GisConfig::getFeatureBranchPrefixes).thenReturn(prefixes); + } + public static void close() { if (mock != null && !mock.isClosed()) { mock.close(); diff --git a/src/test/java/org/nqm/helper/StdBaseTest.java b/src/test/java/org/nqm/helper/StdBaseTest.java index 988686f..fcf14e9 100644 --- a/src/test/java/org/nqm/helper/StdBaseTest.java +++ b/src/test/java/org/nqm/helper/StdBaseTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.nqm.config.GisLog; +import org.nqm.utils.GisStringUtils; import org.nqm.utils.StdOutUtils; public abstract class StdBaseTest { @@ -56,7 +57,7 @@ protected void resetOutputStreamTest() { } protected static Function> stripColors = - str -> Stream.of(str.split("%n".formatted())) + str -> Stream.of(str.split(GisStringUtils.NEWLINE)) .map(s -> s.replace(CL_RESET, "")) .map(s -> s.replace(CL_BLACK, "")) .map(s -> s.replace(CL_RED, "")) diff --git a/src/test/java/org/nqm/utils/GisProcessUtilsTest.java b/src/test/java/org/nqm/utils/GisProcessUtilsTest.java index 192b9f8..198ae89 100644 --- a/src/test/java/org/nqm/utils/GisProcessUtilsTest.java +++ b/src/test/java/org/nqm/utils/GisProcessUtilsTest.java @@ -19,7 +19,7 @@ void run_OK() throws IOException, InterruptedException { // then: assertThat(result.exitCode()).isZero(); - assertThat(result.output()).isEqualTo("" + tempPath + "%n".formatted()); + assertThat(result.output()).isEqualTo("" + tempPath + GisStringUtils.NEWLINE); } @Test @@ -38,7 +38,7 @@ void quickRun_OK() throws IOException { // then: assertThat(result.exitCode()).isZero(); - assertThat(result.output()).isEqualTo("" + tempPath + "%n".formatted()); + assertThat(result.output()).isEqualTo("" + tempPath + GisStringUtils.NEWLINE); } @Test diff --git a/src/test/java/org/nqm/utils/StdOutUtilsTest.java b/src/test/java/org/nqm/utils/StdOutUtilsTest.java index 4083140..ad20bbb 100644 --- a/src/test/java/org/nqm/utils/StdOutUtilsTest.java +++ b/src/test/java/org/nqm/utils/StdOutUtilsTest.java @@ -7,10 +7,21 @@ import static org.nqm.utils.StdOutUtils.CL_RESET; import static org.nqm.utils.StdOutUtils.CL_YELLOW; import org.junit.jupiter.api.Test; +import org.nqm.helper.GisConfigMock; import org.nqm.helper.StdBaseTest; class StdOutUtilsTest extends StdBaseTest { + @Override + protected void additionalSetup() { + GisConfigMock.mockBranchesColorDefault(); + } + + @Override + protected void additionalTeardown() { + GisConfigMock.close(); + } + @Test void debugln_OK() { StdOutUtils.debugln("debugln_7_b"); @@ -125,6 +136,30 @@ void gitStatusOneLine_OK() { .isEqualTo(" StdOutUtilsTest.java"); } + @Test + void gitStatusOneLine_withConfigBranchPrefixes_shouldCompareCaseSensitive() { + // given: + GisConfigMock.mockBranchesColorDefault(new String[]{"Master", "MAIN"}, new String[]{"FEA-ture"}); + + // then: + assertThat(StdOutUtils.gitStatusOneLine("# branch.head Master")) + .isEqualTo(" %s".formatted(coloringWord("Master", CL_RED))); + assertThat(StdOutUtils.gitStatusOneLine("# branch.head masTer")) + .isEqualTo(" %s".formatted(coloringWord("masTer", CL_GREEN))); + + assertThat(StdOutUtils.gitStatusOneLine("# branch.head MAIN")) + .isEqualTo(" %s".formatted(coloringWord("MAIN", CL_RED))); + assertThat(StdOutUtils.gitStatusOneLine("# branch.head main")) + .isEqualTo(" %s".formatted(coloringWord("main", CL_GREEN))); + + assertThat(StdOutUtils.gitStatusOneLine("# branch.head FEA-ture/destroy-d-b")) + .isEqualTo(" %s".formatted(coloringWord("FEA-ture/destroy-d-b", CL_YELLOW))); + assertThat(StdOutUtils.gitStatusOneLine("# branch.head Fea-ture/destroy-d-b")) + .isEqualTo(" %s".formatted(coloringWord("Fea-ture/destroy-d-b", CL_GREEN))); + assertThat(StdOutUtils.gitStatusOneLine("# branch.head fea-ture/destroy-d-b")) + .isEqualTo(" %s".formatted(coloringWord("fea-ture/destroy-d-b", CL_GREEN))); + } + @Test void print_OK() { // when: