blob: a8b3427c2e27137c6a9bfe2a1d1bb01a70955fbc [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// 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.googlesource.gerrit.plugins.supermanifest;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestPlugin;
import com.google.gerrit.extensions.api.projects.BranchApi;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.NameKey;
import com.google.gerrit.reviewdb.client.RefNames;
import java.net.URI;
import java.util.Arrays;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.BlobBasedConfig;
import org.eclipse.jgit.lib.Config;
import org.junit.Test;
@TestPlugin(
name = "supermanifest",
sysModule = "com.googlesource.gerrit.plugins.supermanifest.SuperManifestModule"
)
public class JiriSuperManifestIT extends LightweightPluginDaemonTest {
NameKey[] testRepoKeys;
void setupTestRepos(String prefix) throws Exception {
testRepoKeys = new NameKey[2];
for (int i = 0; i < 2; i++) {
testRepoKeys[i] = createProject(prefix + i);
TestRepository<InMemoryRepository> repo = cloneProject(testRepoKeys[i], admin);
PushOneCommit push =
pushFactory.create(db, admin.getIdent(), repo, "Subject", "file" + i, "file");
push.to("refs/heads/master").assertOkStatus();
}
}
void pushConfig(String config) throws Exception {
// This will trigger a configuration reload.
TestRepository<InMemoryRepository> allProjectRepo = cloneProject(allProjects, admin);
GitUtil.fetch(allProjectRepo, RefNames.REFS_CONFIG + ":config");
allProjectRepo.reset("config");
PushOneCommit push =
pushFactory.create(
db, admin.getIdent(), allProjectRepo, "Subject", "supermanifest.config", config);
PushOneCommit.Result res = push.to("refs/meta/config");
res.assertOkStatus();
}
@Test
public void basicFunctionalityWorks() throws Exception {
setupTestRepos("project");
// Make sure the manifest exists so the configuration loads successfully.
NameKey manifestKey = createProject("manifest");
TestRepository<InMemoryRepository> manifestRepo = cloneProject(manifestKey, admin);
NameKey superKey = createProject("superproject");
cloneProject(superKey, admin);
pushConfig(
"[superproject \""
+ superKey.get()
+ ":refs/heads/destbranch\"]\n"
+ " srcRepo = "
+ manifestKey.get()
+ "\n"
+ " srcRef = refs/heads/srcbranch\n"
+ " srcPath = default\n"
+ " toolType = jiri\n");
// XML change will trigger commit to superproject.
String xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<manifest>\n<projects>\n"
+ "<project name=\""
+ testRepoKeys[0].get()
+ "\" remote=\""
+ canonicalWebUrl.get()
+ testRepoKeys[0].get()
+ "\" path=\"project1\" />\n"
+ "</projects>\n</manifest>\n";
pushFactory
.create(db, admin.getIdent(), manifestRepo, "Subject", "default", xml)
.to("refs/heads/srcbranch")
.assertOkStatus();
BranchApi branch = gApi.projects().name(superKey.get()).branch("refs/heads/destbranch");
assertThat(branch.file("project1").getContentType()).isEqualTo("x-git/gitlink; charset=UTF-8");
try {
branch.file("project2");
fail("wanted exception");
} catch (ResourceNotFoundException e) {
// all fine.
}
xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<manifest>\n<projects>\n"
+ " <project name=\""
+ testRepoKeys[0].get()
+ "\" remote=\""
+ canonicalWebUrl.get()
+ testRepoKeys[0].get()
+ "\" path=\"project1\" />\n"
+ " <project name=\""
+ testRepoKeys[1].get()
+ "\" remote=\""
+ canonicalWebUrl.get()
+ testRepoKeys[1].get()
+ "\" path=\"project2\" />\n"
+ "</projects>\n</manifest>\n";
pushFactory
.create(db, admin.getIdent(), manifestRepo, "Subject", "default", xml)
.to("refs/heads/srcbranch")
.assertOkStatus();
branch = gApi.projects().name(superKey.get()).branch("refs/heads/destbranch");
assertThat(branch.file("project2").getContentType()).isEqualTo("x-git/gitlink; charset=UTF-8");
// Make sure config change gets picked up.
pushConfig(
"[superproject \""
+ superKey.get()
+ ":refs/heads/other\"]\n"
+ " srcRepo = "
+ manifestKey.get()
+ "\n"
+ " srcRef = refs/heads/srcbranch\n"
+ " srcPath = default\n"
+ " toolType = jiri\n");
// Push another XML change; this should trigger a commit using the new config.
xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<manifest>\n<projects>\n"
+ " <project name=\""
+ testRepoKeys[1].get()
+ "\" remote=\""
+ canonicalWebUrl.get()
+ testRepoKeys[1].get()
+ "\" path=\"project3\" />\n"
+ "</projects>\n</manifest>\n";
pushFactory
.create(db, admin.getIdent(), manifestRepo, "Subject", "default", xml)
.to("refs/heads/srcbranch")
.assertOkStatus();
branch = gApi.projects().name(superKey.get()).branch("refs/heads/other");
assertThat(branch.file("project3").getContentType()).isEqualTo("x-git/gitlink; charset=UTF-8");
}
private void outer() throws Exception {
inner();
}
private void inner() {
throw new IllegalStateException();
}
private void innerTest() throws Exception {
try {
outer();
fail("should throw");
} catch (IllegalStateException e) {
StackTraceElement[] trimmed =
SuperManifestRefUpdatedListener.trimStack(
e.getStackTrace(), Thread.currentThread().getStackTrace()[1]);
String str = Arrays.toString(trimmed);
assertThat(str).doesNotContain("trimStackTrace");
assertThat(str).contains("innerTest");
}
}
@Test
public void trimStackTrace() throws Exception {
innerTest();
}
@Test
public void wildcardDestBranchWorks() throws Exception {
setupTestRepos("project");
// Make sure the manifest exists so the configuration loads successfully.
NameKey manifestKey = createProject("manifest");
TestRepository<InMemoryRepository> manifestRepo = cloneProject(manifestKey, admin);
NameKey superKey = createProject("superproject");
cloneProject(superKey, admin);
pushConfig(
"[superproject \""
+ superKey.get()
+ ":refs/heads/*\"]\n"
+ " srcRepo = "
+ manifestKey.get()
+ "\n"
+ " srcRef = blablabla\n"
+ " srcPath = default\n"
+ " toolType = jiri\n");
// XML change will trigger commit to superproject.
String xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<manifest>\n<projects>\n"
+ " <project name=\""
+ testRepoKeys[0].get()
+ "\" remote=\""
+ canonicalWebUrl.get()
+ testRepoKeys[0].get()
+ "\" path=\"project1\" />\n"
+ "</projects>\n</manifest>\n";
pushFactory
.create(db, admin.getIdent(), manifestRepo, "Subject", "default", xml)
.to("refs/heads/src1")
.assertOkStatus();
xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<manifest>\n<projects>\n"
+ " <project name=\""
+ testRepoKeys[1].get()
+ "\" remote=\""
+ canonicalWebUrl.get()
+ testRepoKeys[1].get()
+ "\" path=\"project2\" />\n"
+ "</projects>\n</manifest>\n";
pushFactory
.create(db, admin.getIdent(), manifestRepo, "Subject", "default", xml)
.to("refs/heads/src2")
.assertOkStatus();
BranchApi branch1 = gApi.projects().name(superKey.get()).branch("refs/heads/src1");
assertThat(branch1.file("project1").getContentType()).isEqualTo("x-git/gitlink; charset=UTF-8");
try {
branch1.file("project2");
fail("wanted exception");
} catch (ResourceNotFoundException e) {
// all fine.
}
BranchApi branch2 = gApi.projects().name(superKey.get()).branch("refs/heads/src2");
assertThat(branch2.file("project2").getContentType()).isEqualTo("x-git/gitlink; charset=UTF-8");
try {
branch2.file("project1");
fail("wanted exception");
} catch (ResourceNotFoundException e) {
// all fine.
}
}
@Test
public void relativeFetch() throws Exception {
// Test that first party gerrit repos are represented by relative URLs in supermanifest and
// external repos by their absolute URLs.
setupTestRepos("platform/project");
String realPrefix = testRepoKeys[0].get().split("/")[0];
Project.NameKey manifestKey = createProject(realPrefix + "/manifest");
TestRepository<InMemoryRepository> manifestRepo = cloneProject(manifestKey, admin);
Project.NameKey superKey = createProject("superproject");
pushConfig(
"[superproject \""
+ superKey.get()
+ ":refs/heads/destbranch\"]\n"
+ " srcRepo = "
+ manifestKey.get()
+ "\n"
+ " srcRef = refs/heads/srcbranch\n"
+ " srcPath = default\n"
+ " toolType = jiri\n");
// XML change will trigger commit to superproject.
String xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<manifest>\n<projects>\n"
+ "<project name=\""
+ testRepoKeys[0].get()
+ "\" remote=\""
+ canonicalWebUrl.get()
+ testRepoKeys[0].get()
+ "\" path=\"project1\" />\n"
+ "<project name=\"external\""
+ " remote=\"https://external/repo\""
+ " revision=\"c438d02cdf08a08fe29550cb11cb6ae8190919f1\""
+ " path=\"project2\" />\n"
+ "</projects>\n</manifest>\n";
pushFactory
.create(db, admin.getIdent(), manifestRepo, "Subject", "default", xml)
.to("refs/heads/srcbranch")
.assertOkStatus();
BranchApi branch = gApi.projects().name(superKey.get()).branch("refs/heads/destbranch");
assertThat(branch.file("project1").getContentType()).isEqualTo("x-git/gitlink; charset=UTF-8");
assertThat(branch.file("project2").getContentType()).isEqualTo("x-git/gitlink; charset=UTF-8");
Config base = new Config();
BlobBasedConfig cfg =
new BlobBasedConfig(base, branch.file(".gitmodules").asString().getBytes(UTF_8));
String subUrl = cfg.getString("submodule", "project1", "url");
// URL is valid.
URI.create(subUrl);
// The suburl must be interpreted as relative to the parent project as a directory, i.e.
// to go from superproject/ to platform/project0, you have to do ../platform/project0
// URL is clean.
assertThat(subUrl).isEqualTo("../" + realPrefix + "/project0");
subUrl = cfg.getString("submodule", "project2", "url");
// URL is valid.
URI.create(subUrl);
// The suburl must be absolute as this is external repo
assertThat(subUrl).isEqualTo("https://external/repo");
}
@Test
public void manifestIncludesOtherManifest() throws Exception {
setupTestRepos("project");
String xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<manifest>\n<projects>\n"
+ "<project name=\""
+ testRepoKeys[0].get()
+ "\" remote=\""
+ canonicalWebUrl.get()
+ testRepoKeys[0].get()
+ "\" path=\"project1\" />\n"
+ "</projects>\n</manifest>\n";
NameKey manifestKey = createProject("manifest");
TestRepository<InMemoryRepository> manifestRepo = cloneProject(manifestKey, admin);
pushFactory
.create(db, admin.getIdent(), manifestRepo, "Subject", "default", xml)
.to("refs/heads/master")
.assertOkStatus();
String superXml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<manifest>\n<imports>\n"
+ " <localimport file=\"default\"/>"
+ "</imports>\n</manifest>";
pushFactory
.create(db, admin.getIdent(), manifestRepo, "Subject", "super", superXml)
.to("refs/heads/master")
.assertOkStatus();
NameKey superKey = createProject("superproject");
cloneProject(superKey, admin);
pushConfig(
"[superproject \""
+ superKey.get()
+ ":refs/heads/master\"]\n"
+ " srcRepo = "
+ manifestKey.get()
+ "\n"
+ " srcRef = refs/heads/master\n"
+ " srcPath = super\n"
+ " toolType = jiri\n");
// Push a change to the source branch. We intentionally change the included XML file
// (rather than the one mentioned in srcPath), to double check that we don't try to be too
// smart about eluding nops.
pushFactory
.create(db, admin.getIdent(), manifestRepo, "Subject", "default", xml + " ")
.to("refs/heads/master")
.assertOkStatus();
BranchApi branch = gApi.projects().name(superKey.get()).branch("refs/heads/master");
assertThat(branch.file("project1").getContentType()).isEqualTo("x-git/gitlink; charset=UTF-8");
}
@Test
public void remoteImportFails() throws Exception {
setupTestRepos("project");
String xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<manifest>\n<projects>\n"
+ "<project name=\""
+ testRepoKeys[0].get()
+ "\" remote=\""
+ canonicalWebUrl.get()
+ testRepoKeys[0].get()
+ "\" path=\"project1\" />\n"
+ "</projects>\n</manifest>\n";
NameKey manifestKey = createProject("manifest");
NameKey superKey = createProject("superproject");
cloneProject(superKey, admin);
pushConfig(
"[superproject \""
+ superKey.get()
+ ":refs/heads/master\"]\n"
+ " srcRepo = "
+ manifestKey.get()
+ "\n"
+ " srcRef = refs/heads/master\n"
+ " srcPath = super\n"
+ " toolType = jiri\n");
TestRepository<InMemoryRepository> manifestRepo = cloneProject(manifestKey, admin);
pushFactory
.create(db, admin.getIdent(), manifestRepo, "Subject", "default", xml)
.to("refs/heads/master")
.assertOkStatus();
String superXml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<manifest>\n<imports>\n"
+ "<import manifest=\""
+ manifestKey.get()
+ "\" name=\"default\" remote=\""
+ canonicalWebUrl.get()
+ manifestKey.get()
+ "\"/>"
+ "</imports>\n</manifest>";
pushFactory
.create(db, admin.getIdent(), manifestRepo, "Subject", "super", superXml)
.to("refs/heads/master")
.assertOkStatus();
BranchApi branch = gApi.projects().name(superKey.get()).branch("refs/heads/master");
try {
branch.file("project1");
fail("wanted exception");
} catch (ResourceNotFoundException e) {
// all fine.
}
}
/*
* Copied test from https://github.com/eclipse/jgit/blob/e9fb111182b55cc82c530d82f13176c7a85cd958/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java#L1105
*/
void testRelative(String a, String b, String want) throws Exception {
String got = JiriUpdater.relativize(URI.create(a), URI.create(b)).toString();
if (!got.equals(want)) {
fail(String.format("relative('%s', '%s') = '%s', want '%s'", a, b, got, want));
}
}
@Test
public void relative() throws Exception {
testRelative("a/b/", "a/", "../");
// Normalization:
testRelative("a/p/..//b/", "a/", "../");
testRelative("a/b", "a/", "");
testRelative("a/", "a/b/", "b/");
testRelative("a/", "a/b", "b");
testRelative("/a/b/c", "/b/c", "../../b/c");
testRelative("/abc", "bcd", "bcd");
testRelative("abc", "def", "def");
testRelative("abc", "/bcd", "/bcd");
testRelative("http://a", "a/b", "a/b");
testRelative("http://base.com/a/", "http://child.com/a/b", "http://child.com/a/b");
}
}