Path: blob/master/test/jdk/java/lang/StringBuffer/TestSynchronization.java
41149 views
/*1* Copyright (c) 2012, 2013, 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/* @test24* @bug 620678025* @summary Test that all public unsynchronized methods of StringBuffer are either directly or indirectly synchronized26*/27import java.lang.reflect.Constructor;28import java.lang.reflect.InvocationTargetException;29import java.lang.reflect.Method;30import java.lang.reflect.Modifier;31import java.util.ArrayList;32import java.util.Arrays;33import java.util.List;3435/**36* TestSynchronization tests whether synchronized methods calls on an object37* result in synchronized calls. Note that this may not test all cases desired.38* It only tests whether some synchronization has occurred on the object during39* the call chain, and can't tell whether the object was locked across all40* operations that have been performed on the object.41*/42public class TestSynchronization {4344/**45* Define parameters used in methods of StringBuffer - admittedly a bit of46* hack but 'purpose-built' for StringBuffer. Something more general could47* probably be developed if the test needs to be more widely adopted.48* <p/>49* boolean char char[] int double float long Object CharSequence String50* StringBuffer StringBuilder51* <p/>52*/53private static final boolean BOOLEAN_VAL = true;54private static final char CHAR_VAL = 'x';55private static final char[] CHAR_ARRAY_VAL = {'c', 'h', 'a', 'r', 'a', 'r',56'r', 'a', 'y'};57private static final int INT_VAL = 1;58private static final double DOUBLE_VAL = 1.0d;59private static final float FLOAT_VAL = 1.0f;60private static final long LONG_VAL = 1L;61private static final Object OBJECT_VAL = new Object();62private static final String STRING_VAL = "String value";63private static final StringBuilder STRING_BUILDER_VAL =64new StringBuilder("StringBuilder value");65private static final StringBuffer STRING_BUFFER_VAL =66new StringBuffer("StringBuffer value");67private static final CharSequence[] CHAR_SEQUENCE_VAL = {STRING_VAL,68STRING_BUILDER_VAL, STRING_BUFFER_VAL};6970public static void main(String... args) throws Exception {71// First, test the tester72testClass(MyTestClass.class, /*73* self-test74*/ true);75// Finally, test StringBuffer76testClass(StringBuffer.class, /*77* self-test78*/ false);79}8081/**82* Test all the public, unsynchronized methods of the given class. If83* isSelfTest is true, this is a self-test to ensure that the test program84* itself is working correctly. Should help ensure correctness of this85* program if it changes.86* <p/>87* @param aClass - the class to test88* @param isSelfTest - true if this is the special self-test class89* @throws SecurityException90*/91private static void testClass(Class<?> aClass, boolean isSelfTest) throws92Exception {93// Get all unsynchronized public methods via reflection. We don't need94// to test synchronized methods. By definition. they are already doing95// the right thing.96List<Method> methods = Arrays.asList(aClass.getDeclaredMethods());97for (Method m : methods) {98// skip synthetic methods, like default interface methods and lambdas99if (m.isSynthetic()) {100continue;101}102int modifiers = m.getModifiers();103if (Modifier.isPublic(modifiers)104&& !Modifier.isSynchronized(modifiers)) {105try {106testMethod(aClass, m);107} catch (TestFailedException e) {108if (isSelfTest) {109String methodName = e.getMethod().getName();110switch (methodName) {111case "should_pass":112throw new RuntimeException(113"Test failed: self-test failed. The 'should_pass' method did not pass the synchronization test. Check the test code.");114case "should_fail":115break;116default:117throw new RuntimeException(118"Test failed: something is amiss with the test. A TestFailedException was generated on a call to "119+ methodName + " which we didn't expect to test in the first place.");120}121} else {122throw new RuntimeException("Test failed: the method "123+ e.getMethod().toString()124+ " should be synchronized, but isn't.");125}126}127}128}129}130131private static void invokeMethod(Class<?> aClass, final Method m,132final Object[] args) throws TestFailedException, Exception {133//System.out.println( "Invoking " + m.toString() + " with parameters " + Arrays.toString(args));134final Constructor<?> objConstructor;135Object obj = null;136137objConstructor = aClass.getConstructor(String.class);138obj = objConstructor.newInstance("LeftPalindrome-emordnilaP-thgiR");139140// test method m for synchronization141if (!isSynchronized(m, obj, args)) {142throw new TestFailedException(m);143}144}145146private static void testMethod(Class<?> aClass, Method m) throws147Exception {148/*149* Construct call with arguments of the correct type. Note that the150* values are somewhat irrelevant. If the call actually succeeds, it151* means we aren't synchronized and the test has failed.152*/153Class<?>[] pTypes = m.getParameterTypes();154List<Integer> charSequenceArgs = new ArrayList<>();155Object[] args = new Object[pTypes.length];156for (int i = 0; i < pTypes.length; i++) {157// determine the type and create the corresponding actual argument158Class<?> pType = pTypes[i];159if (pType.equals(boolean.class)) {160args[i] = BOOLEAN_VAL;161} else if (pType.equals(char.class)) {162args[i] = CHAR_VAL;163} else if (pType.equals(int.class)) {164args[i] = INT_VAL;165} else if (pType.equals(double.class)) {166args[i] = DOUBLE_VAL;167} else if (pType.equals(float.class)) {168args[i] = FLOAT_VAL;169} else if (pType.equals(long.class)) {170args[i] = LONG_VAL;171} else if (pType.equals(Object.class)) {172args[i] = OBJECT_VAL;173} else if (pType.equals(StringBuilder.class)) {174args[i] = STRING_BUILDER_VAL;175} else if (pType.equals(StringBuffer.class)) {176args[i] = STRING_BUFFER_VAL;177} else if (pType.equals(String.class)) {178args[i] = STRING_VAL;179} else if (pType.isArray() && pType.getComponentType().equals(char.class)) {180args[i] = CHAR_ARRAY_VAL;181} else if (pType.equals(CharSequence.class)) {182charSequenceArgs.add(new Integer(i));183} else {184throw new RuntimeException("Test Failed: not accounting for method call with parameter type of " + pType.getName() + " You must update the test.");185}186}187/*188* If there are no CharSequence args, we can simply invoke our method189* and test it190*/191if (charSequenceArgs.isEmpty()) {192invokeMethod(aClass, m, args);193} else {194/*195* Iterate through the different CharSequence types and invoke the196* method for each type.197*/198if (charSequenceArgs.size() > 1) {199throw new RuntimeException("Test Failed: the test cannot handle a method with multiple CharSequence arguments. You must update the test to handle the method "200+ m.toString());201}202for (int j = 0; j < CHAR_SEQUENCE_VAL.length; j++) {203args[charSequenceArgs.get(0)] = CHAR_SEQUENCE_VAL[j];204invokeMethod(aClass, m, args);205}206}207}208209@SuppressWarnings("serial")210private static class TestFailedException extends Exception {211212final Method m;213214public Method getMethod() {215return m;216}217218public TestFailedException(Method m) {219this.m = m;220}221}222223static class InvokeTask implements Runnable {224225private final Method m;226private final Object target;227private final Object[] args;228229InvokeTask(Method m, Object target, Object... args) {230this.m = m;231this.target = target;232this.args = args;233}234235@Override236public void run() {237try {238m.invoke(target, args);239} catch (IllegalAccessException | IllegalArgumentException |240InvocationTargetException e) {241e.printStackTrace();242}243}244}245246/**247* isSynchronized tests whether the given method is synchronized or not by248* invoking it in a thread and testing the thread state after starting the249* thread250* <p/>251* @param m the method to test252* @param target the object the method is executed on253* @param args the arguments passed to the method254* @return true iff the method is synchronized255*/256private static boolean isSynchronized(Method m, Object target,257Object... args) {258Thread t = new Thread(new InvokeTask(m, target, args));259260Boolean isSynchronized = null;261262synchronized (target) {263t.start();264265while (isSynchronized == null) {266switch (t.getState()) {267case NEW:268case RUNNABLE:269case WAITING:270case TIMED_WAITING:271Thread.yield();272break;273case BLOCKED:274isSynchronized = true;275break;276case TERMINATED:277isSynchronized = false;278break;279}280}281}282283try {284t.join();285} catch (InterruptedException ex) {286ex.printStackTrace();287}288289return isSynchronized;290}291292/*293* This class is used to test the synchronization tester above. It has a294* method, should_pass, that is unsynchronized but calls a synchronized295* method. It has another method, should_fail, which isn't synchronized and296* doesn't call a synchronized method. The former should pass and the latter297* should fail.298*/299private static class MyTestClass {300301@SuppressWarnings("unused")302public MyTestClass(String s) {303}304305@SuppressWarnings("unused")306public void should_pass() {307// call sync method308sync_shouldnt_be_tested();309}310311@SuppressWarnings("unused")312public void should_fail() {313}314315public synchronized void sync_shouldnt_be_tested() {316}317}318}319320321