Skip to content

Commit

Permalink
#310 other implementation of a state machine for synchronous commands
Browse files Browse the repository at this point in the history
  • Loading branch information
manuel-mauky committed Jan 8, 2016
1 parent f3263e7 commit eb649bf
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.function.Supplier;

import javafx.beans.property.ObjectProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -129,27 +130,35 @@ public void execute() {
start();
}
} else {
// When the Command is not executed in background, we have to imitate a Service execution, so the
// Service Statemachine provides the
// correct Service State to the command.
// When the command is not executed in background, we have to imitate a service execution, so the
// service statemachine provides the
// correct service state to the command.
callActionAndSynthesizeServiceRun();
}
}
}

/**
* For internal purposes we need to change the state property of the service.
*/
private ObjectProperty<State> getStateObjectPropertyReadable() {
return (ObjectProperty<State>) stateProperty();
}

private void callActionAndSynthesizeServiceRun() {
try {
// We call the User Action. If an exception occures we save it, therefore we can use it in the Test
// (createSynthesizedTask) to throw it during the Service invokation.
getStateObjectPropertyReadable().setValue(State.SCHEDULED);

// We call the User Action. If an exception occurs we save it, therefore we can use it in the Test
// (createSynthesizedTask) to throw it during the Service invocation.
actionSupplier.get().action();

getStateObjectPropertyReadable().setValue(State.SUCCEEDED);
} catch (Exception e) {
LOG.error("Exception in Command Execution", occuredException);
this.occuredException = e;
getStateObjectPropertyReadable().setValue(State.FAILED);
}
// Start the Service to trigger the Service state machine. createTask->createSynthesizedTask will be called and
// will throw the Exception which was catched some lines before
reset();
start();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package de.saxsys.mvvmfx.utils.commands;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import org.junit.Test;

import java.util.function.Supplier;

import static org.assertj.core.api.Assertions.assertThat;

/**
* There are use cases where the commands have to be used without a UI thread, for example when running unit tests.
*
* To verify this behaviour we use a dedicated test class where we don't use a special JUnit runner for UI threading.
*/
public class CommandsWithoutUiThreadTest {

public class MyViewModel {

public IntegerProperty a = new SimpleIntegerProperty();
public IntegerProperty b = new SimpleIntegerProperty();
public IntegerProperty result = new SimpleIntegerProperty();

private final DelegateCommand calcCommand;

public MyViewModel() {

BooleanProperty executable = new SimpleBooleanProperty();
executable.bind(a.greaterThan(0).and(b.greaterThan(0)));

calcCommand = new DelegateCommand(() -> {
return new Action() {
@Override
protected void action() throws Exception {
result.set(a.get() + b.get());
}
};
}, executable, false);
}

public Command calcCommand() {
return calcCommand;
}
}


@Test
public void test() {

MyViewModel viewModel = new MyViewModel();
assertThat(viewModel.calcCommand.isExecutable()).isFalse();
assertThat(viewModel.result.get()).isEqualTo(0);


viewModel.a.setValue(2);
assertThat(viewModel.calcCommand.isExecutable()).isFalse();
assertThat(viewModel.result.get()).isEqualTo(0);

viewModel.b.setValue(3);
assertThat(viewModel.calcCommand.isExecutable()).isTrue();
assertThat(viewModel.result.get()).isEqualTo(0);

viewModel.calcCommand.execute();

assertThat(viewModel.result.get()).isEqualTo(5);

}



}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;

import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
Expand Down Expand Up @@ -57,86 +58,93 @@ protected void action() {
assertFalse(delegateCommand.isNotExecutable());
}


@Test
public void firePositive() throws InterruptedException, ExecutionException, TimeoutException {
public void executeSynchronousSucceeded() throws Exception {
BooleanProperty condition = new SimpleBooleanProperty(true);
BooleanProperty called = new SimpleBooleanProperty();

BooleanProperty succeeded = new SimpleBooleanProperty();
BooleanProperty failed = new SimpleBooleanProperty();

DelegateCommand delegateCommand = new DelegateCommand(() -> new Action() {
@Override
protected void action() {
called.set(true);
}
}, condition);

assertFalse(called.get());
delegateCommand.execute();
assertTrue(called.get());

CompletableFuture<State> stateFromFxThread = new CompletableFuture<>();
Platform.runLater(() -> {
delegateCommand.stateProperty().addListener((b, o, n) -> {
if (n == State.SUCCEEDED) {
stateFromFxThread.complete(n);
}
});
delegateCommand.setOnSucceeded(workerStateEvent -> {
succeeded.set(true);
});
assertThat(stateFromFxThread.get(3, TimeUnit.SECONDS)).isEqualTo(State.SUCCEEDED);
}

@Test(expected = RuntimeException.class)
public void fireNegative() {
BooleanProperty condition = new SimpleBooleanProperty(false);

delegateCommand.setOnFailed(workerStateEvent -> {
failed.set(true);
});

// given
assertThat(called.get()).isFalse();
assertThat(succeeded.get()).isFalse();
assertThat(failed.get()).isFalse();

// when
delegateCommand.execute();

// then
assertThat(called.get()).isTrue();
assertThat(succeeded.get()).isTrue();
assertThat(failed.get()).isFalse();
}

@Test
public void executeSynchronousFailed() throws Exception {
BooleanProperty condition = new SimpleBooleanProperty(true);
BooleanProperty called = new SimpleBooleanProperty();
BooleanProperty succeeded = new SimpleBooleanProperty();
BooleanProperty failed = new SimpleBooleanProperty();

DelegateCommand delegateCommand = new DelegateCommand(() -> new Action() {
@Override
protected void action() {
called.set(true);
throw new RuntimeException("Some reason");
}
}, condition);


delegateCommand.setOnSucceeded(workerStateEvent -> {
succeeded.set(true);
});

delegateCommand.setOnFailed(workerStateEvent -> {
failed.set(true);
});

// given
assertThat(called.get()).isFalse();
assertThat(succeeded.get()).isFalse();
assertThat(failed.get()).isFalse();

// when
delegateCommand.execute();

// then
assertThat(called.get()).isTrue();
assertThat(succeeded.get()).isFalse();
assertThat(failed.get()).isTrue();
}

@Test
public void firePositiveWithExc() throws InterruptedException, ExecutionException, TimeoutException {
BooleanProperty throwExc = new SimpleBooleanProperty(true);
@Test(expected = RuntimeException.class)
public void commandNotExecutable() {
BooleanProperty condition = new SimpleBooleanProperty(false);

DelegateCommand delegateCommand = new DelegateCommand(() -> new Action() {
@Override
protected void action() {
if (throwExc.get())
throw new RuntimeException("Someerror");
}
}, new SimpleBooleanProperty(true));



CompletableFuture<State> stateFromFxThread1 = new CompletableFuture<>();
Platform.runLater(() -> {
delegateCommand.stateProperty().addListener((b, o, n) -> {
if (n == State.FAILED) {
stateFromFxThread1.complete(n);
}
});
});
Platform.runLater(() -> delegateCommand.execute());
assertThat(stateFromFxThread1.get(3, TimeUnit.SECONDS)).isEqualTo(State.FAILED);

throwExc.set(false);
CompletableFuture<State> stateFromFxThread2 = new CompletableFuture<>();
Platform.runLater(() -> {
delegateCommand.stateProperty().addListener((b, o, n) -> {
if (n == State.SUCCEEDED) {
stateFromFxThread2.complete(n);
}
});
});
Platform.runLater(() -> delegateCommand.execute());
assertThat(stateFromFxThread2.get(3, TimeUnit.SECONDS)).isEqualTo(State.SUCCEEDED);
}, condition);

delegateCommand.execute();
}


@Test
public void longRunningAsync() throws Exception {

Expand Down

0 comments on commit eb649bf

Please sign in to comment.