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