Add CanonicalManifest
CanonicalManifest take a set of Manifests and their path and return
manifests with <include> and <remove-project> resolved.
Change-Id: I744c61be0fe8b7e082a8bb63554e63d23442bbbd
diff --git a/src/main/java/com/amd/gerrit/plugins/manifestsubscription/CanonicalManifest.java b/src/main/java/com/amd/gerrit/plugins/manifestsubscription/CanonicalManifest.java
new file mode 100644
index 0000000..06df789
--- /dev/null
+++ b/src/main/java/com/amd/gerrit/plugins/manifestsubscription/CanonicalManifest.java
@@ -0,0 +1,142 @@
+// Copyright (C) 2015 Advanced Micro Devices, 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.amd.gerrit.plugins.manifestsubscription;
+
+import com.amd.gerrit.plugins.manifestsubscription.manifest.Include;
+import com.amd.gerrit.plugins.manifestsubscription.manifest.Manifest;
+import com.amd.gerrit.plugins.manifestsubscription.manifest.Project;
+import com.amd.gerrit.plugins.manifestsubscription.manifest.RemoveProject;
+import com.google.common.collect.Sets;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+public class CanonicalManifest {
+ private Map<String, Manifest> manifests;
+
+ public CanonicalManifest(VersionedManifests manifests) {
+ this.manifests = manifests.getManifests();
+ }
+
+ public CanonicalManifest(Map<String, Manifest> manifests) {
+ this.manifests = manifests;
+ }
+
+ Manifest getCanonicalManifest(String path) throws ManifestReadException {
+ if (manifests.containsKey(path)) {
+ Manifest manifest = (Manifest) manifests.get(path).clone();
+ Path manifestPath = Paths.get(path);
+
+
+ Manifest includedManifest;
+ Path includedPath;
+ Iterator<Include> i = manifest.getInclude().listIterator();
+ String include;
+ String parent;
+ while (i.hasNext()) {
+ parent = manifestPath.getParent() != null ? manifestPath.getParent().toString() + "/" : "";
+ include = i.next().getName();
+ includedPath = Paths.get(parent+include);
+ i.remove();
+
+ includedManifest = getCanonicalManifest(includedPath.normalize().toString());
+
+ try {
+ mergeManifestInto(includedManifest, manifest);
+ } catch (Exception e) {
+ throw new ManifestReadException(path);
+ }
+
+ }
+ // Clear remove project after all include manifest is processed
+ manifest.getRemoveProject().clear();
+
+ return manifest;
+ }
+
+ throw new ManifestReadException(path);
+ }
+
+ private Manifest mergeManifestInto(Manifest inner, Manifest outer)
+ throws Exception {
+ if (outer.getDefault() != null && inner.getDefault() != null) {
+ throw new Exception();
+ }
+ if (outer.getNotice() != null && inner.getNotice() != null) {
+ throw new Exception();
+ }
+ if (outer.getManifestServer() != null && inner.getManifestServer() != null) {
+ throw new Exception();
+ }
+ if (outer.getRepoHooks() != null && inner.getRepoHooks() != null) {
+ throw new Exception();
+ }
+
+ //TODO add more check
+ resolveRemoveProject(inner, outer);
+
+ if (outer.getDefault() == null) {
+ outer.setDefault(inner.getDefault());
+ }
+ if (outer.getNotice() == null) {
+ outer.setNotice(inner.getNotice());
+ }
+ if (outer.getManifestServer() == null) {
+ outer.setManifestServer(inner.getManifestServer());
+ }
+ if (outer.getRepoHooks() == null) {
+ outer.setRepoHooks(inner.getRepoHooks());
+ }
+
+
+ //TODO name remote name duplication check
+ outer.getRemote().addAll(inner.getRemote());
+
+ outer.getProject().addAll(inner.getProject());
+ outer.getExtendProject().addAll(inner.getExtendProject());
+
+ return outer;
+ }
+
+ private void resolveRemoveProject(Manifest manifest, Set<String> toBeRemoved) {
+ Project p;
+ Iterator<Project> i = manifest.getProject().listIterator();
+ while (i.hasNext()) {
+ p = i.next();
+
+ if (toBeRemoved.contains(p.getName())) {
+ i.remove();
+ }
+ }
+ }
+
+ private void resolveRemoveProject(Manifest manifest, Manifest toRemove) {
+ Set<String> removeProjects = Sets.newHashSet();
+
+ RemoveProject rp;
+ Iterator<RemoveProject> i = toRemove.getRemoveProject().listIterator();
+ while (i.hasNext()) {
+ rp = i.next();
+ removeProjects.add(rp.getName());
+ }
+
+ if (removeProjects.size() > 0) {
+ resolveRemoveProject(manifest, removeProjects);
+ }
+ }
+}
diff --git a/src/test/java/com/amd/gerrit/plugins/manifestsubscription/CanonicalManifestTest.java b/src/test/java/com/amd/gerrit/plugins/manifestsubscription/CanonicalManifestTest.java
new file mode 100644
index 0000000..24f7e25
--- /dev/null
+++ b/src/test/java/com/amd/gerrit/plugins/manifestsubscription/CanonicalManifestTest.java
@@ -0,0 +1,151 @@
+// Copyright (C) 2015 Advanced Micro Devices, 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.amd.gerrit.plugins.manifestsubscription;
+
+import com.amd.gerrit.plugins.manifestsubscription.manifest.Manifest;
+import com.amd.gerrit.plugins.manifestsubscription.manifest.Project;
+import org.apache.commons.compress.utils.IOUtils;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static com.amd.gerrit.plugins.manifestsubscription.manifest.ManifestTest.checkAOSPcontent;
+import static com.amd.gerrit.plugins.manifestsubscription.manifest.ManifestTest.checkTestOnlyContent;
+import static com.google.common.truth.Truth.assertThat;
+
+public class CanonicalManifestTest extends LocalDiskRepositoryTestCase{
+ private Repository db;
+ private TestRepository<Repository> util;
+ private VersionedManifests versionedManifests;
+ private CanonicalManifest canonicalManifest;
+ private Manifest manifest;
+ private ManifestProvider provider;
+
+
+ @BeforeClass
+ public static void setUpBeforeClass() {
+
+ }
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ db = createBareRepository();
+ util = new TestRepository<>(db);
+
+ RevCommit rev = util.commit(util.tree(
+ util.file("aosp.xml",
+ util.blob(IOUtils.toByteArray(
+ getClass().getResourceAsStream("/aosp.xml")))),
+ util.file("aospinclude.xml",
+ util.blob(IOUtils.toByteArray(
+ getClass().getResourceAsStream("/aospinclude.xml")))),
+ util.file("aospincludereplace.xml",
+ util.blob(IOUtils.toByteArray(
+ getClass().getResourceAsStream("/aospincludereplace.xml")))),
+ util.file("multipleincludes.xml",
+ util.blob(IOUtils.toByteArray(
+ getClass().getResourceAsStream("/multipleincludes.xml")))),
+ util.file("subdir/aospincludereplace.xml",
+ util.blob(IOUtils.toByteArray(
+ getClass().getResourceAsStream("/subdir/aospincludereplace.xml")))),
+ util.file("subdir/testonly1.xml",
+ util.blob(IOUtils.toByteArray(
+ getClass().getResourceAsStream("/subdir/testonly1.xml")))),
+ util.file("testonly.xml",
+ util.blob(IOUtils.toByteArray(
+ getClass().getResourceAsStream("/testonly.xml"))))
+ ));
+
+ versionedManifests = new VersionedManifests(
+ "master");
+
+ versionedManifests.load(db, rev);
+ canonicalManifest = new CanonicalManifest(versionedManifests);
+ }
+
+ @Test
+ public void testCanonicalManifestsAOSPInclude() throws Exception {
+ manifest = canonicalManifest.getCanonicalManifest("aospinclude.xml");
+ checkAOSPcontent(manifest);
+ checkTestOnlyContent(manifest);
+ }
+
+ @Test
+ public void testCanonicalManifestsAOSPIncludeReplace() throws Exception {
+ manifest = canonicalManifest.getCanonicalManifest("aospincludereplace.xml");
+ checkAOSPcontent(manifest);
+
+ for (Project p : manifest.getProject()) {
+ switch (p.getPath()) {
+ case "git/test/project1":
+ assertThat(p.getGroups()).isNull();
+ assertThat(p.getRevision()).isEqualTo("replaced");
+ break;
+ case "git/test/project2":
+ assertThat(p.getGroups()).isNull();
+ assertThat(p.getRevision()).isEqualTo("replaced");
+ break;
+ case "git/test/project3":
+ assertThat(p.getGroups()).isNull();
+ assertThat(p.getRevision()).isEqualTo("replaced");
+ break;
+ }
+ }
+ }
+
+ @Test
+ public void testCanonicalManifestsMultipleIncludes() throws Exception {
+ manifest = canonicalManifest.getCanonicalManifest("multipleincludes.xml");
+ checkAOSPcontent(manifest);
+ checkTestOnlyContent(manifest);
+ }
+
+ @Test
+ public void testCanonicalManifestsTestOnly() throws Exception {
+ manifest = canonicalManifest.getCanonicalManifest("testonly.xml");
+ assertThat(manifest.getInclude()).isEmpty();
+ checkTestOnlyContent(manifest);
+ }
+
+ @Test
+ public void testCanonicalManifestsSubdir() throws Exception {
+ manifest = canonicalManifest.getCanonicalManifest("subdir/aospincludereplace.xml");
+ checkAOSPcontent(manifest);
+ checkTestOnlyContent(manifest);
+
+ for (Project p : manifest.getProject()) {
+ switch (p.getPath()) {
+ case "git/test/project4":
+ assertThat(p.getGroups()).isNull();
+ assertThat(p.getRevision()).isEqualTo("replaced");
+ break;
+ case "git/test/project5":
+ assertThat(p.getGroups()).isNull();
+ assertThat(p.getRevision()).isEqualTo("replaced");
+ break;
+ case "git/test/project6":
+ assertThat(p.getGroups()).isNull();
+ assertThat(p.getRevision()).isEqualTo("replaced");
+ break;
+ }
+ }
+ }
+}