diff --git a/api/pom.xml b/api/pom.xml
new file mode 100644
index 000000000..7503452d5
--- /dev/null
+++ b/api/pom.xml
@@ -0,0 +1,12 @@
+
+
+
+ smart-testing-parent
+ org.arquillian.smart.testing
+ 0.0.4-SNAPSHOT
+
+ 4.0.0
+
+ api
+
+
diff --git a/api/src/main/java/org/arquillian/smart/testing/strategies/affected/ComponentUnderTest.java b/api/src/main/java/org/arquillian/smart/testing/strategies/affected/ComponentUnderTest.java
new file mode 100644
index 000000000..a139f3ef7
--- /dev/null
+++ b/api/src/main/java/org/arquillian/smart/testing/strategies/affected/ComponentUnderTest.java
@@ -0,0 +1,42 @@
+package org.arquillian.smart.testing.strategies.affected;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation used to set which production classes are tested in current test.
+ * By default it appends all classes defined in all attributes.
+ *
+ * If none of the attributes are set, then all production classes with same package as test and its subpackages are added.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Repeatable(ComponentsUnderTest.class)
+@Documented
+public @interface ComponentUnderTest {
+
+ /**
+ * Packages of classes that needs to be added as tested classes in current test. You can set the package name "org.superbiz" which means only classes defined in this package,
+ * or ending with start (*) operator "org.superbiz.*" which means all classes of current package and its subpackages.
+ * @return Packages containing Java classes.
+ */
+ String[] packages() default {};
+
+ /**
+ * Packages of classes that needs to be added as tested classes in current test. It is used Class to get the package.
+ * Notice that in this case subpackages are not scanned.
+ * @return Packages containing Java classes.
+ */
+ Class[] packagesOf() default {};
+
+ /**
+ * Classes to be added as tested classes in current test.
+ * @return Classes
+ */
+ Class[] classes() default {};
+
+}
diff --git a/api/src/main/java/org/arquillian/smart/testing/strategies/affected/ComponentsUnderTest.java b/api/src/main/java/org/arquillian/smart/testing/strategies/affected/ComponentsUnderTest.java
new file mode 100644
index 000000000..7ca39b471
--- /dev/null
+++ b/api/src/main/java/org/arquillian/smart/testing/strategies/affected/ComponentsUnderTest.java
@@ -0,0 +1,14 @@
+package org.arquillian.smart.testing.strategies.affected;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+public @interface ComponentsUnderTest {
+ ComponentUnderTest[] value();
+}
diff --git a/docs/configuration.adoc b/docs/configuration.adoc
index c14eff4be..294f682ed 100644
--- a/docs/configuration.adoc
+++ b/docs/configuration.adoc
@@ -142,6 +142,36 @@ IMPORTANT: This strategy is currently only applicable for _white box_ testing ap
WARNING: At this moment, this strategy does not work with Java 9.
+===== Explicitly Set
+
+By default affected strategy uses _imports_ of tests to build the graph of collaborators.
+This approach is fine for unit tests (white box tests) but might not work in all cases of high level tests (black box test).
+
+There are some test technologies that allows you to deploy an application locally and then run tests against it.
+For example Wildfly Swarm has `@DefaultDeployment` annotation or Spring (Boot) deploys all application automatically.
+
+This means that production classes are not directly imported into the test, so there is no way to get a relationship between test and production classes.
+For this reason `affected` provides an annotation to set package(s) of production classes that are deployed by the test.
+
+The first thing you need to do is register following artifact into your build script: `org.arquillian.smart.testing:api:`.
+
+Then you can annotate your test with `org.arquillian.smart.testing.strategies.affected.ComponentUnderTest` annotation.
+
+For example:
+
+[source, java]
+----
+include::../strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/ZTest.java[tag=docs]
+----
+
+In previous example all classes belonging to packages and subpackages specified at `packages` attribute are considered as classes used by the test.
+
+You can also use `packageOf` attribute to set a reference class.
+With this attribute all classes that are placed in the same package as the reference class are considered as classes used by the test.
+With this approach your tests are resilient to package name changes.
+
+If none of the attributes are set, then all production classes with same package as test and its subpackages are added automatically as classes used by the test.
+
==== Failed
`Failed` strategy just gets all tests that failed from previous executions and mark them as *important* tests to run first (_ordering_) or not filtered (_selecting_).
diff --git a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/affected/LocalChangesAffectedAnnotationTestsSelectionExecutionFunctionalTest.java b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/affected/LocalChangesAffectedAnnotationTestsSelectionExecutionFunctionalTest.java
new file mode 100644
index 000000000..d336a4b26
--- /dev/null
+++ b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/affected/LocalChangesAffectedAnnotationTestsSelectionExecutionFunctionalTest.java
@@ -0,0 +1,50 @@
+package org.arquillian.smart.testing.ftest.affected;
+
+import java.util.Collection;
+import org.arquillian.smart.testing.ftest.testbed.project.Project;
+import org.arquillian.smart.testing.ftest.testbed.project.TestResults;
+import org.arquillian.smart.testing.ftest.testbed.testresults.TestResult;
+import org.arquillian.smart.testing.mvn.ext.dependencies.ExtensionVersion;
+import org.arquillian.smart.testing.rules.TestBed;
+import org.arquillian.smart.testing.rules.git.GitClone;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.arquillian.smart.testing.ftest.testbed.TestRepository.testRepository;
+import static org.arquillian.smart.testing.ftest.testbed.configuration.Mode.SELECTING;
+import static org.arquillian.smart.testing.ftest.testbed.configuration.Strategy.AFFECTED;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LocalChangesAffectedAnnotationTestsSelectionExecutionFunctionalTest {
+
+ @ClassRule
+ public static final GitClone GIT_CLONE = new GitClone(testRepository());
+
+ @Rule
+ public final TestBed testBed = new TestBed(GIT_CLONE);
+
+ @Test
+ public void should_only_execute_tests_with_affected_changes_annotated() throws Exception {
+ // given
+ final Project project = testBed.getProject();
+
+ project.configureSmartTesting()
+ .executionOrder(AFFECTED)
+ .inMode(SELECTING)
+ .enable();
+
+ final Collection expectedTestResults = project
+ .applyAsLocalChanges("Uses annotation to detect affected classes");
+
+ // when
+ final TestResults actualTestResults = project.build("config/impl-base")
+ .options().withSystemProperties("smart.testing.version", ExtensionVersion.version().toString())
+ .configure()
+ .run();
+
+ // then
+ assertThat(actualTestResults.accumulatedPerTestClass()).containsAll(expectedTestResults).hasSameSizeAs(expectedTestResults);
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index da4235984..340b5dae9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,6 +47,7 @@
3.8.0
1.16.1
3.21.0-GA
+ 2.7.4
1.0.1
3.0.0-beta-2
1.19
@@ -70,6 +71,7 @@
core
+ api
surefire-provider
junit-test-result-parser
strategies/affected
@@ -127,6 +129,11 @@
snakeyaml
${version.snakeyaml}
+
+ io.github.lukehutch
+ fast-classpath-scanner
+ ${version.fast-classpath-scanner}
+
diff --git a/strategies/affected/pom.xml b/strategies/affected/pom.xml
index c95ff1521..8fa8f180e 100644
--- a/strategies/affected/pom.xml
+++ b/strategies/affected/pom.xml
@@ -16,6 +16,11 @@
core
${project.version}
+
+ org.arquillian.smart.testing
+ api
+ ${project.version}
+
org.arquillian.smart.testing
strategy-changed
@@ -29,6 +34,10 @@
org.jgrapht
jgrapht-core
+
+ io.github.lukehutch
+ fast-classpath-scanner
+
junit
junit
diff --git a/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ClassDependenciesGraph.java b/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ClassDependenciesGraph.java
index 82e6d6191..1ea401106 100644
--- a/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ClassDependenciesGraph.java
+++ b/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ClassDependenciesGraph.java
@@ -27,14 +27,19 @@
*/
package org.arquillian.smart.testing.strategies.affected;
+import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
import java.io.File;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.arquillian.smart.testing.api.TestVerifier;
+import org.arquillian.smart.testing.logger.Log;
+import org.arquillian.smart.testing.logger.Logger;
import org.arquillian.smart.testing.configuration.Configuration;
import org.arquillian.smart.testing.strategies.affected.ast.JavaClass;
import org.arquillian.smart.testing.strategies.affected.ast.JavaClassBuilder;
@@ -47,6 +52,7 @@
public class ClassDependenciesGraph {
+ private static final Logger logger = Log.getLogger();
private static final Filter coreJava = new Filter(Collections.singletonList(""), Collections.singletonList("java.*"));
private final JavaClassBuilder builder;
@@ -77,14 +83,86 @@ void buildTestDependencyGraph(Collection testJavaFiles) {
// Then find dependencies
for (String changedTestClassNames : testClassesNames) {
- JavaClass javaClass = builder.getClassDescription(changedTestClassNames);
- if (javaClass != null) {
- addToIndex(new JavaElement(javaClass), javaClass.getImports());
+ JavaClass testJavaClass = builder.getClassDescription(changedTestClassNames);
+ if (testJavaClass != null) {
+ final String[] imports = testJavaClass.getImports();
+ final List manualProductionClasses = calculateManualAddedDependencies(testJavaClass);
+ manualProductionClasses.addAll(Arrays.asList(imports));
+ addToIndex(new JavaElement(testJavaClass), manualProductionClasses);
}
}
}
- private void addToIndex(JavaElement javaElement, String[] imports) {
+ private List calculateManualAddedDependencies(JavaClass testJavaClass) {
+ final List manualDependencyClasses = new ArrayList<>();
+ final ComponentUnderTest[] allTestsAnnotation = getAllAnnotations(testJavaClass);
+
+ for (ComponentUnderTest tests : allTestsAnnotation) {
+ List packages = getPackages(testJavaClass.packageName(), tests);
+ for (String pkg : packages) {
+ final String trimmedPackage = pkg.trim();
+ manualDependencyClasses.addAll(scanClassesFromPackage(trimmedPackage));
+ }
+ }
+
+ return manualDependencyClasses;
+
+ }
+
+ private ComponentUnderTest[] getAllAnnotations(JavaClass testJavaClass) {
+
+ final Optional testsListOptional = testJavaClass.getAnnotationByType(ComponentsUnderTest.class);
+
+ ComponentUnderTest[] tests = testsListOptional
+ .map(ComponentsUnderTest::value)
+ .orElseGet(() -> testJavaClass.getAnnotationByType(ComponentUnderTest.class)
+ .map(annotation -> new ComponentUnderTest[] {annotation})
+ .orElse(new ComponentUnderTest[0]));
+
+
+ return tests;
+ }
+
+ private List scanClassesFromPackage(String trimmedPackage) {
+ final List manualDependencyClasses = new ArrayList<>();
+ if (trimmedPackage.endsWith(".*")) {
+ String realPackage = trimmedPackage.substring(0, trimmedPackage.indexOf(".*"));
+ final List classesOfPackage =
+ new FastClasspathScanner(realPackage).scan()
+ .getNamesOfAllClasses();
+
+ manualDependencyClasses.addAll(
+ classesOfPackage);
+ } else {
+ final List classesOfPackage =
+ new FastClasspathScanner(trimmedPackage).disableRecursiveScanning().scan()
+ .getNamesOfAllClasses();
+ manualDependencyClasses.addAll(
+ classesOfPackage);
+ }
+
+ if (manualDependencyClasses.isEmpty()) {
+ logger.warn("You set %s package as reference classes to run tests, but no classes found. Maybe a package refactor?", trimmedPackage);
+ }
+
+ return manualDependencyClasses;
+ }
+
+ private List getPackages(String testPackage, ComponentUnderTest tests) {
+ List packages = new ArrayList<>();
+ if (tests.classes().length == 0 && tests.packages().length == 0 && tests.packagesOf().length == 0) {
+ packages.add(testPackage + ".*");
+ } else {
+ packages.addAll(Arrays.asList(tests.packages()));
+
+ packages.addAll(Arrays.stream(tests.packagesOf())
+ .map(clazz -> clazz.getPackage().getName())
+ .collect(Collectors.toList()));
+ }
+ return packages;
+ }
+
+ private void addToIndex(JavaElement javaElement, List imports) {
addToGraph(javaElement);
updateJavaElementWithImportReferences(javaElement, imports);
}
@@ -105,14 +183,14 @@ private void replaceVertex(JavaElement newClass) {
}
}
- private void updateJavaElementWithImportReferences(JavaElement javaElementParentClass, String[] imports) {
+ private void updateJavaElementWithImportReferences(JavaElement javaElementParentClass, List imports) {
for (String importz : imports) {
if (addImport(javaElementParentClass, importz) && filter.shouldBeIncluded(importz) && this.enableTransitivity) {
JavaClass javaClass = builder.getClassDescription(importz);
if (javaClass != null) {
- updateJavaElementWithImportReferences(javaElementParentClass, javaClass.getImports());
+ updateJavaElementWithImportReferences(javaElementParentClass, Arrays.asList(javaClass.getImports()));
}
}
}
diff --git a/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ast/JavaAssistClass.java b/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ast/JavaAssistClass.java
index 4fb8ff661..272ea1a6a 100644
--- a/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ast/JavaAssistClass.java
+++ b/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ast/JavaAssistClass.java
@@ -31,6 +31,7 @@
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import javassist.CtClass;
import javassist.CtField;
@@ -53,10 +54,12 @@ public class JavaAssistClass extends AbstractJavaClass {
private final String[] imports;
private final String className;
private File classFile;
+ private final CtClass classReference;
JavaAssistClass(CtClass classReference) {
imports = findImports(classReference);
className = classReference.getName();
+ this.classReference = classReference;
}
@Override
@@ -163,6 +166,11 @@ public String getName() {
return className;
}
+ @Override
+ public String packageName() {
+ return classReference.getPackageName();
+ }
+
@Override
public String toString() {
return getName();
@@ -176,4 +184,13 @@ public void setClassFile(File classFile) {
public File getClassFile() {
return classFile;
}
+
+ @Override
+ public Optional getAnnotationByType(Class type) {
+ try {
+ return Optional.ofNullable((T) this.classReference.getAnnotation(type));
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(e);
+ }
+ }
}
diff --git a/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ast/JavaClass.java b/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ast/JavaClass.java
index 22f459406..c8e417f28 100644
--- a/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ast/JavaClass.java
+++ b/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ast/JavaClass.java
@@ -28,10 +28,12 @@
package org.arquillian.smart.testing.strategies.affected.ast;
import java.io.File;
+import java.util.Optional;
public interface JavaClass {
String getName();
+ String packageName();
/**
* Gets the collection on classes that this class depends on. i.e. the list
* of this classes children.
@@ -39,4 +41,7 @@ public interface JavaClass {
String[] getImports();
File getClassFile();
+
+ Optional getAnnotationByType(Class type);
+
}
diff --git a/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ast/UnparsableClass.java b/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ast/UnparsableClass.java
index 6b38a7d35..40dde4697 100644
--- a/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ast/UnparsableClass.java
+++ b/strategies/affected/src/main/java/org/arquillian/smart/testing/strategies/affected/ast/UnparsableClass.java
@@ -28,6 +28,7 @@
package org.arquillian.smart.testing.strategies.affected.ast;
import java.io.File;
+import java.util.Optional;
public class UnparsableClass implements JavaClass {
private static final String[] NO_IMPORT = new String[0];
@@ -43,6 +44,11 @@ public File getClassFile() {
return null;
}
+ @Override
+ public Optional getAnnotationByType(Class type) {
+ return Optional.empty();
+ }
+
@Override
public String[] getImports() {
return NO_IMPORT;
@@ -53,6 +59,11 @@ public String getName() {
return classname;
}
+ @Override
+ public String packageName() {
+ return "";
+ }
+
@Override
public String toString() {
return "UnparsableClass{" + "classname='" + classname + '\'' + '}';
diff --git a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/ClassDependenciesGraphTest.java b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/ClassDependenciesGraphTest.java
index 9b4da8424..a277787a2 100644
--- a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/ClassDependenciesGraphTest.java
+++ b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/ClassDependenciesGraphTest.java
@@ -5,15 +5,21 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
+import org.arquillian.smart.testing.strategies.affected.fakeproject.main.A;
import org.arquillian.smart.testing.configuration.Configuration;
import org.arquillian.smart.testing.configuration.ConfigurationLoader;
import org.arquillian.smart.testing.strategies.affected.fakeproject.main.D;
import org.arquillian.smart.testing.strategies.affected.fakeproject.main.MyBusinessObject;
import org.arquillian.smart.testing.strategies.affected.fakeproject.main.MyControllerObject;
+import org.arquillian.smart.testing.strategies.affected.fakeproject.main.superbiz.Alone;
+import org.arquillian.smart.testing.strategies.affected.fakeproject.main.superbiz.component.Unwanted;
import org.arquillian.smart.testing.strategies.affected.fakeproject.test.ATest;
import org.arquillian.smart.testing.strategies.affected.fakeproject.test.BTest;
import org.arquillian.smart.testing.strategies.affected.fakeproject.test.CTest;
import org.arquillian.smart.testing.strategies.affected.fakeproject.test.MyBusinessObjectTest;
+import org.arquillian.smart.testing.strategies.affected.fakeproject.test.MySecondBusinessObjectTest;
+import org.arquillian.smart.testing.strategies.affected.fakeproject.test.YTest;
+import org.arquillian.smart.testing.strategies.affected.fakeproject.test.ZTest;
import org.junit.Test;
import static java.util.Collections.singletonList;
@@ -31,12 +37,12 @@ public void should_detect_simple_test_to_execute() {
final ClassDependenciesGraph
classDependenciesGraph = new ClassDependenciesGraph(new EndingWithTestTestVerifier(), configuration);
- final String testLocation = MyBusinessObjectTest.class.getResource("MyBusinessObjectTest.class").getPath();
+ final String testLocation = getClassLocation(MyBusinessObjectTest.class);
classDependenciesGraph.buildTestDependencyGraph(singletonList(new File(testLocation)));
// when
Set mainObjectsChanged = new HashSet<>();
- mainObjectsChanged.add(new File(MyBusinessObject.class.getResource("MyBusinessObject.class").getPath()));
+ mainObjectsChanged.add(new File(getClassLocation(MyBusinessObject.class)));
final Set testsDependingOn = classDependenciesGraph.findTestsDependingOn(mainObjectsChanged);
@@ -58,7 +64,7 @@ public void should_detect_multiple_tests_to_execute_against_same_main_class() {
// when
Set mainObjectsChanged = new HashSet<>();
- mainObjectsChanged.add(new File(MyBusinessObject.class.getResource("MyBusinessObject.class").getPath()));
+ mainObjectsChanged.add(new File(getClassLocation(MyBusinessObject.class)));
final Set testsDependingOn = classDependenciesGraph.findTestsDependingOn(mainObjectsChanged);
@@ -81,7 +87,7 @@ public void should_detect_test_with_multiple_main_classes() {
// when
Set mainObjectsChanged = new HashSet<>();
- mainObjectsChanged.add(new File(MyControllerObject.class.getResource("MyControllerObject.class").getPath()));
+ mainObjectsChanged.add(new File(getClassLocation(MyControllerObject.class)));
final Set testsDependingOn = classDependenciesGraph.findTestsDependingOn(mainObjectsChanged);
@@ -103,8 +109,8 @@ public void should_detect_multiple_tests_to_execute_against_same_main_class_avoi
// when
Set mainObjectsChanged = new HashSet<>();
- mainObjectsChanged.add(new File(MyBusinessObject.class.getResource("MyBusinessObject.class").getPath()));
- mainObjectsChanged.add(new File(MyControllerObject.class.getResource("MyControllerObject.class").getPath()));
+ mainObjectsChanged.add(new File(getClassLocation(MyBusinessObject.class)));
+ mainObjectsChanged.add(new File(getClassLocation(MyControllerObject.class)));
final Set testsDependingOn = classDependenciesGraph.findTestsDependingOn(mainObjectsChanged);
@@ -128,7 +134,7 @@ public void should_detect_all_changes_transitive() {
// when
Set mainObjectsChanged = new HashSet<>();
- mainObjectsChanged.add(new File(D.class.getResource("D.class").getPath()));
+ mainObjectsChanged.add(new File(getClassLocation(D.class)));
final Set testsDependingOn = classDependenciesGraph.findTestsDependingOn(mainObjectsChanged);
@@ -138,6 +144,53 @@ public void should_detect_all_changes_transitive() {
"org.arquillian.smart.testing.strategies.affected.fakeproject.test.ATest", "org.arquillian.smart.testing.strategies.affected.fakeproject.test.BTest");
}
+ @Test
+ public void should_detect_all_changes_adding_package_annotated_transitive() {
+ // given
+ final Configuration configuration = ConfigurationLoader.load();
+ configuration.loadStrategyConfigurations(AFFECTED);
+
+ final ClassDependenciesGraph
+ classDependenciesGraph = new ClassDependenciesGraph(new EndingWithTestTestVerifier(), configuration);
+
+ final String testLocation = getClassLocation(ZTest.class);
+ classDependenciesGraph.buildTestDependencyGraph(Arrays.asList(new File(testLocation)));
+
+ // when
+ Set mainObjectsChanged = new HashSet<>();
+ mainObjectsChanged.add(new File(getClassLocation(Unwanted.class)));
+
+ final Set testsDependingOn = classDependenciesGraph.findTestsDependingOn(mainObjectsChanged);
+
+ // then
+ assertThat(testsDependingOn)
+ .containsExactlyInAnyOrder(
+ "org.arquillian.smart.testing.strategies.affected.fakeproject.test.ZTest");
+ }
+
+ @Test
+ public void should_detect_all_changes_adding_class_package_annotated_transitive() {
+ // given
+ final Configuration configuration = ConfigurationLoader.load();
+ configuration.loadStrategyConfigurations(AFFECTED);
+ final ClassDependenciesGraph
+ classDependenciesGraph = new ClassDependenciesGraph(new EndingWithTestTestVerifier(), configuration);
+
+ final String testLocation = getClassLocation(YTest.class);
+ classDependenciesGraph.buildTestDependencyGraph(Arrays.asList(new File(testLocation)));
+
+ // when
+ Set mainObjectsChanged = new HashSet<>();
+ mainObjectsChanged.add(new File(getClassLocation(Alone.class)));
+
+ final Set testsDependingOn = classDependenciesGraph.findTestsDependingOn(mainObjectsChanged);
+
+ // then
+ assertThat(testsDependingOn)
+ .containsExactlyInAnyOrder(
+ "org.arquillian.smart.testing.strategies.affected.fakeproject.test.YTest");
+ }
+
@Test
public void should_not_detect_all_changes_transitive_if_transitivity_is_disabled() {
// given
@@ -153,7 +206,7 @@ public void should_not_detect_all_changes_transitive_if_transitivity_is_disabled
// when
Set mainObjectsChanged = new HashSet<>();
- mainObjectsChanged.add(new File(D.class.getResource("D.class").getPath()));
+ mainObjectsChanged.add(new File(getClassLocation(D.class)));
final Set testsDependingOn = classDependenciesGraph.findTestsDependingOn(mainObjectsChanged);
@@ -178,7 +231,7 @@ public void should_exclude_imports_if_property_set() {
// when
Set mainObjectsChanged = new HashSet<>();
- mainObjectsChanged.add(new File(D.class.getResource("D.class").getPath()));
+ mainObjectsChanged.add(new File(getClassLocation(D.class)));
final Set testsDependingOn = classDependenciesGraph.findTestsDependingOn(mainObjectsChanged);
@@ -203,7 +256,7 @@ public void should_include_only_imports_if_property_set() {
// when
Set mainObjectsChanged = new HashSet<>();
- mainObjectsChanged.add(new File(D.class.getResource("A.class").getPath()));
+ mainObjectsChanged.add(new File(getClassLocation(A.class)));
final Set testsDependingOn = classDependenciesGraph.findTestsDependingOn(mainObjectsChanged);
@@ -214,16 +267,20 @@ public void should_include_only_imports_if_property_set() {
}
private void buildTestDependencyGraphWithTestLocation(ClassDependenciesGraph classDependenciesGraph) {
- final String testLocation = ATest.class.getResource("ATest.class").getPath();
- final String testLocation2 = BTest.class.getResource("BTest.class").getPath();
- final String testLocation3 = CTest.class.getResource("CTest.class").getPath();
+ final String testLocation = getClassLocation(ATest.class);
+ final String testLocation2 = getClassLocation(BTest.class);
+ final String testLocation3 = getClassLocation(CTest.class);
classDependenciesGraph.buildTestDependencyGraph(Arrays.asList(new File(testLocation), new File(testLocation2),
new File(testLocation3)));
}
private void buildTestDependencyGraphWithLocation(ClassDependenciesGraph classDependenciesGraph) {
- final String testLocation = MyBusinessObjectTest.class.getResource("MyBusinessObjectTest.class").getPath();
- final String testLocation2 = MyBusinessObjectTest.class.getResource("MySecondBusinessObjectTest.class").getPath();
+ final String testLocation = getClassLocation(MyBusinessObjectTest.class);
+ final String testLocation2 = getClassLocation(MySecondBusinessObjectTest.class);
classDependenciesGraph.buildTestDependencyGraph(Arrays.asList(new File(testLocation), new File(testLocation2)));
}
+
+ private String getClassLocation(Class> clazz) {
+ return clazz.getResource(clazz.getSimpleName() + ".class").getPath();
+ }
}
diff --git a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/main/superbiz/Alone.java b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/main/superbiz/Alone.java
new file mode 100644
index 000000000..9e6e8f9fd
--- /dev/null
+++ b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/main/superbiz/Alone.java
@@ -0,0 +1,4 @@
+package org.arquillian.smart.testing.strategies.affected.fakeproject.main.superbiz;
+
+public class Alone {
+}
diff --git a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/main/superbiz/component/Unwanted.java b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/main/superbiz/component/Unwanted.java
new file mode 100644
index 000000000..8e335da9c
--- /dev/null
+++ b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/main/superbiz/component/Unwanted.java
@@ -0,0 +1,4 @@
+package org.arquillian.smart.testing.strategies.affected.fakeproject.main.superbiz.component;
+
+public class Unwanted {
+}
diff --git a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/ATest.java b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/ATest.java
index 2dfa26400..8e956dc07 100644
--- a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/ATest.java
+++ b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/ATest.java
@@ -4,8 +4,7 @@
import org.junit.Ignore;
import org.junit.Test;
-// Test ignored because it is a test that is used to in tests and not to be run as real test by test runner
-@Ignore
+@Ignore("Test ignored because it is used internally")
public class ATest {
private A a;
diff --git a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/BTest.java b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/BTest.java
index ef4f6d93b..d58798c8e 100644
--- a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/BTest.java
+++ b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/BTest.java
@@ -4,8 +4,7 @@
import org.junit.Ignore;
import org.junit.Test;
-// Test ignored because it is a test that is used to in tests and not to be run as real test by test runner
-@Ignore
+@Ignore("Test ignored because it is used internally")
public class BTest {
private B b;
diff --git a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/CTest.java b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/CTest.java
index fca32a88c..abbc225fb 100644
--- a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/CTest.java
+++ b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/CTest.java
@@ -4,8 +4,7 @@
import org.junit.Ignore;
import org.junit.Test;
-// Test ignored because it is a test that is used to in tests and not to be run as real test by test runner
-@Ignore
+@Ignore("Test ignored because it is used internally")
public class CTest {
private C c;
diff --git a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/MyBusinessObjectTest.java b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/MyBusinessObjectTest.java
index de86d2b48..e83bef77f 100644
--- a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/MyBusinessObjectTest.java
+++ b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/MyBusinessObjectTest.java
@@ -4,8 +4,7 @@
import org.junit.Ignore;
import org.junit.Test;
-// Test ignored because it is a test that is used to in tests and not to be run as real test by test runner
-@Ignore
+@Ignore("Test ignored because it is used internally")
public class MyBusinessObjectTest {
private MyBusinessObject myBusinessObject;
diff --git a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/MyBusinessObjectTestCase.java b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/MyBusinessObjectTestCase.java
index d7e4ba97e..24c3ade2c 100644
--- a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/MyBusinessObjectTestCase.java
+++ b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/MyBusinessObjectTestCase.java
@@ -4,8 +4,7 @@
import org.junit.Ignore;
import org.junit.Test;
-// Test ignored because it is a test that is used to in tests and not to be run as real test by test runner
-@Ignore
+@Ignore("Test ignored because it is used internally")
public class MyBusinessObjectTestCase {
private MyBusinessObject myBusinessObject;
diff --git a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/MySecondBusinessObjectTest.java b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/MySecondBusinessObjectTest.java
index 087059f89..893c31e58 100644
--- a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/MySecondBusinessObjectTest.java
+++ b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/MySecondBusinessObjectTest.java
@@ -5,8 +5,7 @@
import org.junit.Ignore;
import org.junit.Test;
-// Test ignored because it is a test that is used to in tests and not to be run as real test by test runner
-@Ignore
+@Ignore("Test ignored because it is used internally")
public class MySecondBusinessObjectTest {
private MyBusinessObject myBusinessObject;
diff --git a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/YTest.java b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/YTest.java
new file mode 100644
index 000000000..a8512e2bf
--- /dev/null
+++ b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/YTest.java
@@ -0,0 +1,17 @@
+package org.arquillian.smart.testing.strategies.affected.fakeproject.test;
+
+import org.arquillian.smart.testing.strategies.affected.ComponentUnderTest;
+import org.arquillian.smart.testing.strategies.affected.fakeproject.main.superbiz.Alone;
+import org.junit.Ignore;
+import org.junit.Test;
+
+@Ignore("Test ignored because it is used internally")
+@ComponentUnderTest(packagesOf = Alone.class)
+public class YTest {
+
+ @Test
+ public void black_box() {
+ System.out.println("Black Box Testing");
+ }
+
+}
diff --git a/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/ZTest.java b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/ZTest.java
new file mode 100644
index 000000000..fb505e816
--- /dev/null
+++ b/strategies/affected/src/test/java/org/arquillian/smart/testing/strategies/affected/fakeproject/test/ZTest.java
@@ -0,0 +1,17 @@
+package org.arquillian.smart.testing.strategies.affected.fakeproject.test;
+
+import org.arquillian.smart.testing.strategies.affected.ComponentUnderTest;
+import org.junit.Ignore;
+import org.junit.Test;
+
+@Ignore("Test ignored because it is used internally")
+//tag::docs[]
+@ComponentUnderTest(packages = "org.arquillian.smart.testing.strategies.affected.fakeproject.main.superbiz.*")
+public class ZTest {
+//end::docs[]
+ @Test
+ public void black_box() {
+ System.out.println("Black Box Testing");
+ }
+
+}