diff --git a/app/build.gradle b/app/build.gradle index d0039d8..ebc8c2d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,6 +7,10 @@ buildscript { } } +repositories { + maven { url = "https://oss.sonatype.org/content/repositories/snapshots" } +} + apply plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt' @@ -45,6 +49,6 @@ dependencies { apt 'com.google.dagger:dagger-compiler:2.0' provided 'javax.annotation:jsr250-api:1.0' - testCompile "junit:junit:4.12" - testCompile "org.mockito:mockito-core:1.9.5" + testCompile 'junit:junit:4.12' + testCompile 'org.mockito:mockito-core:1.9.5' } diff --git a/app/src/main/java/com/anbroidsdev/converter/GsonObjectSharedPreferences.java b/app/src/main/java/com/anbroidsdev/converter/GsonObjectSharedPreferences.java new file mode 100644 index 0000000..6fa202b --- /dev/null +++ b/app/src/main/java/com/anbroidsdev/converter/GsonObjectSharedPreferences.java @@ -0,0 +1,198 @@ +package com.anbroidsdev.converter; + +import android.content.SharedPreferences; +import android.support.annotation.Nullable; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +import java.lang.reflect.Type; +import java.util.Map; +import java.util.Set; + +public class GsonObjectSharedPreferences implements ObjectSharedPreferences { + + private final SharedPreferences delegate; + private final Editor editor; + private final Gson gson; + + public GsonObjectSharedPreferences(SharedPreferences delegate, Gson gson) { + this.delegate = delegate; + this.editor = new Editor(delegate, gson); + this.gson = gson; + } + + public static class Editor implements ObjectSharedPreferences.ObjectEditor { + + private final SharedPreferences delegate; + private final Gson gson; + + public Editor(SharedPreferences delegate, Gson gson) { + this.delegate = delegate; + this.gson = gson; + } + + @Override + public Editor putBoolean(String key, boolean value) { + delegate.edit().putBoolean(key, value); + + return this; + } + + @Override + public Editor putFloat(String key, float value) { + delegate.edit().putFloat(key, value); + + return this; + } + + @Override + public Editor putInt(String key, int value) { + delegate.edit().putInt(key, value); + + return this; + } + + @Override + public Editor putLong(String key, long value) { + delegate.edit().putLong(key, value); + + return this; + } + + @Override + public Editor putString(String key, String value) { + delegate.edit().putString(key, value); + + return this; + } + + @Override + public Editor putStringSet(String key, Set values) { + delegate.edit().putStringSet(key, values); + + return this; + } + + @Override + public SharedPreferences.Editor putObject(String key, T object) { + delegate.edit().putString(key, object != null ? gson.toJson(object) : null); + + return this; + } + + @Override + public void apply() { + delegate.edit().apply(); + } + + @Override + public Editor clear() { + delegate.edit().clear(); + + return this; + } + + @Override + public boolean commit() { + delegate.edit().commit(); + + return true; + } + + @Override + public Editor remove(String s) { + delegate.edit().remove(s); + + return this; + } + } + + @Override + public ObjectEditor edit() { + return editor; + } + + @Override + public Map getAll() { + return delegate.getAll(); + } + + @Override + public boolean getBoolean(String key, boolean defValue) { + return delegate.getBoolean(key, defValue); + } + + @Override + public float getFloat(String key, float defValue) { + return delegate.getFloat(key, defValue); + } + + @Override + public int getInt(String key, int defValue) { + return delegate.getInt(key, defValue); + } + + @Override + public long getLong(String key, long defValue) { + return delegate.getLong(key, defValue); + } + + @Nullable + @Override + public String getString(String key, String defValue) { + return delegate.getString(key, defValue); + } + + @Nullable + @Override + public Set getStringSet(String key, Set defValues) { + return delegate.getStringSet(key, defValues); + } + + @Nullable + @Override + public T getObject(String key, Class theClass) { + try { + final String json = delegate.getString(key, null); + + return gson.fromJson(json, theClass); + } catch (JsonSyntaxException e) { + editor.remove(key); + editor.commit(); + + return null; + } + } + + @Nullable + @Override + public T getObject(String key, Type typeOf) { + try { + final String json = delegate.getString(key, null); + + return gson.fromJson(json, typeOf); + } catch (JsonSyntaxException e) { + editor.remove(key); + editor.commit(); + + return null; + } + } + + @Override + public boolean contains(String s) { + return delegate.contains(s); + } + + @Override + public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) { + delegate.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener); + } + + @Override + public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) { + delegate.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener); + } + +} diff --git a/app/src/main/java/com/anbroidsdev/converter/MoneyConverter.java b/app/src/main/java/com/anbroidsdev/converter/MoneyConverter.java index a8802c0..977f6de 100644 --- a/app/src/main/java/com/anbroidsdev/converter/MoneyConverter.java +++ b/app/src/main/java/com/anbroidsdev/converter/MoneyConverter.java @@ -1,7 +1,6 @@ package com.anbroidsdev.converter; import java.util.Currency; -import java.util.Map; /** * Created by david on 23/2/15. @@ -9,9 +8,9 @@ public class MoneyConverter { private Currency base; - private Map rates; + private Rates rates; - public MoneyConverter(Currency base, Map rates) { + public MoneyConverter(Currency base, Rates rates) { setBase(base); setRates(rates); } @@ -22,19 +21,19 @@ public void setBase(Currency base) { } if (rates != null) { - if (!rates.containsKey(base)) { + if (!rates.containsCurrency(base)) { throw new IllegalArgumentException("Currency is not supported"); } - final Double inverseRate = 1.0 / rates.get(base); + final Double inverseRate = 1.0 / rates.get(base).getValue(); rates.remove(base); - for (Map.Entry entry : rates.entrySet()) { - entry.setValue(inverseRate * entry.getValue()); + for (Rate rate : rates.list()) { + rate.setValue(inverseRate * rate.getValue()); } - rates.put(this.base, inverseRate); + rates.put(new Rate(this.base, inverseRate)); } this.base = base; @@ -44,13 +43,13 @@ public Currency getBase() { return base; } - public void setRates(Map rates) { + public void setRates(Rates rates) { checkRates(rates); this.rates = rates; } - public Map getRates() { + public Rates getRates() { return rates; } @@ -59,11 +58,11 @@ public double convert(double amount, Currency currency) { } public double convert(double amount, Currency currency, Currency base) { - if (!rates.containsKey(currency) && currency != this.base) { + if (!rates.containsCurrency(currency) && currency != this.base) { throw new IllegalArgumentException("Currency is not supported"); } - if (!rates.containsKey(base) && base != this.base) { + if (!rates.containsCurrency(base) && base != this.base) { throw new IllegalArgumentException("Base is not supported"); } @@ -73,31 +72,31 @@ public double convert(double amount, Currency currency, Currency base) { final Double rate; if (currency == this.base) { - rate = 1.0 / rates.get(base); + rate = 1.0 / rates.get(base).getValue(); } else if (base == this.base) { - rate = rates.get(currency); + rate = rates.get(currency).getValue(); } else { - rate = 1.0 / rates.get(base) * rates.get(currency); + rate = 1.0 / rates.get(base).getValue() * rates.get(currency).getValue(); } return rate * amount; } - private void checkRates(Map rates) { + private void checkRates(Rates rates) { if (rates == null || rates.isEmpty()) { throw new IllegalArgumentException("Rates cannot be null or empty"); } - if (base != null && rates.containsKey(base)) { + if (base != null && rates.containsCurrency(base)) { throw new IllegalArgumentException("Rates cannot contain the Base currency"); } - for (Map.Entry entry : rates.entrySet()) { - if (entry.getValue() == null) { + for (Rate rate : rates.list()) { + if (rate.getValue() == null) { throw new IllegalArgumentException("Rates cannot have any null value"); } - if (entry.getValue() < 0) { + if (rate.getValue() < 0) { throw new IllegalArgumentException("Rates cannot have any negative value"); } } diff --git a/app/src/main/java/com/anbroidsdev/converter/ObjectSharedPreferences.java b/app/src/main/java/com/anbroidsdev/converter/ObjectSharedPreferences.java new file mode 100644 index 0000000..f452187 --- /dev/null +++ b/app/src/main/java/com/anbroidsdev/converter/ObjectSharedPreferences.java @@ -0,0 +1,40 @@ +package com.anbroidsdev.converter; + +import android.content.SharedPreferences; + +import java.lang.reflect.Type; + +/** + * {@inheritDoc} + */ +public interface ObjectSharedPreferences extends SharedPreferences { + + /** + * Retrieve an Object of a given class from the preferences. + * + * @param key The name of the preference to retrieve. + * @param theClass Type of the object to be returned. + * + * @return Returns the preference value if it exists, or {@code null}. + */ + public T getObject(String key, Class theClass); + + /** + * Retrieve an Object of a given class from the preferences. + * + * @param key The name of the preference to retrieve. + * @param typeOf Type of the object to be returned. + * + * @return Returns the preference value if it exists, or {@code null}. + */ + public T getObject(String key, Type typeOf); + + public interface ObjectEditor extends SharedPreferences.Editor { + + public SharedPreferences.Editor putObject(String key, T object); + + } + + public ObjectEditor edit(); + +} diff --git a/app/src/main/java/com/anbroidsdev/converter/Rate.java b/app/src/main/java/com/anbroidsdev/converter/Rate.java new file mode 100644 index 0000000..8000cca --- /dev/null +++ b/app/src/main/java/com/anbroidsdev/converter/Rate.java @@ -0,0 +1,55 @@ +package com.anbroidsdev.converter; + +import java.util.Arrays; +import java.util.Currency; + +public class Rate { + + private Currency currency; + private Double value; + + public Rate(Currency currency, Double value) { + if (currency == null) { + throw new IllegalArgumentException("currency cannot be null"); + } + if (value == null) { + throw new IllegalArgumentException("value cannot be null"); + } + if (value < 0) { + throw new IllegalArgumentException("value cannot less than 0"); + } + + this.currency = currency; + this.value = value; + } + + public void setCurrency(Currency currency) { + this.currency = currency; + } + + public Currency getCurrency() { + return currency; + } + + public void setValue(Double value) { + this.value = value; + } + + public Double getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Rate that = (Rate) o; + return currency.equals(that.currency) && value.equals(that.value); + } + + @Override + public int hashCode() { + return Arrays.hashCode(new Object[]{currency, value}); + } + +} diff --git a/app/src/main/java/com/anbroidsdev/converter/Rates.java b/app/src/main/java/com/anbroidsdev/converter/Rates.java new file mode 100644 index 0000000..36d5726 --- /dev/null +++ b/app/src/main/java/com/anbroidsdev/converter/Rates.java @@ -0,0 +1,81 @@ +package com.anbroidsdev.converter; + +import java.util.ArrayList; +import java.util.Currency; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Rates { + + private final Map rates; + + public Rates() { + this.rates = new HashMap<>(); + } + + public Rates(Map rates) { + this.rates = new HashMap<>(rates.size()); + + for (Map.Entry rate : rates.entrySet()) { + try { + final Currency currency = Currency.getInstance(rate.getKey()); + this.rates.put(currency, new Rate(currency, rate.getValue())); + } catch (IllegalArgumentException e) {} + } + } + + public boolean containsCurrency(Currency currency) { + return rates.containsKey(currency); + } + + public void put(Rate rate) { + rates.put(rate.getCurrency(), rate); + } + + public Rate get(Currency currency) { + return rates.get(currency); + } + + public Rate remove(Currency currency) { + return rates.remove(currency); + } + + public boolean isEmpty() { + return rates.isEmpty(); + } + + public Rates copy() { + final Rates copyRates = new Rates(); + + for (Map.Entry entry : rates.entrySet()) { + copyRates.put(new Rate(entry.getValue().getCurrency(), entry.getValue().getValue())); + } + + return copyRates; + } + + public List list() { + final List list = new ArrayList<>(rates.size()); + + for (Map.Entry entry : rates.entrySet()) { + list.add(entry.getValue()); + } + + return list; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Rates that = (Rates) o; + return rates.equals(that.rates); + } + + @Override + public int hashCode() { + return rates.hashCode(); + } + +} diff --git a/app/src/main/java/com/anbroidsdev/converter/RatesSaver.java b/app/src/main/java/com/anbroidsdev/converter/RatesSaver.java new file mode 100644 index 0000000..d57e39f --- /dev/null +++ b/app/src/main/java/com/anbroidsdev/converter/RatesSaver.java @@ -0,0 +1,9 @@ +package com.anbroidsdev.converter; + +public interface RatesSaver { + + public void saveRates(Rates rates, long timestamp); + public Rates getLastSavedRates(); + public long getLastSavedRatesTimestamp(); + +} diff --git a/app/src/main/java/com/anbroidsdev/converter/MoneyConverterRatesUpdater.java b/app/src/main/java/com/anbroidsdev/converter/RatesUpdater.java similarity index 52% rename from app/src/main/java/com/anbroidsdev/converter/MoneyConverterRatesUpdater.java rename to app/src/main/java/com/anbroidsdev/converter/RatesUpdater.java index c643771..2d888f5 100644 --- a/app/src/main/java/com/anbroidsdev/converter/MoneyConverterRatesUpdater.java +++ b/app/src/main/java/com/anbroidsdev/converter/RatesUpdater.java @@ -1,23 +1,23 @@ package com.anbroidsdev.converter; -import java.util.Currency; -import java.util.HashMap; import java.util.Map; -public class MoneyConverterRatesUpdater { +public class RatesUpdater { public interface OnRatesUpdateListener { - public void onRatesUpdated(Map rates, long timestamp); + public void onRatesUpdated(); } private final MoneyConverter moneyConverter; private final OpenExchangeRatesApi openExchangeRatesApi; + private final RatesSaver ratesSaver; - public MoneyConverterRatesUpdater(MoneyConverter moneyConverter, OpenExchangeRatesApi openExchangeRatesApi) { + public RatesUpdater(MoneyConverter moneyConverter, OpenExchangeRatesApi openExchangeRatesApi, RatesSaver ratesSaver) { this.moneyConverter = moneyConverter; this.openExchangeRatesApi = openExchangeRatesApi; + this.ratesSaver = ratesSaver; } public void updateRates(final OnRatesUpdateListener listener) { @@ -25,18 +25,14 @@ public void updateRates(final OnRatesUpdateListener listener) { @Override public void onLatestRates(Map rates, long timestamp) { - final Map newRates = new HashMap<>(rates.size()); + final Rates newRates = new Rates(rates); - for (Map.Entry rate : rates.entrySet()) { - try { - newRates.put(Currency.getInstance(rate.getKey()), rate.getValue()); - } catch (IllegalArgumentException e) {} - } + ratesSaver.saveRates(newRates, timestamp); moneyConverter.setRates(newRates); if (listener != null) { - listener.onRatesUpdated(newRates, timestamp); + listener.onRatesUpdated(); } } diff --git a/app/src/main/java/com/anbroidsdev/converter/SharedPreferencesRatesSaver.java b/app/src/main/java/com/anbroidsdev/converter/SharedPreferencesRatesSaver.java new file mode 100644 index 0000000..36e0404 --- /dev/null +++ b/app/src/main/java/com/anbroidsdev/converter/SharedPreferencesRatesSaver.java @@ -0,0 +1,46 @@ +package com.anbroidsdev.converter; + +public class SharedPreferencesRatesSaver implements RatesSaver { + + private static final String RATES = "rates"; + private static final String TIMESTAMP = "timestamp"; + + private final ObjectSharedPreferences sharedPreferences; + + private Rates lastSavedRates; + private long lastSavedRatesTimestamp; + + public SharedPreferencesRatesSaver(ObjectSharedPreferences sharedPreferences) { + this.sharedPreferences = sharedPreferences; + + lastSavedRates = sharedPreferences.getObject(RATES, Rates.class); + lastSavedRatesTimestamp = sharedPreferences.getLong(TIMESTAMP, 0L); + } + + public void saveRates(Rates rates, long timestamp) { + lastSavedRates = rates; + lastSavedRatesTimestamp = timestamp; + + sharedPreferences.edit().putObject(RATES, rates); + sharedPreferences.edit().putLong(TIMESTAMP, timestamp); + sharedPreferences.edit().apply(); + } + + public Rates getLastSavedRates() { + return lastSavedRates; + } + + public long getLastSavedRatesTimestamp() { + return lastSavedRatesTimestamp; + } + + public void clear() { + lastSavedRates = null; + lastSavedRatesTimestamp = 0; + + sharedPreferences.edit().remove(RATES); + sharedPreferences.edit().remove(TIMESTAMP); + sharedPreferences.edit().apply(); + } + +} diff --git a/app/src/test/java/com/anbroidsdev/converter/MoneyConverterTest.java b/app/src/test/java/com/anbroidsdev/converter/MoneyConverterTest.java index cd43949..3540822 100644 --- a/app/src/test/java/com/anbroidsdev/converter/MoneyConverterTest.java +++ b/app/src/test/java/com/anbroidsdev/converter/MoneyConverterTest.java @@ -3,11 +3,9 @@ import org.junit.Before; import org.junit.Test; +import java.util.ArrayList; import java.util.Currency; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.List; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -20,12 +18,12 @@ public class MoneyConverterTest { */ private static final Currency BASE = Currency.getInstance("USD"); - private static final Map RATES = new HashMap<>(); + private static final Rates RATES = new Rates(); static { - RATES.put(Currency.getInstance("EUR"), 0.879689294); - RATES.put(Currency.getInstance("GBP"), 0.656183); - RATES.put(Currency.getInstance("JPY"), 120.171); + RATES.put(new Rate(Currency.getInstance("EUR"), 0.879689294)); + RATES.put(new Rate(Currency.getInstance("GBP"), 0.656183)); + RATES.put(new Rate(Currency.getInstance("JPY"), 120.171)); } private static final Double[] AMOUNTS = new Double[]{-2.0, @@ -37,7 +35,7 @@ public class MoneyConverterTest { @Before public void setUp() throws Exception { - moneyConverter = new MoneyConverter(BASE, new HashMap<>(RATES)); + moneyConverter = new MoneyConverter(BASE, RATES.copy()); } @Test(expected = IllegalArgumentException.class) @@ -52,61 +50,45 @@ public void shouldThrowExceptionIfRatesAreNull() throws Exception { @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfRatesAreEmpty() throws Exception { - new MoneyConverter(BASE, new HashMap()); + new MoneyConverter(BASE, new Rates()); } @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfRatesHasAnyNullValue() throws Exception { final Currency key = Currency.getInstance("EUR"); - final Double value = RATES.get(key); + final Rates rates = RATES.copy(); - RATES.put(key, null); + rates.put(new Rate(key, null)); - try { - new MoneyConverter(BASE, RATES); - } catch (IllegalArgumentException e) { - RATES.put(key, value); - - throw e; - } + new MoneyConverter(BASE, rates); } @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfRatesHasAnyNegativeValue() throws Exception { final Currency key = Currency.getInstance("EUR"); - final Double value = RATES.get(key); + final Rates rates = RATES.copy(); + final Double value = rates.get(key).getValue(); - RATES.put(key, -value); + rates.put(new Rate(key, -value)); - try { - new MoneyConverter(BASE, RATES); - } catch (IllegalArgumentException e) { - RATES.put(key, value); - - throw e; - } + new MoneyConverter(BASE, rates); } @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfRatesContainsTheBase() throws Exception { - RATES.put(BASE, null); + final Rates rates = RATES.copy(); + rates.put(new Rate(BASE, null)); - try { - new MoneyConverter(BASE, RATES); - } catch (IllegalArgumentException e) { - RATES.remove(BASE); - - throw e; - } + new MoneyConverter(BASE, rates); } @Test public void shouldConvertAmountToAGivenCurrencyWithDefaultBase() throws Exception { - for (Map.Entry entry : RATES.entrySet()) { + for (Rate rate : RATES.list()) { for (Double amount : AMOUNTS) { - final double convertedAmount = moneyConverter.convert(moneyConverter.convert(amount, entry.getKey()), + final double convertedAmount = moneyConverter.convert(moneyConverter.convert(amount, rate.getCurrency()), BASE, - entry.getKey()); + rate.getCurrency()); // Converting twice should return the original amount assertEquals(amount, convertedAmount, 0.000000000000001); @@ -116,9 +98,11 @@ public void shouldConvertAmountToAGivenCurrencyWithDefaultBase() throws Exceptio @Test public void shouldConvertAmountToAGivenCurrencyWithCustomBase() throws Exception { - final Set currencies = new HashSet<>(); + final List currencies = new ArrayList<>(); currencies.add(BASE); - currencies.addAll(RATES.keySet()); + for (Rate rate : RATES.list()) { + currencies.add(rate.getCurrency()); + } for (Currency currency : currencies) { for (Currency base : currencies) { @@ -167,18 +151,15 @@ public void shouldUpdateRatesWhenBaseIsChanged() throws Exception { moneyConverter.setBase(newBaseCurrency); - double inverseRate = 1.0 / RATES.get(newBaseCurrency); - - assertFalse(moneyConverter.getRates().containsKey(newBaseCurrency)); - assertTrue(moneyConverter.getRates().containsKey(baseCurrency)); - assertEquals(inverseRate, moneyConverter.getRates().get(baseCurrency), 0); + double inverseRate = 1.0 / RATES.get(newBaseCurrency).getValue(); - final Map rates = new HashMap<>(moneyConverter.getRates()); - rates.remove(newBaseCurrency); + assertFalse(moneyConverter.getRates().containsCurrency(newBaseCurrency)); + assertTrue(moneyConverter.getRates().containsCurrency(baseCurrency)); + assertEquals(inverseRate, moneyConverter.getRates().get(baseCurrency).getValue(), 0); - for (Map.Entry entry : rates.entrySet()) { - if (entry.getKey() != baseCurrency) { - assertEquals(inverseRate * RATES.get(entry.getKey()), entry.getValue(), 0); + for (Rate rate : moneyConverter.getRates().list()) { + if (rate.getCurrency() != baseCurrency) { + assertEquals(inverseRate * RATES.get(rate.getCurrency()).getValue(), rate.getValue(), 0); } } } diff --git a/app/src/test/java/com/anbroidsdev/converter/MoneyConverterRatesUpdaterTest.java b/app/src/test/java/com/anbroidsdev/converter/RatesUpdaterTest.java similarity index 70% rename from app/src/test/java/com/anbroidsdev/converter/MoneyConverterRatesUpdaterTest.java rename to app/src/test/java/com/anbroidsdev/converter/RatesUpdaterTest.java index 5c92228..6334ff9 100644 --- a/app/src/test/java/com/anbroidsdev/converter/MoneyConverterRatesUpdaterTest.java +++ b/app/src/test/java/com/anbroidsdev/converter/RatesUpdaterTest.java @@ -21,7 +21,7 @@ import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) -public class MoneyConverterRatesUpdaterTest { +public class RatesUpdaterTest { private static final Currency BASE = Currency.getInstance("USD"); private static final Map RATES = new HashMap<>(); @@ -30,35 +30,48 @@ public class MoneyConverterRatesUpdaterTest { RATES.put("GBP", 0.656183); RATES.put("JPY", 120.171); } + private static final Rates CONVERTED_RATES = new Rates(RATES); private static final long TIMESTAMP = 1425916861; @Mock private MoneyConverter moneyConverter; @Mock private OpenExchangeRatesApi openExchangeRatesApi; + @Mock + private SharedPreferencesRatesSaver ratesSaver; @InjectMocks - private MoneyConverterRatesUpdater moneyConverterRatesUpdater; + private RatesUpdater ratesUpdater; @Mock - private MoneyConverterRatesUpdater.OnRatesUpdateListener listener; + private RatesUpdater.OnRatesUpdateListener listener; @Test public void shouldGetLatestRatesFromOpenExchangeRates() throws Exception { prepareMoneyConverter(); - moneyConverterRatesUpdater.updateRates(listener); + ratesUpdater.updateRates(listener); verify(openExchangeRatesApi).getLatestRates(eq(BASE.getCurrencyCode()), any(OpenExchangeRatesApi.OnLatestRatesCallback.class)); } + @Test + public void shouldSaveTheRetrievedRates() throws Exception { + prepareMoneyConverter(); + prepareOnLatestRatesCallback(); + + ratesUpdater.updateRates(listener); + + verify(ratesSaver, times(1)).saveRates(eq(CONVERTED_RATES), eq(TIMESTAMP)); + } + @Test public void shouldUpdateMoneyConverterRates() throws Exception { prepareMoneyConverter(); prepareOnLatestRatesCallback(); - moneyConverterRatesUpdater.updateRates(listener); + ratesUpdater.updateRates(listener); - verify(moneyConverter, times(1)).setRates(eq(convertRates(RATES))); + verify(moneyConverter, times(1)).setRates(eq(CONVERTED_RATES)); } @Test @@ -68,9 +81,9 @@ public void shouldNotThrowExceptionIfACurrencyIsNotValid() throws Exception { RATES.put("WRONG", 0.43672); - moneyConverterRatesUpdater.updateRates(listener); + ratesUpdater.updateRates(listener); - verify(moneyConverter, times(1)).setRates(eq(convertRates(RATES))); + verify(moneyConverter, times(1)).setRates(eq(CONVERTED_RATES)); RATES.remove("WRONG"); } @@ -80,9 +93,9 @@ public void shouldNotifyTheListenerWhenTheRatesAreUpdated() throws Exception { prepareMoneyConverter(); prepareOnLatestRatesCallback(); - moneyConverterRatesUpdater.updateRates(listener); + ratesUpdater.updateRates(listener); - verify(listener).onRatesUpdated(eq(convertRates(RATES)), eq(TIMESTAMP)); + verify(listener).onRatesUpdated(); } private void prepareMoneyConverter() { @@ -99,16 +112,4 @@ public Object answer(InvocationOnMock invocation) throws Throwable { }).when(openExchangeRatesApi).getLatestRates(anyString(), any(OpenExchangeRatesApi.OnLatestRatesCallback.class)); } - private static Map convertRates(Map rates) { - final Map expectedRates = new HashMap<>(rates.size()); - - for (Map.Entry rate : rates.entrySet()) { - try { - expectedRates.put(Currency.getInstance(rate.getKey()), rate.getValue()); - } catch (IllegalArgumentException e) {} - } - - return expectedRates; - } - } diff --git a/app/src/test/java/com/anbroidsdev/converter/SharedPreferencesRatesSaverTest.java b/app/src/test/java/com/anbroidsdev/converter/SharedPreferencesRatesSaverTest.java new file mode 100644 index 0000000..2ee8432 --- /dev/null +++ b/app/src/test/java/com/anbroidsdev/converter/SharedPreferencesRatesSaverTest.java @@ -0,0 +1,72 @@ +package com.anbroidsdev.converter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.Currency; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@RunWith(MockitoJUnitRunner.class) +public class SharedPreferencesRatesSaverTest { + + private static final String KEY_RATES = "rates"; + private static final String KEY_TIMESTAMP = "timestamp"; + + private static final Rates RATES = new Rates(); + static { + RATES.put(new Rate(Currency.getInstance("EUR"), 0.879689294)); + RATES.put(new Rate(Currency.getInstance("GBP"), 0.656183)); + RATES.put(new Rate(Currency.getInstance("JPY"), 120.171)); + } + private static final long TIMESTAMP = 1425916861; + + @Mock + private ObjectSharedPreferences sharedPreferences; + @Mock + private ObjectSharedPreferences.ObjectEditor sharedPreferencesEditor; + + private SharedPreferencesRatesSaver ratesSaver; + + @Before + public void setUp() throws Exception { + doReturn(RATES).when(sharedPreferences).getObject(eq(KEY_RATES), eq(Rates.class)); + doReturn(TIMESTAMP).when(sharedPreferences).getLong(eq(KEY_TIMESTAMP), eq(0L)); + doReturn(sharedPreferencesEditor).when(sharedPreferences).edit(); + + ratesSaver = new SharedPreferencesRatesSaver(sharedPreferences); + } + + @Test + public void shouldRestoreLastSavedRatesDuringInitialization() throws Exception { + assertEquals(RATES, ratesSaver.getLastSavedRates()); + assertEquals(TIMESTAMP, ratesSaver.getLastSavedRatesTimestamp()); + } + + @Test + public void shouldSaveRatesInsideTheSharedPreferences() throws Exception { + ratesSaver.saveRates(RATES, TIMESTAMP); + + verify(sharedPreferencesEditor, times(1)).putObject(eq(KEY_RATES), eq(RATES)); + verify(sharedPreferencesEditor, times(1)).putLong(eq(KEY_TIMESTAMP), eq(TIMESTAMP)); + assertEquals(RATES, ratesSaver.getLastSavedRates()); + assertEquals(TIMESTAMP, ratesSaver.getLastSavedRatesTimestamp()); + } + + @Test + public void shouldClearSavedRates() throws Exception { + ratesSaver.clear(); + + assertNull(ratesSaver.getLastSavedRates()); + assertEquals(0, ratesSaver.getLastSavedRatesTimestamp()); + } + +}