diff --git a/README.md b/README.md index 236470ec..4ec2d350 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,7 @@ characteristic | Characteristic | Possible values: "sw"\|"ap groups | Map<String, Integer> | map the name of a pool to their lowest dimension for a characteristic ## Runtime Permissions -By default fork auto-grants all runtime permissions on Android Marshmallow +. It is possible anyway to selectively revoke one or more permissions per single test case. +By default Fork auto-grants all runtime permissions on Android Marshmallow +. It is possible anyway to selectively revoke one or more permissions per single test case. To do so, you have to add an annotation called `RevokePermission`. Here is an example: ```java @Test @@ -159,18 +159,16 @@ After every test case, all the runtime permissions will be automatically re-gran This feature will impact only Marshmallow and subsequent devices. ## Arbitrary metadata -Fork supports adding arbitrary metadata on any test and get them returned as `properties` on the suite level of the JUnit xml report. +Using Fork you can set metadata on tests and get them back in its JUnit xml reports. The metadata are added as additional `property` tags on the suite level of the report, as each test produces its own report. ```java @Test -@TestProperty(key = "key1", value = "value1") -@TestProperty(key = "key2", value = "value2") +@TestProperties(keys = {"k1", "k2"}, values = {"v1", "v2"}) public void testWithProperties() { // Test normally here } ``` - -This feature relies on Repeated Annotations and therefore requires Java 8. +Note that Fork will stop adding pairs after it encounters an unpaired key or value, so make sure you have the same number of keys and values. ## Examples diff --git a/fork-client/src/main/java/com/shazam/fork/TestProperties.java b/fork-client/src/main/java/com/shazam/fork/TestProperties.java index 67b71ef7..c593e55f 100644 --- a/fork-client/src/main/java/com/shazam/fork/TestProperties.java +++ b/fork-client/src/main/java/com/shazam/fork/TestProperties.java @@ -9,5 +9,6 @@ @Retention(RetentionPolicy.RUNTIME) @Target({METHOD}) public @interface TestProperties { - TestProperty[] value() default {}; + String[] keys() default {}; + String[] values() default {}; } diff --git a/fork-client/src/main/java/com/shazam/fork/TestProperty.java b/fork-client/src/main/java/com/shazam/fork/TestProperty.java deleted file mode 100644 index 3ee02c57..00000000 --- a/fork-client/src/main/java/com/shazam/fork/TestProperty.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.shazam.fork; - -import java.lang.annotation.Repeatable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; - -@Retention(RetentionPolicy.RUNTIME) -@Target({METHOD}) -@Repeatable(value = TestProperties.class) -public @interface TestProperty { - String key(); - String value(); -} diff --git a/fork-common-test-dex/app/build.gradle b/fork-common-test-dex/app/build.gradle index 1aa61319..173d8938 100644 --- a/fork-common-test-dex/app/build.gradle +++ b/fork-common-test-dex/app/build.gradle @@ -12,8 +12,8 @@ android { testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 } buildTypes { release { diff --git a/fork-common-test-dex/app/src/androidTest/java/com/shazam/forktest/PropertiesClassTest.java b/fork-common-test-dex/app/src/androidTest/java/com/shazam/forktest/PropertiesClassTest.java index 0e935d88..1f645224 100644 --- a/fork-common-test-dex/app/src/androidTest/java/com/shazam/forktest/PropertiesClassTest.java +++ b/fork-common-test-dex/app/src/androidTest/java/com/shazam/forktest/PropertiesClassTest.java @@ -1,6 +1,6 @@ package com.shazam.forktest; -import com.shazam.fork.TestProperty; +import com.shazam.fork.TestProperties; import org.junit.Ignore; import org.junit.Test; @@ -10,14 +10,13 @@ public class PropertiesClassTest { @Test - @TestProperty(key = "foo", value = "bar") + @TestProperties(keys = {"foo"}, values = {"bar"}) public void methodWithProperties() { assertEquals(4, 2 + 2); } @Test - @TestProperty(key = "foo", value = "bar") - @TestProperty(key = "bux", value = "poi") + @TestProperties(keys = {"foo", "bux"}, values = {"bar", "poi"}) public void methodWithMultipleProperties() { assertEquals(4, 2 + 2); } diff --git a/fork-common/src/main/java/com/shazam/fork/suite/TestSuiteLoader.java b/fork-common/src/main/java/com/shazam/fork/suite/TestSuiteLoader.java index 937658e9..04532b2b 100644 --- a/fork-common/src/main/java/com/shazam/fork/suite/TestSuiteLoader.java +++ b/fork-common/src/main/java/com/shazam/fork/suite/TestSuiteLoader.java @@ -14,8 +14,8 @@ import com.shazam.fork.model.TestCaseEvent; import org.jf.dexlib.*; import org.jf.dexlib.EncodedValue.AnnotationEncodedSubValue; -import org.jf.dexlib.EncodedValue.AnnotationEncodedValue; import org.jf.dexlib.EncodedValue.ArrayEncodedValue; +import org.jf.dexlib.EncodedValue.EncodedValue; import org.jf.dexlib.EncodedValue.StringEncodedValue; import javax.annotation.Nonnull; @@ -23,17 +23,16 @@ import java.util.*; import static com.shazam.fork.model.TestCaseEvent.newTestCase; +import static java.lang.Math.min; import static java.util.Arrays.stream; import static java.util.Collections.emptyList; import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toMap; public class TestSuiteLoader { private static final String TEST_ANNOTATION = "Lorg/junit/Test;"; private static final String IGNORE_ANNOTATION = "Lorg/junit/Ignore;"; private static final String REVOKE_PERMISSION_ANNOTATION = "Lcom/shazam/fork/RevokePermission;"; private static final String TEST_PROPERTIES_ANNOTATION = "Lcom/shazam/fork/TestProperties;"; - private static final String TEST_PROPERTY_ANNOTATION = "Lcom/shazam/fork/TestProperty;"; private final File instrumentationApkFile; private final DexFileExtractor dexFileExtractor; @@ -111,30 +110,27 @@ private List getPermissionsToRevoke(AnnotationItem[] annotations) { private Map getTestProperties(AnnotationItem[] annotations) { Map properties = new HashMap<>(); - - // If only one was found on the top level. - properties.putAll(stream(annotations) - .filter(annotationItem -> TEST_PROPERTY_ANNOTATION.equals(stringType(annotationItem))) - .map(AnnotationItem::getEncodedAnnotation) - .collect(toMap( - p -> ((StringEncodedValue) p.values[indexOfName(p, "key")]).value.getStringValue(), - p -> ((StringEncodedValue) p.values[indexOfName(p, "value")]).value.getStringValue() - ))); - - // Else, multiple, and by definition not on top level - properties.putAll(stream(annotations) + stream(annotations) .filter(annotationItem -> TEST_PROPERTIES_ANNOTATION.equals(stringType(annotationItem))) - .map(propertiesAnnotationItem -> propertiesAnnotationItem.getEncodedAnnotation().values) - .map(propertiesAnnotationValues -> propertiesAnnotationValues[0]) - .flatMap(propertyAnnotationsContainer -> stream(((ArrayEncodedValue) propertyAnnotationsContainer).values)) - .map(propertyAnnotation -> ((AnnotationEncodedValue) propertyAnnotation)) - .collect(toMap( - p -> ((StringEncodedValue) p.values[indexOfName(p, "key")]).value.getStringValue(), - p -> ((StringEncodedValue) p.values[indexOfName(p, "value")]).value.getStringValue() - ))); + .map(AnnotationItem::getEncodedAnnotation) + .forEach(an -> { + List keys = getAnnotationProperty(an, "keys"); + List values = getAnnotationProperty(an, "values"); + + for (int i = 0; i < min(values.size(), keys.size()); i++) { + properties.put(keys.get(i), values.get(i)); + } + }); return properties; } + private List getAnnotationProperty(AnnotationEncodedSubValue an, String propertyName) { + EncodedValue[] values = ((ArrayEncodedValue) an.values[indexOfName(an, propertyName)]).values; + return stream(values) + .map(stringEncodedValue -> ((StringEncodedValue) stringEncodedValue).value.getStringValue()) + .collect(toList()); + } + private int indexOfName(AnnotationEncodedSubValue p, String key) { int index = -1; StringIdItem[] names = p.names; diff --git a/fork-common/src/test/java/com/shazam/fork/suite/TestSuiteLoaderTest.java b/fork-common/src/test/java/com/shazam/fork/suite/TestSuiteLoaderTest.java index 98f1332b..e8b852ea 100644 --- a/fork-common/src/test/java/com/shazam/fork/suite/TestSuiteLoaderTest.java +++ b/fork-common/src/test/java/com/shazam/fork/suite/TestSuiteLoaderTest.java @@ -12,19 +12,17 @@ import com.shazam.fork.io.DexFileExtractor; import com.shazam.fork.model.TestCaseEvent; - import org.hamcrest.Matcher; import org.jf.dexlib.DexFile; import org.junit.Test; +import javax.annotation.Nonnull; import java.io.File; import java.net.URL; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; - import static com.shazam.fork.io.FakeDexFileExtractor.fakeDexFileExtractor; import static com.shazam.fork.io.Files.convertFileToDexFile; import static com.shazam.fork.model.TestCaseEvent.newTestCase; @@ -32,9 +30,7 @@ import static com.shazam.shazamcrest.MatcherAssert.assertThat; import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs; import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonMap; +import static java.util.Collections.*; import static org.hamcrest.Matchers.containsInAnyOrder; /** @@ -102,7 +98,7 @@ public void populatesTestCaseEvents() throws Exception { false, asList("android.permission.RECORD_AUDIO", "android.permission.ACCESS_FINE_LOCATION")), sameTestEventAs("methodAnnotatedWithEmptyRevokePermissionsTest", "com.shazam.forktest.RevokePermissionsClassTest", false), sameTestEventAs("methodWithProperties", "com.shazam.forktest.PropertiesClassTest", singletonMap("foo", "bar")), - sameTestEventAs("methodWithMultipleProperties", "com.shazam.forktest.PropertiesClassTest", multiPropertiesMap ) + sameTestEventAs("methodWithMultipleProperties", "com.shazam.forktest.PropertiesClassTest", multiPropertiesMap) ) ); diff --git a/fork-common/src/test/resources/_readme_when_updating.txt b/fork-common/src/test/resources/_readme_when_updating.txt index e805593a..a6be3115 100644 --- a/fork-common/src/test/resources/_readme_when_updating.txt +++ b/fork-common/src/test/resources/_readme_when_updating.txt @@ -3,6 +3,8 @@ Source code to generate tests.dex and app-debug.apk in the folder fork-common-te 1)Cd to the sub-folder `fork-common-test-dex` 2)run `./gradlew clean assemble assembleAndroidTest` 3)copy ./app/build/intermediates/transforms/dexMerger/androidTest/debug/0/classes.dex into the main module called `fork-common` in src/test/resources (and rename the file in tests.dex) + aka: cp app/build/intermediates/transforms/dexMerger/androidTest/debug/0/classes.dex ../fork-common/src/test/resources/tests.dex 4)copy ./app/build/outputs/apk/debug/app-debug.apk into the main module called `fork-common` in src/test/resources -4)Cd back to `fork` -5)run at least once `./gradlew test` + aka: cp app/build/outputs/apk/debug/app-debug.apk ../fork-common/src/test/resources +5)Cd back to `fork` +6)run at least once `./gradlew test` diff --git a/fork-common/src/test/resources/app-debug.apk b/fork-common/src/test/resources/app-debug.apk index 0d4a24e8..dc91a18a 100644 Binary files a/fork-common/src/test/resources/app-debug.apk and b/fork-common/src/test/resources/app-debug.apk differ diff --git a/fork-common/src/test/resources/tests.dex b/fork-common/src/test/resources/tests.dex index 2b96a035..7bd78a65 100644 Binary files a/fork-common/src/test/resources/tests.dex and b/fork-common/src/test/resources/tests.dex differ