| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.sling.ide.test.impl; |
| |
| import static org.apache.sling.ide.test.impl.helpers.jcr.JcrMatchers.hasChildrenCount; |
| import static org.apache.sling.ide.test.impl.helpers.jcr.JcrMatchers.hasChildrenNames; |
| import static org.apache.sling.ide.test.impl.helpers.jcr.JcrMatchers.hasPath; |
| import static org.apache.sling.ide.test.impl.helpers.jcr.JcrMatchers.hasPrimaryType; |
| import static org.apache.sling.ide.test.impl.helpers.jcr.JcrMatchers.hasPropertyValue; |
| import static org.hamcrest.CoreMatchers.allOf; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.InputStream; |
| import java.util.concurrent.Callable; |
| |
| import javax.jcr.Node; |
| import javax.jcr.RepositoryException; |
| |
| import org.apache.sling.ide.test.impl.helpers.DisableDebugStatusHandlers; |
| import org.apache.sling.ide.test.impl.helpers.ExternalSlingLaunchpad; |
| import org.apache.sling.ide.test.impl.helpers.FailOnUnsuccessfulEventsRule; |
| import org.apache.sling.ide.test.impl.helpers.LaunchpadConfig; |
| import org.apache.sling.ide.test.impl.helpers.Poller; |
| import org.apache.sling.ide.test.impl.helpers.ProjectAdapter; |
| import org.apache.sling.ide.test.impl.helpers.RepositoryAccessor; |
| import org.apache.sling.ide.test.impl.helpers.ServerAdapter; |
| import org.apache.sling.ide.test.impl.helpers.SlingWstServer; |
| import org.apache.sling.ide.test.impl.helpers.TemporaryProject; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.Path; |
| import org.hamcrest.Matcher; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.RuleChain; |
| import org.junit.rules.TestRule; |
| |
| /** |
| * The <tt>JcrFullCoverageAggregatesDeploymentTest</tt> validates deployment of full-coverage aggregates |
| * |
| * @see <a href="https://jackrabbit.apache.org/filevault/vaultfs.html">Vault FS</a> |
| * |
| */ |
| public class JcrPartialCoverageAggregatesDeploymentTest { |
| |
| private final LaunchpadConfig config = LaunchpadConfig.getInstance(); |
| |
| private final SlingWstServer wstServer = new SlingWstServer(config); |
| |
| @Rule |
| public TestRule chain = RuleChain.outerRule(new ExternalSlingLaunchpad(config)).around(wstServer); |
| |
| @Rule |
| public TemporaryProject projectRule = new TemporaryProject(); |
| |
| @Rule |
| public DisableDebugStatusHandlers disableDebugHandlers = new DisableDebugStatusHandlers(); |
| |
| @Rule |
| public FailOnUnsuccessfulEventsRule failOnEventsRule = new FailOnUnsuccessfulEventsRule(); |
| |
| @Test |
| public void deploySlingFolder() throws Exception { |
| |
| wstServer.waitForServerToStart(); |
| |
| // create faceted project |
| IProject contentProject = projectRule.getProject(); |
| |
| ProjectAdapter project = new ProjectAdapter(contentProject); |
| project.addNatures("org.eclipse.wst.common.project.facet.core.nature"); |
| |
| // install content facet |
| project.installFacet("sling.content", "1.0"); |
| |
| ServerAdapter server = new ServerAdapter(wstServer.getServer()); |
| server.installModule(contentProject); |
| |
| // create a sling:Folder at /content/test-root |
| InputStream contentXml = getClass().getResourceAsStream("sling-folder-nodetype.xml"); |
| project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/.content.xml"), contentXml); |
| |
| // create a sling:Folder at /content/test-root/nested |
| InputStream childContentXml = getClass().getResourceAsStream("sling-folder-nodetype.xml"); |
| project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/nested/.content.xml"), |
| childContentXml); |
| |
| Matcher<Node> postConditions = allOf(hasPath("/content/test-root"), hasPrimaryType("sling:Folder"), |
| hasChildrenCount(1)); |
| |
| final RepositoryAccessor repo = new RepositoryAccessor(config); |
| Poller poller = new Poller(); |
| poller.pollUntil(new Callable<Node>() { |
| @Override |
| public Node call() throws RepositoryException { |
| return repo.getNode("/content/test-root"); |
| |
| } |
| }, postConditions); |
| |
| // update jcr:title for /content/test-root |
| InputStream updatedContentXml = getClass().getResourceAsStream("sling-folder-nodetype-with-title.xml"); |
| project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/.content.xml"), |
| updatedContentXml); |
| |
| poller.pollUntil(new Callable<Node>() { |
| @Override |
| public Node call() throws RepositoryException { |
| return repo.getNode("/content/test-root"); |
| |
| } |
| }, allOf(hasPath("/content/test-root"), hasChildrenCount(1), hasPropertyValue("jcr:title", "Some Folder"))); |
| |
| } |
| |
| @Test |
| public void deployNodeWithChildrenAndOrderableNodeTypes() throws Exception { |
| |
| wstServer.waitForServerToStart(); |
| |
| // create faceted project |
| IProject contentProject = projectRule.getProject(); |
| |
| ProjectAdapter project = new ProjectAdapter(contentProject); |
| project.addNatures("org.eclipse.wst.common.project.facet.core.nature"); |
| |
| // install content facet |
| project.installFacet("sling.content", "1.0"); |
| |
| ServerAdapter server = new ServerAdapter(wstServer.getServer()); |
| server.installModule(contentProject); |
| |
| // create a sling:Folder at /content/test-root |
| project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/.content.xml"), getClass() |
| .getResourceAsStream("sling-folder-nodetype.xml")); |
| |
| // create a nt:unstructured at /content/test-root/nested |
| project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/nested/.content.xml"), |
| getClass().getResourceAsStream("nt-unstructured-nodetype.xml")); |
| |
| // create a nt:unstructured at /content/test-root/nested/nested |
| project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/nested/nested/.content.xml"), |
| getClass().getResourceAsStream("nt-unstructured-nodetype.xml")); |
| |
| Matcher<Node> postConditions = allOf(hasPath("/content/test-root/nested"), hasPrimaryType("nt:unstructured"), |
| hasChildrenCount(1)); |
| |
| final RepositoryAccessor repo = new RepositoryAccessor(config); |
| Poller poller = new Poller(); |
| poller.pollUntil(new Callable<Node>() { |
| @Override |
| public Node call() throws RepositoryException { |
| return repo.getNode("/content/test-root/nested"); |
| |
| } |
| }, postConditions); |
| |
| // update jcr:title for /content/test-root |
| project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/nested/.content.xml"), |
| getClass().getResourceAsStream("nt-unstructured-nodetype-with-title.xml")); |
| |
| poller.pollUntil(new Callable<Node>() { |
| @Override |
| public Node call() throws RepositoryException { |
| return repo.getNode("/content/test-root/nested"); |
| |
| } |
| }, allOf(hasPath("/content/test-root/nested"), hasChildrenCount(1), hasPropertyValue("jcr:title", "Some Folder"))); |
| } |
| |
| @Test |
| public void deploySlingOrderedFolderWithJcrContentNode() throws Exception { |
| |
| wstServer.waitForServerToStart(); |
| |
| // create faceted project |
| IProject contentProject = projectRule.getProject(); |
| |
| ProjectAdapter project = new ProjectAdapter(contentProject); |
| project.addNatures("org.eclipse.wst.common.project.facet.core.nature"); |
| |
| // install content facet |
| project.installFacet("sling.content", "1.0"); |
| |
| ServerAdapter server = new ServerAdapter(wstServer.getServer()); |
| server.installModule(contentProject); |
| |
| // create a nt:file at /content/test-root/file.txt |
| project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/folder/file.txt"), |
| new ByteArrayInputStream("hello, world".getBytes())); |
| |
| // create a sling:OrderedFolder at /content/test-root |
| project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/.content.xml"), getClass() |
| .getResourceAsStream("sling-ordered-folder-with-children.xml")); |
| |
| Matcher<Node> postConditions = allOf(hasPath("/content/test-root"), hasPrimaryType("sling:OrderedFolder"), |
| hasChildrenNames("folder", "jcr:content")); |
| |
| final RepositoryAccessor repo = new RepositoryAccessor(config); |
| Poller poller = new Poller(); |
| poller.pollUntil(new Callable<Node>() { |
| @Override |
| public Node call() throws RepositoryException { |
| return repo.getNode("/content/test-root"); |
| |
| } |
| }, postConditions); |
| |
| // reorder the children of the /content/test-root node |
| project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/.content.xml"), getClass() |
| .getResourceAsStream("sling-ordered-folder-with-children-reordered.xml")); |
| |
| postConditions = allOf(hasPath("/content/test-root"), hasPrimaryType("sling:OrderedFolder"), |
| hasChildrenNames("jcr:content", "folder")); |
| |
| poller.pollUntil(new Callable<Node>() { |
| @Override |
| public Node call() throws RepositoryException { |
| return repo.getNode("/content/test-root"); |
| |
| } |
| }, postConditions); |
| } |
| |
| @Test |
| public void deployNodeWithContentXmlInParentFolder() throws Exception { |
| |
| wstServer.waitForServerToStart(); |
| |
| // create faceted project |
| IProject contentProject = projectRule.getProject(); |
| |
| ProjectAdapter project = new ProjectAdapter(contentProject); |
| project.addNatures("org.eclipse.wst.common.project.facet.core.nature"); |
| |
| // install content facet |
| project.installFacet("sling.content", "1.0"); |
| |
| ServerAdapter server = new ServerAdapter(wstServer.getServer()); |
| server.installModule(contentProject); |
| |
| // the expected repository structure is |
| // mapping [sling:Mapping] |
| // \- jcr:content [nt:unstructured] |
| // \- par [nt:unstructured] |
| // \- folder [sling:Folder] |
| |
| // first create the local structure where the content is completely serialized inside the .content.xml file |
| project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/mapping/.content.xml"), |
| getClass().getResourceAsStream("sling-mapping-with-folder-child.xml")); |
| |
| final RepositoryAccessor repo = new RepositoryAccessor(config); |
| Poller poller = new Poller(); |
| |
| // wait until the structure is published |
| poller.pollUntil(new Callable<Node>() { |
| @Override |
| public Node call() throws RepositoryException { |
| return repo.getNode("/content/test-root/mapping/jcr:content/par"); |
| |
| } |
| }, hasPrimaryType("nt:unstructured")); |
| |
| // now create the folder and its associated .content.xml file ; this will cause |
| // intermediate folders to be created whose serialization data is present in the |
| // parent's .content.xml file |
| project.createOrUpdateFile( |
| Path.fromPortableString("jcr_root/content/test-root/mapping/_jcr_content/par/folder/.content.xml"), |
| getClass().getResourceAsStream("sling-folder-nodetype.xml")); |
| |
| // first wait until the sling:Folder child node is created, to ensure that all changes are processed |
| poller.pollUntil(new Callable<Node>() { |
| @Override |
| public Node call() throws RepositoryException { |
| return repo.getNode("/content/test-root/mapping/jcr:content/par/folder"); |
| |
| } |
| }, hasPrimaryType("sling:Folder")); |
| |
| // then, verify that the nt:unstructured node is correctly written |
| poller.pollUntil(new Callable<Node>() { |
| @Override |
| public Node call() throws RepositoryException { |
| return repo.getNode("/content/test-root/mapping/jcr:content/par"); |
| |
| } |
| }, hasPrimaryType("nt:unstructured")); |
| } |
| |
| @Test |
| public void deployNodeWithContentXmlInParentFolder_reverse() throws Exception { |
| |
| wstServer.waitForServerToStart(); |
| |
| // create faceted project |
| IProject contentProject = projectRule.getProject(); |
| |
| ProjectAdapter project = new ProjectAdapter(contentProject); |
| project.addNatures("org.eclipse.wst.common.project.facet.core.nature"); |
| |
| // install content facet |
| project.installFacet("sling.content", "1.0"); |
| |
| ServerAdapter server = new ServerAdapter(wstServer.getServer()); |
| server.installModule(contentProject); |
| |
| // the expected repository structure is |
| // mapping [sling:Mapping] |
| // \- jcr:content [nt:unstructured] |
| // \- par [nt:unstructured] |
| // \- folder [sling:Folder] |
| |
| // first create the local structure where the content is completely serialized inside the .content.xml file |
| project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/mapping/.content.xml"), |
| getClass().getResourceAsStream("sling-mapping-with-folder-child.xml")); |
| // now create the folder and its associated .content.xml file ; this will cause |
| // intermediate folders to be created whose serialization data is present in the |
| // parent's .content.xml file |
| project.createOrUpdateFile( |
| Path.fromPortableString("jcr_root/content/test-root/mapping/_jcr_content/par/folder/.content.xml"), |
| getClass().getResourceAsStream("sling-folder-nodetype.xml")); |
| |
| final RepositoryAccessor repo = new RepositoryAccessor(config); |
| Poller poller = new Poller(); |
| |
| // wait until the structure is published |
| poller.pollUntil(new Callable<Node>() { |
| @Override |
| public Node call() throws RepositoryException { |
| return repo.getNode("/content/test-root/mapping/jcr:content/par/folder"); |
| |
| } |
| }, hasPrimaryType("sling:Folder")); |
| |
| // change the folder's node type to nt:unstructured and make sure it's covered by the parent |
| project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/mapping/.content.xml"), |
| getClass().getResourceAsStream("sling-mapping-with-unstructured-child.xml")); |
| |
| // oh boy, where do I start ... |
| // this test keeps failing with issues which suggest concurrency problems, e.g. InvalidStateException |
| // I've tried to work around this issue by refreshing the session, serialising the executions with a |
| // SingleThreadExecutor, inspecting the before/after repo state ... but in the end only this |
| // 'harmless' sleep fixes the test. It will have to do for now, at least until SLING-4438 is fixed |
| Thread.sleep(1000); |
| |
| // delete the sling folder node type since the serialization is now completely covered by |
| // the parent node |
| project.deleteMember(Path.fromPortableString("jcr_root/content/test-root/mapping/_jcr_content")); |
| |
| // validate that the primary type has changed |
| poller.pollUntil(new Callable<Node>() { |
| @Override |
| public Node call() throws RepositoryException { |
| return repo.getNode("/content/test-root/mapping/jcr:content/par/folder"); |
| |
| } |
| }, hasPrimaryType("nt:unstructured")); |
| } |
| |
| @Before |
| public void ensureCleanState() throws Exception { |
| new RepositoryAccessor(config).tryDeleteResource("/content/test-root"); |
| } |
| |
| @After |
| public void cleanup() throws Exception { |
| new RepositoryAccessor(config).tryDeleteResource("/content/test-root"); |
| } |
| } |