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
Noumenon
  • 5,099
  • 4
  • 53
  • 73
  • Could this be a problem with the version of Gradle or the plugin? – Scratte Jan 22 '20 at 19:29
  • @Scratte 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 https://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". – Noumenon Jan 22 '20 at 20:07
  • 1
    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 https://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. – Scratte Jan 22 '20 at 20:11
  • 1
    Can you provide a [mcve]? 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. – Chriki Jan 26 '20 at 21:01
  • 1
    @Chriki I would appreciate seeing yours, since when I tried to make an MCVE, Gradle didn't run my test. – Noumenon Jan 28 '20 at 15:50
  • 1
    @Noumenon Thanks, I have [added it](https://stackoverflow.com/a/59957709/1797912). – Chriki Jan 28 '20 at 22:04

3 Answers3

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.

Martin Zeitler
  • 1
  • 19
  • 155
  • 216
  • 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? – Noumenon Jan 28 '20 at 20:46
  • @Noumenon 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. – Martin Zeitler Jan 28 '20 at 21:44
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.

Chriki
  • 15,638
  • 3
  • 51
  • 66
  • 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! – Noumenon Jan 28 '20 at 22:26
  • 1
    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. – Noumenon Jan 29 '20 at 15:33
2

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

test {
    jvmArgs '-Dillegal-access=deny'
}
Julian
  • 3,678
  • 7
  • 40
  • 72
  • 2
    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 Gradle – Jacob G. Jan 22 '20 at 20:30
  • @JacobG. What about `jvmArgs = ['-Dillegal-access=deny']` or `['--illegal-access=deny']`? – Scratte Jan 22 '20 at 20:45
  • 1
    @Scratte 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. – Noumenon Jan 22 '20 at 20:59
  • 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. – Noumenon Jan 24 '20 at 22:55
  • 1
    I actually don’t think this answers the question. The OP needs to pass the [`--illegal-access=deny` Java option](https://docs.oracle.com/en/java/javase/11/tools/java.html#GUID-3B1CE181-CD30-4178-9602-230B800D4FAE). The answer sets the _system property_ named `illegal-access` to the value `deny` which is something totally different. – Chriki Jan 26 '20 at 20:53