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 assumeLinuxPlatform();
85
86 String emptyToolName = "";
87
88 StoredConfig config = db.getConfig();
89
90 String subsection = null;
91 config.setString(CONFIG_DIFF_SECTION, subsection, CONFIG_KEY_TOOL,
92 emptyToolName);
93
94 createUnstagedChanges();
95
96 String araxisErrorLine = "compare: unrecognized option `-wait' @ error/compare.c/CompareImageCommand/1123.";
97 String[] expectedErrorOutput = { araxisErrorLine, araxisErrorLine, };
98 runAndCaptureUsingInitRaw(Arrays.asList(expectedErrorOutput), DIFF_TOOL,
99 "--no-prompt");
100 fail("Expected exception to be thrown due to external tool exiting with an error");
101 }
102
103 @Test
104 public void testToolWithPrompt() throws Exception {
105 String[] inputLines = {
106 "y",
107 "y",
108 };
109
110 String[] conflictingFilenames = createUnstagedChanges();
111 String[] expectedOutput = getExpectedCompareOutput(conflictingFilenames);
112
113 String option = "--tool";
114
115 InputStream inputStream = createInputStream(inputLines);
116 assertArrayOfLinesEquals("Incorrect output for option: " + option,
117 expectedOutput, runAndCaptureUsingInitRaw(inputStream,
118 DIFF_TOOL, "--prompt", option, TOOL_NAME));
119 }
120
121 @Test
122 public void testToolAbortLaunch() throws Exception {
123 String[] inputLines = {
124 "y",
125 "n",
126 };
127
128 String[] conflictingFilenames = createUnstagedChanges();
129 int abortIndex = 1;
130 String[] expectedOutput = getExpectedAbortOutput(conflictingFilenames, abortIndex);
131
132 String option = "--tool";
133
134 InputStream inputStream = createInputStream(inputLines);
135 assertArrayOfLinesEquals("Incorrect output for option: " + option,
136 expectedOutput,
137 runAndCaptureUsingInitRaw(inputStream, DIFF_TOOL, "--prompt", option,
138 TOOL_NAME));
139 }
140
141 @Test(expected = Die.class)
142 public void testNotDefinedTool() throws Exception {
143 createUnstagedChanges();
144
145 runAndCaptureUsingInitRaw(DIFF_TOOL, "--tool", "undefined");
146 fail("Expected exception when trying to run undefined tool");
147 }
148
149 @Test
150 public void testTool() throws Exception {
151 String[] conflictFilenames = createUnstagedChanges();
152 String[] expectedOutput = getExpectedToolOutputNoPrompt(conflictFilenames);
153
154 String[] options = {
155 "--tool",
156 "-t",
157 };
158
159 for (String option : options) {
160 assertArrayOfLinesEquals("Incorrect output for option: " + option,
161 expectedOutput,
162 runAndCaptureUsingInitRaw(DIFF_TOOL, option,
163 TOOL_NAME));
164 }
165 }
166
167 @Test
168 public void testToolTrustExitCode() throws Exception {
169 String[] conflictingFilenames = createUnstagedChanges();
170 String[] expectedOutput = getExpectedToolOutputNoPrompt(conflictingFilenames);
171
172 String[] options = { "--tool", "-t", };
173
174 for (String option : options) {
175 assertArrayOfLinesEquals("Incorrect output for option: " + option,
176 expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
177 "--trust-exit-code", option, TOOL_NAME));
178 }
179 }
180
181 @Test
182 public void testToolNoGuiNoPromptNoTrustExitcode() throws Exception {
183 String[] conflictingFilenames = createUnstagedChanges();
184 String[] expectedOutput = getExpectedToolOutputNoPrompt(conflictingFilenames);
185
186 String[] options = { "--tool", "-t", };
187
188 for (String option : options) {
189 assertArrayOfLinesEquals("Incorrect output for option: " + option,
190 expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
191 "--no-gui", "--no-prompt", "--no-trust-exit-code",
192 option, TOOL_NAME));
193 }
194 }
195
196 @Test
197 public void testToolCached() throws Exception {
198 String[] conflictingFilenames = createStagedChanges();
199 Pattern[] expectedOutput = getExpectedCachedToolOutputNoPrompt(conflictingFilenames);
200
201 String[] options = { "--cached", "--staged", };
202
203 for (String option : options) {
204 assertArrayOfMatchingLines("Incorrect output for option: " + option,
205 expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
206 option, "--tool", TOOL_NAME));
207 }
208 }
209
210 @Test
211 public void testToolHelp() throws Exception {
212 List<String> expectedOutput = new ArrayList<>();
213
214 DiffTools diffTools = new DiffTools(db);
215 Map<String, ExternalDiffTool> predefinedTools = diffTools
216 .getPredefinedTools(true);
217 List<ExternalDiffTool> availableTools = new ArrayList<>();
218 List<ExternalDiffTool> notAvailableTools = new ArrayList<>();
219 for (ExternalDiffTool tool : predefinedTools.values()) {
220 if (tool.isAvailable()) {
221 availableTools.add(tool);
222 } else {
223 notAvailableTools.add(tool);
224 }
225 }
226
227 expectedOutput.add(
228 "'git difftool --tool=<tool>' may be set to one of the following:");
229 for (ExternalDiffTool tool : availableTools) {
230 String toolName = tool.getName();
231 expectedOutput.add(toolName);
232 }
233 String customToolHelpLine = TOOL_NAME + "." + CONFIG_KEY_CMD + " "
234 + getEchoCommand();
235 expectedOutput.add("user-defined:");
236 expectedOutput.add(customToolHelpLine);
237 expectedOutput.add(
238 "The following tools are valid, but not currently available:");
239 for (ExternalDiffTool tool : notAvailableTools) {
240 String toolName = tool.getName();
241 expectedOutput.add(toolName);
242 }
243 String[] userDefinedToolsHelp = {
244 "Some of the tools listed above only work in a windowed",
245 "environment. If run in a terminal-only session, they will fail.",
246 };
247 expectedOutput.addAll(Arrays.asList(userDefinedToolsHelp));
248
249 String option = "--tool-help";
250 assertArrayOfLinesEquals("Incorrect output for option: " + option,
251 expectedOutput.toArray(new String[0]),
252 runAndCaptureUsingInitRaw(DIFF_TOOL, option));
253 }
254
255 private void configureEchoTool(String toolName) {
256 StoredConfig config = db.getConfig();
257
258 String subsection = null;
259 config.setString(CONFIG_DIFF_SECTION, subsection, CONFIG_KEY_TOOL,
260 toolName);
261
262 String command = getEchoCommand();
263
264 config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_CMD,
265 command);
266
267
268
269
270 config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_PROMPT,
271 String.valueOf(false));
272 }
273
274 private String[] getExpectedToolOutputNoPrompt(String[] conflictingFilenames) {
275 String[] expectedToolOutput = new String[conflictingFilenames.length];
276 for (int i = 0; i < conflictingFilenames.length; ++i) {
277 String newPath = conflictingFilenames[i];
278 Path fullPath = getFullPath(newPath);
279 expectedToolOutput[i] = fullPath.toString();
280 }
281 return expectedToolOutput;
282 }
283
284 private Pattern[] getExpectedCachedToolOutputNoPrompt(String[] conflictingFilenames) {
285 String tmpDir = System.getProperty("java.io.tmpdir");
286 if (tmpDir.endsWith(File.separator)) {
287 tmpDir = tmpDir.substring(0, tmpDir.length() - 1);
288 }
289 Pattern emptyPattern = Pattern.compile("");
290 List<Pattern> expectedToolOutput = new ArrayList<>();
291 for (int i = 0; i < conflictingFilenames.length; ++i) {
292 String changedFilename = conflictingFilenames[i];
293 Path fullPath = getFullPath(changedFilename);
294 String filename = fullPath.getFileName().toString();
295 String regexp = tmpDir + File.separatorChar + filename
296 + "_REMOTE_.*";
297 Pattern pattern = Pattern.compile(regexp);
298 expectedToolOutput.add(pattern);
299 expectedToolOutput.add(emptyPattern);
300 }
301 expectedToolOutput.add(emptyPattern);
302 return expectedToolOutput.toArray(new Pattern[0]);
303 }
304
305 private String[] getExpectedCompareOutput(String[] conflictingFilenames) {
306 List<String> expected = new ArrayList<>();
307 int n = conflictingFilenames.length;
308 for (int i = 0; i < n; ++i) {
309 String changedFilename = conflictingFilenames[i];
310 expected.add(
311 "Viewing (" + (i + 1) + "/" + n + "): '" + changedFilename
312 + "'");
313 expected.add("Launch '" + TOOL_NAME + "' [Y/n]?");
314 Path fullPath = getFullPath(changedFilename);
315 expected.add(fullPath.toString());
316 }
317 return expected.toArray(new String[0]);
318 }
319
320 private String[] getExpectedAbortOutput(String[] conflictingFilenames,
321 int abortIndex) {
322 List<String> expected = new ArrayList<>();
323 int n = conflictingFilenames.length;
324 for (int i = 0; i < n; ++i) {
325 String changedFilename = conflictingFilenames[i];
326 expected.add(
327 "Viewing (" + (i + 1) + "/" + n + "): '" + changedFilename
328 + "'");
329 expected.add("Launch '" + TOOL_NAME + "' [Y/n]?");
330 if (i == abortIndex) {
331 break;
332 }
333 Path fullPath = getFullPath(changedFilename);
334 expected.add(fullPath.toString());
335 }
336 return expected.toArray(new String[0]);
337 }
338
339 private static String getEchoCommand() {
340
341
342
343
344 return "(echo \"$REMOTE\")";
345 }
346 }