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_KEY_CMD;
13 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PROMPT;
14 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL;
15 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGETOOL_SECTION;
16 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
17 import static org.junit.Assert.fail;
18
19 import java.io.InputStream;
20 import java.nio.file.Path;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.eclipse.jgit.internal.diffmergetool.ExternalMergeTool;
27 import org.eclipse.jgit.internal.diffmergetool.MergeTools;
28 import org.eclipse.jgit.lib.StoredConfig;
29 import org.junit.Before;
30 import org.junit.Test;
31
32
33
34
35 public class MergeToolTest extends ToolTestCase {
36
37 private static final String MERGE_TOOL = CONFIG_MERGETOOL_SECTION;
38
39 @Override
40 @Before
41 public void setUp() throws Exception {
42 super.setUp();
43 configureEchoTool(TOOL_NAME);
44 }
45
46 @Test
47 public void testUndefinedTool() throws Exception {
48 String toolName = "undefined";
49 String[] conflictingFilenames = createMergeConflict();
50
51 List<String> expectedErrors = new ArrayList<>();
52 for (String conflictingFilename : conflictingFilenames) {
53 expectedErrors.add("External merge tool is not defined: " + toolName);
54 expectedErrors.add("merge of " + conflictingFilename + " failed");
55 }
56
57 runAndCaptureUsingInitRaw(expectedErrors, MERGE_TOOL,
58 "--no-prompt", "--tool", toolName);
59 }
60
61 @Test(expected = Die.class)
62 public void testUserToolWithCommandNotFoundError() throws Exception {
63 String toolName = "customTool";
64
65 int errorReturnCode = 127;
66 String command = "exit " + errorReturnCode;
67
68 StoredConfig config = db.getConfig();
69 config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_CMD,
70 command);
71
72 createMergeConflict();
73 runAndCaptureUsingInitRaw(MERGE_TOOL, "--no-prompt", "--tool",
74 toolName);
75
76 fail("Expected exception to be thrown due to external tool exiting with error code: "
77 + errorReturnCode);
78 }
79
80 @Test
81 public void testEmptyToolName() throws Exception {
82 assumeLinuxPlatform();
83
84 String emptyToolName = "";
85
86 StoredConfig config = db.getConfig();
87
88 String subsection = null;
89 config.setString(CONFIG_MERGE_SECTION, subsection, CONFIG_KEY_TOOL,
90 emptyToolName);
91
92 createMergeConflict();
93
94 String araxisErrorLine = "compare: unrecognized option `-wait' @ error/compare.c/CompareImageCommand/1123.";
95 String[] expectedErrorOutput = { araxisErrorLine, araxisErrorLine, };
96 runAndCaptureUsingInitRaw(Arrays.asList(expectedErrorOutput),
97 MERGE_TOOL, "--no-prompt");
98 }
99
100 @Test
101 public void testAbortMerge() throws Exception {
102 String[] inputLines = {
103 "y",
104 "n",
105 "n",
106 };
107 String[] conflictingFilenames = createMergeConflict();
108 int abortIndex = 1;
109 String[] expectedOutput = getExpectedAbortMergeOutput(
110 conflictingFilenames,
111 abortIndex);
112
113 String option = "--tool";
114
115 InputStream inputStream = createInputStream(inputLines);
116 assertArrayOfLinesEquals("Incorrect output for option: " + option,
117 expectedOutput, runAndCaptureUsingInitRaw(inputStream,
118 MERGE_TOOL, "--prompt", option, TOOL_NAME));
119 }
120
121 @Test
122 public void testAbortLaunch() throws Exception {
123 String[] inputLines = {
124 "n",
125 };
126 String[] conflictingFilenames = createMergeConflict();
127 String[] expectedOutput = getExpectedAbortLaunchOutput(
128 conflictingFilenames);
129
130 String option = "--tool";
131
132 InputStream inputStream = createInputStream(inputLines);
133 assertArrayOfLinesEquals("Incorrect output for option: " + option,
134 expectedOutput, runAndCaptureUsingInitRaw(inputStream,
135 MERGE_TOOL, "--prompt", option, TOOL_NAME));
136 }
137
138 @Test
139 public void testMergeConflict() throws Exception {
140 String[] inputLines = {
141 "y",
142 "y",
143 "y",
144 "y",
145 };
146 String[] conflictingFilenames = createMergeConflict();
147 String[] expectedOutput = getExpectedMergeConflictOutput(
148 conflictingFilenames);
149
150 String option = "--tool";
151
152 InputStream inputStream = createInputStream(inputLines);
153 assertArrayOfLinesEquals("Incorrect output for option: " + option,
154 expectedOutput, runAndCaptureUsingInitRaw(inputStream,
155 MERGE_TOOL, "--prompt", option, TOOL_NAME));
156 }
157
158 @Test
159 public void testDeletedConflict() throws Exception {
160 String[] inputLines = {
161 "d",
162 "m",
163 };
164 String[] conflictingFilenames = createDeletedConflict();
165 String[] expectedOutput = getExpectedDeletedConflictOutput(
166 conflictingFilenames);
167
168 String option = "--tool";
169
170 InputStream inputStream = createInputStream(inputLines);
171 assertArrayOfLinesEquals("Incorrect output for option: " + option,
172 expectedOutput, runAndCaptureUsingInitRaw(inputStream,
173 MERGE_TOOL, "--prompt", option, TOOL_NAME));
174 }
175
176 @Test
177 public void testNoConflict() throws Exception {
178 createStagedChanges();
179 String[] expectedOutput = { "No files need merging" };
180
181 String[] options = { "--tool", "-t", };
182
183 for (String option : options) {
184 assertArrayOfLinesEquals("Incorrect output for option: " + option,
185 expectedOutput,
186 runAndCaptureUsingInitRaw(MERGE_TOOL, option, TOOL_NAME));
187 }
188 }
189
190 @Test
191 public void testMergeConflictNoPrompt() throws Exception {
192 String[] conflictingFilenames = createMergeConflict();
193 String[] expectedOutput = getExpectedMergeConflictOutputNoPrompt(
194 conflictingFilenames);
195
196 String option = "--tool";
197
198 assertArrayOfLinesEquals("Incorrect output for option: " + option,
199 expectedOutput,
200 runAndCaptureUsingInitRaw(MERGE_TOOL, option, TOOL_NAME));
201 }
202
203 @Test
204 public void testMergeConflictNoGuiNoPrompt() throws Exception {
205 String[] conflictingFilenames = createMergeConflict();
206 String[] expectedOutput = getExpectedMergeConflictOutputNoPrompt(
207 conflictingFilenames);
208
209 String option = "--tool";
210
211 assertArrayOfLinesEquals("Incorrect output for option: " + option,
212 expectedOutput, runAndCaptureUsingInitRaw(MERGE_TOOL,
213 "--no-gui", "--no-prompt", option, TOOL_NAME));
214 }
215
216 @Test
217 public void testToolHelp() throws Exception {
218 List<String> expectedOutput = new ArrayList<>();
219
220 MergeTools diffTools = new MergeTools(db);
221 Map<String, ExternalMergeTool> predefinedTools = diffTools
222 .getPredefinedTools(true);
223 List<ExternalMergeTool> availableTools = new ArrayList<>();
224 List<ExternalMergeTool> notAvailableTools = new ArrayList<>();
225 for (ExternalMergeTool tool : predefinedTools.values()) {
226 if (tool.isAvailable()) {
227 availableTools.add(tool);
228 } else {
229 notAvailableTools.add(tool);
230 }
231 }
232
233 expectedOutput.add(
234 "'git mergetool --tool=<tool>' may be set to one of the following:");
235 for (ExternalMergeTool tool : availableTools) {
236 String toolName = tool.getName();
237 expectedOutput.add(toolName);
238 }
239 String customToolHelpLine = TOOL_NAME + "." + CONFIG_KEY_CMD + " "
240 + getEchoCommand();
241 expectedOutput.add("user-defined:");
242 expectedOutput.add(customToolHelpLine);
243 expectedOutput.add(
244 "The following tools are valid, but not currently available:");
245 for (ExternalMergeTool tool : notAvailableTools) {
246 String toolName = tool.getName();
247 expectedOutput.add(toolName);
248 }
249 String[] userDefinedToolsHelp = {
250 "Some of the tools listed above only work in a windowed",
251 "environment. If run in a terminal-only session, they will fail.", };
252 expectedOutput.addAll(Arrays.asList(userDefinedToolsHelp));
253
254 String option = "--tool-help";
255 assertArrayOfLinesEquals("Incorrect output for option: " + option,
256 expectedOutput.toArray(new String[0]),
257 runAndCaptureUsingInitRaw(MERGE_TOOL, option));
258 }
259
260 private void configureEchoTool(String toolName) {
261 StoredConfig config = db.getConfig();
262
263 String subsection = null;
264 config.setString(CONFIG_MERGE_SECTION, subsection, CONFIG_KEY_TOOL,
265 toolName);
266
267 String command = getEchoCommand();
268
269 config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_CMD,
270 command);
271
272
273
274
275 config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_PROMPT,
276 String.valueOf(false));
277 }
278
279 private String[] getExpectedMergeConflictOutputNoPrompt(
280 String[] conflictFilenames) {
281 List<String> expected = new ArrayList<>();
282 expected.add("Merging:");
283 for (String conflictFilename : conflictFilenames) {
284 expected.add(conflictFilename);
285 }
286 for (String conflictFilename : conflictFilenames) {
287 expected.add("Normal merge conflict for '" + conflictFilename
288 + "':");
289 expected.add("{local}: modified file");
290 expected.add("{remote}: modified file");
291 Path filePath = getFullPath(conflictFilename);
292 expected.add(filePath.toString());
293 expected.add(conflictFilename + " seems unchanged.");
294 }
295 return expected.toArray(new String[0]);
296 }
297
298 private static String[] getExpectedAbortLaunchOutput(
299 String[] conflictFilenames) {
300 List<String> expected = new ArrayList<>();
301 expected.add("Merging:");
302 for (String conflictFilename : conflictFilenames) {
303 expected.add(conflictFilename);
304 }
305 if (conflictFilenames.length > 1) {
306 String conflictFilename = conflictFilenames[0];
307 expected.add(
308 "Normal merge conflict for '" + conflictFilename + "':");
309 expected.add("{local}: modified file");
310 expected.add("{remote}: modified file");
311 expected.add("Hit return to start merge resolution tool ("
312 + TOOL_NAME + "):");
313 }
314 return expected.toArray(new String[0]);
315 }
316
317 private String[] getExpectedAbortMergeOutput(
318 String[] conflictFilenames, int abortIndex) {
319 List<String> expected = new ArrayList<>();
320 expected.add("Merging:");
321 for (String conflictFilename : conflictFilenames) {
322 expected.add(conflictFilename);
323 }
324 for (int i = 0; i < conflictFilenames.length; ++i) {
325 if (i == abortIndex) {
326 break;
327 }
328
329 String conflictFilename = conflictFilenames[i];
330 expected.add(
331 "Normal merge conflict for '" + conflictFilename + "':");
332 expected.add("{local}: modified file");
333 expected.add("{remote}: modified file");
334 Path fullPath = getFullPath(conflictFilename);
335 expected.add("Hit return to start merge resolution tool ("
336 + TOOL_NAME + "): " + fullPath);
337 expected.add(conflictFilename + " seems unchanged.");
338 expected.add("Was the merge successful [y/n]?");
339 if (i < conflictFilenames.length - 1) {
340 expected.add(
341 "\tContinue merging other unresolved paths [y/n]?");
342 }
343 }
344 return expected.toArray(new String[0]);
345 }
346
347 private String[] getExpectedMergeConflictOutput(
348 String[] conflictFilenames) {
349 List<String> expected = new ArrayList<>();
350 expected.add("Merging:");
351 for (String conflictFilename : conflictFilenames) {
352 expected.add(conflictFilename);
353 }
354 for (int i = 0; i < conflictFilenames.length; ++i) {
355 String conflictFilename = conflictFilenames[i];
356 expected.add("Normal merge conflict for '" + conflictFilename
357 + "':");
358 expected.add("{local}: modified file");
359 expected.add("{remote}: modified file");
360 Path filePath = getFullPath(conflictFilename);
361 expected.add("Hit return to start merge resolution tool ("
362 + TOOL_NAME + "): " + filePath);
363 expected.add(conflictFilename + " seems unchanged.");
364 expected.add("Was the merge successful [y/n]?");
365 if (i < conflictFilenames.length - 1) {
366
367
368 }
369 }
370 return expected.toArray(new String[0]);
371 }
372
373 private static String[] getExpectedDeletedConflictOutput(
374 String[] conflictFilenames) {
375 List<String> expected = new ArrayList<>();
376 expected.add("Merging:");
377 for (String mergeConflictFilename : conflictFilenames) {
378 expected.add(mergeConflictFilename);
379 }
380 for (int i = 0; i < conflictFilenames.length; ++i) {
381 String conflictFilename = conflictFilenames[i];
382 expected.add(conflictFilename + " seems unchanged.");
383 expected.add("{local}: deleted");
384 expected.add("{remote}: modified file");
385 expected.add("Use (m)odified or (d)eleted file, or (a)bort?");
386 }
387 return expected.toArray(new String[0]);
388 }
389
390 private static String getEchoCommand() {
391
392
393
394
395 return "(echo \"$MERGED\")";
396 }
397 }