View Javadoc
1   /*
2    * Copyright (C) 2021, Simeon Andreev <simeon.danailov.andreev@gmail.com> and others.
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.pgm;
11  
12  import static org.junit.Assert.assertEquals;
13  
14  import java.util.ArrayList;
15  import java.util.Arrays;
16  import java.util.List;
17  
18  import org.eclipse.jgit.api.Git;
19  import org.eclipse.jgit.diff.DiffEntry;
20  import org.eclipse.jgit.internal.diffmergetool.CommandLineDiffTool;
21  import org.eclipse.jgit.lib.CLIRepositoryTestCase;
22  import org.eclipse.jgit.pgm.opt.CmdLineParser;
23  import org.eclipse.jgit.pgm.opt.SubcommandHandler;
24  import org.eclipse.jgit.revwalk.RevCommit;
25  import org.eclipse.jgit.treewalk.FileTreeIterator;
26  import org.eclipse.jgit.treewalk.TreeWalk;
27  import org.junit.Before;
28  import org.junit.Test;
29  import org.kohsuke.args4j.Argument;
30  
31  /**
32   * Testing the {@code difftool} command.
33   */
34  public class DiffToolTest extends CLIRepositoryTestCase {
35  	public static class GitCliJGitWrapperParser {
36  		@Argument(index = 0, metaVar = "metaVar_command", required = true, handler = SubcommandHandler.class)
37  		TextBuiltin subcommand;
38  
39  		@Argument(index = 1, metaVar = "metaVar_arg")
40  		List<String> arguments = new ArrayList<>();
41  	}
42  
43  	private String[] runAndCaptureUsingInitRaw(String... args)
44  			throws Exception {
45  		CLIGitCommand.Result result = new CLIGitCommand.Result();
46  
47  		GitCliJGitWrapperParser bean = new GitCliJGitWrapperParser();
48  		CmdLineParser clp = new CmdLineParser(bean);
49  		clp.parseArgument(args);
50  
51  		TextBuiltin cmd = bean.subcommand;
52  		cmd.initRaw(db, null, null, result.out, result.err);
53  		cmd.execute(bean.arguments.toArray(new String[bean.arguments.size()]));
54  		if (cmd.getOutputWriter() != null) {
55  			cmd.getOutputWriter().flush();
56  		}
57  		if (cmd.getErrorWriter() != null) {
58  			cmd.getErrorWriter().flush();
59  		}
60  		return result.outLines().toArray(new String[0]);
61  	}
62  
63  	private Git git;
64  
65  	@Override
66  	@Before
67  	public void setUp() throws Exception {
68  		super.setUp();
69  		git = new Git(db);
70  		git.commit().setMessage("initial commit").call();
71  	}
72  
73  	@Test
74  	public void testTool() throws Exception {
75  		RevCommit commit = createUnstagedChanges();
76  		List<DiffEntry> changes = getRepositoryChanges(commit);
77  		String[] expectedOutput = getExpectedDiffToolOutput(changes);
78  
79  		String[] options = {
80  				"--tool",
81  				"-t",
82  		};
83  
84  		for (String option : options) {
85  			assertArrayOfLinesEquals("Incorrect output for option: " + option,
86  					expectedOutput,
87  					runAndCaptureUsingInitRaw("difftool", option,
88  							"some_tool"));
89  		}
90  	}
91  
92  	@Test
93  	public void testToolTrustExitCode() throws Exception {
94  		RevCommit commit = createUnstagedChanges();
95  		List<DiffEntry> changes = getRepositoryChanges(commit);
96  		String[] expectedOutput = getExpectedDiffToolOutput(changes);
97  
98  		String[] options = { "--tool", "-t", };
99  
100 		for (String option : options) {
101 			assertArrayOfLinesEquals("Incorrect output for option: " + option,
102 					expectedOutput, runAndCaptureUsingInitRaw("difftool",
103 							"--trust-exit-code", option, "some_tool"));
104 		}
105 	}
106 
107 	@Test
108 	public void testToolNoGuiNoPromptNoTrustExitcode() throws Exception {
109 		RevCommit commit = createUnstagedChanges();
110 		List<DiffEntry> changes = getRepositoryChanges(commit);
111 		String[] expectedOutput = getExpectedDiffToolOutput(changes);
112 
113 		String[] options = { "--tool", "-t", };
114 
115 		for (String option : options) {
116 			assertArrayOfLinesEquals("Incorrect output for option: " + option,
117 					expectedOutput, runAndCaptureUsingInitRaw("difftool",
118 							"--no-gui", "--no-prompt", "--no-trust-exit-code",
119 							option, "some_tool"));
120 		}
121 	}
122 
123 	@Test
124 	public void testToolCached() throws Exception {
125 		RevCommit commit = createStagedChanges();
126 		List<DiffEntry> changes = getRepositoryChanges(commit);
127 		String[] expectedOutput = getExpectedDiffToolOutput(changes);
128 
129 		String[] options = { "--cached", "--staged", };
130 
131 		for (String option : options) {
132 			assertArrayOfLinesEquals("Incorrect output for option: " + option,
133 					expectedOutput, runAndCaptureUsingInitRaw("difftool",
134 							option, "--tool", "some_tool"));
135 		}
136 	}
137 
138 	@Test
139 	public void testToolHelp() throws Exception {
140 		CommandLineDiffTool[] defaultTools = CommandLineDiffTool.values();
141 		List<String> expectedOutput = new ArrayList<>();
142 		expectedOutput.add("git difftool --tool=<tool> may be set to one of the following:");
143 		for (CommandLineDiffTool defaultTool : defaultTools) {
144 			String toolName = defaultTool.name();
145 			expectedOutput.add(toolName);
146 		}
147 		String[] userDefinedToolsHelp = {
148 				"user-defined:",
149 				"The following tools are valid, but not currently available:",
150 				"Some of the tools listed above only work in a windowed",
151 				"environment. If run in a terminal-only session, they will fail.",
152 		};
153 		expectedOutput.addAll(Arrays.asList(userDefinedToolsHelp));
154 
155 		String option = "--tool-help";
156 		assertArrayOfLinesEquals("Incorrect output for option: " + option,
157 				expectedOutput.toArray(new String[0]), runAndCaptureUsingInitRaw("difftool", option));
158 	}
159 
160 	private RevCommit createUnstagedChanges() throws Exception {
161 		writeTrashFile("a", "Hello world a");
162 		writeTrashFile("b", "Hello world b");
163 		git.add().addFilepattern(".").call();
164 		RevCommit commit = git.commit().setMessage("files a & b").call();
165 		writeTrashFile("a", "New Hello world a");
166 		writeTrashFile("b", "New Hello world b");
167 		return commit;
168 	}
169 
170 	private RevCommit createStagedChanges() throws Exception {
171 		RevCommit commit = createUnstagedChanges();
172 		git.add().addFilepattern(".").call();
173 		return commit;
174 	}
175 
176 	private List<DiffEntry> getRepositoryChanges(RevCommit commit)
177 			throws Exception {
178 		TreeWalk tw = new TreeWalk(db);
179 		tw.addTree(commit.getTree());
180 		FileTreeIterator modifiedTree = new FileTreeIterator(db);
181 		tw.addTree(modifiedTree);
182 		List<DiffEntry> changes = DiffEntry.scan(tw);
183 		return changes;
184 	}
185 
186 	private String[] getExpectedDiffToolOutput(List<DiffEntry> changes) {
187 		String[] expectedToolOutput = new String[changes.size()];
188 		for (int i = 0; i < changes.size(); ++i) {
189 			DiffEntry change = changes.get(i);
190 			String newPath = change.getNewPath();
191 			String oldPath = change.getOldPath();
192 			String newIdName = change.getNewId().name();
193 			String oldIdName = change.getOldId().name();
194 			String expectedLine = "M\t" + newPath + " (" + newIdName + ")"
195 					+ "\t" + oldPath + " (" + oldIdName + ")";
196 			expectedToolOutput[i] = expectedLine;
197 		}
198 		return expectedToolOutput;
199 	}
200 
201 	private static void assertArrayOfLinesEquals(String failMessage,
202 			String[] expected, String[] actual) {
203 		assertEquals(failMessage, toString(expected), toString(actual));
204 	}
205 }