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