JOGL v2.6.0-rc-20250712
JOGL, High-Performance Graphics Binding for Java™ (public API).
UITestCase.java
Go to the documentation of this file.
1/**
2 * Copyright 2010 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28
29package com.jogamp.opengl.test.junit.util;
30
31import java.io.BufferedReader;
32import java.io.File;
33import java.io.OutputStream;
34import java.io.StringReader;
35import java.util.ArrayList;
36import java.util.Arrays;
37import java.util.Iterator;
38import java.util.List;
39import java.util.Locale;
40import java.util.StringTokenizer;
41import java.util.concurrent.atomic.AtomicInteger;
42
43import com.jogamp.nativewindow.NativeWindowFactory;
44import com.jogamp.opengl.GL;
45import com.jogamp.opengl.GLAutoDrawable;
46import com.jogamp.opengl.GLCapabilitiesImmutable;
47import com.jogamp.opengl.GLDrawable;
48import com.jogamp.opengl.GLEventListener;
49
50import com.jogamp.junit.util.SingletonJunitCase;
51import com.jogamp.opengl.util.GLReadBufferUtil;
52import com.jogamp.opengl.util.texture.TextureIO;
53
54import org.junit.BeforeClass;
55import org.junit.AfterClass;
56import org.junit.FixMethodOrder;
57import org.junit.runners.MethodSorters;
58import org.junit.runners.model.FrameworkMethod;
59import org.junit.runners.model.TestClass;
60
61@FixMethodOrder(MethodSorters.NAME_ASCENDING)
62public abstract class UITestCase extends SingletonJunitCase {
63 private static volatile boolean resetXRandRIfX11AfterClass = false;
64
65 private static volatile int maxMethodNameLen = 0;
66
67 public static void setResetXRandRIfX11AfterClass() {
68 resetXRandRIfX11AfterClass = true;
69 }
70
71 /**
72 * Iterates through all outputs and sets the preferred mode and normal rotation using RandR 1.3.
73 * <p>
74 * With NV drivers, one need to add the Modes in proper order to the Screen's Subsection "Display",
75 * otherwise they are either in unsorted resolution order or even n/a!
76 * </p>
77 * @return error-code with {@code zero} for no error
78 */
79 @SuppressWarnings("unused")
80 public static int resetXRandRIfX11() {
81 int errorCode = 0;
83 try {
84 final List<String> outputDevices = new ArrayList<String>();
85 // final List<String> outputSizes = new ArrayList<String>();
86 final StringBuilder out = new StringBuilder();
87 final String[] cmdlineQuery = new String[] { "xrandr", "-q" };
88 errorCode = processCommand(cmdlineQuery, null, out, "xrandr-query> ");
89 if( 0 != errorCode ) {
90 System.err.println("XRandR Query Error Code "+errorCode);
91 System.err.println(out.toString());
92 } else {
93 // Parse connected output devices !
94 final BufferedReader in = new BufferedReader( new StringReader( out.toString() ) );
95 String line = null;
96 while ( ( line = in.readLine() ) != null) {
97 final String lline = line.toLowerCase();
98 if( lline.contains("connected") && !lline.contains("disconnected") ) {
99 final String od = getFirst(line);
100 if( null != od ) {
101 outputDevices.add( od );
102 /**
103 if ( ( line = in.readLine() ) != null ) {
104 outputSizes.add( getFirst(line) );
105 } else {
106 outputSizes.add( null );
107 } */
108 }
109 }
110 }
111 for(int i=0; i<outputDevices.size(); i++) {
112 final String outputDevice = outputDevices.get(i);
113 final String outputSize = null; // outputSizes.get(i)
114 final String[] cmdline;
115 if( null != outputSize ) {
116 cmdline = new String[] { "xrandr", "--output", outputDevice, "--mode", outputSize, "--rotate", "normal" };
117 } else {
118 cmdline = new String[] { "xrandr", "--output", outputDevice, "--preferred", "--rotate", "normal" };
119 }
120 System.err.println("XRandR 1.2 Reset: "+Arrays.asList(cmdline));
121 errorCode = processCommand(cmdline, System.err, null, "xrandr-1.2-reset> ");
122 if( 0 != errorCode ) {
123 System.err.println("XRandR 1.2 Reset Error Code "+errorCode);
124 break;
125 }
126 }
127 /**
128 * RandR 1.1 reset does not work ..
129 if( 0 != errorCode ) {
130 final String[] cmdline = new String[] { "xrandr", "-s", "0", "-o", "normal" };
131 System.err.println("XRandR 1.1 Reset: "+Arrays.asList(cmdline));
132 errorCode = processCommand(cmdline, System.err, null, "xrandr-1.1-reset> ");
133 if( 0 != errorCode ) {
134 System.err.println("XRandR 1.1 Reset Error Code "+errorCode);
135 }
136 } */
137 }
138 } catch (final Exception e) {
139 System.err.println("Caught "+e.getClass().getName()+": "+e.getMessage());
140 e.printStackTrace();
141 errorCode = -1;
142 }
143 }
144 return errorCode;
145 }
146 private static String getFirst(final String line) {
147 final StringTokenizer tok = new StringTokenizer(line);
148 if( tok.hasMoreTokens() ) {
149 final String s = tok.nextToken().trim();
150 if( s.length() > 0 ) {
151 return s;
152 }
153 }
154 return null;
155 }
156
157 public static int processCommand(final String[] cmdline, final OutputStream outstream, final StringBuilder outstring, final String outPrefix) {
158 int errorCode = 0;
159 final Object ioSync = new Object();
160 try {
161 synchronized ( ioSync ) {
162 final ProcessBuilder pb = new ProcessBuilder(cmdline);
163 pb.redirectErrorStream(true);
164 final Process p = pb.start();
165 final MiscUtils.StreamDump dump;
166 if( null != outstream ) {
167 dump = new MiscUtils.StreamDump( outstream, outPrefix, p.getInputStream(), ioSync);
168 } else if( null != outstring ) {
169 dump = new MiscUtils.StreamDump( outstring, outPrefix, p.getInputStream(), ioSync);
170 } else {
171 throw new IllegalArgumentException("Output stream and string are null");
172 }
173 dump.start();
174 while( !dump.eos() ) {
175 ioSync.wait();
176 }
177 p.waitFor(); // should be fine by now ..
178 errorCode = p.exitValue();
179 }
180 } catch (final Exception e) {
181 System.err.println("Caught "+e.getClass().getName()+": "+e.getMessage());
182 e.printStackTrace();
183 errorCode = Integer.MIN_VALUE;
184 }
185 return errorCode;
186 }
187
188 public int getMaxTestNameLen() {
189 if(0 == maxMethodNameLen) {
190 int ml = 0;
191 final TestClass tc = new TestClass(getClass());
192 final List<FrameworkMethod> testMethods = tc.getAnnotatedMethods(org.junit.Test.class);
193 for(final Iterator<FrameworkMethod> iter=testMethods.iterator(); iter.hasNext(); ) {
194 final int l = iter.next().getName().length();
195 if( ml < l ) { ml = l; }
196 }
197 maxMethodNameLen = ml;
198 }
199 return maxMethodNameLen;
200 }
201
202 @BeforeClass
203 public static final void oneTimeSetUpUITest() {
204 // one-time initialization code
205 }
206
207 @AfterClass
208 public static final void oneTimeTearDownUITest() {
209 // one-time cleanup code
210 if( resetXRandRIfX11AfterClass ) {
211 resetXRandRIfX11();
212 }
213 }
214
215 public String getSnapshotFilename(final int sn, String postSNDetail, final GLCapabilitiesImmutable caps, final int width, final int height, final boolean sinkHasAlpha, String fileSuffix, final String destPath) {
216 if(null == fileSuffix) {
217 fileSuffix = TextureIO.PNG;
218 }
219 final int maxSimpleTestNameLen = getMaxTestNameLen()+getClass().getSimpleName().length()+1;
220 final String simpleTestName = this.getSimpleTestName(".");
221 final String filenameBaseName;
222 {
223 final String accel = caps.getHardwareAccelerated() ? "hw" : "sw" ;
224 final String scrnm;
225 if(caps.isOnscreen()) {
226 scrnm = "onscreen";
227 } else if(caps.isFBO()) {
228 scrnm = "fbobject";
229 } else if(caps.isPBuffer()) {
230 scrnm = "pbuffer_";
231 } else if(caps.isBitmap()) {
232 scrnm = "bitmap__";
233 } else {
234 scrnm = "unknown_";
235 }
236 final String dblb = caps.getDoubleBuffered() ? "dbl" : "one";
237 final String F_pfmt = sinkHasAlpha ? "rgba" : "rgb_";
238 final String pfmt = "rgba" + caps.getRedBits() + caps.getGreenBits() + caps.getBlueBits() + caps.getAlphaBits();
239 final int depthBits = caps.getDepthBits();
240 final int stencilBits = caps.getStencilBits();
241 final int samples = caps.getNumSamples() ;
242 final String aaext = caps.getSampleExtension();
243 postSNDetail = null != postSNDetail ? "-"+postSNDetail : "";
244
245 filenameBaseName = String.format((Locale)null, "%-"+maxSimpleTestNameLen+"s-n%04d%s-%-6s-%s-%s-B%s-F%s_I%s-D%02d-St%02d-Sa%02d_%s-%04dx%04d.%s",
246 simpleTestName, sn, postSNDetail, caps.getGLProfile().getName(), accel,
247 scrnm, dblb, F_pfmt, pfmt, depthBits, stencilBits, samples, aaext,
248 width, height, fileSuffix).replace(' ', '_');
249 }
250 return null != destPath ? destPath + File.separator + filenameBaseName : filenameBaseName;
251 }
252
253 /**
254 * Takes a snapshot of the drawable's current front framebuffer. Example filenames:
255 * <pre>
256 * TestGLDrawableAutoDelegateOnOffscrnCapsNEWT.testES2OffScreenFBOSglBuf____-n0001-msaa0-GLES2_-sw-fbobject-Bdbl-Frgb__Irgba8888_-D24-St00-Sa00_default-0400x0300.png
257 * TestGLDrawableAutoDelegateOnOffscrnCapsNEWT.testES2OffScreenPbufferDblBuf-n0003-msaa0-GLES2_-sw-pbuffer_-Bdbl-Frgb__Irgba8880-D24-St00-Sa00_default-0200x0150.png
258 * TestGLDrawableAutoDelegateOnOffscrnCapsNEWT.testGL2OffScreenPbufferSglBuf-n0003-msaa0-GL2___-hw-pbuffer_-Bone-Frgb__Irgba5551-D24-St00-Sa00_default-0200x0150.png
259 * </pre>
260 * @param sn sequential number
261 * @param postSNDetail optional detail to be added to the filename after <code>sn</code>
262 * @param gl the current GL context object. It's read drawable is being used as the pixel source and to gather some details which will end up in the filename.
263 * @param readBufferUtil the {@link GLReadBufferUtil} to be used to read the pixels for the screenshot.
264 * @param fileSuffix Optional file suffix without a <i>dot</i> defining the file type, i.e. <code>"png"</code>.
265 * If <code>null</code> the <code>"png"</code> as defined in {@link TextureIO#PNG} is being used.
266 * @param destPath Optional platform dependent file path. It shall use {@link File#separatorChar} as is directory separator.
267 * It shall not end with a directory separator, {@link File#separatorChar}.
268 * If <code>null</code> the current working directory is being used.
269 */
270 public void snapshot(final int sn, final String postSNDetail, final GL gl, final GLReadBufferUtil readBufferUtil, final String fileSuffix, final String destPath) {
271
272 final GLDrawable drawable = gl.getContext().getGLReadDrawable();
273 final String filename = getSnapshotFilename(sn, postSNDetail,
274 drawable.getChosenGLCapabilities(), drawable.getSurfaceWidth(), drawable.getSurfaceHeight(),
275 readBufferUtil.hasAlpha(), fileSuffix, destPath);
276 System.err.println(Thread.currentThread().getName()+": ** screenshot: "+filename);
277 gl.glFinish(); // just make sure rendering finished ..
278 try {
279 snapshot(gl, readBufferUtil, filename);
280 } catch (final ClassNotFoundException cnfe) {
281 // Texture class belongs to jogl-util.jar which my not be included in test environment!
282 System.err.println("Caught ClassNotFoundException: "+cnfe.getMessage());
283 } catch (final NoClassDefFoundError cnfe) {
284 // Texture class belongs to jogl-util.jar which my not be included in test environment!
285 System.err.println("Caught NoClassDefFoundError: "+cnfe.getMessage());
286 }
287 }
288 private void snapshot(final GL gl, final GLReadBufferUtil readBufferUtil, final String filename) throws ClassNotFoundException, NoClassDefFoundError {
289 if(readBufferUtil.readPixels(gl, false)) {
290 readBufferUtil.write(new File(filename));
291 }
292 }
293
294 public class SnapshotGLEventListener implements GLEventListener {
295 private final GLReadBufferUtil screenshot;
296 private volatile boolean makeShot = false;
297 private volatile boolean makeShotAlways = false;
298 private volatile boolean verbose = false;
299 private final AtomicInteger displayCount = new AtomicInteger(0);
300 private final AtomicInteger reshapeCount = new AtomicInteger(0);
301 private volatile String postSNDetail = null;
302 public SnapshotGLEventListener(final GLReadBufferUtil screenshot) {
303 this.screenshot = screenshot;
304 }
306 this.screenshot = new GLReadBufferUtil(false, false);
307 }
308 public int getDisplayCount() { return displayCount.get(); }
309 public int getReshapeCount() { return reshapeCount.get(); }
310 public GLReadBufferUtil getGLReadBufferUtil() { return screenshot; }
311 public void init(final GLAutoDrawable drawable) {}
312 public void dispose(final GLAutoDrawable drawable) {}
313 public void display(final GLAutoDrawable drawable) {
314 final GL gl = drawable.getGL();
315 final boolean _makeShot = makeShot || makeShotAlways;
316 if(verbose) {
317 System.err.println(Thread.currentThread().getName()+": ** display: "+displayCount+": "+drawable.getSurfaceWidth()+"x"+drawable.getSurfaceHeight()+", makeShot "+_makeShot);
318 }
319 if(_makeShot) {
320 makeShot=false;
321 snapshot(displayCount.get(), postSNDetail, gl, screenshot, TextureIO.PNG, null);
322 }
323 displayCount.incrementAndGet();
324 }
325 public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {
326 if(verbose) {
327 System.err.println(Thread.currentThread().getName()+": ** reshape: "+reshapeCount+": "+width+"x"+height+" - "+drawable.getSurfaceWidth()+"x"+drawable.getSurfaceHeight());
328 }
329 reshapeCount.incrementAndGet();
330 }
331 public void setMakeSnapshot() {
332 makeShot=true;
333 }
334 public void setMakeSnapshotAlways(final boolean v) {
335 makeShotAlways=v;
336 }
337 public void setVerbose(final boolean v) {
338 verbose=v;
339 }
340 public void setPostSNDetail(final String v) {
341 postSNDetail = v;
342 }
343 };
344
345}
346
Provides a pluggable mechanism for arbitrary window toolkits to adapt their components to the NativeW...
static final String TYPE_X11
X11 type, as retrieved with getNativeWindowType(boolean).
static String getNativeWindowType(final boolean useCustom)
static synchronized boolean isInitialized()
Returns true if initSingleton() has been called w/o subsequent shutdown(boolean).
abstract GLDrawable getGLReadDrawable()
Returns the read-Drawable this context uses for read framebuffer operations.
final String getName()
return this profiles name
void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height)
Called by the drawable during the first repaint after the component has been resized.
void init(final GLAutoDrawable drawable)
Called by the drawable immediately after the OpenGL context is initialized.
void display(final GLAutoDrawable drawable)
Called by the drawable to initiate OpenGL rendering by the client.
void dispose(final GLAutoDrawable drawable)
Notifies the listener to perform the release of all OpenGL resources per GLContext,...
String getSnapshotFilename(final int sn, String postSNDetail, final GLCapabilitiesImmutable caps, final int width, final int height, final boolean sinkHasAlpha, String fileSuffix, final String destPath)
void snapshot(final int sn, final String postSNDetail, final GL gl, final GLReadBufferUtil readBufferUtil, final String fileSuffix, final String destPath)
Takes a snapshot of the drawable's current front framebuffer.
static int processCommand(final String[] cmdline, final OutputStream outstream, final StringBuilder outstring, final String outPrefix)
Utility to read out the current FB to TextureData, optionally writing the data back to a texture obje...
static final String PNG
Constant which can be used as a file suffix to indicate a PNG file, value {@value}.
Definition: TextureIO.java:171
int getAlphaBits()
Returns the number of bits for the color buffer's alpha component.
int getBlueBits()
Returns the number of bits for the color buffer's blue component.
boolean isBitmap()
Returns whether bitmap offscreen mode is requested, available or chosen.
int getRedBits()
Returns the number of bits for the color buffer's red component.
int getGreenBits()
Returns the number of bits for the color buffer's green component.
boolean isOnscreen()
Returns whether an on- or offscreen surface is requested, available or chosen.
A higher-level abstraction than GLDrawable which supplies an event based mechanism (GLEventListener) ...
GL getGL()
Returns the GL pipeline object this GLAutoDrawable uses.
GLContext getContext()
Returns the GLContext associated which this GL object.
Specifies an immutable set of OpenGL capabilities.
String getSampleExtension()
Returns the extension for full-scene antialiasing (FSAA).
int getNumSamples()
Returns the number of sample buffers to be allocated if sample buffers are enabled,...
boolean getHardwareAccelerated()
Returns whether hardware acceleration is requested, available or chosen.
int getDepthBits()
Returns the number of depth buffer bits.
boolean isPBuffer()
Returns whether pbuffer offscreen mode is requested, available or chosen.
GLProfile getGLProfile()
Returns the GL profile you desire or used by the drawable.
boolean getDoubleBuffered()
Returns whether double-buffering is requested, available or chosen.
boolean isFBO()
Returns whether FBO offscreen mode is requested, available or chosen.
int getStencilBits()
Returns the number of stencil buffer bits.
An abstraction for an OpenGL rendering target.
Definition: GLDrawable.java:51
GLCapabilitiesImmutable getChosenGLCapabilities()
Fetches the GLCapabilitiesImmutable corresponding to the chosen OpenGL capabilities (pixel format / v...
int getSurfaceWidth()
Returns the width of this GLDrawable's surface client area in pixel units.
int getSurfaceHeight()
Returns the height of this GLDrawable's surface client area in pixel units.
Declares events which client code can use to manage OpenGL rendering into a GLAutoDrawable.
void glFinish()
Entry point to C language function: void {@native glFinish}() Part of GL_ES_VERSION_2_0,...