PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/test/java/com/aragost/javahg/internals/ServerTest.java

https://bitbucket.org/hansfbaier/javahg-some-additions
Java | 352 lines | 240 code | 36 blank | 76 comment | 6 complexity | 95a33e2ca51cddcfaa00c5204f1e69e0 MD5 | raw file
  1. /*
  2. * #%L
  3. * JavaHg
  4. * %%
  5. * Copyright (C) 2011 aragost Trifork ag
  6. * %%
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to deal
  9. * in the Software without restriction, including without limitation the rights
  10. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. * copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. * THE SOFTWARE.
  24. * #L%
  25. */
  26. package com.aragost.javahg.internals;
  27. import java.io.File;
  28. import java.io.IOException;
  29. import java.io.InputStream;
  30. import java.lang.reflect.Field;
  31. import java.util.Collections;
  32. import java.util.List;
  33. import org.junit.Assert;
  34. import org.junit.Ignore;
  35. import org.junit.Test;
  36. import com.aragost.javahg.BaseRepository;
  37. import com.aragost.javahg.Bundle;
  38. import com.aragost.javahg.Changeset;
  39. import com.aragost.javahg.Repository;
  40. import com.aragost.javahg.RepositoryConfiguration;
  41. import com.aragost.javahg.commands.AddCommand;
  42. import com.aragost.javahg.commands.CommitCommand;
  43. import com.aragost.javahg.commands.ExecutionException;
  44. import com.aragost.javahg.commands.IncomingCommand;
  45. import com.aragost.javahg.commands.LogCommand;
  46. import com.aragost.javahg.commands.VersionCommand;
  47. import com.aragost.javahg.test.AbstractTestCase;
  48. import com.aragost.javahg.test.JavaHgTestExtension;
  49. import com.google.common.base.Strings;
  50. import com.google.common.collect.Lists;
  51. import com.google.common.io.Files;
  52. public class ServerTest extends AbstractTestCase {
  53. private List<String> empty = Collections.emptyList();
  54. @Test
  55. public void testStartStop() throws IOException {
  56. File dir = Files.createTempDir();
  57. Server server = new Server(RepositoryConfiguration.DEFAULT.getHgBin(),
  58. RepositoryConfiguration.DEFAULT.getEncoding());
  59. server.initMecurialRepository(dir);
  60. server.start(dir, null, empty, null, null);
  61. server.stop();
  62. deleteTempDir(dir);
  63. }
  64. @Test
  65. public void testStopWhileProducingOutput() throws IOException {
  66. Repository repo = getTestRepository();
  67. Server server = getFirstServer(repo);
  68. InputStream stdout = server.runCommand(Lists.newArrayList("version"), VersionCommand.on(repo));
  69. // Nothing is yet read from stdout, stop the server. You
  70. // should then get
  71. // an IOException reading from stdout.
  72. server.stop();
  73. try {
  74. stdout.read();
  75. Assert.fail("IOException expected");
  76. } catch (IOException e) {
  77. // success
  78. }
  79. server.start(repo.getDirectory(), null, empty, null, null);
  80. VersionCommand.on(repo).execute();
  81. }
  82. @Test
  83. public void testLock() throws IOException, InterruptedException {
  84. Repository repo = getTestRepository();
  85. TestableCommand command = new TestableCommand(repo, "version");
  86. // The command must produce output so that it wont finish
  87. // before we empty the stdout. That way we can keep two
  88. // commands running at the same time and trigger the
  89. // IllegalStateException below .
  90. InputStream stdout = command.executeToStream();
  91. try {
  92. Server server = getFirstServer(repo);
  93. server.runCommand(Lists.newArrayList("version"), command);
  94. Assert.fail("Exception expected");
  95. } catch (IllegalStateException e) {
  96. Utils.consumeAll(stdout);
  97. }
  98. command.executeToStream();
  99. }
  100. @Test
  101. public void testServerRefCount() throws IOException {
  102. BaseRepository repo = getTestRepository();
  103. BaseRepository repo2 = getTestRepository2();
  104. writeFile("a");
  105. commit();
  106. Bundle bundle = IncomingCommand.on(repo2).execute(repo);
  107. Repository repo3 = bundle.getOverlayRepository();
  108. Assert.assertSame(repo2.getServerPool(), repo3.getServerPool());
  109. ServerPool pool = repo2.getServerPool();
  110. Assert.assertEquals(1, pool.getServers().size());
  111. Assert.assertNotNull(pool.getServers().get(0).getProcess());
  112. repo2.close();
  113. Assert.assertNotNull(pool.getServers().get(0).getProcess());
  114. Server server = pool.getServers().get(0);
  115. bundle.close();
  116. Assert.assertNull(server.getProcess());
  117. Assert.assertTrue(pool.getServers().isEmpty());
  118. }
  119. @Test
  120. public void testConfigChanges() throws IOException {
  121. BaseRepository repo = getTestRepository();
  122. GenericCommand cmd = new GenericCommand(repo, "version") {
  123. {
  124. {
  125. cmdAppend("--config", "ui.username=xxx");
  126. }
  127. }
  128. };
  129. cmd.execute();
  130. writeFile("A");
  131. repo.workingCopy().add("A");
  132. try {
  133. // The previous config change is not forgotten
  134. CommitCommand commit = CommitCommand.on(repo).message("m");
  135. Changeset cs = commit.execute();
  136. assertFailedExecution(commit, "Username is " + cs.getUser());
  137. } catch (ExecutionException e) {
  138. Assert.assertTrue(e.getMessage().startsWith("no username supplied "));
  139. }
  140. }
  141. @Test
  142. public void testKillServerProcess() throws IOException {
  143. File dir = Files.createTempDir();
  144. RepositoryConfiguration repoConfig = new RepositoryConfiguration();
  145. repoConfig.addExtension(JavaHgTestExtension.class);
  146. Repository repo = Repository.create(repoConfig, dir);
  147. Process process = getFirstServer(repo).getProcess();
  148. process.destroy();
  149. // Process is dead and we can't send command
  150. try {
  151. VersionCommand cmd = VersionCommand.on(repo);
  152. cmd.execute();
  153. assertFailedExecution(cmd);
  154. } catch (UnexpectedServerTerminationException e) {
  155. // success
  156. }
  157. repo.close();
  158. repo = Repository.open(repoConfig, dir);
  159. String longStringThatDoesntFitInBuffers = Strings.repeat("x", 10 * 1000 * 1000);
  160. GenericCommand cmd = new GenericCommand(repo, "javahg-write");
  161. HgInputStream stream = cmd.launchStream("o", longStringThatDoesntFitInBuffers);
  162. boolean killed = killProcess(process);
  163. if (killed) {
  164. // Command is now sent, but we can't read output
  165. try {
  166. // String s =
  167. Utils.readStream(stream, repo.getServerPool().newDecoder());
  168. // TODO This doesn't work on Linux, why?
  169. // Assert.fail("Exception expected. Read " +
  170. // s.length() + " bytes");
  171. } catch (UnexpectedServerTerminationException e) {
  172. System.err.println("Exit value in testKillServerProcess: " + e.getExitValue());
  173. // success
  174. }
  175. }
  176. repo.close();
  177. deleteTempDir(dir);
  178. }
  179. @Test
  180. @Ignore
  181. public void testStderrDuringStartup() throws IOException {
  182. RepositoryConfiguration conf = new RepositoryConfiguration();
  183. String stderr = retrieveStartupStderr(conf);
  184. Assert.assertTrue("stderr=" + stderr,
  185. stderr.startsWith("*** failed to import extension javahgmissing from javahgmissing: [Errno 2]"));
  186. }
  187. @Test
  188. public void testStderrDuringStartupWillFullBuffer() throws IOException {
  189. RepositoryConfiguration conf = new RepositoryConfiguration();
  190. conf.setStderrBufferSize(1);
  191. String stderr = retrieveStartupStderr(conf);
  192. Assert.assertEquals("*", stderr);
  193. }
  194. /** validate that clone requiring auth will use auth in hgrc */
  195. @Test
  196. public void testCloneRequiringAuth() throws Exception {
  197. BaseRepository repoA = getTestRepository();
  198. writeFile(repoA, "x", "abc");
  199. AddCommand.on(repoA).execute();
  200. CommitCommand.on(repoA).message("added x").user("user").execute();
  201. // extension requires auth for requests via hg serve
  202. String extConfig = "extensions.ra="
  203. + Utils.resourceAsFile("/require-auth.py").getPath();
  204. // repository accessed via http requires auth
  205. ServeState serveState = startServing(repoA, "--config", extConfig);
  206. try {
  207. int port = serveState.getPort();
  208. // cloning with an hgrc with valid auth section should succeed
  209. File cloneDir = Files.createTempDir();
  210. Server server = new Server(
  211. RepositoryConfiguration.DEFAULT.getHgBin(),
  212. RepositoryConfiguration.DEFAULT.getEncoding());
  213. server.cloneMercurialRepository(cloneDir,
  214. Utils.resourceAsFile("/test-hgrc-auth").getPath(),
  215. "http://localhost:" + port);
  216. String xContents = Files.readFirstLine(new File(cloneDir, "x"),
  217. utf8());
  218. Assert.assertEquals("abc", xContents);
  219. deleteTempDir(cloneDir);
  220. // cloning with no hgrc should generate auth exception
  221. File cloneDir2 = Files.createTempDir();
  222. Server server2 = new Server(
  223. RepositoryConfiguration.DEFAULT.getHgBin(),
  224. RepositoryConfiguration.DEFAULT.getEncoding());
  225. try {
  226. server2.cloneMercurialRepository(cloneDir, "",
  227. "http://localhost:" + port);
  228. Assert.fail("Didn't get expected http authorization exception");
  229. } catch (Exception e) {
  230. if (!e.getMessage().contains("http authorization required")) {
  231. Assert.fail("Didn't get expected http authorization exception. Got "
  232. + e);
  233. }
  234. }
  235. deleteTempDir(cloneDir2);
  236. } finally {
  237. serveState.stop();
  238. }
  239. }
  240. /**
  241. * Helper function for a couple of test cases.
  242. *
  243. * @param conf
  244. * @return
  245. * @throws IOException
  246. */
  247. private String retrieveStartupStderr(RepositoryConfiguration conf) throws IOException {
  248. conf.setHgrcPath(Utils.resourceAsFile("/missing-extension.hgrc").getPath());
  249. File dir = Files.createTempDir();
  250. BaseRepository repo = Repository.create(conf, dir);
  251. String stderr = getFirstServer(repo).getStartupStderr();
  252. repo.close();
  253. deleteTempDir(dir);
  254. return stderr;
  255. }
  256. /**
  257. * Kill the process with 'kill -9'. The 'kill -9' does not give
  258. * the process a change to react to the kill signal. Using the
  259. * {@link Process#destroy()} the process is performing cleanup
  260. * <p>
  261. * This method calls the 'kill' via system call, and it will for
  262. * example not work on Windows. It is also using platform
  263. * dependent reflection code to obtain the pid of the process.
  264. *
  265. * @return true if 'kill' command was successful, false otherwise
  266. * @param process
  267. */
  268. private boolean killProcess(Process process) {
  269. int pid = readPid(process);
  270. if (pid != 0) {
  271. killProcess(pid);
  272. return true;
  273. }
  274. return false;
  275. }
  276. /**
  277. * Attempt to read the pid for the process. If not possible return
  278. * 0 otherwise return -1
  279. *
  280. * @param process
  281. * @return
  282. */
  283. private static int readPid(Process process) {
  284. try {
  285. Field pidField = process.getClass().getDeclaredField("pid");
  286. pidField.setAccessible(true);
  287. if (pidField.getType().equals(Integer.TYPE)) {
  288. String osName = System.getProperty("os.name");
  289. int pid = pidField.getInt(process);
  290. if (osName.startsWith("Mac OS X")) {
  291. // Hmm, strange but it actuall seems like the
  292. // value of
  293. // the pid is 1 less than the actual pid?!
  294. pid++;
  295. }
  296. return pid;
  297. }
  298. } catch (NoSuchFieldException e) {
  299. return 0;
  300. } catch (Exception e) {
  301. throw Utils.asRuntime(e);
  302. }
  303. return 0;
  304. }
  305. @Test
  306. public void testServerIdle() throws InterruptedException {
  307. RepositoryConfiguration conf = makeRepoConf();
  308. conf.setServerIdleTime(1);
  309. BaseRepository repo = Repository.create(conf, Files.createTempDir());
  310. Assert.assertEquals(1, repo.getServerPool().getNumIdleServers());
  311. Thread.sleep(2000);
  312. Assert.assertEquals(0, repo.getServerPool().getNumIdleServers());
  313. LogCommand.on(repo).execute();
  314. Assert.assertEquals(1, repo.getServerPool().getNumIdleServers());
  315. }
  316. }