Gitiles is a simple browser for Git repositories It is based on JGit and uses Google Closure Templates as a templating language. Access to underlying repositories is based on a few simple interfaces; currently, there is only a simple disk-based implementation, but other implementations are possible. Features include viewing repositories by branch, shortlogs, showing individual files and diffs with syntax highlighting, with many more planned. The application itself is a standard Java servlet and is configured primarily via a git config format file. Deploying the WAR in any servlet container should be possible. In addition, a standalone server may be run with jetty-maven-plugin with `mvn package && mvn jetty:run`. Change-Id: I0ab8875b6c50f7df03b9a42b4a60923a4827bde7
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/ConfigUtilTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/ConfigUtilTest.java new file mode 100644 index 0000000..96439c2 --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/ConfigUtilTest.java
@@ -0,0 +1,43 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import static com.google.gitiles.ConfigUtil.getDuration; + +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.Config; +import org.joda.time.Duration; + +/** Tests for configuration utilities. */ +public class ConfigUtilTest extends TestCase { + public void testGetDuration() throws Exception { + Duration def = Duration.standardSeconds(2); + Config config = new Config(); + Duration t; + + config.setString("core", "dht", "timeout", "500 ms"); + t = getDuration(config, "core", "dht", "timeout", def); + assertEquals(500, t.getMillis()); + + config.setString("core", "dht", "timeout", "5.2 sec"); + t = getDuration(config, "core", "dht", "timeout", def); + assertEquals(5200, t.getMillis()); + + config.setString("core", "dht", "timeout", "1 min"); + t = getDuration(config, "core", "dht", "timeout", def); + assertEquals(60000, t.getMillis()); + } +}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/FakeHttpServletRequest.java b/gitiles-servlet/src/test/java/com/google/gitiles/FakeHttpServletRequest.java new file mode 100644 index 0000000..c1afa74 --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/FakeHttpServletRequest.java
@@ -0,0 +1,389 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import static com.google.common.base.Charsets.UTF_8; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.gitiles.TestGitilesUrls.URLS; + +import com.google.common.base.Function; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Maps; + +import org.eclipse.jgit.http.server.ServletUtils; +import org.eclipse.jgit.storage.dfs.DfsRepository; + +import java.io.BufferedReader; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.security.Principal; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletInputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +/** Simple fake implementation of {@link HttpServletRequest}. */ +public class FakeHttpServletRequest implements HttpServletRequest { + public static final String SERVLET_PATH = "/b"; + + public static FakeHttpServletRequest newRequest() { + return new FakeHttpServletRequest( + URLS.getHostName(null), + 80, + "", + SERVLET_PATH, + ""); + } + + public static FakeHttpServletRequest newRequest(DfsRepository repo) { + FakeHttpServletRequest req = newRequest(); + req.setAttribute(ServletUtils.ATTRIBUTE_REPOSITORY, repo); + return req; + } + + private final Map<String, Object> attributes; + private final ListMultimap<String, String> headers; + + private ListMultimap<String, String> parameters; + private String hostName; + private int port; + private String contextPath; + private String servletPath; + private String path; + + private FakeHttpServletRequest(String hostName, int port, String contextPath, String servletPath, + String path) { + this.hostName = checkNotNull(hostName, "hostName"); + checkArgument(port > 0); + this.port = port; + this.contextPath = checkNotNull(contextPath, "contextPath"); + this.servletPath = checkNotNull(servletPath, "servletPath"); + attributes = Maps.newConcurrentMap(); + parameters = LinkedListMultimap.create(); + headers = LinkedListMultimap.create(); + } + + @Override + public Object getAttribute(String name) { + return attributes.get(name); + } + + @Override + public Enumeration<String> getAttributeNames() { + return Collections.enumeration(attributes.keySet()); + } + + @Override + public String getCharacterEncoding() { + return UTF_8.name(); + } + + @Override + public int getContentLength() { + return -1; + } + + @Override + public String getContentType() { + return null; + } + + @Override + public ServletInputStream getInputStream() { + throw new UnsupportedOperationException(); + } + + @Override + public String getLocalAddr() { + return "1.2.3.4"; + } + + @Override + public String getLocalName() { + return hostName; + } + + @Override + public int getLocalPort() { + return port; + } + + @Override + public Locale getLocale() { + return Locale.US; + } + + @Override + public Enumeration<Locale> getLocales() { + return Collections.enumeration(Collections.singleton(Locale.US)); + } + + @Override + public String getParameter(String name) { + return Iterables.getFirst(parameters.get(name), null); + } + + private static final Function<Collection<String>, String[]> STRING_COLLECTION_TO_ARRAY = + new Function<Collection<String>, String[]>() { + @Override + public String[] apply(Collection<String> values) { + return values.toArray(new String[0]); + } + }; + + @Override + public Map<String, String[]> getParameterMap() { + return Collections.unmodifiableMap( + Maps.transformValues(parameters.asMap(), STRING_COLLECTION_TO_ARRAY)); + } + + @Override + public Enumeration<String> getParameterNames() { + return Collections.enumeration(parameters.keySet()); + } + + @Override + public String[] getParameterValues(String name) { + return STRING_COLLECTION_TO_ARRAY.apply(parameters.get(name)); + } + + public void setQueryString(String qs) { + ListMultimap<String, String> params = LinkedListMultimap.create(); + for (String entry : Splitter.on('&').split(qs)) { + List<String> kv = ImmutableList.copyOf(Splitter.on('=').limit(2).split(entry)); + try { + params.put(URLDecoder.decode(kv.get(0), UTF_8.name()), + kv.size() == 2 ? URLDecoder.decode(kv.get(1), UTF_8.name()) : ""); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException(e); + } + } + parameters = params; + } + + @Override + public String getProtocol() { + return "HTTP/1.1"; + } + + @Override + public BufferedReader getReader() { + throw new UnsupportedOperationException(); + } + + @Override + @Deprecated + public String getRealPath(String path) { + throw new UnsupportedOperationException(); + } + + @Override + public String getRemoteAddr() { + return "5.6.7.8"; + } + + @Override + public String getRemoteHost() { + return "remotehost"; + } + + @Override + public int getRemotePort() { + return 1234; + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) { + throw new UnsupportedOperationException(); + } + + @Override + public String getScheme() { + return port == 443 ? "https" : "http"; + } + + @Override + public String getServerName() { + return hostName; + } + + @Override + public int getServerPort() { + return port; + } + + @Override + public boolean isSecure() { + return port == 443; + } + + @Override + public void removeAttribute(String name) { + attributes.remove(name); + } + + @Override + public void setAttribute(String name, Object value) { + attributes.put(name, value); + } + + @Override + public void setCharacterEncoding(String env) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public String getAuthType() { + return null; + } + + @Override + public String getContextPath() { + return contextPath; + } + + @Override + public Cookie[] getCookies() { + return new Cookie[0]; + } + + @Override + public long getDateHeader(String name) { + throw new UnsupportedOperationException(); + } + + @Override + public String getHeader(String name) { + return Iterables.getFirst(headers.get(name), null); + } + + @Override + public Enumeration<String> getHeaderNames() { + return Collections.enumeration(headers.keySet()); + } + + @Override + public Enumeration<String> getHeaders(String name) { + return Collections.enumeration(headers.get(name)); + } + + @Override + public int getIntHeader(String name) { + return Integer.parseInt(getHeader(name)); + } + + @Override + public String getMethod() { + return "GET"; + } + + @Override + public String getPathInfo() { + return path; + } + + public void setPathInfo(String path) { + this.path = checkNotNull(path); + } + + @Override + public String getPathTranslated() { + return path; + } + + @Override + public String getQueryString() { + return null; + } + + @Override + public String getRemoteUser() { + return null; + } + + @Override + public String getRequestURI() { + return null; + } + + @Override + public StringBuffer getRequestURL() { + return null; + } + + @Override + public String getRequestedSessionId() { + return null; + } + + @Override + public String getServletPath() { + return servletPath; + } + + @Override + public HttpSession getSession() { + throw new UnsupportedOperationException(); + } + + @Override + public HttpSession getSession(boolean create) { + throw new UnsupportedOperationException(); + } + + @Override + public Principal getUserPrincipal() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isRequestedSessionIdFromURL() { + throw new UnsupportedOperationException(); + } + + @Override + @Deprecated + public boolean isRequestedSessionIdFromUrl() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isRequestedSessionIdValid() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isUserInRole(String role) { + throw new UnsupportedOperationException(); + } +}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/FakeHttpServletResponse.java b/gitiles-servlet/src/test/java/com/google/gitiles/FakeHttpServletResponse.java new file mode 100644 index 0000000..87a0099 --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/FakeHttpServletResponse.java
@@ -0,0 +1,201 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import static com.google.common.base.Charsets.UTF_8; + +import java.io.PrintWriter; +import java.util.Locale; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + +/** Simple fake implementation of {@link HttpServletResponse}. */ +public class FakeHttpServletResponse implements HttpServletResponse { + + private volatile int status; + + public FakeHttpServletResponse() { + status = 200; + } + + @Override + public void flushBuffer() { + throw new UnsupportedOperationException(); + } + + @Override + public int getBufferSize() { + throw new UnsupportedOperationException(); + } + + @Override + public String getCharacterEncoding() { + return UTF_8.name(); + } + + @Override + public String getContentType() { + return null; + } + + @Override + public Locale getLocale() { + return Locale.US; + } + + @Override + public ServletOutputStream getOutputStream() { + throw new UnsupportedOperationException(); + } + + @Override + public PrintWriter getWriter() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isCommitted() { + return false; + } + + @Override + public void reset() { + throw new UnsupportedOperationException(); + } + + @Override + public void resetBuffer() { + throw new UnsupportedOperationException(); + } + + @Override + public void setBufferSize(int sz) { + throw new UnsupportedOperationException(); + } + + @Override + public void setCharacterEncoding(String name) { + throw new UnsupportedOperationException(); + } + + @Override + public void setContentLength(int length) { + throw new UnsupportedOperationException(); + } + + @Override + public void setContentType(String type) { + throw new UnsupportedOperationException(); + } + + @Override + public void setLocale(Locale locale) { + throw new UnsupportedOperationException(); + } + + @Override + public void addCookie(Cookie cookie) { + throw new UnsupportedOperationException(); + } + + @Override + public void addDateHeader(String name, long value) { + throw new UnsupportedOperationException(); + } + + @Override + public void addHeader(String name, String value) { + throw new UnsupportedOperationException(); + } + + @Override + public void addIntHeader(String name, int value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsHeader(String name) { + return false; + } + + @Override + public String encodeRedirectURL(String url) { + throw new UnsupportedOperationException(); + } + + @Override + @Deprecated + public String encodeRedirectUrl(String url) { + throw new UnsupportedOperationException(); + } + + @Override + public String encodeURL(String url) { + throw new UnsupportedOperationException(); + } + + @Override + @Deprecated + public String encodeUrl(String url) { + throw new UnsupportedOperationException(); + } + + @Override + public void sendError(int sc) { + status = sc; + } + + @Override + public void sendError(int sc, String msg) { + status = sc; + } + + @Override + public void sendRedirect(String msg) { + status = SC_FOUND; + } + + @Override + public void setDateHeader(String name, long value) { + throw new UnsupportedOperationException(); + } + + @Override + public void setHeader(String name, String value) { + throw new UnsupportedOperationException(); + } + + @Override + public void setIntHeader(String name, int value) { + throw new UnsupportedOperationException(); + } + + @Override + public void setStatus(int sc) { + status = sc; + } + + @Override + @Deprecated + public void setStatus(int sc, String msg) { + status = sc; + } + + public int getStatus() { + return status; + } +}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/GitilesFilterTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/GitilesFilterTest.java new file mode 100644 index 0000000..0d32dd4 --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/GitilesFilterTest.java
@@ -0,0 +1,171 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import static com.google.gitiles.GitilesFilter.REPO_PATH_REGEX; +import static com.google.gitiles.GitilesFilter.REPO_REGEX; +import static com.google.gitiles.GitilesFilter.ROOT_REGEX; + +import junit.framework.TestCase; + +import java.util.regex.Matcher; + +/** Tests for the Gitiles filter. */ +public class GitilesFilterTest extends TestCase { + public void testRootUrls() throws Exception { + assertFalse(ROOT_REGEX.matcher("").matches()); + assertFalse(ROOT_REGEX.matcher("/foo").matches()); + assertFalse(ROOT_REGEX.matcher("/foo/").matches()); + assertFalse(ROOT_REGEX.matcher("/foo/ ").matches()); + assertFalse(ROOT_REGEX.matcher("/foo/+").matches()); + assertFalse(ROOT_REGEX.matcher("/foo/+").matches()); + assertFalse(ROOT_REGEX.matcher("/foo/ /").matches()); + assertFalse(ROOT_REGEX.matcher("/foo/+/").matches()); + assertFalse(ROOT_REGEX.matcher("/foo/+/bar").matches()); + Matcher m; + + m = ROOT_REGEX.matcher("/"); + assertTrue(m.matches()); + assertEquals("/", m.group(0)); + assertEquals(m.group(0), m.group(1)); + assertEquals("/", m.group(2)); + assertEquals("", m.group(3)); + assertEquals("", m.group(4)); + + m = ROOT_REGEX.matcher("//"); + assertTrue(m.matches()); + assertEquals("//", m.group(0)); + assertEquals(m.group(0), m.group(1)); + assertEquals("/", m.group(2)); + assertEquals("", m.group(3)); + assertEquals("", m.group(4)); + } + + public void testRepoUrls() throws Exception { + assertFalse(REPO_REGEX.matcher("").matches()); + + // These match the regex but are served by the root regex binder, which is + // matched first. + assertTrue(REPO_REGEX.matcher("/").matches()); + assertTrue(REPO_REGEX.matcher("//").matches()); + + assertFalse(REPO_REGEX.matcher("/foo/+").matches()); + assertFalse(REPO_REGEX.matcher("/foo/bar/+").matches()); + assertFalse(REPO_REGEX.matcher("/foo/bar/+/").matches()); + assertFalse(REPO_REGEX.matcher("/foo/bar/+/baz").matches()); + Matcher m; + + m = REPO_REGEX.matcher("/foo"); + assertTrue(m.matches()); + assertEquals("/foo", m.group(0)); + assertEquals(m.group(0), m.group(1)); + assertEquals("/foo", m.group(2)); + assertEquals("", m.group(3)); + assertEquals("", m.group(4)); + + m = REPO_REGEX.matcher("/foo/"); + assertTrue(m.matches()); + assertEquals("/foo/", m.group(0)); + assertEquals(m.group(0), m.group(1)); + assertEquals("/foo", m.group(2)); + assertEquals("", m.group(3)); + assertEquals("", m.group(4)); + + m = REPO_REGEX.matcher("/foo/bar"); + assertTrue(m.matches()); + assertEquals("/foo/bar", m.group(0)); + assertEquals(m.group(0), m.group(1)); + assertEquals("/foo/bar", m.group(2)); + assertEquals("", m.group(3)); + assertEquals("", m.group(4)); + + m = REPO_REGEX.matcher("/foo/bar+baz"); + assertTrue(m.matches()); + assertEquals("/foo/bar+baz", m.group(0)); + assertEquals(m.group(0), m.group(1)); + assertEquals("/foo/bar+baz", m.group(2)); + assertEquals("", m.group(3)); + assertEquals("", m.group(4)); + } + + public void testRepoPathUrls() throws Exception { + assertFalse(REPO_PATH_REGEX.matcher("").matches()); + assertFalse(REPO_PATH_REGEX.matcher("/").matches()); + assertFalse(REPO_PATH_REGEX.matcher("//").matches()); + assertFalse(REPO_PATH_REGEX.matcher("/foo").matches()); + assertFalse(REPO_PATH_REGEX.matcher("/foo/ ").matches()); + assertFalse(REPO_PATH_REGEX.matcher("/foo/ /").matches()); + assertFalse(REPO_PATH_REGEX.matcher("/foo/ /bar").matches()); + assertFalse(REPO_PATH_REGEX.matcher("/foo/bar").matches()); + assertFalse(REPO_PATH_REGEX.matcher("/foo/bar+baz").matches()); + Matcher m; + + m = REPO_PATH_REGEX.matcher("/foo/+"); + assertTrue(m.matches()); + assertEquals("/foo/+", m.group(0)); + assertEquals(m.group(0), m.group(1)); + assertEquals("/foo", m.group(2)); + assertEquals("+", m.group(3)); + assertEquals("", m.group(4)); + + m = REPO_PATH_REGEX.matcher("/foo/+/"); + assertTrue(m.matches()); + assertEquals("/foo/+/", m.group(0)); + assertEquals(m.group(0), m.group(1)); + assertEquals("/foo", m.group(2)); + assertEquals("+", m.group(3)); + assertEquals("/", m.group(4)); + + m = REPO_PATH_REGEX.matcher("/foo/+/bar/baz"); + assertTrue(m.matches()); + assertEquals("/foo/+/bar/baz", m.group(0)); + assertEquals(m.group(0), m.group(1)); + assertEquals("/foo", m.group(2)); + assertEquals("+", m.group(3)); + assertEquals("/bar/baz", m.group(4)); + + m = REPO_PATH_REGEX.matcher("/foo/+/bar/baz/"); + assertTrue(m.matches()); + assertEquals("/foo/+/bar/baz/", m.group(0)); + assertEquals(m.group(0), m.group(1)); + assertEquals("/foo", m.group(2)); + assertEquals("+", m.group(3)); + assertEquals("/bar/baz/", m.group(4)); + + m = REPO_PATH_REGEX.matcher("/foo/+/bar baz"); + assertTrue(m.matches()); + assertEquals("/foo/+/bar baz", m.group(0)); + assertEquals(m.group(0), m.group(1)); + assertEquals("/foo", m.group(2)); + assertEquals("+", m.group(3)); + assertEquals("/bar baz", m.group(4)); + + m = REPO_PATH_REGEX.matcher("/foo/+/bar/+/baz"); + assertTrue(m.matches()); + assertEquals("/foo/+/bar/+/baz", m.group(0)); + assertEquals(m.group(0), m.group(1)); + assertEquals("/foo", m.group(2)); + assertEquals("+", m.group(3)); + assertEquals("/bar/+/baz", m.group(4)); + + m = REPO_PATH_REGEX.matcher("/foo/+bar/baz"); + assertTrue(m.matches()); + assertEquals("/foo/+bar/baz", m.group(0)); + assertEquals(m.group(0), m.group(1)); + assertEquals("/foo", m.group(2)); + assertEquals("+bar", m.group(3)); + assertEquals("/baz", m.group(4)); + } +}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/GitilesUrlsTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/GitilesUrlsTest.java new file mode 100644 index 0000000..4c2ff8b --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/GitilesUrlsTest.java
@@ -0,0 +1,48 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import static com.google.gitiles.GitilesUrls.NAME_ESCAPER; + +import junit.framework.TestCase; + +/** Unit tests for {@link GitilesUrls}. */ +public class GitilesUrlsTest extends TestCase { + public void testNameEscaperEscapesAppropriateSpecialCharacters() throws Exception { + assertEquals("foo_bar", NAME_ESCAPER.apply("foo_bar")); + assertEquals("foo-bar", NAME_ESCAPER.apply("foo-bar")); + assertEquals("foo%25bar", NAME_ESCAPER.apply("foo%bar")); + assertEquals("foo%26bar", NAME_ESCAPER.apply("foo&bar")); + assertEquals("foo%28bar", NAME_ESCAPER.apply("foo(bar")); + assertEquals("foo%29bar", NAME_ESCAPER.apply("foo)bar")); + assertEquals("foo%3Abar", NAME_ESCAPER.apply("foo:bar")); + assertEquals("foo%3Bbar", NAME_ESCAPER.apply("foo;bar")); + assertEquals("foo%3Dbar", NAME_ESCAPER.apply("foo=bar")); + assertEquals("foo%3Fbar", NAME_ESCAPER.apply("foo?bar")); + assertEquals("foo%5Bbar", NAME_ESCAPER.apply("foo[bar")); + assertEquals("foo%5Dbar", NAME_ESCAPER.apply("foo]bar")); + assertEquals("foo%7Bbar", NAME_ESCAPER.apply("foo{bar")); + assertEquals("foo%7Dbar", NAME_ESCAPER.apply("foo}bar")); + } + public void testNameEscaperDoesNotEscapeSlashes() throws Exception { + assertEquals("foo/bar", NAME_ESCAPER.apply("foo/bar")); + } + + public void testNameEscaperEscapesSpacesWithPercentInsteadOfPlus() throws Exception { + assertEquals("foo+bar", NAME_ESCAPER.apply("foo+bar")); + assertEquals("foo%20bar", NAME_ESCAPER.apply("foo bar")); + assertEquals("foo%2520bar", NAME_ESCAPER.apply("foo%20bar")); + } +}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/GitilesViewTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/GitilesViewTest.java new file mode 100644 index 0000000..a8a5c4f --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/GitilesViewTest.java
@@ -0,0 +1,504 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.gitiles.GitilesView.Type; + +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.ObjectId; + +/** Tests for Gitiles views. */ +public class GitilesViewTest extends TestCase { + private static final GitilesView HOST = GitilesView.hostIndex() + .setServletPath("/b") + .setHostName("host") + .build(); + + public void testEmptyServletPath() throws Exception { + GitilesView view = GitilesView.hostIndex() + .setServletPath("") + .setHostName("host") + .build(); + assertEquals("", view.getServletPath()); + assertEquals(Type.HOST_INDEX, view.getType()); + assertEquals("host", view.getHostName()); + assertNull(view.getRepositoryName()); + assertEquals(Revision.NULL, view.getRevision()); + assertNull(view.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/?format=HTML", view.toUrl()); + assertEquals(ImmutableList.of(ImmutableMap.of("text", "host", "url", "/?format=HTML")), + view.getBreadcrumbs()); + } + + public void testHostIndex() throws Exception { + assertEquals("/b", HOST.getServletPath()); + assertEquals(Type.HOST_INDEX, HOST.getType()); + assertEquals("host", HOST.getHostName()); + assertNull(HOST.getRepositoryName()); + assertEquals(Revision.NULL, HOST.getRevision()); + assertNull(HOST.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/b/?format=HTML", HOST.toUrl()); + assertEquals(ImmutableList.of(ImmutableMap.of("text", "host", "url", "/b/?format=HTML")), + HOST.getBreadcrumbs()); + } + + public void testQueryParams() throws Exception { + GitilesView view = GitilesView.hostIndex().copyFrom(HOST) + .putParam("foo", "foovalue") + .putParam("bar", "barvalue") + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.HOST_INDEX, view.getType()); + assertEquals("host", view.getHostName()); + assertNull(view.getRepositoryName()); + assertEquals(Revision.NULL, view.getRevision()); + assertNull(view.getTreePath()); + assertEquals( + ImmutableListMultimap.of( + "foo", "foovalue", + "bar", "barvalue"), + view.getParameters()); + + assertEquals("/b/?format=HTML&foo=foovalue&bar=barvalue", view.toUrl()); + assertEquals(ImmutableList.of(ImmutableMap.of("text", "host", "url", "/b/?format=HTML")), + view.getBreadcrumbs()); + } + + public void testQueryParamsNotCopied() throws Exception { + GitilesView view = GitilesView.hostIndex().copyFrom(HOST) + .putParam("foo", "foovalue") + .putParam("bar", "barvalue") + .build(); + GitilesView copy = GitilesView.hostIndex().copyFrom(view).build(); + assertFalse(view.getParameters().isEmpty()); + assertTrue(copy.getParameters().isEmpty()); + } + + public void testRepositoryIndex() throws Exception { + GitilesView view = GitilesView.repositoryIndex() + .copyFrom(HOST) + .setRepositoryName("foo/bar") + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.REPOSITORY_INDEX, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo/bar", view.getRepositoryName()); + assertEquals(Revision.NULL, view.getRevision()); + assertNull(view.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/b/foo/bar/", view.toUrl()); + assertEquals( + ImmutableList.of( + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo/bar", "/b/foo/bar/")), + view.getBreadcrumbs()); + } + + public void testRefWithRevision() throws Exception { + ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); + GitilesView view = GitilesView.revision() + .copyFrom(HOST) + .setRepositoryName("foo/bar") + .setRevision(Revision.unpeeled("master", id)) + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.REVISION, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo/bar", view.getRepositoryName()); + assertEquals(id, view.getRevision().getId()); + assertEquals("master", view.getRevision().getName()); + assertNull(view.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/b/foo/bar/+show/master", view.toUrl()); + assertEquals( + ImmutableList.of( + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo/bar", "/b/foo/bar/"), + breadcrumb("master", "/b/foo/bar/+show/master")), + view.getBreadcrumbs()); + } + + public void testNoPathComponents() throws Exception { + ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); + GitilesView view = GitilesView.path() + .copyFrom(HOST) + .setRepositoryName("foo/bar") + .setRevision(Revision.unpeeled("master", id)) + .setTreePath("/") + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.PATH, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo/bar", view.getRepositoryName()); + assertEquals(id, view.getRevision().getId()); + assertEquals("master", view.getRevision().getName()); + assertEquals("", view.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/b/foo/bar/+/master/", view.toUrl()); + assertEquals( + ImmutableList.of( + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo/bar", "/b/foo/bar/"), + breadcrumb("master", "/b/foo/bar/+show/master"), + breadcrumb(".", "/b/foo/bar/+/master/")), + view.getBreadcrumbs()); + } + + public void testOnePathComponent() throws Exception { + ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); + GitilesView view = GitilesView.path() + .copyFrom(HOST) + .setRepositoryName("foo/bar") + .setRevision(Revision.unpeeled("master", id)) + .setTreePath("/file") + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.PATH, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo/bar", view.getRepositoryName()); + assertEquals(id, view.getRevision().getId()); + assertEquals("master", view.getRevision().getName()); + assertEquals("file", view.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/b/foo/bar/+/master/file", view.toUrl()); + assertEquals( + ImmutableList.of( + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo/bar", "/b/foo/bar/"), + breadcrumb("master", "/b/foo/bar/+show/master"), + breadcrumb(".", "/b/foo/bar/+/master/"), + breadcrumb("file", "/b/foo/bar/+/master/file")), + view.getBreadcrumbs()); + } + + public void testMultiplePathComponents() throws Exception { + ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); + GitilesView view = GitilesView.path() + .copyFrom(HOST) + .setRepositoryName("foo/bar") + .setRevision(Revision.unpeeled("master", id)) + .setTreePath("/path/to/a/file") + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.PATH, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo/bar", view.getRepositoryName()); + assertEquals(id, view.getRevision().getId()); + assertEquals("master", view.getRevision().getName()); + assertEquals("path/to/a/file", view.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/b/foo/bar/+/master/path/to/a/file", view.toUrl()); + assertEquals( + ImmutableList.of( + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo/bar", "/b/foo/bar/"), + breadcrumb("master", "/b/foo/bar/+show/master"), + breadcrumb(".", "/b/foo/bar/+/master/"), + breadcrumb("path", "/b/foo/bar/+/master/path"), + breadcrumb("to", "/b/foo/bar/+/master/path/to"), + breadcrumb("a", "/b/foo/bar/+/master/path/to/a"), + breadcrumb("file", "/b/foo/bar/+/master/path/to/a/file")), + view.getBreadcrumbs()); + } + + public void testDiffAgainstFirstParent() throws Exception { + ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); + ObjectId parent = ObjectId.fromString("efab5678efab5678efab5678efab5678efab5678"); + GitilesView view = GitilesView.diff() + .copyFrom(HOST) + .setRepositoryName("foo/bar") + .setRevision(Revision.unpeeled("master", id)) + .setOldRevision(Revision.unpeeled("master^", parent)) + .setTreePath("/path/to/a/file") + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.DIFF, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo/bar", view.getRepositoryName()); + assertEquals(id, view.getRevision().getId()); + assertEquals("master", view.getRevision().getName()); + assertEquals("master^", view.getOldRevision().getName()); + assertEquals("path/to/a/file", view.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/b/foo/bar/+/master%5E%21/path/to/a/file", view.toUrl()); + assertEquals( + ImmutableList.of( + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo/bar", "/b/foo/bar/"), + breadcrumb("master^!", "/b/foo/bar/+/master%5E%21/"), + breadcrumb(".", "/b/foo/bar/+/master%5E%21/"), + breadcrumb("path", "/b/foo/bar/+/master%5E%21/path"), + breadcrumb("to", "/b/foo/bar/+/master%5E%21/path/to"), + breadcrumb("a", "/b/foo/bar/+/master%5E%21/path/to/a"), + breadcrumb("file", "/b/foo/bar/+/master%5E%21/path/to/a/file")), + view.getBreadcrumbs()); + } + + public void testDiffAgainstEmptyRevision() throws Exception { + ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); + GitilesView view = GitilesView.diff() + .copyFrom(HOST) + .setRepositoryName("foo/bar") + .setRevision(Revision.unpeeled("master", id)) + .setTreePath("/path/to/a/file") + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.DIFF, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo/bar", view.getRepositoryName()); + assertEquals(id, view.getRevision().getId()); + assertEquals("master", view.getRevision().getName()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertEquals("path/to/a/file", view.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/b/foo/bar/+/master%5E%21/path/to/a/file", view.toUrl()); + assertEquals( + ImmutableList.of( + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo/bar", "/b/foo/bar/"), + breadcrumb("master^!", "/b/foo/bar/+/master%5E%21/"), + breadcrumb(".", "/b/foo/bar/+/master%5E%21/"), + breadcrumb("path", "/b/foo/bar/+/master%5E%21/path"), + breadcrumb("to", "/b/foo/bar/+/master%5E%21/path/to"), + breadcrumb("a", "/b/foo/bar/+/master%5E%21/path/to/a"), + breadcrumb("file", "/b/foo/bar/+/master%5E%21/path/to/a/file")), + view.getBreadcrumbs()); + } + + public void testDiffAgainstOther() throws Exception { + ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); + ObjectId other = ObjectId.fromString("efab5678efab5678efab5678efab5678efab5678"); + GitilesView view = GitilesView.diff() + .copyFrom(HOST) + .setRepositoryName("foo/bar") + .setRevision(Revision.unpeeled("master", id)) + .setOldRevision(Revision.unpeeled("efab5678", other)) + .setTreePath("/path/to/a/file") + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.DIFF, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo/bar", view.getRepositoryName()); + assertEquals(id, view.getRevision().getId()); + assertEquals("master", view.getRevision().getName()); + assertEquals("efab5678", view.getOldRevision().getName()); + assertEquals("path/to/a/file", view.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/b/foo/bar/+/efab5678..master/path/to/a/file", view.toUrl()); + assertEquals( + ImmutableList.of( + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo/bar", "/b/foo/bar/"), + breadcrumb("efab5678..master", "/b/foo/bar/+/efab5678..master/"), + breadcrumb(".", "/b/foo/bar/+/efab5678..master/"), + breadcrumb("path", "/b/foo/bar/+/efab5678..master/path"), + breadcrumb("to", "/b/foo/bar/+/efab5678..master/path/to"), + breadcrumb("a", "/b/foo/bar/+/efab5678..master/path/to/a"), + breadcrumb("file", "/b/foo/bar/+/efab5678..master/path/to/a/file")), + view.getBreadcrumbs()); + } + + public void testBranchLogWithoutPath() throws Exception { + ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); + GitilesView view = GitilesView.log() + .copyFrom(HOST) + .setRepositoryName("foo/bar") + .setRevision(Revision.unpeeled("master", id)) + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.LOG, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo/bar", view.getRepositoryName()); + assertEquals(id, view.getRevision().getId()); + assertEquals("master", view.getRevision().getName()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertNull(view.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/b/foo/bar/+/master", view.toUrl()); + assertEquals( + ImmutableList.of( + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo/bar", "/b/foo/bar/"), + breadcrumb("master", "/b/foo/bar/+/master")), + view.getBreadcrumbs()); + } + + public void testIdLogWithoutPath() throws Exception { + ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); + GitilesView view = GitilesView.log() + .copyFrom(HOST) + .setRepositoryName("foo/bar") + .setRevision(Revision.unpeeled("abcd1234", id)) + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.LOG, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo/bar", view.getRepositoryName()); + assertEquals(id, view.getRevision().getId()); + assertEquals("abcd1234", view.getRevision().getName()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertNull(view.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/b/foo/bar/+log/abcd1234", view.toUrl()); + assertEquals( + ImmutableList.of( + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo/bar", "/b/foo/bar/"), + breadcrumb("abcd1234", "/b/foo/bar/+log/abcd1234")), + view.getBreadcrumbs()); + } + + public void testLogWithoutOldRevision() throws Exception { + ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); + GitilesView view = GitilesView.log() + .copyFrom(HOST) + .setRepositoryName("foo/bar") + .setRevision(Revision.unpeeled("master", id)) + .setTreePath("/path/to/a/file") + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.LOG, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo/bar", view.getRepositoryName()); + assertEquals(id, view.getRevision().getId()); + assertEquals("master", view.getRevision().getName()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertEquals("path/to/a/file", view.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/b/foo/bar/+log/master/path/to/a/file", view.toUrl()); + assertEquals( + ImmutableList.of( + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo/bar", "/b/foo/bar/"), + breadcrumb("master", "/b/foo/bar/+/master"), + breadcrumb("path", "/b/foo/bar/+log/master/path"), + breadcrumb("to", "/b/foo/bar/+log/master/path/to"), + breadcrumb("a", "/b/foo/bar/+log/master/path/to/a"), + breadcrumb("file", "/b/foo/bar/+log/master/path/to/a/file")), + view.getBreadcrumbs()); + } + + public void testLogWithOldRevision() throws Exception { + ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); + ObjectId parent = ObjectId.fromString("efab5678efab5678efab5678efab5678efab5678"); + GitilesView view = GitilesView.log() + .copyFrom(HOST) + .setRepositoryName("foo/bar") + .setRevision(Revision.unpeeled("master", id)) + .setOldRevision(Revision.unpeeled("master^", parent)) + .setTreePath("/path/to/a/file") + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.LOG, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo/bar", view.getRepositoryName()); + assertEquals(id, view.getRevision().getId()); + assertEquals("master", view.getRevision().getName()); + assertEquals("master^", view.getOldRevision().getName()); + assertEquals("path/to/a/file", view.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/b/foo/bar/+log/master%5E..master/path/to/a/file", view.toUrl()); + assertEquals( + ImmutableList.of( + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo/bar", "/b/foo/bar/"), + breadcrumb("master^..master", "/b/foo/bar/+log/master%5E..master"), + breadcrumb("path", "/b/foo/bar/+log/master%5E..master/path"), + breadcrumb("to", "/b/foo/bar/+log/master%5E..master/path/to"), + breadcrumb("a", "/b/foo/bar/+log/master%5E..master/path/to/a"), + breadcrumb("file", "/b/foo/bar/+log/master%5E..master/path/to/a/file")), + view.getBreadcrumbs()); + } + + public void testEscaping() throws Exception { + ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); + ObjectId parent = ObjectId.fromString("efab5678efab5678efab5678efab5678efab5678"); + // Some of these values are not valid for Git, but check them anyway. + GitilesView view = GitilesView.log() + .copyFrom(HOST) + .setRepositoryName("foo?bar") + .setRevision(Revision.unpeeled("ba/d#name", id)) + .setOldRevision(Revision.unpeeled("other\"na/me", parent)) + .setTreePath("we ird/pa'th/name") + .putParam("k e y", "val/ue") + .setAnchor("anc#hor") + .build(); + + // Fields returned by getters are not escaped. + assertEquals("/b", view.getServletPath()); + assertEquals(Type.LOG, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo?bar", view.getRepositoryName()); + assertEquals(id, view.getRevision().getId()); + assertEquals("ba/d#name", view.getRevision().getName()); + assertEquals(parent, view.getOldRevision().getId()); + assertEquals("other\"na/me", view.getOldRevision().getName()); + assertEquals("we ird/pa'th/name", view.getTreePath()); + assertEquals(ImmutableListMultimap.<String, String> of("k e y", "val/ue"), + view.getParameters()); + + assertEquals( + "/b/foo%3Fbar/+log/other%22na/me..ba/d%23name/we%20ird/pa%27th/name" + + "?k+e+y=val%2Fue#anc%23hor", view.toUrl()); + assertEquals( + ImmutableList.of( + // Names are not escaped (auto-escaped by Soy) but values are. + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo?bar", "/b/foo%3Fbar/"), + breadcrumb("other\"na/me..ba/d#name", "/b/foo%3Fbar/+log/other%22na/me..ba/d%23name"), + breadcrumb("we ird", "/b/foo%3Fbar/+log/other%22na/me..ba/d%23name/we%20ird"), + breadcrumb("pa'th", "/b/foo%3Fbar/+log/other%22na/me..ba/d%23name/we%20ird/pa%27th"), + breadcrumb("name", + "/b/foo%3Fbar/+log/other%22na/me..ba/d%23name/we%20ird/pa%27th/name")), + view.getBreadcrumbs()); + } + + private static ImmutableMap<String, String> breadcrumb(String text, String url) { + return ImmutableMap.of("text", text, "url", url); + } +}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/LinkifierTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/LinkifierTest.java new file mode 100644 index 0000000..6f6caf2 --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/LinkifierTest.java
@@ -0,0 +1,113 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +import junit.framework.TestCase; + +import javax.servlet.http.HttpServletRequest; + +/** Tests for {@link Linkifier}. */ +public class LinkifierTest extends TestCase { + private static final HttpServletRequest REQ = FakeHttpServletRequest.newRequest(); + + @Override + protected void setUp() throws Exception { + } + + public void testlinkifyMessageNoMatch() throws Exception { + Linkifier l = new Linkifier(TestGitilesUrls.URLS); + assertEquals(ImmutableList.of(ImmutableMap.of("text", "some message text")), + l.linkify(FakeHttpServletRequest.newRequest(), "some message text")); + } + + public void testlinkifyMessageUrl() throws Exception { + Linkifier l = new Linkifier(TestGitilesUrls.URLS); + assertEquals(ImmutableList.of( + ImmutableMap.of("text", "http://my/url", "url", "http://my/url")), + l.linkify(REQ, "http://my/url")); + assertEquals(ImmutableList.of( + ImmutableMap.of("text", "https://my/url", "url", "https://my/url")), + l.linkify(REQ, "https://my/url")); + assertEquals(ImmutableList.of( + ImmutableMap.of("text", "foo "), + ImmutableMap.of("text", "http://my/url", "url", "http://my/url"), + ImmutableMap.of("text", " bar")), + l.linkify(REQ, "foo http://my/url bar")); + assertEquals(ImmutableList.of( + ImmutableMap.of("text", "foo "), + ImmutableMap.of("text", "http://my/url", "url", "http://my/url"), + ImmutableMap.of("text", " bar "), + ImmutableMap.of("text", "http://my/other/url", "url", "http://my/other/url"), + ImmutableMap.of("text", " baz")), + l.linkify(REQ, "foo http://my/url bar http://my/other/url baz")); + } + + public void testlinkifyMessageChangeIdNoGerrit() throws Exception { + Linkifier l = new Linkifier(new GitilesUrls() { + @Override + public String getBaseGerritUrl(HttpServletRequest req) { + return null; + } + + @Override + public String getHostName(HttpServletRequest req) { + throw new UnsupportedOperationException(); + } + + @Override + public String getBaseGitUrl(HttpServletRequest req) { + throw new UnsupportedOperationException(); + } + }); + assertEquals(ImmutableList.of(ImmutableMap.of("text", "I0123456789")), + l.linkify(REQ, "I0123456789")); + assertEquals(ImmutableList.of(ImmutableMap.of("text", "Change-Id: I0123456789")), + l.linkify(REQ, "Change-Id: I0123456789")); + assertEquals(ImmutableList.of(ImmutableMap.of("text", "Change-Id: I0123456789 does not exist")), + l.linkify(REQ, "Change-Id: I0123456789 does not exist")); + } + + public void testlinkifyMessageChangeId() throws Exception { + Linkifier l = new Linkifier(TestGitilesUrls.URLS); + assertEquals(ImmutableList.of( + ImmutableMap.of("text", "I0123456789", + "url", "http://test-host-review/foo/#/q/I0123456789,n,z")), + l.linkify(REQ, "I0123456789")); + assertEquals(ImmutableList.of( + ImmutableMap.of("text", "Change-Id: "), + ImmutableMap.of("text", "I0123456789", + "url", "http://test-host-review/foo/#/q/I0123456789,n,z")), + l.linkify(REQ, "Change-Id: I0123456789")); + assertEquals(ImmutableList.of( + ImmutableMap.of("text", "Change-Id: "), + ImmutableMap.of("text", "I0123456789", + "url", "http://test-host-review/foo/#/q/I0123456789,n,z"), + ImmutableMap.of("text", " exists")), + l.linkify(REQ, "Change-Id: I0123456789 exists")); + } + + public void testlinkifyMessageUrlAndChangeId() throws Exception { + Linkifier l = new Linkifier(TestGitilesUrls.URLS); + assertEquals(ImmutableList.of( + ImmutableMap.of("text", "http://my/url/I0123456789", "url", "http://my/url/I0123456789"), + ImmutableMap.of("text", " is not change "), + ImmutableMap.of("text", "I0123456789", + "url", "http://test-host-review/foo/#/q/I0123456789,n,z")), + l.linkify(REQ, "http://my/url/I0123456789 is not change I0123456789")); + } +}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/PaginatorTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/PaginatorTest.java new file mode 100644 index 0000000..09661f2 --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/PaginatorTest.java
@@ -0,0 +1,155 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import junit.framework.TestCase; + +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.storage.dfs.DfsRepository; +import org.eclipse.jgit.storage.dfs.DfsRepositoryDescription; +import org.eclipse.jgit.storage.dfs.InMemoryRepository; + +import java.util.List; + +/** Unit tests for {@link LogServlet}. */ +public class PaginatorTest extends TestCase { + private TestRepository<DfsRepository> repo; + private RevWalk walk; + + @Override + protected void setUp() throws Exception { + repo = new TestRepository<DfsRepository>( + new InMemoryRepository(new DfsRepositoryDescription("test"))); + walk = new RevWalk(repo.getRepository()); + } + + @Override + protected void tearDown() throws Exception { + walk.release(); + } + + public void testStart() throws Exception { + List<RevCommit> commits = linearCommits(10); + walk.markStart(commits.get(9)); + Paginator p = new Paginator(walk, 3, commits.get(9)); + assertEquals( + ImmutableList.of( + commits.get(9), + commits.get(8), + commits.get(7)), + ImmutableList.copyOf(p)); + assertNull(p.getPreviousStart()); + assertEquals(commits.get(6), p.getNextStart()); + } + + public void testNoStartCommit() throws Exception { + List<RevCommit> commits = linearCommits(10); + walk.markStart(commits.get(9)); + Paginator p = new Paginator(walk, 3, null); + assertEquals( + ImmutableList.of( + commits.get(9), + commits.get(8), + commits.get(7)), + ImmutableList.copyOf(p)); + assertNull(p.getPreviousStart()); + assertEquals(commits.get(6), p.getNextStart()); + } + + public void testLessThanOnePageIn() throws Exception { + List<RevCommit> commits = linearCommits(10); + walk.markStart(commits.get(9)); + Paginator p = new Paginator(walk, 3, commits.get(8)); + assertEquals( + ImmutableList.of( + commits.get(8), + commits.get(7), + commits.get(6)), + ImmutableList.copyOf(p)); + assertEquals(commits.get(9), p.getPreviousStart()); + assertEquals(commits.get(5), p.getNextStart()); + } + + public void testAtLeastOnePageIn() throws Exception { + List<RevCommit> commits = linearCommits(10); + walk.markStart(commits.get(9)); + Paginator p = new Paginator(walk, 3, commits.get(7)); + assertEquals( + ImmutableList.of( + commits.get(7), + commits.get(6), + commits.get(5)), + ImmutableList.copyOf(p)); + assertEquals(commits.get(9), p.getPreviousStart()); + assertEquals(commits.get(4), p.getNextStart()); + } + + public void testEnd() throws Exception { + List<RevCommit> commits = linearCommits(10); + walk.markStart(commits.get(9)); + Paginator p = new Paginator(walk, 3, commits.get(2)); + assertEquals( + ImmutableList.of( + commits.get(2), + commits.get(1), + commits.get(0)), + ImmutableList.copyOf(p)); + assertEquals(commits.get(5), p.getPreviousStart()); + assertNull(p.getNextStart()); + } + + public void testOnePastEnd() throws Exception { + List<RevCommit> commits = linearCommits(10); + walk.markStart(commits.get(9)); + Paginator p = new Paginator(walk, 3, commits.get(1)); + assertEquals( + ImmutableList.of( + commits.get(1), + commits.get(0)), + ImmutableList.copyOf(p)); + assertEquals(commits.get(4), p.getPreviousStart()); + assertNull(p.getNextStart()); + } + + public void testManyPastEnd() throws Exception { + List<RevCommit> commits = linearCommits(10); + walk.markStart(commits.get(9)); + Paginator p = new Paginator(walk, 5, commits.get(1)); + assertEquals( + ImmutableList.of( + commits.get(1), + commits.get(0)), + ImmutableList.copyOf(p)); + assertEquals(commits.get(6), p.getPreviousStart()); + assertNull(p.getNextStart()); + } + + private List<RevCommit> linearCommits(int n) throws Exception { + checkArgument(n > 0); + List<RevCommit> commits = Lists.newArrayList(); + commits.add(repo.commit().create()); + for (int i = 1; i < 10; i++) { + commits.add(repo.commit().parent(commits.get(commits.size() - 1)).create()); + } + return commits; + } +}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/RepositoryIndexServletTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/RepositoryIndexServletTest.java new file mode 100644 index 0000000..3071f70 --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/RepositoryIndexServletTest.java
@@ -0,0 +1,132 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import static com.google.gitiles.TestGitilesUrls.URLS; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +import junit.framework.TestCase; + +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.storage.dfs.DfsRepository; +import org.eclipse.jgit.storage.dfs.DfsRepositoryDescription; +import org.eclipse.jgit.storage.dfs.InMemoryRepository; + +import java.io.IOException; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +/** Tests for {@link RepositoryIndexServlet}. */ +public class RepositoryIndexServletTest extends TestCase { + private TestRepository<DfsRepository> repo; + private RepositoryIndexServlet servlet; + + @Override + protected void setUp() throws Exception { + repo = new TestRepository<DfsRepository>( + new InMemoryRepository(new DfsRepositoryDescription("test"))); + servlet = new RepositoryIndexServlet( + new DefaultRenderer(), + new TestGitilesAccess(repo.getRepository())); + } + + public void testEmpty() throws Exception { + Map<String, ?> data = buildData(); + assertEquals(ImmutableList.of(), data.get("branches")); + assertEquals(ImmutableList.of(), data.get("tags")); + } + + public void testBranchesAndTags() throws Exception { + repo.branch("refs/heads/foo").commit().create(); + repo.branch("refs/heads/bar").commit().create(); + repo.branch("refs/tags/baz").commit().create(); + repo.branch("refs/nope/quux").commit().create(); + Map<String, ?> data = buildData(); + + assertEquals( + ImmutableList.of( + ref("/b/test/+/bar", "bar"), + ref("/b/test/+/foo", "foo")), + data.get("branches")); + assertEquals( + ImmutableList.of( + ref("/b/test/+/baz", "baz")), + data.get("tags")); + } + + public void testAmbiguousBranch() throws Exception { + repo.branch("refs/heads/foo").commit().create(); + repo.branch("refs/heads/bar").commit().create(); + repo.branch("refs/tags/foo").commit().create(); + Map<String, ?> data = buildData(); + + assertEquals( + ImmutableList.of( + ref("/b/test/+/bar", "bar"), + ref("/b/test/+/refs/heads/foo", "foo")), + data.get("branches")); + assertEquals( + ImmutableList.of( + // refs/tags/ is searched before refs/heads/, so this does not + // appear ambiguous. + ref("/b/test/+/foo", "foo")), + data.get("tags")); + } + + public void testAmbiguousRelativeToNonBranchOrTag() throws Exception { + repo.branch("refs/foo").commit().create(); + repo.branch("refs/heads/foo").commit().create(); + repo.branch("refs/tags/foo").commit().create(); + Map<String, ?> data = buildData(); + + assertEquals( + ImmutableList.of( + ref("/b/test/+/refs/heads/foo", "foo")), + data.get("branches")); + assertEquals( + ImmutableList.of( + ref("/b/test/+/refs/tags/foo", "foo")), + data.get("tags")); + } + + public void testRefsHeads() throws Exception { + repo.branch("refs/heads/foo").commit().create(); + repo.branch("refs/heads/refs/heads/foo").commit().create(); + Map<String, ?> data = buildData(); + + assertEquals( + ImmutableList.of( + ref("/b/test/+/foo", "foo"), + ref("/b/test/+/refs/heads/refs/heads/foo", "refs/heads/foo")), + data.get("branches")); + } + + private Map<String, ?> buildData() throws IOException { + HttpServletRequest req = FakeHttpServletRequest.newRequest(repo.getRepository()); + ViewFilter.setView(req, GitilesView.repositoryIndex() + .setHostName(URLS.getHostName(req)) + .setServletPath(req.getServletPath()) + .setRepositoryName("test") + .build()); + return servlet.buildData(req); + } + + private Map<String, String> ref(String url, String name) { + return ImmutableMap.of("url", url, "name", name); + } +}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/RevisionParserTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/RevisionParserTest.java new file mode 100644 index 0000000..23033e0 --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/RevisionParserTest.java
@@ -0,0 +1,273 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; +import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT; +import static org.eclipse.jgit.lib.Constants.OBJ_TAG; + +import com.google.common.cache.CacheBuilder; +import com.google.gitiles.RevisionParser.Result; + +import junit.framework.TestCase; + +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.revwalk.RevBlob; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevTag; +import org.eclipse.jgit.storage.dfs.DfsRepository; +import org.eclipse.jgit.storage.dfs.DfsRepositoryDescription; +import org.eclipse.jgit.storage.dfs.InMemoryRepository; + +/** Tests for the revision parser. */ +public class RevisionParserTest extends TestCase { + private TestRepository<DfsRepository> repo; + private RevisionParser parser; + + @Override + protected void setUp() throws Exception { + repo = new TestRepository<DfsRepository>( + new InMemoryRepository(new DfsRepositoryDescription("test"))); + parser = new RevisionParser( + repo.getRepository(), + new TestGitilesAccess(repo.getRepository()).forRequest(null), + new VisibilityCache(false, CacheBuilder.newBuilder().maximumSize(0))); + } + + public void testParseRef() throws Exception { + RevCommit master = repo.branch("refs/heads/master").commit().create(); + assertEquals(new Result(Revision.peeled("master", master)), + parser.parse("master")); + assertEquals(new Result(Revision.peeled("refs/heads/master", master)), + parser.parse("refs/heads/master")); + assertNull(parser.parse("refs//heads//master")); + } + + public void testParseRefParentExpression() throws Exception { + RevCommit root = repo.commit().create(); + RevCommit parent1 = repo.commit().parent(root).create(); + RevCommit parent2 = repo.commit().parent(root).create(); + RevCommit merge = repo.branch("master").commit() + .parent(parent1) + .parent(parent2) + .create(); + assertEquals(new Result(Revision.peeled("master", merge)), parser.parse("master")); + assertEquals(new Result(Revision.peeled("master^", parent1)), parser.parse("master^")); + assertEquals(new Result(Revision.peeled("master~1", parent1)), parser.parse("master~1")); + assertEquals(new Result(Revision.peeled("master^2", parent2)), parser.parse("master^2")); + assertEquals(new Result(Revision.peeled("master~2", root)), parser.parse("master~2")); + } + + public void testParseCommitShaVisibleFromHead() throws Exception { + RevCommit parent = repo.commit().create(); + RevCommit commit = repo.branch("master").commit().parent(parent).create(); + assertEquals(new Result(Revision.peeled(commit.name(), commit)), parser.parse(commit.name())); + assertEquals(new Result(Revision.peeled(parent.name(), parent)), parser.parse(parent.name())); + + String abbrev = commit.name().substring(0, 6); + assertEquals(new Result(Revision.peeled(abbrev, commit)), parser.parse(abbrev)); + } + + public void testParseCommitShaVisibleFromTag() throws Exception { + RevCommit parent = repo.commit().create(); + RevCommit commit = repo.commit().parent(parent).create(); + repo.branch("master").commit().create(); + repo.update("refs/tags/tag", repo.tag("tag", commit)); + + assertEquals(new Result(Revision.peeled(commit.name(), commit)), parser.parse(commit.name())); + assertEquals(new Result(Revision.peeled(parent.name(), parent)), parser.parse(parent.name())); + } + + public void testParseCommitShaVisibleFromOther() throws Exception { + RevCommit parent = repo.commit().create(); + RevCommit commit = repo.commit().parent(parent).create(); + repo.branch("master").commit().create(); + repo.update("refs/tags/tag", repo.tag("tag", repo.commit().create())); + repo.update("refs/meta/config", commit); + + assertEquals(new Result(Revision.peeled(commit.name(), commit)), parser.parse(commit.name())); + assertEquals(new Result(Revision.peeled(parent.name(), parent)), parser.parse(parent.name())); + } + + public void testParseCommitShaVisibleFromChange() throws Exception { + RevCommit parent = repo.commit().create(); + RevCommit commit = repo.commit().parent(parent).create(); + repo.branch("master").commit().create(); + repo.update("refs/changes/01/0001", commit); + + // Matches exactly. + assertEquals(new Result(Revision.peeled(commit.name(), commit)), parser.parse(commit.name())); + // refs/changes/* is excluded from ancestry search. + assertEquals(null, parser.parse(parent.name())); + } + + public void testParseNonVisibleCommitSha() throws Exception { + RevCommit other = repo.commit().create(); + RevCommit master = repo.branch("master").commit().create(); + assertEquals(null, parser.parse(other.name())); + + repo.branch("other").update(other); + assertEquals(new Result(Revision.peeled(other.name(), other)), parser.parse(other.name())); + } + + public void testParseDiffRevisions() throws Exception { + RevCommit parent = repo.commit().create(); + RevCommit commit = repo.branch("master").commit().parent(parent).create(); + RevCommit other = repo.branch("other").commit().create(); + + assertEquals( + new Result( + Revision.peeled("master", commit), + Revision.peeled("master^", parent), + 15), + parser.parse("master^..master")); + assertEquals( + new Result( + Revision.peeled("master", commit), + Revision.peeled("master^", parent), + 15), + parser.parse("master^..master/")); + assertEquals( + new Result( + Revision.peeled("master", commit), + Revision.peeled("master^", parent), + 15), + parser.parse("master^..master/path/to/a/file")); + assertEquals( + new Result( + Revision.peeled("master", commit), + Revision.peeled("master^", parent), + 15), + parser.parse("master^..master/path/to/a/..file")); + assertEquals( + new Result( + Revision.peeled("refs/heads/master", commit), + Revision.peeled("refs/heads/master^", parent), + 37), + parser.parse("refs/heads/master^..refs/heads/master")); + assertEquals( + new Result( + Revision.peeled("master", commit), + Revision.peeled("master~1", parent), + 16), + parser.parse("master~1..master")); + // TODO(dborowitz): 2a2362fbb in JGit causes master~2 to resolve to master + // rather than null. Uncomment when upstream regression is fixed. + //assertNull(parser.parse("master~2..master")); + assertEquals( + new Result( + Revision.peeled("master", commit), + Revision.peeled("other", other), + 13), + parser.parse("other..master")); + } + + public void testParseFirstParentExpression() throws Exception { + RevCommit parent = repo.commit().create(); + RevCommit commit = repo.branch("master").commit().parent(parent).create(); + + assertEquals( + new Result( + Revision.peeled("master", commit), + Revision.peeled("master^", parent), + 8), + parser.parse("master^!")); + assertEquals( + new Result( + Revision.peeled("master^", parent), + Revision.NULL, + 9), + parser.parse("master^^!")); + assertEquals( + new Result( + Revision.peeled(parent.name(), parent), + Revision.NULL, + 42), + parser.parse(parent.name() + "^!")); + + RevTag tag = repo.update("refs/tags/tag", repo.tag("tag", commit)); + assertEquals( + new Result( + Revision.peeled("tag", commit), + Revision.peeled("tag^", parent), + 5), + parser.parse("tag^!")); + assertEquals( + new Result( + Revision.peeled("tag^", parent), + Revision.NULL, + 6), + parser.parse("tag^^!")); + } + + public void testNonVisibleDiffShas() throws Exception { + RevCommit other = repo.commit().create(); + RevCommit master = repo.branch("master").commit().create(); + assertEquals(null, parser.parse("other..master")); + assertEquals(null, parser.parse("master..other")); + + repo.branch("other").update(other); + assertEquals( + new Result( + Revision.peeled("master", master), + Revision.peeled("other", other), + 13), + parser.parse("other..master")); + assertEquals( + new Result( + Revision.peeled("other", other), + Revision.peeled("master", master), + 13), + parser.parse("master..other")); + } + + public void testParseTag() throws Exception { + RevCommit master = repo.branch("master").commit().create(); + RevTag masterTag = repo.update("refs/tags/master-tag", repo.tag("master-tag", master)); + RevTag masterTagTag = repo.update("refs/tags/master-tag-tag", + repo.tag("master-tag-tag", master)); + + assertEquals(new Result( + new Revision("master-tag", masterTag, OBJ_TAG, master, OBJ_COMMIT)), + parser.parse("master-tag")); + assertEquals(new Result( + new Revision("master-tag-tag", masterTagTag, OBJ_TAG, master, OBJ_COMMIT)), + parser.parse("master-tag-tag")); + + RevBlob blob = repo.update("refs/tags/blob", repo.blob("blob")); + RevTag blobTag = repo.update("refs/tags/blob-tag", repo.tag("blob-tag", blob)); + assertEquals(new Result(Revision.peeled("blob", blob)), parser.parse("blob")); + assertEquals(new Result(new Revision("blob-tag", blobTag, OBJ_TAG, blob, OBJ_BLOB)), + parser.parse("blob-tag")); + } + + public void testParseUnsupportedRevisionExpressions() throws Exception { + RevBlob blob = repo.blob("blob contents"); + RevCommit master = repo.branch("master").commit().add("blob", blob).create(); + + assertEquals(master, repo.getRepository().resolve("master^{}")); + assertEquals(null, parser.parse("master^{}")); + + assertEquals(master, repo.getRepository().resolve("master^{commit}")); + assertEquals(null, parser.parse("master^{commit}")); + + assertEquals(blob, repo.getRepository().resolve("master:blob")); + assertEquals(null, parser.parse("master:blob")); + + // TestRepository has no simple way of setting the reflog. + //assertEquals(null, repo.getRepository().resolve("master@{0}")); + assertEquals(null, parser.parse("master@{0}")); + } +}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/TestGitilesAccess.java b/gitiles-servlet/src/test/java/com/google/gitiles/TestGitilesAccess.java new file mode 100644 index 0000000..0b31e24 --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/TestGitilesAccess.java
@@ -0,0 +1,64 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.eclipse.jgit.storage.dfs.DfsRepository; + +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +/** Gitiles access for testing. */ +public class TestGitilesAccess implements GitilesAccess.Factory { + private final DfsRepository repo; + + public TestGitilesAccess(DfsRepository repo) { + this.repo = checkNotNull(repo); + } + + @Override + public GitilesAccess forRequest(final HttpServletRequest req) { + return new GitilesAccess() { + @Override + public Map<String, RepositoryDescription> listRepositories(Set<String> branches) { + // TODO(dborowitz): Implement this, using the DfsRepositoryDescriptions to + // get the repository names. + throw new UnsupportedOperationException(); + } + + @Override + public Object getUserKey() { + return "a user"; + } + + @Override + public String getRepositoryName() { + return repo.getDescription().getRepositoryName(); + } + + @Override + public RepositoryDescription getRepositoryDescription() { + RepositoryDescription d = new RepositoryDescription(); + d.name = getRepositoryName(); + d.description = "a test data set"; + d.cloneUrl = TestGitilesUrls.URLS.getBaseGitUrl(req) + "/" + d.name; + return d; + } + }; + } +}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/TestGitilesUrls.java b/gitiles-servlet/src/test/java/com/google/gitiles/TestGitilesUrls.java new file mode 100644 index 0000000..f8a0883 --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/TestGitilesUrls.java
@@ -0,0 +1,40 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import javax.servlet.http.HttpServletRequest; + +/** {@link GitilesUrls} for testing. */ +public class TestGitilesUrls implements GitilesUrls { + public static final GitilesUrls URLS = new TestGitilesUrls(); + + @Override + public String getHostName(HttpServletRequest req) { + return "test-host"; + } + + @Override + public String getBaseGitUrl(HttpServletRequest req) { + return "git://test-host/foo"; + } + + @Override + public String getBaseGerritUrl(HttpServletRequest req) { + return "http://test-host-review/foo/"; + } + + private TestGitilesUrls() { + } +}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/TreeSoyDataTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/TreeSoyDataTest.java new file mode 100644 index 0000000..85423d9 --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/TreeSoyDataTest.java
@@ -0,0 +1,61 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import static com.google.gitiles.TreeSoyData.getTargetDisplayName; +import static com.google.gitiles.TreeSoyData.resolveTargetUrl; + +import com.google.common.base.Strings; + +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.ObjectId; + +/** Tests for {@link TreeSoyData}. */ +public class TreeSoyDataTest extends TestCase { + public void testGetTargetDisplayName() throws Exception { + assertEquals("foo", getTargetDisplayName("foo")); + assertEquals("foo/bar", getTargetDisplayName("foo/bar")); + assertEquals("a/a/a/a/a/a/a/a/a/a/bar", + getTargetDisplayName(Strings.repeat("a/", 10) + "bar")); + assertEquals("a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/bar", + getTargetDisplayName(Strings.repeat("a/", 34) + "bar")); + assertEquals(".../bar", getTargetDisplayName(Strings.repeat("a/", 35) + "bar")); + assertEquals(".../bar", getTargetDisplayName(Strings.repeat("a/", 100) + "bar")); + assertEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + getTargetDisplayName(Strings.repeat("a", 80))); + } + + public void testResolveTargetUrl() throws Exception { + ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); + GitilesView view = GitilesView.path() + .setServletPath("/x") + .setHostName("host") + .setRepositoryName("repo") + .setRevision(Revision.unpeeled("m", id)) + .setTreePath("a/b/c") + .build(); + assertNull(resolveTargetUrl(view, "/foo")); + assertEquals("/x/repo/+/m/a", resolveTargetUrl(view, "../../")); + assertEquals("/x/repo/+/m/a", resolveTargetUrl(view, ".././../")); + assertEquals("/x/repo/+/m/a", resolveTargetUrl(view, "..//../")); + assertEquals("/x/repo/+/m/a/d", resolveTargetUrl(view, "../../d")); + assertEquals("/x/repo/+/m/", resolveTargetUrl(view, "../../..")); + assertEquals("/x/repo/+/m/a/d/e", resolveTargetUrl(view, "../../d/e")); + assertEquals("/x/repo/+/m/a/b", resolveTargetUrl(view, "../d/../e/../")); + assertNull(resolveTargetUrl(view, "../../../../")); + assertNull(resolveTargetUrl(view, "../../a/../../..")); + } +}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java new file mode 100644 index 0000000..1427b9e --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java
@@ -0,0 +1,346 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import static com.google.gitiles.FakeHttpServletRequest.newRequest; +import static com.google.gitiles.GitilesFilter.REPO_PATH_REGEX; +import static com.google.gitiles.GitilesFilter.REPO_REGEX; +import static com.google.gitiles.GitilesFilter.ROOT_REGEX; + +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.Atomics; +import com.google.gitiles.GitilesView.Type; + +import junit.framework.TestCase; + +import org.eclipse.jgit.http.server.glue.MetaFilter; +import org.eclipse.jgit.http.server.glue.MetaServlet; +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.storage.dfs.DfsRepository; +import org.eclipse.jgit.storage.dfs.DfsRepositoryDescription; +import org.eclipse.jgit.storage.dfs.InMemoryRepository; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Pattern; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** Tests for the view filter. */ +public class ViewFilterTest extends TestCase { + private TestRepository<DfsRepository> repo; + + @Override + protected void setUp() throws Exception { + repo = new TestRepository<DfsRepository>( + new InMemoryRepository(new DfsRepositoryDescription("test"))); + } + + public void testNoCommand() throws Exception { + assertEquals(Type.HOST_INDEX, getView("/").getType()); + assertEquals(Type.REPOSITORY_INDEX, getView("/repo").getType()); + assertNull(getView("/repo/+")); + assertNull(getView("/repo/+/")); + } + + public void testAutoCommand() throws Exception { + RevCommit parent = repo.commit().create(); + RevCommit master = repo.branch("refs/heads/master").commit().parent(parent).create(); + String hex = master.name(); + String hexBranch = hex.substring(0, 10); + RevCommit hexCommit = repo.branch(hexBranch).commit().create(); + + assertEquals(Type.LOG, getView("/repo/+/master").getType()); + assertEquals(Type.LOG, getView("/repo/+/" + hexBranch).getType()); + assertEquals(Type.REVISION, getView("/repo/+/" + hex).getType()); + assertEquals(Type.REVISION, getView("/repo/+/" + hex.substring(0, 7)).getType()); + assertEquals(Type.PATH, getView("/repo/+/master/").getType()); + assertEquals(Type.PATH, getView("/repo/+/" + hex + "/").getType()); + assertEquals(Type.DIFF, getView("/repo/+/master^..master").getType()); + assertEquals(Type.DIFF, getView("/repo/+/master^..master/").getType()); + assertEquals(Type.DIFF, getView("/repo/+/" + parent.name() + ".." + hex + "/").getType()); + } + + public void testHostIndex() throws Exception { + GitilesView view = getView("/"); + assertEquals(Type.HOST_INDEX, view.getType()); + assertEquals("test-host", view.getHostName()); + assertNull(view.getRepositoryName()); + assertEquals(Revision.NULL, view.getRevision()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertNull(view.getTreePath()); + } + + public void testRepositoryIndex() throws Exception { + GitilesView view = getView("/repo"); + assertEquals(Type.REPOSITORY_INDEX, view.getType()); + assertEquals("repo", view.getRepositoryName()); + assertEquals(Revision.NULL, view.getRevision()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertNull(view.getTreePath()); + } + + public void testBranches() throws Exception { + RevCommit master = repo.branch("refs/heads/master").commit().create(); + RevCommit stable = repo.branch("refs/heads/stable").commit().create(); + GitilesView view; + + view = getView("/repo/+show/master"); + assertEquals(Type.REVISION, view.getType()); + assertEquals("master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertNull(view.getTreePath()); + + view = getView("/repo/+show/heads/master"); + assertEquals(Type.REVISION, view.getType()); + assertEquals("heads/master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertNull(view.getTreePath()); + + view = getView("/repo/+show/refs/heads/master"); + assertEquals(Type.REVISION, view.getType()); + assertEquals("refs/heads/master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertNull(view.getTreePath()); + + view = getView("/repo/+show/stable"); + assertEquals(Type.REVISION, view.getType()); + assertEquals("stable", view.getRevision().getName()); + assertEquals(stable, view.getRevision().getId()); + assertNull(view.getTreePath()); + } + + public void testAmbiguousBranchAndTag() throws Exception { + RevCommit branch = repo.branch("refs/heads/name").commit().create(); + RevCommit tag = repo.branch("refs/tags/name").commit().create(); + GitilesView view; + + view = getView("/repo/+show/name"); + assertEquals(Type.REVISION, view.getType()); + assertEquals("name", view.getRevision().getName()); + assertEquals(tag, view.getRevision().getId()); + assertNull(view.getTreePath()); + + view = getView("/repo/+show/heads/name"); + assertEquals(Type.REVISION, view.getType()); + assertEquals("heads/name", view.getRevision().getName()); + assertEquals(branch, view.getRevision().getId()); + assertNull(view.getTreePath()); + + view = getView("/repo/+show/refs/heads/name"); + assertEquals(Type.REVISION, view.getType()); + assertEquals("refs/heads/name", view.getRevision().getName()); + assertEquals(branch, view.getRevision().getId()); + assertNull(view.getTreePath()); + + view = getView("/repo/+show/tags/name"); + assertEquals(Type.REVISION, view.getType()); + assertEquals("tags/name", view.getRevision().getName()); + assertEquals(tag, view.getRevision().getId()); + assertNull(view.getTreePath()); + + view = getView("/repo/+show/refs/tags/name"); + assertEquals(Type.REVISION, view.getType()); + assertEquals("refs/tags/name", view.getRevision().getName()); + assertEquals(tag, view.getRevision().getId()); + assertNull(view.getTreePath()); + } + + public void testPath() throws Exception { + RevCommit master = repo.branch("refs/heads/master").commit().create(); + GitilesView view; + + view = getView("/repo/+show/master/"); + assertEquals(Type.PATH, view.getType()); + assertEquals(master, view.getRevision().getId()); + assertEquals("", view.getTreePath()); + + view = getView("/repo/+show/master/foo"); + assertEquals(Type.PATH, view.getType()); + assertEquals(master, view.getRevision().getId()); + assertEquals("foo", view.getTreePath()); + + view = getView("/repo/+show/master/foo/"); + assertEquals(Type.PATH, view.getType()); + assertEquals(master, view.getRevision().getId()); + assertEquals("foo", view.getTreePath()); + + view = getView("/repo/+show/master/foo/bar"); + assertEquals(Type.PATH, view.getType()); + assertEquals(master, view.getRevision().getId()); + assertEquals("foo/bar", view.getTreePath()); + } + + public void testMultipleSlashes() throws Exception { + RevCommit master = repo.branch("refs/heads/master").commit().create(); + GitilesView view; + + assertEquals(Type.HOST_INDEX, getView("//").getType()); + assertEquals(Type.REPOSITORY_INDEX, getView("//repo").getType()); + assertEquals(Type.REPOSITORY_INDEX, getView("//repo//").getType()); + assertNull(getView("/repo/+//master")); + assertNull(getView("/repo/+/refs//heads//master")); + assertNull(getView("/repo/+//master//")); + assertNull(getView("/repo/+//master/foo//bar")); + } + + public void testDiff() throws Exception { + RevCommit parent = repo.commit().create(); + RevCommit master = repo.branch("refs/heads/master").commit().parent(parent).create(); + GitilesView view; + + view = getView("/repo/+diff/master^..master"); + assertEquals(Type.DIFF, view.getType()); + assertEquals("master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertEquals("master^", view.getOldRevision().getName()); + assertEquals(parent, view.getOldRevision().getId()); + assertEquals("", view.getTreePath()); + + view = getView("/repo/+diff/master^..master/"); + assertEquals(Type.DIFF, view.getType()); + assertEquals("master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertEquals("master^", view.getOldRevision().getName()); + assertEquals(parent, view.getOldRevision().getId()); + assertEquals("", view.getTreePath()); + + view = getView("/repo/+diff/master^..master/foo"); + assertEquals(Type.DIFF, view.getType()); + assertEquals("master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertEquals("master^", view.getOldRevision().getName()); + assertEquals(parent, view.getOldRevision().getId()); + assertEquals("foo", view.getTreePath()); + + view = getView("/repo/+diff/refs/heads/master^..refs/heads/master"); + assertEquals(Type.DIFF, view.getType()); + assertEquals("refs/heads/master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertEquals("refs/heads/master^", view.getOldRevision().getName()); + assertEquals(parent, view.getOldRevision().getId()); + assertEquals("", view.getTreePath()); + } + + public void testDiffAgainstEmptyCommit() throws Exception { + RevCommit master = repo.branch("refs/heads/master").commit().create(); + GitilesView view = getView("/repo/+diff/master^!"); + assertEquals(Type.DIFF, view.getType()); + assertEquals("master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertEquals("", view.getTreePath()); + } + + public void testLog() throws Exception { + RevCommit parent = repo.commit().create(); + RevCommit master = repo.branch("refs/heads/master").commit().parent(parent).create(); + GitilesView view; + + assertNull(getView("/repo/+log")); + assertNull(getView("/repo/+log/")); + + view = getView("/repo/+log/master"); + assertEquals(Type.LOG, view.getType()); + assertEquals("master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertEquals("", view.getTreePath()); + + view = getView("/repo/+log/master/"); + assertEquals(Type.LOG, view.getType()); + assertEquals("master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertEquals("", view.getTreePath()); + + view = getView("/repo/+log/master/foo"); + assertEquals(Type.LOG, view.getType()); + assertEquals("master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertEquals("foo", view.getTreePath()); + + view = getView("/repo/+log/master^..master"); + assertEquals(Type.LOG, view.getType()); + assertEquals("master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertEquals("master^", view.getOldRevision().getName()); + assertEquals(parent, view.getOldRevision().getId()); + assertEquals("", view.getTreePath()); + + view = getView("/repo/+log/master^..master/"); + assertEquals(Type.LOG, view.getType()); + assertEquals("master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertEquals("master^", view.getOldRevision().getName()); + assertEquals(parent, view.getOldRevision().getId()); + assertEquals("", view.getTreePath()); + + view = getView("/repo/+log/master^..master/foo"); + assertEquals(Type.LOG, view.getType()); + assertEquals("master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertEquals("master^", view.getOldRevision().getName()); + assertEquals(parent, view.getOldRevision().getId()); + assertEquals("foo", view.getTreePath()); + + view = getView("/repo/+log/refs/heads/master^..refs/heads/master"); + assertEquals(Type.LOG, view.getType()); + assertEquals("refs/heads/master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertEquals("refs/heads/master^", view.getOldRevision().getName()); + assertEquals(parent, view.getOldRevision().getId()); + assertEquals("", view.getTreePath()); + } + + private GitilesView getView(String pathAndQuery) throws ServletException, IOException { + final AtomicReference<GitilesView> view = Atomics.newReference(); + HttpServlet testServlet = new HttpServlet() { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse res) { + view.set(ViewFilter.getView(req)); + } + }; + + ViewFilter vf = new ViewFilter( + new TestGitilesAccess(repo.getRepository()), + TestGitilesUrls.URLS, + new VisibilityCache(false)); + MetaFilter mf = new MetaFilter(); + + for (Pattern p : ImmutableList.of(ROOT_REGEX, REPO_REGEX, REPO_PATH_REGEX)) { + mf.serveRegex(p) + .through(vf) + .with(testServlet); + } + + FakeHttpServletRequest req = newRequest(repo.getRepository()); + int q = pathAndQuery.indexOf('?'); + if (q > 0) { + req.setPathInfo(pathAndQuery.substring(0, q)); + req.setQueryString(pathAndQuery.substring(q + 1)); + } else { + req.setPathInfo(pathAndQuery); + } + new MetaServlet(mf){}.service(req, new FakeHttpServletResponse()); + + return view.get(); + } +}