View Javadoc
1   /*
2    * Copyright (C) 2009, Google Inc. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.lib;
12  
13  import static org.hamcrest.CoreMatchers.hasItem;
14  import static org.hamcrest.MatcherAssert.assertThat;
15  import static org.junit.Assert.assertEquals;
16  import static org.junit.Assert.assertFalse;
17  import static org.junit.Assert.assertNotNull;
18  import static org.junit.Assert.assertNotSame;
19  import static org.junit.Assert.assertSame;
20  import static org.junit.Assert.assertTrue;
21  import static org.junit.Assert.fail;
22  
23  import java.io.File;
24  import java.io.IOException;
25  
26  import org.eclipse.jgit.errors.RepositoryNotFoundException;
27  import org.eclipse.jgit.junit.RepositoryTestCase;
28  import org.eclipse.jgit.lib.RepositoryCache.FileKey;
29  import org.junit.Test;
30  
31  public class RepositoryCacheTest extends RepositoryTestCase {
32  	@Test
33  	public void testNonBareFileKey() throws IOException {
34  		File gitdir = db.getDirectory();
35  		File parent = gitdir.getParentFile();
36  		File other = new File(parent, "notagit");
37  		assertEqualsFile(gitdir, FileKey.exact(gitdir, db.getFS()).getFile());
38  		assertEqualsFile(parent, FileKey.exact(parent, db.getFS()).getFile());
39  		assertEqualsFile(other, FileKey.exact(other, db.getFS()).getFile());
40  
41  		assertEqualsFile(gitdir, FileKey.lenient(gitdir, db.getFS()).getFile());
42  		assertEqualsFile(gitdir, FileKey.lenient(parent, db.getFS()).getFile());
43  		assertEqualsFile(other, FileKey.lenient(other, db.getFS()).getFile());
44  	}
45  
46  	@Test
47  	public void testBareFileKey() throws IOException {
48  		Repository bare = createBareRepository();
49  		File gitdir = bare.getDirectory();
50  		File parent = gitdir.getParentFile();
51  		String name = gitdir.getName();
52  		assertTrue(name.endsWith(".git"));
53  		name = name.substring(0, name.length() - 4);
54  
55  		assertEqualsFile(gitdir, FileKey.exact(gitdir, db.getFS()).getFile());
56  
57  		assertEqualsFile(gitdir, FileKey.lenient(gitdir, db.getFS()).getFile());
58  		assertEqualsFile(gitdir,
59  				FileKey.lenient(new File(parent, name), db.getFS()).getFile());
60  	}
61  
62  	@Test
63  	public void testFileKeyOpenExisting() throws IOException {
64  		try (Repository r = new FileKey(db.getDirectory(), db.getFS())
65  				.open(true)) {
66  			assertNotNull(r);
67  			assertEqualsFile(db.getDirectory(), r.getDirectory());
68  		}
69  
70  		try (Repository r = new FileKey(db.getDirectory(), db.getFS())
71  				.open(false)) {
72  			assertNotNull(r);
73  			assertEqualsFile(db.getDirectory(), r.getDirectory());
74  		}
75  	}
76  
77  	@Test
78  	public void testFileKeyOpenNew() throws IOException {
79  		File gitdir;
80  		try (Repository n = createRepository(true)) {
81  			gitdir = n.getDirectory();
82  		}
83  		recursiveDelete(gitdir);
84  		assertFalse(gitdir.exists());
85  
86  		try {
87  			new FileKey(gitdir, db.getFS()).open(true);
88  			fail("incorrectly opened a non existant repository");
89  		} catch (RepositoryNotFoundException e) {
90  			assertEquals("repository not found: " + gitdir.getCanonicalPath(),
91  					e.getMessage());
92  		}
93  
94  		final Repository o = new FileKey(gitdir, db.getFS()).open(false);
95  		assertNotNull(o);
96  		assertEqualsFile(gitdir, o.getDirectory());
97  		assertFalse(gitdir.exists());
98  	}
99  
100 	@Test
101 	public void testCacheRegisterOpen() throws Exception {
102 		final File dir = db.getDirectory();
103 		RepositoryCache.register(db);
104 		assertSame(db, RepositoryCache.open(FileKey.exact(dir, db.getFS())));
105 
106 		assertEquals(".git", dir.getName());
107 		final File parent = dir.getParentFile();
108 		assertSame(db, RepositoryCache.open(FileKey.lenient(parent, db.getFS())));
109 	}
110 
111 	@Test
112 	public void testCacheOpen() throws Exception {
113 		final FileKey loc = FileKey.exact(db.getDirectory(), db.getFS());
114 		@SuppressWarnings("resource") // We are testing the close() method
115 		final Repository d2 = RepositoryCache.open(loc);
116 		assertNotSame(db, d2);
117 		assertSame(d2, RepositoryCache.open(FileKey.exact(loc.getFile(), db.getFS())));
118 		d2.close();
119 		d2.close();
120 	}
121 
122 	@Test
123 	public void testGetRegisteredWhenEmpty() {
124 		assertEquals(0, RepositoryCache.getRegisteredKeys().size());
125 	}
126 
127 	@Test
128 	public void testGetRegistered() {
129 		RepositoryCache.register(db);
130 
131 		assertThat(RepositoryCache.getRegisteredKeys(),
132 				hasItem(FileKey.exact(db.getDirectory(), db.getFS())));
133 		assertEquals(1, RepositoryCache.getRegisteredKeys().size());
134 	}
135 
136 	@Test
137 	public void testUnregister() {
138 		RepositoryCache.register(db);
139 		RepositoryCache
140 				.unregister(FileKey.exact(db.getDirectory(), db.getFS()));
141 
142 		assertEquals(0, RepositoryCache.getRegisteredKeys().size());
143 	}
144 
145 	@Test
146 	public void testRepositoryUsageCount() throws Exception {
147 		FileKey loc = FileKey.exact(db.getDirectory(), db.getFS());
148 		@SuppressWarnings("resource") // We are testing the close() method
149 		Repository d2 = RepositoryCache.open(loc);
150 		assertEquals(1, d2.useCnt.get());
151 		RepositoryCache.open(FileKey.exact(loc.getFile(), db.getFS()));
152 		assertEquals(2, d2.useCnt.get());
153 		d2.close();
154 		assertEquals(1, d2.useCnt.get());
155 		d2.close();
156 		assertEquals(0, d2.useCnt.get());
157 	}
158 
159 	@Test
160 	public void testRepositoryUsageCountWithRegisteredRepository()
161 			throws IOException {
162 		@SuppressWarnings({"resource", "deprecation"}) // We are testing the close() method
163 		Repository repo = createRepository(false, false);
164 		assertEquals(1, repo.useCnt.get());
165 		RepositoryCache.register(repo);
166 		assertEquals(1, repo.useCnt.get());
167 		repo.close();
168 		assertEquals(0, repo.useCnt.get());
169 	}
170 
171 	@Test
172 	public void testRepositoryNotUnregisteringWhenClosing() throws Exception {
173 		FileKey loc = FileKey.exact(db.getDirectory(), db.getFS());
174 		@SuppressWarnings("resource") // We are testing the close() method
175 		Repository d2 = RepositoryCache.open(loc);
176 		assertEquals(1, d2.useCnt.get());
177 		assertThat(RepositoryCache.getRegisteredKeys(),
178 				hasItem(FileKey.exact(db.getDirectory(), db.getFS())));
179 		assertEquals(1, RepositoryCache.getRegisteredKeys().size());
180 		d2.close();
181 		assertEquals(0, d2.useCnt.get());
182 		assertEquals(1, RepositoryCache.getRegisteredKeys().size());
183 		assertTrue(RepositoryCache.isCached(d2));
184 	}
185 
186 	@Test
187 	public void testRepositoryUnregisteringWhenExpiredAndUsageCountNegative()
188 			throws Exception {
189 		@SuppressWarnings("resource") // We are testing the close() method
190 		Repository repoA = createBareRepository();
191 		RepositoryCache.register(repoA);
192 
193 		assertEquals(1, RepositoryCache.getRegisteredKeys().size());
194 		assertTrue(RepositoryCache.isCached(repoA));
195 
196 		// close the repo twice to make usage count negative
197 		repoA.close();
198 		repoA.close();
199 		// fake that repoA was closed more than 1 hour ago (default expiration
200 		// time)
201 		repoA.closedAt.set(System.currentTimeMillis() - 65 * 60 * 1000);
202 
203 		RepositoryCache.clearExpired();
204 
205 		assertEquals(0, RepositoryCache.getRegisteredKeys().size());
206 	}
207 
208 	@Test
209 	public void testRepositoryUnregisteringWhenExpired() throws Exception {
210 		@SuppressWarnings({"resource", "deprecation"}) // We are testing the close() method
211 		Repository repoA = createRepository(true, false);
212 		@SuppressWarnings({"resource", "deprecation"}) // We are testing the close() method
213 		Repository repoB = createRepository(true, false);
214 		Repository repoC = createBareRepository();
215 		RepositoryCache.register(repoA);
216 		RepositoryCache.register(repoB);
217 		RepositoryCache.register(repoC);
218 
219 		assertEquals(3, RepositoryCache.getRegisteredKeys().size());
220 		assertTrue(RepositoryCache.isCached(repoA));
221 		assertTrue(RepositoryCache.isCached(repoB));
222 		assertTrue(RepositoryCache.isCached(repoC));
223 
224 		// fake that repoA was closed more than 1 hour ago (default expiration
225 		// time)
226 		repoA.close();
227 		repoA.closedAt.set(System.currentTimeMillis() - 65 * 60 * 1000);
228 		// close repoB but this one will not be expired
229 		repoB.close();
230 
231 		assertEquals(3, RepositoryCache.getRegisteredKeys().size());
232 		assertTrue(RepositoryCache.isCached(repoA));
233 		assertTrue(RepositoryCache.isCached(repoB));
234 		assertTrue(RepositoryCache.isCached(repoC));
235 
236 		RepositoryCache.clearExpired();
237 
238 		assertEquals(2, RepositoryCache.getRegisteredKeys().size());
239 		assertFalse(RepositoryCache.isCached(repoA));
240 		assertTrue(RepositoryCache.isCached(repoB));
241 		assertTrue(RepositoryCache.isCached(repoC));
242 	}
243 
244 	@Test
245 	public void testReconfigure() throws InterruptedException, IOException {
246 		@SuppressWarnings({"resource", "deprecation"}) // We are testing the close() method
247 		Repository repo = createRepository(false, false);
248 		RepositoryCache.register(repo);
249 		assertTrue(RepositoryCache.isCached(repo));
250 		repo.close();
251 		assertTrue(RepositoryCache.isCached(repo));
252 
253 		// Actually, we would only need to validate that
254 		// WorkQueue.getExecutor().scheduleWithFixedDelay is called with proper
255 		// values but since we do not have a mock library, we test
256 		// reconfiguration from a black box perspective. I.e. reconfigure
257 		// expireAfter and cleanupDelay to 1 ms and wait until the Repository
258 		// is evicted to prove that reconfiguration worked.
259 		RepositoryCacheConfig config = new RepositoryCacheConfig();
260 		config.setExpireAfter(1);
261 		config.setCleanupDelay(1);
262 		config.install();
263 
264 		// Instead of using a fixed waiting time, start with small and increase:
265 		// sleep 1, 2, 4, 8, 16, ..., 1024 ms
266 		// This wait will time out after 2048 ms
267 		for (int i = 0; i <= 10; i++) {
268 			Thread.sleep(1 << i);
269 			if (!RepositoryCache.isCached(repo)) {
270 				return;
271 			}
272 		}
273 		fail("Repository should have been evicted from cache");
274 	}
275 }