Skip to content
This repository has been archived by the owner on Jun 28, 2024. It is now read-only.

Issue#133 - Can't run Parameterized tests #184

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.shazam.forktest;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;

import com.shazam.forktest.ExcludedAnnotation;

import java.util.Arrays;
import java.util.Collection;

@RunWith(Parameterized.class)
public class ParameterizedClassTest {
private String id;
private boolean flag;

@Parameterized.Parameters(name = "{0}({1})")
public static Collection params() {
return Arrays.asList(new Object[][] {
{ "first", false },
{ "second", true },
{ "third", false },
});
}

@Test
public void test() {
assertEquals(flag, ("second".equals(id)));
}
}
14 changes: 14 additions & 0 deletions fork-common/src/main/java/com/shazam/fork/model/TestCaseEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ public class TestCaseEvent {
@Nonnull
private final Map<String, String> properties;

private final boolean isParameterized;

private TestCaseEvent(Builder builder) {
this.testClass = builder.testClass;
this.testMethod = builder.testMethod;
this.isIgnored = builder.isIgnored;
this.permissionsToRevoke = builder.permissionsToRevoke;
this.properties = builder.properties;
this.isParameterized = builder.isParameterized;
}

@Nonnull
Expand Down Expand Up @@ -68,6 +71,10 @@ public Map<String, String> getProperties() {
return unmodifiableMap(properties);
}

public boolean isParameterized() {
return isParameterized;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down Expand Up @@ -103,6 +110,7 @@ public static class Builder {
private boolean isIgnored;
private List<String> permissionsToRevoke = new ArrayList<>();
private Map<String, String> properties = new HashMap<>();
private boolean isParameterized = false;

@Nonnull
public static Builder testCaseEvent(@Nonnull TestIdentifier testIdentifier) {
Expand Down Expand Up @@ -148,6 +156,12 @@ public Builder withProperties(@Nonnull Map<String, String> properties) {
return this;
}

@Nonnull
public Builder withIsParameterized(boolean flag) {
this.isParameterized = flag;
return this;
}

@Nonnull
public TestCaseEvent build() {
return new TestCaseEvent(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.shazam.fork.io.DexFileExtractor;
import com.shazam.fork.model.TestCaseEvent;

import com.shazam.fork.utils.ParameterizedTestDetector;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.AnnotationElement;
import org.jf.dexlib2.iface.ClassDef;
Expand All @@ -28,6 +29,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -37,6 +39,7 @@

import static com.shazam.fork.model.TestCaseEvent.Builder.testCaseEvent;
import static java.lang.Math.min;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;

public class TestSuiteLoader {
Expand Down Expand Up @@ -74,6 +77,13 @@ public Collection<TestCaseEvent> loadTestSuite() throws NoTestCasesFoundExceptio
if (testCaseEvents.isEmpty()) {
throw new NoTestCasesFoundException("No tests cases were found in the test APK: " + instrumentationApkFile.getAbsolutePath());
}

// put parameterized tests to begin of queue to run them first
// because most probably they will require more time than any regular test
Map<Boolean, List<TestCaseEvent>> testEventsByParametrizedFlag = testCaseEvents.stream()
.collect(groupingBy(TestCaseEvent::isParameterized, toList()));
testCaseEvents = testEventsByParametrizedFlag.getOrDefault(true, new ArrayList<>());
testCaseEvents.addAll(testEventsByParametrizedFlag.getOrDefault(false, new ArrayList<>()));
return testCaseEvents;
}

Expand All @@ -82,9 +92,23 @@ private List<TestCaseEvent> convertClassToTestCaseEvents(ClassDef classDefItem)
List<TestCaseEvent> testCaseEvents = new ArrayList<>();

Iterable<? extends Method> methods = classDefItem.getMethods();
StreamSupport.stream(methods.spliterator(), false)
.filter(method -> method.getAnnotations().stream().anyMatch(annotation -> TEST_ANNOTATION.equals(annotation.getType())))
.map(method -> convertToTestCaseEvent(classDefItem, method)).forEach(testCaseEvents::add);
if (ParameterizedTestDetector.isParameterizedClass(classDefItem)) {
return Collections.singletonList(
TestCaseEvent.Builder.testCaseEvent()
.withIsParameterized(true)
.withTestClass(getClassName(classDefItem))
.withTestMethod("")
// we can't properly filter tests in parameterized classes
// because we invoke whole class, not methods
// so can check only marker for class
.withIsIgnored(isClassIgnored(classDefItem))
.build()
);
} else {
StreamSupport.stream(methods.spliterator(), false)
.filter(method -> method.getAnnotations().stream().anyMatch(annotation -> TEST_ANNOTATION.equals(annotation.getType())))
.map(method -> convertToTestCaseEvent(classDefItem, method)).forEach(testCaseEvents::add);
}

return testCaseEvents;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.shazam.fork.utils;

import org.jf.dexlib2.ValueType;
import org.jf.dexlib2.dexbacked.value.DexBackedTypeEncodedValue;
import org.jf.dexlib2.iface.ClassDef;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

public class ParameterizedTestDetector {
public static final String RUN_WITH_ANNOTATION = "Lorg/junit/runner/RunWith;";
public static final Set<String> PARAMETRIZED_ANNOTATIONS = new HashSet<>(Arrays.asList(
"Lorg/junit/runners/Parameterized;",
"Lio/qameta/allure/kotlin/junit4/AllureParametrizedRunner;"
));

public static boolean isParameterizedClass(ClassDef classDef) {
Optional<DexBackedTypeEncodedValue> encodedValue = classDef.getAnnotations().stream()
.filter(it -> RUN_WITH_ANNOTATION.equals(it.getType()))
.flatMap(it -> it.getElements().stream())
.filter(it ->
it.getValue().getValueType() == ValueType.TYPE
&& it.getValue() instanceof DexBackedTypeEncodedValue)
.findFirst()
.map(it -> (DexBackedTypeEncodedValue) it.getValue());
return encodedValue.isPresent()
&& PARAMETRIZED_ANNOTATIONS.contains(encodedValue.get().getValue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import javax.annotation.Nonnull;
import java.io.File;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -34,6 +35,7 @@
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.is;

/**
* This test is based on the <code>tests.dex</code> file, which contains test classes with the following code:
Expand Down Expand Up @@ -160,6 +162,15 @@ public void populatesTestProperties() throws Exception {
sameTestEventAs("methodWithUnmatchedKey", testClass, singlePropertyMap)));
}

@SuppressWarnings("unchecked")
@Test
public void checkParameterizedTest() throws Exception {
Collection<TestCaseEvent> testCaseEvents = testSuiteLoader.loadTestSuite();
assertThat(testCaseEvents.stream().filter(TestCaseEvent::isParameterized).count(), is(1L));
assertThat(testCaseEvents, hasItems(
sameTestEventAs("com.shazam.forktest.ParameterizedClassTest", true)));
}

@Nonnull
private Matcher<TestCaseEvent> sameTestEventAs(String testMethod, String testClass, Map<String, String> properties) {
return sameBeanAs(
Expand All @@ -171,6 +182,16 @@ private Matcher<TestCaseEvent> sameTestEventAs(String testMethod, String testCla
);
}

private Matcher<TestCaseEvent> sameTestEventAs(String testClass, boolean isParameterized) {
return sameBeanAs(
testCaseEvent()
.withTestClass(testClass)
.withTestMethod("")
.withIsParameterized(isParameterized)
.build()
);
}

@Nonnull
private Matcher<TestCaseEvent> sameTestEventAs(String testMethod, String testClass, boolean isIgnored) {
return sameBeanAs(
Expand Down
Binary file modified fork-common/src/test/resources/app-debug.apk
Binary file not shown.
Binary file modified fork-common/src/test/resources/tests.dex
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ private static List<TestResult> getFatalCrashedTests(Collection<TestResult> proc
.map(testResult -> new TestResultItem(testResult.getTestClass(), testResult.getTestMethod()))
.collect(toSet());
Set<TestResultItem> allTests = testCases.stream()
.filter(it -> !it.isParameterized())
.map(testCaseEvent -> new TestResultItem(testCaseEvent.getTestClass(), testCaseEvent.getTestMethod()))
.collect(toSet());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ public void execute() {
runner.setTestSize(testSize);
}
runner.setRunName(poolName);
runner.setMethodName(testClassName, testMethodName);
if (test.isParameterized()) {
runner.setClassName(testClassName);
} else {
runner.setMethodName(testClassName, testMethodName);
}
runner.setMaxtimeToOutputResponse(testRunParameters.getTestOutputTimeout());

if (testRunParameters.isCoverageEnabled()) {
Expand Down