1 /*
  2  * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /*
 25  * @test
 26  * @enablePreview
 27  * @requires jdk.foreign.linker != "UNSUPPORTED"
 28  * @requires !vm.musl
 29  *
 30  * @library /test/lib
 31  * @build TestEnableNativeAccessDynamic
 32  *        panama_module/*
 33           NativeAccessDynamicMain
 34  * @run testng/othervm/timeout=180 TestEnableNativeAccessDynamic
 35  * @summary Test for dynamically setting --enable-native-access flag for a module
 36  */
 37 
 38 import java.util.ArrayList;
 39 import java.util.List;
 40 import java.util.stream.Stream;
 41 
 42 import jdk.test.lib.process.ProcessTools;
 43 import jdk.test.lib.process.OutputAnalyzer;
 44 
 45 import org.testng.annotations.DataProvider;
 46 import org.testng.annotations.Test;
 47 import static org.testng.Assert.*;
 48 
 49 @Test
 50 public class TestEnableNativeAccessDynamic {
 51 
 52     static final String MODULE_PATH = System.getProperty("jdk.module.path");
 53 
 54     static final String PANAMA_MAIN = "panama_module/org.openjdk.foreigntest.PanamaMainDirect";
 55     static final String PANAMA_REFLECTION = "panama_module/org.openjdk.foreigntest.PanamaMainReflection";
 56     static final String PANAMA_INVOKE = "panama_module/org.openjdk.foreigntest.PanamaMainInvoke";
 57     static final String PANAMA_JNI = "panama_module/org.openjdk.foreigntest.PanamaMainJNI";
 58 
 59     /**
 60      * Represents the expected result of a test.
 61      */
 62     static final class Result {
 63         private final boolean success;
 64         private final List<String> expectedOutput = new ArrayList<>();
 65         private final List<String> notExpectedOutput = new ArrayList<>();
 66 
 67         Result(boolean success) {
 68             this.success = success;
 69         }
 70 
 71         Result expect(String msg) {
 72             expectedOutput.add(msg);
 73             return this;
 74         }
 75 
 76         Result doNotExpect(String msg) {
 77             notExpectedOutput.add(msg);
 78             return this;
 79         }
 80 
 81         boolean shouldSucceed() {
 82             return success;
 83         }
 84 
 85         Stream<String> expectedOutput() {
 86             return expectedOutput.stream();
 87         }
 88 
 89         Stream<String> notExpectedOutput() {
 90             return notExpectedOutput.stream();
 91         }
 92 
 93         @Override
 94         public String toString() {
 95             String s = (success) ? "success" : "failure";
 96             for (String msg : expectedOutput) {
 97                 s += "/" + msg;
 98             }
 99             return s;
100         }
101     }
102 
103     static Result success() {
104         return new Result(true);
105     }
106 
107     static Result successNoWarning() {
108         return success().doNotExpect("WARNING");
109     }
110 
111     static Result failWithError(String expectedOutput) {
112         return new Result(false).expect(expectedOutput);
113     }
114 
115     @DataProvider(name = "succeedCases")
116     public Object[][] succeedCases() {
117         return new Object[][] {
118                 { "panama_enable_native_access", PANAMA_MAIN, successNoWarning() },
119                 { "panama_enable_native_access_reflection", PANAMA_REFLECTION, successNoWarning() },
120                 { "panama_enable_native_access_invoke", PANAMA_INVOKE, successNoWarning() },
121         };
122     }
123 
124     @DataProvider(name = "failureCases")
125     public Object[][] failureCases() {
126         String errMsg = "Illegal native access from: module panama_module";
127         return new Object[][] {
128                 { "panama_enable_native_access_fail", PANAMA_MAIN, failWithError(errMsg) },
129                 { "panama_enable_native_access_fail_reflection", PANAMA_REFLECTION, failWithError(errMsg) },
130                 { "panama_enable_native_access_fail_invoke", PANAMA_INVOKE, failWithError(errMsg) },
131         };
132     }
133 
134     /**
135      * Checks an expected result with the output captured by the given
136      * OutputAnalyzer.
137      */
138     void checkResult(Result expectedResult, OutputAnalyzer outputAnalyzer) {
139         expectedResult.expectedOutput().forEach(outputAnalyzer::shouldContain);
140         expectedResult.notExpectedOutput().forEach(outputAnalyzer::shouldNotContain);
141         int exitValue = outputAnalyzer.getExitValue();
142         if (expectedResult.shouldSucceed()) {
143             assertTrue(exitValue == 0);
144         } else {
145             assertTrue(exitValue != 0);
146         }
147     }
148 
149     /**
150      * Runs the test to execute the given test action. The VM is run with the
151      * given VM options and the output checked to see that it matches the
152      * expected result.
153      */
154     OutputAnalyzer run(String action, String moduleAndCls, boolean enableNativeAccess,
155             Result expectedResult, boolean panamaModuleInBootLayer) throws Exception
156     {
157         List<String> list = new ArrayList<>();
158         list.add("--enable-preview");
159         if (panamaModuleInBootLayer) {
160             list.addAll(List.of("-p", MODULE_PATH));
161             list.add("--add-modules=panama_module");
162             list.add("--enable-native-access=panama_module");
163         } else {
164             list.add("--enable-native-access=ALL-UNNAMED");
165         }
166         list.addAll(List.of("NativeAccessDynamicMain", MODULE_PATH,
167                 moduleAndCls, Boolean.toString(enableNativeAccess), action));
168         String[] opts = list.toArray(String[]::new);
169         OutputAnalyzer outputAnalyzer = ProcessTools
170                 .executeTestJava(opts)
171                 .outputTo(System.out)
172                 .errorTo(System.out);
173         checkResult(expectedResult, outputAnalyzer);
174         return outputAnalyzer;
175     }
176 
177     @Test(dataProvider = "succeedCases")
178     public void testSucceed(String action, String moduleAndCls,
179             Result expectedResult) throws Exception {
180         run(action, moduleAndCls, true, expectedResult, false);
181     }
182 
183     @Test(dataProvider = "failureCases")
184     public void testFailures(String action, String moduleAndCls,
185             Result expectedResult) throws Exception {
186         run(action, moduleAndCls, false, expectedResult, false);
187     }
188 
189     // make sure that having a same named module in boot layer with native access
190     // does not influence same named dynamic module.
191     @Test(dataProvider = "failureCases")
192     public void testFailuresWithPanamaModuleInBootLayer(String action, String moduleAndCls,
193             Result expectedResult) throws Exception {
194         run(action, moduleAndCls, false, expectedResult, true);
195     }
196 }