├── settings.gradle ├── .travis.yml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── src ├── main │ └── java │ │ ├── Country.java │ │ ├── CountriesService.java │ │ └── CountriesServiceSolved.java └── test │ └── java │ ├── CountriesTestProvider.java │ └── CountriesServiceSolvedTest.java ├── LICENSE.txt ├── README.md ├── gradlew.bat └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'test' -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | branches: 5 | only: 6 | - solved -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergiiz/RxBasicsKata/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.iws 3 | .gradle 4 | .idea 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | /secret 11 | /out 12 | 13 | local.properties 14 | release.properties 15 | project.properties 16 | .gitignore 17 | .idea/ 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Jan 11 07:52:05 GMT 2020 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /src/main/java/Country.java: -------------------------------------------------------------------------------- 1 | class Country { 2 | final String name; 3 | final String currency; 4 | final long population; 5 | 6 | Country(String name, String currency, long population) { 7 | this.name = name; 8 | this.currency = currency; 9 | this.population = population; 10 | } 11 | 12 | public String getName() { 13 | return name; 14 | } 15 | 16 | public String getCurrency() { 17 | return currency; 18 | } 19 | 20 | public long getPopulation() { 21 | return population; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return "Country{" + 27 | "name='" + name + '\'' + 28 | ", currency='" + currency + '\'' + 29 | ", population=" + population + 30 | '}'; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sergii Zhuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/CountriesService.java: -------------------------------------------------------------------------------- 1 | import io.reactivex.rxjava3.core.Observable; 2 | import io.reactivex.rxjava3.core.Single; 3 | 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.concurrent.FutureTask; 7 | 8 | interface CountriesService { 9 | 10 | Single countryNameInCapitals(Country country); 11 | 12 | Single countCountries(List countries); 13 | 14 | Observable listPopulationOfEachCountry(List countries); 15 | 16 | Observable listNameOfEachCountry(List countries); 17 | 18 | Observable listOnly3rdAnd4thCountry(List countries); 19 | 20 | Single isAllCountriesPopulationMoreThanOneMillion(List countries); 21 | 22 | Observable listPopulationMoreThanOneMillion(List countries); 23 | 24 | /** 25 | * @param countriesFromNetwork an async task which is sometimes very very slow 26 | * @return the filtered values from the {@link FutureTask} or an {@link Observable#empty()} if there are no values within 1 second 27 | */ 28 | Observable listPopulationMoreThanOneMillionWithTimeoutFallbackToEmpty(FutureTask> countriesFromNetwork); 29 | 30 | Observable getCurrencyUsdIfNotFound(String countryName, List countries); 31 | 32 | Observable sumPopulationOfCountries(List countries); 33 | 34 | Observable sumPopulationOfCountries(Observable countryObservable1, 35 | Observable countryObservable2); 36 | 37 | Single> mapCountriesToNamePopulation(List countries); 38 | 39 | Single areEmittingSameSequences(Observable countryObservable1, 40 | Observable countryObservable2); 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Practical challenges for RxJava learners 2 | 3 | A set of simple code challenges to learn RxJava using JUnit tests as an acceptance criteria. Focused on some basic concepts and doesn't cover any Android topics yet. 4 | 5 | 6 | [!["Solved" branch](https://travis-ci.org/sergiiz/RxBasicsKata.svg?branch=solved)](https://travis-ci.org/sergiiz/RxBasicsKata) 7 | 8 | 9 | ## Current implementation 10 | ### Dependencies: 11 | - RxJava 2.1.7  12 | - JUnit 4.12 13 | 14 | ### Reactive types covered: 15 | - Observable: the heart of Rx, a class that emits a stream of data or events 16 | - Single : a version of an Observable that emits a single item or fails 17 | - Maybe: lazy emission pattern, can emit 1 or 0 items or an error signal 18 | 19 | ### Operators covered: 20 | - map: transforms the items by applying a function to each item 21 | - flatMap: takes the emissions of one Observable and returns merged emissions in another Observable to take its place 22 | - filter: emits only those items from that pass a criteria (predicate test) 23 | - skip/take: suppress or takes the first n items  24 | - all: determines whether all items meet some criteria 25 | - reduce: applies a function to each item sequentially, and emit the final value. For example, it can be used to sum up all emitted items 26 | - toMap: converts an Observable into another object or data structure 27 | - merge: combine multiple Observables into one by merging their emissions 28 | - sequenceEqual: determine whether two Observables emit the same sequence of items 29 | - test: returns TestObserver with current Observable subscribed 30 | - timeout: to handle timeouts, e.g. deliver some fallback data 31 | 32 | ### Testing approach: 33 | - The set of test cases are defined in a separate java file 34 | - As a “receiver” of emitted test events we use TestObserver. It records events and allows to make assertions about them 35 | - All tests are failing when you just took them from the repo. This is expected behaviour. You should make tests pass by implementing the logic in CountriesServiceSolved class 36 | 37 | ## Blog post 38 | See my blog post at Medium for more details: https://proandroiddev.com/practical-challenges-for-rxjava-learners-1821c454de9. 39 | 40 | ## Contribution 41 | Pull requests and new code challenges are really welcome. 42 | -------------------------------------------------------------------------------- /src/test/java/CountriesTestProvider.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.List; 3 | 4 | final class CountriesTestProvider { 5 | static final String CURRENCY_EUR = "EUR"; 6 | static final String CURRENCY_PLN = "PLN"; 7 | static final String CURRENCY_GBP = "GBP"; 8 | static final String CURRENCY_UAH = "UAH"; 9 | static final String CURRENCY_CHF = "CHF"; 10 | 11 | private static ArrayList countries = new ArrayList<>(); 12 | 13 | static { 14 | countries.add(new Country("Germany", CURRENCY_EUR, 80620000)); 15 | countries.add(new Country("France", CURRENCY_EUR, 66030000)); 16 | countries.add(new Country("United Kingdom", CURRENCY_GBP, 64100000)); 17 | countries.add(new Country("Poland", CURRENCY_PLN, 38530000)); 18 | countries.add(new Country("Ukraine", CURRENCY_UAH, 45490000)); 19 | countries.add(new Country("Austria", CURRENCY_EUR, 8474000)); 20 | countries.add(new Country("Switzerland", CURRENCY_CHF, 8081000)); 21 | countries.add(new Country("Luxembourg", CURRENCY_EUR, 576249)); 22 | } 23 | 24 | private CountriesTestProvider() { 25 | // hidden 26 | } 27 | 28 | static List countries() { 29 | return new ArrayList<>(countries); 30 | } 31 | 32 | static List countriesPopulationMoreThanOneMillion() { 33 | List result = new ArrayList<>(); 34 | for (Country country : countries) { 35 | if (country.population > 1000000) { 36 | result.add(country); 37 | } 38 | } 39 | return result; 40 | } 41 | 42 | 43 | static List populationOfCountries() { 44 | List result = new ArrayList<>(countries.size()); 45 | for (Country country : countries) { 46 | result.add(country.population); 47 | } 48 | return result; 49 | } 50 | 51 | static List namesOfCountries() { 52 | List result = new ArrayList<>(countries.size()); 53 | for (Country country : countries) { 54 | result.add(country.name); 55 | } 56 | return result; 57 | } 58 | 59 | static Long sumPopulationOfAllCountries() { 60 | long result = 0L; 61 | for (Country country : countries) { 62 | result += country.population; 63 | } 64 | return result; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/main/java/CountriesServiceSolved.java: -------------------------------------------------------------------------------- 1 | import io.reactivex.rxjava3.core.Observable; 2 | import io.reactivex.rxjava3.core.Single; 3 | 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.concurrent.FutureTask; 7 | 8 | class CountriesServiceSolved implements CountriesService { 9 | 10 | @Override 11 | public Single countryNameInCapitals(Country country) { 12 | return null; // put your solution here 13 | } 14 | 15 | public Single countCountries(List countries) { 16 | return null; // put your solution here 17 | } 18 | 19 | public Observable listPopulationOfEachCountry(List countries) { 20 | return null; // put your solution here; 21 | } 22 | 23 | @Override 24 | public Observable listNameOfEachCountry(List countries) { 25 | return null; // put your solution here 26 | } 27 | 28 | @Override 29 | public Observable listOnly3rdAnd4thCountry(List countries) { 30 | return null; // put your solution here 31 | } 32 | 33 | @Override 34 | public Single isAllCountriesPopulationMoreThanOneMillion(List countries) { 35 | return null; // put your solution here 36 | } 37 | 38 | @Override 39 | public Observable listPopulationMoreThanOneMillion(List countries) { 40 | return null; // put your solution here 41 | } 42 | 43 | @Override 44 | public Observable listPopulationMoreThanOneMillionWithTimeoutFallbackToEmpty(final FutureTask> countriesFromNetwork) { 45 | return null; // put your solution here 46 | } 47 | 48 | @Override 49 | public Observable getCurrencyUsdIfNotFound(String countryName, List countries) { 50 | return null; // put your solution here 51 | } 52 | 53 | @Override 54 | public Observable sumPopulationOfCountries(List countries) { 55 | return null; // put your solution here 56 | } 57 | 58 | @Override 59 | public Single> mapCountriesToNamePopulation(List countries) { 60 | return null; // put your solution here 61 | } 62 | 63 | @Override 64 | public Observable sumPopulationOfCountries(Observable countryObservable1, 65 | Observable countryObservable2) { 66 | return null; // put your solution here 67 | } 68 | 69 | @Override 70 | public Single areEmittingSameSequences(Observable countryObservable1, 71 | Observable countryObservable2) { 72 | return null; // put your solution here 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /src/test/java/CountriesServiceSolvedTest.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.Collections; 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Locale; 6 | import java.util.Map; 7 | import java.util.concurrent.FutureTask; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | import io.reactivex.rxjava3.core.Observable; 11 | import io.reactivex.rxjava3.observers.TestObserver; 12 | import org.junit.jupiter.api.BeforeEach; 13 | import org.junit.jupiter.api.Test; 14 | 15 | public class CountriesServiceSolvedTest { 16 | 17 | private CountriesService countriesService; 18 | private List allCountries; 19 | 20 | @BeforeEach 21 | public void setUp() { 22 | countriesService = new CountriesServiceSolved(); 23 | allCountries = CountriesTestProvider.countries(); 24 | } 25 | 26 | @Test 27 | public void rx_CountryNameInCapitals() { 28 | Country testCountry = CountriesTestProvider.countries().get(0); 29 | String expected = testCountry.name.toUpperCase(Locale.US); 30 | TestObserver testObserver = countriesService 31 | .countryNameInCapitals(testCountry) 32 | .test(); 33 | testObserver.assertNoErrors(); 34 | testObserver.assertValue(expected); 35 | } 36 | 37 | @Test 38 | public void rx_CountAmountOfCountries() { 39 | Integer expected = CountriesTestProvider.countries().size(); 40 | TestObserver testObserver = countriesService 41 | .countCountries(allCountries) 42 | .test(); 43 | testObserver.assertNoErrors(); 44 | testObserver.assertValue(expected); 45 | } 46 | 47 | @Test 48 | public void rx_ListPopulationOfEachCountry() { 49 | List expectedResult = CountriesTestProvider.populationOfCountries(); 50 | TestObserver testObserver = countriesService 51 | .listPopulationOfEachCountry(allCountries) 52 | .test(); 53 | testObserver.assertValueSequence(expectedResult); 54 | testObserver.assertNoErrors(); 55 | } 56 | 57 | @Test 58 | public void rx_ListNameOfEachCountry() { 59 | List expectedResult = CountriesTestProvider.namesOfCountries(); 60 | TestObserver testObserver = countriesService 61 | .listNameOfEachCountry(allCountries) 62 | .test(); 63 | testObserver.assertValueSequence(expectedResult); 64 | testObserver.assertNoErrors(); 65 | } 66 | 67 | @Test 68 | public void rx_ListOnly3rdAnd4thCountry() { 69 | List expectedResult = new ArrayList<>(); 70 | expectedResult.add(allCountries.get(2)); 71 | expectedResult.add(allCountries.get(3)); 72 | 73 | TestObserver testObserver = countriesService 74 | .listOnly3rdAnd4thCountry(allCountries) 75 | .test(); 76 | testObserver.assertValueSequence(expectedResult); 77 | testObserver.assertNoErrors(); 78 | } 79 | 80 | @Test 81 | public void rx_IsAllCountriesPopulationMoreThanOneMillion_Positive() { 82 | TestObserver testObserver = countriesService 83 | .isAllCountriesPopulationMoreThanOneMillion(CountriesTestProvider.countriesPopulationMoreThanOneMillion()) 84 | .test(); 85 | testObserver.assertResult(true); 86 | testObserver.assertNoErrors(); 87 | } 88 | 89 | @Test 90 | public void rx_IsAllCountriesPopulationMoreThanOneMillion_Negative() { 91 | TestObserver testObserver = countriesService 92 | .isAllCountriesPopulationMoreThanOneMillion(allCountries) 93 | .test(); 94 | testObserver.assertResult(false); 95 | testObserver.assertNoErrors(); 96 | } 97 | 98 | @Test 99 | public void rx_ListPopulationMoreThanOneMillion() { 100 | List expectedResult = CountriesTestProvider.countriesPopulationMoreThanOneMillion(); 101 | TestObserver testObserver = countriesService 102 | .listPopulationMoreThanOneMillion(allCountries) 103 | .test(); 104 | testObserver.assertValueSequence(expectedResult); 105 | testObserver.assertNoErrors(); 106 | } 107 | 108 | @Test 109 | public void rx_ListPopulationMoreThanOneMillionWithTimeoutFallbackToEmpty_When_NoTimeout() throws InterruptedException { 110 | FutureTask> futureTask = new FutureTask<>(() -> { 111 | TimeUnit.MILLISECONDS.sleep(100); 112 | return allCountries; 113 | }); 114 | new Thread(futureTask).start(); 115 | TestObserver testObserver = countriesService 116 | .listPopulationMoreThanOneMillionWithTimeoutFallbackToEmpty(futureTask) 117 | .test(); 118 | List expectedResult = CountriesTestProvider.countriesPopulationMoreThanOneMillion(); 119 | testObserver.await(); 120 | testObserver.assertComplete(); 121 | testObserver.assertValueSequence(expectedResult); 122 | testObserver.assertNoErrors(); 123 | } 124 | 125 | @Test 126 | public void rx_ListPopulationMoreThanOneMillionWithTimeoutFallbackToEmpty_When_Timeout() throws InterruptedException { 127 | FutureTask> futureTask = new FutureTask<>(() -> { 128 | TimeUnit.SECONDS.sleep(15); 129 | return allCountries; 130 | }); 131 | new Thread(futureTask).start(); 132 | TestObserver testObserver = countriesService 133 | .listPopulationMoreThanOneMillionWithTimeoutFallbackToEmpty(futureTask) 134 | .test(); 135 | testObserver.await(); 136 | testObserver.assertComplete(); 137 | testObserver.assertNoValues(); 138 | testObserver.assertNoErrors(); 139 | } 140 | 141 | @Test 142 | public void rx_GetCurrencyUsdIfNotFound_When_CountryFound() { 143 | String countryRequested = "Austria"; 144 | String expectedCurrencyValue = "EUR"; 145 | TestObserver testObserver = countriesService 146 | .getCurrencyUsdIfNotFound(countryRequested, allCountries) 147 | .test(); 148 | testObserver.assertResult(expectedCurrencyValue); 149 | testObserver.assertNoErrors(); 150 | } 151 | 152 | @Test 153 | public void rx_GetCurrencyUsdIfNotFound_When_CountryNotFound() { 154 | String countryRequested = "Senegal"; 155 | String expectedCurrencyValue = "USD"; 156 | TestObserver testObserver = countriesService 157 | .getCurrencyUsdIfNotFound(countryRequested, allCountries) 158 | .test(); 159 | testObserver.assertResult(expectedCurrencyValue); 160 | testObserver.assertNoErrors(); 161 | } 162 | 163 | @Test 164 | public void rx_SumPopulationOfCountries() { 165 | // hint: use "reduce" operator 166 | TestObserver testObserver = countriesService 167 | .sumPopulationOfCountries(allCountries) 168 | .test(); 169 | testObserver.assertResult(CountriesTestProvider.sumPopulationOfAllCountries()); 170 | testObserver.assertNoErrors(); 171 | } 172 | 173 | 174 | @Test 175 | public void rx_MapCountriesToNamePopulation() { 176 | TestObserver> values = countriesService.mapCountriesToNamePopulation(allCountries).test(); 177 | Map expected = new HashMap<>(); 178 | for (Country country : allCountries) { 179 | expected.put(country.name, country.population); 180 | } 181 | values.assertResult(expected); 182 | values.assertNoErrors(); 183 | } 184 | 185 | @Test 186 | public void rx_sumPopulationOfCountries() { 187 | // hint: use "map" operator 188 | TestObserver testObserver = countriesService 189 | .sumPopulationOfCountries(Observable.fromIterable(allCountries), Observable.fromIterable(allCountries)) 190 | .test(); 191 | testObserver.assertResult(CountriesTestProvider.sumPopulationOfAllCountries() 192 | + CountriesTestProvider.sumPopulationOfAllCountries()); 193 | testObserver.assertNoErrors(); 194 | } 195 | 196 | @Test 197 | public void rx_areEmittingSameSequences_Positive() { 198 | // hint: use "sequenceEqual" operator 199 | TestObserver testObserver = countriesService 200 | .areEmittingSameSequences(Observable.fromIterable(allCountries), Observable.fromIterable(allCountries)) 201 | .test(); 202 | testObserver.assertResult(true); 203 | testObserver.assertNoErrors(); 204 | } 205 | 206 | @Test 207 | public void rx_areEmittingSameSequences_Negative() { 208 | List allCountriesDifferentSequence = new ArrayList<>(allCountries); 209 | Collections.swap(allCountriesDifferentSequence, 0, 1); 210 | TestObserver testObserver = countriesService 211 | .areEmittingSameSequences( 212 | Observable.fromIterable(allCountries), 213 | Observable.fromIterable(allCountriesDifferentSequence)) 214 | .test(); 215 | testObserver.assertResult(false); 216 | testObserver.assertNoErrors(); 217 | } 218 | 219 | } 220 | --------------------------------------------------------------------------------