This commit is contained in:
Sergey Solovyev
2012-10-10 00:32:33 +04:00
parent 4fd6e94e96
commit ac411a549f
37 changed files with 988 additions and 1032 deletions

View File

@@ -13,10 +13,7 @@ import static org.solovyev.android.calculator.CalculatorEventType.*;
public class CalculatorDisplayImpl implements CalculatorDisplay {
@NotNull
private volatile CalculatorEventData lastCalculatorEventData;
@NotNull
private final Object lastCalculatorEventDataLock = new Object();
private final CalculatorEventHolder lastEvent;
@Nullable
private CalculatorDisplayView view;
@@ -32,7 +29,7 @@ public class CalculatorDisplayImpl implements CalculatorDisplay {
public CalculatorDisplayImpl(@NotNull Calculator calculator) {
this.calculator = calculator;
this.lastCalculatorEventData = CalculatorUtils.createFirstEventDataId();
this.lastEvent = new CalculatorEventHolder(CalculatorUtils.createFirstEventDataId());
this.calculator.addCalculatorEventListener(this);
}
@@ -91,9 +88,7 @@ public class CalculatorDisplayImpl implements CalculatorDisplay {
@Override
@NotNull
public CalculatorEventData getLastEventData() {
synchronized (lastCalculatorEventDataLock) {
return lastCalculatorEventData;
}
return lastEvent.getLastEventData();
}
@Override
@@ -102,18 +97,9 @@ public class CalculatorDisplayImpl implements CalculatorDisplay {
@Nullable Object data) {
if (calculatorEventType.isOfType(calculation_result, calculation_failed, calculation_cancelled, conversion_result, conversion_failed)) {
boolean processEvent = false;
boolean sameSequence = false;
final CalculatorEventHolder.Result result = lastEvent.apply(calculatorEventData);
synchronized (lastCalculatorEventDataLock) {
if (calculatorEventData.isAfter(lastCalculatorEventData)) {
sameSequence = calculatorEventData.isSameSequence(lastCalculatorEventData);
lastCalculatorEventData = calculatorEventData;
processEvent = true;
}
}
if (processEvent) {
if (result.isNewAfter()) {
switch (calculatorEventType) {
case conversion_failed:
processConversationFailed((CalculatorConversionEventData) calculatorEventData, (ConversionFailure) data);

View File

@@ -26,12 +26,16 @@ public class CalculatorEditorImpl implements CalculatorEditor {
@NotNull
private final Calculator calculator;
@NotNull
private final CalculatorEventHolder lastEventHolder;
@NotNull
private final CursorControlAdapter cursorControlAdapter = new CursorControlAdapter(this);
public CalculatorEditorImpl(@NotNull Calculator calculator) {
this.calculator = calculator;
this.calculator.addCalculatorEventListener(this);
this.lastEventHolder = new CalculatorEventHolder(CalculatorUtils.createFirstEventDataId());
}
@Override
@@ -80,12 +84,16 @@ public class CalculatorEditorImpl implements CalculatorEditor {
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData,
@NotNull CalculatorEventType calculatorEventType,
@Nullable Object data) {
switch (calculatorEventType) {
case use_history_state:
final CalculatorHistoryState calculatorHistoryState = (CalculatorHistoryState)data;
final EditorHistoryState editorState = calculatorHistoryState.getEditorState();
this.setText(StringUtils.getNotEmpty(editorState.getText(), ""), editorState.getCursorPosition());
break;
final CalculatorEventHolder.Result result = lastEventHolder.apply(calculatorEventData);
if (result.isNewAfter()) {
switch (calculatorEventType) {
case use_history_state:
final CalculatorHistoryState calculatorHistoryState = (CalculatorHistoryState)data;
final EditorHistoryState editorState = calculatorHistoryState.getEditorState();
this.setText(StringUtils.getNotEmpty(editorState.getText(), ""), editorState.getCursorPosition());
break;
}
}
}

View File

@@ -0,0 +1,70 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 10/9/12
* Time: 9:59 PM
*/
public class CalculatorEventHolder {
@NotNull
private volatile CalculatorEventData lastEventData;
public CalculatorEventHolder(@NotNull CalculatorEventData lastEventData) {
this.lastEventData = lastEventData;
}
@NotNull
public synchronized CalculatorEventData getLastEventData() {
return lastEventData;
}
@NotNull
public synchronized Result apply(@NotNull CalculatorEventData newEventData) {
final Result result = new Result(lastEventData, newEventData);
if (result.isNewAfter()) {
this.lastEventData = newEventData;
}
return result;
}
public static class Result {
@NotNull
private final CalculatorEventData lastEventData;
@NotNull
private final CalculatorEventData newEventData;
@Nullable
private Boolean after = null;
@Nullable
private Boolean sameSequence = null;
public Result(@NotNull CalculatorEventData lastEventData,
@NotNull CalculatorEventData newEventData) {
this.lastEventData = lastEventData;
this.newEventData = newEventData;
}
public boolean isNewAfter() {
if (after == null) {
after = newEventData.isAfter(lastEventData);
}
return after;
}
public boolean isSameSequence() {
if (sameSequence == null) {
sameSequence = newEventData.isSameSequence(lastEventData);
}
return sameSequence;
}
}
}

View File

@@ -1,138 +1,144 @@
package org.solovyev.android.calculator;
import jscl.JsclMathEngine;
import junit.framework.Assert;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mockito.Mockito;
import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.jscl.JsclOperation;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* User: serso
* Date: 10/7/12
* Time: 8:40 PM
*/
public class CalculatorTestUtils {
// in seconds
public static final int TIMEOUT = 1000;
public static void staticSetUp() throws Exception {
CalculatorLocatorImpl.getInstance().init(new CalculatorImpl(), newCalculatorEngine(), Mockito.mock(CalculatorClipboard.class), Mockito.mock(CalculatorNotifier.class), Mockito.mock(CalculatorHistory.class));
CalculatorLocatorImpl.getInstance().getEngine().init();
}
@NotNull
static CalculatorEngineImpl newCalculatorEngine() {
final MathEntityDao mathEntityDao = Mockito.mock(MathEntityDao.class);
final JsclMathEngine jsclEngine = JsclMathEngine.getInstance();
final CalculatorVarsRegistry varsRegistry = new CalculatorVarsRegistry(jsclEngine.getConstantsRegistry(), mathEntityDao);
final CalculatorFunctionsMathRegistry functionsRegistry = new CalculatorFunctionsMathRegistry(jsclEngine.getFunctionsRegistry(), mathEntityDao);
final CalculatorOperatorsMathRegistry operatorsRegistry = new CalculatorOperatorsMathRegistry(jsclEngine.getOperatorsRegistry(), mathEntityDao);
final CalculatorPostfixFunctionsRegistry postfixFunctionsRegistry = new CalculatorPostfixFunctionsRegistry(jsclEngine.getPostfixFunctionsRegistry(), mathEntityDao);
return new CalculatorEngineImpl(jsclEngine, varsRegistry, functionsRegistry, operatorsRegistry, postfixFunctionsRegistry, null);
}
public static void assertEval(@NotNull String expected, @NotNull String expression) {
assertEval(expected, expression, JsclOperation.numeric);
}
public static void assertEval(@NotNull String expected, @NotNull String expression, @NotNull JsclOperation operation) {
final Calculator calculator = CalculatorLocatorImpl.getInstance().getCalculator();
CalculatorLocatorImpl.getInstance().getDisplay().setViewState(CalculatorDisplayViewStateImpl.newDefaultInstance());
final CountDownLatch latch = new CountDownLatch(1);
final TestCalculatorEventListener calculatorEventListener = new TestCalculatorEventListener(latch);
try {
calculator.addCalculatorEventListener(calculatorEventListener);
calculatorEventListener.setCalculatorEventData(calculator.evaluate(operation, expression));
if (latch.await(TIMEOUT, TimeUnit.SECONDS)) {
Assert.assertNotNull(calculatorEventListener.getResult());
Assert.assertEquals(expected, calculatorEventListener.getResult().getText());
} else {
Assert.fail("Too long wait for: " + expression);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
calculator.removeCalculatorEventListener(calculatorEventListener);
}
}
public static void assertError(@NotNull String expression) {
assertError(expression, JsclOperation.numeric);
}
private static final class TestCalculatorEventListener implements CalculatorEventListener {
@Nullable
private CalculatorEventData calculatorEventData;
@NotNull
private final CountDownLatch latch;
@Nullable
private volatile CalculatorDisplayViewState result = null;
public TestCalculatorEventListener(@NotNull CountDownLatch latch) {
this.latch = latch;
}
public void setCalculatorEventData(@Nullable CalculatorEventData calculatorEventData) {
this.calculatorEventData = calculatorEventData;
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
if ( this.calculatorEventData != null && calculatorEventData.isSameSequence(this.calculatorEventData) ) {
if ( calculatorEventType == CalculatorEventType.display_state_changed ) {
final CalculatorDisplayChangeEventData displayChange = (CalculatorDisplayChangeEventData)data;
result = displayChange.getNewValue();
latch.countDown();
}
}
}
@Nullable
public CalculatorDisplayViewState getResult() {
return result;
}
}
public static void assertError(@NotNull String expression, @NotNull JsclOperation operation) {
final Calculator calculator = CalculatorLocatorImpl.getInstance().getCalculator();
CalculatorLocatorImpl.getInstance().getDisplay().setViewState(CalculatorDisplayViewStateImpl.newDefaultInstance());
final CountDownLatch latch = new CountDownLatch(1);
final TestCalculatorEventListener calculatorEventListener = new TestCalculatorEventListener(latch);
try {
calculator.addCalculatorEventListener(calculatorEventListener);
calculatorEventListener.setCalculatorEventData(calculator.evaluate(operation, expression));
if (latch.await(TIMEOUT, TimeUnit.SECONDS)) {
Assert.assertNotNull(calculatorEventListener.getResult());
Assert.assertFalse(calculatorEventListener.getResult().isValid());
} else {
Assert.fail("Too long wait for: " + expression);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
calculator.removeCalculatorEventListener(calculatorEventListener);
}
}
}
package org.solovyev.android.calculator;
import jscl.JsclMathEngine;
import junit.framework.Assert;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mockito.Mockito;
import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.jscl.JsclOperation;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* User: serso
* Date: 10/7/12
* Time: 8:40 PM
*/
public class CalculatorTestUtils {
// in seconds
public static final int TIMEOUT = 1;
public static void staticSetUp() throws Exception {
CalculatorLocatorImpl.getInstance().init(new CalculatorImpl(), newCalculatorEngine(), Mockito.mock(CalculatorClipboard.class), Mockito.mock(CalculatorNotifier.class), Mockito.mock(CalculatorHistory.class));
CalculatorLocatorImpl.getInstance().getEngine().init();
}
@NotNull
static CalculatorEngineImpl newCalculatorEngine() {
final MathEntityDao mathEntityDao = Mockito.mock(MathEntityDao.class);
final JsclMathEngine jsclEngine = JsclMathEngine.getInstance();
final CalculatorVarsRegistry varsRegistry = new CalculatorVarsRegistry(jsclEngine.getConstantsRegistry(), mathEntityDao);
final CalculatorFunctionsMathRegistry functionsRegistry = new CalculatorFunctionsMathRegistry(jsclEngine.getFunctionsRegistry(), mathEntityDao);
final CalculatorOperatorsMathRegistry operatorsRegistry = new CalculatorOperatorsMathRegistry(jsclEngine.getOperatorsRegistry(), mathEntityDao);
final CalculatorPostfixFunctionsRegistry postfixFunctionsRegistry = new CalculatorPostfixFunctionsRegistry(jsclEngine.getPostfixFunctionsRegistry(), mathEntityDao);
return new CalculatorEngineImpl(jsclEngine, varsRegistry, functionsRegistry, operatorsRegistry, postfixFunctionsRegistry, null);
}
public static void assertEval(@NotNull String expected, @NotNull String expression) {
assertEval(expected, expression, JsclOperation.numeric);
}
public static void assertEval(@NotNull String expected, @NotNull String expression, @NotNull JsclOperation operation) {
final Calculator calculator = CalculatorLocatorImpl.getInstance().getCalculator();
CalculatorLocatorImpl.getInstance().getDisplay().setViewState(CalculatorDisplayViewStateImpl.newDefaultInstance());
final CountDownLatch latch = new CountDownLatch(1);
final TestCalculatorEventListener calculatorEventListener = new TestCalculatorEventListener(latch);
try {
calculator.addCalculatorEventListener(calculatorEventListener);
calculatorEventListener.setCalculatorEventData(calculator.evaluate(operation, expression));
if (latch.await(TIMEOUT, TimeUnit.SECONDS)) {
Assert.assertNotNull(calculatorEventListener.getResult());
Assert.assertEquals(expected, calculatorEventListener.getResult().getText());
} else {
Assert.fail("Too long wait for: " + expression);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
calculator.removeCalculatorEventListener(calculatorEventListener);
}
}
public static void assertError(@NotNull String expression) {
assertError(expression, JsclOperation.numeric);
}
private static final class TestCalculatorEventListener implements CalculatorEventListener {
@Nullable
private CalculatorEventData calculatorEventData;
@NotNull
private final CountDownLatch latch;
@Nullable
private volatile CalculatorDisplayViewState result = null;
public TestCalculatorEventListener(@NotNull CountDownLatch latch) {
this.latch = latch;
}
public void setCalculatorEventData(@Nullable CalculatorEventData calculatorEventData) {
this.calculatorEventData = calculatorEventData;
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
if ( this.calculatorEventData != null && calculatorEventData.isSameSequence(this.calculatorEventData) ) {
if (calculatorEventType == CalculatorEventType.display_state_changed) {
final CalculatorDisplayChangeEventData displayChange = (CalculatorDisplayChangeEventData) data;
result = displayChange.getNewValue();
try {
// need to sleep a little bit as await
new CountDownLatch(1).await(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
}
latch.countDown();
}
}
}
@Nullable
public CalculatorDisplayViewState getResult() {
return result;
}
}
public static void assertError(@NotNull String expression, @NotNull JsclOperation operation) {
final Calculator calculator = CalculatorLocatorImpl.getInstance().getCalculator();
CalculatorLocatorImpl.getInstance().getDisplay().setViewState(CalculatorDisplayViewStateImpl.newDefaultInstance());
final CountDownLatch latch = new CountDownLatch(1);
final TestCalculatorEventListener calculatorEventListener = new TestCalculatorEventListener(latch);
try {
calculator.addCalculatorEventListener(calculatorEventListener);
calculatorEventListener.setCalculatorEventData(calculator.evaluate(operation, expression));
if (latch.await(TIMEOUT, TimeUnit.SECONDS)) {
Assert.assertNotNull(calculatorEventListener.getResult());
Assert.assertFalse(calculatorEventListener.getResult().isValid());
} else {
Assert.fail("Too long wait for: " + expression);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
calculator.removeCalculatorEventListener(calculatorEventListener);
}
}
}