Path: blob/master/test/jdk/javax/management/descriptor/DescriptorTest.java
41152 views
/*1* Copyright (c) 2004, 2015, 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 6204469 627376526* @summary Test various aspects of the Descriptor interface27* @author Eamonn McManus28*29* @run clean DescriptorTest30* @run build DescriptorTest31* @run main DescriptorTest32*/3334import java.io.*;35import java.lang.reflect.*;36import java.util.*;37import javax.management.*;3839public class DescriptorTest {40private static String failureMessage;4142// Warning: many tests here know the contents of these variables43// so if you change them you must change the tests44private static final String[] testFieldNames = {45"a", "C", "aa", "int", "nul",46};47private static final Object[] testFieldValues = {48"b", "D", "bb", 5, null,49};50private static final String[] testFieldStrings = {51"a=b", "C=D", "aa=bb", "int=(5)", "nul=",52};5354public static void main(String[] args) throws Exception {55genericTests(ImmutableDescriptor.class);56genericTests(javax.management.modelmbean.DescriptorSupport.class);57if (failureMessage != null)58throw new Exception("TEST FAILED: " + failureMessage);59else60System.out.println("Test passed");61}6263private static void genericTests(Class<? extends Descriptor> descrClass) {64System.out.println("--- generic tests for " + descrClass.getName() +65" ---");66for (Case<Class<? extends Descriptor>, ?, ?> test :67genericDescriptorTests)68test.run(descrClass);69}7071/*72Testing has three parts. We take the input parameter, of type P,73and give it to the "prepare" method. That returns us a test74parameter, of type T. We give that to the "test" method. That75in turn returns us a check value, of type C. We give this to the76"check" method. If the "check" method returns null, the test passes.77If the "check" method returns a string, that string explains the78test failure. If any of the methods throws an exception, the79test fails.80*/81private static abstract class Case<P, T, C> {82Case(String name) {83this.name = name;84}8586void run(P p) {87System.out.println("test: " + name);88try {89T t = prepare(p);90C c = test(t);91String failed = check(c);92if (failed != null) {93System.out.println("FAILED: " + name + ": " + failed);94failureMessage = failed;95}96} catch (Exception e) {97System.out.println("FAILED: " + name + ": exception:");98e.printStackTrace(System.out);99failureMessage = e.toString();100}101}102103abstract T prepare(P p) throws Exception;104abstract C test(T t) throws Exception;105abstract String check(C c) throws Exception;106107private final String name;108}109110/*111Test case where the preparation step consists of constructing an112instance of the given Descriptor subclass containing test values,113then giving that to the "test" method.114*/115private static abstract class ProtoCase<C>116extends Case<Class<? extends Descriptor>, Descriptor, C> {117118ProtoCase(String name) {119super(name);120}121122Descriptor prepare(Class<? extends Descriptor> descrClass)123throws Exception {124Constructor<? extends Descriptor> con =125descrClass.getConstructor(String[].class, Object[].class);126return con.newInstance(testFieldNames, testFieldValues);127}128}129130/*131Test case where the "test" method must return a value of type C132which we will compare against the testValue parameter given to133the test constructor.134*/135private static abstract class ValueProtoCase<C> extends ProtoCase<C> {136ValueProtoCase(String name, C testValue) {137super(name);138this.testValue = testValue;139}140141String check(C c) {142final boolean array = (testValue instanceof Object[]);143final boolean equal =144array ?145Arrays.deepEquals((Object[]) testValue, (Object[]) c) :146testValue.equals(c);147if (equal)148return null;149return "wrong value: " + string(c) + " should be " +150string(testValue);151}152153private final C testValue;154}155156/*157Test case where the dontChange method does some operation on the158test Descriptor that is not supposed to change the contents of159the Descriptor. This should work for both mutable and immutable160Descriptors, since immutable Descriptors are supposed to do161nothing (rather than throw an exception) for mutation operations162that would not in fact change the contents.163*/164private static abstract class UnchangedCase extends ProtoCase<Descriptor> {165UnchangedCase(String name) {166super(name);167}168169Descriptor test(Descriptor d) {170dontChange(d);171return d;172}173174String check(Descriptor d) {175String[] dnames = d.getFieldNames();176if (!strings(dnames).equals(strings(testFieldNames)))177return "descriptor names changed: " + strings(dnames);178Object[] values = d.getFieldValues(testFieldNames);179if (values.length != testFieldValues.length)180return "getFieldValues: bogus length: " + values.length;181for (int i = 0; i < values.length; i++) {182Object expected = testFieldValues[i];183Object found = values[i];184if ((expected == null) ?185found != null :186!expected.equals(found))187return "descriptor value changed: " + testFieldNames[i] +188" was " + expected + " now " + found;189}190return null;191}192193abstract void dontChange(Descriptor d);194}195196/*197Test case where the change(d) method attempts to make some198change to the Descriptor d. The behaviour depends on whether199the Descriptor is mutable or not. If the Descriptor is200immutable, then the change attempt must throw a201RuntimeOperationsException wrapping an202UnsupportedOperationException. If the Descriptor is mutable,203then the change attempt must succeed, and the Descriptor must204then look like the fieldsAndValues parameter to the constructor.205This is simply an alternating set of field names and corresponding206values. So for example if it is207208"a", "b", "x", 5209210that represents a Descriptor with fields "a" and "x" whose211corresponding values are "x" and Integer.valueOf(5).212*/213private static abstract class ChangedCase extends ProtoCase<Object> {214ChangedCase(String name, Object... fieldsAndValues) {215super(name);216if (fieldsAndValues.length % 2 != 0)217throw new AssertionError("test wrong: odd fieldsAndValues");218this.fieldsAndValues = fieldsAndValues;219this.immutableTest = new UnsupportedExceptionCase(name) {220void provoke(Descriptor d) {221ChangedCase.this.change(d);222}223};224}225226Object test(Descriptor d) {227if (immutable(d))228return immutableTest.test(d);229else {230change(d);231return d;232}233}234235String check(Object c) {236if (c instanceof Exception)237return immutableTest.check((Exception) c);238else if (!(c instanceof Descriptor)) {239return "test returned strange value: " +240c.getClass() + ": " + c;241} else {242Descriptor d = (Descriptor) c;243String[] names = new String[fieldsAndValues.length / 2];244Object[] expected = new Object[names.length];245for (int i = 0; i < fieldsAndValues.length; i += 2) {246names[i / 2] = (String) fieldsAndValues[i];247expected[i / 2] = fieldsAndValues[i + 1];248}249String[] foundNames = d.getFieldNames();250if (!strings(foundNames).equals(strings(names))) {251return "wrong field names after change: found " +252strings(foundNames) + ", expected " + strings(names);253}254Object[] found = d.getFieldValues(names);255if (!Arrays.deepEquals(expected, found)) {256return "wrong value after change: for fields " +257Arrays.asList(names) + " values are " +258Arrays.asList(found) + ", should be " +259Arrays.asList(expected);260}261return null;262}263}264265abstract void change(Descriptor d);266267private final Object[] fieldsAndValues;268private final ExceptionCase immutableTest;269}270271/*272Test case where an operation provoke(d) on the test Descriptor d273is supposed to provoke an exception. The exception must be a274RuntimeOperationsException wrapping another exception whose type275is determined by the exceptionClass() method.276*/277private static abstract class ExceptionCase extends ProtoCase<Exception> {278279ExceptionCase(String name) {280super(name);281}282283Exception test(Descriptor d) {284try {285provoke(d);286return null;287} catch (Exception e) {288return e;289}290}291292String check(Exception e) {293if (e == null)294return "did not throw exception: " + expected();295if (!(e instanceof RuntimeOperationsException)) {296StringWriter sw = new StringWriter();297PrintWriter pw = new PrintWriter(sw);298e.printStackTrace(pw);299pw.flush();300return "wrong exception: " + expected() + ": found: " + sw;301}302Throwable cause = e.getCause();303if (!exceptionClass().isInstance(cause))304return "wrong wrapped exception: " + cause + ": " + expected();305return null;306}307308String expected() {309return "expected " + RuntimeOperationsException.class.getName() +310" wrapping " + exceptionClass().getName();311}312313abstract Class<? extends Exception> exceptionClass();314abstract void provoke(Descriptor d);315}316317private static abstract class IllegalExceptionCase extends ExceptionCase {318IllegalExceptionCase(String name) {319super(name);320}321322Class<IllegalArgumentException> exceptionClass() {323return IllegalArgumentException.class;324}325}326327private static abstract class UnsupportedExceptionCase328extends ExceptionCase {329UnsupportedExceptionCase(String name) {330super(name);331}332333Class<UnsupportedOperationException> exceptionClass() {334return UnsupportedOperationException.class;335}336}337338/*339List of test cases. We will run through these once for340ImmutableDescriptor and once for DescriptorSupport.341342Expect a compiler [unchecked] warning for this initialization.343Writing344345new Case<Class<? extends Descriptor>, ?, ?>[] = {...}346347would cause a compiler error since you can't have arrays of348parameterized types unless all the parameters are just "?".349This hack with varargs gives us a compiler warning instead.350Writing just:351352new Case<?, ?, ?>[] = {...}353354would compile here, but not where we call test.run, since you355cannot pass an object to the run(P) method if P is "?".356*/357private static final Case<Class<? extends Descriptor>, ?, ?>358genericDescriptorTests[] = constantArray(359360// TEST VALUES RETURNED BY GETTERS361362new Case<Class<? extends Descriptor>, Descriptor, Object[]>(363"getFieldValues on empty Descriptor") {364Descriptor prepare(Class<? extends Descriptor> c)365throws Exception {366Constructor<? extends Descriptor> con =367c.getConstructor(String[].class);368return con.newInstance(new Object[] {new String[0]});369}370Object[] test(Descriptor d) {371return d.getFieldValues("foo", "bar");372}373String check(Object[] v) {374if (v.length == 2 && v[0] == null && v[1] == null)375return null;376return "value should be array with null elements: " +377Arrays.deepToString(v);378}379},380381new ValueProtoCase<Set<String>>("getFieldNames",382strings(testFieldNames)) {383Set<String> test(Descriptor d) {384return set(d.getFieldNames());385}386},387new ValueProtoCase<Set<String>>("getFields",388strings(testFieldStrings)) {389Set<String> test(Descriptor d) {390return set(d.getFields());391}392},393new ValueProtoCase<Object>("getFieldValue with exact case", "b") {394Object test(Descriptor d) {395return d.getFieldValue("a");396}397},398new ValueProtoCase<Object>("getFieldValue with lower case for upper",399"D") {400Object test(Descriptor d) {401return d.getFieldValue("c");402}403},404new ValueProtoCase<Object>("getFieldValue with upper case for lower",405"bb") {406Object test(Descriptor d) {407return d.getFieldValue("AA");408}409},410new ValueProtoCase<Object>("getFieldValue with mixed case for lower",411"bb") {412Object test(Descriptor d) {413return d.getFieldValue("aA");414}415},416new ValueProtoCase<Set<?>>("getFieldValues with null arg",417set(testFieldValues)) {418Set<?> test(Descriptor d) {419return set(d.getFieldValues((String[]) null));420}421},422new ValueProtoCase<Object[]>("getFieldValues with not all values",423new Object[] {"b", "D", 5}) {424Object[] test(Descriptor d) {425return d.getFieldValues("a", "c", "int");426}427},428new ValueProtoCase<Object[]>("getFieldValues with all values " +429"lower case",430new Object[]{"bb", "D", "b", 5}) {431Object[] test(Descriptor d) {432return d.getFieldValues("aa", "c", "a", "int");433}434},435new ValueProtoCase<Object[]>("getFieldValues with all values " +436"upper case",437new Object[] {5, "b", "D", "bb"}) {438Object[] test(Descriptor d) {439return d.getFieldValues("int", "A", "C", "AA");440}441},442new ValueProtoCase<Object[]>("getFieldValues with null name",443new Object[] {null}) {444Object[] test(Descriptor d) {445return d.getFieldValues((String) null);446}447},448new ValueProtoCase<Object[]>("getFieldValues with empty name",449new Object[] {null}) {450Object[] test(Descriptor d) {451return d.getFieldValues("");452}453},454new ValueProtoCase<Object[]>("getFieldValues with no names",455new Object[0]) {456Object[] test(Descriptor d) {457return d.getFieldValues();458}459},460461// TEST OPERATIONS THAT DON'T CHANGE THE DESCRIPTOR462// Even for immutable descriptors, these are allowed463464new UnchangedCase("removeField with nonexistent field") {465void dontChange(Descriptor d) {466d.removeField("noddy");467}468},469new UnchangedCase("removeField with null field") {470void dontChange(Descriptor d) {471d.removeField(null);472}473},474new UnchangedCase("removeField with empty field") {475void dontChange(Descriptor d) {476d.removeField("");477}478},479new UnchangedCase("setField leaving string unchanged") {480void dontChange(Descriptor d) {481d.setField("a", "b");482}483},484new UnchangedCase("setField leaving int unchanged") {485void dontChange(Descriptor d) {486d.setField("int", 5);487}488},489// We do not test whether you can do a setField/s with an490// unchanged value but the case of the name different.491// From the spec, that should probably be illegal, but492// it's such a corner case that we leave it alone.493494new UnchangedCase("setFields with empty arrays") {495void dontChange(Descriptor d) {496d.setFields(new String[0], new Object[0]);497}498},499new UnchangedCase("setFields with unchanged values") {500void dontChange(Descriptor d) {501d.setFields(new String[] {"a", "int"},502new Object[] {"b", 5});503}504},505506// TEST OPERATIONS THAT DO CHANGE THE DESCRIPTOR507// For immutable descriptors, these should provoke an exception508509new ChangedCase("removeField with exact case",510"a", "b", "C", "D", "int", 5, "nul", null) {511void change(Descriptor d) {512d.removeField("aa");513}514},515new ChangedCase("removeField with upper case for lower",516"a", "b", "C", "D", "int", 5, "nul", null) {517void change(Descriptor d) {518d.removeField("AA");519}520},521new ChangedCase("removeField with lower case for upper",522"a", "b", "aa", "bb", "int", 5, "nul", null) {523void change(Descriptor d) {524d.removeField("c");525}526},527new ChangedCase("setField keeping lower case",528"a", "x", "C", "D", "aa", "bb", "int", 5,529"nul", null) {530void change(Descriptor d) {531d.setField("a", "x");532}533},534535// spec says we should conserve the original case of the field name:536new ChangedCase("setField changing lower case to upper",537"a", "x", "C", "D", "aa", "bb", "int", 5,538"nul", null) {539void change(Descriptor d) {540d.setField("A", "x");541}542},543new ChangedCase("setField changing upper case to lower",544"a", "b", "C", "x", "aa", "bb", "int", 5,545"nul", null) {546void change(Descriptor d) {547d.setField("c", "x");548}549},550new ChangedCase("setField adding new field",551"a", "b", "C", "D", "aa", "bb", "int", 5, "xX", "yY",552"nul", null) {553void change(Descriptor d) {554d.setField("xX", "yY");555}556},557new ChangedCase("setField changing type of field",558"a", true, "C", "D", "aa", "bb", "int", 5,559"nul", null) {560void change(Descriptor d) {561d.setField("a", true);562}563},564new ChangedCase("setField changing non-null to null",565"a", null, "C", "D", "aa", "bb", "int", 5,566"nul", null) {567void change(Descriptor d) {568d.setField("a", null);569}570},571new ChangedCase("setField changing null to non-null",572"a", "b", "C", "D", "aa", "bb", "int", 5,573"nul", 3.14) {574void change(Descriptor d) {575d.setField("nul", 3.14);576}577},578579// TEST EXCEPTION BEHAVIOUR COMMON BETWEEN MUTABLE AND IMMUTABLE580581new IllegalExceptionCase("getFieldValue with null name") {582void provoke(Descriptor d) {583d.getFieldValue(null);584}585},586new IllegalExceptionCase("getFieldValue with empty name") {587void provoke(Descriptor d) {588d.getFieldValue("");589}590},591new IllegalExceptionCase("setField with null name") {592void provoke(Descriptor d) {593d.setField(null, "x");594}595},596new IllegalExceptionCase("setField with empty name") {597void provoke(Descriptor d) {598d.setField("", "x");599}600},601new IllegalExceptionCase("setFields with null fieldNames") {602void provoke(Descriptor d) {603d.setFields(null, new Object[] {"X"});604}605},606new IllegalExceptionCase("setFields with null fieldValues") {607void provoke(Descriptor d) {608d.setFields(new String[] {"X"}, null);609}610},611new IllegalExceptionCase("setFields with null fieldNames and " +612"fieldValues") {613void provoke(Descriptor d) {614d.setFields(null, null);615}616},617new IllegalExceptionCase("setFields with more fieldNames than " +618"fieldValues") {619void provoke(Descriptor d) {620d.setFields(new String[] {"A", "B"}, new String[] {"C"});621}622},623new IllegalExceptionCase("setFields with more fieldValues than " +624"fieldNames") {625void provoke(Descriptor d) {626d.setFields(new String[] {"A"}, new String[] {"B", "C"});627}628},629new IllegalExceptionCase("setFields with null element of fieldNames") {630void provoke(Descriptor d) {631d.setFields(new String[] {null}, new String[] {"X"});632}633}634635);636637static <T> T[] constantArray(T... array) {638return array;639}640641static String string(Object x) {642if (x instanceof Object[])643return Arrays.asList((Object[]) x).toString();644else645return String.valueOf(x);646}647648static Set<String> strings(String... values) {649return new TreeSet<String>(Arrays.asList(values));650}651652static <T> Set<T> set(T[] values) {653return new HashSet<T>(Arrays.asList(values));654}655656static boolean immutable(Descriptor d) {657return (d instanceof ImmutableDescriptor);658// good enough for our purposes659}660}661662663