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 org.junit.Assert.assertArrayEquals;
52  import static org.junit.Assert.assertEquals;
53  import static org.junit.Assert.assertFalse;
54  import static org.junit.Assert.assertNull;
55  import static org.junit.Assert.assertSame;
56  import static org.junit.Assert.assertTrue;
57  import static org.junit.Assert.fail;
58  
59  import java.text.MessageFormat;
60  import java.util.Arrays;
61  import java.util.Iterator;
62  import java.util.LinkedList;
63  import java.util.Set;
64  
65  import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
66  import org.eclipse.jgit.errors.ConfigInvalidException;
67  import org.eclipse.jgit.junit.MockSystemReader;
68  import org.eclipse.jgit.merge.MergeConfig;
69  import org.eclipse.jgit.util.FS;
70  import org.eclipse.jgit.util.SystemReader;
71  import org.junit.After;
72  import org.junit.Test;
73  
74  /**
75   * Test reading of git config
76   */
77  public class ConfigTest {
78  
79  	@After
80  	public void tearDown() {
81  		SystemReader.setInstance(null);
82  	}
83  
84  	@Test
85  	public void test001_ReadBareKey() throws ConfigInvalidException {
86  		final Config c = parse("[foo]\nbar\n");
87  		assertTrue(c.getBoolean("foo", null, "bar", false));
88  		assertEquals("", c.getString("foo", null, "bar"));
89  	}
90  
91  	@Test
92  	public void test002_ReadWithSubsection() throws ConfigInvalidException {
93  		final Config c = parse("[foo \"zip\"]\nbar\n[foo \"zap\"]\nbar=false\nn=3\n");
94  		assertTrue(c.getBoolean("foo", "zip", "bar", false));
95  		assertEquals("", c.getString("foo","zip", "bar"));
96  		assertFalse(c.getBoolean("foo", "zap", "bar", true));
97  		assertEquals("false", c.getString("foo", "zap", "bar"));
98  		assertEquals(3, c.getInt("foo", "zap", "n", 4));
99  		assertEquals(4, c.getInt("foo", "zap","m", 4));
100 	}
101 
102 	@Test
103 	public void test003_PutRemote() {
104 		final Config c = new Config();
105 		c.setString("sec", "ext", "name", "value");
106 		c.setString("sec", "ext", "name2", "value2");
107 		final String expText = "[sec \"ext\"]\n\tname = value\n\tname2 = value2\n";
108 		assertEquals(expText, c.toText());
109 	}
110 
111 	@Test
112 	public void test004_PutGetSimple() {
113 		Config c = new Config();
114 		c.setString("my", null, "somename", "false");
115 		assertEquals("false", c.getString("my", null, "somename"));
116 		assertEquals("[my]\n\tsomename = false\n", c.toText());
117 	}
118 
119 	@Test
120 	public void test005_PutGetStringList() {
121 		Config c = new Config();
122 		final LinkedList<String> values = new LinkedList<String>();
123 		values.add("value1");
124 		values.add("value2");
125 		c.setStringList("my", null, "somename", values);
126 
127 		final Object[] expArr = values.toArray();
128 		final String[] actArr = c.getStringList("my", null, "somename");
129 		assertArrayEquals(expArr, actArr);
130 
131 		final String expText = "[my]\n\tsomename = value1\n\tsomename = value2\n";
132 		assertEquals(expText, c.toText());
133 	}
134 
135 	@Test
136 	public void test006_readCaseInsensitive() throws ConfigInvalidException {
137 		final Config c = parse("[Foo]\nBar\n");
138 		assertTrue(c.getBoolean("foo", null, "bar", false));
139 		assertEquals("", c.getString("foo", null, "bar"));
140 	}
141 
142 	@Test
143 	public void test007_readUserConfig() {
144 		final MockSystemReader mockSystemReader = new MockSystemReader();
145 		SystemReader.setInstance(mockSystemReader);
146 		final String hostname = mockSystemReader.getHostname();
147 		final Config userGitConfig = mockSystemReader.openUserConfig(null,
148 				FS.DETECTED);
149 		final Config localConfig = new Config(userGitConfig);
150 		mockSystemReader.clearProperties();
151 
152 		String authorName;
153 		String authorEmail;
154 
155 		// no values defined nowhere
156 		authorName = localConfig.get(UserConfig.KEY).getAuthorName();
157 		authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
158 		assertEquals(Constants.UNKNOWN_USER_DEFAULT, authorName);
159 		assertEquals(Constants.UNKNOWN_USER_DEFAULT + "@" + hostname, authorEmail);
160 		assertTrue(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
161 		assertTrue(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
162 
163 		// the system user name is defined
164 		mockSystemReader.setProperty(Constants.OS_USER_NAME_KEY, "os user name");
165 		localConfig.uncache(UserConfig.KEY);
166 		authorName = localConfig.get(UserConfig.KEY).getAuthorName();
167 		assertEquals("os user name", authorName);
168 		assertTrue(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
169 
170 		if (hostname != null && hostname.length() != 0) {
171 			authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
172 			assertEquals("os user name@" + hostname, authorEmail);
173 		}
174 		assertTrue(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
175 
176 		// the git environment variables are defined
177 		mockSystemReader.setProperty(Constants.GIT_AUTHOR_NAME_KEY, "git author name");
178 		mockSystemReader.setProperty(Constants.GIT_AUTHOR_EMAIL_KEY, "author@email");
179 		localConfig.uncache(UserConfig.KEY);
180 		authorName = localConfig.get(UserConfig.KEY).getAuthorName();
181 		authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
182 		assertEquals("git author name", authorName);
183 		assertEquals("author@email", authorEmail);
184 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
185 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
186 
187 		// the values are defined in the global configuration
188 		// first clear environment variables since they would override
189 		// configuration files
190 		mockSystemReader.clearProperties();
191 		userGitConfig.setString("user", null, "name", "global username");
192 		userGitConfig.setString("user", null, "email", "author@globalemail");
193 		authorName = localConfig.get(UserConfig.KEY).getAuthorName();
194 		authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
195 		assertEquals("global username", authorName);
196 		assertEquals("author@globalemail", authorEmail);
197 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
198 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
199 
200 		// the values are defined in the local configuration
201 		localConfig.setString("user", null, "name", "local username");
202 		localConfig.setString("user", null, "email", "author@localemail");
203 		authorName = localConfig.get(UserConfig.KEY).getAuthorName();
204 		authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
205 		assertEquals("local username", authorName);
206 		assertEquals("author@localemail", authorEmail);
207 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
208 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
209 
210 		authorName = localConfig.get(UserConfig.KEY).getCommitterName();
211 		authorEmail = localConfig.get(UserConfig.KEY).getCommitterEmail();
212 		assertEquals("local username", authorName);
213 		assertEquals("author@localemail", authorEmail);
214 		assertFalse(localConfig.get(UserConfig.KEY).isCommitterNameImplicit());
215 		assertFalse(localConfig.get(UserConfig.KEY).isCommitterEmailImplicit());
216 
217 		// also git environment variables are defined
218 		mockSystemReader.setProperty(Constants.GIT_AUTHOR_NAME_KEY,
219 				"git author name");
220 		mockSystemReader.setProperty(Constants.GIT_AUTHOR_EMAIL_KEY,
221 				"author@email");
222 		localConfig.setString("user", null, "name", "local username");
223 		localConfig.setString("user", null, "email", "author@localemail");
224 		authorName = localConfig.get(UserConfig.KEY).getAuthorName();
225 		authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
226 		assertEquals("git author name", authorName);
227 		assertEquals("author@email", authorEmail);
228 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
229 		assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
230 	}
231 
232 	@Test
233 	public void testReadUserConfigWithInvalidCharactersStripped() {
234 		final MockSystemReader mockSystemReader = new MockSystemReader();
235 		final Config localConfig = new Config(mockSystemReader.openUserConfig(
236 				null, FS.DETECTED));
237 
238 		localConfig.setString("user", null, "name", "foo<bar");
239 		localConfig.setString("user", null, "email", "baz>\nqux@example.com");
240 
241 		UserConfig userConfig = localConfig.get(UserConfig.KEY);
242 		assertEquals("foobar", userConfig.getAuthorName());
243 		assertEquals("bazqux@example.com", userConfig.getAuthorEmail());
244 	}
245 
246 	@Test
247 	public void testReadBoolean_TrueFalse1() throws ConfigInvalidException {
248 		final Config c = parse("[s]\na = true\nb = false\n");
249 		assertEquals("true", c.getString("s", null, "a"));
250 		assertEquals("false", c.getString("s", null, "b"));
251 
252 		assertTrue(c.getBoolean("s", "a", false));
253 		assertFalse(c.getBoolean("s", "b", true));
254 	}
255 
256 	@Test
257 	public void testReadBoolean_TrueFalse2() throws ConfigInvalidException {
258 		final Config c = parse("[s]\na = TrUe\nb = fAlSe\n");
259 		assertEquals("TrUe", c.getString("s", null, "a"));
260 		assertEquals("fAlSe", c.getString("s", null, "b"));
261 
262 		assertTrue(c.getBoolean("s", "a", false));
263 		assertFalse(c.getBoolean("s", "b", true));
264 	}
265 
266 	@Test
267 	public void testReadBoolean_YesNo1() throws ConfigInvalidException {
268 		final Config c = parse("[s]\na = yes\nb = no\n");
269 		assertEquals("yes", c.getString("s", null, "a"));
270 		assertEquals("no", 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_YesNo2() throws ConfigInvalidException {
278 		final Config c = parse("[s]\na = yEs\nb = NO\n");
279 		assertEquals("yEs", c.getString("s", null, "a"));
280 		assertEquals("NO", 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_OnOff1() throws ConfigInvalidException {
288 		final Config c = parse("[s]\na = on\nb = off\n");
289 		assertEquals("on", c.getString("s", null, "a"));
290 		assertEquals("off", 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_OnOff2() throws ConfigInvalidException {
298 		final Config c = parse("[s]\na = ON\nb = OFF\n");
299 		assertEquals("ON", c.getString("s", null, "a"));
300 		assertEquals("OFF", c.getString("s", null, "b"));
301 
302 		assertTrue(c.getBoolean("s", "a", false));
303 		assertFalse(c.getBoolean("s", "b", true));
304 	}
305 
306 	static enum TestEnum {
307 		ONE_TWO;
308 	}
309 
310 	@Test
311 	public void testGetEnum() throws ConfigInvalidException {
312 		Config c = parse("[s]\na = ON\nb = input\nc = true\nd = off\n");
313 		assertSame(CoreConfig.AutoCRLF.TRUE, c.getEnum("s", null, "a",
314 				CoreConfig.AutoCRLF.FALSE));
315 
316 		assertSame(CoreConfig.AutoCRLF.INPUT, c.getEnum("s", null, "b",
317 				CoreConfig.AutoCRLF.FALSE));
318 
319 		assertSame(CoreConfig.AutoCRLF.TRUE, c.getEnum("s", null, "c",
320 				CoreConfig.AutoCRLF.FALSE));
321 
322 		assertSame(CoreConfig.AutoCRLF.FALSE, c.getEnum("s", null, "d",
323 				CoreConfig.AutoCRLF.TRUE));
324 
325 		c = new Config();
326 		assertSame(CoreConfig.AutoCRLF.FALSE, c.getEnum("s", null, "d",
327 				CoreConfig.AutoCRLF.FALSE));
328 
329 		c = parse("[s \"b\"]\n\tc = one two\n");
330 		assertSame(TestEnum.ONE_TWO, c.getEnum("s", "b", "c", TestEnum.ONE_TWO));
331 
332 		c = parse("[s \"b\"]\n\tc = one-two\n");
333 		assertSame(TestEnum.ONE_TWO, c.getEnum("s", "b", "c", TestEnum.ONE_TWO));
334 	}
335 
336 	@Test
337 	public void testGetInvalidEnum() throws ConfigInvalidException {
338 		Config c = parse("[a]\n\tb = invalid\n");
339 		try {
340 			c.getEnum("a", null, "b", TestEnum.ONE_TWO);
341 			fail();
342 		} catch (IllegalArgumentException e) {
343 			assertEquals("Invalid value: a.b=invalid", e.getMessage());
344 		}
345 
346 		c = parse("[a \"b\"]\n\tc = invalid\n");
347 		try {
348 			c.getEnum("a", "b", "c", TestEnum.ONE_TWO);
349 			fail();
350 		} catch (IllegalArgumentException e) {
351 			assertEquals("Invalid value: a.b.c=invalid", e.getMessage());
352 		}
353 	}
354 
355 	@Test
356 	public void testSetEnum() {
357 		final Config c = new Config();
358 		c.setEnum("s", "b", "c", TestEnum.ONE_TWO);
359 		assertEquals("[s \"b\"]\n\tc = one two\n", c.toText());
360 	}
361 
362 	@Test
363 	public void testGetFastForwardMergeoptions() throws ConfigInvalidException {
364 		Config c = new Config(null); // not set
365 		assertSame(FastForwardMode.FF, c.getEnum(
366 				ConfigConstants.CONFIG_BRANCH_SECTION, "side",
367 				ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.FF));
368 		MergeConfig mergeConfig = c.get(MergeConfig.getParser("side"));
369 		assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
370 		c = parse("[branch \"side\"]\n\tmergeoptions = --ff-only\n");
371 		assertSame(FastForwardMode.FF_ONLY, c.getEnum(
372 				ConfigConstants.CONFIG_BRANCH_SECTION, "side",
373 				ConfigConstants.CONFIG_KEY_MERGEOPTIONS,
374 				FastForwardMode.FF_ONLY));
375 		mergeConfig = c.get(MergeConfig.getParser("side"));
376 		assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
377 		c = parse("[branch \"side\"]\n\tmergeoptions = --ff\n");
378 		assertSame(FastForwardMode.FF, c.getEnum(
379 				ConfigConstants.CONFIG_BRANCH_SECTION, "side",
380 				ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.FF));
381 		mergeConfig = c.get(MergeConfig.getParser("side"));
382 		assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
383 		c = parse("[branch \"side\"]\n\tmergeoptions = --no-ff\n");
384 		assertSame(FastForwardMode.NO_FF, c.getEnum(
385 				ConfigConstants.CONFIG_BRANCH_SECTION, "side",
386 				ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.NO_FF));
387 		mergeConfig = c.get(MergeConfig.getParser("side"));
388 		assertSame(FastForwardMode.NO_FF, mergeConfig.getFastForwardMode());
389 	}
390 
391 	@Test
392 	public void testSetFastForwardMergeoptions() {
393 		final Config c = new Config();
394 		c.setEnum("branch", "side", "mergeoptions", FastForwardMode.FF);
395 		assertEquals("[branch \"side\"]\n\tmergeoptions = --ff\n", c.toText());
396 		c.setEnum("branch", "side", "mergeoptions", FastForwardMode.FF_ONLY);
397 		assertEquals("[branch \"side\"]\n\tmergeoptions = --ff-only\n",
398 				c.toText());
399 		c.setEnum("branch", "side", "mergeoptions", FastForwardMode.NO_FF);
400 		assertEquals("[branch \"side\"]\n\tmergeoptions = --no-ff\n",
401 				c.toText());
402 	}
403 
404 	@Test
405 	public void testGetFastForwardMerge() throws ConfigInvalidException {
406 		Config c = new Config(null); // not set
407 		assertSame(FastForwardMode.Merge.TRUE, c.getEnum(
408 				ConfigConstants.CONFIG_KEY_MERGE, null,
409 				ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.TRUE));
410 		MergeConfig mergeConfig = c.get(MergeConfig.getParser("side"));
411 		assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
412 		c = parse("[merge]\n\tff = only\n");
413 		assertSame(FastForwardMode.Merge.ONLY, c.getEnum(
414 				ConfigConstants.CONFIG_KEY_MERGE, null,
415 				ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.ONLY));
416 		mergeConfig = c.get(MergeConfig.getParser("side"));
417 		assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
418 		c = parse("[merge]\n\tff = true\n");
419 		assertSame(FastForwardMode.Merge.TRUE, c.getEnum(
420 				ConfigConstants.CONFIG_KEY_MERGE, null,
421 				ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.TRUE));
422 		mergeConfig = c.get(MergeConfig.getParser("side"));
423 		assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
424 		c = parse("[merge]\n\tff = false\n");
425 		assertSame(FastForwardMode.Merge.FALSE, c.getEnum(
426 				ConfigConstants.CONFIG_KEY_MERGE, null,
427 				ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.FALSE));
428 		mergeConfig = c.get(MergeConfig.getParser("side"));
429 		assertSame(FastForwardMode.NO_FF, mergeConfig.getFastForwardMode());
430 	}
431 
432 	@Test
433 	public void testCombinedMergeOptions() throws ConfigInvalidException {
434 		Config c = new Config(null); // not set
435 		MergeConfig mergeConfig = c.get(MergeConfig.getParser("side"));
436 		assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
437 		assertTrue(mergeConfig.isCommit());
438 		assertFalse(mergeConfig.isSquash());
439 		// branch..mergeoptions should win over merge.ff
440 		c = parse("[merge]\n\tff = false\n"
441 				+ "[branch \"side\"]\n\tmergeoptions = --ff-only\n");
442 		mergeConfig = c.get(MergeConfig.getParser("side"));
443 		assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
444 		assertTrue(mergeConfig.isCommit());
445 		assertFalse(mergeConfig.isSquash());
446 		// merge.ff used for ff setting if not set via mergeoptions
447 		c = parse("[merge]\n\tff = only\n"
448 				+ "[branch \"side\"]\n\tmergeoptions = --squash\n");
449 		mergeConfig = c.get(MergeConfig.getParser("side"));
450 		assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
451 		assertTrue(mergeConfig.isCommit());
452 		assertTrue(mergeConfig.isSquash());
453 		// mergeoptions wins if it has ff options amongst other options
454 		c = parse("[merge]\n\tff = false\n"
455 				+ "[branch \"side\"]\n\tmergeoptions = --ff-only --no-commit\n");
456 		mergeConfig = c.get(MergeConfig.getParser("side"));
457 		assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
458 		assertFalse(mergeConfig.isCommit());
459 		assertFalse(mergeConfig.isSquash());
460 	}
461 
462 	@Test
463 	public void testSetFastForwardMerge() {
464 		final Config c = new Config();
465 		c.setEnum("merge", null, "ff",
466 				FastForwardMode.Merge.valueOf(FastForwardMode.FF));
467 		assertEquals("[merge]\n\tff = true\n", c.toText());
468 		c.setEnum("merge", null, "ff",
469 				FastForwardMode.Merge.valueOf(FastForwardMode.FF_ONLY));
470 		assertEquals("[merge]\n\tff = only\n", c.toText());
471 		c.setEnum("merge", null, "ff",
472 				FastForwardMode.Merge.valueOf(FastForwardMode.NO_FF));
473 		assertEquals("[merge]\n\tff = false\n", c.toText());
474 	}
475 
476 	@Test
477 	public void testReadLong() throws ConfigInvalidException {
478 		assertReadLong(1L);
479 		assertReadLong(-1L);
480 		assertReadLong(Long.MIN_VALUE);
481 		assertReadLong(Long.MAX_VALUE);
482 		assertReadLong(4L * 1024 * 1024 * 1024, "4g");
483 		assertReadLong(3L * 1024 * 1024, "3 m");
484 		assertReadLong(8L * 1024, "8 k");
485 
486 		try {
487 			assertReadLong(-1, "1.5g");
488 			fail("incorrectly accepted 1.5g");
489 		} catch (IllegalArgumentException e) {
490 			assertEquals("Invalid integer value: s.a=1.5g", e.getMessage());
491 		}
492 	}
493 
494 	@Test
495 	public void testBooleanWithNoValue() throws ConfigInvalidException {
496 		Config c = parse("[my]\n\tempty\n");
497 		assertEquals("", c.getString("my", null, "empty"));
498 		assertEquals(1, c.getStringList("my", null, "empty").length);
499 		assertEquals("", c.getStringList("my", null, "empty")[0]);
500 		assertTrue(c.getBoolean("my", "empty", false));
501 		assertEquals("[my]\n\tempty\n", c.toText());
502 	}
503 
504 	@Test
505 	public void testUnsetBranchSection() throws ConfigInvalidException {
506 		Config c = parse("" //
507 				+ "[branch \"keep\"]\n"
508 				+ "  merge = master.branch.to.keep.in.the.file\n"
509 				+ "\n"
510 				+ "[branch \"remove\"]\n"
511 				+ "  merge = this.will.get.deleted\n"
512 				+ "  remote = origin-for-some-long-gone-place\n"
513 				+ "\n"
514 				+ "[core-section-not-to-remove-in-test]\n"
515 				+ "  packedGitLimit = 14\n");
516 		c.unsetSection("branch", "does.not.exist");
517 		c.unsetSection("branch", "remove");
518 		assertEquals("" //
519 				+ "[branch \"keep\"]\n"
520 				+ "  merge = master.branch.to.keep.in.the.file\n"
521 				+ "\n"
522 				+ "[core-section-not-to-remove-in-test]\n"
523 				+ "  packedGitLimit = 14\n", c.toText());
524 	}
525 
526 	@Test
527 	public void testUnsetSingleSection() throws ConfigInvalidException {
528 		Config c = parse("" //
529 				+ "[branch \"keep\"]\n"
530 				+ "  merge = master.branch.to.keep.in.the.file\n"
531 				+ "\n"
532 				+ "[single]\n"
533 				+ "  merge = this.will.get.deleted\n"
534 				+ "  remote = origin-for-some-long-gone-place\n"
535 				+ "\n"
536 				+ "[core-section-not-to-remove-in-test]\n"
537 				+ "  packedGitLimit = 14\n");
538 		c.unsetSection("single", null);
539 		assertEquals("" //
540 				+ "[branch \"keep\"]\n"
541 				+ "  merge = master.branch.to.keep.in.the.file\n"
542 				+ "\n"
543 				+ "[core-section-not-to-remove-in-test]\n"
544 				+ "  packedGitLimit = 14\n", c.toText());
545 	}
546 
547 	@Test
548 	public void test008_readSectionNames() throws ConfigInvalidException {
549 		final Config c = parse("[a]\n [B]\n");
550 		Set<String> sections = c.getSections();
551 		assertTrue("Sections should contain \"a\"", sections.contains("a"));
552 		assertTrue("Sections should contain \"b\"", sections.contains("b"));
553 	}
554 
555 	@Test
556 	public void test009_readNamesInSection() throws ConfigInvalidException {
557 		String configString = "[core]\n" + "repositoryFormatVersion = 0\n"
558 				+ "filemode = false\n" + "logAllRefUpdates = true\n";
559 		final Config c = parse(configString);
560 		Set<String> names = c.getNames("core");
561 		assertEquals("Core section size", 3, names.size());
562 		assertTrue("Core section should contain \"filemode\"", names
563 				.contains("filemode"));
564 
565 		assertTrue("Core section should contain \"repositoryFormatVersion\"",
566 				names.contains("repositoryFormatVersion"));
567 
568 		assertTrue("Core section should contain \"repositoryformatversion\"",
569 				names.contains("repositoryformatversion"));
570 
571 		Iterator<String> itr = names.iterator();
572 		assertEquals("filemode", itr.next());
573 		assertEquals("logAllRefUpdates", itr.next());
574 		assertEquals("repositoryFormatVersion", itr.next());
575 		assertFalse(itr.hasNext());
576 	}
577 
578 	@Test
579 	public void test_ReadNamesInSectionRecursive()
580 			throws ConfigInvalidException {
581 		String baseConfigString = "[core]\n" + "logAllRefUpdates = true\n";
582 		String configString = "[core]\n" + "repositoryFormatVersion = 0\n"
583 				+ "filemode = false\n";
584 		final Config c = parse(configString, parse(baseConfigString));
585 		Set<String> names = c.getNames("core", true);
586 		assertEquals("Core section size", 3, names.size());
587 		assertTrue("Core section should contain \"filemode\"",
588 				names.contains("filemode"));
589 		assertTrue("Core section should contain \"repositoryFormatVersion\"",
590 				names.contains("repositoryFormatVersion"));
591 		assertTrue("Core section should contain \"logAllRefUpdates\"",
592 				names.contains("logAllRefUpdates"));
593 		assertTrue("Core section should contain \"logallrefupdates\"",
594 				names.contains("logallrefupdates"));
595 
596 		Iterator<String> itr = names.iterator();
597 		assertEquals("filemode", itr.next());
598 		assertEquals("repositoryFormatVersion", itr.next());
599 		assertEquals("logAllRefUpdates", itr.next());
600 		assertFalse(itr.hasNext());
601 	}
602 
603 	@Test
604 	public void test010_readNamesInSubSection() throws ConfigInvalidException {
605 		String configString = "[a \"sub1\"]\n"//
606 				+ "x = 0\n" //
607 				+ "y = false\n"//
608 				+ "z = true\n"//
609 				+ "[a \"sub2\"]\n"//
610 				+ "a=0\n"//
611 				+ "b=1\n";
612 		final Config c = parse(configString);
613 		Set<String> names = c.getNames("a", "sub1");
614 		assertEquals("Subsection size", 3, names.size());
615 		assertTrue("Subsection should contain \"x\"", names.contains("x"));
616 		assertTrue("Subsection should contain \"y\"", names.contains("y"));
617 		assertTrue("Subsection should contain \"z\"", names.contains("z"));
618 		names = c.getNames("a", "sub2");
619 		assertEquals("Subsection size", 2, names.size());
620 		assertTrue("Subsection should contain \"a\"", names.contains("a"));
621 		assertTrue("Subsection should contain \"b\"", names.contains("b"));
622 	}
623 
624 	@Test
625 	public void readNamesInSubSectionRecursive() throws ConfigInvalidException {
626 		String baseConfigString = "[a \"sub1\"]\n"//
627 				+ "x = 0\n" //
628 				+ "y = false\n"//
629 				+ "[a \"sub2\"]\n"//
630 				+ "A=0\n";//
631 		String configString = "[a \"sub1\"]\n"//
632 				+ "z = true\n"//
633 				+ "[a \"sub2\"]\n"//
634 				+ "B=1\n";
635 		final Config c = parse(configString, parse(baseConfigString));
636 		Set<String> names = c.getNames("a", "sub1", true);
637 		assertEquals("Subsection size", 3, names.size());
638 		assertTrue("Subsection should contain \"x\"", names.contains("x"));
639 		assertTrue("Subsection should contain \"y\"", names.contains("y"));
640 		assertTrue("Subsection should contain \"z\"", names.contains("z"));
641 		names = c.getNames("a", "sub2", true);
642 		assertEquals("Subsection size", 2, names.size());
643 		assertTrue("Subsection should contain \"A\"", names.contains("A"));
644 		assertTrue("Subsection should contain \"a\"", names.contains("a"));
645 		assertTrue("Subsection should contain \"B\"", names.contains("B"));
646 	}
647 
648 	@Test
649 	public void testQuotingForSubSectionNames() {
650 		String resultPattern = "[testsection \"{0}\"]\n\ttestname = testvalue\n";
651 		String result;
652 
653 		Config config = new Config();
654 		config.setString("testsection", "testsubsection", "testname",
655 				"testvalue");
656 
657 		result = MessageFormat.format(resultPattern, "testsubsection");
658 		assertEquals(result, config.toText());
659 		config.clear();
660 
661 		config.setString("testsection", "#quotable", "testname", "testvalue");
662 		result = MessageFormat.format(resultPattern, "#quotable");
663 		assertEquals(result, config.toText());
664 		config.clear();
665 
666 		config.setString("testsection", "with\"quote", "testname", "testvalue");
667 		result = MessageFormat.format(resultPattern, "with\\\"quote");
668 		assertEquals(result, config.toText());
669 	}
670 
671 	@Test
672 	public void testNoFinalNewline() throws ConfigInvalidException {
673 		Config c = parse("[a]\n"
674 				+ "x = 0\n"
675 				+ "y = 1");
676 		assertEquals("0", c.getString("a", null, "x"));
677 		assertEquals("1", c.getString("a", null, "y"));
678 	}
679 
680 	@Test
681 	public void testExplicitlySetEmptyString() throws Exception {
682 		Config c = new Config();
683 		c.setString("a", null, "x", "0");
684 		c.setString("a", null, "y", "");
685 
686 		assertEquals("0", c.getString("a", null, "x"));
687 		assertEquals(0, c.getInt("a", null, "x", 1));
688 
689 		assertEquals("", c.getString("a", null, "y"));
690 		assertArrayEquals(new String[]{""}, c.getStringList("a", null, "y"));
691 		try {
692 			c.getInt("a", null, "y", 1);
693 		} catch (IllegalArgumentException e) {
694 			assertEquals("Invalid integer value: a.y=", e.getMessage());
695 		}
696 
697 		assertNull(c.getString("a", null, "z"));
698 		assertArrayEquals(new String[]{}, c.getStringList("a", null, "z"));
699 	}
700 
701 	@Test
702 	public void testParsedEmptyString() throws Exception {
703 		Config c = parse("[a]\n"
704 				+ "x = 0\n"
705 				+ "y =\n");
706 
707 		assertEquals("0", c.getString("a", null, "x"));
708 		assertEquals(0, c.getInt("a", null, "x", 1));
709 
710 		assertNull(c.getString("a", null, "y"));
711 		assertArrayEquals(new String[]{null}, c.getStringList("a", null, "y"));
712 		try {
713 			c.getInt("a", null, "y", 1);
714 		} catch (IllegalArgumentException e) {
715 			assertEquals("Invalid integer value: a.y=", e.getMessage());
716 		}
717 
718 		assertNull(c.getString("a", null, "z"));
719 		assertArrayEquals(new String[]{}, c.getStringList("a", null, "z"));
720 	}
721 
722 	@Test
723 	public void testSetStringListWithEmptyValue() throws Exception {
724 		Config c = new Config();
725 		c.setStringList("a", null, "x", Arrays.asList(""));
726 		assertArrayEquals(new String[]{""}, c.getStringList("a", null, "x"));
727 	}
728 
729 	@Test
730 	public void testEmptyValueAtEof() throws Exception {
731 		String text = "[a]\nx =";
732 		Config c = parse(text);
733 		assertNull(c.getString("a", null, "x"));
734 		assertArrayEquals(new String[]{null},
735 				c.getStringList("a", null, "x"));
736 		c = parse(text + "\n");
737 		assertNull(c.getString("a", null, "x"));
738 		assertArrayEquals(new String[]{null},
739 				c.getStringList("a", null, "x"));
740 	}
741 
742 	private static void assertReadLong(long exp) throws ConfigInvalidException {
743 		assertReadLong(exp, String.valueOf(exp));
744 	}
745 
746 	private static void assertReadLong(long exp, String act)
747 			throws ConfigInvalidException {
748 		final Config c = parse("[s]\na = " + act + "\n");
749 		assertEquals(exp, c.getLong("s", null, "a", 0L));
750 	}
751 
752 	private static Config parse(final String content)
753 			throws ConfigInvalidException {
754 		return parse(content, null);
755 	}
756 
757 	private static Config parse(final String content, Config baseConfig)
758 			throws ConfigInvalidException {
759 		final Config c = new Config(baseConfig);
760 		c.fromText(content);
761 		return c;
762 	}
763 }