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; + } + } + } +}