1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.internal.diffmergetool;
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_GUITOOL;
16 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PATH;
17 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PROMPT;
18 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL;
19 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TRUST_EXIT_CODE;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assert.assertNull;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assert.fail;
26
27 import java.io.File;
28 import java.io.IOException;
29 import java.nio.file.Files;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.LinkedHashSet;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Optional;
36 import java.util.Set;
37
38 import org.eclipse.jgit.junit.TestRepository;
39 import org.eclipse.jgit.lib.Repository;
40 import org.eclipse.jgit.lib.internal.BooleanTriState;
41 import org.eclipse.jgit.storage.file.FileBasedConfig;
42 import org.eclipse.jgit.util.FS.ExecutionResult;
43 import org.junit.Test;
44
45
46
47
48 public class ExternalDiffToolTest extends ExternalToolTestCase {
49
50 @Test(expected = ToolException.class)
51 public void testUserToolWithError() throws Exception {
52 String toolName = "customTool";
53
54 int errorReturnCode = 1;
55 String command = "exit " + errorReturnCode;
56
57 FileBasedConfig config = db.getConfig();
58 config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_CMD,
59 command);
60
61 invokeCompare(toolName);
62
63 fail("Expected exception to be thrown due to external tool exiting with error code: "
64 + errorReturnCode);
65 }
66
67 @Test(expected = ToolException.class)
68 public void testUserToolWithCommandNotFoundError() throws Exception {
69 String toolName = "customTool";
70
71 int errorReturnCode = 127;
72 String command = "exit " + errorReturnCode;
73
74 FileBasedConfig config = db.getConfig();
75 config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_CMD,
76 command);
77
78 invokeCompare(toolName);
79 fail("Expected exception to be thrown due to external tool exiting with error code: "
80 + errorReturnCode);
81 }
82
83 @Test
84 public void testUserDefinedTool() throws Exception {
85 String command = getEchoCommand();
86
87 FileBasedConfig config = db.getConfig();
88 String customToolName = "customTool";
89 config.setString(CONFIG_DIFFTOOL_SECTION, customToolName,
90 CONFIG_KEY_CMD, command);
91
92 DiffTools manager = new DiffTools(db);
93
94 Map<String, ExternalDiffTool> tools = manager.getUserDefinedTools();
95 ExternalDiffTool externalTool = tools.get(customToolName);
96 boolean trustExitCode = true;
97 manager.compare(local, remote, externalTool, trustExitCode);
98
99 assertEchoCommandHasCorrectOutput();
100 }
101
102 @Test
103 public void testUserDefinedToolWithPrompt() throws Exception {
104 String command = getEchoCommand();
105
106 FileBasedConfig config = db.getConfig();
107 String customToolName = "customTool";
108 config.setString(CONFIG_DIFFTOOL_SECTION, customToolName,
109 CONFIG_KEY_CMD, command);
110
111 DiffTools manager = new DiffTools(db);
112
113 PromptHandler promptHandler = PromptHandler.acceptPrompt();
114 MissingToolHandler noToolHandler = new MissingToolHandler();
115
116 manager.compare(local, remote, Optional.of(customToolName),
117 BooleanTriState.TRUE, false, BooleanTriState.TRUE,
118 promptHandler, noToolHandler);
119
120 assertEchoCommandHasCorrectOutput();
121
122 List<String> actualToolPrompts = promptHandler.toolPrompts;
123 List<String> expectedToolPrompts = Arrays.asList("customTool");
124 assertEquals("Expected a user prompt for custom tool call",
125 expectedToolPrompts, actualToolPrompts);
126
127 assertEquals("Expected to no informing about missing tools",
128 Collections.EMPTY_LIST, noToolHandler.missingTools);
129 }
130
131 @Test
132 public void testUserDefinedToolWithCancelledPrompt() throws Exception {
133 String command = getEchoCommand();
134
135 FileBasedConfig config = db.getConfig();
136 String customToolName = "customTool";
137 config.setString(CONFIG_DIFFTOOL_SECTION, customToolName,
138 CONFIG_KEY_CMD, command);
139
140 DiffTools manager = new DiffTools(db);
141
142 PromptHandler promptHandler = PromptHandler.cancelPrompt();
143 MissingToolHandler noToolHandler = new MissingToolHandler();
144
145 Optional<ExecutionResult> result = manager.compare(local, remote,
146 Optional.of(customToolName), BooleanTriState.TRUE, false,
147 BooleanTriState.TRUE, promptHandler, noToolHandler);
148 assertFalse("Expected no result if user cancels the operation",
149 result.isPresent());
150 }
151
152 @Test
153 public void testAllTools() {
154 FileBasedConfig config = db.getConfig();
155 String customToolName = "customTool";
156 config.setString(CONFIG_DIFFTOOL_SECTION, customToolName,
157 CONFIG_KEY_CMD, "echo");
158
159 DiffTools manager = new DiffTools(db);
160 Set<String> actualToolNames = manager.getAllToolNames();
161 Set<String> expectedToolNames = new LinkedHashSet<>();
162 expectedToolNames.add(customToolName);
163 CommandLineDiffTool[] defaultTools = CommandLineDiffTool.values();
164 for (CommandLineDiffTool defaultTool : defaultTools) {
165 String toolName = defaultTool.name();
166 expectedToolNames.add(toolName);
167 }
168 assertEquals("Incorrect set of external diff tools", expectedToolNames,
169 actualToolNames);
170 }
171
172 @Test
173 public void testOverridePredefinedToolPath() {
174 String toolName = CommandLineDiffTool.guiffy.name();
175 String customToolPath = "/usr/bin/echo";
176
177 FileBasedConfig config = db.getConfig();
178 config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_CMD,
179 "echo");
180 config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_PATH,
181 customToolPath);
182
183 DiffTools manager = new DiffTools(db);
184 Map<String, ExternalDiffTool> tools = manager.getUserDefinedTools();
185 ExternalDiffTool diffTool = tools.get(toolName);
186 assertNotNull("Expected tool \"" + toolName + "\" to be user defined",
187 diffTool);
188
189 String toolPath = diffTool.getPath();
190 assertEquals("Expected external diff tool to have an overriden path",
191 customToolPath, toolPath);
192 }
193
194 @Test
195 public void testUserDefinedTools() {
196 FileBasedConfig config = db.getConfig();
197 String customToolname = "customTool";
198 config.setString(CONFIG_DIFFTOOL_SECTION, customToolname,
199 CONFIG_KEY_CMD, "echo");
200 config.setString(CONFIG_DIFFTOOL_SECTION, customToolname,
201 CONFIG_KEY_PATH, "/usr/bin/echo");
202 config.setString(CONFIG_DIFFTOOL_SECTION, customToolname,
203 CONFIG_KEY_PROMPT, String.valueOf(false));
204 config.setString(CONFIG_DIFFTOOL_SECTION, customToolname,
205 CONFIG_KEY_GUITOOL, String.valueOf(false));
206 config.setString(CONFIG_DIFFTOOL_SECTION, customToolname,
207 CONFIG_KEY_TRUST_EXIT_CODE, String.valueOf(false));
208 DiffTools manager = new DiffTools(db);
209 Set<String> actualToolNames = manager.getUserDefinedTools().keySet();
210 Set<String> expectedToolNames = new LinkedHashSet<>();
211 expectedToolNames.add(customToolname);
212 assertEquals("Incorrect set of external diff tools", expectedToolNames,
213 actualToolNames);
214 }
215
216 @Test
217 public void testCompare() throws ToolException {
218 String toolName = "customTool";
219
220 FileBasedConfig config = db.getConfig();
221
222 String subsection = null;
223 config.setString(CONFIG_DIFF_SECTION, subsection, CONFIG_KEY_TOOL,
224 toolName);
225
226 String command = getEchoCommand();
227
228 config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_CMD,
229 command);
230 Optional<ExecutionResult> result = invokeCompare(toolName);
231 assertTrue("Expected external diff tool result to be available",
232 result.isPresent());
233 int expectedCompareResult = 0;
234 assertEquals("Incorrect compare result for external diff tool",
235 expectedCompareResult, result.get().getRc());
236 }
237
238 @Test
239 public void testDefaultTool() throws Exception {
240 String toolName = "customTool";
241 String guiToolName = "customGuiTool";
242
243 FileBasedConfig config = db.getConfig();
244
245 String subsection = null;
246 config.setString(CONFIG_DIFF_SECTION, subsection, CONFIG_KEY_TOOL,
247 toolName);
248
249 DiffTools manager = new DiffTools(db);
250 boolean gui = false;
251 String defaultToolName = manager.getDefaultToolName(gui);
252 assertEquals(
253 "Expected configured difftool to be the default external diff tool",
254 toolName, defaultToolName);
255
256 gui = true;
257 String defaultGuiToolName = manager.getDefaultToolName(gui);
258 assertEquals(
259 "Expected default gui difftool to be the default tool if no gui tool is set",
260 toolName, defaultGuiToolName);
261
262 config.setString(CONFIG_DIFF_SECTION, subsection, CONFIG_KEY_GUITOOL,
263 guiToolName);
264 manager = new DiffTools(db);
265 defaultGuiToolName = manager.getDefaultToolName(gui);
266 assertEquals(
267 "Expected configured difftool to be the default external diff guitool",
268 guiToolName, defaultGuiToolName);
269 }
270
271 @Test
272 public void testOverridePreDefinedToolPath() {
273 String newToolPath = "/tmp/path/";
274
275 CommandLineDiffTool[] defaultTools = CommandLineDiffTool.values();
276 assertTrue("Expected to find pre-defined external diff tools",
277 defaultTools.length > 0);
278
279 CommandLineDiffTool overridenTool = defaultTools[0];
280 String overridenToolName = overridenTool.name();
281 String overridenToolPath = newToolPath + overridenToolName;
282 FileBasedConfig config = db.getConfig();
283 config.setString(CONFIG_DIFFTOOL_SECTION, overridenToolName,
284 CONFIG_KEY_PATH, overridenToolPath);
285
286 DiffTools manager = new DiffTools(db);
287 Map<String, ExternalDiffTool> availableTools = manager
288 .getPredefinedTools(true);
289 ExternalDiffTool externalDiffTool = availableTools
290 .get(overridenToolName);
291 String actualDiffToolPath = externalDiffTool.getPath();
292 assertEquals(
293 "Expected pre-defined external diff tool to have overriden path",
294 overridenToolPath, actualDiffToolPath);
295 String expectedDiffToolCommand = overridenToolPath + " "
296 + overridenTool.getParameters();
297 String actualDiffToolCommand = externalDiffTool.getCommand();
298 assertEquals(
299 "Expected pre-defined external diff tool to have overriden command",
300 expectedDiffToolCommand, actualDiffToolCommand);
301 }
302
303 @Test(expected = ToolException.class)
304 public void testUndefinedTool() throws Exception {
305 String toolName = "undefined";
306 invokeCompare(toolName);
307 fail("Expected exception to be thrown due to not defined external diff tool");
308 }
309
310 @Test
311 public void testDefaultToolExecutionWithPrompt() throws Exception {
312 FileBasedConfig config = db.getConfig();
313
314 String subsection = null;
315 config.setString("diff", subsection, "tool", "customTool");
316
317 String command = getEchoCommand();
318
319 config.setString("difftool", "customTool", "cmd", command);
320
321 DiffTools manager = new DiffTools(db);
322
323 PromptHandler promptHandler = PromptHandler.acceptPrompt();
324 MissingToolHandler noToolHandler = new MissingToolHandler();
325
326 manager.compare(local, remote, Optional.empty(), BooleanTriState.TRUE,
327 false, BooleanTriState.TRUE, promptHandler, noToolHandler);
328
329 assertEchoCommandHasCorrectOutput();
330 }
331
332 @Test
333 public void testNoDefaultToolName() {
334 DiffTools manager = new DiffTools(db);
335 boolean gui = false;
336 String defaultToolName = manager.getDefaultToolName(gui);
337 assertNull("Expected no default tool when none is configured",
338 defaultToolName);
339
340 gui = true;
341 defaultToolName = manager.getDefaultToolName(gui);
342 assertNull("Expected no default tool when none is configured",
343 defaultToolName);
344 }
345
346 @Test
347 public void testExternalToolInGitAttributes() throws Exception {
348 String content = "attributes:\n*.txt difftool=customTool";
349 File gitattributes = writeTrashFile(".gitattributes", content);
350 gitattributes.deleteOnExit();
351 try (TestRepository<Repository> testRepository = new TestRepository<>(
352 db)) {
353 FileBasedConfig config = db.getConfig();
354 config.setString("difftool", "customTool", "cmd", "echo");
355 testRepository.git().add().addFilepattern(localFile.getName())
356 .call();
357
358 testRepository.git().add().addFilepattern(".gitattributes").call();
359
360 testRepository.branch("master").commit().message("first commit")
361 .create();
362
363 DiffTools manager = new DiffTools(db);
364 Optional<String> tool = manager
365 .getExternalToolFromAttributes(localFile.getName());
366 assertTrue("Failed to find user defined tool", tool.isPresent());
367 assertEquals("Failed to find user defined tool", "customTool",
368 tool.get());
369 } finally {
370 Files.delete(gitattributes.toPath());
371 }
372 }
373
374 @Test
375 public void testNotExternalToolInGitAttributes() throws Exception {
376 String content = "";
377 File gitattributes = writeTrashFile(".gitattributes", content);
378 gitattributes.deleteOnExit();
379 try (TestRepository<Repository> testRepository = new TestRepository<>(
380 db)) {
381 FileBasedConfig config = db.getConfig();
382 config.setString("difftool", "customTool", "cmd", "echo");
383 testRepository.git().add().addFilepattern(localFile.getName())
384 .call();
385
386 testRepository.git().add().addFilepattern(".gitattributes").call();
387
388 testRepository.branch("master").commit().message("first commit")
389 .create();
390
391 DiffTools manager = new DiffTools(db);
392 Optional<String> tool = manager
393 .getExternalToolFromAttributes(localFile.getName());
394 assertFalse(
395 "Expected no external tool if no default tool is specified in .gitattributes",
396 tool.isPresent());
397 } finally {
398 Files.delete(gitattributes.toPath());
399 }
400 }
401
402 @Test(expected = ToolException.class)
403 public void testNullTool() throws Exception {
404 DiffTools manager = new DiffTools(db);
405
406 boolean trustExitCode = true;
407 ExternalDiffTool tool = null;
408 manager.compare(local, remote, tool, trustExitCode);
409 }
410
411 @Test(expected = ToolException.class)
412 public void testNullToolWithPrompt() throws Exception {
413 DiffTools manager = new DiffTools(db);
414
415 PromptHandler promptHandler = PromptHandler.cancelPrompt();
416 MissingToolHandler noToolHandler = new MissingToolHandler();
417
418 Optional<String> tool = null;
419 manager.compare(local, remote, tool, BooleanTriState.TRUE, false,
420 BooleanTriState.TRUE, promptHandler, noToolHandler);
421 }
422
423 private Optional<ExecutionResult> invokeCompare(String toolName)
424 throws ToolException {
425 DiffTools manager = new DiffTools(db);
426
427 BooleanTriState prompt = BooleanTriState.UNSET;
428 boolean gui = false;
429 BooleanTriState trustExitCode = BooleanTriState.TRUE;
430 PromptHandler promptHandler = PromptHandler.acceptPrompt();
431 MissingToolHandler noToolHandler = new MissingToolHandler();
432
433 Optional<ExecutionResult> result = manager.compare(local, remote,
434 Optional.of(toolName), prompt, gui, trustExitCode,
435 promptHandler, noToolHandler);
436 return result;
437 }
438
439 private String getEchoCommand() {
440 return "(echo \"$LOCAL\" \"$REMOTE\") > "
441 + commandResult.getAbsolutePath();
442 }
443
444 private void assertEchoCommandHasCorrectOutput() throws IOException {
445 List<String> actualLines = Files.readAllLines(commandResult.toPath());
446 String actualContent = String.join(System.lineSeparator(), actualLines);
447 actualLines = Arrays.asList(actualContent.split(" "));
448 List<String> expectedLines = Arrays.asList(localFile.getAbsolutePath(),
449 remoteFile.getAbsolutePath());
450 assertEquals("Dummy test tool called with unexpected arguments",
451 expectedLines, actualLines);
452 }
453 }