/**
* Copyright 2015 JogAmp Community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of JogAmp Community.
*/
package com.jogamp.opengl.test.junit.newt;
import java.io.IOException;
import org.junit.Assert;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import com.jogamp.common.ExceptionUtils;
import com.jogamp.common.util.InterruptSource;
import com.jogamp.common.util.SourcedInterruptedException;
import com.jogamp.common.util.VersionUtil;
import com.jogamp.junit.util.SingletonJunitCase;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.newt.util.EDTUtil;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLCapabilitiesImmutable;
import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
import com.jogamp.opengl.util.Animator;
/**
* Unit test to identify Thread.interrupt() caller for DefaultEDTUtil.invokeImpl(..) wait interruption.
* <ul>
* <li>resize</li>
* <li>create/destroy</li>
* </ul>
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestBug1211IRQ00NEWT extends SingletonJunitCase {
static long durationTest00 = 1000; // ms
static long durationTest01 = 1000; // ms
static int width = 800;
static int height = 600;
static GLWindow createWindow(final GLCapabilitiesImmutable caps) {
Assert.assertNotNull(caps);
//
// Create native windowing resources .. X11/Win/OSX
//
final GLWindow glWindow = GLWindow.create(caps);
Assert.assertNotNull(glWindow);
glWindow.setSize(width, height);
glWindow.setUpdateFPSFrames(1, null);
final GearsES2 demo = new GearsES2();
demo.setVerbose(false);
glWindow.addGLEventListener(demo);
return glWindow;
}
static void destroyWindow(final GLWindow glWindow) throws InterruptedException {
if(null!=glWindow) {
glWindow.destroy();
Assert.assertEquals(false,glWindow.isNativeValid());
}
}
static class MyThread extends InterruptSource.Thread implements Thread.UncaughtExceptionHandler {
volatile boolean myThreadStarted = false;
volatile boolean myThreadStopped = false;
public MyThread(final Runnable target, final String name) {
super(null, target, name);
setUncaughtExceptionHandler(this);
}
public static void testInterrupted1() throws InterruptedException {
if( java.lang.Thread.interrupted() ) {
throw SourcedInterruptedException.wrap(
new InterruptedException(java.lang.Thread.currentThread().getName()+".testInterrupted -> TRUE (silent interruption)"));
}
}
public synchronized void testInterrupted(final boolean ignore) throws InterruptedException {
if( isInterrupted() ) {
final boolean current;
if( this == java.lang.Thread.currentThread() ) {
java.lang.Thread.interrupted(); // clear!
current = true;
} else {
current = false;
}
final int counter = getInterruptCounter(false);
final Throwable source = getInterruptSource(true);
final InterruptedException e = new SourcedInterruptedException(
getName()+".testInterrupted -> TRUE (current "+current+", counter "+counter+")",
null, source);
if( !ignore ) {
throw e;
} else {
ExceptionUtils.dumpThrowable("Ignored", e);
}
}
}
@Override
public void run() {
myThreadStarted = true;
try {
super.run();
} finally {
myThreadStopped = true;
}
}
@Override
public void uncaughtException(final java.lang.Thread t, final Throwable e) {
System.err.println("UncaughtException on Thread "+t.getName()+": "+e.getMessage());
ExceptionUtils.dumpThrowable("UncaughtException", e);
}
}
volatile boolean interrupt1 = false;
volatile boolean interrupt2 = false;
volatile boolean interruptInit0 = false;
public void initTest() {
interrupt1 = false;
interrupt2 = false;
}
/**
* Test whether resize triggers DefaultEDTUtil.invokeImpl(..) wait interruption.
*/
public void subTest00() {
final MyThread t = (MyThread)Thread.currentThread();
final GLCapabilities caps = new GLCapabilities(GLProfile.getDefault());
Assert.assertNotNull(caps);
final GLWindow window1 = createWindow(caps); // local
final EDTUtil edt = window1.getScreen().getDisplay().getEDTUtil();
final Animator anim = new Animator(window1);
try {
window1.setVisible(true);
Assert.assertEquals(true,window1.isVisible());
Assert.assertEquals(true,window1.isNativeValid());
anim.start();
boolean ok = true;
for(int i=0; ok && i*100<durationTest00; i++) {
Thread.sleep(100);
final int ow = window1.getWidth();
final int oh = window1.getHeight();
final int nw, nh;
if( 0 == i % 2 ) {
nw = ow + 100;
nh = oh + 100;
} else {
nw = ow - 100;
nh = oh - 100;
}
System.err.println("test00.resize["+i+"]: "+ow+"x"+oh+" -> "+nw+"x"+nh);
window1.setSize(nw, nh);
ok = 0==t.getInterruptCounter(false) && !t.isInterrupted() && edt.isRunning() && anim.isAnimating();
t.testInterrupted(false);
}
} catch (final InterruptedException e) {
ExceptionUtils.dumpThrowable("InterruptedException-1", e);
interrupt1 = true;
}
try {
anim.stop();
destroyWindow(window1);
t.testInterrupted(false);
} catch (final InterruptedException e) {
ExceptionUtils.dumpThrowable("InterruptedException-2", e);
interrupt2 = true;
}
Assert.assertEquals("interruptCounter not zero", 0, t.getInterruptCounter(false));
Assert.assertFalse("interrupt() occured!", t.isInterrupted());
Assert.assertFalse("Interrupt-1 occured!", interrupt1);
Assert.assertFalse("Interrupt-2 occured!", interrupt2);
}
/**
* Test whether create/destroy triggers DefaultEDTUtil.invokeImpl(..) wait interruption.
*/
public void subTest01() {
final MyThread t = (MyThread)Thread.currentThread();
GLWindow lastWindow = null;
try {
final boolean ok = true;
for(int i=0; ok && i*100<durationTest01; i++) {
final GLCapabilities caps = new GLCapabilities(GLProfile.getDefault());
Assert.assertNotNull(caps);
final GLWindow window1 = createWindow(caps); // local
lastWindow = window1;
window1.setVisible(true);
Assert.assertEquals(true,window1.isVisible());
Assert.assertEquals(true,window1.isNativeValid());
System.err.println("test01.create["+i+"]: "+window1.getStateMaskString()+", "+window1.getWidth()+"x"+window1.getHeight());
final Animator anim = new Animator(window1);
anim.start();
Thread.sleep(100);
anim.stop();
destroyWindow(window1);
t.testInterrupted(false);
}
} catch (final InterruptedException e) {
ExceptionUtils.dumpThrowable("InterruptedException-1", e);
interrupt1 = true;
}
try {
destroyWindow(lastWindow);
t.testInterrupted(false);
} catch (final InterruptedException e) {
ExceptionUtils.dumpThrowable("InterruptedException-2", e);
interrupt2 = true;
}
Assert.assertEquals("interruptCounter not zero", 0, t.getInterruptCounter(false));
Assert.assertFalse("interrupt() occured!", t.isInterrupted());
Assert.assertFalse("Interrupt-1 occured!", interrupt1);
Assert.assertFalse("Interrupt-2 occured!", interrupt2);
}
@Test
public void testAll() {
interruptInit0 = false;
final MyThread t = new MyThread(new Runnable() {
public void run() {
final MyThread t = (MyThread)Thread.currentThread();
TestBug1211IRQ00NEWT test = null;
try {
System.err.println(VersionUtil.getPlatformInfo());
GLProfile.initSingleton();
test = new TestBug1211IRQ00NEWT();
t.testInterrupted(false);
} catch (final InterruptedException e) {
ExceptionUtils.dumpThrowable("InterruptedException-Init0", e);
interruptInit0 = true;
test = null;
}
t.clearInterruptSource();
if( null != test ) {
test.initTest();
test.subTest00();
test.initTest();
test.subTest01();
}
}
}, "MyMainThread");
t.start();
boolean interrupted = false;
try {
MyThread.testInterrupted1();
while( !t.myThreadStarted ) {
Thread.yield();
MyThread.testInterrupted1();
}
while( !t.myThreadStopped ) {
Thread.yield();
MyThread.testInterrupted1();
}
MyThread.testInterrupted1();
} catch (final InterruptedException e) {
ExceptionUtils.dumpThrowable("InterruptedException-All", e);
interrupted = true;
}
Assert.assertFalse("Thread Interrupt-All occured!", interrupted);
Assert.assertFalse("Interrupt-Init0 occured!", interruptInit0);
}
static int atoi(final String a) {
int i=0;
try {
i = Integer.parseInt(a);
} catch (final Exception ex) { ex.printStackTrace(); }
return i;
}
public static void main(final String args[]) throws IOException {
// We like to allow concurrent manual tests!
SingletonJunitCase.enableSingletonLock(false);
for(int i=0; i<args.length; i++) {
if(args[i].equals("-time00")) {
durationTest00 = atoi(args[++i]);
} else if(args[i].equals("-time01")) {
durationTest01 = atoi(args[++i]);
} else if(args[i].equals("-width")) {
width = atoi(args[++i]);
} else if(args[i].equals("-height")) {
height = atoi(args[++i]);
}
}
System.out.println("durationTest00: "+durationTest00);
System.out.println("durationTest01: "+durationTest01);
System.out.println("defaultSize : "+width+"x"+height);
final String tstname = TestBug1211IRQ00NEWT.class.getName();
org.junit.runner.JUnitCore.main(tstname);
}
}