Pragmatists/JUnitParams

Exception before running instrumentation tests on Android 19 and below

aaalaniz opened this issue · 3 comments

Description

I am seeing an exception when using the JunitParams runner on Android 19 and below.

Steps to Reproduce

  1. Write simple test suite and execute on Android 19 or lower with latest Android Testing Support Libaries

Code

The following test suite will reproduce the issue

@RunWith(JUnitParamsRunner.class)
public class SimpleTest {
    @Test
    @Parameters({ "false", "true" })
    public void simpleParameterizedTest(boolean enabled) {
        // Should execute and pass
    }
}

Expected Behavior

Tests should execute and pass. (Android Studio screenshot below)

screen shot 2017-09-07 at 11 36 13 am

Actual Behavior

Tests are not run because "no tests are found" (Android Studio screenshot below)

screen shot 2017-09-07 at 11 38 01 am

Logs

I checked the logs and I see the following exception occurring.

09-07 12:17:01.187 3338-3355/com.twilio.video.test W/dalvikvm: Exception Ljava/util/regex/PatternSyntaxException; thrown while initializing Ljunitparams/naming/MacroSubstitutionNamingStrategy;
09-07 12:17:01.187 3338-3355/com.twilio.video.test E/TestExecutor: Fatal exception when running tests
                                                                   java.lang.ExceptionInInitializerError
                                                                       at junitparams.internal.TestMethod$1.computeValue(TestMethod.java:40)
                                                                       at junitparams.internal.TestMethod$1.computeValue(TestMethod.java:35)
                                                                       at junitparams.internal.Memoizer.get(Memoizer.java:11)
                                                                       at junitparams.internal.TestMethod.describe(TestMethod.java:116)
                                                                       at junitparams.internal.ParameterisedTestClassRunner.describeParameterisedMethod(ParameterisedTestClassRunner.java:161)
                                                                       at junitparams.JUnitParamsRunner.describeMethod(JUnitParamsRunner.java:502)
                                                                       at junitparams.internal.ParametrizedTestMethodsFilter.filteredMethods(ParametrizedTestMethodsFilter.java:30)
                                                                       at junitparams.JUnitParamsRunner.getListOfMethods(JUnitParamsRunner.java:498)
                                                                       at junitparams.JUnitParamsRunner.getDescription(JUnitParamsRunner.java:487)
                                                                       at org.junit.runners.Suite.describeChild(Suite.java:123)
                                                                       at org.junit.runners.Suite.describeChild(Suite.java:27)
                                                                       at org.junit.runners.ParentRunner.shouldRun(ParentRunner.java:434)
                                                                       at org.junit.runners.ParentRunner.filter(ParentRunner.java:382)
                                                                       at org.junit.runner.manipulation.Filter.apply(Filter.java:97)
                                                                       at android.support.test.internal.runner.TestRequestBuilder$LenientFilterRequest.getRunner(TestRequestBuilder.java:413)
                                                                       at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
                                                                       at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:58)
                                                                       at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:375)
                                                                       at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)
                                                                    Caused by: java.util.regex.PatternSyntaxException: Look-behind pattern matches must have a bounded maximum length near index 40:
                                                                   (?=\{[^\}]{0,50}\})|(?<=\{[^\}]{0,50}\})
                                                                                                           ^
                                                                       at java.util.regex.Pattern.compileImpl(Native Method)
                                                                       at java.util.regex.Pattern.compile(Pattern.java:411)
                                                                       at java.util.regex.Pattern.<init>(Pattern.java:394)
                                                                       at java.util.regex.Pattern.compile(Pattern.java:381)
                                                                       at junitparams.naming.MacroSubstitutionNamingStrategy.<clinit>(MacroSubstitutionNamingStrategy.java:13)
                                                                       	... 19 more

Versions

Here is a snippet from my build.gradle file.

androidTestCompile 'pl.pragmatists:JUnitParams:1.1.0'
androidTestCompile 'com.android.support.test.espresso:espresso-core:3.0.1'
androidTestCompile 'com.android.support.test:runner:1.0.1'
androidTestCompile 'com.android.support.test:rules:1.0.1'
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'

I've created a PR #139 to fix this issue by replacing the offending RegEx with a simple state machine. The issue occurs because older versions of Android don't support look-behind properly.

@aaalaniz thanks for reporting issue.
I created a repo https://github.com/marmatys/JUnitParamsRegexpAndroid for reproducing bug reported by you. It contains simple Android project.

@mattmook thank you for your PR. I merged you PR to my local branch, build JUnitParams from this version and then I use this snapshot version in repository mentioned earlier. Unfortunately when I now run android instrumentation tests I get following error:

TestExecutor: Fatal exception when running tests
TestExecutor: java.lang.ArrayIndexOutOfBoundsException: length=1; index=-1
TestExecutor: 	at java.util.ArrayList.get(ArrayList.java:439)
TestExecutor: 	at junitparams.internal.ParameterisedTestMethodRunner.currentTestDescription(ParameterisedTestMethodRunner.java:110)
TestExecutor: 	at junitparams.internal.ParameterisedTestClassRunner.getDescriptionFor(ParameterisedTestClassRunner.java:189)
TestExecutor: 	at junitparams.JUnitParamsRunner.describeChild(JUnitParamsRunner.java:455)
TestExecutor: 	at junitparams.JUnitParamsRunner.describeChild(JUnitParamsRunner.java:393)
TestExecutor: 	at org.junit.runners.ParentRunner.shouldRun(ParentRunner.java:434)
TestExecutor: 	at org.junit.runners.ParentRunner.filter(ParentRunner.java:382)
TestExecutor: 	at junitparams.JUnitParamsRunner.filter(JUnitParamsRunner.java:406)
TestExecutor: 	at org.junit.runner.manipulation.Filter.apply(Filter.java:97)
TestExecutor: 	at org.junit.runners.ParentRunner.filter(ParentRunner.java:384)
TestExecutor: 	at org.junit.runner.manipulation.Filter.apply(Filter.java:97)
TestExecutor: 	at android.support.test.internal.runner.TestRequestBuilder$LenientFilterRequest.getRunner(TestRequestBuilder.java:413)
TestExecutor: 	at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
TestExecutor: 	at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:58)
TestExecutor: 	at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:375)
TestExecutor: 	at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2074)

This problem even occurs on Android version bigger than 19. So problem needs to be investigated further. If I miss something please let me know. Any help is appreciated.

The bug with the regexp existed in v1.0.6 however it seemed v1.1.0 introduced this ArrayIndexOutOfBoundsException regardless of the regexp fix. As such, I was considering it a separate issue as it exists independently.

I don't know enough about the internals to know what ParameterisedTestMethodRunner does with currentTestDescription, however locally I had patched it with the following:

Description currentTestDescription() {
    try {
        return method.description().getChildren().get(count - 1);
    } catch (IndexOutOfBoundsException ex) {
        return null;
    }
}

Clearly, this just masks the issue rather than resolves it. As count starts at 0 it is implying that currentParamsFromAnnotation has not been called to ensure it is incremented to 1 before currentTestDescription is called.