History tests

This commit is contained in:
serso 2016-01-13 23:09:07 +01:00
parent 1e8be31ab5
commit 6734bfeaa4
14 changed files with 295 additions and 337 deletions

View File

@ -95,7 +95,6 @@ public final class App {
private static volatile Application application; private static volatile Application application;
@Nonnull @Nonnull
private static Executor uiThreadExecutor; private static Executor uiThreadExecutor;
private static volatile boolean initialized;
@Nonnull @Nonnull
private static SharedPreferences preferences; private static SharedPreferences preferences;
@Nonnull @Nonnull
@ -132,9 +131,6 @@ public final class App {
public static void init(@Nonnull CalculatorApplication application, public static void init(@Nonnull CalculatorApplication application,
@Nonnull Languages languages) { @Nonnull Languages languages) {
if (initialized) {
throw new IllegalStateException("Already initialized!");
}
App.application = application; App.application = application;
App.preferences = PreferenceManager.getDefaultSharedPreferences(application); App.preferences = PreferenceManager.getDefaultSharedPreferences(application);
App.uiThreadExecutor = application.uiThread; App.uiThreadExecutor = application.uiThread;
@ -163,14 +159,6 @@ public final class App {
App.editor = application.editor; App.editor = application.editor;
App.display = application.display; App.display = application.display;
App.bus = application.bus; App.bus = application.bus;
App.initialized = true;
}
private static void checkInit() {
if (!initialized) {
throw new IllegalStateException("App should be initialized!");
}
} }
/** /**

View File

@ -1,11 +1,11 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import dagger.Component;
import org.solovyev.android.calculator.history.BaseHistoryFragment;
import org.solovyev.android.calculator.onscreen.CalculatorOnscreenService; import org.solovyev.android.calculator.onscreen.CalculatorOnscreenService;
import javax.inject.Singleton; import javax.inject.Singleton;
import dagger.Component;
@Singleton @Singleton
@Component(modules = AppModule.class) @Component(modules = AppModule.class)
public interface AppComponent { public interface AppComponent {
@ -13,4 +13,5 @@ public interface AppComponent {
void inject(CalculatorEditorFragment fragment); void inject(CalculatorEditorFragment fragment);
void inject(BaseUi ui); void inject(BaseUi ui);
void inject(CalculatorOnscreenService service); void inject(CalculatorOnscreenService service);
void inject(BaseHistoryFragment fragment);
} }

View File

@ -6,21 +6,17 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import com.squareup.otto.Bus; import com.squareup.otto.Bus;
import dagger.Module;
import dagger.Provides;
import org.solovyev.android.UiThreadExecutor; import org.solovyev.android.UiThreadExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.util.concurrent.Executor;
import dagger.Module; import java.util.concurrent.Executors;
import dagger.Provides; import java.util.concurrent.ThreadFactory;
@Module @Module
public class AppModule { public class AppModule {
@ -43,6 +39,12 @@ public class AppModule {
return new Handler(Looper.getMainLooper()); return new Handler(Looper.getMainLooper());
} }
@Provides
@Singleton
Application provideApplication() {
return application;
}
@Provides @Provides
@Singleton @Singleton
Bus provideBus(Handler handler) { Bus provideBus(Handler handler) {

View File

@ -80,19 +80,6 @@ public enum CalculatorEventType {
engine_preferences_changed, engine_preferences_changed,
/*
**********************************************************************
*
* HISTORY
*
**********************************************************************
*/
// @Nonnull CalculatorHistoryState
history_state_added,
clear_history_requested,
/* /*
********************************************************************** **********************************************************************
* *

View File

@ -30,43 +30,24 @@ import android.os.Bundle;
import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentActivity;
import android.support.v4.app.ListFragment; import android.support.v4.app.ListFragment;
import android.text.ClipboardManager; import android.text.ClipboardManager;
import android.view.ContextMenu; import android.view.*;
import android.view.LayoutInflater; import android.widget.*;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.melnykov.fab.FloatingActionButton; import com.melnykov.fab.FloatingActionButton;
import com.squareup.otto.Bus;
import org.solovyev.android.calculator.App; import com.squareup.otto.Subscribe;
import org.solovyev.android.calculator.CalculatorActivity; import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.CalculatorEventData;
import org.solovyev.android.calculator.CalculatorEventListener;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.CalculatorFragmentType;
import org.solovyev.android.calculator.FragmentUi;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.text.Strings; import org.solovyev.common.text.Strings;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import static android.view.Menu.NONE; import static android.view.Menu.NONE;
import static org.solovyev.android.calculator.CalculatorEventType.clear_history_requested;
public abstract class BaseHistoryFragment extends ListFragment implements CalculatorEventListener { public abstract class BaseHistoryFragment extends ListFragment {
@Nonnull @Nonnull
private final DialogInterface.OnClickListener clearDialogListener = new DialogInterface.OnClickListener() { private final DialogInterface.OnClickListener clearDialogListener = new DialogInterface.OnClickListener() {
@ -79,14 +60,16 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
} }
} }
}; };
@Inject
History history;
@Inject
Bus bus;
@Nonnull @Nonnull
private HistoryArrayAdapter adapter; private HistoryArrayAdapter adapter;
@Nonnull @Nonnull
private FragmentUi ui; private FragmentUi ui;
@Nullable @Nullable
private AlertDialog clearDialog; private AlertDialog clearDialog;
@Inject
History history;
protected BaseHistoryFragment(@Nonnull CalculatorFragmentType fragmentType) { protected BaseHistoryFragment(@Nonnull CalculatorFragmentType fragmentType) {
ui = new FragmentUi(fragmentType.getDefaultLayoutId(), fragmentType.getDefaultTitleResId(), false); ui = new FragmentUi(fragmentType.getDefaultLayoutId(), fragmentType.getDefaultTitleResId(), false);
@ -114,6 +97,8 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
((CalculatorApplication) getActivity().getApplication()).getComponent().inject(this);
bus.register(this);
ui.onCreate(this); ui.onCreate(this);
} }
@ -139,7 +124,12 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
fab.setOnClickListener(new View.OnClickListener() { fab.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Locator.getInstance().getCalculator().fireCalculatorEvent(clear_history_requested, null); clearDialog = new AlertDialog.Builder(getActivity()).setTitle(R.string.cpp_clear_history_title)
.setMessage(R.string.cpp_clear_history_message)
.setPositiveButton(R.string.cpp_clear_history, clearDialogListener)
.setNegativeButton(R.string.c_cancel, clearDialogListener)
.create();
clearDialog.show();
} }
}); });
@ -159,7 +149,7 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
this.ui.onResume(this); ui.onResume(this);
updateAdapter(); updateAdapter();
} }
@ -285,6 +275,7 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
@Override @Override
public void onDestroy() { public void onDestroy() {
bus.unregister(this);
if (clearDialog != null) { if (clearDialog != null) {
clearDialog.dismiss(); clearDialog.dismiss();
clearDialog = null; clearDialog = null;
@ -323,31 +314,13 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
return adapter; return adapter;
} }
@Override @Subscribe
public void onCalculatorEvent(@Nonnull CalculatorEventData calculatorEventData, @Nonnull CalculatorEventType calculatorEventType, @Nullable Object data) { void onHistoryChanged(@Nonnull History.ChangedEvent e) {
switch (calculatorEventType) { if (e.recent != isRecentHistory()) {
case history_state_added: return;
getActivity().runOnUiThread(new Runnable() { }
@Override
public void run() {
updateAdapter(); updateAdapter();
} }
});
break;
case clear_history_requested:
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
clearDialog = new AlertDialog.Builder(getActivity()).setTitle(R.string.cpp_clear_history_title)
.setMessage(R.string.cpp_clear_history_message)
.setPositiveButton(R.string.cpp_clear_history, clearDialogListener)
.setNegativeButton(R.string.c_cancel, clearDialogListener)
.create();
clearDialog.show();
}
});
break;
}
} protected abstract boolean isRecentHistory();
} }

View File

@ -22,30 +22,24 @@
package org.solovyev.android.calculator.history; package org.solovyev.android.calculator.history;
import android.app.Application;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Handler; import android.os.Handler;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.squareup.otto.Bus; import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe; import com.squareup.otto.Subscribe;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.solovyev.android.Check; import org.solovyev.android.Check;
import org.solovyev.android.calculator.App; import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.AppModule;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.Display;
import org.solovyev.android.calculator.DisplayState;
import org.solovyev.android.calculator.Editor;
import org.solovyev.android.calculator.EditorState;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
import org.solovyev.android.io.FileLoader; import org.solovyev.android.io.FileLoader;
import org.solovyev.android.io.FileSaver; import org.solovyev.android.io.FileSaver;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -53,16 +47,13 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import javax.annotation.Nonnull; import static android.text.TextUtils.isEmpty;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import static java.lang.Character.isDigit;
public class History { public class History {
public static final String TAG = App.subTag("History"); public static final String TAG = App.subTag("History");
private static final ChangedEvent CHANGED_EVENT_RECENT = new ChangedEvent(true);
private static final ChangedEvent CHANGED_EVENT_SAVED = new ChangedEvent(false);
@NonNull @NonNull
private final Runnable writeRecent = new WriteTask(true); private final Runnable writeRecent = new WriteTask(true);
@NonNull @NonNull
@ -71,17 +62,22 @@ public class History {
private final RecentHistory recent = new RecentHistory(); private final RecentHistory recent = new RecentHistory();
@Nonnull @Nonnull
private final List<HistoryState> saved = new ArrayList<>(); private final List<HistoryState> saved = new ArrayList<>();
@Nonnull
private final Bus bus;
@Inject @Inject
Handler handler; Handler handler;
@Inject @Inject
SharedPreferences preferences; SharedPreferences preferences;
@Nullable @Inject
private EditorState lastEditorState; Editor editor;
@Inject
Application application;
private boolean initialized; private boolean initialized;
@Inject @Inject
public History(Bus bus, @Named(AppModule.THREAD_INIT) Executor initThread) { public History(@NonNull Bus bus, @Nonnull @Named(AppModule.THREAD_INIT) Executor initThread) {
bus.register(this); this.bus = bus;
this.bus.register(this);
initThread.execute(new Runnable() { initThread.execute(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -90,23 +86,6 @@ public class History {
}); });
} }
private void migrateOldHistory() {
try {
final String xml = preferences.getString("org.solovyev.android.calculator.CalculatorModel_history", null);
if (TextUtils.isEmpty(xml)) {
return;
}
final List<HistoryState> states = convertOldHistory(xml);
if (states == null) {
return;
}
final JSONArray json = RecentHistory.toJson(states);
FileSaver.save(getSavedHistoryFile(), json.toString());
} catch (Exception e) {
Locator.getInstance().getLogger().error(TAG, e.getMessage(), e);
}
}
@Nullable @Nullable
static List<HistoryState> convertOldHistory(@NonNull String xml) { static List<HistoryState> convertOldHistory(@NonNull String xml) {
final OldHistory history = OldHistory.fromXml(xml); final OldHistory history = OldHistory.fromXml(xml);
@ -129,13 +108,13 @@ public class History {
} }
@NonNull @NonNull
private static File getSavedHistoryFile() { private File getSavedHistoryFile() {
return new File(App.getApplication().getFilesDir(), "history-saved.json"); return new File(application.getFilesDir(), "history-saved.json");
} }
@NonNull @NonNull
private static File getRecentHistoryFile() { private File getRecentHistoryFile() {
return new File(App.getApplication().getFilesDir(), "history-recent.json"); return new File(application.getFilesDir(), "history-recent.json");
} }
@Nonnull @Nonnull
@ -144,7 +123,7 @@ public class History {
return Collections.emptyList(); return Collections.emptyList();
} }
final CharSequence json = FileLoader.load(file); final CharSequence json = FileLoader.load(file);
if (TextUtils.isEmpty(json)) { if (isEmpty(json)) {
return Collections.emptyList(); return Collections.emptyList();
} }
try { try {
@ -155,6 +134,44 @@ public class History {
return Collections.emptyList(); return Collections.emptyList();
} }
private static boolean isIntermediate(@Nonnull String olderText,
@Nonnull String newerText) {
if (isEmpty(olderText)) {
return true;
}
if (isEmpty(newerText)) {
return false;
}
final int diff = newerText.length() - olderText.length();
if (diff >= 1) {
return newerText.startsWith(olderText);
} else if (diff <= 1) {
return olderText.startsWith(newerText);
} else if (diff == 0) {
return olderText.equals(newerText);
}
return false;
}
private void migrateOldHistory() {
try {
final String xml = preferences.getString("org.solovyev.android.calculator.CalculatorModel_history", null);
if (isEmpty(xml)) {
return;
}
final List<HistoryState> states = convertOldHistory(xml);
if (states == null) {
return;
}
final JSONArray json = RecentHistory.toJson(states);
FileSaver.save(getSavedHistoryFile(), json.toString());
} catch (Exception e) {
Locator.getInstance().getLogger().error(TAG, e.getMessage(), e);
}
}
private void init() { private void init() {
Check.isNotMainThread(); Check.isNotMainThread();
migrateOldHistory(); migrateOldHistory();
@ -175,7 +192,6 @@ public class History {
public void addRecent(@Nonnull HistoryState state) { public void addRecent(@Nonnull HistoryState state) {
Check.isMainThread(); Check.isMainThread();
recent.add(state); recent.add(state);
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.history_state_added, state);
onRecentChanged(); onRecentChanged();
} }
@ -188,11 +204,13 @@ public class History {
private void onRecentChanged() { private void onRecentChanged() {
handler.removeCallbacks(writeRecent); handler.removeCallbacks(writeRecent);
handler.postDelayed(writeRecent, 500); handler.postDelayed(writeRecent, 500);
bus.post(CHANGED_EVENT_RECENT);
} }
private void onSavedChanged() { private void onSavedChanged() {
handler.removeCallbacks(writeSaved); handler.removeCallbacks(writeSaved);
handler.postDelayed(writeSaved, 500); handler.postDelayed(writeSaved, 500);
bus.post(CHANGED_EVENT_SAVED);
} }
@Nonnull @Nonnull
@ -201,8 +219,6 @@ public class History {
final List<HistoryState> result = new LinkedList<>(); final List<HistoryState> result = new LinkedList<>();
final String groupingSeparator = AndroidCalculatorEngine.Preferences.groupingSeparator.getPreference(App.getPreferences());
final List<HistoryState> states = recent.asList(); final List<HistoryState> states = recent.asList();
final int statesCount = states.size(); final int statesCount = states.size();
for (int i = 1; i < statesCount; i++) { for (int i = 1; i < statesCount; i++) {
@ -210,13 +226,14 @@ public class History {
final HistoryState newerState = states.get(i); final HistoryState newerState = states.get(i);
final String olderText = olderState.editor.getTextString(); final String olderText = olderState.editor.getTextString();
final String newerText = newerState.editor.getTextString(); final String newerText = newerState.editor.getTextString();
if (!isIntermediate(olderText, newerText, groupingSeparator)) { if (!isIntermediate(olderText, newerText)) {
result.add(0, olderState); result.add(0, olderState);
} }
} }
if (statesCount > 0) { if (statesCount > 0) {
// try add last state if not empty
final HistoryState state = states.get(statesCount - 1); final HistoryState state = states.get(statesCount - 1);
if (!TextUtils.isEmpty(state.editor.getTextString())) { if (!isEmpty(state.editor.getTextString())) {
result.add(0, state); result.add(0, state);
} }
} }
@ -229,53 +246,6 @@ public class History {
return new ArrayList<>(saved); return new ArrayList<>(saved);
} }
private static boolean isIntermediate(@Nonnull String olderText,
@Nonnull String newerText,
@NonNull String groupingSeparator) {
if (TextUtils.isEmpty(olderText)) {
return true;
}
if (TextUtils.isEmpty(newerText)) {
return false;
}
olderText = trimGroupingSeparators(olderText, groupingSeparator);
newerText = trimGroupingSeparators(newerText, groupingSeparator);
final int diff = newerText.length() - olderText.length();
if (diff >= 1) {
return newerText.startsWith(olderText);
} else if (diff <= 1) {
return olderText.startsWith(newerText);
} else if (diff == 0) {
return olderText.equals(newerText);
}
return false;
}
@NonNull
private static String trimGroupingSeparators(@NonNull String text, @NonNull String groupingSeparator) {
if (TextUtils.isEmpty(groupingSeparator)) {
return text;
}
Check.isTrue(groupingSeparator.length() == 1);
final StringBuilder sb = new StringBuilder(text.length());
for (int i = 0; i < text.length(); i++) {
if (i == 0 || i == text.length() - 1) {
// grouping separator can't be the first and the last character
sb.append(text.charAt(i));
continue;
}
if (isDigit(text.charAt(i - 1)) && text.charAt(i) == groupingSeparator.charAt(0) && isDigit(text.charAt(i + 1))) {
// grouping separator => skip
continue;
}
sb.append(text.charAt(i));
}
return sb.toString();
}
public void clearRecent() { public void clearRecent() {
Check.isMainThread(); Check.isMainThread();
recent.clear(); recent.clear();
@ -321,27 +291,25 @@ public class History {
onRecentChanged(); onRecentChanged();
} }
@Subscribe
public void onEditorChanged(@Nonnull Editor.ChangedEvent e) {
if (!initialized) {
return;
}
lastEditorState = e.newState;
}
@Subscribe @Subscribe
public void onDisplayChanged(@Nonnull Display.ChangedEvent e) { public void onDisplayChanged(@Nonnull Display.ChangedEvent e) {
if (!initialized) { if (!initialized) {
return; return;
} }
if (lastEditorState == null) { final EditorState editorState = editor.getState();
final DisplayState displayState = e.newState;
if (editorState.sequence != displayState.sequence) {
return; return;
} }
if (lastEditorState.sequence != e.newState.sequence) { addRecent(HistoryState.newBuilder(editorState, displayState).build());
return; }
public static class ChangedEvent {
public final boolean recent;
public ChangedEvent(boolean recent) {
this.recent = recent;
} }
addRecent(HistoryState.newBuilder(lastEditorState, e.newState).build());
lastEditorState = null;
} }
private class WriteTask implements Runnable { private class WriteTask implements Runnable {

View File

@ -25,9 +25,8 @@ package org.solovyev.android.calculator.history;
import org.solovyev.android.calculator.CalculatorFragmentType; import org.solovyev.android.calculator.CalculatorFragmentType;
import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.R;
import java.util.List;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.List;
public class RecentHistoryFragment extends BaseHistoryFragment { public class RecentHistoryFragment extends BaseHistoryFragment {
@ -51,4 +50,9 @@ public class RecentHistoryFragment extends BaseHistoryFragment {
history.clearRecent(); history.clearRecent();
getAdapter().clear(); getAdapter().clear();
} }
@Override
protected boolean isRecentHistory() {
return true;
}
} }

View File

@ -25,9 +25,8 @@ package org.solovyev.android.calculator.history;
import org.solovyev.android.calculator.CalculatorFragmentType; import org.solovyev.android.calculator.CalculatorFragmentType;
import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.R;
import java.util.List;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.List;
public class SavedHistoryFragment extends BaseHistoryFragment { public class SavedHistoryFragment extends BaseHistoryFragment {
@ -51,4 +50,9 @@ public class SavedHistoryFragment extends BaseHistoryFragment {
history.clearSaved(); history.clearSaved();
getAdapter().clear(); getAdapter().clear();
} }
@Override
protected boolean isRecentHistory() {
return false;
}
} }

View File

@ -1,5 +1,6 @@
package org.solovyev.android; package org.solovyev.android;
import android.os.Build;
import org.junit.runners.model.InitializationError; import org.junit.runners.model.InitializationError;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
@ -7,7 +8,7 @@ import org.robolectric.manifest.AndroidManifest;
import org.robolectric.res.Fs; import org.robolectric.res.Fs;
public class CalculatorTestRunner extends RobolectricGradleTestRunner { public class CalculatorTestRunner extends RobolectricGradleTestRunner {
private static final int MAX_SDK_SUPPORTED_BY_ROBOLECTRIC = 18; public static final int SUPPORTED_SDK = Build.VERSION_CODES.LOLLIPOP;
public CalculatorTestRunner(Class<?> testClass) throws InitializationError { public CalculatorTestRunner(Class<?> testClass) throws InitializationError {
super(testClass); super(testClass);
@ -21,7 +22,7 @@ public class CalculatorTestRunner extends RobolectricGradleTestRunner {
return new AndroidManifest(Fs.fileFromPath(manifestFilePath), Fs.fileFromPath(resourcesFilePath), Fs.fileFromPath(assetsFilePath)) { return new AndroidManifest(Fs.fileFromPath(manifestFilePath), Fs.fileFromPath(resourcesFilePath), Fs.fileFromPath(assetsFilePath)) {
@Override @Override
public int getTargetSdkVersion() { public int getTargetSdkVersion() {
return MAX_SDK_SUPPORTED_BY_ROBOLECTRIC; return SUPPORTED_SDK;
} }
}; };
} }

View File

@ -22,9 +22,12 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import com.squareup.otto.Bus;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.solovyev.android.calculator.plot.CalculatorPlotter; import org.solovyev.android.calculator.plot.CalculatorPlotter;
import java.util.concurrent.Executor;
/** /**
* User: serso * User: serso
* Date: 10/7/12 * Date: 10/7/12
@ -33,7 +36,7 @@ import org.solovyev.android.calculator.plot.CalculatorPlotter;
public class AbstractCalculatorTest { public class AbstractCalculatorTest {
protected void setUp() throws Exception { protected void setUp() throws Exception {
Locator.getInstance().init(new CalculatorImpl(null, eventExecutor), CalculatorTestUtils.newCalculatorEngine(), Mockito.mock(CalculatorClipboard.class), Mockito.mock(CalculatorNotifier.class), new SystemOutCalculatorLogger(), Mockito.mock(CalculatorPreferenceService.class), Mockito.mock(Keyboard.class), Mockito.mock(CalculatorPlotter.class)); Locator.getInstance().init(new CalculatorImpl(Mockito.mock(Bus.class), Mockito.mock(Executor.class)), CalculatorTestUtils.newCalculatorEngine(), Mockito.mock(CalculatorClipboard.class), Mockito.mock(CalculatorNotifier.class), new SystemOutCalculatorLogger(), Mockito.mock(CalculatorPreferenceService.class), Mockito.mock(Keyboard.class), Mockito.mock(CalculatorPlotter.class));
Locator.getInstance().getEngine().init(); Locator.getInstance().getEngine().init();
} }

View File

@ -23,32 +23,25 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import android.content.Context; import android.content.Context;
import com.squareup.otto.Bus;
import jscl.JsclMathEngine;
import org.junit.Assert; import org.junit.Assert;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.fakes.RoboSharedPreferences; import org.robolectric.fakes.RoboSharedPreferences;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.language.Languages; import org.solovyev.android.calculator.language.Languages;
import org.solovyev.android.calculator.plot.CalculatorPlotter; import org.solovyev.android.calculator.plot.CalculatorPlotter;
import java.io.ByteArrayInputStream; import javax.annotation.Nonnull;
import java.io.ByteArrayOutputStream; import javax.annotation.Nullable;
import java.io.IOException; import java.io.*;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import jscl.JsclMathEngine;
/** /**
* User: serso * User: serso
* Date: 10/7/12 * Date: 10/7/12
@ -60,8 +53,8 @@ public class CalculatorTestUtils {
public static final int TIMEOUT = 3; public static final int TIMEOUT = 3;
public static void staticSetUp() throws Exception { public static void staticSetUp() throws Exception {
App.init(RuntimeEnvironment.application, new Languages(new RoboSharedPreferences(new HashMap<String, Map<String, Object>>(), "test", 0))); App.init(new CalculatorApplication(), new Languages(new RoboSharedPreferences(new HashMap<String, Map<String, Object>>(), "test", 0)));
Locator.getInstance().init(new CalculatorImpl(), newCalculatorEngine(), Mockito.mock(CalculatorClipboard.class), Mockito.mock(CalculatorNotifier.class), new SystemOutCalculatorLogger(), Mockito.mock(CalculatorPreferenceService.class), Mockito.mock(Keyboard.class), Mockito.mock(CalculatorPlotter.class)); Locator.getInstance().init(new CalculatorImpl(Mockito.mock(Bus.class), Mockito.mock(Executor.class)), newCalculatorEngine(), Mockito.mock(CalculatorClipboard.class), Mockito.mock(CalculatorNotifier.class), new SystemOutCalculatorLogger(), Mockito.mock(CalculatorPreferenceService.class), Mockito.mock(Keyboard.class), Mockito.mock(CalculatorPlotter.class));
Locator.getInstance().getEngine().init(); Locator.getInstance().getEngine().init();
final DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(); final DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols();
@ -71,7 +64,7 @@ public class CalculatorTestUtils {
} }
public static void staticSetUp(@Nullable Context context) throws Exception { public static void staticSetUp(@Nullable Context context) throws Exception {
Locator.getInstance().init(new CalculatorImpl(), newCalculatorEngine(), Mockito.mock(CalculatorClipboard.class), Mockito.mock(CalculatorNotifier.class), new SystemOutCalculatorLogger(), Mockito.mock(CalculatorPreferenceService.class), Mockito.mock(Keyboard.class), Mockito.mock(CalculatorPlotter.class)); Locator.getInstance().init(new CalculatorImpl(Mockito.mock(Bus.class), Mockito.mock(Executor.class)), newCalculatorEngine(), Mockito.mock(CalculatorClipboard.class), Mockito.mock(CalculatorNotifier.class), new SystemOutCalculatorLogger(), Mockito.mock(CalculatorPreferenceService.class), Mockito.mock(Keyboard.class), Mockito.mock(CalculatorPlotter.class));
Locator.getInstance().getEngine().init(); Locator.getInstance().getEngine().init();
if (context != null) { if (context != null) {

View File

@ -22,9 +22,11 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import android.preference.PreferenceManager;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.robolectric.RuntimeEnvironment;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -41,7 +43,7 @@ public class EditorTest extends AbstractCalculatorTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
this.editor = new Editor(null); this.editor = new Editor(PreferenceManager.getDefaultSharedPreferences(RuntimeEnvironment.application));
} }
@Test @Test

View File

@ -22,61 +22,172 @@
package org.solovyev.android.calculator.history; package org.solovyev.android.calculator.history;
import android.content.SharedPreferences;
import android.os.Handler;
import com.squareup.otto.Bus; import com.squareup.otto.Bus;
import org.junit.Before;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.solovyev.android.CalculatorTestRunner; import org.solovyev.android.CalculatorTestRunner;
import org.solovyev.android.calculator.CalculatorTestUtils; import org.solovyev.android.calculator.BuildConfig;
import org.solovyev.android.calculator.DisplayState; import org.solovyev.android.calculator.DisplayState;
import org.solovyev.android.calculator.Editor;
import org.solovyev.android.calculator.EditorState; import org.solovyev.android.calculator.EditorState;
import javax.annotation.Nonnull;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import javax.annotation.Nonnull; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(CalculatorTestRunner.class) @Config(constants = BuildConfig.class, sdk = CalculatorTestRunner.SUPPORTED_SDK)
@RunWith(RobolectricGradleTestRunner.class)
public class HistoryTest { public class HistoryTest {
@BeforeClass private History history;
public static void setUp() throws Exception {
CalculatorTestUtils.staticSetUp(); @Before
public void setUp() throws Exception {
history = new History(Mockito.mock(Bus.class), Mockito.mock(Executor.class));
history.handler = Mockito.mock(Handler.class);
history.preferences = Mockito.mock(SharedPreferences.class);
history.editor = Mockito.mock(Editor.class);
history.application = RuntimeEnvironment.application;
} }
@Test @Test
public void testGetStates() throws Exception { public void testGetStates() throws Exception {
History history = new History(Mockito.any(Bus.class), new Executor() { addState("1");
@Override addState("12");
public void execute(@Nonnull Runnable command) { addState("123");
command.run(); addState("123+");
} addState("123+3");
}); addState("");
addState("2");
addState(history, "1"); addState("23");
addState(history, "12"); addState("235");
addState(history, "123"); addState("2355");
addState(history, "123+"); addState("235");
addState(history, "123+3"); addState("2354");
addState(history, ""); addState("23547");
addState(history, "2");
addState(history, "23");
addState(history, "235");
addState(history, "2355");
addState(history, "235");
addState(history, "2354");
addState(history, "23547");
final List<HistoryState> states = history.getRecent(); final List<HistoryState> states = history.getRecent();
Assert.assertEquals(2, states.size()); assertEquals(2, states.size());
Assert.assertEquals("23547", states.get(1).editor.getTextString()); assertEquals("23547", states.get(0).editor.getTextString());
Assert.assertEquals("123+3", states.get(0).editor.getTextString()); assertEquals("123+3", states.get(1).editor.getTextString());
} }
private void addState(@Nonnull History history, @Nonnull String text) { @Test
public void testRecentHistoryShouldNotContainEmptyStates() throws Exception {
addState("");
addState("1");
addState("12");
addState("");
addState("");
addState("34");
addState("");
final List<HistoryState> states = history.getRecent();
assertEquals(2, states.size());
assertEquals("34", states.get(0).editor.getTextString());
assertEquals("12", states.get(1).editor.getTextString());
}
private void addState(@Nonnull String text) {
history.addRecent(HistoryState.newBuilder(EditorState.create(text, 3), DisplayState.empty())); history.addRecent(HistoryState.newBuilder(EditorState.create(text, 3), DisplayState.empty()));
} }
private static final String oldXml1 = "<history>\n" +
" <historyItems class=\"java.util.ArrayList\">\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>3</cursorPosition>\n" +
" <text>1+1</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>0</cursorPosition>\n" +
" <text>Error</text>\n" +
" </editorState>\n" +
" <jsclOperation>simplify</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" </historyItems>\n" +
"</history>";
private static final String oldXml2 = "<history>\n" +
" <historyItems class=\"java.util.ArrayList\">\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>3</cursorPosition>\n" +
" <text>1+1</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>0</cursorPosition>\n" +
" <text>Error</text>\n" +
" </editorState>\n" +
" <jsclOperation>simplify</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>2</cursorPosition>\n" +
" <text>5/6</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>3</cursorPosition>\n" +
" <text>5/6</text>\n" +
" </editorState>\n" +
" <jsclOperation>numeric</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>1</cursorPosition>\n" +
" <text></text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>0</cursorPosition>\n" +
" <text>Error</text>\n" +
" </editorState>\n" +
" <jsclOperation>elementary</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>0</cursorPosition>\n" +
" <text>4+5/35sin(41)+dfdsfsdfs</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>1</cursorPosition>\n" +
" <text>4+5/35sin(41)+dfdsfsdfs</text>\n" +
" </editorState>\n" +
" <jsclOperation>numeric</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" </historyItems>\n" +
"</history>";
@Test
public void testShouldConvertOldHistory() throws Exception {
List<HistoryState> states = History.convertOldHistory(oldXml1);
assertNotNull(states);
assertEquals(1, states.size());
HistoryState state = states.get(0);
assertEquals(100000000, state.time);
}
} }

View File

@ -34,85 +34,6 @@ public class HistoryUtilsTest {
private static final String emptyHistory = "<history>\n" + private static final String emptyHistory = "<history>\n" +
" <historyItems class=\"java.util.ArrayList\"/>\n" + " <historyItems class=\"java.util.ArrayList\"/>\n" +
"</history>"; "</history>";
private static final String toXml1 = "<history>\n" +
" <historyItems class=\"java.util.ArrayList\">\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>3</cursorPosition>\n" +
" <text>1+1</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>0</cursorPosition>\n" +
" <text>Error</text>\n" +
" </editorState>\n" +
" <jsclOperation>simplify</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" </historyItems>\n" +
"</history>";
private static final String toXml2 = "<history>\n" +
" <historyItems class=\"java.util.ArrayList\">\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>3</cursorPosition>\n" +
" <text>1+1</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>0</cursorPosition>\n" +
" <text>Error</text>\n" +
" </editorState>\n" +
" <jsclOperation>simplify</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>2</cursorPosition>\n" +
" <text>5/6</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>3</cursorPosition>\n" +
" <text>5/6</text>\n" +
" </editorState>\n" +
" <jsclOperation>numeric</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>1</cursorPosition>\n" +
" <text></text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>0</cursorPosition>\n" +
" <text>Error</text>\n" +
" </editorState>\n" +
" <jsclOperation>elementary</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>0</cursorPosition>\n" +
" <text>4+5/35sin(41)+dfdsfsdfs</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>1</cursorPosition>\n" +
" <text>4+5/35sin(41)+dfdsfsdfs</text>\n" +
" </editorState>\n" +
" <jsclOperation>numeric</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" </historyItems>\n" +
"</history>";
@Test @Test
public void testFromXml() throws Exception { public void testFromXml() throws Exception {