I would like to test methods that have JavaFX scene elements like TextField or DatePicker. I found here How do you unit test a JavaFX controller with JUnit how to run test for JavaFX app and it works fine for me if I have just one test class where Thread is initialized. But what about if I have two classes? I am not able to initialize the same thread with launch method because exception will be thrown. If I will not create thread in second test class there is also another exception. Below you can find test classes and stack trace. I was also thinking about stopping this thread in method with #AfterClass annotation and then starting it in second class again but I do not think it is possible. Does anyone can help me?
FirstTestClass
#RunWith(PowerMockRunner.class)
#PrepareForTest({TextField.class, DatePicker.class})
public class FirstTestClass {
#BeforeClass
public static void javaFXInitializer() throws SQLException, InterruptedException {
Thread thread = new Thread("JavaFX Init Thread") {
public void run() {
Application.launch(Main.class);
}
};
thread.setDaemon(true);
thread.start();
Thread.sleep(5000);
}
#Test
public void firstTest() {
TextField textFieldMock = mock(TextField.class);
DatePicker datePickerMock = mock(DatePicker.class);
TestClass objectUnderTest = new TestClass();
when(textFieldMock.getText()).thenReturn("2000");
when(datePickerMock.getValue()).thenReturn(LocalDate.of(1992,1,1));
//rest of code to test
}
}
SecondTestClass
#RunWith(PowerMockRunner.class)
#PrepareForTest({TextField.class, DatePicker.class})
public class SecondTestClass {
#BeforeClass
public static void javaFXInitializer() throws SQLException, InterruptedException {
Thread thread = new Thread("JavaFX Init Thread") {
public void run() {
Application.launch(Main.class);
}
};
thread.setDaemon(true);
thread.start();
Thread.sleep(5000);
}
#Test
public void firstTest() {
TextField textFieldMock = mock(TextField.class);
DatePicker datePickerMock = mock(DatePicker.class);
TestClass objectUnderTest = new TestClass();
when(textFieldMock.getText()).thenReturn("5000");
when(datePickerMock.getValue()).thenReturn(LocalDate.of(1990,1,1));
//rest of code to test
}
}
Exception when I initialize thread in two classes
Exception in thread "JavaFX IncomeTest Init Thread" java.lang.RuntimeException: java.lang.UnsatisfiedLinkError: Native Library C:\Program Files\Java\jdk1.8.0_144\jre\bin\glass.dll already loaded in another classloader
at com.sun.javafx.tk.quantum.QuantumToolkit.startup(QuantumToolkit.java:267)
at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:211)
at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:675)
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:695)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$155(LauncherImpl.java:182)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.UnsatisfiedLinkError: Native Library C:\Program Files\Java\jdk1.8.0_144\jre\bin\glass.dll already loaded in another classloader
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1907)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1824)
at java.lang.Runtime.load0(Runtime.java:809)
at java.lang.System.load(System.java:1086)
at com.sun.glass.utils.NativeLibLoader.loadLibraryFullPath(NativeLibLoader.java:201)
at com.sun.glass.utils.NativeLibLoader.loadLibraryInternal(NativeLibLoader.java:94)
at com.sun.glass.utils.NativeLibLoader.loadLibrary(NativeLibLoader.java:39)
at com.sun.glass.ui.Application.loadNativeLibrary(Application.java:112)
at com.sun.glass.ui.Application.loadNativeLibrary(Application.java:120)
at com.sun.glass.ui.win.WinApplication.access$300(WinApplication.java:39)
at com.sun.glass.ui.win.WinApplication$1.run(WinApplication.java:118)
at com.sun.glass.ui.win.WinApplication$1.run(WinApplication.java:91)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.glass.ui.win.WinApplication.<clinit>(WinApplication.java:91)
at com.sun.glass.ui.win.WinPlatformFactory.createApplication(WinPlatformFactory.java:39)
at com.sun.glass.ui.win.WinPlatformFactory.createApplication(WinPlatformFactory.java:36)
at com.sun.glass.ui.Application.run(Application.java:146)
at com.sun.javafx.tk.quantum.QuantumToolkit.startup(QuantumToolkit.java:257)
... 5 more
Exception when Thread is initialized only in first class
java.lang.ExceptionInInitializerError
at sun.reflect.GeneratedSerializationConstructorAccessor12.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:45)
at org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:73)
at org.mockito.internal.creation.instance.ObjenesisInstantiator.newInstance(ObjenesisInstantiator.java:14)
at org.powermock.api.mockito.repackaged.ClassImposterizer.createProxy(ClassImposterizer.java:149)
at org.powermock.api.mockito.repackaged.ClassImposterizer.imposterise(ClassImposterizer.java:64)
at org.powermock.api.mockito.internal.mockcreation.DefaultMockCreator.createMethodInvocationControl(DefaultMockCreator.java:121)
at org.powermock.api.mockito.internal.mockcreation.DefaultMockCreator.createMock(DefaultMockCreator.java:69)
at org.powermock.api.mockito.internal.mockcreation.DefaultMockCreator.mock(DefaultMockCreator.java:46)
at org.powermock.api.mockito.PowerMockito.mock(PowerMockito.java:141)
at
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:326)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:310)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:131)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.access$100(PowerMockJUnit47RunnerDelegateImpl.java:59)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner$TestExecutorStatement.evaluate(PowerMockJUnit47RunnerDelegateImpl.java:147)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.evaluateStatement(PowerMockJUnit47RunnerDelegateImpl.java:107)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:298)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:218)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:160)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:134)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:136)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:121)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:57)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Caused by: java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:273)
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:268)
at com.sun.javafx.application.PlatformImpl.setPlatformUserAgentStylesheet(PlatformImpl.java:550)
at com.sun.javafx.application.PlatformImpl.setDefaultPlatformUserAgentStylesheet(PlatformImpl.java:512)
at javafx.scene.control.Control.<clinit>(Control.java:87)
... 50 more
Create a class MockApp extends Application and have a static method init(). In that method create a new single thread in which to invoke the JavaFX platform start (basically what you've done already) with an appropriate guard. The guard (a boolean flag) guarantees that the method gets called maximum once. The point is that the thread never terminates, thus allowing you to access the JavaFX platform during the lifetime of all your tests. The catch is you need to add something like:
#BeforeClass
public void init() {
MockApp.init();
}
for each test class where you use JavaFX since you don't know the order in which tests run.
The approach (with minor changes) can be seen in practice here.
Related
I'm still fairly new to Java and I've started on a simple GUI project with the netbeans java IDE 8.2 using swing and am running into a null pointer exception.
I've removed the majority of the code down to the bottom portion in order to try and find where the issue is coming from and I still get an exception running only these lines of code.
package finalmodel;
import javax.swing.*;
public class FinalModel extends JFrame {
public static void main (String[] args) {
new FinalModel();
}
public FinalModel () {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}
The application runs fine, and as far as I can tell, the exception doesn't affect functionality (a window pops up and can be successfully closed from the above code), but the exception is thrown upon the window's closure.
The stack trace given to me by the IDE is
java.lang.NullPointerException: peer
at sun.awt.windows.WInputMethod.openCandidateWindow(Native Method)
at sun.awt.windows.WInputMethod.access$400(WInputMethod.java:45)
at sun.awt.windows.WInputMethod$1.run(WInputMethod.java:602)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:90)
at java.awt.EventQueue$4.run(EventQueue.java:733)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:730)
at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:159) [catch]
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:205)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
Version Info
your example is very minimal. it might be missing some properties. give this a try:
package finalmodel;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class FinalModel extends JFrame {
public FinalModel () {
setTitle("setTitle()");
setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
FinalModel fm = new FinalModel ();
fm.setVisible(true);
}
}
here's a good article on jframe, for practice jframes
Netbeans bug! Reinstall Netbeans with un updated version or something like that.
I'm working on an app, that searches some files in the directed folder and prints them in TableView<myFile> foundFilesList, that is stored in the Main class. myFile just extends File a bit. The searching is done using service in background thread, that puts found data to ObservableList<myFile> filesOfUser.
I want to display current amount of find files in TextField foundFilesAmount in the same view, where TableView with files is located -- ResultsView
To do that, I added a ListChangeListener for foundFilesList to ResultsView controller, that uses method setText to print current size of filesOfUser. It looks like:
Main.filesOfUser.addListener(new ListChangeListener<myFile>() {
#Override
public void onChanged(Change<? extends myFile> c) {
while (c.next()){
if (c.wasAdded())
setCounter(c.getAddedSize());
}
}
});
void setCounter (int number) contains only
int currValue = Integer.valueOf(foundFilesAmount.getText());
foundFilesAmount.setText(String.valueOf(currValue + number));
And now what the problem is. Textfield with current amount of find files is updated very fast, and from one moment it stops doing it. In the console I see lots of repeated NullPointerException's from JavaFX Application Thread. Its' contents:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at com.sun.javafx.text.PrismTextLayout.getRuns(PrismTextLayout.java:236)
at javafx.scene.text.Text.getRuns(Text.java:317)
at javafx.scene.text.Text.updatePGText(Text.java:1465)
at javafx.scene.text.Text.impl_updatePeer(Text.java:1500)
at javafx.scene.Node.impl_syncPeer(Node.java:503)
at javafx.scene.Scene$ScenePulseListener.synchronizeSceneNodes(Scene.java:2290)
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2419)
at com.sun.javafx.tk.Toolkit.lambda$runPulse$30(Toolkit.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:354)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:381)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:510)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:490)
at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$404(QuantumToolkit.java:319)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
I tried to set sort of delay before using setText, like updating value in foundFilesAmount only after 5'th, 10'th, 15'th update, etc. But if the search works longer, exceptions are still thrown.
Is there a correct method to show current amount of found files, that contains real amount and doesn't cause so much exceptions?
Thanks in advance.
The correct way is not doing the updates of the UI on a thread other than the application thread. This can otherwise lead to issues with rendering/layouting.
You could use a Task to do the updates to the updates however:
Task<ObservableList<myFile>> task = new Task<
Task<ObservableList<myFile>>() {
#Override
protected ObservableList<myFile> call() throws Exception {
ObservableList<myFile> files = FXCollections.observableArrayList();
while (...) {
...
files.add(...);
updateMessage(Integer.toString(files.size()));
...
}
return files;
}
};
task.setOnSucceeded(evt -> tableView.setItems(task.getValue()));
Thread t = new Thread(task);
textField.textProperty().bind(task.messageProperty());
t.setDaemon(true);
t.start();
try something like this:
Platform.runLater(new Runnable() {
#Override
public void run() {
// update your JavaFX controls here
}
});
I have a Processing project making use of the ControlP5 library running within eclipse in which, upon any keypress on the keyboard, crashes with an IllegalArgumentException:
Exception in thread "Animation Thread" java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at processing.core.PApplet$RegisteredMethods.handle(PApplet.java:1076)
at processing.core.PApplet.handleKeyEvent(PApplet.java:2848)
at processing.core.PApplet.dequeueKeyEvents(PApplet.java:2793)
at processing.core.PApplet.handleDraw(PApplet.java:2132)
at processing.core.PGraphicsJava2D.requestDraw(PGraphicsJava2D.java:197)
at processing.core.PApplet.run(PApplet.java:1998)
at java.lang.Thread.run(Unknown Source)
The program (running in an applet) runs perfectly fine with mouse dragging, sliders, etc, until a key is pressed. It seems like there is some sort of unknown keylistener waiting for input and using it incorrectly? It is difficult to tell because the exception refers to java code which is unrelated to the processing code which I wrote.
Even if I have a program which only defines a ControlP5 object, the program encounters the same error:
import processing.core.*;
import controlP5.*;
public class Lensing extends PApplet {
ControlP5 controlP5;
public Lensing() {
}
public void setup() {
controlP5 = new ControlP5(this);
}
public void draw() {
}
public static void main(String args[]) {
PApplet.main(new String[] { "--present", "edu.umd.astro.Lensing" });
}
}
Comment out the single controlP5 definition, and no exception occurs.
Turns out it was an issue related to using the 2.0b1 core jar file, and can be remedied by updating to 2.0b3 from http://processing.org/download/
credit to reply on here https://forum.processing.org/topic/using-controlp5-with-processing-in-eclipse-results-in-illegalargumentexception-on-keypress
What's the proper way of initializing the JavaFX runtime so you can unit test (with JUnit) controllers that make use of the concurrency facilities and Platform.runLater(Runnable)?
Calling Application.launch(...) from the #BeforeClass method results in a dead lock. If Application.launch(...) is not called then the following error is thrown:
java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:121)
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:116)
at javafx.application.Platform.runLater(Platform.java:52)
at javafx.concurrent.Task.runLater(Task.java:1042)
at javafx.concurrent.Task.updateMessage(Task.java:987)
at com.xyz.AudioSegmentExtractor.call(AudioSegmentExtractor.java:64)
at com.xyz.CompletionControllerTest.setUp(CompletionControllerTest.java:69)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Followup: this is the motif I've been using based on recommendation by #SergeyGrinev.
... // Inside test class
public static class AsNonApp extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
// noop
}
}
#BeforeClass
public static void initJFX() {
Thread t = new Thread("JavaFX Init Thread") {
public void run() {
Application.launch(AsNonApp.class, new String[0]);
}
};
t.setDaemon(true);
t.start();
}
... // controller tests follow...
Calling launch() from #BeforeClass is a correct approach. Just note that launch() doesn't return control to calling code. So you have to wrap it into new Thread(...).start().
A 7 years later update:
Use TestFX! It will take care of launching in a proper way. E.g. you can extend your test from a TestFX's ApplicaionTest class and just use the same code:
public class MyTest extends ApplicationTest {
#Override
public void start (Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(
getClass().getResource("mypage.fxml"));
stage.setScene(scene = new Scene(loader.load(), 300, 300));
stage.show();
}
and write tests like that:
#Test
public void testBlueHasOnlyOneEntry() {
clickOn("#tfSearch").write("blue");
verifyThat("#labelCount", hasText("1"));
}
I found this to work,... but only after adding a Thread.sleep(500) after starting the JavaFX application thread. Presumably it takes some time to get the FX environment up and ready (about 200ms on my MacBook Pro retina)
#BeforeClass
public static void setUpClass() throws InterruptedException {
// Initialise Java FX
System.out.printf("About to launch FX App\n");
Thread t = new Thread("JavaFX Init Thread") {
public void run() {
Application.launch(AsNonApp.class, new String[0]);
}
};
t.setDaemon(true);
t.start();
System.out.printf("FX App thread started\n");
Thread.sleep(500);
}
This works for me as well for testing code that uses JavaFX concurrency constructs such as javafx.concurrent.Task. Compared to the other solutions it does not need explicit Thread management or supplying a dummy Application.
#BeforeAll
public static void setUpJavaFXRuntime() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
Platform.startup(() -> {
latch.countDown();
});
latch.await(5, TimeUnit.SECONDS);
}
#AfterAll
public static void tearDownJavaFXRuntime() throws InterruptedException {
Platform.exit();
}
We are building an application based on the Netbeans Platform, and one part of it is an editor for a specific language we use.
We have the following class to highlight errors in the syntax:
class SyntaxErrorsHighlightingTask extends org.netbeans.modules.parsing.spi.ParserResultTask {
public SyntaxErrorsHighlightingTask () {
}
#Override
public void run (org.netbeans.modules.parsing.spi.Parser.Result result, org.netbeans.modules.parsing.spi.SchedulerEvent event) {
try {
final javax.swing.text.Document document = result.getSnapshot().getSource ().getDocument(false);
final List<ErrorDescription> errors = new ArrayList<ErrorDescription> ();
// finds errors on the document and add them to 'errors' list
}
/***
OFFENDING CODE GOES HERE
***/
} catch (javax.swing.text.BadLocationException ex1) {
org.openide.util.Exceptions.printStackTrace (ex1);
} catch (org.netbeans.modules.parsing.spi.ParseException ex1) {
Exceptions.printStackTrace (ex1);
}
}
#Override
public int getPriority () {
return 100;
}
#Override
public Class<? extends Scheduler> getSchedulerClass () {
return Scheduler.EDITOR_SENSITIVE_TASK_SCHEDULER;
}
#Override
public void cancel () {
}
}
The offending code, that throws an exception, is this:
org.netbeans.spi.editor.hints.HintsController.setErrors (document, "testsequence", errors);
Based on searching results, it was changed to the following:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
System.err.println("is EDT? " + SwingUtilities.isEventDispatchThread());
HintsController.setErrors (document, "testsequence", errors);
}
});
The following is what happens when a syntax error is introduced in the editor:
is EDT? true
SEVERE [org.openide.util.RequestProcessor]: Error in RequestProcessor org.netbeans.spi.editor.hints.HintsController$1
java.lang.IllegalStateException: Must be run in EQ
at org.netbeans.editor.Annotations.addAnnotation(Annotations.java:195)
at org.netbeans.modules.editor.NbEditorDocument.addAnnotation(NbEditorDocument.java:251)
at org.openide.text.NbDocument.addAnnotation(NbDocument.java:504)
at org.netbeans.modules.editor.hints.AnnotationHolder$NbDocumentAttacher.attachAnnotation(AnnotationHolder.java:235)
at org.netbeans.modules.editor.hints.AnnotationHolder.attachAnnotation(AnnotationHolder.java:208)
at org.netbeans.modules.editor.hints.AnnotationHolder.updateAnnotationOnLine(AnnotationHolder.java:674)
at org.netbeans.modules.editor.hints.AnnotationHolder.setErrorDescriptionsImpl(AnnotationHolder.java:899)
at org.netbeans.modules.editor.hints.AnnotationHolder.access$1300(AnnotationHolder.java:113)
at org.netbeans.modules.editor.hints.AnnotationHolder$4.run(AnnotationHolder.java:812)
at org.netbeans.editor.BaseDocument.render(BaseDocument.java:1409)
at org.netbeans.modules.editor.hints.AnnotationHolder.setErrorDescriptions(AnnotationHolder.java:809)
at org.netbeans.modules.editor.hints.HintsControllerImpl.setErrorsImpl(HintsControllerImpl.java:111)
at org.netbeans.modules.editor.hints.HintsControllerImpl.setErrors(HintsControllerImpl.java:93)
at org.netbeans.spi.editor.hints.HintsController$1.run(HintsController.java:79)
at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1424)
at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:1968)
Caused: org.openide.util.RequestProcessor$SlowItem: task failed due to
at org.openide.util.RequestProcessor.post(RequestProcessor.java:425)
at org.netbeans.spi.editor.hints.HintsController.setErrors(HintsController.java:77)
at com.#.#.#.editor.parser.SyntaxErrorsHighlightingTask$1.run(SyntaxErrorsHighlightingTask.java:74)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:641)
at java.awt.EventQueue.access$000(EventQueue.java:84)
at java.awt.EventQueue$1.run(EventQueue.java:602)
at java.awt.EventQueue$1.run(EventQueue.java:600)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:611)
at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:148)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
[catch] at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
What happens is, the call is to HintsController is being made in the EDT (EventDispatch Thread). However, Annotations.addAnnotation() is being run in another thread - sometimes in the "System clipboard synchronizer" thread, sometimes in the "Inactive RequestProcessor" thread. Since it checks if its being run on the EDT, it always throws an IllegalStateException.
I'm no expert in using the Netbeans Platform, and I'm pretty new to this specific application on the company - so I might be missing something really obvious. Google didn't help much. Anyone has any advice?
Turns out, it was not a problem with the code after all.
As pointed on the NetBeans-dev list:
HintsController.setErrors can be called from any thread - it uses its
own worker thread, and reschedules to AWT thread when necessary.
The requirement to invoke Annotations.addAnnotation in AWT thread has
been removed quite some time ago by:
http://hg.netbeans.org/main-silver/rev/db82e4e0fbcc
The same changeset also removed automatic rescheduling into AWT thread
in NbDocument.addAnnotation. So it seems that the build you are using
has the second part of the changeset, but not the first part (...)
After a careful review of maven's pom.xml files, I realized that the application was loading newer versions of the libs while the module was loading older versions, so it would run the wrong code. Related SO question about that here.
Maybe you should try to update your errors with a method like this one :
private void updateError(javax.swing.text.Document document, List<ErrorDescription> errors) {
if(javax.swing.SwingUtilities.isEventDispatchThread()) {
HintsController.setErrors (document, "testsequence", errors);
}
else {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
HintsController.setErrors (document, "testsequence", errors);
}
});
}
}