1/*
2 * Copyright (c) 2010, 2011, 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/*
25  @test
26  @bug 5089429 6982632 8145808
27  @summary Checks that we don't crash if rendering operations and state
28  changes are performed on a graphics context from different threads.
29
30  @author Dmitri.Trembovetski@sun.com area=Graphics
31  @run main MTGraphicsAccessTest
32 */
33
34import java.awt.*;
35import java.awt.image.*;
36import java.awt.geom.*;
37import java.util.concurrent.atomic.AtomicInteger;
38
39public class MTGraphicsAccessTest {
40
41    // in seconds
42    static final long STANDALONE_RUN_TIME = 20;
43    static final long JTREG_RUN_TIME = 7;
44
45    static boolean standaloneMode;
46    static boolean allowExceptions = true;
47    static long testRunTime;
48
49    volatile boolean done;
50    AtomicInteger stillRunning = new AtomicInteger(0);
51    volatile int numexceptions;
52
53    Graphics2D sharedGraphics;
54    BufferedImage sharedBI =
55            new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB);
56
57    static final Paint colors[] = {
58        Color.red,
59        new Color(0x7f, 0xff, 0x00, 0x7f),
60        new GradientPaint(0,  0, Color.red,
61                          50, 50, new Color(0x7f, 0xff, 0x00, 0x7f)),
62    };
63    static final Font fonts[] = {
64        new Font("Dialog", Font.PLAIN, 12),
65        new Font("Dialog", Font.BOLD, 16),
66        new Font("Dialog", Font.ITALIC, 18),
67    };
68    static final AlphaComposite comps[] = {
69        AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f),
70        AlphaComposite.Src,
71        AlphaComposite.Xor,
72        AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f),
73        null,
74    };
75    static final Stroke strokes[] = {
76        new BasicStroke(),
77        new BasicStroke(0.0f),
78        new BasicStroke(2.0f),
79        new BasicStroke(2.0f, BasicStroke.CAP_ROUND,
80                        BasicStroke.JOIN_BEVEL),
81        new BasicStroke(5.0f, BasicStroke.CAP_SQUARE,
82                        BasicStroke.JOIN_ROUND),
83        new BasicStroke(0.0f, BasicStroke.CAP_ROUND,
84                        BasicStroke.JOIN_ROUND, 0,
85                        new float[]{0,6,0,6}, 0),
86    };
87    static final AffineTransform transforms[] = {
88        new AffineTransform(),
89        AffineTransform.getRotateInstance(10.0),
90        AffineTransform.getShearInstance(10.0, 4.0),
91        AffineTransform.getScaleInstance(1.1, 1.2),
92        AffineTransform.getScaleInstance(3.0, 2.0),
93    };
94
95    public MTGraphicsAccessTest() {
96        BufferedImage bi =
97            new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB);
98        sharedGraphics = (Graphics2D)bi.getGraphics();
99
100        done = false;
101        numexceptions = 0;
102
103        for (int i = 0; i < (standaloneMode ? stateChangers.length : 3); i++) {
104            (new TesterThread(stateChangers[i])).start();
105        }
106        for (int i = 0; i < (standaloneMode ? renderTests.length : 5); i++) {
107            (new TesterThread(renderTests[i])).start();
108        }
109
110        mysleep(testRunTime);
111        done = true;
112        while (stillRunning.get() > 0) { mysleep(500); }
113
114        if (numexceptions == 0) {
115            System.err.println("Test passed");
116        } else if (!allowExceptions) {
117            throw new RuntimeException("Test failed with "+
118                                       numexceptions+" exceptions");
119        } else {
120            System.err.println("Test finished with "+
121                               numexceptions+" exceptions");
122        }
123    }
124
125    private void mysleep(long time) {
126        try {
127            // add +/-5ms variance to increase randomness
128            Thread.sleep(time + (long)(5 - Math.random()*10));
129        } catch (InterruptedException e) {};
130    }
131
132    public static void usage(String message) {
133        if (message != null) {
134            System.err.println(message);
135        }
136        System.err.println("Usage: MTGraphicsAccessTest [-full] "+
137            "[-time N/forever] [-help]");
138        System.err.println(" -full: run full suite of tests "+
139            "(default: limited number of tests is run)");
140        System.err.println(" -time N: test duration in seconds/forever"+
141            " (default: "+JTREG_RUN_TIME+"s for the short suite, "+
142            STANDALONE_RUN_TIME+"s for the full suite)");
143        System.err.println(" -help: print this help page");
144        System.exit(1);
145    }
146
147    public static void main(String[] args) {
148        boolean testRunSet = false;
149        for (int i = 0; i < args.length; i++) {
150            if ("-full".equals(args[i])) {
151                standaloneMode = true;
152                System.err.println("Running complete list of tests");
153            } else if ("-noexc".equals(args[i])) {
154                allowExceptions = false;
155            } else if ("-time".equals(args[i])) {
156                try {
157                    String time = args[++i];
158                    if ("forever".equals(time)) {
159                        testRunTime = (Long.MAX_VALUE - 20)/1000;
160                    } else {
161                        testRunTime = 1000*Integer.parseInt(time);
162                    }
163                    testRunSet = true;
164                } catch (NumberFormatException e) {
165                    usage("Can't parse number of seconds: " + args[i]);
166                } catch (ArrayIndexOutOfBoundsException e1) {
167                    usage("Missing the 'seconds' argument for -time parameter");
168                }
169            } else if ("-help".equals(args[i])) {
170                usage(null);
171            } else {
172                usage("Unknown argument:" + args[i]);
173            }
174        }
175
176        if (!testRunSet) {
177            testRunTime = 1000 *
178                (standaloneMode ? STANDALONE_RUN_TIME : JTREG_RUN_TIME);
179        }
180
181        System.err.println("Approximate test run time: "+
182             testRunTime/1000+" seconds");
183
184        new MTGraphicsAccessTest();
185    }
186
187    class TesterThread extends Thread {
188        Runnable testRunnable;
189
190        public TesterThread(Runnable testRunnable) {
191            stillRunning.incrementAndGet();
192            this.testRunnable = testRunnable;
193        }
194
195        public void run() {
196            try {
197                while (!done) {
198                    try {
199                        testRunnable.run();
200                        yield();
201                    } catch (Throwable t) {
202                        numexceptions++;
203                        t.printStackTrace();
204                    }
205                }
206            } finally {
207                stillRunning.decrementAndGet();
208            }
209        }
210    }
211
212    final Runnable stateChangers[] = {
213        new Runnable() {
214            public void run() {
215                sharedGraphics.setClip(10, 10, 30, 30);
216                mysleep(10);
217            }
218        },
219        new Runnable() {
220            public void run() {
221                sharedGraphics.setClip(10, 10, 30, 30);
222                mysleep(10);
223            }
224        },
225        new Runnable() {
226            int c = 0;
227            public void run() {
228                sharedGraphics.setPaint(colors[c++ % colors.length]);
229                mysleep(10);
230            }
231        },
232        new Runnable() {
233            boolean AA = false;
234            public void run() {
235                if (AA) {
236                    sharedGraphics.setRenderingHint(
237                        RenderingHints.KEY_ANTIALIASING,
238                        RenderingHints.VALUE_ANTIALIAS_ON);
239                } else {
240                    sharedGraphics.setRenderingHint(
241                        RenderingHints.KEY_ANTIALIASING,
242                        RenderingHints.VALUE_ANTIALIAS_OFF);
243                }
244                AA = !AA;
245                mysleep(10);
246            }
247        },
248        new Runnable() {
249            int t = 0;
250            public void run() {
251                sharedGraphics.setTransform(
252                    transforms[t++ % transforms.length]);
253                mysleep(10);
254            }
255        },
256        new Runnable() {
257            int c = 0;
258            public void run() {
259                AlphaComposite comp = comps[c++ % comps.length];
260                if (comp == null) {
261                    sharedGraphics.setXORMode(Color.green);
262                } else {
263                    sharedGraphics.setComposite(comp);
264                }
265                mysleep(10);
266            }
267        },
268        new Runnable() {
269            int s = 0;
270            public void run() {
271                sharedGraphics.setStroke(strokes[s++ % strokes.length]);
272                mysleep(10);
273            }
274        },
275        new Runnable() {
276            int f = 0;
277            public void run() {
278                sharedGraphics.setFont(fonts[f++ % fonts.length]);
279                mysleep(10);
280            }
281        },
282    };
283
284    final Runnable renderTests[] = {
285        new Runnable() {
286            public void run() {
287                sharedGraphics.drawLine(10, 10, 30, 30);
288            }
289        },
290        new Runnable() {
291            public void run() {
292                sharedGraphics.drawLine(10, 10, 30, 30);
293            }
294        },
295        new Runnable() {
296            public void run() {
297                sharedGraphics.drawRect(10, 10, 30, 30);
298            }
299        },
300        new Runnable() {
301            public void run() {
302                sharedGraphics.fillRect(10, 10, 30, 30);
303            }
304        },
305        new Runnable() {
306            public void run() {
307                sharedGraphics.drawString("Stuff", 10, 10);
308            }
309        },
310        new Runnable() {
311            public void run() {
312                sharedGraphics.draw3DRect(10, 10, 30, 30, true);
313            }
314        },
315        new Runnable() {
316            public void run() {
317                sharedGraphics.drawImage(sharedBI, 10, 10, null);
318            }
319        },
320        new Runnable() {
321            public void run() {
322                sharedGraphics.fill3DRect(10, 10, 30, 30, false);
323            }
324        },
325        // REMIND: copyArea doesn't work when transform is set..
326        //          new Runnable() {
327        //              public void run() {
328        //                  sharedGraphics.copyArea(10, 10, 30, 30, 20, 20);
329        //              }
330        //          },
331        new Runnable() {
332            public void run() {
333                sharedGraphics.drawRoundRect(10, 10, 30, 30, 20, 20);
334            }
335        },
336        new Runnable() {
337            public void run() {
338                sharedGraphics.fillRoundRect(10, 10, 30, 30, 20, 20);
339            }
340        },
341        new Runnable() {
342            public void run() {
343                sharedGraphics.drawArc(10, 10, 30, 30, 0, 90);
344            }
345        },
346        new Runnable() {
347            public void run() {
348                sharedGraphics.fillArc(10, 10, 30, 30, 0, 90);
349            }
350        },
351        new Runnable() {
352            public void run() {
353                sharedGraphics.drawOval(10, 10, 30, 30);
354            }
355        },
356        new Runnable() {
357            public void run() {
358                sharedGraphics.fillOval(10, 10, 30, 30);
359            }
360        }
361    };
362}
363