How to pass "illegal-access=deny" to unit test's JVM args using Gradle java plugin?
Asked Answered
L

3

11

I have a test that correctly fails with an InaccessibleObjectException when I run it with JVM args --illegal-access=deny in Eclipse. I want it to fail the same way when I run gradle check.

I tried the solution from How to pass args to JVM which runs tests with Gradle:

# build.gradle
apply plugin: 'java'

test {
  jvmArgs '--illegal-access=deny'

  # also tried
  # jvmArgs('--illegal-access', 'deny')
  # jvmArgs '-Dillegal-access=deny'
}

The test passed instead of failing. I did see tests saying they were dirty because jvmArgs had changed.

Here's the JUnit test that fails to fail. Sorry it doesn't have an "expectedException" set up, but it does throw when run with --illegal-access=deny from Eclipse.

import static org.junit.Assert.fail;

import java.lang.reflect.Field;

import org.junit.Test;


public class IllegalAccessTest {
  @Test
  public void testIllegalAccess() throws NoSuchFieldException, SecurityException {
    Field libraries = ClassLoader.class.getDeclaredField("loadedLibraryNames");
    System.out.println("About to set accessible");
    libraries.setAccessible(true);
    fail("Should fail before getting here when run with --illegal-access=deny");
  }
}

The output from this test when run with Gradle shows -Dillegal-access=deny is getting passed to Gradle, just not causing the test to fail:

Starting process 'Gradle Test Executor 33'. Working directory: xxx Command: /usr/java/jdk-11.0.4/bin/java -Dillegal-access=deny -Dorg.gradle.native=false -javaagent:xxx,jmx=false @/tmp/gradle-worker-classpath17509364376879385105txt -Xmx512m -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant -ea worker.org.gradle.process.internal.worker.GradleWorkerMain 'Gradle Test Executor 33'
Successfully started process 'Gradle Test Executor 33'

x.y.z.IllegalAccessTest  > testIllegalAccessQS STANDARD_OUT
    About to set accessible

x.y.z.IllegalAccessTest  > testIllegalAccessQS FAILED
    java.lang.AssertionError: Should fail before getting here when run with --illegal-access=deny
        at org.junit.Assert.fail(Assert.java:88)
        at x.y.z.IllegalAccessTest.testIllegalAccessQS(IllegalAccessTest.java:36)

The error message when run with Eclipse is the correct

java.lang.reflect.InaccessibleObjectException: Unable to make field private static final java.util.Set java.lang.ClassLoader.loadedLibraryNames accessible: module java.base does not "opens java.lang" to unnamed module @6b9651f3
Lacy answered 16/1, 2020 at 16:41 Comment(6)
Could this be a problem with the version of Gradle or the plugin?Bootle
@Bootle My gradle wrapper version is 5.6.2. I can't find how to check the version of the plugin; it just says apply plugin 'java'. I gather from docs.gradle.org/current/userguide/java_plugin.html that the java plugin is a bit obsolete now in favor of java-library or application plugins. But I didn't find anything that explicitly said "This version or plugin doesn't work with Java 9 modules".Lacy
I looked at the Grade documentation and 'java' is a core plugin, so I guess not really a plugin. I found this strange problem one person had when adding multiple jvmArgs github.com/gradle/gradle/issues/7045 Almost at the bottom there's a suggestion for that particular issue (not yours) where double quotes are used jvmArgs("--illegal-access=deny"). I'm guessing it's worth a try.Bootle
Can you provide a minimal reproducible example? I have created a minimal example with your test class myself and it works for me as expected, i.e., I get the java.lang.reflect.InaccessibleObjectException. FWIW, I’m using OpenJDK 11.0.5 and Gradle 5.6.2. If you’d find my minimal example useful, then I could post it as an “answer”; just let me know.Kori
@Kori I would appreciate seeing yours, since when I tried to make an MCVE, Gradle didn't run my test.Lacy
@Lacy Thanks, I have added it.Kori
C
4

The documentation for Test tasks reads: List<String> jvmArgs:

The extra arguments to use to launch the JVM for the process. Does not include system properties and the minimum/maximum heap size.

And there is nothing else, which would make sense - therefore this might be:

test.jvmArgs = ["--illegal-access=deny"]

Possibly with or without -- or -. Be aware that JUnit 5 may behave differently.

Chute answered 28/1, 2020 at 7:26 Comment(2)
This is the correct answer in that it causes all of my reflection-using tests to start failing with InaccessibleObjectException: Unable to make field private int java.lang.reflect.Field.modifiers accessible: module java.base does not "opens java.lang.reflect" to unnamed module @7c16905e. The odd thing is that the test I posted doesn't fail. Maybe Gradle is opening the classloader to the unnamed module or something?Lacy
@Lacy you might have to set it globally and not only for test... or maybe for run. Sorry, but I don't have any such project here, else I would simply try to reproduce the issue.Chute
K
3

As discussed in the question comments, a minimal reproducible example would help in tracking down the issue. Given that it wasn’t possible to provide one, maybe it’ll help to have a minimal example that works.

Minimal Working Setup

This is the complete setup (excluding the Gradle 5.6.2 Wrapper files):

.
├── build.gradle
└── src
    └── test
        └── java
            └── IllegalAccessTest.java

build.gradle

plugins {
    id 'java'
}

repositories {
    jcenter()
}

dependencies {
    testImplementation 'junit:junit:4.10'
}

test {
    jvmArgs '--illegal-access=deny'
}

src/test/java/IllegalAccessTest.java

exactly the same as given in the question

Testing if it Works

Running ./gradlew test yields the expected java.lang.reflect.InaccessibleObjectException at line 13 of src/test/java/IllegalAccessTest.java:

> Task :test FAILED

IllegalAccessTest > testIllegalAccess FAILED
    java.lang.reflect.InaccessibleObjectException at IllegalAccessTest.java:13

1 test completed, 1 failed

FAILURE: Build failed with an exception.

I have used OpenJDK 11.0.5 in this test.

A Note on Different Methods for Changing the Test JVM Arguments

If the following makes a difference (as suggested in this comment)

test.jvmArgs = ["--illegal-access=deny"]

compared to

test.jvmArgs '--illegal-access=deny'

then you may have set JVM args in multiple locations which interfere with each other. The former replaces all previously set JVM args with only --illegal-access=deny while the latter only adds the --illegal-access=deny option.

Kori answered 28/1, 2020 at 22:1 Comment(2)
I very much appreciate the example, as I was very far from being able to make one and it will help solve any future problems I have. Your example does work with my gradle wrapper. I have a little investigation to do. Thanks for explaining that the setter jvmArgs and the function jvmArgs don't work the same. All very helpful!Lacy
I'm planning to give this the bounty because it was the most helpful. It turns out the jvmArgs were correct either way, but the original test doesn't fail in my Gradle-run tests. All my other reflective tests do. So the MCVE helped show that it's not Gradle's fault. Not sure how it passes in Gradle and not Eclipse, since the module descriptor for Classloader.class is not open in either case. But that can be its own question.Lacy
E
2

Please try this. This is how I pass JVM arguments to my tests from gradle and it works.

test {
    jvmArgs '-Dillegal-access=deny'
}
Eldoria answered 22/1, 2020 at 20:21 Comment(5)
I just tested this and it doesn't seem to work. The fail message with OP's example is still Should fail before getting here when run with --illegal-access=deny, even when run from GradleMountfort
@JacobG. What about jvmArgs = ['-Dillegal-access=deny'] or ['--illegal-access=deny']?Bootle
@Bootle I've tried those now, didn't help. I edited the question with the Gradle log to show that the jvmArgs are being passed to Gradle, just not causing the test to fail.Lacy
I guess this answers the question in the title, but I'd really rather award the bounty to someone answering "Why doesn't the test fail when it gets these jvm args?" We'll see if that comes along.Lacy
I actually don’t think this answers the question. The OP needs to pass the --illegal-access=deny Java option. The answer sets the system property named illegal-access to the value deny which is something totally different.Kori

© 2022 - 2024 — McMap. All rights reserved.