diff options
author | Jens Baumgart <jens.baumgart@sap.com> | 2010-12-08 16:20:28 +0100 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2010-12-08 16:20:28 +0100 |
commit | 51e2646d4a17a22210111675030dee54f4cfee49 (patch) | |
tree | ca46985247e23b7abc282b8167750459550b0a11 /org.eclipse.jgit.junit.http/src | |
parent | a02be9725c9f83f0a0280a9a1f00e8d484825885 (diff) | |
download | jgit-51e2646d4a17a22210111675030dee54f4cfee49.tar.gz jgit-51e2646d4a17a22210111675030dee54f4cfee49.zip |
Introduce http test bundle
Introduce a http test bundle to make this functionality available for
EGit tests. A simple http server class is provided. The jetty version
was updated to a version that is also available via p2 (needed in EGit
UI tests).
Change-Id: I13bfc4c6c47e27d8f97d3e9752347d6d23e553d4
Signed-off-by: Jens Baumgart <jens.baumgart@sap.com>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
Diffstat (limited to 'org.eclipse.jgit.junit.http/src')
7 files changed, 1171 insertions, 0 deletions
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java new file mode 100644 index 0000000000..aaccc66f55 --- /dev/null +++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.junit.http; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; + +/** A single request made through {@link AppServer}. */ +public class AccessEvent { + private final String method; + + private final String uri; + + private final Map<String, String> requestHeaders; + + private final Map<String, String[]> parameters; + + private final int status; + + private final Map<String, String> responseHeaders; + + AccessEvent(final Request req, final Response rsp) { + method = req.getMethod(); + uri = req.getRequestURI(); + requestHeaders = cloneHeaders(req); + parameters = clone(req.getParameterMap()); + + status = rsp.getStatus(); + responseHeaders = cloneHeaders(rsp); + } + + private static Map<String, String> cloneHeaders(final Request req) { + Map<String, String> r = new TreeMap<String, String>(); + Enumeration hn = req.getHeaderNames(); + while (hn.hasMoreElements()) { + String key = (String) hn.nextElement(); + if (!r.containsKey(key)) { + r.put(key, req.getHeader(key)); + } + } + return Collections.unmodifiableMap(r); + } + + private static Map<String, String> cloneHeaders(final Response rsp) { + Map<String, String> r = new TreeMap<String, String>(); + Enumeration<String> hn = rsp.getHttpFields().getFieldNames(); + while (hn.hasMoreElements()) { + String key = hn.nextElement(); + if (!r.containsKey(key)) { + Enumeration<String> v = rsp.getHttpFields().getValues(key); + r.put(key, v.nextElement()); + } + } + return Collections.unmodifiableMap(r); + } + + @SuppressWarnings("unchecked") + private static Map<String, String[]> clone(Map parameterMap) { + return new TreeMap<String, String[]>(parameterMap); + } + + /** @return {@code "GET"} or {@code "POST"} */ + public String getMethod() { + return method; + } + + /** @return path of the file on the server, e.g. {@code /git/HEAD}. */ + public String getPath() { + return uri; + } + + /** + * @param name + * name of the request header to read. + * @return first value of the request header; null if not sent. + */ + public String getRequestHeader(String name) { + return requestHeaders.get(name); + } + + /** + * @param name + * name of the request parameter to read. + * @return first value of the request parameter; null if not sent. + */ + public String getParameter(String name) { + String[] r = parameters.get(name); + return r != null && 1 <= r.length ? r[0] : null; + } + + /** @return all parameters in the request. */ + public Map<String, String[]> getParameters() { + return parameters; + } + + /** @return HTTP status code of the response, e.g. 200, 403, 500. */ + public int getStatus() { + return status; + } + + /** + * @param name + * name of the response header to read. + * @return first value of the response header; null if not sent. + */ + public String getResponseHeader(String name) { + return responseHeaders.get(name); + } + + public String toString() { + StringBuilder b = new StringBuilder(); + b.append(method); + b.append(' '); + b.append(uri); + if (!parameters.isEmpty()) { + b.append('?'); + boolean first = true; + for (Map.Entry<String, String[]> e : parameters.entrySet()) { + for (String val : e.getValue()) { + if (!first) { + b.append('&'); + } + first = false; + + b.append(e.getKey()); + b.append('='); + b.append(val); + } + } + } + b.append(' '); + b.append(status); + return b.toString(); + } +} diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java new file mode 100644 index 0000000000..2d1c98c911 --- /dev/null +++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.junit.http; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; + +import junit.framework.Assert; + +import org.eclipse.jetty.http.security.Constraint; +import org.eclipse.jetty.http.security.Password; +import org.eclipse.jetty.security.Authenticator; +import org.eclipse.jetty.security.ConstraintMapping; +import org.eclipse.jetty.security.ConstraintSecurityHandler; +import org.eclipse.jetty.security.MappedLoginService; +import org.eclipse.jetty.security.authentication.BasicAuthenticator; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jgit.transport.URIish; + +/** + * Tiny web application server for unit testing. + * <p> + * Tests should start the server in their {@code setUp()} method and stop the + * server in their {@code tearDown()} method. Only while started the server's + * URL and/or port number can be obtained. + */ +public class AppServer { + /** Realm name for the secure access areas. */ + public static final String realm = "Secure Area"; + + /** Username for secured access areas. */ + public static final String username = "agitter"; + + /** Password for {@link #username} in secured access areas. */ + public static final String password = "letmein"; + + static { + // Install a logger that throws warning messages. + // + final String prop = "org.eclipse.jetty.util.log.class"; + System.setProperty(prop, RecordingLogger.class.getName()); + } + + private final Server server; + + private final Connector connector; + + private final ContextHandlerCollection contexts; + + private final TestRequestLog log; + + public AppServer() { + connector = new SelectChannelConnector(); + connector.setPort(0); + try { + final InetAddress me = InetAddress.getByName("localhost"); + connector.setHost(me.getHostAddress()); + } catch (UnknownHostException e) { + throw new RuntimeException("Cannot find localhost", e); + } + + // We need a handful of threads in the thread pool, otherwise + // our tests will deadlock when they can't open enough requests. + // In theory we only need 1 concurrent connection at a time, but + // I suspect the JRE isn't doing request pipelining on existing + // connections like we want it to. + // + final QueuedThreadPool pool = new QueuedThreadPool(); + pool.setMinThreads(1); + pool.setMaxThreads(4); + pool.setMaxQueued(8); + + contexts = new ContextHandlerCollection(); + + log = new TestRequestLog(); + log.setHandler(contexts); + + server = new Server(); + server.setConnectors(new Connector[] { connector }); + server.setThreadPool(pool); + server.setHandler(log); + + server.setStopAtShutdown(false); + server.setGracefulShutdown(0); + } + + /** + * Create a new servlet context within the server. + * <p> + * This method should be invoked before the server is started, once for each + * context the caller wants to register. + * + * @param path + * path of the context; use "/" for the root context if binding + * to the root is desired. + * @return the context to add servlets into. + */ + public ServletContextHandler addContext(String path) { + assertNotYetSetUp(); + if ("".equals(path)) + path = "/"; + + ServletContextHandler ctx = new ServletContextHandler(); + ctx.setContextPath(path); + contexts.addHandler(ctx); + + return ctx; + } + + public ServletContextHandler authBasic(ServletContextHandler ctx) { + assertNotYetSetUp(); + auth(ctx, new BasicAuthenticator()); + return ctx; + } + + private void auth(ServletContextHandler ctx, Authenticator authType) { + final String role = "can-access"; + + MappedLoginService users = new MappedLoginService() { + @Override + protected UserIdentity loadUser(String who) { + return null; + } + + @Override + protected void loadUsers() throws IOException { + putUser(username, new Password(password), new String[] { role }); + } + }; + + ConstraintMapping cm = new ConstraintMapping(); + cm.setConstraint(new Constraint()); + cm.getConstraint().setAuthenticate(true); + cm.getConstraint().setDataConstraint(Constraint.DC_NONE); + cm.getConstraint().setRoles(new String[] { role }); + cm.setPathSpec("/*"); + + ConstraintSecurityHandler sec = new ConstraintSecurityHandler(); + sec.setStrict(false); + sec.setRealmName(realm); + sec.setAuthenticator(authType); + sec.setLoginService(users); + sec.setConstraintMappings(new ConstraintMapping[] { cm }); + sec.setHandler(ctx); + + contexts.removeHandler(ctx); + contexts.addHandler(sec); + } + + /** + * Start the server on a random local port. + * + * @throws Exception + * the server cannot be started, testing is not possible. + */ + public void setUp() throws Exception { + RecordingLogger.clear(); + log.clear(); + server.start(); + } + + /** + * Shutdown the server. + * + * @throws Exception + * the server refuses to halt, or wasn't running. + */ + public void tearDown() throws Exception { + RecordingLogger.clear(); + log.clear(); + server.stop(); + } + + /** + * Get the URI to reference this server. + * <p> + * The returned URI includes the proper host name and port number, but does + * not contain a path. + * + * @return URI to reference this server's root context. + */ + public URI getURI() { + assertAlreadySetUp(); + String host = connector.getHost(); + if (host.contains(":") && !host.startsWith("[")) + host = "[" + host + "]"; + final String uri = "http://" + host + ":" + getPort(); + try { + return new URI(uri); + } catch (URISyntaxException e) { + throw new RuntimeException("Unexpected URI error on " + uri, e); + } + } + + /** @return the local port number the server is listening on. */ + public int getPort() { + assertAlreadySetUp(); + return ((SelectChannelConnector) connector).getLocalPort(); + } + + /** @return all requests since the server was started. */ + public List<AccessEvent> getRequests() { + return new ArrayList<AccessEvent>(log.getEvents()); + } + + /** + * @param base + * base URI used to access the server. + * @param path + * the path to locate requests for, relative to {@code base}. + * @return all requests which match the given path. + */ + public List<AccessEvent> getRequests(URIish base, String path) { + return getRequests(HttpTestCase.join(base, path)); + } + + /** + * @param path + * the path to locate requests for. + * @return all requests which match the given path. + */ + public List<AccessEvent> getRequests(String path) { + ArrayList<AccessEvent> r = new ArrayList<AccessEvent>(); + for (AccessEvent event : log.getEvents()) { + if (event.getPath().equals(path)) { + r.add(event); + } + } + return r; + } + + private void assertNotYetSetUp() { + Assert.assertFalse("server is not running", server.isRunning()); + } + + private void assertAlreadySetUp() { + Assert.assertTrue("server is running", server.isRunning()); + } +} diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java new file mode 100644 index 0000000000..7da903461d --- /dev/null +++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2009-2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.junit.http; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.storage.file.FileRepository; +import org.eclipse.jgit.transport.RefSpec; +import org.eclipse.jgit.transport.RemoteRefUpdate; +import org.eclipse.jgit.transport.URIish; + +/** Base class for HTTP related transport testing. */ +public abstract class HttpTestCase extends LocalDiskRepositoryTestCase { + protected static final String master = Constants.R_HEADS + Constants.MASTER; + + /** In-memory application server; subclass must start. */ + protected AppServer server; + + protected void setUp() throws Exception { + super.setUp(); + server = new AppServer(); + } + + protected void tearDown() throws Exception { + server.tearDown(); + super.tearDown(); + } + + protected TestRepository<FileRepository> createTestRepository() + throws IOException { + return new TestRepository<FileRepository>(createBareRepository()); + } + + protected URIish toURIish(String path) throws URISyntaxException { + URI u = server.getURI().resolve(path); + return new URIish(u.toString()); + } + + protected URIish toURIish(ServletContextHandler app, String name) + throws URISyntaxException { + String p = app.getContextPath(); + if (!p.endsWith("/") && !name.startsWith("/")) + p += "/"; + p += name; + return toURIish(p); + } + + protected List<AccessEvent> getRequests() { + return server.getRequests(); + } + + protected List<AccessEvent> getRequests(URIish base, String path) { + return server.getRequests(base, path); + } + + protected List<AccessEvent> getRequests(String path) { + return server.getRequests(path); + } + + protected static void fsck(Repository db, RevObject... tips) + throws Exception { + new TestRepository(db).fsck(tips); + } + + protected static Set<RefSpec> mirror(String... refs) { + HashSet<RefSpec> r = new HashSet<RefSpec>(); + for (String name : refs) { + RefSpec rs = new RefSpec(name); + rs = rs.setDestination(name); + rs = rs.setForceUpdate(true); + r.add(rs); + } + return r; + } + + protected static Collection<RemoteRefUpdate> push(TestRepository from, + RevCommit q) throws IOException { + final Repository db = from.getRepository(); + final String srcExpr = q.name(); + final String dstName = master; + final boolean forceUpdate = true; + final String localName = null; + final ObjectId oldId = null; + + RemoteRefUpdate u = new RemoteRefUpdate(db, srcExpr, dstName, + forceUpdate, localName, oldId); + return Collections.singleton(u); + } + + public static String loose(URIish base, AnyObjectId id) { + final String objectName = id.name(); + final String d = objectName.substring(0, 2); + final String f = objectName.substring(2); + return join(base, "objects/" + d + "/" + f); + } + + public static String join(URIish base, String path) { + if (path.startsWith("/")) + fail("Cannot join absolute path " + path + " to URIish " + base); + + String dir = base.getPath(); + if (!dir.endsWith("/")) + dir += "/"; + return dir + path; + } +} diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/MockServletConfig.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/MockServletConfig.java new file mode 100644 index 0000000000..0b4530ded2 --- /dev/null +++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/MockServletConfig.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.junit.http; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; + +public class MockServletConfig implements ServletConfig { + private final Map<String, String> parameters = new HashMap<String, String>(); + + public void setInitParameter(String name, String value) { + parameters.put(name, value); + } + + public String getInitParameter(String name) { + return parameters.get(name); + } + + public Enumeration getInitParameterNames() { + final Iterator<String> i = parameters.keySet().iterator(); + return new Enumeration<String>() { + public boolean hasMoreElements() { + return i.hasNext(); + } + + public String nextElement() { + return i.next(); + } + }; + } + + public String getServletName() { + return "MOCK_SERVLET"; + } + + public ServletContext getServletContext() { + return null; + } +}
\ No newline at end of file diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java new file mode 100644 index 0000000000..3295d44391 --- /dev/null +++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.junit.http; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jetty.util.log.Logger; + +/** Logs warnings into an array for later inspection. */ +public class RecordingLogger implements Logger { + private static List<Warning> warnings = new ArrayList<Warning>(); + + /** Clear the warnings, automatically done by {@link AppServer#setUp()} */ + public static void clear() { + synchronized (warnings) { + warnings.clear(); + } + } + + /** @return the warnings (if any) from the last execution */ + public static List<Warning> getWarnings() { + synchronized (warnings) { + ArrayList<Warning> copy = new ArrayList<Warning>(warnings); + return Collections.unmodifiableList(copy); + } + } + + @SuppressWarnings("serial") + public static class Warning extends Exception { + public Warning(String msg) { + super(msg); + } + + public Warning(String msg, Throwable cause) { + super(msg, cause); + } + + public Warning(Throwable thrown) { + super(thrown); + } + } + + private final String name; + + public RecordingLogger() { + this(""); + } + + public RecordingLogger(final String name) { + this.name = name; + } + + public Logger getLogger(@SuppressWarnings("hiding") String name) { + return new RecordingLogger(name); + } + + public String getName() { + return name; + } + + public void warn(String msg, Object arg0, Object arg1) { + synchronized (warnings) { + warnings.add(new Warning(MessageFormat.format(msg, arg0, arg1))); + } + } + + public void warn(String msg, Throwable th) { + synchronized (warnings) { + warnings.add(new Warning(msg, th)); + } + } + + public void warn(String msg) { + synchronized (warnings) { + warnings.add(new Warning(msg)); + } + } + + public void debug(@SuppressWarnings("unused") String msg, + @SuppressWarnings("unused") Object arg0, + @SuppressWarnings("unused") Object arg1) { + // Ignore (not relevant to test failures) + } + + public void debug(String msg, Throwable th) { + // Ignore (not relevant to test failures) + } + + public void debug(@SuppressWarnings("unused") String msg) { + // Ignore (not relevant to test failures) + } + + public void info(@SuppressWarnings("unused") String msg, + @SuppressWarnings("unused") Object arg0, + @SuppressWarnings("unused") Object arg1) { + // Ignore (not relevant to test failures) + } + + public void info(@SuppressWarnings("unused") String msg) { + // Ignore (not relevant to test failures) + } + + public boolean isDebugEnabled() { + return false; + } + + public void setDebugEnabled(boolean enabled) { + // Ignore (not relevant to test failures) + } + + public void warn(String msg, Object... args) { + synchronized (warnings) { + warnings.add(new Warning(MessageFormat.format(msg, args))); + } + } + + public void warn(Throwable thrown) { + synchronized (warnings) { + warnings.add(new Warning(thrown)); + } + } + + public void info(String msg, Object... args) { + // Ignore (not relevant to test failures) + } + + public void info(Throwable thrown) { + // Ignore (not relevant to test failures) + } + + public void info(String msg, Throwable thrown) { + // Ignore (not relevant to test failures) + } + + public void debug(String msg, Object... args) { + // Ignore (not relevant to test failures) + } + + public void debug(Throwable thrown) { + // Ignore (not relevant to test failures) + } +} diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java new file mode 100644 index 0000000000..987f2118ea --- /dev/null +++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2009-2010, Google Inc. + * Copyright (C) 2010, Jens Baumgart <jens.baumgart@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.junit.http; + +import java.net.URI; +import java.net.URISyntaxException; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.http.server.GitServlet; +import org.eclipse.jgit.http.server.resolver.RepositoryResolver; +import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.storage.file.FileRepository; +import org.eclipse.jgit.transport.URIish; + +/** + * Simple http server for testing http access to Git repositories. + * Authentication with hardcoded credentials user:agitter password:letmein. + */ +public class SimpleHttpServer { + + AppServer server; + + private final FileRepository db; + + private URIish uri; + + public SimpleHttpServer(FileRepository repository) { + this.db = repository; + server = new AppServer(); + } + + public void start() throws Exception { + ServletContextHandler sBasic = server.authBasic(smart("/sbasic")); + server.setUp(); + final String srcName = db.getDirectory().getName(); + uri = toURIish(sBasic, srcName); + } + + public void stop() throws Exception { + server.tearDown(); + } + + public URIish getUri() { + return uri; + } + + private ServletContextHandler smart(final String path) { + GitServlet gs = new GitServlet(); + gs.setRepositoryResolver(new RepositoryResolver() { + public Repository open(HttpServletRequest req, String name) + throws RepositoryNotFoundException, + ServiceNotEnabledException { + if (!name.equals(nameOf(db))) + throw new RepositoryNotFoundException(name); + + db.incrementOpen(); + return db; + } + }); + + ServletContextHandler ctx = server.addContext(path); + ctx.addServlet(new ServletHolder(gs), "/*"); + return ctx; + } + + private static String nameOf(final FileRepository db) { + return db.getDirectory().getName(); + } + + private URIish toURIish(String path) throws URISyntaxException { + URI u = server.getURI().resolve(path); + return new URIish(u.toString()); + } + + private URIish toURIish(ServletContextHandler app, String name) + throws URISyntaxException { + String p = app.getContextPath(); + if (!p.endsWith("/") && !name.startsWith("/")) + p += "/"; + p += name; + return toURIish(p); + } +} diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java new file mode 100644 index 0000000000..b186e1f248 --- /dev/null +++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.junit.http; + +import org.eclipse.jetty.server.DispatcherType; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.server.handler.HandlerWrapper; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Semaphore; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** Logs request made through {@link AppServer}. */ +class TestRequestLog extends HandlerWrapper { + private static final int MAX = 16; + + private final List<AccessEvent> events = new ArrayList<AccessEvent>(); + + private final Semaphore active = new Semaphore(MAX); + + /** Reset the log back to its original empty state. */ + void clear() { + try { + for (;;) { + try { + active.acquire(MAX); + break; + } catch (InterruptedException e) { + continue; + } + } + + synchronized (events) { + events.clear(); + } + } finally { + active.release(MAX); + } + } + + /** @return all of the events made since the last clear. */ + List<AccessEvent> getEvents() { + try { + for (;;) { + try { + active.acquire(MAX); + break; + } catch (InterruptedException e) { + continue; + } + } + + synchronized (events) { + return events; + } + } finally { + active.release(MAX); + } + } + + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + try { + for (;;) { + try { + active.acquire(); + break; + } catch (InterruptedException e) { + continue; + } + } + + super.handle(target, baseRequest, request, response); + + if (DispatcherType.REQUEST.equals(baseRequest.getDispatcherType())) + log((Request) request, (Response) response); + + } finally { + active.release(); + } + } + + private void log(Request request, Response response) { + synchronized (events) { + events.add(new AccessEvent(request, response)); + } + } +} |