View Javadoc
1   /*
2    * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
3    * Copyright (C) 2009-2010, Google Inc.
4    * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
5    * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
6    * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
7    * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
8    * and other copyright owners as documented in the project's IP log.
9    *
10   * This program and the accompanying materials are made available
11   * under the terms of the Eclipse Distribution License v1.0 which
12   * accompanies this distribution, is reproduced below, and is
13   * available at http://www.eclipse.org/org/documents/edl-v10.php
14   *
15   * All rights reserved.
16   *
17   * Redistribution and use in source and binary forms, with or
18   * without modification, are permitted provided that the following
19   * conditions are met:
20   *
21   * - Redistributions of source code must retain the above copyright
22   *   notice, this list of conditions and the following disclaimer.
23   *
24   * - Redistributions in binary form must reproduce the above
25   *   copyright notice, this list of conditions and the following
26   *   disclaimer in the documentation and/or other materials provided
27   *   with the distribution.
28   *
29   * - Neither the name of the Eclipse Foundation, Inc. nor the
30   *   names of its contributors may be used to endorse or promote
31   *   products derived from this software without specific prior
32   *   written permission.
33   *
34   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
35   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
36   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
39   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
41   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
43   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
44   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
46   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47   */
48  
49  package org.eclipse.jgit.lib;
50  
51  import static java.util.concurrent.TimeUnit.DAYS;
52  import static java.util.concurrent.TimeUnit.HOURS;
53  import static java.util.concurrent.TimeUnit.MILLISECONDS;
54  import static java.util.concurrent.TimeUnit.MINUTES;
55  import static java.util.concurrent.TimeUnit.SECONDS;
56  import static org.junit.Assert.assertArrayEquals;
57  import static org.junit.Assert.assertEquals;
58  import static org.junit.Assert.assertFalse;
59  import static org.junit.Assert.assertNull;
60  import static org.junit.Assert.assertSame;
61  import static org.junit.Assert.assertTrue;
62  import static org.junit.Assert.fail;
63  
64  import java.io.File;
65  import java.io.IOException;
66  import java.nio.file.Files;
67  import java.text.MessageFormat;
68  import java.util.Arrays;
69  import java.util.Iterator;
70  import java.util.LinkedList;
71  import java.util.Set;
72  import java.util.concurrent.TimeUnit;
73  
74  import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
75  import org.eclipse.jgit.errors.ConfigInvalidException;
76  import org.eclipse.jgit.internal.JGitText;
77  import org.eclipse.jgit.junit.MockSystemReader;
78  import org.eclipse.jgit.merge.MergeConfig;
79  import org.eclipse.jgit.storage.file.FileBasedConfig;
80  import org.eclipse.jgit.util.FS;
81  import org.eclipse.jgit.util.SystemReader;
82  import org.junit.After;
83  import org.junit.Rule;
84  import org.junit.Test;
85  import org.junit.rules.ExpectedException;
86  import org.junit.rules.TemporaryFolder;
87  
88  /**
89   * Test reading of git config
90   */
91  public class ConfigTest {
92  
93  	@Rule
94  	public ExpectedException expectedEx = ExpectedException.none();
95  
96  	@Rule
97  	public TemporaryFolder tmp = new TemporaryFolder();
98  
99  	@After
100 	public void tearDown() {
101 		SystemReader.setInstance(null);
102 	}
103 
104 	@Test
105 	public void test001_ReadBareKey() throws ConfigInvalidException {
106 		final Config c = parse("[foo]\nbar\n");
107 		assertTrue(c.getBoolean("foo", null, "bar", false));
108 		assertEquals("", c.getString("foo", null, "bar"));
109 	}
110 
111 	@Test
112 	public void test002_ReadWithSubsection() throws ConfigInvalidException {
113 		final Config c = parse("[foo \"zip\"]\nbar\n[foo \"zap\"]\nbar=false\nn=3\n");
114 		assertTrue(c.getBoolean("foo", "zip", "bar", false));
115 		assertEquals("", c.getString("foo","zip", "bar"));
116 		assertFalse(c.getBoolean("foo", "zap", "bar", true));
117 		assertEquals("false", c.getString("foo", "zap", "bar"));
118 		assertEquals(3, c.getInt("foo", "zap", "n", 4));
119 		assertEquals(4, c.getInt("foo", "zap","m", 4));
120 	}
121 
122 	@Test
123 	public void test003_PutRemote() {
124 		final Config c = new Config();
125 		c.setString("sec", "ext", "name", "value");
126 		c.setString("sec", "ext", "name2", "value2");
127 		final String expText = "[sec \"ext\"]\n\tname = value\n\tname2 = value2\n";
128 		assertEquals(expText, c.toText());
129 	}
130 
131 	@Test
132 	public void test004_PutGetSimple() {
133 		Config c = new Config();
134 		c.setString("my", null, "somename", "false");
135 		assertEquals("false", c.getString("my", null, "somename"));
136 		assertEquals("[my]\n\tsomename = false\n", c.toText());
137 	}
138 
139 	@Test
140 	public void test005_PutGetStringList() {
141 		Config c = new Config();
142 		final LinkedList<String> values = new LinkedList<String>();
143 		values.add("value1");
144 		values.add("value2");
145 		c.setStringList("my", null, "somename", values);
146 
147 		final Object[] expArr = values.toArray();
148 		final String[] actArr = c.getStringList("my", null, "somename");
149 		assertArrayEquals(expArr, actArr);
150 
151 		final String expText = "[my]\n\tsomename = value1\n\tsomename = value2\n";
152 		assertEquals(expText, c.toText());
153 	}
154 
155 	@Test
156 	public void test006_readCaseInsensitive() throws ConfigInvalidException {
157 		final Config c = parse("[Foo]\nBar\n");
158 		assertTrue(c.getBoolean("foo", null, "bar", false));
159 		assertEquals("", c.getString("foo", null, "bar"));
160 	}
161 
162 	@Test
163 	public void test007_readUserConfig() {
164 		final MockSystemReader mockSystemReader = new MockSystemReader();
165 		SystemReader.setInstance(mockSystemReader);
166 		final String hostname = mockSystemReader.getHostname();
167 		final Config userGitConfig = mockSystemReader.openUserConfig(null,
168 				FS.DETECTED);
169 		final Config localConfig = new Config(userGitConfig);
170 		mockSystemReader.clearProperties();
171 
172 		String authorName;
173 		String authorEmail;
174 
175 		// no values defined nowhere
176 		authorName = localConfig.get(UserConfig.KEY).getAuthorName();
177 		authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
178 		assertEquals(Constants.UNKNOWN_USER_DEFAULT, authorName);
179 		assertEquals(Constants.UNKNOWN_USER_DEFAULT + "@" + hostname, authorEmail);
180 		assertTrue(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
181 		assertTrue(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
182 
183 		// the system user name is defined
184 		mockSystemReader.setProperty(Constants.OS_USER_NAME_KEY, "os user name");
185 		localConfig.uncache(UserConfig.KEY);
186 		authorName = localConfig.get(UserConfig.KEY).getAuthorName();
187 		assertEquals("os user name", authorName);
188 		assertTrue(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
189 
190 		if (hostname != null && hostname.length() != 0) {
191 			authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
192 			assertEquals("os user name@" + hostname, authorEmail);
193 		}
194 		assertTrue(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
195 
196 		// the git environment variables are defined
197 		mockSystemReader.setProperty(Constants.GIT_AUTHOR_NAME_KEY, "git author name");
198 		mockSystemReader.setProperty(Constants.GIT_AUTHOR_EMAIL_KEY, "author@email");
199 		localConfig.uncache(UserConfig.KEY);
200 		authorName = localConfig.get(UserConfig.KEY).getAuthorName();
201 		authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
202 		assertEquals("git author name", authorName);
203 		assertEquals("author@email", authorEmail);
204 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
205 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
206 
207 		// the values are defined in the global configuration
208 		// first clear environment variables since they would override
209 		// configuration files
210 		mockSystemReader.clearProperties();
211 		userGitConfig.setString("user", null, "name", "global username");
212 		userGitConfig.setString("user", null, "email", "author@globalemail");
213 		authorName = localConfig.get(UserConfig.KEY).getAuthorName();
214 		authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
215 		assertEquals("global username", authorName);
216 		assertEquals("author@globalemail", authorEmail);
217 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
218 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
219 
220 		// the values are defined in the local configuration
221 		localConfig.setString("user", null, "name", "local username");
222 		localConfig.setString("user", null, "email", "author@localemail");
223 		authorName = localConfig.get(UserConfig.KEY).getAuthorName();
224 		authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
225 		assertEquals("local username", authorName);
226 		assertEquals("author@localemail", authorEmail);
227 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
228 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
229 
230 		authorName = localConfig.get(UserConfig.KEY).getCommitterName();
231 		authorEmail = localConfig.get(UserConfig.KEY).getCommitterEmail();
232 		assertEquals("local username", authorName);
233 		assertEquals("author@localemail", authorEmail);
234 		assertFalse(localConfig.get(UserConfig.KEY).isCommitterNameImplicit());
235 		assertFalse(localConfig.get(UserConfig.KEY).isCommitterEmailImplicit());
236 
237 		// also git environment variables are defined
238 		mockSystemReader.setProperty(Constants.GIT_AUTHOR_NAME_KEY,
239 				"git author name");
240 		mockSystemReader.setProperty(Constants.GIT_AUTHOR_EMAIL_KEY,
241 				"author@email");
242 		localConfig.setString("user", null, "name", "local username");
243 		localConfig.setString("user", null, "email", "author@localemail");
244 		authorName = localConfig.get(UserConfig.KEY).getAuthorName();
245 		authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
246 		assertEquals("git author name", authorName);
247 		assertEquals("author@email", authorEmail);
248 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
249 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
250 	}
251 
252 	@Test
253 	public void testReadUserConfigWithInvalidCharactersStripped() {
254 		final MockSystemReader mockSystemReader = new MockSystemReader();
255 		final Config localConfig = new Config(mockSystemReader.openUserConfig(
256 				null, FS.DETECTED));
257 
258 		localConfig.setString("user", null, "name", "foo<bar");
259 		localConfig.setString("user", null, "email", "baz>\nqux@example.com");
260 
261 		UserConfig userConfig = localConfig.get(UserConfig.KEY);
262 		assertEquals("foobar", userConfig.getAuthorName());
263 		assertEquals("bazqux@example.com", userConfig.getAuthorEmail());
264 	}
265 
266 	@Test
267 	public void testReadBoolean_TrueFalse1() throws ConfigInvalidException {
268 		final Config c = parse("[s]\na = true\nb = false\n");
269 		assertEquals("true", c.getString("s", null, "a"));
270 		assertEquals("false", c.getString("s", null, "b"));
271 
272 		assertTrue(c.getBoolean("s", "a", false));
273 		assertFalse(c.getBoolean("s", "b", true));
274 	}
275 
276 	@Test
277 	public void testReadBoolean_TrueFalse2() throws ConfigInvalidException {
278 		final Config c = parse("[s]\na = TrUe\nb = fAlSe\n");
279 		assertEquals("TrUe", c.getString("s", null, "a"));
280 		assertEquals("fAlSe", c.getString("s", null, "b"));
281 
282 		assertTrue(c.getBoolean("s", "a", false));
283 		assertFalse(c.getBoolean("s", "b", true));
284 	}
285 
286 	@Test
287 	public void testReadBoolean_YesNo1() throws ConfigInvalidException {
288 		final Config c = parse("[s]\na = yes\nb = no\n");
289 		assertEquals("yes", c.getString("s", null, "a"));
290 		assertEquals("no", c.getString("s", null, "b"));
291 
292 		assertTrue(c.getBoolean("s", "a", false));
293 		assertFalse(c.getBoolean("s", "b", true));
294 	}
295 
296 	@Test
297 	public void testReadBoolean_YesNo2() throws ConfigInvalidException {
298 		final Config c = parse("[s]\na = yEs\nb = NO\n");
299 		assertEquals("yEs", c.getString("s", null, "a"));
300 		assertEquals("NO", c.getString("s", null, "b"));
301 
302 		assertTrue(c.getBoolean("s", "a", false));
303 		assertFalse(c.getBoolean("s", "b", true));
304 	}
305 
306 	@Test
307 	public void testReadBoolean_OnOff1() throws ConfigInvalidException {
308 		final Config c = parse("[s]\na = on\nb = off\n");
309 		assertEquals("on", c.getString("s", null, "a"));
310 		assertEquals("off", c.getString("s", null, "b"));
311 
312 		assertTrue(c.getBoolean("s", "a", false));
313 		assertFalse(c.getBoolean("s", "b", true));
314 	}
315 
316 	@Test
317 	public void testReadBoolean_OnOff2() throws ConfigInvalidException {
318 		final Config c = parse("[s]\na = ON\nb = OFF\n");
319 		assertEquals("ON", c.getString("s", null, "a"));
320 		assertEquals("OFF", c.getString("s", null, "b"));
321 
322 		assertTrue(c.getBoolean("s", "a", false));
323 		assertFalse(c.getBoolean("s", "b", true));
324 	}
325 
326 	static enum TestEnum {
327 		ONE_TWO;
328 	}
329 
330 	@Test
331 	public void testGetEnum() throws ConfigInvalidException {
332 		Config c = parse("[s]\na = ON\nb = input\nc = true\nd = off\n");
333 		assertSame(CoreConfig.AutoCRLF.TRUE, c.getEnum("s", null, "a",
334 				CoreConfig.AutoCRLF.FALSE));
335 
336 		assertSame(CoreConfig.AutoCRLF.INPUT, c.getEnum("s", null, "b",
337 				CoreConfig.AutoCRLF.FALSE));
338 
339 		assertSame(CoreConfig.AutoCRLF.TRUE, c.getEnum("s", null, "c",
340 				CoreConfig.AutoCRLF.FALSE));
341 
342 		assertSame(CoreConfig.AutoCRLF.FALSE, c.getEnum("s", null, "d",
343 				CoreConfig.AutoCRLF.TRUE));
344 
345 		c = new Config();
346 		assertSame(CoreConfig.AutoCRLF.FALSE, c.getEnum("s", null, "d",
347 				CoreConfig.AutoCRLF.FALSE));
348 
349 		c = parse("[s \"b\"]\n\tc = one two\n");
350 		assertSame(TestEnum.ONE_TWO, c.getEnum("s", "b", "c", TestEnum.ONE_TWO));
351 
352 		c = parse("[s \"b\"]\n\tc = one-two\n");
353 		assertSame(TestEnum.ONE_TWO, c.getEnum("s", "b", "c", TestEnum.ONE_TWO));
354 	}
355 
356 	@Test
357 	public void testGetInvalidEnum() throws ConfigInvalidException {
358 		Config c = parse("[a]\n\tb = invalid\n");
359 		try {
360 			c.getEnum("a", null, "b", TestEnum.ONE_TWO);
361 			fail();
362 		} catch (IllegalArgumentException e) {
363 			assertEquals("Invalid value: a.b=invalid", e.getMessage());
364 		}
365 
366 		c = parse("[a \"b\"]\n\tc = invalid\n");
367 		try {
368 			c.getEnum("a", "b", "c", TestEnum.ONE_TWO);
369 			fail();
370 		} catch (IllegalArgumentException e) {
371 			assertEquals("Invalid value: a.b.c=invalid", e.getMessage());
372 		}
373 	}
374 
375 	@Test
376 	public void testSetEnum() {
377 		final Config c = new Config();
378 		c.setEnum("s", "b", "c", TestEnum.ONE_TWO);
379 		assertEquals("[s \"b\"]\n\tc = one two\n", c.toText());
380 	}
381 
382 	@Test
383 	public void testGetFastForwardMergeoptions() throws ConfigInvalidException {
384 		Config c = new Config(null); // not set
385 		assertSame(FastForwardMode.FF, c.getEnum(
386 				ConfigConstants.CONFIG_BRANCH_SECTION, "side",
387 				ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.FF));
388 		MergeConfig mergeConfig = c.get(MergeConfig.getParser("side"));
389 		assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
390 		c = parse("[branch \"side\"]\n\tmergeoptions = --ff-only\n");
391 		assertSame(FastForwardMode.FF_ONLY, c.getEnum(
392 				ConfigConstants.CONFIG_BRANCH_SECTION, "side",
393 				ConfigConstants.CONFIG_KEY_MERGEOPTIONS,
394 				FastForwardMode.FF_ONLY));
395 		mergeConfig = c.get(MergeConfig.getParser("side"));
396 		assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
397 		c = parse("[branch \"side\"]\n\tmergeoptions = --ff\n");
398 		assertSame(FastForwardMode.FF, c.getEnum(
399 				ConfigConstants.CONFIG_BRANCH_SECTION, "side",
400 				ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.FF));
401 		mergeConfig = c.get(MergeConfig.getParser("side"));
402 		assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
403 		c = parse("[branch \"side\"]\n\tmergeoptions = --no-ff\n");
404 		assertSame(FastForwardMode.NO_FF, c.getEnum(
405 				ConfigConstants.CONFIG_BRANCH_SECTION, "side",
406 				ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.NO_FF));
407 		mergeConfig = c.get(MergeConfig.getParser("side"));
408 		assertSame(FastForwardMode.NO_FF, mergeConfig.getFastForwardMode());
409 	}
410 
411 	@Test
412 	public void testSetFastForwardMergeoptions() {
413 		final Config c = new Config();
414 		c.setEnum("branch", "side", "mergeoptions", FastForwardMode.FF);
415 		assertEquals("[branch \"side\"]\n\tmergeoptions = --ff\n", c.toText());
416 		c.setEnum("branch", "side", "mergeoptions", FastForwardMode.FF_ONLY);
417 		assertEquals("[branch \"side\"]\n\tmergeoptions = --ff-only\n",
418 				c.toText());
419 		c.setEnum("branch", "side", "mergeoptions", FastForwardMode.NO_FF);
420 		assertEquals("[branch \"side\"]\n\tmergeoptions = --no-ff\n",
421 				c.toText());
422 	}
423 
424 	@Test
425 	public void testGetFastForwardMerge() throws ConfigInvalidException {
426 		Config c = new Config(null); // not set
427 		assertSame(FastForwardMode.Merge.TRUE, c.getEnum(
428 				ConfigConstants.CONFIG_KEY_MERGE, null,
429 				ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.TRUE));
430 		MergeConfig mergeConfig = c.get(MergeConfig.getParser("side"));
431 		assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
432 		c = parse("[merge]\n\tff = only\n");
433 		assertSame(FastForwardMode.Merge.ONLY, c.getEnum(
434 				ConfigConstants.CONFIG_KEY_MERGE, null,
435 				ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.ONLY));
436 		mergeConfig = c.get(MergeConfig.getParser("side"));
437 		assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
438 		c = parse("[merge]\n\tff = true\n");
439 		assertSame(FastForwardMode.Merge.TRUE, c.getEnum(
440 				ConfigConstants.CONFIG_KEY_MERGE, null,
441 				ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.TRUE));
442 		mergeConfig = c.get(MergeConfig.getParser("side"));
443 		assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
444 		c = parse("[merge]\n\tff = false\n");
445 		assertSame(FastForwardMode.Merge.FALSE, c.getEnum(
446 				ConfigConstants.CONFIG_KEY_MERGE, null,
447 				ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.FALSE));
448 		mergeConfig = c.get(MergeConfig.getParser("side"));
449 		assertSame(FastForwardMode.NO_FF, mergeConfig.getFastForwardMode());
450 	}
451 
452 	@Test
453 	public void testCombinedMergeOptions() throws ConfigInvalidException {
454 		Config c = new Config(null); // not set
455 		MergeConfig mergeConfig = c.get(MergeConfig.getParser("side"));
456 		assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
457 		assertTrue(mergeConfig.isCommit());
458 		assertFalse(mergeConfig.isSquash());
459 		// branch..mergeoptions should win over merge.ff
460 		c = parse("[merge]\n\tff = false\n"
461 				+ "[branch \"side\"]\n\tmergeoptions = --ff-only\n");
462 		mergeConfig = c.get(MergeConfig.getParser("side"));
463 		assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
464 		assertTrue(mergeConfig.isCommit());
465 		assertFalse(mergeConfig.isSquash());
466 		// merge.ff used for ff setting if not set via mergeoptions
467 		c = parse("[merge]\n\tff = only\n"
468 				+ "[branch \"side\"]\n\tmergeoptions = --squash\n");
469 		mergeConfig = c.get(MergeConfig.getParser("side"));
470 		assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
471 		assertTrue(mergeConfig.isCommit());
472 		assertTrue(mergeConfig.isSquash());
473 		// mergeoptions wins if it has ff options amongst other options
474 		c = parse("[merge]\n\tff = false\n"
475 				+ "[branch \"side\"]\n\tmergeoptions = --ff-only --no-commit\n");
476 		mergeConfig = c.get(MergeConfig.getParser("side"));
477 		assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
478 		assertFalse(mergeConfig.isCommit());
479 		assertFalse(mergeConfig.isSquash());
480 	}
481 
482 	@Test
483 	public void testSetFastForwardMerge() {
484 		final Config c = new Config();
485 		c.setEnum("merge", null, "ff",
486 				FastForwardMode.Merge.valueOf(FastForwardMode.FF));
487 		assertEquals("[merge]\n\tff = true\n", c.toText());
488 		c.setEnum("merge", null, "ff",
489 				FastForwardMode.Merge.valueOf(FastForwardMode.FF_ONLY));
490 		assertEquals("[merge]\n\tff = only\n", c.toText());
491 		c.setEnum("merge", null, "ff",
492 				FastForwardMode.Merge.valueOf(FastForwardMode.NO_FF));
493 		assertEquals("[merge]\n\tff = false\n", c.toText());
494 	}
495 
496 	@Test
497 	public void testReadLong() throws ConfigInvalidException {
498 		assertReadLong(1L);
499 		assertReadLong(-1L);
500 		assertReadLong(Long.MIN_VALUE);
501 		assertReadLong(Long.MAX_VALUE);
502 		assertReadLong(4L * 1024 * 1024 * 1024, "4g");
503 		assertReadLong(3L * 1024 * 1024, "3 m");
504 		assertReadLong(8L * 1024, "8 k");
505 
506 		try {
507 			assertReadLong(-1, "1.5g");
508 			fail("incorrectly accepted 1.5g");
509 		} catch (IllegalArgumentException e) {
510 			assertEquals("Invalid integer value: s.a=1.5g", e.getMessage());
511 		}
512 	}
513 
514 	@Test
515 	public void testBooleanWithNoValue() throws ConfigInvalidException {
516 		Config c = parse("[my]\n\tempty\n");
517 		assertEquals("", c.getString("my", null, "empty"));
518 		assertEquals(1, c.getStringList("my", null, "empty").length);
519 		assertEquals("", c.getStringList("my", null, "empty")[0]);
520 		assertTrue(c.getBoolean("my", "empty", false));
521 		assertEquals("[my]\n\tempty\n", c.toText());
522 	}
523 
524 	@Test
525 	public void testUnsetBranchSection() throws ConfigInvalidException {
526 		Config c = parse("" //
527 				+ "[branch \"keep\"]\n"
528 				+ "  merge = master.branch.to.keep.in.the.file\n"
529 				+ "\n"
530 				+ "[branch \"remove\"]\n"
531 				+ "  merge = this.will.get.deleted\n"
532 				+ "  remote = origin-for-some-long-gone-place\n"
533 				+ "\n"
534 				+ "[core-section-not-to-remove-in-test]\n"
535 				+ "  packedGitLimit = 14\n");
536 		c.unsetSection("branch", "does.not.exist");
537 		c.unsetSection("branch", "remove");
538 		assertEquals("" //
539 				+ "[branch \"keep\"]\n"
540 				+ "  merge = master.branch.to.keep.in.the.file\n"
541 				+ "\n"
542 				+ "[core-section-not-to-remove-in-test]\n"
543 				+ "  packedGitLimit = 14\n", c.toText());
544 	}
545 
546 	@Test
547 	public void testUnsetSingleSection() throws ConfigInvalidException {
548 		Config c = parse("" //
549 				+ "[branch \"keep\"]\n"
550 				+ "  merge = master.branch.to.keep.in.the.file\n"
551 				+ "\n"
552 				+ "[single]\n"
553 				+ "  merge = this.will.get.deleted\n"
554 				+ "  remote = origin-for-some-long-gone-place\n"
555 				+ "\n"
556 				+ "[core-section-not-to-remove-in-test]\n"
557 				+ "  packedGitLimit = 14\n");
558 		c.unsetSection("single", null);
559 		assertEquals("" //
560 				+ "[branch \"keep\"]\n"
561 				+ "  merge = master.branch.to.keep.in.the.file\n"
562 				+ "\n"
563 				+ "[core-section-not-to-remove-in-test]\n"
564 				+ "  packedGitLimit = 14\n", c.toText());
565 	}
566 
567 	@Test
568 	public void test008_readSectionNames() throws ConfigInvalidException {
569 		final Config c = parse("[a]\n [B]\n");
570 		Set<String> sections = c.getSections();
571 		assertTrue("Sections should contain \"a\"", sections.contains("a"));
572 		assertTrue("Sections should contain \"b\"", sections.contains("b"));
573 	}
574 
575 	@Test
576 	public void test009_readNamesInSection() throws ConfigInvalidException {
577 		String configString = "[core]\n" + "repositoryFormatVersion = 0\n"
578 				+ "filemode = false\n" + "logAllRefUpdates = true\n";
579 		final Config c = parse(configString);
580 		Set<String> names = c.getNames("core");
581 		assertEquals("Core section size", 3, names.size());
582 		assertTrue("Core section should contain \"filemode\"", names
583 				.contains("filemode"));
584 
585 		assertTrue("Core section should contain \"repositoryFormatVersion\"",
586 				names.contains("repositoryFormatVersion"));
587 
588 		assertTrue("Core section should contain \"repositoryformatversion\"",
589 				names.contains("repositoryformatversion"));
590 
591 		Iterator<String> itr = names.iterator();
592 		assertEquals("filemode", itr.next());
593 		assertEquals("logAllRefUpdates", itr.next());
594 		assertEquals("repositoryFormatVersion", itr.next());
595 		assertFalse(itr.hasNext());
596 	}
597 
598 	@Test
599 	public void test_ReadNamesInSectionRecursive()
600 			throws ConfigInvalidException {
601 		String baseConfigString = "[core]\n" + "logAllRefUpdates = true\n";
602 		String configString = "[core]\n" + "repositoryFormatVersion = 0\n"
603 				+ "filemode = false\n";
604 		final Config c = parse(configString, parse(baseConfigString));
605 		Set<String> names = c.getNames("core", true);
606 		assertEquals("Core section size", 3, names.size());
607 		assertTrue("Core section should contain \"filemode\"",
608 				names.contains("filemode"));
609 		assertTrue("Core section should contain \"repositoryFormatVersion\"",
610 				names.contains("repositoryFormatVersion"));
611 		assertTrue("Core section should contain \"logAllRefUpdates\"",
612 				names.contains("logAllRefUpdates"));
613 		assertTrue("Core section should contain \"logallrefupdates\"",
614 				names.contains("logallrefupdates"));
615 
616 		Iterator<String> itr = names.iterator();
617 		assertEquals("filemode", itr.next());
618 		assertEquals("repositoryFormatVersion", itr.next());
619 		assertEquals("logAllRefUpdates", itr.next());
620 		assertFalse(itr.hasNext());
621 	}
622 
623 	@Test
624 	public void test010_readNamesInSubSection() throws ConfigInvalidException {
625 		String configString = "[a \"sub1\"]\n"//
626 				+ "x = 0\n" //
627 				+ "y = false\n"//
628 				+ "z = true\n"//
629 				+ "[a \"sub2\"]\n"//
630 				+ "a=0\n"//
631 				+ "b=1\n";
632 		final Config c = parse(configString);
633 		Set<String> names = c.getNames("a", "sub1");
634 		assertEquals("Subsection size", 3, names.size());
635 		assertTrue("Subsection should contain \"x\"", names.contains("x"));
636 		assertTrue("Subsection should contain \"y\"", names.contains("y"));
637 		assertTrue("Subsection should contain \"z\"", names.contains("z"));
638 		names = c.getNames("a", "sub2");
639 		assertEquals("Subsection size", 2, names.size());
640 		assertTrue("Subsection should contain \"a\"", names.contains("a"));
641 		assertTrue("Subsection should contain \"b\"", names.contains("b"));
642 	}
643 
644 	@Test
645 	public void readNamesInSubSectionRecursive() throws ConfigInvalidException {
646 		String baseConfigString = "[a \"sub1\"]\n"//
647 				+ "x = 0\n" //
648 				+ "y = false\n"//
649 				+ "[a \"sub2\"]\n"//
650 				+ "A=0\n";//
651 		String configString = "[a \"sub1\"]\n"//
652 				+ "z = true\n"//
653 				+ "[a \"sub2\"]\n"//
654 				+ "B=1\n";
655 		final Config c = parse(configString, parse(baseConfigString));
656 		Set<String> names = c.getNames("a", "sub1", true);
657 		assertEquals("Subsection size", 3, names.size());
658 		assertTrue("Subsection should contain \"x\"", names.contains("x"));
659 		assertTrue("Subsection should contain \"y\"", names.contains("y"));
660 		assertTrue("Subsection should contain \"z\"", names.contains("z"));
661 		names = c.getNames("a", "sub2", true);
662 		assertEquals("Subsection size", 2, names.size());
663 		assertTrue("Subsection should contain \"A\"", names.contains("A"));
664 		assertTrue("Subsection should contain \"a\"", names.contains("a"));
665 		assertTrue("Subsection should contain \"B\"", names.contains("B"));
666 	}
667 
668 	@Test
669 	public void testQuotingForSubSectionNames() {
670 		String resultPattern = "[testsection \"{0}\"]\n\ttestname = testvalue\n";
671 		String result;
672 
673 		Config config = new Config();
674 		config.setString("testsection", "testsubsection", "testname",
675 				"testvalue");
676 
677 		result = MessageFormat.format(resultPattern, "testsubsection");
678 		assertEquals(result, config.toText());
679 		config.clear();
680 
681 		config.setString("testsection", "#quotable", "testname", "testvalue");
682 		result = MessageFormat.format(resultPattern, "#quotable");
683 		assertEquals(result, config.toText());
684 		config.clear();
685 
686 		config.setString("testsection", "with\"quote", "testname", "testvalue");
687 		result = MessageFormat.format(resultPattern, "with\\\"quote");
688 		assertEquals(result, config.toText());
689 	}
690 
691 	@Test
692 	public void testNoFinalNewline() throws ConfigInvalidException {
693 		Config c = parse("[a]\n"
694 				+ "x = 0\n"
695 				+ "y = 1");
696 		assertEquals("0", c.getString("a", null, "x"));
697 		assertEquals("1", c.getString("a", null, "y"));
698 	}
699 
700 	@Test
701 	public void testExplicitlySetEmptyString() throws Exception {
702 		Config c = new Config();
703 		c.setString("a", null, "x", "0");
704 		c.setString("a", null, "y", "");
705 
706 		assertEquals("0", c.getString("a", null, "x"));
707 		assertEquals(0, c.getInt("a", null, "x", 1));
708 
709 		assertEquals("", c.getString("a", null, "y"));
710 		assertArrayEquals(new String[]{""}, c.getStringList("a", null, "y"));
711 		try {
712 			c.getInt("a", null, "y", 1);
713 		} catch (IllegalArgumentException e) {
714 			assertEquals("Invalid integer value: a.y=", e.getMessage());
715 		}
716 
717 		assertNull(c.getString("a", null, "z"));
718 		assertArrayEquals(new String[]{}, c.getStringList("a", null, "z"));
719 	}
720 
721 	@Test
722 	public void testParsedEmptyString() throws Exception {
723 		Config c = parse("[a]\n"
724 				+ "x = 0\n"
725 				+ "y =\n");
726 
727 		assertEquals("0", c.getString("a", null, "x"));
728 		assertEquals(0, c.getInt("a", null, "x", 1));
729 
730 		assertNull(c.getString("a", null, "y"));
731 		assertArrayEquals(new String[]{null}, c.getStringList("a", null, "y"));
732 		try {
733 			c.getInt("a", null, "y", 1);
734 		} catch (IllegalArgumentException e) {
735 			assertEquals("Invalid integer value: a.y=", e.getMessage());
736 		}
737 
738 		assertNull(c.getString("a", null, "z"));
739 		assertArrayEquals(new String[]{}, c.getStringList("a", null, "z"));
740 	}
741 
742 	@Test
743 	public void testSetStringListWithEmptyValue() throws Exception {
744 		Config c = new Config();
745 		c.setStringList("a", null, "x", Arrays.asList(""));
746 		assertArrayEquals(new String[]{""}, c.getStringList("a", null, "x"));
747 	}
748 
749 	@Test
750 	public void testEmptyValueAtEof() throws Exception {
751 		String text = "[a]\nx =";
752 		Config c = parse(text);
753 		assertNull(c.getString("a", null, "x"));
754 		assertArrayEquals(new String[]{null},
755 				c.getStringList("a", null, "x"));
756 		c = parse(text + "\n");
757 		assertNull(c.getString("a", null, "x"));
758 		assertArrayEquals(new String[]{null},
759 				c.getStringList("a", null, "x"));
760 	}
761 
762 	@Test
763 	public void testReadMultipleValuesForName() throws ConfigInvalidException {
764 		Config c = parse("[foo]\nbar=false\nbar=true\n");
765 		assertTrue(c.getBoolean("foo", "bar", false));
766 	}
767 
768 	@Test
769 	public void testIncludeInvalidName() throws ConfigInvalidException {
770 		expectedEx.expect(ConfigInvalidException.class);
771 		expectedEx.expectMessage(JGitText.get().invalidLineInConfigFile);
772 		parse("[include]\nbar\n");
773 	}
774 
775 	@Test
776 	public void testIncludeNoValue() throws ConfigInvalidException {
777 		expectedEx.expect(ConfigInvalidException.class);
778 		expectedEx.expectMessage(JGitText.get().invalidLineInConfigFile);
779 		parse("[include]\npath\n");
780 	}
781 
782 	@Test
783 	public void testIncludeEmptyValue() throws ConfigInvalidException {
784 		expectedEx.expect(ConfigInvalidException.class);
785 		expectedEx.expectMessage(JGitText.get().invalidLineInConfigFile);
786 		parse("[include]\npath=\n");
787 	}
788 
789 	@Test
790 	public void testIncludeValuePathNotFound() throws ConfigInvalidException {
791 		// we do not expect an exception, included path not found are ignored
792 		String notFound = "/not/found";
793 		Config parsed = parse("[include]\npath=" + notFound + "\n");
794 		assertEquals(1, parsed.getSections().size());
795 		assertEquals(notFound, parsed.getString("include", null, "path"));
796 	}
797 
798 	@Test
799 	public void testIncludeValuePathWithTilde() throws ConfigInvalidException {
800 		// we do not expect an exception, included path not supported are
801 		// ignored
802 		String notSupported = "~/someFile";
803 		Config parsed = parse("[include]\npath=" + notSupported + "\n");
804 		assertEquals(1, parsed.getSections().size());
805 		assertEquals(notSupported, parsed.getString("include", null, "path"));
806 	}
807 
808 	@Test
809 	public void testIncludeValuePathRelative() throws ConfigInvalidException {
810 		// we do not expect an exception, included path not supported are
811 		// ignored
812 		String notSupported = "someRelativeFile";
813 		Config parsed = parse("[include]\npath=" + notSupported + "\n");
814 		assertEquals(1, parsed.getSections().size());
815 		assertEquals(notSupported, parsed.getString("include", null, "path"));
816 	}
817 
818 	@Test
819 	public void testIncludeTooManyRecursions() throws IOException {
820 		File config = tmp.newFile("config");
821 		String include = "[include]\npath=" + config.toPath() + "\n";
822 		Files.write(config.toPath(), include.getBytes());
823 		FileBasedConfig fbConfig = new FileBasedConfig(null, config,
824 				FS.DETECTED);
825 		try {
826 			fbConfig.load();
827 			fail();
828 		} catch (ConfigInvalidException cie) {
829 			assertEquals(JGitText.get().tooManyIncludeRecursions,
830 					cie.getCause().getMessage());
831 		}
832 	}
833 
834 	@Test
835 	public void testInclude() throws IOException, ConfigInvalidException {
836 		File config = tmp.newFile("config");
837 		File more = tmp.newFile("config.more");
838 		File other = tmp.newFile("config.other");
839 
840 		String fooBar = "[foo]\nbar=true\n";
841 		String includeMore = "[include]\npath=" + more.toPath() + "\n";
842 		String includeOther = "path=" + other.toPath() + "\n";
843 		String fooPlus = fooBar + includeMore + includeOther;
844 		Files.write(config.toPath(), fooPlus.getBytes());
845 
846 		String fooMore = "[foo]\nmore=bar\n";
847 		Files.write(more.toPath(), fooMore.getBytes());
848 
849 		String otherMore = "[other]\nmore=bar\n";
850 		Files.write(other.toPath(), otherMore.getBytes());
851 
852 		Config parsed = parse("[include]\npath=" + config.toPath() + "\n");
853 		assertTrue(parsed.getBoolean("foo", "bar", false));
854 		assertEquals("bar", parsed.getString("foo", null, "more"));
855 		assertEquals("bar", parsed.getString("other", null, "more"));
856 	}
857 
858 	private static void assertReadLong(long exp) throws ConfigInvalidException {
859 		assertReadLong(exp, String.valueOf(exp));
860 	}
861 
862 	private static void assertReadLong(long exp, String act)
863 			throws ConfigInvalidException {
864 		final Config c = parse("[s]\na = " + act + "\n");
865 		assertEquals(exp, c.getLong("s", null, "a", 0L));
866 	}
867 
868 	private static Config parse(final String content)
869 			throws ConfigInvalidException {
870 		return parse(content, null);
871 	}
872 
873 	private static Config parse(final String content, Config baseConfig)
874 			throws ConfigInvalidException {
875 		final Config c = new Config(baseConfig);
876 		c.fromText(content);
877 		return c;
878 	}
879 
880 	@Test
881 	public void testTimeUnit() throws ConfigInvalidException {
882 		assertEquals(0, parseTime("0", MILLISECONDS));
883 		assertEquals(2, parseTime("2ms", MILLISECONDS));
884 		assertEquals(200, parseTime("200 milliseconds", MILLISECONDS));
885 
886 		assertEquals(0, parseTime("0s", SECONDS));
887 		assertEquals(2, parseTime("2s", SECONDS));
888 		assertEquals(231, parseTime("231sec", SECONDS));
889 		assertEquals(1, parseTime("1second", SECONDS));
890 		assertEquals(300, parseTime("300 seconds", SECONDS));
891 
892 		assertEquals(2, parseTime("2m", MINUTES));
893 		assertEquals(2, parseTime("2min", MINUTES));
894 		assertEquals(1, parseTime("1 minute", MINUTES));
895 		assertEquals(10, parseTime("10 minutes", MINUTES));
896 
897 		assertEquals(5, parseTime("5h", HOURS));
898 		assertEquals(5, parseTime("5hr", HOURS));
899 		assertEquals(1, parseTime("1hour", HOURS));
900 		assertEquals(48, parseTime("48hours", HOURS));
901 
902 		assertEquals(5, parseTime("5 h", HOURS));
903 		assertEquals(5, parseTime("5 hr", HOURS));
904 		assertEquals(1, parseTime("1 hour", HOURS));
905 		assertEquals(48, parseTime("48 hours", HOURS));
906 		assertEquals(48, parseTime("48 \t \r hours", HOURS));
907 
908 		assertEquals(4, parseTime("4d", DAYS));
909 		assertEquals(1, parseTime("1day", DAYS));
910 		assertEquals(14, parseTime("14days", DAYS));
911 
912 		assertEquals(7, parseTime("1w", DAYS));
913 		assertEquals(7, parseTime("1week", DAYS));
914 		assertEquals(14, parseTime("2w", DAYS));
915 		assertEquals(14, parseTime("2weeks", DAYS));
916 
917 		assertEquals(30, parseTime("1mon", DAYS));
918 		assertEquals(30, parseTime("1month", DAYS));
919 		assertEquals(60, parseTime("2mon", DAYS));
920 		assertEquals(60, parseTime("2months", DAYS));
921 
922 		assertEquals(365, parseTime("1y", DAYS));
923 		assertEquals(365, parseTime("1year", DAYS));
924 		assertEquals(365 * 2, parseTime("2years", DAYS));
925 	}
926 
927 	private long parseTime(String value, TimeUnit unit)
928 			throws ConfigInvalidException {
929 		Config c = parse("[a]\na=" + value + "\n");
930 		return c.getTimeUnit("a", null, "a", 0, unit);
931 	}
932 
933 	@Test
934 	public void testTimeUnitDefaultValue() throws ConfigInvalidException {
935 		// value not present
936 		assertEquals(20, parse("[a]\na=0\n").getTimeUnit("a", null, "b", 20,
937 				MILLISECONDS));
938 		// value is empty
939 		assertEquals(20, parse("[a]\na=\" \"\n").getTimeUnit("a", null, "a", 20,
940 				MILLISECONDS));
941 
942 		// value is not numeric
943 		assertEquals(20, parse("[a]\na=test\n").getTimeUnit("a", null, "a", 20,
944 				MILLISECONDS));
945 	}
946 
947 	@Test
948 	public void testTimeUnitInvalid() throws ConfigInvalidException {
949 		expectedEx.expect(IllegalArgumentException.class);
950 		expectedEx
951 				.expectMessage("Invalid time unit value: a.a=1 monttthhh");
952 		parseTime("1 monttthhh", DAYS);
953 	}
954 
955 	@Test
956 	public void testTimeUnitInvalidWithSection() throws ConfigInvalidException {
957 		Config c = parse("[a \"b\"]\na=1 monttthhh\n");
958 		expectedEx.expect(IllegalArgumentException.class);
959 		expectedEx.expectMessage("Invalid time unit value: a.b.a=1 monttthhh");
960 		c.getTimeUnit("a", "b", "a", 0, DAYS);
961 	}
962 
963 	@Test
964 	public void testTimeUnitNegative() throws ConfigInvalidException {
965 		expectedEx.expect(IllegalArgumentException.class);
966 		parseTime("-1", MILLISECONDS);
967 	}
968 }