1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.storage.file;
11
12 import static java.nio.charset.StandardCharsets.UTF_8;
13 import static org.eclipse.jgit.util.FileUtils.pathToString;
14 import static org.junit.Assert.assertArrayEquals;
15 import static org.junit.Assert.assertEquals;
16 import static org.junit.Assert.assertNotNull;
17 import static org.junit.Assert.assertFalse;
18
19 import java.io.ByteArrayOutputStream;
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.nio.charset.StandardCharsets;
23 import java.nio.file.Files;
24 import java.nio.file.Path;
25 import java.nio.file.StandardOpenOption;
26 import java.util.StringTokenizer;
27 import java.util.concurrent.TimeUnit;
28 import java.util.concurrent.atomic.AtomicBoolean;
29
30 import org.eclipse.jgit.errors.ConfigInvalidException;
31 import org.eclipse.jgit.junit.MockSystemReader;
32 import org.eclipse.jgit.util.FS;
33 import org.eclipse.jgit.util.FileUtils;
34 import org.eclipse.jgit.util.IO;
35 import org.eclipse.jgit.util.SystemReader;
36 import org.junit.After;
37 import org.junit.Before;
38 import org.junit.Test;
39
40 public class FileBasedConfigTest {
41
42 private static final String USER = "user";
43
44 private static final String NAME = "name";
45
46 private static final String EMAIL = "email";
47
48 private static final String ALICE = "Alice";
49
50 private static final String BOB = "Bob";
51
52 private static final String ALICE_EMAIL = "alice@home";
53
54 private static final String CONTENT1 = "[" + USER + "]\n\t" + NAME + " = "
55 + ALICE + "\n";
56
57 private static final String CONTENT2 = "[" + USER + "]\n\t" + NAME + " = "
58 + BOB + "\n";
59
60 private static final String CONTENT3 = "[" + USER + "]\n\t" + NAME + " = "
61 + ALICE + "\n" + "[" + USER + "]\n\t" + EMAIL + " = " + ALICE_EMAIL;
62
63 private Path trash;
64
65 private MockSystemReader mockSystemReader;
66
67 @Before
68 public void setUp() throws Exception {
69 mockSystemReader = new MockSystemReader();
70 SystemReader.setInstance(mockSystemReader);
71 trash = Files.createTempDirectory("tmp_");
72 }
73
74 @After
75 public void tearDown() throws Exception {
76 FileUtils.delete(trash.toFile(),
77 FileUtils.RECURSIVE | FileUtils.SKIP_MISSING | FileUtils.RETRY);
78 }
79
80 @Test
81 public void testSystemEncoding() throws IOException, ConfigInvalidException {
82 final Path file = createFile(CONTENT1.getBytes(UTF_8));
83 final FileBasedConfig config = new FileBasedConfig(file.toFile(),
84 FS.DETECTED);
85 config.load();
86 assertEquals(ALICE, config.getString(USER, null, NAME));
87
88 config.setString(USER, null, NAME, BOB);
89 config.save();
90 assertArrayEquals(CONTENT2.getBytes(UTF_8), IO.readFully(file.toFile()));
91 }
92
93 @Test
94 public void testUTF8withoutBOM() throws IOException, ConfigInvalidException {
95 final Path file = createFile(CONTENT1.getBytes(UTF_8));
96 final FileBasedConfig config = new FileBasedConfig(file.toFile(),
97 FS.DETECTED);
98 config.load();
99 assertEquals(ALICE, config.getString(USER, null, NAME));
100
101 config.setString(USER, null, NAME, BOB);
102 config.save();
103 assertArrayEquals(CONTENT2.getBytes(UTF_8), IO.readFully(file.toFile()));
104 }
105
106 @Test
107 public void testUTF8withBOM() throws IOException, ConfigInvalidException {
108 final ByteArrayOutputStream bos1 = new ByteArrayOutputStream();
109 bos1.write(0xEF);
110 bos1.write(0xBB);
111 bos1.write(0xBF);
112 bos1.write(CONTENT1.getBytes(UTF_8));
113
114 final Path file = createFile(bos1.toByteArray());
115 final FileBasedConfig config = new FileBasedConfig(file.toFile(),
116 FS.DETECTED);
117 config.load();
118 assertEquals(ALICE, config.getString(USER, null, NAME));
119
120 config.setString(USER, null, NAME, BOB);
121 config.save();
122
123 final ByteArrayOutputStream bos2 = new ByteArrayOutputStream();
124 bos2.write(0xEF);
125 bos2.write(0xBB);
126 bos2.write(0xBF);
127 bos2.write(CONTENT2.getBytes(UTF_8));
128 assertArrayEquals(bos2.toByteArray(), IO.readFully(file.toFile()));
129 }
130
131 @Test
132 public void testLeadingWhitespaces() throws IOException, ConfigInvalidException {
133 final ByteArrayOutputStream bos1 = new ByteArrayOutputStream();
134 bos1.write(" \n\t".getBytes(UTF_8));
135 bos1.write(CONTENT1.getBytes(UTF_8));
136
137 final Path file = createFile(bos1.toByteArray());
138 final FileBasedConfig config = new FileBasedConfig(file.toFile(),
139 FS.DETECTED);
140 config.load();
141 assertEquals(ALICE, config.getString(USER, null, NAME));
142
143 config.setString(USER, null, NAME, BOB);
144 config.save();
145
146 final ByteArrayOutputStream bos2 = new ByteArrayOutputStream();
147 bos2.write(" \n\t".getBytes(UTF_8));
148 bos2.write(CONTENT2.getBytes(UTF_8));
149 assertArrayEquals(bos2.toByteArray(), IO.readFully(file.toFile()));
150 }
151
152 @Test
153 public void testIncludeAbsolute()
154 throws IOException, ConfigInvalidException {
155 final Path includedFile = createFile(CONTENT1.getBytes(UTF_8));
156 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
157 bos.write("[include]\npath=".getBytes(UTF_8));
158 bos.write(pathToString(includedFile.toFile()).getBytes(UTF_8));
159
160 final Path file = createFile(bos.toByteArray());
161 final FileBasedConfig config = new FileBasedConfig(file.toFile(),
162 FS.DETECTED);
163 config.load();
164 assertEquals(ALICE, config.getString(USER, null, NAME));
165 }
166
167 @Test
168 public void testIncludeRelativeDot()
169 throws IOException, ConfigInvalidException {
170 final Path includedFile = createFile(CONTENT1.getBytes(UTF_8), "dir1");
171 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
172 bos.write("[include]\npath=".getBytes(UTF_8));
173 bos.write(("./" + includedFile.getFileName()).getBytes(UTF_8));
174
175 final Path file = createFile(bos.toByteArray(), "dir1");
176 final FileBasedConfig config = new FileBasedConfig(file.toFile(),
177 FS.DETECTED);
178 config.load();
179 assertEquals(ALICE, config.getString(USER, null, NAME));
180 }
181
182 @Test
183 public void testIncludeRelativeDotDot()
184 throws IOException, ConfigInvalidException {
185 final Path includedFile = createFile(CONTENT1.getBytes(UTF_8), "dir1");
186 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
187 bos.write("[include]\npath=".getBytes(UTF_8));
188 bos.write(("../" + parent(includedFile).getFileName() + "/"
189 + includedFile.getFileName()).getBytes(UTF_8));
190
191 final Path file = createFile(bos.toByteArray(), "dir2");
192 final FileBasedConfig config = new FileBasedConfig(file.toFile(),
193 FS.DETECTED);
194 config.load();
195 assertEquals(ALICE, config.getString(USER, null, NAME));
196 }
197
198 @Test
199 public void testIncludeRelativeDotDotNotFound()
200 throws IOException, ConfigInvalidException {
201 final Path includedFile = createFile(CONTENT1.getBytes(UTF_8));
202 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
203 bos.write("[include]\npath=".getBytes(UTF_8));
204 bos.write(("../" + includedFile.getFileName()).getBytes(UTF_8));
205
206 final Path file = createFile(bos.toByteArray());
207 final FileBasedConfig config = new FileBasedConfig(file.toFile(),
208 FS.DETECTED);
209 config.load();
210 assertEquals(null, config.getString(USER, null, NAME));
211 }
212
213 @Test
214 public void testIncludeWithTilde()
215 throws IOException, ConfigInvalidException {
216 final Path includedFile = createFile(CONTENT1.getBytes(UTF_8), "home");
217 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
218 bos.write("[include]\npath=".getBytes(UTF_8));
219 bos.write(("~/" + includedFile.getFileName()).getBytes(UTF_8));
220
221 final Path file = createFile(bos.toByteArray(), "repo");
222 final FS fs = FS.DETECTED.newInstance();
223 fs.setUserHome(parent(includedFile).toFile());
224
225 final FileBasedConfig config = new FileBasedConfig(file.toFile(), fs);
226 config.load();
227 assertEquals(ALICE, config.getString(USER, null, NAME));
228 }
229
230 @Test
231 public void testIncludeDontInlineIncludedLinesOnSave()
232 throws IOException, ConfigInvalidException {
233
234
235 final Path includedFile = createFile(CONTENT3.getBytes(UTF_8), "dir1");
236
237 final Path file = createFile(new byte[0], "dir2");
238 FileBasedConfig config = new FileBasedConfig(file.toFile(),
239 FS.DETECTED);
240 config.setString("include", null, "path",
241 ("../" + parent(includedFile).getFileName() + "/"
242 + includedFile.getFileName()));
243
244
245 assertEquals(null, config.getString(USER, null, NAME));
246 assertEquals(null, config.getString(USER, null, EMAIL));
247 config.save();
248
249
250 assertEquals(null, config.getString(USER, null, NAME));
251 assertEquals(null, config.getString(USER, null, EMAIL));
252
253 final String expectedText = config.toText();
254 assertEquals(2,
255 new StringTokenizer(expectedText, "\n", false).countTokens());
256
257 config = new FileBasedConfig(file.toFile(), FS.DETECTED);
258 config.load();
259
260 String actualText = config.toText();
261 assertEquals(expectedText, actualText);
262
263 assertEquals(ALICE, config.getString(USER, null, NAME));
264 assertEquals(ALICE_EMAIL, config.getString(USER, null, EMAIL));
265
266 config.save();
267
268 actualText = config.toText();
269 assertEquals(expectedText, actualText);
270
271 assertEquals(ALICE, config.getString(USER, null, NAME));
272 assertEquals(ALICE_EMAIL, config.getString(USER, null, EMAIL));
273 }
274
275 @Test
276 public void testSavedConfigFileShouldNotReadUserGitConfig()
277 throws IOException {
278 AtomicBoolean userConfigTimeRead = new AtomicBoolean(false);
279
280 Path userConfigFile = createFile(
281 CONTENT1.getBytes(StandardCharsets.UTF_8), "home");
282 mockSystemReader.setUserGitConfig(
283 new FileBasedConfig(userConfigFile.toFile(), FS.DETECTED) {
284
285 @Override
286 public long getTimeUnit(String section, String subsection,
287 String name, long defaultValue, TimeUnit wantUnit) {
288 userConfigTimeRead.set(true);
289 return super.getTimeUnit(section, subsection, name,
290 defaultValue, wantUnit);
291 }
292 });
293
294 Path file = createFile(CONTENT2.getBytes(StandardCharsets.UTF_8),
295 "repo");
296 FileBasedConfig fileBasedConfig = new FileBasedConfig(file.toFile(),
297 FS.DETECTED);
298 fileBasedConfig.save();
299
300
301 fileBasedConfig.isOutdated();
302 assertFalse(
303 "User config should not be read when accessing config files "
304 + "for avoiding deadlocks",
305 userConfigTimeRead.get());
306 }
307
308 private Path createFile(byte[] content) throws IOException {
309 return createFile(content, null);
310 }
311
312 private Path createFile(byte[] content, String subdir) throws IOException {
313 Path dir = subdir != null ? trash.resolve(subdir) : trash;
314 Files.createDirectories(dir);
315
316 Path f = Files.createTempFile(dir, getClass().getName(), null);
317 try (OutputStream os = Files.newOutputStream(f,
318 StandardOpenOption.APPEND)) {
319 os.write(content);
320 }
321 return f;
322 }
323
324 private Path parent(Path file) {
325 Path parent = file.getParent();
326 assertNotNull(parent);
327 return parent;
328 }
329 }