You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 10:20:42 UTC

[sling-org-apache-sling-testing-rules] 01/14: SLING-5703 - new junit/rules module, extracted and enhanced from testing/tools. Contributed by Andrei Dulvac, thanks!

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.testing.rules-1.0.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-rules.git

commit cfb4095df59ee3b2e16807191e6f95c0df7a6ff7
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Fri Apr 29 14:04:50 2016 +0000

    SLING-5703 - new junit/rules module, extracted and enhanced from testing/tools. Contributed by Andrei Dulvac, thanks!
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/testing/junit/rules@1741634 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            | 120 ++++++++++++++++
 .../sling/testing/junit/rules/Condition.java       |  22 +++
 .../sling/testing/junit/rules/FilterRule.java      | 156 +++++++++++++++++++++
 .../testing/junit/rules/InstanceConfigRule.java    |  86 ++++++++++++
 .../sling/testing/junit/rules/RunOnceRule.java     |  55 ++++++++
 .../sling/testing/junit/rules/SlingClassRule.java  |  40 ++++++
 .../testing/junit/rules/SlingInstanceRule.java     |  72 ++++++++++
 .../sling/testing/junit/rules/SlingRule.java       |  53 +++++++
 .../testing/junit/rules/TestDescriptionRule.java   |  41 ++++++
 .../testing/junit/rules/TestStickyCookieRule.java  |  49 +++++++
 .../sling/testing/junit/rules/TestTimeout.java     |  50 +++++++
 .../testing/junit/rules/annotation/IgnoreIf.java   |  33 +++++
 .../junit/rules/annotation/IgnoreIfProperties.java |  31 ++++
 .../junit/rules/annotation/IgnoreIfProperty.java   |  33 +++++
 .../testing/junit/rules/annotation/Issue.java      |  38 +++++
 .../testing/junit/rules/annotation/Issues.java     |  36 +++++
 .../junit/rules/category/ClusterAwareTest.java     |  24 ++++
 .../testing/junit/rules/category/FailingTest.java  |  23 +++
 .../junit/rules/category/FastRunningTest.java      |  24 ++++
 .../junit/rules/category/SlowRunningTest.java      |  24 ++++
 .../testing/junit/rules/category/SmokeTest.java    |  23 +++
 .../junit/rules/instance/AbstractInstance.java     |  56 ++++++++
 .../junit/rules/instance/ExistingInstance.java     |  50 +++++++
 .../rules/instance/ExistingInstanceStatement.java  | 119 ++++++++++++++++
 .../testing/junit/rules/instance/Instance.java     |  35 +++++
 .../rules/instance/util/ConfigurationPool.java     | 131 +++++++++++++++++
 .../rules/instance/util/ContextPathAllocator.java  |  62 ++++++++
 .../rules/instance/util/DirectoryAllocator.java    |  43 ++++++
 .../testing/junit/rules/instance/util/Options.java | 105 ++++++++++++++
 .../sling/testing/junit/rules/util/Action.java     |  21 +++
 .../junit/rules/util/IgnoreTestsConfig.java        | 129 +++++++++++++++++
 .../sling/testing/junit/rules/util/Match.java      |  40 ++++++
 .../testing/junit/rules/util/RunOnceRuleTest.java  |  63 +++++++++
 .../testing/junit/rules/util/UniquePathsTest.java  |  76 ++++++++++
 .../filterrule/FilterRuleAsteriskMatchTest.java    |  66 +++++++++
 .../util/filterrule/FilterRuleDefaultTest.java     |  67 +++++++++
 .../FilterRuleExcludeCategoryIgnoreIfTest.java     | 117 ++++++++++++++++
 .../filterrule/FilterRuleExcludeCategoryTest.java  | 104 ++++++++++++++
 .../filterrule/FilterRuleIgnorelistSkipTest.java   |  47 +++++++
 .../util/filterrule/FilterRuleIgnorelistTest.java  |  51 +++++++
 .../FilterRuleIncludeCategoryIgnoreIfTest.java     | 106 ++++++++++++++
 .../filterrule/FilterRuleIncludeCategoryTest.java  | 117 ++++++++++++++++
 .../rules/util/filterrule/FilterRuleTest.java      | 116 +++++++++++++++
 .../util/instanceconfig/DebugInstanceConfig.java   |  46 ++++++
 .../instanceconfig/InstanceConfigRuleTest.java     |  48 +++++++
 .../rules/util/poller/AbstractPollerTest.java      | 107 ++++++++++++++
 46 files changed, 2955 insertions(+)

diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..d7596a4
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>26</version>
+        <relativePath />
+    </parent>
+
+    <artifactId>org.apache.sling.testing.rules</artifactId>
+    <version>0.1.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <name>Apache Sling Testing Rules</name>
+    <description>
+        Sling testing junit rules and utils
+    </description>
+    
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/testing/http/rules</connection>
+        <developerConnection> scm:svn:https://svn.apache.org/repos/asf/sling/trunk/testing/http/rules</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/testing/http/rules</url>
+    </scm>
+    
+    <build>
+        <plugins>
+           <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+             <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Export-Package>
+                            org.apache.sling.testing.rules.*
+                        </Export-Package>
+                        <Import-Package>
+                            org.apache.commons.exec.*; resolution:=optional,
+                            org.apache.sling.testing.clients.*,
+                            *
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.testing.clients</artifactId>
+            <version>0.1.0-SNAPSHOT</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.sling</groupId>
+                    <artifactId>org.apache.sling.testing.tools</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+       <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-exec</artifactId>
+            <version>1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.7.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>1.7.5</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.testing.tools</artifactId>
+            <version>1.0.12</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/Condition.java b/src/main/java/org/apache/sling/testing/junit/rules/Condition.java
new file mode 100644
index 0000000..1518e57
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/Condition.java
@@ -0,0 +1,22 @@
+/*
+ * 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.testing.junit.rules;
+
+public interface Condition {
+    boolean satisfy();
+    String description();
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/FilterRule.java b/src/main/java/org/apache/sling/testing/junit/rules/FilterRule.java
new file mode 100644
index 0000000..11b66b3
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/FilterRule.java
@@ -0,0 +1,156 @@
+/*
+ * 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.testing.junit.rules;
+
+import org.apache.sling.testing.junit.rules.annotation.IgnoreIf;
+import org.apache.sling.testing.junit.rules.annotation.IgnoreIfProperties;
+import org.apache.sling.testing.junit.rules.annotation.IgnoreIfProperty;
+import org.apache.sling.testing.junit.rules.util.Match;
+import org.apache.sling.testing.junit.rules.util.IgnoreTestsConfig;
+import org.junit.AssumptionViolatedException;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.junit.rules.TestRule;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class FilterRule implements TestRule {
+    private static final Logger LOG = LoggerFactory.getLogger(FilterRule.class);
+    public static final String CATEGORY_PROPERTY = "filterCategory";
+    public static final String INCLUDE_CATEGORY_PROPERTY = "runOnlyFilteredCategories";
+    private List<String> defaultCategories = new ArrayList<String>();
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+
+        // get the annotations in order and skip the tests accordingly
+
+        // IgnoreIf
+        IgnoreIf ignoreIf = description.getAnnotation(IgnoreIf.class);
+        if (null != ignoreIf) {
+            for (Class condClass : Arrays.asList(ignoreIf.value())) {
+                try {
+                    Constructor<? extends Condition> constructor = condClass.getConstructor();
+                    Condition cond = constructor.newInstance();
+                    if (cond.satisfy()) {
+                        return emptyStatement("IgnoreIf condition met to skip: " + cond.description());
+                    }
+                } catch (Exception e) {
+                    LOG.error("Error getting condition object", e);
+                }
+            }
+        }
+
+        // IgnoreIfProperties
+        List<IgnoreIfProperty> ignoreIfPropertyList = new ArrayList<IgnoreIfProperty>();
+        IgnoreIfProperties ignoreIfProperties = description.getAnnotation(IgnoreIfProperties.class);
+        if (null != ignoreIfProperties) {
+            ignoreIfPropertyList.addAll(Arrays.asList(ignoreIfProperties.value()));
+        }
+
+        // IgnoreIfProperty
+        IgnoreIfProperty ignoreIfProperty = description.getAnnotation(IgnoreIfProperty.class);
+        if (null != ignoreIfProperty) {
+            ignoreIfPropertyList.add(ignoreIfProperty);
+        }
+        // Process the ignoreIfProperties
+        for (IgnoreIfProperty ignoreIfProp : ignoreIfPropertyList) {
+            if (null != System.getProperty(ignoreIfProp.name())
+                    && System.getProperty(ignoreIfProp.name()).equals(ignoreIfProp.value())) {
+                return emptyStatement("IgnoreIfProperty condition met to skip: system property '" + ignoreIfProp.name()
+                        + "' is equal to '" + ignoreIfProp.value() + "'.");
+            }
+        }
+
+        // Filter using IgnoreTestsConfig
+        String fqdn = description.getClassName();
+        String methodName = description.getMethodName();
+        if (null != methodName) {
+            fqdn += "#" + methodName;
+        }
+
+        Match match = IgnoreTestsConfig.get().match(fqdn);
+        if (match.isIgnored()) {
+            return emptyStatement(match.getReason());
+        }
+
+       // filterCategory processing
+        List<String> filteredCategories;
+        if (System.getProperty(CATEGORY_PROPERTY) != null) {
+            filteredCategories = Arrays.asList(System.getProperty(CATEGORY_PROPERTY).split(","));
+        } else {
+            filteredCategories = defaultCategories;
+        }
+
+        Category testCategory = description.getAnnotation(Category.class);
+        Class[] testCategories = new Class[0];
+        if (testCategory != null) {
+            testCategories = testCategory.value();
+        }
+        /*
+         * Category annotation exists and the -DfilterCategory property is also set. If test category exists
+         * in -DfilterCategory list & -DrunOnlyFilteredCategories is NOT set, then the test is skipped If test
+         * category exists in -DfilterCategory & -DrunOnlyFilteredCategories is set, then the test is included
+         */
+        if ((System.getProperty(INCLUDE_CATEGORY_PROPERTY) == null)) {
+            // Skip Tests from CATEGORY_PROPERTY
+            for (Class<?> category : testCategories) {
+                if (filteredCategories.contains(category.getSimpleName())) {
+                    return emptyStatement("Excluding category: " + category.getSimpleName());
+                }
+            }
+        } else {
+            // Run only Tests from CATEGORY_PROPERTY
+            boolean categorySelected = false;
+            for (Class<?> category : testCategories) {
+                if ((filteredCategories.contains(category.getSimpleName()))) {
+                    categorySelected = true;
+                }
+            }
+
+            if (!categorySelected) {
+                // No @Category from Test is in CATEGORY_PROPERTY (which should be executed), so skip
+                return emptyStatement("Test has no category in (" + INCLUDE_CATEGORY_PROPERTY + "=true): '" + filteredCategories + "'");
+            }
+        }
+
+        // No Filter excluded this test, so execute
+        return base;
+    }
+
+    private Statement emptyStatement(final String reason) {
+        return new Statement() {
+
+            @Override
+            public void evaluate() throws Throwable {
+                throw new AssumptionViolatedException("Test was ignored by FilterRule: " + reason);
+            }
+        };
+    }
+
+    public FilterRule addDefaultIgnoreCategories(Class... ignoredCategories) {
+        for (Class c : ignoredCategories) {
+            this.defaultCategories.add(c.getSimpleName());
+        }
+        return this;
+    }
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/InstanceConfigRule.java b/src/main/java/org/apache/sling/testing/junit/rules/InstanceConfigRule.java
new file mode 100644
index 0000000..fc81b04
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/InstanceConfigRule.java
@@ -0,0 +1,86 @@
+/*
+ * 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.testing.junit.rules;
+
+import org.apache.sling.testing.junit.rules.util.Action;
+import org.apache.sling.testing.clients.util.config.InstanceConfig;
+import org.apache.sling.testing.clients.util.config.impl.EmptyInstanceConfig;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class InstanceConfigRule implements TestRule {
+    public static final Logger LOG = LoggerFactory.getLogger(InstanceConfigRule.class);
+    
+    private final boolean withRestore;
+    private InstanceConfig instanceConfig;
+    private List<Action> actions;
+
+    public <T extends InstanceConfig> InstanceConfigRule(T instanceConfig, boolean withRestore) {
+        this.instanceConfig = instanceConfig;
+        this.withRestore = withRestore;
+        this.actions = new ArrayList<Action>();
+    }
+
+    public <T extends InstanceConfig> InstanceConfigRule(T instanceConfig) {
+        this(instanceConfig, true);
+    }
+
+    /**
+     * Uses an EmptyInstanceConfig (e.g. does nothing)
+     */
+    public InstanceConfigRule() {
+        this(new EmptyInstanceConfig(), true);
+    }
+
+    public <T extends Action> InstanceConfigRule withAction(T action) {
+        this.actions.add(action);
+        return this;
+    }
+
+    public Statement apply(final Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                // save the instance config
+                LOG.debug("Saving instance config {}", instanceConfig.getClass());
+                instanceConfig.save();
+                
+                // Call any actions if any
+                for (Action action : actions) {
+                    LOG.debug("Calling action {}", action.getClass());
+                    action.call();
+                }
+                
+                // run the base statement
+                LOG.debug("Running base statement");
+                base.evaluate();
+                
+                if (withRestore) {
+                    LOG.debug("Restoring instance config {}", instanceConfig.getClass());
+                    instanceConfig.restore();
+                }
+            }
+        };
+    }
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/RunOnceRule.java b/src/main/java/org/apache/sling/testing/junit/rules/RunOnceRule.java
new file mode 100644
index 0000000..79f0277
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/RunOnceRule.java
@@ -0,0 +1,55 @@
+/*
+ * 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.testing.junit.rules;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Allows wrapping a junit {@link TestRule} that will be run only once.
+ * Once means once per class RunOnceRule being loaded 
+ */
+public class RunOnceRule implements TestRule {
+    private static final Logger LOG = LoggerFactory.getLogger(RunOnceRule.class);
+    private static AtomicBoolean run = new AtomicBoolean(false);
+    private final TestRule rule;
+
+    /**
+     * Constructor*
+     * @param rule The rule to wrap and run once per classloader load
+     * @param <T> The class of the TestRule
+     */
+    public <T extends TestRule> RunOnceRule(T rule) {
+        this.rule = rule;
+    }
+
+    @Override
+    public Statement apply(final Statement base, final Description description) {
+        // if not done yet
+        if (!RunOnceRule.run.getAndSet(true)) {
+            LOG.debug("Applying {} once", rule.getClass());
+            return rule.apply(base, description);
+        } else {
+            return base;
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/SlingClassRule.java b/src/main/java/org/apache/sling/testing/junit/rules/SlingClassRule.java
new file mode 100644
index 0000000..a748307
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/SlingClassRule.java
@@ -0,0 +1,40 @@
+/*
+ * 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.testing.junit.rules;
+
+import org.apache.sling.testing.junit.rules.category.FailingTest;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Sling Rule that wraps all the ClassRules, useful when running a Sling Integration Test.
+ * Can be used in any junit test class using @ClassRule annotation
+ */
+public class SlingClassRule implements TestRule {
+    /** Rule to filter tests at class level */
+    public final FilterRule filterRule = new FilterRule().addDefaultIgnoreCategories(FailingTest.class);
+
+    /** Main RuleChain describing the order of execution of all the rules */
+    protected TestRule ruleChain = RuleChain.outerRule(filterRule);
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return ruleChain.apply(base, description);
+    }
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/SlingInstanceRule.java b/src/main/java/org/apache/sling/testing/junit/rules/SlingInstanceRule.java
new file mode 100644
index 0000000..0e8439f
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/SlingInstanceRule.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.testing.junit.rules;
+
+import org.apache.sling.testing.clients.SlingClient;
+import org.apache.sling.testing.clients.instance.InstanceConfiguration;
+import org.apache.sling.testing.junit.rules.instance.ExistingInstance;
+import org.apache.sling.testing.junit.rules.instance.Instance;
+import org.apache.sling.testing.tools.sling.SlingTestBase;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URI;
+
+/**
+ * Junit Rule that allows access to a Sling instance.
+ * It is wrapped by a {@link SlingClassRule}
+ */
+public class SlingInstanceRule implements TestRule {
+
+    // Until we extract this logic from SlingTestBase, we can just reuse it here
+    // #getServerBaseUrl() starts the instance if needed, internally.
+    private static final SlingTestBase S = new SlingTestBase();
+
+    public static final InstanceConfiguration DEFAULT_INSTANCE =
+            new InstanceConfiguration(URI.create(S.getServerBaseUrl()), "default");
+    private static final Logger LOG = LoggerFactory.getLogger(SlingInstanceRule.class);
+
+    /** Sling rules to be executed at class level */
+    public final SlingClassRule slingClassRule = new SlingClassRule();
+
+
+    /** ExistingInstance for default instance */
+    public final Instance defaultInstance = new ExistingInstance().withRunMode("default").orDefault(DEFAULT_INSTANCE);
+
+    protected TestRule ruleChain = RuleChain.outerRule(slingClassRule)
+            .around(defaultInstance);
+
+
+    public <T extends SlingClient> T getAdminClient(Class<T> clientClass) {
+        return defaultInstance.getClient(clientClass, S.getServerUsername(), S.getServerPassword());
+    }
+
+    public SlingClient getAdminClient() {
+        return getAdminClient(SlingClient.class);
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return ruleChain.apply(base, description);
+    }
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/SlingRule.java b/src/main/java/org/apache/sling/testing/junit/rules/SlingRule.java
new file mode 100644
index 0000000..34fd6f8
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/SlingRule.java
@@ -0,0 +1,53 @@
+/*
+ * 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.testing.junit.rules;
+
+import org.apache.sling.testing.junit.rules.category.FailingTest;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Sling Rule that wraps all the Rules (at method level) useful when running a Sling Integration Test.
+ * Can be used in any junit test using the @Rule annotation
+ * I chains: {@link TestTimeout}, {@link TestDescriptionRule}, {@link TestStickyCookieRule}, {@link FilterRule}
+ */
+public class SlingRule implements TestRule {
+    /** Rule to define the max timeout for all the tests */
+    public final TestTimeout testTimeoutRule = new TestTimeout();
+
+    /** Rule to add a Sticky Session Cookie for requests */
+    public final TestStickyCookieRule testStickySessionRule = new TestStickyCookieRule();
+
+    /** Rule to filter tests per method name */
+    public final FilterRule filterRule = new FilterRule().addDefaultIgnoreCategories(FailingTest.class);
+
+    /** Rule to send in every request a header with the test name */
+    public final TestDescriptionRule testDescriptionRule = new TestDescriptionRule();
+
+    /** Main RuleChain describing the order of execution of all the rules */
+    protected TestRule ruleChain = RuleChain.outerRule(testTimeoutRule)
+            .around(testStickySessionRule)
+            .around(filterRule)
+            .around(testDescriptionRule);
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return ruleChain.apply(base, description);
+    }
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/TestDescriptionRule.java b/src/main/java/org/apache/sling/testing/junit/rules/TestDescriptionRule.java
new file mode 100644
index 0000000..080e423
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/TestDescriptionRule.java
@@ -0,0 +1,41 @@
+/*
+ * 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.testing.junit.rules;
+
+import org.apache.sling.testing.clients.interceptors.TestDescriptionHolder;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+/**
+ * Junit rule which exposes the description of the current executing test as a thread local instance
+ */
+public class TestDescriptionRule extends TestWatcher {
+
+
+    @Override
+    protected void finished(Description description) {
+        TestDescriptionHolder.setClassName(description.getClassName());
+        TestDescriptionHolder.setMethodName(description.getMethodName());
+    }
+
+    @Override
+    protected void starting (Description description) {
+        TestDescriptionHolder.removeMethodName();
+        TestDescriptionHolder.removeClassName();
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/TestStickyCookieRule.java b/src/main/java/org/apache/sling/testing/junit/rules/TestStickyCookieRule.java
new file mode 100644
index 0000000..7a7c9b9
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/TestStickyCookieRule.java
@@ -0,0 +1,49 @@
+/*
+ * 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.testing.junit.rules;
+
+import org.apache.sling.testing.clients.interceptors.StickyCookieHolder;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class TestStickyCookieRule implements TestRule {
+
+    @Override
+    public Statement apply(final Statement base, final Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+
+                starting(description);
+                try {
+                    base.evaluate();
+                } finally {
+                    finished(description);
+                }
+            }
+        };
+    }
+
+    protected void starting(Description description) {
+        StickyCookieHolder.setTestStickySessionCookie(null);
+    }
+
+    protected void finished(Description description) {
+        StickyCookieHolder.remove();
+    }
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/TestTimeout.java b/src/main/java/org/apache/sling/testing/junit/rules/TestTimeout.java
new file mode 100644
index 0000000..3a63fa0
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/TestTimeout.java
@@ -0,0 +1,50 @@
+/*
+ * 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.testing.junit.rules;
+
+import org.junit.internal.runners.statements.FailOnTimeout;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Rule to timeout a test.
+ */
+public class TestTimeout implements TestRule {
+    private int fMillis;
+    private static final int DEFAULT_TIMEOUT = Integer.getInteger("test.timeout.milliseconds", 0);
+
+    public TestTimeout(int fMillis) {
+        this.fMillis = fMillis;
+    }
+
+    /**
+     * Constructor: reads the default timeout value from the "test.timeout.milliseconds" property.
+     * If the property doesn't exist or has the value "0", the statement won't timeout.
+     */
+    public TestTimeout() {
+        this.fMillis = DEFAULT_TIMEOUT;
+    }
+
+    public Statement apply(Statement base, Description description) {
+        if (fMillis > 0) {
+            return new FailOnTimeout(base, fMillis);
+        } else {
+            return base;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/annotation/IgnoreIf.java b/src/main/java/org/apache/sling/testing/junit/rules/annotation/IgnoreIf.java
new file mode 100644
index 0000000..742d5fc
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/annotation/IgnoreIf.java
@@ -0,0 +1,33 @@
+/*
+ * 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.testing.junit.rules.annotation;
+
+
+import org.apache.sling.testing.junit.rules.Condition;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+
+@java.lang.annotation.Retention(value = RUNTIME)
+@java.lang.annotation.Target(value = {METHOD, TYPE})
+public @interface IgnoreIf {
+    Class <? extends Condition>[] value();
+}
+
+
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/annotation/IgnoreIfProperties.java b/src/main/java/org/apache/sling/testing/junit/rules/annotation/IgnoreIfProperties.java
new file mode 100644
index 0000000..cc0c8e1
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/annotation/IgnoreIfProperties.java
@@ -0,0 +1,31 @@
+/*
+ * 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.testing.junit.rules.annotation;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Ignores the test at runtime if ANY of the system properties defined
+ * by {@link IgnoreIfProperty} is defined
+ */
+@java.lang.annotation.Retention(value = RUNTIME)
+@java.lang.annotation.Target(value = {METHOD, TYPE})
+public @interface IgnoreIfProperties {
+    IgnoreIfProperty[] value();
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/annotation/IgnoreIfProperty.java b/src/main/java/org/apache/sling/testing/junit/rules/annotation/IgnoreIfProperty.java
new file mode 100644
index 0000000..4bbf238
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/annotation/IgnoreIfProperty.java
@@ -0,0 +1,33 @@
+/*
+ * 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.testing.junit.rules.annotation;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+
+/**
+ * Ignores the test at runtime if the system property {@code name}
+ * equals {@code value}
+ */
+@java.lang.annotation.Retention(value = RUNTIME)
+@java.lang.annotation.Target(value = {METHOD, TYPE})
+public @interface IgnoreIfProperty {
+    String name();
+    String value() default "";
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/annotation/Issue.java b/src/main/java/org/apache/sling/testing/junit/rules/annotation/Issue.java
new file mode 100644
index 0000000..edb302a
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/annotation/Issue.java
@@ -0,0 +1,38 @@
+/*
+ * 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.testing.junit.rules.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Use this annotation to document that an issue is related to a test class or method. Useful to attach issue
+ * information to failing tests.
+ */
+
+@Retention(RetentionPolicy.SOURCE)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface Issue {
+
+    String value();
+
+    String description() default "";
+
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/annotation/Issues.java b/src/main/java/org/apache/sling/testing/junit/rules/annotation/Issues.java
new file mode 100644
index 0000000..6220259
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/annotation/Issues.java
@@ -0,0 +1,36 @@
+/*
+ * 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.testing.junit.rules.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Use this annotation to document that issues are related to a test class or method. Useful to attach issue information
+ * to failing tests.
+ */
+
+@Retention(RetentionPolicy.SOURCE)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface Issues {
+
+    Issue[] value();
+
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/category/ClusterAwareTest.java b/src/main/java/org/apache/sling/testing/junit/rules/category/ClusterAwareTest.java
new file mode 100644
index 0000000..c111fed
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/category/ClusterAwareTest.java
@@ -0,0 +1,24 @@
+/*
+ * 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.testing.junit.rules.category;
+
+/**
+ * Interface used to mark a JUnit test which has the ability to run in parallel with other tests against the same instance instance.
+ * The definition of a parallelizable test is defined externally.
+ */
+public interface ClusterAwareTest {
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/category/FailingTest.java b/src/main/java/org/apache/sling/testing/junit/rules/category/FailingTest.java
new file mode 100644
index 0000000..c2ee0f3
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/category/FailingTest.java
@@ -0,0 +1,23 @@
+/*
+ * 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.testing.junit.rules.category;
+
+/**
+ * Marker interface for the tests that fail independently of the Java version or other environment settings
+ */
+public interface FailingTest {
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/category/FastRunningTest.java b/src/main/java/org/apache/sling/testing/junit/rules/category/FastRunningTest.java
new file mode 100644
index 0000000..95bed50
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/category/FastRunningTest.java
@@ -0,0 +1,24 @@
+/*
+ * 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.testing.junit.rules.category;
+
+/**
+ * Interface used to mark a JUnit test as FAST running
+ * The definition of a fast running test is defined externally
+ */
+public interface FastRunningTest {
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/category/SlowRunningTest.java b/src/main/java/org/apache/sling/testing/junit/rules/category/SlowRunningTest.java
new file mode 100644
index 0000000..9a177fa
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/category/SlowRunningTest.java
@@ -0,0 +1,24 @@
+/*
+ * 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.testing.junit.rules.category;
+
+/**
+ * Interface used to mark a JUnit test as SLOW running
+ * The definition of a slow running test is defined externally
+ */
+public @interface SlowRunningTest {
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/category/SmokeTest.java b/src/main/java/org/apache/sling/testing/junit/rules/category/SmokeTest.java
new file mode 100644
index 0000000..9a83a98
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/category/SmokeTest.java
@@ -0,0 +1,23 @@
+/*
+ * 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.testing.junit.rules.category;
+
+/**
+ * Marker interface for the tests that should be run as smoke tests after a new build
+ */
+public interface SmokeTest {
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/instance/AbstractInstance.java b/src/main/java/org/apache/sling/testing/junit/rules/instance/AbstractInstance.java
new file mode 100644
index 0000000..f04d557
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/instance/AbstractInstance.java
@@ -0,0 +1,56 @@
+/*
+ * 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.testing.junit.rules.instance;
+
+
+import org.apache.sling.testing.clients.SlingClient;
+import org.apache.sling.testing.clients.instance.InstanceConfiguration;
+
+import java.lang.reflect.Constructor;
+import java.net.URI;
+
+public abstract class AbstractInstance implements Instance {
+
+    @Override
+    public <T extends SlingClient> T getClient(Class<T> clientClass, String user, String pass) {
+        InstanceConfiguration configuration = getConfiguration();
+
+        Constructor<T> constructor;
+
+        try {
+            constructor = clientClass.getConstructor(URI.class, String.class, String.class);
+        } catch (NoSuchMethodException e) {
+            return null;
+        }
+
+        T client;
+
+        try {
+            client = constructor.newInstance(configuration.getUrl(), user, pass);
+        } catch (Exception e) {
+            return null;
+        }
+
+        return client;
+    }
+
+    @Override
+    public <T extends SlingClient> T getAdminClient(Class<T> clientClass) {
+        return getClient(clientClass, "admin", "admin");
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/instance/ExistingInstance.java b/src/main/java/org/apache/sling/testing/junit/rules/instance/ExistingInstance.java
new file mode 100644
index 0000000..dc9074d
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/instance/ExistingInstance.java
@@ -0,0 +1,50 @@
+/*
+ * 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.testing.junit.rules.instance;
+
+import org.apache.sling.testing.clients.instance.InstanceConfiguration;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class ExistingInstance extends AbstractInstance {
+
+    private String runMode;
+
+    private ExistingInstanceStatement statement;
+    private InstanceConfiguration defaultConfiguration;
+
+    public Instance withRunMode(String runMode) {
+        this.runMode = runMode;
+        return this;
+    }
+
+    @Override
+    public Instance orDefault(InstanceConfiguration instanceConfiguration) {
+        this.defaultConfiguration = instanceConfiguration;
+        return this;
+    }
+
+    public InstanceConfiguration getConfiguration() {
+        return statement.getConfiguration();
+    }
+
+    public Statement apply(Statement base, Description description) {
+        this.statement = new ExistingInstanceStatement(description, base, runMode, defaultConfiguration);
+        return this.statement;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/instance/ExistingInstanceStatement.java b/src/main/java/org/apache/sling/testing/junit/rules/instance/ExistingInstanceStatement.java
new file mode 100644
index 0000000..953d60c
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/instance/ExistingInstanceStatement.java
@@ -0,0 +1,119 @@
+/*
+ * 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.testing.junit.rules.instance;
+
+import org.apache.sling.testing.junit.rules.instance.util.ConfigurationPool;
+import org.apache.sling.testing.clients.instance.InstanceConfiguration;
+import org.junit.Assume;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+class ExistingInstanceStatement extends Statement {
+
+    private final Logger logger = LoggerFactory.getLogger(ExistingInstanceStatement.class);
+
+    private final ConfigurationPool configurationPool = new ConfigurationPool();
+
+    private final Description description;
+
+    private final Statement statement;
+
+    private final String runMode;
+
+    private Integer index;
+
+    private InstanceConfiguration configuration;
+
+    private final InstanceConfiguration defaultConfiguration;
+
+    public ExistingInstanceStatement(Description description, Statement statement,
+                                     String runMode, InstanceConfiguration defaultConfiguration) {
+        this.description = description;
+        this.statement = statement;
+        this.runMode = runMode;
+        this.defaultConfiguration = defaultConfiguration;
+    }
+
+    @Override
+    public void evaluate() throws Throwable {
+        takeMatchingQuickstart();
+
+        assumeMatchingQuickstartIsFound();
+
+        try {
+            statement.evaluate();
+        } finally {
+            returnQuickstart();
+        }
+    }
+
+    private void assumeMatchingQuickstartIsFound() {
+        if (this.index == null) {
+            Assume.assumeNotNull(this.defaultConfiguration);
+            this.configuration = defaultConfiguration;
+            logger.info("Using default InstanceConfiguration provided (URL: {}, runmode: {}) for test {}",
+                    configuration.getUrl(), configuration.getRunmode(), description);
+        }
+    }
+
+    private void takeMatchingQuickstart() {
+        List<InstanceConfiguration> configurations = configurationPool.getConfigurations();
+
+        for (int i = 0; i < configurations.size(); i++) {
+            InstanceConfiguration configuration = configurations.get(i);
+
+            // Does the configuration match the requested run mode?
+            if (!runMode.equals(configuration.getRunmode())) {
+                continue;
+            }
+
+            // Is the configuration already used by the current test?
+            if (configurationPool.isTaken(i)) {
+                continue;
+            }
+
+            // The configuration is valid, save the index
+            logger.info("InstanceConfiguration (URL: {}, runmode: {}) found for test {}", configuration.getUrl(), runMode, description);
+
+            takeQuickstart(i);
+
+            return;
+        }
+    }
+
+    private void takeQuickstart(Integer index) {
+        this.configuration = configurationPool.takeConfiguration(description, index);
+        this.index = index;
+    }
+
+    private void returnQuickstart() {
+        if (index == null) {
+            return;
+        }
+        configurationPool.returnConfiguration(description, index);
+        index = null;
+    }
+
+    public InstanceConfiguration getConfiguration() {
+        return configuration;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/instance/Instance.java b/src/main/java/org/apache/sling/testing/junit/rules/instance/Instance.java
new file mode 100644
index 0000000..7747158
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/instance/Instance.java
@@ -0,0 +1,35 @@
+/*
+ * 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.testing.junit.rules.instance;
+
+import org.apache.sling.testing.clients.SlingClient;
+import org.apache.sling.testing.clients.instance.InstanceConfiguration;
+import org.junit.rules.TestRule;
+
+public interface Instance extends TestRule {
+
+    Instance withRunMode(String runMode);
+
+    Instance orDefault(InstanceConfiguration instanceConfiguration);
+
+    InstanceConfiguration getConfiguration();
+
+    <T extends SlingClient> T getClient(Class<T> clientClass, String user, String pass);
+
+    <T extends SlingClient> T getAdminClient(Class<T> clientClass);
+
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/instance/util/ConfigurationPool.java b/src/main/java/org/apache/sling/testing/junit/rules/instance/util/ConfigurationPool.java
new file mode 100644
index 0000000..98fadf0
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/instance/util/ConfigurationPool.java
@@ -0,0 +1,131 @@
+/*
+ * 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.testing.junit.rules.instance.util;
+
+import org.apache.sling.testing.clients.instance.InstanceConfiguration;
+import org.apache.sling.testing.clients.instance.InstanceSetup;
+import org.junit.runner.Description;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+public class ConfigurationPool {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ConfigurationPool.class);
+
+    private static final List<InstanceConfiguration> configurations = initialConfigurations();
+
+    private static final Set<Integer> takenConfigurations = new HashSet<Integer>();
+
+    private static final Map<Description, Set<Integer>> takenConfigurationsByDescription = new HashMap<Description, Set<Integer>>();
+
+    private static List<InstanceConfiguration> initialConfigurations() {
+
+        LOG.info("Reading initial configurations from the system properties");
+
+        List<InstanceConfiguration> configurationsFromPlugin = InstanceSetup.get().getConfigurations();
+
+        if (!configurationsFromPlugin.isEmpty()) {
+
+            LOG.info("Found {} instance configuration(s) from the system properties", configurationsFromPlugin.size());
+
+            return configurationsFromPlugin;
+        }
+
+        LOG.info("No instance configurations found from the system properties");
+
+        List<InstanceConfiguration> configurations = new ArrayList<InstanceConfiguration>();
+
+        return configurations;
+    }
+
+    public List<InstanceConfiguration> getConfigurations() {
+        return new ArrayList<InstanceConfiguration>(configurations);
+    }
+
+    public boolean isTaken(int configurationIndex) {
+        synchronized (ConfigurationPool.class) {
+            return takenConfigurations.contains(configurationIndex);
+        }
+    }
+
+    public InstanceConfiguration takeConfiguration(Description description, int configurationIndex) {
+        synchronized (ConfigurationPool.class) {
+            if (isTaken(configurationIndex)) {
+                throw new IllegalStateException("Requested configuration is already taken");
+            }
+
+            Set<Integer> indices = takenConfigurationsByDescription.get(description);
+
+            if (indices == null) {
+                indices = new HashSet<Integer>();
+            }
+
+            indices.add(configurationIndex);
+
+            LOG.debug("Test {} took configuration with index {}", description, configurationIndex);
+
+            takenConfigurationsByDescription.put(description, indices);
+
+            takenConfigurations.add(configurationIndex);
+
+            return configurations.get(configurationIndex);
+        }
+    }
+
+    public void returnConfiguration(Description description, int configurationIndex) {
+        synchronized (ConfigurationPool.class) {
+            if (!isTaken(configurationIndex)) {
+                throw new IllegalStateException("Returned configuration is not taken by anyone");
+            }
+
+            Set<Integer> indices = takenConfigurationsByDescription.get(description);
+
+            if (indices == null) {
+                throw new IllegalStateException("The test didn't take any configuration");
+            }
+
+            if (!indices.contains(configurationIndex)) {
+                throw new IllegalStateException("The returned configuration was not taken by the test");
+            }
+
+            indices.remove(configurationIndex);
+
+            LOG.debug("Test {} returned configuration with index {}", description, configurationIndex);
+
+            if (indices.isEmpty()) {
+                takenConfigurationsByDescription.remove(description);
+            }
+
+            takenConfigurations.remove(configurationIndex);
+        }
+    }
+
+    public Integer addAndTakeConfiguration(Description description, InstanceConfiguration configuration) {
+        synchronized (ConfigurationPool.class) {
+            configurations.add(configuration);
+
+            Integer index = configurations.size() - 1;
+
+            takeConfiguration(description, index);
+
+            return index;
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/instance/util/ContextPathAllocator.java b/src/main/java/org/apache/sling/testing/junit/rules/instance/util/ContextPathAllocator.java
new file mode 100644
index 0000000..cd7b70a
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/instance/util/ContextPathAllocator.java
@@ -0,0 +1,62 @@
+/*
+ * 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.testing.junit.rules.instance.util;
+
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+
+public class ContextPathAllocator {
+
+    private static Set<String> allocatedContextPaths;
+
+    static {
+        allocatedContextPaths = new HashSet<String>();
+    }
+
+    public String allocateContextPath() {
+        while (true) {
+            String contextPath = generateContextPath();
+
+            boolean contextPathAdded = checkAndAdd(contextPath);
+
+            if (contextPathAdded) {
+                return "/" + contextPath;
+            }
+        }
+    }
+
+    public String generateContextPath() {
+        int start = (int) 'a', end = (int) 'z';
+
+        String contextPath = "";
+
+        Random random = new Random();
+
+        for (int i = 0; i < 8; i++) {
+            int next = start + random.nextInt(end - start);
+            contextPath += Character.toString((char) next);
+        }
+
+        return contextPath;
+    }
+
+    public synchronized boolean checkAndAdd(String contextPath) {
+        return allocatedContextPaths.add(contextPath);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/instance/util/DirectoryAllocator.java b/src/main/java/org/apache/sling/testing/junit/rules/instance/util/DirectoryAllocator.java
new file mode 100644
index 0000000..94c5b9d
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/instance/util/DirectoryAllocator.java
@@ -0,0 +1,43 @@
+/*
+ * 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.testing.junit.rules.instance.util;
+
+import java.io.File;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class DirectoryAllocator {
+
+    private static AtomicInteger counter;
+
+    static {
+        counter = new AtomicInteger(1);
+    }
+
+    public File allocateDirectory(File root, String basename) {
+        while (true) {
+            File folder = new File(root, basename + "-" + counter.getAndIncrement());
+
+            if (folder.exists()) {
+                continue;
+            }
+
+            folder.mkdirs();
+
+            return folder;
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/instance/util/Options.java b/src/main/java/org/apache/sling/testing/junit/rules/instance/util/Options.java
new file mode 100644
index 0000000..82a8280
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/instance/util/Options.java
@@ -0,0 +1,105 @@
+/*
+ * 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.testing.junit.rules.instance.util;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public enum Options {
+
+    JAR("instance.jar.file"),
+    JVM_ARGUMENTS("instance.vm.args"),
+    QUICKSTART_OPTIONS("instance.options"),
+    INSTALLATIONS("instance.installations"),
+    START_TIMEOUT("instance.timeout");
+
+    private String name;
+
+    Options(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public boolean isSpecified() {
+        return System.getProperty(name) != null;
+    }
+
+    public boolean isNotSpecified() {
+        return !isSpecified();
+    }
+
+    public String asString() {
+        return System.getProperty(name);
+    }
+
+    public String asPath() {
+        String value = asString();
+
+        if (value == null) {
+            return null;
+        }
+
+        return new File(value).getAbsolutePath();
+    }
+
+    public File asFile() {
+        String path = asPath();
+
+        if (path == null) {
+            return null;
+        }
+
+        return new File(path);
+    }
+
+    public List<String> asList() {
+        List<String> result = new ArrayList<String>();
+
+        String values = asString();
+
+        if (values == null) {
+            return result;
+        }
+
+        String[] array = values.trim().split("\\s");
+
+        for (String element : array) {
+            String trimmed = element.trim();
+
+            if (!trimmed.isEmpty()) {
+                result.add(trimmed);
+            }
+        }
+
+        return result;
+    }
+
+    public Integer asInteger(int defaultValue) {
+        String string  = asString();
+
+        if (string == null) {
+            return defaultValue;
+        }
+
+        return new Integer(asString());
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/util/Action.java b/src/main/java/org/apache/sling/testing/junit/rules/util/Action.java
new file mode 100644
index 0000000..3e5dde0
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/util/Action.java
@@ -0,0 +1,21 @@
+/*
+ * 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.testing.junit.rules.util;
+
+public interface Action {
+    void call() throws Throwable;
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/util/IgnoreTestsConfig.java b/src/main/java/org/apache/sling/testing/junit/rules/util/IgnoreTestsConfig.java
new file mode 100644
index 0000000..655f9de
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/util/IgnoreTestsConfig.java
@@ -0,0 +1,129 @@
+/*
+ * 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.testing.junit.rules.util;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.testing.clients.Constants;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+public class IgnoreTestsConfig {
+
+    public static final String IGNORE_LIST_PROP = Constants.CONFIG_PROP_PREFIX + "ignorelist";
+    public static final String RUN_IGNORE_LIST_PROP = Constants.CONFIG_PROP_PREFIX + "ignorelist.run";
+
+    private final int numberOfIgnoreLists = 3;
+    private final boolean runIgnoreList;
+    private static IgnoreTestsConfig INSTANCE;
+    private Map<String, String> ignoreTokens = new HashMap<String, String>();
+
+
+    /**
+     * @return the singleton config object.
+     */
+    public static IgnoreTestsConfig get() {
+        if (INSTANCE == null) {
+            INSTANCE = new IgnoreTestsConfig();
+        }
+        return INSTANCE;
+    }
+
+    /**
+     * Recreate the singleton config object.
+     */
+    public static void reCreate() {
+        INSTANCE = new IgnoreTestsConfig();
+    }
+
+    private IgnoreTestsConfig() {
+        for (int i = 0; i <= numberOfIgnoreLists; i++) {
+            StringTokenizer st = new StringTokenizer(System.getProperty(IGNORE_LIST_PROP, ""), ",");
+            while (st.hasMoreElements()) {
+                String token = st.nextToken();
+                String[] pair = token.split(":");
+
+                // Split by ":" and get the ignore (partial) java FQDN and a reason
+                // Ex: org.apache.sling.tests.*:SLING-4242
+                //     org.apache.sling.tests.MyTest:SLING-555
+                String ignoreToken = (pair.length > 0) ? pair[0] : "";
+                String reason = (pair.length > 1) ? pair[1] : "";
+
+                // Add to ignore token map
+                ignoreTokens.put(ignoreToken.trim(), reason.trim());
+            }
+        }
+        this.runIgnoreList = System.getProperty(RUN_IGNORE_LIST_PROP) != null;
+    }
+
+    public Match match(String fqdn) {
+        if (null == fqdn || "".equals(fqdn)) {
+            throw new IllegalArgumentException("The ignore class/method String must not be null or empty");
+        }
+        String className = StringUtils.substringBefore(fqdn, "#");
+        Match match = matchToken(fqdn);
+        if (!match.isIgnored() && (fqdn.indexOf('#') > 0)) {
+            return matchToken(className);
+        } else {
+            return match;
+        }
+    }
+
+    private Match matchToken(String matchToken) {
+        if (!runIgnoreList ) {
+            // run the tests that are not in the ignorelist
+            for (String ignoreToken : ignoreTokens.keySet()) {
+                if (asteriskMatch(ignoreToken, matchToken)) {
+                    return new Match(true, ignoreTokens.get(ignoreToken));
+                }
+            }
+            return new Match(false);
+        } else {
+            // run only the ignore list, so reverse logic.
+            for (String ignoreToken : ignoreTokens.keySet()) {
+                if (asteriskMatch(ignoreToken, matchToken)) {
+                    return new Match(false, ignoreTokens.get(ignoreToken));
+                }
+            }
+            return new Match(true, "Running tests in ignorelist only");
+        }
+    }
+
+
+    private static String createRegexFromGlob(String glob) {
+        String out = "^";
+        for(int i = 0; i < glob.length(); ++i) {
+            final char c = glob.charAt(i);
+            switch(c) {
+                case '*': out += ".*"; break;
+                case '?': out += '.'; break;
+                case '.': out += "\\."; break;
+                case '\\': out += "\\\\"; break;
+                default: out += c;
+            }
+        }
+        out += '$';
+        return out;
+    }
+
+    public static boolean asteriskMatch(String pattern, String text) {
+        return text.matches(createRegexFromGlob(pattern));
+    }
+
+
+}
diff --git a/src/main/java/org/apache/sling/testing/junit/rules/util/Match.java b/src/main/java/org/apache/sling/testing/junit/rules/util/Match.java
new file mode 100644
index 0000000..2dd431d
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/junit/rules/util/Match.java
@@ -0,0 +1,40 @@
+/*
+ * 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.testing.junit.rules.util;
+
+public class Match {
+    private final boolean matched;
+    private final String reason;
+
+    public Match(boolean matched, String reason) {
+        this.matched = matched;
+        this.reason = reason;
+    }
+
+    public Match(boolean matched) {
+        this.matched = matched;
+        reason = "";
+    }
+
+    public boolean isIgnored() {
+        return matched;
+    }
+
+    public String getReason() {
+        return reason;
+    }
+}
diff --git a/src/test/java/org/apache/sling/testing/junit/rules/util/RunOnceRuleTest.java b/src/test/java/org/apache/sling/testing/junit/rules/util/RunOnceRuleTest.java
new file mode 100644
index 0000000..6b89e5c
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/junit/rules/util/RunOnceRuleTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.testing.junit.rules.util;
+
+import org.apache.sling.testing.junit.rules.RunOnceRule;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RunOnceRuleTest {
+    private static final Logger LOG = LoggerFactory.getLogger(RunOnceRule.class);
+    public static int count = 0;
+    
+    @Rule
+    public RunOnceRule roc = new RunOnceRule(new TestRule() {
+        @Override
+        public Statement apply(Statement base, Description description) {
+            count ++;
+            LOG.debug("Run {} times", count);
+            return base;
+        }
+    });
+    
+    @Test
+    public void test1() {
+        Assert.assertEquals("Should have run only once", 1, count);
+    }
+
+    @Test
+    public void test2() {
+        Assert.assertEquals("Should have run only once", 1, count);
+    }
+
+    @Test
+    public void test3() {
+        Assert.assertEquals("Should have run only once", 1, count);
+    }
+
+    @Test
+    public void test4() {
+        Assert.assertEquals("Should have run only once", 1, count);
+    }
+    
+}
diff --git a/src/test/java/org/apache/sling/testing/junit/rules/util/UniquePathsTest.java b/src/test/java/org/apache/sling/testing/junit/rules/util/UniquePathsTest.java
new file mode 100644
index 0000000..d1907db
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/junit/rules/util/UniquePathsTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.testing.junit.rules.util;
+
+import org.apache.sling.testing.clients.util.UniquePaths;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.assertEquals;
+
+public class UniquePathsTest {
+
+    @Before
+    public void setup() throws Exception {
+        // Set known startTime and counter values for tests
+        {
+            final Field f = UniquePaths.class.getDeclaredField("startTime");
+            f.setAccessible(true);
+            f.set(UniquePaths.class, 1234L);
+        }
+        {
+            final Field f = UniquePaths.class.getDeclaredField("counter");
+            f.setAccessible(true);
+            f.set(UniquePaths.class, new AtomicLong(9362L));
+        }
+    }
+    
+    @Test
+    public void testNoUPattern() {
+        assertEquals("/tmp/UniquePathsTest_1234_9363", UniquePaths.get(this, "/tmp/"));
+        assertEquals("/bar/UniquePathsTest_1234_9364", UniquePaths.get(this, "/bar/"));
+    }
+    
+    @Test
+    public void testSingleUPattern() {
+        assertEquals("/tmp/UniquePathsTest_1234_9363/foo", UniquePaths.get(this, "/tmp/_UNIQ_/foo"));
+    }
+    
+    @Test
+    public void testMultipleUPattern() {
+        assertEquals(
+                "/tmp/UniquePathsTest_1234_9363/foo/UniquePathsTest_1234_9363.html", 
+                UniquePaths.get(this, "/tmp/_UNIQ_/foo/_UNIQ_.html"));
+    }
+    
+    @Test
+    public void testNullPattern() {
+        assertEquals(
+                "UniquePathsTest_1234_9363", 
+                UniquePaths.get(this, null));
+    }
+    
+    @Test
+    public void testNoPattern() {
+        assertEquals(
+                "UniquePathsTest_1234_9363", 
+                UniquePaths.get(this));
+    }
+}
diff --git a/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleAsteriskMatchTest.java b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleAsteriskMatchTest.java
new file mode 100644
index 0000000..26a5cc2
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleAsteriskMatchTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.testing.junit.rules.util.filterrule;
+
+import org.apache.sling.testing.junit.rules.util.IgnoreTestsConfig;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+public class FilterRuleAsteriskMatchTest {
+
+
+
+    private final String pattern;
+    private final String text;
+    private final boolean match;
+
+    public FilterRuleAsteriskMatchTest(String pattern, String text, boolean match) {
+        this.pattern = pattern;
+        this.text = text;
+        this.match = match;
+    }
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] {
+                {"a.b.c", "a.b.c", true},
+                {"a.b.c.*", "a.b.c.d", true},
+                {"*.c", "a.b.c", true},
+                {"*", "a.b.c.MyTest", true},
+                {"*.MyTest", "a.b.c.MyTest", true},
+                {"a.b.*.c", "a.b.x.y.c", true},
+                {"a.b.*.c.*", "a.b.x.y.c.MyTest", true},
+
+                {"a.b.*.c", "a.b.x.y.c.NotMyTest", false},
+                {"*.MyTest", "a.b.c.NotMyTest", false},
+                {"*.c", "a.b.c.d", false},
+                {"a.b.c", "x", false},
+                {"", "x", false},
+        });
+    }
+
+    @Test
+    public void testAsteriskMatch() {
+        Assert.assertEquals(this.match, IgnoreTestsConfig.asteriskMatch(this.pattern, this.text));
+    }
+}
diff --git a/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleDefaultTest.java b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleDefaultTest.java
new file mode 100644
index 0000000..55e1f05
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleDefaultTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.testing.junit.rules.util.filterrule;
+
+import org.apache.sling.testing.junit.rules.FilterRule;
+import org.apache.sling.testing.junit.rules.annotation.IgnoreIfProperty;
+import org.apache.sling.testing.junit.rules.category.FailingTest;
+import org.apache.sling.testing.junit.rules.category.SlowRunningTest;
+import org.junit.*;
+import org.junit.experimental.categories.Category;
+
+@IgnoreIfProperty(name = "test.filterrule.a", value = "x")
+public class FilterRuleDefaultTest {
+    @ClassRule
+    public static FilterRule testFilterRuleClass = new FilterRule().addDefaultIgnoreCategories(FailingTest.class);
+
+    @Rule
+    public FilterRule testFilterRule = new FilterRule().addDefaultIgnoreCategories(FailingTest.class);
+
+    @BeforeClass
+    public static void beforeClass() {
+        System.out.println("BeforeClass");
+        System.clearProperty(FilterRule.CATEGORY_PROPERTY);
+        System.clearProperty(FilterRule.INCLUDE_CATEGORY_PROPERTY);
+    }
+
+    @Before
+    public void before() {
+        System.out.println("Before");
+    }
+
+    @After
+    public void after() {
+        System.out.println("After");
+    }
+
+    @Test
+    public void testWithoutShouldRun() {
+        // Should pass
+    }
+
+    @Test
+    @Category(SlowRunningTest.class)
+    public void testSingleShouldRun() {
+        // Should pass
+    }
+
+    @Test
+    @Category(FailingTest.class)
+    public void testSingleShouldSkip() {
+        Assert.fail("Test should be Ignored");
+    }
+}
diff --git a/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleExcludeCategoryIgnoreIfTest.java b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleExcludeCategoryIgnoreIfTest.java
new file mode 100644
index 0000000..360ca6b
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleExcludeCategoryIgnoreIfTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.testing.junit.rules.util.filterrule;
+
+import org.apache.sling.testing.junit.rules.annotation.IgnoreIfProperty;
+import org.apache.sling.testing.junit.rules.annotation.Issue;
+import org.apache.sling.testing.junit.rules.FilterRule;
+import org.apache.sling.testing.junit.rules.category.FailingTest;
+import org.apache.sling.testing.junit.rules.category.SlowRunningTest;
+import org.apache.sling.testing.junit.rules.util.IgnoreTestsConfig;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TestName;
+
+public class FilterRuleExcludeCategoryIgnoreIfTest {
+
+    @Rule
+    public FilterRule testFilterRule = new FilterRule();
+
+    @Rule
+    public TestName name = new TestName();
+
+    static {
+        System.setProperty(FilterRule.CATEGORY_PROPERTY, "Issue,SlowRunningTest");
+        System.setProperty("test.filterrule.a", "a");
+        IgnoreTestsConfig.reCreate();
+    }
+
+    /*
+     * System prop is set for ignoring tests and also for excluding a category Setup: a test is annotated with just
+     * the @IgnoreIf Result: The test is skipped
+     */
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    public void testIgnoreIfOnly() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    /*
+     * System prop is set for ignoring tests and also for excluding a category Setup: a test is annotated with
+     * @IgnoreIf and @Category which is excluded Result: The test is skipped
+     */
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    @Category(Issue.class)
+    public void testIgnoreIfPropExistsandExcludedCategoryExists() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    /*
+     * System prop is set for ignoring tests and also for excluding a category Setup: a test is annotated with
+     * @IgnoreIf and @Category which is excluded Result: The test is skipped
+     */
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    @Category(SlowRunningTest.class)
+    public void testIgnoreIfPropExistsandExcludedCategoryExists_2() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    /*
+     * System prop is set for ignoring tests and also for excluding a category Setup: a test is annotated with
+     * @IgnoreIf and @Category which is not excluded Result: The test is skipped
+     */
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    @Category(FailingTest.class)
+    public void testIgnoreIfPropExixtsandExcludedCategoryNotExists() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    /*
+     * System prop is set for ignoring tests and also for excluding a category Setup: a test is annotated with
+     * @Category which is excluded Result: The test is skipped
+     */
+    @Test
+    @Category(Issue.class)
+    public void testExcludedCategoryExists() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    /*
+     * System prop is set for ignoring tests and also for excluding a category Setup: a test is annotated with
+     * @Category which is not excluded Result: The test is not skipped
+     */
+    @Test
+    @Category(FailingTest.class)
+    public void testExcludedCategoryNotExists() {
+        Assert.assertTrue("Test should be Run", true);
+    }
+
+    /*
+     * System prop is set for ignoring tests and also for excluding a category Setup: a test is not annotated with
+     * @Category & @IgnoreIf Result: The test is not skipped
+     */
+    @Test
+    public void testNoAnnotationsExists() {
+        Assert.assertTrue("Test should be Run", true);
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleExcludeCategoryTest.java b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleExcludeCategoryTest.java
new file mode 100644
index 0000000..a2bdb87
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleExcludeCategoryTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.testing.junit.rules.util.filterrule;
+
+import org.apache.sling.testing.junit.rules.FilterRule;
+import org.apache.sling.testing.junit.rules.annotation.IgnoreIfProperty;
+import org.apache.sling.testing.junit.rules.annotation.Issue;
+import org.apache.sling.testing.junit.rules.category.FailingTest;
+import org.apache.sling.testing.junit.rules.util.IgnoreTestsConfig;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TestName;
+
+public class FilterRuleExcludeCategoryTest {
+
+    @Rule
+    public FilterRule testFilterRule = new FilterRule();
+
+    @Rule
+    public TestName name = new TestName();
+
+    static {
+        System.setProperty(FilterRule.CATEGORY_PROPERTY, "Issue");
+        IgnoreTestsConfig.reCreate();
+    }
+
+    /*
+     * System prop is set for excluding a category only Setup: a test is annotated with just the @IgnoreIf Result: The
+     * test is executed
+     */
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    public void testIgnoreIfOnly() {
+        Assert.assertTrue("Test should be Run", true);
+    }
+
+    /*
+     * System prop is set for excluding a category only Setup: a test is annotated with @IgnoreIf and @Category which
+     * is excluded Result: The test is skipped
+     */
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    @Category(Issue.class)
+    public void testIgnoreIfPropExistsandExcludedCategoryExists() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    /*
+     * System prop is set for excluding a category only Setup: a test is annotated with @IgnoreIf and @Category which
+     * is not excluded Result: The test is skipped
+     */
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    @Category(FailingTest.class)
+    public void testIgnoreIfPropExixtsandExcludedCategoryNotExists() {
+        Assert.assertTrue("Test should be Run", true);
+    }
+
+    /*
+     * System prop is set for excluding a category only Setup: a test is annotated with @Category which is excluded
+     * Result: The test is skipped
+     */
+    @Test
+    @Category(Issue.class)
+    public void testExcludedCategoryExists() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    /*
+     * System prop is set for excluding a category only Setup: a test is annotated with @Category which is not
+     * excluded Result: The test is executed
+     */
+    @Test
+    @Category(FailingTest.class)
+    public void testExcludedCategoryNotExists() {
+        Assert.assertTrue("Test should be Run", true);
+    }
+
+    /*
+     * System prop is set for ignoring tests and also for excluding a category Setup: a test is not annotated with
+     * @Category & @IgnoreIf Result: The test is skipped
+     */
+    @Test
+    public void testNoAnnotationsExists() {
+        Assert.assertTrue("Test should be Run", true);
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleIgnorelistSkipTest.java b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleIgnorelistSkipTest.java
new file mode 100644
index 0000000..853dcf8
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleIgnorelistSkipTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.testing.junit.rules.util.filterrule;
+
+import org.apache.sling.testing.junit.rules.FilterRule;
+import org.apache.sling.testing.junit.rules.util.IgnoreTestsConfig;
+import org.junit.*;
+
+// TODO use ParallelComputer in junit and make these classes into one test?
+public class FilterRuleIgnorelistSkipTest {
+
+    @ClassRule
+    public static FilterRule classRule = new FilterRule();
+    @Rule
+    public FilterRule methodRule = new FilterRule();
+
+    static {
+        System.clearProperty(IgnoreTestsConfig.IGNORE_LIST_PROP);
+        System.setProperty(IgnoreTestsConfig.IGNORE_LIST_PROP, "*.notmypackage.*, *.FilterRuleIgnorelistSkipTest#shouldSkip:WTF-9999");
+        IgnoreTestsConfig.reCreate();
+    }
+
+    @AfterClass
+    public static void clearIgnoreList() {
+        System.clearProperty(IgnoreTestsConfig.IGNORE_LIST_PROP);
+    }
+
+    @Test
+    public void shouldSkip() {
+        Assert.fail("Test should be skipped");
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleIgnorelistTest.java b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleIgnorelistTest.java
new file mode 100644
index 0000000..8c5bbc1
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleIgnorelistTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.testing.junit.rules.util.filterrule;
+
+import org.apache.sling.testing.junit.rules.FilterRule;
+import org.apache.sling.testing.junit.rules.util.IgnoreTestsConfig;
+import org.junit.*;
+
+public class FilterRuleIgnorelistTest {
+
+    @ClassRule
+    public static FilterRule classRule = new FilterRule();
+    @Rule
+    public FilterRule methodRule = new FilterRule();
+
+    static {
+        System.clearProperty(IgnoreTestsConfig.IGNORE_LIST_PROP);
+        System.setProperty(IgnoreTestsConfig.IGNORE_LIST_PROP, "*.FilterRuleIgnorelistTest#shouldSkip:WTF-9999, *.notmypackage.*");
+        IgnoreTestsConfig.reCreate();
+    }
+
+    @AfterClass
+    public static void clearIgnoreList() {
+        System.clearProperty(IgnoreTestsConfig.IGNORE_LIST_PROP);
+    }
+
+    @Test
+    public void shouldSkip() {
+        Assert.fail("Test should be skipped");
+    }
+
+    @Test
+    public void shouldPass() {
+
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleIncludeCategoryIgnoreIfTest.java b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleIncludeCategoryIgnoreIfTest.java
new file mode 100644
index 0000000..3060fcb
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleIncludeCategoryIgnoreIfTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.testing.junit.rules.util.filterrule;
+
+import org.apache.sling.testing.junit.rules.FilterRule;
+import org.apache.sling.testing.junit.rules.annotation.IgnoreIfProperty;
+import org.apache.sling.testing.junit.rules.annotation.Issue;
+import org.apache.sling.testing.junit.rules.category.FailingTest;
+import org.apache.sling.testing.junit.rules.util.IgnoreTestsConfig;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TestName;
+
+public class FilterRuleIncludeCategoryIgnoreIfTest {
+
+    @Rule
+    public FilterRule testFilterRule = new FilterRule();
+
+    @Rule
+    public TestName name = new TestName();
+
+    static {
+        System.setProperty(FilterRule.CATEGORY_PROPERTY, "Issue");
+        System.setProperty(FilterRule.INCLUDE_CATEGORY_PROPERTY, "");
+        System.setProperty("test.filterrule.a", "a");
+        IgnoreTestsConfig.reCreate();
+    }
+
+    /*
+     * System prop is set for ignoring tests and for including a category Setup: a test is annotated with just the
+     * @IgnoreIf Result: The test is skipped
+     */
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    public void testIgnoreIfOnly() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    /*
+     * System prop is set for ignoring tests and for including a category Setup: a test is annotated with @IgnoreIf
+     * and @Category which is included Result: The test is skipped
+     */
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    @Category(Issue.class)
+    public void testIgnoreIfPropExistsandIncludedCategoryExists() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    /*
+     * System prop is set for ignoring tests and for including a category Setup: a test is annotated with @IgnoreIf
+     * and @Category which is not included Result: The test is skipped
+     */
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    @Category(FailingTest.class)
+    public void testIgnoreIfPropExixtsandIncludedCategoryNotExists() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    /*
+     * System prop is set for ignoring tests and for including a category Setup: a test is annotated with @Category
+     * which is not included Result: The test is skipped
+     */
+    @Test
+    @Category(FailingTest.class)
+    public void testIncludedCategoryNotExists() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    /*
+     * System prop is set for ignoring tests and for including a category Setup: a test is annotated with @Category
+     * which is included Result: The test is executed
+     */
+    @Test
+    @Category(Issue.class)
+    public void testIncludedCategoryExists() {
+        Assert.assertTrue("Test should be Run", true);
+    }
+
+    /*
+     * System prop is set for ignoring tests and for including a category Setup: a test is not annotated with
+     * @Category & @IgnoreIf Result: The test is skipped
+     */
+    @Test
+    public void testNoAnnotationsExists() {
+        Assert.fail("Test should be Ignored");
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleIncludeCategoryTest.java b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleIncludeCategoryTest.java
new file mode 100644
index 0000000..8b4b564
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleIncludeCategoryTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.testing.junit.rules.util.filterrule;
+
+import org.apache.sling.testing.junit.rules.annotation.IgnoreIfProperty;
+import org.apache.sling.testing.junit.rules.FilterRule;
+import org.apache.sling.testing.junit.rules.annotation.Issue;
+import org.apache.sling.testing.junit.rules.category.FailingTest;
+import org.apache.sling.testing.junit.rules.category.SlowRunningTest;
+import org.apache.sling.testing.junit.rules.util.IgnoreTestsConfig;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TestName;
+
+public class FilterRuleIncludeCategoryTest {
+
+    @Rule
+    public FilterRule testFilterRule = new FilterRule();
+
+    @Rule
+    public TestName name = new TestName();
+
+    static {
+        System.setProperty(FilterRule.CATEGORY_PROPERTY, "Issue,SlowRunningTest");
+        System.setProperty(FilterRule.INCLUDE_CATEGORY_PROPERTY, "");
+        IgnoreTestsConfig.reCreate();
+    }
+
+    /*
+     * System prop is set for including a category only Setup: a test is annotated with just the @IgnoreIf Result: The
+     * test is skipped
+     */
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    public void testIgnoreIfOnly() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    /*
+     * System prop is set for including a category only Setup: a test is annotated with @IgnoreIf and @Category which
+     * is included Result: The test is executed
+     */
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    @Category(Issue.class)
+    public void testIgnoreIfPropExistsandIncludedCategoryExists() {
+        Assert.assertTrue("Test should be Run", true);
+    }
+
+    /*
+     * System prop is set for including a category only Setup: a test is annotated with @IgnoreIf and @Category which
+     * is included Result: The test is executed
+     */
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    @Category(SlowRunningTest.class)
+    public void testIgnoreIfPropExistsandIncludedCategoryExists_2() {
+        Assert.assertTrue("Test should be Run", true);
+    }
+
+    /*
+     * System prop is set for including a category only Setup: a test is annotated with @IgnoreIf and @Category which
+     * is not included Result: The test is skipped
+     */
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    @Category(FailingTest.class)
+    public void testIgnoreIfPropExixtsandIncludedCategoryNotExists() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    /*
+     * System prop is set for including a category only Setup: a test is annotated with @Category which is not
+     * included Result: The test is skipped
+     */
+    @Test
+    @Category(FailingTest.class)
+    public void testIncludedCategoryNotExists() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    /*
+     * System prop is set for including a category only Setup: a test is annotated with @Category which is included
+     * Result: The test is executed
+     */
+    @Test
+    @Category(Issue.class)
+    public void testIncludedCategoryExists() {
+        Assert.assertTrue("Test should be Run", true);
+    }
+
+    /*
+     * System prop is set for ignoring tests and also for excluding a category Setup: a test is not annotated with
+     * @Category & @IgnoreIf Result: The test is skipped
+     */
+    @Test
+    public void testNoAnnotationsExists() {
+        Assert.fail("Test should be Ignored");
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleTest.java b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleTest.java
new file mode 100644
index 0000000..60f93cc
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/junit/rules/util/filterrule/FilterRuleTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.testing.junit.rules.util.filterrule;
+
+import org.apache.sling.testing.junit.rules.annotation.IgnoreIfProperty;
+import org.apache.sling.testing.junit.rules.FilterRule;
+import org.apache.sling.testing.junit.rules.annotation.IgnoreIfProperties;
+import org.junit.*;
+
+@IgnoreIfProperty(name = "test.filterrule.a", value = "x")
+public class FilterRuleTest {
+    @ClassRule
+    public static FilterRule testFilterRuleClass = new FilterRule();
+
+    @Rule
+    public FilterRule testFilterRule = new FilterRule();
+
+    @BeforeClass
+    public static void beforeClass() {
+        System.out.println("BeforeClass");
+    }
+
+    @Before
+    public void before() {
+        System.out.println("Before");
+    }
+
+    @After
+    public void after() {
+        System.out.println("After");
+    }
+
+    static {
+        System.clearProperty(FilterRule.CATEGORY_PROPERTY);
+        System.clearProperty(FilterRule.INCLUDE_CATEGORY_PROPERTY);
+
+        System.setProperty("test.filterrule.a", "a");
+        System.setProperty("test.filterrule.b", "b");
+        System.setProperty("test.filterrule.c", "");
+    }
+
+    @Test
+    public void testWithoutShouldRun() {
+        // Should pass
+    }
+
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "x")
+    public void testSingleShouldRun() {
+        // Should pass
+    }
+
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.a", value = "a")
+    public void testSingleShouldSkip() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    @Test
+    @IgnoreIfProperty(name = "test.filterrule.c")
+    public void testSingleDefaultShouldSkip() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    @Test
+    @IgnoreIfProperties({@IgnoreIfProperty(name = "test.filterrule.a", value = "a"),
+            @IgnoreIfProperty(name = "test.filterrule.noexist")})
+    public void testMultipleShouldSkip() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    @Test
+    @IgnoreIfProperties({@IgnoreIfProperty(name = "test.filterrule.noexist1", value = "a"),
+            @IgnoreIfProperty(name = "test.filterrule.noexist2")})
+    public void testMultipleShouldPass() {
+        // Should pass
+    }
+
+    @Test
+    @IgnoreIfProperties({@IgnoreIfProperty(name = "test.filterrule.noexist1", value = "a"),
+            @IgnoreIfProperty(name = "test.filterrule.noexist2")})
+    @IgnoreIfProperty(name = "test.filterrule.noexist3")
+    public void testBothShouldPass() {
+        // Should pass
+    }
+
+    @Test
+    @IgnoreIfProperties({@IgnoreIfProperty(name = "test.filterrule.noexist1", value = "a"),
+            @IgnoreIfProperty(name = "test.filterrule.c")})
+    @IgnoreIfProperty(name = "test.filterrule.noexist3")
+    public void testBothShouldSkip1() {
+        Assert.fail("Test should be Ignored");
+    }
+
+    @Test
+    @IgnoreIfProperties({@IgnoreIfProperty(name = "test.filterrule.noexist1", value = "a"),
+            @IgnoreIfProperty(name = "test.filterrule.noexist2")})
+    @IgnoreIfProperty(name = "test.filterrule.b", value = "b")
+    public void testBothShouldSkip2() {
+        Assert.fail("Test should be Ignored");
+    }
+}
diff --git a/src/test/java/org/apache/sling/testing/junit/rules/util/instanceconfig/DebugInstanceConfig.java b/src/test/java/org/apache/sling/testing/junit/rules/util/instanceconfig/DebugInstanceConfig.java
new file mode 100644
index 0000000..c3f41ec
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/junit/rules/util/instanceconfig/DebugInstanceConfig.java
@@ -0,0 +1,46 @@
+/*
+ * 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.testing.junit.rules.util.instanceconfig;
+
+import org.apache.sling.testing.clients.util.config.InstanceConfig;
+import org.apache.sling.testing.clients.util.config.InstanceConfigException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DebugInstanceConfig implements InstanceConfig {
+    private static final Logger LOG = LoggerFactory.getLogger(DebugInstanceConfig.class);
+    private int value = 0;
+
+    @Override
+    public InstanceConfig save() throws InstanceConfigException {
+        this.value = 1;
+        LOG.debug("Saved 1");
+        return this;
+    }
+
+    @Override
+    public InstanceConfig restore() throws InstanceConfigException {
+        this.value = 2;
+        LOG.debug("Restored 2");
+        return this;
+    }
+
+
+    public int getValue() {
+        return value;
+    }
+}
diff --git a/src/test/java/org/apache/sling/testing/junit/rules/util/instanceconfig/InstanceConfigRuleTest.java b/src/test/java/org/apache/sling/testing/junit/rules/util/instanceconfig/InstanceConfigRuleTest.java
new file mode 100644
index 0000000..a9ba61d
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/junit/rules/util/instanceconfig/InstanceConfigRuleTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.testing.junit.rules.util.instanceconfig;
+
+import org.apache.sling.testing.junit.rules.InstanceConfigRule;
+import org.apache.sling.testing.junit.rules.util.Action;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+
+// TODO: Use a junit processor to test this
+public class InstanceConfigRuleTest {
+    public boolean actionCalled = false;
+    
+    class DebugAction implements Action {
+        @Override
+        public void call() throws Throwable {
+            actionCalled = true;
+        }
+    }
+    
+    DebugInstanceConfig dic = new DebugInstanceConfig();
+    
+    @Rule
+    public InstanceConfigRule myInstanceConfig = new InstanceConfigRule(dic).withAction(new DebugAction());
+    
+    @Test
+    public void myTest() {
+        Assert.assertEquals("Value should be the one set on save: 1", 1, dic.getValue());
+        Assert.assertTrue(actionCalled);
+    }
+
+
+}
diff --git a/src/test/java/org/apache/sling/testing/junit/rules/util/poller/AbstractPollerTest.java b/src/test/java/org/apache/sling/testing/junit/rules/util/poller/AbstractPollerTest.java
new file mode 100644
index 0000000..9b0de33
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/junit/rules/util/poller/AbstractPollerTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.testing.junit.rules.util.poller;
+
+import org.apache.sling.testing.clients.util.poller.AbstractPoller;
+import org.junit.Assert;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AbstractPollerTest {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractPollerTest.class);
+
+    @Test
+    public void testCallAndWaitSuccess() throws InterruptedException {
+        AbstractPoller poller = new AbstractPoller(100, 5) {
+            int callNumber = 0;
+
+            @Override
+            public boolean call() {
+                return true;
+            }
+
+            @Override
+            public boolean condition() {
+                callNumber += 1;
+                LOG.debug("Call nr " + callNumber);
+                if (callNumber == 4) {
+                    return true;
+                }
+                return false;
+            }
+        };
+        Assert.assertTrue(poller.callAndWait());
+    }
+
+    @Test
+    public void testCallAndWaitFailure() throws InterruptedException {
+        AbstractPoller poller = new AbstractPoller(100, 5) {
+            @Override
+            public boolean call() {
+                return true;
+            }
+
+            @Override
+            public boolean condition() {
+                return false;
+            }
+        };
+        Assert.assertFalse(poller.callAndWait());
+    }
+
+    @Test
+    public void testCallUntilSuccess() throws InterruptedException {
+        AbstractPoller poller = new AbstractPoller(100, 5) {
+            int callNumber = 0;
+
+            @Override
+            public boolean call() {
+                callNumber += 1;
+                LOG.debug("Call nr " + callNumber);
+                return true;
+            }
+
+            @Override
+            public boolean condition() {
+                if (callNumber == 4) {
+                    return true;
+                }
+                return false;
+            }
+        };
+        Assert.assertTrue(poller.callUntilCondition());
+    }
+
+    @Test
+    public void testCallUntilFailure() throws InterruptedException {
+        AbstractPoller poller = new AbstractPoller(100, 5) {
+            @Override
+            public boolean call() {
+                return true;
+            }
+
+            @Override
+            public boolean condition() {
+                return false;
+            }
+        };
+        Assert.assertFalse(poller.callUntilCondition());
+    }
+
+
+}

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.