Path: blob/master/test/jdk/tools/jar/multiRelease/ApiValidatorTest.java
41149 views
/*1* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25# @bug 819674826* @summary Tests for API validator.27* @library /test/lib28* @modules java.base/jdk.internal.misc29* jdk.compiler30* jdk.jartool31* @build jdk.test.lib.Utils32* jdk.test.lib.Asserts33* jdk.test.lib.JDKToolFinder34* jdk.test.lib.JDKToolLauncher35* jdk.test.lib.Platform36* jdk.test.lib.process.*37* MRTestBase38* @run testng/timeout=1200 ApiValidatorTest39*/4041import jdk.test.lib.process.OutputAnalyzer;42import org.testng.annotations.BeforeMethod;43import org.testng.annotations.DataProvider;44import org.testng.annotations.Test;4546import java.lang.reflect.Method;47import java.nio.file.Files;48import java.nio.file.Path;49import java.nio.file.Paths;50import java.util.regex.Matcher;51import java.util.regex.Pattern;5253public class ApiValidatorTest extends MRTestBase {5455static final Pattern MODULE_PATTERN = Pattern.compile("module (\\w+)");56static final Pattern CLASS_PATTERN = Pattern.compile("package (\\w+).*public class (\\w+)");5758private Path root;59private Path classes;6061@BeforeMethod62void testInit(Method method) {63root = Paths.get(method.getName());64classes = root.resolve("classes");65}6667@Test(dataProvider = "signatureChange")68public void changeMethodSignature(String sigBase, String sigV10,69boolean isAcceptable) throws Throwable {7071String METHOD_SIG = "#SIG";72String classTemplate =73"public class C { \n" +74" " + METHOD_SIG + "{ throw new RuntimeException(); };\n" +75"}\n";76String base = classTemplate.replace(METHOD_SIG, sigBase);77String v10 = classTemplate.replace(METHOD_SIG, sigV10);7879compileTemplate(classes.resolve("base"), base);80compileTemplate(classes.resolve("v10"), v10);8182String jarfile = root.resolve("test.jar").toString();83OutputAnalyzer result = jar("cf", jarfile,84"-C", classes.resolve("base").toString(), ".",85"--release", "10", "-C", classes.resolve("v10").toString(),86".");87if (isAcceptable) {88result.shouldHaveExitValue(SUCCESS)89.shouldBeEmptyIgnoreVMWarnings();90} else {91result.shouldNotHaveExitValue(SUCCESS)92.shouldContain("contains a class with different api from earlier version");93}94}9596@DataProvider97Object[][] signatureChange() {98return new Object[][]{99{"public int m()", "protected int m()", false},100{"protected int m()", "public int m()", false},101{"public int m()", "int m()", false},102{"protected int m()", "private int m()", false},103{"private int m()", "int m()", true},104{"int m()", "private int m()", true},105{"int m()", "private int m(boolean b)", true},106{"public int m()", "public int m(int i)", false},107{"public int m()", "public int k()", false},108{"public int m()", "private int k()", false},109// @ignore JDK-8172147 {"public int m()", "public boolean m()", false},110// @ignore JDK-8172147 {"public boolean", "public Boolean", false},111// @ignore JDK-8172147 {"public <T> T", "public <T extends String> T", false},112};113}114115@Test(dataProvider = "publicAPI")116public void introducingPublicMembers(String publicAPI) throws Throwable {117String API = "#API";118String classTemplate =119"public class C { \n" +120" " + API + "\n" +121" public void method(){ };\n" +122"}\n";123String base = classTemplate.replace(API, "");124String v10 = classTemplate.replace(API, publicAPI);125126compileTemplate(classes.resolve("base"), base);127compileTemplate(classes.resolve("v10"), v10);128129String jarfile = root.resolve("test.jar").toString();130jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",131"--release", "10", "-C", classes.resolve("v10").toString(), ".")132.shouldNotHaveExitValue(SUCCESS)133.shouldContain("contains a class with different api from earlier version");134}135136@DataProvider137Object[][] publicAPI() {138return new Object[][]{139// @ignore JDK-8172148 {"protected class Inner { public void m(){ } } "}, // protected inner class140// @ignore JDK-8172148 {"public class Inner { public void m(){ } }"}, // public inner class141// @ignore JDK-8172148 {"public enum E { A; }"}, // public enum142{"public void m(){ }"}, // public method143{"protected void m(){ }"}, // protected method144};145}146147@Test(dataProvider = "privateAPI")148public void introducingPrivateMembers(String privateAPI) throws Throwable {149String API = "#API";150String classTemplate =151"public class C { \n" +152" " + API + "\n" +153" public void method(){ };\n" +154"}\n";155String base = classTemplate.replace(API, "");156String v10 = classTemplate.replace(API, privateAPI);157158compileTemplate(classes.resolve("base"), base);159compileTemplate(classes.resolve("v10"), v10);160161String jarfile = root.resolve("test.jar").toString();162jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",163"--release", "10", "-C", classes.resolve("v10").toString(), ".")164.shouldHaveExitValue(SUCCESS);165// add release166jar("uf", jarfile,167"--release", "11", "-C", classes.resolve("v10").toString(), ".")168.shouldHaveExitValue(SUCCESS);169// replace release170jar("uf", jarfile,171"--release", "11", "-C", classes.resolve("v10").toString(), ".")172.shouldHaveExitValue(SUCCESS);173}174175@DataProvider176Object[][] privateAPI() {177return new Object[][]{178{"private class Inner { public void m(){ } } "}, // private inner class179{"class Inner { public void m(){ } }"}, // package private inner class180{"enum E { A; }"}, // package private enum181// Local class and private method182{"private void m(){ class Inner { public void m(){} } Inner i = null; }"},183{"void m(){ }"}, // package private method184};185}186187private void compileTemplate(Path classes, String template) throws Throwable {188Path classSourceFile = Files.createDirectories(189classes.getParent().resolve("src").resolve(classes.getFileName()))190.resolve("C.java");191Files.write(classSourceFile, template.getBytes());192javac(classes, classSourceFile);193}194195/* Modular multi-release checks */196197@Test198public void moduleNameHasChanged() throws Throwable {199200compileModule(classes.resolve("base"), "module A { }");201compileModule(classes.resolve("v10"), "module B { }");202203String jarfile = root.resolve("test.jar").toString();204jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",205"--release", "10", "-C", classes.resolve("v10").toString(), ".")206.shouldNotHaveExitValue(SUCCESS)207.shouldContain("incorrect name");208209// update module-info release210jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",211"--release", "10", "-C", classes.resolve("base").toString(), ".")212.shouldHaveExitValue(SUCCESS);213jar("uf", jarfile,214"--release", "10", "-C", classes.resolve("v10").toString(), ".")215.shouldNotHaveExitValue(SUCCESS)216.shouldContain("incorrect name");217}218219// @Test @ignore 8173370220public void moduleBecomeOpen() throws Throwable {221222compileModule(classes.resolve("base"), "module A { }");223compileModule(classes.resolve("v10"), "open module A { }");224225String jarfile = root.resolve("test.jar").toString();226jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",227"--release", "10", "-C", classes.resolve("v10").toString(), ".")228.shouldNotHaveExitValue(SUCCESS)229.shouldContain("FIX ME");230231// update module-info release232jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",233"--release", "10", "-C", classes.resolve("base").toString(), ".")234.shouldHaveExitValue(SUCCESS);235jar("uf", jarfile,236"--release", "10", "-C", classes.resolve("v10").toString(), ".")237.shouldNotHaveExitValue(SUCCESS)238.shouldContain("FIX ME");239}240241@Test242public void moduleRequires() throws Throwable {243244String BASE_VERSION_DIRECTIVE = "requires jdk.compiler;";245// add transitive flag246moduleDirectivesCase(BASE_VERSION_DIRECTIVE,247"requires transitive jdk.compiler;",248false,249"contains additional \"requires transitive\"");250// remove requires251moduleDirectivesCase(BASE_VERSION_DIRECTIVE,252"",253true,254"");255// add requires256moduleDirectivesCase(BASE_VERSION_DIRECTIVE,257"requires jdk.compiler; requires jdk.jartool;",258true,259"");260// add requires transitive261moduleDirectivesCase(BASE_VERSION_DIRECTIVE,262"requires jdk.compiler; requires transitive jdk.jartool;",263false,264"contains additional \"requires transitive\"");265}266267@Test268public void moduleExports() throws Throwable {269270String BASE_VERSION_DIRECTIVE = "exports pkg1; exports pkg2 to jdk.compiler;";271// add export272moduleDirectivesCase(BASE_VERSION_DIRECTIVE,273BASE_VERSION_DIRECTIVE + " exports pkg3;",274false,275"contains different \"exports\"");276// change exports to qualified exports277moduleDirectivesCase(BASE_VERSION_DIRECTIVE,278"exports pkg1 to jdk.compiler; exports pkg2;",279false,280"contains different \"exports\"");281// remove exports282moduleDirectivesCase(BASE_VERSION_DIRECTIVE,283"exports pkg1;",284false,285"contains different \"exports\"");286// add qualified exports287moduleDirectivesCase(BASE_VERSION_DIRECTIVE,288BASE_VERSION_DIRECTIVE + " exports pkg3 to jdk.compiler;",289false,290"contains different \"exports\"");291}292293@Test294public void moduleOpens() throws Throwable {295296String BASE_VERSION_DIRECTIVE = "opens pkg1; opens pkg2 to jdk.compiler;";297// add opens298moduleDirectivesCase(BASE_VERSION_DIRECTIVE,299BASE_VERSION_DIRECTIVE + " opens pkg3;",300false,301"contains different \"opens\"");302// change opens to qualified opens303moduleDirectivesCase(BASE_VERSION_DIRECTIVE,304"opens pkg1 to jdk.compiler; opens pkg2;",305false,306"contains different \"opens\"");307// remove opens308moduleDirectivesCase(BASE_VERSION_DIRECTIVE,309"opens pkg1;",310false,311"contains different \"opens\"");312// add qualified opens313moduleDirectivesCase(BASE_VERSION_DIRECTIVE,314BASE_VERSION_DIRECTIVE + " opens pkg3 to jdk.compiler;",315false,316"contains different \"opens\"");317}318319@Test320public void moduleProvides() throws Throwable {321322String BASE_VERSION_DIRECTIVE = "provides pkg1.A with pkg1.A;";323// add provides324moduleDirectivesCase(BASE_VERSION_DIRECTIVE,325BASE_VERSION_DIRECTIVE + " provides pkg2.B with pkg2.B;",326false,327"contains different \"provides\"");328// change service impl329moduleDirectivesCase(BASE_VERSION_DIRECTIVE,330"provides pkg1.A with pkg2.B;",331false,332"contains different \"provides\"");333// remove provides334moduleDirectivesCase(BASE_VERSION_DIRECTIVE,335"",336false,337"contains different \"provides\"");338}339340@Test341public void moduleUses() throws Throwable {342343String BASE_VERSION_DIRECTIVE = "uses pkg1.A;";344// add345moduleDirectivesCase(BASE_VERSION_DIRECTIVE,346BASE_VERSION_DIRECTIVE + " uses pkg2.B;",347true,348"");349// replace350moduleDirectivesCase(BASE_VERSION_DIRECTIVE,351"uses pkg2.B;",352true,353"");354// remove355moduleDirectivesCase(BASE_VERSION_DIRECTIVE,356"",357true,358"");359}360361private void moduleDirectivesCase(String baseDirectives,362String versionedDirectives,363boolean expectSuccess,364String expectedMessage) throws Throwable {365String[] moduleClasses = {366"package pkg1; public class A { }",367"package pkg2; public class B extends pkg1.A { }",368"package pkg3; public class C extends pkg2.B { }"};369compileModule(classes.resolve("base"),370"module A { " + baseDirectives + " }",371moduleClasses);372compileModule(classes.resolve("v10"),373"module A { " + versionedDirectives + " }",374moduleClasses);375376String jarfile = root.resolve("test.jar").toString();377OutputAnalyzer output = jar("cf", jarfile,378"-C", classes.resolve("base").toString(), ".",379"--release", "10", "-C", classes.resolve("v10").toString(), ".");380if (expectSuccess) {381output.shouldHaveExitValue(SUCCESS);382} else {383output.shouldNotHaveExitValue(SUCCESS)384.shouldContain(expectedMessage);385}386}387388private void compileModule(Path classes, String moduleSource,389String... classSources) throws Throwable {390Matcher moduleMatcher = MODULE_PATTERN.matcher(moduleSource);391moduleMatcher.find();392String name = moduleMatcher.group(1);393Path moduleinfo = Files.createDirectories(394classes.getParent().resolve("src").resolve(name))395.resolve("module-info.java");396Files.write(moduleinfo, moduleSource.getBytes());397398Path[] sourceFiles = new Path[classSources.length + 1];399sourceFiles[0] = moduleinfo;400401for (int i = 0; i < classSources.length; i++) {402String classSource = classSources[i];403Matcher classMatcher = CLASS_PATTERN.matcher(classSource);404classMatcher.find();405String packageName = classMatcher.group(1);406String className = classMatcher.group(2);407408Path packagePath = moduleinfo.getParent()409.resolve(packageName.replace('.', '/'));410Path sourceFile = Files.createDirectories(packagePath)411.resolve(className + ".java");412Files.write(sourceFile, classSource.getBytes());413414sourceFiles[i + 1] = sourceFile;415}416417javac(classes, sourceFiles);418}419}420421422