/src/test/java/com/aragost/javahg/internals/ServerTest.java
Java | 352 lines | 240 code | 36 blank | 76 comment | 6 complexity | 95a33e2ca51cddcfaa00c5204f1e69e0 MD5 | raw file
- /*
- * #%L
- * JavaHg
- * %%
- * Copyright (C) 2011 aragost Trifork ag
- * %%
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- * #L%
- */
- package com.aragost.javahg.internals;
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
- import java.lang.reflect.Field;
- import java.util.Collections;
- import java.util.List;
- import org.junit.Assert;
- import org.junit.Ignore;
- import org.junit.Test;
- import com.aragost.javahg.BaseRepository;
- import com.aragost.javahg.Bundle;
- import com.aragost.javahg.Changeset;
- import com.aragost.javahg.Repository;
- import com.aragost.javahg.RepositoryConfiguration;
- import com.aragost.javahg.commands.AddCommand;
- import com.aragost.javahg.commands.CommitCommand;
- import com.aragost.javahg.commands.ExecutionException;
- import com.aragost.javahg.commands.IncomingCommand;
- import com.aragost.javahg.commands.LogCommand;
- import com.aragost.javahg.commands.VersionCommand;
- import com.aragost.javahg.test.AbstractTestCase;
- import com.aragost.javahg.test.JavaHgTestExtension;
- import com.google.common.base.Strings;
- import com.google.common.collect.Lists;
- import com.google.common.io.Files;
- public class ServerTest extends AbstractTestCase {
- private List<String> empty = Collections.emptyList();
- @Test
- public void testStartStop() throws IOException {
- File dir = Files.createTempDir();
- Server server = new Server(RepositoryConfiguration.DEFAULT.getHgBin(),
- RepositoryConfiguration.DEFAULT.getEncoding());
- server.initMecurialRepository(dir);
- server.start(dir, null, empty, null, null);
- server.stop();
- deleteTempDir(dir);
- }
- @Test
- public void testStopWhileProducingOutput() throws IOException {
- Repository repo = getTestRepository();
- Server server = getFirstServer(repo);
- InputStream stdout = server.runCommand(Lists.newArrayList("version"), VersionCommand.on(repo));
- // Nothing is yet read from stdout, stop the server. You
- // should then get
- // an IOException reading from stdout.
- server.stop();
- try {
- stdout.read();
- Assert.fail("IOException expected");
- } catch (IOException e) {
- // success
- }
- server.start(repo.getDirectory(), null, empty, null, null);
- VersionCommand.on(repo).execute();
- }
- @Test
- public void testLock() throws IOException, InterruptedException {
- Repository repo = getTestRepository();
- TestableCommand command = new TestableCommand(repo, "version");
- // The command must produce output so that it wont finish
- // before we empty the stdout. That way we can keep two
- // commands running at the same time and trigger the
- // IllegalStateException below .
- InputStream stdout = command.executeToStream();
- try {
- Server server = getFirstServer(repo);
- server.runCommand(Lists.newArrayList("version"), command);
- Assert.fail("Exception expected");
- } catch (IllegalStateException e) {
- Utils.consumeAll(stdout);
- }
- command.executeToStream();
- }
- @Test
- public void testServerRefCount() throws IOException {
- BaseRepository repo = getTestRepository();
- BaseRepository repo2 = getTestRepository2();
- writeFile("a");
- commit();
- Bundle bundle = IncomingCommand.on(repo2).execute(repo);
- Repository repo3 = bundle.getOverlayRepository();
- Assert.assertSame(repo2.getServerPool(), repo3.getServerPool());
- ServerPool pool = repo2.getServerPool();
- Assert.assertEquals(1, pool.getServers().size());
- Assert.assertNotNull(pool.getServers().get(0).getProcess());
- repo2.close();
- Assert.assertNotNull(pool.getServers().get(0).getProcess());
- Server server = pool.getServers().get(0);
- bundle.close();
- Assert.assertNull(server.getProcess());
- Assert.assertTrue(pool.getServers().isEmpty());
- }
- @Test
- public void testConfigChanges() throws IOException {
- BaseRepository repo = getTestRepository();
- GenericCommand cmd = new GenericCommand(repo, "version") {
- {
- {
- cmdAppend("--config", "ui.username=xxx");
- }
- }
- };
- cmd.execute();
- writeFile("A");
- repo.workingCopy().add("A");
- try {
- // The previous config change is not forgotten
- CommitCommand commit = CommitCommand.on(repo).message("m");
- Changeset cs = commit.execute();
- assertFailedExecution(commit, "Username is " + cs.getUser());
- } catch (ExecutionException e) {
- Assert.assertTrue(e.getMessage().startsWith("no username supplied "));
- }
- }
- @Test
- public void testKillServerProcess() throws IOException {
- File dir = Files.createTempDir();
- RepositoryConfiguration repoConfig = new RepositoryConfiguration();
- repoConfig.addExtension(JavaHgTestExtension.class);
- Repository repo = Repository.create(repoConfig, dir);
- Process process = getFirstServer(repo).getProcess();
- process.destroy();
- // Process is dead and we can't send command
- try {
- VersionCommand cmd = VersionCommand.on(repo);
- cmd.execute();
- assertFailedExecution(cmd);
- } catch (UnexpectedServerTerminationException e) {
- // success
- }
- repo.close();
- repo = Repository.open(repoConfig, dir);
- String longStringThatDoesntFitInBuffers = Strings.repeat("x", 10 * 1000 * 1000);
- GenericCommand cmd = new GenericCommand(repo, "javahg-write");
- HgInputStream stream = cmd.launchStream("o", longStringThatDoesntFitInBuffers);
- boolean killed = killProcess(process);
- if (killed) {
- // Command is now sent, but we can't read output
- try {
- // String s =
- Utils.readStream(stream, repo.getServerPool().newDecoder());
- // TODO This doesn't work on Linux, why?
- // Assert.fail("Exception expected. Read " +
- // s.length() + " bytes");
- } catch (UnexpectedServerTerminationException e) {
- System.err.println("Exit value in testKillServerProcess: " + e.getExitValue());
- // success
- }
- }
- repo.close();
- deleteTempDir(dir);
- }
- @Test
- @Ignore
- public void testStderrDuringStartup() throws IOException {
- RepositoryConfiguration conf = new RepositoryConfiguration();
- String stderr = retrieveStartupStderr(conf);
- Assert.assertTrue("stderr=" + stderr,
- stderr.startsWith("*** failed to import extension javahgmissing from javahgmissing: [Errno 2]"));
- }
- @Test
- public void testStderrDuringStartupWillFullBuffer() throws IOException {
- RepositoryConfiguration conf = new RepositoryConfiguration();
- conf.setStderrBufferSize(1);
- String stderr = retrieveStartupStderr(conf);
- Assert.assertEquals("*", stderr);
- }
- /** validate that clone requiring auth will use auth in hgrc */
- @Test
- public void testCloneRequiringAuth() throws Exception {
- BaseRepository repoA = getTestRepository();
- writeFile(repoA, "x", "abc");
- AddCommand.on(repoA).execute();
- CommitCommand.on(repoA).message("added x").user("user").execute();
- // extension requires auth for requests via hg serve
- String extConfig = "extensions.ra="
- + Utils.resourceAsFile("/require-auth.py").getPath();
- // repository accessed via http requires auth
- ServeState serveState = startServing(repoA, "--config", extConfig);
- try {
- int port = serveState.getPort();
- // cloning with an hgrc with valid auth section should succeed
- File cloneDir = Files.createTempDir();
- Server server = new Server(
- RepositoryConfiguration.DEFAULT.getHgBin(),
- RepositoryConfiguration.DEFAULT.getEncoding());
- server.cloneMercurialRepository(cloneDir,
- Utils.resourceAsFile("/test-hgrc-auth").getPath(),
- "http://localhost:" + port);
- String xContents = Files.readFirstLine(new File(cloneDir, "x"),
- utf8());
- Assert.assertEquals("abc", xContents);
- deleteTempDir(cloneDir);
- // cloning with no hgrc should generate auth exception
- File cloneDir2 = Files.createTempDir();
- Server server2 = new Server(
- RepositoryConfiguration.DEFAULT.getHgBin(),
- RepositoryConfiguration.DEFAULT.getEncoding());
- try {
- server2.cloneMercurialRepository(cloneDir, "",
- "http://localhost:" + port);
- Assert.fail("Didn't get expected http authorization exception");
- } catch (Exception e) {
- if (!e.getMessage().contains("http authorization required")) {
- Assert.fail("Didn't get expected http authorization exception. Got "
- + e);
- }
- }
- deleteTempDir(cloneDir2);
- } finally {
- serveState.stop();
- }
- }
- /**
- * Helper function for a couple of test cases.
- *
- * @param conf
- * @return
- * @throws IOException
- */
- private String retrieveStartupStderr(RepositoryConfiguration conf) throws IOException {
- conf.setHgrcPath(Utils.resourceAsFile("/missing-extension.hgrc").getPath());
- File dir = Files.createTempDir();
- BaseRepository repo = Repository.create(conf, dir);
- String stderr = getFirstServer(repo).getStartupStderr();
- repo.close();
- deleteTempDir(dir);
- return stderr;
- }
- /**
- * Kill the process with 'kill -9'. The 'kill -9' does not give
- * the process a change to react to the kill signal. Using the
- * {@link Process#destroy()} the process is performing cleanup
- * <p>
- * This method calls the 'kill' via system call, and it will for
- * example not work on Windows. It is also using platform
- * dependent reflection code to obtain the pid of the process.
- *
- * @return true if 'kill' command was successful, false otherwise
- * @param process
- */
- private boolean killProcess(Process process) {
- int pid = readPid(process);
- if (pid != 0) {
- killProcess(pid);
- return true;
- }
- return false;
- }
- /**
- * Attempt to read the pid for the process. If not possible return
- * 0 otherwise return -1
- *
- * @param process
- * @return
- */
- private static int readPid(Process process) {
- try {
- Field pidField = process.getClass().getDeclaredField("pid");
- pidField.setAccessible(true);
- if (pidField.getType().equals(Integer.TYPE)) {
- String osName = System.getProperty("os.name");
- int pid = pidField.getInt(process);
- if (osName.startsWith("Mac OS X")) {
- // Hmm, strange but it actuall seems like the
- // value of
- // the pid is 1 less than the actual pid?!
- pid++;
- }
- return pid;
- }
- } catch (NoSuchFieldException e) {
- return 0;
- } catch (Exception e) {
- throw Utils.asRuntime(e);
- }
- return 0;
- }
-
- @Test
- public void testServerIdle() throws InterruptedException {
- RepositoryConfiguration conf = makeRepoConf();
- conf.setServerIdleTime(1);
- BaseRepository repo = Repository.create(conf, Files.createTempDir());
- Assert.assertEquals(1, repo.getServerPool().getNumIdleServers());
- Thread.sleep(2000);
- Assert.assertEquals(0, repo.getServerPool().getNumIdleServers());
- LogCommand.on(repo).execute();
- Assert.assertEquals(1, repo.getServerPool().getNumIdleServers());
- }
- }