You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140
  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. package org.eclipse.jgit.lib;
  49. import static java.util.concurrent.TimeUnit.DAYS;
  50. import static java.util.concurrent.TimeUnit.HOURS;
  51. import static java.util.concurrent.TimeUnit.MILLISECONDS;
  52. import static java.util.concurrent.TimeUnit.MINUTES;
  53. import static java.util.concurrent.TimeUnit.SECONDS;
  54. import static org.eclipse.jgit.util.FileUtils.pathToString;
  55. import static org.junit.Assert.assertArrayEquals;
  56. import static org.junit.Assert.assertEquals;
  57. import static org.junit.Assert.assertFalse;
  58. import static org.junit.Assert.assertNull;
  59. import static org.junit.Assert.assertSame;
  60. import static org.junit.Assert.assertTrue;
  61. import static org.junit.Assert.fail;
  62. import java.io.File;
  63. import java.io.IOException;
  64. import java.nio.file.Files;
  65. import java.text.MessageFormat;
  66. import java.util.Arrays;
  67. import java.util.Iterator;
  68. import java.util.LinkedList;
  69. import java.util.Set;
  70. import java.util.concurrent.TimeUnit;
  71. import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
  72. import org.eclipse.jgit.errors.ConfigInvalidException;
  73. import org.eclipse.jgit.internal.JGitText;
  74. import org.eclipse.jgit.junit.MockSystemReader;
  75. import org.eclipse.jgit.merge.MergeConfig;
  76. import org.eclipse.jgit.storage.file.FileBasedConfig;
  77. import org.eclipse.jgit.util.FS;
  78. import org.eclipse.jgit.util.SystemReader;
  79. import org.junit.After;
  80. import org.junit.Rule;
  81. import org.junit.Test;
  82. import org.junit.rules.ExpectedException;
  83. import org.junit.rules.TemporaryFolder;
  84. /**
  85. * Test reading of git config
  86. */
  87. @SuppressWarnings("boxing")
  88. public class ConfigTest {
  89. // A non-ASCII whitespace character: U+2002 EN QUAD.
  90. private static final char WS = '\u2002';
  91. @Rule
  92. public ExpectedException expectedEx = ExpectedException.none();
  93. @Rule
  94. public TemporaryFolder tmp = new TemporaryFolder();
  95. @After
  96. public void tearDown() {
  97. SystemReader.setInstance(null);
  98. }
  99. @Test
  100. public void test001_ReadBareKey() throws ConfigInvalidException {
  101. final Config c = parse("[foo]\nbar\n");
  102. assertTrue(c.getBoolean("foo", null, "bar", false));
  103. assertEquals("", c.getString("foo", null, "bar"));
  104. }
  105. @Test
  106. public void test002_ReadWithSubsection() throws ConfigInvalidException {
  107. final Config c = parse("[foo \"zip\"]\nbar\n[foo \"zap\"]\nbar=false\nn=3\n");
  108. assertTrue(c.getBoolean("foo", "zip", "bar", false));
  109. assertEquals("", c.getString("foo","zip", "bar"));
  110. assertFalse(c.getBoolean("foo", "zap", "bar", true));
  111. assertEquals("false", c.getString("foo", "zap", "bar"));
  112. assertEquals(3, c.getInt("foo", "zap", "n", 4));
  113. assertEquals(4, c.getInt("foo", "zap","m", 4));
  114. }
  115. @Test
  116. public void test003_PutRemote() {
  117. final Config c = new Config();
  118. c.setString("sec", "ext", "name", "value");
  119. c.setString("sec", "ext", "name2", "value2");
  120. final String expText = "[sec \"ext\"]\n\tname = value\n\tname2 = value2\n";
  121. assertEquals(expText, c.toText());
  122. }
  123. @Test
  124. public void test004_PutGetSimple() {
  125. Config c = new Config();
  126. c.setString("my", null, "somename", "false");
  127. assertEquals("false", c.getString("my", null, "somename"));
  128. assertEquals("[my]\n\tsomename = false\n", c.toText());
  129. }
  130. @Test
  131. public void test005_PutGetStringList() {
  132. Config c = new Config();
  133. final LinkedList<String> values = new LinkedList<>();
  134. values.add("value1");
  135. values.add("value2");
  136. c.setStringList("my", null, "somename", values);
  137. final Object[] expArr = values.toArray();
  138. final String[] actArr = c.getStringList("my", null, "somename");
  139. assertArrayEquals(expArr, actArr);
  140. final String expText = "[my]\n\tsomename = value1\n\tsomename = value2\n";
  141. assertEquals(expText, c.toText());
  142. }
  143. @Test
  144. public void test006_readCaseInsensitive() throws ConfigInvalidException {
  145. final Config c = parse("[Foo]\nBar\n");
  146. assertTrue(c.getBoolean("foo", null, "bar", false));
  147. assertEquals("", c.getString("foo", null, "bar"));
  148. }
  149. @Test
  150. public void test007_readUserConfig() {
  151. final MockSystemReader mockSystemReader = new MockSystemReader();
  152. SystemReader.setInstance(mockSystemReader);
  153. final String hostname = mockSystemReader.getHostname();
  154. final Config userGitConfig = mockSystemReader.openUserConfig(null,
  155. FS.DETECTED);
  156. final Config localConfig = new Config(userGitConfig);
  157. mockSystemReader.clearProperties();
  158. String authorName;
  159. String authorEmail;
  160. // no values defined nowhere
  161. authorName = localConfig.get(UserConfig.KEY).getAuthorName();
  162. authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
  163. assertEquals(Constants.UNKNOWN_USER_DEFAULT, authorName);
  164. assertEquals(Constants.UNKNOWN_USER_DEFAULT + "@" + hostname, authorEmail);
  165. assertTrue(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
  166. assertTrue(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
  167. // the system user name is defined
  168. mockSystemReader.setProperty(Constants.OS_USER_NAME_KEY, "os user name");
  169. localConfig.uncache(UserConfig.KEY);
  170. authorName = localConfig.get(UserConfig.KEY).getAuthorName();
  171. assertEquals("os user name", authorName);
  172. assertTrue(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
  173. if (hostname != null && hostname.length() != 0) {
  174. authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
  175. assertEquals("os user name@" + hostname, authorEmail);
  176. }
  177. assertTrue(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
  178. // the git environment variables are defined
  179. mockSystemReader.setProperty(Constants.GIT_AUTHOR_NAME_KEY, "git author name");
  180. mockSystemReader.setProperty(Constants.GIT_AUTHOR_EMAIL_KEY, "author@email");
  181. localConfig.uncache(UserConfig.KEY);
  182. authorName = localConfig.get(UserConfig.KEY).getAuthorName();
  183. authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
  184. assertEquals("git author name", authorName);
  185. assertEquals("author@email", authorEmail);
  186. assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
  187. assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
  188. // the values are defined in the global configuration
  189. // first clear environment variables since they would override
  190. // configuration files
  191. mockSystemReader.clearProperties();
  192. userGitConfig.setString("user", null, "name", "global username");
  193. userGitConfig.setString("user", null, "email", "author@globalemail");
  194. authorName = localConfig.get(UserConfig.KEY).getAuthorName();
  195. authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
  196. assertEquals("global username", authorName);
  197. assertEquals("author@globalemail", authorEmail);
  198. assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
  199. assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
  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. authorName = localConfig.get(UserConfig.KEY).getCommitterName();
  210. authorEmail = localConfig.get(UserConfig.KEY).getCommitterEmail();
  211. assertEquals("local username", authorName);
  212. assertEquals("author@localemail", authorEmail);
  213. assertFalse(localConfig.get(UserConfig.KEY).isCommitterNameImplicit());
  214. assertFalse(localConfig.get(UserConfig.KEY).isCommitterEmailImplicit());
  215. // also git environment variables are defined
  216. mockSystemReader.setProperty(Constants.GIT_AUTHOR_NAME_KEY,
  217. "git author name");
  218. mockSystemReader.setProperty(Constants.GIT_AUTHOR_EMAIL_KEY,
  219. "author@email");
  220. localConfig.setString("user", null, "name", "local username");
  221. localConfig.setString("user", null, "email", "author@localemail");
  222. authorName = localConfig.get(UserConfig.KEY).getAuthorName();
  223. authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
  224. assertEquals("git author name", authorName);
  225. assertEquals("author@email", authorEmail);
  226. assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
  227. assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
  228. }
  229. @Test
  230. public void testReadUserConfigWithInvalidCharactersStripped() {
  231. final MockSystemReader mockSystemReader = new MockSystemReader();
  232. final Config localConfig = new Config(mockSystemReader.openUserConfig(
  233. null, FS.DETECTED));
  234. localConfig.setString("user", null, "name", "foo<bar");
  235. localConfig.setString("user", null, "email", "baz>\nqux@example.com");
  236. UserConfig userConfig = localConfig.get(UserConfig.KEY);
  237. assertEquals("foobar", userConfig.getAuthorName());
  238. assertEquals("bazqux@example.com", userConfig.getAuthorEmail());
  239. }
  240. @Test
  241. public void testReadBoolean_TrueFalse1() throws ConfigInvalidException {
  242. final Config c = parse("[s]\na = true\nb = false\n");
  243. assertEquals("true", c.getString("s", null, "a"));
  244. assertEquals("false", c.getString("s", null, "b"));
  245. assertTrue(c.getBoolean("s", "a", false));
  246. assertFalse(c.getBoolean("s", "b", true));
  247. }
  248. @Test
  249. public void testReadBoolean_TrueFalse2() throws ConfigInvalidException {
  250. final Config c = parse("[s]\na = TrUe\nb = fAlSe\n");
  251. assertEquals("TrUe", c.getString("s", null, "a"));
  252. assertEquals("fAlSe", c.getString("s", null, "b"));
  253. assertTrue(c.getBoolean("s", "a", false));
  254. assertFalse(c.getBoolean("s", "b", true));
  255. }
  256. @Test
  257. public void testReadBoolean_YesNo1() throws ConfigInvalidException {
  258. final Config c = parse("[s]\na = yes\nb = no\n");
  259. assertEquals("yes", c.getString("s", null, "a"));
  260. assertEquals("no", c.getString("s", null, "b"));
  261. assertTrue(c.getBoolean("s", "a", false));
  262. assertFalse(c.getBoolean("s", "b", true));
  263. }
  264. @Test
  265. public void testReadBoolean_YesNo2() throws ConfigInvalidException {
  266. final Config c = parse("[s]\na = yEs\nb = NO\n");
  267. assertEquals("yEs", c.getString("s", null, "a"));
  268. assertEquals("NO", c.getString("s", null, "b"));
  269. assertTrue(c.getBoolean("s", "a", false));
  270. assertFalse(c.getBoolean("s", "b", true));
  271. }
  272. @Test
  273. public void testReadBoolean_OnOff1() throws ConfigInvalidException {
  274. final Config c = parse("[s]\na = on\nb = off\n");
  275. assertEquals("on", c.getString("s", null, "a"));
  276. assertEquals("off", c.getString("s", null, "b"));
  277. assertTrue(c.getBoolean("s", "a", false));
  278. assertFalse(c.getBoolean("s", "b", true));
  279. }
  280. @Test
  281. public void testReadBoolean_OnOff2() throws ConfigInvalidException {
  282. final Config c = parse("[s]\na = ON\nb = OFF\n");
  283. assertEquals("ON", c.getString("s", null, "a"));
  284. assertEquals("OFF", c.getString("s", null, "b"));
  285. assertTrue(c.getBoolean("s", "a", false));
  286. assertFalse(c.getBoolean("s", "b", true));
  287. }
  288. static enum TestEnum {
  289. ONE_TWO;
  290. }
  291. @Test
  292. public void testGetEnum() throws ConfigInvalidException {
  293. Config c = parse("[s]\na = ON\nb = input\nc = true\nd = off\n");
  294. assertSame(CoreConfig.AutoCRLF.TRUE, c.getEnum("s", null, "a",
  295. CoreConfig.AutoCRLF.FALSE));
  296. assertSame(CoreConfig.AutoCRLF.INPUT, c.getEnum("s", null, "b",
  297. CoreConfig.AutoCRLF.FALSE));
  298. assertSame(CoreConfig.AutoCRLF.TRUE, c.getEnum("s", null, "c",
  299. CoreConfig.AutoCRLF.FALSE));
  300. assertSame(CoreConfig.AutoCRLF.FALSE, c.getEnum("s", null, "d",
  301. CoreConfig.AutoCRLF.TRUE));
  302. c = new Config();
  303. assertSame(CoreConfig.AutoCRLF.FALSE, c.getEnum("s", null, "d",
  304. CoreConfig.AutoCRLF.FALSE));
  305. c = parse("[s \"b\"]\n\tc = one two\n");
  306. assertSame(TestEnum.ONE_TWO, c.getEnum("s", "b", "c", TestEnum.ONE_TWO));
  307. c = parse("[s \"b\"]\n\tc = one-two\n");
  308. assertSame(TestEnum.ONE_TWO, c.getEnum("s", "b", "c", TestEnum.ONE_TWO));
  309. }
  310. @Test
  311. public void testGetInvalidEnum() throws ConfigInvalidException {
  312. Config c = parse("[a]\n\tb = invalid\n");
  313. try {
  314. c.getEnum("a", null, "b", TestEnum.ONE_TWO);
  315. fail();
  316. } catch (IllegalArgumentException e) {
  317. assertEquals("Invalid value: a.b=invalid", e.getMessage());
  318. }
  319. c = parse("[a \"b\"]\n\tc = invalid\n");
  320. try {
  321. c.getEnum("a", "b", "c", TestEnum.ONE_TWO);
  322. fail();
  323. } catch (IllegalArgumentException e) {
  324. assertEquals("Invalid value: a.b.c=invalid", e.getMessage());
  325. }
  326. }
  327. @Test
  328. public void testSetEnum() {
  329. final Config c = new Config();
  330. c.setEnum("s", "b", "c", TestEnum.ONE_TWO);
  331. assertEquals("[s \"b\"]\n\tc = one two\n", c.toText());
  332. }
  333. @Test
  334. public void testGetFastForwardMergeoptions() throws ConfigInvalidException {
  335. Config c = new Config(null); // not set
  336. assertSame(FastForwardMode.FF, c.getEnum(
  337. ConfigConstants.CONFIG_BRANCH_SECTION, "side",
  338. ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.FF));
  339. MergeConfig mergeConfig = c.get(MergeConfig.getParser("side"));
  340. assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
  341. c = parse("[branch \"side\"]\n\tmergeoptions = --ff-only\n");
  342. assertSame(FastForwardMode.FF_ONLY, c.getEnum(
  343. ConfigConstants.CONFIG_BRANCH_SECTION, "side",
  344. ConfigConstants.CONFIG_KEY_MERGEOPTIONS,
  345. FastForwardMode.FF_ONLY));
  346. mergeConfig = c.get(MergeConfig.getParser("side"));
  347. assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
  348. c = parse("[branch \"side\"]\n\tmergeoptions = --ff\n");
  349. assertSame(FastForwardMode.FF, c.getEnum(
  350. ConfigConstants.CONFIG_BRANCH_SECTION, "side",
  351. ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.FF));
  352. mergeConfig = c.get(MergeConfig.getParser("side"));
  353. assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
  354. c = parse("[branch \"side\"]\n\tmergeoptions = --no-ff\n");
  355. assertSame(FastForwardMode.NO_FF, c.getEnum(
  356. ConfigConstants.CONFIG_BRANCH_SECTION, "side",
  357. ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.NO_FF));
  358. mergeConfig = c.get(MergeConfig.getParser("side"));
  359. assertSame(FastForwardMode.NO_FF, mergeConfig.getFastForwardMode());
  360. }
  361. @Test
  362. public void testSetFastForwardMergeoptions() {
  363. final Config c = new Config();
  364. c.setEnum("branch", "side", "mergeoptions", FastForwardMode.FF);
  365. assertEquals("[branch \"side\"]\n\tmergeoptions = --ff\n", c.toText());
  366. c.setEnum("branch", "side", "mergeoptions", FastForwardMode.FF_ONLY);
  367. assertEquals("[branch \"side\"]\n\tmergeoptions = --ff-only\n",
  368. c.toText());
  369. c.setEnum("branch", "side", "mergeoptions", FastForwardMode.NO_FF);
  370. assertEquals("[branch \"side\"]\n\tmergeoptions = --no-ff\n",
  371. c.toText());
  372. }
  373. @Test
  374. public void testGetFastForwardMerge() throws ConfigInvalidException {
  375. Config c = new Config(null); // not set
  376. assertSame(FastForwardMode.Merge.TRUE, c.getEnum(
  377. ConfigConstants.CONFIG_KEY_MERGE, null,
  378. ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.TRUE));
  379. MergeConfig mergeConfig = c.get(MergeConfig.getParser("side"));
  380. assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
  381. c = parse("[merge]\n\tff = only\n");
  382. assertSame(FastForwardMode.Merge.ONLY, c.getEnum(
  383. ConfigConstants.CONFIG_KEY_MERGE, null,
  384. ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.ONLY));
  385. mergeConfig = c.get(MergeConfig.getParser("side"));
  386. assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
  387. c = parse("[merge]\n\tff = true\n");
  388. assertSame(FastForwardMode.Merge.TRUE, c.getEnum(
  389. ConfigConstants.CONFIG_KEY_MERGE, null,
  390. ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.TRUE));
  391. mergeConfig = c.get(MergeConfig.getParser("side"));
  392. assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
  393. c = parse("[merge]\n\tff = false\n");
  394. assertSame(FastForwardMode.Merge.FALSE, c.getEnum(
  395. ConfigConstants.CONFIG_KEY_MERGE, null,
  396. ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.FALSE));
  397. mergeConfig = c.get(MergeConfig.getParser("side"));
  398. assertSame(FastForwardMode.NO_FF, mergeConfig.getFastForwardMode());
  399. }
  400. @Test
  401. public void testCombinedMergeOptions() throws ConfigInvalidException {
  402. Config c = new Config(null); // not set
  403. MergeConfig mergeConfig = c.get(MergeConfig.getParser("side"));
  404. assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
  405. assertTrue(mergeConfig.isCommit());
  406. assertFalse(mergeConfig.isSquash());
  407. // branch..mergeoptions should win over merge.ff
  408. c = parse("[merge]\n\tff = false\n"
  409. + "[branch \"side\"]\n\tmergeoptions = --ff-only\n");
  410. mergeConfig = c.get(MergeConfig.getParser("side"));
  411. assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
  412. assertTrue(mergeConfig.isCommit());
  413. assertFalse(mergeConfig.isSquash());
  414. // merge.ff used for ff setting if not set via mergeoptions
  415. c = parse("[merge]\n\tff = only\n"
  416. + "[branch \"side\"]\n\tmergeoptions = --squash\n");
  417. mergeConfig = c.get(MergeConfig.getParser("side"));
  418. assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
  419. assertTrue(mergeConfig.isCommit());
  420. assertTrue(mergeConfig.isSquash());
  421. // mergeoptions wins if it has ff options amongst other options
  422. c = parse("[merge]\n\tff = false\n"
  423. + "[branch \"side\"]\n\tmergeoptions = --ff-only --no-commit\n");
  424. mergeConfig = c.get(MergeConfig.getParser("side"));
  425. assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
  426. assertFalse(mergeConfig.isCommit());
  427. assertFalse(mergeConfig.isSquash());
  428. }
  429. @Test
  430. public void testSetFastForwardMerge() {
  431. final Config c = new Config();
  432. c.setEnum("merge", null, "ff",
  433. FastForwardMode.Merge.valueOf(FastForwardMode.FF));
  434. assertEquals("[merge]\n\tff = true\n", c.toText());
  435. c.setEnum("merge", null, "ff",
  436. FastForwardMode.Merge.valueOf(FastForwardMode.FF_ONLY));
  437. assertEquals("[merge]\n\tff = only\n", c.toText());
  438. c.setEnum("merge", null, "ff",
  439. FastForwardMode.Merge.valueOf(FastForwardMode.NO_FF));
  440. assertEquals("[merge]\n\tff = false\n", c.toText());
  441. }
  442. @Test
  443. public void testReadLong() throws ConfigInvalidException {
  444. assertReadLong(1L);
  445. assertReadLong(-1L);
  446. assertReadLong(Long.MIN_VALUE);
  447. assertReadLong(Long.MAX_VALUE);
  448. assertReadLong(4L * 1024 * 1024 * 1024, "4g");
  449. assertReadLong(3L * 1024 * 1024, "3 m");
  450. assertReadLong(8L * 1024, "8 k");
  451. try {
  452. assertReadLong(-1, "1.5g");
  453. fail("incorrectly accepted 1.5g");
  454. } catch (IllegalArgumentException e) {
  455. assertEquals("Invalid integer value: s.a=1.5g", e.getMessage());
  456. }
  457. }
  458. @Test
  459. public void testBooleanWithNoValue() throws ConfigInvalidException {
  460. Config c = parse("[my]\n\tempty\n");
  461. assertEquals("", c.getString("my", null, "empty"));
  462. assertEquals(1, c.getStringList("my", null, "empty").length);
  463. assertEquals("", c.getStringList("my", null, "empty")[0]);
  464. assertTrue(c.getBoolean("my", "empty", false));
  465. assertEquals("[my]\n\tempty\n", c.toText());
  466. }
  467. @Test
  468. public void testUnsetBranchSection() throws ConfigInvalidException {
  469. Config c = parse("" //
  470. + "[branch \"keep\"]\n"
  471. + " merge = master.branch.to.keep.in.the.file\n"
  472. + "\n"
  473. + "[branch \"remove\"]\n"
  474. + " merge = this.will.get.deleted\n"
  475. + " remote = origin-for-some-long-gone-place\n"
  476. + "\n"
  477. + "[core-section-not-to-remove-in-test]\n"
  478. + " packedGitLimit = 14\n");
  479. c.unsetSection("branch", "does.not.exist");
  480. c.unsetSection("branch", "remove");
  481. assertEquals("" //
  482. + "[branch \"keep\"]\n"
  483. + " merge = master.branch.to.keep.in.the.file\n"
  484. + "\n"
  485. + "[core-section-not-to-remove-in-test]\n"
  486. + " packedGitLimit = 14\n", c.toText());
  487. }
  488. @Test
  489. public void testUnsetSingleSection() throws ConfigInvalidException {
  490. Config c = parse("" //
  491. + "[branch \"keep\"]\n"
  492. + " merge = master.branch.to.keep.in.the.file\n"
  493. + "\n"
  494. + "[single]\n"
  495. + " merge = this.will.get.deleted\n"
  496. + " remote = origin-for-some-long-gone-place\n"
  497. + "\n"
  498. + "[core-section-not-to-remove-in-test]\n"
  499. + " packedGitLimit = 14\n");
  500. c.unsetSection("single", null);
  501. assertEquals("" //
  502. + "[branch \"keep\"]\n"
  503. + " merge = master.branch.to.keep.in.the.file\n"
  504. + "\n"
  505. + "[core-section-not-to-remove-in-test]\n"
  506. + " packedGitLimit = 14\n", c.toText());
  507. }
  508. @Test
  509. public void test008_readSectionNames() throws ConfigInvalidException {
  510. final Config c = parse("[a]\n [B]\n");
  511. Set<String> sections = c.getSections();
  512. assertTrue("Sections should contain \"a\"", sections.contains("a"));
  513. assertTrue("Sections should contain \"b\"", sections.contains("b"));
  514. }
  515. @Test
  516. public void test009_readNamesInSection() throws ConfigInvalidException {
  517. String configString = "[core]\n" + "repositoryFormatVersion = 0\n"
  518. + "filemode = false\n" + "logAllRefUpdates = true\n";
  519. final Config c = parse(configString);
  520. Set<String> names = c.getNames("core");
  521. assertEquals("Core section size", 3, names.size());
  522. assertTrue("Core section should contain \"filemode\"", names
  523. .contains("filemode"));
  524. assertTrue("Core section should contain \"repositoryFormatVersion\"",
  525. names.contains("repositoryFormatVersion"));
  526. assertTrue("Core section should contain \"repositoryformatversion\"",
  527. names.contains("repositoryformatversion"));
  528. Iterator<String> itr = names.iterator();
  529. assertEquals("filemode", itr.next());
  530. assertEquals("logAllRefUpdates", itr.next());
  531. assertEquals("repositoryFormatVersion", itr.next());
  532. assertFalse(itr.hasNext());
  533. }
  534. @Test
  535. public void test_ReadNamesInSectionRecursive()
  536. throws ConfigInvalidException {
  537. String baseConfigString = "[core]\n" + "logAllRefUpdates = true\n";
  538. String configString = "[core]\n" + "repositoryFormatVersion = 0\n"
  539. + "filemode = false\n";
  540. final Config c = parse(configString, parse(baseConfigString));
  541. Set<String> names = c.getNames("core", true);
  542. assertEquals("Core section size", 3, names.size());
  543. assertTrue("Core section should contain \"filemode\"",
  544. names.contains("filemode"));
  545. assertTrue("Core section should contain \"repositoryFormatVersion\"",
  546. names.contains("repositoryFormatVersion"));
  547. assertTrue("Core section should contain \"logAllRefUpdates\"",
  548. names.contains("logAllRefUpdates"));
  549. assertTrue("Core section should contain \"logallrefupdates\"",
  550. names.contains("logallrefupdates"));
  551. Iterator<String> itr = names.iterator();
  552. assertEquals("filemode", itr.next());
  553. assertEquals("repositoryFormatVersion", itr.next());
  554. assertEquals("logAllRefUpdates", itr.next());
  555. assertFalse(itr.hasNext());
  556. }
  557. @Test
  558. public void test010_readNamesInSubSection() throws ConfigInvalidException {
  559. String configString = "[a \"sub1\"]\n"//
  560. + "x = 0\n" //
  561. + "y = false\n"//
  562. + "z = true\n"//
  563. + "[a \"sub2\"]\n"//
  564. + "a=0\n"//
  565. + "b=1\n";
  566. final Config c = parse(configString);
  567. Set<String> names = c.getNames("a", "sub1");
  568. assertEquals("Subsection size", 3, names.size());
  569. assertTrue("Subsection should contain \"x\"", names.contains("x"));
  570. assertTrue("Subsection should contain \"y\"", names.contains("y"));
  571. assertTrue("Subsection should contain \"z\"", names.contains("z"));
  572. names = c.getNames("a", "sub2");
  573. assertEquals("Subsection size", 2, names.size());
  574. assertTrue("Subsection should contain \"a\"", names.contains("a"));
  575. assertTrue("Subsection should contain \"b\"", names.contains("b"));
  576. }
  577. @Test
  578. public void readNamesInSubSectionRecursive() throws ConfigInvalidException {
  579. String baseConfigString = "[a \"sub1\"]\n"//
  580. + "x = 0\n" //
  581. + "y = false\n"//
  582. + "[a \"sub2\"]\n"//
  583. + "A=0\n";//
  584. String configString = "[a \"sub1\"]\n"//
  585. + "z = true\n"//
  586. + "[a \"sub2\"]\n"//
  587. + "B=1\n";
  588. final Config c = parse(configString, parse(baseConfigString));
  589. Set<String> names = c.getNames("a", "sub1", true);
  590. assertEquals("Subsection size", 3, names.size());
  591. assertTrue("Subsection should contain \"x\"", names.contains("x"));
  592. assertTrue("Subsection should contain \"y\"", names.contains("y"));
  593. assertTrue("Subsection should contain \"z\"", names.contains("z"));
  594. names = c.getNames("a", "sub2", true);
  595. assertEquals("Subsection size", 2, names.size());
  596. assertTrue("Subsection should contain \"A\"", names.contains("A"));
  597. assertTrue("Subsection should contain \"a\"", names.contains("a"));
  598. assertTrue("Subsection should contain \"B\"", names.contains("B"));
  599. }
  600. @Test
  601. public void testNoFinalNewline() throws ConfigInvalidException {
  602. Config c = parse("[a]\n"
  603. + "x = 0\n"
  604. + "y = 1");
  605. assertEquals("0", c.getString("a", null, "x"));
  606. assertEquals("1", c.getString("a", null, "y"));
  607. }
  608. @Test
  609. public void testExplicitlySetEmptyString() throws Exception {
  610. Config c = new Config();
  611. c.setString("a", null, "x", "0");
  612. c.setString("a", null, "y", "");
  613. assertEquals("0", c.getString("a", null, "x"));
  614. assertEquals(0, c.getInt("a", null, "x", 1));
  615. assertEquals("", c.getString("a", null, "y"));
  616. assertArrayEquals(new String[]{""}, c.getStringList("a", null, "y"));
  617. try {
  618. c.getInt("a", null, "y", 1);
  619. } catch (IllegalArgumentException e) {
  620. assertEquals("Invalid integer value: a.y=", e.getMessage());
  621. }
  622. assertNull(c.getString("a", null, "z"));
  623. assertArrayEquals(new String[]{}, c.getStringList("a", null, "z"));
  624. }
  625. @Test
  626. public void testParsedEmptyString() throws Exception {
  627. Config c = parse("[a]\n"
  628. + "x = 0\n"
  629. + "y =\n");
  630. assertEquals("0", c.getString("a", null, "x"));
  631. assertEquals(0, c.getInt("a", null, "x", 1));
  632. assertNull(c.getString("a", null, "y"));
  633. assertArrayEquals(new String[]{null}, c.getStringList("a", null, "y"));
  634. try {
  635. c.getInt("a", null, "y", 1);
  636. } catch (IllegalArgumentException e) {
  637. assertEquals("Invalid integer value: a.y=", e.getMessage());
  638. }
  639. assertNull(c.getString("a", null, "z"));
  640. assertArrayEquals(new String[]{}, c.getStringList("a", null, "z"));
  641. }
  642. @Test
  643. public void testSetStringListWithEmptyValue() throws Exception {
  644. Config c = new Config();
  645. c.setStringList("a", null, "x", Arrays.asList(""));
  646. assertArrayEquals(new String[]{""}, c.getStringList("a", null, "x"));
  647. }
  648. @Test
  649. public void testEmptyValueAtEof() throws Exception {
  650. String text = "[a]\nx =";
  651. Config c = parse(text);
  652. assertNull(c.getString("a", null, "x"));
  653. assertArrayEquals(new String[]{null},
  654. c.getStringList("a", null, "x"));
  655. c = parse(text + "\n");
  656. assertNull(c.getString("a", null, "x"));
  657. assertArrayEquals(new String[]{null},
  658. c.getStringList("a", null, "x"));
  659. }
  660. @Test
  661. public void testReadMultipleValuesForName() throws ConfigInvalidException {
  662. Config c = parse("[foo]\nbar=false\nbar=true\n");
  663. assertTrue(c.getBoolean("foo", "bar", false));
  664. }
  665. @Test
  666. public void testIncludeInvalidName() throws ConfigInvalidException {
  667. expectedEx.expect(ConfigInvalidException.class);
  668. expectedEx.expectMessage(JGitText.get().invalidLineInConfigFile);
  669. parse("[include]\nbar\n");
  670. }
  671. @Test
  672. public void testIncludeNoValue() throws ConfigInvalidException {
  673. expectedEx.expect(ConfigInvalidException.class);
  674. expectedEx.expectMessage(JGitText.get().invalidLineInConfigFile);
  675. parse("[include]\npath\n");
  676. }
  677. @Test
  678. public void testIncludeEmptyValue() throws ConfigInvalidException {
  679. expectedEx.expect(ConfigInvalidException.class);
  680. expectedEx.expectMessage(JGitText.get().invalidLineInConfigFile);
  681. parse("[include]\npath=\n");
  682. }
  683. @Test
  684. public void testIncludeValuePathNotFound() throws ConfigInvalidException {
  685. // we do not expect an exception, included path not found are ignored
  686. String notFound = "/not/found";
  687. Config parsed = parse("[include]\npath=" + notFound + "\n");
  688. assertEquals(1, parsed.getSections().size());
  689. assertEquals(notFound, parsed.getString("include", null, "path"));
  690. }
  691. @Test
  692. public void testIncludeValuePathWithTilde() throws ConfigInvalidException {
  693. // we do not expect an exception, included path not supported are
  694. // ignored
  695. String notSupported = "~/someFile";
  696. Config parsed = parse("[include]\npath=" + notSupported + "\n");
  697. assertEquals(1, parsed.getSections().size());
  698. assertEquals(notSupported, parsed.getString("include", null, "path"));
  699. }
  700. @Test
  701. public void testIncludeValuePathRelative() throws ConfigInvalidException {
  702. // we do not expect an exception, included path not supported are
  703. // ignored
  704. String notSupported = "someRelativeFile";
  705. Config parsed = parse("[include]\npath=" + notSupported + "\n");
  706. assertEquals(1, parsed.getSections().size());
  707. assertEquals(notSupported, parsed.getString("include", null, "path"));
  708. }
  709. @Test
  710. public void testIncludeTooManyRecursions() throws IOException {
  711. File config = tmp.newFile("config");
  712. String include = "[include]\npath=" + pathToString(config) + "\n";
  713. Files.write(config.toPath(), include.getBytes());
  714. FileBasedConfig fbConfig = new FileBasedConfig(null, config,
  715. FS.DETECTED);
  716. try {
  717. fbConfig.load();
  718. fail();
  719. } catch (ConfigInvalidException cie) {
  720. assertEquals(JGitText.get().tooManyIncludeRecursions,
  721. cie.getCause().getMessage());
  722. }
  723. }
  724. @Test
  725. public void testIncludeIsNoop() throws IOException, ConfigInvalidException {
  726. File config = tmp.newFile("config");
  727. String fooBar = "[foo]\nbar=true\n";
  728. Files.write(config.toPath(), fooBar.getBytes());
  729. Config parsed = parse("[include]\npath=" + pathToString(config) + "\n");
  730. assertFalse(parsed.getBoolean("foo", "bar", false));
  731. }
  732. private static void assertReadLong(long exp) throws ConfigInvalidException {
  733. assertReadLong(exp, String.valueOf(exp));
  734. }
  735. private static void assertReadLong(long exp, String act)
  736. throws ConfigInvalidException {
  737. final Config c = parse("[s]\na = " + act + "\n");
  738. assertEquals(exp, c.getLong("s", null, "a", 0L));
  739. }
  740. private static Config parse(final String content)
  741. throws ConfigInvalidException {
  742. return parse(content, null);
  743. }
  744. private static Config parse(final String content, Config baseConfig)
  745. throws ConfigInvalidException {
  746. final Config c = new Config(baseConfig);
  747. c.fromText(content);
  748. return c;
  749. }
  750. @Test
  751. public void testTimeUnit() throws ConfigInvalidException {
  752. assertEquals(0, parseTime("0", MILLISECONDS));
  753. assertEquals(2, parseTime("2ms", MILLISECONDS));
  754. assertEquals(200, parseTime("200 milliseconds", MILLISECONDS));
  755. assertEquals(0, parseTime("0s", SECONDS));
  756. assertEquals(2, parseTime("2s", SECONDS));
  757. assertEquals(231, parseTime("231sec", SECONDS));
  758. assertEquals(1, parseTime("1second", SECONDS));
  759. assertEquals(300, parseTime("300 seconds", SECONDS));
  760. assertEquals(2, parseTime("2m", MINUTES));
  761. assertEquals(2, parseTime("2min", MINUTES));
  762. assertEquals(1, parseTime("1 minute", MINUTES));
  763. assertEquals(10, parseTime("10 minutes", MINUTES));
  764. assertEquals(5, parseTime("5h", HOURS));
  765. assertEquals(5, parseTime("5hr", HOURS));
  766. assertEquals(1, parseTime("1hour", HOURS));
  767. assertEquals(48, parseTime("48hours", HOURS));
  768. assertEquals(5, parseTime("5 h", HOURS));
  769. assertEquals(5, parseTime("5 hr", HOURS));
  770. assertEquals(1, parseTime("1 hour", HOURS));
  771. assertEquals(48, parseTime("48 hours", HOURS));
  772. assertEquals(48, parseTime("48 \t \r hours", HOURS));
  773. assertEquals(4, parseTime("4d", DAYS));
  774. assertEquals(1, parseTime("1day", DAYS));
  775. assertEquals(14, parseTime("14days", DAYS));
  776. assertEquals(7, parseTime("1w", DAYS));
  777. assertEquals(7, parseTime("1week", DAYS));
  778. assertEquals(14, parseTime("2w", DAYS));
  779. assertEquals(14, parseTime("2weeks", DAYS));
  780. assertEquals(30, parseTime("1mon", DAYS));
  781. assertEquals(30, parseTime("1month", DAYS));
  782. assertEquals(60, parseTime("2mon", DAYS));
  783. assertEquals(60, parseTime("2months", DAYS));
  784. assertEquals(365, parseTime("1y", DAYS));
  785. assertEquals(365, parseTime("1year", DAYS));
  786. assertEquals(365 * 2, parseTime("2years", DAYS));
  787. }
  788. private long parseTime(String value, TimeUnit unit)
  789. throws ConfigInvalidException {
  790. Config c = parse("[a]\na=" + value + "\n");
  791. return c.getTimeUnit("a", null, "a", 0, unit);
  792. }
  793. @Test
  794. public void testTimeUnitDefaultValue() throws ConfigInvalidException {
  795. // value not present
  796. assertEquals(20, parse("[a]\na=0\n").getTimeUnit("a", null, "b", 20,
  797. MILLISECONDS));
  798. // value is empty
  799. assertEquals(20, parse("[a]\na=\" \"\n").getTimeUnit("a", null, "a", 20,
  800. MILLISECONDS));
  801. // value is not numeric
  802. assertEquals(20, parse("[a]\na=test\n").getTimeUnit("a", null, "a", 20,
  803. MILLISECONDS));
  804. }
  805. @Test
  806. public void testTimeUnitInvalid() throws ConfigInvalidException {
  807. expectedEx.expect(IllegalArgumentException.class);
  808. expectedEx
  809. .expectMessage("Invalid time unit value: a.a=1 monttthhh");
  810. parseTime("1 monttthhh", DAYS);
  811. }
  812. @Test
  813. public void testTimeUnitInvalidWithSection() throws ConfigInvalidException {
  814. Config c = parse("[a \"b\"]\na=1 monttthhh\n");
  815. expectedEx.expect(IllegalArgumentException.class);
  816. expectedEx.expectMessage("Invalid time unit value: a.b.a=1 monttthhh");
  817. c.getTimeUnit("a", "b", "a", 0, DAYS);
  818. }
  819. @Test
  820. public void testTimeUnitNegative() throws ConfigInvalidException {
  821. expectedEx.expect(IllegalArgumentException.class);
  822. parseTime("-1", MILLISECONDS);
  823. }
  824. @Test
  825. public void testEscapeSpacesOnly() throws ConfigInvalidException {
  826. // Empty string is read back as null, so this doesn't round-trip.
  827. assertEquals("", Config.escapeValue(""));
  828. assertValueRoundTrip(" ", "\" \"");
  829. assertValueRoundTrip(" ", "\" \"");
  830. }
  831. @Test
  832. public void testEscapeLeadingSpace() throws ConfigInvalidException {
  833. assertValueRoundTrip("x", "x");
  834. assertValueRoundTrip(" x", "\" x\"");
  835. assertValueRoundTrip(" x", "\" x\"");
  836. }
  837. @Test
  838. public void testEscapeTrailingSpace() throws ConfigInvalidException {
  839. assertValueRoundTrip("x", "x");
  840. assertValueRoundTrip("x ","\"x \"");
  841. assertValueRoundTrip("x ","\"x \"");
  842. }
  843. @Test
  844. public void testEscapeLeadingAndTrailingSpace()
  845. throws ConfigInvalidException {
  846. assertValueRoundTrip(" x ", "\" x \"");
  847. assertValueRoundTrip(" x ", "\" x \"");
  848. assertValueRoundTrip(" x ", "\" x \"");
  849. assertValueRoundTrip(" x ", "\" x \"");
  850. }
  851. @Test
  852. public void testNoEscapeInternalSpaces() throws ConfigInvalidException {
  853. assertValueRoundTrip("x y");
  854. assertValueRoundTrip("x y");
  855. assertValueRoundTrip("x y");
  856. assertValueRoundTrip("x y z");
  857. assertValueRoundTrip("x " + WS + " y");
  858. }
  859. @Test
  860. public void testNoEscapeSpecialCharacters() throws ConfigInvalidException {
  861. assertValueRoundTrip("x\\y", "x\\\\y");
  862. assertValueRoundTrip("x\"y", "x\\\"y");
  863. assertValueRoundTrip("x\ny", "x\\ny");
  864. assertValueRoundTrip("x\ty", "x\\ty");
  865. assertValueRoundTrip("x\by", "x\\by");
  866. }
  867. @Test
  868. public void testParseLiteralBackspace() throws ConfigInvalidException {
  869. // This is round-tripped with an escape sequence by JGit, but C git writes
  870. // it out as a literal backslash.
  871. assertEquals("x\by", parseEscapedValue("x\by"));
  872. }
  873. @Test
  874. public void testEscapeCommentCharacters() throws ConfigInvalidException {
  875. assertValueRoundTrip("x#y", "\"x#y\"");
  876. assertValueRoundTrip("x;y", "\"x;y\"");
  877. }
  878. @Test
  879. public void testEscapeValueInvalidCharacters() {
  880. assertIllegalArgumentException(() -> Config.escapeSubsection("x\0y"));
  881. }
  882. @Test
  883. public void testEscapeSubsectionInvalidCharacters() {
  884. assertIllegalArgumentException(() -> Config.escapeSubsection("x\ny"));
  885. assertIllegalArgumentException(() -> Config.escapeSubsection("x\0y"));
  886. }
  887. @Test
  888. public void testParseMultipleQuotedRegions() throws ConfigInvalidException {
  889. assertEquals("b a z; \n", parseEscapedValue("b\" a\"\" z; \\n\""));
  890. }
  891. @Test
  892. public void testParseComments() throws ConfigInvalidException {
  893. assertEquals("baz", parseEscapedValue("baz; comment"));
  894. assertEquals("baz", parseEscapedValue("baz# comment"));
  895. assertEquals("baz", parseEscapedValue("baz ; comment"));
  896. assertEquals("baz", parseEscapedValue("baz # comment"));
  897. assertEquals("baz", parseEscapedValue("baz ; comment"));
  898. assertEquals("baz", parseEscapedValue("baz # comment"));
  899. assertEquals("baz", parseEscapedValue("baz " + WS + " ; comment"));
  900. assertEquals("baz", parseEscapedValue("baz " + WS + " # comment"));
  901. assertEquals("baz ", parseEscapedValue("\"baz \"; comment"));
  902. assertEquals("baz ", parseEscapedValue("\"baz \"# comment"));
  903. assertEquals("baz ", parseEscapedValue("\"baz \" ; comment"));
  904. assertEquals("baz ", parseEscapedValue("\"baz \" # comment"));
  905. }
  906. @Test
  907. public void testEscapeSubsection() throws ConfigInvalidException {
  908. assertSubsectionRoundTrip("", "\"\"");
  909. assertSubsectionRoundTrip("x", "\"x\"");
  910. assertSubsectionRoundTrip(" x", "\" x\"");
  911. assertSubsectionRoundTrip("x ", "\"x \"");
  912. assertSubsectionRoundTrip(" x ", "\" x \"");
  913. assertSubsectionRoundTrip("x y", "\"x y\"");
  914. assertSubsectionRoundTrip("x y", "\"x y\"");
  915. assertSubsectionRoundTrip("x\\y", "\"x\\\\y\"");
  916. assertSubsectionRoundTrip("x\"y", "\"x\\\"y\"");
  917. // Unlike for values, \b and \t are not escaped.
  918. assertSubsectionRoundTrip("x\by", "\"x\by\"");
  919. assertSubsectionRoundTrip("x\ty", "\"x\ty\"");
  920. }
  921. @Test
  922. public void testParseInvalidValues() {
  923. assertInvalidValue(JGitText.get().newlineInQuotesNotAllowed, "x\"\n\"y");
  924. assertInvalidValue(JGitText.get().endOfFileInEscape, "x\\");
  925. assertInvalidValue(
  926. MessageFormat.format(JGitText.get().badEscape, 'q'), "x\\q");
  927. }
  928. @Test
  929. public void testParseInvalidSubsections() {
  930. assertInvalidSubsection(
  931. JGitText.get().newlineInQuotesNotAllowed, "\"x\ny\"");
  932. }
  933. @Test
  934. public void testDropBackslashFromInvalidEscapeSequenceInSubsectionName()
  935. throws ConfigInvalidException {
  936. assertEquals("x0", parseEscapedSubsection("\"x\\0\""));
  937. assertEquals("xq", parseEscapedSubsection("\"x\\q\""));
  938. // Unlike for values, \b, \n, and \t are not valid escape sequences.
  939. assertEquals("xb", parseEscapedSubsection("\"x\\b\""));
  940. assertEquals("xn", parseEscapedSubsection("\"x\\n\""));
  941. assertEquals("xt", parseEscapedSubsection("\"x\\t\""));
  942. }
  943. private static void assertValueRoundTrip(String value)
  944. throws ConfigInvalidException {
  945. assertValueRoundTrip(value, value);
  946. }
  947. private static void assertValueRoundTrip(String value, String expectedEscaped)
  948. throws ConfigInvalidException {
  949. String escaped = Config.escapeValue(value);
  950. assertEquals("escape failed;", expectedEscaped, escaped);
  951. assertEquals("parse failed;", value, parseEscapedValue(escaped));
  952. }
  953. private static String parseEscapedValue(String escapedValue)
  954. throws ConfigInvalidException {
  955. String text = "[foo]\nbar=" + escapedValue;
  956. Config c = parse(text);
  957. return c.getString("foo", null, "bar");
  958. }
  959. private static void assertInvalidValue(String expectedMessage,
  960. String escapedValue) {
  961. try {
  962. parseEscapedValue(escapedValue);
  963. fail("expected ConfigInvalidException");
  964. } catch (ConfigInvalidException e) {
  965. assertEquals(expectedMessage, e.getMessage());
  966. }
  967. }
  968. private static void assertSubsectionRoundTrip(String subsection,
  969. String expectedEscaped) throws ConfigInvalidException {
  970. String escaped = Config.escapeSubsection(subsection);
  971. assertEquals("escape failed;", expectedEscaped, escaped);
  972. assertEquals("parse failed;", subsection, parseEscapedSubsection(escaped));
  973. }
  974. private static String parseEscapedSubsection(String escapedSubsection)
  975. throws ConfigInvalidException {
  976. String text = "[foo " + escapedSubsection + "]\nbar = value";
  977. Config c = parse(text);
  978. Set<String> subsections = c.getSubsections("foo");
  979. assertEquals("only one section", 1, subsections.size());
  980. return subsections.iterator().next();
  981. }
  982. private static void assertIllegalArgumentException(Runnable r) {
  983. try {
  984. r.run();
  985. fail("expected IllegalArgumentException");
  986. } catch (IllegalArgumentException e) {
  987. // Expected.
  988. }
  989. }
  990. private static void assertInvalidSubsection(String expectedMessage,
  991. String escapedSubsection) {
  992. try {
  993. parseEscapedSubsection(escapedSubsection);
  994. fail("expected ConfigInvalidException");
  995. } catch (ConfigInvalidException e) {
  996. assertEquals(expectedMessage, e.getMessage());
  997. }
  998. }
  999. }