blob: cde4a285de690f7a31059e5e147f78ed5a9b09dd [file] [log] [blame]
jgish1cb7fcf2012-10-29 16:51:59 -07001/*
katlemand08780c2012-12-20 16:24:50 -08002 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
jgish1cb7fcf2012-10-29 16:51:59 -07003 * 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 */
28import java.lang.reflect.Constructor;
29import java.lang.reflect.InvocationTargetException;
30import java.lang.reflect.Method;
31import java.lang.reflect.Modifier;
32import java.util.ArrayList;
33import java.util.Arrays;
34import 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 */
43public 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 int modifiers = m.getModifiers();
100 if (Modifier.isPublic(modifiers)
101 && !Modifier.isSynchronized(modifiers)) {
102 try {
103 testMethod(aClass, m);
104 } catch (TestFailedException e) {
105 if (isSelfTest) {
106 String methodName = e.getMethod().getName();
107 switch (methodName) {
108 case "should_pass":
109 throw new RuntimeException(
110 "Test failed: self-test failed. The 'should_pass' method did not pass the synchronization test. Check the test code.");
111 case "should_fail":
112 break;
113 default:
114 throw new RuntimeException(
115 "Test failed: something is amiss with the test. A TestFailedException was generated on a call to "
116 + methodName + " which we didn't expect to test in the first place.");
117 }
118 } else {
119 throw new RuntimeException("Test failed: the method "
120 + e.getMethod().toString()
121 + " should be synchronized, but isn't.");
122 }
123 }
124 }
125 }
126 }
127
128 private static void invokeMethod(Class<?> aClass, final Method m,
129 final Object[] args) throws TestFailedException, Exception {
130 //System.out.println( "Invoking " + m.toString() + " with parameters " + Arrays.toString(args));
131 final Constructor<?> objConstructor;
132 Object obj = null;
133
134 objConstructor = aClass.getConstructor(String.class);
135 obj = objConstructor.newInstance("LeftPalindrome-emordnilaP-thgiR");
136
137 // test method m for synchronization
138 if (!isSynchronized(m, obj, args)) {
139 throw new TestFailedException(m);
140 }
141 }
142
143 private static void testMethod(Class<?> aClass, Method m) throws
144 Exception {
145 /*
146 * Construct call with arguments of the correct type. Note that the
147 * values are somewhat irrelevant. If the call actually succeeds, it
148 * means we aren't synchronized and the test has failed.
149 */
150 Class<?>[] pTypes = m.getParameterTypes();
151 List<Integer> charSequenceArgs = new ArrayList<>();
152 Object[] args = new Object[pTypes.length];
153 for (int i = 0; i < pTypes.length; i++) {
154 // determine the type and create the corresponding actual argument
155 Class<?> pType = pTypes[i];
156 if (pType.equals(boolean.class)) {
157 args[i] = BOOLEAN_VAL;
158 } else if (pType.equals(char.class)) {
159 args[i] = CHAR_VAL;
160 } else if (pType.equals(int.class)) {
161 args[i] = INT_VAL;
162 } else if (pType.equals(double.class)) {
163 args[i] = DOUBLE_VAL;
164 } else if (pType.equals(float.class)) {
165 args[i] = FLOAT_VAL;
166 } else if (pType.equals(long.class)) {
167 args[i] = LONG_VAL;
168 } else if (pType.equals(Object.class)) {
169 args[i] = OBJECT_VAL;
170 } else if (pType.equals(StringBuilder.class)) {
171 args[i] = STRING_BUILDER_VAL;
172 } else if (pType.equals(StringBuffer.class)) {
173 args[i] = STRING_BUFFER_VAL;
174 } else if (pType.equals(String.class)) {
175 args[i] = STRING_VAL;
176 } else if (pType.isArray() && pType.getComponentType().equals(char.class)) {
177 args[i] = CHAR_ARRAY_VAL;
178 } else if (pType.equals(CharSequence.class)) {
179 charSequenceArgs.add(new Integer(i));
180 } else {
181 throw new RuntimeException("Test Failed: not accounting for method call with parameter type of " + pType.getName() + " You must update the test.");
182 }
183 }
184 /*
185 * If there are no CharSequence args, we can simply invoke our method
186 * and test it
187 */
188 if (charSequenceArgs.isEmpty()) {
189 invokeMethod(aClass, m, args);
190 } else {
191 /*
192 * Iterate through the different CharSequence types and invoke the
193 * method for each type.
194 */
195 if (charSequenceArgs.size() > 1) {
196 throw new RuntimeException("Test Failed: the test cannot handle a method with multiple CharSequence arguments. You must update the test to handle the method "
197 + m.toString());
198 }
199 for (int j = 0; j < CHAR_SEQUENCE_VAL.length; j++) {
200 args[charSequenceArgs.get(0)] = CHAR_SEQUENCE_VAL[j];
201 invokeMethod(aClass, m, args);
202 }
203 }
204 }
205
206 @SuppressWarnings("serial")
207 private static class TestFailedException extends Exception {
208
209 final Method m;
210
211 public Method getMethod() {
212 return m;
213 }
214
215 public TestFailedException(Method m) {
216 this.m = m;
217 }
218 }
219
220 static class InvokeTask implements Runnable {
221
222 private final Method m;
223 private final Object target;
224 private final Object[] args;
225
226 InvokeTask(Method m, Object target, Object... args) {
227 this.m = m;
228 this.target = target;
229 this.args = args;
230 }
231
232 @Override
233 public void run() {
234 try {
235 m.invoke(target, args);
236 } catch (IllegalAccessException | IllegalArgumentException |
237 InvocationTargetException e) {
238 e.printStackTrace();
239 }
240 }
241 }
242
243 /**
244 * isSynchronized tests whether the given method is synchronized or not by
245 * invoking it in a thread and testing the thread state after starting the
246 * thread
247 * <p/>
248 * @param m the method to test
249 * @param target the object the method is executed on
250 * @param args the arguments passed to the method
251 * @return true iff the method is synchronized
252 */
253 private static boolean isSynchronized(Method m, Object target,
254 Object... args) {
255 Thread t = new Thread(new InvokeTask(m, target, args));
256
257 Boolean isSynchronized = null;
258
259 synchronized (target) {
260 t.start();
261
262 while (isSynchronized == null) {
263 switch (t.getState()) {
264 case NEW:
265 case RUNNABLE:
266 case WAITING:
267 case TIMED_WAITING:
268 Thread.yield();
269 break;
270 case BLOCKED:
271 isSynchronized = true;
272 break;
273 case TERMINATED:
274 isSynchronized = false;
275 break;
276 }
277 }
278 }
279
280 try {
281 t.join();
282 } catch (InterruptedException ex) {
283 ex.printStackTrace();
284 }
285
286 return isSynchronized;
287 }
288
289 /*
290 * This class is used to test the synchronization tester above. It has a
291 * method, should_pass, that is unsynchronized but calls a synchronized
292 * method. It has another method, should_fail, which isn't synchronized and
293 * doesn't call a synchronized method. The former should pass and the latter
294 * should fail.
295 */
296 private static class MyTestClass {
297
298 @SuppressWarnings("unused")
299 public MyTestClass(String s) {
300 }
301
302 @SuppressWarnings("unused")
303 public void should_pass() {
304 // call sync method
305 sync_shouldnt_be_tested();
306 }
307
308 @SuppressWarnings("unused")
309 public void should_fail() {
310 }
311
312 public synchronized void sync_shouldnt_be_tested() {
313 }
314 }
315}