| /* |
| * 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.jackrabbit.oak.plugins.observation.filter; |
| |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.assertFalse; |
| |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import com.google.common.base.Stopwatch; |
| import com.google.common.collect.Sets; |
| |
| import org.apache.jackrabbit.oak.commons.PathUtils; |
| import org.apache.jackrabbit.oak.spi.observation.ChangeSet; |
| import org.apache.jackrabbit.oak.spi.observation.ChangeSetBuilder; |
| import org.junit.Test; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| public class ChangeSetFilterImplTest { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(ChangeSetFilterImplTest.class); |
| |
| /** shortcut for creating a set of strings */ |
| private Set<String> s(String... entries) { |
| return new HashSet<String>(Arrays.asList(entries)); |
| } |
| |
| private ChangeSet newChangeSet(int maxPathDepth, Set<String> parentPaths, |
| Set<String> parentNodeNames, |
| Set<String> parentNodeTypes, |
| Set<String> propertyNames) { |
| return newChangeSet(maxPathDepth, parentPaths, parentNodeNames, parentNodeTypes, propertyNames, s()); |
| } |
| |
| private ChangeSet newChangeSet(int maxPathDepth, Set<String> parentPaths, |
| Set<String> parentNodeNames, |
| Set<String> parentNodeTypes, |
| Set<String> propertyNames, |
| Set<String> allNodeTypes) { |
| ChangeSetBuilder changeSetBuilder = new ChangeSetBuilder(Integer.MAX_VALUE, maxPathDepth); |
| for (String path : parentPaths){ |
| changeSetBuilder.addParentPath(path); |
| } |
| for (String nodeName : parentNodeNames){ |
| changeSetBuilder.addParentNodeName(nodeName); |
| } |
| for (String parentNodeType : parentNodeTypes){ |
| changeSetBuilder.addParentNodeType(parentNodeType); |
| } |
| for (String propertyName : propertyNames){ |
| changeSetBuilder.addPropertyName(propertyName); |
| } |
| for (String nodeType : allNodeTypes){ |
| changeSetBuilder.addNodeType(nodeType); |
| } |
| return changeSetBuilder.build(); |
| } |
| |
| private ChangeSetBuilder newBuilder(int maxItems, int maxPathDepth) { |
| return new ChangeSetBuilder(maxItems, maxPathDepth); |
| } |
| |
| private ChangeSetBuilder overflowAllNodeTypes(ChangeSetBuilder builder) { |
| int i = 0; |
| while (!builder.isAllNodeTypeOverflown()) { |
| builder.addNodeType("foo" + i++); |
| } |
| return builder; |
| } |
| |
| private ChangeSetBuilder overflowParentNodeTypes(ChangeSetBuilder builder) { |
| int i = 0; |
| while (!builder.isParentNodeTypeOverflown()) { |
| builder.addParentNodeType("foo" + i++); |
| } |
| return builder; |
| } |
| |
| private ChangeSetBuilder overflowParentNodeNames(ChangeSetBuilder builder) { |
| int i = 0; |
| while (!builder.isParentNodeNameOverflown()) { |
| builder.addParentNodeName("foo" + i++); |
| } |
| return builder; |
| } |
| |
| private ChangeSetBuilder overflowParentPaths(ChangeSetBuilder builder) { |
| int i = 0; |
| while (!builder.isParentPathOverflown()) { |
| builder.addParentPath("foo" + i++); |
| } |
| return builder; |
| } |
| |
| private ChangeSetBuilder overflowPropertyNames(ChangeSetBuilder builder) { |
| int i = 0; |
| while (!builder.isPropertyNameOverflown()) { |
| builder.addPropertyName("foo" + i++); |
| } |
| return builder; |
| } |
| |
| @Test |
| public void testIsDeepFalse() throws Exception { |
| ChangeSetFilterImpl prefilter = new ChangeSetFilterImpl(s("/"), false, null, s("/excluded"), s(), s(), s()); |
| |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/child1", "/child2"), s("child1", "child2"), s(), s()))); |
| assertFalse(prefilter.excludes(newChangeSet(5, s("/", "/child2"), s("child2"), s(), s()))); |
| } |
| |
| @Test |
| public void testParentPathsIncludeExclude() throws Exception { |
| ChangeSetFilterImpl prefilter = new ChangeSetFilterImpl(s("/"), true, null, s("/excluded"), s(), s(), s()); |
| assertFalse(prefilter.excludes(newChangeSet(5, s("/a", "/b"), s("a", "b"), s(), s()))); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/excluded/foo", "/excluded/bar"), s("foo", "bar"), s(), s()))); |
| |
| prefilter = new ChangeSetFilterImpl(s("/included"), true, null, s("/excluded"), s(), s(), s()); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/a", "/b"), s(), s(), s()))); |
| assertFalse(prefilter.excludes(newChangeSet(5, s("/included/a", "/included/b"), s(), s(), s()))); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/excluded/foo", "/excluded/bar"), s(), s(), s()))); |
| |
| prefilter = new ChangeSetFilterImpl(s("/foo/**/included/**"), true /*ignored for globs */, null, s("/excluded"), s(), s(), s()); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/a", "/b"), s(), s(), s()))); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/included/a", "/included/b"), s(), s(), s()))); |
| assertFalse(prefilter.excludes(newChangeSet(5, s("/foo/included/a"), s(), s(), s()))); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/included/b"), s(), s(), s()))); |
| assertFalse(prefilter.excludes(newChangeSet(5, s("/foo/bar/included/a", "/included/b"), s(), s(), s()))); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/excluded/foo", "/excluded/bar"), s(), s(), s()))); |
| |
| prefilter = new ChangeSetFilterImpl(s("/main/**/included"), true, null, s("/main/excluded"), s(), s(), s()); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/main", "/main/foo"), s(), s(), s()))); |
| assertFalse(prefilter.excludes(newChangeSet(5, s("/main/included", "/main/excluded"), s(), s(), s()))); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/main/excluded/included", "/main/excluded"), s(), s(), s()))); |
| |
| prefilter = new ChangeSetFilterImpl(s("/main/included/**"), true, null, s("/main/excluded"), s(), s(), s()); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/main", "/main/foo"), s(), s(), s()))); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/main/excluded"), s(), s(), s()))); |
| assertFalse(prefilter.excludes(newChangeSet(5, s("/main/included"), s(), s(), s()))); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/main/excluded/included", "/main/excluded"), s(), s(), s()))); |
| |
| prefilter = new ChangeSetFilterImpl(s("/main/inc-*/**"), true, null, s("/main/excluded"), s(), s(), s()); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/main", "/main/foo"), s(), s(), s()))); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/main/excluded"), s(), s(), s()))); |
| assertFalse(prefilter.excludes(newChangeSet(5, s("/main/inc-luded"), s(), s(), s()))); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/main/excluded/included", "/main/excluded"), s(), s(), s()))); |
| } |
| |
| @Test |
| public void testParentNodeNames() throws Exception { |
| ChangeSetFilterImpl prefilter = new ChangeSetFilterImpl(s("/"), true, null, s(), s("foo", "bar"), s(), s()); |
| assertFalse(prefilter.excludes(newChangeSet(5, s("/a/foo", "/b"), s("foo", "b"), s(), s()))); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/a/zoo", "/b"), s("zoo", "b"), s(), s()))); |
| assertFalse(prefilter.excludes(newChangeSet(5, s("/a/zoo", "/bar"), s("zoo", "bar"), s(), s()))); |
| } |
| |
| @Test |
| public void testParentNodeTypes() throws Exception { |
| ChangeSetFilterImpl prefilter = new ChangeSetFilterImpl(s("/"), true, null, s(), s(), s("nt:folder"), s()); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/a"), s("a"), s("nt:unstructured"), s()))); |
| assertFalse(prefilter.excludes(newChangeSet(5, s("/a"), s("a"), s("nt:folder"), s()))); |
| } |
| |
| @Test |
| public void testPropertyNames() throws Exception { |
| ChangeSetFilterImpl prefilter = new ChangeSetFilterImpl(s("/"), true, null, s(), s(), s(), s("jcr:data")); |
| assertTrue(prefilter.excludes(newChangeSet(5, s("/a"), s("a"), s(), s("myProperty")))); |
| assertFalse(prefilter.excludes(newChangeSet(5, s("/a"), s("a"), s(), s("jcr:data")))); |
| } |
| |
| @Test |
| public void testOverflowing() throws Exception { |
| ChangeSetBuilder builder = newBuilder(5, 5); |
| assertTrue(overflowAllNodeTypes(builder).isAllNodeTypeOverflown()); |
| assertTrue(overflowParentNodeTypes(builder).isParentNodeTypeOverflown()); |
| assertTrue(overflowParentNodeNames(builder).isParentNodeNameOverflown()); |
| assertTrue(overflowParentPaths(builder).isParentPathOverflown()); |
| assertTrue(overflowPropertyNames(builder).isPropertyNameOverflown()); |
| } |
| |
| private ChangeSetBuilder sampleBuilder() { |
| ChangeSetBuilder builder = newBuilder(5, 5); |
| builder.addNodeType("nt:file"); |
| builder.addParentNodeType("nt:file"); |
| builder.addParentPath("/bar"); |
| builder.addParentNodeName("bar"); |
| builder.addPropertyName("a"); |
| builder.addPropertyName("b"); |
| return builder; |
| } |
| |
| @Test |
| public void testIncludeOnAllNodeTypeOverflow() throws Exception { |
| ChangeSetBuilder builder = sampleBuilder(); |
| ChangeSetFilterImpl prefilter = new ChangeSetFilterImpl(s("/"), true, null, s("/excluded"), s("foo", "bars"), s("nt:file"), s()); |
| assertTrue(prefilter.excludes(builder.build())); |
| overflowAllNodeTypes(builder); |
| assertFalse(prefilter.excludes(builder.build())); |
| } |
| |
| @Test |
| public void testIncludeOnParentNodeNameOverflow() throws Exception { |
| ChangeSetBuilder builder = sampleBuilder(); |
| ChangeSetFilterImpl prefilter = new ChangeSetFilterImpl(s("/"), true, null, s("/excluded"), s("foo", "bars"), s("nt:file"), s()); |
| assertTrue(prefilter.excludes(builder.build())); |
| overflowParentNodeNames(builder); |
| assertFalse(prefilter.excludes(builder.build())); |
| } |
| |
| @Test |
| public void testIncludeOnPropertyNamesOverflow() throws Exception { |
| ChangeSetBuilder builder = sampleBuilder(); |
| ChangeSetFilterImpl prefilter = new ChangeSetFilterImpl(s("/"), true, null, s("/excluded"), s("foo", "bars"), s("nt:file"), s()); |
| assertTrue(prefilter.excludes(builder.build())); |
| overflowPropertyNames(builder); |
| assertFalse(prefilter.excludes(builder.build())); |
| } |
| |
| @Test |
| public void testIncludeOnParentNodeTypeOverflow() throws Exception { |
| ChangeSetBuilder builder = sampleBuilder(); |
| ChangeSetFilterImpl prefilter = new ChangeSetFilterImpl(s("/"), true, null, s("/excluded"), s("foo", "bars"), s("nt:file"), s()); |
| assertTrue(prefilter.excludes(builder.build())); |
| overflowParentNodeTypes(builder); |
| assertFalse(prefilter.excludes(builder.build())); |
| } |
| |
| @Test |
| public void testIncludeOnParentPathsOverflow() throws Exception { |
| ChangeSetBuilder builder = sampleBuilder(); |
| ChangeSetFilterImpl prefilter = new ChangeSetFilterImpl(s("/"), true, null, s("/excluded"), s("foo", "bars"), s("nt:file"), s()); |
| assertTrue(prefilter.excludes(builder.build())); |
| overflowParentPaths(builder); |
| assertFalse(prefilter.excludes(builder.build())); |
| } |
| |
| @Test |
| public void testUnpreciseInclude() throws Exception { |
| ChangeSetBuilder builder = newBuilder(5, 5); |
| builder.addNodeType("nt:file"); |
| builder.addParentNodeType("nt:file"); |
| builder.addParentPath("/a/b/c/e"); |
| builder.addParentNodeName("d"); |
| builder.addPropertyName("e"); |
| builder.addPropertyName("f"); |
| Set<String> largeExcludeSet = new HashSet<String>(); |
| for(int a=0;a<3;a++) { |
| for(int b=0;b<3;b++) { |
| for(int c=0;c<10;c++) { |
| String s = "/a"; |
| if (a > 0) { |
| s += a; |
| } |
| s += "/b"; |
| if (b > 0) { |
| s += b; |
| } |
| s += "/c"; |
| if (c > 0) { |
| s += c; |
| } |
| s += "/d"; |
| largeExcludeSet.add(s); |
| } |
| } |
| } |
| ChangeSetFilterImpl prefilter = new ChangeSetFilterImpl(s("/"), true, null, largeExcludeSet, s("foo", "bars"), s("nt:file"), s(), 999); |
| assertTrue(prefilter.excludes(builder.build())); |
| |
| prefilter = new ChangeSetFilterImpl(s("/"), true, null, largeExcludeSet, s("foo", "bars"), s("nt:file"), s(), 15); |
| assertFalse(prefilter.excludes(builder.build())); |
| |
| prefilter = new ChangeSetFilterImpl(s("/"), true, null, largeExcludeSet, s("foo", "bars"), s("nt:file"), s(), 1); |
| assertFalse(prefilter.excludes(builder.build())); |
| } |
| |
| @Test |
| public void testDeepPaths() throws Exception { |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/a", 5, false); |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/a/b", 5, false); |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/a/b/c", 5, false); |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/a/b/c/d", 5, false); |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/a/b/c/d/e", 5, false); |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/a/b/c/d/e/f", 5, false); |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/a/b/c/d/e/f/g", 5, false); |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/a/b/c/d/e/f/g/h", 5, false); |
| |
| for (int maxPathDepth = 1; maxPathDepth < 13; maxPathDepth++) { |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/a/b/c/d/e/f/g/h", maxPathDepth, false); |
| } |
| |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/x", 15, true); |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/a/x", 15, true); |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/a/b/x", 15, true); |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/a/b/c/x", 15, true); |
| |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/x", 9, true); |
| doTestDeepPath("/a/b/c/d/e/f/g/h/i/j/k/l", "/a/x", 9, false); |
| } |
| |
| private void doTestDeepPath(String changeSetPath, String includePath, int maxPathDepth, boolean expectExclude) { |
| ChangeSetBuilder builder = newBuilder(5, maxPathDepth); |
| builder.addNodeType("nt:file"); |
| builder.addParentNodeType("nt:file"); |
| builder.addParentPath("/bar"); |
| builder.addParentNodeName("bar"); |
| builder.addPropertyName("a"); |
| builder.addPropertyName("b"); |
| builder.addParentPath(changeSetPath); |
| builder.addParentNodeName(PathUtils.getName(changeSetPath)); |
| ChangeSetFilterImpl prefilter = new ChangeSetFilterImpl(s(includePath), true, null, s("/excluded"), s("foo", "bars", "l"), s("nt:file"), s()); |
| if (expectExclude) { |
| assertTrue(prefilter.excludes(builder.build())); |
| } else { |
| assertFalse(prefilter.excludes(builder.build())); |
| } |
| } |
| |
| @Test |
| public void manyIncludePaths() throws Exception { |
| int numPaths = 50; |
| ChangeSetBuilder builder = newBuilder(50, 9); |
| for (int i = 0; i < numPaths; i++) { |
| builder.addParentPath("/a/b/c/d/e/n" + i); |
| } |
| ChangeSet cs = builder.build(); |
| |
| Set<String> includes = Sets.newHashSet(); |
| for (int i = 0; i < 100; i++) { |
| includes.add("/foo/bar/n-" + i + "/*.jsp"); |
| } |
| ChangeSetFilter filter = new ChangeSetFilterImpl(s(),true, |
| includes, s(), s(), s(), s()); |
| |
| // warm up |
| doManyIncludePaths(filter, cs); |
| |
| // and measure |
| Stopwatch sw = Stopwatch.createStarted(); |
| doManyIncludePaths(filter, cs); |
| LOG.info("manyIncludePaths() took {}", sw.stop()); |
| } |
| |
| private void doManyIncludePaths(ChangeSetFilter filter, ChangeSet cs) |
| throws Exception { |
| for (int i = 0; i < 20000; i++) { |
| assertTrue(filter.excludes(cs)); |
| } |
| } |
| } |