1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.pgm;
11
12 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFFTOOL_SECTION;
13 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFF_SECTION;
14 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CMD;
15 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PROMPT;
16 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL;
17 import static org.junit.Assert.fail;
18
19 import java.io.File;
20 import java.io.InputStream;
21 import java.nio.file.Path;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.regex.Pattern;
27
28 import org.eclipse.jgit.internal.diffmergetool.DiffTools;
29 import org.eclipse.jgit.internal.diffmergetool.ExternalDiffTool;
30 import org.eclipse.jgit.lib.StoredConfig;
31 import org.junit.Before;
32 import org.junit.Test;
33
34
35
36
37 public class DiffToolTest extends ToolTestCase {
38
39 private static final String DIFF_TOOL = CONFIG_DIFFTOOL_SECTION;
40
41 @Override
42 @Before
43 public void setUp() throws Exception {
44 super.setUp();
45 configureEchoTool(TOOL_NAME);
46 }
47
48 @Test(expected = Die.class)
49 public void testUndefinedTool() throws Exception {
50 String toolName = "undefined";
51 String[] conflictingFilenames = createUnstagedChanges();
52
53 List<String> expectedErrors = new ArrayList<>();
54 for (String changedFilename : conflictingFilenames) {
55 expectedErrors.add("External diff tool is not defined: " + toolName);
56 expectedErrors.add("compare of " + changedFilename + " failed");
57 }
58
59 runAndCaptureUsingInitRaw(expectedErrors, DIFF_TOOL, "--no-prompt",
60 "--tool", toolName);
61 fail("Expected exception to be thrown due to undefined external tool");
62 }
63
64 @Test(expected = Die.class)
65 public void testUserToolWithCommandNotFoundError() throws Exception {
66 String toolName = "customTool";
67
68 int errorReturnCode = 127;
69 String command = "exit " + errorReturnCode;
70
71 StoredConfig config = db.getConfig();
72 config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_CMD,
73 command);
74
75 createMergeConflict();
76 runAndCaptureUsingInitRaw(DIFF_TOOL, "--no-prompt", "--tool", toolName);
77
78 fail("Expected exception to be thrown due to external tool exiting with error code: "
79 + errorReturnCode);
80 }
81
82 @Test(expected = Die.class)
83 public void testEmptyToolName() throws Exception {
84 String emptyToolName = "";
85
86 StoredConfig config = db.getConfig();
87
88 String subsection = null;
89 config.setString(CONFIG_DIFF_SECTION, subsection, CONFIG_KEY_TOOL,
90 emptyToolName);
91
92 createUnstagedChanges();
93
94 String araxisErrorLine = "compare: unrecognized option `-wait' @ error/compare.c/CompareImageCommand/1123.";
95 String[] expectedErrorOutput = { araxisErrorLine, araxisErrorLine, };
96 runAndCaptureUsingInitRaw(Arrays.asList(expectedErrorOutput), DIFF_TOOL,
97 "--no-prompt");
98 fail("Expected exception to be thrown due to external tool exiting with an error");
99 }
100
101 @Test
102 public void testToolWithPrompt() throws Exception {
103 String[] inputLines = {
104 "y",
105 "y",
106 };
107
108 String[] conflictingFilenames = createUnstagedChanges();
109 String[] expectedOutput = getExpectedCompareOutput(conflictingFilenames);
110
111 String option = "--tool";
112
113 InputStream inputStream = createInputStream(inputLines);
114 assertArrayOfLinesEquals("Incorrect output for option: " + option,
115 expectedOutput, runAndCaptureUsingInitRaw(inputStream,
116 DIFF_TOOL, "--prompt", option, TOOL_NAME));
117 }
118
119 @Test
120 public void testToolAbortLaunch() throws Exception {
121 String[] inputLines = {
122 "y",
123 "n",
124 };
125
126 String[] conflictingFilenames = createUnstagedChanges();
127 int abortIndex = 1;
128 String[] expectedOutput = getExpectedAbortOutput(conflictingFilenames, abortIndex);
129
130 String option = "--tool";
131
132 InputStream inputStream = createInputStream(inputLines);
133 assertArrayOfLinesEquals("Incorrect output for option: " + option,
134 expectedOutput,
135 runAndCaptureUsingInitRaw(inputStream, DIFF_TOOL, "--prompt", option,
136 TOOL_NAME));
137 }
138
139 @Test(expected = Die.class)
140 public void testNotDefinedTool() throws Exception {
141 createUnstagedChanges();
142
143 runAndCaptureUsingInitRaw(DIFF_TOOL, "--tool", "undefined");
144 fail("Expected exception when trying to run undefined tool");
145 }
146
147 @Test
148 public void testTool() throws Exception {
149 String[] conflictFilenames = createUnstagedChanges();
150 String[] expectedOutput = getExpectedToolOutputNoPrompt(conflictFilenames);
151
152 String[] options = {
153 "--tool",
154 "-t",
155 };
156
157 for (String option : options) {
158 assertArrayOfLinesEquals("Incorrect output for option: " + option,
159 expectedOutput,
160 runAndCaptureUsingInitRaw(DIFF_TOOL, option,
161 TOOL_NAME));
162 }
163 }
164
165 @Test
166 public void testToolTrustExitCode() throws Exception {
167 String[] conflictingFilenames = createUnstagedChanges();
168 String[] expectedOutput = getExpectedToolOutputNoPrompt(conflictingFilenames);
169
170 String[] options = { "--tool", "-t", };
171
172 for (String option : options) {
173 assertArrayOfLinesEquals("Incorrect output for option: " + option,
174 expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
175 "--trust-exit-code", option, TOOL_NAME));
176 }
177 }
178
179 @Test
180 public void testToolNoGuiNoPromptNoTrustExitcode() throws Exception {
181 String[] conflictingFilenames = createUnstagedChanges();
182 String[] expectedOutput = getExpectedToolOutputNoPrompt(conflictingFilenames);
183
184 String[] options = { "--tool", "-t", };
185
186 for (String option : options) {
187 assertArrayOfLinesEquals("Incorrect output for option: " + option,
188 expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
189 "--no-gui", "--no-prompt", "--no-trust-exit-code",
190 option, TOOL_NAME));
191 }
192 }
193
194 @Test
195 public void testToolCached() throws Exception {
196 String[] conflictingFilenames = createStagedChanges();
197 Pattern[] expectedOutput = getExpectedCachedToolOutputNoPrompt(conflictingFilenames);
198
199 String[] options = { "--cached", "--staged", };
200
201 for (String option : options) {
202 assertArrayOfMatchingLines("Incorrect output for option: " + option,
203 expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
204 option, "--tool", TOOL_NAME));
205 }
206 }
207
208 @Test
209 public void testToolHelp() throws Exception {
210 List<String> expectedOutput = new ArrayList<>();
211
212 DiffTools diffTools = new DiffTools(db);
213 Map<String, ExternalDiffTool> predefinedTools = diffTools
214 .getPredefinedTools(true);
215 List<ExternalDiffTool> availableTools = new ArrayList<>();
216 List<ExternalDiffTool> notAvailableTools = new ArrayList<>();
217 for (ExternalDiffTool tool : predefinedTools.values()) {
218 if (tool.isAvailable()) {
219 availableTools.add(tool);
220 } else {
221 notAvailableTools.add(tool);
222 }
223 }
224
225 expectedOutput.add(
226 "'git difftool --tool=<tool>' may be set to one of the following:");
227 for (ExternalDiffTool tool : availableTools) {
228 String toolName = tool.getName();
229 expectedOutput.add(toolName);
230 }
231 String customToolHelpLine = TOOL_NAME + "." + CONFIG_KEY_CMD + " "
232 + getEchoCommand();
233 expectedOutput.add("user-defined:");
234 expectedOutput.add(customToolHelpLine);
235 expectedOutput.add(
236 "The following tools are valid, but not currently available:");
237 for (ExternalDiffTool tool : notAvailableTools) {
238 String toolName = tool.getName();
239 expectedOutput.add(toolName);
240 }
241 String[] userDefinedToolsHelp = {
242 "Some of the tools listed above only work in a windowed",
243 "environment. If run in a terminal-only session, they will fail.",
244 };
245 expectedOutput.addAll(Arrays.asList(userDefinedToolsHelp));
246
247 String option = "--tool-help";
248 assertArrayOfLinesEquals("Incorrect output for option: " + option,
249 expectedOutput.toArray(new String[0]),
250 runAndCaptureUsingInitRaw(DIFF_TOOL, option));
251 }
252
253 private void configureEchoTool(String toolName) {
254 StoredConfig config = db.getConfig();
255
256 String subsection = null;
257 config.setString(CONFIG_DIFF_SECTION, subsection, CONFIG_KEY_TOOL,
258 toolName);
259
260 String command = getEchoCommand();
261
262 config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_CMD,
263 command);
264
265
266
267
268 config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_PROMPT,
269 String.valueOf(false));
270 }
271
272 private String[] getExpectedToolOutputNoPrompt(String[] conflictingFilenames) {
273 String[] expectedToolOutput = new String[conflictingFilenames.length];
274 for (int i = 0; i < conflictingFilenames.length; ++i) {
275 String newPath = conflictingFilenames[i];
276 Path fullPath = getFullPath(newPath);
277 expectedToolOutput[i] = fullPath.toString();
278 }
279 return expectedToolOutput;
280 }
281
282 private Pattern[] getExpectedCachedToolOutputNoPrompt(String[] conflictingFilenames) {
283 String tmpDir = System.getProperty("java.io.tmpdir");
284 if (tmpDir.endsWith(File.separator)) {
285 tmpDir = tmpDir.substring(0, tmpDir.length() - 1);
286 }
287 Pattern emptyPattern = Pattern.compile("");
288 List<Pattern> expectedToolOutput = new ArrayList<>();
289 for (int i = 0; i < conflictingFilenames.length; ++i) {
290 String changedFilename = conflictingFilenames[i];
291 Path fullPath = getFullPath(changedFilename);
292 String filename = fullPath.getFileName().toString();
293 String regexp = tmpDir + File.separatorChar + filename
294 + "_REMOTE_.*";
295 Pattern pattern = Pattern.compile(regexp);
296 expectedToolOutput.add(pattern);
297 expectedToolOutput.add(emptyPattern);
298 }
299 expectedToolOutput.add(emptyPattern);
300 return expectedToolOutput.toArray(new Pattern[0]);
301 }
302
303 private String[] getExpectedCompareOutput(String[] conflictingFilenames) {
304 List<String> expected = new ArrayList<>();
305 int n = conflictingFilenames.length;
306 for (int i = 0; i < n; ++i) {
307 String changedFilename = conflictingFilenames[i];
308 expected.add(
309 "Viewing (" + (i + 1) + "/" + n + "): '" + changedFilename
310 + "'");
311 expected.add("Launch '" + TOOL_NAME + "' [Y/n]?");
312 Path fullPath = getFullPath(changedFilename);
313 expected.add(fullPath.toString());
314 }
315 return expected.toArray(new String[0]);
316 }
317
318 private String[] getExpectedAbortOutput(String[] conflictingFilenames,
319 int abortIndex) {
320 List<String> expected = new ArrayList<>();
321 int n = conflictingFilenames.length;
322 for (int i = 0; i < n; ++i) {
323 String changedFilename = conflictingFilenames[i];
324 expected.add(
325 "Viewing (" + (i + 1) + "/" + n + "): '" + changedFilename
326 + "'");
327 expected.add("Launch '" + TOOL_NAME + "' [Y/n]?");
328 if (i == abortIndex) {
329 break;
330 }
331 Path fullPath = getFullPath(changedFilename);
332 expected.add(fullPath.toString());
333 }
334 return expected.toArray(new String[0]);
335 }
336
337 private static String getEchoCommand() {
338
339
340
341
342 return "(echo \"$REMOTE\")";
343 }
344 }