test/jdk/java/lang/StringBuffer/TestSynchronization.java
changeset 47216 71c04702a3d5
parent 23010 6dadb192ad81
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2012, 2013, 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 /* @test
       
    25  * @bug 6206780
       
    26  * @summary Test that all public unsynchronized methods of StringBuffer are either directly or indirectly synchronized
       
    27  */
       
    28 import java.lang.reflect.Constructor;
       
    29 import java.lang.reflect.InvocationTargetException;
       
    30 import java.lang.reflect.Method;
       
    31 import java.lang.reflect.Modifier;
       
    32 import java.util.ArrayList;
       
    33 import java.util.Arrays;
       
    34 import java.util.List;
       
    35 
       
    36 /**
       
    37  * TestSynchronization tests whether synchronized methods calls on an object
       
    38  * result in synchronized calls. Note that this may not test all cases desired.
       
    39  * It only tests whether some synchronization has occurred on the object during
       
    40  * the call chain, and can't tell whether the object was locked across all
       
    41  * operations that have been performed on the object.
       
    42  */
       
    43 public class TestSynchronization {
       
    44 
       
    45     /**
       
    46      * Define parameters used in methods of StringBuffer - admittedly a bit of
       
    47      * hack but 'purpose-built' for StringBuffer. Something more general could
       
    48      * probably be developed if the test needs to be more widely adopted.
       
    49      * <p/>
       
    50      * boolean char char[] int double float long Object CharSequence String
       
    51      * StringBuffer StringBuilder
       
    52      * <p/>
       
    53      */
       
    54     private static final boolean BOOLEAN_VAL = true;
       
    55     private static final char CHAR_VAL = 'x';
       
    56     private static final char[] CHAR_ARRAY_VAL = {'c', 'h', 'a', 'r', 'a', 'r',
       
    57         'r', 'a', 'y'};
       
    58     private static final int INT_VAL = 1;
       
    59     private static final double DOUBLE_VAL = 1.0d;
       
    60     private static final float FLOAT_VAL = 1.0f;
       
    61     private static final long LONG_VAL = 1L;
       
    62     private static final Object OBJECT_VAL = new Object();
       
    63     private static final String STRING_VAL = "String value";
       
    64     private static final StringBuilder STRING_BUILDER_VAL =
       
    65             new StringBuilder("StringBuilder value");
       
    66     private static final StringBuffer STRING_BUFFER_VAL =
       
    67             new StringBuffer("StringBuffer value");
       
    68     private static final CharSequence[] CHAR_SEQUENCE_VAL = {STRING_VAL,
       
    69         STRING_BUILDER_VAL, STRING_BUFFER_VAL};
       
    70 
       
    71     public static void main(String... args) throws Exception {
       
    72         // First, test the tester
       
    73         testClass(MyTestClass.class, /*
       
    74                  * self-test
       
    75                  */ true);
       
    76         // Finally, test StringBuffer
       
    77         testClass(StringBuffer.class, /*
       
    78                  * self-test
       
    79                  */ false);
       
    80     }
       
    81 
       
    82     /**
       
    83      * Test all the public, unsynchronized methods of the given class. If
       
    84      * isSelfTest is true, this is a self-test to ensure that the test program
       
    85      * itself is working correctly. Should help ensure correctness of this
       
    86      * program if it changes.
       
    87      * <p/>
       
    88      * @param aClass - the class to test
       
    89      * @param isSelfTest - true if this is the special self-test class
       
    90      * @throws SecurityException
       
    91      */
       
    92     private static void testClass(Class<?> aClass, boolean isSelfTest) throws
       
    93             Exception {
       
    94         // Get all unsynchronized public methods via reflection.  We don't need
       
    95         // to test synchronized methods.  By definition. they are already doing
       
    96         // the right thing.
       
    97         List<Method> methods = Arrays.asList(aClass.getDeclaredMethods());
       
    98         for (Method m : methods) {
       
    99             // skip synthetic methods, like default interface methods and lambdas
       
   100             if (m.isSynthetic()) {
       
   101                 continue;
       
   102             }
       
   103             int modifiers = m.getModifiers();
       
   104             if (Modifier.isPublic(modifiers)
       
   105                     && !Modifier.isSynchronized(modifiers)) {
       
   106                 try {
       
   107                     testMethod(aClass, m);
       
   108                 } catch (TestFailedException e) {
       
   109                     if (isSelfTest) {
       
   110                         String methodName = e.getMethod().getName();
       
   111                         switch (methodName) {
       
   112                             case "should_pass":
       
   113                                 throw new RuntimeException(
       
   114                                         "Test failed: self-test failed.  The 'should_pass' method did not pass the synchronization test. Check the test code.");
       
   115                             case "should_fail":
       
   116                                 break;
       
   117                             default:
       
   118                                 throw new RuntimeException(
       
   119                                         "Test failed: something is amiss with the test. A TestFailedException was generated on a call to "
       
   120                                         + methodName + " which we didn't expect to test in the first place.");
       
   121                         }
       
   122                     } else {
       
   123                         throw new RuntimeException("Test failed: the method "
       
   124                                 + e.getMethod().toString()
       
   125                                 + " should be synchronized, but isn't.");
       
   126                     }
       
   127                 }
       
   128             }
       
   129         }
       
   130     }
       
   131 
       
   132     private static void invokeMethod(Class<?> aClass, final Method m,
       
   133             final Object[] args) throws TestFailedException, Exception {
       
   134         //System.out.println( "Invoking " + m.toString() + " with parameters " + Arrays.toString(args));
       
   135         final Constructor<?> objConstructor;
       
   136         Object obj = null;
       
   137 
       
   138         objConstructor = aClass.getConstructor(String.class);
       
   139         obj = objConstructor.newInstance("LeftPalindrome-emordnilaP-thgiR");
       
   140 
       
   141         // test method m for synchronization
       
   142         if (!isSynchronized(m, obj, args)) {
       
   143             throw new TestFailedException(m);
       
   144         }
       
   145     }
       
   146 
       
   147     private static void testMethod(Class<?> aClass, Method m) throws
       
   148             Exception {
       
   149         /*
       
   150          * Construct call with arguments of the correct type. Note that the
       
   151          * values are somewhat irrelevant. If the call actually succeeds, it
       
   152          * means we aren't synchronized and the test has failed.
       
   153          */
       
   154         Class<?>[] pTypes = m.getParameterTypes();
       
   155         List<Integer> charSequenceArgs = new ArrayList<>();
       
   156         Object[] args = new Object[pTypes.length];
       
   157         for (int i = 0; i < pTypes.length; i++) {
       
   158             // determine the type and create the corresponding actual argument
       
   159             Class<?> pType = pTypes[i];
       
   160             if (pType.equals(boolean.class)) {
       
   161                 args[i] = BOOLEAN_VAL;
       
   162             } else if (pType.equals(char.class)) {
       
   163                 args[i] = CHAR_VAL;
       
   164             } else if (pType.equals(int.class)) {
       
   165                 args[i] = INT_VAL;
       
   166             } else if (pType.equals(double.class)) {
       
   167                 args[i] = DOUBLE_VAL;
       
   168             } else if (pType.equals(float.class)) {
       
   169                 args[i] = FLOAT_VAL;
       
   170             } else if (pType.equals(long.class)) {
       
   171                 args[i] = LONG_VAL;
       
   172             } else if (pType.equals(Object.class)) {
       
   173                 args[i] = OBJECT_VAL;
       
   174             } else if (pType.equals(StringBuilder.class)) {
       
   175                 args[i] = STRING_BUILDER_VAL;
       
   176             } else if (pType.equals(StringBuffer.class)) {
       
   177                 args[i] = STRING_BUFFER_VAL;
       
   178             } else if (pType.equals(String.class)) {
       
   179                 args[i] = STRING_VAL;
       
   180             } else if (pType.isArray() && pType.getComponentType().equals(char.class)) {
       
   181                 args[i] = CHAR_ARRAY_VAL;
       
   182             } else if (pType.equals(CharSequence.class)) {
       
   183                 charSequenceArgs.add(new Integer(i));
       
   184             } else {
       
   185                 throw new RuntimeException("Test Failed: not accounting for method call with parameter type of " + pType.getName() + " You must update the test.");
       
   186             }
       
   187         }
       
   188         /*
       
   189          * If there are no CharSequence args, we can simply invoke our method
       
   190          * and test it
       
   191          */
       
   192         if (charSequenceArgs.isEmpty()) {
       
   193             invokeMethod(aClass, m, args);
       
   194         } else {
       
   195             /*
       
   196              * Iterate through the different CharSequence types and invoke the
       
   197              * method for each type.
       
   198              */
       
   199             if (charSequenceArgs.size() > 1) {
       
   200                 throw new RuntimeException("Test Failed: the test cannot handle a method with multiple CharSequence arguments.  You must update the test to handle the method "
       
   201                         + m.toString());
       
   202             }
       
   203             for (int j = 0; j < CHAR_SEQUENCE_VAL.length; j++) {
       
   204                 args[charSequenceArgs.get(0)] = CHAR_SEQUENCE_VAL[j];
       
   205                 invokeMethod(aClass, m, args);
       
   206             }
       
   207         }
       
   208     }
       
   209 
       
   210     @SuppressWarnings("serial")
       
   211     private static class TestFailedException extends Exception {
       
   212 
       
   213         final Method m;
       
   214 
       
   215         public Method getMethod() {
       
   216             return m;
       
   217         }
       
   218 
       
   219         public TestFailedException(Method m) {
       
   220             this.m = m;
       
   221         }
       
   222     }
       
   223 
       
   224     static class InvokeTask implements Runnable {
       
   225 
       
   226         private final Method m;
       
   227         private final Object target;
       
   228         private final Object[] args;
       
   229 
       
   230         InvokeTask(Method m, Object target, Object... args) {
       
   231             this.m = m;
       
   232             this.target = target;
       
   233             this.args = args;
       
   234         }
       
   235 
       
   236         @Override
       
   237         public void run() {
       
   238             try {
       
   239                 m.invoke(target, args);
       
   240             } catch (IllegalAccessException | IllegalArgumentException |
       
   241                     InvocationTargetException e) {
       
   242                 e.printStackTrace();
       
   243             }
       
   244         }
       
   245     }
       
   246 
       
   247     /**
       
   248      * isSynchronized tests whether the given method is synchronized or not by
       
   249      * invoking it in a thread and testing the thread state after starting the
       
   250      * thread
       
   251      * <p/>
       
   252      * @param m the method to test
       
   253      * @param target the object the method is executed on
       
   254      * @param args the arguments passed to the method
       
   255      * @return true iff the method is synchronized
       
   256      */
       
   257     private static boolean isSynchronized(Method m, Object target,
       
   258             Object... args) {
       
   259         Thread t = new Thread(new InvokeTask(m, target, args));
       
   260 
       
   261         Boolean isSynchronized = null;
       
   262 
       
   263         synchronized (target) {
       
   264             t.start();
       
   265 
       
   266             while (isSynchronized == null) {
       
   267                 switch (t.getState()) {
       
   268                     case NEW:
       
   269                     case RUNNABLE:
       
   270                     case WAITING:
       
   271                     case TIMED_WAITING:
       
   272                         Thread.yield();
       
   273                         break;
       
   274                     case BLOCKED:
       
   275                         isSynchronized = true;
       
   276                         break;
       
   277                     case TERMINATED:
       
   278                         isSynchronized = false;
       
   279                         break;
       
   280                 }
       
   281             }
       
   282         }
       
   283 
       
   284         try {
       
   285             t.join();
       
   286         } catch (InterruptedException ex) {
       
   287             ex.printStackTrace();
       
   288         }
       
   289 
       
   290         return isSynchronized;
       
   291     }
       
   292 
       
   293     /*
       
   294      * This class is used to test the synchronization tester above. It has a
       
   295      * method, should_pass, that is unsynchronized but calls a synchronized
       
   296      * method. It has another method, should_fail, which isn't synchronized and
       
   297      * doesn't call a synchronized method. The former should pass and the latter
       
   298      * should fail.
       
   299      */
       
   300     private static class MyTestClass {
       
   301 
       
   302         @SuppressWarnings("unused")
       
   303         public MyTestClass(String s) {
       
   304         }
       
   305 
       
   306         @SuppressWarnings("unused")
       
   307         public void should_pass() {
       
   308             // call sync method
       
   309             sync_shouldnt_be_tested();
       
   310         }
       
   311 
       
   312         @SuppressWarnings("unused")
       
   313         public void should_fail() {
       
   314         }
       
   315 
       
   316         public synchronized void sync_shouldnt_be_tested() {
       
   317         }
       
   318     }
       
   319 }