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