1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.util;
11
12 import static java.nio.charset.StandardCharsets.UTF_8;
13 import static org.junit.Assert.assertEquals;
14 import static org.junit.Assert.assertNull;
15 import static org.junit.Assert.fail;
16
17 import java.io.ByteArrayOutputStream;
18 import java.io.File;
19 import java.io.IOException;
20 import java.io.PrintStream;
21
22 import org.eclipse.jgit.api.Git;
23 import org.eclipse.jgit.api.errors.AbortedByHookException;
24 import org.eclipse.jgit.hooks.CommitMsgHook;
25 import org.eclipse.jgit.hooks.PostCommitHook;
26 import org.eclipse.jgit.hooks.PreCommitHook;
27 import org.eclipse.jgit.junit.JGitTestUtil;
28 import org.eclipse.jgit.junit.RepositoryTestCase;
29 import org.eclipse.jgit.lib.ConfigConstants;
30 import org.eclipse.jgit.lib.StoredConfig;
31 import org.eclipse.jgit.revwalk.RevCommit;
32 import org.junit.Assume;
33 import org.junit.Test;
34
35 public class HookTest extends RepositoryTestCase {
36
37 @Test
38 public void testFindHook() throws Exception {
39 assumeSupportedPlatform();
40
41 assertNull("no hook should be installed",
42 FS.DETECTED.findHook(db, PreCommitHook.NAME));
43 File hookFile = writeHookFile(PreCommitHook.NAME,
44 "#!/bin/bash\necho \"test $1 $2\"");
45 assertEquals("expected to find pre-commit hook", hookFile,
46 FS.DETECTED.findHook(db, PreCommitHook.NAME));
47 }
48
49 @Test
50 public void testFindPostCommitHook() throws Exception {
51 assumeSupportedPlatform();
52
53 assertNull("no hook should be installed",
54 FS.DETECTED.findHook(db, PostCommitHook.NAME));
55 File hookFile = writeHookFile(PostCommitHook.NAME,
56 "#!/bin/bash\necho \"test $1 $2\"");
57 assertEquals("expected to find post-commit hook", hookFile,
58 FS.DETECTED.findHook(db, PostCommitHook.NAME));
59 }
60
61 @Test
62 public void testFailedCommitMsgHookBlocksCommit() throws Exception {
63 assumeSupportedPlatform();
64
65 writeHookFile(CommitMsgHook.NAME,
66 "#!/bin/sh\necho \"test\"\n\necho 1>&2 \"stderr\"\nexit 1");
67 Git git = Git.wrap(db);
68 String path = "a.txt";
69 writeTrashFile(path, "content");
70 git.add().addFilepattern(path).call();
71 ByteArrayOutputStream out = new ByteArrayOutputStream();
72 try {
73 git.commit().setMessage("commit")
74 .setHookOutputStream(new PrintStream(out)).call();
75 fail("expected commit-msg hook to abort commit");
76 } catch (AbortedByHookException e) {
77 assertEquals("unexpected error message from commit-msg hook",
78 "Rejected by \"commit-msg\" hook.\nstderr\n",
79 e.getMessage());
80 assertEquals("unexpected output from commit-msg hook", "test\n",
81 out.toString(UTF_8));
82 }
83 }
84
85 @Test
86 public void testCommitMsgHookReceivesCorrectParameter() throws Exception {
87 assumeSupportedPlatform();
88
89 writeHookFile(CommitMsgHook.NAME,
90 "#!/bin/sh\necho $1\n\necho 1>&2 \"stderr\"\nexit 0");
91 Git git = Git.wrap(db);
92 String path = "a.txt";
93 writeTrashFile(path, "content");
94 git.add().addFilepattern(path).call();
95 ByteArrayOutputStream out = new ByteArrayOutputStream();
96 git.commit().setMessage("commit")
97 .setHookOutputStream(new PrintStream(out)).call();
98 assertEquals(".git/COMMIT_EDITMSG\n",
99 out.toString(UTF_8));
100 }
101
102 @Test
103 public void testCommitMsgHookCanModifyCommitMessage() throws Exception {
104 assumeSupportedPlatform();
105
106 writeHookFile(CommitMsgHook.NAME,
107 "#!/bin/sh\necho \"new message\" > $1\nexit 0");
108 Git git = Git.wrap(db);
109 String path = "a.txt";
110 writeTrashFile(path, "content");
111 git.add().addFilepattern(path).call();
112 ByteArrayOutputStream out = new ByteArrayOutputStream();
113 RevCommit revCommit = git.commit().setMessage("commit")
114 .setHookOutputStream(new PrintStream(out)).call();
115 assertEquals("new message\n", revCommit.getFullMessage());
116 }
117
118 @Test
119 public void testPostCommitRunHook() throws Exception {
120 assumeSupportedPlatform();
121
122 writeHookFile(PostCommitHook.NAME,
123 "#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\necho 1>&2 \"stderr\"");
124 ByteArrayOutputStream out = new ByteArrayOutputStream();
125 ByteArrayOutputStream err = new ByteArrayOutputStream();
126 ProcessResult res = FS.DETECTED.runHookIfPresent(db,
127 PostCommitHook.NAME,
128 new String[] {
129 "arg1", "arg2" },
130 new PrintStream(out), new PrintStream(err), "stdin");
131
132 assertEquals("unexpected hook output", "test arg1 arg2\nstdin\n",
133 out.toString(UTF_8));
134 assertEquals("unexpected output on stderr stream", "stderr\n",
135 err.toString(UTF_8));
136 assertEquals("unexpected exit code", 0, res.getExitCode());
137 assertEquals("unexpected process status", ProcessResult.Status.OK,
138 res.getStatus());
139 }
140
141 @Test
142 public void testAllCommitHooks() throws Exception {
143 assumeSupportedPlatform();
144
145 writeHookFile(PreCommitHook.NAME,
146 "#!/bin/sh\necho \"test pre-commit\"\n\necho 1>&2 \"stderr pre-commit\"\nexit 0");
147 writeHookFile(CommitMsgHook.NAME,
148 "#!/bin/sh\necho \"test commit-msg $1\"\n\necho 1>&2 \"stderr commit-msg\"\nexit 0");
149 writeHookFile(PostCommitHook.NAME,
150 "#!/bin/sh\necho \"test post-commit\"\necho 1>&2 \"stderr post-commit\"\nexit 0");
151 Git git = Git.wrap(db);
152 String path = "a.txt";
153 writeTrashFile(path, "content");
154 git.add().addFilepattern(path).call();
155 ByteArrayOutputStream out = new ByteArrayOutputStream();
156 try {
157 git.commit().setMessage("commit")
158 .setHookOutputStream(new PrintStream(out)).call();
159 } catch (AbortedByHookException e) {
160 fail("unexpected hook failure");
161 }
162 assertEquals("unexpected hook output",
163 "test pre-commit\ntest commit-msg .git/COMMIT_EDITMSG\ntest post-commit\n",
164 out.toString(UTF_8));
165 }
166
167 @Test
168 public void testRunHook() throws Exception {
169 assumeSupportedPlatform();
170
171 writeHookFile(PreCommitHook.NAME,
172 "#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\n"
173 + "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
174 ByteArrayOutputStream out = new ByteArrayOutputStream();
175 ByteArrayOutputStream err = new ByteArrayOutputStream();
176 ProcessResult res = FS.DETECTED.runHookIfPresent(db,
177 PreCommitHook.NAME,
178 new String[] {
179 "arg1", "arg2" },
180 new PrintStream(out), new PrintStream(err), "stdin");
181
182 assertEquals("unexpected hook output",
183 "test arg1 arg2\nstdin\n" + db.getDirectory().getAbsolutePath()
184 + '\n' + db.getWorkTree().getAbsolutePath() + '\n',
185 out.toString(UTF_8));
186 assertEquals("unexpected output on stderr stream", "stderr\n",
187 err.toString(UTF_8));
188 assertEquals("unexpected exit code", 0, res.getExitCode());
189 assertEquals("unexpected process status", ProcessResult.Status.OK,
190 res.getStatus());
191 }
192
193 @Test
194 public void testRunHookHooksPathRelative() throws Exception {
195 assumeSupportedPlatform();
196
197 writeHookFile(PreCommitHook.NAME,
198 "#!/bin/sh\necho \"Wrong hook $1 $2\"\nread INPUT\necho $INPUT\n"
199 + "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
200 writeHookFile("../../" + PreCommitHook.NAME,
201 "#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\n"
202 + "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
203 StoredConfig cfg = db.getConfig();
204 cfg.load();
205 cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
206 ConfigConstants.CONFIG_KEY_HOOKS_PATH, ".");
207 cfg.save();
208 try (ByteArrayOutputStream out = new ByteArrayOutputStream();
209 ByteArrayOutputStream err = new ByteArrayOutputStream()) {
210 ProcessResult res = FS.DETECTED.runHookIfPresent(db,
211 PreCommitHook.NAME, new String[] { "arg1", "arg2" },
212 new PrintStream(out), new PrintStream(err), "stdin");
213
214 assertEquals("unexpected hook output",
215 "test arg1 arg2\nstdin\n"
216 + db.getDirectory().getAbsolutePath() + '\n'
217 + db.getWorkTree().getAbsolutePath() + '\n',
218 out.toString(UTF_8));
219 assertEquals("unexpected output on stderr stream", "stderr\n",
220 err.toString(UTF_8));
221 assertEquals("unexpected exit code", 0, res.getExitCode());
222 assertEquals("unexpected process status", ProcessResult.Status.OK,
223 res.getStatus());
224 }
225 }
226
227 @Test
228 public void testRunHookHooksPathAbsolute() throws Exception {
229 assumeSupportedPlatform();
230
231 writeHookFile(PreCommitHook.NAME,
232 "#!/bin/sh\necho \"Wrong hook $1 $2\"\nread INPUT\necho $INPUT\n"
233 + "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
234 writeHookFile("../../" + PreCommitHook.NAME,
235 "#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\n"
236 + "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
237 StoredConfig cfg = db.getConfig();
238 cfg.load();
239 cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
240 ConfigConstants.CONFIG_KEY_HOOKS_PATH,
241 db.getWorkTree().getAbsolutePath());
242 cfg.save();
243 try (ByteArrayOutputStream out = new ByteArrayOutputStream();
244 ByteArrayOutputStream err = new ByteArrayOutputStream()) {
245 ProcessResult res = FS.DETECTED.runHookIfPresent(db,
246 PreCommitHook.NAME, new String[] { "arg1", "arg2" },
247 new PrintStream(out), new PrintStream(err), "stdin");
248
249 assertEquals("unexpected hook output",
250 "test arg1 arg2\nstdin\n"
251 + db.getDirectory().getAbsolutePath() + '\n'
252 + db.getWorkTree().getAbsolutePath() + '\n',
253 out.toString(UTF_8));
254 assertEquals("unexpected output on stderr stream", "stderr\n",
255 err.toString(UTF_8));
256 assertEquals("unexpected exit code", 0, res.getExitCode());
257 assertEquals("unexpected process status", ProcessResult.Status.OK,
258 res.getStatus());
259 }
260 }
261
262 @Test
263 public void testHookPathWithBlank() throws Exception {
264 assumeSupportedPlatform();
265
266 File file = writeHookFile("../../a directory/" + PreCommitHook.NAME,
267 "#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\n"
268 + "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
269 StoredConfig cfg = db.getConfig();
270 cfg.load();
271 cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
272 ConfigConstants.CONFIG_KEY_HOOKS_PATH,
273 file.getParentFile().getAbsolutePath());
274 cfg.save();
275 try (ByteArrayOutputStream out = new ByteArrayOutputStream();
276 ByteArrayOutputStream err = new ByteArrayOutputStream()) {
277 ProcessResult res = FS.DETECTED.runHookIfPresent(db,
278 PreCommitHook.NAME, new String[] { "arg1", "arg2" },
279 new PrintStream(out), new PrintStream(err), "stdin");
280
281 assertEquals("unexpected hook output",
282 "test arg1 arg2\nstdin\n"
283 + db.getDirectory().getAbsolutePath() + '\n'
284 + db.getWorkTree().getAbsolutePath() + '\n',
285 out.toString(UTF_8));
286 assertEquals("unexpected output on stderr stream", "stderr\n",
287 err.toString(UTF_8));
288 assertEquals("unexpected exit code", 0, res.getExitCode());
289 assertEquals("unexpected process status", ProcessResult.Status.OK,
290 res.getStatus());
291 }
292 }
293
294 @Test
295 public void testFailedPreCommitHookBlockCommit() throws Exception {
296 assumeSupportedPlatform();
297
298 writeHookFile(PreCommitHook.NAME,
299 "#!/bin/sh\necho \"test\"\n\necho 1>&2 \"stderr\"\nexit 1");
300 Git git = Git.wrap(db);
301 String path = "a.txt";
302 writeTrashFile(path, "content");
303 git.add().addFilepattern(path).call();
304 ByteArrayOutputStream out = new ByteArrayOutputStream();
305 try {
306 git.commit().setMessage("commit")
307 .setHookOutputStream(new PrintStream(out)).call();
308 fail("expected pre-commit hook to abort commit");
309 } catch (AbortedByHookException e) {
310 assertEquals("unexpected error message from pre-commit hook",
311 "Rejected by \"pre-commit\" hook.\nstderr\n",
312 e.getMessage());
313 assertEquals("unexpected output from pre-commit hook", "test\n",
314 out.toString(UTF_8));
315 }
316 }
317
318 private File writeHookFile(String name, String data)
319 throws IOException {
320 File path = new File(db.getWorkTree() + "/.git/hooks/", name);
321 JGitTestUtil.write(path, data);
322 FS.DETECTED.setExecute(path, true);
323 return path;
324 }
325
326 private void assumeSupportedPlatform() {
327 Assume.assumeTrue(FS.DETECTED instanceof FS_POSIX
328 || FS.DETECTED instanceof FS_Win32_Cygwin);
329 }
330 }